diff -urN oldtree/Documentation/acpi/sony_acpi.txt newtree/Documentation/acpi/sony_acpi.txt
--- oldtree/Documentation/acpi/sony_acpi.txt	1969-12-31 19:00:00.000000000 -0500
+++ newtree/Documentation/acpi/sony_acpi.txt	2006-02-13 19:19:59.582575760 -0500
@@ -0,0 +1,87 @@
+ACPI Sony Notebook Control Driver (SNC) Readme
+----------------------------------------------
+	Copyright (C) 2004- 2005 Stelian Pop <stelian@popies.net>
+
+This mini-driver drives the ACPI SNC device present in the
+ACPI BIOS of the Sony Vaio laptops.
+
+It gives access to some extra laptop functionalities. In
+its current form, this driver is mainly useful for controlling the
+screen brightness, but it may do more in the future.
+
+You should probably start by trying the sonypi driver, and try
+sony_acpi only if sonypi doesn't work for you.
+
+Usage:
+------
+
+Loading the sony_acpi module will create a /proc/acpi/sony/
+directory populated with a couple of files.
+
+You then read/write integer values from/to those files by using
+standard UNIX tools.
+
+The files are:
+	brightness		current screen brightness
+	brightness_default	screen brightness which will be set
+				when the laptop will be rebooted
+	cdpower			power on/off the internal CD drive
+
+Note that some files may be missing if they are not supported
+by your particular laptop model.
+
+Example usage:
+	# echo "1" > /proc/acpi/sony/brightness
+sets the lowest screen brightness,
+	# echo "8" > /proc/acpi/sony/brightness
+sets the highest screen brightness,
+	# cat /proc/acpi/sony/brightness
+retrieves the current screen brightness.
+
+Development:
+------------
+
+If you want to help with the development of this driver (and
+you are not afraid of any side effects doing strange things with
+your ACPI BIOS could have on your laptop), load the driver and
+pass the option 'debug=1'.
+
+REPEAT: DON'T DO THIS IF YOU DON'T LIKE RISKY BUSINESS.
+
+In your kernel logs you will find the list of all ACPI methods
+the SNC device has on your laptop. You can see the GBRT/SBRT methods
+used to get/set the brightness, but there are others.
+
+I HAVE NO IDEA WHAT THOSE METHODS DO.
+
+The sony_acpi driver creates, for some of those methods (the most
+current ones found on several Vaio models), an entry under
+/proc/acpi/sony/, just like the 'brightness' one. You can create
+other entries corresponding to your own laptop methods by further
+editing the source (see the 'sony_acpi_values' table, and add a new
+structure to this table with your get/set method names).
+
+Your mission, should you accept it, is to try finding out what
+those entries are for, by reading/writing random values from/to those
+files and find out what is the impact on your laptop.
+
+Should you find anything interesting, please report it back to me,
+I will not disavow all knowledge of your actions :)
+
+Bugs/Limitations:
+-----------------
+
+* This driver is not based on official documentation from Sony
+  (because there is none), so there is no guarantee this driver
+  will work at all, or do the right thing. Although this hasn't
+  happened to me, this driver could do very bad things to your
+  laptop, including permanent damage.
+
+* The sony_acpi and sonypi drivers do not interact at all. In the
+  future, sonypi could use sony_acpi to do (part of) its business.
+
+* spicctrl, which is the userspace tool used to communicate with the
+  sonypi driver (through /dev/sonypi) does not try to use the
+  sony_acpi driver. In the future, spicctrl could try sonypi first,
+  and if it isn't present, try sony_acpi instead.
+
diff -urN oldtree/Documentation/filesystems/00-INDEX newtree/Documentation/filesystems/00-INDEX
--- oldtree/Documentation/filesystems/00-INDEX	2006-01-02 22:21:10.000000000 -0500
+++ newtree/Documentation/filesystems/00-INDEX	2006-02-13 19:19:59.582575760 -0500
@@ -6,6 +6,8 @@
 	- info and mount options for the Acorn Advanced Disc Filing System.
 affs.txt
 	- info and mount options for the Amiga Fast File System.
+asfs.txt
+	- info and mount options for the Amiga Smart File System.
 bfs.txt
 	- info for the SCO UnixWare Boot Filesystem (BFS).
 cifs.txt
diff -urN oldtree/Documentation/filesystems/asfs.txt newtree/Documentation/filesystems/asfs.txt
--- oldtree/Documentation/filesystems/asfs.txt	1969-12-31 19:00:00.000000000 -0500
+++ newtree/Documentation/filesystems/asfs.txt	2006-02-13 19:19:59.582575760 -0500
@@ -0,0 +1,161 @@
+
+Amiga SmartFileSystem, Linux implementation
+===========================================
+
+ASFS is a Amiga Smart FileSystem driver for Linux. It supports reading
+files and directories. From version 1.0 there is also an experimental
+(almost full) write support. Experimental means that it hasn't been
+tested enough yet, so use it with care. Symbolic links (in AmigaOS
+called soft links) are also supported read/write. Read notes below
+about symlinks support.
+
+
+Unsupported features of Amiga SFS
+================================
+
+ASFS currently does not support safe-delete feature of Amiga SFS
+filesystem. It simply deletes files instead of moving them to
+".recycled" directory. It also doesn't remove files from ".recycled"
+directory, when there is no space left on drive.
+
+If there is no space left, you need to manually remove files from
+".recycled" directory. Also if you want to delete a file in a safe
+way, you need to move it to ".recycled" directory by hand.
+
+Because of all of above, the amount of free space on disk does not
+include space used by all files from ".recycled" directory.
+
+
+Limitations
+===========
+
+There is no Amiga protection bits into Linux permission bits tranlation
+and vice versa. If you need this feature, mail me.
+
+ASFS will always keep some amount of blocks free. This means that you
+cannot fill the drive completely. It is because Amiga SFS uses some
+special methods of writing data (called safe write), which needs some
+additional free space.
+
+File systems with unfinished transactions (this happens when system crashed
+during writing data to disk on AmigaOS/MorphOS) will be mounted read-only
+to protect data. The only way to fix such filesystem is to mount it under
+AmigaOS or MorphOS.
+
+Do not try to mount and write to filesystem with errors. Bad things will
+happen.
+
+
+Mount options for the ASFS
+==========================
+
+setuid=uid
+		This sets the owner of all files and directories in the file
+		system to uid.
+
+setgid=gid
+		Same as above, but for gid.
+
+mode=mode
+		Sets the mode flags to the given (octal) value. Directories
+		will get an x permission if the corresponding r bit is set.
+		The default mode is 0644, which means that everybody are allowed
+		to read files, but only root can write to them.
+		(for directories this means also that search bits are set).
+
+prefix=path
+		Path will be prefixed to every absolute path name of symbolic
+		links on an ASFS/AFFS partition. Default = "/". (See below.)
+
+volume=name
+		When symbolic links with an absolute path are created
+		on an ASFS/AFFS partition, name will be prepended as the
+		volume name. Default = "" (empty string). (See below.)
+
+lowercasevol
+		Translate all volume names in symlinks to lower case.
+		Disabled by default. (See below.)
+
+iocharset=name
+		Character set to use for converting file names. Specifies
+		character set used by your Linux system.
+codepage=name
+		Set the codepage number for converting file names. Specifies
+		character set used by your Amiga. Use full name (for example
+		'cp1251' instead of '1251') here, this allows to specify any
+		character set, not only numbered one (like 'iso8859-2').
+		Use special name 'none' to disable the NLS file name
+		translation.
+
+Symbolic links
+==============
+
+Although the Amiga and Linux file systems resemble each other, there
+are some, not always subtle, differences. One of them becomes apparent
+with symbolic links. While Linux has a file system with exactly one
+root directory, the Amiga has a separate root directory for each
+file system (for example, partition, floppy disk, ...). With the Amiga,
+these entities are called "volumes". They have symbolic names which
+can be used to access them. Thus, symbolic links can point to a
+different volume. ASFS turns the volume name into a directory name
+and prepends the prefix path (see prefix option) to it. When option
+"lowercasevol" is set, it also translates volume names to lower case.
+If the volume name is the same as a name given in "volume" option,
+it will be ignored and an absolute path will be created.
+
+Example:
+You mount all your Amiga partitions under /amiga/<volume> (where
+<volume> is the name of the volume), and you give options
+`prefix="/amiga/",volume="Linux",lowercasevol' when mounting all your
+ASFS partitions. (They might be "User", "WB" and "Graphics", the mount
+points /amiga/user, /amiga/wb and /amiga/graphics).
+
+A symbolic link referring to "USER:sc/include/dos/dos.h" will be
+translated to "/amiga/user/sc/include/dos/dos.h".
+A symbolic link referring to "Linux:etc/fstab" will be translated to
+"/etc/fstab".
+If you create a symlink referring to "/amiga/graphics/data/pict.jpg",
+it will be saved as "graphics:data/pict.jpg".
+If you create a symlink referring to "/boot/System.map", it will be
+saved as "Linux:boot/System.map".
+
+
+Other information
+=================
+
+Supported block sizes are: 512, 1024, 2048 and 4096 bytes. Larger blocks
+speed up almost everything at the expense of wasted disk space. The speed
+gain above 4K seems not really worth the price, so you don't lose too
+much here, either.
+
+This file system has been tested on Motorola PPC and 68k, as well as
+Intel x86 systems. I don't know, if it works on other Linux systems.
+
+This filesystem is in BETA STAGE. This means that driver MIGHT corrupt
+or damage data on your disk. Remember! YOU USE IT ON YOUR OWN RISK!
+
+I made almost all I could to minimalize this risk. On my systems several
+gigabytes has been succesfully copied from and to SFS disks. I would also
+appreciate any infomation if this filesystem works on your system or not.
+See next paragraph for my email.
+
+Some parts of this documentation has been adapted from AFFS driver docs.
+
+
+Author, contact and copyright infos
+===================================
+
+ASFS has been written by Marek 'March' Szyprowski <marek@amiga.pl>.
+Mail me if you have any suggestions or found a bug.
+
+Copyright (C) 2003,2004,2005  Marek 'March' Szyprowski <marek@amiga.pl>
+
+Thanks to Marcin Kurek (Morgoth/Dreamolers-CAPS) for help and parts
+of original amiga version of SmartFilesystem source code.
+
+SmartFilesystem is copyrighted (C) 2003,2004 by: John Hendrikx,
+Ralph Schmidt, Emmanuel Lesueur, David Gerber and Marcin Kurek
+
+The ASFS driver is realased under the terms of of the GNU General
+Public License. See source code for more details.
+
diff -urN oldtree/Documentation/filesystems/tmpfs.txt newtree/Documentation/filesystems/tmpfs.txt
--- oldtree/Documentation/filesystems/tmpfs.txt	2006-01-02 22:21:10.000000000 -0500
+++ newtree/Documentation/filesystems/tmpfs.txt	2006-02-13 19:19:59.583575608 -0500
@@ -78,6 +78,18 @@
 that instance in a system with many cpus making intensive use of it.
 
 
+tmpfs has a mount option to set the NUMA memory allocation policy for
+all files in that instance:
+mpol=interleave		prefers to allocate memory from each node in turn
+mpol=default		prefers to allocate memory from the local node
+mpol=bind		prefers to allocate from mpol_nodelist
+mpol=preferred		prefers to allocate from first node in mpol_nodelist
+
+The following mount option is used in conjunction with mpol=interleave,
+mpol=bind or mpol=preferred:
+mpol_nodelist:	nodelist suitable for parsing with nodelist_parse.
+
+
 To specify the initial root directory you can use the following mount
 options:
 
diff -urN oldtree/Documentation/page_owner.c newtree/Documentation/page_owner.c
--- oldtree/Documentation/page_owner.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/Documentation/page_owner.c	2006-02-13 19:19:59.583575608 -0500
@@ -0,0 +1,140 @@
+/*
+ * User-space helper to sort the output of /proc/page_owner
+ *
+ * Example use:
+ * cat /proc/page_owner > page_owner.txt
+ * ./sort page_owner.txt sorted_page_owner.txt
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+struct block_list {
+	char *txt;
+	int len;
+	int num;
+};
+
+
+static struct block_list *list;
+static int list_size;
+static int max_size;
+
+struct block_list *block_head;
+
+int read_block(char *buf, FILE *fin)
+{
+	int ret = 0;
+	int hit = 0;
+	char *curr = buf;
+
+	for (;;) {
+		*curr = getc(fin);
+		if (*curr == EOF) return -1;
+
+		ret++;
+		if (*curr == '\n' && hit == 1)
+			return ret - 1;
+		else if (*curr == '\n')
+			hit = 1;
+		else
+			hit = 0;
+		curr++;
+	}
+}
+
+static int compare_txt(struct block_list *l1, struct block_list *l2)
+{
+	return strcmp(l1->txt, l2->txt);
+}
+
+static int compare_num(struct block_list *l1, struct block_list *l2)
+{
+	return l2->num - l1->num;
+}
+
+static void add_list(char *buf, int len)
+{
+	if (list_size != 0 &&
+	    len == list[list_size-1].len &&
+	    memcmp(buf, list[list_size-1].txt, len) == 0) {
+		list[list_size-1].num++;
+		return;
+	}
+	if (list_size == max_size) {
+		printf("max_size too small??\n");
+		exit(1);
+	}
+	list[list_size].txt = malloc(len+1);
+	list[list_size].len = len;
+	list[list_size].num = 1;
+	memcpy(list[list_size].txt, buf, len);
+	list[list_size].txt[len] = 0;
+	list_size++;
+	if (list_size % 1000 == 0) {
+		printf("loaded %d\r", list_size);
+		fflush(stdout);
+	}
+}
+
+int main(int argc, char **argv)
+{
+	FILE *fin, *fout;
+	char buf[1024];
+	int ret, i, count;
+	struct block_list *list2;
+	struct stat st;
+
+	fin = fopen(argv[1], "r");
+	fout = fopen(argv[2], "w");
+	if (!fin || !fout) {
+		printf("Usage: ./program <input> <output>\n");
+		perror("open: ");
+		exit(2);
+	}
+
+	fstat(fileno(fin), &st);
+	max_size = st.st_size / 100; /* hack ... */
+
+	list = malloc(max_size * sizeof(*list));
+
+	for(;;) {
+		ret = read_block(buf, fin);
+		if (ret < 0)
+			break;
+
+		buf[ret] = '\0';
+		add_list(buf, ret);
+	}
+
+	printf("loaded %d\n", list_size);
+
+	printf("sorting ....\n");
+
+	qsort(list, list_size, sizeof(list[0]), compare_txt);
+
+	list2 = malloc(sizeof(*list) * list_size);
+
+	printf("culling\n");
+
+	for (i=count=0;i<list_size;i++) {
+		if (count == 0 ||
+		    strcmp(list2[count-1].txt, list[i].txt) != 0) {
+			list2[count++] = list[i];
+		} else {
+			list2[count-1].num += list[i].num;
+		}
+	}
+
+	qsort(list2, count, sizeof(list[0]), compare_num);
+
+	for (i=0;i<count;i++) {
+		fprintf(fout, "%d times:\n%s\n", list2[i].num, list2[i].txt);
+	}
+	return 0;
+}
diff -urN oldtree/Makefile newtree/Makefile
--- oldtree/Makefile	2006-01-02 22:21:10.000000000 -0500
+++ newtree/Makefile	2006-02-13 19:20:29.688998888 -0500
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 6
 SUBLEVEL = 15
-EXTRAVERSION =
+EXTRAVERSION = .4
 NAME=Sliding Snow Leopard
 
 # *DOCUMENTATION*
diff -urN oldtree/arch/arm/mach-integrator/lm.c newtree/arch/arm/mach-integrator/lm.c
--- oldtree/arch/arm/mach-integrator/lm.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/arm/mach-integrator/lm.c	2006-02-13 19:19:59.583575608 -0500
@@ -22,20 +22,6 @@
 	return 1;
 }
 
-static struct bus_type lm_bustype = {
-	.name		= "logicmodule",
-	.match		= lm_match,
-//	.suspend	= lm_suspend,
-//	.resume		= lm_resume,
-};
-
-static int __init lm_init(void)
-{
-	return bus_register(&lm_bustype);
-}
-
-postcore_initcall(lm_init);
-
 static int lm_bus_probe(struct device *dev)
 {
 	struct lm_device *lmdev = to_lm_device(dev);
@@ -49,16 +35,30 @@
 	struct lm_device *lmdev = to_lm_device(dev);
 	struct lm_driver *lmdrv = to_lm_driver(dev->driver);
 
-	lmdrv->remove(lmdev);
+	if (lmdrv->remove)
+		lmdrv->remove(lmdev);
 	return 0;
 }
 
+static struct bus_type lm_bustype = {
+	.name		= "logicmodule",
+	.match		= lm_match,
+	.probe		= lm_bus_probe,
+	.remove		= lm_bus_remove,
+//	.suspend	= lm_bus_suspend,
+//	.resume		= lm_bus_resume,
+};
+
+static int __init lm_init(void)
+{
+	return bus_register(&lm_bustype);
+}
+
+postcore_initcall(lm_init);
+
 int lm_driver_register(struct lm_driver *drv)
 {
 	drv->drv.bus = &lm_bustype;
-	drv->drv.probe = lm_bus_probe;
-	drv->drv.remove = lm_bus_remove;
-
 	return driver_register(&drv->drv);
 }
 
diff -urN oldtree/arch/i386/Kconfig newtree/arch/i386/Kconfig
--- oldtree/arch/i386/Kconfig	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/i386/Kconfig	2006-02-13 19:19:59.584575456 -0500
@@ -618,13 +618,10 @@
 	default y
 
 config REGPARM
-	bool "Use register arguments (EXPERIMENTAL)"
-	depends on EXPERIMENTAL
-	default n
+	bool "Use register arguments"
 	help
 	Compile the kernel with -mregparm=3. This uses a different ABI
 	and passes the first three arguments of a function call in registers.
-	This will probably break binary only modules.
 
 	This feature is only enabled for gcc-3.0 and later - earlier compilers
 	generate incorrect output with certain kernel constructs when
diff -urN oldtree/arch/i386/Kconfig.debug newtree/arch/i386/Kconfig.debug
--- oldtree/arch/i386/Kconfig.debug	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/i386/Kconfig.debug	2006-02-13 19:19:59.584575456 -0500
@@ -43,14 +43,15 @@
 	  of memory corruptions.
 
 config 4KSTACKS
-	bool "Use 4Kb for kernel stacks instead of 8Kb"
-	depends on DEBUG_KERNEL
+	bool "Use 4Kb + 4Kb for kernel stacks instead of 8Kb" if DEBUG_KERNEL
+	default y
 	help
 	  If you say Y here the kernel will use a 4Kb stacksize for the
 	  kernel stack attached to each process/thread. This facilitates
 	  running more threads on a system and also reduces the pressure
 	  on the VM subsystem for higher order allocations. This option
-	  will also use IRQ stacks to compensate for the reduced stackspace.
+	  will also use separate 4Kb IRQ stacks to compensate for the
+	  reduced stackspace.
 
 config X86_FIND_SMP_CONFIG
 	bool
diff -urN oldtree/arch/i386/Kconfig.debug.orig newtree/arch/i386/Kconfig.debug.orig
--- oldtree/arch/i386/Kconfig.debug.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/arch/i386/Kconfig.debug.orig	2006-02-13 19:19:59.585575304 -0500
@@ -0,0 +1,65 @@
+menu "Kernel hacking"
+
+source "lib/Kconfig.debug"
+
+config EARLY_PRINTK
+	bool "Early printk" if EMBEDDED && DEBUG_KERNEL
+	default y
+	help
+	  Write kernel log output directly into the VGA buffer or to a serial
+	  port.
+
+	  This is useful for kernel debugging when your machine crashes very
+	  early before the console code is initialized. For normal operation
+	  it is not recommended because it looks ugly and doesn't cooperate
+	  with klogd/syslogd or the X server. You should normally N here,
+	  unless you want to debug such a crash.
+
+config DEBUG_STACKOVERFLOW
+	bool "Check for stack overflows"
+	depends on DEBUG_KERNEL
+	help
+	  This option will cause messages to be printed if free stack space
+	  drops below a certain limit.
+
+config DEBUG_STACK_USAGE
+	bool "Stack utilization instrumentation"
+	depends on DEBUG_KERNEL
+	help
+	  Enables the display of the minimum amount of free stack which each
+	  task has ever had available in the sysrq-T and sysrq-P debug output.
+
+	  This option will slow down process creation somewhat.
+
+comment "Page alloc debug is incompatible with Software Suspend on i386"
+	depends on DEBUG_KERNEL && SOFTWARE_SUSPEND
+
+config DEBUG_PAGEALLOC
+	bool "Page alloc debugging"
+	depends on DEBUG_KERNEL && !SOFTWARE_SUSPEND
+	help
+	  Unmap pages from the kernel linear mapping after free_pages().
+	  This results in a large slowdown, but helps to find certain types
+	  of memory corruptions.
+
+config 4KSTACKS
+	bool "Use 4Kb for kernel stacks instead of 8Kb"
+	depends on DEBUG_KERNEL
+	help
+	  If you say Y here the kernel will use a 4Kb stacksize for the
+	  kernel stack attached to each process/thread. This facilitates
+	  running more threads on a system and also reduces the pressure
+	  on the VM subsystem for higher order allocations. This option
+	  will also use IRQ stacks to compensate for the reduced stackspace.
+
+config X86_FIND_SMP_CONFIG
+	bool
+	depends on X86_LOCAL_APIC || X86_VOYAGER
+	default y
+
+config X86_MPPARSE
+	bool
+	depends on X86_LOCAL_APIC && !X86_VISWS
+	default y
+
+endmenu
diff -urN oldtree/arch/i386/Makefile newtree/arch/i386/Makefile
--- oldtree/arch/i386/Makefile	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/i386/Makefile	2006-02-13 19:19:59.585575304 -0500
@@ -42,9 +42,9 @@
 GCC_VERSION			:= $(call cc-version)
 cflags-$(CONFIG_REGPARM) 	+= $(shell if [ $(GCC_VERSION) -ge 0300 ] ; then echo "-mregparm=3"; fi ;)
 
-# Disable unit-at-a-time mode, it makes gcc use a lot more stack
-# due to the lack of sharing of stacklots.
-CFLAGS += $(call cc-option,-fno-unit-at-a-time)
+# Disable unit-at-a-time mode on pre-gcc-4.0 compilers, it makes gcc use
+# a lot more stack due to the lack of sharing of stacklots:
+CFLAGS				+= $(shell if [ $(call cc-version) -lt 0400 ] ; then echo $(call cc-option,-fno-unit-at-a-time); fi ;)
 
 CFLAGS += $(cflags-y)
 
diff -urN oldtree/arch/i386/kernel/acpi/boot.c newtree/arch/i386/kernel/acpi/boot.c
--- oldtree/arch/i386/kernel/acpi/boot.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/i386/kernel/acpi/boot.c	2006-02-13 19:20:29.688998888 -0500
@@ -248,10 +248,17 @@
 
 	acpi_table_print_madt_entry(header);
 
-	/* Register even disabled CPUs for cpu hotplug */
-
-	x86_acpiid_to_apicid[processor->acpi_id] = processor->id;
+	/* Record local apic id only when enabled */
+	if (processor->flags.enabled)
+		x86_acpiid_to_apicid[processor->acpi_id] = processor->id;
 
+	/*
+	 * We need to register disabled CPU as well to permit
+	 * counting disabled CPUs. This allows us to size
+	 * cpus_possible_map more accurately, to permit
+	 * to not preallocating memory for all NR_CPUS
+	 * when we use CPU hotplug.
+	 */
 	mp_register_lapic(processor->id,	/* APIC ID */
 			  processor->flags.enabled);	/* Enabled? */
 
diff -urN oldtree/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c newtree/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c
--- oldtree/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c	2006-02-13 19:19:59.585575304 -0500
@@ -52,6 +52,7 @@
 
 
 static int has_N44_O17_errata[NR_CPUS];
+static int has_N60_errata[NR_CPUS];
 static unsigned int stock_freq;
 static struct cpufreq_driver p4clockmod_driver;
 static unsigned int cpufreq_p4_get(unsigned int cpu);
@@ -226,6 +227,12 @@
 	case 0x0f12:
 		has_N44_O17_errata[policy->cpu] = 1;
 		dprintk("has errata -- disabling low frequencies\n");
+		break;
+
+	case 0x0f29:
+		has_N60_errata[policy->cpu] = 1;
+		dprintk("has errata -- disabling frequencies lower than 2ghz\n");
+		break;
 	}
 	
 	/* get max frequency */
@@ -237,6 +244,8 @@
 	for (i=1; (p4clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++) {
 		if ((i<2) && (has_N44_O17_errata[policy->cpu]))
 			p4clockmod_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+		else if (has_N60_errata[policy->cpu] && p4clockmod_table[i].frequency < 2000000)
+			p4clockmod_table[i].frequency = CPUFREQ_ENTRY_INVALID;
 		else
 			p4clockmod_table[i].frequency = (stock_freq * i)/8;
 	}
diff -urN oldtree/arch/i386/kernel/cpu/cpufreq/powernow-k7.c newtree/arch/i386/kernel/cpu/cpufreq/powernow-k7.c
--- oldtree/arch/i386/kernel/cpu/cpufreq/powernow-k7.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/i386/kernel/cpu/cpufreq/powernow-k7.c	2006-02-13 19:19:59.586575152 -0500
@@ -576,9 +576,6 @@
 	union msr_fidvidstatus fidvidstatus;
 	int result;
 
-	if (policy->cpu != 0)
-		return -ENODEV;
-
 	rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);
 
 	/* recalibrate cpu_khz */
@@ -664,8 +661,13 @@
 
 static int __init powernow_init (void)
 {
-	if (check_powernow()==0)
+	/* Does not support multi-cpu systems */
+	if (num_online_cpus() != 1 || num_possible_cpus() != 1)
 		return -ENODEV;
+
+	if (check_powernow() == 0)
+		return -ENODEV;
+
 	return cpufreq_register_driver(&powernow_driver);
 }
 
diff -urN oldtree/arch/i386/kernel/cpu/cpufreq/powernow-k8.c newtree/arch/i386/kernel/cpu/cpufreq/powernow-k8.c
--- oldtree/arch/i386/kernel/cpu/cpufreq/powernow-k8.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/i386/kernel/cpu/cpufreq/powernow-k8.c	2006-02-13 19:19:59.586575152 -0500
@@ -976,7 +976,7 @@
 }
 
 /* per CPU init entry point to the driver */
-static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
+static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
 {
 	struct powernow_k8_data *data;
 	cpumask_t oldmask = CPU_MASK_ALL;
@@ -1134,7 +1134,7 @@
 };
 
 /* driver entry point for init */
-static int __init powernowk8_init(void)
+static int __cpuinit powernowk8_init(void)
 {
 	unsigned int i, supported_cpus = 0;
 
diff -urN oldtree/arch/i386/kernel/cpu/cpufreq/powernow-k8.c.orig newtree/arch/i386/kernel/cpu/cpufreq/powernow-k8.c.orig
--- oldtree/arch/i386/kernel/cpu/cpufreq/powernow-k8.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/arch/i386/kernel/cpu/cpufreq/powernow-k8.c.orig	2006-02-13 19:19:59.588574848 -0500
@@ -0,0 +1,1171 @@
+/*
+ *   (c) 2003, 2004, 2005 Advanced Micro Devices, Inc.
+ *  Your use of this code is subject to the terms and conditions of the
+ *  GNU general public license version 2. See "COPYING" or
+ *  http://www.gnu.org/licenses/gpl.html
+ *
+ *  Support : mark.langsdorf@amd.com
+ *
+ *  Based on the powernow-k7.c module written by Dave Jones.
+ *  (C) 2003 Dave Jones <davej@codemonkey.org.uk> on behalf of SuSE Labs
+ *  (C) 2004 Dominik Brodowski <linux@brodo.de>
+ *  (C) 2004 Pavel Machek <pavel@suse.cz>
+ *  Licensed under the terms of the GNU GPL License version 2.
+ *  Based upon datasheets & sample CPUs kindly provided by AMD.
+ *
+ *  Valuable input gratefully received from Dave Jones, Pavel Machek,
+ *  Dominik Brodowski, and others.
+ *  Originally developed by Paul Devriendt.
+ *  Processor information obtained from Chapter 9 (Power and Thermal Management)
+ *  of the "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD
+ *  Opteron Processors" available for download from www.amd.com
+ *
+ *  Tables for specific CPUs can be infrerred from
+ *     http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/30430.pdf
+ */
+
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/cpumask.h>
+#include <linux/sched.h>	/* for current / set_cpus_allowed() */
+
+#include <asm/msr.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+
+#ifdef CONFIG_X86_POWERNOW_K8_ACPI
+#include <linux/acpi.h>
+#include <acpi/processor.h>
+#endif
+
+#define PFX "powernow-k8: "
+#define BFX PFX "BIOS error: "
+#define VERSION "version 1.50.4"
+#include "powernow-k8.h"
+
+/* serialize freq changes  */
+static DECLARE_MUTEX(fidvid_sem);
+
+static struct powernow_k8_data *powernow_data[NR_CPUS];
+
+#ifndef CONFIG_SMP
+static cpumask_t cpu_core_map[1];
+#endif
+
+/* Return a frequency in MHz, given an input fid */
+static u32 find_freq_from_fid(u32 fid)
+{
+	return 800 + (fid * 100);
+}
+
+/* Return a frequency in KHz, given an input fid */
+static u32 find_khz_freq_from_fid(u32 fid)
+{
+	return 1000 * find_freq_from_fid(fid);
+}
+
+/* Return a voltage in miliVolts, given an input vid */
+static u32 find_millivolts_from_vid(struct powernow_k8_data *data, u32 vid)
+{
+	return 1550-vid*25;
+}
+
+/* Return the vco fid for an input fid
+ *
+ * Each "low" fid has corresponding "high" fid, and you can get to "low" fids
+ * only from corresponding high fids. This returns "high" fid corresponding to
+ * "low" one.
+ */
+static u32 convert_fid_to_vco_fid(u32 fid)
+{
+	if (fid < HI_FID_TABLE_BOTTOM) {
+		return 8 + (2 * fid);
+	} else {
+		return fid;
+	}
+}
+
+/*
+ * Return 1 if the pending bit is set. Unless we just instructed the processor
+ * to transition to a new state, seeing this bit set is really bad news.
+ */
+static int pending_bit_stuck(void)
+{
+	u32 lo, hi;
+
+	rdmsr(MSR_FIDVID_STATUS, lo, hi);
+	return lo & MSR_S_LO_CHANGE_PENDING ? 1 : 0;
+}
+
+/*
+ * Update the global current fid / vid values from the status msr.
+ * Returns 1 on error.
+ */
+static int query_current_values_with_pending_wait(struct powernow_k8_data *data)
+{
+	u32 lo, hi;
+	u32 i = 0;
+
+	do {
+		if (i++ > 10000) {
+			dprintk("detected change pending stuck\n");
+			return 1;
+		}
+		rdmsr(MSR_FIDVID_STATUS, lo, hi);
+	} while (lo & MSR_S_LO_CHANGE_PENDING);
+
+	data->currvid = hi & MSR_S_HI_CURRENT_VID;
+	data->currfid = lo & MSR_S_LO_CURRENT_FID;
+
+	return 0;
+}
+
+/* the isochronous relief time */
+static void count_off_irt(struct powernow_k8_data *data)
+{
+	udelay((1 << data->irt) * 10);
+	return;
+}
+
+/* the voltage stabalization time */
+static void count_off_vst(struct powernow_k8_data *data)
+{
+	udelay(data->vstable * VST_UNITS_20US);
+	return;
+}
+
+/* need to init the control msr to a safe value (for each cpu) */
+static void fidvid_msr_init(void)
+{
+	u32 lo, hi;
+	u8 fid, vid;
+
+	rdmsr(MSR_FIDVID_STATUS, lo, hi);
+	vid = hi & MSR_S_HI_CURRENT_VID;
+	fid = lo & MSR_S_LO_CURRENT_FID;
+	lo = fid | (vid << MSR_C_LO_VID_SHIFT);
+	hi = MSR_C_HI_STP_GNT_BENIGN;
+	dprintk("cpu%d, init lo 0x%x, hi 0x%x\n", smp_processor_id(), lo, hi);
+	wrmsr(MSR_FIDVID_CTL, lo, hi);
+}
+
+
+/* write the new fid value along with the other control fields to the msr */
+static int write_new_fid(struct powernow_k8_data *data, u32 fid)
+{
+	u32 lo;
+	u32 savevid = data->currvid;
+	u32 i = 0;
+
+	if ((fid & INVALID_FID_MASK) || (data->currvid & INVALID_VID_MASK)) {
+		printk(KERN_ERR PFX "internal error - overflow on fid write\n");
+		return 1;
+	}
+
+	lo = fid | (data->currvid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
+
+	dprintk("writing fid 0x%x, lo 0x%x, hi 0x%x\n",
+		fid, lo, data->plllock * PLL_LOCK_CONVERSION);
+
+	do {
+		wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION);
+		if (i++ > 100) {
+			printk(KERN_ERR PFX "internal error - pending bit very stuck - no further pstate changes possible\n");
+			return 1;
+		}			
+	} while (query_current_values_with_pending_wait(data));
+
+	count_off_irt(data);
+
+	if (savevid != data->currvid) {
+		printk(KERN_ERR PFX "vid change on fid trans, old 0x%x, new 0x%x\n",
+		       savevid, data->currvid);
+		return 1;
+	}
+
+	if (fid != data->currfid) {
+		printk(KERN_ERR PFX "fid trans failed, fid 0x%x, curr 0x%x\n", fid,
+		        data->currfid);
+		return 1;
+	}
+
+	return 0;
+}
+
+/* Write a new vid to the hardware */
+static int write_new_vid(struct powernow_k8_data *data, u32 vid)
+{
+	u32 lo;
+	u32 savefid = data->currfid;
+	int i = 0;
+
+	if ((data->currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) {
+		printk(KERN_ERR PFX "internal error - overflow on vid write\n");
+		return 1;
+	}
+
+	lo = data->currfid | (vid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
+
+	dprintk("writing vid 0x%x, lo 0x%x, hi 0x%x\n",
+		vid, lo, STOP_GRANT_5NS);
+
+	do {
+		wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS);
+                if (i++ > 100) {
+                        printk(KERN_ERR PFX "internal error - pending bit very stuck - no further pstate changes possible\n");
+                        return 1;
+                }
+	} while (query_current_values_with_pending_wait(data));
+
+	if (savefid != data->currfid) {
+		printk(KERN_ERR PFX "fid changed on vid trans, old 0x%x new 0x%x\n",
+		       savefid, data->currfid);
+		return 1;
+	}
+
+	if (vid != data->currvid) {
+		printk(KERN_ERR PFX "vid trans failed, vid 0x%x, curr 0x%x\n", vid,
+				data->currvid);
+		return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * Reduce the vid by the max of step or reqvid.
+ * Decreasing vid codes represent increasing voltages:
+ * vid of 0 is 1.550V, vid of 0x1e is 0.800V, vid of VID_OFF is off.
+ */
+static int decrease_vid_code_by_step(struct powernow_k8_data *data, u32 reqvid, u32 step)
+{
+	if ((data->currvid - reqvid) > step)
+		reqvid = data->currvid - step;
+
+	if (write_new_vid(data, reqvid))
+		return 1;
+
+	count_off_vst(data);
+
+	return 0;
+}
+
+/* Change the fid and vid, by the 3 phases. */
+static int transition_fid_vid(struct powernow_k8_data *data, u32 reqfid, u32 reqvid)
+{
+	if (core_voltage_pre_transition(data, reqvid))
+		return 1;
+
+	if (core_frequency_transition(data, reqfid))
+		return 1;
+
+	if (core_voltage_post_transition(data, reqvid))
+		return 1;
+
+	if (query_current_values_with_pending_wait(data))
+		return 1;
+
+	if ((reqfid != data->currfid) || (reqvid != data->currvid)) {
+		printk(KERN_ERR PFX "failed (cpu%d): req 0x%x 0x%x, curr 0x%x 0x%x\n",
+				smp_processor_id(),
+				reqfid, reqvid, data->currfid, data->currvid);
+		return 1;
+	}
+
+	dprintk("transitioned (cpu%d): new fid 0x%x, vid 0x%x\n",
+		smp_processor_id(), data->currfid, data->currvid);
+
+	return 0;
+}
+
+/* Phase 1 - core voltage transition ... setup voltage */
+static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid)
+{
+	u32 rvosteps = data->rvo;
+	u32 savefid = data->currfid;
+	u32 maxvid, lo;
+
+	dprintk("ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo 0x%x\n",
+		smp_processor_id(),
+		data->currfid, data->currvid, reqvid, data->rvo);
+
+	rdmsr(MSR_FIDVID_STATUS, lo, maxvid);
+	maxvid = 0x1f & (maxvid >> 16);
+	dprintk("ph1 maxvid=0x%x\n", maxvid);
+	if (reqvid < maxvid) /* lower numbers are higher voltages */
+		reqvid = maxvid;
+
+	while (data->currvid > reqvid) {
+		dprintk("ph1: curr 0x%x, req vid 0x%x\n",
+			data->currvid, reqvid);
+		if (decrease_vid_code_by_step(data, reqvid, data->vidmvs))
+			return 1;
+	}
+
+	while ((rvosteps > 0) && ((data->rvo + data->currvid) > reqvid)) {
+		if (data->currvid == maxvid) {
+			rvosteps = 0;
+		} else {
+			dprintk("ph1: changing vid for rvo, req 0x%x\n",
+				data->currvid - 1);
+			if (decrease_vid_code_by_step(data, data->currvid - 1, 1))
+				return 1;
+			rvosteps--;
+		}
+	}
+
+	if (query_current_values_with_pending_wait(data))
+		return 1;
+
+	if (savefid != data->currfid) {
+		printk(KERN_ERR PFX "ph1 err, currfid changed 0x%x\n", data->currfid);
+		return 1;
+	}
+
+	dprintk("ph1 complete, currfid 0x%x, currvid 0x%x\n",
+		data->currfid, data->currvid);
+
+	return 0;
+}
+
+/* Phase 2 - core frequency transition */
+static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
+{
+	u32 vcoreqfid, vcocurrfid, vcofiddiff, savevid = data->currvid;
+
+	if ((reqfid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
+		printk(KERN_ERR PFX "ph2: illegal lo-lo transition 0x%x 0x%x\n",
+			reqfid, data->currfid);
+		return 1;
+	}
+
+	if (data->currfid == reqfid) {
+		printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n", data->currfid);
+		return 0;
+	}
+
+	dprintk("ph2 (cpu%d): starting, currfid 0x%x, currvid 0x%x, reqfid 0x%x\n",
+		smp_processor_id(),
+		data->currfid, data->currvid, reqfid);
+
+	vcoreqfid = convert_fid_to_vco_fid(reqfid);
+	vcocurrfid = convert_fid_to_vco_fid(data->currfid);
+	vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
+	    : vcoreqfid - vcocurrfid;
+
+	while (vcofiddiff > 2) {
+		if (reqfid > data->currfid) {
+			if (data->currfid > LO_FID_TABLE_TOP) {
+				if (write_new_fid(data, data->currfid + 2)) {
+					return 1;
+				}
+			} else {
+				if (write_new_fid
+				    (data, 2 + convert_fid_to_vco_fid(data->currfid))) {
+					return 1;
+				}
+			}
+		} else {
+			if (write_new_fid(data, data->currfid - 2))
+				return 1;
+		}
+
+		vcocurrfid = convert_fid_to_vco_fid(data->currfid);
+		vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
+		    : vcoreqfid - vcocurrfid;
+	}
+
+	if (write_new_fid(data, reqfid))
+		return 1;
+
+	if (query_current_values_with_pending_wait(data))
+		return 1;
+
+	if (data->currfid != reqfid) {
+		printk(KERN_ERR PFX
+			"ph2: mismatch, failed fid transition, curr 0x%x, req 0x%x\n",
+			data->currfid, reqfid);
+		return 1;
+	}
+
+	if (savevid != data->currvid) {
+		printk(KERN_ERR PFX "ph2: vid changed, save 0x%x, curr 0x%x\n",
+			savevid, data->currvid);
+		return 1;
+	}
+
+	dprintk("ph2 complete, currfid 0x%x, currvid 0x%x\n",
+		data->currfid, data->currvid);
+
+	return 0;
+}
+
+/* Phase 3 - core voltage transition flow ... jump to the final vid. */
+static int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid)
+{
+	u32 savefid = data->currfid;
+	u32 savereqvid = reqvid;
+
+	dprintk("ph3 (cpu%d): starting, currfid 0x%x, currvid 0x%x\n",
+		smp_processor_id(),
+		data->currfid, data->currvid);
+
+	if (reqvid != data->currvid) {
+		if (write_new_vid(data, reqvid))
+			return 1;
+
+		if (savefid != data->currfid) {
+			printk(KERN_ERR PFX
+			       "ph3: bad fid change, save 0x%x, curr 0x%x\n",
+			       savefid, data->currfid);
+			return 1;
+		}
+
+		if (data->currvid != reqvid) {
+			printk(KERN_ERR PFX
+			       "ph3: failed vid transition\n, req 0x%x, curr 0x%x",
+			       reqvid, data->currvid);
+			return 1;
+		}
+	}
+
+	if (query_current_values_with_pending_wait(data))
+		return 1;
+
+	if (savereqvid != data->currvid) {
+		dprintk("ph3 failed, currvid 0x%x\n", data->currvid);
+		return 1;
+	}
+
+	if (savefid != data->currfid) {
+		dprintk("ph3 failed, currfid changed 0x%x\n",
+			data->currfid);
+		return 1;
+	}
+
+	dprintk("ph3 complete, currfid 0x%x, currvid 0x%x\n",
+		data->currfid, data->currvid);
+
+	return 0;
+}
+
+static int check_supported_cpu(unsigned int cpu)
+{
+	cpumask_t oldmask = CPU_MASK_ALL;
+	u32 eax, ebx, ecx, edx;
+	unsigned int rc = 0;
+
+	oldmask = current->cpus_allowed;
+	set_cpus_allowed(current, cpumask_of_cpu(cpu));
+
+	if (smp_processor_id() != cpu) {
+		printk(KERN_ERR "limiting to cpu %u failed\n", cpu);
+		goto out;
+	}
+
+	if (current_cpu_data.x86_vendor != X86_VENDOR_AMD)
+		goto out;
+
+	eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
+	if (((eax & CPUID_USE_XFAM_XMOD) != CPUID_USE_XFAM_XMOD) ||
+	    ((eax & CPUID_XFAM) != CPUID_XFAM_K8) ||
+	    ((eax & CPUID_XMOD) > CPUID_XMOD_REV_F)) {
+		printk(KERN_INFO PFX "Processor cpuid %x not supported\n", eax);
+		goto out;
+	}
+
+	eax = cpuid_eax(CPUID_GET_MAX_CAPABILITIES);
+	if (eax < CPUID_FREQ_VOLT_CAPABILITIES) {
+		printk(KERN_INFO PFX
+		       "No frequency change capabilities detected\n");
+		goto out;
+	}
+
+	cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
+	if ((edx & P_STATE_TRANSITION_CAPABLE) != P_STATE_TRANSITION_CAPABLE) {
+		printk(KERN_INFO PFX "Power state transitions not supported\n");
+		goto out;
+	}
+
+	rc = 1;
+
+out:
+	set_cpus_allowed(current, oldmask);
+	return rc;
+}
+
+static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid)
+{
+	unsigned int j;
+	u8 lastfid = 0xff;
+
+	for (j = 0; j < data->numps; j++) {
+		if (pst[j].vid > LEAST_VID) {
+			printk(KERN_ERR PFX "vid %d invalid : 0x%x\n", j, pst[j].vid);
+			return -EINVAL;
+		}
+		if (pst[j].vid < data->rvo) {	/* vid + rvo >= 0 */
+			printk(KERN_ERR BFX "0 vid exceeded with pstate %d\n", j);
+			return -ENODEV;
+		}
+		if (pst[j].vid < maxvid + data->rvo) {	/* vid + rvo >= maxvid */
+			printk(KERN_ERR BFX "maxvid exceeded with pstate %d\n", j);
+			return -ENODEV;
+		}
+		if ((pst[j].fid > MAX_FID)
+		    || (pst[j].fid & 1)
+		    || (j && (pst[j].fid < HI_FID_TABLE_BOTTOM))) {
+			/* Only first fid is allowed to be in "low" range */
+			printk(KERN_ERR PFX "two low fids - %d : 0x%x\n", j, pst[j].fid);
+			return -EINVAL;
+		}
+		if (pst[j].fid < lastfid)
+			lastfid = pst[j].fid;
+	}
+	if (lastfid & 1) {
+		printk(KERN_ERR PFX "lastfid invalid\n");
+		return -EINVAL;
+	}
+	if (lastfid > LO_FID_TABLE_TOP)
+		printk(KERN_INFO PFX  "first fid not from lo freq table\n");
+
+	return 0;
+}
+
+static void print_basics(struct powernow_k8_data *data)
+{
+	int j;
+	for (j = 0; j < data->numps; j++) {
+		if (data->powernow_table[j].frequency != CPUFREQ_ENTRY_INVALID)
+			printk(KERN_INFO PFX "   %d : fid 0x%x (%d MHz), vid 0x%x (%d mV)\n", j,
+				data->powernow_table[j].index & 0xff,
+				data->powernow_table[j].frequency/1000,
+				data->powernow_table[j].index >> 8,
+				find_millivolts_from_vid(data, data->powernow_table[j].index >> 8));
+	}
+	if (data->batps)
+		printk(KERN_INFO PFX "Only %d pstates on battery\n", data->batps);
+}
+
+static int fill_powernow_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid)
+{
+	struct cpufreq_frequency_table *powernow_table;
+	unsigned int j;
+
+	if (data->batps) {    /* use ACPI support to get full speed on mains power */
+		printk(KERN_WARNING PFX "Only %d pstates usable (use ACPI driver for full range\n", data->batps);
+		data->numps = data->batps;
+	}
+
+	for ( j=1; j<data->numps; j++ ) {
+		if (pst[j-1].fid >= pst[j].fid) {
+			printk(KERN_ERR PFX "PST out of sequence\n");
+			return -EINVAL;
+		}
+	}
+
+	if (data->numps < 2) {
+		printk(KERN_ERR PFX "no p states to transition\n");
+		return -ENODEV;
+	}
+
+	if (check_pst_table(data, pst, maxvid))
+		return -EINVAL;
+
+	powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table)
+		* (data->numps + 1)), GFP_KERNEL);
+	if (!powernow_table) {
+		printk(KERN_ERR PFX "powernow_table memory alloc failure\n");
+		return -ENOMEM;
+	}
+
+	for (j = 0; j < data->numps; j++) {
+		powernow_table[j].index = pst[j].fid; /* lower 8 bits */
+		powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */
+		powernow_table[j].frequency = find_khz_freq_from_fid(pst[j].fid);
+	}
+	powernow_table[data->numps].frequency = CPUFREQ_TABLE_END;
+	powernow_table[data->numps].index = 0;
+
+	if (query_current_values_with_pending_wait(data)) {
+		kfree(powernow_table);
+		return -EIO;
+	}
+
+	dprintk("cfid 0x%x, cvid 0x%x\n", data->currfid, data->currvid);
+	data->powernow_table = powernow_table;
+	print_basics(data);
+
+	for (j = 0; j < data->numps; j++)
+		if ((pst[j].fid==data->currfid) && (pst[j].vid==data->currvid))
+			return 0;
+
+	dprintk("currfid/vid do not match PST, ignoring\n");
+	return 0;
+}
+
+/* Find and validate the PSB/PST table in BIOS. */
+static int find_psb_table(struct powernow_k8_data *data)
+{
+	struct psb_s *psb;
+	unsigned int i;
+	u32 mvs;
+	u8 maxvid;
+	u32 cpst = 0;
+	u32 thiscpuid;
+
+	for (i = 0xc0000; i < 0xffff0; i += 0x10) {
+		/* Scan BIOS looking for the signature. */
+		/* It can not be at ffff0 - it is too big. */
+
+		psb = phys_to_virt(i);
+		if (memcmp(psb, PSB_ID_STRING, PSB_ID_STRING_LEN) != 0)
+			continue;
+
+		dprintk("found PSB header at 0x%p\n", psb);
+
+		dprintk("table vers: 0x%x\n", psb->tableversion);
+		if (psb->tableversion != PSB_VERSION_1_4) {
+			printk(KERN_INFO BFX "PSB table is not v1.4\n");
+			return -ENODEV;
+		}
+
+		dprintk("flags: 0x%x\n", psb->flags1);
+		if (psb->flags1) {
+			printk(KERN_ERR BFX "unknown flags\n");
+			return -ENODEV;
+		}
+
+		data->vstable = psb->vstable;
+		dprintk("voltage stabilization time: %d(*20us)\n", data->vstable);
+
+		dprintk("flags2: 0x%x\n", psb->flags2);
+		data->rvo = psb->flags2 & 3;
+		data->irt = ((psb->flags2) >> 2) & 3;
+		mvs = ((psb->flags2) >> 4) & 3;
+		data->vidmvs = 1 << mvs;
+		data->batps = ((psb->flags2) >> 6) & 3;
+
+		dprintk("ramp voltage offset: %d\n", data->rvo);
+		dprintk("isochronous relief time: %d\n", data->irt);
+		dprintk("maximum voltage step: %d - 0x%x\n", mvs, data->vidmvs);
+
+		dprintk("numpst: 0x%x\n", psb->num_tables);
+		cpst = psb->num_tables;
+		if ((psb->cpuid == 0x00000fc0) || (psb->cpuid == 0x00000fe0) ){
+			thiscpuid = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
+			if ((thiscpuid == 0x00000fc0) || (thiscpuid == 0x00000fe0) ) {
+				cpst = 1;
+			}
+		}
+		if (cpst != 1) {
+			printk(KERN_ERR BFX "numpst must be 1\n");
+			return -ENODEV;
+		}
+
+		data->plllock = psb->plllocktime;
+		dprintk("plllocktime: 0x%x (units 1us)\n", psb->plllocktime);
+		dprintk("maxfid: 0x%x\n", psb->maxfid);
+		dprintk("maxvid: 0x%x\n", psb->maxvid);
+		maxvid = psb->maxvid;
+
+		data->numps = psb->numps;
+		dprintk("numpstates: 0x%x\n", data->numps);
+		return fill_powernow_table(data, (struct pst_s *)(psb+1), maxvid);
+	}
+	/*
+	 * If you see this message, complain to BIOS manufacturer. If
+	 * he tells you "we do not support Linux" or some similar
+	 * nonsense, remember that Windows 2000 uses the same legacy
+	 * mechanism that the old Linux PSB driver uses. Tell them it
+	 * is broken with Windows 2000.
+	 *
+	 * The reference to the AMD documentation is chapter 9 in the
+	 * BIOS and Kernel Developer's Guide, which is available on
+	 * www.amd.com
+	 */
+	printk(KERN_INFO PFX "BIOS error - no PSB or ACPI _PSS objects\n");
+	return -ENODEV;
+}
+
+#ifdef CONFIG_X86_POWERNOW_K8_ACPI
+static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index)
+{
+	if (!data->acpi_data.state_count)
+		return;
+
+	data->irt = (data->acpi_data.states[index].control >> IRT_SHIFT) & IRT_MASK;
+	data->rvo = (data->acpi_data.states[index].control >> RVO_SHIFT) & RVO_MASK;
+	data->exttype = (data->acpi_data.states[index].control >> EXT_TYPE_SHIFT) & EXT_TYPE_MASK;
+	data->plllock = (data->acpi_data.states[index].control >> PLL_L_SHIFT) & PLL_L_MASK;
+	data->vidmvs = 1 << ((data->acpi_data.states[index].control >> MVS_SHIFT) & MVS_MASK);
+	data->vstable = (data->acpi_data.states[index].control >> VST_SHIFT) & VST_MASK;
+}
+
+static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
+{
+	int i;
+	int cntlofreq = 0;
+	struct cpufreq_frequency_table *powernow_table;
+
+	if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) {
+		dprintk("register performance failed: bad ACPI data\n");
+		return -EIO;
+	}
+
+	/* verify the data contained in the ACPI structures */
+	if (data->acpi_data.state_count <= 1) {
+		dprintk("No ACPI P-States\n");
+		goto err_out;
+	}
+
+	if ((data->acpi_data.control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) ||
+		(data->acpi_data.status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) {
+		dprintk("Invalid control/status registers (%x - %x)\n",
+			data->acpi_data.control_register.space_id,
+			data->acpi_data.status_register.space_id);
+		goto err_out;
+	}
+
+	/* fill in data->powernow_table */
+	powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table)
+		* (data->acpi_data.state_count + 1)), GFP_KERNEL);
+	if (!powernow_table) {
+		dprintk("powernow_table memory alloc failure\n");
+		goto err_out;
+	}
+
+	for (i = 0; i < data->acpi_data.state_count; i++) {
+		u32 fid;
+		u32 vid;
+
+		if (data->exttype) {
+			fid = data->acpi_data.states[i].status & FID_MASK;
+			vid = (data->acpi_data.states[i].status >> VID_SHIFT) & VID_MASK;
+		} else {
+			fid = data->acpi_data.states[i].control & FID_MASK;
+			vid = (data->acpi_data.states[i].control >> VID_SHIFT) & VID_MASK;
+		}
+
+		dprintk("   %d : fid 0x%x, vid 0x%x\n", i, fid, vid);
+
+		powernow_table[i].index = fid; /* lower 8 bits */
+		powernow_table[i].index |= (vid << 8); /* upper 8 bits */
+		powernow_table[i].frequency = find_khz_freq_from_fid(fid);
+
+		/* verify frequency is OK */
+		if ((powernow_table[i].frequency > (MAX_FREQ * 1000)) ||
+			(powernow_table[i].frequency < (MIN_FREQ * 1000))) {
+			dprintk("invalid freq %u kHz, ignoring\n", powernow_table[i].frequency);
+			powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+			continue;
+		}
+
+		/* verify voltage is OK - BIOSs are using "off" to indicate invalid */
+		if (vid == VID_OFF) {
+			dprintk("invalid vid %u, ignoring\n", vid);
+			powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+			continue;
+		}
+
+		/* verify only 1 entry from the lo frequency table */
+		if (fid < HI_FID_TABLE_BOTTOM) {
+			if (cntlofreq) {
+				/* if both entries are the same, ignore this
+				 * one... 
+				 */
+				if ((powernow_table[i].frequency != powernow_table[cntlofreq].frequency) ||
+				    (powernow_table[i].index != powernow_table[cntlofreq].index)) {
+					printk(KERN_ERR PFX "Too many lo freq table entries\n");
+					goto err_out_mem;
+				}
+
+				dprintk("double low frequency table entry, ignoring it.\n");
+				powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+				continue;
+			} else
+				cntlofreq = i;
+		}
+
+		if (powernow_table[i].frequency != (data->acpi_data.states[i].core_frequency * 1000)) {
+			printk(KERN_INFO PFX "invalid freq entries %u kHz vs. %u kHz\n",
+				powernow_table[i].frequency,
+				(unsigned int) (data->acpi_data.states[i].core_frequency * 1000));
+			powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+			continue;
+		}
+	}
+
+	powernow_table[data->acpi_data.state_count].frequency = CPUFREQ_TABLE_END;
+	powernow_table[data->acpi_data.state_count].index = 0;
+	data->powernow_table = powernow_table;
+
+	/* fill in data */
+	data->numps = data->acpi_data.state_count;
+	print_basics(data);
+	powernow_k8_acpi_pst_values(data, 0);
+
+	/* notify BIOS that we exist */
+	acpi_processor_notify_smm(THIS_MODULE);
+
+	return 0;
+
+err_out_mem:
+	kfree(powernow_table);
+
+err_out:
+	acpi_processor_unregister_performance(&data->acpi_data, data->cpu);
+
+	/* data->acpi_data.state_count informs us at ->exit() whether ACPI was used */
+	data->acpi_data.state_count = 0;
+
+	return -ENODEV;
+}
+
+static void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data)
+{
+	if (data->acpi_data.state_count)
+		acpi_processor_unregister_performance(&data->acpi_data, data->cpu);
+}
+
+#else
+static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) { return -ENODEV; }
+static void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data) { return; }
+static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index) { return; }
+#endif /* CONFIG_X86_POWERNOW_K8_ACPI */
+
+/* Take a frequency, and issue the fid/vid transition command */
+static int transition_frequency(struct powernow_k8_data *data, unsigned int index)
+{
+	u32 fid;
+	u32 vid;
+	int res, i;
+	struct cpufreq_freqs freqs;
+
+	dprintk("cpu %d transition to index %u\n", smp_processor_id(), index);
+
+	/* fid are the lower 8 bits of the index we stored into
+	 * the cpufreq frequency table in find_psb_table, vid are 
+	 * the upper 8 bits.
+	 */
+
+	fid = data->powernow_table[index].index & 0xFF;
+	vid = (data->powernow_table[index].index & 0xFF00) >> 8;
+
+	dprintk("table matched fid 0x%x, giving vid 0x%x\n", fid, vid);
+
+	if (query_current_values_with_pending_wait(data))
+		return 1;
+
+	if ((data->currvid == vid) && (data->currfid == fid)) {
+		dprintk("target matches current values (fid 0x%x, vid 0x%x)\n",
+			fid, vid);
+		return 0;
+	}
+
+	if ((fid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
+		printk(KERN_ERR PFX
+		       "ignoring illegal change in lo freq table-%x to 0x%x\n",
+		       data->currfid, fid);
+		return 1;
+	}
+
+	dprintk("cpu %d, changing to fid 0x%x, vid 0x%x\n",
+		smp_processor_id(), fid, vid);
+
+	freqs.cpu = data->cpu;
+	freqs.old = find_khz_freq_from_fid(data->currfid);
+	freqs.new = find_khz_freq_from_fid(fid);
+	for_each_cpu_mask(i, cpu_core_map[data->cpu]) {
+		freqs.cpu = i;
+		cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+	}
+
+	res = transition_fid_vid(data, fid, vid);
+
+	freqs.new = find_khz_freq_from_fid(data->currfid);
+	for_each_cpu_mask(i, cpu_core_map[data->cpu]) {
+		freqs.cpu = i;
+		cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+        }
+	return res;
+}
+
+/* Driver entry point to switch to the target frequency */
+static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsigned relation)
+{
+	cpumask_t oldmask = CPU_MASK_ALL;
+	struct powernow_k8_data *data = powernow_data[pol->cpu];
+	u32 checkfid = data->currfid;
+	u32 checkvid = data->currvid;
+	unsigned int newstate;
+	int ret = -EIO;
+	int i;
+
+	/* only run on specific CPU from here on */
+	oldmask = current->cpus_allowed;
+	set_cpus_allowed(current, cpumask_of_cpu(pol->cpu));
+
+	if (smp_processor_id() != pol->cpu) {
+		printk(KERN_ERR "limiting to cpu %u failed\n", pol->cpu);
+		goto err_out;
+	}
+
+	if (pending_bit_stuck()) {
+		printk(KERN_ERR PFX "failing targ, change pending bit set\n");
+		goto err_out;
+	}
+
+	dprintk("targ: cpu %d, %d kHz, min %d, max %d, relation %d\n",
+		pol->cpu, targfreq, pol->min, pol->max, relation);
+
+	if (query_current_values_with_pending_wait(data)) {
+		ret = -EIO;
+		goto err_out;
+	}
+
+	dprintk("targ: curr fid 0x%x, vid 0x%x\n",
+		data->currfid, data->currvid);
+
+	if ((checkvid != data->currvid) || (checkfid != data->currfid)) {
+		printk(KERN_INFO PFX
+			"error - out of sync, fix 0x%x 0x%x, vid 0x%x 0x%x\n",
+			checkfid, data->currfid, checkvid, data->currvid);
+	}
+
+	if (cpufreq_frequency_table_target(pol, data->powernow_table, targfreq, relation, &newstate))
+		goto err_out;
+
+	down(&fidvid_sem);
+
+	powernow_k8_acpi_pst_values(data, newstate);
+
+	if (transition_frequency(data, newstate)) {
+		printk(KERN_ERR PFX "transition frequency failed\n");
+		ret = 1;
+		up(&fidvid_sem);
+		goto err_out;
+	}
+
+	/* Update all the fid/vids of our siblings */
+	for_each_cpu_mask(i, cpu_core_map[pol->cpu]) {
+		powernow_data[i]->currvid = data->currvid;
+		powernow_data[i]->currfid = data->currfid;
+	}	
+	up(&fidvid_sem);
+
+	pol->cur = find_khz_freq_from_fid(data->currfid);
+	ret = 0;
+
+err_out:
+	set_cpus_allowed(current, oldmask);
+	return ret;
+}
+
+/* Driver entry point to verify the policy and range of frequencies */
+static int powernowk8_verify(struct cpufreq_policy *pol)
+{
+	struct powernow_k8_data *data = powernow_data[pol->cpu];
+
+	return cpufreq_frequency_table_verify(pol, data->powernow_table);
+}
+
+/* per CPU init entry point to the driver */
+static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
+{
+	struct powernow_k8_data *data;
+	cpumask_t oldmask = CPU_MASK_ALL;
+	int rc, i;
+
+	if (!check_supported_cpu(pol->cpu))
+		return -ENODEV;
+
+	data = kzalloc(sizeof(struct powernow_k8_data), GFP_KERNEL);
+	if (!data) {
+		printk(KERN_ERR PFX "unable to alloc powernow_k8_data");
+		return -ENOMEM;
+	}
+
+	data->cpu = pol->cpu;
+
+	if (powernow_k8_cpu_init_acpi(data)) {
+		/*
+		 * Use the PSB BIOS structure. This is only availabe on
+		 * an UP version, and is deprecated by AMD.
+		 */
+
+		if ((num_online_cpus() != 1) || (num_possible_cpus() != 1)) {
+			printk(KERN_ERR PFX "MP systems not supported by PSB BIOS structure\n");
+			kfree(data);
+			return -ENODEV;
+		}
+		if (pol->cpu != 0) {
+			printk(KERN_ERR PFX "init not cpu 0\n");
+			kfree(data);
+			return -ENODEV;
+		}
+		rc = find_psb_table(data);
+		if (rc) {
+			kfree(data);
+			return -ENODEV;
+		}
+	}
+
+	/* only run on specific CPU from here on */
+	oldmask = current->cpus_allowed;
+	set_cpus_allowed(current, cpumask_of_cpu(pol->cpu));
+
+	if (smp_processor_id() != pol->cpu) {
+		printk(KERN_ERR "limiting to cpu %u failed\n", pol->cpu);
+		goto err_out;
+	}
+
+	if (pending_bit_stuck()) {
+		printk(KERN_ERR PFX "failing init, change pending bit set\n");
+		goto err_out;
+	}
+
+	if (query_current_values_with_pending_wait(data))
+		goto err_out;
+
+	fidvid_msr_init();
+
+	/* run on any CPU again */
+	set_cpus_allowed(current, oldmask);
+
+	pol->governor = CPUFREQ_DEFAULT_GOVERNOR;
+	pol->cpus = cpu_core_map[pol->cpu];
+
+	/* Take a crude guess here. 
+	 * That guess was in microseconds, so multiply with 1000 */
+	pol->cpuinfo.transition_latency = (((data->rvo + 8) * data->vstable * VST_UNITS_20US)
+	    + (3 * (1 << data->irt) * 10)) * 1000;
+
+	pol->cur = find_khz_freq_from_fid(data->currfid);
+	dprintk("policy current frequency %d kHz\n", pol->cur);
+
+	/* min/max the cpu is capable of */
+	if (cpufreq_frequency_table_cpuinfo(pol, data->powernow_table)) {
+		printk(KERN_ERR PFX "invalid powernow_table\n");
+		powernow_k8_cpu_exit_acpi(data);
+		kfree(data->powernow_table);
+		kfree(data);
+		return -EINVAL;
+	}
+
+	cpufreq_frequency_table_get_attr(data->powernow_table, pol->cpu);
+
+	printk("cpu_init done, current fid 0x%x, vid 0x%x\n",
+	       data->currfid, data->currvid);
+
+	for_each_cpu_mask(i, cpu_core_map[pol->cpu]) {
+		powernow_data[i] = data;
+	}
+
+	return 0;
+
+err_out:
+	set_cpus_allowed(current, oldmask);
+	powernow_k8_cpu_exit_acpi(data);
+
+	kfree(data);
+	return -ENODEV;
+}
+
+static int __devexit powernowk8_cpu_exit (struct cpufreq_policy *pol)
+{
+	struct powernow_k8_data *data = powernow_data[pol->cpu];
+
+	if (!data)
+		return -EINVAL;
+
+	powernow_k8_cpu_exit_acpi(data);
+
+	cpufreq_frequency_table_put_attr(pol->cpu);
+
+	kfree(data->powernow_table);
+	kfree(data);
+
+	return 0;
+}
+
+static unsigned int powernowk8_get (unsigned int cpu)
+{
+	struct powernow_k8_data *data = powernow_data[cpu];
+	cpumask_t oldmask = current->cpus_allowed;
+	unsigned int khz = 0;
+
+	set_cpus_allowed(current, cpumask_of_cpu(cpu));
+	if (smp_processor_id() != cpu) {
+		printk(KERN_ERR PFX "limiting to CPU %d failed in powernowk8_get\n", cpu);
+		set_cpus_allowed(current, oldmask);
+		return 0;
+	}
+
+	if (query_current_values_with_pending_wait(data))
+		goto out;
+
+	khz = find_khz_freq_from_fid(data->currfid);
+
+out:
+	set_cpus_allowed(current, oldmask);
+	return khz;
+}
+
+static struct freq_attr* powernow_k8_attr[] = {
+	&cpufreq_freq_attr_scaling_available_freqs,
+	NULL,
+};
+
+static struct cpufreq_driver cpufreq_amd64_driver = {
+	.verify = powernowk8_verify,
+	.target = powernowk8_target,
+	.init = powernowk8_cpu_init,
+	.exit = __devexit_p(powernowk8_cpu_exit),
+	.get = powernowk8_get,
+	.name = "powernow-k8",
+	.owner = THIS_MODULE,
+	.attr = powernow_k8_attr,
+};
+
+/* driver entry point for init */
+static int __init powernowk8_init(void)
+{
+	unsigned int i, supported_cpus = 0;
+
+	for (i=0; i<NR_CPUS; i++) {
+		if (!cpu_online(i))
+			continue;
+		if (check_supported_cpu(i))
+			supported_cpus++;
+	}
+
+	if (supported_cpus == num_online_cpus()) {
+		printk(KERN_INFO PFX "Found %d AMD Athlon 64 / Opteron processors (" VERSION ")\n",
+			supported_cpus);
+		return cpufreq_register_driver(&cpufreq_amd64_driver);
+	}
+
+	return -ENODEV;
+}
+
+/* driver entry point for term */
+static void __exit powernowk8_exit(void)
+{
+	dprintk("exit\n");
+
+	cpufreq_unregister_driver(&cpufreq_amd64_driver);
+}
+
+MODULE_AUTHOR("Paul Devriendt <paul.devriendt@amd.com> and Mark Langsdorf <mark.langsdorf@amd.com.");
+MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver.");
+MODULE_LICENSE("GPL");
+
+late_initcall(powernowk8_init);
+module_exit(powernowk8_exit);
+
diff -urN oldtree/arch/i386/kernel/time_hpet.c newtree/arch/i386/kernel/time_hpet.c
--- oldtree/arch/i386/kernel/time_hpet.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/i386/kernel/time_hpet.c	2006-02-13 19:19:59.588574848 -0500
@@ -413,9 +413,45 @@
 
 int hpet_rtc_dropped_irq(void)
 {
+	unsigned int cnt, ticks_per_int, lost_ints;
+
 	if (!is_hpet_enabled())
 		return 0;
 
+	if (UIE_on | PIE_on | AIE_on) {
+		/*
+		 * The interrupt handler schedules the next interrupt at a
+		 * constant offset from the time the current interrupt was
+		 * scheduled, without regard to the actual time. When the
+		 * handler is delayed too long, it tries to schedule the next
+		 * interrupt in the past and the hardware would not interrupt
+		 * until the counter had wrapped around. We catch it here.
+		 */
+		cnt = hpet_readl(HPET_COUNTER);
+		/* was the comparator set to a time in the past? */
+		if ((int)(cnt - hpet_t1_cmp) > 0) {
+			/* determine how many interrupts were actually lost */
+			ticks_per_int = (hpet_tick * HZ) / hpet_rtc_int_freq;
+			lost_ints = (cnt - hpet_t1_cmp) / ticks_per_int + 1;
+			/*
+			 * Make sure that, even with the time needed to execute
+			 * this code, the next scheduled interrupt has been
+			 * moved back to the future.
+			 */
+			lost_ints++;
+
+			cnt = hpet_t1_cmp + lost_ints * ticks_per_int;
+			hpet_writel(cnt, HPET_T1_CMP);
+			hpet_t1_cmp = cnt;
+
+			if (PIE_on)
+				PIE_count += lost_ints;
+
+			printk(KERN_WARNING "rtc: lost some interrupts"
+			       " at %ldHz.\n", hpet_rtc_int_freq);
+		}
+	}
+
 	return 1;
 }
 
diff -urN oldtree/arch/i386/kernel/time_hpet.c.orig newtree/arch/i386/kernel/time_hpet.c.orig
--- oldtree/arch/i386/kernel/time_hpet.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/arch/i386/kernel/time_hpet.c.orig	2006-02-13 19:19:59.682560560 -0500
@@ -0,0 +1,466 @@
+/*
+ *  linux/arch/i386/kernel/time_hpet.c
+ *  This code largely copied from arch/x86_64/kernel/time.c
+ *  See that file for credits.
+ *
+ *  2003-06-30    Venkatesh Pallipadi - Additional changes for HPET support
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+
+#include <asm/timer.h>
+#include <asm/fixmap.h>
+#include <asm/apic.h>
+
+#include <linux/timex.h>
+#include <linux/config.h>
+
+#include <asm/hpet.h>
+#include <linux/hpet.h>
+
+static unsigned long hpet_period;	/* fsecs / HPET clock */
+unsigned long hpet_tick;		/* hpet clks count per tick */
+unsigned long hpet_address;		/* hpet memory map physical address */
+int hpet_use_timer;
+
+static int use_hpet; 		/* can be used for runtime check of hpet */
+static int boot_hpet_disable; 	/* boottime override for HPET timer */
+static void __iomem * hpet_virt_address;	/* hpet kernel virtual address */
+
+#define FSEC_TO_USEC (1000000000UL)
+
+int hpet_readl(unsigned long a)
+{
+	return readl(hpet_virt_address + a);
+}
+
+static void hpet_writel(unsigned long d, unsigned long a)
+{
+	writel(d, hpet_virt_address + a);
+}
+
+#ifdef CONFIG_X86_LOCAL_APIC
+/*
+ * HPET counters dont wrap around on every tick. They just change the
+ * comparator value and continue. Next tick can be caught by checking
+ * for a change in the comparator value. Used in apic.c.
+ */
+static void __devinit wait_hpet_tick(void)
+{
+	unsigned int start_cmp_val, end_cmp_val;
+
+	start_cmp_val = hpet_readl(HPET_T0_CMP);
+	do {
+		end_cmp_val = hpet_readl(HPET_T0_CMP);
+	} while (start_cmp_val == end_cmp_val);
+}
+#endif
+
+static int hpet_timer_stop_set_go(unsigned long tick)
+{
+	unsigned int cfg;
+
+	/*
+	 * Stop the timers and reset the main counter.
+	 */
+	cfg = hpet_readl(HPET_CFG);
+	cfg &= ~HPET_CFG_ENABLE;
+	hpet_writel(cfg, HPET_CFG);
+	hpet_writel(0, HPET_COUNTER);
+	hpet_writel(0, HPET_COUNTER + 4);
+
+	if (hpet_use_timer) {
+		/*
+		 * Set up timer 0, as periodic with first interrupt to happen at
+		 * hpet_tick, and period also hpet_tick.
+		 */
+		cfg = hpet_readl(HPET_T0_CFG);
+		cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
+		       HPET_TN_SETVAL | HPET_TN_32BIT;
+		hpet_writel(cfg, HPET_T0_CFG);
+
+		/*
+		 * The first write after writing TN_SETVAL to the config register sets
+		 * the counter value, the second write sets the threshold.
+		 */
+		hpet_writel(tick, HPET_T0_CMP);
+		hpet_writel(tick, HPET_T0_CMP);
+	}
+	/*
+ 	 * Go!
+ 	 */
+	cfg = hpet_readl(HPET_CFG);
+	if (hpet_use_timer)
+		cfg |= HPET_CFG_LEGACY;
+	cfg |= HPET_CFG_ENABLE;
+	hpet_writel(cfg, HPET_CFG);
+
+	return 0;
+}
+
+/*
+ * Check whether HPET was found by ACPI boot parse. If yes setup HPET
+ * counter 0 for kernel base timer.
+ */
+int __init hpet_enable(void)
+{
+	unsigned int id;
+	unsigned long tick_fsec_low, tick_fsec_high; /* tick in femto sec */
+	unsigned long hpet_tick_rem;
+
+	if (boot_hpet_disable)
+		return -1;
+
+	if (!hpet_address) {
+		return -1;
+	}
+	hpet_virt_address = ioremap_nocache(hpet_address, HPET_MMAP_SIZE);
+	/*
+	 * Read the period, compute tick and quotient.
+	 */
+	id = hpet_readl(HPET_ID);
+
+	/*
+	 * We are checking for value '1' or more in number field if
+	 * CONFIG_HPET_EMULATE_RTC is set because we will need an
+	 * additional timer for RTC emulation.
+	 * However, we can do with one timer otherwise using the
+	 * the single HPET timer for system time.
+	 */
+#ifdef CONFIG_HPET_EMULATE_RTC
+	if (!(id & HPET_ID_NUMBER))
+		return -1;
+#endif
+
+
+	hpet_period = hpet_readl(HPET_PERIOD);
+	if ((hpet_period < HPET_MIN_PERIOD) || (hpet_period > HPET_MAX_PERIOD))
+		return -1;
+
+	/*
+	 * 64 bit math
+	 * First changing tick into fsec
+	 * Then 64 bit div to find number of hpet clk per tick
+	 */
+	ASM_MUL64_REG(tick_fsec_low, tick_fsec_high,
+			KERNEL_TICK_USEC, FSEC_TO_USEC);
+	ASM_DIV64_REG(hpet_tick, hpet_tick_rem,
+			hpet_period, tick_fsec_low, tick_fsec_high);
+
+	if (hpet_tick_rem > (hpet_period >> 1))
+		hpet_tick++; /* rounding the result */
+
+	hpet_use_timer = id & HPET_ID_LEGSUP;
+
+	if (hpet_timer_stop_set_go(hpet_tick))
+		return -1;
+
+	use_hpet = 1;
+
+#ifdef	CONFIG_HPET
+	{
+		struct hpet_data	hd;
+		unsigned int 		ntimer;
+
+		memset(&hd, 0, sizeof (hd));
+
+		ntimer = hpet_readl(HPET_ID);
+		ntimer = (ntimer & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT;
+		ntimer++;
+
+		/*
+		 * Register with driver.
+		 * Timer0 and Timer1 is used by platform.
+		 */
+		hd.hd_phys_address = hpet_address;
+		hd.hd_address = hpet_virt_address;
+		hd.hd_nirqs = ntimer;
+		hd.hd_flags = HPET_DATA_PLATFORM;
+		hpet_reserve_timer(&hd, 0);
+#ifdef	CONFIG_HPET_EMULATE_RTC
+		hpet_reserve_timer(&hd, 1);
+#endif
+		hd.hd_irq[0] = HPET_LEGACY_8254;
+		hd.hd_irq[1] = HPET_LEGACY_RTC;
+		if (ntimer > 2) {
+			struct hpet __iomem	*hpet;
+			struct hpet_timer __iomem *timer;
+			int			i;
+
+			hpet = hpet_virt_address;
+
+			for (i = 2, timer = &hpet->hpet_timers[2]; i < ntimer;
+				timer++, i++)
+				hd.hd_irq[i] = (timer->hpet_config &
+					Tn_INT_ROUTE_CNF_MASK) >>
+					Tn_INT_ROUTE_CNF_SHIFT;
+
+		}
+
+		hpet_alloc(&hd);
+	}
+#endif
+
+#ifdef CONFIG_X86_LOCAL_APIC
+	if (hpet_use_timer)
+		wait_timer_tick = wait_hpet_tick;
+#endif
+	return 0;
+}
+
+int hpet_reenable(void)
+{
+	return hpet_timer_stop_set_go(hpet_tick);
+}
+
+int is_hpet_enabled(void)
+{
+	return use_hpet;
+}
+
+int is_hpet_capable(void)
+{
+	if (!boot_hpet_disable && hpet_address)
+		return 1;
+	return 0;
+}
+
+static int __init hpet_setup(char* str)
+{
+	if (str) {
+		if (!strncmp("disable", str, 7))
+			boot_hpet_disable = 1;
+	}
+	return 1;
+}
+
+__setup("hpet=", hpet_setup);
+
+#ifdef CONFIG_HPET_EMULATE_RTC
+/* HPET in LegacyReplacement Mode eats up RTC interrupt line. When, HPET
+ * is enabled, we support RTC interrupt functionality in software.
+ * RTC has 3 kinds of interrupts:
+ * 1) Update Interrupt - generate an interrupt, every sec, when RTC clock
+ *    is updated
+ * 2) Alarm Interrupt - generate an interrupt at a specific time of day
+ * 3) Periodic Interrupt - generate periodic interrupt, with frequencies
+ *    2Hz-8192Hz (2Hz-64Hz for non-root user) (all freqs in powers of 2)
+ * (1) and (2) above are implemented using polling at a frequency of
+ * 64 Hz. The exact frequency is a tradeoff between accuracy and interrupt
+ * overhead. (DEFAULT_RTC_INT_FREQ)
+ * For (3), we use interrupts at 64Hz or user specified periodic
+ * frequency, whichever is higher.
+ */
+#include <linux/mc146818rtc.h>
+#include <linux/rtc.h>
+
+extern irqreturn_t rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+#define DEFAULT_RTC_INT_FREQ 	64
+#define RTC_NUM_INTS 		1
+
+static unsigned long UIE_on;
+static unsigned long prev_update_sec;
+
+static unsigned long AIE_on;
+static struct rtc_time alarm_time;
+
+static unsigned long PIE_on;
+static unsigned long PIE_freq = DEFAULT_RTC_INT_FREQ;
+static unsigned long PIE_count;
+
+static unsigned long hpet_rtc_int_freq; /* RTC interrupt frequency */
+static unsigned int hpet_t1_cmp; /* cached comparator register */
+
+/*
+ * Timer 1 for RTC, we do not use periodic interrupt feature,
+ * even if HPET supports periodic interrupts on Timer 1.
+ * The reason being, to set up a periodic interrupt in HPET, we need to
+ * stop the main counter. And if we do that everytime someone diables/enables
+ * RTC, we will have adverse effect on main kernel timer running on Timer 0.
+ * So, for the time being, simulate the periodic interrupt in software.
+ *
+ * hpet_rtc_timer_init() is called for the first time and during subsequent
+ * interuppts reinit happens through hpet_rtc_timer_reinit().
+ */
+int hpet_rtc_timer_init(void)
+{
+	unsigned int cfg, cnt;
+	unsigned long flags;
+
+	if (!is_hpet_enabled())
+		return 0;
+	/*
+	 * Set the counter 1 and enable the interrupts.
+	 */
+	if (PIE_on && (PIE_freq > DEFAULT_RTC_INT_FREQ))
+		hpet_rtc_int_freq = PIE_freq;
+	else
+		hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;
+
+	local_irq_save(flags);
+	cnt = hpet_readl(HPET_COUNTER);
+	cnt += ((hpet_tick*HZ)/hpet_rtc_int_freq);
+	hpet_writel(cnt, HPET_T1_CMP);
+	hpet_t1_cmp = cnt;
+	local_irq_restore(flags);
+
+	cfg = hpet_readl(HPET_T1_CFG);
+	cfg &= ~HPET_TN_PERIODIC;
+	cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
+	hpet_writel(cfg, HPET_T1_CFG);
+
+	return 1;
+}
+
+static void hpet_rtc_timer_reinit(void)
+{
+	unsigned int cfg, cnt;
+
+	if (unlikely(!(PIE_on | AIE_on | UIE_on))) {
+		cfg = hpet_readl(HPET_T1_CFG);
+		cfg &= ~HPET_TN_ENABLE;
+		hpet_writel(cfg, HPET_T1_CFG);
+		return;
+	}
+
+	if (PIE_on && (PIE_freq > DEFAULT_RTC_INT_FREQ))
+		hpet_rtc_int_freq = PIE_freq;
+	else
+		hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;
+
+	/* It is more accurate to use the comparator value than current count.*/
+	cnt = hpet_t1_cmp;
+	cnt += hpet_tick*HZ/hpet_rtc_int_freq;
+	hpet_writel(cnt, HPET_T1_CMP);
+	hpet_t1_cmp = cnt;
+}
+
+/*
+ * The functions below are called from rtc driver.
+ * Return 0 if HPET is not being used.
+ * Otherwise do the necessary changes and return 1.
+ */
+int hpet_mask_rtc_irq_bit(unsigned long bit_mask)
+{
+	if (!is_hpet_enabled())
+		return 0;
+
+	if (bit_mask & RTC_UIE)
+		UIE_on = 0;
+	if (bit_mask & RTC_PIE)
+		PIE_on = 0;
+	if (bit_mask & RTC_AIE)
+		AIE_on = 0;
+
+	return 1;
+}
+
+int hpet_set_rtc_irq_bit(unsigned long bit_mask)
+{
+	int timer_init_reqd = 0;
+
+	if (!is_hpet_enabled())
+		return 0;
+
+	if (!(PIE_on | AIE_on | UIE_on))
+		timer_init_reqd = 1;
+
+	if (bit_mask & RTC_UIE) {
+		UIE_on = 1;
+	}
+	if (bit_mask & RTC_PIE) {
+		PIE_on = 1;
+		PIE_count = 0;
+	}
+	if (bit_mask & RTC_AIE) {
+		AIE_on = 1;
+	}
+
+	if (timer_init_reqd)
+		hpet_rtc_timer_init();
+
+	return 1;
+}
+
+int hpet_set_alarm_time(unsigned char hrs, unsigned char min, unsigned char sec)
+{
+	if (!is_hpet_enabled())
+		return 0;
+
+	alarm_time.tm_hour = hrs;
+	alarm_time.tm_min = min;
+	alarm_time.tm_sec = sec;
+
+	return 1;
+}
+
+int hpet_set_periodic_freq(unsigned long freq)
+{
+	if (!is_hpet_enabled())
+		return 0;
+
+	PIE_freq = freq;
+	PIE_count = 0;
+
+	return 1;
+}
+
+int hpet_rtc_dropped_irq(void)
+{
+	if (!is_hpet_enabled())
+		return 0;
+
+	return 1;
+}
+
+irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct rtc_time curr_time;
+	unsigned long rtc_int_flag = 0;
+	int call_rtc_interrupt = 0;
+
+	hpet_rtc_timer_reinit();
+
+	if (UIE_on | AIE_on) {
+		rtc_get_rtc_time(&curr_time);
+	}
+	if (UIE_on) {
+		if (curr_time.tm_sec != prev_update_sec) {
+			/* Set update int info, call real rtc int routine */
+			call_rtc_interrupt = 1;
+			rtc_int_flag = RTC_UF;
+			prev_update_sec = curr_time.tm_sec;
+		}
+	}
+	if (PIE_on) {
+		PIE_count++;
+		if (PIE_count >= hpet_rtc_int_freq/PIE_freq) {
+			/* Set periodic int info, call real rtc int routine */
+			call_rtc_interrupt = 1;
+			rtc_int_flag |= RTC_PF;
+			PIE_count = 0;
+		}
+	}
+	if (AIE_on) {
+		if ((curr_time.tm_sec == alarm_time.tm_sec) &&
+		    (curr_time.tm_min == alarm_time.tm_min) &&
+		    (curr_time.tm_hour == alarm_time.tm_hour)) {
+			/* Set alarm int info, call real rtc int routine */
+			call_rtc_interrupt = 1;
+			rtc_int_flag |= RTC_AF;
+		}
+	}
+	if (call_rtc_interrupt) {
+		rtc_int_flag |= (RTC_IRQF | (RTC_NUM_INTS << 8));
+		rtc_interrupt(rtc_int_flag, dev_id, regs);
+	}
+	return IRQ_HANDLED;
+}
+#endif
+
diff -urN oldtree/arch/i386/mm/init.c newtree/arch/i386/mm/init.c
--- oldtree/arch/i386/mm/init.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/i386/mm/init.c	2006-02-13 19:19:59.683560408 -0500
@@ -268,7 +268,7 @@
 	pkmap_page_table = pte;	
 }
 
-static void __devinit free_new_highpage(struct page *page)
+static void __meminit free_new_highpage(struct page *page)
 {
 	set_page_count(page, 1);
 	__free_page(page);
diff -urN oldtree/arch/ia64/kernel/ptrace.c newtree/arch/ia64/kernel/ptrace.c
--- oldtree/arch/ia64/kernel/ptrace.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/ia64/kernel/ptrace.c	2006-02-13 19:19:59.684560256 -0500
@@ -1663,8 +1663,14 @@
 		     long arg4, long arg5, long arg6, long arg7,
 		     struct pt_regs regs)
 {
-	if (unlikely(current->audit_context))
-		audit_syscall_exit(current, AUDITSC_RESULT(regs.r10), regs.r8);
+	if (unlikely(current->audit_context)) {
+		int success = AUDITSC_RESULT(regs.r10);
+		long result = regs.r8;
+
+		if (success != AUDITSC_SUCCESS)
+			result = -result;
+		audit_syscall_exit(current, success, result);
+	}
 
 	if (test_thread_flag(TIF_SYSCALL_TRACE)
 	    && (current->ptrace & PT_PTRACED))
diff -urN oldtree/arch/ppc/boot/simple/Makefile newtree/arch/ppc/boot/simple/Makefile
--- oldtree/arch/ppc/boot/simple/Makefile	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/ppc/boot/simple/Makefile	2006-02-13 19:20:29.690998584 -0500
@@ -190,6 +190,8 @@
 boot-$(CONFIG_REDWOOD_6)	+= embed_config.o
 boot-$(CONFIG_8xx)		+= embed_config.o
 boot-$(CONFIG_8260)		+= embed_config.o
+boot-$(CONFIG_EP405)		+= embed_config.o
+boot-$(CONFIG_XILINX_ML300)	+= embed_config.o
 boot-$(CONFIG_BSEIP)		+= iic.o
 boot-$(CONFIG_MBX)		+= iic.o pci.o qspan_pci.o
 boot-$(CONFIG_MV64X60)		+= misc-mv64x60.o
diff -urN oldtree/arch/s390/kernel/process.c newtree/arch/s390/kernel/process.c
--- oldtree/arch/s390/kernel/process.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/s390/kernel/process.c	2006-02-13 19:19:59.685560104 -0500
@@ -58,10 +58,19 @@
  */
 unsigned long thread_saved_pc(struct task_struct *tsk)
 {
-	struct stack_frame *sf;
+	struct stack_frame *sf, *low, *high;
 
-	sf = (struct stack_frame *) tsk->thread.ksp;
-	sf = (struct stack_frame *) sf->back_chain;
+	if (!tsk || !tsk->thread_info)
+		return 0;
+	low = (struct stack_frame *) tsk->thread_info;
+	high = (struct stack_frame *)
+		((unsigned long) tsk->thread_info + THREAD_SIZE) - 1;
+	sf = (struct stack_frame *) (tsk->thread.ksp & PSW_ADDR_INSN);
+	if (sf <= low || sf > high)
+		return 0;
+	sf = (struct stack_frame *) (sf->back_chain & PSW_ADDR_INSN);
+	if (sf <= low || sf > high)
+		return 0;
 	return sf->gprs[8];
 }
 
diff -urN oldtree/arch/s390/kernel/setup.c newtree/arch/s390/kernel/setup.c
--- oldtree/arch/s390/kernel/setup.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/s390/kernel/setup.c	2006-02-13 19:19:59.685560104 -0500
@@ -315,6 +315,11 @@
 	_machine_power_off();
 }
 
+/*
+ * Dummy power off function.
+ */
+void (*pm_power_off)(void) = machine_power_off;
+
 static void __init
 add_memory_hole(unsigned long start, unsigned long end)
 {
diff -urN oldtree/arch/s390/kernel/time.c newtree/arch/s390/kernel/time.c
--- oldtree/arch/s390/kernel/time.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/s390/kernel/time.c	2006-02-13 19:19:59.686559952 -0500
@@ -214,7 +214,7 @@
 #endif
 
 #ifdef CONFIG_VIRT_CPU_ACCOUNTING
-	account_user_vtime(current);
+	account_tick_vtime(current);
 #else
 	while (ticks--)
 		update_process_times(user_mode(regs));
diff -urN oldtree/arch/s390/kernel/vtime.c newtree/arch/s390/kernel/vtime.c
--- oldtree/arch/s390/kernel/vtime.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/s390/kernel/vtime.c	2006-02-13 19:19:59.686559952 -0500
@@ -32,7 +32,7 @@
  * Update process times based on virtual cpu times stored by entry.S
  * to the lowcore fields user_timer, system_timer & steal_clock.
  */
-void account_user_vtime(struct task_struct *tsk)
+void account_tick_vtime(struct task_struct *tsk)
 {
 	cputime_t cputime;
 	__u64 timer, clock;
@@ -76,6 +76,31 @@
  * Update process times based on virtual cpu times stored by entry.S
  * to the lowcore fields user_timer, system_timer & steal_clock.
  */
+void account_vtime(struct task_struct *tsk)
+{
+	cputime_t cputime;
+	__u64 timer;
+
+	timer = S390_lowcore.last_update_timer;
+	asm volatile ("  STPT %0"    /* Store current cpu timer value */
+		      : "=m" (S390_lowcore.last_update_timer) );
+	S390_lowcore.system_timer += timer - S390_lowcore.last_update_timer;
+
+	cputime = S390_lowcore.user_timer >> 12;
+	S390_lowcore.user_timer -= cputime << 12;
+	S390_lowcore.steal_clock -= cputime << 12;
+	account_user_time(tsk, cputime);
+
+	cputime =  S390_lowcore.system_timer >> 12;
+	S390_lowcore.system_timer -= cputime << 12;
+	S390_lowcore.steal_clock -= cputime << 12;
+	account_system_time(tsk, 0, cputime);
+}
+
+/*
+ * Update process times based on virtual cpu times stored by entry.S
+ * to the lowcore fields user_timer, system_timer & steal_clock.
+ */
 void account_system_vtime(struct task_struct *tsk)
 {
 	cputime_t cputime;
diff -urN oldtree/arch/sh/kernel/cpu/bus.c newtree/arch/sh/kernel/cpu/bus.c
--- oldtree/arch/sh/kernel/cpu/bus.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/sh/kernel/cpu/bus.c	2006-02-13 19:19:59.686559952 -0500
@@ -53,21 +53,6 @@
 	return 0;
 }
 
-static struct device sh_bus_devices[SH_NR_BUSES] = {
-	{
-		.bus_id		= SH_BUS_NAME_VIRT,
-	},
-};
-
-struct bus_type sh_bus_types[SH_NR_BUSES] = {
-	{
-		.name		= SH_BUS_NAME_VIRT,
-		.match		= sh_bus_match,
-		.suspend	= sh_bus_suspend,
-		.resume		= sh_bus_resume,
-	},
-};
-
 static int sh_device_probe(struct device *dev)
 {
 	struct sh_dev *shdev = to_sh_dev(dev);
@@ -90,6 +75,23 @@
 	return 0;
 }
 
+static struct device sh_bus_devices[SH_NR_BUSES] = {
+	{
+		.bus_id		= SH_BUS_NAME_VIRT,
+	},
+};
+
+struct bus_type sh_bus_types[SH_NR_BUSES] = {
+	{
+		.name		= SH_BUS_NAME_VIRT,
+		.match		= sh_bus_match,
+		.probe		= sh_bus_probe,
+		.remove		= sh_bus_remove,
+		.suspend	= sh_bus_suspend,
+		.resume		= sh_bus_resume,
+	},
+};
+
 int sh_device_register(struct sh_dev *dev)
 {
 	if (!dev)
@@ -133,8 +135,6 @@
 		return -EINVAL;
 	}
 
-	drv->drv.probe  = sh_device_probe;
-	drv->drv.remove = sh_device_remove;
 	drv->drv.bus    = &sh_bus_types[drv->bus_id];
 
 	return driver_register(&drv->drv);
diff -urN oldtree/arch/sparc64/Kconfig newtree/arch/sparc64/Kconfig
--- oldtree/arch/sparc64/Kconfig	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/sparc64/Kconfig	2006-02-13 19:20:29.691998432 -0500
@@ -179,7 +179,7 @@
 	bool "512K"
 
 config HUGETLB_PAGE_SIZE_64K
-	depends on !SPARC64_PAGE_SIZE_4MB && !SPARC64_PAGE_SIZE_512K
+	depends on !SPARC64_PAGE_SIZE_4MB && !SPARC64_PAGE_SIZE_512KB
 	bool "64K"
 
 endchoice
diff -urN oldtree/arch/sparc64/kernel/entry.S newtree/arch/sparc64/kernel/entry.S
--- oldtree/arch/sparc64/kernel/entry.S	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/sparc64/kernel/entry.S	2006-02-13 19:20:29.691998432 -0500
@@ -1657,13 +1657,10 @@
 	/* Check if force_successful_syscall_return()
 	 * was invoked.
 	 */
-	ldub		[%curptr + TI_SYS_NOERROR], %l0
-	brz,pt		%l0, 1f
-	 nop
-	ba,pt		%xcc, 80f
+	ldub            [%curptr + TI_SYS_NOERROR], %l2
+	brnz,a,pn       %l2, 80f
 	 stb		%g0, [%curptr + TI_SYS_NOERROR]
 
-1:
 	cmp		%o0, -ERESTART_RESTARTBLOCK
 	bgeu,pn		%xcc, 1f
 	 andcc		%l0, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT), %l6
diff -urN oldtree/arch/sparc64/kernel/sys32.S newtree/arch/sparc64/kernel/sys32.S
--- oldtree/arch/sparc64/kernel/sys32.S	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/sparc64/kernel/sys32.S	2006-02-13 19:20:29.693998128 -0500
@@ -84,7 +84,6 @@
 SIGN2(sys32_bdflush, sys_bdflush, %o0, %o1)
 SIGN1(sys32_mlockall, sys_mlockall, %o0)
 SIGN1(sys32_nfsservctl, compat_sys_nfsservctl, %o0)
-SIGN1(sys32_clock_settime, compat_sys_clock_settime, %o1)
 SIGN1(sys32_clock_nanosleep, compat_sys_clock_nanosleep, %o1)
 SIGN1(sys32_timer_settime, compat_sys_timer_settime, %o1)
 SIGN1(sys32_io_submit, compat_sys_io_submit, %o1)
diff -urN oldtree/arch/sparc64/kernel/systbls.S newtree/arch/sparc64/kernel/systbls.S
--- oldtree/arch/sparc64/kernel/systbls.S	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/sparc64/kernel/systbls.S	2006-02-13 19:20:29.694997976 -0500
@@ -71,7 +71,7 @@
 /*240*/	.word sys_munlockall, sys32_sched_setparam, sys32_sched_getparam, sys32_sched_setscheduler, sys32_sched_getscheduler
 	.word sys_sched_yield, sys32_sched_get_priority_max, sys32_sched_get_priority_min, sys32_sched_rr_get_interval, compat_sys_nanosleep
 /*250*/	.word sys32_mremap, sys32_sysctl, sys32_getsid, sys_fdatasync, sys32_nfsservctl
-	.word sys_ni_syscall, sys32_clock_settime, compat_sys_clock_gettime, compat_sys_clock_getres, sys32_clock_nanosleep
+	.word sys_ni_syscall, compat_sys_clock_settime, compat_sys_clock_gettime, compat_sys_clock_getres, sys32_clock_nanosleep
 /*260*/	.word compat_sys_sched_getaffinity, compat_sys_sched_setaffinity, sys32_timer_settime, compat_sys_timer_gettime, sys_timer_getoverrun
 	.word sys_timer_delete, sys32_timer_create, sys_ni_syscall, compat_sys_io_setup, sys_io_destroy
 /*270*/	.word sys32_io_submit, sys_io_cancel, compat_sys_io_getevents, sys32_mq_open, sys_mq_unlink
@@ -98,7 +98,7 @@
 	.word sys_umount, sys_setgid, sys_getgid, sys_signal, sys_geteuid
 /*50*/	.word sys_getegid, sys_acct, sys_memory_ordering, sys_nis_syscall, sys_ioctl
 	.word sys_reboot, sys_nis_syscall, sys_symlink, sys_readlink, sys_execve
-/*60*/	.word sys_umask, sys_chroot, sys_newfstat, sys_stat64, sys_getpagesize
+/*60*/	.word sys_umask, sys_chroot, sys_newfstat, sys_fstat64, sys_getpagesize
 	.word sys_msync, sys_vfork, sys_pread64, sys_pwrite64, sys_nis_syscall
 /*70*/	.word sys_nis_syscall, sys_mmap, sys_nis_syscall, sys64_munmap, sys_mprotect
 	.word sys_madvise, sys_vhangup, sys_nis_syscall, sys_mincore, sys_getgroups
diff -urN oldtree/arch/sparc64/kernel/time.c newtree/arch/sparc64/kernel/time.c
--- oldtree/arch/sparc64/kernel/time.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/sparc64/kernel/time.c	2006-02-13 19:20:29.694997976 -0500
@@ -280,9 +280,9 @@
  * Since STICK is constantly updating, we have to access it carefully.
  *
  * The sequence we use to read is:
- * 1) read low
- * 2) read high
- * 3) read low again, if it rolled over increment high by 1
+ * 1) read high
+ * 2) read low
+ * 3) read high again, if it rolled re-read both low and high again.
  *
  * Writing STICK safely is also tricky:
  * 1) write low to zero
@@ -295,18 +295,18 @@
 static unsigned long __hbird_read_stick(void)
 {
 	unsigned long ret, tmp1, tmp2, tmp3;
-	unsigned long addr = HBIRD_STICK_ADDR;
+	unsigned long addr = HBIRD_STICK_ADDR+8;
 
-	__asm__ __volatile__("ldxa	[%1] %5, %2\n\t"
-			     "add	%1, 0x8, %1\n\t"
-			     "ldxa	[%1] %5, %3\n\t"
+	__asm__ __volatile__("ldxa	[%1] %5, %2\n"
+			     "1:\n\t"
 			     "sub	%1, 0x8, %1\n\t"
+			     "ldxa	[%1] %5, %3\n\t"
+			     "add	%1, 0x8, %1\n\t"
 			     "ldxa	[%1] %5, %4\n\t"
 			     "cmp	%4, %2\n\t"
-			     "blu,a,pn	%%xcc, 1f\n\t"
-			     " add	%3, 1, %3\n"
-			     "1:\n\t"
-			     "sllx	%3, 32, %3\n\t"
+			     "bne,a,pn	%%xcc, 1b\n\t"
+			     " mov	%4, %2\n\t"
+			     "sllx	%4, 32, %4\n\t"
 			     "or	%3, %4, %0\n\t"
 			     : "=&r" (ret), "=&r" (addr),
 			       "=&r" (tmp1), "=&r" (tmp2), "=&r" (tmp3)
diff -urN oldtree/arch/x86_64/kernel/pci-gart.c newtree/arch/x86_64/kernel/pci-gart.c
--- oldtree/arch/x86_64/kernel/pci-gart.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/x86_64/kernel/pci-gart.c	2006-02-13 19:20:29.696997672 -0500
@@ -244,6 +244,7 @@
 					   get_order(size));
 
 				if (swiotlb) {
+					gfp &= ~(GFP_DMA32|GFP_DMA);
 					return
 					swiotlb_alloc_coherent(dev, size,
 							       dma_handle,
diff -urN oldtree/arch/x86_64/kernel/process.c newtree/arch/x86_64/kernel/process.c
--- oldtree/arch/x86_64/kernel/process.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/x86_64/kernel/process.c	2006-02-13 19:19:59.687559800 -0500
@@ -478,6 +478,33 @@
 }
 
 /*
+ * This function selects if the context switch from prev to next
+ * has to tweak the TSC disable bit in the cr4.
+ */
+static inline void disable_tsc(struct task_struct *prev_p,
+			       struct task_struct *next_p)
+{
+	struct thread_info *prev, *next;
+
+	/*
+	 * gcc should eliminate the ->thread_info dereference if
+	 * has_secure_computing returns 0 at compile time (SECCOMP=n).
+	 */
+	prev = prev_p->thread_info;
+	next = next_p->thread_info;
+
+	if (has_secure_computing(prev) || has_secure_computing(next)) {
+		/* slow path here */
+		if (has_secure_computing(prev) &&
+		    !has_secure_computing(next)) {
+			write_cr4(read_cr4() & ~X86_CR4_TSD);
+		} else if (!has_secure_computing(prev) &&
+			   has_secure_computing(next))
+			write_cr4((read_cr4() | X86_CR4_TSD) & ~X86_CR4_PCE);
+	}
+}
+
+/*
  * This special macro can be used to load a debugging register
  */
 #define loaddebug(thread,r) set_debug(thread->debugreg ## r, r)
@@ -597,6 +624,8 @@
 		}
 	}
 
+	disable_tsc(prev_p, next_p);
+
 	return prev_p;
 }
 
diff -urN oldtree/arch/x86_64/kernel/process.c.orig newtree/arch/x86_64/kernel/process.c.orig
--- oldtree/arch/x86_64/kernel/process.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/arch/x86_64/kernel/process.c.orig	2006-02-13 19:19:59.688559648 -0500
@@ -0,0 +1,814 @@
+/*
+ *  linux/arch/x86-64/kernel/process.c
+ *
+ *  Copyright (C) 1995  Linus Torvalds
+ *
+ *  Pentium III FXSR, SSE support
+ *	Gareth Hughes <gareth@valinux.com>, May 2000
+ * 
+ *  X86-64 port
+ *	Andi Kleen.
+ *
+ *	CPU hotplug support - ashok.raj@intel.com
+ *  $Id: process.c,v 1.38 2002/01/15 10:08:03 ak Exp $
+ */
+
+/*
+ * This file handles the architecture-dependent parts of process handling..
+ */
+
+#include <stdarg.h>
+
+#include <linux/cpu.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/elfcore.h>
+#include <linux/smp.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/module.h>
+#include <linux/a.out.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/ptrace.h>
+#include <linux/utsname.h>
+#include <linux/random.h>
+#include <linux/kprobes.h>
+
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/i387.h>
+#include <asm/mmu_context.h>
+#include <asm/pda.h>
+#include <asm/prctl.h>
+#include <asm/kdebug.h>
+#include <asm/desc.h>
+#include <asm/proto.h>
+#include <asm/ia32.h>
+
+asmlinkage extern void ret_from_fork(void);
+
+unsigned long kernel_thread_flags = CLONE_VM | CLONE_UNTRACED;
+
+static atomic_t hlt_counter = ATOMIC_INIT(0);
+
+unsigned long boot_option_idle_override = 0;
+EXPORT_SYMBOL(boot_option_idle_override);
+
+/*
+ * Powermanagement idle function, if any..
+ */
+void (*pm_idle)(void);
+static DEFINE_PER_CPU(unsigned int, cpu_idle_state);
+
+void disable_hlt(void)
+{
+	atomic_inc(&hlt_counter);
+}
+
+EXPORT_SYMBOL(disable_hlt);
+
+void enable_hlt(void)
+{
+	atomic_dec(&hlt_counter);
+}
+
+EXPORT_SYMBOL(enable_hlt);
+
+/*
+ * We use this if we don't have any better
+ * idle routine..
+ */
+void default_idle(void)
+{
+	local_irq_enable();
+
+	if (!atomic_read(&hlt_counter)) {
+		clear_thread_flag(TIF_POLLING_NRFLAG);
+		smp_mb__after_clear_bit();
+		while (!need_resched()) {
+			local_irq_disable();
+			if (!need_resched())
+				safe_halt();
+			else
+				local_irq_enable();
+		}
+		set_thread_flag(TIF_POLLING_NRFLAG);
+	} else {
+		while (!need_resched())
+			cpu_relax();
+	}
+}
+
+/*
+ * On SMP it's slightly faster (but much more power-consuming!)
+ * to poll the ->need_resched flag instead of waiting for the
+ * cross-CPU IPI to arrive. Use this option with caution.
+ */
+static void poll_idle (void)
+{
+	local_irq_enable();
+
+	asm volatile(
+		"2:"
+		"testl %0,%1;"
+		"rep; nop;"
+		"je 2b;"
+		: :
+		"i" (_TIF_NEED_RESCHED),
+		"m" (current_thread_info()->flags));
+}
+
+void cpu_idle_wait(void)
+{
+	unsigned int cpu, this_cpu = get_cpu();
+	cpumask_t map;
+
+	set_cpus_allowed(current, cpumask_of_cpu(this_cpu));
+	put_cpu();
+
+	cpus_clear(map);
+	for_each_online_cpu(cpu) {
+		per_cpu(cpu_idle_state, cpu) = 1;
+		cpu_set(cpu, map);
+	}
+
+	__get_cpu_var(cpu_idle_state) = 0;
+
+	wmb();
+	do {
+		ssleep(1);
+		for_each_online_cpu(cpu) {
+			if (cpu_isset(cpu, map) &&
+					!per_cpu(cpu_idle_state, cpu))
+				cpu_clear(cpu, map);
+		}
+		cpus_and(map, map, cpu_online_map);
+	} while (!cpus_empty(map));
+}
+EXPORT_SYMBOL_GPL(cpu_idle_wait);
+
+#ifdef CONFIG_HOTPLUG_CPU
+DECLARE_PER_CPU(int, cpu_state);
+
+#include <asm/nmi.h>
+/* We don't actually take CPU down, just spin without interrupts. */
+static inline void play_dead(void)
+{
+	idle_task_exit();
+	wbinvd();
+	mb();
+	/* Ack it */
+	__get_cpu_var(cpu_state) = CPU_DEAD;
+
+	while (1)
+		safe_halt();
+}
+#else
+static inline void play_dead(void)
+{
+	BUG();
+}
+#endif /* CONFIG_HOTPLUG_CPU */
+
+/*
+ * The idle thread. There's no useful work to be
+ * done, so just try to conserve power and have a
+ * low exit latency (ie sit in a loop waiting for
+ * somebody to say that they'd like to reschedule)
+ */
+void cpu_idle (void)
+{
+	set_thread_flag(TIF_POLLING_NRFLAG);
+
+	/* endless idle loop with no priority at all */
+	while (1) {
+		while (!need_resched()) {
+			void (*idle)(void);
+
+			if (__get_cpu_var(cpu_idle_state))
+				__get_cpu_var(cpu_idle_state) = 0;
+
+			rmb();
+			idle = pm_idle;
+			if (!idle)
+				idle = default_idle;
+			if (cpu_is_offline(smp_processor_id()))
+				play_dead();
+			idle();
+		}
+
+		preempt_enable_no_resched();
+		schedule();
+		preempt_disable();
+	}
+}
+
+/*
+ * This uses new MONITOR/MWAIT instructions on P4 processors with PNI,
+ * which can obviate IPI to trigger checking of need_resched.
+ * We execute MONITOR against need_resched and enter optimized wait state
+ * through MWAIT. Whenever someone changes need_resched, we would be woken
+ * up from MWAIT (without an IPI).
+ */
+static void mwait_idle(void)
+{
+	local_irq_enable();
+
+	while (!need_resched()) {
+		__monitor((void *)&current_thread_info()->flags, 0, 0);
+		smp_mb();
+		if (need_resched())
+			break;
+		__mwait(0, 0);
+	}
+}
+
+void __cpuinit select_idle_routine(const struct cpuinfo_x86 *c)
+{
+	static int printed;
+	if (cpu_has(c, X86_FEATURE_MWAIT)) {
+		/*
+		 * Skip, if setup has overridden idle.
+		 * One CPU supports mwait => All CPUs supports mwait
+		 */
+		if (!pm_idle) {
+			if (!printed) {
+				printk("using mwait in idle threads.\n");
+				printed = 1;
+			}
+			pm_idle = mwait_idle;
+		}
+	}
+}
+
+static int __init idle_setup (char *str)
+{
+	if (!strncmp(str, "poll", 4)) {
+		printk("using polling idle threads.\n");
+		pm_idle = poll_idle;
+	}
+
+	boot_option_idle_override = 1;
+	return 1;
+}
+
+__setup("idle=", idle_setup);
+
+/* Prints also some state that isn't saved in the pt_regs */ 
+void __show_regs(struct pt_regs * regs)
+{
+	unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L, fs, gs, shadowgs;
+	unsigned int fsindex,gsindex;
+	unsigned int ds,cs,es; 
+
+	printk("\n");
+	print_modules();
+	printk("Pid: %d, comm: %.20s %s %s %.*s\n",
+		current->pid, current->comm, print_tainted(),
+		system_utsname.release,
+		(int)strcspn(system_utsname.version, " "),
+		system_utsname.version);
+	printk("RIP: %04lx:[<%016lx>] ", regs->cs & 0xffff, regs->rip);
+	printk_address(regs->rip); 
+	printk("\nRSP: %04lx:%016lx  EFLAGS: %08lx\n", regs->ss, regs->rsp,
+		regs->eflags);
+	printk("RAX: %016lx RBX: %016lx RCX: %016lx\n",
+	       regs->rax, regs->rbx, regs->rcx);
+	printk("RDX: %016lx RSI: %016lx RDI: %016lx\n",
+	       regs->rdx, regs->rsi, regs->rdi); 
+	printk("RBP: %016lx R08: %016lx R09: %016lx\n",
+	       regs->rbp, regs->r8, regs->r9); 
+	printk("R10: %016lx R11: %016lx R12: %016lx\n",
+	       regs->r10, regs->r11, regs->r12); 
+	printk("R13: %016lx R14: %016lx R15: %016lx\n",
+	       regs->r13, regs->r14, regs->r15); 
+
+	asm("movl %%ds,%0" : "=r" (ds)); 
+	asm("movl %%cs,%0" : "=r" (cs)); 
+	asm("movl %%es,%0" : "=r" (es)); 
+	asm("movl %%fs,%0" : "=r" (fsindex));
+	asm("movl %%gs,%0" : "=r" (gsindex));
+
+	rdmsrl(MSR_FS_BASE, fs);
+	rdmsrl(MSR_GS_BASE, gs); 
+	rdmsrl(MSR_KERNEL_GS_BASE, shadowgs); 
+
+	asm("movq %%cr0, %0": "=r" (cr0));
+	asm("movq %%cr2, %0": "=r" (cr2));
+	asm("movq %%cr3, %0": "=r" (cr3));
+	asm("movq %%cr4, %0": "=r" (cr4));
+
+	printk("FS:  %016lx(%04x) GS:%016lx(%04x) knlGS:%016lx\n", 
+	       fs,fsindex,gs,gsindex,shadowgs); 
+	printk("CS:  %04x DS: %04x ES: %04x CR0: %016lx\n", cs, ds, es, cr0); 
+	printk("CR2: %016lx CR3: %016lx CR4: %016lx\n", cr2, cr3, cr4);
+}
+
+void show_regs(struct pt_regs *regs)
+{
+	printk("CPU %d:", smp_processor_id());
+	__show_regs(regs);
+	show_trace(&regs->rsp);
+}
+
+/*
+ * Free current thread data structures etc..
+ */
+void exit_thread(void)
+{
+	struct task_struct *me = current;
+	struct thread_struct *t = &me->thread;
+
+	/*
+	 * Remove function-return probe instances associated with this task
+	 * and put them back on the free list. Do not insert an exit probe for
+	 * this function, it will be disabled by kprobe_flush_task if you do.
+	 */
+	kprobe_flush_task(me);
+
+	if (me->thread.io_bitmap_ptr) { 
+		struct tss_struct *tss = &per_cpu(init_tss, get_cpu());
+
+		kfree(t->io_bitmap_ptr);
+		t->io_bitmap_ptr = NULL;
+		/*
+		 * Careful, clear this in the TSS too:
+		 */
+		memset(tss->io_bitmap, 0xff, t->io_bitmap_max);
+		t->io_bitmap_max = 0;
+		put_cpu();
+	}
+}
+
+void flush_thread(void)
+{
+	struct task_struct *tsk = current;
+	struct thread_info *t = current_thread_info();
+
+	if (t->flags & _TIF_ABI_PENDING)
+		t->flags ^= (_TIF_ABI_PENDING | _TIF_IA32);
+
+	tsk->thread.debugreg0 = 0;
+	tsk->thread.debugreg1 = 0;
+	tsk->thread.debugreg2 = 0;
+	tsk->thread.debugreg3 = 0;
+	tsk->thread.debugreg6 = 0;
+	tsk->thread.debugreg7 = 0;
+	memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array));	
+	/*
+	 * Forget coprocessor state..
+	 */
+	clear_fpu(tsk);
+	clear_used_math();
+}
+
+void release_thread(struct task_struct *dead_task)
+{
+	if (dead_task->mm) {
+		if (dead_task->mm->context.size) {
+			printk("WARNING: dead process %8s still has LDT? <%p/%d>\n",
+					dead_task->comm,
+					dead_task->mm->context.ldt,
+					dead_task->mm->context.size);
+			BUG();
+		}
+	}
+}
+
+static inline void set_32bit_tls(struct task_struct *t, int tls, u32 addr)
+{
+	struct user_desc ud = { 
+		.base_addr = addr,
+		.limit = 0xfffff,
+		.seg_32bit = 1,
+		.limit_in_pages = 1,
+		.useable = 1,
+	};
+	struct n_desc_struct *desc = (void *)t->thread.tls_array;
+	desc += tls;
+	desc->a = LDT_entry_a(&ud); 
+	desc->b = LDT_entry_b(&ud); 
+}
+
+static inline u32 read_32bit_tls(struct task_struct *t, int tls)
+{
+	struct desc_struct *desc = (void *)t->thread.tls_array;
+	desc += tls;
+	return desc->base0 | 
+		(((u32)desc->base1) << 16) | 
+		(((u32)desc->base2) << 24);
+}
+
+/*
+ * This gets called before we allocate a new thread and copy
+ * the current task into it.
+ */
+void prepare_to_copy(struct task_struct *tsk)
+{
+	unlazy_fpu(tsk);
+}
+
+int copy_thread(int nr, unsigned long clone_flags, unsigned long rsp, 
+		unsigned long unused,
+	struct task_struct * p, struct pt_regs * regs)
+{
+	int err;
+	struct pt_regs * childregs;
+	struct task_struct *me = current;
+
+	childregs = ((struct pt_regs *)
+			(THREAD_SIZE + (unsigned long) p->thread_info)) - 1;
+	*childregs = *regs;
+
+	childregs->rax = 0;
+	childregs->rsp = rsp;
+	if (rsp == ~0UL)
+		childregs->rsp = (unsigned long)childregs;
+
+	p->thread.rsp = (unsigned long) childregs;
+	p->thread.rsp0 = (unsigned long) (childregs+1);
+	p->thread.userrsp = me->thread.userrsp; 
+
+	set_ti_thread_flag(p->thread_info, TIF_FORK);
+
+	p->thread.fs = me->thread.fs;
+	p->thread.gs = me->thread.gs;
+
+	asm("mov %%gs,%0" : "=m" (p->thread.gsindex));
+	asm("mov %%fs,%0" : "=m" (p->thread.fsindex));
+	asm("mov %%es,%0" : "=m" (p->thread.es));
+	asm("mov %%ds,%0" : "=m" (p->thread.ds));
+
+	if (unlikely(me->thread.io_bitmap_ptr != NULL)) { 
+		p->thread.io_bitmap_ptr = kmalloc(IO_BITMAP_BYTES, GFP_KERNEL);
+		if (!p->thread.io_bitmap_ptr) {
+			p->thread.io_bitmap_max = 0;
+			return -ENOMEM;
+		}
+		memcpy(p->thread.io_bitmap_ptr, me->thread.io_bitmap_ptr,
+				IO_BITMAP_BYTES);
+	} 
+
+	/*
+	 * Set a new TLS for the child thread?
+	 */
+	if (clone_flags & CLONE_SETTLS) {
+#ifdef CONFIG_IA32_EMULATION
+		if (test_thread_flag(TIF_IA32))
+			err = ia32_child_tls(p, childregs); 
+		else 			
+#endif	 
+			err = do_arch_prctl(p, ARCH_SET_FS, childregs->r8); 
+		if (err) 
+			goto out;
+	}
+	err = 0;
+out:
+	if (err && p->thread.io_bitmap_ptr) {
+		kfree(p->thread.io_bitmap_ptr);
+		p->thread.io_bitmap_max = 0;
+	}
+	return err;
+}
+
+/*
+ * This special macro can be used to load a debugging register
+ */
+#define loaddebug(thread,r) set_debug(thread->debugreg ## r, r)
+
+/*
+ *	switch_to(x,y) should switch tasks from x to y.
+ *
+ * This could still be optimized: 
+ * - fold all the options into a flag word and test it with a single test.
+ * - could test fs/gs bitsliced
+ */
+struct task_struct *
+__switch_to(struct task_struct *prev_p, struct task_struct *next_p)
+{
+	struct thread_struct *prev = &prev_p->thread,
+				 *next = &next_p->thread;
+	int cpu = smp_processor_id();  
+	struct tss_struct *tss = &per_cpu(init_tss, cpu);
+
+	unlazy_fpu(prev_p);
+
+	/*
+	 * Reload esp0, LDT and the page table pointer:
+	 */
+	tss->rsp0 = next->rsp0;
+
+	/* 
+	 * Switch DS and ES.
+	 * This won't pick up thread selector changes, but I guess that is ok.
+	 */
+	asm volatile("mov %%es,%0" : "=m" (prev->es));
+	if (unlikely(next->es | prev->es))
+		loadsegment(es, next->es); 
+	
+	asm volatile ("mov %%ds,%0" : "=m" (prev->ds));
+	if (unlikely(next->ds | prev->ds))
+		loadsegment(ds, next->ds);
+
+	load_TLS(next, cpu);
+
+	/* 
+	 * Switch FS and GS.
+	 */
+	{ 
+		unsigned fsindex;
+		asm volatile("movl %%fs,%0" : "=r" (fsindex)); 
+		/* segment register != 0 always requires a reload. 
+		   also reload when it has changed. 
+		   when prev process used 64bit base always reload
+		   to avoid an information leak. */
+		if (unlikely(fsindex | next->fsindex | prev->fs)) {
+			loadsegment(fs, next->fsindex);
+			/* check if the user used a selector != 0
+	                 * if yes clear 64bit base, since overloaded base
+                         * is always mapped to the Null selector
+                         */
+			if (fsindex)
+			prev->fs = 0;				
+		}
+		/* when next process has a 64bit base use it */
+		if (next->fs) 
+			wrmsrl(MSR_FS_BASE, next->fs); 
+		prev->fsindex = fsindex;
+	}
+	{ 
+		unsigned gsindex;
+		asm volatile("movl %%gs,%0" : "=r" (gsindex)); 
+		if (unlikely(gsindex | next->gsindex | prev->gs)) {
+			load_gs_index(next->gsindex);
+			if (gsindex)
+			prev->gs = 0;				
+		}
+		if (next->gs)
+			wrmsrl(MSR_KERNEL_GS_BASE, next->gs); 
+		prev->gsindex = gsindex;
+	}
+
+	/* 
+	 * Switch the PDA context.
+	 */
+	prev->userrsp = read_pda(oldrsp); 
+	write_pda(oldrsp, next->userrsp); 
+	write_pda(pcurrent, next_p); 
+	write_pda(kernelstack,
+	    (unsigned long)next_p->thread_info + THREAD_SIZE - PDA_STACKOFFSET);
+
+	/*
+	 * Now maybe reload the debug registers
+	 */
+	if (unlikely(next->debugreg7)) {
+		loaddebug(next, 0);
+		loaddebug(next, 1);
+		loaddebug(next, 2);
+		loaddebug(next, 3);
+		/* no 4 and 5 */
+		loaddebug(next, 6);
+		loaddebug(next, 7);
+	}
+
+
+	/* 
+	 * Handle the IO bitmap 
+	 */ 
+	if (unlikely(prev->io_bitmap_ptr || next->io_bitmap_ptr)) {
+		if (next->io_bitmap_ptr)
+			/*
+			 * Copy the relevant range of the IO bitmap.
+			 * Normally this is 128 bytes or less:
+ 			 */
+			memcpy(tss->io_bitmap, next->io_bitmap_ptr,
+				max(prev->io_bitmap_max, next->io_bitmap_max));
+		else {
+			/*
+			 * Clear any possible leftover bits:
+			 */
+			memset(tss->io_bitmap, 0xff, prev->io_bitmap_max);
+		}
+	}
+
+	return prev_p;
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+asmlinkage 
+long sys_execve(char __user *name, char __user * __user *argv,
+		char __user * __user *envp, struct pt_regs regs)
+{
+	long error;
+	char * filename;
+
+	filename = getname(name);
+	error = PTR_ERR(filename);
+	if (IS_ERR(filename)) 
+		return error;
+	error = do_execve(filename, argv, envp, &regs); 
+	if (error == 0) {
+		task_lock(current);
+		current->ptrace &= ~PT_DTRACE;
+		task_unlock(current);
+	}
+	putname(filename);
+	return error;
+}
+
+void set_personality_64bit(void)
+{
+	/* inherit personality from parent */
+
+	/* Make sure to be in 64bit mode */
+	clear_thread_flag(TIF_IA32); 
+
+	/* TBD: overwrites user setup. Should have two bits.
+	   But 64bit processes have always behaved this way,
+	   so it's not too bad. The main problem is just that
+   	   32bit childs are affected again. */
+	current->personality &= ~READ_IMPLIES_EXEC;
+}
+
+asmlinkage long sys_fork(struct pt_regs *regs)
+{
+	return do_fork(SIGCHLD, regs->rsp, regs, 0, NULL, NULL);
+}
+
+asmlinkage long
+sys_clone(unsigned long clone_flags, unsigned long newsp,
+	  void __user *parent_tid, void __user *child_tid, struct pt_regs *regs)
+{
+	if (!newsp)
+		newsp = regs->rsp;
+	return do_fork(clone_flags, newsp, regs, 0, parent_tid, child_tid);
+}
+
+/*
+ * This is trivial, and on the face of it looks like it
+ * could equally well be done in user mode.
+ *
+ * Not so, for quite unobvious reasons - register pressure.
+ * In user mode vfork() cannot have a stack frame, and if
+ * done by calling the "clone()" system call directly, you
+ * do not have enough call-clobbered registers to hold all
+ * the information you need.
+ */
+asmlinkage long sys_vfork(struct pt_regs *regs)
+{
+	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->rsp, regs, 0,
+		    NULL, NULL);
+}
+
+unsigned long get_wchan(struct task_struct *p)
+{
+	unsigned long stack;
+	u64 fp,rip;
+	int count = 0;
+
+	if (!p || p == current || p->state==TASK_RUNNING)
+		return 0; 
+	stack = (unsigned long)p->thread_info; 
+	if (p->thread.rsp < stack || p->thread.rsp > stack+THREAD_SIZE)
+		return 0;
+	fp = *(u64 *)(p->thread.rsp);
+	do { 
+		if (fp < (unsigned long)stack ||
+		    fp > (unsigned long)stack+THREAD_SIZE)
+			return 0; 
+		rip = *(u64 *)(fp+8); 
+		if (!in_sched_functions(rip))
+			return rip; 
+		fp = *(u64 *)fp; 
+	} while (count++ < 16); 
+	return 0;
+}
+
+long do_arch_prctl(struct task_struct *task, int code, unsigned long addr)
+{ 
+	int ret = 0; 
+	int doit = task == current;
+	int cpu;
+
+	switch (code) { 
+	case ARCH_SET_GS:
+		if (addr >= TASK_SIZE_OF(task))
+			return -EPERM; 
+		cpu = get_cpu();
+		/* handle small bases via the GDT because that's faster to 
+		   switch. */
+		if (addr <= 0xffffffff) {  
+			set_32bit_tls(task, GS_TLS, addr); 
+			if (doit) { 
+				load_TLS(&task->thread, cpu);
+				load_gs_index(GS_TLS_SEL); 
+			}
+			task->thread.gsindex = GS_TLS_SEL; 
+			task->thread.gs = 0;
+		} else { 
+			task->thread.gsindex = 0;
+			task->thread.gs = addr;
+			if (doit) {
+				load_gs_index(0);
+				ret = checking_wrmsrl(MSR_KERNEL_GS_BASE, addr);
+			} 
+		}
+		put_cpu();
+		break;
+	case ARCH_SET_FS:
+		/* Not strictly needed for fs, but do it for symmetry
+		   with gs */
+		if (addr >= TASK_SIZE_OF(task))
+			return -EPERM; 
+		cpu = get_cpu();
+		/* handle small bases via the GDT because that's faster to 
+		   switch. */
+		if (addr <= 0xffffffff) { 
+			set_32bit_tls(task, FS_TLS, addr);
+			if (doit) { 
+				load_TLS(&task->thread, cpu); 
+				asm volatile("movl %0,%%fs" :: "r"(FS_TLS_SEL));
+			}
+			task->thread.fsindex = FS_TLS_SEL;
+			task->thread.fs = 0;
+		} else { 
+			task->thread.fsindex = 0;
+			task->thread.fs = addr;
+			if (doit) {
+				/* set the selector to 0 to not confuse
+				   __switch_to */
+				asm volatile("movl %0,%%fs" :: "r" (0));
+				ret = checking_wrmsrl(MSR_FS_BASE, addr);
+			}
+		}
+		put_cpu();
+		break;
+	case ARCH_GET_FS: { 
+		unsigned long base; 
+		if (task->thread.fsindex == FS_TLS_SEL)
+			base = read_32bit_tls(task, FS_TLS);
+		else if (doit)
+			rdmsrl(MSR_FS_BASE, base);
+		else
+			base = task->thread.fs;
+		ret = put_user(base, (unsigned long __user *)addr); 
+		break; 
+	}
+	case ARCH_GET_GS: { 
+		unsigned long base;
+		if (task->thread.gsindex == GS_TLS_SEL)
+			base = read_32bit_tls(task, GS_TLS);
+		else if (doit)
+			rdmsrl(MSR_KERNEL_GS_BASE, base);
+		else
+			base = task->thread.gs;
+		ret = put_user(base, (unsigned long __user *)addr); 
+		break;
+	}
+
+	default:
+		ret = -EINVAL;
+		break;
+	} 
+
+	return ret;	
+} 
+
+long sys_arch_prctl(int code, unsigned long addr)
+{
+	return do_arch_prctl(current, code, addr);
+} 
+
+/* 
+ * Capture the user space registers if the task is not running (in user space)
+ */
+int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs)
+{
+	struct pt_regs *pp, ptregs;
+
+	pp = (struct pt_regs *)(tsk->thread.rsp0);
+	--pp; 
+
+	ptregs = *pp; 
+	ptregs.cs &= 0xffff;
+	ptregs.ss &= 0xffff;
+
+	elf_core_copy_regs(regs, &ptregs);
+ 
+	return 1;
+}
+
+unsigned long arch_align_stack(unsigned long sp)
+{
+	if (randomize_va_space)
+		sp -= get_random_int() % 8192;
+	return sp & ~0xf;
+}
diff -urN oldtree/arch/x86_64/kernel/time.c newtree/arch/x86_64/kernel/time.c
--- oldtree/arch/x86_64/kernel/time.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/x86_64/kernel/time.c	2006-02-13 19:19:59.689559496 -0500
@@ -1234,9 +1234,45 @@
 
 int hpet_rtc_dropped_irq(void)
 {
+	unsigned int cnt, ticks_per_int, lost_ints;
+
 	if (!is_hpet_enabled())
 		return 0;
 
+	if (UIE_on | PIE_on | AIE_on) {
+		/*
+		 * The interrupt handler schedules the next interrupt at a
+		 * constant offset from the time the current interrupt was
+		 * scheduled, without regard to the actual time. When the
+		 * handler is delayed too long, it tries to schedule the next
+		 * interrupt in the past and the hardware would not interrupt
+		 * until the counter had wrapped around. We catch it here.
+		 */
+		cnt = hpet_readl(HPET_COUNTER);
+		/* was the comparator set to a time in the past? */
+		if ((int)(cnt - hpet_t1_cmp) > 0) {
+			/* determine how many interrupts were actually lost */
+			ticks_per_int = (hpet_tick * HZ) / hpet_rtc_int_freq;
+			lost_ints = (cnt - hpet_t1_cmp) / ticks_per_int + 1;
+			/*
+			 * Make sure that, even with the time needed to execute
+			 * this code, the next scheduled interrupt has been
+			 * moved back to the future.
+			 */
+			lost_ints++;
+
+			cnt = hpet_t1_cmp + lost_ints * ticks_per_int;
+			hpet_writel(cnt, HPET_T1_CMP);
+			hpet_t1_cmp = cnt;
+
+			if (PIE_on)
+				PIE_count += lost_ints;
+
+			printk(KERN_WARNING "rtc: lost some interrupts"
+			       " at %ldHz.\n", hpet_rtc_int_freq);
+		}
+	}
+
 	return 1;
 }
 
diff -urN oldtree/arch/x86_64/kernel/time.c.orig newtree/arch/x86_64/kernel/time.c.orig
--- oldtree/arch/x86_64/kernel/time.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/arch/x86_64/kernel/time.c.orig	2006-02-13 19:19:59.690559344 -0500
@@ -0,0 +1,1307 @@
+/*
+ *  linux/arch/x86-64/kernel/time.c
+ *
+ *  "High Precision Event Timer" based timekeeping.
+ *
+ *  Copyright (c) 1991,1992,1995  Linus Torvalds
+ *  Copyright (c) 1994  Alan Modra
+ *  Copyright (c) 1995  Markus Kuhn
+ *  Copyright (c) 1996  Ingo Molnar
+ *  Copyright (c) 1998  Andrea Arcangeli
+ *  Copyright (c) 2002  Vojtech Pavlik
+ *  Copyright (c) 2003  Andi Kleen
+ *  RTC support code taken from arch/i386/kernel/timers/time_hpet.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/mc146818rtc.h>
+#include <linux/time.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+#include <linux/bcd.h>
+#include <linux/kallsyms.h>
+#include <linux/acpi.h>
+#ifdef CONFIG_ACPI
+#include <acpi/achware.h>	/* for PM timer frequency */
+#endif
+#include <asm/8253pit.h>
+#include <asm/pgtable.h>
+#include <asm/vsyscall.h>
+#include <asm/timex.h>
+#include <asm/proto.h>
+#include <asm/hpet.h>
+#include <asm/sections.h>
+#include <linux/cpufreq.h>
+#include <linux/hpet.h>
+#ifdef CONFIG_X86_LOCAL_APIC
+#include <asm/apic.h>
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+static void cpufreq_delayed_get(void);
+#endif
+extern void i8254_timer_resume(void);
+extern int using_apic_timer;
+
+DEFINE_SPINLOCK(rtc_lock);
+DEFINE_SPINLOCK(i8253_lock);
+
+static int nohpet __initdata = 0;
+static int notsc __initdata = 0;
+
+#undef HPET_HACK_ENABLE_DANGEROUS
+
+unsigned int cpu_khz;					/* TSC clocks / usec, not used here */
+static unsigned long hpet_period;			/* fsecs / HPET clock */
+unsigned long hpet_tick;				/* HPET clocks / interrupt */
+static int hpet_use_timer;				/* Use counter of hpet for time keeping, otherwise PIT */
+unsigned long vxtime_hz = PIT_TICK_RATE;
+int report_lost_ticks;				/* command line option */
+unsigned long long monotonic_base;
+
+struct vxtime_data __vxtime __section_vxtime;	/* for vsyscalls */
+
+volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES;
+unsigned long __wall_jiffies __section_wall_jiffies = INITIAL_JIFFIES;
+struct timespec __xtime __section_xtime;
+struct timezone __sys_tz __section_sys_tz;
+
+static inline void rdtscll_sync(unsigned long *tsc)
+{
+#ifdef CONFIG_SMP
+	sync_core();
+#endif
+	rdtscll(*tsc);
+}
+
+/*
+ * do_gettimeoffset() returns microseconds since last timer interrupt was
+ * triggered by hardware. A memory read of HPET is slower than a register read
+ * of TSC, but much more reliable. It's also synchronized to the timer
+ * interrupt. Note that do_gettimeoffset() may return more than hpet_tick, if a
+ * timer interrupt has happened already, but vxtime.trigger wasn't updated yet.
+ * This is not a problem, because jiffies hasn't updated either. They are bound
+ * together by xtime_lock.
+ */
+
+static inline unsigned int do_gettimeoffset_tsc(void)
+{
+	unsigned long t;
+	unsigned long x;
+	rdtscll_sync(&t);
+	if (t < vxtime.last_tsc) t = vxtime.last_tsc; /* hack */
+	x = ((t - vxtime.last_tsc) * vxtime.tsc_quot) >> 32;
+	return x;
+}
+
+static inline unsigned int do_gettimeoffset_hpet(void)
+{
+	/* cap counter read to one tick to avoid inconsistencies */
+	unsigned long counter = hpet_readl(HPET_COUNTER) - vxtime.last;
+	return (min(counter,hpet_tick) * vxtime.quot) >> 32;
+}
+
+unsigned int (*do_gettimeoffset)(void) = do_gettimeoffset_tsc;
+
+/*
+ * This version of gettimeofday() has microsecond resolution and better than
+ * microsecond precision, as we're using at least a 10 MHz (usually 14.31818
+ * MHz) HPET timer.
+ */
+
+void do_gettimeofday(struct timeval *tv)
+{
+	unsigned long seq, t;
+ 	unsigned int sec, usec;
+
+	do {
+		seq = read_seqbegin(&xtime_lock);
+
+		sec = xtime.tv_sec;
+		usec = xtime.tv_nsec / 1000;
+
+		/* i386 does some correction here to keep the clock 
+		   monotonous even when ntpd is fixing drift.
+		   But they didn't work for me, there is a non monotonic
+		   clock anyways with ntp.
+		   I dropped all corrections now until a real solution can
+		   be found. Note when you fix it here you need to do the same
+		   in arch/x86_64/kernel/vsyscall.c and export all needed
+		   variables in vmlinux.lds. -AK */ 
+
+		t = (jiffies - wall_jiffies) * (1000000L / HZ) +
+			do_gettimeoffset();
+		usec += t;
+
+	} while (read_seqretry(&xtime_lock, seq));
+
+	tv->tv_sec = sec + usec / 1000000;
+	tv->tv_usec = usec % 1000000;
+}
+
+EXPORT_SYMBOL(do_gettimeofday);
+
+/*
+ * settimeofday() first undoes the correction that gettimeofday would do
+ * on the time, and then saves it. This is ugly, but has been like this for
+ * ages already.
+ */
+
+int do_settimeofday(struct timespec *tv)
+{
+	time_t wtm_sec, sec = tv->tv_sec;
+	long wtm_nsec, nsec = tv->tv_nsec;
+
+	if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
+		return -EINVAL;
+
+	write_seqlock_irq(&xtime_lock);
+
+	nsec -= do_gettimeoffset() * 1000 +
+		(jiffies - wall_jiffies) * (NSEC_PER_SEC/HZ);
+
+	wtm_sec  = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
+	wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
+
+	set_normalized_timespec(&xtime, sec, nsec);
+	set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
+
+	ntp_clear();
+
+	write_sequnlock_irq(&xtime_lock);
+	clock_was_set();
+	return 0;
+}
+
+EXPORT_SYMBOL(do_settimeofday);
+
+unsigned long profile_pc(struct pt_regs *regs)
+{
+	unsigned long pc = instruction_pointer(regs);
+
+	/* Assume the lock function has either no stack frame or only a single word.
+	   This checks if the address on the stack looks like a kernel text address.
+	   There is a small window for false hits, but in that case the tick
+	   is just accounted to the spinlock function.
+	   Better would be to write these functions in assembler again
+	   and check exactly. */
+	if (in_lock_functions(pc)) {
+		char *v = *(char **)regs->rsp;
+		if ((v >= _stext && v <= _etext) ||
+			(v >= _sinittext && v <= _einittext) ||
+			(v >= (char *)MODULES_VADDR  && v <= (char *)MODULES_END))
+			return (unsigned long)v;
+		return ((unsigned long *)regs->rsp)[1];
+	}
+	return pc;
+}
+EXPORT_SYMBOL(profile_pc);
+
+/*
+ * In order to set the CMOS clock precisely, set_rtc_mmss has to be called 500
+ * ms after the second nowtime has started, because when nowtime is written
+ * into the registers of the CMOS clock, it will jump to the next second
+ * precisely 500 ms later. Check the Motorola MC146818A or Dallas DS12887 data
+ * sheet for details.
+ */
+
+static void set_rtc_mmss(unsigned long nowtime)
+{
+	int real_seconds, real_minutes, cmos_minutes;
+	unsigned char control, freq_select;
+
+/*
+ * IRQs are disabled when we're called from the timer interrupt,
+ * no need for spin_lock_irqsave()
+ */
+
+	spin_lock(&rtc_lock);
+
+/*
+ * Tell the clock it's being set and stop it.
+ */
+
+	control = CMOS_READ(RTC_CONTROL);
+	CMOS_WRITE(control | RTC_SET, RTC_CONTROL);
+
+	freq_select = CMOS_READ(RTC_FREQ_SELECT);
+	CMOS_WRITE(freq_select | RTC_DIV_RESET2, RTC_FREQ_SELECT);
+
+	cmos_minutes = CMOS_READ(RTC_MINUTES);
+		BCD_TO_BIN(cmos_minutes);
+
+/*
+ * since we're only adjusting minutes and seconds, don't interfere with hour
+ * overflow. This avoids messing with unknown time zones but requires your RTC
+ * not to be off by more than 15 minutes. Since we're calling it only when
+ * our clock is externally synchronized using NTP, this shouldn't be a problem.
+ */
+
+	real_seconds = nowtime % 60;
+	real_minutes = nowtime / 60;
+	if (((abs(real_minutes - cmos_minutes) + 15) / 30) & 1)
+		real_minutes += 30;		/* correct for half hour time zone */
+	real_minutes %= 60;
+
+#if 0
+	/* AMD 8111 is a really bad time keeper and hits this regularly. 
+	   It probably was an attempt to avoid screwing up DST, but ignore
+	   that for now. */	   
+	if (abs(real_minutes - cmos_minutes) >= 30) {
+		printk(KERN_WARNING "time.c: can't update CMOS clock "
+		       "from %d to %d\n", cmos_minutes, real_minutes);
+	} else
+#endif
+
+	{
+			BIN_TO_BCD(real_seconds);
+			BIN_TO_BCD(real_minutes);
+		CMOS_WRITE(real_seconds, RTC_SECONDS);
+		CMOS_WRITE(real_minutes, RTC_MINUTES);
+	}
+
+/*
+ * The following flags have to be released exactly in this order, otherwise the
+ * DS12887 (popular MC146818A clone with integrated battery and quartz) will
+ * not reset the oscillator and will not update precisely 500 ms later. You
+ * won't find this mentioned in the Dallas Semiconductor data sheets, but who
+ * believes data sheets anyway ... -- Markus Kuhn
+ */
+
+	CMOS_WRITE(control, RTC_CONTROL);
+	CMOS_WRITE(freq_select, RTC_FREQ_SELECT);
+
+	spin_unlock(&rtc_lock);
+}
+
+
+/* monotonic_clock(): returns # of nanoseconds passed since time_init()
+ *		Note: This function is required to return accurate
+ *		time even in the absence of multiple timer ticks.
+ */
+unsigned long long monotonic_clock(void)
+{
+	unsigned long seq;
+ 	u32 last_offset, this_offset, offset;
+	unsigned long long base;
+
+	if (vxtime.mode == VXTIME_HPET) {
+		do {
+			seq = read_seqbegin(&xtime_lock);
+
+			last_offset = vxtime.last;
+			base = monotonic_base;
+			this_offset = hpet_readl(HPET_COUNTER);
+
+		} while (read_seqretry(&xtime_lock, seq));
+		offset = (this_offset - last_offset);
+		offset *=(NSEC_PER_SEC/HZ)/hpet_tick;
+		return base + offset;
+	}else{
+		do {
+			seq = read_seqbegin(&xtime_lock);
+
+			last_offset = vxtime.last_tsc;
+			base = monotonic_base;
+		} while (read_seqretry(&xtime_lock, seq));
+		sync_core();
+		rdtscll(this_offset);
+		offset = (this_offset - last_offset)*1000/cpu_khz; 
+		return base + offset;
+	}
+
+
+}
+EXPORT_SYMBOL(monotonic_clock);
+
+static noinline void handle_lost_ticks(int lost, struct pt_regs *regs)
+{
+    static long lost_count;
+    static int warned;
+
+    if (report_lost_ticks) {
+	    printk(KERN_WARNING "time.c: Lost %d timer "
+		   "tick(s)! ", lost);
+	    print_symbol("rip %s)\n", regs->rip);
+    }
+
+    if (lost_count == 1000 && !warned) {
+	    printk(KERN_WARNING
+		   "warning: many lost ticks.\n"
+		   KERN_WARNING "Your time source seems to be instable or "
+		   		"some driver is hogging interupts\n");
+	    print_symbol("rip %s\n", regs->rip);
+	    if (vxtime.mode == VXTIME_TSC && vxtime.hpet_address) {
+		    printk(KERN_WARNING "Falling back to HPET\n");
+		    vxtime.last = hpet_readl(HPET_T0_CMP) - hpet_tick;
+		    vxtime.mode = VXTIME_HPET;
+		    do_gettimeoffset = do_gettimeoffset_hpet;
+	    }
+	    /* else should fall back to PIT, but code missing. */
+	    warned = 1;
+    } else
+	    lost_count++;
+
+#ifdef CONFIG_CPU_FREQ
+    /* In some cases the CPU can change frequency without us noticing
+       (like going into thermal throttle)
+       Give cpufreq a change to catch up. */
+    if ((lost_count+1) % 25 == 0) {
+	    cpufreq_delayed_get();
+    }
+#endif
+}
+
+static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	static unsigned long rtc_update = 0;
+	unsigned long tsc;
+	int delay, offset = 0, lost = 0;
+
+/*
+ * Here we are in the timer irq handler. We have irqs locally disabled (so we
+ * don't need spin_lock_irqsave()) but we don't know if the timer_bh is running
+ * on the other CPU, so we need a lock. We also need to lock the vsyscall
+ * variables, because both do_timer() and us change them -arca+vojtech
+ */
+
+	write_seqlock(&xtime_lock);
+
+	if (vxtime.hpet_address)
+		offset = hpet_readl(HPET_COUNTER);
+
+	if (hpet_use_timer) {
+		/* if we're using the hpet timer functionality,
+		 * we can more accurately know the counter value
+		 * when the timer interrupt occured.
+		 */
+		offset = hpet_readl(HPET_T0_CMP) - hpet_tick;
+		delay = hpet_readl(HPET_COUNTER) - offset;
+	} else {
+		spin_lock(&i8253_lock);
+		outb_p(0x00, 0x43);
+		delay = inb_p(0x40);
+		delay |= inb(0x40) << 8;
+		spin_unlock(&i8253_lock);
+		delay = LATCH - 1 - delay;
+	}
+
+	rdtscll_sync(&tsc);
+
+	if (vxtime.mode == VXTIME_HPET) {
+		if (offset - vxtime.last > hpet_tick) {
+			lost = (offset - vxtime.last) / hpet_tick - 1;
+		}
+
+		monotonic_base += 
+			(offset - vxtime.last)*(NSEC_PER_SEC/HZ) / hpet_tick;
+
+		vxtime.last = offset;
+#ifdef CONFIG_X86_PM_TIMER
+	} else if (vxtime.mode == VXTIME_PMTMR) {
+		lost = pmtimer_mark_offset();
+#endif
+	} else {
+		offset = (((tsc - vxtime.last_tsc) *
+			   vxtime.tsc_quot) >> 32) - (USEC_PER_SEC / HZ);
+
+		if (offset < 0)
+			offset = 0;
+
+		if (offset > (USEC_PER_SEC / HZ)) {
+			lost = offset / (USEC_PER_SEC / HZ);
+			offset %= (USEC_PER_SEC / HZ);
+		}
+
+		monotonic_base += (tsc - vxtime.last_tsc)*1000000/cpu_khz ;
+
+		vxtime.last_tsc = tsc - vxtime.quot * delay / vxtime.tsc_quot;
+
+		if ((((tsc - vxtime.last_tsc) *
+		      vxtime.tsc_quot) >> 32) < offset)
+			vxtime.last_tsc = tsc -
+				(((long) offset << 32) / vxtime.tsc_quot) - 1;
+	}
+
+	if (lost > 0) {
+		handle_lost_ticks(lost, regs);
+		jiffies += lost;
+	}
+
+/*
+ * Do the timer stuff.
+ */
+
+	do_timer(regs);
+#ifndef CONFIG_SMP
+	update_process_times(user_mode(regs));
+#endif
+
+/*
+ * In the SMP case we use the local APIC timer interrupt to do the profiling,
+ * except when we simulate SMP mode on a uniprocessor system, in that case we
+ * have to call the local interrupt handler.
+ */
+
+#ifndef CONFIG_X86_LOCAL_APIC
+	profile_tick(CPU_PROFILING, regs);
+#else
+	if (!using_apic_timer)
+		smp_local_timer_interrupt(regs);
+#endif
+
+/*
+ * If we have an externally synchronized Linux clock, then update CMOS clock
+ * accordingly every ~11 minutes. set_rtc_mmss() will be called in the jiffy
+ * closest to exactly 500 ms before the next second. If the update fails, we
+ * don't care, as it'll be updated on the next turn, and the problem (time way
+ * off) isn't likely to go away much sooner anyway.
+ */
+
+	if (ntp_synced() && xtime.tv_sec > rtc_update &&
+		abs(xtime.tv_nsec - 500000000) <= tick_nsec / 2) {
+		set_rtc_mmss(xtime.tv_sec);
+		rtc_update = xtime.tv_sec + 660;
+	}
+ 
+	write_sequnlock(&xtime_lock);
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int cyc2ns_scale;
+#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */
+
+static inline void set_cyc2ns_scale(unsigned long cpu_khz)
+{
+	cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR)/cpu_khz;
+}
+
+static inline unsigned long long cycles_2_ns(unsigned long long cyc)
+{
+	return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR;
+}
+
+unsigned long long sched_clock(void)
+{
+	unsigned long a = 0;
+
+#if 0
+	/* Don't do a HPET read here. Using TSC always is much faster
+	   and HPET may not be mapped yet when the scheduler first runs.
+           Disadvantage is a small drift between CPUs in some configurations,
+	   but that should be tolerable. */
+	if (__vxtime.mode == VXTIME_HPET)
+		return (hpet_readl(HPET_COUNTER) * vxtime.quot) >> 32;
+#endif
+
+	/* Could do CPU core sync here. Opteron can execute rdtsc speculatively,
+	   which means it is not completely exact and may not be monotonous between
+	   CPUs. But the errors should be too small to matter for scheduling
+	   purposes. */
+
+	rdtscll(a);
+	return cycles_2_ns(a);
+}
+
+unsigned long get_cmos_time(void)
+{
+	unsigned int timeout, year, mon, day, hour, min, sec;
+	unsigned char last, this;
+	unsigned long flags;
+
+/*
+ * The Linux interpretation of the CMOS clock register contents: When the
+ * Update-In-Progress (UIP) flag goes from 1 to 0, the RTC registers show the
+ * second which has precisely just started. Waiting for this can take up to 1
+ * second, we timeout approximately after 2.4 seconds on a machine with
+ * standard 8.3 MHz ISA bus.
+ */
+
+	spin_lock_irqsave(&rtc_lock, flags);
+
+	timeout = 1000000;
+	last = this = 0;
+
+	while (timeout && last && !this) {
+		last = this;
+		this = CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP;
+		timeout--;
+	}
+
+/*
+ * Here we are safe to assume the registers won't change for a whole second, so
+ * we just go ahead and read them.
+	 */
+
+		sec = CMOS_READ(RTC_SECONDS);
+		min = CMOS_READ(RTC_MINUTES);
+		hour = CMOS_READ(RTC_HOURS);
+		day = CMOS_READ(RTC_DAY_OF_MONTH);
+		mon = CMOS_READ(RTC_MONTH);
+		year = CMOS_READ(RTC_YEAR);
+
+	spin_unlock_irqrestore(&rtc_lock, flags);
+
+/*
+ * We know that x86-64 always uses BCD format, no need to check the config
+ * register.
+ */
+
+	    BCD_TO_BIN(sec);
+	    BCD_TO_BIN(min);
+	    BCD_TO_BIN(hour);
+	    BCD_TO_BIN(day);
+	    BCD_TO_BIN(mon);
+	    BCD_TO_BIN(year);
+
+/*
+ * x86-64 systems only exists since 2002.
+ * This will work up to Dec 31, 2100
+ */
+	year += 2000;
+
+	return mktime(year, mon, day, hour, min, sec);
+}
+
+#ifdef CONFIG_CPU_FREQ
+
+/* Frequency scaling support. Adjust the TSC based timer when the cpu frequency
+   changes.
+   
+   RED-PEN: On SMP we assume all CPUs run with the same frequency.  It's
+   not that important because current Opteron setups do not support
+   scaling on SMP anyroads.
+
+   Should fix up last_tsc too. Currently gettimeofday in the
+   first tick after the change will be slightly wrong. */
+
+#include <linux/workqueue.h>
+
+static unsigned int cpufreq_delayed_issched = 0;
+static unsigned int cpufreq_init = 0;
+static struct work_struct cpufreq_delayed_get_work;
+
+static void handle_cpufreq_delayed_get(void *v)
+{
+	unsigned int cpu;
+	for_each_online_cpu(cpu) {
+		cpufreq_get(cpu);
+	}
+	cpufreq_delayed_issched = 0;
+}
+
+/* if we notice lost ticks, schedule a call to cpufreq_get() as it tries
+ * to verify the CPU frequency the timing core thinks the CPU is running
+ * at is still correct.
+ */
+static void cpufreq_delayed_get(void)
+{
+	static int warned;
+	if (cpufreq_init && !cpufreq_delayed_issched) {
+		cpufreq_delayed_issched = 1;
+		if (!warned) {
+			warned = 1;
+			printk(KERN_DEBUG "Losing some ticks... checking if CPU frequency changed.\n");
+		}
+		schedule_work(&cpufreq_delayed_get_work);
+	}
+}
+
+static unsigned int  ref_freq = 0;
+static unsigned long loops_per_jiffy_ref = 0;
+
+static unsigned long cpu_khz_ref = 0;
+
+static int time_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
+				 void *data)
+{
+        struct cpufreq_freqs *freq = data;
+	unsigned long *lpj, dummy;
+
+	if (cpu_has(&cpu_data[freq->cpu], X86_FEATURE_CONSTANT_TSC))
+		return 0;
+
+	lpj = &dummy;
+	if (!(freq->flags & CPUFREQ_CONST_LOOPS))
+#ifdef CONFIG_SMP
+	lpj = &cpu_data[freq->cpu].loops_per_jiffy;
+#else
+	lpj = &boot_cpu_data.loops_per_jiffy;
+#endif
+
+	if (!ref_freq) {
+		ref_freq = freq->old;
+		loops_per_jiffy_ref = *lpj;
+		cpu_khz_ref = cpu_khz;
+	}
+        if ((val == CPUFREQ_PRECHANGE  && freq->old < freq->new) ||
+            (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) ||
+	    (val == CPUFREQ_RESUMECHANGE)) {
+                *lpj =
+		cpufreq_scale(loops_per_jiffy_ref, ref_freq, freq->new);
+
+		cpu_khz = cpufreq_scale(cpu_khz_ref, ref_freq, freq->new);
+		if (!(freq->flags & CPUFREQ_CONST_LOOPS))
+			vxtime.tsc_quot = (1000L << 32) / cpu_khz;
+	}
+	
+	set_cyc2ns_scale(cpu_khz_ref);
+
+	return 0;
+}
+ 
+static struct notifier_block time_cpufreq_notifier_block = {
+         .notifier_call  = time_cpufreq_notifier
+};
+
+static int __init cpufreq_tsc(void)
+{
+	INIT_WORK(&cpufreq_delayed_get_work, handle_cpufreq_delayed_get, NULL);
+	if (!cpufreq_register_notifier(&time_cpufreq_notifier_block,
+				       CPUFREQ_TRANSITION_NOTIFIER))
+		cpufreq_init = 1;
+	return 0;
+}
+
+core_initcall(cpufreq_tsc);
+
+#endif
+
+/*
+ * calibrate_tsc() calibrates the processor TSC in a very simple way, comparing
+ * it to the HPET timer of known frequency.
+ */
+
+#define TICK_COUNT 100000000
+
+static unsigned int __init hpet_calibrate_tsc(void)
+{
+	int tsc_start, hpet_start;
+	int tsc_now, hpet_now;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	local_irq_disable();
+
+	hpet_start = hpet_readl(HPET_COUNTER);
+	rdtscl(tsc_start);
+
+	do {
+		local_irq_disable();
+		hpet_now = hpet_readl(HPET_COUNTER);
+		sync_core();
+		rdtscl(tsc_now);
+		local_irq_restore(flags);
+	} while ((tsc_now - tsc_start) < TICK_COUNT &&
+		 (hpet_now - hpet_start) < TICK_COUNT);
+
+	return (tsc_now - tsc_start) * 1000000000L
+		/ ((hpet_now - hpet_start) * hpet_period / 1000);
+}
+
+
+/*
+ * pit_calibrate_tsc() uses the speaker output (channel 2) of
+ * the PIT. This is better than using the timer interrupt output,
+ * because we can read the value of the speaker with just one inb(),
+ * where we need three i/o operations for the interrupt channel.
+ * We count how many ticks the TSC does in 50 ms.
+ */
+
+static unsigned int __init pit_calibrate_tsc(void)
+{
+	unsigned long start, end;
+	unsigned long flags;
+
+	spin_lock_irqsave(&i8253_lock, flags);
+
+	outb((inb(0x61) & ~0x02) | 0x01, 0x61);
+
+	outb(0xb0, 0x43);
+	outb((PIT_TICK_RATE / (1000 / 50)) & 0xff, 0x42);
+	outb((PIT_TICK_RATE / (1000 / 50)) >> 8, 0x42);
+	rdtscll(start);
+	sync_core();
+	while ((inb(0x61) & 0x20) == 0);
+	sync_core();
+	rdtscll(end);
+
+	spin_unlock_irqrestore(&i8253_lock, flags);
+	
+	return (end - start) / 50;
+}
+
+#ifdef	CONFIG_HPET
+static __init int late_hpet_init(void)
+{
+	struct hpet_data	hd;
+	unsigned int 		ntimer;
+
+	if (!vxtime.hpet_address)
+          return -1;
+
+	memset(&hd, 0, sizeof (hd));
+
+	ntimer = hpet_readl(HPET_ID);
+	ntimer = (ntimer & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT;
+	ntimer++;
+
+	/*
+	 * Register with driver.
+	 * Timer0 and Timer1 is used by platform.
+	 */
+	hd.hd_phys_address = vxtime.hpet_address;
+	hd.hd_address = (void *)fix_to_virt(FIX_HPET_BASE);
+	hd.hd_nirqs = ntimer;
+	hd.hd_flags = HPET_DATA_PLATFORM;
+	hpet_reserve_timer(&hd, 0);
+#ifdef	CONFIG_HPET_EMULATE_RTC
+	hpet_reserve_timer(&hd, 1);
+#endif
+	hd.hd_irq[0] = HPET_LEGACY_8254;
+	hd.hd_irq[1] = HPET_LEGACY_RTC;
+	if (ntimer > 2) {
+		struct hpet		*hpet;
+		struct hpet_timer	*timer;
+		int			i;
+
+		hpet = (struct hpet *) fix_to_virt(FIX_HPET_BASE);
+
+		for (i = 2, timer = &hpet->hpet_timers[2]; i < ntimer;
+		     timer++, i++)
+			hd.hd_irq[i] = (timer->hpet_config &
+					Tn_INT_ROUTE_CNF_MASK) >>
+				Tn_INT_ROUTE_CNF_SHIFT;
+
+	}
+
+	hpet_alloc(&hd);
+	return 0;
+}
+fs_initcall(late_hpet_init);
+#endif
+
+static int hpet_timer_stop_set_go(unsigned long tick)
+{
+	unsigned int cfg;
+
+/*
+ * Stop the timers and reset the main counter.
+ */
+
+	cfg = hpet_readl(HPET_CFG);
+	cfg &= ~(HPET_CFG_ENABLE | HPET_CFG_LEGACY);
+	hpet_writel(cfg, HPET_CFG);
+	hpet_writel(0, HPET_COUNTER);
+	hpet_writel(0, HPET_COUNTER + 4);
+
+/*
+ * Set up timer 0, as periodic with first interrupt to happen at hpet_tick,
+ * and period also hpet_tick.
+ */
+	if (hpet_use_timer) {
+		hpet_writel(HPET_TN_ENABLE | HPET_TN_PERIODIC | HPET_TN_SETVAL |
+		    HPET_TN_32BIT, HPET_T0_CFG);
+		hpet_writel(hpet_tick, HPET_T0_CMP);
+		hpet_writel(hpet_tick, HPET_T0_CMP); /* AK: why twice? */
+		cfg |= HPET_CFG_LEGACY;
+	}
+/*
+ * Go!
+ */
+
+	cfg |= HPET_CFG_ENABLE;
+	hpet_writel(cfg, HPET_CFG);
+
+	return 0;
+}
+
+static int hpet_init(void)
+{
+	unsigned int id;
+
+	if (!vxtime.hpet_address)
+		return -1;
+	set_fixmap_nocache(FIX_HPET_BASE, vxtime.hpet_address);
+	__set_fixmap(VSYSCALL_HPET, vxtime.hpet_address, PAGE_KERNEL_VSYSCALL_NOCACHE);
+
+/*
+ * Read the period, compute tick and quotient.
+ */
+
+	id = hpet_readl(HPET_ID);
+
+	if (!(id & HPET_ID_VENDOR) || !(id & HPET_ID_NUMBER))
+		return -1;
+
+	hpet_period = hpet_readl(HPET_PERIOD);
+	if (hpet_period < 100000 || hpet_period > 100000000)
+		return -1;
+
+	hpet_tick = (1000000000L * (USEC_PER_SEC / HZ) + hpet_period / 2) /
+		hpet_period;
+
+	hpet_use_timer = (id & HPET_ID_LEGSUP);
+
+	return hpet_timer_stop_set_go(hpet_tick);
+}
+
+static int hpet_reenable(void)
+{
+	return hpet_timer_stop_set_go(hpet_tick);
+}
+
+void __init pit_init(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&i8253_lock, flags);
+	outb_p(0x34, 0x43);		/* binary, mode 2, LSB/MSB, ch 0 */
+	outb_p(LATCH & 0xff, 0x40);	/* LSB */
+	outb_p(LATCH >> 8, 0x40);	/* MSB */
+	spin_unlock_irqrestore(&i8253_lock, flags);
+}
+
+int __init time_setup(char *str)
+{
+	report_lost_ticks = 1;
+	return 1;
+}
+
+static struct irqaction irq0 = {
+	timer_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "timer", NULL, NULL
+};
+
+extern void __init config_acpi_tables(void);
+
+void __init time_init(void)
+{
+	char *timename;
+
+#ifdef HPET_HACK_ENABLE_DANGEROUS
+        if (!vxtime.hpet_address) {
+		printk(KERN_WARNING "time.c: WARNING: Enabling HPET base "
+		       "manually!\n");
+                outl(0x800038a0, 0xcf8);
+                outl(0xff000001, 0xcfc);
+                outl(0x800038a0, 0xcf8);
+                vxtime.hpet_address = inl(0xcfc) & 0xfffffffe;
+		printk(KERN_WARNING "time.c: WARNING: Enabled HPET "
+		       "at %#lx.\n", vxtime.hpet_address);
+        }
+#endif
+	if (nohpet)
+		vxtime.hpet_address = 0;
+
+	xtime.tv_sec = get_cmos_time();
+	xtime.tv_nsec = 0;
+
+	set_normalized_timespec(&wall_to_monotonic,
+	                        -xtime.tv_sec, -xtime.tv_nsec);
+
+	if (!hpet_init())
+                vxtime_hz = (1000000000000000L + hpet_period / 2) /
+			hpet_period;
+	else
+		vxtime.hpet_address = 0;
+
+	if (hpet_use_timer) {
+		cpu_khz = hpet_calibrate_tsc();
+		timename = "HPET";
+#ifdef CONFIG_X86_PM_TIMER
+	} else if (pmtmr_ioport && !vxtime.hpet_address) {
+		vxtime_hz = PM_TIMER_FREQUENCY;
+		timename = "PM";
+		pit_init();
+		cpu_khz = pit_calibrate_tsc();
+#endif
+	} else {
+		pit_init();
+		cpu_khz = pit_calibrate_tsc();
+		timename = "PIT";
+	}
+
+	printk(KERN_INFO "time.c: Using %ld.%06ld MHz %s timer.\n",
+	       vxtime_hz / 1000000, vxtime_hz % 1000000, timename);
+	printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n",
+		cpu_khz / 1000, cpu_khz % 1000);
+	vxtime.mode = VXTIME_TSC;
+	vxtime.quot = (1000000L << 32) / vxtime_hz;
+	vxtime.tsc_quot = (1000L << 32) / cpu_khz;
+	rdtscll_sync(&vxtime.last_tsc);
+	setup_irq(0, &irq0);
+
+	set_cyc2ns_scale(cpu_khz);
+
+#ifndef CONFIG_SMP
+	time_init_gtod();
+#endif
+}
+
+/*
+ * Make an educated guess if the TSC is trustworthy and synchronized
+ * over all CPUs.
+ */
+static __init int unsynchronized_tsc(void)
+{
+#ifdef CONFIG_SMP
+	if (oem_force_hpet_timer())
+		return 1;
+ 	/* Intel systems are normally all synchronized. Exceptions
+ 	   are handled in the OEM check above. */
+ 	if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL)
+ 		return 0;
+#endif
+ 	/* Assume multi socket systems are not synchronized */
+ 	return num_online_cpus() > 1;
+}
+
+/*
+ * Decide after all CPUs are booted what mode gettimeofday should use.
+ */
+void __init time_init_gtod(void)
+{
+	char *timetype;
+
+	if (unsynchronized_tsc())
+		notsc = 1;
+	if (vxtime.hpet_address && notsc) {
+		timetype = hpet_use_timer ? "HPET" : "PIT/HPET";
+		vxtime.last = hpet_readl(HPET_T0_CMP) - hpet_tick;
+		vxtime.mode = VXTIME_HPET;
+		do_gettimeoffset = do_gettimeoffset_hpet;
+#ifdef CONFIG_X86_PM_TIMER
+	/* Using PM for gettimeofday is quite slow, but we have no other
+	   choice because the TSC is too unreliable on some systems. */
+	} else if (pmtmr_ioport && !vxtime.hpet_address && notsc) {
+		timetype = "PM";
+		do_gettimeoffset = do_gettimeoffset_pm;
+		vxtime.mode = VXTIME_PMTMR;
+		sysctl_vsyscall = 0;
+		printk(KERN_INFO "Disabling vsyscall due to use of PM timer\n");
+#endif
+	} else {
+		timetype = hpet_use_timer ? "HPET/TSC" : "PIT/TSC";
+		vxtime.mode = VXTIME_TSC;
+	}
+
+	printk(KERN_INFO "time.c: Using %s based timekeeping.\n", timetype);
+}
+
+__setup("report_lost_ticks", time_setup);
+
+static long clock_cmos_diff;
+static unsigned long sleep_start;
+
+static int timer_suspend(struct sys_device *dev, pm_message_t state)
+{
+	/*
+	 * Estimate time zone so that set_time can update the clock
+	 */
+	long cmos_time =  get_cmos_time();
+
+	clock_cmos_diff = -cmos_time;
+	clock_cmos_diff += get_seconds();
+	sleep_start = cmos_time;
+	return 0;
+}
+
+static int timer_resume(struct sys_device *dev)
+{
+	unsigned long flags;
+	unsigned long sec;
+	unsigned long ctime = get_cmos_time();
+	unsigned long sleep_length = (ctime - sleep_start) * HZ;
+
+	if (vxtime.hpet_address)
+		hpet_reenable();
+	else
+		i8254_timer_resume();
+
+	sec = ctime + clock_cmos_diff;
+	write_seqlock_irqsave(&xtime_lock,flags);
+	xtime.tv_sec = sec;
+	xtime.tv_nsec = 0;
+	write_sequnlock_irqrestore(&xtime_lock,flags);
+	jiffies += sleep_length;
+	wall_jiffies += sleep_length;
+	touch_softlockup_watchdog();
+	return 0;
+}
+
+static struct sysdev_class timer_sysclass = {
+	.resume = timer_resume,
+	.suspend = timer_suspend,
+	set_kset_name("timer"),
+};
+
+
+/* XXX this driverfs stuff should probably go elsewhere later -john */
+static struct sys_device device_timer = {
+	.id	= 0,
+	.cls	= &timer_sysclass,
+};
+
+static int time_init_device(void)
+{
+	int error = sysdev_class_register(&timer_sysclass);
+	if (!error)
+		error = sysdev_register(&device_timer);
+	return error;
+}
+
+device_initcall(time_init_device);
+
+#ifdef CONFIG_HPET_EMULATE_RTC
+/* HPET in LegacyReplacement Mode eats up RTC interrupt line. When, HPET
+ * is enabled, we support RTC interrupt functionality in software.
+ * RTC has 3 kinds of interrupts:
+ * 1) Update Interrupt - generate an interrupt, every sec, when RTC clock
+ *    is updated
+ * 2) Alarm Interrupt - generate an interrupt at a specific time of day
+ * 3) Periodic Interrupt - generate periodic interrupt, with frequencies
+ *    2Hz-8192Hz (2Hz-64Hz for non-root user) (all freqs in powers of 2)
+ * (1) and (2) above are implemented using polling at a frequency of
+ * 64 Hz. The exact frequency is a tradeoff between accuracy and interrupt
+ * overhead. (DEFAULT_RTC_INT_FREQ)
+ * For (3), we use interrupts at 64Hz or user specified periodic
+ * frequency, whichever is higher.
+ */
+#include <linux/rtc.h>
+
+extern irqreturn_t rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+#define DEFAULT_RTC_INT_FREQ 	64
+#define RTC_NUM_INTS 		1
+
+static unsigned long UIE_on;
+static unsigned long prev_update_sec;
+
+static unsigned long AIE_on;
+static struct rtc_time alarm_time;
+
+static unsigned long PIE_on;
+static unsigned long PIE_freq = DEFAULT_RTC_INT_FREQ;
+static unsigned long PIE_count;
+
+static unsigned long hpet_rtc_int_freq; /* RTC interrupt frequency */
+static unsigned int hpet_t1_cmp; /* cached comparator register */
+
+int is_hpet_enabled(void)
+{
+	return vxtime.hpet_address != 0;
+}
+
+/*
+ * Timer 1 for RTC, we do not use periodic interrupt feature,
+ * even if HPET supports periodic interrupts on Timer 1.
+ * The reason being, to set up a periodic interrupt in HPET, we need to
+ * stop the main counter. And if we do that everytime someone diables/enables
+ * RTC, we will have adverse effect on main kernel timer running on Timer 0.
+ * So, for the time being, simulate the periodic interrupt in software.
+ *
+ * hpet_rtc_timer_init() is called for the first time and during subsequent
+ * interuppts reinit happens through hpet_rtc_timer_reinit().
+ */
+int hpet_rtc_timer_init(void)
+{
+	unsigned int cfg, cnt;
+	unsigned long flags;
+
+	if (!is_hpet_enabled())
+		return 0;
+	/*
+	 * Set the counter 1 and enable the interrupts.
+	 */
+	if (PIE_on && (PIE_freq > DEFAULT_RTC_INT_FREQ))
+		hpet_rtc_int_freq = PIE_freq;
+	else
+		hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;
+
+	local_irq_save(flags);
+	cnt = hpet_readl(HPET_COUNTER);
+	cnt += ((hpet_tick*HZ)/hpet_rtc_int_freq);
+	hpet_writel(cnt, HPET_T1_CMP);
+	hpet_t1_cmp = cnt;
+	local_irq_restore(flags);
+
+	cfg = hpet_readl(HPET_T1_CFG);
+	cfg &= ~HPET_TN_PERIODIC;
+	cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
+	hpet_writel(cfg, HPET_T1_CFG);
+
+	return 1;
+}
+
+static void hpet_rtc_timer_reinit(void)
+{
+	unsigned int cfg, cnt;
+
+	if (unlikely(!(PIE_on | AIE_on | UIE_on))) {
+		cfg = hpet_readl(HPET_T1_CFG);
+		cfg &= ~HPET_TN_ENABLE;
+		hpet_writel(cfg, HPET_T1_CFG);
+		return;
+	}
+
+	if (PIE_on && (PIE_freq > DEFAULT_RTC_INT_FREQ))
+		hpet_rtc_int_freq = PIE_freq;
+	else
+		hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;
+
+	/* It is more accurate to use the comparator value than current count.*/
+	cnt = hpet_t1_cmp;
+	cnt += hpet_tick*HZ/hpet_rtc_int_freq;
+	hpet_writel(cnt, HPET_T1_CMP);
+	hpet_t1_cmp = cnt;
+}
+
+/*
+ * The functions below are called from rtc driver.
+ * Return 0 if HPET is not being used.
+ * Otherwise do the necessary changes and return 1.
+ */
+int hpet_mask_rtc_irq_bit(unsigned long bit_mask)
+{
+	if (!is_hpet_enabled())
+		return 0;
+
+	if (bit_mask & RTC_UIE)
+		UIE_on = 0;
+	if (bit_mask & RTC_PIE)
+		PIE_on = 0;
+	if (bit_mask & RTC_AIE)
+		AIE_on = 0;
+
+	return 1;
+}
+
+int hpet_set_rtc_irq_bit(unsigned long bit_mask)
+{
+	int timer_init_reqd = 0;
+
+	if (!is_hpet_enabled())
+		return 0;
+
+	if (!(PIE_on | AIE_on | UIE_on))
+		timer_init_reqd = 1;
+
+	if (bit_mask & RTC_UIE) {
+		UIE_on = 1;
+	}
+	if (bit_mask & RTC_PIE) {
+		PIE_on = 1;
+		PIE_count = 0;
+	}
+	if (bit_mask & RTC_AIE) {
+		AIE_on = 1;
+	}
+
+	if (timer_init_reqd)
+		hpet_rtc_timer_init();
+
+	return 1;
+}
+
+int hpet_set_alarm_time(unsigned char hrs, unsigned char min, unsigned char sec)
+{
+	if (!is_hpet_enabled())
+		return 0;
+
+	alarm_time.tm_hour = hrs;
+	alarm_time.tm_min = min;
+	alarm_time.tm_sec = sec;
+
+	return 1;
+}
+
+int hpet_set_periodic_freq(unsigned long freq)
+{
+	if (!is_hpet_enabled())
+		return 0;
+
+	PIE_freq = freq;
+	PIE_count = 0;
+
+	return 1;
+}
+
+int hpet_rtc_dropped_irq(void)
+{
+	if (!is_hpet_enabled())
+		return 0;
+
+	return 1;
+}
+
+irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct rtc_time curr_time;
+	unsigned long rtc_int_flag = 0;
+	int call_rtc_interrupt = 0;
+
+	hpet_rtc_timer_reinit();
+
+	if (UIE_on | AIE_on) {
+		rtc_get_rtc_time(&curr_time);
+	}
+	if (UIE_on) {
+		if (curr_time.tm_sec != prev_update_sec) {
+			/* Set update int info, call real rtc int routine */
+			call_rtc_interrupt = 1;
+			rtc_int_flag = RTC_UF;
+			prev_update_sec = curr_time.tm_sec;
+		}
+	}
+	if (PIE_on) {
+		PIE_count++;
+		if (PIE_count >= hpet_rtc_int_freq/PIE_freq) {
+			/* Set periodic int info, call real rtc int routine */
+			call_rtc_interrupt = 1;
+			rtc_int_flag |= RTC_PF;
+			PIE_count = 0;
+		}
+	}
+	if (AIE_on) {
+		if ((curr_time.tm_sec == alarm_time.tm_sec) &&
+		    (curr_time.tm_min == alarm_time.tm_min) &&
+		    (curr_time.tm_hour == alarm_time.tm_hour)) {
+			/* Set alarm int info, call real rtc int routine */
+			call_rtc_interrupt = 1;
+			rtc_int_flag |= RTC_AF;
+		}
+	}
+	if (call_rtc_interrupt) {
+		rtc_int_flag |= (RTC_IRQF | (RTC_NUM_INTS << 8));
+		rtc_interrupt(rtc_int_flag, dev_id, regs);
+	}
+	return IRQ_HANDLED;
+}
+#endif
+
+
+
+static int __init nohpet_setup(char *s) 
+{ 
+	nohpet = 1;
+	return 0;
+} 
+
+__setup("nohpet", nohpet_setup);
+
+
+static int __init notsc_setup(char *s)
+{
+	notsc = 1;
+	return 0;
+}
+
+__setup("notsc", notsc_setup);
+
+
diff -urN oldtree/arch/x86_64/kernel/vmlinux.lds.S newtree/arch/x86_64/kernel/vmlinux.lds.S
--- oldtree/arch/x86_64/kernel/vmlinux.lds.S	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/x86_64/kernel/vmlinux.lds.S	2006-02-13 19:20:29.697997520 -0500
@@ -170,13 +170,15 @@
   . = ALIGN(4096);
   __initramfs_start = .;
   .init.ramfs : AT(ADDR(.init.ramfs) - LOAD_OFFSET) { *(.init.ramfs) }
-  __initramfs_end = .;	
-  . = ALIGN(32);
+  __initramfs_end = .;
+  /* temporary here to work around NR_CPUS. If you see this comment in 2.6.17+
+   complain */
+  . = ALIGN(4096);	
+  __init_end = .;	
+  . = ALIGN(128);
   __per_cpu_start = .;
   .data.percpu  : AT(ADDR(.data.percpu) - LOAD_OFFSET) { *(.data.percpu) }
   __per_cpu_end = .;
-  . = ALIGN(4096);
-  __init_end = .;
 
   . = ALIGN(4096);
   __nosave_begin = .;
diff -urN oldtree/arch/x86_64/lib/Makefile newtree/arch/x86_64/lib/Makefile
--- oldtree/arch/x86_64/lib/Makefile	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/x86_64/lib/Makefile	2006-02-13 19:19:59.691559192 -0500
@@ -4,7 +4,7 @@
 
 CFLAGS_csum-partial.o := -funroll-loops
 
-obj-y := io.o
+obj-y := io.o iomap_copy.o
 
 lib-y := csum-partial.o csum-copy.o csum-wrappers.o delay.o \
 	usercopy.o getuser.o putuser.o  \
diff -urN oldtree/arch/x86_64/lib/iomap_copy.S newtree/arch/x86_64/lib/iomap_copy.S
--- oldtree/arch/x86_64/lib/iomap_copy.S	1969-12-31 19:00:00.000000000 -0500
+++ newtree/arch/x86_64/lib/iomap_copy.S	2006-02-13 19:19:59.691559192 -0500
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2006 PathScale, Inc.  All Rights Reserved.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * override generic version in lib/iomap_copy.c
+ */
+ 	.globl __iowrite32_copy
+	.p2align 4
+__iowrite32_copy:
+	movl %edx,%ecx
+	rep movsd
+	ret
diff -urN oldtree/arch/x86_64/mm/srat.c newtree/arch/x86_64/mm/srat.c
--- oldtree/arch/x86_64/mm/srat.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/arch/x86_64/mm/srat.c	2006-02-13 19:20:29.697997520 -0500
@@ -25,6 +25,10 @@
 static struct node nodes[MAX_NUMNODES] __initdata;
 static __u8  pxm2node[256] = { [0 ... 255] = 0xff };
 
+/* Too small nodes confuse the VM badly. Usually they result
+   from BIOS bugs. */
+#define NODE_MIN_SIZE (4*1024*1024)
+
 static int node_to_pxm(int n);
 
 int pxm_to_node(int pxm)
@@ -168,22 +172,32 @@
 	       nd->start, nd->end);
 }
 
+static void unparse_node(int node)
+{
+	int i;
+	node_clear(node, nodes_parsed);
+	for (i = 0; i < MAX_LOCAL_APIC; i++) {
+		if (apicid_to_node[i] == node)
+			apicid_to_node[i] = NUMA_NO_NODE;
+	}
+}
+
 void __init acpi_numa_arch_fixup(void) {}
 
 /* Use the information discovered above to actually set up the nodes. */
 int __init acpi_scan_nodes(unsigned long start, unsigned long end)
 {
 	int i;
+
+	for (i = 0; i < MAX_NUMNODES; i++) {
+ 		cutoff_node(i, start, end);
+		if ((nodes[i].end - nodes[i].start) < NODE_MIN_SIZE)
+			unparse_node(i);
+ 	}
+
 	if (acpi_numa <= 0)
 		return -1;
 
-	/* First clean up the node list */
-	for_each_node_mask(i, nodes_parsed) {
-		cutoff_node(i, start, end);
-		if (nodes[i].start == nodes[i].end)
-			node_clear(i, nodes_parsed);
-	}
-
 	memnode_shift = compute_hash_shift(nodes, nodes_weight(nodes_parsed));
 	if (memnode_shift < 0) {
 		printk(KERN_ERR
diff -urN oldtree/block/ll_rw_blk.c newtree/block/ll_rw_blk.c
--- oldtree/block/ll_rw_blk.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/block/ll_rw_blk.c	2006-02-13 19:20:29.700997064 -0500
@@ -1472,6 +1472,7 @@
  **/
 void generic_unplug_device(request_queue_t *q)
 {
+	might_sleep();
 	spin_lock_irq(q->queue_lock);
 	__generic_unplug_device(q);
 	spin_unlock_irq(q->queue_lock);
@@ -2609,30 +2610,6 @@
 	return 0;
 }
 
-/**
- * blk_attempt_remerge  - attempt to remerge active head with next request
- * @q:    The &request_queue_t belonging to the device
- * @rq:   The head request (usually)
- *
- * Description:
- *    For head-active devices, the queue can easily be unplugged so quickly
- *    that proper merging is not done on the front request. This may hurt
- *    performance greatly for some devices. The block layer cannot safely
- *    do merging on that first request for these queues, but the driver can
- *    call this function and make it happen any way. Only the driver knows
- *    when it is safe to do so.
- **/
-void blk_attempt_remerge(request_queue_t *q, struct request *rq)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(q->queue_lock, flags);
-	attempt_back_merge(q, rq);
-	spin_unlock_irqrestore(q->queue_lock, flags);
-}
-
-EXPORT_SYMBOL(blk_attempt_remerge);
-
 static int __make_request(request_queue_t *q, struct bio *bio)
 {
 	struct request *req;
@@ -2829,7 +2806,7 @@
  * bi_sector for remaps as it sees fit.  So the values of these fields
  * should NOT be depended on after the call to generic_make_request.
  */
-void generic_make_request(struct bio *bio)
+static inline void __generic_make_request(struct bio *bio)
 {
 	request_queue_t *q;
 	sector_t maxsector;
@@ -2896,6 +2873,57 @@
 	} while (ret);
 }
 
+/*
+ * We only want one ->make_request_fn to be active at a time,
+ * else stack usage with stacked devices could be a problem.
+ * So use current->bio_{list,tail} to keep a list of requests
+ * submited by a make_request_fn function.
+ * current->bio_tail is also used as a flag to say if
+ * generic_make_request is currently active in this task or not.
+ * If it is NULL, then no make_request is active.  If it is non-NULL,
+ * then a make_request is active, and new requests should be added
+ * at the tail
+ */
+void generic_make_request(struct bio *bio)
+{
+	if (current->bio_tail) {
+		/* make_request is active */
+		*(current->bio_tail) = bio;
+		bio->bi_next = NULL;
+		current->bio_tail = &bio->bi_next;
+		return;
+	}
+	/* following loop may be a bit non-obvious, and so deserves some
+	 * explanation.
+	 * Before entering the loop, bio->bi_next is NULL (as all callers
+	 * ensure that) so we have a list with a single bio.
+	 * We pretend that we have just taken it off a longer list, so
+	 * we assign bio_list to the next (which is NULL) and bio_tail
+	 * to &bio_list, thus initialising the bio_list of new bios to be
+	 * added.  __generic_make_request may indeed add some more bios
+	 * through a recursive call to generic_make_request.  If it
+	 * did, we find a non-NULL value in bio_list and re-enter the loop
+	 * from the top.  In this case we really did just take the bio
+	 * of the top of the list (no pretending) and so fixup bio_list and
+	 * bio_tail or bi_next, and call into __generic_make_request again.
+	 *
+	 * The loop was structured like this to make only one call to
+	 * __generic_make_request (which is important as it is large and
+	 * inlined) and to keep the structure simple.
+	 */
+	BUG_ON(bio->bi_next);
+	do {
+		current->bio_list = bio->bi_next;
+		if (bio->bi_next == NULL)
+			current->bio_tail = &current->bio_list;
+		else
+			bio->bi_next = NULL;
+		__generic_make_request(bio);
+		bio = current->bio_list;
+	} while (bio);
+	current->bio_tail = NULL; /* deactivate */
+}
+
 EXPORT_SYMBOL(generic_make_request);
 
 /**
diff -urN oldtree/block/ll_rw_blk.c.orig newtree/block/ll_rw_blk.c.orig
--- oldtree/block/ll_rw_blk.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/block/ll_rw_blk.c.orig	2006-02-13 19:19:59.692559040 -0500
@@ -0,0 +1,3662 @@
+/*
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 1994,      Karl Keyte: Added support for disk statistics
+ * Elevator latency, (C) 2000  Andrea Arcangeli <andrea@suse.de> SuSE
+ * Queue request tables / lock, selectable elevator, Jens Axboe <axboe@suse.de>
+ * kernel-doc documentation started by NeilBrown <neilb@cse.unsw.edu.au> -  July2000
+ * bio rewrite, highmem i/o, etc, Jens Axboe <axboe@suse.de> - may 2001
+ */
+
+/*
+ * This handles all read/write requests to block devices
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/backing-dev.h>
+#include <linux/bio.h>
+#include <linux/blkdev.h>
+#include <linux/highmem.h>
+#include <linux/mm.h>
+#include <linux/kernel_stat.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>	/* for max_pfn/max_low_pfn */
+#include <linux/completion.h>
+#include <linux/slab.h>
+#include <linux/swap.h>
+#include <linux/writeback.h>
+#include <linux/blkdev.h>
+
+/*
+ * for max sense size
+ */
+#include <scsi/scsi_cmnd.h>
+
+static void blk_unplug_work(void *data);
+static void blk_unplug_timeout(unsigned long data);
+static void drive_stat_acct(struct request *rq, int nr_sectors, int new_io);
+
+/*
+ * For the allocated request tables
+ */
+static kmem_cache_t *request_cachep;
+
+/*
+ * For queue allocation
+ */
+static kmem_cache_t *requestq_cachep;
+
+/*
+ * For io context allocations
+ */
+static kmem_cache_t *iocontext_cachep;
+
+static wait_queue_head_t congestion_wqh[2] = {
+		__WAIT_QUEUE_HEAD_INITIALIZER(congestion_wqh[0]),
+		__WAIT_QUEUE_HEAD_INITIALIZER(congestion_wqh[1])
+	};
+
+/*
+ * Controlling structure to kblockd
+ */
+static struct workqueue_struct *kblockd_workqueue; 
+
+unsigned long blk_max_low_pfn, blk_max_pfn;
+
+EXPORT_SYMBOL(blk_max_low_pfn);
+EXPORT_SYMBOL(blk_max_pfn);
+
+/* Amount of time in which a process may batch requests */
+#define BLK_BATCH_TIME	(HZ/50UL)
+
+/* Number of requests a "batching" process may submit */
+#define BLK_BATCH_REQ	32
+
+/*
+ * Return the threshold (number of used requests) at which the queue is
+ * considered to be congested.  It include a little hysteresis to keep the
+ * context switch rate down.
+ */
+static inline int queue_congestion_on_threshold(struct request_queue *q)
+{
+	return q->nr_congestion_on;
+}
+
+/*
+ * The threshold at which a queue is considered to be uncongested
+ */
+static inline int queue_congestion_off_threshold(struct request_queue *q)
+{
+	return q->nr_congestion_off;
+}
+
+static void blk_queue_congestion_threshold(struct request_queue *q)
+{
+	int nr;
+
+	nr = q->nr_requests - (q->nr_requests / 8) + 1;
+	if (nr > q->nr_requests)
+		nr = q->nr_requests;
+	q->nr_congestion_on = nr;
+
+	nr = q->nr_requests - (q->nr_requests / 8) - (q->nr_requests / 16) - 1;
+	if (nr < 1)
+		nr = 1;
+	q->nr_congestion_off = nr;
+}
+
+/*
+ * A queue has just exitted congestion.  Note this in the global counter of
+ * congested queues, and wake up anyone who was waiting for requests to be
+ * put back.
+ */
+static void clear_queue_congested(request_queue_t *q, int rw)
+{
+	enum bdi_state bit;
+	wait_queue_head_t *wqh = &congestion_wqh[rw];
+
+	bit = (rw == WRITE) ? BDI_write_congested : BDI_read_congested;
+	clear_bit(bit, &q->backing_dev_info.state);
+	smp_mb__after_clear_bit();
+	if (waitqueue_active(wqh))
+		wake_up(wqh);
+}
+
+/*
+ * A queue has just entered congestion.  Flag that in the queue's VM-visible
+ * state flags and increment the global gounter of congested queues.
+ */
+static void set_queue_congested(request_queue_t *q, int rw)
+{
+	enum bdi_state bit;
+
+	bit = (rw == WRITE) ? BDI_write_congested : BDI_read_congested;
+	set_bit(bit, &q->backing_dev_info.state);
+}
+
+/**
+ * blk_get_backing_dev_info - get the address of a queue's backing_dev_info
+ * @bdev:	device
+ *
+ * Locates the passed device's request queue and returns the address of its
+ * backing_dev_info
+ *
+ * Will return NULL if the request queue cannot be located.
+ */
+struct backing_dev_info *blk_get_backing_dev_info(struct block_device *bdev)
+{
+	struct backing_dev_info *ret = NULL;
+	request_queue_t *q = bdev_get_queue(bdev);
+
+	if (q)
+		ret = &q->backing_dev_info;
+	return ret;
+}
+
+EXPORT_SYMBOL(blk_get_backing_dev_info);
+
+void blk_queue_activity_fn(request_queue_t *q, activity_fn *fn, void *data)
+{
+	q->activity_fn = fn;
+	q->activity_data = data;
+}
+
+EXPORT_SYMBOL(blk_queue_activity_fn);
+
+/**
+ * blk_queue_prep_rq - set a prepare_request function for queue
+ * @q:		queue
+ * @pfn:	prepare_request function
+ *
+ * It's possible for a queue to register a prepare_request callback which
+ * is invoked before the request is handed to the request_fn. The goal of
+ * the function is to prepare a request for I/O, it can be used to build a
+ * cdb from the request data for instance.
+ *
+ */
+void blk_queue_prep_rq(request_queue_t *q, prep_rq_fn *pfn)
+{
+	q->prep_rq_fn = pfn;
+}
+
+EXPORT_SYMBOL(blk_queue_prep_rq);
+
+/**
+ * blk_queue_merge_bvec - set a merge_bvec function for queue
+ * @q:		queue
+ * @mbfn:	merge_bvec_fn
+ *
+ * Usually queues have static limitations on the max sectors or segments that
+ * we can put in a request. Stacking drivers may have some settings that
+ * are dynamic, and thus we have to query the queue whether it is ok to
+ * add a new bio_vec to a bio at a given offset or not. If the block device
+ * has such limitations, it needs to register a merge_bvec_fn to control
+ * the size of bio's sent to it. Note that a block device *must* allow a
+ * single page to be added to an empty bio. The block device driver may want
+ * to use the bio_split() function to deal with these bio's. By default
+ * no merge_bvec_fn is defined for a queue, and only the fixed limits are
+ * honored.
+ */
+void blk_queue_merge_bvec(request_queue_t *q, merge_bvec_fn *mbfn)
+{
+	q->merge_bvec_fn = mbfn;
+}
+
+EXPORT_SYMBOL(blk_queue_merge_bvec);
+
+/**
+ * blk_queue_make_request - define an alternate make_request function for a device
+ * @q:  the request queue for the device to be affected
+ * @mfn: the alternate make_request function
+ *
+ * Description:
+ *    The normal way for &struct bios to be passed to a device
+ *    driver is for them to be collected into requests on a request
+ *    queue, and then to allow the device driver to select requests
+ *    off that queue when it is ready.  This works well for many block
+ *    devices. However some block devices (typically virtual devices
+ *    such as md or lvm) do not benefit from the processing on the
+ *    request queue, and are served best by having the requests passed
+ *    directly to them.  This can be achieved by providing a function
+ *    to blk_queue_make_request().
+ *
+ * Caveat:
+ *    The driver that does this *must* be able to deal appropriately
+ *    with buffers in "highmemory". This can be accomplished by either calling
+ *    __bio_kmap_atomic() to get a temporary kernel mapping, or by calling
+ *    blk_queue_bounce() to create a buffer in normal memory.
+ **/
+void blk_queue_make_request(request_queue_t * q, make_request_fn * mfn)
+{
+	/*
+	 * set defaults
+	 */
+	q->nr_requests = BLKDEV_MAX_RQ;
+	blk_queue_max_phys_segments(q, MAX_PHYS_SEGMENTS);
+	blk_queue_max_hw_segments(q, MAX_HW_SEGMENTS);
+	q->make_request_fn = mfn;
+	q->backing_dev_info.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
+	q->backing_dev_info.state = 0;
+	q->backing_dev_info.capabilities = BDI_CAP_MAP_COPY;
+	blk_queue_max_sectors(q, MAX_SECTORS);
+	blk_queue_hardsect_size(q, 512);
+	blk_queue_dma_alignment(q, 511);
+	blk_queue_congestion_threshold(q);
+	q->nr_batching = BLK_BATCH_REQ;
+
+	q->unplug_thresh = 4;		/* hmm */
+	q->unplug_delay = (3 * HZ) / 1000;	/* 3 milliseconds */
+	if (q->unplug_delay == 0)
+		q->unplug_delay = 1;
+
+	INIT_WORK(&q->unplug_work, blk_unplug_work, q);
+
+	q->unplug_timer.function = blk_unplug_timeout;
+	q->unplug_timer.data = (unsigned long)q;
+
+	/*
+	 * by default assume old behaviour and bounce for any highmem page
+	 */
+	blk_queue_bounce_limit(q, BLK_BOUNCE_HIGH);
+
+	blk_queue_activity_fn(q, NULL, NULL);
+}
+
+EXPORT_SYMBOL(blk_queue_make_request);
+
+static inline void rq_init(request_queue_t *q, struct request *rq)
+{
+	INIT_LIST_HEAD(&rq->queuelist);
+
+	rq->errors = 0;
+	rq->rq_status = RQ_ACTIVE;
+	rq->bio = rq->biotail = NULL;
+	rq->ioprio = 0;
+	rq->buffer = NULL;
+	rq->ref_count = 1;
+	rq->q = q;
+	rq->waiting = NULL;
+	rq->special = NULL;
+	rq->data_len = 0;
+	rq->data = NULL;
+	rq->nr_phys_segments = 0;
+	rq->sense = NULL;
+	rq->end_io = NULL;
+	rq->end_io_data = NULL;
+}
+
+/**
+ * blk_queue_ordered - does this queue support ordered writes
+ * @q:     the request queue
+ * @flag:  see below
+ *
+ * Description:
+ *   For journalled file systems, doing ordered writes on a commit
+ *   block instead of explicitly doing wait_on_buffer (which is bad
+ *   for performance) can be a big win. Block drivers supporting this
+ *   feature should call this function and indicate so.
+ *
+ **/
+void blk_queue_ordered(request_queue_t *q, int flag)
+{
+	switch (flag) {
+		case QUEUE_ORDERED_NONE:
+			if (q->flush_rq)
+				kmem_cache_free(request_cachep, q->flush_rq);
+			q->flush_rq = NULL;
+			q->ordered = flag;
+			break;
+		case QUEUE_ORDERED_TAG:
+			q->ordered = flag;
+			break;
+		case QUEUE_ORDERED_FLUSH:
+			q->ordered = flag;
+			if (!q->flush_rq)
+				q->flush_rq = kmem_cache_alloc(request_cachep,
+								GFP_KERNEL);
+			break;
+		default:
+			printk("blk_queue_ordered: bad value %d\n", flag);
+			break;
+	}
+}
+
+EXPORT_SYMBOL(blk_queue_ordered);
+
+/**
+ * blk_queue_issue_flush_fn - set function for issuing a flush
+ * @q:     the request queue
+ * @iff:   the function to be called issuing the flush
+ *
+ * Description:
+ *   If a driver supports issuing a flush command, the support is notified
+ *   to the block layer by defining it through this call.
+ *
+ **/
+void blk_queue_issue_flush_fn(request_queue_t *q, issue_flush_fn *iff)
+{
+	q->issue_flush_fn = iff;
+}
+
+EXPORT_SYMBOL(blk_queue_issue_flush_fn);
+
+/*
+ * Cache flushing for ordered writes handling
+ */
+static void blk_pre_flush_end_io(struct request *flush_rq)
+{
+	struct request *rq = flush_rq->end_io_data;
+	request_queue_t *q = rq->q;
+
+	elv_completed_request(q, flush_rq);
+
+	rq->flags |= REQ_BAR_PREFLUSH;
+
+	if (!flush_rq->errors)
+		elv_requeue_request(q, rq);
+	else {
+		q->end_flush_fn(q, flush_rq);
+		clear_bit(QUEUE_FLAG_FLUSH, &q->queue_flags);
+		q->request_fn(q);
+	}
+}
+
+static void blk_post_flush_end_io(struct request *flush_rq)
+{
+	struct request *rq = flush_rq->end_io_data;
+	request_queue_t *q = rq->q;
+
+	elv_completed_request(q, flush_rq);
+
+	rq->flags |= REQ_BAR_POSTFLUSH;
+
+	q->end_flush_fn(q, flush_rq);
+	clear_bit(QUEUE_FLAG_FLUSH, &q->queue_flags);
+	q->request_fn(q);
+}
+
+struct request *blk_start_pre_flush(request_queue_t *q, struct request *rq)
+{
+	struct request *flush_rq = q->flush_rq;
+
+	BUG_ON(!blk_barrier_rq(rq));
+
+	if (test_and_set_bit(QUEUE_FLAG_FLUSH, &q->queue_flags))
+		return NULL;
+
+	rq_init(q, flush_rq);
+	flush_rq->elevator_private = NULL;
+	flush_rq->flags = REQ_BAR_FLUSH;
+	flush_rq->rq_disk = rq->rq_disk;
+	flush_rq->rl = NULL;
+
+	/*
+	 * prepare_flush returns 0 if no flush is needed, just mark both
+	 * pre and post flush as done in that case
+	 */
+	if (!q->prepare_flush_fn(q, flush_rq)) {
+		rq->flags |= REQ_BAR_PREFLUSH | REQ_BAR_POSTFLUSH;
+		clear_bit(QUEUE_FLAG_FLUSH, &q->queue_flags);
+		return rq;
+	}
+
+	/*
+	 * some drivers dequeue requests right away, some only after io
+	 * completion. make sure the request is dequeued.
+	 */
+	if (!list_empty(&rq->queuelist))
+		blkdev_dequeue_request(rq);
+
+	flush_rq->end_io_data = rq;
+	flush_rq->end_io = blk_pre_flush_end_io;
+
+	__elv_add_request(q, flush_rq, ELEVATOR_INSERT_FRONT, 0);
+	return flush_rq;
+}
+
+static void blk_start_post_flush(request_queue_t *q, struct request *rq)
+{
+	struct request *flush_rq = q->flush_rq;
+
+	BUG_ON(!blk_barrier_rq(rq));
+
+	rq_init(q, flush_rq);
+	flush_rq->elevator_private = NULL;
+	flush_rq->flags = REQ_BAR_FLUSH;
+	flush_rq->rq_disk = rq->rq_disk;
+	flush_rq->rl = NULL;
+
+	if (q->prepare_flush_fn(q, flush_rq)) {
+		flush_rq->end_io_data = rq;
+		flush_rq->end_io = blk_post_flush_end_io;
+
+		__elv_add_request(q, flush_rq, ELEVATOR_INSERT_FRONT, 0);
+		q->request_fn(q);
+	}
+}
+
+static inline int blk_check_end_barrier(request_queue_t *q, struct request *rq,
+					int sectors)
+{
+	if (sectors > rq->nr_sectors)
+		sectors = rq->nr_sectors;
+
+	rq->nr_sectors -= sectors;
+	return rq->nr_sectors;
+}
+
+static int __blk_complete_barrier_rq(request_queue_t *q, struct request *rq,
+				     int sectors, int queue_locked)
+{
+	if (q->ordered != QUEUE_ORDERED_FLUSH)
+		return 0;
+	if (!blk_fs_request(rq) || !blk_barrier_rq(rq))
+		return 0;
+	if (blk_barrier_postflush(rq))
+		return 0;
+
+	if (!blk_check_end_barrier(q, rq, sectors)) {
+		unsigned long flags = 0;
+
+		if (!queue_locked)
+			spin_lock_irqsave(q->queue_lock, flags);
+
+		blk_start_post_flush(q, rq);
+
+		if (!queue_locked)
+			spin_unlock_irqrestore(q->queue_lock, flags);
+	}
+
+	return 1;
+}
+
+/**
+ * blk_complete_barrier_rq - complete possible barrier request
+ * @q:  the request queue for the device
+ * @rq:  the request
+ * @sectors:  number of sectors to complete
+ *
+ * Description:
+ *   Used in driver end_io handling to determine whether to postpone
+ *   completion of a barrier request until a post flush has been done. This
+ *   is the unlocked variant, used if the caller doesn't already hold the
+ *   queue lock.
+ **/
+int blk_complete_barrier_rq(request_queue_t *q, struct request *rq, int sectors)
+{
+	return __blk_complete_barrier_rq(q, rq, sectors, 0);
+}
+EXPORT_SYMBOL(blk_complete_barrier_rq);
+
+/**
+ * blk_complete_barrier_rq_locked - complete possible barrier request
+ * @q:  the request queue for the device
+ * @rq:  the request
+ * @sectors:  number of sectors to complete
+ *
+ * Description:
+ *   See blk_complete_barrier_rq(). This variant must be used if the caller
+ *   holds the queue lock.
+ **/
+int blk_complete_barrier_rq_locked(request_queue_t *q, struct request *rq,
+				   int sectors)
+{
+	return __blk_complete_barrier_rq(q, rq, sectors, 1);
+}
+EXPORT_SYMBOL(blk_complete_barrier_rq_locked);
+
+/**
+ * blk_queue_bounce_limit - set bounce buffer limit for queue
+ * @q:  the request queue for the device
+ * @dma_addr:   bus address limit
+ *
+ * Description:
+ *    Different hardware can have different requirements as to what pages
+ *    it can do I/O directly to. A low level driver can call
+ *    blk_queue_bounce_limit to have lower memory pages allocated as bounce
+ *    buffers for doing I/O to pages residing above @page. By default
+ *    the block layer sets this to the highest numbered "low" memory page.
+ **/
+void blk_queue_bounce_limit(request_queue_t *q, u64 dma_addr)
+{
+	unsigned long bounce_pfn = dma_addr >> PAGE_SHIFT;
+
+	/*
+	 * set appropriate bounce gfp mask -- unfortunately we don't have a
+	 * full 4GB zone, so we have to resort to low memory for any bounces.
+	 * ISA has its own < 16MB zone.
+	 */
+	if (bounce_pfn < blk_max_low_pfn) {
+		BUG_ON(dma_addr < BLK_BOUNCE_ISA);
+		init_emergency_isa_pool();
+		q->bounce_gfp = GFP_NOIO | GFP_DMA;
+	} else
+		q->bounce_gfp = GFP_NOIO;
+
+	q->bounce_pfn = bounce_pfn;
+}
+
+EXPORT_SYMBOL(blk_queue_bounce_limit);
+
+/**
+ * blk_queue_max_sectors - set max sectors for a request for this queue
+ * @q:  the request queue for the device
+ * @max_sectors:  max sectors in the usual 512b unit
+ *
+ * Description:
+ *    Enables a low level driver to set an upper limit on the size of
+ *    received requests.
+ **/
+void blk_queue_max_sectors(request_queue_t *q, unsigned short max_sectors)
+{
+	if ((max_sectors << 9) < PAGE_CACHE_SIZE) {
+		max_sectors = 1 << (PAGE_CACHE_SHIFT - 9);
+		printk("%s: set to minimum %d\n", __FUNCTION__, max_sectors);
+	}
+
+	q->max_sectors = q->max_hw_sectors = max_sectors;
+}
+
+EXPORT_SYMBOL(blk_queue_max_sectors);
+
+/**
+ * blk_queue_max_phys_segments - set max phys segments for a request for this queue
+ * @q:  the request queue for the device
+ * @max_segments:  max number of segments
+ *
+ * Description:
+ *    Enables a low level driver to set an upper limit on the number of
+ *    physical data segments in a request.  This would be the largest sized
+ *    scatter list the driver could handle.
+ **/
+void blk_queue_max_phys_segments(request_queue_t *q, unsigned short max_segments)
+{
+	if (!max_segments) {
+		max_segments = 1;
+		printk("%s: set to minimum %d\n", __FUNCTION__, max_segments);
+	}
+
+	q->max_phys_segments = max_segments;
+}
+
+EXPORT_SYMBOL(blk_queue_max_phys_segments);
+
+/**
+ * blk_queue_max_hw_segments - set max hw segments for a request for this queue
+ * @q:  the request queue for the device
+ * @max_segments:  max number of segments
+ *
+ * Description:
+ *    Enables a low level driver to set an upper limit on the number of
+ *    hw data segments in a request.  This would be the largest number of
+ *    address/length pairs the host adapter can actually give as once
+ *    to the device.
+ **/
+void blk_queue_max_hw_segments(request_queue_t *q, unsigned short max_segments)
+{
+	if (!max_segments) {
+		max_segments = 1;
+		printk("%s: set to minimum %d\n", __FUNCTION__, max_segments);
+	}
+
+	q->max_hw_segments = max_segments;
+}
+
+EXPORT_SYMBOL(blk_queue_max_hw_segments);
+
+/**
+ * blk_queue_max_segment_size - set max segment size for blk_rq_map_sg
+ * @q:  the request queue for the device
+ * @max_size:  max size of segment in bytes
+ *
+ * Description:
+ *    Enables a low level driver to set an upper limit on the size of a
+ *    coalesced segment
+ **/
+void blk_queue_max_segment_size(request_queue_t *q, unsigned int max_size)
+{
+	if (max_size < PAGE_CACHE_SIZE) {
+		max_size = PAGE_CACHE_SIZE;
+		printk("%s: set to minimum %d\n", __FUNCTION__, max_size);
+	}
+
+	q->max_segment_size = max_size;
+}
+
+EXPORT_SYMBOL(blk_queue_max_segment_size);
+
+/**
+ * blk_queue_hardsect_size - set hardware sector size for the queue
+ * @q:  the request queue for the device
+ * @size:  the hardware sector size, in bytes
+ *
+ * Description:
+ *   This should typically be set to the lowest possible sector size
+ *   that the hardware can operate on (possible without reverting to
+ *   even internal read-modify-write operations). Usually the default
+ *   of 512 covers most hardware.
+ **/
+void blk_queue_hardsect_size(request_queue_t *q, unsigned short size)
+{
+	q->hardsect_size = size;
+}
+
+EXPORT_SYMBOL(blk_queue_hardsect_size);
+
+/*
+ * Returns the minimum that is _not_ zero, unless both are zero.
+ */
+#define min_not_zero(l, r) (l == 0) ? r : ((r == 0) ? l : min(l, r))
+
+/**
+ * blk_queue_stack_limits - inherit underlying queue limits for stacked drivers
+ * @t:	the stacking driver (top)
+ * @b:  the underlying device (bottom)
+ **/
+void blk_queue_stack_limits(request_queue_t *t, request_queue_t *b)
+{
+	/* zero is "infinity" */
+	t->max_sectors = t->max_hw_sectors =
+		min_not_zero(t->max_sectors,b->max_sectors);
+
+	t->max_phys_segments = min(t->max_phys_segments,b->max_phys_segments);
+	t->max_hw_segments = min(t->max_hw_segments,b->max_hw_segments);
+	t->max_segment_size = min(t->max_segment_size,b->max_segment_size);
+	t->hardsect_size = max(t->hardsect_size,b->hardsect_size);
+}
+
+EXPORT_SYMBOL(blk_queue_stack_limits);
+
+/**
+ * blk_queue_segment_boundary - set boundary rules for segment merging
+ * @q:  the request queue for the device
+ * @mask:  the memory boundary mask
+ **/
+void blk_queue_segment_boundary(request_queue_t *q, unsigned long mask)
+{
+	if (mask < PAGE_CACHE_SIZE - 1) {
+		mask = PAGE_CACHE_SIZE - 1;
+		printk("%s: set to minimum %lx\n", __FUNCTION__, mask);
+	}
+
+	q->seg_boundary_mask = mask;
+}
+
+EXPORT_SYMBOL(blk_queue_segment_boundary);
+
+/**
+ * blk_queue_dma_alignment - set dma length and memory alignment
+ * @q:     the request queue for the device
+ * @mask:  alignment mask
+ *
+ * description:
+ *    set required memory and length aligment for direct dma transactions.
+ *    this is used when buiding direct io requests for the queue.
+ *
+ **/
+void blk_queue_dma_alignment(request_queue_t *q, int mask)
+{
+	q->dma_alignment = mask;
+}
+
+EXPORT_SYMBOL(blk_queue_dma_alignment);
+
+/**
+ * blk_queue_find_tag - find a request by its tag and queue
+ * @q:	 The request queue for the device
+ * @tag: The tag of the request
+ *
+ * Notes:
+ *    Should be used when a device returns a tag and you want to match
+ *    it with a request.
+ *
+ *    no locks need be held.
+ **/
+struct request *blk_queue_find_tag(request_queue_t *q, int tag)
+{
+	struct blk_queue_tag *bqt = q->queue_tags;
+
+	if (unlikely(bqt == NULL || tag >= bqt->real_max_depth))
+		return NULL;
+
+	return bqt->tag_index[tag];
+}
+
+EXPORT_SYMBOL(blk_queue_find_tag);
+
+/**
+ * __blk_queue_free_tags - release tag maintenance info
+ * @q:  the request queue for the device
+ *
+ *  Notes:
+ *    blk_cleanup_queue() will take care of calling this function, if tagging
+ *    has been used. So there's no need to call this directly.
+ **/
+static void __blk_queue_free_tags(request_queue_t *q)
+{
+	struct blk_queue_tag *bqt = q->queue_tags;
+
+	if (!bqt)
+		return;
+
+	if (atomic_dec_and_test(&bqt->refcnt)) {
+		BUG_ON(bqt->busy);
+		BUG_ON(!list_empty(&bqt->busy_list));
+
+		kfree(bqt->tag_index);
+		bqt->tag_index = NULL;
+
+		kfree(bqt->tag_map);
+		bqt->tag_map = NULL;
+
+		kfree(bqt);
+	}
+
+	q->queue_tags = NULL;
+	q->queue_flags &= ~(1 << QUEUE_FLAG_QUEUED);
+}
+
+/**
+ * blk_queue_free_tags - release tag maintenance info
+ * @q:  the request queue for the device
+ *
+ *  Notes:
+ *	This is used to disabled tagged queuing to a device, yet leave
+ *	queue in function.
+ **/
+void blk_queue_free_tags(request_queue_t *q)
+{
+	clear_bit(QUEUE_FLAG_QUEUED, &q->queue_flags);
+}
+
+EXPORT_SYMBOL(blk_queue_free_tags);
+
+static int
+init_tag_map(request_queue_t *q, struct blk_queue_tag *tags, int depth)
+{
+	struct request **tag_index;
+	unsigned long *tag_map;
+	int nr_ulongs;
+
+	if (depth > q->nr_requests * 2) {
+		depth = q->nr_requests * 2;
+		printk(KERN_ERR "%s: adjusted depth to %d\n",
+				__FUNCTION__, depth);
+	}
+
+	tag_index = kmalloc(depth * sizeof(struct request *), GFP_ATOMIC);
+	if (!tag_index)
+		goto fail;
+
+	nr_ulongs = ALIGN(depth, BITS_PER_LONG) / BITS_PER_LONG;
+	tag_map = kmalloc(nr_ulongs * sizeof(unsigned long), GFP_ATOMIC);
+	if (!tag_map)
+		goto fail;
+
+	memset(tag_index, 0, depth * sizeof(struct request *));
+	memset(tag_map, 0, nr_ulongs * sizeof(unsigned long));
+	tags->real_max_depth = depth;
+	tags->max_depth = depth;
+	tags->tag_index = tag_index;
+	tags->tag_map = tag_map;
+
+	return 0;
+fail:
+	kfree(tag_index);
+	return -ENOMEM;
+}
+
+/**
+ * blk_queue_init_tags - initialize the queue tag info
+ * @q:  the request queue for the device
+ * @depth:  the maximum queue depth supported
+ * @tags: the tag to use
+ **/
+int blk_queue_init_tags(request_queue_t *q, int depth,
+			struct blk_queue_tag *tags)
+{
+	int rc;
+
+	BUG_ON(tags && q->queue_tags && tags != q->queue_tags);
+
+	if (!tags && !q->queue_tags) {
+		tags = kmalloc(sizeof(struct blk_queue_tag), GFP_ATOMIC);
+		if (!tags)
+			goto fail;
+
+		if (init_tag_map(q, tags, depth))
+			goto fail;
+
+		INIT_LIST_HEAD(&tags->busy_list);
+		tags->busy = 0;
+		atomic_set(&tags->refcnt, 1);
+	} else if (q->queue_tags) {
+		if ((rc = blk_queue_resize_tags(q, depth)))
+			return rc;
+		set_bit(QUEUE_FLAG_QUEUED, &q->queue_flags);
+		return 0;
+	} else
+		atomic_inc(&tags->refcnt);
+
+	/*
+	 * assign it, all done
+	 */
+	q->queue_tags = tags;
+	q->queue_flags |= (1 << QUEUE_FLAG_QUEUED);
+	return 0;
+fail:
+	kfree(tags);
+	return -ENOMEM;
+}
+
+EXPORT_SYMBOL(blk_queue_init_tags);
+
+/**
+ * blk_queue_resize_tags - change the queueing depth
+ * @q:  the request queue for the device
+ * @new_depth: the new max command queueing depth
+ *
+ *  Notes:
+ *    Must be called with the queue lock held.
+ **/
+int blk_queue_resize_tags(request_queue_t *q, int new_depth)
+{
+	struct blk_queue_tag *bqt = q->queue_tags;
+	struct request **tag_index;
+	unsigned long *tag_map;
+	int max_depth, nr_ulongs;
+
+	if (!bqt)
+		return -ENXIO;
+
+	/*
+	 * if we already have large enough real_max_depth.  just
+	 * adjust max_depth.  *NOTE* as requests with tag value
+	 * between new_depth and real_max_depth can be in-flight, tag
+	 * map can not be shrunk blindly here.
+	 */
+	if (new_depth <= bqt->real_max_depth) {
+		bqt->max_depth = new_depth;
+		return 0;
+	}
+
+	/*
+	 * save the old state info, so we can copy it back
+	 */
+	tag_index = bqt->tag_index;
+	tag_map = bqt->tag_map;
+	max_depth = bqt->real_max_depth;
+
+	if (init_tag_map(q, bqt, new_depth))
+		return -ENOMEM;
+
+	memcpy(bqt->tag_index, tag_index, max_depth * sizeof(struct request *));
+	nr_ulongs = ALIGN(max_depth, BITS_PER_LONG) / BITS_PER_LONG;
+	memcpy(bqt->tag_map, tag_map, nr_ulongs * sizeof(unsigned long));
+
+	kfree(tag_index);
+	kfree(tag_map);
+	return 0;
+}
+
+EXPORT_SYMBOL(blk_queue_resize_tags);
+
+/**
+ * blk_queue_end_tag - end tag operations for a request
+ * @q:  the request queue for the device
+ * @rq: the request that has completed
+ *
+ *  Description:
+ *    Typically called when end_that_request_first() returns 0, meaning
+ *    all transfers have been done for a request. It's important to call
+ *    this function before end_that_request_last(), as that will put the
+ *    request back on the free list thus corrupting the internal tag list.
+ *
+ *  Notes:
+ *   queue lock must be held.
+ **/
+void blk_queue_end_tag(request_queue_t *q, struct request *rq)
+{
+	struct blk_queue_tag *bqt = q->queue_tags;
+	int tag = rq->tag;
+
+	BUG_ON(tag == -1);
+
+	if (unlikely(tag >= bqt->real_max_depth))
+		/*
+		 * This can happen after tag depth has been reduced.
+		 * FIXME: how about a warning or info message here?
+		 */
+		return;
+
+	if (unlikely(!__test_and_clear_bit(tag, bqt->tag_map))) {
+		printk(KERN_ERR "%s: attempt to clear non-busy tag (%d)\n",
+		       __FUNCTION__, tag);
+		return;
+	}
+
+	list_del_init(&rq->queuelist);
+	rq->flags &= ~REQ_QUEUED;
+	rq->tag = -1;
+
+	if (unlikely(bqt->tag_index[tag] == NULL))
+		printk(KERN_ERR "%s: tag %d is missing\n",
+		       __FUNCTION__, tag);
+
+	bqt->tag_index[tag] = NULL;
+	bqt->busy--;
+}
+
+EXPORT_SYMBOL(blk_queue_end_tag);
+
+/**
+ * blk_queue_start_tag - find a free tag and assign it
+ * @q:  the request queue for the device
+ * @rq:  the block request that needs tagging
+ *
+ *  Description:
+ *    This can either be used as a stand-alone helper, or possibly be
+ *    assigned as the queue &prep_rq_fn (in which case &struct request
+ *    automagically gets a tag assigned). Note that this function
+ *    assumes that any type of request can be queued! if this is not
+ *    true for your device, you must check the request type before
+ *    calling this function.  The request will also be removed from
+ *    the request queue, so it's the drivers responsibility to readd
+ *    it if it should need to be restarted for some reason.
+ *
+ *  Notes:
+ *   queue lock must be held.
+ **/
+int blk_queue_start_tag(request_queue_t *q, struct request *rq)
+{
+	struct blk_queue_tag *bqt = q->queue_tags;
+	int tag;
+
+	if (unlikely((rq->flags & REQ_QUEUED))) {
+		printk(KERN_ERR 
+		       "%s: request %p for device [%s] already tagged %d",
+		       __FUNCTION__, rq,
+		       rq->rq_disk ? rq->rq_disk->disk_name : "?", rq->tag);
+		BUG();
+	}
+
+	tag = find_first_zero_bit(bqt->tag_map, bqt->max_depth);
+	if (tag >= bqt->max_depth)
+		return 1;
+
+	__set_bit(tag, bqt->tag_map);
+
+	rq->flags |= REQ_QUEUED;
+	rq->tag = tag;
+	bqt->tag_index[tag] = rq;
+	blkdev_dequeue_request(rq);
+	list_add(&rq->queuelist, &bqt->busy_list);
+	bqt->busy++;
+	return 0;
+}
+
+EXPORT_SYMBOL(blk_queue_start_tag);
+
+/**
+ * blk_queue_invalidate_tags - invalidate all pending tags
+ * @q:  the request queue for the device
+ *
+ *  Description:
+ *   Hardware conditions may dictate a need to stop all pending requests.
+ *   In this case, we will safely clear the block side of the tag queue and
+ *   readd all requests to the request queue in the right order.
+ *
+ *  Notes:
+ *   queue lock must be held.
+ **/
+void blk_queue_invalidate_tags(request_queue_t *q)
+{
+	struct blk_queue_tag *bqt = q->queue_tags;
+	struct list_head *tmp, *n;
+	struct request *rq;
+
+	list_for_each_safe(tmp, n, &bqt->busy_list) {
+		rq = list_entry_rq(tmp);
+
+		if (rq->tag == -1) {
+			printk(KERN_ERR
+			       "%s: bad tag found on list\n", __FUNCTION__);
+			list_del_init(&rq->queuelist);
+			rq->flags &= ~REQ_QUEUED;
+		} else
+			blk_queue_end_tag(q, rq);
+
+		rq->flags &= ~REQ_STARTED;
+		__elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 0);
+	}
+}
+
+EXPORT_SYMBOL(blk_queue_invalidate_tags);
+
+static char *rq_flags[] = {
+	"REQ_RW",
+	"REQ_FAILFAST",
+	"REQ_SORTED",
+	"REQ_SOFTBARRIER",
+	"REQ_HARDBARRIER",
+	"REQ_CMD",
+	"REQ_NOMERGE",
+	"REQ_STARTED",
+	"REQ_DONTPREP",
+	"REQ_QUEUED",
+	"REQ_ELVPRIV",
+	"REQ_PC",
+	"REQ_BLOCK_PC",
+	"REQ_SENSE",
+	"REQ_FAILED",
+	"REQ_QUIET",
+	"REQ_SPECIAL",
+	"REQ_DRIVE_CMD",
+	"REQ_DRIVE_TASK",
+	"REQ_DRIVE_TASKFILE",
+	"REQ_PREEMPT",
+	"REQ_PM_SUSPEND",
+	"REQ_PM_RESUME",
+	"REQ_PM_SHUTDOWN",
+};
+
+void blk_dump_rq_flags(struct request *rq, char *msg)
+{
+	int bit;
+
+	printk("%s: dev %s: flags = ", msg,
+		rq->rq_disk ? rq->rq_disk->disk_name : "?");
+	bit = 0;
+	do {
+		if (rq->flags & (1 << bit))
+			printk("%s ", rq_flags[bit]);
+		bit++;
+	} while (bit < __REQ_NR_BITS);
+
+	printk("\nsector %llu, nr/cnr %lu/%u\n", (unsigned long long)rq->sector,
+						       rq->nr_sectors,
+						       rq->current_nr_sectors);
+	printk("bio %p, biotail %p, buffer %p, data %p, len %u\n", rq->bio, rq->biotail, rq->buffer, rq->data, rq->data_len);
+
+	if (rq->flags & (REQ_BLOCK_PC | REQ_PC)) {
+		printk("cdb: ");
+		for (bit = 0; bit < sizeof(rq->cmd); bit++)
+			printk("%02x ", rq->cmd[bit]);
+		printk("\n");
+	}
+}
+
+EXPORT_SYMBOL(blk_dump_rq_flags);
+
+void blk_recount_segments(request_queue_t *q, struct bio *bio)
+{
+	struct bio_vec *bv, *bvprv = NULL;
+	int i, nr_phys_segs, nr_hw_segs, seg_size, hw_seg_size, cluster;
+	int high, highprv = 1;
+
+	if (unlikely(!bio->bi_io_vec))
+		return;
+
+	cluster = q->queue_flags & (1 << QUEUE_FLAG_CLUSTER);
+	hw_seg_size = seg_size = nr_phys_segs = nr_hw_segs = 0;
+	bio_for_each_segment(bv, bio, i) {
+		/*
+		 * the trick here is making sure that a high page is never
+		 * considered part of another segment, since that might
+		 * change with the bounce page.
+		 */
+		high = page_to_pfn(bv->bv_page) >= q->bounce_pfn;
+		if (high || highprv)
+			goto new_hw_segment;
+		if (cluster) {
+			if (seg_size + bv->bv_len > q->max_segment_size)
+				goto new_segment;
+			if (!BIOVEC_PHYS_MERGEABLE(bvprv, bv))
+				goto new_segment;
+			if (!BIOVEC_SEG_BOUNDARY(q, bvprv, bv))
+				goto new_segment;
+			if (BIOVEC_VIRT_OVERSIZE(hw_seg_size + bv->bv_len))
+				goto new_hw_segment;
+
+			seg_size += bv->bv_len;
+			hw_seg_size += bv->bv_len;
+			bvprv = bv;
+			continue;
+		}
+new_segment:
+		if (BIOVEC_VIRT_MERGEABLE(bvprv, bv) &&
+		    !BIOVEC_VIRT_OVERSIZE(hw_seg_size + bv->bv_len)) {
+			hw_seg_size += bv->bv_len;
+		} else {
+new_hw_segment:
+			if (hw_seg_size > bio->bi_hw_front_size)
+				bio->bi_hw_front_size = hw_seg_size;
+			hw_seg_size = BIOVEC_VIRT_START_SIZE(bv) + bv->bv_len;
+			nr_hw_segs++;
+		}
+
+		nr_phys_segs++;
+		bvprv = bv;
+		seg_size = bv->bv_len;
+		highprv = high;
+	}
+	if (hw_seg_size > bio->bi_hw_back_size)
+		bio->bi_hw_back_size = hw_seg_size;
+	if (nr_hw_segs == 1 && hw_seg_size > bio->bi_hw_front_size)
+		bio->bi_hw_front_size = hw_seg_size;
+	bio->bi_phys_segments = nr_phys_segs;
+	bio->bi_hw_segments = nr_hw_segs;
+	bio->bi_flags |= (1 << BIO_SEG_VALID);
+}
+
+
+static int blk_phys_contig_segment(request_queue_t *q, struct bio *bio,
+				   struct bio *nxt)
+{
+	if (!(q->queue_flags & (1 << QUEUE_FLAG_CLUSTER)))
+		return 0;
+
+	if (!BIOVEC_PHYS_MERGEABLE(__BVEC_END(bio), __BVEC_START(nxt)))
+		return 0;
+	if (bio->bi_size + nxt->bi_size > q->max_segment_size)
+		return 0;
+
+	/*
+	 * bio and nxt are contigous in memory, check if the queue allows
+	 * these two to be merged into one
+	 */
+	if (BIO_SEG_BOUNDARY(q, bio, nxt))
+		return 1;
+
+	return 0;
+}
+
+static int blk_hw_contig_segment(request_queue_t *q, struct bio *bio,
+				 struct bio *nxt)
+{
+	if (unlikely(!bio_flagged(bio, BIO_SEG_VALID)))
+		blk_recount_segments(q, bio);
+	if (unlikely(!bio_flagged(nxt, BIO_SEG_VALID)))
+		blk_recount_segments(q, nxt);
+	if (!BIOVEC_VIRT_MERGEABLE(__BVEC_END(bio), __BVEC_START(nxt)) ||
+	    BIOVEC_VIRT_OVERSIZE(bio->bi_hw_front_size + bio->bi_hw_back_size))
+		return 0;
+	if (bio->bi_size + nxt->bi_size > q->max_segment_size)
+		return 0;
+
+	return 1;
+}
+
+/*
+ * map a request to scatterlist, return number of sg entries setup. Caller
+ * must make sure sg can hold rq->nr_phys_segments entries
+ */
+int blk_rq_map_sg(request_queue_t *q, struct request *rq, struct scatterlist *sg)
+{
+	struct bio_vec *bvec, *bvprv;
+	struct bio *bio;
+	int nsegs, i, cluster;
+
+	nsegs = 0;
+	cluster = q->queue_flags & (1 << QUEUE_FLAG_CLUSTER);
+
+	/*
+	 * for each bio in rq
+	 */
+	bvprv = NULL;
+	rq_for_each_bio(bio, rq) {
+		/*
+		 * for each segment in bio
+		 */
+		bio_for_each_segment(bvec, bio, i) {
+			int nbytes = bvec->bv_len;
+
+			if (bvprv && cluster) {
+				if (sg[nsegs - 1].length + nbytes > q->max_segment_size)
+					goto new_segment;
+
+				if (!BIOVEC_PHYS_MERGEABLE(bvprv, bvec))
+					goto new_segment;
+				if (!BIOVEC_SEG_BOUNDARY(q, bvprv, bvec))
+					goto new_segment;
+
+				sg[nsegs - 1].length += nbytes;
+			} else {
+new_segment:
+				memset(&sg[nsegs],0,sizeof(struct scatterlist));
+				sg[nsegs].page = bvec->bv_page;
+				sg[nsegs].length = nbytes;
+				sg[nsegs].offset = bvec->bv_offset;
+
+				nsegs++;
+			}
+			bvprv = bvec;
+		} /* segments in bio */
+	} /* bios in rq */
+
+	return nsegs;
+}
+
+EXPORT_SYMBOL(blk_rq_map_sg);
+
+/*
+ * the standard queue merge functions, can be overridden with device
+ * specific ones if so desired
+ */
+
+static inline int ll_new_mergeable(request_queue_t *q,
+				   struct request *req,
+				   struct bio *bio)
+{
+	int nr_phys_segs = bio_phys_segments(q, bio);
+
+	if (req->nr_phys_segments + nr_phys_segs > q->max_phys_segments) {
+		req->flags |= REQ_NOMERGE;
+		if (req == q->last_merge)
+			q->last_merge = NULL;
+		return 0;
+	}
+
+	/*
+	 * A hw segment is just getting larger, bump just the phys
+	 * counter.
+	 */
+	req->nr_phys_segments += nr_phys_segs;
+	return 1;
+}
+
+static inline int ll_new_hw_segment(request_queue_t *q,
+				    struct request *req,
+				    struct bio *bio)
+{
+	int nr_hw_segs = bio_hw_segments(q, bio);
+	int nr_phys_segs = bio_phys_segments(q, bio);
+
+	if (req->nr_hw_segments + nr_hw_segs > q->max_hw_segments
+	    || req->nr_phys_segments + nr_phys_segs > q->max_phys_segments) {
+		req->flags |= REQ_NOMERGE;
+		if (req == q->last_merge)
+			q->last_merge = NULL;
+		return 0;
+	}
+
+	/*
+	 * This will form the start of a new hw segment.  Bump both
+	 * counters.
+	 */
+	req->nr_hw_segments += nr_hw_segs;
+	req->nr_phys_segments += nr_phys_segs;
+	return 1;
+}
+
+static int ll_back_merge_fn(request_queue_t *q, struct request *req, 
+			    struct bio *bio)
+{
+	int len;
+
+	if (req->nr_sectors + bio_sectors(bio) > q->max_sectors) {
+		req->flags |= REQ_NOMERGE;
+		if (req == q->last_merge)
+			q->last_merge = NULL;
+		return 0;
+	}
+	if (unlikely(!bio_flagged(req->biotail, BIO_SEG_VALID)))
+		blk_recount_segments(q, req->biotail);
+	if (unlikely(!bio_flagged(bio, BIO_SEG_VALID)))
+		blk_recount_segments(q, bio);
+	len = req->biotail->bi_hw_back_size + bio->bi_hw_front_size;
+	if (BIOVEC_VIRT_MERGEABLE(__BVEC_END(req->biotail), __BVEC_START(bio)) &&
+	    !BIOVEC_VIRT_OVERSIZE(len)) {
+		int mergeable =  ll_new_mergeable(q, req, bio);
+
+		if (mergeable) {
+			if (req->nr_hw_segments == 1)
+				req->bio->bi_hw_front_size = len;
+			if (bio->bi_hw_segments == 1)
+				bio->bi_hw_back_size = len;
+		}
+		return mergeable;
+	}
+
+	return ll_new_hw_segment(q, req, bio);
+}
+
+static int ll_front_merge_fn(request_queue_t *q, struct request *req, 
+			     struct bio *bio)
+{
+	int len;
+
+	if (req->nr_sectors + bio_sectors(bio) > q->max_sectors) {
+		req->flags |= REQ_NOMERGE;
+		if (req == q->last_merge)
+			q->last_merge = NULL;
+		return 0;
+	}
+	len = bio->bi_hw_back_size + req->bio->bi_hw_front_size;
+	if (unlikely(!bio_flagged(bio, BIO_SEG_VALID)))
+		blk_recount_segments(q, bio);
+	if (unlikely(!bio_flagged(req->bio, BIO_SEG_VALID)))
+		blk_recount_segments(q, req->bio);
+	if (BIOVEC_VIRT_MERGEABLE(__BVEC_END(bio), __BVEC_START(req->bio)) &&
+	    !BIOVEC_VIRT_OVERSIZE(len)) {
+		int mergeable =  ll_new_mergeable(q, req, bio);
+
+		if (mergeable) {
+			if (bio->bi_hw_segments == 1)
+				bio->bi_hw_front_size = len;
+			if (req->nr_hw_segments == 1)
+				req->biotail->bi_hw_back_size = len;
+		}
+		return mergeable;
+	}
+
+	return ll_new_hw_segment(q, req, bio);
+}
+
+static int ll_merge_requests_fn(request_queue_t *q, struct request *req,
+				struct request *next)
+{
+	int total_phys_segments;
+	int total_hw_segments;
+
+	/*
+	 * First check if the either of the requests are re-queued
+	 * requests.  Can't merge them if they are.
+	 */
+	if (req->special || next->special)
+		return 0;
+
+	/*
+	 * Will it become too large?
+	 */
+	if ((req->nr_sectors + next->nr_sectors) > q->max_sectors)
+		return 0;
+
+	total_phys_segments = req->nr_phys_segments + next->nr_phys_segments;
+	if (blk_phys_contig_segment(q, req->biotail, next->bio))
+		total_phys_segments--;
+
+	if (total_phys_segments > q->max_phys_segments)
+		return 0;
+
+	total_hw_segments = req->nr_hw_segments + next->nr_hw_segments;
+	if (blk_hw_contig_segment(q, req->biotail, next->bio)) {
+		int len = req->biotail->bi_hw_back_size + next->bio->bi_hw_front_size;
+		/*
+		 * propagate the combined length to the end of the requests
+		 */
+		if (req->nr_hw_segments == 1)
+			req->bio->bi_hw_front_size = len;
+		if (next->nr_hw_segments == 1)
+			next->biotail->bi_hw_back_size = len;
+		total_hw_segments--;
+	}
+
+	if (total_hw_segments > q->max_hw_segments)
+		return 0;
+
+	/* Merge is OK... */
+	req->nr_phys_segments = total_phys_segments;
+	req->nr_hw_segments = total_hw_segments;
+	return 1;
+}
+
+/*
+ * "plug" the device if there are no outstanding requests: this will
+ * force the transfer to start only after we have put all the requests
+ * on the list.
+ *
+ * This is called with interrupts off and no requests on the queue and
+ * with the queue lock held.
+ */
+void blk_plug_device(request_queue_t *q)
+{
+	WARN_ON(!irqs_disabled());
+
+	/*
+	 * don't plug a stopped queue, it must be paired with blk_start_queue()
+	 * which will restart the queueing
+	 */
+	if (test_bit(QUEUE_FLAG_STOPPED, &q->queue_flags))
+		return;
+
+	if (!test_and_set_bit(QUEUE_FLAG_PLUGGED, &q->queue_flags))
+		mod_timer(&q->unplug_timer, jiffies + q->unplug_delay);
+}
+
+EXPORT_SYMBOL(blk_plug_device);
+
+/*
+ * remove the queue from the plugged list, if present. called with
+ * queue lock held and interrupts disabled.
+ */
+int blk_remove_plug(request_queue_t *q)
+{
+	WARN_ON(!irqs_disabled());
+
+	if (!test_and_clear_bit(QUEUE_FLAG_PLUGGED, &q->queue_flags))
+		return 0;
+
+	del_timer(&q->unplug_timer);
+	return 1;
+}
+
+EXPORT_SYMBOL(blk_remove_plug);
+
+/*
+ * remove the plug and let it rip..
+ */
+void __generic_unplug_device(request_queue_t *q)
+{
+	if (unlikely(test_bit(QUEUE_FLAG_STOPPED, &q->queue_flags)))
+		return;
+
+	if (!blk_remove_plug(q))
+		return;
+
+	q->request_fn(q);
+}
+EXPORT_SYMBOL(__generic_unplug_device);
+
+/**
+ * generic_unplug_device - fire a request queue
+ * @q:    The &request_queue_t in question
+ *
+ * Description:
+ *   Linux uses plugging to build bigger requests queues before letting
+ *   the device have at them. If a queue is plugged, the I/O scheduler
+ *   is still adding and merging requests on the queue. Once the queue
+ *   gets unplugged, the request_fn defined for the queue is invoked and
+ *   transfers started.
+ **/
+void generic_unplug_device(request_queue_t *q)
+{
+	might_sleep();
+	spin_lock_irq(q->queue_lock);
+	__generic_unplug_device(q);
+	spin_unlock_irq(q->queue_lock);
+}
+EXPORT_SYMBOL(generic_unplug_device);
+
+static void blk_backing_dev_unplug(struct backing_dev_info *bdi,
+				   struct page *page)
+{
+	request_queue_t *q = bdi->unplug_io_data;
+
+	/*
+	 * devices don't necessarily have an ->unplug_fn defined
+	 */
+	if (q->unplug_fn)
+		q->unplug_fn(q);
+}
+
+static void blk_unplug_work(void *data)
+{
+	request_queue_t *q = data;
+
+	q->unplug_fn(q);
+}
+
+static void blk_unplug_timeout(unsigned long data)
+{
+	request_queue_t *q = (request_queue_t *)data;
+
+	kblockd_schedule_work(&q->unplug_work);
+}
+
+/**
+ * blk_start_queue - restart a previously stopped queue
+ * @q:    The &request_queue_t in question
+ *
+ * Description:
+ *   blk_start_queue() will clear the stop flag on the queue, and call
+ *   the request_fn for the queue if it was in a stopped state when
+ *   entered. Also see blk_stop_queue(). Queue lock must be held.
+ **/
+void blk_start_queue(request_queue_t *q)
+{
+	clear_bit(QUEUE_FLAG_STOPPED, &q->queue_flags);
+
+	/*
+	 * one level of recursion is ok and is much faster than kicking
+	 * the unplug handling
+	 */
+	if (!test_and_set_bit(QUEUE_FLAG_REENTER, &q->queue_flags)) {
+		q->request_fn(q);
+		clear_bit(QUEUE_FLAG_REENTER, &q->queue_flags);
+	} else {
+		blk_plug_device(q);
+		kblockd_schedule_work(&q->unplug_work);
+	}
+}
+
+EXPORT_SYMBOL(blk_start_queue);
+
+/**
+ * blk_stop_queue - stop a queue
+ * @q:    The &request_queue_t in question
+ *
+ * Description:
+ *   The Linux block layer assumes that a block driver will consume all
+ *   entries on the request queue when the request_fn strategy is called.
+ *   Often this will not happen, because of hardware limitations (queue
+ *   depth settings). If a device driver gets a 'queue full' response,
+ *   or if it simply chooses not to queue more I/O at one point, it can
+ *   call this function to prevent the request_fn from being called until
+ *   the driver has signalled it's ready to go again. This happens by calling
+ *   blk_start_queue() to restart queue operations. Queue lock must be held.
+ **/
+void blk_stop_queue(request_queue_t *q)
+{
+	blk_remove_plug(q);
+	set_bit(QUEUE_FLAG_STOPPED, &q->queue_flags);
+}
+EXPORT_SYMBOL(blk_stop_queue);
+
+/**
+ * blk_sync_queue - cancel any pending callbacks on a queue
+ * @q: the queue
+ *
+ * Description:
+ *     The block layer may perform asynchronous callback activity
+ *     on a queue, such as calling the unplug function after a timeout.
+ *     A block device may call blk_sync_queue to ensure that any
+ *     such activity is cancelled, thus allowing it to release resources
+ *     the the callbacks might use. The caller must already have made sure
+ *     that its ->make_request_fn will not re-add plugging prior to calling
+ *     this function.
+ *
+ */
+void blk_sync_queue(struct request_queue *q)
+{
+	del_timer_sync(&q->unplug_timer);
+	kblockd_flush();
+}
+EXPORT_SYMBOL(blk_sync_queue);
+
+/**
+ * blk_run_queue - run a single device queue
+ * @q:	The queue to run
+ */
+void blk_run_queue(struct request_queue *q)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(q->queue_lock, flags);
+	blk_remove_plug(q);
+	if (!elv_queue_empty(q))
+		q->request_fn(q);
+	spin_unlock_irqrestore(q->queue_lock, flags);
+}
+EXPORT_SYMBOL(blk_run_queue);
+
+/**
+ * blk_cleanup_queue: - release a &request_queue_t when it is no longer needed
+ * @q:    the request queue to be released
+ *
+ * Description:
+ *     blk_cleanup_queue is the pair to blk_init_queue() or
+ *     blk_queue_make_request().  It should be called when a request queue is
+ *     being released; typically when a block device is being de-registered.
+ *     Currently, its primary task it to free all the &struct request
+ *     structures that were allocated to the queue and the queue itself.
+ *
+ * Caveat:
+ *     Hopefully the low level driver will have finished any
+ *     outstanding requests first...
+ **/
+void blk_cleanup_queue(request_queue_t * q)
+{
+	struct request_list *rl = &q->rq;
+
+	if (!atomic_dec_and_test(&q->refcnt))
+		return;
+
+	if (q->elevator)
+		elevator_exit(q->elevator);
+
+	blk_sync_queue(q);
+
+	if (rl->rq_pool)
+		mempool_destroy(rl->rq_pool);
+
+	if (q->queue_tags)
+		__blk_queue_free_tags(q);
+
+	blk_queue_ordered(q, QUEUE_ORDERED_NONE);
+
+	kmem_cache_free(requestq_cachep, q);
+}
+
+EXPORT_SYMBOL(blk_cleanup_queue);
+
+static int blk_init_free_list(request_queue_t *q)
+{
+	struct request_list *rl = &q->rq;
+
+	rl->count[READ] = rl->count[WRITE] = 0;
+	rl->starved[READ] = rl->starved[WRITE] = 0;
+	rl->elvpriv = 0;
+	init_waitqueue_head(&rl->wait[READ]);
+	init_waitqueue_head(&rl->wait[WRITE]);
+
+	rl->rq_pool = mempool_create_node(BLKDEV_MIN_RQ, mempool_alloc_slab,
+				mempool_free_slab, request_cachep, q->node);
+
+	if (!rl->rq_pool)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int __make_request(request_queue_t *, struct bio *);
+
+request_queue_t *blk_alloc_queue(gfp_t gfp_mask)
+{
+	return blk_alloc_queue_node(gfp_mask, -1);
+}
+EXPORT_SYMBOL(blk_alloc_queue);
+
+request_queue_t *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
+{
+	request_queue_t *q;
+
+	q = kmem_cache_alloc_node(requestq_cachep, gfp_mask, node_id);
+	if (!q)
+		return NULL;
+
+	memset(q, 0, sizeof(*q));
+	init_timer(&q->unplug_timer);
+	atomic_set(&q->refcnt, 1);
+
+	q->backing_dev_info.unplug_io_fn = blk_backing_dev_unplug;
+	q->backing_dev_info.unplug_io_data = q;
+
+	return q;
+}
+EXPORT_SYMBOL(blk_alloc_queue_node);
+
+/**
+ * blk_init_queue  - prepare a request queue for use with a block device
+ * @rfn:  The function to be called to process requests that have been
+ *        placed on the queue.
+ * @lock: Request queue spin lock
+ *
+ * Description:
+ *    If a block device wishes to use the standard request handling procedures,
+ *    which sorts requests and coalesces adjacent requests, then it must
+ *    call blk_init_queue().  The function @rfn will be called when there
+ *    are requests on the queue that need to be processed.  If the device
+ *    supports plugging, then @rfn may not be called immediately when requests
+ *    are available on the queue, but may be called at some time later instead.
+ *    Plugged queues are generally unplugged when a buffer belonging to one
+ *    of the requests on the queue is needed, or due to memory pressure.
+ *
+ *    @rfn is not required, or even expected, to remove all requests off the
+ *    queue, but only as many as it can handle at a time.  If it does leave
+ *    requests on the queue, it is responsible for arranging that the requests
+ *    get dealt with eventually.
+ *
+ *    The queue spin lock must be held while manipulating the requests on the
+ *    request queue.
+ *
+ *    Function returns a pointer to the initialized request queue, or NULL if
+ *    it didn't succeed.
+ *
+ * Note:
+ *    blk_init_queue() must be paired with a blk_cleanup_queue() call
+ *    when the block device is deactivated (such as at module unload).
+ **/
+
+request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)
+{
+	return blk_init_queue_node(rfn, lock, -1);
+}
+EXPORT_SYMBOL(blk_init_queue);
+
+request_queue_t *
+blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id)
+{
+	request_queue_t *q = blk_alloc_queue_node(GFP_KERNEL, node_id);
+
+	if (!q)
+		return NULL;
+
+	q->node = node_id;
+	if (blk_init_free_list(q))
+		goto out_init;
+
+	/*
+	 * if caller didn't supply a lock, they get per-queue locking with
+	 * our embedded lock
+	 */
+	if (!lock) {
+		spin_lock_init(&q->__queue_lock);
+		lock = &q->__queue_lock;
+	}
+
+	q->request_fn		= rfn;
+	q->back_merge_fn       	= ll_back_merge_fn;
+	q->front_merge_fn      	= ll_front_merge_fn;
+	q->merge_requests_fn	= ll_merge_requests_fn;
+	q->prep_rq_fn		= NULL;
+	q->unplug_fn		= generic_unplug_device;
+	q->queue_flags		= (1 << QUEUE_FLAG_CLUSTER);
+	q->queue_lock		= lock;
+
+	blk_queue_segment_boundary(q, 0xffffffff);
+
+	blk_queue_make_request(q, __make_request);
+	blk_queue_max_segment_size(q, MAX_SEGMENT_SIZE);
+
+	blk_queue_max_hw_segments(q, MAX_HW_SEGMENTS);
+	blk_queue_max_phys_segments(q, MAX_PHYS_SEGMENTS);
+
+	/*
+	 * all done
+	 */
+	if (!elevator_init(q, NULL)) {
+		blk_queue_congestion_threshold(q);
+		return q;
+	}
+
+	blk_cleanup_queue(q);
+out_init:
+	kmem_cache_free(requestq_cachep, q);
+	return NULL;
+}
+EXPORT_SYMBOL(blk_init_queue_node);
+
+int blk_get_queue(request_queue_t *q)
+{
+	if (likely(!test_bit(QUEUE_FLAG_DEAD, &q->queue_flags))) {
+		atomic_inc(&q->refcnt);
+		return 0;
+	}
+
+	return 1;
+}
+
+EXPORT_SYMBOL(blk_get_queue);
+
+static inline void blk_free_request(request_queue_t *q, struct request *rq)
+{
+	if (rq->flags & REQ_ELVPRIV)
+		elv_put_request(q, rq);
+	mempool_free(rq, q->rq.rq_pool);
+}
+
+static inline struct request *
+blk_alloc_request(request_queue_t *q, int rw, struct bio *bio,
+		  int priv, gfp_t gfp_mask)
+{
+	struct request *rq = mempool_alloc(q->rq.rq_pool, gfp_mask);
+
+	if (!rq)
+		return NULL;
+
+	/*
+	 * first three bits are identical in rq->flags and bio->bi_rw,
+	 * see bio.h and blkdev.h
+	 */
+	rq->flags = rw;
+
+	if (priv) {
+		if (unlikely(elv_set_request(q, rq, bio, gfp_mask))) {
+			mempool_free(rq, q->rq.rq_pool);
+			return NULL;
+		}
+		rq->flags |= REQ_ELVPRIV;
+	}
+
+	return rq;
+}
+
+/*
+ * ioc_batching returns true if the ioc is a valid batching request and
+ * should be given priority access to a request.
+ */
+static inline int ioc_batching(request_queue_t *q, struct io_context *ioc)
+{
+	if (!ioc)
+		return 0;
+
+	/*
+	 * Make sure the process is able to allocate at least 1 request
+	 * even if the batch times out, otherwise we could theoretically
+	 * lose wakeups.
+	 */
+	return ioc->nr_batch_requests == q->nr_batching ||
+		(ioc->nr_batch_requests > 0
+		&& time_before(jiffies, ioc->last_waited + BLK_BATCH_TIME));
+}
+
+/*
+ * ioc_set_batching sets ioc to be a new "batcher" if it is not one. This
+ * will cause the process to be a "batcher" on all queues in the system. This
+ * is the behaviour we want though - once it gets a wakeup it should be given
+ * a nice run.
+ */
+static void ioc_set_batching(request_queue_t *q, struct io_context *ioc)
+{
+	if (!ioc || ioc_batching(q, ioc))
+		return;
+
+	ioc->nr_batch_requests = q->nr_batching;
+	ioc->last_waited = jiffies;
+}
+
+static void __freed_request(request_queue_t *q, int rw)
+{
+	struct request_list *rl = &q->rq;
+
+	if (rl->count[rw] < queue_congestion_off_threshold(q))
+		clear_queue_congested(q, rw);
+
+	if (rl->count[rw] + 1 <= q->nr_requests) {
+		if (waitqueue_active(&rl->wait[rw]))
+			wake_up(&rl->wait[rw]);
+
+		blk_clear_queue_full(q, rw);
+	}
+}
+
+/*
+ * A request has just been released.  Account for it, update the full and
+ * congestion status, wake up any waiters.   Called under q->queue_lock.
+ */
+static void freed_request(request_queue_t *q, int rw, int priv)
+{
+	struct request_list *rl = &q->rq;
+
+	rl->count[rw]--;
+	if (priv)
+		rl->elvpriv--;
+
+	__freed_request(q, rw);
+
+	if (unlikely(rl->starved[rw ^ 1]))
+		__freed_request(q, rw ^ 1);
+}
+
+#define blkdev_free_rq(list) list_entry((list)->next, struct request, queuelist)
+/*
+ * Get a free request, queue_lock must be held.
+ * Returns NULL on failure, with queue_lock held.
+ * Returns !NULL on success, with queue_lock *not held*.
+ */
+static struct request *get_request(request_queue_t *q, int rw, struct bio *bio,
+				   gfp_t gfp_mask)
+{
+	struct request *rq = NULL;
+	struct request_list *rl = &q->rq;
+	struct io_context *ioc = current_io_context(GFP_ATOMIC);
+	int priv;
+
+	if (rl->count[rw]+1 >= q->nr_requests) {
+		/*
+		 * The queue will fill after this allocation, so set it as
+		 * full, and mark this process as "batching". This process
+		 * will be allowed to complete a batch of requests, others
+		 * will be blocked.
+		 */
+		if (!blk_queue_full(q, rw)) {
+			ioc_set_batching(q, ioc);
+			blk_set_queue_full(q, rw);
+		}
+	}
+
+	switch (elv_may_queue(q, rw, bio)) {
+		case ELV_MQUEUE_NO:
+			goto rq_starved;
+		case ELV_MQUEUE_MAY:
+			break;
+		case ELV_MQUEUE_MUST:
+			goto get_rq;
+	}
+
+	if (blk_queue_full(q, rw) && !ioc_batching(q, ioc)) {
+		/*
+		 * The queue is full and the allocating process is not a
+		 * "batcher", and not exempted by the IO scheduler
+		 */
+		goto out;
+	}
+
+get_rq:
+	/*
+	 * Only allow batching queuers to allocate up to 50% over the defined
+	 * limit of requests, otherwise we could have thousands of requests
+	 * allocated with any setting of ->nr_requests
+	 */
+	if (rl->count[rw] >= (3 * q->nr_requests / 2))
+		goto out;
+
+	rl->count[rw]++;
+	rl->starved[rw] = 0;
+	if (rl->count[rw] >= queue_congestion_on_threshold(q))
+		set_queue_congested(q, rw);
+
+	priv = !test_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags);
+	if (priv)
+		rl->elvpriv++;
+
+	spin_unlock_irq(q->queue_lock);
+
+	rq = blk_alloc_request(q, rw, bio, priv, gfp_mask);
+	if (!rq) {
+		/*
+		 * Allocation failed presumably due to memory. Undo anything
+		 * we might have messed up.
+		 *
+		 * Allocating task should really be put onto the front of the
+		 * wait queue, but this is pretty rare.
+		 */
+		spin_lock_irq(q->queue_lock);
+		freed_request(q, rw, priv);
+
+		/*
+		 * in the very unlikely event that allocation failed and no
+		 * requests for this direction was pending, mark us starved
+		 * so that freeing of a request in the other direction will
+		 * notice us. another possible fix would be to split the
+		 * rq mempool into READ and WRITE
+		 */
+rq_starved:
+		if (unlikely(rl->count[rw] == 0))
+			rl->starved[rw] = 1;
+
+		goto out;
+	}
+
+	if (ioc_batching(q, ioc))
+		ioc->nr_batch_requests--;
+	
+	rq_init(q, rq);
+	rq->rl = rl;
+out:
+	return rq;
+}
+
+/*
+ * No available requests for this queue, unplug the device and wait for some
+ * requests to become available.
+ *
+ * Called with q->queue_lock held, and returns with it unlocked.
+ */
+static struct request *get_request_wait(request_queue_t *q, int rw,
+					struct bio *bio)
+{
+	struct request *rq;
+
+	rq = get_request(q, rw, bio, GFP_NOIO);
+	while (!rq) {
+		DEFINE_WAIT(wait);
+		struct request_list *rl = &q->rq;
+
+		prepare_to_wait_exclusive(&rl->wait[rw], &wait,
+				TASK_UNINTERRUPTIBLE);
+
+		rq = get_request(q, rw, bio, GFP_NOIO);
+
+		if (!rq) {
+			struct io_context *ioc;
+
+			__generic_unplug_device(q);
+			spin_unlock_irq(q->queue_lock);
+			io_schedule();
+
+			/*
+			 * After sleeping, we become a "batching" process and
+			 * will be able to allocate at least one request, and
+			 * up to a big batch of them for a small period time.
+			 * See ioc_batching, ioc_set_batching
+			 */
+			ioc = current_io_context(GFP_NOIO);
+			ioc_set_batching(q, ioc);
+
+			spin_lock_irq(q->queue_lock);
+		}
+		finish_wait(&rl->wait[rw], &wait);
+	}
+
+	return rq;
+}
+
+struct request *blk_get_request(request_queue_t *q, int rw, gfp_t gfp_mask)
+{
+	struct request *rq;
+
+	BUG_ON(rw != READ && rw != WRITE);
+
+	spin_lock_irq(q->queue_lock);
+	if (gfp_mask & __GFP_WAIT) {
+		rq = get_request_wait(q, rw, NULL);
+	} else {
+		rq = get_request(q, rw, NULL, gfp_mask);
+		if (!rq)
+			spin_unlock_irq(q->queue_lock);
+	}
+	/* q->queue_lock is unlocked at this point */
+
+	return rq;
+}
+EXPORT_SYMBOL(blk_get_request);
+
+/**
+ * blk_requeue_request - put a request back on queue
+ * @q:		request queue where request should be inserted
+ * @rq:		request to be inserted
+ *
+ * Description:
+ *    Drivers often keep queueing requests until the hardware cannot accept
+ *    more, when that condition happens we need to put the request back
+ *    on the queue. Must be called with queue lock held.
+ */
+void blk_requeue_request(request_queue_t *q, struct request *rq)
+{
+	if (blk_rq_tagged(rq))
+		blk_queue_end_tag(q, rq);
+
+	elv_requeue_request(q, rq);
+}
+
+EXPORT_SYMBOL(blk_requeue_request);
+
+/**
+ * blk_insert_request - insert a special request in to a request queue
+ * @q:		request queue where request should be inserted
+ * @rq:		request to be inserted
+ * @at_head:	insert request at head or tail of queue
+ * @data:	private data
+ *
+ * Description:
+ *    Many block devices need to execute commands asynchronously, so they don't
+ *    block the whole kernel from preemption during request execution.  This is
+ *    accomplished normally by inserting aritficial requests tagged as
+ *    REQ_SPECIAL in to the corresponding request queue, and letting them be
+ *    scheduled for actual execution by the request queue.
+ *
+ *    We have the option of inserting the head or the tail of the queue.
+ *    Typically we use the tail for new ioctls and so forth.  We use the head
+ *    of the queue for things like a QUEUE_FULL message from a device, or a
+ *    host that is unable to accept a particular command.
+ */
+void blk_insert_request(request_queue_t *q, struct request *rq,
+			int at_head, void *data)
+{
+	int where = at_head ? ELEVATOR_INSERT_FRONT : ELEVATOR_INSERT_BACK;
+	unsigned long flags;
+
+	/*
+	 * tell I/O scheduler that this isn't a regular read/write (ie it
+	 * must not attempt merges on this) and that it acts as a soft
+	 * barrier
+	 */
+	rq->flags |= REQ_SPECIAL | REQ_SOFTBARRIER;
+
+	rq->special = data;
+
+	spin_lock_irqsave(q->queue_lock, flags);
+
+	/*
+	 * If command is tagged, release the tag
+	 */
+	if (blk_rq_tagged(rq))
+		blk_queue_end_tag(q, rq);
+
+	drive_stat_acct(rq, rq->nr_sectors, 1);
+	__elv_add_request(q, rq, where, 0);
+
+	if (blk_queue_plugged(q))
+		__generic_unplug_device(q);
+	else
+		q->request_fn(q);
+	spin_unlock_irqrestore(q->queue_lock, flags);
+}
+
+EXPORT_SYMBOL(blk_insert_request);
+
+/**
+ * blk_rq_map_user - map user data to a request, for REQ_BLOCK_PC usage
+ * @q:		request queue where request should be inserted
+ * @rq:		request structure to fill
+ * @ubuf:	the user buffer
+ * @len:	length of user data
+ *
+ * Description:
+ *    Data will be mapped directly for zero copy io, if possible. Otherwise
+ *    a kernel bounce buffer is used.
+ *
+ *    A matching blk_rq_unmap_user() must be issued at the end of io, while
+ *    still in process context.
+ *
+ *    Note: The mapped bio may need to be bounced through blk_queue_bounce()
+ *    before being submitted to the device, as pages mapped may be out of
+ *    reach. It's the callers responsibility to make sure this happens. The
+ *    original bio must be passed back in to blk_rq_unmap_user() for proper
+ *    unmapping.
+ */
+int blk_rq_map_user(request_queue_t *q, struct request *rq, void __user *ubuf,
+		    unsigned int len)
+{
+	unsigned long uaddr;
+	struct bio *bio;
+	int reading;
+
+	if (len > (q->max_sectors << 9))
+		return -EINVAL;
+	if (!len || !ubuf)
+		return -EINVAL;
+
+	reading = rq_data_dir(rq) == READ;
+
+	/*
+	 * if alignment requirement is satisfied, map in user pages for
+	 * direct dma. else, set up kernel bounce buffers
+	 */
+	uaddr = (unsigned long) ubuf;
+	if (!(uaddr & queue_dma_alignment(q)) && !(len & queue_dma_alignment(q)))
+		bio = bio_map_user(q, NULL, uaddr, len, reading);
+	else
+		bio = bio_copy_user(q, uaddr, len, reading);
+
+	if (!IS_ERR(bio)) {
+		rq->bio = rq->biotail = bio;
+		blk_rq_bio_prep(q, rq, bio);
+
+		rq->buffer = rq->data = NULL;
+		rq->data_len = len;
+		return 0;
+	}
+
+	/*
+	 * bio is the err-ptr
+	 */
+	return PTR_ERR(bio);
+}
+
+EXPORT_SYMBOL(blk_rq_map_user);
+
+/**
+ * blk_rq_map_user_iov - map user data to a request, for REQ_BLOCK_PC usage
+ * @q:		request queue where request should be inserted
+ * @rq:		request to map data to
+ * @iov:	pointer to the iovec
+ * @iov_count:	number of elements in the iovec
+ *
+ * Description:
+ *    Data will be mapped directly for zero copy io, if possible. Otherwise
+ *    a kernel bounce buffer is used.
+ *
+ *    A matching blk_rq_unmap_user() must be issued at the end of io, while
+ *    still in process context.
+ *
+ *    Note: The mapped bio may need to be bounced through blk_queue_bounce()
+ *    before being submitted to the device, as pages mapped may be out of
+ *    reach. It's the callers responsibility to make sure this happens. The
+ *    original bio must be passed back in to blk_rq_unmap_user() for proper
+ *    unmapping.
+ */
+int blk_rq_map_user_iov(request_queue_t *q, struct request *rq,
+			struct sg_iovec *iov, int iov_count)
+{
+	struct bio *bio;
+
+	if (!iov || iov_count <= 0)
+		return -EINVAL;
+
+	/* we don't allow misaligned data like bio_map_user() does.  If the
+	 * user is using sg, they're expected to know the alignment constraints
+	 * and respect them accordingly */
+	bio = bio_map_user_iov(q, NULL, iov, iov_count, rq_data_dir(rq)== READ);
+	if (IS_ERR(bio))
+		return PTR_ERR(bio);
+
+	rq->bio = rq->biotail = bio;
+	blk_rq_bio_prep(q, rq, bio);
+	rq->buffer = rq->data = NULL;
+	rq->data_len = bio->bi_size;
+	return 0;
+}
+
+EXPORT_SYMBOL(blk_rq_map_user_iov);
+
+/**
+ * blk_rq_unmap_user - unmap a request with user data
+ * @bio:	bio to be unmapped
+ * @ulen:	length of user buffer
+ *
+ * Description:
+ *    Unmap a bio previously mapped by blk_rq_map_user().
+ */
+int blk_rq_unmap_user(struct bio *bio, unsigned int ulen)
+{
+	int ret = 0;
+
+	if (bio) {
+		if (bio_flagged(bio, BIO_USER_MAPPED))
+			bio_unmap_user(bio);
+		else
+			ret = bio_uncopy_user(bio);
+	}
+
+	return 0;
+}
+
+EXPORT_SYMBOL(blk_rq_unmap_user);
+
+/**
+ * blk_rq_map_kern - map kernel data to a request, for REQ_BLOCK_PC usage
+ * @q:		request queue where request should be inserted
+ * @rq:		request to fill
+ * @kbuf:	the kernel buffer
+ * @len:	length of user data
+ * @gfp_mask:	memory allocation flags
+ */
+int blk_rq_map_kern(request_queue_t *q, struct request *rq, void *kbuf,
+		    unsigned int len, gfp_t gfp_mask)
+{
+	struct bio *bio;
+
+	if (len > (q->max_sectors << 9))
+		return -EINVAL;
+	if (!len || !kbuf)
+		return -EINVAL;
+
+	bio = bio_map_kern(q, kbuf, len, gfp_mask);
+	if (IS_ERR(bio))
+		return PTR_ERR(bio);
+
+	if (rq_data_dir(rq) == WRITE)
+		bio->bi_rw |= (1 << BIO_RW);
+
+	rq->bio = rq->biotail = bio;
+	blk_rq_bio_prep(q, rq, bio);
+
+	rq->buffer = rq->data = NULL;
+	rq->data_len = len;
+	return 0;
+}
+
+EXPORT_SYMBOL(blk_rq_map_kern);
+
+/**
+ * blk_execute_rq_nowait - insert a request into queue for execution
+ * @q:		queue to insert the request in
+ * @bd_disk:	matching gendisk
+ * @rq:		request to insert
+ * @at_head:    insert request at head or tail of queue
+ * @done:	I/O completion handler
+ *
+ * Description:
+ *    Insert a fully prepared request at the back of the io scheduler queue
+ *    for execution.  Don't wait for completion.
+ */
+void blk_execute_rq_nowait(request_queue_t *q, struct gendisk *bd_disk,
+			   struct request *rq, int at_head,
+			   void (*done)(struct request *))
+{
+	int where = at_head ? ELEVATOR_INSERT_FRONT : ELEVATOR_INSERT_BACK;
+
+	rq->rq_disk = bd_disk;
+	rq->flags |= REQ_NOMERGE;
+	rq->end_io = done;
+	elv_add_request(q, rq, where, 1);
+	generic_unplug_device(q);
+}
+
+/**
+ * blk_execute_rq - insert a request into queue for execution
+ * @q:		queue to insert the request in
+ * @bd_disk:	matching gendisk
+ * @rq:		request to insert
+ * @at_head:    insert request at head or tail of queue
+ *
+ * Description:
+ *    Insert a fully prepared request at the back of the io scheduler queue
+ *    for execution and wait for completion.
+ */
+int blk_execute_rq(request_queue_t *q, struct gendisk *bd_disk,
+		   struct request *rq, int at_head)
+{
+	DECLARE_COMPLETION(wait);
+	char sense[SCSI_SENSE_BUFFERSIZE];
+	int err = 0;
+
+	/*
+	 * we need an extra reference to the request, so we can look at
+	 * it after io completion
+	 */
+	rq->ref_count++;
+
+	if (!rq->sense) {
+		memset(sense, 0, sizeof(sense));
+		rq->sense = sense;
+		rq->sense_len = 0;
+	}
+
+	rq->waiting = &wait;
+	blk_execute_rq_nowait(q, bd_disk, rq, at_head, blk_end_sync_rq);
+	wait_for_completion(&wait);
+	rq->waiting = NULL;
+
+	if (rq->errors)
+		err = -EIO;
+
+	return err;
+}
+
+EXPORT_SYMBOL(blk_execute_rq);
+
+/**
+ * blkdev_issue_flush - queue a flush
+ * @bdev:	blockdev to issue flush for
+ * @error_sector:	error sector
+ *
+ * Description:
+ *    Issue a flush for the block device in question. Caller can supply
+ *    room for storing the error offset in case of a flush error, if they
+ *    wish to.  Caller must run wait_for_completion() on its own.
+ */
+int blkdev_issue_flush(struct block_device *bdev, sector_t *error_sector)
+{
+	request_queue_t *q;
+
+	if (bdev->bd_disk == NULL)
+		return -ENXIO;
+
+	q = bdev_get_queue(bdev);
+	if (!q)
+		return -ENXIO;
+	if (!q->issue_flush_fn)
+		return -EOPNOTSUPP;
+
+	return q->issue_flush_fn(q, bdev->bd_disk, error_sector);
+}
+
+EXPORT_SYMBOL(blkdev_issue_flush);
+
+static void drive_stat_acct(struct request *rq, int nr_sectors, int new_io)
+{
+	int rw = rq_data_dir(rq);
+
+	if (!blk_fs_request(rq) || !rq->rq_disk)
+		return;
+
+	if (!new_io) {
+		__disk_stat_inc(rq->rq_disk, merges[rw]);
+	} else {
+		disk_round_stats(rq->rq_disk);
+		rq->rq_disk->in_flight++;
+	}
+}
+
+/*
+ * add-request adds a request to the linked list.
+ * queue lock is held and interrupts disabled, as we muck with the
+ * request queue list.
+ */
+static inline void add_request(request_queue_t * q, struct request * req)
+{
+	drive_stat_acct(req, req->nr_sectors, 1);
+
+	if (q->activity_fn)
+		q->activity_fn(q->activity_data, rq_data_dir(req));
+
+	/*
+	 * elevator indicated where it wants this request to be
+	 * inserted at elevator_merge time
+	 */
+	__elv_add_request(q, req, ELEVATOR_INSERT_SORT, 0);
+}
+ 
+/*
+ * disk_round_stats()	- Round off the performance stats on a struct
+ * disk_stats.
+ *
+ * The average IO queue length and utilisation statistics are maintained
+ * by observing the current state of the queue length and the amount of
+ * time it has been in this state for.
+ *
+ * Normally, that accounting is done on IO completion, but that can result
+ * in more than a second's worth of IO being accounted for within any one
+ * second, leading to >100% utilisation.  To deal with that, we call this
+ * function to do a round-off before returning the results when reading
+ * /proc/diskstats.  This accounts immediately for all queue usage up to
+ * the current jiffies and restarts the counters again.
+ */
+void disk_round_stats(struct gendisk *disk)
+{
+	unsigned long now = jiffies;
+
+	if (now == disk->stamp)
+		return;
+
+	if (disk->in_flight) {
+		__disk_stat_add(disk, time_in_queue,
+				disk->in_flight * (now - disk->stamp));
+		__disk_stat_add(disk, io_ticks, (now - disk->stamp));
+	}
+	disk->stamp = now;
+}
+
+/*
+ * queue lock must be held
+ */
+static void __blk_put_request(request_queue_t *q, struct request *req)
+{
+	struct request_list *rl = req->rl;
+
+	if (unlikely(!q))
+		return;
+	if (unlikely(--req->ref_count))
+		return;
+
+	elv_completed_request(q, req);
+
+	req->rq_status = RQ_INACTIVE;
+	req->rl = NULL;
+
+	/*
+	 * Request may not have originated from ll_rw_blk. if not,
+	 * it didn't come out of our reserved rq pools
+	 */
+	if (rl) {
+		int rw = rq_data_dir(req);
+		int priv = req->flags & REQ_ELVPRIV;
+
+		BUG_ON(!list_empty(&req->queuelist));
+
+		blk_free_request(q, req);
+		freed_request(q, rw, priv);
+	}
+}
+
+void blk_put_request(struct request *req)
+{
+	unsigned long flags;
+	request_queue_t *q = req->q;
+
+	/*
+	 * Gee, IDE calls in w/ NULL q.  Fix IDE and remove the
+	 * following if (q) test.
+	 */
+	if (q) {
+		spin_lock_irqsave(q->queue_lock, flags);
+		__blk_put_request(q, req);
+		spin_unlock_irqrestore(q->queue_lock, flags);
+	}
+}
+
+EXPORT_SYMBOL(blk_put_request);
+
+/**
+ * blk_end_sync_rq - executes a completion event on a request
+ * @rq: request to complete
+ */
+void blk_end_sync_rq(struct request *rq)
+{
+	struct completion *waiting = rq->waiting;
+
+	rq->waiting = NULL;
+	__blk_put_request(rq->q, rq);
+
+	/*
+	 * complete last, if this is a stack request the process (and thus
+	 * the rq pointer) could be invalid right after this complete()
+	 */
+	complete(waiting);
+}
+EXPORT_SYMBOL(blk_end_sync_rq);
+
+/**
+ * blk_congestion_wait - wait for a queue to become uncongested
+ * @rw: READ or WRITE
+ * @timeout: timeout in jiffies
+ *
+ * Waits for up to @timeout jiffies for a queue (any queue) to exit congestion.
+ * If no queues are congested then just wait for the next request to be
+ * returned.
+ */
+long blk_congestion_wait(int rw, long timeout)
+{
+	long ret;
+	DEFINE_WAIT(wait);
+	wait_queue_head_t *wqh = &congestion_wqh[rw];
+
+	prepare_to_wait(wqh, &wait, TASK_UNINTERRUPTIBLE);
+	ret = io_schedule_timeout(timeout);
+	finish_wait(wqh, &wait);
+	return ret;
+}
+
+EXPORT_SYMBOL(blk_congestion_wait);
+
+/*
+ * Has to be called with the request spinlock acquired
+ */
+static int attempt_merge(request_queue_t *q, struct request *req,
+			  struct request *next)
+{
+	if (!rq_mergeable(req) || !rq_mergeable(next))
+		return 0;
+
+	/*
+	 * not contigious
+	 */
+	if (req->sector + req->nr_sectors != next->sector)
+		return 0;
+
+	if (rq_data_dir(req) != rq_data_dir(next)
+	    || req->rq_disk != next->rq_disk
+	    || next->waiting || next->special)
+		return 0;
+
+	/*
+	 * If we are allowed to merge, then append bio list
+	 * from next to rq and release next. merge_requests_fn
+	 * will have updated segment counts, update sector
+	 * counts here.
+	 */
+	if (!q->merge_requests_fn(q, req, next))
+		return 0;
+
+	/*
+	 * At this point we have either done a back merge
+	 * or front merge. We need the smaller start_time of
+	 * the merged requests to be the current request
+	 * for accounting purposes.
+	 */
+	if (time_after(req->start_time, next->start_time))
+		req->start_time = next->start_time;
+
+	req->biotail->bi_next = next->bio;
+	req->biotail = next->biotail;
+
+	req->nr_sectors = req->hard_nr_sectors += next->hard_nr_sectors;
+
+	elv_merge_requests(q, req, next);
+
+	if (req->rq_disk) {
+		disk_round_stats(req->rq_disk);
+		req->rq_disk->in_flight--;
+	}
+
+	req->ioprio = ioprio_best(req->ioprio, next->ioprio);
+
+	__blk_put_request(q, next);
+	return 1;
+}
+
+static inline int attempt_back_merge(request_queue_t *q, struct request *rq)
+{
+	struct request *next = elv_latter_request(q, rq);
+
+	if (next)
+		return attempt_merge(q, rq, next);
+
+	return 0;
+}
+
+static inline int attempt_front_merge(request_queue_t *q, struct request *rq)
+{
+	struct request *prev = elv_former_request(q, rq);
+
+	if (prev)
+		return attempt_merge(q, prev, rq);
+
+	return 0;
+}
+
+/**
+ * blk_attempt_remerge  - attempt to remerge active head with next request
+ * @q:    The &request_queue_t belonging to the device
+ * @rq:   The head request (usually)
+ *
+ * Description:
+ *    For head-active devices, the queue can easily be unplugged so quickly
+ *    that proper merging is not done on the front request. This may hurt
+ *    performance greatly for some devices. The block layer cannot safely
+ *    do merging on that first request for these queues, but the driver can
+ *    call this function and make it happen any way. Only the driver knows
+ *    when it is safe to do so.
+ **/
+void blk_attempt_remerge(request_queue_t *q, struct request *rq)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(q->queue_lock, flags);
+	attempt_back_merge(q, rq);
+	spin_unlock_irqrestore(q->queue_lock, flags);
+}
+
+EXPORT_SYMBOL(blk_attempt_remerge);
+
+static int __make_request(request_queue_t *q, struct bio *bio)
+{
+	struct request *req;
+	int el_ret, rw, nr_sectors, cur_nr_sectors, barrier, err, sync;
+	unsigned short prio;
+	sector_t sector;
+
+	sector = bio->bi_sector;
+	nr_sectors = bio_sectors(bio);
+	cur_nr_sectors = bio_cur_sectors(bio);
+	prio = bio_prio(bio);
+
+	rw = bio_data_dir(bio);
+	sync = bio_sync(bio);
+
+	/*
+	 * low level driver can indicate that it wants pages above a
+	 * certain limit bounced to low memory (ie for highmem, or even
+	 * ISA dma in theory)
+	 */
+	blk_queue_bounce(q, &bio);
+
+	spin_lock_prefetch(q->queue_lock);
+
+	barrier = bio_barrier(bio);
+	if (unlikely(barrier) && (q->ordered == QUEUE_ORDERED_NONE)) {
+		err = -EOPNOTSUPP;
+		goto end_io;
+	}
+
+	spin_lock_irq(q->queue_lock);
+
+	if (unlikely(barrier) || elv_queue_empty(q))
+		goto get_rq;
+
+	el_ret = elv_merge(q, &req, bio);
+	switch (el_ret) {
+		case ELEVATOR_BACK_MERGE:
+			BUG_ON(!rq_mergeable(req));
+
+			if (!q->back_merge_fn(q, req, bio))
+				break;
+
+			req->biotail->bi_next = bio;
+			req->biotail = bio;
+			req->nr_sectors = req->hard_nr_sectors += nr_sectors;
+			req->ioprio = ioprio_best(req->ioprio, prio);
+			drive_stat_acct(req, nr_sectors, 0);
+			if (!attempt_back_merge(q, req))
+				elv_merged_request(q, req);
+			goto out;
+
+		case ELEVATOR_FRONT_MERGE:
+			BUG_ON(!rq_mergeable(req));
+
+			if (!q->front_merge_fn(q, req, bio))
+				break;
+
+			bio->bi_next = req->bio;
+			req->bio = bio;
+
+			/*
+			 * may not be valid. if the low level driver said
+			 * it didn't need a bounce buffer then it better
+			 * not touch req->buffer either...
+			 */
+			req->buffer = bio_data(bio);
+			req->current_nr_sectors = cur_nr_sectors;
+			req->hard_cur_sectors = cur_nr_sectors;
+			req->sector = req->hard_sector = sector;
+			req->nr_sectors = req->hard_nr_sectors += nr_sectors;
+			req->ioprio = ioprio_best(req->ioprio, prio);
+			drive_stat_acct(req, nr_sectors, 0);
+			if (!attempt_front_merge(q, req))
+				elv_merged_request(q, req);
+			goto out;
+
+		/* ELV_NO_MERGE: elevator says don't/can't merge. */
+		default:
+			;
+	}
+
+get_rq:
+	/*
+	 * Grab a free request. This is might sleep but can not fail.
+	 * Returns with the queue unlocked.
+	 */
+	req = get_request_wait(q, rw, bio);
+
+	/*
+	 * After dropping the lock and possibly sleeping here, our request
+	 * may now be mergeable after it had proven unmergeable (above).
+	 * We don't worry about that case for efficiency. It won't happen
+	 * often, and the elevators are able to handle it.
+	 */
+
+	req->flags |= REQ_CMD;
+
+	/*
+	 * inherit FAILFAST from bio (for read-ahead, and explicit FAILFAST)
+	 */
+	if (bio_rw_ahead(bio) || bio_failfast(bio))
+		req->flags |= REQ_FAILFAST;
+
+	/*
+	 * REQ_BARRIER implies no merging, but lets make it explicit
+	 */
+	if (unlikely(barrier))
+		req->flags |= (REQ_HARDBARRIER | REQ_NOMERGE);
+
+	req->errors = 0;
+	req->hard_sector = req->sector = sector;
+	req->hard_nr_sectors = req->nr_sectors = nr_sectors;
+	req->current_nr_sectors = req->hard_cur_sectors = cur_nr_sectors;
+	req->nr_phys_segments = bio_phys_segments(q, bio);
+	req->nr_hw_segments = bio_hw_segments(q, bio);
+	req->buffer = bio_data(bio);	/* see ->buffer comment above */
+	req->waiting = NULL;
+	req->bio = req->biotail = bio;
+	req->ioprio = prio;
+	req->rq_disk = bio->bi_bdev->bd_disk;
+	req->start_time = jiffies;
+
+	spin_lock_irq(q->queue_lock);
+	if (elv_queue_empty(q))
+		blk_plug_device(q);
+	add_request(q, req);
+out:
+	if (sync)
+		__generic_unplug_device(q);
+
+	spin_unlock_irq(q->queue_lock);
+	return 0;
+
+end_io:
+	bio_endio(bio, nr_sectors << 9, err);
+	return 0;
+}
+
+/*
+ * If bio->bi_dev is a partition, remap the location
+ */
+static inline void blk_partition_remap(struct bio *bio)
+{
+	struct block_device *bdev = bio->bi_bdev;
+
+	if (bdev != bdev->bd_contains) {
+		struct hd_struct *p = bdev->bd_part;
+		const int rw = bio_data_dir(bio);
+
+		p->sectors[rw] += bio_sectors(bio);
+		p->ios[rw]++;
+
+		bio->bi_sector += p->start_sect;
+		bio->bi_bdev = bdev->bd_contains;
+	}
+}
+
+static void handle_bad_sector(struct bio *bio)
+{
+	char b[BDEVNAME_SIZE];
+
+	printk(KERN_INFO "attempt to access beyond end of device\n");
+	printk(KERN_INFO "%s: rw=%ld, want=%Lu, limit=%Lu\n",
+			bdevname(bio->bi_bdev, b),
+			bio->bi_rw,
+			(unsigned long long)bio->bi_sector + bio_sectors(bio),
+			(long long)(bio->bi_bdev->bd_inode->i_size >> 9));
+
+	set_bit(BIO_EOF, &bio->bi_flags);
+}
+
+/**
+ * generic_make_request: hand a buffer to its device driver for I/O
+ * @bio:  The bio describing the location in memory and on the device.
+ *
+ * generic_make_request() is used to make I/O requests of block
+ * devices. It is passed a &struct bio, which describes the I/O that needs
+ * to be done.
+ *
+ * generic_make_request() does not return any status.  The
+ * success/failure status of the request, along with notification of
+ * completion, is delivered asynchronously through the bio->bi_end_io
+ * function described (one day) else where.
+ *
+ * The caller of generic_make_request must make sure that bi_io_vec
+ * are set to describe the memory buffer, and that bi_dev and bi_sector are
+ * set to describe the device address, and the
+ * bi_end_io and optionally bi_private are set to describe how
+ * completion notification should be signaled.
+ *
+ * generic_make_request and the drivers it calls may use bi_next if this
+ * bio happens to be merged with someone else, and may change bi_dev and
+ * bi_sector for remaps as it sees fit.  So the values of these fields
+ * should NOT be depended on after the call to generic_make_request.
+ */
+static inline void __generic_make_request(struct bio *bio)
+{
+	request_queue_t *q;
+	sector_t maxsector;
+	int ret, nr_sectors = bio_sectors(bio);
+
+	might_sleep();
+	/* Test device or partition size, when known. */
+	maxsector = bio->bi_bdev->bd_inode->i_size >> 9;
+	if (maxsector) {
+		sector_t sector = bio->bi_sector;
+
+		if (maxsector < nr_sectors || maxsector - nr_sectors < sector) {
+			/*
+			 * This may well happen - the kernel calls bread()
+			 * without checking the size of the device, e.g., when
+			 * mounting a device.
+			 */
+			handle_bad_sector(bio);
+			goto end_io;
+		}
+	}
+
+	/*
+	 * Resolve the mapping until finished. (drivers are
+	 * still free to implement/resolve their own stacking
+	 * by explicitly returning 0)
+	 *
+	 * NOTE: we don't repeat the blk_size check for each new device.
+	 * Stacking drivers are expected to know what they are doing.
+	 */
+	do {
+		char b[BDEVNAME_SIZE];
+
+		q = bdev_get_queue(bio->bi_bdev);
+		if (!q) {
+			printk(KERN_ERR
+			       "generic_make_request: Trying to access "
+				"nonexistent block-device %s (%Lu)\n",
+				bdevname(bio->bi_bdev, b),
+				(long long) bio->bi_sector);
+end_io:
+			bio_endio(bio, bio->bi_size, -EIO);
+			break;
+		}
+
+		if (unlikely(bio_sectors(bio) > q->max_hw_sectors)) {
+			printk("bio too big device %s (%u > %u)\n", 
+				bdevname(bio->bi_bdev, b),
+				bio_sectors(bio),
+				q->max_hw_sectors);
+			goto end_io;
+		}
+
+		if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags)))
+			goto end_io;
+
+		/*
+		 * If this device has partitions, remap block n
+		 * of partition p to block n+start(p) of the disk.
+		 */
+		blk_partition_remap(bio);
+
+		ret = q->make_request_fn(q, bio);
+	} while (ret);
+}
+
+/*
+ * We only want one ->make_request_fn to be active at a time,
+ * else stack usage with stacked devices could be a problem.
+ * So use current->bio_{list,tail} to keep a list of requests
+ * submited by a make_request_fn function.
+ * current->bio_tail is also used as a flag to say if
+ * generic_make_request is currently active in this task or not.
+ * If it is NULL, then no make_request is active.  If it is non-NULL,
+ * then a make_request is active, and new requests should be added
+ * at the tail
+ */
+void generic_make_request(struct bio *bio)
+{
+	if (current->bio_tail) {
+		/* make_request is active */
+		*(current->bio_tail) = bio;
+		bio->bi_next = NULL;
+		current->bio_tail = &bio->bi_next;
+		return;
+	}
+	/* following loop may be a bit non-obvious, and so deserves some
+	 * explanation.
+	 * Before entering the loop, bio->bi_next is NULL (as all callers
+	 * ensure that) so we have a list with a single bio.
+	 * We pretend that we have just taken it off a longer list, so
+	 * we assign bio_list to the next (which is NULL) and bio_tail
+	 * to &bio_list, thus initialising the bio_list of new bios to be
+	 * added.  __generic_make_request may indeed add some more bios
+	 * through a recursive call to generic_make_request.  If it
+	 * did, we find a non-NULL value in bio_list and re-enter the loop
+	 * from the top.  In this case we really did just take the bio
+	 * of the top of the list (no pretending) and so fixup bio_list and
+	 * bio_tail or bi_next, and call into __generic_make_request again.
+	 *
+	 * The loop was structured like this to make only one call to
+	 * __generic_make_request (which is important as it is large and
+	 * inlined) and to keep the structure simple.
+	 */
+	BUG_ON(bio->bi_next);
+	do {
+		current->bio_list = bio->bi_next;
+		if (bio->bi_next == NULL)
+			current->bio_tail = &current->bio_list;
+		else
+			bio->bi_next = NULL;
+		__generic_make_request(bio);
+		bio = current->bio_list;
+	} while (bio);
+	current->bio_tail = NULL; /* deactivate */
+}
+
+EXPORT_SYMBOL(generic_make_request);
+
+/**
+ * submit_bio: submit a bio to the block device layer for I/O
+ * @rw: whether to %READ or %WRITE, or maybe to %READA (read ahead)
+ * @bio: The &struct bio which describes the I/O
+ *
+ * submit_bio() is very similar in purpose to generic_make_request(), and
+ * uses that function to do most of the work. Both are fairly rough
+ * interfaces, @bio must be presetup and ready for I/O.
+ *
+ */
+void submit_bio(int rw, struct bio *bio)
+{
+	int count = bio_sectors(bio);
+
+	BIO_BUG_ON(!bio->bi_size);
+	BIO_BUG_ON(!bio->bi_io_vec);
+	bio->bi_rw |= rw;
+	if (rw & WRITE)
+		mod_page_state(pgpgout, count);
+	else
+		mod_page_state(pgpgin, count);
+
+	if (unlikely(block_dump)) {
+		char b[BDEVNAME_SIZE];
+		printk(KERN_DEBUG "%s(%d): %s block %Lu on %s\n",
+			current->comm, current->pid,
+			(rw & WRITE) ? "WRITE" : "READ",
+			(unsigned long long)bio->bi_sector,
+			bdevname(bio->bi_bdev,b));
+	}
+
+	generic_make_request(bio);
+}
+
+EXPORT_SYMBOL(submit_bio);
+
+static void blk_recalc_rq_segments(struct request *rq)
+{
+	struct bio *bio, *prevbio = NULL;
+	int nr_phys_segs, nr_hw_segs;
+	unsigned int phys_size, hw_size;
+	request_queue_t *q = rq->q;
+
+	if (!rq->bio)
+		return;
+
+	phys_size = hw_size = nr_phys_segs = nr_hw_segs = 0;
+	rq_for_each_bio(bio, rq) {
+		/* Force bio hw/phys segs to be recalculated. */
+		bio->bi_flags &= ~(1 << BIO_SEG_VALID);
+
+		nr_phys_segs += bio_phys_segments(q, bio);
+		nr_hw_segs += bio_hw_segments(q, bio);
+		if (prevbio) {
+			int pseg = phys_size + prevbio->bi_size + bio->bi_size;
+			int hseg = hw_size + prevbio->bi_size + bio->bi_size;
+
+			if (blk_phys_contig_segment(q, prevbio, bio) &&
+			    pseg <= q->max_segment_size) {
+				nr_phys_segs--;
+				phys_size += prevbio->bi_size + bio->bi_size;
+			} else
+				phys_size = 0;
+
+			if (blk_hw_contig_segment(q, prevbio, bio) &&
+			    hseg <= q->max_segment_size) {
+				nr_hw_segs--;
+				hw_size += prevbio->bi_size + bio->bi_size;
+			} else
+				hw_size = 0;
+		}
+		prevbio = bio;
+	}
+
+	rq->nr_phys_segments = nr_phys_segs;
+	rq->nr_hw_segments = nr_hw_segs;
+}
+
+static void blk_recalc_rq_sectors(struct request *rq, int nsect)
+{
+	if (blk_fs_request(rq)) {
+		rq->hard_sector += nsect;
+		rq->hard_nr_sectors -= nsect;
+
+		/*
+		 * Move the I/O submission pointers ahead if required.
+		 */
+		if ((rq->nr_sectors >= rq->hard_nr_sectors) &&
+		    (rq->sector <= rq->hard_sector)) {
+			rq->sector = rq->hard_sector;
+			rq->nr_sectors = rq->hard_nr_sectors;
+			rq->hard_cur_sectors = bio_cur_sectors(rq->bio);
+			rq->current_nr_sectors = rq->hard_cur_sectors;
+			rq->buffer = bio_data(rq->bio);
+		}
+
+		/*
+		 * if total number of sectors is less than the first segment
+		 * size, something has gone terribly wrong
+		 */
+		if (rq->nr_sectors < rq->current_nr_sectors) {
+			printk("blk: request botched\n");
+			rq->nr_sectors = rq->current_nr_sectors;
+		}
+	}
+}
+
+static int __end_that_request_first(struct request *req, int uptodate,
+				    int nr_bytes)
+{
+	int total_bytes, bio_nbytes, error, next_idx = 0;
+	struct bio *bio;
+
+	/*
+	 * extend uptodate bool to allow < 0 value to be direct io error
+	 */
+	error = 0;
+	if (end_io_error(uptodate))
+		error = !uptodate ? -EIO : uptodate;
+
+	/*
+	 * for a REQ_BLOCK_PC request, we want to carry any eventual
+	 * sense key with us all the way through
+	 */
+	if (!blk_pc_request(req))
+		req->errors = 0;
+
+	if (!uptodate) {
+		if (blk_fs_request(req) && !(req->flags & REQ_QUIET))
+			printk("end_request: I/O error, dev %s, sector %llu\n",
+				req->rq_disk ? req->rq_disk->disk_name : "?",
+				(unsigned long long)req->sector);
+	}
+
+	if (blk_fs_request(req) && req->rq_disk) {
+		const int rw = rq_data_dir(req);
+
+		__disk_stat_add(req->rq_disk, sectors[rw], nr_bytes >> 9);
+	}
+
+	total_bytes = bio_nbytes = 0;
+	while ((bio = req->bio) != NULL) {
+		int nbytes;
+
+		if (nr_bytes >= bio->bi_size) {
+			req->bio = bio->bi_next;
+			nbytes = bio->bi_size;
+			bio_endio(bio, nbytes, error);
+			next_idx = 0;
+			bio_nbytes = 0;
+		} else {
+			int idx = bio->bi_idx + next_idx;
+
+			if (unlikely(bio->bi_idx >= bio->bi_vcnt)) {
+				blk_dump_rq_flags(req, "__end_that");
+				printk("%s: bio idx %d >= vcnt %d\n",
+						__FUNCTION__,
+						bio->bi_idx, bio->bi_vcnt);
+				break;
+			}
+
+			nbytes = bio_iovec_idx(bio, idx)->bv_len;
+			BIO_BUG_ON(nbytes > bio->bi_size);
+
+			/*
+			 * not a complete bvec done
+			 */
+			if (unlikely(nbytes > nr_bytes)) {
+				bio_nbytes += nr_bytes;
+				total_bytes += nr_bytes;
+				break;
+			}
+
+			/*
+			 * advance to the next vector
+			 */
+			next_idx++;
+			bio_nbytes += nbytes;
+		}
+
+		total_bytes += nbytes;
+		nr_bytes -= nbytes;
+
+		if ((bio = req->bio)) {
+			/*
+			 * end more in this run, or just return 'not-done'
+			 */
+			if (unlikely(nr_bytes <= 0))
+				break;
+		}
+	}
+
+	/*
+	 * completely done
+	 */
+	if (!req->bio)
+		return 0;
+
+	/*
+	 * if the request wasn't completed, update state
+	 */
+	if (bio_nbytes) {
+		bio_endio(bio, bio_nbytes, error);
+		bio->bi_idx += next_idx;
+		bio_iovec(bio)->bv_offset += nr_bytes;
+		bio_iovec(bio)->bv_len -= nr_bytes;
+	}
+
+	blk_recalc_rq_sectors(req, total_bytes >> 9);
+	blk_recalc_rq_segments(req);
+	return 1;
+}
+
+/**
+ * end_that_request_first - end I/O on a request
+ * @req:      the request being processed
+ * @uptodate: 1 for success, 0 for I/O error, < 0 for specific error
+ * @nr_sectors: number of sectors to end I/O on
+ *
+ * Description:
+ *     Ends I/O on a number of sectors attached to @req, and sets it up
+ *     for the next range of segments (if any) in the cluster.
+ *
+ * Return:
+ *     0 - we are done with this request, call end_that_request_last()
+ *     1 - still buffers pending for this request
+ **/
+int end_that_request_first(struct request *req, int uptodate, int nr_sectors)
+{
+	return __end_that_request_first(req, uptodate, nr_sectors << 9);
+}
+
+EXPORT_SYMBOL(end_that_request_first);
+
+/**
+ * end_that_request_chunk - end I/O on a request
+ * @req:      the request being processed
+ * @uptodate: 1 for success, 0 for I/O error, < 0 for specific error
+ * @nr_bytes: number of bytes to complete
+ *
+ * Description:
+ *     Ends I/O on a number of bytes attached to @req, and sets it up
+ *     for the next range of segments (if any). Like end_that_request_first(),
+ *     but deals with bytes instead of sectors.
+ *
+ * Return:
+ *     0 - we are done with this request, call end_that_request_last()
+ *     1 - still buffers pending for this request
+ **/
+int end_that_request_chunk(struct request *req, int uptodate, int nr_bytes)
+{
+	return __end_that_request_first(req, uptodate, nr_bytes);
+}
+
+EXPORT_SYMBOL(end_that_request_chunk);
+
+/*
+ * queue lock must be held
+ */
+void end_that_request_last(struct request *req)
+{
+	struct gendisk *disk = req->rq_disk;
+
+	if (unlikely(laptop_mode) && blk_fs_request(req))
+		laptop_io_completion();
+
+	if (disk && blk_fs_request(req)) {
+		unsigned long duration = jiffies - req->start_time;
+		const int rw = rq_data_dir(req);
+
+		__disk_stat_inc(disk, ios[rw]);
+		__disk_stat_add(disk, ticks[rw], duration);
+		disk_round_stats(disk);
+		disk->in_flight--;
+	}
+	if (req->end_io)
+		req->end_io(req);
+	else
+		__blk_put_request(req->q, req);
+}
+
+EXPORT_SYMBOL(end_that_request_last);
+
+void end_request(struct request *req, int uptodate)
+{
+	if (!end_that_request_first(req, uptodate, req->hard_cur_sectors)) {
+		add_disk_randomness(req->rq_disk);
+		blkdev_dequeue_request(req);
+		end_that_request_last(req);
+	}
+}
+
+EXPORT_SYMBOL(end_request);
+
+void blk_rq_bio_prep(request_queue_t *q, struct request *rq, struct bio *bio)
+{
+	/* first three bits are identical in rq->flags and bio->bi_rw */
+	rq->flags |= (bio->bi_rw & 7);
+
+	rq->nr_phys_segments = bio_phys_segments(q, bio);
+	rq->nr_hw_segments = bio_hw_segments(q, bio);
+	rq->current_nr_sectors = bio_cur_sectors(bio);
+	rq->hard_cur_sectors = rq->current_nr_sectors;
+	rq->hard_nr_sectors = rq->nr_sectors = bio_sectors(bio);
+	rq->buffer = bio_data(bio);
+
+	rq->bio = rq->biotail = bio;
+}
+
+EXPORT_SYMBOL(blk_rq_bio_prep);
+
+int kblockd_schedule_work(struct work_struct *work)
+{
+	return queue_work(kblockd_workqueue, work);
+}
+
+EXPORT_SYMBOL(kblockd_schedule_work);
+
+void kblockd_flush(void)
+{
+	flush_workqueue(kblockd_workqueue);
+}
+EXPORT_SYMBOL(kblockd_flush);
+
+int __init blk_dev_init(void)
+{
+	kblockd_workqueue = create_workqueue("kblockd");
+	if (!kblockd_workqueue)
+		panic("Failed to create kblockd\n");
+
+	request_cachep = kmem_cache_create("blkdev_requests",
+			sizeof(struct request), 0, SLAB_PANIC, NULL, NULL);
+
+	requestq_cachep = kmem_cache_create("blkdev_queue",
+			sizeof(request_queue_t), 0, SLAB_PANIC, NULL, NULL);
+
+	iocontext_cachep = kmem_cache_create("blkdev_ioc",
+			sizeof(struct io_context), 0, SLAB_PANIC, NULL, NULL);
+
+	blk_max_low_pfn = max_low_pfn;
+	blk_max_pfn = max_pfn;
+
+	return 0;
+}
+
+/*
+ * IO Context helper functions
+ */
+void put_io_context(struct io_context *ioc)
+{
+	if (ioc == NULL)
+		return;
+
+	BUG_ON(atomic_read(&ioc->refcount) == 0);
+
+	if (atomic_dec_and_test(&ioc->refcount)) {
+		if (ioc->aic && ioc->aic->dtor)
+			ioc->aic->dtor(ioc->aic);
+		if (ioc->cic && ioc->cic->dtor)
+			ioc->cic->dtor(ioc->cic);
+
+		kmem_cache_free(iocontext_cachep, ioc);
+	}
+}
+EXPORT_SYMBOL(put_io_context);
+
+/* Called by the exitting task */
+void exit_io_context(void)
+{
+	unsigned long flags;
+	struct io_context *ioc;
+
+	local_irq_save(flags);
+	task_lock(current);
+	ioc = current->io_context;
+	current->io_context = NULL;
+	ioc->task = NULL;
+	task_unlock(current);
+	local_irq_restore(flags);
+
+	if (ioc->aic && ioc->aic->exit)
+		ioc->aic->exit(ioc->aic);
+	if (ioc->cic && ioc->cic->exit)
+		ioc->cic->exit(ioc->cic);
+
+	put_io_context(ioc);
+}
+
+/*
+ * If the current task has no IO context then create one and initialise it.
+ * Otherwise, return its existing IO context.
+ *
+ * This returned IO context doesn't have a specifically elevated refcount,
+ * but since the current task itself holds a reference, the context can be
+ * used in general code, so long as it stays within `current` context.
+ */
+struct io_context *current_io_context(gfp_t gfp_flags)
+{
+	struct task_struct *tsk = current;
+	struct io_context *ret;
+
+	ret = tsk->io_context;
+	if (likely(ret))
+		return ret;
+
+	ret = kmem_cache_alloc(iocontext_cachep, gfp_flags);
+	if (ret) {
+		atomic_set(&ret->refcount, 1);
+		ret->task = current;
+		ret->set_ioprio = NULL;
+		ret->last_waited = jiffies; /* doesn't matter... */
+		ret->nr_batch_requests = 0; /* because this is 0 */
+		ret->aic = NULL;
+		ret->cic = NULL;
+		tsk->io_context = ret;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(current_io_context);
+
+/*
+ * If the current task has no IO context then create one and initialise it.
+ * If it does have a context, take a ref on it.
+ *
+ * This is always called in the context of the task which submitted the I/O.
+ */
+struct io_context *get_io_context(gfp_t gfp_flags)
+{
+	struct io_context *ret;
+	ret = current_io_context(gfp_flags);
+	if (likely(ret))
+		atomic_inc(&ret->refcount);
+	return ret;
+}
+EXPORT_SYMBOL(get_io_context);
+
+void copy_io_context(struct io_context **pdst, struct io_context **psrc)
+{
+	struct io_context *src = *psrc;
+	struct io_context *dst = *pdst;
+
+	if (src) {
+		BUG_ON(atomic_read(&src->refcount) == 0);
+		atomic_inc(&src->refcount);
+		put_io_context(dst);
+		*pdst = src;
+	}
+}
+EXPORT_SYMBOL(copy_io_context);
+
+void swap_io_context(struct io_context **ioc1, struct io_context **ioc2)
+{
+	struct io_context *temp;
+	temp = *ioc1;
+	*ioc1 = *ioc2;
+	*ioc2 = temp;
+}
+EXPORT_SYMBOL(swap_io_context);
+
+/*
+ * sysfs parts below
+ */
+struct queue_sysfs_entry {
+	struct attribute attr;
+	ssize_t (*show)(struct request_queue *, char *);
+	ssize_t (*store)(struct request_queue *, const char *, size_t);
+};
+
+static ssize_t
+queue_var_show(unsigned int var, char *page)
+{
+	return sprintf(page, "%d\n", var);
+}
+
+static ssize_t
+queue_var_store(unsigned long *var, const char *page, size_t count)
+{
+	char *p = (char *) page;
+
+	*var = simple_strtoul(p, &p, 10);
+	return count;
+}
+
+static ssize_t queue_requests_show(struct request_queue *q, char *page)
+{
+	return queue_var_show(q->nr_requests, (page));
+}
+
+static ssize_t
+queue_requests_store(struct request_queue *q, const char *page, size_t count)
+{
+	struct request_list *rl = &q->rq;
+
+	int ret = queue_var_store(&q->nr_requests, page, count);
+	if (q->nr_requests < BLKDEV_MIN_RQ)
+		q->nr_requests = BLKDEV_MIN_RQ;
+	blk_queue_congestion_threshold(q);
+
+	if (rl->count[READ] >= queue_congestion_on_threshold(q))
+		set_queue_congested(q, READ);
+	else if (rl->count[READ] < queue_congestion_off_threshold(q))
+		clear_queue_congested(q, READ);
+
+	if (rl->count[WRITE] >= queue_congestion_on_threshold(q))
+		set_queue_congested(q, WRITE);
+	else if (rl->count[WRITE] < queue_congestion_off_threshold(q))
+		clear_queue_congested(q, WRITE);
+
+	if (rl->count[READ] >= q->nr_requests) {
+		blk_set_queue_full(q, READ);
+	} else if (rl->count[READ]+1 <= q->nr_requests) {
+		blk_clear_queue_full(q, READ);
+		wake_up(&rl->wait[READ]);
+	}
+
+	if (rl->count[WRITE] >= q->nr_requests) {
+		blk_set_queue_full(q, WRITE);
+	} else if (rl->count[WRITE]+1 <= q->nr_requests) {
+		blk_clear_queue_full(q, WRITE);
+		wake_up(&rl->wait[WRITE]);
+	}
+	return ret;
+}
+
+static ssize_t queue_ra_show(struct request_queue *q, char *page)
+{
+	int ra_kb = q->backing_dev_info.ra_pages << (PAGE_CACHE_SHIFT - 10);
+
+	return queue_var_show(ra_kb, (page));
+}
+
+static ssize_t
+queue_ra_store(struct request_queue *q, const char *page, size_t count)
+{
+	unsigned long ra_kb;
+	ssize_t ret = queue_var_store(&ra_kb, page, count);
+
+	spin_lock_irq(q->queue_lock);
+	if (ra_kb > (q->max_sectors >> 1))
+		ra_kb = (q->max_sectors >> 1);
+
+	q->backing_dev_info.ra_pages = ra_kb >> (PAGE_CACHE_SHIFT - 10);
+	spin_unlock_irq(q->queue_lock);
+
+	return ret;
+}
+
+static ssize_t queue_max_sectors_show(struct request_queue *q, char *page)
+{
+	int max_sectors_kb = q->max_sectors >> 1;
+
+	return queue_var_show(max_sectors_kb, (page));
+}
+
+static ssize_t
+queue_max_sectors_store(struct request_queue *q, const char *page, size_t count)
+{
+	unsigned long max_sectors_kb,
+			max_hw_sectors_kb = q->max_hw_sectors >> 1,
+			page_kb = 1 << (PAGE_CACHE_SHIFT - 10);
+	ssize_t ret = queue_var_store(&max_sectors_kb, page, count);
+	int ra_kb;
+
+	if (max_sectors_kb > max_hw_sectors_kb || max_sectors_kb < page_kb)
+		return -EINVAL;
+	/*
+	 * Take the queue lock to update the readahead and max_sectors
+	 * values synchronously:
+	 */
+	spin_lock_irq(q->queue_lock);
+	/*
+	 * Trim readahead window as well, if necessary:
+	 */
+	ra_kb = q->backing_dev_info.ra_pages << (PAGE_CACHE_SHIFT - 10);
+	if (ra_kb > max_sectors_kb)
+		q->backing_dev_info.ra_pages =
+				max_sectors_kb >> (PAGE_CACHE_SHIFT - 10);
+
+	q->max_sectors = max_sectors_kb << 1;
+	spin_unlock_irq(q->queue_lock);
+
+	return ret;
+}
+
+static ssize_t queue_max_hw_sectors_show(struct request_queue *q, char *page)
+{
+	int max_hw_sectors_kb = q->max_hw_sectors >> 1;
+
+	return queue_var_show(max_hw_sectors_kb, (page));
+}
+
+
+static struct queue_sysfs_entry queue_requests_entry = {
+	.attr = {.name = "nr_requests", .mode = S_IRUGO | S_IWUSR },
+	.show = queue_requests_show,
+	.store = queue_requests_store,
+};
+
+static struct queue_sysfs_entry queue_ra_entry = {
+	.attr = {.name = "read_ahead_kb", .mode = S_IRUGO | S_IWUSR },
+	.show = queue_ra_show,
+	.store = queue_ra_store,
+};
+
+static struct queue_sysfs_entry queue_max_sectors_entry = {
+	.attr = {.name = "max_sectors_kb", .mode = S_IRUGO | S_IWUSR },
+	.show = queue_max_sectors_show,
+	.store = queue_max_sectors_store,
+};
+
+static struct queue_sysfs_entry queue_max_hw_sectors_entry = {
+	.attr = {.name = "max_hw_sectors_kb", .mode = S_IRUGO },
+	.show = queue_max_hw_sectors_show,
+};
+
+static struct queue_sysfs_entry queue_iosched_entry = {
+	.attr = {.name = "scheduler", .mode = S_IRUGO | S_IWUSR },
+	.show = elv_iosched_show,
+	.store = elv_iosched_store,
+};
+
+static struct attribute *default_attrs[] = {
+	&queue_requests_entry.attr,
+	&queue_ra_entry.attr,
+	&queue_max_hw_sectors_entry.attr,
+	&queue_max_sectors_entry.attr,
+	&queue_iosched_entry.attr,
+	NULL,
+};
+
+#define to_queue(atr) container_of((atr), struct queue_sysfs_entry, attr)
+
+static ssize_t
+queue_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
+{
+	struct queue_sysfs_entry *entry = to_queue(attr);
+	struct request_queue *q;
+
+	q = container_of(kobj, struct request_queue, kobj);
+	if (!entry->show)
+		return -EIO;
+
+	return entry->show(q, page);
+}
+
+static ssize_t
+queue_attr_store(struct kobject *kobj, struct attribute *attr,
+		    const char *page, size_t length)
+{
+	struct queue_sysfs_entry *entry = to_queue(attr);
+	struct request_queue *q;
+
+	q = container_of(kobj, struct request_queue, kobj);
+	if (!entry->store)
+		return -EIO;
+
+	return entry->store(q, page, length);
+}
+
+static struct sysfs_ops queue_sysfs_ops = {
+	.show	= queue_attr_show,
+	.store	= queue_attr_store,
+};
+
+static struct kobj_type queue_ktype = {
+	.sysfs_ops	= &queue_sysfs_ops,
+	.default_attrs	= default_attrs,
+};
+
+int blk_register_queue(struct gendisk *disk)
+{
+	int ret;
+
+	request_queue_t *q = disk->queue;
+
+	if (!q || !q->request_fn)
+		return -ENXIO;
+
+	q->kobj.parent = kobject_get(&disk->kobj);
+	if (!q->kobj.parent)
+		return -EBUSY;
+
+	snprintf(q->kobj.name, KOBJ_NAME_LEN, "%s", "queue");
+	q->kobj.ktype = &queue_ktype;
+
+	ret = kobject_register(&q->kobj);
+	if (ret < 0)
+		return ret;
+
+	ret = elv_register_queue(q);
+	if (ret) {
+		kobject_unregister(&q->kobj);
+		return ret;
+	}
+
+	return 0;
+}
+
+void blk_unregister_queue(struct gendisk *disk)
+{
+	request_queue_t *q = disk->queue;
+
+	if (q && q->request_fn) {
+		elv_unregister_queue(q);
+
+		kobject_unregister(&q->kobj);
+		kobject_put(&disk->kobj);
+	}
+}
diff -urN oldtree/drivers/Kconfig newtree/drivers/Kconfig
--- oldtree/drivers/Kconfig	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/Kconfig	2006-02-13 19:19:59.693558888 -0500
@@ -66,4 +66,6 @@
 
 source "drivers/sn/Kconfig"
 
+source "drivers/dlm/Kconfig"
+
 endmenu
diff -urN oldtree/drivers/Kconfig.orig newtree/drivers/Kconfig.orig
--- oldtree/drivers/Kconfig.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/Kconfig.orig	2006-02-13 19:19:59.693558888 -0500
@@ -0,0 +1,69 @@
+# drivers/Kconfig
+
+menu "Device Drivers"
+
+source "drivers/base/Kconfig"
+
+source "drivers/connector/Kconfig"
+
+source "drivers/mtd/Kconfig"
+
+source "drivers/parport/Kconfig"
+
+source "drivers/pnp/Kconfig"
+
+source "drivers/block/Kconfig"
+
+source "drivers/ide/Kconfig"
+
+source "drivers/scsi/Kconfig"
+
+source "drivers/cdrom/Kconfig"
+
+source "drivers/md/Kconfig"
+
+source "drivers/message/fusion/Kconfig"
+
+source "drivers/ieee1394/Kconfig"
+
+source "drivers/message/i2o/Kconfig"
+
+source "drivers/macintosh/Kconfig"
+
+source "drivers/net/Kconfig"
+
+source "drivers/isdn/Kconfig"
+
+source "drivers/telephony/Kconfig"
+
+# input before char - char/joystick depends on it. As does USB.
+
+source "drivers/input/Kconfig"
+
+source "drivers/char/Kconfig"
+
+source "drivers/i2c/Kconfig"
+
+source "drivers/w1/Kconfig"
+
+source "drivers/hwmon/Kconfig"
+
+source "drivers/misc/Kconfig"
+
+source "drivers/mfd/Kconfig"
+
+source "drivers/media/Kconfig"
+
+source "drivers/video/Kconfig"
+
+source "sound/Kconfig"
+
+source "drivers/usb/Kconfig"
+
+source "drivers/mmc/Kconfig"
+
+source "drivers/infiniband/Kconfig"
+
+source "drivers/sn/Kconfig"
+
+endmenu
diff -urN oldtree/drivers/Makefile newtree/drivers/Makefile
--- oldtree/drivers/Makefile	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/Makefile	2006-02-13 19:19:59.693558888 -0500
@@ -57,6 +57,7 @@
 obj-$(CONFIG_I2C)		+= i2c/
 obj-$(CONFIG_W1)		+= w1/
 obj-$(CONFIG_HWMON)		+= hwmon/
+obj-$(CONFIG_DLM)		+= dlm/
 obj-$(CONFIG_PHONE)		+= telephony/
 obj-$(CONFIG_MD)		+= md/
 obj-$(CONFIG_BT)		+= bluetooth/
diff -urN oldtree/drivers/Makefile.orig newtree/drivers/Makefile.orig
--- oldtree/drivers/Makefile.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/Makefile.orig	2006-02-13 19:19:59.694558736 -0500
@@ -0,0 +1,72 @@
+#
+# Makefile for the Linux kernel device drivers.
+#
+# 15 Sep 2000, Christoph Hellwig <hch@infradead.org>
+# Rewritten to use lists instead of if-statements.
+#
+
+obj-$(CONFIG_PCI)		+= pci/
+obj-$(CONFIG_PARISC)		+= parisc/
+obj-$(CONFIG_RAPIDIO)		+= rapidio/
+obj-y				+= video/
+obj-$(CONFIG_ACPI)		+= acpi/
+# PnP must come after ACPI since it will eventually need to check if acpi
+# was used and do nothing if so
+obj-$(CONFIG_PNP)		+= pnp/
+
+# char/ comes before serial/ etc so that the VT console is the boot-time
+# default.
+obj-y				+= char/
+
+obj-$(CONFIG_CONNECTOR)		+= connector/
+
+# i810fb and intelfb depend on char/agp/
+obj-$(CONFIG_FB_I810)           += video/i810/
+obj-$(CONFIG_FB_INTEL)          += video/intelfb/
+
+# we also need input/serio early so serio bus is initialized by the time
+# serial drivers start registering their serio ports
+obj-$(CONFIG_SERIO)		+= input/serio/
+obj-y				+= serial/
+obj-$(CONFIG_PARPORT)		+= parport/
+obj-y				+= base/ block/ misc/ mfd/ net/ media/
+obj-$(CONFIG_NUBUS)		+= nubus/
+obj-$(CONFIG_ATM)		+= atm/
+obj-$(CONFIG_PPC_PMAC)		+= macintosh/
+obj-$(CONFIG_IDE)		+= ide/
+obj-$(CONFIG_FC4)		+= fc4/
+obj-$(CONFIG_SCSI)		+= scsi/
+obj-$(CONFIG_FUSION)		+= message/
+obj-$(CONFIG_IEEE1394)		+= ieee1394/
+obj-y				+= cdrom/
+obj-$(CONFIG_MTD)		+= mtd/
+obj-$(CONFIG_PCCARD)		+= pcmcia/
+obj-$(CONFIG_DIO)		+= dio/
+obj-$(CONFIG_SBUS)		+= sbus/
+obj-$(CONFIG_ZORRO)		+= zorro/
+obj-$(CONFIG_MAC)		+= macintosh/
+obj-$(CONFIG_ATA_OVER_ETH)	+= block/aoe/
+obj-$(CONFIG_PARIDE) 		+= block/paride/
+obj-$(CONFIG_TC)		+= tc/
+obj-$(CONFIG_USB)		+= usb/
+obj-$(CONFIG_PCI)		+= usb/
+obj-$(CONFIG_USB_GADGET)	+= usb/gadget/
+obj-$(CONFIG_GAMEPORT)		+= input/gameport/
+obj-$(CONFIG_INPUT)		+= input/
+obj-$(CONFIG_I2O)		+= message/
+obj-$(CONFIG_I2C)		+= i2c/
+obj-$(CONFIG_W1)		+= w1/
+obj-$(CONFIG_HWMON)		+= hwmon/
+obj-$(CONFIG_PHONE)		+= telephony/
+obj-$(CONFIG_MD)		+= md/
+obj-$(CONFIG_BT)		+= bluetooth/
+obj-$(CONFIG_ISDN)		+= isdn/
+obj-$(CONFIG_MCA)		+= mca/
+obj-$(CONFIG_EISA)		+= eisa/
+obj-$(CONFIG_CPU_FREQ)		+= cpufreq/
+obj-$(CONFIG_MMC)		+= mmc/
+obj-$(CONFIG_INFINIBAND)	+= infiniband/
+obj-$(CONFIG_SGI_IOC4)		+= sn/
+obj-y				+= firmware/
+obj-$(CONFIG_CRYPTO)		+= crypto/
+obj-$(CONFIG_SUPERH)		+= sh/
diff -urN oldtree/drivers/acpi/Kconfig newtree/drivers/acpi/Kconfig
--- oldtree/drivers/acpi/Kconfig	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/acpi/Kconfig	2006-02-13 19:19:59.694558736 -0500
@@ -233,6 +233,19 @@
 	  If you have a legacy free Toshiba laptop (such as the Libretto L1
 	  series), say Y.
 
+config ACPI_SONY
+	tristate "Sony Laptop Extras"
+	depends on X86 && ACPI
+	  ---help---
+	  This mini-driver drives the ACPI SNC device present in the
+	  ACPI BIOS of the Sony Vaio laptops.
+
+	  It gives access to some extra laptop functionalities. In
+	  its current form, the only thing this driver does is letting
+	  the user set or query the screen brightness.
+
+	  Read <file:Documentation/acpi/sony_acpi.txt> for more information.
+
 config ACPI_CUSTOM_DSDT
 	bool "Include Custom DSDT"
 	depends on !STANDALONE
diff -urN oldtree/drivers/acpi/Makefile newtree/drivers/acpi/Makefile
--- oldtree/drivers/acpi/Makefile	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/acpi/Makefile	2006-02-13 19:19:59.694558736 -0500
@@ -55,5 +55,6 @@
 obj-$(CONFIG_ACPI_ASUS)		+= asus_acpi.o
 obj-$(CONFIG_ACPI_IBM)		+= ibm_acpi.o
 obj-$(CONFIG_ACPI_TOSHIBA)	+= toshiba_acpi.o
+obj-$(CONFIG_ACPI_SONY)		+= sony_acpi.o
 obj-y				+= scan.o motherboard.o
 obj-$(CONFIG_ACPI_HOTPLUG_MEMORY)	+= acpi_memhotplug.o
diff -urN oldtree/drivers/acpi/asus_acpi.c newtree/drivers/acpi/asus_acpi.c
--- oldtree/drivers/acpi/asus_acpi.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/acpi/asus_acpi.c	2006-02-13 19:19:59.695558584 -0500
@@ -490,13 +490,13 @@
  */
 
 /* Generic LED functions */
-static int read_led(const char *ledname, int ledmask)
+static int read_led(const char *ledname, int ledmask, int invert)
 {
 	if (ledname) {
 		int led_status;
 
 		if (read_acpi_int(NULL, ledname, &led_status))
-			return led_status;
+			return (invert) ? !led_status : led_status;
 		else
 			printk(KERN_WARNING "Asus ACPI: Error reading LED "
 			       "status\n");
@@ -552,7 +552,7 @@
 	       void *data)
 {
 	return sprintf(page, "%d\n",
-		       read_led(hotk->methods->mled_status, MLED_ON));
+		       read_led(hotk->methods->mled_status, MLED_ON, 0));
 }
 
 static int
@@ -570,7 +570,7 @@
 	       void *data)
 {
 	return sprintf(page, "%d\n",
-		       read_led(hotk->methods->wled_status, WLED_ON));
+		       read_led(hotk->methods->wled_status, WLED_ON, 1));
 }
 
 static int
@@ -588,7 +588,7 @@
 	       void *data)
 {
 	return sprintf(page, "%d\n",
-		       read_led(hotk->methods->tled_status, TLED_ON));
+		       read_led(hotk->methods->tled_status, TLED_ON, 0));
 }
 
 static int
diff -urN oldtree/drivers/acpi/sony_acpi.c newtree/drivers/acpi/sony_acpi.c
--- oldtree/drivers/acpi/sony_acpi.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/acpi/sony_acpi.c	2006-02-13 19:19:59.696558432 -0500
@@ -0,0 +1,392 @@
+/*
+ * ACPI Sony Notebook Control Driver (SNC)
+ *
+ * Copyright (C) 2004-2005 Stelian Pop <stelian@popies.net>
+ *
+ * Parts of this driver inspired from asus_acpi.c and ibm_acpi.c
+ * which are copyrighted by their respective authors.
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/acpi_bus.h>
+#include <asm/uaccess.h>
+
+#define ACPI_SNC_CLASS		"sony"
+#define ACPI_SNC_HID		"SNY5001"
+#define ACPI_SNC_DRIVER_NAME	"ACPI Sony Notebook Control Driver v0.2"
+
+#define LOG_PFX			KERN_WARNING "sony_acpi: "
+
+MODULE_AUTHOR("Stelian Pop");
+MODULE_DESCRIPTION(ACPI_SNC_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help "
+			"the development of this driver");
+
+static int sony_acpi_add (struct acpi_device *device);
+static int sony_acpi_remove (struct acpi_device *device, int type);
+
+static struct acpi_driver sony_acpi_driver = {
+	.name	= ACPI_SNC_DRIVER_NAME,
+	.class	= ACPI_SNC_CLASS,
+	.ids	= ACPI_SNC_HID,
+	.ops	= {
+			.add	= sony_acpi_add,
+			.remove	= sony_acpi_remove,
+		  },
+};
+
+static acpi_handle sony_acpi_handle;
+static struct proc_dir_entry *sony_acpi_dir;
+
+static struct sony_acpi_value {
+	char			*name;	 /* name of the entry */
+	struct proc_dir_entry 	*proc;	 /* /proc entry */
+	char			*acpiget;/* name of the ACPI get function */
+	char			*acpiset;/* name of the ACPI get function */
+	int 			min;	 /* minimum allowed value or -1 */
+	int			max;	 /* maximum allowed value or -1 */
+	int			debug;	 /* active only in debug mode ? */
+} sony_acpi_values[] = {
+	{
+		.name		= "brightness",
+		.acpiget	= "GBRT",
+		.acpiset	= "SBRT",
+		.min		= 1,
+		.max		= 8,
+		.debug		= 0,
+	},
+	{
+		.name		= "brightness_default",
+		.acpiget	= "GPBR",
+		.acpiset	= "SPBR",
+		.min		= 1,
+		.max		= 8,
+		.debug		= 0,
+	},
+	{
+		.name		= "cdpower",
+		.acpiget	= "GCDP",
+		.acpiset	= "SCDP",
+		.min		= -1,
+		.max		= -1,
+		.debug		= 0,
+	},
+	{
+		.name		= "PID",
+		.acpiget	= "GPID",
+		.debug		= 1,
+	},
+	{
+		.name		= "CTR",
+		.acpiget	= "GCTR",
+		.acpiset	= "SCTR",
+		.min		= -1,
+		.max		= -1,
+		.debug		= 1,
+	},
+	{
+		.name		= "PCR",
+		.acpiget	= "GPCR",
+		.acpiset	= "SPCR",
+		.min		= -1,
+		.max		= -1,
+		.debug		= 1,
+	},
+	{
+		.name		= "CMI",
+		.acpiget	= "GCMI",
+		.acpiset	= "SCMI",
+		.min		= -1,
+		.max		= -1,
+		.debug		= 1,
+	},
+	{
+		.name		= NULL,
+	}
+};
+
+static int acpi_callgetfunc(acpi_handle handle, char *name, int *result)
+{
+	struct acpi_buffer output;
+	union acpi_object out_obj;
+	acpi_status status;
+
+	output.length = sizeof(out_obj);
+	output.pointer = &out_obj;
+
+	status = acpi_evaluate_object(handle, name, NULL, &output);
+	if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) {
+		*result = out_obj.integer.value;
+		return 0;
+	}
+
+	printk(LOG_PFX "acpi_callreadfunc failed\n");
+
+	return -1;
+}
+
+static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
+			    int *result)
+{
+	struct acpi_object_list params;
+	union acpi_object in_obj;
+	struct acpi_buffer output;
+	union acpi_object out_obj;
+	acpi_status status;
+
+	params.count = 1;
+	params.pointer = &in_obj;
+	in_obj.type = ACPI_TYPE_INTEGER;
+	in_obj.integer.value = value;
+
+	output.length = sizeof(out_obj);
+	output.pointer = &out_obj;
+
+	status = acpi_evaluate_object(handle, name, &params, &output);
+	if (status == AE_OK) {
+		if (result != NULL) {
+			if (out_obj.type != ACPI_TYPE_INTEGER) {
+				printk(LOG_PFX "acpi_evaluate_object bad "
+				       "return type\n");
+				return -1;
+			}
+			*result = out_obj.integer.value;
+		}
+		return 0;
+	}
+
+	printk(LOG_PFX "acpi_evaluate_object failed\n");
+
+	return -1;
+}
+
+static int parse_buffer(const char __user *buffer, unsigned long count,
+			int *val) {
+	char s[32];
+	int ret;
+
+	if (count > 31)
+		return -EINVAL;
+	if (copy_from_user(s, buffer, count))
+		return -EFAULT;
+	s[count] = '\0';
+	ret = simple_strtoul(s, NULL, 10);
+	*val = ret;
+	return 0;
+}
+
+static int sony_acpi_read(char* page, char** start, off_t off, int count,
+			  int* eof, void *data)
+{
+	struct sony_acpi_value *item = data;
+	int value;
+
+	if (!item->acpiget)
+		return -EIO;
+
+	if (acpi_callgetfunc(sony_acpi_handle, item->acpiget, &value) < 0)
+		return -EIO;
+
+	return sprintf(page, "%d\n", value);
+}
+
+static int sony_acpi_write(struct file *file, const char __user *buffer,
+			   unsigned long count, void *data)
+{
+	struct sony_acpi_value *item = data;
+	int result;
+	int value;
+
+	if (!item->acpiset)
+		return -EIO;
+
+	if ((result = parse_buffer(buffer, count, &value)) < 0)
+		return result;
+
+	if (item->min != -1 && value < item->min)
+		return -EINVAL;
+	if (item->max != -1 && value > item->max)
+		return -EINVAL;
+
+	if (acpi_callsetfunc(sony_acpi_handle, item->acpiset, value, NULL) < 0)
+		return -EIO;
+
+	return count;
+}
+
+static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
+{
+	printk(LOG_PFX "sony_acpi_notify\n");
+}
+
+static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
+				      void *context, void **return_value)
+{
+	struct acpi_namespace_node *node;
+	union acpi_operand_object *operand;
+
+	node = (struct acpi_namespace_node *) handle;
+	operand = (union acpi_operand_object *) node->object;
+
+	printk(LOG_PFX "method: name: %4.4s, args %X\n", node->name.ascii,
+	       (u32) operand->method.param_count);
+
+	return AE_OK;
+}
+
+static int __init sony_acpi_add(struct acpi_device *device)
+{
+	acpi_status status;
+	int result;
+	struct sony_acpi_value *item;
+
+	sony_acpi_handle = device->handle;
+
+	acpi_driver_data(device) = NULL;
+	acpi_device_dir(device) = sony_acpi_dir;
+
+	if (debug) {
+		status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_acpi_handle,
+					     1, sony_walk_callback, NULL, NULL);
+		if (ACPI_FAILURE(status)) {
+			printk(LOG_PFX "unable to walk acpi resources\n");
+			result = -ENODEV;
+			goto outwalk;
+		}
+
+		status = acpi_install_notify_handler(sony_acpi_handle,
+						     ACPI_DEVICE_NOTIFY,
+						     sony_acpi_notify,
+						     NULL);
+		if (ACPI_FAILURE(status)) {
+			printk(LOG_PFX "unable to install notify handler\n");
+			result = -ENODEV;
+			goto outnotify;
+		}
+	}
+
+	for (item = sony_acpi_values; item->name; ++item) {
+		acpi_handle handle;
+
+		if (!debug && item->debug)
+			continue;
+
+		if (item->acpiget &&
+		    ACPI_FAILURE(acpi_get_handle(sony_acpi_handle,
+		    		 item->acpiget, &handle)))
+		    	continue;
+
+		if (item->acpiset &&
+		    ACPI_FAILURE(acpi_get_handle(sony_acpi_handle,
+		    		 item->acpiset, &handle)))
+		    	continue;
+
+		item->proc = create_proc_entry(item->name, 0600,
+					       acpi_device_dir(device));
+		if (!item->proc) {
+			printk(LOG_PFX "unable to create proc entry\n");
+			result = -EIO;
+			goto outproc;
+		}
+
+		item->proc->read_proc = sony_acpi_read;
+		item->proc->write_proc = sony_acpi_write;
+		item->proc->data = item;
+		item->proc->owner = THIS_MODULE;
+	}
+
+	printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully installed\n");
+
+	return 0;
+
+outproc:
+	if (debug) {
+		status = acpi_remove_notify_handler(sony_acpi_handle,
+						    ACPI_DEVICE_NOTIFY,
+						    sony_acpi_notify);
+		if (ACPI_FAILURE(status))
+			printk(LOG_PFX "unable to remove notify handler\n");
+	}
+outnotify:
+	for (item = sony_acpi_values; item->name; ++item)
+		if (item->proc)
+			remove_proc_entry(item->name, acpi_device_dir(device));
+outwalk:
+	return result;
+}
+
+
+static int __exit sony_acpi_remove(struct acpi_device *device, int type)
+{
+	acpi_status status;
+	struct sony_acpi_value *item;
+
+	if (debug) {
+		status = acpi_remove_notify_handler(sony_acpi_handle,
+						    ACPI_DEVICE_NOTIFY,
+						    sony_acpi_notify);
+		if (ACPI_FAILURE(status))
+			printk(LOG_PFX "unable to remove notify handler\n");
+	}
+
+	for (item = sony_acpi_values; item->name; ++item)
+		if (item->proc)
+			remove_proc_entry(item->name, acpi_device_dir(device));
+
+	printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully removed\n");
+
+	return 0;
+}
+
+static int __init sony_acpi_init(void)
+{
+	int result;
+
+	sony_acpi_dir = proc_mkdir("sony", acpi_root_dir);
+	if (!sony_acpi_dir) {
+		printk(LOG_PFX "unable to create /proc entry\n");
+		return -ENODEV;
+	}
+	sony_acpi_dir->owner = THIS_MODULE;
+
+	result = acpi_bus_register_driver(&sony_acpi_driver);
+	if (result < 0) {
+		remove_proc_entry("sony", acpi_root_dir);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+
+static void __exit sony_acpi_exit(void)
+{
+	acpi_bus_unregister_driver(&sony_acpi_driver);
+	remove_proc_entry("sony", acpi_root_dir);
+}
+
+module_init(sony_acpi_init);
+module_exit(sony_acpi_exit);
diff -urN oldtree/drivers/atm/firestream.c newtree/drivers/atm/firestream.c
--- oldtree/drivers/atm/firestream.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/atm/firestream.c	2006-02-13 19:19:59.696558432 -0500
@@ -576,7 +576,7 @@
 }
 
 
-static inline u32  read_fs (struct fs_dev *dev, int offset)
+static inline u32 read_fs (struct fs_dev *dev, int offset)
 {
 	return readl (dev->base + offset);
 }
@@ -1497,7 +1497,7 @@
 		ne->skb = skb;
 		ne->fp = fp;
 
-		qe = (struct FS_BPENTRY *) (read_fs (dev, FP_EA(fp->offset)));
+		qe = (struct FS_BPENTRY *)(long)(read_fs (dev, FP_EA(fp->offset)));
 		fs_dprintk (FS_DEBUG_QUEUE, "link at %p\n", qe);
 		if (qe) {
 			qe = bus_to_virt ((long) qe);
diff -urN oldtree/drivers/block/aoe/aoe.h newtree/drivers/block/aoe/aoe.h
--- oldtree/drivers/block/aoe/aoe.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/block/aoe/aoe.h	2006-02-13 19:19:59.697558280 -0500
@@ -1,5 +1,5 @@
 /* Copyright (c) 2004 Coraid, Inc.  See COPYING for GPL terms. */
-#define VERSION "14"
+#define VERSION "18"
 #define AOE_MAJOR 152
 #define DEVICE_NAME "aoe"
 
diff -urN oldtree/drivers/block/aoe/aoecmd.c newtree/drivers/block/aoe/aoecmd.c
--- oldtree/drivers/block/aoe/aoecmd.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/block/aoe/aoecmd.c	2006-02-13 19:19:59.697558280 -0500
@@ -590,7 +590,7 @@
 	ulong flags, sysminor, aoemajor;
 	u16 bufcnt;
 	struct sk_buff *sl;
-	enum { MAXFRAMES = 8 };
+	enum { MAXFRAMES = 16 };
 
 	h = (struct aoe_hdr *) skb->mac.raw;
 	ch = (struct aoe_cfghdr *) (h+1);
diff -urN oldtree/drivers/block/aoe/aoecmd.c.orig newtree/drivers/block/aoe/aoecmd.c.orig
--- oldtree/drivers/block/aoe/aoecmd.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/block/aoe/aoecmd.c.orig	2006-02-13 19:19:59.698558128 -0500
@@ -0,0 +1,643 @@
+/* Copyright (c) 2004 Coraid, Inc.  See COPYING for GPL terms. */
+/*
+ * aoecmd.c
+ * Filesystem request handling methods
+ */
+
+#include <linux/hdreg.h>
+#include <linux/blkdev.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <asm/unaligned.h>
+#include "aoe.h"
+
+#define TIMERTICK (HZ / 10)
+#define MINTIMER (2 * TIMERTICK)
+#define MAXTIMER (HZ << 1)
+#define MAXWAIT (60 * 3)	/* After MAXWAIT seconds, give up and fail dev */
+
+static struct sk_buff *
+new_skb(struct net_device *if_dev, ulong len)
+{
+	struct sk_buff *skb;
+
+	skb = alloc_skb(len, GFP_ATOMIC);
+	if (skb) {
+		skb->nh.raw = skb->mac.raw = skb->data;
+		skb->dev = if_dev;
+		skb->protocol = __constant_htons(ETH_P_AOE);
+		skb->priority = 0;
+		skb_put(skb, len);
+		skb->next = skb->prev = NULL;
+
+		/* tell the network layer not to perform IP checksums
+		 * or to get the NIC to do it
+		 */
+		skb->ip_summed = CHECKSUM_NONE;
+	}
+	return skb;
+}
+
+static struct sk_buff *
+skb_prepare(struct aoedev *d, struct frame *f)
+{
+	struct sk_buff *skb;
+	char *p;
+
+	skb = new_skb(d->ifp, f->ndata + f->writedatalen);
+	if (!skb) {
+		printk(KERN_INFO "aoe: skb_prepare: failure to allocate skb\n");
+		return NULL;
+	}
+
+	p = skb->mac.raw;
+	memcpy(p, f->data, f->ndata);
+
+	if (f->writedatalen) {
+		p += sizeof(struct aoe_hdr) + sizeof(struct aoe_atahdr);
+		memcpy(p, f->bufaddr, f->writedatalen);
+	}
+
+	return skb;
+}
+
+static struct frame *
+getframe(struct aoedev *d, int tag)
+{
+	struct frame *f, *e;
+
+	f = d->frames;
+	e = f + d->nframes;
+	for (; f<e; f++)
+		if (f->tag == tag)
+			return f;
+	return NULL;
+}
+
+/*
+ * Leave the top bit clear so we have tagspace for userland.
+ * The bottom 16 bits are the xmit tick for rexmit/rttavg processing.
+ * This driver reserves tag -1 to mean "unused frame."
+ */
+static int
+newtag(struct aoedev *d)
+{
+	register ulong n;
+
+	n = jiffies & 0xffff;
+	return n |= (++d->lasttag & 0x7fff) << 16;
+}
+
+static int
+aoehdr_atainit(struct aoedev *d, struct aoe_hdr *h)
+{
+	u32 host_tag = newtag(d);
+
+	memcpy(h->src, d->ifp->dev_addr, sizeof h->src);
+	memcpy(h->dst, d->addr, sizeof h->dst);
+	h->type = __constant_cpu_to_be16(ETH_P_AOE);
+	h->verfl = AOE_HVER;
+	h->major = cpu_to_be16(d->aoemajor);
+	h->minor = d->aoeminor;
+	h->cmd = AOECMD_ATA;
+	h->tag = cpu_to_be32(host_tag);
+
+	return host_tag;
+}
+
+static void
+aoecmd_ata_rw(struct aoedev *d, struct frame *f)
+{
+	struct aoe_hdr *h;
+	struct aoe_atahdr *ah;
+	struct buf *buf;
+	struct sk_buff *skb;
+	ulong bcnt;
+	register sector_t sector;
+	char writebit, extbit;
+
+	writebit = 0x10;
+	extbit = 0x4;
+
+	buf = d->inprocess;
+
+	sector = buf->sector;
+	bcnt = buf->bv_resid;
+	if (bcnt > MAXATADATA)
+		bcnt = MAXATADATA;
+
+	/* initialize the headers & frame */
+	h = (struct aoe_hdr *) f->data;
+	ah = (struct aoe_atahdr *) (h+1);
+	f->ndata = sizeof *h + sizeof *ah;
+	memset(h, 0, f->ndata);
+	f->tag = aoehdr_atainit(d, h);
+	f->waited = 0;
+	f->buf = buf;
+	f->bufaddr = buf->bufaddr;
+
+	/* set up ata header */
+	ah->scnt = bcnt >> 9;
+	ah->lba0 = sector;
+	ah->lba1 = sector >>= 8;
+	ah->lba2 = sector >>= 8;
+	ah->lba3 = sector >>= 8;
+	if (d->flags & DEVFL_EXT) {
+		ah->aflags |= AOEAFL_EXT;
+		ah->lba4 = sector >>= 8;
+		ah->lba5 = sector >>= 8;
+	} else {
+		extbit = 0;
+		ah->lba3 &= 0x0f;
+		ah->lba3 |= 0xe0;	/* LBA bit + obsolete 0xa0 */
+	}
+
+	if (bio_data_dir(buf->bio) == WRITE) {
+		ah->aflags |= AOEAFL_WRITE;
+		f->writedatalen = bcnt;
+	} else {
+		writebit = 0;
+		f->writedatalen = 0;
+	}
+
+	ah->cmdstat = WIN_READ | writebit | extbit;
+
+	/* mark all tracking fields and load out */
+	buf->nframesout += 1;
+	buf->bufaddr += bcnt;
+	buf->bv_resid -= bcnt;
+/* printk(KERN_INFO "aoe: bv_resid=%ld\n", buf->bv_resid); */
+	buf->resid -= bcnt;
+	buf->sector += bcnt >> 9;
+	if (buf->resid == 0) {
+		d->inprocess = NULL;
+	} else if (buf->bv_resid == 0) {
+		buf->bv++;
+		buf->bv_resid = buf->bv->bv_len;
+		buf->bufaddr = page_address(buf->bv->bv_page) + buf->bv->bv_offset;
+	}
+
+	skb = skb_prepare(d, f);
+	if (skb) {
+		skb->next = NULL;
+		if (d->sendq_hd)
+			d->sendq_tl->next = skb;
+		else
+			d->sendq_hd = skb;
+		d->sendq_tl = skb;
+	}
+}
+
+/* enters with d->lock held */
+void
+aoecmd_work(struct aoedev *d)
+{
+	struct frame *f;
+	struct buf *buf;
+loop:
+	f = getframe(d, FREETAG);
+	if (f == NULL)
+		return;
+	if (d->inprocess == NULL) {
+		if (list_empty(&d->bufq))
+			return;
+		buf = container_of(d->bufq.next, struct buf, bufs);
+		list_del(d->bufq.next);
+/*printk(KERN_INFO "aoecmd_work: bi_size=%ld\n", buf->bio->bi_size); */
+		d->inprocess = buf;
+	}
+	aoecmd_ata_rw(d, f);
+	goto loop;
+}
+
+static void
+rexmit(struct aoedev *d, struct frame *f)
+{
+	struct sk_buff *skb;
+	struct aoe_hdr *h;
+	char buf[128];
+	u32 n;
+
+	n = newtag(d);
+
+	snprintf(buf, sizeof buf,
+		"%15s e%ld.%ld oldtag=%08x@%08lx newtag=%08x\n",
+		"retransmit",
+		d->aoemajor, d->aoeminor, f->tag, jiffies, n);
+	aoechr_error(buf);
+
+	h = (struct aoe_hdr *) f->data;
+	f->tag = n;
+	h->tag = cpu_to_be32(n);
+
+	skb = skb_prepare(d, f);
+	if (skb) {
+		skb->next = NULL;
+		if (d->sendq_hd)
+			d->sendq_tl->next = skb;
+		else
+			d->sendq_hd = skb;
+		d->sendq_tl = skb;
+	}
+}
+
+static int
+tsince(int tag)
+{
+	int n;
+
+	n = jiffies & 0xffff;
+	n -= tag & 0xffff;
+	if (n < 0)
+		n += 1<<16;
+	return n;
+}
+
+static void
+rexmit_timer(ulong vp)
+{
+	struct aoedev *d;
+	struct frame *f, *e;
+	struct sk_buff *sl;
+	register long timeout;
+	ulong flags, n;
+
+	d = (struct aoedev *) vp;
+	sl = NULL;
+
+	/* timeout is always ~150% of the moving average */
+	timeout = d->rttavg;
+	timeout += timeout >> 1;
+
+	spin_lock_irqsave(&d->lock, flags);
+
+	if (d->flags & DEVFL_TKILL) {
+tdie:		spin_unlock_irqrestore(&d->lock, flags);
+		return;
+	}
+	f = d->frames;
+	e = f + d->nframes;
+	for (; f<e; f++) {
+		if (f->tag != FREETAG && tsince(f->tag) >= timeout) {
+			n = f->waited += timeout;
+			n /= HZ;
+			if (n > MAXWAIT) { /* waited too long.  device failure. */
+				aoedev_downdev(d);
+				goto tdie;
+			}
+			rexmit(d, f);
+		}
+	}
+
+	sl = d->sendq_hd;
+	d->sendq_hd = d->sendq_tl = NULL;
+	if (sl) {
+		n = d->rttavg <<= 1;
+		if (n > MAXTIMER)
+			d->rttavg = MAXTIMER;
+	}
+
+	d->timer.expires = jiffies + TIMERTICK;
+	add_timer(&d->timer);
+
+	spin_unlock_irqrestore(&d->lock, flags);
+
+	aoenet_xmit(sl);
+}
+
+static void
+ataid_complete(struct aoedev *d, unsigned char *id)
+{
+	u64 ssize;
+	u16 n;
+
+	/* word 83: command set supported */
+	n = le16_to_cpu(get_unaligned((__le16 *) &id[83<<1]));
+
+	/* word 86: command set/feature enabled */
+	n |= le16_to_cpu(get_unaligned((__le16 *) &id[86<<1]));
+
+	if (n & (1<<10)) {	/* bit 10: LBA 48 */
+		d->flags |= DEVFL_EXT;
+
+		/* word 100: number lba48 sectors */
+		ssize = le64_to_cpu(get_unaligned((__le64 *) &id[100<<1]));
+
+		/* set as in ide-disk.c:init_idedisk_capacity */
+		d->geo.cylinders = ssize;
+		d->geo.cylinders /= (255 * 63);
+		d->geo.heads = 255;
+		d->geo.sectors = 63;
+	} else {
+		d->flags &= ~DEVFL_EXT;
+
+		/* number lba28 sectors */
+		ssize = le32_to_cpu(get_unaligned((__le32 *) &id[60<<1]));
+
+		/* NOTE: obsolete in ATA 6 */
+		d->geo.cylinders = le16_to_cpu(get_unaligned((__le16 *) &id[54<<1]));
+		d->geo.heads = le16_to_cpu(get_unaligned((__le16 *) &id[55<<1]));
+		d->geo.sectors = le16_to_cpu(get_unaligned((__le16 *) &id[56<<1]));
+	}
+	d->ssize = ssize;
+	d->geo.start = 0;
+	if (d->gd != NULL) {
+		d->gd->capacity = ssize;
+		d->flags |= DEVFL_UP;
+		return;
+	}
+	if (d->flags & DEVFL_WORKON) {
+		printk(KERN_INFO "aoe: ataid_complete: can't schedule work, it's already on!  "
+			"(This really shouldn't happen).\n");
+		return;
+	}
+	INIT_WORK(&d->work, aoeblk_gdalloc, d);
+	schedule_work(&d->work);
+	d->flags |= DEVFL_WORKON;
+}
+
+static void
+calc_rttavg(struct aoedev *d, int rtt)
+{
+	register long n;
+
+	n = rtt;
+	if (n < MINTIMER)
+		n = MINTIMER;
+	else if (n > MAXTIMER)
+		n = MAXTIMER;
+
+	/* g == .25; cf. Congestion Avoidance and Control, Jacobson & Karels; 1988 */
+	n -= d->rttavg;
+	d->rttavg += n >> 2;
+}
+
+void
+aoecmd_ata_rsp(struct sk_buff *skb)
+{
+	struct aoedev *d;
+	struct aoe_hdr *hin;
+	struct aoe_atahdr *ahin, *ahout;
+	struct frame *f;
+	struct buf *buf;
+	struct sk_buff *sl;
+	register long n;
+	ulong flags;
+	char ebuf[128];
+	u16 aoemajor;
+
+	hin = (struct aoe_hdr *) skb->mac.raw;
+	aoemajor = be16_to_cpu(hin->major);
+	d = aoedev_by_aoeaddr(aoemajor, hin->minor);
+	if (d == NULL) {
+		snprintf(ebuf, sizeof ebuf, "aoecmd_ata_rsp: ata response "
+			"for unknown device %d.%d\n",
+			 aoemajor, hin->minor);
+		aoechr_error(ebuf);
+		return;
+	}
+
+	spin_lock_irqsave(&d->lock, flags);
+
+	f = getframe(d, be32_to_cpu(hin->tag));
+	if (f == NULL) {
+		spin_unlock_irqrestore(&d->lock, flags);
+		snprintf(ebuf, sizeof ebuf,
+			"%15s e%d.%d    tag=%08x@%08lx\n",
+			"unexpected rsp",
+			be16_to_cpu(hin->major),
+			hin->minor,
+			be32_to_cpu(hin->tag),
+			jiffies);
+		aoechr_error(ebuf);
+		return;
+	}
+
+	calc_rttavg(d, tsince(f->tag));
+
+	ahin = (struct aoe_atahdr *) (hin+1);
+	ahout = (struct aoe_atahdr *) (f->data + sizeof(struct aoe_hdr));
+	buf = f->buf;
+
+	if (ahin->cmdstat & 0xa9) {	/* these bits cleared on success */
+		printk(KERN_CRIT "aoe: aoecmd_ata_rsp: ata error cmd=%2.2Xh "
+			"stat=%2.2Xh from e%ld.%ld\n", 
+			ahout->cmdstat, ahin->cmdstat,
+			d->aoemajor, d->aoeminor);
+		if (buf)
+			buf->flags |= BUFFL_FAIL;
+	} else {
+		switch (ahout->cmdstat) {
+		case WIN_READ:
+		case WIN_READ_EXT:
+			n = ahout->scnt << 9;
+			if (skb->len - sizeof *hin - sizeof *ahin < n) {
+				printk(KERN_CRIT "aoe: aoecmd_ata_rsp: runt "
+					"ata data size in read.  skb->len=%d\n",
+					skb->len);
+				/* fail frame f?  just returning will rexmit. */
+				spin_unlock_irqrestore(&d->lock, flags);
+				return;
+			}
+			memcpy(f->bufaddr, ahin+1, n);
+		case WIN_WRITE:
+		case WIN_WRITE_EXT:
+			break;
+		case WIN_IDENTIFY:
+			if (skb->len - sizeof *hin - sizeof *ahin < 512) {
+				printk(KERN_INFO "aoe: aoecmd_ata_rsp: runt data size "
+					"in ataid.  skb->len=%d\n", skb->len);
+				spin_unlock_irqrestore(&d->lock, flags);
+				return;
+			}
+			ataid_complete(d, (char *) (ahin+1));
+			/* d->flags |= DEVFL_WC_UPDATE; */
+			break;
+		default:
+			printk(KERN_INFO "aoe: aoecmd_ata_rsp: unrecognized "
+			       "outbound ata command %2.2Xh for %d.%d\n", 
+			       ahout->cmdstat,
+			       be16_to_cpu(hin->major),
+			       hin->minor);
+		}
+	}
+
+	if (buf) {
+		buf->nframesout -= 1;
+		if (buf->nframesout == 0 && buf->resid == 0) {
+			unsigned long duration = jiffies - buf->start_time;
+			unsigned long n_sect = buf->bio->bi_size >> 9;
+			struct gendisk *disk = d->gd;
+			const int rw = bio_data_dir(buf->bio);
+
+			disk_stat_inc(disk, ios[rw]);
+			disk_stat_add(disk, ticks[rw], duration);
+			disk_stat_add(disk, sectors[rw], n_sect);
+			disk_stat_add(disk, io_ticks, duration);
+			n = (buf->flags & BUFFL_FAIL) ? -EIO : 0;
+			bio_endio(buf->bio, buf->bio->bi_size, n);
+			mempool_free(buf, d->bufpool);
+		}
+	}
+
+	f->buf = NULL;
+	f->tag = FREETAG;
+
+	aoecmd_work(d);
+
+	sl = d->sendq_hd;
+	d->sendq_hd = d->sendq_tl = NULL;
+
+	spin_unlock_irqrestore(&d->lock, flags);
+
+	aoenet_xmit(sl);
+}
+
+void
+aoecmd_cfg(ushort aoemajor, unsigned char aoeminor)
+{
+	struct aoe_hdr *h;
+	struct aoe_cfghdr *ch;
+	struct sk_buff *skb, *sl;
+	struct net_device *ifp;
+
+	sl = NULL;
+
+	read_lock(&dev_base_lock);
+	for (ifp = dev_base; ifp; dev_put(ifp), ifp = ifp->next) {
+		dev_hold(ifp);
+		if (!is_aoe_netif(ifp))
+			continue;
+
+		skb = new_skb(ifp, sizeof *h + sizeof *ch);
+		if (skb == NULL) {
+			printk(KERN_INFO "aoe: aoecmd_cfg: skb alloc failure\n");
+			continue;
+		}
+		h = (struct aoe_hdr *) skb->mac.raw;
+		memset(h, 0, sizeof *h + sizeof *ch);
+
+		memset(h->dst, 0xff, sizeof h->dst);
+		memcpy(h->src, ifp->dev_addr, sizeof h->src);
+		h->type = __constant_cpu_to_be16(ETH_P_AOE);
+		h->verfl = AOE_HVER;
+		h->major = cpu_to_be16(aoemajor);
+		h->minor = aoeminor;
+		h->cmd = AOECMD_CFG;
+
+		skb->next = sl;
+		sl = skb;
+	}
+	read_unlock(&dev_base_lock);
+
+	aoenet_xmit(sl);
+}
+ 
+/*
+ * Since we only call this in one place (and it only prepares one frame)
+ * we just return the skb.  Usually we'd chain it up to the aoedev sendq.
+ */
+static struct sk_buff *
+aoecmd_ata_id(struct aoedev *d)
+{
+	struct aoe_hdr *h;
+	struct aoe_atahdr *ah;
+	struct frame *f;
+	struct sk_buff *skb;
+
+	f = getframe(d, FREETAG);
+	if (f == NULL) {
+		printk(KERN_CRIT "aoe: aoecmd_ata_id: can't get a frame.  "
+			"This shouldn't happen.\n");
+		return NULL;
+	}
+
+	/* initialize the headers & frame */
+	h = (struct aoe_hdr *) f->data;
+	ah = (struct aoe_atahdr *) (h+1);
+	f->ndata = sizeof *h + sizeof *ah;
+	memset(h, 0, f->ndata);
+	f->tag = aoehdr_atainit(d, h);
+	f->waited = 0;
+	f->writedatalen = 0;
+
+	/* this message initializes the device, so we reset the rttavg */
+	d->rttavg = MAXTIMER;
+
+	/* set up ata header */
+	ah->scnt = 1;
+	ah->cmdstat = WIN_IDENTIFY;
+	ah->lba3 = 0xa0;
+
+	skb = skb_prepare(d, f);
+
+	/* we now want to start the rexmit tracking */
+	d->flags &= ~DEVFL_TKILL;
+	d->timer.data = (ulong) d;
+	d->timer.function = rexmit_timer;
+	d->timer.expires = jiffies + TIMERTICK;
+	add_timer(&d->timer);
+
+	return skb;
+}
+ 
+void
+aoecmd_cfg_rsp(struct sk_buff *skb)
+{
+	struct aoedev *d;
+	struct aoe_hdr *h;
+	struct aoe_cfghdr *ch;
+	ulong flags, sysminor, aoemajor;
+	u16 bufcnt;
+	struct sk_buff *sl;
+	enum { MAXFRAMES = 8 };
+
+	h = (struct aoe_hdr *) skb->mac.raw;
+	ch = (struct aoe_cfghdr *) (h+1);
+
+	/*
+	 * Enough people have their dip switches set backwards to
+	 * warrant a loud message for this special case.
+	 */
+	aoemajor = be16_to_cpu(h->major);
+	if (aoemajor == 0xfff) {
+		printk(KERN_CRIT "aoe: aoecmd_cfg_rsp: Warning: shelf "
+			"address is all ones.  Check shelf dip switches\n");
+		return;
+	}
+
+	sysminor = SYSMINOR(aoemajor, h->minor);
+	if (sysminor * AOE_PARTITIONS + AOE_PARTITIONS > MINORMASK) {
+		printk(KERN_INFO
+			"aoe: e%ld.%d: minor number too large\n", 
+			aoemajor, (int) h->minor);
+		return;
+	}
+
+	bufcnt = be16_to_cpu(ch->bufcnt);
+	if (bufcnt > MAXFRAMES)	/* keep it reasonable */
+		bufcnt = MAXFRAMES;
+
+	d = aoedev_set(sysminor, h->src, skb->dev, bufcnt);
+	if (d == NULL) {
+		printk(KERN_INFO "aoe: aoecmd_cfg_rsp: device set failure\n");
+		return;
+	}
+
+	spin_lock_irqsave(&d->lock, flags);
+
+	if (d->flags & (DEVFL_UP | DEVFL_CLOSEWAIT)) {
+		spin_unlock_irqrestore(&d->lock, flags);
+		return;
+	}
+
+	d->fw_ver = be16_to_cpu(ch->fwver);
+
+	/* we get here only if the device is new */
+	sl = aoecmd_ata_id(d);
+
+	spin_unlock_irqrestore(&d->lock, flags);
+
+	aoenet_xmit(sl);
+}
+
diff -urN oldtree/drivers/block/cciss.c newtree/drivers/block/cciss.c
--- oldtree/drivers/block/cciss.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/block/cciss.c	2006-02-13 19:19:59.699557976 -0500
@@ -3172,8 +3172,7 @@
 
 clean4:
 #ifdef CONFIG_CISS_SCSI_TAPE
-	if(hba[i]->scsi_rejects.complete)
-		kfree(hba[i]->scsi_rejects.complete);
+	kfree(hba[i]->scsi_rejects.complete);
 #endif
 	kfree(hba[i]->cmd_pool_bits);
 	if(hba[i]->cmd_pool)
diff -urN oldtree/drivers/block/pktcdvd.c newtree/drivers/block/pktcdvd.c
--- oldtree/drivers/block/pktcdvd.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/block/pktcdvd.c	2006-02-13 19:19:59.701557672 -0500
@@ -247,7 +247,7 @@
 	return rb_entry(n, struct pkt_rb_node, rb_node);
 }
 
-static inline void pkt_rbtree_erase(struct pktcdvd_device *pd, struct pkt_rb_node *node)
+static void pkt_rbtree_erase(struct pktcdvd_device *pd, struct pkt_rb_node *node)
 {
 	rb_erase(&node->rb_node, &pd->bio_queue);
 	mempool_free(node, pd->rb_pool);
@@ -315,7 +315,7 @@
 /*
  * Add a bio to a single linked list defined by its head and tail pointers.
  */
-static inline void pkt_add_list_last(struct bio *bio, struct bio **list_head, struct bio **list_tail)
+static void pkt_add_list_last(struct bio *bio, struct bio **list_head, struct bio **list_tail)
 {
 	bio->bi_next = NULL;
 	if (*list_tail) {
diff -urN oldtree/drivers/block/pktcdvd.c.orig newtree/drivers/block/pktcdvd.c.orig
--- oldtree/drivers/block/pktcdvd.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/block/pktcdvd.c.orig	2006-02-13 19:19:59.705557064 -0500
@@ -0,0 +1,2698 @@
+/*
+ * Copyright (C) 2000 Jens Axboe <axboe@suse.de>
+ * Copyright (C) 2001-2004 Peter Osterlund <petero2@telia.com>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ * Packet writing layer for ATAPI and SCSI CD-RW, DVD+RW, DVD-RW and
+ * DVD-RAM devices.
+ *
+ * Theory of operation:
+ *
+ * At the lowest level, there is the standard driver for the CD/DVD device,
+ * typically ide-cd.c or sr.c. This driver can handle read and write requests,
+ * but it doesn't know anything about the special restrictions that apply to
+ * packet writing. One restriction is that write requests must be aligned to
+ * packet boundaries on the physical media, and the size of a write request
+ * must be equal to the packet size. Another restriction is that a
+ * GPCMD_FLUSH_CACHE command has to be issued to the drive before a read
+ * command, if the previous command was a write.
+ *
+ * The purpose of the packet writing driver is to hide these restrictions from
+ * higher layers, such as file systems, and present a block device that can be
+ * randomly read and written using 2kB-sized blocks.
+ *
+ * The lowest layer in the packet writing driver is the packet I/O scheduler.
+ * Its data is defined by the struct packet_iosched and includes two bio
+ * queues with pending read and write requests. These queues are processed
+ * by the pkt_iosched_process_queue() function. The write requests in this
+ * queue are already properly aligned and sized. This layer is responsible for
+ * issuing the flush cache commands and scheduling the I/O in a good order.
+ *
+ * The next layer transforms unaligned write requests to aligned writes. This
+ * transformation requires reading missing pieces of data from the underlying
+ * block device, assembling the pieces to full packets and queuing them to the
+ * packet I/O scheduler.
+ *
+ * At the top layer there is a custom make_request_fn function that forwards
+ * read requests directly to the iosched queue and puts write requests in the
+ * unaligned write queue. A kernel thread performs the necessary read
+ * gathering to convert the unaligned writes to aligned writes and then feeds
+ * them to the packet I/O scheduler.
+ *
+ *************************************************************************/
+
+#define VERSION_CODE	"v0.2.0a 2004-07-14 Jens Axboe (axboe@suse.de) and petero2@telia.com"
+
+#include <linux/pktcdvd.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/file.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/miscdevice.h>
+#include <linux/suspend.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_ioctl.h>
+
+#include <asm/uaccess.h>
+
+#if PACKET_DEBUG
+#define DPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#if PACKET_DEBUG > 1
+#define VPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args)
+#else
+#define VPRINTK(fmt, args...)
+#endif
+
+#define MAX_SPEED 0xffff
+
+#define ZONE(sector, pd) (((sector) + (pd)->offset) & ~((pd)->settings.size - 1))
+
+static struct pktcdvd_device *pkt_devs[MAX_WRITERS];
+static struct proc_dir_entry *pkt_proc;
+static int pkt_major;
+static struct semaphore ctl_mutex;	/* Serialize open/close/setup/teardown */
+static mempool_t *psd_pool;
+
+
+static void pkt_bio_finished(struct pktcdvd_device *pd)
+{
+	BUG_ON(atomic_read(&pd->cdrw.pending_bios) <= 0);
+	if (atomic_dec_and_test(&pd->cdrw.pending_bios)) {
+		VPRINTK("pktcdvd: queue empty\n");
+		atomic_set(&pd->iosched.attention, 1);
+		wake_up(&pd->wqueue);
+	}
+}
+
+static void pkt_bio_destructor(struct bio *bio)
+{
+	kfree(bio->bi_io_vec);
+	kfree(bio);
+}
+
+static struct bio *pkt_bio_alloc(int nr_iovecs)
+{
+	struct bio_vec *bvl = NULL;
+	struct bio *bio;
+
+	bio = kmalloc(sizeof(struct bio), GFP_KERNEL);
+	if (!bio)
+		goto no_bio;
+	bio_init(bio);
+
+	bvl = kcalloc(nr_iovecs, sizeof(struct bio_vec), GFP_KERNEL);
+	if (!bvl)
+		goto no_bvl;
+
+	bio->bi_max_vecs = nr_iovecs;
+	bio->bi_io_vec = bvl;
+	bio->bi_destructor = pkt_bio_destructor;
+
+	return bio;
+
+ no_bvl:
+	kfree(bio);
+ no_bio:
+	return NULL;
+}
+
+/*
+ * Allocate a packet_data struct
+ */
+static struct packet_data *pkt_alloc_packet_data(void)
+{
+	int i;
+	struct packet_data *pkt;
+
+	pkt = kzalloc(sizeof(struct packet_data), GFP_KERNEL);
+	if (!pkt)
+		goto no_pkt;
+
+	pkt->w_bio = pkt_bio_alloc(PACKET_MAX_SIZE);
+	if (!pkt->w_bio)
+		goto no_bio;
+
+	for (i = 0; i < PAGES_PER_PACKET; i++) {
+		pkt->pages[i] = alloc_page(GFP_KERNEL|__GFP_ZERO);
+		if (!pkt->pages[i])
+			goto no_page;
+	}
+
+	spin_lock_init(&pkt->lock);
+
+	for (i = 0; i < PACKET_MAX_SIZE; i++) {
+		struct bio *bio = pkt_bio_alloc(1);
+		if (!bio)
+			goto no_rd_bio;
+		pkt->r_bios[i] = bio;
+	}
+
+	return pkt;
+
+no_rd_bio:
+	for (i = 0; i < PACKET_MAX_SIZE; i++) {
+		struct bio *bio = pkt->r_bios[i];
+		if (bio)
+			bio_put(bio);
+	}
+
+no_page:
+	for (i = 0; i < PAGES_PER_PACKET; i++)
+		if (pkt->pages[i])
+			__free_page(pkt->pages[i]);
+	bio_put(pkt->w_bio);
+no_bio:
+	kfree(pkt);
+no_pkt:
+	return NULL;
+}
+
+/*
+ * Free a packet_data struct
+ */
+static void pkt_free_packet_data(struct packet_data *pkt)
+{
+	int i;
+
+	for (i = 0; i < PACKET_MAX_SIZE; i++) {
+		struct bio *bio = pkt->r_bios[i];
+		if (bio)
+			bio_put(bio);
+	}
+	for (i = 0; i < PAGES_PER_PACKET; i++)
+		__free_page(pkt->pages[i]);
+	bio_put(pkt->w_bio);
+	kfree(pkt);
+}
+
+static void pkt_shrink_pktlist(struct pktcdvd_device *pd)
+{
+	struct packet_data *pkt, *next;
+
+	BUG_ON(!list_empty(&pd->cdrw.pkt_active_list));
+
+	list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_free_list, list) {
+		pkt_free_packet_data(pkt);
+	}
+}
+
+static int pkt_grow_pktlist(struct pktcdvd_device *pd, int nr_packets)
+{
+	struct packet_data *pkt;
+
+	INIT_LIST_HEAD(&pd->cdrw.pkt_free_list);
+	INIT_LIST_HEAD(&pd->cdrw.pkt_active_list);
+	spin_lock_init(&pd->cdrw.active_list_lock);
+	while (nr_packets > 0) {
+		pkt = pkt_alloc_packet_data();
+		if (!pkt) {
+			pkt_shrink_pktlist(pd);
+			return 0;
+		}
+		pkt->id = nr_packets;
+		pkt->pd = pd;
+		list_add(&pkt->list, &pd->cdrw.pkt_free_list);
+		nr_packets--;
+	}
+	return 1;
+}
+
+static void *pkt_rb_alloc(gfp_t gfp_mask, void *data)
+{
+	return kmalloc(sizeof(struct pkt_rb_node), gfp_mask);
+}
+
+static void pkt_rb_free(void *ptr, void *data)
+{
+	kfree(ptr);
+}
+
+static inline struct pkt_rb_node *pkt_rbtree_next(struct pkt_rb_node *node)
+{
+	struct rb_node *n = rb_next(&node->rb_node);
+	if (!n)
+		return NULL;
+	return rb_entry(n, struct pkt_rb_node, rb_node);
+}
+
+static inline void pkt_rbtree_erase(struct pktcdvd_device *pd, struct pkt_rb_node *node)
+{
+	rb_erase(&node->rb_node, &pd->bio_queue);
+	mempool_free(node, pd->rb_pool);
+	pd->bio_queue_size--;
+	BUG_ON(pd->bio_queue_size < 0);
+}
+
+/*
+ * Find the first node in the pd->bio_queue rb tree with a starting sector >= s.
+ */
+static struct pkt_rb_node *pkt_rbtree_find(struct pktcdvd_device *pd, sector_t s)
+{
+	struct rb_node *n = pd->bio_queue.rb_node;
+	struct rb_node *next;
+	struct pkt_rb_node *tmp;
+
+	if (!n) {
+		BUG_ON(pd->bio_queue_size > 0);
+		return NULL;
+	}
+
+	for (;;) {
+		tmp = rb_entry(n, struct pkt_rb_node, rb_node);
+		if (s <= tmp->bio->bi_sector)
+			next = n->rb_left;
+		else
+			next = n->rb_right;
+		if (!next)
+			break;
+		n = next;
+	}
+
+	if (s > tmp->bio->bi_sector) {
+		tmp = pkt_rbtree_next(tmp);
+		if (!tmp)
+			return NULL;
+	}
+	BUG_ON(s > tmp->bio->bi_sector);
+	return tmp;
+}
+
+/*
+ * Insert a node into the pd->bio_queue rb tree.
+ */
+static void pkt_rbtree_insert(struct pktcdvd_device *pd, struct pkt_rb_node *node)
+{
+	struct rb_node **p = &pd->bio_queue.rb_node;
+	struct rb_node *parent = NULL;
+	sector_t s = node->bio->bi_sector;
+	struct pkt_rb_node *tmp;
+
+	while (*p) {
+		parent = *p;
+		tmp = rb_entry(parent, struct pkt_rb_node, rb_node);
+		if (s < tmp->bio->bi_sector)
+			p = &(*p)->rb_left;
+		else
+			p = &(*p)->rb_right;
+	}
+	rb_link_node(&node->rb_node, parent, p);
+	rb_insert_color(&node->rb_node, &pd->bio_queue);
+	pd->bio_queue_size++;
+}
+
+/*
+ * Add a bio to a single linked list defined by its head and tail pointers.
+ */
+static inline void pkt_add_list_last(struct bio *bio, struct bio **list_head, struct bio **list_tail)
+{
+	bio->bi_next = NULL;
+	if (*list_tail) {
+		BUG_ON((*list_head) == NULL);
+		(*list_tail)->bi_next = bio;
+		(*list_tail) = bio;
+	} else {
+		BUG_ON((*list_head) != NULL);
+		(*list_head) = bio;
+		(*list_tail) = bio;
+	}
+}
+
+/*
+ * Remove and return the first bio from a single linked list defined by its
+ * head and tail pointers.
+ */
+static inline struct bio *pkt_get_list_first(struct bio **list_head, struct bio **list_tail)
+{
+	struct bio *bio;
+
+	if (*list_head == NULL)
+		return NULL;
+
+	bio = *list_head;
+	*list_head = bio->bi_next;
+	if (*list_head == NULL)
+		*list_tail = NULL;
+
+	bio->bi_next = NULL;
+	return bio;
+}
+
+/*
+ * Send a packet_command to the underlying block device and
+ * wait for completion.
+ */
+static int pkt_generic_packet(struct pktcdvd_device *pd, struct packet_command *cgc)
+{
+	char sense[SCSI_SENSE_BUFFERSIZE];
+	request_queue_t *q;
+	struct request *rq;
+	DECLARE_COMPLETION(wait);
+	int err = 0;
+
+	q = bdev_get_queue(pd->bdev);
+
+	rq = blk_get_request(q, (cgc->data_direction == CGC_DATA_WRITE) ? WRITE : READ,
+			     __GFP_WAIT);
+	rq->errors = 0;
+	rq->rq_disk = pd->bdev->bd_disk;
+	rq->bio = NULL;
+	rq->buffer = NULL;
+	rq->timeout = 60*HZ;
+	rq->data = cgc->buffer;
+	rq->data_len = cgc->buflen;
+	rq->sense = sense;
+	memset(sense, 0, sizeof(sense));
+	rq->sense_len = 0;
+	rq->flags |= REQ_BLOCK_PC | REQ_HARDBARRIER;
+	if (cgc->quiet)
+		rq->flags |= REQ_QUIET;
+	memcpy(rq->cmd, cgc->cmd, CDROM_PACKET_SIZE);
+	if (sizeof(rq->cmd) > CDROM_PACKET_SIZE)
+		memset(rq->cmd + CDROM_PACKET_SIZE, 0, sizeof(rq->cmd) - CDROM_PACKET_SIZE);
+
+	rq->ref_count++;
+	rq->flags |= REQ_NOMERGE;
+	rq->waiting = &wait;
+	rq->end_io = blk_end_sync_rq;
+	elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 1);
+	generic_unplug_device(q);
+	wait_for_completion(&wait);
+
+	if (rq->errors)
+		err = -EIO;
+
+	blk_put_request(rq);
+	return err;
+}
+
+/*
+ * A generic sense dump / resolve mechanism should be implemented across
+ * all ATAPI + SCSI devices.
+ */
+static void pkt_dump_sense(struct packet_command *cgc)
+{
+	static char *info[9] = { "No sense", "Recovered error", "Not ready",
+				 "Medium error", "Hardware error", "Illegal request",
+				 "Unit attention", "Data protect", "Blank check" };
+	int i;
+	struct request_sense *sense = cgc->sense;
+
+	printk("pktcdvd:");
+	for (i = 0; i < CDROM_PACKET_SIZE; i++)
+		printk(" %02x", cgc->cmd[i]);
+	printk(" - ");
+
+	if (sense == NULL) {
+		printk("no sense\n");
+		return;
+	}
+
+	printk("sense %02x.%02x.%02x", sense->sense_key, sense->asc, sense->ascq);
+
+	if (sense->sense_key > 8) {
+		printk(" (INVALID)\n");
+		return;
+	}
+
+	printk(" (%s)\n", info[sense->sense_key]);
+}
+
+/*
+ * flush the drive cache to media
+ */
+static int pkt_flush_cache(struct pktcdvd_device *pd)
+{
+	struct packet_command cgc;
+
+	init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+	cgc.cmd[0] = GPCMD_FLUSH_CACHE;
+	cgc.quiet = 1;
+
+	/*
+	 * the IMMED bit -- we default to not setting it, although that
+	 * would allow a much faster close, this is safer
+	 */
+#if 0
+	cgc.cmd[1] = 1 << 1;
+#endif
+	return pkt_generic_packet(pd, &cgc);
+}
+
+/*
+ * speed is given as the normal factor, e.g. 4 for 4x
+ */
+static int pkt_set_speed(struct pktcdvd_device *pd, unsigned write_speed, unsigned read_speed)
+{
+	struct packet_command cgc;
+	struct request_sense sense;
+	int ret;
+
+	init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+	cgc.sense = &sense;
+	cgc.cmd[0] = GPCMD_SET_SPEED;
+	cgc.cmd[2] = (read_speed >> 8) & 0xff;
+	cgc.cmd[3] = read_speed & 0xff;
+	cgc.cmd[4] = (write_speed >> 8) & 0xff;
+	cgc.cmd[5] = write_speed & 0xff;
+
+	if ((ret = pkt_generic_packet(pd, &cgc)))
+		pkt_dump_sense(&cgc);
+
+	return ret;
+}
+
+/*
+ * Queue a bio for processing by the low-level CD device. Must be called
+ * from process context.
+ */
+static void pkt_queue_bio(struct pktcdvd_device *pd, struct bio *bio)
+{
+	spin_lock(&pd->iosched.lock);
+	if (bio_data_dir(bio) == READ) {
+		pkt_add_list_last(bio, &pd->iosched.read_queue,
+				  &pd->iosched.read_queue_tail);
+	} else {
+		pkt_add_list_last(bio, &pd->iosched.write_queue,
+				  &pd->iosched.write_queue_tail);
+	}
+	spin_unlock(&pd->iosched.lock);
+
+	atomic_set(&pd->iosched.attention, 1);
+	wake_up(&pd->wqueue);
+}
+
+/*
+ * Process the queued read/write requests. This function handles special
+ * requirements for CDRW drives:
+ * - A cache flush command must be inserted before a read request if the
+ *   previous request was a write.
+ * - Switching between reading and writing is slow, so don't do it more often
+ *   than necessary.
+ * - Optimize for throughput at the expense of latency. This means that streaming
+ *   writes will never be interrupted by a read, but if the drive has to seek
+ *   before the next write, switch to reading instead if there are any pending
+ *   read requests.
+ * - Set the read speed according to current usage pattern. When only reading
+ *   from the device, it's best to use the highest possible read speed, but
+ *   when switching often between reading and writing, it's better to have the
+ *   same read and write speeds.
+ */
+static void pkt_iosched_process_queue(struct pktcdvd_device *pd)
+{
+
+	if (atomic_read(&pd->iosched.attention) == 0)
+		return;
+	atomic_set(&pd->iosched.attention, 0);
+
+	for (;;) {
+		struct bio *bio;
+		int reads_queued, writes_queued;
+
+		spin_lock(&pd->iosched.lock);
+		reads_queued = (pd->iosched.read_queue != NULL);
+		writes_queued = (pd->iosched.write_queue != NULL);
+		spin_unlock(&pd->iosched.lock);
+
+		if (!reads_queued && !writes_queued)
+			break;
+
+		if (pd->iosched.writing) {
+			int need_write_seek = 1;
+			spin_lock(&pd->iosched.lock);
+			bio = pd->iosched.write_queue;
+			spin_unlock(&pd->iosched.lock);
+			if (bio && (bio->bi_sector == pd->iosched.last_write))
+				need_write_seek = 0;
+			if (need_write_seek && reads_queued) {
+				if (atomic_read(&pd->cdrw.pending_bios) > 0) {
+					VPRINTK("pktcdvd: write, waiting\n");
+					break;
+				}
+				pkt_flush_cache(pd);
+				pd->iosched.writing = 0;
+			}
+		} else {
+			if (!reads_queued && writes_queued) {
+				if (atomic_read(&pd->cdrw.pending_bios) > 0) {
+					VPRINTK("pktcdvd: read, waiting\n");
+					break;
+				}
+				pd->iosched.writing = 1;
+			}
+		}
+
+		spin_lock(&pd->iosched.lock);
+		if (pd->iosched.writing) {
+			bio = pkt_get_list_first(&pd->iosched.write_queue,
+						 &pd->iosched.write_queue_tail);
+		} else {
+			bio = pkt_get_list_first(&pd->iosched.read_queue,
+						 &pd->iosched.read_queue_tail);
+		}
+		spin_unlock(&pd->iosched.lock);
+
+		if (!bio)
+			continue;
+
+		if (bio_data_dir(bio) == READ)
+			pd->iosched.successive_reads += bio->bi_size >> 10;
+		else {
+			pd->iosched.successive_reads = 0;
+			pd->iosched.last_write = bio->bi_sector + bio_sectors(bio);
+		}
+		if (pd->iosched.successive_reads >= HI_SPEED_SWITCH) {
+			if (pd->read_speed == pd->write_speed) {
+				pd->read_speed = MAX_SPEED;
+				pkt_set_speed(pd, pd->write_speed, pd->read_speed);
+			}
+		} else {
+			if (pd->read_speed != pd->write_speed) {
+				pd->read_speed = pd->write_speed;
+				pkt_set_speed(pd, pd->write_speed, pd->read_speed);
+			}
+		}
+
+		atomic_inc(&pd->cdrw.pending_bios);
+		generic_make_request(bio);
+	}
+}
+
+/*
+ * Special care is needed if the underlying block device has a small
+ * max_phys_segments value.
+ */
+static int pkt_set_segment_merging(struct pktcdvd_device *pd, request_queue_t *q)
+{
+	if ((pd->settings.size << 9) / CD_FRAMESIZE <= q->max_phys_segments) {
+		/*
+		 * The cdrom device can handle one segment/frame
+		 */
+		clear_bit(PACKET_MERGE_SEGS, &pd->flags);
+		return 0;
+	} else if ((pd->settings.size << 9) / PAGE_SIZE <= q->max_phys_segments) {
+		/*
+		 * We can handle this case at the expense of some extra memory
+		 * copies during write operations
+		 */
+		set_bit(PACKET_MERGE_SEGS, &pd->flags);
+		return 0;
+	} else {
+		printk("pktcdvd: cdrom max_phys_segments too small\n");
+		return -EIO;
+	}
+}
+
+/*
+ * Copy CD_FRAMESIZE bytes from src_bio into a destination page
+ */
+static void pkt_copy_bio_data(struct bio *src_bio, int seg, int offs, struct page *dst_page, int dst_offs)
+{
+	unsigned int copy_size = CD_FRAMESIZE;
+
+	while (copy_size > 0) {
+		struct bio_vec *src_bvl = bio_iovec_idx(src_bio, seg);
+		void *vfrom = kmap_atomic(src_bvl->bv_page, KM_USER0) +
+			src_bvl->bv_offset + offs;
+		void *vto = page_address(dst_page) + dst_offs;
+		int len = min_t(int, copy_size, src_bvl->bv_len - offs);
+
+		BUG_ON(len < 0);
+		memcpy(vto, vfrom, len);
+		kunmap_atomic(vfrom, KM_USER0);
+
+		seg++;
+		offs = 0;
+		dst_offs += len;
+		copy_size -= len;
+	}
+}
+
+/*
+ * Copy all data for this packet to pkt->pages[], so that
+ * a) The number of required segments for the write bio is minimized, which
+ *    is necessary for some scsi controllers.
+ * b) The data can be used as cache to avoid read requests if we receive a
+ *    new write request for the same zone.
+ */
+static void pkt_make_local_copy(struct packet_data *pkt, struct page **pages, int *offsets)
+{
+	int f, p, offs;
+
+	/* Copy all data to pkt->pages[] */
+	p = 0;
+	offs = 0;
+	for (f = 0; f < pkt->frames; f++) {
+		if (pages[f] != pkt->pages[p]) {
+			void *vfrom = kmap_atomic(pages[f], KM_USER0) + offsets[f];
+			void *vto = page_address(pkt->pages[p]) + offs;
+			memcpy(vto, vfrom, CD_FRAMESIZE);
+			kunmap_atomic(vfrom, KM_USER0);
+			pages[f] = pkt->pages[p];
+			offsets[f] = offs;
+		} else {
+			BUG_ON(offsets[f] != offs);
+		}
+		offs += CD_FRAMESIZE;
+		if (offs >= PAGE_SIZE) {
+			offs = 0;
+			p++;
+		}
+	}
+}
+
+static int pkt_end_io_read(struct bio *bio, unsigned int bytes_done, int err)
+{
+	struct packet_data *pkt = bio->bi_private;
+	struct pktcdvd_device *pd = pkt->pd;
+	BUG_ON(!pd);
+
+	if (bio->bi_size)
+		return 1;
+
+	VPRINTK("pkt_end_io_read: bio=%p sec0=%llx sec=%llx err=%d\n", bio,
+		(unsigned long long)pkt->sector, (unsigned long long)bio->bi_sector, err);
+
+	if (err)
+		atomic_inc(&pkt->io_errors);
+	if (atomic_dec_and_test(&pkt->io_wait)) {
+		atomic_inc(&pkt->run_sm);
+		wake_up(&pd->wqueue);
+	}
+	pkt_bio_finished(pd);
+
+	return 0;
+}
+
+static int pkt_end_io_packet_write(struct bio *bio, unsigned int bytes_done, int err)
+{
+	struct packet_data *pkt = bio->bi_private;
+	struct pktcdvd_device *pd = pkt->pd;
+	BUG_ON(!pd);
+
+	if (bio->bi_size)
+		return 1;
+
+	VPRINTK("pkt_end_io_packet_write: id=%d, err=%d\n", pkt->id, err);
+
+	pd->stats.pkt_ended++;
+
+	pkt_bio_finished(pd);
+	atomic_dec(&pkt->io_wait);
+	atomic_inc(&pkt->run_sm);
+	wake_up(&pd->wqueue);
+	return 0;
+}
+
+/*
+ * Schedule reads for the holes in a packet
+ */
+static void pkt_gather_data(struct pktcdvd_device *pd, struct packet_data *pkt)
+{
+	int frames_read = 0;
+	struct bio *bio;
+	int f;
+	char written[PACKET_MAX_SIZE];
+
+	BUG_ON(!pkt->orig_bios);
+
+	atomic_set(&pkt->io_wait, 0);
+	atomic_set(&pkt->io_errors, 0);
+
+	/*
+	 * Figure out which frames we need to read before we can write.
+	 */
+	memset(written, 0, sizeof(written));
+	spin_lock(&pkt->lock);
+	for (bio = pkt->orig_bios; bio; bio = bio->bi_next) {
+		int first_frame = (bio->bi_sector - pkt->sector) / (CD_FRAMESIZE >> 9);
+		int num_frames = bio->bi_size / CD_FRAMESIZE;
+		pd->stats.secs_w += num_frames * (CD_FRAMESIZE >> 9);
+		BUG_ON(first_frame < 0);
+		BUG_ON(first_frame + num_frames > pkt->frames);
+		for (f = first_frame; f < first_frame + num_frames; f++)
+			written[f] = 1;
+	}
+	spin_unlock(&pkt->lock);
+
+	if (pkt->cache_valid) {
+		VPRINTK("pkt_gather_data: zone %llx cached\n",
+			(unsigned long long)pkt->sector);
+		goto out_account;
+	}
+
+	/*
+	 * Schedule reads for missing parts of the packet.
+	 */
+	for (f = 0; f < pkt->frames; f++) {
+		int p, offset;
+		if (written[f])
+			continue;
+		bio = pkt->r_bios[f];
+		bio_init(bio);
+		bio->bi_max_vecs = 1;
+		bio->bi_sector = pkt->sector + f * (CD_FRAMESIZE >> 9);
+		bio->bi_bdev = pd->bdev;
+		bio->bi_end_io = pkt_end_io_read;
+		bio->bi_private = pkt;
+
+		p = (f * CD_FRAMESIZE) / PAGE_SIZE;
+		offset = (f * CD_FRAMESIZE) % PAGE_SIZE;
+		VPRINTK("pkt_gather_data: Adding frame %d, page:%p offs:%d\n",
+			f, pkt->pages[p], offset);
+		if (!bio_add_page(bio, pkt->pages[p], CD_FRAMESIZE, offset))
+			BUG();
+
+		atomic_inc(&pkt->io_wait);
+		bio->bi_rw = READ;
+		pkt_queue_bio(pd, bio);
+		frames_read++;
+	}
+
+out_account:
+	VPRINTK("pkt_gather_data: need %d frames for zone %llx\n",
+		frames_read, (unsigned long long)pkt->sector);
+	pd->stats.pkt_started++;
+	pd->stats.secs_rg += frames_read * (CD_FRAMESIZE >> 9);
+}
+
+/*
+ * Find a packet matching zone, or the least recently used packet if
+ * there is no match.
+ */
+static struct packet_data *pkt_get_packet_data(struct pktcdvd_device *pd, int zone)
+{
+	struct packet_data *pkt;
+
+	list_for_each_entry(pkt, &pd->cdrw.pkt_free_list, list) {
+		if (pkt->sector == zone || pkt->list.next == &pd->cdrw.pkt_free_list) {
+			list_del_init(&pkt->list);
+			if (pkt->sector != zone)
+				pkt->cache_valid = 0;
+			return pkt;
+		}
+	}
+	BUG();
+	return NULL;
+}
+
+static void pkt_put_packet_data(struct pktcdvd_device *pd, struct packet_data *pkt)
+{
+	if (pkt->cache_valid) {
+		list_add(&pkt->list, &pd->cdrw.pkt_free_list);
+	} else {
+		list_add_tail(&pkt->list, &pd->cdrw.pkt_free_list);
+	}
+}
+
+/*
+ * recover a failed write, query for relocation if possible
+ *
+ * returns 1 if recovery is possible, or 0 if not
+ *
+ */
+static int pkt_start_recovery(struct packet_data *pkt)
+{
+	/*
+	 * FIXME. We need help from the file system to implement
+	 * recovery handling.
+	 */
+	return 0;
+#if 0
+	struct request *rq = pkt->rq;
+	struct pktcdvd_device *pd = rq->rq_disk->private_data;
+	struct block_device *pkt_bdev;
+	struct super_block *sb = NULL;
+	unsigned long old_block, new_block;
+	sector_t new_sector;
+
+	pkt_bdev = bdget(kdev_t_to_nr(pd->pkt_dev));
+	if (pkt_bdev) {
+		sb = get_super(pkt_bdev);
+		bdput(pkt_bdev);
+	}
+
+	if (!sb)
+		return 0;
+
+	if (!sb->s_op || !sb->s_op->relocate_blocks)
+		goto out;
+
+	old_block = pkt->sector / (CD_FRAMESIZE >> 9);
+	if (sb->s_op->relocate_blocks(sb, old_block, &new_block))
+		goto out;
+
+	new_sector = new_block * (CD_FRAMESIZE >> 9);
+	pkt->sector = new_sector;
+
+	pkt->bio->bi_sector = new_sector;
+	pkt->bio->bi_next = NULL;
+	pkt->bio->bi_flags = 1 << BIO_UPTODATE;
+	pkt->bio->bi_idx = 0;
+
+	BUG_ON(pkt->bio->bi_rw != (1 << BIO_RW));
+	BUG_ON(pkt->bio->bi_vcnt != pkt->frames);
+	BUG_ON(pkt->bio->bi_size != pkt->frames * CD_FRAMESIZE);
+	BUG_ON(pkt->bio->bi_end_io != pkt_end_io_packet_write);
+	BUG_ON(pkt->bio->bi_private != pkt);
+
+	drop_super(sb);
+	return 1;
+
+out:
+	drop_super(sb);
+	return 0;
+#endif
+}
+
+static inline void pkt_set_state(struct packet_data *pkt, enum packet_data_state state)
+{
+#if PACKET_DEBUG > 1
+	static const char *state_name[] = {
+		"IDLE", "WAITING", "READ_WAIT", "WRITE_WAIT", "RECOVERY", "FINISHED"
+	};
+	enum packet_data_state old_state = pkt->state;
+	VPRINTK("pkt %2d : s=%6llx %s -> %s\n", pkt->id, (unsigned long long)pkt->sector,
+		state_name[old_state], state_name[state]);
+#endif
+	pkt->state = state;
+}
+
+/*
+ * Scan the work queue to see if we can start a new packet.
+ * returns non-zero if any work was done.
+ */
+static int pkt_handle_queue(struct pktcdvd_device *pd)
+{
+	struct packet_data *pkt, *p;
+	struct bio *bio = NULL;
+	sector_t zone = 0; /* Suppress gcc warning */
+	struct pkt_rb_node *node, *first_node;
+	struct rb_node *n;
+
+	VPRINTK("handle_queue\n");
+
+	atomic_set(&pd->scan_queue, 0);
+
+	if (list_empty(&pd->cdrw.pkt_free_list)) {
+		VPRINTK("handle_queue: no pkt\n");
+		return 0;
+	}
+
+	/*
+	 * Try to find a zone we are not already working on.
+	 */
+	spin_lock(&pd->lock);
+	first_node = pkt_rbtree_find(pd, pd->current_sector);
+	if (!first_node) {
+		n = rb_first(&pd->bio_queue);
+		if (n)
+			first_node = rb_entry(n, struct pkt_rb_node, rb_node);
+	}
+	node = first_node;
+	while (node) {
+		bio = node->bio;
+		zone = ZONE(bio->bi_sector, pd);
+		list_for_each_entry(p, &pd->cdrw.pkt_active_list, list) {
+			if (p->sector == zone) {
+				bio = NULL;
+				goto try_next_bio;
+			}
+		}
+		break;
+try_next_bio:
+		node = pkt_rbtree_next(node);
+		if (!node) {
+			n = rb_first(&pd->bio_queue);
+			if (n)
+				node = rb_entry(n, struct pkt_rb_node, rb_node);
+		}
+		if (node == first_node)
+			node = NULL;
+	}
+	spin_unlock(&pd->lock);
+	if (!bio) {
+		VPRINTK("handle_queue: no bio\n");
+		return 0;
+	}
+
+	pkt = pkt_get_packet_data(pd, zone);
+
+	pd->current_sector = zone + pd->settings.size;
+	pkt->sector = zone;
+	pkt->frames = pd->settings.size >> 2;
+	pkt->write_size = 0;
+
+	/*
+	 * Scan work queue for bios in the same zone and link them
+	 * to this packet.
+	 */
+	spin_lock(&pd->lock);
+	VPRINTK("pkt_handle_queue: looking for zone %llx\n", (unsigned long long)zone);
+	while ((node = pkt_rbtree_find(pd, zone)) != NULL) {
+		bio = node->bio;
+		VPRINTK("pkt_handle_queue: found zone=%llx\n",
+			(unsigned long long)ZONE(bio->bi_sector, pd));
+		if (ZONE(bio->bi_sector, pd) != zone)
+			break;
+		pkt_rbtree_erase(pd, node);
+		spin_lock(&pkt->lock);
+		pkt_add_list_last(bio, &pkt->orig_bios, &pkt->orig_bios_tail);
+		pkt->write_size += bio->bi_size / CD_FRAMESIZE;
+		spin_unlock(&pkt->lock);
+	}
+	spin_unlock(&pd->lock);
+
+	pkt->sleep_time = max(PACKET_WAIT_TIME, 1);
+	pkt_set_state(pkt, PACKET_WAITING_STATE);
+	atomic_set(&pkt->run_sm, 1);
+
+	spin_lock(&pd->cdrw.active_list_lock);
+	list_add(&pkt->list, &pd->cdrw.pkt_active_list);
+	spin_unlock(&pd->cdrw.active_list_lock);
+
+	return 1;
+}
+
+/*
+ * Assemble a bio to write one packet and queue the bio for processing
+ * by the underlying block device.
+ */
+static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt)
+{
+	struct bio *bio;
+	struct page *pages[PACKET_MAX_SIZE];
+	int offsets[PACKET_MAX_SIZE];
+	int f;
+	int frames_write;
+
+	for (f = 0; f < pkt->frames; f++) {
+		pages[f] = pkt->pages[(f * CD_FRAMESIZE) / PAGE_SIZE];
+		offsets[f] = (f * CD_FRAMESIZE) % PAGE_SIZE;
+	}
+
+	/*
+	 * Fill-in pages[] and offsets[] with data from orig_bios.
+	 */
+	frames_write = 0;
+	spin_lock(&pkt->lock);
+	for (bio = pkt->orig_bios; bio; bio = bio->bi_next) {
+		int segment = bio->bi_idx;
+		int src_offs = 0;
+		int first_frame = (bio->bi_sector - pkt->sector) / (CD_FRAMESIZE >> 9);
+		int num_frames = bio->bi_size / CD_FRAMESIZE;
+		BUG_ON(first_frame < 0);
+		BUG_ON(first_frame + num_frames > pkt->frames);
+		for (f = first_frame; f < first_frame + num_frames; f++) {
+			struct bio_vec *src_bvl = bio_iovec_idx(bio, segment);
+
+			while (src_offs >= src_bvl->bv_len) {
+				src_offs -= src_bvl->bv_len;
+				segment++;
+				BUG_ON(segment >= bio->bi_vcnt);
+				src_bvl = bio_iovec_idx(bio, segment);
+			}
+
+			if (src_bvl->bv_len - src_offs >= CD_FRAMESIZE) {
+				pages[f] = src_bvl->bv_page;
+				offsets[f] = src_bvl->bv_offset + src_offs;
+			} else {
+				pkt_copy_bio_data(bio, segment, src_offs,
+						  pages[f], offsets[f]);
+			}
+			src_offs += CD_FRAMESIZE;
+			frames_write++;
+		}
+	}
+	pkt_set_state(pkt, PACKET_WRITE_WAIT_STATE);
+	spin_unlock(&pkt->lock);
+
+	VPRINTK("pkt_start_write: Writing %d frames for zone %llx\n",
+		frames_write, (unsigned long long)pkt->sector);
+	BUG_ON(frames_write != pkt->write_size);
+
+	if (test_bit(PACKET_MERGE_SEGS, &pd->flags) || (pkt->write_size < pkt->frames)) {
+		pkt_make_local_copy(pkt, pages, offsets);
+		pkt->cache_valid = 1;
+	} else {
+		pkt->cache_valid = 0;
+	}
+
+	/* Start the write request */
+	bio_init(pkt->w_bio);
+	pkt->w_bio->bi_max_vecs = PACKET_MAX_SIZE;
+	pkt->w_bio->bi_sector = pkt->sector;
+	pkt->w_bio->bi_bdev = pd->bdev;
+	pkt->w_bio->bi_end_io = pkt_end_io_packet_write;
+	pkt->w_bio->bi_private = pkt;
+	for (f = 0; f < pkt->frames; f++) {
+		if ((f + 1 < pkt->frames) && (pages[f + 1] == pages[f]) &&
+		    (offsets[f + 1] = offsets[f] + CD_FRAMESIZE)) {
+			if (!bio_add_page(pkt->w_bio, pages[f], CD_FRAMESIZE * 2, offsets[f]))
+				BUG();
+			f++;
+		} else {
+			if (!bio_add_page(pkt->w_bio, pages[f], CD_FRAMESIZE, offsets[f]))
+				BUG();
+		}
+	}
+	VPRINTK("pktcdvd: vcnt=%d\n", pkt->w_bio->bi_vcnt);
+
+	atomic_set(&pkt->io_wait, 1);
+	pkt->w_bio->bi_rw = WRITE;
+	pkt_queue_bio(pd, pkt->w_bio);
+}
+
+static void pkt_finish_packet(struct packet_data *pkt, int uptodate)
+{
+	struct bio *bio, *next;
+
+	if (!uptodate)
+		pkt->cache_valid = 0;
+
+	/* Finish all bios corresponding to this packet */
+	bio = pkt->orig_bios;
+	while (bio) {
+		next = bio->bi_next;
+		bio->bi_next = NULL;
+		bio_endio(bio, bio->bi_size, uptodate ? 0 : -EIO);
+		bio = next;
+	}
+	pkt->orig_bios = pkt->orig_bios_tail = NULL;
+}
+
+static void pkt_run_state_machine(struct pktcdvd_device *pd, struct packet_data *pkt)
+{
+	int uptodate;
+
+	VPRINTK("run_state_machine: pkt %d\n", pkt->id);
+
+	for (;;) {
+		switch (pkt->state) {
+		case PACKET_WAITING_STATE:
+			if ((pkt->write_size < pkt->frames) && (pkt->sleep_time > 0))
+				return;
+
+			pkt->sleep_time = 0;
+			pkt_gather_data(pd, pkt);
+			pkt_set_state(pkt, PACKET_READ_WAIT_STATE);
+			break;
+
+		case PACKET_READ_WAIT_STATE:
+			if (atomic_read(&pkt->io_wait) > 0)
+				return;
+
+			if (atomic_read(&pkt->io_errors) > 0) {
+				pkt_set_state(pkt, PACKET_RECOVERY_STATE);
+			} else {
+				pkt_start_write(pd, pkt);
+			}
+			break;
+
+		case PACKET_WRITE_WAIT_STATE:
+			if (atomic_read(&pkt->io_wait) > 0)
+				return;
+
+			if (test_bit(BIO_UPTODATE, &pkt->w_bio->bi_flags)) {
+				pkt_set_state(pkt, PACKET_FINISHED_STATE);
+			} else {
+				pkt_set_state(pkt, PACKET_RECOVERY_STATE);
+			}
+			break;
+
+		case PACKET_RECOVERY_STATE:
+			if (pkt_start_recovery(pkt)) {
+				pkt_start_write(pd, pkt);
+			} else {
+				VPRINTK("No recovery possible\n");
+				pkt_set_state(pkt, PACKET_FINISHED_STATE);
+			}
+			break;
+
+		case PACKET_FINISHED_STATE:
+			uptodate = test_bit(BIO_UPTODATE, &pkt->w_bio->bi_flags);
+			pkt_finish_packet(pkt, uptodate);
+			return;
+
+		default:
+			BUG();
+			break;
+		}
+	}
+}
+
+static void pkt_handle_packets(struct pktcdvd_device *pd)
+{
+	struct packet_data *pkt, *next;
+
+	VPRINTK("pkt_handle_packets\n");
+
+	/*
+	 * Run state machine for active packets
+	 */
+	list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+		if (atomic_read(&pkt->run_sm) > 0) {
+			atomic_set(&pkt->run_sm, 0);
+			pkt_run_state_machine(pd, pkt);
+		}
+	}
+
+	/*
+	 * Move no longer active packets to the free list
+	 */
+	spin_lock(&pd->cdrw.active_list_lock);
+	list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_active_list, list) {
+		if (pkt->state == PACKET_FINISHED_STATE) {
+			list_del(&pkt->list);
+			pkt_put_packet_data(pd, pkt);
+			pkt_set_state(pkt, PACKET_IDLE_STATE);
+			atomic_set(&pd->scan_queue, 1);
+		}
+	}
+	spin_unlock(&pd->cdrw.active_list_lock);
+}
+
+static void pkt_count_states(struct pktcdvd_device *pd, int *states)
+{
+	struct packet_data *pkt;
+	int i;
+
+	for (i = 0; i < PACKET_NUM_STATES; i++)
+		states[i] = 0;
+
+	spin_lock(&pd->cdrw.active_list_lock);
+	list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+		states[pkt->state]++;
+	}
+	spin_unlock(&pd->cdrw.active_list_lock);
+}
+
+/*
+ * kcdrwd is woken up when writes have been queued for one of our
+ * registered devices
+ */
+static int kcdrwd(void *foobar)
+{
+	struct pktcdvd_device *pd = foobar;
+	struct packet_data *pkt;
+	long min_sleep_time, residue;
+
+	set_user_nice(current, -20);
+
+	for (;;) {
+		DECLARE_WAITQUEUE(wait, current);
+
+		/*
+		 * Wait until there is something to do
+		 */
+		add_wait_queue(&pd->wqueue, &wait);
+		for (;;) {
+			set_current_state(TASK_INTERRUPTIBLE);
+
+			/* Check if we need to run pkt_handle_queue */
+			if (atomic_read(&pd->scan_queue) > 0)
+				goto work_to_do;
+
+			/* Check if we need to run the state machine for some packet */
+			list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+				if (atomic_read(&pkt->run_sm) > 0)
+					goto work_to_do;
+			}
+
+			/* Check if we need to process the iosched queues */
+			if (atomic_read(&pd->iosched.attention) != 0)
+				goto work_to_do;
+
+			/* Otherwise, go to sleep */
+			if (PACKET_DEBUG > 1) {
+				int states[PACKET_NUM_STATES];
+				pkt_count_states(pd, states);
+				VPRINTK("kcdrwd: i:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n",
+					states[0], states[1], states[2], states[3],
+					states[4], states[5]);
+			}
+
+			min_sleep_time = MAX_SCHEDULE_TIMEOUT;
+			list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+				if (pkt->sleep_time && pkt->sleep_time < min_sleep_time)
+					min_sleep_time = pkt->sleep_time;
+			}
+
+			generic_unplug_device(bdev_get_queue(pd->bdev));
+
+			VPRINTK("kcdrwd: sleeping\n");
+			residue = schedule_timeout(min_sleep_time);
+			VPRINTK("kcdrwd: wake up\n");
+
+			/* make swsusp happy with our thread */
+			try_to_freeze();
+
+			list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+				if (!pkt->sleep_time)
+					continue;
+				pkt->sleep_time -= min_sleep_time - residue;
+				if (pkt->sleep_time <= 0) {
+					pkt->sleep_time = 0;
+					atomic_inc(&pkt->run_sm);
+				}
+			}
+
+			if (signal_pending(current)) {
+				flush_signals(current);
+			}
+			if (kthread_should_stop())
+				break;
+		}
+work_to_do:
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&pd->wqueue, &wait);
+
+		if (kthread_should_stop())
+			break;
+
+		/*
+		 * if pkt_handle_queue returns true, we can queue
+		 * another request.
+		 */
+		while (pkt_handle_queue(pd))
+			;
+
+		/*
+		 * Handle packet state machine
+		 */
+		pkt_handle_packets(pd);
+
+		/*
+		 * Handle iosched queues
+		 */
+		pkt_iosched_process_queue(pd);
+	}
+
+	return 0;
+}
+
+static void pkt_print_settings(struct pktcdvd_device *pd)
+{
+	printk("pktcdvd: %s packets, ", pd->settings.fp ? "Fixed" : "Variable");
+	printk("%u blocks, ", pd->settings.size >> 2);
+	printk("Mode-%c disc\n", pd->settings.block_mode == 8 ? '1' : '2');
+}
+
+static int pkt_mode_sense(struct pktcdvd_device *pd, struct packet_command *cgc, int page_code, int page_control)
+{
+	memset(cgc->cmd, 0, sizeof(cgc->cmd));
+
+	cgc->cmd[0] = GPCMD_MODE_SENSE_10;
+	cgc->cmd[2] = page_code | (page_control << 6);
+	cgc->cmd[7] = cgc->buflen >> 8;
+	cgc->cmd[8] = cgc->buflen & 0xff;
+	cgc->data_direction = CGC_DATA_READ;
+	return pkt_generic_packet(pd, cgc);
+}
+
+static int pkt_mode_select(struct pktcdvd_device *pd, struct packet_command *cgc)
+{
+	memset(cgc->cmd, 0, sizeof(cgc->cmd));
+	memset(cgc->buffer, 0, 2);
+	cgc->cmd[0] = GPCMD_MODE_SELECT_10;
+	cgc->cmd[1] = 0x10;		/* PF */
+	cgc->cmd[7] = cgc->buflen >> 8;
+	cgc->cmd[8] = cgc->buflen & 0xff;
+	cgc->data_direction = CGC_DATA_WRITE;
+	return pkt_generic_packet(pd, cgc);
+}
+
+static int pkt_get_disc_info(struct pktcdvd_device *pd, disc_information *di)
+{
+	struct packet_command cgc;
+	int ret;
+
+	/* set up command and get the disc info */
+	init_cdrom_command(&cgc, di, sizeof(*di), CGC_DATA_READ);
+	cgc.cmd[0] = GPCMD_READ_DISC_INFO;
+	cgc.cmd[8] = cgc.buflen = 2;
+	cgc.quiet = 1;
+
+	if ((ret = pkt_generic_packet(pd, &cgc)))
+		return ret;
+
+	/* not all drives have the same disc_info length, so requeue
+	 * packet with the length the drive tells us it can supply
+	 */
+	cgc.buflen = be16_to_cpu(di->disc_information_length) +
+		     sizeof(di->disc_information_length);
+
+	if (cgc.buflen > sizeof(disc_information))
+		cgc.buflen = sizeof(disc_information);
+
+	cgc.cmd[8] = cgc.buflen;
+	return pkt_generic_packet(pd, &cgc);
+}
+
+static int pkt_get_track_info(struct pktcdvd_device *pd, __u16 track, __u8 type, track_information *ti)
+{
+	struct packet_command cgc;
+	int ret;
+
+	init_cdrom_command(&cgc, ti, 8, CGC_DATA_READ);
+	cgc.cmd[0] = GPCMD_READ_TRACK_RZONE_INFO;
+	cgc.cmd[1] = type & 3;
+	cgc.cmd[4] = (track & 0xff00) >> 8;
+	cgc.cmd[5] = track & 0xff;
+	cgc.cmd[8] = 8;
+	cgc.quiet = 1;
+
+	if ((ret = pkt_generic_packet(pd, &cgc)))
+		return ret;
+
+	cgc.buflen = be16_to_cpu(ti->track_information_length) +
+		     sizeof(ti->track_information_length);
+
+	if (cgc.buflen > sizeof(track_information))
+		cgc.buflen = sizeof(track_information);
+
+	cgc.cmd[8] = cgc.buflen;
+	return pkt_generic_packet(pd, &cgc);
+}
+
+static int pkt_get_last_written(struct pktcdvd_device *pd, long *last_written)
+{
+	disc_information di;
+	track_information ti;
+	__u32 last_track;
+	int ret = -1;
+
+	if ((ret = pkt_get_disc_info(pd, &di)))
+		return ret;
+
+	last_track = (di.last_track_msb << 8) | di.last_track_lsb;
+	if ((ret = pkt_get_track_info(pd, last_track, 1, &ti)))
+		return ret;
+
+	/* if this track is blank, try the previous. */
+	if (ti.blank) {
+		last_track--;
+		if ((ret = pkt_get_track_info(pd, last_track, 1, &ti)))
+			return ret;
+	}
+
+	/* if last recorded field is valid, return it. */
+	if (ti.lra_v) {
+		*last_written = be32_to_cpu(ti.last_rec_address);
+	} else {
+		/* make it up instead */
+		*last_written = be32_to_cpu(ti.track_start) +
+				be32_to_cpu(ti.track_size);
+		if (ti.free_blocks)
+			*last_written -= (be32_to_cpu(ti.free_blocks) + 7);
+	}
+	return 0;
+}
+
+/*
+ * write mode select package based on pd->settings
+ */
+static int pkt_set_write_settings(struct pktcdvd_device *pd)
+{
+	struct packet_command cgc;
+	struct request_sense sense;
+	write_param_page *wp;
+	char buffer[128];
+	int ret, size;
+
+	/* doesn't apply to DVD+RW or DVD-RAM */
+	if ((pd->mmc3_profile == 0x1a) || (pd->mmc3_profile == 0x12))
+		return 0;
+
+	memset(buffer, 0, sizeof(buffer));
+	init_cdrom_command(&cgc, buffer, sizeof(*wp), CGC_DATA_READ);
+	cgc.sense = &sense;
+	if ((ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0))) {
+		pkt_dump_sense(&cgc);
+		return ret;
+	}
+
+	size = 2 + ((buffer[0] << 8) | (buffer[1] & 0xff));
+	pd->mode_offset = (buffer[6] << 8) | (buffer[7] & 0xff);
+	if (size > sizeof(buffer))
+		size = sizeof(buffer);
+
+	/*
+	 * now get it all
+	 */
+	init_cdrom_command(&cgc, buffer, size, CGC_DATA_READ);
+	cgc.sense = &sense;
+	if ((ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0))) {
+		pkt_dump_sense(&cgc);
+		return ret;
+	}
+
+	/*
+	 * write page is offset header + block descriptor length
+	 */
+	wp = (write_param_page *) &buffer[sizeof(struct mode_page_header) + pd->mode_offset];
+
+	wp->fp = pd->settings.fp;
+	wp->track_mode = pd->settings.track_mode;
+	wp->write_type = pd->settings.write_type;
+	wp->data_block_type = pd->settings.block_mode;
+
+	wp->multi_session = 0;
+
+#ifdef PACKET_USE_LS
+	wp->link_size = 7;
+	wp->ls_v = 1;
+#endif
+
+	if (wp->data_block_type == PACKET_BLOCK_MODE1) {
+		wp->session_format = 0;
+		wp->subhdr2 = 0x20;
+	} else if (wp->data_block_type == PACKET_BLOCK_MODE2) {
+		wp->session_format = 0x20;
+		wp->subhdr2 = 8;
+#if 0
+		wp->mcn[0] = 0x80;
+		memcpy(&wp->mcn[1], PACKET_MCN, sizeof(wp->mcn) - 1);
+#endif
+	} else {
+		/*
+		 * paranoia
+		 */
+		printk("pktcdvd: write mode wrong %d\n", wp->data_block_type);
+		return 1;
+	}
+	wp->packet_size = cpu_to_be32(pd->settings.size >> 2);
+
+	cgc.buflen = cgc.cmd[8] = size;
+	if ((ret = pkt_mode_select(pd, &cgc))) {
+		pkt_dump_sense(&cgc);
+		return ret;
+	}
+
+	pkt_print_settings(pd);
+	return 0;
+}
+
+/*
+ * 0 -- we can write to this track, 1 -- we can't
+ */
+static int pkt_good_track(track_information *ti)
+{
+	/*
+	 * only good for CD-RW at the moment, not DVD-RW
+	 */
+
+	/*
+	 * FIXME: only for FP
+	 */
+	if (ti->fp == 0)
+		return 0;
+
+	/*
+	 * "good" settings as per Mt Fuji.
+	 */
+	if (ti->rt == 0 && ti->blank == 0 && ti->packet == 1)
+		return 0;
+
+	if (ti->rt == 0 && ti->blank == 1 && ti->packet == 1)
+		return 0;
+
+	if (ti->rt == 1 && ti->blank == 0 && ti->packet == 1)
+		return 0;
+
+	printk("pktcdvd: bad state %d-%d-%d\n", ti->rt, ti->blank, ti->packet);
+	return 1;
+}
+
+/*
+ * 0 -- we can write to this disc, 1 -- we can't
+ */
+static int pkt_good_disc(struct pktcdvd_device *pd, disc_information *di)
+{
+	switch (pd->mmc3_profile) {
+		case 0x0a: /* CD-RW */
+		case 0xffff: /* MMC3 not supported */
+			break;
+		case 0x1a: /* DVD+RW */
+		case 0x13: /* DVD-RW */
+		case 0x12: /* DVD-RAM */
+			return 0;
+		default:
+			printk("pktcdvd: Wrong disc profile (%x)\n", pd->mmc3_profile);
+			return 1;
+	}
+
+	/*
+	 * for disc type 0xff we should probably reserve a new track.
+	 * but i'm not sure, should we leave this to user apps? probably.
+	 */
+	if (di->disc_type == 0xff) {
+		printk("pktcdvd: Unknown disc. No track?\n");
+		return 1;
+	}
+
+	if (di->disc_type != 0x20 && di->disc_type != 0) {
+		printk("pktcdvd: Wrong disc type (%x)\n", di->disc_type);
+		return 1;
+	}
+
+	if (di->erasable == 0) {
+		printk("pktcdvd: Disc not erasable\n");
+		return 1;
+	}
+
+	if (di->border_status == PACKET_SESSION_RESERVED) {
+		printk("pktcdvd: Can't write to last track (reserved)\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+static int pkt_probe_settings(struct pktcdvd_device *pd)
+{
+	struct packet_command cgc;
+	unsigned char buf[12];
+	disc_information di;
+	track_information ti;
+	int ret, track;
+
+	init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
+	cgc.cmd[0] = GPCMD_GET_CONFIGURATION;
+	cgc.cmd[8] = 8;
+	ret = pkt_generic_packet(pd, &cgc);
+	pd->mmc3_profile = ret ? 0xffff : buf[6] << 8 | buf[7];
+
+	memset(&di, 0, sizeof(disc_information));
+	memset(&ti, 0, sizeof(track_information));
+
+	if ((ret = pkt_get_disc_info(pd, &di))) {
+		printk("failed get_disc\n");
+		return ret;
+	}
+
+	if (pkt_good_disc(pd, &di))
+		return -ENXIO;
+
+	switch (pd->mmc3_profile) {
+		case 0x1a: /* DVD+RW */
+			printk("pktcdvd: inserted media is DVD+RW\n");
+			break;
+		case 0x13: /* DVD-RW */
+			printk("pktcdvd: inserted media is DVD-RW\n");
+			break;
+		case 0x12: /* DVD-RAM */
+			printk("pktcdvd: inserted media is DVD-RAM\n");
+			break;
+		default:
+			printk("pktcdvd: inserted media is CD-R%s\n", di.erasable ? "W" : "");
+			break;
+	}
+	pd->type = di.erasable ? PACKET_CDRW : PACKET_CDR;
+
+	track = 1; /* (di.last_track_msb << 8) | di.last_track_lsb; */
+	if ((ret = pkt_get_track_info(pd, track, 1, &ti))) {
+		printk("pktcdvd: failed get_track\n");
+		return ret;
+	}
+
+	if (pkt_good_track(&ti)) {
+		printk("pktcdvd: can't write to this track\n");
+		return -ENXIO;
+	}
+
+	/*
+	 * we keep packet size in 512 byte units, makes it easier to
+	 * deal with request calculations.
+	 */
+	pd->settings.size = be32_to_cpu(ti.fixed_packet_size) << 2;
+	if (pd->settings.size == 0) {
+		printk("pktcdvd: detected zero packet size!\n");
+		pd->settings.size = 128;
+	}
+	if (pd->settings.size > PACKET_MAX_SECTORS) {
+		printk("pktcdvd: packet size is too big\n");
+		return -ENXIO;
+	}
+	pd->settings.fp = ti.fp;
+	pd->offset = (be32_to_cpu(ti.track_start) << 2) & (pd->settings.size - 1);
+
+	if (ti.nwa_v) {
+		pd->nwa = be32_to_cpu(ti.next_writable);
+		set_bit(PACKET_NWA_VALID, &pd->flags);
+	}
+
+	/*
+	 * in theory we could use lra on -RW media as well and just zero
+	 * blocks that haven't been written yet, but in practice that
+	 * is just a no-go. we'll use that for -R, naturally.
+	 */
+	if (ti.lra_v) {
+		pd->lra = be32_to_cpu(ti.last_rec_address);
+		set_bit(PACKET_LRA_VALID, &pd->flags);
+	} else {
+		pd->lra = 0xffffffff;
+		set_bit(PACKET_LRA_VALID, &pd->flags);
+	}
+
+	/*
+	 * fine for now
+	 */
+	pd->settings.link_loss = 7;
+	pd->settings.write_type = 0;	/* packet */
+	pd->settings.track_mode = ti.track_mode;
+
+	/*
+	 * mode1 or mode2 disc
+	 */
+	switch (ti.data_mode) {
+		case PACKET_MODE1:
+			pd->settings.block_mode = PACKET_BLOCK_MODE1;
+			break;
+		case PACKET_MODE2:
+			pd->settings.block_mode = PACKET_BLOCK_MODE2;
+			break;
+		default:
+			printk("pktcdvd: unknown data mode\n");
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * enable/disable write caching on drive
+ */
+static int pkt_write_caching(struct pktcdvd_device *pd, int set)
+{
+	struct packet_command cgc;
+	struct request_sense sense;
+	unsigned char buf[64];
+	int ret;
+
+	memset(buf, 0, sizeof(buf));
+	init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
+	cgc.sense = &sense;
+	cgc.buflen = pd->mode_offset + 12;
+
+	/*
+	 * caching mode page might not be there, so quiet this command
+	 */
+	cgc.quiet = 1;
+
+	if ((ret = pkt_mode_sense(pd, &cgc, GPMODE_WCACHING_PAGE, 0)))
+		return ret;
+
+	buf[pd->mode_offset + 10] |= (!!set << 2);
+
+	cgc.buflen = cgc.cmd[8] = 2 + ((buf[0] << 8) | (buf[1] & 0xff));
+	ret = pkt_mode_select(pd, &cgc);
+	if (ret) {
+		printk("pktcdvd: write caching control failed\n");
+		pkt_dump_sense(&cgc);
+	} else if (!ret && set)
+		printk("pktcdvd: enabled write caching on %s\n", pd->name);
+	return ret;
+}
+
+static int pkt_lock_door(struct pktcdvd_device *pd, int lockflag)
+{
+	struct packet_command cgc;
+
+	init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+	cgc.cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
+	cgc.cmd[4] = lockflag ? 1 : 0;
+	return pkt_generic_packet(pd, &cgc);
+}
+
+/*
+ * Returns drive maximum write speed
+ */
+static int pkt_get_max_speed(struct pktcdvd_device *pd, unsigned *write_speed)
+{
+	struct packet_command cgc;
+	struct request_sense sense;
+	unsigned char buf[256+18];
+	unsigned char *cap_buf;
+	int ret, offset;
+
+	memset(buf, 0, sizeof(buf));
+	cap_buf = &buf[sizeof(struct mode_page_header) + pd->mode_offset];
+	init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_UNKNOWN);
+	cgc.sense = &sense;
+
+	ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
+	if (ret) {
+		cgc.buflen = pd->mode_offset + cap_buf[1] + 2 +
+			     sizeof(struct mode_page_header);
+		ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
+		if (ret) {
+			pkt_dump_sense(&cgc);
+			return ret;
+		}
+	}
+
+	offset = 20;			    /* Obsoleted field, used by older drives */
+	if (cap_buf[1] >= 28)
+		offset = 28;		    /* Current write speed selected */
+	if (cap_buf[1] >= 30) {
+		/* If the drive reports at least one "Logical Unit Write
+		 * Speed Performance Descriptor Block", use the information
+		 * in the first block. (contains the highest speed)
+		 */
+		int num_spdb = (cap_buf[30] << 8) + cap_buf[31];
+		if (num_spdb > 0)
+			offset = 34;
+	}
+
+	*write_speed = (cap_buf[offset] << 8) | cap_buf[offset + 1];
+	return 0;
+}
+
+/* These tables from cdrecord - I don't have orange book */
+/* standard speed CD-RW (1-4x) */
+static char clv_to_speed[16] = {
+	/* 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 */
+	   0, 2, 4, 6, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+/* high speed CD-RW (-10x) */
+static char hs_clv_to_speed[16] = {
+	/* 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 */
+	   0, 2, 4, 6, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+/* ultra high speed CD-RW */
+static char us_clv_to_speed[16] = {
+	/* 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 */
+	   0, 2, 4, 8, 0, 0,16, 0,24,32,40,48, 0, 0, 0, 0
+};
+
+/*
+ * reads the maximum media speed from ATIP
+ */
+static int pkt_media_speed(struct pktcdvd_device *pd, unsigned *speed)
+{
+	struct packet_command cgc;
+	struct request_sense sense;
+	unsigned char buf[64];
+	unsigned int size, st, sp;
+	int ret;
+
+	init_cdrom_command(&cgc, buf, 2, CGC_DATA_READ);
+	cgc.sense = &sense;
+	cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
+	cgc.cmd[1] = 2;
+	cgc.cmd[2] = 4; /* READ ATIP */
+	cgc.cmd[8] = 2;
+	ret = pkt_generic_packet(pd, &cgc);
+	if (ret) {
+		pkt_dump_sense(&cgc);
+		return ret;
+	}
+	size = ((unsigned int) buf[0]<<8) + buf[1] + 2;
+	if (size > sizeof(buf))
+		size = sizeof(buf);
+
+	init_cdrom_command(&cgc, buf, size, CGC_DATA_READ);
+	cgc.sense = &sense;
+	cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
+	cgc.cmd[1] = 2;
+	cgc.cmd[2] = 4;
+	cgc.cmd[8] = size;
+	ret = pkt_generic_packet(pd, &cgc);
+	if (ret) {
+		pkt_dump_sense(&cgc);
+		return ret;
+	}
+
+	if (!buf[6] & 0x40) {
+		printk("pktcdvd: Disc type is not CD-RW\n");
+		return 1;
+	}
+	if (!buf[6] & 0x4) {
+		printk("pktcdvd: A1 values on media are not valid, maybe not CDRW?\n");
+		return 1;
+	}
+
+	st = (buf[6] >> 3) & 0x7; /* disc sub-type */
+
+	sp = buf[16] & 0xf; /* max speed from ATIP A1 field */
+
+	/* Info from cdrecord */
+	switch (st) {
+		case 0: /* standard speed */
+			*speed = clv_to_speed[sp];
+			break;
+		case 1: /* high speed */
+			*speed = hs_clv_to_speed[sp];
+			break;
+		case 2: /* ultra high speed */
+			*speed = us_clv_to_speed[sp];
+			break;
+		default:
+			printk("pktcdvd: Unknown disc sub-type %d\n",st);
+			return 1;
+	}
+	if (*speed) {
+		printk("pktcdvd: Max. media speed: %d\n",*speed);
+		return 0;
+	} else {
+		printk("pktcdvd: Unknown speed %d for sub-type %d\n",sp,st);
+		return 1;
+	}
+}
+
+static int pkt_perform_opc(struct pktcdvd_device *pd)
+{
+	struct packet_command cgc;
+	struct request_sense sense;
+	int ret;
+
+	VPRINTK("pktcdvd: Performing OPC\n");
+
+	init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+	cgc.sense = &sense;
+	cgc.timeout = 60*HZ;
+	cgc.cmd[0] = GPCMD_SEND_OPC;
+	cgc.cmd[1] = 1;
+	if ((ret = pkt_generic_packet(pd, &cgc)))
+		pkt_dump_sense(&cgc);
+	return ret;
+}
+
+static int pkt_open_write(struct pktcdvd_device *pd)
+{
+	int ret;
+	unsigned int write_speed, media_write_speed, read_speed;
+
+	if ((ret = pkt_probe_settings(pd))) {
+		DPRINTK("pktcdvd: %s failed probe\n", pd->name);
+		return -EIO;
+	}
+
+	if ((ret = pkt_set_write_settings(pd))) {
+		DPRINTK("pktcdvd: %s failed saving write settings\n", pd->name);
+		return -EIO;
+	}
+
+	pkt_write_caching(pd, USE_WCACHING);
+
+	if ((ret = pkt_get_max_speed(pd, &write_speed)))
+		write_speed = 16 * 177;
+	switch (pd->mmc3_profile) {
+		case 0x13: /* DVD-RW */
+		case 0x1a: /* DVD+RW */
+		case 0x12: /* DVD-RAM */
+			DPRINTK("pktcdvd: write speed %ukB/s\n", write_speed);
+			break;
+		default:
+			if ((ret = pkt_media_speed(pd, &media_write_speed)))
+				media_write_speed = 16;
+			write_speed = min(write_speed, media_write_speed * 177);
+			DPRINTK("pktcdvd: write speed %ux\n", write_speed / 176);
+			break;
+	}
+	read_speed = write_speed;
+
+	if ((ret = pkt_set_speed(pd, write_speed, read_speed))) {
+		DPRINTK("pktcdvd: %s couldn't set write speed\n", pd->name);
+		return -EIO;
+	}
+	pd->write_speed = write_speed;
+	pd->read_speed = read_speed;
+
+	if ((ret = pkt_perform_opc(pd))) {
+		DPRINTK("pktcdvd: %s Optimum Power Calibration failed\n", pd->name);
+	}
+
+	return 0;
+}
+
+/*
+ * called at open time.
+ */
+static int pkt_open_dev(struct pktcdvd_device *pd, int write)
+{
+	int ret;
+	long lba;
+	request_queue_t *q;
+
+	/*
+	 * We need to re-open the cdrom device without O_NONBLOCK to be able
+	 * to read/write from/to it. It is already opened in O_NONBLOCK mode
+	 * so bdget() can't fail.
+	 */
+	bdget(pd->bdev->bd_dev);
+	if ((ret = blkdev_get(pd->bdev, FMODE_READ, O_RDONLY)))
+		goto out;
+
+	if ((ret = pkt_get_last_written(pd, &lba))) {
+		printk("pktcdvd: pkt_get_last_written failed\n");
+		goto out_putdev;
+	}
+
+	set_capacity(pd->disk, lba << 2);
+	set_capacity(pd->bdev->bd_disk, lba << 2);
+	bd_set_size(pd->bdev, (loff_t)lba << 11);
+
+	q = bdev_get_queue(pd->bdev);
+	if (write) {
+		if ((ret = pkt_open_write(pd)))
+			goto out_putdev;
+		/*
+		 * Some CDRW drives can not handle writes larger than one packet,
+		 * even if the size is a multiple of the packet size.
+		 */
+		spin_lock_irq(q->queue_lock);
+		blk_queue_max_sectors(q, pd->settings.size);
+		spin_unlock_irq(q->queue_lock);
+		set_bit(PACKET_WRITABLE, &pd->flags);
+	} else {
+		pkt_set_speed(pd, MAX_SPEED, MAX_SPEED);
+		clear_bit(PACKET_WRITABLE, &pd->flags);
+	}
+
+	if ((ret = pkt_set_segment_merging(pd, q)))
+		goto out_putdev;
+
+	if (write)
+		printk("pktcdvd: %lukB available on disc\n", lba << 1);
+
+	return 0;
+
+out_putdev:
+	blkdev_put(pd->bdev);
+out:
+	return ret;
+}
+
+/*
+ * called when the device is closed. makes sure that the device flushes
+ * the internal cache before we close.
+ */
+static void pkt_release_dev(struct pktcdvd_device *pd, int flush)
+{
+	if (flush && pkt_flush_cache(pd))
+		DPRINTK("pktcdvd: %s not flushing cache\n", pd->name);
+
+	pkt_lock_door(pd, 0);
+
+	pkt_set_speed(pd, MAX_SPEED, MAX_SPEED);
+	blkdev_put(pd->bdev);
+}
+
+static struct pktcdvd_device *pkt_find_dev_from_minor(int dev_minor)
+{
+	if (dev_minor >= MAX_WRITERS)
+		return NULL;
+	return pkt_devs[dev_minor];
+}
+
+static int pkt_open(struct inode *inode, struct file *file)
+{
+	struct pktcdvd_device *pd = NULL;
+	int ret;
+
+	VPRINTK("pktcdvd: entering open\n");
+
+	down(&ctl_mutex);
+	pd = pkt_find_dev_from_minor(iminor(inode));
+	if (!pd) {
+		ret = -ENODEV;
+		goto out;
+	}
+	BUG_ON(pd->refcnt < 0);
+
+	pd->refcnt++;
+	if (pd->refcnt > 1) {
+		if ((file->f_mode & FMODE_WRITE) &&
+		    !test_bit(PACKET_WRITABLE, &pd->flags)) {
+			ret = -EBUSY;
+			goto out_dec;
+		}
+	} else {
+		if (pkt_open_dev(pd, file->f_mode & FMODE_WRITE)) {
+			ret = -EIO;
+			goto out_dec;
+		}
+		/*
+		 * needed here as well, since ext2 (among others) may change
+		 * the blocksize at mount time
+		 */
+		set_blocksize(inode->i_bdev, CD_FRAMESIZE);
+	}
+
+	up(&ctl_mutex);
+	return 0;
+
+out_dec:
+	pd->refcnt--;
+out:
+	VPRINTK("pktcdvd: failed open (%d)\n", ret);
+	up(&ctl_mutex);
+	return ret;
+}
+
+static int pkt_close(struct inode *inode, struct file *file)
+{
+	struct pktcdvd_device *pd = inode->i_bdev->bd_disk->private_data;
+	int ret = 0;
+
+	down(&ctl_mutex);
+	pd->refcnt--;
+	BUG_ON(pd->refcnt < 0);
+	if (pd->refcnt == 0) {
+		int flush = test_bit(PACKET_WRITABLE, &pd->flags);
+		pkt_release_dev(pd, flush);
+	}
+	up(&ctl_mutex);
+	return ret;
+}
+
+
+static void *psd_pool_alloc(gfp_t gfp_mask, void *data)
+{
+	return kmalloc(sizeof(struct packet_stacked_data), gfp_mask);
+}
+
+static void psd_pool_free(void *ptr, void *data)
+{
+	kfree(ptr);
+}
+
+static int pkt_end_io_read_cloned(struct bio *bio, unsigned int bytes_done, int err)
+{
+	struct packet_stacked_data *psd = bio->bi_private;
+	struct pktcdvd_device *pd = psd->pd;
+
+	if (bio->bi_size)
+		return 1;
+
+	bio_put(bio);
+	bio_endio(psd->bio, psd->bio->bi_size, err);
+	mempool_free(psd, psd_pool);
+	pkt_bio_finished(pd);
+	return 0;
+}
+
+static int pkt_make_request(request_queue_t *q, struct bio *bio)
+{
+	struct pktcdvd_device *pd;
+	char b[BDEVNAME_SIZE];
+	sector_t zone;
+	struct packet_data *pkt;
+	int was_empty, blocked_bio;
+	struct pkt_rb_node *node;
+
+	pd = q->queuedata;
+	if (!pd) {
+		printk("pktcdvd: %s incorrect request queue\n", bdevname(bio->bi_bdev, b));
+		goto end_io;
+	}
+
+	/*
+	 * Clone READ bios so we can have our own bi_end_io callback.
+	 */
+	if (bio_data_dir(bio) == READ) {
+		struct bio *cloned_bio = bio_clone(bio, GFP_NOIO);
+		struct packet_stacked_data *psd = mempool_alloc(psd_pool, GFP_NOIO);
+
+		psd->pd = pd;
+		psd->bio = bio;
+		cloned_bio->bi_bdev = pd->bdev;
+		cloned_bio->bi_private = psd;
+		cloned_bio->bi_end_io = pkt_end_io_read_cloned;
+		pd->stats.secs_r += bio->bi_size >> 9;
+		pkt_queue_bio(pd, cloned_bio);
+		return 0;
+	}
+
+	if (!test_bit(PACKET_WRITABLE, &pd->flags)) {
+		printk("pktcdvd: WRITE for ro device %s (%llu)\n",
+			pd->name, (unsigned long long)bio->bi_sector);
+		goto end_io;
+	}
+
+	if (!bio->bi_size || (bio->bi_size % CD_FRAMESIZE)) {
+		printk("pktcdvd: wrong bio size\n");
+		goto end_io;
+	}
+
+	blk_queue_bounce(q, &bio);
+
+	zone = ZONE(bio->bi_sector, pd);
+	VPRINTK("pkt_make_request: start = %6llx stop = %6llx\n",
+		(unsigned long long)bio->bi_sector,
+		(unsigned long long)(bio->bi_sector + bio_sectors(bio)));
+
+	/* Check if we have to split the bio */
+	{
+		struct bio_pair *bp;
+		sector_t last_zone;
+		int first_sectors;
+
+		last_zone = ZONE(bio->bi_sector + bio_sectors(bio) - 1, pd);
+		if (last_zone != zone) {
+			BUG_ON(last_zone != zone + pd->settings.size);
+			first_sectors = last_zone - bio->bi_sector;
+			bp = bio_split(bio, bio_split_pool, first_sectors);
+			BUG_ON(!bp);
+			pkt_make_request(q, &bp->bio1);
+			pkt_make_request(q, &bp->bio2);
+			bio_pair_release(bp);
+			return 0;
+		}
+	}
+
+	/*
+	 * If we find a matching packet in state WAITING or READ_WAIT, we can
+	 * just append this bio to that packet.
+	 */
+	spin_lock(&pd->cdrw.active_list_lock);
+	blocked_bio = 0;
+	list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+		if (pkt->sector == zone) {
+			spin_lock(&pkt->lock);
+			if ((pkt->state == PACKET_WAITING_STATE) ||
+			    (pkt->state == PACKET_READ_WAIT_STATE)) {
+				pkt_add_list_last(bio, &pkt->orig_bios,
+						  &pkt->orig_bios_tail);
+				pkt->write_size += bio->bi_size / CD_FRAMESIZE;
+				if ((pkt->write_size >= pkt->frames) &&
+				    (pkt->state == PACKET_WAITING_STATE)) {
+					atomic_inc(&pkt->run_sm);
+					wake_up(&pd->wqueue);
+				}
+				spin_unlock(&pkt->lock);
+				spin_unlock(&pd->cdrw.active_list_lock);
+				return 0;
+			} else {
+				blocked_bio = 1;
+			}
+			spin_unlock(&pkt->lock);
+		}
+	}
+	spin_unlock(&pd->cdrw.active_list_lock);
+
+	/*
+	 * No matching packet found. Store the bio in the work queue.
+	 */
+	node = mempool_alloc(pd->rb_pool, GFP_NOIO);
+	node->bio = bio;
+	spin_lock(&pd->lock);
+	BUG_ON(pd->bio_queue_size < 0);
+	was_empty = (pd->bio_queue_size == 0);
+	pkt_rbtree_insert(pd, node);
+	spin_unlock(&pd->lock);
+
+	/*
+	 * Wake up the worker thread.
+	 */
+	atomic_set(&pd->scan_queue, 1);
+	if (was_empty) {
+		/* This wake_up is required for correct operation */
+		wake_up(&pd->wqueue);
+	} else if (!list_empty(&pd->cdrw.pkt_free_list) && !blocked_bio) {
+		/*
+		 * This wake up is not required for correct operation,
+		 * but improves performance in some cases.
+		 */
+		wake_up(&pd->wqueue);
+	}
+	return 0;
+end_io:
+	bio_io_error(bio, bio->bi_size);
+	return 0;
+}
+
+
+
+static int pkt_merge_bvec(request_queue_t *q, struct bio *bio, struct bio_vec *bvec)
+{
+	struct pktcdvd_device *pd = q->queuedata;
+	sector_t zone = ZONE(bio->bi_sector, pd);
+	int used = ((bio->bi_sector - zone) << 9) + bio->bi_size;
+	int remaining = (pd->settings.size << 9) - used;
+	int remaining2;
+
+	/*
+	 * A bio <= PAGE_SIZE must be allowed. If it crosses a packet
+	 * boundary, pkt_make_request() will split the bio.
+	 */
+	remaining2 = PAGE_SIZE - bio->bi_size;
+	remaining = max(remaining, remaining2);
+
+	BUG_ON(remaining < 0);
+	return remaining;
+}
+
+static void pkt_init_queue(struct pktcdvd_device *pd)
+{
+	request_queue_t *q = pd->disk->queue;
+
+	blk_queue_make_request(q, pkt_make_request);
+	blk_queue_hardsect_size(q, CD_FRAMESIZE);
+	blk_queue_max_sectors(q, PACKET_MAX_SECTORS);
+	blk_queue_merge_bvec(q, pkt_merge_bvec);
+	q->queuedata = pd;
+}
+
+static int pkt_seq_show(struct seq_file *m, void *p)
+{
+	struct pktcdvd_device *pd = m->private;
+	char *msg;
+	char bdev_buf[BDEVNAME_SIZE];
+	int states[PACKET_NUM_STATES];
+
+	seq_printf(m, "Writer %s mapped to %s:\n", pd->name,
+		   bdevname(pd->bdev, bdev_buf));
+
+	seq_printf(m, "\nSettings:\n");
+	seq_printf(m, "\tpacket size:\t\t%dkB\n", pd->settings.size / 2);
+
+	if (pd->settings.write_type == 0)
+		msg = "Packet";
+	else
+		msg = "Unknown";
+	seq_printf(m, "\twrite type:\t\t%s\n", msg);
+
+	seq_printf(m, "\tpacket type:\t\t%s\n", pd->settings.fp ? "Fixed" : "Variable");
+	seq_printf(m, "\tlink loss:\t\t%d\n", pd->settings.link_loss);
+
+	seq_printf(m, "\ttrack mode:\t\t%d\n", pd->settings.track_mode);
+
+	if (pd->settings.block_mode == PACKET_BLOCK_MODE1)
+		msg = "Mode 1";
+	else if (pd->settings.block_mode == PACKET_BLOCK_MODE2)
+		msg = "Mode 2";
+	else
+		msg = "Unknown";
+	seq_printf(m, "\tblock mode:\t\t%s\n", msg);
+
+	seq_printf(m, "\nStatistics:\n");
+	seq_printf(m, "\tpackets started:\t%lu\n", pd->stats.pkt_started);
+	seq_printf(m, "\tpackets ended:\t\t%lu\n", pd->stats.pkt_ended);
+	seq_printf(m, "\twritten:\t\t%lukB\n", pd->stats.secs_w >> 1);
+	seq_printf(m, "\tread gather:\t\t%lukB\n", pd->stats.secs_rg >> 1);
+	seq_printf(m, "\tread:\t\t\t%lukB\n", pd->stats.secs_r >> 1);
+
+	seq_printf(m, "\nMisc:\n");
+	seq_printf(m, "\treference count:\t%d\n", pd->refcnt);
+	seq_printf(m, "\tflags:\t\t\t0x%lx\n", pd->flags);
+	seq_printf(m, "\tread speed:\t\t%ukB/s\n", pd->read_speed);
+	seq_printf(m, "\twrite speed:\t\t%ukB/s\n", pd->write_speed);
+	seq_printf(m, "\tstart offset:\t\t%lu\n", pd->offset);
+	seq_printf(m, "\tmode page offset:\t%u\n", pd->mode_offset);
+
+	seq_printf(m, "\nQueue state:\n");
+	seq_printf(m, "\tbios queued:\t\t%d\n", pd->bio_queue_size);
+	seq_printf(m, "\tbios pending:\t\t%d\n", atomic_read(&pd->cdrw.pending_bios));
+	seq_printf(m, "\tcurrent sector:\t\t0x%llx\n", (unsigned long long)pd->current_sector);
+
+	pkt_count_states(pd, states);
+	seq_printf(m, "\tstate:\t\t\ti:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n",
+		   states[0], states[1], states[2], states[3], states[4], states[5]);
+
+	return 0;
+}
+
+static int pkt_seq_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, pkt_seq_show, PDE(inode)->data);
+}
+
+static struct file_operations pkt_proc_fops = {
+	.open	= pkt_seq_open,
+	.read	= seq_read,
+	.llseek	= seq_lseek,
+	.release = single_release
+};
+
+static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev)
+{
+	int i;
+	int ret = 0;
+	char b[BDEVNAME_SIZE];
+	struct proc_dir_entry *proc;
+	struct block_device *bdev;
+
+	if (pd->pkt_dev == dev) {
+		printk("pktcdvd: Recursive setup not allowed\n");
+		return -EBUSY;
+	}
+	for (i = 0; i < MAX_WRITERS; i++) {
+		struct pktcdvd_device *pd2 = pkt_devs[i];
+		if (!pd2)
+			continue;
+		if (pd2->bdev->bd_dev == dev) {
+			printk("pktcdvd: %s already setup\n", bdevname(pd2->bdev, b));
+			return -EBUSY;
+		}
+		if (pd2->pkt_dev == dev) {
+			printk("pktcdvd: Can't chain pktcdvd devices\n");
+			return -EBUSY;
+		}
+	}
+
+	bdev = bdget(dev);
+	if (!bdev)
+		return -ENOMEM;
+	ret = blkdev_get(bdev, FMODE_READ, O_RDONLY | O_NONBLOCK);
+	if (ret)
+		return ret;
+
+	/* This is safe, since we have a reference from open(). */
+	__module_get(THIS_MODULE);
+
+	if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) {
+		printk("pktcdvd: not enough memory for buffers\n");
+		ret = -ENOMEM;
+		goto out_mem;
+	}
+
+	pd->bdev = bdev;
+	set_blocksize(bdev, CD_FRAMESIZE);
+
+	pkt_init_queue(pd);
+
+	atomic_set(&pd->cdrw.pending_bios, 0);
+	pd->cdrw.thread = kthread_run(kcdrwd, pd, "%s", pd->name);
+	if (IS_ERR(pd->cdrw.thread)) {
+		printk("pktcdvd: can't start kernel thread\n");
+		ret = -ENOMEM;
+		goto out_thread;
+	}
+
+	proc = create_proc_entry(pd->name, 0, pkt_proc);
+	if (proc) {
+		proc->data = pd;
+		proc->proc_fops = &pkt_proc_fops;
+	}
+	DPRINTK("pktcdvd: writer %s mapped to %s\n", pd->name, bdevname(bdev, b));
+	return 0;
+
+out_thread:
+	pkt_shrink_pktlist(pd);
+out_mem:
+	blkdev_put(bdev);
+	/* This is safe: open() is still holding a reference. */
+	module_put(THIS_MODULE);
+	return ret;
+}
+
+static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct pktcdvd_device *pd = inode->i_bdev->bd_disk->private_data;
+
+	VPRINTK("pkt_ioctl: cmd %x, dev %d:%d\n", cmd, imajor(inode), iminor(inode));
+
+	switch (cmd) {
+	/*
+	 * forward selected CDROM ioctls to CD-ROM, for UDF
+	 */
+	case CDROMMULTISESSION:
+	case CDROMREADTOCENTRY:
+	case CDROM_LAST_WRITTEN:
+	case CDROM_SEND_PACKET:
+	case SCSI_IOCTL_SEND_COMMAND:
+		return blkdev_ioctl(pd->bdev->bd_inode, file, cmd, arg);
+
+	case CDROMEJECT:
+		/*
+		 * The door gets locked when the device is opened, so we
+		 * have to unlock it or else the eject command fails.
+		 */
+		pkt_lock_door(pd, 0);
+		return blkdev_ioctl(pd->bdev->bd_inode, file, cmd, arg);
+
+	default:
+		printk("pktcdvd: Unknown ioctl for %s (%x)\n", pd->name, cmd);
+		return -ENOTTY;
+	}
+
+	return 0;
+}
+
+static int pkt_media_changed(struct gendisk *disk)
+{
+	struct pktcdvd_device *pd = disk->private_data;
+	struct gendisk *attached_disk;
+
+	if (!pd)
+		return 0;
+	if (!pd->bdev)
+		return 0;
+	attached_disk = pd->bdev->bd_disk;
+	if (!attached_disk)
+		return 0;
+	return attached_disk->fops->media_changed(attached_disk);
+}
+
+static struct block_device_operations pktcdvd_ops = {
+	.owner =		THIS_MODULE,
+	.open =			pkt_open,
+	.release =		pkt_close,
+	.ioctl =		pkt_ioctl,
+	.media_changed =	pkt_media_changed,
+};
+
+/*
+ * Set up mapping from pktcdvd device to CD-ROM device.
+ */
+static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd)
+{
+	int idx;
+	int ret = -ENOMEM;
+	struct pktcdvd_device *pd;
+	struct gendisk *disk;
+	dev_t dev = new_decode_dev(ctrl_cmd->dev);
+
+	for (idx = 0; idx < MAX_WRITERS; idx++)
+		if (!pkt_devs[idx])
+			break;
+	if (idx == MAX_WRITERS) {
+		printk("pktcdvd: max %d writers supported\n", MAX_WRITERS);
+		return -EBUSY;
+	}
+
+	pd = kzalloc(sizeof(struct pktcdvd_device), GFP_KERNEL);
+	if (!pd)
+		return ret;
+
+	pd->rb_pool = mempool_create(PKT_RB_POOL_SIZE, pkt_rb_alloc, pkt_rb_free, NULL);
+	if (!pd->rb_pool)
+		goto out_mem;
+
+	disk = alloc_disk(1);
+	if (!disk)
+		goto out_mem;
+	pd->disk = disk;
+
+	spin_lock_init(&pd->lock);
+	spin_lock_init(&pd->iosched.lock);
+	sprintf(pd->name, "pktcdvd%d", idx);
+	init_waitqueue_head(&pd->wqueue);
+	pd->bio_queue = RB_ROOT;
+
+	disk->major = pkt_major;
+	disk->first_minor = idx;
+	disk->fops = &pktcdvd_ops;
+	disk->flags = GENHD_FL_REMOVABLE;
+	sprintf(disk->disk_name, "pktcdvd%d", idx);
+	disk->private_data = pd;
+	disk->queue = blk_alloc_queue(GFP_KERNEL);
+	if (!disk->queue)
+		goto out_mem2;
+
+	pd->pkt_dev = MKDEV(disk->major, disk->first_minor);
+	ret = pkt_new_dev(pd, dev);
+	if (ret)
+		goto out_new_dev;
+
+	add_disk(disk);
+	pkt_devs[idx] = pd;
+	ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev);
+	return 0;
+
+out_new_dev:
+	blk_put_queue(disk->queue);
+out_mem2:
+	put_disk(disk);
+out_mem:
+	if (pd->rb_pool)
+		mempool_destroy(pd->rb_pool);
+	kfree(pd);
+	return ret;
+}
+
+/*
+ * Tear down mapping from pktcdvd device to CD-ROM device.
+ */
+static int pkt_remove_dev(struct pkt_ctrl_command *ctrl_cmd)
+{
+	struct pktcdvd_device *pd;
+	int idx;
+	dev_t pkt_dev = new_decode_dev(ctrl_cmd->pkt_dev);
+
+	for (idx = 0; idx < MAX_WRITERS; idx++) {
+		pd = pkt_devs[idx];
+		if (pd && (pd->pkt_dev == pkt_dev))
+			break;
+	}
+	if (idx == MAX_WRITERS) {
+		DPRINTK("pktcdvd: dev not setup\n");
+		return -ENXIO;
+	}
+
+	if (pd->refcnt > 0)
+		return -EBUSY;
+
+	if (!IS_ERR(pd->cdrw.thread))
+		kthread_stop(pd->cdrw.thread);
+
+	blkdev_put(pd->bdev);
+
+	pkt_shrink_pktlist(pd);
+
+	remove_proc_entry(pd->name, pkt_proc);
+	DPRINTK("pktcdvd: writer %s unmapped\n", pd->name);
+
+	del_gendisk(pd->disk);
+	blk_put_queue(pd->disk->queue);
+	put_disk(pd->disk);
+
+	pkt_devs[idx] = NULL;
+	mempool_destroy(pd->rb_pool);
+	kfree(pd);
+
+	/* This is safe: open() is still holding a reference. */
+	module_put(THIS_MODULE);
+	return 0;
+}
+
+static void pkt_get_status(struct pkt_ctrl_command *ctrl_cmd)
+{
+	struct pktcdvd_device *pd = pkt_find_dev_from_minor(ctrl_cmd->dev_index);
+	if (pd) {
+		ctrl_cmd->dev = new_encode_dev(pd->bdev->bd_dev);
+		ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev);
+	} else {
+		ctrl_cmd->dev = 0;
+		ctrl_cmd->pkt_dev = 0;
+	}
+	ctrl_cmd->num_devices = MAX_WRITERS;
+}
+
+static int pkt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	void __user *argp = (void __user *)arg;
+	struct pkt_ctrl_command ctrl_cmd;
+	int ret = 0;
+
+	if (cmd != PACKET_CTRL_CMD)
+		return -ENOTTY;
+
+	if (copy_from_user(&ctrl_cmd, argp, sizeof(struct pkt_ctrl_command)))
+		return -EFAULT;
+
+	switch (ctrl_cmd.command) {
+	case PKT_CTRL_CMD_SETUP:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		down(&ctl_mutex);
+		ret = pkt_setup_dev(&ctrl_cmd);
+		up(&ctl_mutex);
+		break;
+	case PKT_CTRL_CMD_TEARDOWN:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		down(&ctl_mutex);
+		ret = pkt_remove_dev(&ctrl_cmd);
+		up(&ctl_mutex);
+		break;
+	case PKT_CTRL_CMD_STATUS:
+		down(&ctl_mutex);
+		pkt_get_status(&ctrl_cmd);
+		up(&ctl_mutex);
+		break;
+	default:
+		return -ENOTTY;
+	}
+
+	if (copy_to_user(argp, &ctrl_cmd, sizeof(struct pkt_ctrl_command)))
+		return -EFAULT;
+	return ret;
+}
+
+
+static struct file_operations pkt_ctl_fops = {
+	.ioctl	 = pkt_ctl_ioctl,
+	.owner	 = THIS_MODULE,
+};
+
+static struct miscdevice pkt_misc = {
+	.minor 		= MISC_DYNAMIC_MINOR,
+	.name  		= "pktcdvd",
+	.devfs_name 	= "pktcdvd/control",
+	.fops  		= &pkt_ctl_fops
+};
+
+static int __init pkt_init(void)
+{
+	int ret;
+
+	psd_pool = mempool_create(PSD_POOL_SIZE, psd_pool_alloc, psd_pool_free, NULL);
+	if (!psd_pool)
+		return -ENOMEM;
+
+	ret = register_blkdev(pkt_major, "pktcdvd");
+	if (ret < 0) {
+		printk("pktcdvd: Unable to register block device\n");
+		goto out2;
+	}
+	if (!pkt_major)
+		pkt_major = ret;
+
+	ret = misc_register(&pkt_misc);
+	if (ret) {
+		printk("pktcdvd: Unable to register misc device\n");
+		goto out;
+	}
+
+	init_MUTEX(&ctl_mutex);
+
+	pkt_proc = proc_mkdir("pktcdvd", proc_root_driver);
+
+	DPRINTK("pktcdvd: %s\n", VERSION_CODE);
+	return 0;
+
+out:
+	unregister_blkdev(pkt_major, "pktcdvd");
+out2:
+	mempool_destroy(psd_pool);
+	return ret;
+}
+
+static void __exit pkt_exit(void)
+{
+	remove_proc_entry("pktcdvd", proc_root_driver);
+	misc_deregister(&pkt_misc);
+	unregister_blkdev(pkt_major, "pktcdvd");
+	mempool_destroy(psd_pool);
+}
+
+MODULE_DESCRIPTION("Packet writing layer for CD/DVD drives");
+MODULE_AUTHOR("Jens Axboe <axboe@suse.de>");
+MODULE_LICENSE("GPL");
+
+module_init(pkt_init);
+module_exit(pkt_exit);
diff -urN oldtree/drivers/cdrom/cdrom.c newtree/drivers/cdrom/cdrom.c
--- oldtree/drivers/cdrom/cdrom.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/cdrom/cdrom.c	2006-02-13 19:19:59.707556760 -0500
@@ -407,7 +407,6 @@
 	ENSURE(get_mcn, CDC_MCN);
 	ENSURE(reset, CDC_RESET);
 	ENSURE(audio_ioctl, CDC_PLAY_AUDIO);
-	ENSURE(dev_ioctl, CDC_IOCTLS);
 	ENSURE(generic_packet, CDC_GENERIC_PACKET);
 	cdi->mc_flags = 0;
 	cdo->n_minors = 0;
@@ -2196,395 +2195,586 @@
 	return cdrom_read_cdda_old(cdi, ubuf, lba, nframes);	
 }
 
-/* Just about every imaginable ioctl is supported in the Uniform layer
- * these days. ATAPI / SCSI specific code now mainly resides in
- * mmc_ioct().
- */
-int cdrom_ioctl(struct file * file, struct cdrom_device_info *cdi,
-		struct inode *ip, unsigned int cmd, unsigned long arg)
+static int cdrom_ioctl_multisession(struct cdrom_device_info *cdi,
+		void __user *argp)
 {
-	struct cdrom_device_ops *cdo = cdi->ops;
+	struct cdrom_multisession ms_info;
+	u8 requested_format;
 	int ret;
 
-	/* Try the generic SCSI command ioctl's first.. */
-	ret = scsi_cmd_ioctl(file, ip->i_bdev->bd_disk, cmd, (void __user *)arg);
-	if (ret != -ENOTTY)
+	cdinfo(CD_DO_IOCTL, "entering CDROMMULTISESSION\n");
+
+	if (!(cdi->ops->capability & CDC_MULTI_SESSION))
+		return -ENOSYS;
+
+	if (copy_from_user(&ms_info, argp, sizeof(ms_info)))
+		return -EFAULT;
+
+	requested_format = ms_info.addr_format;
+	if (requested_format != CDROM_MSF && requested_format != CDROM_LBA)
+		return -EINVAL;
+	ms_info.addr_format = CDROM_LBA;
+
+	ret = cdi->ops->get_last_session(cdi, &ms_info);
+	if (ret)
 		return ret;
 
-	/* the first few commands do not deal with audio drive_info, but
-	   only with routines in cdrom device operations. */
-	switch (cmd) {
-	case CDROMMULTISESSION: {
-		struct cdrom_multisession ms_info;
-		u_char requested_format;
-		cdinfo(CD_DO_IOCTL, "entering CDROMMULTISESSION\n"); 
-                if (!(cdo->capability & CDC_MULTI_SESSION))
-                        return -ENOSYS;
-		IOCTL_IN(arg, struct cdrom_multisession, ms_info);
-		requested_format = ms_info.addr_format;
-		if (!((requested_format == CDROM_MSF) ||
-			(requested_format == CDROM_LBA)))
-				return -EINVAL;
-		ms_info.addr_format = CDROM_LBA;
-		if ((ret=cdo->get_last_session(cdi, &ms_info)))
+	sanitize_format(&ms_info.addr, &ms_info.addr_format, requested_format);
+
+	if (copy_to_user(argp, &ms_info, sizeof(ms_info)))
+		return -EFAULT;
+
+	cdinfo(CD_DO_IOCTL, "CDROMMULTISESSION successful\n");
+	return 0;
+}
+
+static int cdrom_ioctl_eject(struct cdrom_device_info *cdi)
+{
+	cdinfo(CD_DO_IOCTL, "entering CDROMEJECT\n");
+
+	if (!CDROM_CAN(CDC_OPEN_TRAY))
+		return -ENOSYS;
+	if (cdi->use_count != 1 || keeplocked)
+		return -EBUSY;
+	if (CDROM_CAN(CDC_LOCK)) {
+		int ret = cdi->ops->lock_door(cdi, 0);
+		if (ret)
 			return ret;
-		sanitize_format(&ms_info.addr, &ms_info.addr_format,
-				requested_format);
-		IOCTL_OUT(arg, struct cdrom_multisession, ms_info);
-		cdinfo(CD_DO_IOCTL, "CDROMMULTISESSION successful\n"); 
-		return 0;
-		}
+	}
 
-	case CDROMEJECT: {
-		cdinfo(CD_DO_IOCTL, "entering CDROMEJECT\n"); 
-		if (!CDROM_CAN(CDC_OPEN_TRAY))
-			return -ENOSYS;
-		if (cdi->use_count != 1 || keeplocked)
-			return -EBUSY;
-		if (CDROM_CAN(CDC_LOCK))
-			if ((ret=cdo->lock_door(cdi, 0)))
-				return ret;
+	return cdi->ops->tray_move(cdi, 1);
+}
 
-		return cdo->tray_move(cdi, 1);
-		}
+static int cdrom_ioctl_closetray(struct cdrom_device_info *cdi)
+{
+	cdinfo(CD_DO_IOCTL, "entering CDROMCLOSETRAY\n");
 
-	case CDROMCLOSETRAY: {
-		cdinfo(CD_DO_IOCTL, "entering CDROMCLOSETRAY\n"); 
-		if (!CDROM_CAN(CDC_CLOSE_TRAY))
-			return -ENOSYS;
-		return cdo->tray_move(cdi, 0);
-		}
+	if (!CDROM_CAN(CDC_CLOSE_TRAY))
+		return -ENOSYS;
+	return cdi->ops->tray_move(cdi, 0);
+}
 
-	case CDROMEJECT_SW: {
-		cdinfo(CD_DO_IOCTL, "entering CDROMEJECT_SW\n"); 
-		if (!CDROM_CAN(CDC_OPEN_TRAY))
-			return -ENOSYS;
-		if (keeplocked)
-			return -EBUSY;
-		cdi->options &= ~(CDO_AUTO_CLOSE | CDO_AUTO_EJECT);
-		if (arg)
-			cdi->options |= CDO_AUTO_CLOSE | CDO_AUTO_EJECT;
-		return 0;
-		}
+static int cdrom_ioctl_eject_sw(struct cdrom_device_info *cdi,
+		unsigned long arg)
+{
+	cdinfo(CD_DO_IOCTL, "entering CDROMEJECT_SW\n");
 
-	case CDROM_MEDIA_CHANGED: {
-		struct cdrom_changer_info *info;
-		int changed;
+	if (!CDROM_CAN(CDC_OPEN_TRAY))
+		return -ENOSYS;
+	if (keeplocked)
+		return -EBUSY;
 
-		cdinfo(CD_DO_IOCTL, "entering CDROM_MEDIA_CHANGED\n"); 
-		if (!CDROM_CAN(CDC_MEDIA_CHANGED))
+	cdi->options &= ~(CDO_AUTO_CLOSE | CDO_AUTO_EJECT);
+	if (arg)
+		cdi->options |= CDO_AUTO_CLOSE | CDO_AUTO_EJECT;
+	return 0;
+}
+
+static int cdrom_ioctl_media_changed(struct cdrom_device_info *cdi,
+		unsigned long arg)
+{
+	struct cdrom_changer_info *info;
+	int ret;
+
+	cdinfo(CD_DO_IOCTL, "entering CDROM_MEDIA_CHANGED\n");
+
+	if (!CDROM_CAN(CDC_MEDIA_CHANGED))
+		return -ENOSYS;
+
+	/* cannot select disc or select current disc */
+	if (!CDROM_CAN(CDC_SELECT_DISC) || arg == CDSL_CURRENT)
+		return media_changed(cdi, 1);
+
+	if ((unsigned int)arg >= cdi->capacity)
+		return -EINVAL;
+
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	ret = cdrom_read_mech_status(cdi, info);
+	if (!ret)
+		ret = info->slots[arg].change;
+	kfree(info);
+	return ret;
+}
+
+static int cdrom_ioctl_set_options(struct cdrom_device_info *cdi,
+		unsigned long arg)
+{
+	cdinfo(CD_DO_IOCTL, "entering CDROM_SET_OPTIONS\n");
+
+	/*
+	 * Options need to be in sync with capability.
+	 * Too late for that, so we have to check each one separately.
+	 */
+	switch (arg) {
+	case CDO_USE_FFLAGS:
+	case CDO_CHECK_TYPE:
+		break;
+	case CDO_LOCK:
+		if (!CDROM_CAN(CDC_LOCK))
+			return -ENOSYS;
+		break;
+	case 0:
+		return cdi->options;
+	/* default is basically CDO_[AUTO_CLOSE|AUTO_EJECT] */
+	default:
+		if (!CDROM_CAN(arg))
 			return -ENOSYS;
+	}
+	cdi->options |= (int) arg;
+	return cdi->options;
+}
+
+static int cdrom_ioctl_clear_options(struct cdrom_device_info *cdi,
+		unsigned long arg)
+{
+	cdinfo(CD_DO_IOCTL, "entering CDROM_CLEAR_OPTIONS\n");
+
+	cdi->options &= ~(int) arg;
+	return cdi->options;
+}
 
-		/* cannot select disc or select current disc */
-		if (!CDROM_CAN(CDC_SELECT_DISC) || arg == CDSL_CURRENT)
-			return media_changed(cdi, 1);
+static int cdrom_ioctl_select_speed(struct cdrom_device_info *cdi,
+		unsigned long arg)
+{
+	cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_SPEED\n");
+
+	if (!CDROM_CAN(CDC_SELECT_SPEED))
+		return -ENOSYS;
+	return cdi->ops->select_speed(cdi, arg);
+}
+
+static int cdrom_ioctl_select_disc(struct cdrom_device_info *cdi,
+		unsigned long arg)
+{
+	cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_DISC\n");
 
-		if ((unsigned int)arg >= cdi->capacity)
+	if (!CDROM_CAN(CDC_SELECT_DISC))
+		return -ENOSYS;
+
+	if (arg != CDSL_CURRENT && arg != CDSL_NONE) {
+		if ((int)arg >= cdi->capacity)
 			return -EINVAL;
+	}
 
-		info = kmalloc(sizeof(*info), GFP_KERNEL);
-		if (!info)
-			return -ENOMEM;
+	/*
+	 * ->select_disc is a hook to allow a driver-specific way of
+	 * seleting disc.  However, since there is no equivalent hook for
+	 * cdrom_slot_status this may not actually be useful...
+	 */
+	if (cdi->ops->select_disc)
+		return cdi->ops->select_disc(cdi, arg);
 
-		if ((ret = cdrom_read_mech_status(cdi, info))) {
-			kfree(info);
-			return ret;
-		}
+	cdinfo(CD_CHANGER, "Using generic cdrom_select_disc()\n");
+	return cdrom_select_disc(cdi, arg);
+}
 
-		changed = info->slots[arg].change;
-		kfree(info);
-		return changed;
-		}
+static int cdrom_ioctl_reset(struct cdrom_device_info *cdi,
+		struct block_device *bdev)
+{
+	cdinfo(CD_DO_IOCTL, "entering CDROM_RESET\n");
 
-	case CDROM_SET_OPTIONS: {
-		cdinfo(CD_DO_IOCTL, "entering CDROM_SET_OPTIONS\n"); 
-		/* options need to be in sync with capability. too late for
-		   that, so we have to check each one separately... */
-		switch (arg) {
-		case CDO_USE_FFLAGS:
-		case CDO_CHECK_TYPE:
-			break;
-		case CDO_LOCK:
-			if (!CDROM_CAN(CDC_LOCK))
-				return -ENOSYS;
-			break;
-		case 0:
-			return cdi->options;
-		/* default is basically CDO_[AUTO_CLOSE|AUTO_EJECT] */
-		default:
-			if (!CDROM_CAN(arg))
-				return -ENOSYS;
-		}
-		cdi->options |= (int) arg;
-		return cdi->options;
-		}
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (!CDROM_CAN(CDC_RESET))
+		return -ENOSYS;
+	invalidate_bdev(bdev, 0);
+	return cdi->ops->reset(cdi);
+}
 
-	case CDROM_CLEAR_OPTIONS: {
-		cdinfo(CD_DO_IOCTL, "entering CDROM_CLEAR_OPTIONS\n"); 
-		cdi->options &= ~(int) arg;
-		return cdi->options;
-		}
+static int cdrom_ioctl_lock_door(struct cdrom_device_info *cdi,
+		unsigned long arg)
+{
+	cdinfo(CD_DO_IOCTL, "%socking door.\n", arg ? "L" : "Unl");
 
-	case CDROM_SELECT_SPEED: {
-		cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_SPEED\n"); 
-		if (!CDROM_CAN(CDC_SELECT_SPEED))
-			return -ENOSYS;
-		return cdo->select_speed(cdi, arg);
-		}
+	if (!CDROM_CAN(CDC_LOCK))
+		return -EDRIVE_CANT_DO_THIS;
 
-	case CDROM_SELECT_DISC: {
-		cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_DISC\n"); 
-		if (!CDROM_CAN(CDC_SELECT_DISC))
-			return -ENOSYS;
+	keeplocked = arg ? 1 : 0;
 
-                if ((arg != CDSL_CURRENT) && (arg != CDSL_NONE))
-			if ((int)arg >= cdi->capacity)
-				return -EINVAL;
-
-		/* cdo->select_disc is a hook to allow a driver-specific
-		 * way of seleting disc.  However, since there is no
-		 * equiv hook for cdrom_slot_status this may not 
-		 * actually be useful...
-		 */
-		if (cdo->select_disc != NULL)
-			return cdo->select_disc(cdi, arg);
-
-		/* no driver specific select_disc(), call our own */
-		cdinfo(CD_CHANGER, "Using generic cdrom_select_disc()\n"); 
-		return cdrom_select_disc(cdi, arg);
-		}
-
-	case CDROMRESET: {
-		if (!capable(CAP_SYS_ADMIN))
-			return -EACCES;
-		cdinfo(CD_DO_IOCTL, "entering CDROM_RESET\n");
-		if (!CDROM_CAN(CDC_RESET))
-			return -ENOSYS;
-		invalidate_bdev(ip->i_bdev, 0);
-		return cdo->reset(cdi);
-		}
+	/*
+	 * Don't unlock the door on multiple opens by default, but allow
+	 * root to do so.
+	 */
+	if (cdi->use_count != 1 && !arg && !capable(CAP_SYS_ADMIN))
+		return -EBUSY;
+	return cdi->ops->lock_door(cdi, arg);
+}
 
-	case CDROM_LOCKDOOR: {
-		cdinfo(CD_DO_IOCTL, "%socking door.\n", arg ? "L" : "Unl");
-		if (!CDROM_CAN(CDC_LOCK))
-			return -EDRIVE_CANT_DO_THIS;
-		keeplocked = arg ? 1 : 0;
-		/* don't unlock the door on multiple opens,but allow root
-		 * to do so */
-		if ((cdi->use_count != 1) && !arg && !capable(CAP_SYS_ADMIN))
-			return -EBUSY;
-		return cdo->lock_door(cdi, arg);
-		}
+static int cdrom_ioctl_debug(struct cdrom_device_info *cdi,
+		unsigned long arg)
+{
+	cdinfo(CD_DO_IOCTL, "%sabling debug.\n", arg ? "En" : "Dis");
 
-	case CDROM_DEBUG: {
-		if (!capable(CAP_SYS_ADMIN))
-			return -EACCES;
-		cdinfo(CD_DO_IOCTL, "%sabling debug.\n", arg ? "En" : "Dis");
-		debug = arg ? 1 : 0;
-		return debug;
-		}
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	debug = arg ? 1 : 0;
+	return debug;
+}
 
-	case CDROM_GET_CAPABILITY: {
-		cdinfo(CD_DO_IOCTL, "entering CDROM_GET_CAPABILITY\n");
-		return (cdo->capability & ~cdi->mask);
-		}
+static int cdrom_ioctl_get_capability(struct cdrom_device_info *cdi)
+{
+	cdinfo(CD_DO_IOCTL, "entering CDROM_GET_CAPABILITY\n");
+	return (cdi->ops->capability & ~cdi->mask);
+}
 
-/* The following function is implemented, although very few audio
+/*
+ * The following function is implemented, although very few audio
  * discs give Universal Product Code information, which should just be
  * the Medium Catalog Number on the box.  Note, that the way the code
  * is written on the CD is /not/ uniform across all discs!
  */
-	case CDROM_GET_MCN: {
-		struct cdrom_mcn mcn;
-		cdinfo(CD_DO_IOCTL, "entering CDROM_GET_MCN\n"); 
-		if (!(cdo->capability & CDC_MCN))
-			return -ENOSYS;
-		if ((ret=cdo->get_mcn(cdi, &mcn)))
-			return ret;
-		IOCTL_OUT(arg, struct cdrom_mcn, mcn);
-		cdinfo(CD_DO_IOCTL, "CDROM_GET_MCN successful\n"); 
-		return 0;
-		}
+static int cdrom_ioctl_get_mcn(struct cdrom_device_info *cdi,
+		void __user *argp)
+{
+	struct cdrom_mcn mcn;
+	int ret;
 
-	case CDROM_DRIVE_STATUS: {
-		cdinfo(CD_DO_IOCTL, "entering CDROM_DRIVE_STATUS\n"); 
-		if (!(cdo->capability & CDC_DRIVE_STATUS))
-			return -ENOSYS;
-		if (!CDROM_CAN(CDC_SELECT_DISC))
-			return cdo->drive_status(cdi, CDSL_CURRENT);
-                if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE)) 
-			return cdo->drive_status(cdi, CDSL_CURRENT);
-		if (((int)arg >= cdi->capacity))
-			return -EINVAL;
-		return cdrom_slot_status(cdi, arg);
-		}
+	cdinfo(CD_DO_IOCTL, "entering CDROM_GET_MCN\n");
 
-	/* Ok, this is where problems start.  The current interface for the
-	   CDROM_DISC_STATUS ioctl is flawed.  It makes the false assumption
-	   that CDs are all CDS_DATA_1 or all CDS_AUDIO, etc.  Unfortunatly,
-	   while this is often the case, it is also very common for CDs to
-	   have some tracks with data, and some tracks with audio.  Just 
-	   because I feel like it, I declare the following to be the best
-	   way to cope.  If the CD has ANY data tracks on it, it will be
-	   returned as a data CD.  If it has any XA tracks, I will return
-	   it as that.  Now I could simplify this interface by combining these 
-	   returns with the above, but this more clearly demonstrates
-	   the problem with the current interface.  Too bad this wasn't 
-	   designed to use bitmasks...         -Erik 
-
-	   Well, now we have the option CDS_MIXED: a mixed-type CD. 
-	   User level programmers might feel the ioctl is not very useful.
-	   					---david
-	*/
-	case CDROM_DISC_STATUS: {
-		tracktype tracks;
-		cdinfo(CD_DO_IOCTL, "entering CDROM_DISC_STATUS\n"); 
-		cdrom_count_tracks(cdi, &tracks);
-		if (tracks.error) 
-			return(tracks.error);
-
-		/* Policy mode on */
-		if (tracks.audio > 0) {
-			if (tracks.data==0 && tracks.cdi==0 && tracks.xa==0) 
-				return CDS_AUDIO;
-			else
-				return CDS_MIXED;
-		}
-		if (tracks.cdi > 0) return CDS_XA_2_2;
-		if (tracks.xa > 0) return CDS_XA_2_1;
-		if (tracks.data > 0) return CDS_DATA_1;
-		/* Policy mode off */
+	if (!(cdi->ops->capability & CDC_MCN))
+		return -ENOSYS;
+	ret = cdi->ops->get_mcn(cdi, &mcn);
+	if (ret)
+		return ret;
 
-		cdinfo(CD_WARNING,"This disc doesn't have any tracks I recognize!\n");
-		return CDS_NO_INFO;
-		}
+	if (copy_to_user(argp, &mcn, sizeof(mcn)))
+		return -EFAULT;
+	cdinfo(CD_DO_IOCTL, "CDROM_GET_MCN successful\n");
+	return 0;
+}
 
-	case CDROM_CHANGER_NSLOTS: {
-		cdinfo(CD_DO_IOCTL, "entering CDROM_CHANGER_NSLOTS\n"); 
-		return cdi->capacity;
-		}
+static int cdrom_ioctl_drive_status(struct cdrom_device_info *cdi,
+		unsigned long arg)
+{
+	cdinfo(CD_DO_IOCTL, "entering CDROM_DRIVE_STATUS\n");
+
+	if (!(cdi->ops->capability & CDC_DRIVE_STATUS))
+		return -ENOSYS;
+	if (!CDROM_CAN(CDC_SELECT_DISC) ||
+	    (arg == CDSL_CURRENT || arg == CDSL_NONE))
+		return cdi->ops->drive_status(cdi, CDSL_CURRENT);
+	if (((int)arg >= cdi->capacity))
+		return -EINVAL;
+	return cdrom_slot_status(cdi, arg);
+}
+
+/*
+ * Ok, this is where problems start.  The current interface for the
+ * CDROM_DISC_STATUS ioctl is flawed.  It makes the false assumption that
+ * CDs are all CDS_DATA_1 or all CDS_AUDIO, etc.  Unfortunatly, while this
+ * is often the case, it is also very common for CDs to have some tracks
+ * with data, and some tracks with audio.  Just because I feel like it,
+ * I declare the following to be the best way to cope.  If the CD has ANY
+ * data tracks on it, it will be returned as a data CD.  If it has any XA
+ * tracks, I will return it as that.  Now I could simplify this interface
+ * by combining these  returns with the above, but this more clearly
+ * demonstrates the problem with the current interface.  Too bad this
+ * wasn't designed to use bitmasks...         -Erik
+ *
+ * Well, now we have the option CDS_MIXED: a mixed-type CD.
+ * User level programmers might feel the ioctl is not very useful.
+ *					---david
+ */
+static int cdrom_ioctl_disc_status(struct cdrom_device_info *cdi)
+{
+	tracktype tracks;
+
+	cdinfo(CD_DO_IOCTL, "entering CDROM_DISC_STATUS\n");
+
+	cdrom_count_tracks(cdi, &tracks);
+	if (tracks.error)
+		return tracks.error;
+
+	/* Policy mode on */
+	if (tracks.audio > 0) {
+		if (!tracks.data && !tracks.cdi && !tracks.xa)
+			return CDS_AUDIO;
+		else
+			return CDS_MIXED;
 	}
 
-	/* use the ioctls that are implemented through the generic_packet()
-	   interface. this may look at bit funny, but if -ENOTTY is
-	   returned that particular ioctl is not implemented and we
-	   let it go through the device specific ones. */
+	if (tracks.cdi > 0)
+		return CDS_XA_2_2;
+	if (tracks.xa > 0)
+		return CDS_XA_2_1;
+	if (tracks.data > 0)
+		return CDS_DATA_1;
+	/* Policy mode off */
+
+	cdinfo(CD_WARNING,"This disc doesn't have any tracks I recognize!\n");
+	return CDS_NO_INFO;
+}
+
+static int cdrom_ioctl_changer_nslots(struct cdrom_device_info *cdi)
+{
+	cdinfo(CD_DO_IOCTL, "entering CDROM_CHANGER_NSLOTS\n");
+	return cdi->capacity;
+}
+
+static int cdrom_ioctl_get_subchnl(struct cdrom_device_info *cdi,
+		void __user *argp)
+{
+	struct cdrom_subchnl q;
+	u8 requested, back;
+	int ret;
+
+	/* cdinfo(CD_DO_IOCTL,"entering CDROMSUBCHNL\n");*/
+
+	if (!CDROM_CAN(CDC_PLAY_AUDIO))
+		return -ENOSYS;
+	if (copy_from_user(&q, argp, sizeof(q)))
+		return -EFAULT;
+
+	requested = q.cdsc_format;
+	if (requested != CDROM_MSF && requested != CDROM_LBA)
+		return -EINVAL;
+	q.cdsc_format = CDROM_MSF;
+
+	ret = cdi->ops->audio_ioctl(cdi, CDROMSUBCHNL, &q);
+	if (ret)
+		return ret;
+
+	back = q.cdsc_format; /* local copy */
+	sanitize_format(&q.cdsc_absaddr, &back, requested);
+	sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested);
+
+	if (copy_to_user(argp, &q, sizeof(q)))
+		return -EFAULT;
+	/* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */
+	return 0;
+}
+
+static int cdrom_ioctl_read_tochdr(struct cdrom_device_info *cdi,
+		void __user *argp)
+{
+	struct cdrom_tochdr header;
+	int ret;
+
+	/* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCHDR\n"); */
+
+	if (!CDROM_CAN(CDC_PLAY_AUDIO))
+		return -ENOSYS;
+	if (copy_from_user(&header, argp, sizeof(header)))
+		return -EFAULT;
+
+	ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCHDR, &header);
+	if (ret)
+		return ret;
+
+	if (copy_to_user(argp, &header, sizeof(header)))
+		return -EFAULT;
+	/* cdinfo(CD_DO_IOCTL, "CDROMREADTOCHDR successful\n"); */
+	return 0;
+}
+
+static int cdrom_ioctl_read_tocentry(struct cdrom_device_info *cdi,
+		void __user *argp)
+{
+	struct cdrom_tocentry entry;
+	u8 requested_format;
+	int ret;
+
+	/* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCENTRY\n"); */
+
+	if (!CDROM_CAN(CDC_PLAY_AUDIO))
+		return -ENOSYS;
+	if (copy_from_user(&entry, argp, sizeof(entry)))
+		return -EFAULT;
+
+	requested_format = entry.cdte_format;
+	if (requested_format != CDROM_MSF || requested_format != CDROM_LBA)
+		return -EINVAL;
+	/* make interface to low-level uniform */
+	entry.cdte_format = CDROM_MSF;
+	ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &entry);
+	if (ret)
+		return ret;
+	sanitize_format(&entry.cdte_addr, &entry.cdte_format, requested_format);
+
+	if (copy_to_user(argp, &entry, sizeof(entry)))
+		return -EFAULT;
+	/* cdinfo(CD_DO_IOCTL, "CDROMREADTOCENTRY successful\n"); */
+	return 0;
+}
+
+static int cdrom_ioctl_play_msf(struct cdrom_device_info *cdi,
+		void __user *argp)
+{
+	struct cdrom_msf msf;
+
+	cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n");
+
+	if (!CDROM_CAN(CDC_PLAY_AUDIO))
+		return -ENOSYS;
+	if (copy_from_user(&msf, argp, sizeof(msf)))
+		return -EFAULT;
+	return cdi->ops->audio_ioctl(cdi, CDROMPLAYMSF, &msf);
+}
+
+static int cdrom_ioctl_play_trkind(struct cdrom_device_info *cdi,
+		void __user *argp)
+{
+	struct cdrom_ti ti;
+	int ret;
+
+	cdinfo(CD_DO_IOCTL, "entering CDROMPLAYTRKIND\n");
+
+	if (!CDROM_CAN(CDC_PLAY_AUDIO))
+		return -ENOSYS;
+	if (copy_from_user(&ti, argp, sizeof(ti)))
+		return -EFAULT;
+
+	ret = check_for_audio_disc(cdi, cdi->ops);
+	if (ret)
+		return ret;
+	return cdi->ops->audio_ioctl(cdi, CDROMPLAYTRKIND, &ti);
+}
+static int cdrom_ioctl_volctrl(struct cdrom_device_info *cdi,
+		void __user *argp)
+{
+	struct cdrom_volctrl volume;
+
+	cdinfo(CD_DO_IOCTL, "entering CDROMVOLCTRL\n");
+
+	if (!CDROM_CAN(CDC_PLAY_AUDIO))
+		return -ENOSYS;
+	if (copy_from_user(&volume, argp, sizeof(volume)))
+		return -EFAULT;
+	return cdi->ops->audio_ioctl(cdi, CDROMVOLCTRL, &volume);
+}
+
+static int cdrom_ioctl_volread(struct cdrom_device_info *cdi,
+		void __user *argp)
+{
+	struct cdrom_volctrl volume;
+	int ret;
+
+	cdinfo(CD_DO_IOCTL, "entering CDROMVOLREAD\n");
+
+	if (!CDROM_CAN(CDC_PLAY_AUDIO))
+		return -ENOSYS;
+
+	ret = cdi->ops->audio_ioctl(cdi, CDROMVOLREAD, &volume);
+	if (ret)
+		return ret;
+
+	if (copy_to_user(argp, &volume, sizeof(volume)))
+		return -EFAULT;
+	return 0;
+}
+
+static int cdrom_ioctl_audioctl(struct cdrom_device_info *cdi,
+		unsigned int cmd)
+{
+	int ret;
+
+	cdinfo(CD_DO_IOCTL, "doing audio ioctl (start/stop/pause/resume)\n");
+
+	if (!CDROM_CAN(CDC_PLAY_AUDIO))
+		return -ENOSYS;
+	ret = check_for_audio_disc(cdi, cdi->ops);
+	if (ret)
+		return ret;
+	return cdi->ops->audio_ioctl(cdi, cmd, NULL);
+}
+
+/*
+ * Just about every imaginable ioctl is supported in the Uniform layer
+ * these days.
+ * ATAPI / SCSI specific code now mainly resides in mmc_ioctl().
+ */
+int cdrom_ioctl(struct file * file, struct cdrom_device_info *cdi,
+		struct inode *ip, unsigned int cmd, unsigned long arg)
+{
+	void __user *argp = (void __user *)arg;
+	int ret;
+
+	/*
+	 * Try the generic SCSI command ioctl's first.
+	 */
+	ret = scsi_cmd_ioctl(file, ip->i_bdev->bd_disk, cmd, argp);
+	if (ret != -ENOTTY)
+		return ret;
+
+	switch (cmd) {
+	case CDROMMULTISESSION:
+		return cdrom_ioctl_multisession(cdi, argp);
+	case CDROMEJECT:
+		return cdrom_ioctl_eject(cdi);
+	case CDROMCLOSETRAY:
+		return cdrom_ioctl_closetray(cdi);
+	case CDROMEJECT_SW:
+		return cdrom_ioctl_eject_sw(cdi, arg);
+	case CDROM_MEDIA_CHANGED:
+		return cdrom_ioctl_media_changed(cdi, arg);
+	case CDROM_SET_OPTIONS:
+		return cdrom_ioctl_set_options(cdi, arg);
+	case CDROM_CLEAR_OPTIONS:
+		return cdrom_ioctl_clear_options(cdi, arg);
+	case CDROM_SELECT_SPEED:
+		return cdrom_ioctl_select_speed(cdi, arg);
+	case CDROM_SELECT_DISC:
+		return cdrom_ioctl_select_disc(cdi, arg);
+	case CDROMRESET:
+		return cdrom_ioctl_reset(cdi, ip->i_bdev);
+	case CDROM_LOCKDOOR:
+		return cdrom_ioctl_lock_door(cdi, arg);
+	case CDROM_DEBUG:
+		return cdrom_ioctl_debug(cdi, arg);
+	case CDROM_GET_CAPABILITY:
+		return cdrom_ioctl_get_capability(cdi);
+	case CDROM_GET_MCN:
+		return cdrom_ioctl_get_mcn(cdi, argp);
+	case CDROM_DRIVE_STATUS:
+		return cdrom_ioctl_drive_status(cdi, arg);
+	case CDROM_DISC_STATUS:
+		return cdrom_ioctl_disc_status(cdi);
+	case CDROM_CHANGER_NSLOTS:
+		return cdrom_ioctl_changer_nslots(cdi);
+	}
+
+	/*
+	 * Use the ioctls that are implemented through the generic_packet()
+	 * interface. this may look at bit funny, but if -ENOTTY is
+	 * returned that particular ioctl is not implemented and we
+	 * let it go through the device specific ones.
+	 */
 	if (CDROM_CAN(CDC_GENERIC_PACKET)) {
 		ret = mmc_ioctl(cdi, cmd, arg);
-		if (ret != -ENOTTY) {
+		if (ret != -ENOTTY)
 			return ret;
-		}
 	}
 
-	/* note: most of the cdinfo() calls are commented out here,
-	   because they fill up the sys log when CD players poll
-	   the drive. */
+	/*
+	 * Note: most of the cdinfo() calls are commented out here,
+	 * because they fill up the sys log when CD players poll
+	 * the drive.
+	 */
 	switch (cmd) {
-	case CDROMSUBCHNL: {
-		struct cdrom_subchnl q;
-		u_char requested, back;
-		if (!CDROM_CAN(CDC_PLAY_AUDIO))
-			return -ENOSYS;
-		/* cdinfo(CD_DO_IOCTL,"entering CDROMSUBCHNL\n");*/ 
-		IOCTL_IN(arg, struct cdrom_subchnl, q);
-		requested = q.cdsc_format;
-		if (!((requested == CDROM_MSF) ||
-		      (requested == CDROM_LBA)))
-			return -EINVAL;
-		q.cdsc_format = CDROM_MSF;
-		if ((ret=cdo->audio_ioctl(cdi, cmd, &q)))
-			return ret;
-		back = q.cdsc_format; /* local copy */
-		sanitize_format(&q.cdsc_absaddr, &back, requested);
-		sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested);
-		IOCTL_OUT(arg, struct cdrom_subchnl, q);
-		/* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */ 
-		return 0;
-		}
-	case CDROMREADTOCHDR: {
-		struct cdrom_tochdr header;
-		if (!CDROM_CAN(CDC_PLAY_AUDIO))
-			return -ENOSYS;
-		/* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCHDR\n"); */ 
-		IOCTL_IN(arg, struct cdrom_tochdr, header);
-		if ((ret=cdo->audio_ioctl(cdi, cmd, &header)))
-			return ret;
-		IOCTL_OUT(arg, struct cdrom_tochdr, header);
-		/* cdinfo(CD_DO_IOCTL, "CDROMREADTOCHDR successful\n"); */ 
-		return 0;
-		}
-	case CDROMREADTOCENTRY: {
-		struct cdrom_tocentry entry;
-		u_char requested_format;
-		if (!CDROM_CAN(CDC_PLAY_AUDIO))
-			return -ENOSYS;
-		/* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCENTRY\n"); */ 
-		IOCTL_IN(arg, struct cdrom_tocentry, entry);
-		requested_format = entry.cdte_format;
-		if (!((requested_format == CDROM_MSF) || 
-			(requested_format == CDROM_LBA)))
-				return -EINVAL;
-		/* make interface to low-level uniform */
-		entry.cdte_format = CDROM_MSF;
-		if ((ret=cdo->audio_ioctl(cdi, cmd, &entry)))
-			return ret;
-		sanitize_format(&entry.cdte_addr,
-		&entry.cdte_format, requested_format);
-		IOCTL_OUT(arg, struct cdrom_tocentry, entry);
-		/* cdinfo(CD_DO_IOCTL, "CDROMREADTOCENTRY successful\n"); */ 
-		return 0;
-		}
-	case CDROMPLAYMSF: {
-		struct cdrom_msf msf;
-		if (!CDROM_CAN(CDC_PLAY_AUDIO))
-			return -ENOSYS;
-		cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n"); 
-		IOCTL_IN(arg, struct cdrom_msf, msf);
-		return cdo->audio_ioctl(cdi, cmd, &msf);
-		}
-	case CDROMPLAYTRKIND: {
-		struct cdrom_ti ti;
-		if (!CDROM_CAN(CDC_PLAY_AUDIO))
-			return -ENOSYS;
-		cdinfo(CD_DO_IOCTL, "entering CDROMPLAYTRKIND\n"); 
-		IOCTL_IN(arg, struct cdrom_ti, ti);
-		CHECKAUDIO;
-		return cdo->audio_ioctl(cdi, cmd, &ti);
-		}
-	case CDROMVOLCTRL: {
-		struct cdrom_volctrl volume;
-		if (!CDROM_CAN(CDC_PLAY_AUDIO))
-			return -ENOSYS;
-		cdinfo(CD_DO_IOCTL, "entering CDROMVOLCTRL\n"); 
-		IOCTL_IN(arg, struct cdrom_volctrl, volume);
-		return cdo->audio_ioctl(cdi, cmd, &volume);
-		}
-	case CDROMVOLREAD: {
-		struct cdrom_volctrl volume;
-		if (!CDROM_CAN(CDC_PLAY_AUDIO))
-			return -ENOSYS;
-		cdinfo(CD_DO_IOCTL, "entering CDROMVOLREAD\n"); 
-		if ((ret=cdo->audio_ioctl(cdi, cmd, &volume)))
-			return ret;
-		IOCTL_OUT(arg, struct cdrom_volctrl, volume);
-		return 0;
-		}
+	case CDROMSUBCHNL:
+		return cdrom_ioctl_get_subchnl(cdi, argp);
+	case CDROMREADTOCHDR:
+		return cdrom_ioctl_read_tochdr(cdi, argp);
+	case CDROMREADTOCENTRY:
+		return cdrom_ioctl_read_tocentry(cdi, argp);
+	case CDROMPLAYMSF:
+		return cdrom_ioctl_play_msf(cdi, argp);
+	case CDROMPLAYTRKIND:
+		return cdrom_ioctl_play_trkind(cdi, argp);
+	case CDROMVOLCTRL:
+		return cdrom_ioctl_volctrl(cdi, argp);
+	case CDROMVOLREAD:
+		return cdrom_ioctl_volread(cdi, argp);
 	case CDROMSTART:
 	case CDROMSTOP:
 	case CDROMPAUSE:
-	case CDROMRESUME: {
-		if (!CDROM_CAN(CDC_PLAY_AUDIO))
-			return -ENOSYS;
-		cdinfo(CD_DO_IOCTL, "doing audio ioctl (start/stop/pause/resume)\n"); 
-		CHECKAUDIO;
-		return cdo->audio_ioctl(cdi, cmd, NULL);
-		}
-	} /* switch */
+	case CDROMRESUME:
+		return cdrom_ioctl_audioctl(cdi, cmd);
+	}
 
-	/* do the device specific ioctls */
-	if (CDROM_CAN(CDC_IOCTLS))
-		return cdo->dev_ioctl(cdi, cmd, arg);
-	
 	return -ENOSYS;
 }
 
diff -urN oldtree/drivers/cdrom/cdu31a.c newtree/drivers/cdrom/cdu31a.c
--- oldtree/drivers/cdrom/cdu31a.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/cdrom/cdu31a.c	2006-02-13 19:19:59.709556456 -0500
@@ -2668,7 +2668,7 @@
 	return retval;
 }
 
-static int scd_dev_ioctl(struct cdrom_device_info *cdi,
+static int scd_read_audio(struct cdrom_device_info *cdi,
 			 unsigned int cmd, unsigned long arg)
 {
 	void __user *argp = (void __user *)arg;
@@ -2894,11 +2894,10 @@
 	.get_mcn		= scd_get_mcn,
 	.reset			= scd_reset,
 	.audio_ioctl		= scd_audio_ioctl,
-	.dev_ioctl		= scd_dev_ioctl,
 	.capability		= CDC_OPEN_TRAY | CDC_CLOSE_TRAY | CDC_LOCK |
 				  CDC_SELECT_SPEED | CDC_MULTI_SESSION |
 				  CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO |
-				  CDC_RESET | CDC_IOCTLS | CDC_DRIVE_STATUS,
+				  CDC_RESET | CDC_DRIVE_STATUS,
 	.n_minors		= 1,
 };
 
@@ -2936,6 +2935,9 @@
 		case CDROMCLOSETRAY:
 			retval = scd_tray_move(&scd_info, 0);
 			break;
+		case CDROMREADAUDIO:
+			retval = scd_read_audio(&scd_info, CDROMREADAUDIO, arg);
+			break;
 		default:
 			retval = cdrom_ioctl(file, &scd_info, inode, cmd, arg);
 	}
diff -urN oldtree/drivers/cdrom/cm206.c newtree/drivers/cdrom/cm206.c
--- oldtree/drivers/cdrom/cm206.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/cdrom/cm206.c	2006-02-13 19:19:59.709556456 -0500
@@ -1157,32 +1157,6 @@
 	}
 }
 
-/* Ioctl. These ioctls are specific to the cm206 driver. I have made
-   some driver statistics accessible through ioctl calls.
- */
-
-static int cm206_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
-		       unsigned long arg)
-{
-	switch (cmd) {
-#ifdef STATISTICS
-	case CM206CTL_GET_STAT:
-		if (arg >= NR_STATS)
-			return -EINVAL;
-		else
-			return cd->stats[arg];
-	case CM206CTL_GET_LAST_STAT:
-		if (arg >= NR_STATS)
-			return -EINVAL;
-		else
-			return cd->last_stat[arg];
-#endif
-	default:
-		debug(("Unknown ioctl call 0x%x\n", cmd));
-		return -EINVAL;
-	}
-}
-
 static int cm206_media_changed(struct cdrom_device_info *cdi, int disc_nr)
 {
 	if (cd != NULL) {
@@ -1321,11 +1295,10 @@
 	.get_mcn		= cm206_get_upc,
 	.reset			= cm206_reset,
 	.audio_ioctl		= cm206_audio_ioctl,
-	.dev_ioctl		= cm206_ioctl,
 	.capability		= CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK |
 				  CDC_MULTI_SESSION | CDC_MEDIA_CHANGED |
 				  CDC_MCN | CDC_PLAY_AUDIO | CDC_SELECT_SPEED |
-				  CDC_IOCTLS | CDC_DRIVE_STATUS,
+				  CDC_DRIVE_STATUS,
 	.n_minors		= 1,
 };
 
@@ -1350,6 +1323,21 @@
 static int cm206_block_ioctl(struct inode *inode, struct file *file,
 				unsigned cmd, unsigned long arg)
 {
+	switch (cmd) {
+#ifdef STATISTICS
+	case CM206CTL_GET_STAT:
+		if (arg >= NR_STATS)
+			return -EINVAL;
+		return cd->stats[arg];
+	case CM206CTL_GET_LAST_STAT:
+		if (arg >= NR_STATS)
+			return -EINVAL;
+		return cd->last_stat[arg];
+#endif
+	default:
+		break;
+	}
+
 	return cdrom_ioctl(file, &cm206_info, inode, cmd, arg);
 }
 
diff -urN oldtree/drivers/cdrom/sbpcd.c newtree/drivers/cdrom/sbpcd.c
--- oldtree/drivers/cdrom/sbpcd.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/cdrom/sbpcd.c	2006-02-13 19:19:59.793543688 -0500
@@ -4160,18 +4160,13 @@
 	return  0;
 }
 
-/*==========================================================================*/
-/*==========================================================================*/
-/*
- * ioctl support
- */
-static int sbpcd_dev_ioctl(struct cdrom_device_info *cdi, u_int cmd,
-		      u_long arg)
+static int sbpcd_audio_ioctl(struct cdrom_device_info *cdi, u_int cmd,
+		       void * arg)
 {
 	struct sbpcd_drive *p = cdi->handle;
-	int i;
+	int i, st, j;
 	
-	msg(DBG_IO2,"ioctl(%s, 0x%08lX, 0x%08lX)\n", cdi->name, cmd, arg);
+	msg(DBG_IO2,"ioctl(%s, 0x%08lX, 0x%08p)\n", cdi->name, cmd, arg);
 	if (p->drv_id==-1) {
 		msg(DBG_INF, "ioctl: bad device: %s\n", cdi->name);
 		return (-ENXIO);             /* no such drive */
@@ -4183,1194 +4178,1192 @@
 	msg(DBG_IO2,"ioctl: device %s, request %04X\n",cdi->name,cmd);
 	switch (cmd) 		/* Sun-compatible */
 	{
-	case DDIOCSDBG:		/* DDI Debug */
-		if (!capable(CAP_SYS_ADMIN)) RETURN_UP(-EPERM);
-		i=sbpcd_dbg_ioctl(arg,1);
-		RETURN_UP(i);
-	case CDROMRESET:      /* hard reset the drive */
-		msg(DBG_IOC,"ioctl: CDROMRESET entered.\n");
-		i=DriveReset();
-		current_drive->audio_state=0;
-		RETURN_UP(i);
 		
-	case CDROMREADMODE1:
-		msg(DBG_IOC,"ioctl: CDROMREADMODE1 requested.\n");
+	case CDROMPAUSE:     /* Pause the drive */
+		msg(DBG_IOC,"ioctl: CDROMPAUSE entered.\n");
+		/* pause the drive unit when it is currently in PLAY mode,         */
+		/* or reset the starting and ending locations when in PAUSED mode. */
+		/* If applicable, at the next stopping point it reaches            */
+		/* the drive will discontinue playing.                             */
+		switch (current_drive->audio_state)
+		{
+		case audio_playing:
+			if (famL_drive) i=cc_ReadSubQ();
+			else i=cc_Pause_Resume(1);
+			if (i<0) RETURN_UP(-EIO);
+			if (famL_drive) i=cc_Pause_Resume(1);
+			else i=cc_ReadSubQ();
+			if (i<0) RETURN_UP(-EIO);
+			current_drive->pos_audio_start=current_drive->SubQ_run_tot;
+			current_drive->audio_state=audio_pausing;
+			RETURN_UP(0);
+		case audio_pausing:
+			i=cc_Seek(current_drive->pos_audio_start,1);
+			if (i<0) RETURN_UP(-EIO);
+			RETURN_UP(0);
+		default:
+			RETURN_UP(-EINVAL);
+		}
+
+	case CDROMRESUME: /* resume paused audio play */
+		msg(DBG_IOC,"ioctl: CDROMRESUME entered.\n");
+		/* resume playing audio tracks when a previous PLAY AUDIO call has  */
+		/* been paused with a PAUSE command.                                */
+		/* It will resume playing from the location saved in SubQ_run_tot.  */
+		if (current_drive->audio_state!=audio_pausing) RETURN_UP(-EINVAL);
+		if (famL_drive)
+			i=cc_PlayAudio(current_drive->pos_audio_start,
+				       current_drive->pos_audio_end);
+		else i=cc_Pause_Resume(3);
+		if (i<0) RETURN_UP(-EIO);
+		current_drive->audio_state=audio_playing;
+		RETURN_UP(0);
+
+	case CDROMPLAYMSF:
+		msg(DBG_IOC,"ioctl: CDROMPLAYMSF entered.\n");
 #ifdef SAFE_MIXED
 		if (current_drive->has_data>1) RETURN_UP(-EBUSY);
 #endif /* SAFE_MIXED */
-		cc_ModeSelect(CD_FRAMESIZE);
-		cc_ModeSense();
-		current_drive->mode=READ_M1;
+		if (current_drive->audio_state==audio_playing)
+		{
+			i=cc_Pause_Resume(1);
+			if (i<0) RETURN_UP(-EIO);
+			i=cc_ReadSubQ();
+			if (i<0) RETURN_UP(-EIO);
+			current_drive->pos_audio_start=current_drive->SubQ_run_tot;
+			i=cc_Seek(current_drive->pos_audio_start,1);
+		}
+		memcpy(&msf, (void *) arg, sizeof(struct cdrom_msf));
+		/* values come as msf-bin */
+		current_drive->pos_audio_start = (msf.cdmsf_min0<<16) |
+                        (msf.cdmsf_sec0<<8) |
+				msf.cdmsf_frame0;
+		current_drive->pos_audio_end = (msf.cdmsf_min1<<16) |
+			(msf.cdmsf_sec1<<8) |
+				msf.cdmsf_frame1;
+		msg(DBG_IOX,"ioctl: CDROMPLAYMSF %08X %08X\n",
+		    current_drive->pos_audio_start,current_drive->pos_audio_end);
+		i=cc_PlayAudio(current_drive->pos_audio_start,current_drive->pos_audio_end);
+		if (i<0)
+		{
+			msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i);
+			DriveReset();
+			current_drive->audio_state=0;
+			RETURN_UP(-EIO);
+		}
+		current_drive->audio_state=audio_playing;
 		RETURN_UP(0);
 		
-	case CDROMREADMODE2: /* not usable at the moment */
-		msg(DBG_IOC,"ioctl: CDROMREADMODE2 requested.\n");
+	case CDROMPLAYTRKIND: /* Play a track.  This currently ignores index. */
+		msg(DBG_IOC,"ioctl: CDROMPLAYTRKIND entered.\n");
 #ifdef SAFE_MIXED
 		if (current_drive->has_data>1) RETURN_UP(-EBUSY);
 #endif /* SAFE_MIXED */
-		cc_ModeSelect(CD_FRAMESIZE_RAW1);
-		cc_ModeSense();
-		current_drive->mode=READ_M2;
+		if (current_drive->audio_state==audio_playing)
+		{
+			msg(DBG_IOX,"CDROMPLAYTRKIND: already audio_playing.\n");
+#if 1
+			RETURN_UP(0); /* just let us play on */
+#else
+			RETURN_UP(-EINVAL); /* play on, but say "error" */
+#endif
+		}
+		memcpy(&ti,(void *) arg,sizeof(struct cdrom_ti));
+		msg(DBG_IOX,"ioctl: trk0: %d, ind0: %d, trk1:%d, ind1:%d\n",
+		    ti.cdti_trk0,ti.cdti_ind0,ti.cdti_trk1,ti.cdti_ind1);
+		if (ti.cdti_trk0<current_drive->n_first_track) RETURN_UP(-EINVAL);
+		if (ti.cdti_trk0>current_drive->n_last_track) RETURN_UP(-EINVAL);
+		if (ti.cdti_trk1<ti.cdti_trk0) ti.cdti_trk1=ti.cdti_trk0;
+		if (ti.cdti_trk1>current_drive->n_last_track) ti.cdti_trk1=current_drive->n_last_track;
+		current_drive->pos_audio_start=current_drive->TocBuffer[ti.cdti_trk0].address;
+		current_drive->pos_audio_end=current_drive->TocBuffer[ti.cdti_trk1+1].address;
+		i=cc_PlayAudio(current_drive->pos_audio_start,current_drive->pos_audio_end);
+		if (i<0)
+		{
+			msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i);
+			DriveReset();
+			current_drive->audio_state=0;
+			RETURN_UP(-EIO);
+		}
+		current_drive->audio_state=audio_playing;
 		RETURN_UP(0);
 		
-	case CDROMAUDIOBUFSIZ: /* configure the audio buffer size */
-		msg(DBG_IOC,"ioctl: CDROMAUDIOBUFSIZ entered.\n");
-		if (current_drive->sbp_audsiz>0)
-			vfree(current_drive->aud_buf);
-		current_drive->aud_buf=NULL;
-		current_drive->sbp_audsiz=arg;
+	case CDROMREADTOCHDR:        /* Read the table of contents header */
+		msg(DBG_IOC,"ioctl: CDROMREADTOCHDR entered.\n");
+		tochdr.cdth_trk0=current_drive->n_first_track;
+		tochdr.cdth_trk1=current_drive->n_last_track;
+		memcpy((void *) arg, &tochdr, sizeof(struct cdrom_tochdr));
+		RETURN_UP(0);
 		
-		if (current_drive->sbp_audsiz>16)
-		{
-			current_drive->sbp_audsiz = 0;
-			RETURN_UP(current_drive->sbp_audsiz);
-		}
-	
-		if (current_drive->sbp_audsiz>0)
+	case CDROMREADTOCENTRY:      /* Read an entry in the table of contents */
+		msg(DBG_IOC,"ioctl: CDROMREADTOCENTRY entered.\n");
+		memcpy(&tocentry, (void *) arg, sizeof(struct cdrom_tocentry));
+		i=tocentry.cdte_track;
+		if (i==CDROM_LEADOUT) i=current_drive->n_last_track+1;
+		else if (i<current_drive->n_first_track||i>current_drive->n_last_track)
+                  RETURN_UP(-EINVAL);
+		tocentry.cdte_adr=current_drive->TocBuffer[i].ctl_adr&0x0F;
+		tocentry.cdte_ctrl=(current_drive->TocBuffer[i].ctl_adr>>4)&0x0F;
+		tocentry.cdte_datamode=current_drive->TocBuffer[i].format;
+		if (tocentry.cdte_format==CDROM_MSF) /* MSF-bin required */
 		{
-			current_drive->aud_buf=(u_char *) vmalloc(current_drive->sbp_audsiz*CD_FRAMESIZE_RAW);
-			if (current_drive->aud_buf==NULL)
-			{
-				msg(DBG_INF,"audio buffer (%d frames) not available.\n",current_drive->sbp_audsiz);
-				current_drive->sbp_audsiz=0;
-			}
-			else msg(DBG_INF,"audio buffer size: %d frames.\n",current_drive->sbp_audsiz);
+			tocentry.cdte_addr.msf.minute=(current_drive->TocBuffer[i].address>>16)&0x00FF;
+			tocentry.cdte_addr.msf.second=(current_drive->TocBuffer[i].address>>8)&0x00FF;
+			tocentry.cdte_addr.msf.frame=current_drive->TocBuffer[i].address&0x00FF;
 		}
-		RETURN_UP(current_drive->sbp_audsiz);
-
-	case CDROMREADAUDIO:
-	{ /* start of CDROMREADAUDIO */
-		int i=0, j=0, frame, block=0;
-		u_int try=0;
-		u_long timeout;
-		u_char *p;
-		u_int data_tries = 0;
-		u_int data_waits = 0;
-		u_int data_retrying = 0;
-		int status_tries;
-		int error_flag;
+		else if (tocentry.cdte_format==CDROM_LBA) /* blk required */
+			tocentry.cdte_addr.lba=msf2blk(current_drive->TocBuffer[i].address);
+		else RETURN_UP(-EINVAL);
+		memcpy((void *) arg, &tocentry, sizeof(struct cdrom_tocentry));
+		RETURN_UP(0);
 		
-		msg(DBG_IOC,"ioctl: CDROMREADAUDIO entered.\n");
-		if (fam0_drive) RETURN_UP(-EINVAL);
-		if (famL_drive) RETURN_UP(-EINVAL);
-		if (famV_drive) RETURN_UP(-EINVAL);
-		if (famT_drive) RETURN_UP(-EINVAL);
+	case CDROMSTOP:      /* Spin down the drive */
+		msg(DBG_IOC,"ioctl: CDROMSTOP entered.\n");
 #ifdef SAFE_MIXED
 		if (current_drive->has_data>1) RETURN_UP(-EBUSY);
 #endif /* SAFE_MIXED */ 
-		if (current_drive->aud_buf==NULL) RETURN_UP(-EINVAL);
-		if (copy_from_user(&read_audio, (void __user *)arg,
-				   sizeof(struct cdrom_read_audio)))
-			RETURN_UP(-EFAULT);
-		if (read_audio.nframes < 0 || read_audio.nframes>current_drive->sbp_audsiz) RETURN_UP(-EINVAL);
-		if (!access_ok(VERIFY_WRITE, read_audio.buf,
-			      read_audio.nframes*CD_FRAMESIZE_RAW))
-                	RETURN_UP(-EFAULT);
-		
-		if (read_audio.addr_format==CDROM_MSF) /* MSF-bin specification of where to start */
-			block=msf2lba(&read_audio.addr.msf.minute);
-		else if (read_audio.addr_format==CDROM_LBA) /* lba specification of where to start */
-			block=read_audio.addr.lba;
-		else RETURN_UP(-EINVAL);
-#if 000
-		i=cc_SetSpeed(speed_150,0,0);
-		if (i) msg(DBG_AUD,"read_audio: SetSpeed error %d\n", i);
+		i=cc_Pause_Resume(1);
+		current_drive->audio_state=0;
+#if 0
+		cc_DriveReset();
 #endif
-		msg(DBG_AUD,"read_audio: lba: %d, msf: %06X\n",
-		    block, blk2msf(block));
-		msg(DBG_AUD,"read_audio: before cc_ReadStatus.\n");
-#if OLD_BUSY
-		while (busy_data) sbp_sleep(HZ/10); /* wait a bit */
-		busy_audio=1;
-#endif /* OLD_BUSY */ 
-		error_flag=0;
-		for (data_tries=5; data_tries>0; data_tries--)
-		{
-			msg(DBG_AUD,"data_tries=%d ...\n", data_tries);
-			current_drive->mode=READ_AU;
-			cc_ModeSelect(CD_FRAMESIZE_RAW);
-			cc_ModeSense();
-			for (status_tries=3; status_tries > 0; status_tries--)
-			{
-				flags_cmd_out |= f_respo3;
-				cc_ReadStatus();
-				if (sbp_status() != 0) break;
-				if (st_check) cc_ReadError();
-				sbp_sleep(1);    /* wait a bit, try again */
-			}
-			if (status_tries == 0)
-			{
-				msg(DBG_AUD,"read_audio: sbp_status: failed after 3 tries in line %d.\n", __LINE__);
-				continue;
-			}
-			msg(DBG_AUD,"read_audio: sbp_status: ok.\n");
-			
-			flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus | f_obey_p_check;
-			if (fam0L_drive)
-			{
-				flags_cmd_out |= f_lopsta | f_getsta | f_bit1;
-				cmd_type=READ_M2;
-				drvcmd[0]=CMD0_READ_XA; /* "read XA frames", old drives */
-				drvcmd[1]=(block>>16)&0x000000ff;
-				drvcmd[2]=(block>>8)&0x000000ff;
-				drvcmd[3]=block&0x000000ff;
-				drvcmd[4]=0;
-				drvcmd[5]=read_audio.nframes; /* # of frames */
-				drvcmd[6]=0;
-			}
-			else if (fam1_drive)
-			{
-				drvcmd[0]=CMD1_READ; /* "read frames", new drives */
-				lba2msf(block,&drvcmd[1]); /* msf-bin format required */
-				drvcmd[4]=0;
-				drvcmd[5]=0;
-				drvcmd[6]=read_audio.nframes; /* # of frames */
-			}
-			else if (fam2_drive)
-			{
-				drvcmd[0]=CMD2_READ_XA2;
-				lba2msf(block,&drvcmd[1]); /* msf-bin format required */
-				drvcmd[4]=0;
-				drvcmd[5]=read_audio.nframes; /* # of frames */
-				drvcmd[6]=0x11; /* raw mode */
-			}
-			else if (famT_drive) /* CD-55A: not tested yet */
-			{
-			}
-			msg(DBG_AUD,"read_audio: before giving \"read\" command.\n");
-			flags_cmd_out=f_putcmd;
-			response_count=0;
-			i=cmd_out();
-			if (i<0) msg(DBG_INF,"error giving READ AUDIO command: %0d\n", i);
-			sbp_sleep(0);
-			msg(DBG_AUD,"read_audio: after giving \"read\" command.\n");
-			for (frame=1;frame<2 && !error_flag; frame++)
-			{
-				try=maxtim_data;
-				for (timeout=jiffies+9*HZ; ; )
-				{
-					for ( ; try!=0;try--)
-					{
-						j=inb(CDi_status);
-						if (!(j&s_not_data_ready)) break;
-						if (!(j&s_not_result_ready)) break;
-						if (fam0L_drive) if (j&s_attention) break;
-					}
-					if (try != 0 || time_after_eq(jiffies, timeout)) break;
-					if (data_retrying == 0) data_waits++;
-					data_retrying = 1;
-					sbp_sleep(1);
-					try = 1;
-				}
-				if (try==0)
-				{
-					msg(DBG_INF,"read_audio: sbp_data: CDi_status timeout.\n");
-					error_flag++;
-					break;
-				}
-				msg(DBG_AUD,"read_audio: sbp_data: CDi_status ok.\n");
-				if (j&s_not_data_ready)
-				{
-					msg(DBG_INF, "read_audio: sbp_data: DATA_READY timeout.\n");
-					error_flag++;
-					break;
-				}
-				msg(DBG_AUD,"read_audio: before reading data.\n");
-				error_flag=0;
-				p = current_drive->aud_buf;
-				if (sbpro_type==1) OUT(CDo_sel_i_d,1);
-				if (do_16bit)
-				{
-					u_short *p2 = (u_short *) p;
+		RETURN_UP(i);
 
-					for (; (u_char *) p2 < current_drive->aud_buf + read_audio.nframes*CD_FRAMESIZE_RAW;)
-				  	{
-						if ((inb_p(CDi_status)&s_not_data_ready)) continue;
+	case CDROMSTART:  /* Spin up the drive */
+		msg(DBG_IOC,"ioctl: CDROMSTART entered.\n");
+		cc_SpinUp();
+		current_drive->audio_state=0;
+		RETURN_UP(0);
 
-						/* get one sample */
-						*p2++ = inw_p(CDi_data);
-						*p2++ = inw_p(CDi_data);
-					}
-				} else {
-					for (; p < current_drive->aud_buf + read_audio.nframes*CD_FRAMESIZE_RAW;)
-				  	{
-						if ((inb_p(CDi_status)&s_not_data_ready)) continue;
+	case CDROMVOLCTRL:   /* Volume control */
+		msg(DBG_IOC,"ioctl: CDROMVOLCTRL entered.\n");
+		memcpy(&volctrl,(char *) arg,sizeof(volctrl));
+		current_drive->vol_chan0=0;
+		current_drive->vol_ctrl0=volctrl.channel0;
+		current_drive->vol_chan1=1;
+		current_drive->vol_ctrl1=volctrl.channel1;
+		i=cc_SetVolume();
+		RETURN_UP(0);
 
-						/* get one sample */
-						*p++ = inb_p(CDi_data);
-						*p++ = inb_p(CDi_data);
-						*p++ = inb_p(CDi_data);
-						*p++ = inb_p(CDi_data);
-					}
-				}
-				if (sbpro_type==1) OUT(CDo_sel_i_d,0);
-				data_retrying = 0;
-			}
-			msg(DBG_AUD,"read_audio: after reading data.\n");
-			if (error_flag)    /* must have been spurious D_RDY or (ATTN&&!D_RDY) */
-			{
-				msg(DBG_AUD,"read_audio: read aborted by drive\n");
-#if 0000
-				i=cc_DriveReset();                /* ugly fix to prevent a hang */
-#else
-				i=cc_ReadError();
-#endif
-				continue;
-			}
-			if (fam0L_drive)
-			{
-				i=maxtim_data;
-				for (timeout=jiffies+9*HZ; time_before(jiffies, timeout); timeout--)
-				{
-					for ( ;i!=0;i--)
-					{
-						j=inb(CDi_status);
-						if (!(j&s_not_data_ready)) break;
-						if (!(j&s_not_result_ready)) break;
-						if (j&s_attention) break;
-					}
-					if (i != 0 || time_after_eq(jiffies, timeout)) break;
-					sbp_sleep(0);
-					i = 1;
-				}
-				if (i==0) msg(DBG_AUD,"read_audio: STATUS TIMEOUT AFTER READ");
-				if (!(j&s_attention))
-				{
-					msg(DBG_AUD,"read_audio: sbp_data: timeout waiting DRV_ATTN - retrying\n");
-					i=cc_DriveReset();  /* ugly fix to prevent a hang */
-					continue;
-				}
+	case CDROMVOLREAD:   /* read Volume settings from drive */
+		msg(DBG_IOC,"ioctl: CDROMVOLREAD entered.\n");
+		st=cc_GetVolume();
+		if (st<0) RETURN_UP(st);
+		volctrl.channel0=current_drive->vol_ctrl0;
+		volctrl.channel1=current_drive->vol_ctrl1;
+		volctrl.channel2=0;
+		volctrl.channel2=0;
+		memcpy((void *)arg,&volctrl,sizeof(volctrl));
+		RETURN_UP(0);
+
+	case CDROMSUBCHNL:   /* Get subchannel info */
+		msg(DBG_IOS,"ioctl: CDROMSUBCHNL entered.\n");
+		/* Bogus, I can do better than this! --AJK
+		if ((st_spinning)||(!subq_valid)) {
+			i=cc_ReadSubQ();
+			if (i<0) RETURN_UP(-EIO);
+		}
+		*/
+		i=cc_ReadSubQ();
+		if (i<0) {
+			j=cc_ReadError(); /* clear out error status from drive */
+			current_drive->audio_state=CDROM_AUDIO_NO_STATUS;
+			/* get and set the disk state here,
+			probably not the right place, but who cares!
+			It makes it work properly! --AJK */
+			if (current_drive->CD_changed==0xFF) {
+				msg(DBG_000,"Disk changed detect\n");
+				current_drive->diskstate_flags &= ~cd_size_bit;
 			}
-			do
-			{
-				if (fam0L_drive) cc_ReadStatus();
-				i=ResponseStatus();  /* builds status_bits, returns orig. status (old) or faked p_success (new) */
-				if (i<0) { msg(DBG_AUD,
-					       "read_audio: cc_ReadStatus error after read: %02X\n",
-					       current_drive->status_bits);
-					   continue; /* FIXME */
-				   }
+			RETURN_UP(-EIO);
+		}
+		if (current_drive->CD_changed==0xFF) {
+			/* reread the TOC because the disk has changed! --AJK */
+			msg(DBG_000,"Disk changed STILL detected, rereading TOC!\n");
+			i=DiskInfo();
+			if(i==0) {
+				current_drive->CD_changed=0x00; /* cd has changed, procede, */
+				RETURN_UP(-EIO); /* and get TOC, etc on next try! --AJK */
+			} else {
+				RETURN_UP(-EIO); /* we weren't ready yet! --AJK */
 			}
-			while ((fam0L_drive)&&(!st_check)&&(!(i&p_success)));
-			if (st_check)
-			{
-				i=cc_ReadError();
-				msg(DBG_AUD,"read_audio: cc_ReadError was necessary after read: %02X\n",i);
-				continue;
+		}
+		memcpy(&SC, (void *) arg, sizeof(struct cdrom_subchnl));
+		/*
+			This virtual crap is very bogus!
+			It doesn't detect when the cd is done playing audio!
+			Lets do this right with proper hardware register reading!
+		*/
+		cc_ReadStatus();
+		i=ResponseStatus();
+		msg(DBG_000,"Drive Status: door_locked =%d.\n", st_door_locked);
+		msg(DBG_000,"Drive Status: door_closed =%d.\n", st_door_closed);
+		msg(DBG_000,"Drive Status: caddy_in =%d.\n", st_caddy_in);
+		msg(DBG_000,"Drive Status: disk_ok =%d.\n", st_diskok);
+		msg(DBG_000,"Drive Status: spinning =%d.\n", st_spinning);
+		msg(DBG_000,"Drive Status: busy =%d.\n", st_busy);
+		/* st_busy indicates if it's _ACTUALLY_ playing audio */
+		switch (current_drive->audio_state)
+		{
+		case audio_playing:
+			if(st_busy==0) {
+				/* CD has stopped playing audio --AJK */
+				current_drive->audio_state=audio_completed;
+				SC.cdsc_audiostatus=CDROM_AUDIO_COMPLETED;
+			} else {
+				SC.cdsc_audiostatus=CDROM_AUDIO_PLAY;
 			}
-			if (copy_to_user(read_audio.buf,
-					 current_drive->aud_buf,
-					 read_audio.nframes * CD_FRAMESIZE_RAW))
-				RETURN_UP(-EFAULT);
-			msg(DBG_AUD,"read_audio: copy_to_user done.\n");
+			break;
+		case audio_pausing:
+			SC.cdsc_audiostatus=CDROM_AUDIO_PAUSED;
+			break;
+		case audio_completed:
+			SC.cdsc_audiostatus=CDROM_AUDIO_COMPLETED;
+			break;
+		default:
+			SC.cdsc_audiostatus=CDROM_AUDIO_NO_STATUS;
 			break;
 		}
-		cc_ModeSelect(CD_FRAMESIZE);
-		cc_ModeSense();
-		current_drive->mode=READ_M1;
-#if OLD_BUSY
-		busy_audio=0;
-#endif /* OLD_BUSY */ 
-		if (data_tries == 0)
+		SC.cdsc_adr=current_drive->SubQ_ctl_adr;
+		SC.cdsc_ctrl=current_drive->SubQ_ctl_adr>>4;
+		SC.cdsc_trk=bcd2bin(current_drive->SubQ_trk);
+		SC.cdsc_ind=bcd2bin(current_drive->SubQ_pnt_idx);
+		if (SC.cdsc_format==CDROM_LBA)
 		{
-			msg(DBG_AUD,"read_audio: failed after 5 tries in line %d.\n", __LINE__);
-			RETURN_UP(-EIO);
+			SC.cdsc_absaddr.lba=msf2blk(current_drive->SubQ_run_tot);
+			SC.cdsc_reladdr.lba=msf2blk(current_drive->SubQ_run_trk);
 		}
-		msg(DBG_AUD,"read_audio: successful return.\n");
+		else /* not only if (SC.cdsc_format==CDROM_MSF) */
+		{
+			SC.cdsc_absaddr.msf.minute=(current_drive->SubQ_run_tot>>16)&0x00FF;
+			SC.cdsc_absaddr.msf.second=(current_drive->SubQ_run_tot>>8)&0x00FF;
+			SC.cdsc_absaddr.msf.frame=current_drive->SubQ_run_tot&0x00FF;
+			SC.cdsc_reladdr.msf.minute=(current_drive->SubQ_run_trk>>16)&0x00FF;
+			SC.cdsc_reladdr.msf.second=(current_drive->SubQ_run_trk>>8)&0x00FF;
+			SC.cdsc_reladdr.msf.frame=current_drive->SubQ_run_trk&0x00FF;
+		}
+		memcpy((void *) arg, &SC, sizeof(struct cdrom_subchnl));
+		msg(DBG_IOS,"CDROMSUBCHNL: %1X %02X %08X %08X %02X %02X %06X %06X\n",
+		    SC.cdsc_format,SC.cdsc_audiostatus,
+		    SC.cdsc_adr,SC.cdsc_ctrl,
+		    SC.cdsc_trk,SC.cdsc_ind,
+		    SC.cdsc_absaddr,SC.cdsc_reladdr);
 		RETURN_UP(0);
-	} /* end of CDROMREADAUDIO */
-		
+
 	default:
 		msg(DBG_IOC,"ioctl: unknown function request %04X\n", cmd);
 		RETURN_UP(-EINVAL);
 	} /* end switch(cmd) */
 }
+/*==========================================================================*/
+/*
+ *  Take care of the different block sizes between cdrom and Linux.
+ */
+static void sbp_transfer(struct request *req)
+{
+	long offs;
 
-static int sbpcd_audio_ioctl(struct cdrom_device_info *cdi, u_int cmd,
-		       void * arg)
+	while ( (req->nr_sectors > 0) &&
+	       (req->sector/4 >= current_drive->sbp_first_frame) &&
+	       (req->sector/4 <= current_drive->sbp_last_frame) )
+	{
+		offs = (req->sector - current_drive->sbp_first_frame * 4) * 512;
+		memcpy(req->buffer, current_drive->sbp_buf + offs, 512);
+		req->nr_sectors--;
+		req->sector++;
+		req->buffer += 512;
+	}
+}
+/*==========================================================================*/
+/*
+ *  special end_request for sbpcd to solve CURRENT==NULL bug. (GTL)
+ *  GTL = Gonzalo Tornaria <tornaria@cmat.edu.uy>
+ *
+ *  This is a kludge so we don't need to modify end_request.
+ *  We put the req we take out after INIT_REQUEST in the requests list,
+ *  so that end_request will discard it.
+ *
+ *  The bug could be present in other block devices, perhaps we
+ *  should modify INIT_REQUEST and end_request instead, and
+ *  change every block device..
+ *
+ *  Could be a race here?? Could e.g. a timer interrupt schedule() us?
+ *  If so, we should copy end_request here, and do it right.. (or
+ *  modify end_request and the block devices).
+ *
+ *  In any case, the race here would be much small than it was, and
+ *  I couldn't reproduce..
+ *
+ *  The race could be: suppose CURRENT==NULL. We put our req in the list,
+ *  and we are scheduled. Other process takes over, and gets into
+ *  do_sbpcd_request. It sees CURRENT!=NULL (it is == to our req), so
+ *  proceeds. It ends, so CURRENT is now NULL.. Now we awake somewhere in
+ *  end_request, but now CURRENT==NULL... oops!
+ *
+ */
+#undef DEBUG_GTL
+
+/*==========================================================================*/
+/*
+ *  I/O request routine, called from Linux kernel.
+ */
+static void do_sbpcd_request(request_queue_t * q)
 {
-	struct sbpcd_drive *p = cdi->handle;
-	int i, st, j;
-	
-	msg(DBG_IO2,"ioctl(%s, 0x%08lX, 0x%08p)\n", cdi->name, cmd, arg);
-	if (p->drv_id==-1) {
-		msg(DBG_INF, "ioctl: bad device: %s\n", cdi->name);
-		return (-ENXIO);             /* no such drive */
+	u_int block;
+	u_int nsect;
+	int status_tries, data_tries;
+	struct request *req;
+	struct sbpcd_drive *p;
+#ifdef DEBUG_GTL
+	static int xx_nr=0;
+	int xnr;
+#endif
+
+ request_loop:
+#ifdef DEBUG_GTL
+	xnr=++xx_nr;
+
+	req = elv_next_request(q);
+
+	if (!req)
+	{
+		printk( "do_sbpcd_request[%di](NULL), Pid:%d, Time:%li\n",
+			xnr, current->pid, jiffies);
+		printk( "do_sbpcd_request[%do](NULL) end 0 (null), Time:%li\n",
+			xnr, jiffies);
+		return;
 	}
+
+	printk(" do_sbpcd_request[%di](%p:%ld+%ld), Pid:%d, Time:%li\n",
+		xnr, req, req->sector, req->nr_sectors, current->pid, jiffies);
+#endif
+
+	req = elv_next_request(q);	/* take out our request so no other */
+	if (!req)
+		return;
+
+	if (req -> sector == -1)
+		end_request(req, 0);
+	spin_unlock_irq(q->queue_lock);
+
 	down(&ioctl_read_sem);
+	if (rq_data_dir(elv_next_request(q)) != READ)
+	{
+		msg(DBG_INF, "bad cmd %d\n", req->cmd[0]);
+		goto err_done;
+	}
+	p = req->rq_disk->private_data;
+#if OLD_BUSY
+	while (busy_audio) sbp_sleep(HZ); /* wait a bit */
+	busy_data=1;
+#endif /* OLD_BUSY */
+
+	if (p->audio_state==audio_playing) goto err_done;
 	if (p != current_drive)
 		switch_drive(p);
-	
-	msg(DBG_IO2,"ioctl: device %s, request %04X\n",cdi->name,cmd);
-	switch (cmd) 		/* Sun-compatible */
+
+	block = req->sector; /* always numbered as 512-byte-pieces */
+	nsect = req->nr_sectors; /* always counted as 512-byte-pieces */
+
+	msg(DBG_BSZ,"read sector %d (%d sectors)\n", block, nsect);
+#if 0
+	msg(DBG_MUL,"read LBA %d\n", block/4);
+#endif
+
+	sbp_transfer(req);
+	/* if we satisfied the request from the buffer, we're done. */
+	if (req->nr_sectors == 0)
 	{
-		
-	case CDROMPAUSE:     /* Pause the drive */
-		msg(DBG_IOC,"ioctl: CDROMPAUSE entered.\n");
-		/* pause the drive unit when it is currently in PLAY mode,         */
-		/* or reset the starting and ending locations when in PAUSED mode. */
-		/* If applicable, at the next stopping point it reaches            */
-		/* the drive will discontinue playing.                             */
-		switch (current_drive->audio_state)
-		{
-		case audio_playing:
-			if (famL_drive) i=cc_ReadSubQ();
-			else i=cc_Pause_Resume(1);
-			if (i<0) RETURN_UP(-EIO);
-			if (famL_drive) i=cc_Pause_Resume(1);
-			else i=cc_ReadSubQ();
-			if (i<0) RETURN_UP(-EIO);
-			current_drive->pos_audio_start=current_drive->SubQ_run_tot;
-			current_drive->audio_state=audio_pausing;
-			RETURN_UP(0);
-		case audio_pausing:
-			i=cc_Seek(current_drive->pos_audio_start,1);
-			if (i<0) RETURN_UP(-EIO);
-			RETURN_UP(0);
-		default:
-			RETURN_UP(-EINVAL);
-		}
-		
-	case CDROMRESUME: /* resume paused audio play */
-		msg(DBG_IOC,"ioctl: CDROMRESUME entered.\n");
-		/* resume playing audio tracks when a previous PLAY AUDIO call has  */
-		/* been paused with a PAUSE command.                                */
-		/* It will resume playing from the location saved in SubQ_run_tot.  */
-		if (current_drive->audio_state!=audio_pausing) RETURN_UP(-EINVAL);
-		if (famL_drive)
-			i=cc_PlayAudio(current_drive->pos_audio_start,
-				       current_drive->pos_audio_end);
-		else i=cc_Pause_Resume(3);
-		if (i<0) RETURN_UP(-EIO);
-		current_drive->audio_state=audio_playing;
-		RETURN_UP(0);
-		
-	case CDROMPLAYMSF:
-		msg(DBG_IOC,"ioctl: CDROMPLAYMSF entered.\n");
-#ifdef SAFE_MIXED
-		if (current_drive->has_data>1) RETURN_UP(-EBUSY);
-#endif /* SAFE_MIXED */ 
-		if (current_drive->audio_state==audio_playing)
+#ifdef DEBUG_GTL
+		printk(" do_sbpcd_request[%do](%p:%ld+%ld) end 2, Time:%li\n",
+			xnr, req, req->sector, req->nr_sectors, jiffies);
+#endif
+		up(&ioctl_read_sem);
+		spin_lock_irq(q->queue_lock);
+		end_request(req, 1);
+		goto request_loop;
+	}
+
+#ifdef FUTURE
+	i=prepare(0,0); /* at moment not really a hassle check, but ... */
+	if (i!=0)
+		msg(DBG_INF,"\"prepare\" tells error %d -- ignored\n", i);
+#endif /* FUTURE */
+
+	if (!st_spinning) cc_SpinUp();
+
+	for (data_tries=n_retries; data_tries > 0; data_tries--)
+	{
+		for (status_tries=3; status_tries > 0; status_tries--)
 		{
-			i=cc_Pause_Resume(1);
-			if (i<0) RETURN_UP(-EIO);
-			i=cc_ReadSubQ();
-			if (i<0) RETURN_UP(-EIO);
-			current_drive->pos_audio_start=current_drive->SubQ_run_tot;
-			i=cc_Seek(current_drive->pos_audio_start,1);
+			flags_cmd_out |= f_respo3;
+			cc_ReadStatus();
+			if (sbp_status() != 0) break;
+			if (st_check) cc_ReadError();
+			sbp_sleep(1);    /* wait a bit, try again */
 		}
-		memcpy(&msf, (void *) arg, sizeof(struct cdrom_msf));
-		/* values come as msf-bin */
-		current_drive->pos_audio_start = (msf.cdmsf_min0<<16) |
-                        (msf.cdmsf_sec0<<8) |
-				msf.cdmsf_frame0;
-		current_drive->pos_audio_end = (msf.cdmsf_min1<<16) |
-			(msf.cdmsf_sec1<<8) |
-				msf.cdmsf_frame1;
-		msg(DBG_IOX,"ioctl: CDROMPLAYMSF %08X %08X\n",
-		    current_drive->pos_audio_start,current_drive->pos_audio_end);
-		i=cc_PlayAudio(current_drive->pos_audio_start,current_drive->pos_audio_end);
-		if (i<0)
+		if (status_tries == 0)
 		{
-			msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i);
-			DriveReset();
-			current_drive->audio_state=0;
-			RETURN_UP(-EIO);
+			msg(DBG_INF,"sbp_status: failed after 3 tries in line %d\n", __LINE__);
+			break;
 		}
-		current_drive->audio_state=audio_playing;
-		RETURN_UP(0);
 		
-	case CDROMPLAYTRKIND: /* Play a track.  This currently ignores index. */
-		msg(DBG_IOC,"ioctl: CDROMPLAYTRKIND entered.\n");
-#ifdef SAFE_MIXED
-		if (current_drive->has_data>1) RETURN_UP(-EBUSY);
-#endif /* SAFE_MIXED */ 
-		if (current_drive->audio_state==audio_playing)
+		sbp_read_cmd(req);
+		sbp_sleep(0);
+		if (sbp_data(req) != 0)
 		{
-			msg(DBG_IOX,"CDROMPLAYTRKIND: already audio_playing.\n");
-#if 1
-			RETURN_UP(0); /* just let us play on */
-#else
-			RETURN_UP(-EINVAL); /* play on, but say "error" */
+#ifdef SAFE_MIXED
+			current_drive->has_data=2; /* is really a data disk */
+#endif /* SAFE_MIXED */
+#ifdef DEBUG_GTL
+			printk(" do_sbpcd_request[%do](%p:%ld+%ld) end 3, Time:%li\n",
+				xnr, req, req->sector, req->nr_sectors, jiffies);
 #endif
+			up(&ioctl_read_sem);
+			spin_lock_irq(q->queue_lock);
+			end_request(req, 1);
+			goto request_loop;
 		}
-		memcpy(&ti,(void *) arg,sizeof(struct cdrom_ti));
-		msg(DBG_IOX,"ioctl: trk0: %d, ind0: %d, trk1:%d, ind1:%d\n",
-		    ti.cdti_trk0,ti.cdti_ind0,ti.cdti_trk1,ti.cdti_ind1);
-		if (ti.cdti_trk0<current_drive->n_first_track) RETURN_UP(-EINVAL);
-		if (ti.cdti_trk0>current_drive->n_last_track) RETURN_UP(-EINVAL);
-		if (ti.cdti_trk1<ti.cdti_trk0) ti.cdti_trk1=ti.cdti_trk0;
-		if (ti.cdti_trk1>current_drive->n_last_track) ti.cdti_trk1=current_drive->n_last_track;
-		current_drive->pos_audio_start=current_drive->TocBuffer[ti.cdti_trk0].address;
-		current_drive->pos_audio_end=current_drive->TocBuffer[ti.cdti_trk1+1].address;
-		i=cc_PlayAudio(current_drive->pos_audio_start,current_drive->pos_audio_end);
-		if (i<0)
-		{
-			msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i);
-			DriveReset();
-			current_drive->audio_state=0;
-			RETURN_UP(-EIO);
-		}
-		current_drive->audio_state=audio_playing;
-		RETURN_UP(0);
-		
-	case CDROMREADTOCHDR:        /* Read the table of contents header */
-		msg(DBG_IOC,"ioctl: CDROMREADTOCHDR entered.\n");
-		tochdr.cdth_trk0=current_drive->n_first_track;
-		tochdr.cdth_trk1=current_drive->n_last_track;
-		memcpy((void *) arg, &tochdr, sizeof(struct cdrom_tochdr));
-		RETURN_UP(0);
-		
-	case CDROMREADTOCENTRY:      /* Read an entry in the table of contents */
-		msg(DBG_IOC,"ioctl: CDROMREADTOCENTRY entered.\n");
-		memcpy(&tocentry, (void *) arg, sizeof(struct cdrom_tocentry));
-		i=tocentry.cdte_track;
-		if (i==CDROM_LEADOUT) i=current_drive->n_last_track+1;
-		else if (i<current_drive->n_first_track||i>current_drive->n_last_track)
-                  RETURN_UP(-EINVAL);
-		tocentry.cdte_adr=current_drive->TocBuffer[i].ctl_adr&0x0F;
-		tocentry.cdte_ctrl=(current_drive->TocBuffer[i].ctl_adr>>4)&0x0F;
-		tocentry.cdte_datamode=current_drive->TocBuffer[i].format;
-		if (tocentry.cdte_format==CDROM_MSF) /* MSF-bin required */
-		{
-			tocentry.cdte_addr.msf.minute=(current_drive->TocBuffer[i].address>>16)&0x00FF;
-			tocentry.cdte_addr.msf.second=(current_drive->TocBuffer[i].address>>8)&0x00FF;
-			tocentry.cdte_addr.msf.frame=current_drive->TocBuffer[i].address&0x00FF;
-		}
-		else if (tocentry.cdte_format==CDROM_LBA) /* blk required */
-			tocentry.cdte_addr.lba=msf2blk(current_drive->TocBuffer[i].address);
-		else RETURN_UP(-EINVAL);
-		memcpy((void *) arg, &tocentry, sizeof(struct cdrom_tocentry));
-		RETURN_UP(0);
-		
-	case CDROMSTOP:      /* Spin down the drive */
-		msg(DBG_IOC,"ioctl: CDROMSTOP entered.\n");
-#ifdef SAFE_MIXED
-		if (current_drive->has_data>1) RETURN_UP(-EBUSY);
-#endif /* SAFE_MIXED */ 
-		i=cc_Pause_Resume(1);
-		current_drive->audio_state=0;
-#if 0
-		cc_DriveReset();
+	}
+
+ err_done:
+#if OLD_BUSY
+	busy_data=0;
+#endif /* OLD_BUSY */
+#ifdef DEBUG_GTL
+	printk(" do_sbpcd_request[%do](%p:%ld+%ld) end 4 (error), Time:%li\n",
+		xnr, req, req->sector, req->nr_sectors, jiffies);
 #endif
-		RETURN_UP(i);
-		
-	case CDROMSTART:  /* Spin up the drive */
-		msg(DBG_IOC,"ioctl: CDROMSTART entered.\n");
-		cc_SpinUp();
-		current_drive->audio_state=0;
-		RETURN_UP(0);
-		
-	case CDROMVOLCTRL:   /* Volume control */
-		msg(DBG_IOC,"ioctl: CDROMVOLCTRL entered.\n");
-		memcpy(&volctrl,(char *) arg,sizeof(volctrl));
-		current_drive->vol_chan0=0;
-		current_drive->vol_ctrl0=volctrl.channel0;
-		current_drive->vol_chan1=1;
-		current_drive->vol_ctrl1=volctrl.channel1;
-		i=cc_SetVolume();
-		RETURN_UP(0);
-		
-	case CDROMVOLREAD:   /* read Volume settings from drive */
-		msg(DBG_IOC,"ioctl: CDROMVOLREAD entered.\n");
-		st=cc_GetVolume();
-		if (st<0) RETURN_UP(st);
-		volctrl.channel0=current_drive->vol_ctrl0;
-		volctrl.channel1=current_drive->vol_ctrl1;
-		volctrl.channel2=0;
-		volctrl.channel2=0;
-		memcpy((void *)arg,&volctrl,sizeof(volctrl));
-		RETURN_UP(0);
+	up(&ioctl_read_sem);
+	sbp_sleep(0);    /* wait a bit, try again */
+	spin_lock_irq(q->queue_lock);
+	end_request(req, 0);
+	goto request_loop;
+}
+/*==========================================================================*/
+/*
+ *  build and send the READ command.
+ */
+static void sbp_read_cmd(struct request *req)
+{
+#undef OLD
 
-	case CDROMSUBCHNL:   /* Get subchannel info */
-		msg(DBG_IOS,"ioctl: CDROMSUBCHNL entered.\n");
-		/* Bogus, I can do better than this! --AJK
-		if ((st_spinning)||(!subq_valid)) {
-			i=cc_ReadSubQ();
-			if (i<0) RETURN_UP(-EIO);
-		}
-		*/
-		i=cc_ReadSubQ();
-		if (i<0) {
-			j=cc_ReadError(); /* clear out error status from drive */
-			current_drive->audio_state=CDROM_AUDIO_NO_STATUS;
-			/* get and set the disk state here, 
-			probably not the right place, but who cares!
-			It makes it work properly! --AJK */
-			if (current_drive->CD_changed==0xFF) {
-				msg(DBG_000,"Disk changed detect\n");
-				current_drive->diskstate_flags &= ~cd_size_bit;
-			}
-			RETURN_UP(-EIO);
-		}
-		if (current_drive->CD_changed==0xFF) {
-			/* reread the TOC because the disk has changed! --AJK */
-			msg(DBG_000,"Disk changed STILL detected, rereading TOC!\n");
-			i=DiskInfo();
-			if(i==0) {
-				current_drive->CD_changed=0x00; /* cd has changed, procede, */
-				RETURN_UP(-EIO); /* and get TOC, etc on next try! --AJK */
-			} else {
-				RETURN_UP(-EIO); /* we weren't ready yet! --AJK */
-			}
-		}
-		memcpy(&SC, (void *) arg, sizeof(struct cdrom_subchnl));
-		/* 
-			This virtual crap is very bogus! 
-			It doesn't detect when the cd is done playing audio!
-			Lets do this right with proper hardware register reading!
-		*/
-		cc_ReadStatus();
-		i=ResponseStatus();
-		msg(DBG_000,"Drive Status: door_locked =%d.\n", st_door_locked);
-		msg(DBG_000,"Drive Status: door_closed =%d.\n", st_door_closed);
-		msg(DBG_000,"Drive Status: caddy_in =%d.\n", st_caddy_in);
-		msg(DBG_000,"Drive Status: disk_ok =%d.\n", st_diskok);
-		msg(DBG_000,"Drive Status: spinning =%d.\n", st_spinning);
-		msg(DBG_000,"Drive Status: busy =%d.\n", st_busy);
-		/* st_busy indicates if it's _ACTUALLY_ playing audio */
-		switch (current_drive->audio_state)
+	int i;
+	int block;
+
+	current_drive->sbp_first_frame=current_drive->sbp_last_frame=-1;      /* purge buffer */
+	current_drive->sbp_current = 0;
+	block=req->sector/4;
+	if (block+current_drive->sbp_bufsiz <= current_drive->CDsize_frm)
+		current_drive->sbp_read_frames = current_drive->sbp_bufsiz;
+	else
+	{
+		current_drive->sbp_read_frames=current_drive->CDsize_frm-block;
+		/* avoid reading past end of data */
+		if (current_drive->sbp_read_frames < 1)
 		{
-		case audio_playing:
-			if(st_busy==0) {
-				/* CD has stopped playing audio --AJK */
-				current_drive->audio_state=audio_completed;
-				SC.cdsc_audiostatus=CDROM_AUDIO_COMPLETED;
-			} else {
-				SC.cdsc_audiostatus=CDROM_AUDIO_PLAY;
-			}
-			break;
-		case audio_pausing:
-			SC.cdsc_audiostatus=CDROM_AUDIO_PAUSED;
-			break;
-		case audio_completed:
-			SC.cdsc_audiostatus=CDROM_AUDIO_COMPLETED;
-			break;
-		default:
-			SC.cdsc_audiostatus=CDROM_AUDIO_NO_STATUS;
-			break;
+			msg(DBG_INF,"requested frame %d, CD size %d ???\n",
+			    block, current_drive->CDsize_frm);
+			current_drive->sbp_read_frames=1;
 		}
-		SC.cdsc_adr=current_drive->SubQ_ctl_adr;
-		SC.cdsc_ctrl=current_drive->SubQ_ctl_adr>>4;
-		SC.cdsc_trk=bcd2bin(current_drive->SubQ_trk);
-		SC.cdsc_ind=bcd2bin(current_drive->SubQ_pnt_idx);
-		if (SC.cdsc_format==CDROM_LBA)
+	}
+
+	flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus | f_obey_p_check;
+	clr_cmdbuf();
+	if (famV_drive)
+	  {
+	    drvcmd[0]=CMDV_READ;
+	    lba2msf(block,&drvcmd[1]); /* msf-bcd format required */
+	    bin2bcdx(&drvcmd[1]);
+	    bin2bcdx(&drvcmd[2]);
+	    bin2bcdx(&drvcmd[3]);
+	    drvcmd[4]=current_drive->sbp_read_frames>>8;
+	    drvcmd[5]=current_drive->sbp_read_frames&0xff;
+	    drvcmd[6]=0x02; /* flag "msf-bcd" */
+	}
+	else if (fam0L_drive)
+	{
+		flags_cmd_out |= f_lopsta | f_getsta | f_bit1;
+		if (current_drive->xa_byte==0x20)
 		{
-			SC.cdsc_absaddr.lba=msf2blk(current_drive->SubQ_run_tot);
-			SC.cdsc_reladdr.lba=msf2blk(current_drive->SubQ_run_trk);
+			cmd_type=READ_M2;
+			drvcmd[0]=CMD0_READ_XA; /* "read XA frames", old drives */
+			drvcmd[1]=(block>>16)&0x0ff;
+			drvcmd[2]=(block>>8)&0x0ff;
+			drvcmd[3]=block&0x0ff;
+			drvcmd[4]=(current_drive->sbp_read_frames>>8)&0x0ff;
+			drvcmd[5]=current_drive->sbp_read_frames&0x0ff;
 		}
-		else /* not only if (SC.cdsc_format==CDROM_MSF) */
+		else
 		{
-			SC.cdsc_absaddr.msf.minute=(current_drive->SubQ_run_tot>>16)&0x00FF;
-			SC.cdsc_absaddr.msf.second=(current_drive->SubQ_run_tot>>8)&0x00FF;
-			SC.cdsc_absaddr.msf.frame=current_drive->SubQ_run_tot&0x00FF;
-			SC.cdsc_reladdr.msf.minute=(current_drive->SubQ_run_trk>>16)&0x00FF;
-			SC.cdsc_reladdr.msf.second=(current_drive->SubQ_run_trk>>8)&0x00FF;
-			SC.cdsc_reladdr.msf.frame=current_drive->SubQ_run_trk&0x00FF;
+			drvcmd[0]=CMD0_READ; /* "read frames", old drives */
+			if (current_drive->drv_type>=drv_201)
+			{
+				lba2msf(block,&drvcmd[1]); /* msf-bcd format required */
+				bin2bcdx(&drvcmd[1]);
+				bin2bcdx(&drvcmd[2]);
+				bin2bcdx(&drvcmd[3]);
+			}
+			else
+			{
+				drvcmd[1]=(block>>16)&0x0ff;
+				drvcmd[2]=(block>>8)&0x0ff;
+				drvcmd[3]=block&0x0ff;
+			}
+			drvcmd[4]=(current_drive->sbp_read_frames>>8)&0x0ff;
+			drvcmd[5]=current_drive->sbp_read_frames&0x0ff;
+			drvcmd[6]=(current_drive->drv_type<drv_201)?0:2; /* flag "lba or msf-bcd format" */
 		}
-		memcpy((void *) arg, &SC, sizeof(struct cdrom_subchnl));
-		msg(DBG_IOS,"CDROMSUBCHNL: %1X %02X %08X %08X %02X %02X %06X %06X\n",
-		    SC.cdsc_format,SC.cdsc_audiostatus,
-		    SC.cdsc_adr,SC.cdsc_ctrl,
-		    SC.cdsc_trk,SC.cdsc_ind,
-		    SC.cdsc_absaddr,SC.cdsc_reladdr);
-		RETURN_UP(0);
-		
-	default:
-		msg(DBG_IOC,"ioctl: unknown function request %04X\n", cmd);
-		RETURN_UP(-EINVAL);
-	} /* end switch(cmd) */
-}
-/*==========================================================================*/
-/*
- *  Take care of the different block sizes between cdrom and Linux.
- */
-static void sbp_transfer(struct request *req)
-{
-	long offs;
-	
-	while ( (req->nr_sectors > 0) &&
-	       (req->sector/4 >= current_drive->sbp_first_frame) &&
-	       (req->sector/4 <= current_drive->sbp_last_frame) )
+	}
+	else if (fam1_drive)
 	{
-		offs = (req->sector - current_drive->sbp_first_frame * 4) * 512;
-		memcpy(req->buffer, current_drive->sbp_buf + offs, 512);
-		req->nr_sectors--;
-		req->sector++;
-		req->buffer += 512;
+		drvcmd[0]=CMD1_READ;
+		lba2msf(block,&drvcmd[1]); /* msf-bin format required */
+		drvcmd[5]=(current_drive->sbp_read_frames>>8)&0x0ff;
+		drvcmd[6]=current_drive->sbp_read_frames&0x0ff;
 	}
-}
-/*==========================================================================*/
-/*
- *  special end_request for sbpcd to solve CURRENT==NULL bug. (GTL)
- *  GTL = Gonzalo Tornaria <tornaria@cmat.edu.uy>
- *
- *  This is a kludge so we don't need to modify end_request.
- *  We put the req we take out after INIT_REQUEST in the requests list,
- *  so that end_request will discard it. 
- *
- *  The bug could be present in other block devices, perhaps we
- *  should modify INIT_REQUEST and end_request instead, and
- *  change every block device.. 
- *
- *  Could be a race here?? Could e.g. a timer interrupt schedule() us?
- *  If so, we should copy end_request here, and do it right.. (or
- *  modify end_request and the block devices).
- *
- *  In any case, the race here would be much small than it was, and
- *  I couldn't reproduce..
- *
- *  The race could be: suppose CURRENT==NULL. We put our req in the list,
- *  and we are scheduled. Other process takes over, and gets into
- *  do_sbpcd_request. It sees CURRENT!=NULL (it is == to our req), so
- *  proceeds. It ends, so CURRENT is now NULL.. Now we awake somewhere in
- *  end_request, but now CURRENT==NULL... oops!
- *
- */
-#undef DEBUG_GTL
-
+	else if (fam2_drive)
+	{
+		drvcmd[0]=CMD2_READ;
+		lba2msf(block,&drvcmd[1]); /* msf-bin format required */
+		drvcmd[4]=(current_drive->sbp_read_frames>>8)&0x0ff;
+		drvcmd[5]=current_drive->sbp_read_frames&0x0ff;
+		drvcmd[6]=0x02;
+	}
+	else if (famT_drive)
+	{
+		drvcmd[0]=CMDT_READ;
+		drvcmd[2]=(block>>24)&0x0ff;
+		drvcmd[3]=(block>>16)&0x0ff;
+		drvcmd[4]=(block>>8)&0x0ff;
+		drvcmd[5]=block&0x0ff;
+		drvcmd[7]=(current_drive->sbp_read_frames>>8)&0x0ff;
+		drvcmd[8]=current_drive->sbp_read_frames&0x0ff;
+	}
+	flags_cmd_out=f_putcmd;
+	response_count=0;
+	i=cmd_out();
+	if (i<0) msg(DBG_INF,"error giving READ command: %0d\n", i);
+	return;
+}
 /*==========================================================================*/
 /*
- *  I/O request routine, called from Linux kernel.
+ *  Check the completion of the read-data command.  On success, read
+ *  the current_drive->sbp_bufsiz * 2048 bytes of data from the disk into buffer.
  */
-static void do_sbpcd_request(request_queue_t * q)
+static int sbp_data(struct request *req)
 {
-	u_int block;
-	u_int nsect;
-	int status_tries, data_tries;
-	struct request *req;
-	struct sbpcd_drive *p;
-#ifdef DEBUG_GTL
-	static int xx_nr=0;
-	int xnr;
-#endif
-
- request_loop:
-#ifdef DEBUG_GTL
-	xnr=++xx_nr;
-
-	req = elv_next_request(q);
-
-	if (!req)
-	{
-		printk( "do_sbpcd_request[%di](NULL), Pid:%d, Time:%li\n",
-			xnr, current->pid, jiffies);
-		printk( "do_sbpcd_request[%do](NULL) end 0 (null), Time:%li\n",
-			xnr, jiffies);
-		return;
-	}
+	int i=0, j=0, l, frame;
+	u_int try=0;
+	u_long timeout;
+	u_char *p;
+	u_int data_tries = 0;
+	u_int data_waits = 0;
+	u_int data_retrying = 0;
+	int error_flag;
+	int xa_count;
+	int max_latency;
+	int success;
+	int wait;
+	int duration;
 
-	printk(" do_sbpcd_request[%di](%p:%ld+%ld), Pid:%d, Time:%li\n",
-		xnr, req, req->sector, req->nr_sectors, current->pid, jiffies);
+	error_flag=0;
+	success=0;
+#if LONG_TIMING
+	max_latency=9*HZ;
+#else
+	if (current_drive->f_multisession) max_latency=15*HZ;
+	else max_latency=5*HZ;
 #endif
+	duration=jiffies;
+	for (frame=0;frame<current_drive->sbp_read_frames&&!error_flag; frame++)
+	{
+		SBPCD_CLI;
 
-	req = elv_next_request(q);	/* take out our request so no other */
-	if (!req)
-		return;
+		del_timer(&data_timer);
+		data_timer.expires=jiffies+max_latency;
+		timed_out_data=0;
+		add_timer(&data_timer);
+		while (!timed_out_data)
+		{
+			if (current_drive->f_multisession) try=maxtim_data*4;
+			else try=maxtim_data;
+			msg(DBG_000,"sbp_data: CDi_status loop: try=%d.\n",try);
+			for ( ; try!=0;try--)
+			{
+				j=inb(CDi_status);
+				if (!(j&s_not_data_ready)) break;
+				if (!(j&s_not_result_ready)) break;
+				if (fam0LV_drive) if (j&s_attention) break;
+			}
+			if (!(j&s_not_data_ready)) goto data_ready;
+			if (try==0)
+			{
+				if (data_retrying == 0) data_waits++;
+				data_retrying = 1;
+				msg(DBG_000,"sbp_data: CDi_status loop: sleeping.\n");
+				sbp_sleep(1);
+				try = 1;
+			}
+		}
+		msg(DBG_INF,"sbp_data: CDi_status loop expired.\n");
+	data_ready:
+		del_timer(&data_timer);
 
-	if (req -> sector == -1)
-		end_request(req, 0);
-	spin_unlock_irq(q->queue_lock);
+		if (timed_out_data)
+		{
+			msg(DBG_INF,"sbp_data: CDi_status timeout (timed_out_data) (%02X).\n", j);
+			error_flag++;
+		}
+		if (try==0)
+		{
+			msg(DBG_INF,"sbp_data: CDi_status timeout (try=0) (%02X).\n", j);
+			error_flag++;
+		}
+		if (!(j&s_not_result_ready))
+		{
+			msg(DBG_INF, "sbp_data: RESULT_READY where DATA_READY awaited (%02X).\n", j);
+			response_count=20;
+			j=ResponseInfo();
+			j=inb(CDi_status);
+		}
+		if (j&s_not_data_ready)
+		{
+			if ((current_drive->ored_ctl_adr&0x40)==0)
+				msg(DBG_INF, "CD contains no data tracks.\n");
+			else msg(DBG_INF, "sbp_data: DATA_READY timeout (%02X).\n", j);
+			error_flag++;
+		}
+		SBPCD_STI;
+		if (error_flag) break;
 
-	down(&ioctl_read_sem);
-	if (rq_data_dir(elv_next_request(q)) != READ)
-	{
-		msg(DBG_INF, "bad cmd %d\n", req->cmd[0]);
-		goto err_done;
+		msg(DBG_000, "sbp_data: beginning to read.\n");
+		p = current_drive->sbp_buf + frame *  CD_FRAMESIZE;
+		if (sbpro_type==1) OUT(CDo_sel_i_d,1);
+		if (cmd_type==READ_M2) {
+                        if (do_16bit) insw(CDi_data, xa_head_buf, CD_XA_HEAD>>1);
+                        else insb(CDi_data, xa_head_buf, CD_XA_HEAD);
+		}
+		if (do_16bit) insw(CDi_data, p, CD_FRAMESIZE>>1);
+		else insb(CDi_data, p, CD_FRAMESIZE);
+		if (cmd_type==READ_M2) {
+                        if (do_16bit) insw(CDi_data, xa_tail_buf, CD_XA_TAIL>>1);
+                        else insb(CDi_data, xa_tail_buf, CD_XA_TAIL);
+		}
+		current_drive->sbp_current++;
+		if (sbpro_type==1) OUT(CDo_sel_i_d,0);
+		if (cmd_type==READ_M2)
+		{
+			for (xa_count=0;xa_count<CD_XA_HEAD;xa_count++)
+				sprintf(&msgbuf[xa_count*3], " %02X", xa_head_buf[xa_count]);
+			msgbuf[xa_count*3]=0;
+			msg(DBG_XA1,"xa head:%s\n", msgbuf);
+		}
+		data_retrying = 0;
+		data_tries++;
+		if (data_tries >= 1000)
+		{
+			msg(DBG_INF,"sbp_data() statistics: %d waits in %d frames.\n", data_waits, data_tries);
+			data_waits = data_tries = 0;
+		}
 	}
-	p = req->rq_disk->private_data;
-#if OLD_BUSY
-	while (busy_audio) sbp_sleep(HZ); /* wait a bit */
-	busy_data=1;
-#endif /* OLD_BUSY */
-	
-	if (p->audio_state==audio_playing) goto err_done;
-	if (p != current_drive)
-		switch_drive(p);
-
-	block = req->sector; /* always numbered as 512-byte-pieces */
-	nsect = req->nr_sectors; /* always counted as 512-byte-pieces */
-	
-	msg(DBG_BSZ,"read sector %d (%d sectors)\n", block, nsect);
+	duration=jiffies-duration;
+	msg(DBG_TEA,"time to read %d frames: %d jiffies .\n",frame,duration);
+	if (famT_drive)
+	{
+		wait=8;
+		do
+		{
+			if (teac==2)
+                          {
+                            if ((i=CDi_stat_loop_T()) == -1) break;
+                          }
+                        else
+                          {
+                            sbp_sleep(1);
+                            OUT(CDo_sel_i_d,0);
+                            i=inb(CDi_status);
+                          }
+			if (!(i&s_not_data_ready))
+			{
+				OUT(CDo_sel_i_d,1);
+				j=0;
+				do
+				{
+					if (do_16bit) i=inw(CDi_data);
+					else i=inb(CDi_data);
+					j++;
+					i=inb(CDi_status);
+				}
+				while (!(i&s_not_data_ready));
+				msg(DBG_TEA, "==========too much data (%d bytes/words)==============.\n", j);
+			}
+			if (!(i&s_not_result_ready))
+			{
+				OUT(CDo_sel_i_d,0);
+				l=0;
+				do
+				{
+					infobuf[l++]=inb(CDi_info);
+					i=inb(CDi_status);
+				}
+				while (!(i&s_not_result_ready));
+				if (infobuf[0]==0x00) success=1;
+#if 1
+				for (j=0;j<l;j++) sprintf(&msgbuf[j*3], " %02X", infobuf[j]);
+				msgbuf[j*3]=0;
+				msg(DBG_TEA,"sbp_data info response:%s\n", msgbuf);
+#endif
+				if (infobuf[0]==0x02)
+				{
+					error_flag++;
+					do
+					{
+						++recursion;
+						if (recursion>1) msg(DBG_TEA,"cmd_out_T READ_ERR recursion (sbp_data): %d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n",recursion);
+						else msg(DBG_TEA,"sbp_data: CMDT_READ_ERR necessary.\n");
+						clr_cmdbuf();
+						drvcmd[0]=CMDT_READ_ERR;
+						j=cmd_out_T(); /* !!! recursive here !!! */
+						--recursion;
+						sbp_sleep(1);
+					}
+					while (j<0);
+					current_drive->error_state=infobuf[2];
+					current_drive->b3=infobuf[3];
+					current_drive->b4=infobuf[4];
+				}
+				break;
+			}
+			else
+			{
 #if 0
-	msg(DBG_MUL,"read LBA %d\n", block/4);
+				msg(DBG_TEA, "============= waiting for result=================.\n");
+				sbp_sleep(1);
 #endif
-	
-	sbp_transfer(req);
-	/* if we satisfied the request from the buffer, we're done. */
-	if (req->nr_sectors == 0)
+			}
+		}
+		while (wait--);
+	}
+
+	if (error_flag) /* must have been spurious D_RDY or (ATTN&&!D_RDY) */
 	{
-#ifdef DEBUG_GTL
-		printk(" do_sbpcd_request[%do](%p:%ld+%ld) end 2, Time:%li\n",
-			xnr, req, req->sector, req->nr_sectors, jiffies);
+		msg(DBG_TEA, "================error flag: %d=================.\n", error_flag);
+		msg(DBG_INF,"sbp_data: read aborted by drive.\n");
+#if 1
+		i=cc_DriveReset(); /* ugly fix to prevent a hang */
+#else
+		i=cc_ReadError();
 #endif
-		up(&ioctl_read_sem);
-		spin_lock_irq(q->queue_lock);
-		end_request(req, 1);
-		goto request_loop;
+		return (0);
 	}
-
-#ifdef FUTURE
-	i=prepare(0,0); /* at moment not really a hassle check, but ... */
-	if (i!=0)
-		msg(DBG_INF,"\"prepare\" tells error %d -- ignored\n", i);
-#endif /* FUTURE */ 
-	
-	if (!st_spinning) cc_SpinUp();
 	
-	for (data_tries=n_retries; data_tries > 0; data_tries--)
+	if (fam0LV_drive)
 	{
-		for (status_tries=3; status_tries > 0; status_tries--)
+		SBPCD_CLI;
+		i=maxtim_data;
+		for (timeout=jiffies+HZ; time_before(jiffies, timeout); timeout--)
 		{
-			flags_cmd_out |= f_respo3;
-			cc_ReadStatus();
-			if (sbp_status() != 0) break;
-			if (st_check) cc_ReadError();
-			sbp_sleep(1);    /* wait a bit, try again */
+			for ( ;i!=0;i--)
+			{
+				j=inb(CDi_status);
+				if (!(j&s_not_data_ready)) break;
+				if (!(j&s_not_result_ready)) break;
+				if (j&s_attention) break;
+			}
+			if (i != 0 || time_after_eq(jiffies, timeout)) break;
+			sbp_sleep(0);
+			i = 1;
 		}
-		if (status_tries == 0)
+		if (i==0) msg(DBG_INF,"status timeout after READ.\n");
+		if (!(j&s_attention))
 		{
-			msg(DBG_INF,"sbp_status: failed after 3 tries in line %d\n", __LINE__);
-			break;
+			msg(DBG_INF,"sbp_data: timeout waiting DRV_ATTN - retrying.\n");
+			i=cc_DriveReset();  /* ugly fix to prevent a hang */
+			SBPCD_STI;
+			return (0);
 		}
-		
-		sbp_read_cmd(req);
-		sbp_sleep(0);
-		if (sbp_data(req) != 0)
+		SBPCD_STI;
+	}
+
+#if 0
+	if (!success)
+#endif
+		do
 		{
-#ifdef SAFE_MIXED
-			current_drive->has_data=2; /* is really a data disk */
-#endif /* SAFE_MIXED */ 
-#ifdef DEBUG_GTL
-			printk(" do_sbpcd_request[%do](%p:%ld+%ld) end 3, Time:%li\n",
-				xnr, req, req->sector, req->nr_sectors, jiffies);
+			if (fam0LV_drive) cc_ReadStatus();
+#if 1
+			if (famT_drive) msg(DBG_TEA, "================before ResponseStatus=================.\n", i);
 #endif
-			up(&ioctl_read_sem);
-			spin_lock_irq(q->queue_lock);
-			end_request(req, 1);
-			goto request_loop;
+			i=ResponseStatus();  /* builds status_bits, returns orig. status (old) or faked p_success (new) */
+#if 1
+			if (famT_drive)	msg(DBG_TEA, "================ResponseStatus: %d=================.\n", i);
+#endif
+			if (i<0)
+			{
+				msg(DBG_INF,"bad cc_ReadStatus after read: %02X\n", current_drive->status_bits);
+				return (0);
+			}
 		}
+		while ((fam0LV_drive)&&(!st_check)&&(!(i&p_success)));
+	if (st_check)
+	{
+		i=cc_ReadError();
+		msg(DBG_INF,"cc_ReadError was necessary after read: %d\n",i);
+		return (0);
+	}
+	if (fatal_err)
+	{
+		fatal_err=0;
+		current_drive->sbp_first_frame=current_drive->sbp_last_frame=-1;      /* purge buffer */
+		current_drive->sbp_current = 0;
+		msg(DBG_INF,"sbp_data: fatal_err - retrying.\n");
+		return (0);
 	}
 	
- err_done:
-#if OLD_BUSY
-	busy_data=0;
-#endif /* OLD_BUSY */
-#ifdef DEBUG_GTL
-	printk(" do_sbpcd_request[%do](%p:%ld+%ld) end 4 (error), Time:%li\n",
-		xnr, req, req->sector, req->nr_sectors, jiffies);
-#endif
-	up(&ioctl_read_sem);
-	sbp_sleep(0);    /* wait a bit, try again */
-	spin_lock_irq(q->queue_lock);
-	end_request(req, 0);
-	goto request_loop;
+	current_drive->sbp_first_frame = req -> sector / 4;
+	current_drive->sbp_last_frame = current_drive->sbp_first_frame + current_drive->sbp_read_frames - 1;
+	sbp_transfer(req);
+	return (1);
 }
 /*==========================================================================*/
-/*
- *  build and send the READ command.
- */
-static void sbp_read_cmd(struct request *req)
+
+static int sbpcd_block_open(struct inode *inode, struct file *file)
 {
-#undef OLD
+	struct sbpcd_drive *p = inode->i_bdev->bd_disk->private_data;
+	return cdrom_open(p->sbpcd_infop, inode, file);
+}
 
-	int i;
-	int block;
+static int sbpcd_block_release(struct inode *inode, struct file *file)
+{
+	struct sbpcd_drive *p = inode->i_bdev->bd_disk->private_data;
+	return cdrom_release(p->sbpcd_infop, file);
+}
+
+static int sbpcd_block_ioctl(struct inode *inode, struct file *file,
+				unsigned cmd, unsigned long arg)
+{
+	struct sbpcd_drive *p = inode->i_bdev->bd_disk->private_data;
+	struct cdrom_device_info *cdi = p->sbpcd_infop;
+	int ret, i;
+
+	ret = cdrom_ioctl(file, p->sbpcd_infop, inode, cmd, arg);
+	if (ret != -ENOSYS)
+		return ret;
+
+	msg(DBG_IO2,"ioctl(%s, 0x%08lX, 0x%08lX)\n", cdi->name, cmd, arg);
+	if (p->drv_id==-1) {
+		msg(DBG_INF, "ioctl: bad device: %s\n", cdi->name);
+		return (-ENXIO);             /* no such drive */
+	}
+	down(&ioctl_read_sem);
+	if (p != current_drive)
+		switch_drive(p);
 	
-	current_drive->sbp_first_frame=current_drive->sbp_last_frame=-1;      /* purge buffer */
-	current_drive->sbp_current = 0;
-	block=req->sector/4;
-	if (block+current_drive->sbp_bufsiz <= current_drive->CDsize_frm)
-		current_drive->sbp_read_frames = current_drive->sbp_bufsiz;
-	else
+	msg(DBG_IO2,"ioctl: device %s, request %04X\n",cdi->name,cmd);
+	switch (cmd) 		/* Sun-compatible */
 	{
-		current_drive->sbp_read_frames=current_drive->CDsize_frm-block;
-		/* avoid reading past end of data */
-		if (current_drive->sbp_read_frames < 1)
+	case DDIOCSDBG:		/* DDI Debug */
+		if (!capable(CAP_SYS_ADMIN)) RETURN_UP(-EPERM);
+		i=sbpcd_dbg_ioctl(arg,1);
+		RETURN_UP(i);
+	case CDROMRESET:      /* hard reset the drive */
+		msg(DBG_IOC,"ioctl: CDROMRESET entered.\n");
+		i=DriveReset();
+		current_drive->audio_state=0;
+		RETURN_UP(i);
+
+	case CDROMREADMODE1:
+		msg(DBG_IOC,"ioctl: CDROMREADMODE1 requested.\n");
+#ifdef SAFE_MIXED
+		if (current_drive->has_data>1) RETURN_UP(-EBUSY);
+#endif /* SAFE_MIXED */
+		cc_ModeSelect(CD_FRAMESIZE);
+		cc_ModeSense();
+		current_drive->mode=READ_M1;
+		RETURN_UP(0);
+
+	case CDROMREADMODE2: /* not usable at the moment */
+		msg(DBG_IOC,"ioctl: CDROMREADMODE2 requested.\n");
+#ifdef SAFE_MIXED
+		if (current_drive->has_data>1) RETURN_UP(-EBUSY);
+#endif /* SAFE_MIXED */
+		cc_ModeSelect(CD_FRAMESIZE_RAW1);
+		cc_ModeSense();
+		current_drive->mode=READ_M2;
+		RETURN_UP(0);
+
+	case CDROMAUDIOBUFSIZ: /* configure the audio buffer size */
+		msg(DBG_IOC,"ioctl: CDROMAUDIOBUFSIZ entered.\n");
+		if (current_drive->sbp_audsiz>0)
+			vfree(current_drive->aud_buf);
+		current_drive->aud_buf=NULL;
+		current_drive->sbp_audsiz=arg;
+
+		if (current_drive->sbp_audsiz>16)
 		{
-			msg(DBG_INF,"requested frame %d, CD size %d ???\n",
-			    block, current_drive->CDsize_frm);
-			current_drive->sbp_read_frames=1;
+			current_drive->sbp_audsiz = 0;
+			RETURN_UP(current_drive->sbp_audsiz);
 		}
-	}
 	
-	flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus | f_obey_p_check;
-	clr_cmdbuf();
-	if (famV_drive)
-	  {
-	    drvcmd[0]=CMDV_READ;
-	    lba2msf(block,&drvcmd[1]); /* msf-bcd format required */
-	    bin2bcdx(&drvcmd[1]);
-	    bin2bcdx(&drvcmd[2]);
-	    bin2bcdx(&drvcmd[3]);
-	    drvcmd[4]=current_drive->sbp_read_frames>>8;
-	    drvcmd[5]=current_drive->sbp_read_frames&0xff;
-	    drvcmd[6]=0x02; /* flag "msf-bcd" */
-	}
-	else if (fam0L_drive)
-	{
-		flags_cmd_out |= f_lopsta | f_getsta | f_bit1;
-		if (current_drive->xa_byte==0x20)
+		if (current_drive->sbp_audsiz>0)
 		{
-			cmd_type=READ_M2;
-			drvcmd[0]=CMD0_READ_XA; /* "read XA frames", old drives */
-			drvcmd[1]=(block>>16)&0x0ff;
-			drvcmd[2]=(block>>8)&0x0ff;
-			drvcmd[3]=block&0x0ff;
-			drvcmd[4]=(current_drive->sbp_read_frames>>8)&0x0ff;
-			drvcmd[5]=current_drive->sbp_read_frames&0x0ff;
+			current_drive->aud_buf=(u_char *) vmalloc(current_drive->sbp_audsiz*CD_FRAMESIZE_RAW);
+			if (current_drive->aud_buf==NULL)
+			{
+				msg(DBG_INF,"audio buffer (%d frames) not available.\n",current_drive->sbp_audsiz);
+				current_drive->sbp_audsiz=0;
+			}
+			else msg(DBG_INF,"audio buffer size: %d frames.\n",current_drive->sbp_audsiz);
 		}
-		else
+		RETURN_UP(current_drive->sbp_audsiz);
+
+	case CDROMREADAUDIO:
+	{ /* start of CDROMREADAUDIO */
+		int i=0, j=0, frame, block=0;
+		u_int try=0;
+		u_long timeout;
+		u_char *p;
+		u_int data_tries = 0;
+		u_int data_waits = 0;
+		u_int data_retrying = 0;
+		int status_tries;
+		int error_flag;
+
+		msg(DBG_IOC,"ioctl: CDROMREADAUDIO entered.\n");
+		if (fam0_drive) RETURN_UP(-EINVAL);
+		if (famL_drive) RETURN_UP(-EINVAL);
+		if (famV_drive) RETURN_UP(-EINVAL);
+		if (famT_drive) RETURN_UP(-EINVAL);
+#ifdef SAFE_MIXED
+		if (current_drive->has_data>1) RETURN_UP(-EBUSY);
+#endif /* SAFE_MIXED */
+		if (current_drive->aud_buf==NULL) RETURN_UP(-EINVAL);
+		if (copy_from_user(&read_audio, (void __user *)arg,
+				   sizeof(struct cdrom_read_audio)))
+			RETURN_UP(-EFAULT);
+		if (read_audio.nframes < 0 || read_audio.nframes>current_drive->sbp_audsiz) RETURN_UP(-EINVAL);
+		if (!access_ok(VERIFY_WRITE, read_audio.buf,
+			      read_audio.nframes*CD_FRAMESIZE_RAW))
+                	RETURN_UP(-EFAULT);
+
+		if (read_audio.addr_format==CDROM_MSF) /* MSF-bin specification of where to start */
+			block=msf2lba(&read_audio.addr.msf.minute);
+		else if (read_audio.addr_format==CDROM_LBA) /* lba specification of where to start */
+			block=read_audio.addr.lba;
+		else RETURN_UP(-EINVAL);
+#if 000
+		i=cc_SetSpeed(speed_150,0,0);
+		if (i) msg(DBG_AUD,"read_audio: SetSpeed error %d\n", i);
+#endif
+		msg(DBG_AUD,"read_audio: lba: %d, msf: %06X\n",
+		    block, blk2msf(block));
+		msg(DBG_AUD,"read_audio: before cc_ReadStatus.\n");
+#if OLD_BUSY
+		while (busy_data) sbp_sleep(HZ/10); /* wait a bit */
+		busy_audio=1;
+#endif /* OLD_BUSY */
+		error_flag=0;
+		for (data_tries=5; data_tries>0; data_tries--)
 		{
-			drvcmd[0]=CMD0_READ; /* "read frames", old drives */
-			if (current_drive->drv_type>=drv_201)
+			msg(DBG_AUD,"data_tries=%d ...\n", data_tries);
+			current_drive->mode=READ_AU;
+			cc_ModeSelect(CD_FRAMESIZE_RAW);
+			cc_ModeSense();
+			for (status_tries=3; status_tries > 0; status_tries--)
 			{
-				lba2msf(block,&drvcmd[1]); /* msf-bcd format required */
-				bin2bcdx(&drvcmd[1]);
-				bin2bcdx(&drvcmd[2]);
-				bin2bcdx(&drvcmd[3]);
+				flags_cmd_out |= f_respo3;
+				cc_ReadStatus();
+				if (sbp_status() != 0) break;
+				if (st_check) cc_ReadError();
+				sbp_sleep(1);    /* wait a bit, try again */
 			}
-			else
+			if (status_tries == 0)
 			{
-				drvcmd[1]=(block>>16)&0x0ff;
-				drvcmd[2]=(block>>8)&0x0ff;
-				drvcmd[3]=block&0x0ff;
+				msg(DBG_AUD,"read_audio: sbp_status: failed after 3 tries in line %d.\n", __LINE__);
+				continue;
 			}
-			drvcmd[4]=(current_drive->sbp_read_frames>>8)&0x0ff;
-			drvcmd[5]=current_drive->sbp_read_frames&0x0ff;
-			drvcmd[6]=(current_drive->drv_type<drv_201)?0:2; /* flag "lba or msf-bcd format" */
-		}
-	}
-	else if (fam1_drive)
-	{
-		drvcmd[0]=CMD1_READ;
-		lba2msf(block,&drvcmd[1]); /* msf-bin format required */
-		drvcmd[5]=(current_drive->sbp_read_frames>>8)&0x0ff;
-		drvcmd[6]=current_drive->sbp_read_frames&0x0ff;
-	}
-	else if (fam2_drive)
-	{
-		drvcmd[0]=CMD2_READ;
-		lba2msf(block,&drvcmd[1]); /* msf-bin format required */
-		drvcmd[4]=(current_drive->sbp_read_frames>>8)&0x0ff;
-		drvcmd[5]=current_drive->sbp_read_frames&0x0ff;
-		drvcmd[6]=0x02;
-	}
-	else if (famT_drive)
-	{
-		drvcmd[0]=CMDT_READ;
-		drvcmd[2]=(block>>24)&0x0ff;
-		drvcmd[3]=(block>>16)&0x0ff;
-		drvcmd[4]=(block>>8)&0x0ff;
-		drvcmd[5]=block&0x0ff;
-		drvcmd[7]=(current_drive->sbp_read_frames>>8)&0x0ff;
-		drvcmd[8]=current_drive->sbp_read_frames&0x0ff;
-	}
-	flags_cmd_out=f_putcmd;
-	response_count=0;
-	i=cmd_out();
-	if (i<0) msg(DBG_INF,"error giving READ command: %0d\n", i);
-	return;
-}
-/*==========================================================================*/
-/*
- *  Check the completion of the read-data command.  On success, read
- *  the current_drive->sbp_bufsiz * 2048 bytes of data from the disk into buffer.
- */
-static int sbp_data(struct request *req)
-{
-	int i=0, j=0, l, frame;
-	u_int try=0;
-	u_long timeout;
-	u_char *p;
-	u_int data_tries = 0;
-	u_int data_waits = 0;
-	u_int data_retrying = 0;
-	int error_flag;
-	int xa_count;
-	int max_latency;
-	int success;
-	int wait;
-	int duration;
-	
-	error_flag=0;
-	success=0;
-#if LONG_TIMING
-	max_latency=9*HZ;
-#else
-	if (current_drive->f_multisession) max_latency=15*HZ;
-	else max_latency=5*HZ;
-#endif
-	duration=jiffies;
-	for (frame=0;frame<current_drive->sbp_read_frames&&!error_flag; frame++)
-	{
-		SBPCD_CLI;
-		
-		del_timer(&data_timer);
-		data_timer.expires=jiffies+max_latency;
-		timed_out_data=0;
-		add_timer(&data_timer);
-		while (!timed_out_data) 
-		{
-			if (current_drive->f_multisession) try=maxtim_data*4;
-			else try=maxtim_data;
-			msg(DBG_000,"sbp_data: CDi_status loop: try=%d.\n",try);
-			for ( ; try!=0;try--)
+			msg(DBG_AUD,"read_audio: sbp_status: ok.\n");
+
+			flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus | f_obey_p_check;
+			if (fam0L_drive)
 			{
-				j=inb(CDi_status);
-				if (!(j&s_not_data_ready)) break;
-				if (!(j&s_not_result_ready)) break;
-				if (fam0LV_drive) if (j&s_attention) break;
+				flags_cmd_out |= f_lopsta | f_getsta | f_bit1;
+				cmd_type=READ_M2;
+				drvcmd[0]=CMD0_READ_XA; /* "read XA frames", old drives */
+				drvcmd[1]=(block>>16)&0x000000ff;
+				drvcmd[2]=(block>>8)&0x000000ff;
+				drvcmd[3]=block&0x000000ff;
+				drvcmd[4]=0;
+				drvcmd[5]=read_audio.nframes; /* # of frames */
+				drvcmd[6]=0;
 			}
-			if (!(j&s_not_data_ready)) goto data_ready;
-			if (try==0)
+			else if (fam1_drive)
 			{
-				if (data_retrying == 0) data_waits++;
-				data_retrying = 1;
-				msg(DBG_000,"sbp_data: CDi_status loop: sleeping.\n");
-				sbp_sleep(1);
-				try = 1;
+				drvcmd[0]=CMD1_READ; /* "read frames", new drives */
+				lba2msf(block,&drvcmd[1]); /* msf-bin format required */
+				drvcmd[4]=0;
+				drvcmd[5]=0;
+				drvcmd[6]=read_audio.nframes; /* # of frames */
 			}
-		}
-		msg(DBG_INF,"sbp_data: CDi_status loop expired.\n");
-	data_ready:
-		del_timer(&data_timer);
-
-		if (timed_out_data)
-		{
-			msg(DBG_INF,"sbp_data: CDi_status timeout (timed_out_data) (%02X).\n", j);
-			error_flag++;
-		}
-		if (try==0)
-		{
-			msg(DBG_INF,"sbp_data: CDi_status timeout (try=0) (%02X).\n", j);
-			error_flag++;
-		}
-		if (!(j&s_not_result_ready))
-		{
-			msg(DBG_INF, "sbp_data: RESULT_READY where DATA_READY awaited (%02X).\n", j);
-			response_count=20;
-			j=ResponseInfo();
-			j=inb(CDi_status);
-		}
-		if (j&s_not_data_ready)
-		{
-			if ((current_drive->ored_ctl_adr&0x40)==0)
-				msg(DBG_INF, "CD contains no data tracks.\n");
-			else msg(DBG_INF, "sbp_data: DATA_READY timeout (%02X).\n", j);
-			error_flag++;
-		}
-		SBPCD_STI;
-		if (error_flag) break;
-
-		msg(DBG_000, "sbp_data: beginning to read.\n");
-		p = current_drive->sbp_buf + frame *  CD_FRAMESIZE;
-		if (sbpro_type==1) OUT(CDo_sel_i_d,1);
-		if (cmd_type==READ_M2) {
-                        if (do_16bit) insw(CDi_data, xa_head_buf, CD_XA_HEAD>>1);
-                        else insb(CDi_data, xa_head_buf, CD_XA_HEAD);
-		}
-		if (do_16bit) insw(CDi_data, p, CD_FRAMESIZE>>1);
-		else insb(CDi_data, p, CD_FRAMESIZE);
-		if (cmd_type==READ_M2) {
-                        if (do_16bit) insw(CDi_data, xa_tail_buf, CD_XA_TAIL>>1);
-                        else insb(CDi_data, xa_tail_buf, CD_XA_TAIL);
-		}
-		current_drive->sbp_current++;
-		if (sbpro_type==1) OUT(CDo_sel_i_d,0);
-		if (cmd_type==READ_M2)
-		{
-			for (xa_count=0;xa_count<CD_XA_HEAD;xa_count++)
-				sprintf(&msgbuf[xa_count*3], " %02X", xa_head_buf[xa_count]);
-			msgbuf[xa_count*3]=0;
-			msg(DBG_XA1,"xa head:%s\n", msgbuf);
-		}
-		data_retrying = 0;
-		data_tries++;
-		if (data_tries >= 1000)
-		{
-			msg(DBG_INF,"sbp_data() statistics: %d waits in %d frames.\n", data_waits, data_tries);
-			data_waits = data_tries = 0;
-		}
-	}
-	duration=jiffies-duration;
-	msg(DBG_TEA,"time to read %d frames: %d jiffies .\n",frame,duration);
-	if (famT_drive)
-	{
-		wait=8;
-		do
-		{
-			if (teac==2)
-                          {
-                            if ((i=CDi_stat_loop_T()) == -1) break;
-                          }
-                        else
-                          {
-                            sbp_sleep(1);
-                            OUT(CDo_sel_i_d,0); 
-                            i=inb(CDi_status);
-                          } 
-			if (!(i&s_not_data_ready))
+			else if (fam2_drive)
 			{
-				OUT(CDo_sel_i_d,1);
-				j=0;
-				do
+				drvcmd[0]=CMD2_READ_XA2;
+				lba2msf(block,&drvcmd[1]); /* msf-bin format required */
+				drvcmd[4]=0;
+				drvcmd[5]=read_audio.nframes; /* # of frames */
+				drvcmd[6]=0x11; /* raw mode */
+			}
+			else if (famT_drive) /* CD-55A: not tested yet */
+			{
+			}
+			msg(DBG_AUD,"read_audio: before giving \"read\" command.\n");
+			flags_cmd_out=f_putcmd;
+			response_count=0;
+			i=cmd_out();
+			if (i<0) msg(DBG_INF,"error giving READ AUDIO command: %0d\n", i);
+			sbp_sleep(0);
+			msg(DBG_AUD,"read_audio: after giving \"read\" command.\n");
+			for (frame=1;frame<2 && !error_flag; frame++)
+			{
+				try=maxtim_data;
+				for (timeout=jiffies+9*HZ; ; )
 				{
-					if (do_16bit) i=inw(CDi_data);
-					else i=inb(CDi_data);
-					j++;
-					i=inb(CDi_status);
+					for ( ; try!=0;try--)
+					{
+						j=inb(CDi_status);
+						if (!(j&s_not_data_ready)) break;
+						if (!(j&s_not_result_ready)) break;
+						if (fam0L_drive) if (j&s_attention) break;
+					}
+					if (try != 0 || time_after_eq(jiffies, timeout)) break;
+					if (data_retrying == 0) data_waits++;
+					data_retrying = 1;
+					sbp_sleep(1);
+					try = 1;
 				}
-				while (!(i&s_not_data_ready));
-				msg(DBG_TEA, "==========too much data (%d bytes/words)==============.\n", j);
-			}
-			if (!(i&s_not_result_ready))
-			{
-				OUT(CDo_sel_i_d,0);
-				l=0;
-				do
+				if (try==0)
 				{
-					infobuf[l++]=inb(CDi_info);
-					i=inb(CDi_status);
+					msg(DBG_INF,"read_audio: sbp_data: CDi_status timeout.\n");
+					error_flag++;
+					break;
 				}
-				while (!(i&s_not_result_ready));
-				if (infobuf[0]==0x00) success=1;
-#if 1
-				for (j=0;j<l;j++) sprintf(&msgbuf[j*3], " %02X", infobuf[j]);
-				msgbuf[j*3]=0;
-				msg(DBG_TEA,"sbp_data info response:%s\n", msgbuf);
-#endif
-				if (infobuf[0]==0x02)
+				msg(DBG_AUD,"read_audio: sbp_data: CDi_status ok.\n");
+				if (j&s_not_data_ready)
 				{
+					msg(DBG_INF, "read_audio: sbp_data: DATA_READY timeout.\n");
 					error_flag++;
-					do
-					{
-						++recursion;
-						if (recursion>1) msg(DBG_TEA,"cmd_out_T READ_ERR recursion (sbp_data): %d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n",recursion);
-						else msg(DBG_TEA,"sbp_data: CMDT_READ_ERR necessary.\n");
-						clr_cmdbuf();
-						drvcmd[0]=CMDT_READ_ERR;
-						j=cmd_out_T(); /* !!! recursive here !!! */
-						--recursion;
-						sbp_sleep(1);
+					break;
+				}
+				msg(DBG_AUD,"read_audio: before reading data.\n");
+				error_flag=0;
+				p = current_drive->aud_buf;
+				if (sbpro_type==1) OUT(CDo_sel_i_d,1);
+				if (do_16bit)
+				{
+					u_short *p2 = (u_short *) p;
+
+					for (; (u_char *) p2 < current_drive->aud_buf + read_audio.nframes*CD_FRAMESIZE_RAW;)
+				  	{
+						if ((inb_p(CDi_status)&s_not_data_ready)) continue;
+
+						/* get one sample */
+						*p2++ = inw_p(CDi_data);
+						*p2++ = inw_p(CDi_data);
+					}
+				} else {
+					for (; p < current_drive->aud_buf + read_audio.nframes*CD_FRAMESIZE_RAW;)
+				  	{
+						if ((inb_p(CDi_status)&s_not_data_ready)) continue;
+
+						/* get one sample */
+						*p++ = inb_p(CDi_data);
+						*p++ = inb_p(CDi_data);
+						*p++ = inb_p(CDi_data);
+						*p++ = inb_p(CDi_data);
 					}
-					while (j<0);
-					current_drive->error_state=infobuf[2];
-					current_drive->b3=infobuf[3];
-					current_drive->b4=infobuf[4];
 				}
-				break;
+				if (sbpro_type==1) OUT(CDo_sel_i_d,0);
+				data_retrying = 0;
 			}
-			else
+			msg(DBG_AUD,"read_audio: after reading data.\n");
+			if (error_flag)    /* must have been spurious D_RDY or (ATTN&&!D_RDY) */
 			{
-#if 0
-				msg(DBG_TEA, "============= waiting for result=================.\n");
-				sbp_sleep(1);
-#endif
-			}
-		}
-		while (wait--);
-	}
-
-	if (error_flag) /* must have been spurious D_RDY or (ATTN&&!D_RDY) */
-	{
-		msg(DBG_TEA, "================error flag: %d=================.\n", error_flag);
-		msg(DBG_INF,"sbp_data: read aborted by drive.\n");
-#if 1
-		i=cc_DriveReset(); /* ugly fix to prevent a hang */
+				msg(DBG_AUD,"read_audio: read aborted by drive\n");
+#if 0000
+				i=cc_DriveReset();                /* ugly fix to prevent a hang */
 #else
-		i=cc_ReadError();
+				i=cc_ReadError();
 #endif
-		return (0);
-	}
-	
-	if (fam0LV_drive)
-	{
-		SBPCD_CLI;
-		i=maxtim_data;
-		for (timeout=jiffies+HZ; time_before(jiffies, timeout); timeout--)
-		{
-			for ( ;i!=0;i--)
+				continue;
+			}
+			if (fam0L_drive)
 			{
-				j=inb(CDi_status);
-				if (!(j&s_not_data_ready)) break;
-				if (!(j&s_not_result_ready)) break;
-				if (j&s_attention) break;
+				i=maxtim_data;
+				for (timeout=jiffies+9*HZ; time_before(jiffies, timeout); timeout--)
+				{
+					for ( ;i!=0;i--)
+					{
+						j=inb(CDi_status);
+						if (!(j&s_not_data_ready)) break;
+						if (!(j&s_not_result_ready)) break;
+						if (j&s_attention) break;
+					}
+					if (i != 0 || time_after_eq(jiffies, timeout)) break;
+					sbp_sleep(0);
+					i = 1;
+				}
+				if (i==0) msg(DBG_AUD,"read_audio: STATUS TIMEOUT AFTER READ");
+				if (!(j&s_attention))
+				{
+					msg(DBG_AUD,"read_audio: sbp_data: timeout waiting DRV_ATTN - retrying\n");
+					i=cc_DriveReset();  /* ugly fix to prevent a hang */
+					continue;
+				}
 			}
-			if (i != 0 || time_after_eq(jiffies, timeout)) break;
-			sbp_sleep(0);
-			i = 1;
-		}
-		if (i==0) msg(DBG_INF,"status timeout after READ.\n");
-		if (!(j&s_attention))
-		{
-			msg(DBG_INF,"sbp_data: timeout waiting DRV_ATTN - retrying.\n");
-			i=cc_DriveReset();  /* ugly fix to prevent a hang */
-			SBPCD_STI;
-			return (0);
-		}
-		SBPCD_STI;
-	}
-	
-#if 0
-	if (!success)
-#endif
-		do
-		{
-			if (fam0LV_drive) cc_ReadStatus();
-#if 1
-			if (famT_drive) msg(DBG_TEA, "================before ResponseStatus=================.\n", i);
-#endif
-			i=ResponseStatus();  /* builds status_bits, returns orig. status (old) or faked p_success (new) */
-#if 1
-			if (famT_drive)	msg(DBG_TEA, "================ResponseStatus: %d=================.\n", i);
-#endif
-			if (i<0)
+			do
 			{
-				msg(DBG_INF,"bad cc_ReadStatus after read: %02X\n", current_drive->status_bits);
-				return (0);
+				if (fam0L_drive) cc_ReadStatus();
+				i=ResponseStatus();  /* builds status_bits, returns orig. status (old) or faked p_success (new) */
+				if (i<0) { msg(DBG_AUD,
+					       "read_audio: cc_ReadStatus error after read: %02X\n",
+					       current_drive->status_bits);
+					   continue; /* FIXME */
+				   }
+			}
+			while ((fam0L_drive)&&(!st_check)&&(!(i&p_success)));
+			if (st_check)
+			{
+				i=cc_ReadError();
+				msg(DBG_AUD,"read_audio: cc_ReadError was necessary after read: %02X\n",i);
+				continue;
 			}
+			if (copy_to_user(read_audio.buf,
+					 current_drive->aud_buf,
+					 read_audio.nframes * CD_FRAMESIZE_RAW))
+				RETURN_UP(-EFAULT);
+			msg(DBG_AUD,"read_audio: copy_to_user done.\n");
+			break;
 		}
-		while ((fam0LV_drive)&&(!st_check)&&(!(i&p_success)));
-	if (st_check)
-	{
-		i=cc_ReadError();
-		msg(DBG_INF,"cc_ReadError was necessary after read: %d\n",i);
-		return (0);
-	}
-	if (fatal_err)
-	{
-		fatal_err=0;
-		current_drive->sbp_first_frame=current_drive->sbp_last_frame=-1;      /* purge buffer */
-		current_drive->sbp_current = 0;
-		msg(DBG_INF,"sbp_data: fatal_err - retrying.\n");
-		return (0);
-	}
-	
-	current_drive->sbp_first_frame = req -> sector / 4;
-	current_drive->sbp_last_frame = current_drive->sbp_first_frame + current_drive->sbp_read_frames - 1;
-	sbp_transfer(req);
-	return (1);
-}
-/*==========================================================================*/
-
-static int sbpcd_block_open(struct inode *inode, struct file *file)
-{
-	struct sbpcd_drive *p = inode->i_bdev->bd_disk->private_data;
-	return cdrom_open(p->sbpcd_infop, inode, file);
-}
-
-static int sbpcd_block_release(struct inode *inode, struct file *file)
-{
-	struct sbpcd_drive *p = inode->i_bdev->bd_disk->private_data;
-	return cdrom_release(p->sbpcd_infop, file);
-}
+		cc_ModeSelect(CD_FRAMESIZE);
+		cc_ModeSense();
+		current_drive->mode=READ_M1;
+#if OLD_BUSY
+		busy_audio=0;
+#endif /* OLD_BUSY */
+		if (data_tries == 0)
+		{
+			msg(DBG_AUD,"read_audio: failed after 5 tries in line %d.\n", __LINE__);
+			RETURN_UP(-EIO);
+		}
+		msg(DBG_AUD,"read_audio: successful return.\n");
+		RETURN_UP(0);
+	} /* end of CDROMREADAUDIO */
 
-static int sbpcd_block_ioctl(struct inode *inode, struct file *file,
-				unsigned cmd, unsigned long arg)
-{
-	struct sbpcd_drive *p = inode->i_bdev->bd_disk->private_data;
-	return cdrom_ioctl(file, p->sbpcd_infop, inode, cmd, arg);
+	default:
+		msg(DBG_IOC,"ioctl: unknown function request %04X\n", cmd);
+		RETURN_UP(-EINVAL);
+	} /* end switch(cmd) */
 }
 
 static int sbpcd_block_media_changed(struct gendisk *disk)
@@ -5478,10 +5471,9 @@
 	.get_mcn		= sbpcd_get_mcn,
 	.reset			= sbpcd_reset,
 	.audio_ioctl		= sbpcd_audio_ioctl,
-	.dev_ioctl		= sbpcd_dev_ioctl,
 	.capability		= CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK |
 				CDC_MULTI_SESSION | CDC_MEDIA_CHANGED |
-				CDC_MCN | CDC_PLAY_AUDIO | CDC_IOCTLS,
+				CDC_MCN | CDC_PLAY_AUDIO,
 	.n_minors		= 1,
 };
 
diff -urN oldtree/drivers/cdrom/viocd.c newtree/drivers/cdrom/viocd.c
--- oldtree/drivers/cdrom/viocd.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/cdrom/viocd.c	2006-02-13 19:19:59.794543536 -0500
@@ -629,7 +629,7 @@
 	.media_changed = viocd_media_changed,
 	.lock_door = viocd_lock_door,
 	.generic_packet = viocd_packet,
-	.capability = CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED | CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | CDC_IOCTLS | CDC_DRIVE_STATUS | CDC_GENERIC_PACKET | CDC_CD_R | CDC_CD_RW | CDC_DVD | CDC_DVD_R | CDC_DVD_RAM | CDC_RAM
+	.capability = CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED | CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | CDC_DRIVE_STATUS | CDC_GENERIC_PACKET | CDC_CD_R | CDC_CD_RW | CDC_DVD | CDC_DVD_R | CDC_DVD_RAM | CDC_RAM
 };
 
 static int __init find_capability(const char *type)
diff -urN oldtree/drivers/char/agp/amd64-agp.c newtree/drivers/char/agp/amd64-agp.c
--- oldtree/drivers/char/agp/amd64-agp.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/char/agp/amd64-agp.c	2006-02-13 19:19:59.795543384 -0500
@@ -600,6 +600,26 @@
 	agp_put_bridge(bridge);
 }
 
+#ifdef CONFIG_PM
+
+static int agp_amd64_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	pci_save_state(pdev);
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+	return 0;
+}
+
+static int agp_amd64_resume(struct pci_dev *pdev)
+{
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+
+	return amd_8151_configure();
+}
+
+#endif /* CONFIG_PM */
+
 static struct pci_device_id agp_amd64_pci_table[] = {
 	{
 	.class		= (PCI_CLASS_BRIDGE_HOST << 8),
@@ -718,6 +738,10 @@
 	.id_table	= agp_amd64_pci_table,
 	.probe		= agp_amd64_probe,
 	.remove		= agp_amd64_remove,
+#ifdef CONFIG_PM
+	.suspend	= agp_amd64_suspend,
+	.resume		= agp_amd64_resume,
+#endif
 };
 
 
diff -urN oldtree/drivers/char/agp/nvidia-agp.c newtree/drivers/char/agp/nvidia-agp.c
--- oldtree/drivers/char/agp/nvidia-agp.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/char/agp/nvidia-agp.c	2006-02-13 19:19:59.813540648 -0500
@@ -11,6 +11,7 @@
 #include <linux/gfp.h>
 #include <linux/page-flags.h>
 #include <linux/mm.h>
+#include <linux/jiffies.h>
 #include "agp.h"
 
 /* NVIDIA registers */
@@ -256,7 +257,7 @@
 		do {
 			pci_read_config_dword(nvidia_private.dev_1,
 					NVIDIA_1_WBC, &wbc_reg);
-			if ((signed)(end - jiffies) <= 0) {
+			if (time_before_eq(end, jiffies)) {
 				printk(KERN_ERR PFX
 				    "TLB flush took more than 3 seconds.\n");
 			}
diff -urN oldtree/drivers/char/moxa.c newtree/drivers/char/moxa.c
--- oldtree/drivers/char/moxa.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/char/moxa.c	2006-02-13 19:20:29.703996608 -0500
@@ -1661,6 +1661,8 @@
 	case MOXA_FIND_BOARD:
 	case MOXA_LOAD_C320B:
 	case MOXA_LOAD_CODE:
+		if (!capable(CAP_SYS_RAWIO))
+			return -EPERM;
 		break;
 	}
 
diff -urN oldtree/drivers/char/serial167.c newtree/drivers/char/serial167.c
--- oldtree/drivers/char/serial167.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/char/serial167.c	2006-02-13 19:19:59.815540344 -0500
@@ -129,7 +129,6 @@
  * memory if large numbers of serial ports are open.
  */
 static unsigned char *tmp_buf = 0;
-DECLARE_MUTEX(tmp_buf_sem);
 
 /*
  * This is used to look up the divisor speeds and the timeouts
diff -urN oldtree/drivers/char/watchdog/pcwd.c newtree/drivers/char/watchdog/pcwd.c
--- oldtree/drivers/char/watchdog/pcwd.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/char/watchdog/pcwd.c	2006-02-13 19:19:59.834537456 -0500
@@ -49,29 +49,37 @@
  *	More info available at http://www.berkprod.com/ or http://www.pcwatchdog.com/
  */
 
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/timer.h>
-#include <linux/jiffies.h>
-#include <linux/config.h>
-#include <linux/wait.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/miscdevice.h>
-#include <linux/watchdog.h>
-#include <linux/notifier.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/reboot.h>
+#include <linux/config.h>	/* For CONFIG_WATCHDOG_NOWAYOUT/... */
+#include <linux/module.h>	/* For module specific items */
+#include <linux/moduleparam.h>	/* For new moduleparam's */
+#include <linux/types.h>	/* For standard types (like size_t) */
+#include <linux/errno.h>	/* For the -ENODEV/... values */
+#include <linux/kernel.h>	/* For printk/panic/... */
+#include <linux/delay.h>	/* For mdelay function */
+#include <linux/timer.h>	/* For timer related operations */
+#include <linux/jiffies.h>	/* For jiffies stuff */
+#include <linux/miscdevice.h>	/* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */
+#include <linux/watchdog.h>	/* For the watchdog specific items */
+#include <linux/notifier.h>	/* For notifier support */
+#include <linux/reboot.h>	/* For reboot_notifier stuff */
+#include <linux/init.h>		/* For __init/__exit/... */
+#include <linux/fs.h>		/* For file operations */
+#include <linux/ioport.h>	/* For io-port access */
+#include <linux/spinlock.h>	/* For spin_lock/spin_unlock/... */
 #include <linux/sched.h>	/* TASK_INTERRUPTIBLE, set_current_state() and friends */
-#include <asm/uaccess.h>
-#include <asm/io.h>
+#include <linux/slab.h>		/* For kmalloc */
 
-#define WD_VER                  "1.16 (06/12/2004)"
-#define PFX			"pcwd: "
+#include <asm/uaccess.h>	/* For copy_to_user/put_user/... */
+#include <asm/io.h>		/* For inb/outb/... */
+
+/* Module and version information */
+#define WATCHDOG_VERSION "1.16"
+#define WATCHDOG_DATE "03 Jan 2006"
+#define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog"
+#define WATCHDOG_NAME "pcwd"
+#define PFX WATCHDOG_NAME ": "
+#define DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION " (" WATCHDOG_DATE ")\n"
+#define WD_VER WATCHDOG_VERSION " (" WATCHDOG_DATE ")"
 
 /*
  * It should be noted that PCWD_REVISION_B was removed because A and B
@@ -85,36 +93,38 @@
 
 /*
  * These are the defines that describe the control status bits for the
- * PC Watchdog card, revision A.
- */
+ * PCI-PC Watchdog card.
+*/
+/* Port 1 : Control Status #1 for the PC Watchdog card, revision A. */
 #define WD_WDRST                0x01	/* Previously reset state */
 #define WD_T110                 0x02	/* Temperature overheat sense */
 #define WD_HRTBT                0x04	/* Heartbeat sense */
 #define WD_RLY2                 0x08	/* External relay triggered */
 #define WD_SRLY2                0x80	/* Software external relay triggered */
-
-/*
- * These are the defines that describe the control status bits for the
- * PC Watchdog card, revision C.
- */
+/* Port 1 : Control Status #1 for the PC Watchdog card, revision C. */
 #define WD_REVC_WTRP            0x01	/* Watchdog Trip status */
 #define WD_REVC_HRBT            0x02	/* Watchdog Heartbeat */
 #define WD_REVC_TTRP            0x04	/* Temperature Trip status */
+/* Port 2 : Control Status #2 */
+#define WD_WDIS			0x10	/* Watchdog Disabled */
+#define WD_ENTP			0x20	/* Watchdog Enable Temperature Trip */
+#define WD_SSEL			0x40	/* Watchdog Switch Select (1:SW1 <-> 0:SW2) */
+#define WD_WCMD			0x80	/* Watchdog Command Mode */
 
 /* max. time we give an ISA watchdog card to process a command */
 /* 500ms for each 4 bit response (according to spec.) */
 #define ISA_COMMAND_TIMEOUT     1000
 
 /* Watchdog's internal commands */
-#define CMD_ISA_IDLE                    0x00
-#define CMD_ISA_VERSION_INTEGER         0x01
-#define CMD_ISA_VERSION_TENTH           0x02
-#define CMD_ISA_VERSION_HUNDRETH        0x03
-#define CMD_ISA_VERSION_MINOR           0x04
-#define CMD_ISA_SWITCH_SETTINGS         0x05
-#define CMD_ISA_DELAY_TIME_2SECS        0x0A
-#define CMD_ISA_DELAY_TIME_4SECS        0x0B
-#define CMD_ISA_DELAY_TIME_8SECS        0x0C
+#define CMD_ISA_IDLE			0x00
+#define CMD_ISA_VERSION_INTEGER		0x01
+#define CMD_ISA_VERSION_TENTH		0x02
+#define CMD_ISA_VERSION_HUNDRETH	0x03
+#define CMD_ISA_VERSION_MINOR		0x04
+#define CMD_ISA_SWITCH_SETTINGS		0x05
+#define CMD_ISA_DELAY_TIME_2SECS	0x0A
+#define CMD_ISA_DELAY_TIME_4SECS	0x0B
+#define CMD_ISA_DELAY_TIME_8SECS	0x0C
 
 /*
  * We are using an kernel timer to do the pinging of the watchdog
@@ -130,15 +140,17 @@
 /* internal variables */
 static atomic_t open_allowed = ATOMIC_INIT(1);
 static char expect_close;
-static struct timer_list timer;
-static unsigned long next_heartbeat;
 static int temp_panic;
-static int revision;			/* The card's revision */
-static int supports_temp;		/* Wether or not the card has a temperature device */
-static int command_mode;		/* Wether or not the card is in command mode */
-static int initial_status;		/* The card's boot status */
-static int current_readport;		/* The cards I/O address */
-static spinlock_t io_lock;
+static struct {				/* this is private data for each ISA-PC watchdog card */
+	int revision;			/* The card's revision */
+	int supports_temp;		/* Wether or not the card has a temperature device */
+	int command_mode;		/* Wether or not the card is in command mode */
+	int boot_status;		/* The card's boot status */
+	int io_addr;			/* The cards I/O address */
+	spinlock_t io_lock;		/* the lock for io operations */
+	struct timer_list timer;	/* The timer that pings the watchdog */
+	unsigned long next_heartbeat;	/* the next_heartbeat for the timer */
+} pcwd_private;
 
 /* module parameters */
 #define WATCHDOG_HEARTBEAT 60		/* 60 sec default heartbeat */
@@ -161,14 +173,14 @@
 	int port0, last_port0;	/* Double read for stabilising */
 
 	/* The WCMD bit must be 1 and the command is only 4 bits in size */
-	control_status = (cmd & 0x0F) | 0x80;
-	outb_p(control_status, current_readport + 2);
+	control_status = (cmd & 0x0F) | WD_WCMD;
+	outb_p(control_status, pcwd_private.io_addr + 2);
 	udelay(ISA_COMMAND_TIMEOUT);
 
-	port0 = inb_p(current_readport);
+	port0 = inb_p(pcwd_private.io_addr);
 	for (i = 0; i < 25; ++i) {
 		last_port0 = port0;
-		port0 = inb_p(current_readport);
+		port0 = inb_p(pcwd_private.io_addr);
 
 		if (port0 == last_port0)
 			break;	/* Data is stable */
@@ -184,7 +196,7 @@
 	int i, found=0, count=0;
 
 	/* Set the card into command mode */
-	spin_lock(&io_lock);
+	spin_lock(&pcwd_private.io_lock);
 	while ((!found) && (count < 3)) {
 		i = send_isa_command(CMD_ISA_IDLE);
 
@@ -192,15 +204,15 @@
 			found = 1;
 		else if (i == 0xF3) {
 			/* Card does not like what we've done to it */
-			outb_p(0x00, current_readport + 2);
+			outb_p(0x00, pcwd_private.io_addr + 2);
 			udelay(1200);	/* Spec says wait 1ms */
-			outb_p(0x00, current_readport + 2);
+			outb_p(0x00, pcwd_private.io_addr + 2);
 			udelay(ISA_COMMAND_TIMEOUT);
 		}
 		count++;
 	}
-	spin_unlock(&io_lock);
-	command_mode = found;
+	spin_unlock(&pcwd_private.io_lock);
+	pcwd_private.command_mode = found;
 
 	return(found);
 }
@@ -208,12 +220,95 @@
 static void unset_command_mode(void)
 {
 	/* Set the card into normal mode */
-	spin_lock(&io_lock);
-	outb_p(0x00, current_readport + 2);
+	spin_lock(&pcwd_private.io_lock);
+	outb_p(0x00, pcwd_private.io_addr + 2);
 	udelay(ISA_COMMAND_TIMEOUT);
-	spin_unlock(&io_lock);
+	spin_unlock(&pcwd_private.io_lock);
+
+	pcwd_private.command_mode = 0;
+}
+
+static inline void pcwd_check_temperature_support(void)
+{
+	if (inb(pcwd_private.io_addr) != 0xF0)
+		pcwd_private.supports_temp = 1;
+}
+
+static inline char *get_firmware(void)
+{
+	int one, ten, hund, minor;
+	char *ret;
+
+	ret = kmalloc(6, GFP_KERNEL);
+	if(ret == NULL)
+		return NULL;
+
+	if (set_command_mode()) {
+		one = send_isa_command(CMD_ISA_VERSION_INTEGER);
+		ten = send_isa_command(CMD_ISA_VERSION_TENTH);
+		hund = send_isa_command(CMD_ISA_VERSION_HUNDRETH);
+		minor = send_isa_command(CMD_ISA_VERSION_MINOR);
+		sprintf(ret, "%c.%c%c%c", one, ten, hund, minor);
+	}
+	else
+		sprintf(ret, "ERROR");
+
+	unset_command_mode();
+	return(ret);
+}
+
+static inline int pcwd_get_option_switches(void)
+{
+	int option_switches=0;
+
+	if (set_command_mode()) {
+		/* Get switch settings */
+		option_switches = send_isa_command(CMD_ISA_SWITCH_SETTINGS);
+	}
+
+	unset_command_mode();
+	return(option_switches);
+}
+
+static void pcwd_show_card_info(void)
+{
+	char *firmware;
+	int option_switches;
+
+	/* Get some extra info from the hardware (in command/debug/diag mode) */
+	if (pcwd_private.revision == PCWD_REVISION_A)
+		printk(KERN_INFO PFX "ISA-PC Watchdog (REV.A) detected at port 0x%04x\n", pcwd_private.io_addr);
+	else if (pcwd_private.revision == PCWD_REVISION_C) {
+		firmware = get_firmware();
+		printk(KERN_INFO PFX "ISA-PC Watchdog (REV.C) detected at port 0x%04x (Firmware version: %s)\n",
+			pcwd_private.io_addr, firmware);
+		kfree(firmware);
+		option_switches = pcwd_get_option_switches();
+		printk(KERN_INFO PFX "Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n",
+			option_switches,
+			((option_switches & 0x10) ? "ON" : "OFF"),
+			((option_switches & 0x08) ? "ON" : "OFF"));
+
+		/* Reprogram internal heartbeat to 2 seconds */
+		if (set_command_mode()) {
+			send_isa_command(CMD_ISA_DELAY_TIME_2SECS);
+			unset_command_mode();
+		}
+	}
+
+	if (pcwd_private.supports_temp)
+		printk(KERN_INFO PFX "Temperature Option Detected\n");
+
+	if (pcwd_private.boot_status & WDIOF_CARDRESET)
+		printk(KERN_INFO PFX "Previous reboot was caused by the card\n");
+
+	if (pcwd_private.boot_status & WDIOF_OVERHEAT) {
+		printk(KERN_EMERG PFX "Card senses a CPU Overheat. Panicking!\n");
+		printk(KERN_EMERG PFX "CPU Overheat\n");
+	}
 
-	command_mode = 0;
+	if (pcwd_private.boot_status == 0)
+		printk(KERN_INFO PFX "No previous trip detected - Cold boot or reset\n");
 }
 
 static void pcwd_timer_ping(unsigned long data)
@@ -222,25 +317,25 @@
 
 	/* If we got a heartbeat pulse within the WDT_INTERVAL
 	 * we agree to ping the WDT */
-	if(time_before(jiffies, next_heartbeat)) {
+	if(time_before(jiffies, pcwd_private.next_heartbeat)) {
 		/* Ping the watchdog */
-		spin_lock(&io_lock);
-		if (revision == PCWD_REVISION_A) {
+		spin_lock(&pcwd_private.io_lock);
+		if (pcwd_private.revision == PCWD_REVISION_A) {
 			/*  Rev A cards are reset by setting the WD_WDRST bit in register 1 */
-			wdrst_stat = inb_p(current_readport);
+			wdrst_stat = inb_p(pcwd_private.io_addr);
 			wdrst_stat &= 0x0F;
 			wdrst_stat |= WD_WDRST;
 
-			outb_p(wdrst_stat, current_readport + 1);
+			outb_p(wdrst_stat, pcwd_private.io_addr + 1);
 		} else {
 			/* Re-trigger watchdog by writing to port 0 */
-			outb_p(0x00, current_readport);
+			outb_p(0x00, pcwd_private.io_addr);
 		}
 
 		/* Re-set the timer interval */
-		mod_timer(&timer, jiffies + WDT_INTERVAL);
+		mod_timer(&pcwd_private.timer, jiffies + WDT_INTERVAL);
 
-		spin_unlock(&io_lock);
+		spin_unlock(&pcwd_private.io_lock);
 	} else {
 		printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n");
 	}
@@ -250,19 +345,19 @@
 {
 	int stat_reg;
 
-	next_heartbeat = jiffies + (heartbeat * HZ);
+	pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ);
 
 	/* Start the timer */
-	mod_timer(&timer, jiffies + WDT_INTERVAL);
+	mod_timer(&pcwd_private.timer, jiffies + WDT_INTERVAL);
 
 	/* Enable the port */
-	if (revision == PCWD_REVISION_C) {
-		spin_lock(&io_lock);
-		outb_p(0x00, current_readport + 3);
+	if (pcwd_private.revision == PCWD_REVISION_C) {
+		spin_lock(&pcwd_private.io_lock);
+		outb_p(0x00, pcwd_private.io_addr + 3);
 		udelay(ISA_COMMAND_TIMEOUT);
-		stat_reg = inb_p(current_readport + 2);
-		spin_unlock(&io_lock);
-		if (stat_reg & 0x10) {
+		stat_reg = inb_p(pcwd_private.io_addr + 2);
+		spin_unlock(&pcwd_private.io_lock);
+		if (stat_reg & WD_WDIS) {
 			printk(KERN_INFO PFX "Could not start watchdog\n");
 			return -EIO;
 		}
@@ -275,18 +370,18 @@
 	int stat_reg;
 
 	/* Stop the timer */
-	del_timer(&timer);
+	del_timer(&pcwd_private.timer);
 
 	/*  Disable the board  */
-	if (revision == PCWD_REVISION_C) {
-		spin_lock(&io_lock);
-		outb_p(0xA5, current_readport + 3);
+	if (pcwd_private.revision == PCWD_REVISION_C) {
+		spin_lock(&pcwd_private.io_lock);
+		outb_p(0xA5, pcwd_private.io_addr + 3);
 		udelay(ISA_COMMAND_TIMEOUT);
-		outb_p(0xA5, current_readport + 3);
+		outb_p(0xA5, pcwd_private.io_addr + 3);
 		udelay(ISA_COMMAND_TIMEOUT);
-		stat_reg = inb_p(current_readport + 2);
-		spin_unlock(&io_lock);
-		if ((stat_reg & 0x10) == 0) {
+		stat_reg = inb_p(pcwd_private.io_addr + 2);
+		spin_unlock(&pcwd_private.io_lock);
+		if ((stat_reg & WD_WDIS) == 0) {
 			printk(KERN_INFO PFX "Could not stop watchdog\n");
 			return -EIO;
 		}
@@ -297,7 +392,7 @@
 static int pcwd_keepalive(void)
 {
 	/* user land ping */
-	next_heartbeat = jiffies + (heartbeat * HZ);
+	pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ);
 	return 0;
 }
 
@@ -315,23 +410,23 @@
 	int card_status;
 
 	*status=0;
-	spin_lock(&io_lock);
-	if (revision == PCWD_REVISION_A)
+	spin_lock(&pcwd_private.io_lock);
+	if (pcwd_private.revision == PCWD_REVISION_A)
 		/* Rev A cards return status information from
 		 * the base register, which is used for the
 		 * temperature in other cards. */
-		card_status = inb(current_readport);
+		card_status = inb(pcwd_private.io_addr);
 	else {
 		/* Rev C cards return card status in the base
 		 * address + 1 register. And use different bits
 		 * to indicate a card initiated reset, and an
 		 * over-temperature condition. And the reboot
 		 * status can be reset. */
-		card_status = inb(current_readport + 1);
+		card_status = inb(pcwd_private.io_addr + 1);
 	}
-	spin_unlock(&io_lock);
+	spin_unlock(&pcwd_private.io_lock);
 
-	if (revision == PCWD_REVISION_A) {
+	if (pcwd_private.revision == PCWD_REVISION_A) {
 		if (card_status & WD_WDRST)
 			*status |= WDIOF_CARDRESET;
 
@@ -360,10 +455,10 @@
 
 static int pcwd_clear_status(void)
 {
-	if (revision == PCWD_REVISION_C) {
-		spin_lock(&io_lock);
-		outb_p(0x00, current_readport + 1); /* clear reset status */
-		spin_unlock(&io_lock);
+	if (pcwd_private.revision == PCWD_REVISION_C) {
+		spin_lock(&pcwd_private.io_lock);
+		outb_p(0x00, pcwd_private.io_addr + 1); /* clear reset status */
+		spin_unlock(&pcwd_private.io_lock);
 	}
 	return 0;
 }
@@ -371,20 +466,20 @@
 static int pcwd_get_temperature(int *temperature)
 {
 	/* check that port 0 gives temperature info and no command results */
-	if (command_mode)
+	if (pcwd_private.command_mode)
 		return -1;
 
 	*temperature = 0;
-	if (!supports_temp)
+	if (!pcwd_private.supports_temp)
 		return -ENODEV;
 
 	/*
 	 * Convert celsius to fahrenheit, since this was
 	 * the decided 'standard' for this return value.
 	 */
-	spin_lock(&io_lock);
-	*temperature = ((inb(current_readport)) * 9 / 5) + 32;
-	spin_unlock(&io_lock);
+	spin_lock(&pcwd_private.io_lock);
+	*temperature = ((inb(pcwd_private.io_addr)) * 9 / 5) + 32;
+	spin_unlock(&pcwd_private.io_lock);
 
 	return 0;
 }
@@ -425,7 +520,7 @@
 		return put_user(status, argp);
 
 	case WDIOC_GETBOOTSTATUS:
-		return put_user(initial_status, argp);
+		return put_user(pcwd_private.boot_status, argp);
 
 	case WDIOC_GETTEMP:
 		if (pcwd_get_temperature(&temperature))
@@ -434,7 +529,7 @@
 		return put_user(temperature, argp);
 
 	case WDIOC_SETOPTIONS:
-		if (revision == PCWD_REVISION_C)
+		if (pcwd_private.revision == PCWD_REVISION_C)
 		{
 			if(copy_from_user(&rv, argp, sizeof(int)))
 				return -EFAULT;
@@ -550,7 +645,7 @@
 
 static int pcwd_temp_open(struct inode *inode, struct file *file)
 {
-	if (!supports_temp)
+	if (!pcwd_private.supports_temp)
 		return -ENODEV;
 
 	return nonseekable_open(inode, file);
@@ -616,68 +711,24 @@
  *	Init & exit routines
  */
 
-static inline void get_support(void)
-{
-	if (inb(current_readport) != 0xF0)
-		supports_temp = 1;
-}
-
 static inline int get_revision(void)
 {
 	int r = PCWD_REVISION_C;
 
-	spin_lock(&io_lock);
+	spin_lock(&pcwd_private.io_lock);
 	/* REV A cards use only 2 io ports; test
 	 * presumes a floating bus reads as 0xff. */
-	if ((inb(current_readport + 2) == 0xFF) ||
-	    (inb(current_readport + 3) == 0xFF))
+	if ((inb(pcwd_private.io_addr + 2) == 0xFF) ||
+	    (inb(pcwd_private.io_addr + 3) == 0xFF))
 		r=PCWD_REVISION_A;
-	spin_unlock(&io_lock);
+	spin_unlock(&pcwd_private.io_lock);
 
 	return r;
 }
 
-static inline char *get_firmware(void)
-{
-	int one, ten, hund, minor;
-	char *ret;
-
-	ret = kmalloc(6, GFP_KERNEL);
-	if(ret == NULL)
-		return NULL;
-
-	if (set_command_mode()) {
-		one = send_isa_command(CMD_ISA_VERSION_INTEGER);
-		ten = send_isa_command(CMD_ISA_VERSION_TENTH);
-		hund = send_isa_command(CMD_ISA_VERSION_HUNDRETH);
-		minor = send_isa_command(CMD_ISA_VERSION_MINOR);
-		sprintf(ret, "%c.%c%c%c", one, ten, hund, minor);
-	}
-	else
-		sprintf(ret, "ERROR");
-
-	unset_command_mode();
-	return(ret);
-}
-
-static inline int get_option_switches(void)
-{
-	int rv=0;
-
-	if (set_command_mode()) {
-		/* Get switch settings */
-		rv = send_isa_command(CMD_ISA_SWITCH_SETTINGS);
-	}
-
-	unset_command_mode();
-	return(rv);
-}
-
 static int __devinit pcwatchdog_init(int base_addr)
 {
 	int ret;
-	char *firmware;
-	int option_switches;
 
 	cards_found++;
 	if (cards_found == 1)
@@ -692,104 +743,66 @@
 		printk(KERN_ERR PFX "No I/O-Address for card detected\n");
 		return -ENODEV;
 	}
-	current_readport = base_addr;
+	pcwd_private.io_addr = base_addr;
 
 	/* Check card's revision */
-	revision = get_revision();
+	pcwd_private.revision = get_revision();
 
-	if (!request_region(current_readport, (revision == PCWD_REVISION_A) ? 2 : 4, "PCWD")) {
+	if (!request_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4, "PCWD")) {
 		printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
-			current_readport);
-		current_readport = 0x0000;
+			pcwd_private.io_addr);
+		pcwd_private.io_addr = 0x0000;
 		return -EIO;
 	}
 
 	/* Initial variables */
-	supports_temp = 0;
+	pcwd_private.supports_temp = 0;
 	temp_panic = 0;
-	initial_status = 0x0000;
+	pcwd_private.boot_status = 0x0000;
 
 	/* get the boot_status */
-	pcwd_get_status(&initial_status);
+	pcwd_get_status(&pcwd_private.boot_status);
 
 	/* clear the "card caused reboot" flag */
 	pcwd_clear_status();
 
-	init_timer(&timer);
-	timer.function = pcwd_timer_ping;
-	timer.data = 0;
+	init_timer(&pcwd_private.timer);
+	pcwd_private.timer.function = pcwd_timer_ping;
+	pcwd_private.timer.data = 0;
 
 	/*  Disable the board  */
 	pcwd_stop();
 
 	/*  Check whether or not the card supports the temperature device */
-	get_support();
-
-	/* Get some extra info from the hardware (in command/debug/diag mode) */
-	if (revision == PCWD_REVISION_A)
-		printk(KERN_INFO PFX "ISA-PC Watchdog (REV.A) detected at port 0x%04x\n", current_readport);
-	else if (revision == PCWD_REVISION_C) {
-		firmware = get_firmware();
-		printk(KERN_INFO PFX "ISA-PC Watchdog (REV.C) detected at port 0x%04x (Firmware version: %s)\n",
-			current_readport, firmware);
-		kfree(firmware);
-		option_switches = get_option_switches();
-		printk(KERN_INFO PFX "Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n",
-			option_switches,
-			((option_switches & 0x10) ? "ON" : "OFF"),
-			((option_switches & 0x08) ? "ON" : "OFF"));
-
-		/* Reprogram internal heartbeat to 2 seconds */
-		if (set_command_mode()) {
-			send_isa_command(CMD_ISA_DELAY_TIME_2SECS);
-			unset_command_mode();
-		}
-	} else {
-		/* Should NEVER happen, unless get_revision() fails. */
-		printk(KERN_INFO PFX "Unable to get revision\n");
-		release_region(current_readport, (revision == PCWD_REVISION_A) ? 2 : 4);
-		current_readport = 0x0000;
-		return -1;
-	}
+	pcwd_check_temperature_support();
 
-	if (supports_temp)
-		printk(KERN_INFO PFX "Temperature Option Detected\n");
-
-	if (initial_status & WDIOF_CARDRESET)
-		printk(KERN_INFO PFX "Previous reboot was caused by the card\n");
-
-	if (initial_status & WDIOF_OVERHEAT) {
-		printk(KERN_EMERG PFX "Card senses a CPU Overheat. Panicking!\n");
-		printk(KERN_EMERG PFX "CPU Overheat\n");
-	}
-
-	if (initial_status == 0)
-		printk(KERN_INFO PFX "No previous trip detected - Cold boot or reset\n");
+	/* Show info about the card itself */
+	pcwd_show_card_info();
 
 	/* Check that the heartbeat value is within it's range ; if not reset to the default */
-        if (pcwd_set_heartbeat(heartbeat)) {
-                pcwd_set_heartbeat(WATCHDOG_HEARTBEAT);
-                printk(KERN_INFO PFX "heartbeat value must be 2<=heartbeat<=7200, using %d\n",
-                        WATCHDOG_HEARTBEAT);
+	if (pcwd_set_heartbeat(heartbeat)) {
+		pcwd_set_heartbeat(WATCHDOG_HEARTBEAT);
+		printk(KERN_INFO PFX "heartbeat value must be 2<=heartbeat<=7200, using %d\n",
+			WATCHDOG_HEARTBEAT);
 	}
 
 	ret = register_reboot_notifier(&pcwd_notifier);
 	if (ret) {
 		printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
 			ret);
-		release_region(current_readport, (revision == PCWD_REVISION_A) ? 2 : 4);
-		current_readport = 0x0000;
+		release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
+		pcwd_private.io_addr = 0x0000;
 		return ret;
 	}
 
-	if (supports_temp) {
+	if (pcwd_private.supports_temp) {
 		ret = misc_register(&temp_miscdev);
 		if (ret) {
 			printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
 				TEMP_MINOR, ret);
 			unregister_reboot_notifier(&pcwd_notifier);
-			release_region(current_readport, (revision == PCWD_REVISION_A) ? 2 : 4);
-			current_readport = 0x0000;
+			release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
+			pcwd_private.io_addr = 0x0000;
 			return ret;
 		}
 	}
@@ -798,11 +811,11 @@
 	if (ret) {
 		printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
 			WATCHDOG_MINOR, ret);
-		if (supports_temp)
+		if (pcwd_private.supports_temp)
 			misc_deregister(&temp_miscdev);
 		unregister_reboot_notifier(&pcwd_notifier);
-		release_region(current_readport, (revision == PCWD_REVISION_A) ? 2 : 4);
-		current_readport = 0x0000;
+		release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
+		pcwd_private.io_addr = 0x0000;
 		return ret;
 	}
 
@@ -820,11 +833,12 @@
 
 	/* Deregister */
 	misc_deregister(&pcwd_miscdev);
-	if (supports_temp)
+	if (pcwd_private.supports_temp)
 		misc_deregister(&temp_miscdev);
 	unregister_reboot_notifier(&pcwd_notifier);
-	release_region(current_readport, (revision == PCWD_REVISION_A) ? 2 : 4);
-	current_readport = 0x0000;
+	release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
+	pcwd_private.io_addr = 0x0000;
+	cards_found--;
 }
 
 /*
@@ -887,7 +901,7 @@
 {
 	int i, found = 0;
 
-	spin_lock_init(&io_lock);
+	spin_lock_init(&pcwd_private.io_lock);
 
 	for (i = 0; pcwd_ioports[i] != 0; i++) {
 		if (pcwd_checkcard(pcwd_ioports[i])) {
@@ -906,7 +920,7 @@
 
 static void __exit pcwd_cleanup_module(void)
 {
-	if (current_readport)
+	if (pcwd_private.io_addr)
 		pcwatchdog_exit();
 	return;
 }
diff -urN oldtree/drivers/char/watchdog/sa1100_wdt.c newtree/drivers/char/watchdog/sa1100_wdt.c
--- oldtree/drivers/char/watchdog/sa1100_wdt.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/char/watchdog/sa1100_wdt.c	2006-02-13 19:19:59.835537304 -0500
@@ -74,7 +74,7 @@
 	return 0;
 }
 
-static ssize_t sa1100dog_write(struct file *file, const char *data, size_t len, loff_t *ppos)
+static ssize_t sa1100dog_write(struct file *file, const char __user *data, size_t len, loff_t *ppos)
 {
 	if (len)
 		/* Refresh OSMR3 timer. */
@@ -96,20 +96,20 @@
 
 	switch (cmd) {
 	case WDIOC_GETSUPPORT:
-		ret = copy_to_user((struct watchdog_info *)arg, &ident,
+		ret = copy_to_user((struct watchdog_info __user *)arg, &ident,
 				   sizeof(ident)) ? -EFAULT : 0;
 		break;
 
 	case WDIOC_GETSTATUS:
-		ret = put_user(0, (int *)arg);
+		ret = put_user(0, (int __user *)arg);
 		break;
 
 	case WDIOC_GETBOOTSTATUS:
-		ret = put_user(boot_status, (int *)arg);
+		ret = put_user(boot_status, (int __user *)arg);
 		break;
 
 	case WDIOC_SETTIMEOUT:
-		ret = get_user(time, (int *)arg);
+		ret = get_user(time, (int __user *)arg);
 		if (ret)
 			break;
 
@@ -123,7 +123,7 @@
 		/*fall through*/
 
 	case WDIOC_GETTIMEOUT:
-		ret = put_user(pre_margin / OSCR_FREQ, (int *)arg);
+		ret = put_user(pre_margin / OSCR_FREQ, (int __user *)arg);
 		break;
 
 	case WDIOC_KEEPALIVE:
diff -urN oldtree/drivers/char/watchdog/sc520_wdt.c newtree/drivers/char/watchdog/sc520_wdt.c
--- oldtree/drivers/char/watchdog/sc520_wdt.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/char/watchdog/sc520_wdt.c	2006-02-13 19:19:59.835537304 -0500
@@ -93,7 +93,7 @@
 #define WATCHDOG_TIMEOUT 30		/* 30 sec default timeout */
 static int timeout = WATCHDOG_TIMEOUT;	/* in seconds, will be multiplied by HZ to get seconds to wait for a ping */
 module_param(timeout, int, 0);
-MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=3600, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (2<=timeout<=3600, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
 
 static int nowayout = WATCHDOG_NOWAYOUT;
 module_param(nowayout, int, 0);
@@ -210,7 +210,7 @@
 
 static int wdt_set_heartbeat(int t)
 {
-	if ((t < 1) || (t > 3600))	/* arbitrary upper limit */
+	if ((t < 2) || (t > 3600))	/* arbitrary upper limit */
 		return -EINVAL;
 
 	timeout = t;
@@ -396,7 +396,7 @@
 	/* Check that the timeout value is within it's range ; if not reset to the default */
 	if (wdt_set_heartbeat(timeout)) {
 		wdt_set_heartbeat(WATCHDOG_TIMEOUT);
-		printk(KERN_INFO PFX "timeout value must be 1<=timeout<=3600, using %d\n",
+		printk(KERN_INFO PFX "timeout value must be 2<=timeout<=3600, using %d\n",
 			WATCHDOG_TIMEOUT);
 	}
 
diff -urN oldtree/drivers/dlm/Kconfig newtree/drivers/dlm/Kconfig
--- oldtree/drivers/dlm/Kconfig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/dlm/Kconfig	2006-02-13 19:19:59.853534568 -0500
@@ -0,0 +1,27 @@
+menu "Distributed Lock Manager"
+	depends on INET && EXPERIMENTAL
+
+config DLM
+	tristate "Distributed Lock Manager (DLM)"
+	select IP_SCTP
+	help
+	A general purpose distributed lock manager for kernel or userspace
+	applications.
+
+config DLM_DEVICE
+	tristate "DLM device for userspace access"
+	depends on DLM
+	help
+	This module creates a misc device through which the dlm lockspace
+	and locking functions become available to userspace applications
+	(usually through the libdlm library).
+
+config DLM_DEBUG
+	bool "DLM debugging"
+	depends on DLM
+	help
+	Under the debugfs mount point, the name of each lockspace will
+	appear as a file in the "dlm" directory.  The output is the
+	list of resource and locks the local node knows about.
+
+endmenu
diff -urN oldtree/drivers/dlm/Makefile newtree/drivers/dlm/Makefile
--- oldtree/drivers/dlm/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/dlm/Makefile	2006-02-13 19:19:59.854534416 -0500
@@ -0,0 +1,23 @@
+obj-$(CONFIG_DLM) +=		dlm.o
+obj-$(CONFIG_DLM_DEVICE) +=	dlm_device.o
+
+dlm-y :=			ast.o \
+				config.o \
+				dir.o \
+				lock.o \
+				lockspace.o \
+				lowcomms.o \
+				main.o \
+				member.o \
+				member_sysfs.o \
+				memory.o \
+				midcomms.o \
+				node_ioctl.o \
+				rcom.o \
+				recover.o \
+				recoverd.o \
+				requestqueue.o \
+				util.o
+dlm-$(CONFIG_DLM_DEBUG) +=	debug_fs.o
+
+dlm_device-y :=			device.o
diff -urN oldtree/drivers/firmware/dcdbas.c newtree/drivers/firmware/dcdbas.c
--- oldtree/drivers/firmware/dcdbas.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/firmware/dcdbas.c	2006-02-13 19:19:59.854534416 -0500
@@ -530,30 +530,27 @@
 static DCDBAS_DEV_ATTR_RW(host_control_smi_type);
 static DCDBAS_DEV_ATTR_RW(host_control_on_shutdown);
 
-static struct device_attribute *dcdbas_dev_attrs[] = {
-	&dev_attr_smi_data_buf_size,
-	&dev_attr_smi_data_buf_phys_addr,
-	&dev_attr_smi_request,
-	&dev_attr_host_control_action,
-	&dev_attr_host_control_smi_type,
-	&dev_attr_host_control_on_shutdown,
+static struct attribute *dcdbas_dev_attrs[] = {
+	&dev_attr_smi_data_buf_size.attr,
+	&dev_attr_smi_data_buf_phys_addr.attr,
+	&dev_attr_smi_request.attr,
+	&dev_attr_host_control_action.attr,
+	&dev_attr_host_control_smi_type.attr,
+	&dev_attr_host_control_on_shutdown.attr,
 	NULL
 };
 
-/**
- * dcdbas_init: initialize driver
- */
-static int __init dcdbas_init(void)
+static struct attribute_group dcdbas_attr_group = {
+	.attrs = dcdbas_dev_attrs,
+};
+
+static int __devinit dcdbas_probe(struct platform_device *dev)
 {
-	int i;
+	int i, error;
 
 	host_control_action = HC_ACTION_NONE;
 	host_control_smi_type = HC_SMITYPE_NONE;
 
-	dcdbas_pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
-	if (IS_ERR(dcdbas_pdev))
-		return PTR_ERR(dcdbas_pdev);
-
 	/*
 	 * BIOS SMI calls require buffer addresses be in 32-bit address space.
 	 * This is done by setting the DMA mask below.
@@ -561,19 +558,79 @@
 	dcdbas_pdev->dev.coherent_dma_mask = DMA_32BIT_MASK;
 	dcdbas_pdev->dev.dma_mask = &dcdbas_pdev->dev.coherent_dma_mask;
 
+	error = sysfs_create_group(&dev->dev.kobj, &dcdbas_attr_group);
+	if (error)
+		return error;
+
+	for (i = 0; dcdbas_bin_attrs[i]; i++) {
+		error = sysfs_create_bin_file(&dev->dev.kobj,
+					      dcdbas_bin_attrs[i]);
+		if (error) {
+			while (--i >= 0)
+				sysfs_remove_bin_file(&dev->dev.kobj,
+						      dcdbas_bin_attrs[i]);
+			sysfs_create_group(&dev->dev.kobj, &dcdbas_attr_group);
+			return error;
+		}
+	}
+
 	register_reboot_notifier(&dcdbas_reboot_nb);
 
+	dev_info(&dev->dev, "%s (version %s)\n",
+		 DRIVER_DESCRIPTION, DRIVER_VERSION);
+
+	return 0;
+}
+
+static int __devexit dcdbas_remove(struct platform_device *dev)
+{
+	int i;
+
+	unregister_reboot_notifier(&dcdbas_reboot_nb);
 	for (i = 0; dcdbas_bin_attrs[i]; i++)
-		sysfs_create_bin_file(&dcdbas_pdev->dev.kobj,
-				      dcdbas_bin_attrs[i]);
+		sysfs_remove_bin_file(&dev->dev.kobj, dcdbas_bin_attrs[i]);
+	sysfs_remove_group(&dev->dev.kobj, &dcdbas_attr_group);
 
-	for (i = 0; dcdbas_dev_attrs[i]; i++)
-		device_create_file(&dcdbas_pdev->dev, dcdbas_dev_attrs[i]);
+	return 0;
+}
 
-	dev_info(&dcdbas_pdev->dev, "%s (version %s)\n",
-		 DRIVER_DESCRIPTION, DRIVER_VERSION);
+static struct platform_driver dcdbas_driver = {
+	.driver		= {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= dcdbas_probe,
+	.remove		= __devexit_p(dcdbas_remove),
+};
+
+/**
+ * dcdbas_init: initialize driver
+ */
+static int __init dcdbas_init(void)
+{
+	int error;
+
+	error = platform_driver_register(&dcdbas_driver);
+	if (error)
+		return error;
+
+	dcdbas_pdev = platform_device_alloc(DRIVER_NAME, -1);
+	if (!dcdbas_pdev) {
+		error = -ENOMEM;
+		goto err_unregister_driver;
+	}
+
+	error = platform_device_add(dcdbas_pdev);
+	if (error)
+		goto err_free_device;
 
 	return 0;
+
+ err_free_device:
+	platform_device_put(dcdbas_pdev);
+ err_unregister_driver:
+	platform_driver_unregister(&dcdbas_driver);
+	return error;
 }
 
 /**
@@ -582,7 +639,14 @@
 static void __exit dcdbas_exit(void)
 {
 	platform_device_unregister(dcdbas_pdev);
-	unregister_reboot_notifier(&dcdbas_reboot_nb);
+	platform_driver_unregister(&dcdbas_driver);
+
+	/*
+	 * We have to free the buffer here instead of dcdbas_remove
+	 * because only in module exit function we can be sure that
+	 * all sysfs attributes belonging to this module have been
+	 * released.
+	 */
 	smi_data_buf_free();
 }
 
diff -urN oldtree/drivers/firmware/dell_rbu.c newtree/drivers/firmware/dell_rbu.c
--- oldtree/drivers/firmware/dell_rbu.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/firmware/dell_rbu.c	2006-02-13 19:19:59.873531528 -0500
@@ -49,7 +49,7 @@
 MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>");
 MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems");
 MODULE_LICENSE("GPL");
-MODULE_VERSION("3.1");
+MODULE_VERSION("3.2");
 
 #define BIOS_SCAN_LIMIT 0xffffffff
 #define MAX_IMAGE_LENGTH 16
@@ -564,12 +564,10 @@
 
 static void callbackfn_rbu(const struct firmware *fw, void *context)
 {
-	int rc = 0;
+	rbu_data.entry_created = 0;
 
-	if (!fw || !fw->size) {
-		rbu_data.entry_created = 0;
+	if (!fw || !fw->size)
 		return;
-	}
 
 	spin_lock(&rbu_data.lock);
 	if (!strcmp(image_type, "mono")) {
@@ -592,15 +590,6 @@
 	} else
 		pr_debug("invalid image type specified.\n");
 	spin_unlock(&rbu_data.lock);
-
-	rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG,
-		"dell_rbu", &rbu_device->dev, &context, callbackfn_rbu);
-	if (rc)
-		printk(KERN_ERR
-			"dell_rbu:%s request_firmware_nowait failed"
-			" %d\n", __FUNCTION__, rc);
-	else
-		rbu_data.entry_created = 1;
 }
 
 static ssize_t read_rbu_image_type(struct kobject *kobj, char *buffer,
@@ -735,14 +724,7 @@
 	sysfs_create_bin_file(&rbu_device->dev.kobj,
 		&rbu_packet_size_attr);
 
-	rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG,
-		"dell_rbu", &rbu_device->dev, &context, callbackfn_rbu);
-	if (rc)
-		printk(KERN_ERR "dell_rbu:%s:request_firmware_nowait"
-			" failed %d\n", __FUNCTION__, rc);
-	else
-		rbu_data.entry_created = 1;
-
+	rbu_data.entry_created = 0;
 	return rc;
 
 }
diff -urN oldtree/drivers/hwmon/hdaps.c newtree/drivers/hwmon/hdaps.c
--- oldtree/drivers/hwmon/hdaps.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/hwmon/hdaps.c	2006-02-13 19:19:59.874531376 -0500
@@ -36,7 +36,7 @@
 #include <asm/io.h>
 
 #define HDAPS_LOW_PORT		0x1600	/* first port used by hdaps */
-#define HDAPS_NR_PORTS		0x30	/* number of ports: 0x1600 - 0x162f */
+#define HDAPS_HI_PORT		0x162f	/* last port used by hdaps */
 
 #define HDAPS_PORT_STATE	0x1611	/* device state */
 #define HDAPS_PORT_YPOS		0x1612	/* y-axis position */
@@ -62,6 +62,12 @@
 #define HDAPS_INPUT_FUZZ	4	/* input event threshold */
 #define HDAPS_INPUT_FLAT	4
 
+static struct resource __initdata hdaps_iores = {
+	.flags	= IORESOURCE_IO,
+	.start	= HDAPS_LOW_PORT,
+	.end	= HDAPS_HI_PORT,
+};
+
 static struct timer_list hdaps_timer;
 static struct platform_device *pdev;
 static struct input_dev *hdaps_idev;
@@ -284,34 +290,6 @@
 }
 
 
-/* Device model stuff */
-
-static int hdaps_probe(struct platform_device *dev)
-{
-	int ret;
-
-	ret = hdaps_device_init();
-	if (ret)
-		return ret;
-
-	printk(KERN_INFO "hdaps: device successfully initialized.\n");
-	return 0;
-}
-
-static int hdaps_resume(struct platform_device *dev)
-{
-	return hdaps_device_init();
-}
-
-static struct platform_driver hdaps_driver = {
-	.probe = hdaps_probe,
-	.resume = hdaps_resume,
-	.driver	= {
-		.name = "hdaps",
-		.owner = THIS_MODULE,
-	},
-};
-
 /*
  * hdaps_calibrate - Set our "resting" values.  Callers must hold hdaps_sem.
  */
@@ -320,13 +298,13 @@
 	__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &rest_x, &rest_y);
 }
 
-static void hdaps_mousedev_poll(unsigned long unused)
+static void hdaps_inputdev_poll(unsigned long unused)
 {
 	int x, y;
 
 	/* Cannot sleep.  Try nonblockingly.  If we fail, try again later. */
 	if (down_trylock(&hdaps_sem)) {
-		mod_timer(&hdaps_timer,jiffies + HDAPS_POLL_PERIOD);
+		mod_timer(&hdaps_timer, jiffies + HDAPS_POLL_PERIOD);
 		return;
 	}
 
@@ -339,7 +317,7 @@
 
 	mod_timer(&hdaps_timer, jiffies + HDAPS_POLL_PERIOD);
 
-out:
+ out:
 	up(&hdaps_sem);
 }
 
@@ -473,6 +451,80 @@
 	.attrs = hdaps_attributes,
 };
 
+/* Device model stuff */
+
+static int __devinit hdaps_probe(struct platform_device *dev)
+{
+	int error;
+
+	/* initial calibrate for the input device */
+	hdaps_calibrate();
+
+	hdaps_idev = input_allocate_device();
+	if (!hdaps_idev)
+		return -ENOMEM;
+
+	/* initialize the input class */
+	hdaps_idev->name = "hdaps";
+	hdaps_idev->phys = "hdaps/input0";
+	hdaps_idev->cdev.dev = &dev->dev;
+	hdaps_idev->evbit[0] = BIT(EV_ABS);
+	input_set_abs_params(hdaps_idev, ABS_X,
+			-256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
+	input_set_abs_params(hdaps_idev, ABS_Y,
+			-256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
+
+	error = sysfs_create_group(&pdev->dev.kobj, &hdaps_attribute_group);
+	if (error)
+		goto err_free_input_dev;
+
+	error = hdaps_device_init();
+	if (error)
+		goto err_sysfs_remove_group;
+
+	error = input_register_device(hdaps_idev);
+	if (error)
+		goto err_sysfs_remove_group;
+
+	/* start up our timer for the input device */
+	init_timer(&hdaps_timer);
+	hdaps_timer.function = hdaps_inputdev_poll;
+	hdaps_timer.expires = jiffies + HDAPS_POLL_PERIOD;
+	add_timer(&hdaps_timer);
+
+	return 0;
+
+ err_free_input_dev:
+	input_free_device(hdaps_idev);
+ err_sysfs_remove_group:
+	sysfs_remove_group(&dev->dev.kobj, &hdaps_attribute_group);
+	return error;
+}
+
+static int __devexit hdaps_remove(struct platform_device *dev)
+{
+	del_timer_sync(&hdaps_timer);
+	input_unregister_device(hdaps_idev);
+	sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
+
+	return 0;
+}
+
+static int hdaps_resume(struct platform_device *dev)
+{
+	return hdaps_device_init();
+}
+
+static struct platform_driver hdaps_driver = {
+	.driver		= {
+		.name	= "hdaps",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= hdaps_probe,
+	.remove		= __devexit_p(hdaps_remove),
+	.resume		= hdaps_resume,
+};
+
 
 /* Module stuff */
 
@@ -511,7 +563,7 @@
 
 static int __init hdaps_init(void)
 {
-	int ret;
+	int error;
 
 	/* Note that DMI_MATCH(...,"ThinkPad T42") will match "ThinkPad T42p" */
 	struct dmi_system_id hdaps_whitelist[] = {
@@ -532,79 +584,43 @@
 
 	if (!dmi_check_system(hdaps_whitelist)) {
 		printk(KERN_WARNING "hdaps: supported laptop not found!\n");
-		ret = -ENXIO;
-		goto out;
+		return -ENODEV;
 	}
 
-	if (!request_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS, "hdaps")) {
-		ret = -ENXIO;
-		goto out;
+	error = platform_driver_register(&hdaps_driver);
+	if (error)
+		return error;
+
+	pdev = platform_device_alloc("hdaps", -1);
+	if (!pdev) {
+		error = -ENOMEM;
+		goto err_unregister_driver;
 	}
 
-	ret = platform_driver_register(&hdaps_driver);
-	if (ret)
-		goto out_region;
-
-	pdev = platform_device_register_simple("hdaps", -1, NULL, 0);
-	if (IS_ERR(pdev)) {
-		ret = PTR_ERR(pdev);
-		goto out_driver;
-	}
-
-	ret = sysfs_create_group(&pdev->dev.kobj, &hdaps_attribute_group);
-	if (ret)
-		goto out_device;
-
-	hdaps_idev = input_allocate_device();
-	if (!hdaps_idev) {
-		ret = -ENOMEM;
-		goto out_group;
-	}
-
-	/* initial calibrate for the input device */
-	hdaps_calibrate();
-
-	/* initialize the input class */
-	hdaps_idev->name = "hdaps";
-	hdaps_idev->cdev.dev = &pdev->dev;
-	hdaps_idev->evbit[0] = BIT(EV_ABS);
-	input_set_abs_params(hdaps_idev, ABS_X,
-			-256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
-	input_set_abs_params(hdaps_idev, ABS_Y,
-			-256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
-
-	input_register_device(hdaps_idev);
-
-	/* start up our timer for the input device */
-	init_timer(&hdaps_timer);
-	hdaps_timer.function = hdaps_mousedev_poll;
-	hdaps_timer.expires = jiffies + HDAPS_POLL_PERIOD;
-	add_timer(&hdaps_timer);
+	error = platform_device_add_resources(pdev, &hdaps_iores, 1);
+	if (error)
+		goto err_free_device;
+
+	error = platform_device_add(pdev);
+	if (error)
+		goto err_free_device;
 
 	printk(KERN_INFO "hdaps: driver successfully loaded.\n");
 	return 0;
 
-out_group:
-	sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
-out_device:
-	platform_device_unregister(pdev);
-out_driver:
+ err_free_device:
+	platform_device_put(pdev);
+ err_unregister_driver:
 	platform_driver_unregister(&hdaps_driver);
-out_region:
-	release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS);
-out:
-	printk(KERN_WARNING "hdaps: driver init failed (ret=%d)!\n", ret);
-	return ret;
+
+	printk(KERN_WARNING "hdaps: driver init failed (error=%d)!\n", error);
+	return error;
 }
 
 static void __exit hdaps_exit(void)
 {
-	del_timer_sync(&hdaps_timer);
-	input_unregister_device(hdaps_idev);
-	sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
 	platform_device_unregister(pdev);
 	platform_driver_unregister(&hdaps_driver);
-	release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS);
 
 	printk(KERN_INFO "hdaps: driver unloaded.\n");
 }
diff -urN oldtree/drivers/ide/ide-cd.c newtree/drivers/ide/ide-cd.c
--- oldtree/drivers/ide/ide-cd.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/ide/ide-cd.c	2006-02-13 19:20:29.704996456 -0500
@@ -1332,8 +1332,6 @@
 	if (cdrom_read_from_buffer(drive))
 		return ide_stopped;
 
-	blk_attempt_remerge(drive->queue, rq);
-
 	/* Clear the local sector buffer. */
 	info->nsectors_buffered = 0;
 
@@ -1874,14 +1872,6 @@
 		return ide_stopped;
 	}
 
-	/*
-	 * for dvd-ram and such media, it's a really big deal to get
-	 * big writes all the time. so scour the queue and attempt to
-	 * remerge requests, often the plugging will not have had time
-	 * to do this properly
-	 */
-	blk_attempt_remerge(drive->queue, rq);
-
 	info->nsectors_buffered = 0;
 
 	/* use dma, if possible. we don't need to check more, since we
@@ -2481,52 +2471,6 @@
 }
 
 static
-int ide_cdrom_dev_ioctl (struct cdrom_device_info *cdi,
-			 unsigned int cmd, unsigned long arg)
-{
-	struct packet_command cgc;
-	char buffer[16];
-	int stat;
-
-	init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_UNKNOWN);
-
-	/* These will be moved into the Uniform layer shortly... */
-	switch (cmd) {
- 	case CDROMSETSPINDOWN: {
- 		char spindown;
- 
- 		if (copy_from_user(&spindown, (void __user *) arg, sizeof(char)))
-			return -EFAULT;
- 
-                if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0)))
-			return stat;
-
- 		buffer[11] = (buffer[11] & 0xf0) | (spindown & 0x0f);
-
- 		return cdrom_mode_select(cdi, &cgc);
- 	} 
- 
- 	case CDROMGETSPINDOWN: {
- 		char spindown;
- 
-                if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0)))
-			return stat;
- 
- 		spindown = buffer[11] & 0x0f;
- 
-		if (copy_to_user((void __user *) arg, &spindown, sizeof (char)))
-			return -EFAULT;
- 
- 		return 0;
- 	}
-  
-	default:
-		return -EINVAL;
-	}
-
-}
-
-static
 int ide_cdrom_audio_ioctl (struct cdrom_device_info *cdi,
 			   unsigned int cmd, void *arg)
 			   
@@ -2862,12 +2806,11 @@
 	.get_mcn		= ide_cdrom_get_mcn,
 	.reset			= ide_cdrom_reset,
 	.audio_ioctl		= ide_cdrom_audio_ioctl,
-	.dev_ioctl		= ide_cdrom_dev_ioctl,
 	.capability		= CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK |
 				CDC_SELECT_SPEED | CDC_SELECT_DISC |
 				CDC_MULTI_SESSION | CDC_MCN |
 				CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET |
-				CDC_IOCTLS | CDC_DRIVE_STATUS | CDC_CD_R |
+				CDC_DRIVE_STATUS | CDC_CD_R |
 				CDC_CD_RW | CDC_DVD | CDC_DVD_R| CDC_DVD_RAM |
 				CDC_GENERIC_PACKET | CDC_MO_DRIVE | CDC_MRW |
 				CDC_MRW_W | CDC_RAM,
@@ -3378,6 +3321,45 @@
 	return 0;
 }
 
+static int idecd_set_spindown(struct cdrom_device_info *cdi, unsigned long arg)
+{
+	struct packet_command cgc;
+	char buffer[16];
+	int stat;
+	char spindown;
+
+	if (copy_from_user(&spindown, (void __user *)arg, sizeof(char)))
+		return -EFAULT;
+
+	init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_UNKNOWN);
+
+	stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0);
+	if (stat)
+		return stat;
+
+	buffer[11] = (buffer[11] & 0xf0) | (spindown & 0x0f);
+	return cdrom_mode_select(cdi, &cgc);
+}
+
+static int idecd_get_spindown(struct cdrom_device_info *cdi, unsigned long arg)
+{
+	struct packet_command cgc;
+	char buffer[16];
+	int stat;
+ 	char spindown;
+
+	init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_UNKNOWN);
+
+	stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0);
+	if (stat)
+		return stat;
+
+	spindown = buffer[11] & 0x0f;
+	if (copy_to_user((void __user *)arg, &spindown, sizeof (char)))
+		return -EFAULT;
+	return 0;
+}
+
 static int idecd_ioctl (struct inode *inode, struct file *file,
 			unsigned int cmd, unsigned long arg)
 {
@@ -3385,7 +3367,16 @@
 	struct cdrom_info *info = ide_cd_g(bdev->bd_disk);
 	int err;
 
-	err  = generic_ide_ioctl(info->drive, file, bdev, cmd, arg);
+	switch (cmd) {
+ 	case CDROMSETSPINDOWN:
+		return idecd_set_spindown(&info->devinfo, arg);
+ 	case CDROMGETSPINDOWN:
+		return idecd_get_spindown(&info->devinfo, arg);
+	default:
+		break;
+ 	}
+
+	err = generic_ide_ioctl(info->drive, file, bdev, cmd, arg);
 	if (err == -EINVAL)
 		err = cdrom_ioctl(file, &info->devinfo, inode, cmd, arg);
 
diff -urN oldtree/drivers/ide/ide-cd.c.orig newtree/drivers/ide/ide-cd.c.orig
--- oldtree/drivers/ide/ide-cd.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/ide/ide-cd.c.orig	2006-02-13 19:19:59.900527424 -0500
@@ -0,0 +1,3514 @@
+/*
+ * linux/drivers/ide/ide-cd.c
+ *
+ * Copyright (C) 1994, 1995, 1996  scott snyder  <snyder@fnald0.fnal.gov>
+ * Copyright (C) 1996-1998  Erik Andersen <andersee@debian.org>
+ * Copyright (C) 1998-2000  Jens Axboe <axboe@suse.de>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ * ATAPI CD-ROM driver.  To be used with ide.c.
+ * See Documentation/cdrom/ide-cd for usage information.
+ *
+ * Suggestions are welcome. Patches that work are more welcome though. ;-)
+ * For those wishing to work on this driver, please be sure you download
+ * and comply with the latest Mt. Fuji (SFF8090 version 4) and ATAPI 
+ * (SFF-8020i rev 2.6) standards. These documents can be obtained by 
+ * anonymous ftp from:
+ * ftp://fission.dt.wdc.com/pub/standards/SFF_atapi/spec/SFF8020-r2.6/PS/8020r26.ps
+ * ftp://ftp.avc-pioneer.com/Mtfuji4/Spec/Fuji4r10.pdf
+ *
+ * Drives that deviate from these standards will be accommodated as much
+ * as possible via compile time or command-line options.  Since I only have
+ * a few drives, you generally need to send me patches...
+ *
+ * ----------------------------------
+ * TO DO LIST:
+ * -Make it so that Pioneer CD DR-A24X and friends don't get screwed up on
+ *   boot
+ *
+ * ----------------------------------
+ * 1.00  Oct 31, 1994 -- Initial version.
+ * 1.01  Nov  2, 1994 -- Fixed problem with starting request in
+ *                       cdrom_check_status.
+ * 1.03  Nov 25, 1994 -- leaving unmask_intr[] as a user-setting (as for disks)
+ * (from mlord)       -- minor changes to cdrom_setup()
+ *                    -- renamed ide_dev_s to ide_drive_t, enable irq on command
+ * 2.00  Nov 27, 1994 -- Generalize packet command interface;
+ *                       add audio ioctls.
+ * 2.01  Dec  3, 1994 -- Rework packet command interface to handle devices
+ *                       which send an interrupt when ready for a command.
+ * 2.02  Dec 11, 1994 -- Cache the TOC in the driver.
+ *                       Don't use SCMD_PLAYAUDIO_TI; it's not included
+ *                       in the current version of ATAPI.
+ *                       Try to use LBA instead of track or MSF addressing
+ *                       when possible.
+ *                       Don't wait for READY_STAT.
+ * 2.03  Jan 10, 1995 -- Rewrite block read routines to handle block sizes
+ *                       other than 2k and to move multiple sectors in a
+ *                       single transaction.
+ * 2.04  Apr 21, 1995 -- Add work-around for Creative Labs CD220E drives.
+ *                       Thanks to Nick Saw <cwsaw@pts7.pts.mot.com> for
+ *                       help in figuring this out.  Ditto for Acer and
+ *                       Aztech drives, which seem to have the same problem.
+ * 2.04b May 30, 1995 -- Fix to match changes in ide.c version 3.16 -ml
+ * 2.05  Jun  8, 1995 -- Don't attempt to retry after an illegal request
+ *                        or data protect error.
+ *                       Use HWIF and DEV_HWIF macros as in ide.c.
+ *                       Always try to do a request_sense after
+ *                        a failed command.
+ *                       Include an option to give textual descriptions
+ *                        of ATAPI errors.
+ *                       Fix a bug in handling the sector cache which
+ *                        showed up if the drive returned data in 512 byte
+ *                        blocks (like Pioneer drives).  Thanks to
+ *                        Richard Hirst <srh@gpt.co.uk> for diagnosing this.
+ *                       Properly supply the page number field in the
+ *                        MODE_SELECT command.
+ *                       PLAYAUDIO12 is broken on the Aztech; work around it.
+ * 2.05x Aug 11, 1995 -- lots of data structure renaming/restructuring in ide.c
+ *                       (my apologies to Scott, but now ide-cd.c is independent)
+ * 3.00  Aug 22, 1995 -- Implement CDROMMULTISESSION ioctl.
+ *                       Implement CDROMREADAUDIO ioctl (UNTESTED).
+ *                       Use input_ide_data() and output_ide_data().
+ *                       Add door locking.
+ *                       Fix usage count leak in cdrom_open, which happened
+ *                        when a read-write mount was attempted.
+ *                       Try to load the disk on open.
+ *                       Implement CDROMEJECT_SW ioctl (off by default).
+ *                       Read total cdrom capacity during open.
+ *                       Rearrange logic in cdrom_decode_status.  Issue
+ *                        request sense commands for failed packet commands
+ *                        from here instead of from cdrom_queue_packet_command.
+ *                        Fix a race condition in retrieving error information.
+ *                       Suppress printing normal unit attention errors and
+ *                        some drive not ready errors.
+ *                       Implement CDROMVOLREAD ioctl.
+ *                       Implement CDROMREADMODE1/2 ioctls.
+ *                       Fix race condition in setting up interrupt handlers
+ *                        when the `serialize' option is used.
+ * 3.01  Sep  2, 1995 -- Fix ordering of reenabling interrupts in
+ *                        cdrom_queue_request.
+ *                       Another try at using ide_[input,output]_data.
+ * 3.02  Sep 16, 1995 -- Stick total disk capacity in partition table as well.
+ *                       Make VERBOSE_IDE_CD_ERRORS dump failed command again.
+ *                       Dump out more information for ILLEGAL REQUEST errs.
+ *                       Fix handling of errors occurring before the
+ *                        packet command is transferred.
+ *                       Fix transfers with odd bytelengths.
+ * 3.03  Oct 27, 1995 -- Some Creative drives have an id of just `CD'.
+ *                       `DCI-2S10' drives are broken too.
+ * 3.04  Nov 20, 1995 -- So are Vertos drives.
+ * 3.05  Dec  1, 1995 -- Changes to go with overhaul of ide.c and ide-tape.c
+ * 3.06  Dec 16, 1995 -- Add support needed for partitions.
+ *                       More workarounds for Vertos bugs (based on patches
+ *                        from Holger Dietze <dietze@aix520.informatik.uni-leipzig.de>).
+ *                       Try to eliminate byteorder assumptions.
+ *                       Use atapi_cdrom_subchnl struct definition.
+ *                       Add STANDARD_ATAPI compilation option.
+ * 3.07  Jan 29, 1996 -- More twiddling for broken drives: Sony 55D,
+ *                        Vertos 300.
+ *                       Add NO_DOOR_LOCKING configuration option.
+ *                       Handle drive_cmd requests w/NULL args (for hdparm -t).
+ *                       Work around sporadic Sony55e audio play problem.
+ * 3.07a Feb 11, 1996 -- check drive->id for NULL before dereferencing, to fix
+ *                        problem with "hde=cdrom" with no drive present.  -ml
+ * 3.08  Mar  6, 1996 -- More Vertos workarounds.
+ * 3.09  Apr  5, 1996 -- Add CDROMCLOSETRAY ioctl.
+ *                       Switch to using MSF addressing for audio commands.
+ *                       Reformat to match kernel tabbing style.
+ *                       Add CDROM_GET_UPC ioctl.
+ * 3.10  Apr 10, 1996 -- Fix compilation error with STANDARD_ATAPI.
+ * 3.11  Apr 29, 1996 -- Patch from Heiko Eissfeldt <heiko@colossus.escape.de>
+ *                       to remove redundant verify_area calls.
+ * 3.12  May  7, 1996 -- Rudimentary changer support.  Based on patches
+ *                        from Gerhard Zuber <zuber@berlin.snafu.de>.
+ *                       Let open succeed even if there's no loaded disc.
+ * 3.13  May 19, 1996 -- Fixes for changer code.
+ * 3.14  May 29, 1996 -- Add work-around for Vertos 600.
+ *                        (From Hennus Bergman <hennus@sky.ow.nl>.)
+ * 3.15  July 2, 1996 -- Added support for Sanyo 3 CD changers
+ *                        from Ben Galliart <bgallia@luc.edu> with 
+ *                        special help from Jeff Lightfoot 
+ *                        <jeffml@pobox.com>
+ * 3.15a July 9, 1996 -- Improved Sanyo 3 CD changer identification
+ * 3.16  Jul 28, 1996 -- Fix from Gadi to reduce kernel stack usage for ioctl.
+ * 3.17  Sep 17, 1996 -- Tweak audio reads for some drives.
+ *                       Start changing CDROMLOADFROMSLOT to CDROM_SELECT_DISC.
+ * 3.18  Oct 31, 1996 -- Added module and DMA support.
+ *                       
+ *                       
+ * 4.00  Nov 5, 1996   -- New ide-cd maintainer,
+ *                                 Erik B. Andersen <andersee@debian.org>
+ *                     -- Newer Creative drives don't always set the error
+ *                          register correctly.  Make sure we see media changes
+ *                          regardless.
+ *                     -- Integrate with generic cdrom driver.
+ *                     -- CDROMGETSPINDOWN and CDROMSETSPINDOWN ioctls, based on
+ *                          a patch from Ciro Cattuto <>.
+ *                     -- Call set_device_ro.
+ *                     -- Implement CDROMMECHANISMSTATUS and CDROMSLOTTABLE
+ *                          ioctls, based on patch by Erik Andersen
+ *                     -- Add some probes of drive capability during setup.
+ *
+ * 4.01  Nov 11, 1996  -- Split into ide-cd.c and ide-cd.h
+ *                     -- Removed CDROMMECHANISMSTATUS and CDROMSLOTTABLE 
+ *                          ioctls in favor of a generalized approach 
+ *                          using the generic cdrom driver.
+ *                     -- Fully integrated with the 2.1.X kernel.
+ *                     -- Other stuff that I forgot (lots of changes)
+ *
+ * 4.02  Dec 01, 1996  -- Applied patch from Gadi Oxman <gadio@netvision.net.il>
+ *                          to fix the drive door locking problems.
+ *
+ * 4.03  Dec 04, 1996  -- Added DSC overlap support.
+ * 4.04  Dec 29, 1996  -- Added CDROMREADRAW ioclt based on patch 
+ *                          by Ales Makarov (xmakarov@sun.felk.cvut.cz)
+ *
+ * 4.05  Nov 20, 1997  -- Modified to print more drive info on init
+ *                        Minor other changes
+ *                        Fix errors on CDROMSTOP (If you have a "Dolphin",
+ *                          you must define IHAVEADOLPHIN)
+ *                        Added identifier so new Sanyo CD-changer works
+ *                        Better detection if door locking isn't supported
+ *
+ * 4.06  Dec 17, 1997  -- fixed endless "tray open" messages  -ml
+ * 4.07  Dec 17, 1997  -- fallback to set pc->stat on "tray open"
+ * 4.08  Dec 18, 1997  -- spew less noise when tray is empty
+ *                     -- fix speed display for ACER 24X, 18X
+ * 4.09  Jan 04, 1998  -- fix handling of the last block so we return
+ *                         an end of file instead of an I/O error (Gadi)
+ * 4.10  Jan 24, 1998  -- fixed a bug so now changers can change to a new
+ *                         slot when there is no disc in the current slot.
+ *                     -- Fixed a memory leak where info->changer_info was
+ *                         malloc'ed but never free'd when closing the device.
+ *                     -- Cleaned up the global namespace a bit by making more
+ *                         functions static that should already have been.
+ * 4.11  Mar 12, 1998  -- Added support for the CDROM_SELECT_SPEED ioctl
+ *                         based on a patch for 2.0.33 by Jelle Foks 
+ *                         <jelle@scintilla.utwente.nl>, a patch for 2.0.33
+ *                         by Toni Giorgino <toni@pcape2.pi.infn.it>, the SCSI
+ *                         version, and my own efforts.  -erik
+ *                     -- Fixed a stupid bug which egcs was kind enough to
+ *                         inform me of where "Illegal mode for this track"
+ *                         was never returned due to a comparison on data
+ *                         types of limited range.
+ * 4.12  Mar 29, 1998  -- Fixed bug in CDROM_SELECT_SPEED so write speed is 
+ *                         now set ionly for CD-R and CD-RW drives.  I had 
+ *                         removed this support because it produced errors.
+ *                         It produced errors _only_ for non-writers. duh.
+ * 4.13  May 05, 1998  -- Suppress useless "in progress of becoming ready"
+ *                         messages, since this is not an error.
+ *                     -- Change error messages to be const
+ *                     -- Remove a "\t" which looks ugly in the syslogs
+ * 4.14  July 17, 1998 -- Change to pointing to .ps version of ATAPI spec
+ *                         since the .pdf version doesn't seem to work...
+ *                     -- Updated the TODO list to something more current.
+ *
+ * 4.15  Aug 25, 1998  -- Updated ide-cd.h to respect mechine endianess, 
+ *                         patch thanks to "Eddie C. Dost" <ecd@skynet.be>
+ *
+ * 4.50  Oct 19, 1998  -- New maintainers!
+ *                         Jens Axboe <axboe@image.dk>
+ *                         Chris Zwilling <chris@cloudnet.com>
+ *
+ * 4.51  Dec 23, 1998  -- Jens Axboe <axboe@image.dk>
+ *                      - ide_cdrom_reset enabled since the ide subsystem
+ *                         handles resets fine now. <axboe@image.dk>
+ *                      - Transfer size fix for Samsung CD-ROMs, thanks to
+ *                        "Ville Hallik" <ville.hallik@mail.ee>.
+ *                      - other minor stuff.
+ *
+ * 4.52  Jan 19, 1999  -- Jens Axboe <axboe@image.dk>
+ *                      - Detect DVD-ROM/RAM drives
+ *
+ * 4.53  Feb 22, 1999   - Include other model Samsung and one Goldstar
+ *                         drive in transfer size limit.
+ *                      - Fix the I/O error when doing eject without a medium
+ *                         loaded on some drives.
+ *                      - CDROMREADMODE2 is now implemented through
+ *                         CDROMREADRAW, since many drives don't support
+ *                         MODE2 (even though ATAPI 2.6 says they must).
+ *                      - Added ignore parameter to ide-cd (as a module), eg
+ *                         	insmod ide-cd ignore='hda hdb'
+ *                         Useful when using ide-cd in conjunction with
+ *                         ide-scsi. TODO: non-modular way of doing the
+ *                         same.
+ *
+ * 4.54  Aug 5, 1999	- Support for MMC2 class commands through the generic
+ *			  packet interface to cdrom.c.
+ *			- Unified audio ioctl support, most of it.
+ *			- cleaned up various deprecated verify_area().
+ *			- Added ide_cdrom_packet() as the interface for
+ *			  the Uniform generic_packet().
+ *			- bunch of other stuff, will fill in logs later.
+ *			- report 1 slot for non-changers, like the other
+ *			  cd-rom drivers. don't report select disc for
+ *			  non-changers as well.
+ *			- mask out audio playing, if the device can't do it.
+ *
+ * 4.55  Sep 1, 1999	- Eliminated the rest of the audio ioctls, except
+ *			  for CDROMREADTOC[ENTRY|HEADER]. Some of the drivers
+ *			  use this independently of the actual audio handling.
+ *			  They will disappear later when I get the time to
+ *			  do it cleanly.
+ *			- Minimize the TOC reading - only do it when we
+ *			  know a media change has occurred.
+ *			- Moved all the CDROMREADx ioctls to the Uniform layer.
+ *			- Heiko Eissfeldt <heiko@colossus.escape.de> supplied
+ *			  some fixes for CDI.
+ *			- CD-ROM leaving door locked fix from Andries
+ *			  Brouwer <Andries.Brouwer@cwi.nl>
+ *			- Erik Andersen <andersen@xmission.com> unified
+ *			  commands across the various drivers and how
+ *			  sense errors are handled.
+ *
+ * 4.56  Sep 12, 1999	- Removed changer support - it is now in the
+ *			  Uniform layer.
+ *			- Added partition based multisession handling.
+ *			- Mode sense and mode select moved to the
+ *			  Uniform layer.
+ *			- Fixed a problem with WPI CDS-32X drive - it
+ *			  failed the capabilities 
+ *
+ * 4.57  Apr 7, 2000	- Fixed sense reporting.
+ *			- Fixed possible oops in ide_cdrom_get_last_session()
+ *			- Fix locking mania and make ide_cdrom_reset relock
+ *			- Stop spewing errors to log when magicdev polls with
+ *			  TEST_UNIT_READY on some drives.
+ *			- Various fixes from Tobias Ringstrom:
+ *			  tray if it was locked prior to the reset.
+ *			  - cdrom_read_capacity returns one frame too little.
+ *			  - Fix real capacity reporting.
+ *
+ * 4.58  May 1, 2000	- Clean up ACER50 stuff.
+ *			- Fix small problem with ide_cdrom_capacity
+ *
+ * 4.59  Aug 11, 2000	- Fix changer problem in cdrom_read_toc, we weren't
+ *			  correctly sensing a disc change.
+ *			- Rearranged some code
+ *			- Use extended sense on drives that support it for
+ *			  correctly reporting tray status -- from
+ *			  Michael D Johnson <johnsom@orst.edu>
+ * 4.60  Dec 17, 2003	- Add mt rainier support
+ *			- Bump timeout for packet commands, matches sr
+ *			- Odd stuff
+ * 4.61  Jan 22, 2004	- support hardware sector sizes other than 2kB,
+ *			  Pascal Schmidt <der.eremit@email.de>
+ *
+ *************************************************************************/
+ 
+#define IDECD_VERSION "4.61"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/cdrom.h>
+#include <linux/ide.h>
+#include <linux/completion.h>
+
+#include <scsi/scsi.h>	/* For SCSI -> ATAPI command conversion */
+
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include "ide-cd.h"
+
+static DECLARE_MUTEX(idecd_ref_sem);
+
+#define to_ide_cd(obj) container_of(obj, struct cdrom_info, kref) 
+
+#define ide_cd_g(disk) \
+	container_of((disk)->private_data, struct cdrom_info, driver)
+
+static struct cdrom_info *ide_cd_get(struct gendisk *disk)
+{
+	struct cdrom_info *cd = NULL;
+
+	down(&idecd_ref_sem);
+	cd = ide_cd_g(disk);
+	if (cd)
+		kref_get(&cd->kref);
+	up(&idecd_ref_sem);
+	return cd;
+}
+
+static void ide_cd_release(struct kref *);
+
+static void ide_cd_put(struct cdrom_info *cd)
+{
+	down(&idecd_ref_sem);
+	kref_put(&cd->kref, ide_cd_release);
+	up(&idecd_ref_sem);
+}
+
+/****************************************************************************
+ * Generic packet command support and error handling routines.
+ */
+
+/* Mark that we've seen a media change, and invalidate our internal
+   buffers. */
+static void cdrom_saw_media_change (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	
+	CDROM_STATE_FLAGS (drive)->media_changed = 1;
+	CDROM_STATE_FLAGS (drive)->toc_valid = 0;
+	info->nsectors_buffered = 0;
+}
+
+static int cdrom_log_sense(ide_drive_t *drive, struct request *rq,
+			   struct request_sense *sense)
+{
+	int log = 0;
+
+	if (!sense || !rq || (rq->flags & REQ_QUIET))
+		return 0;
+
+	switch (sense->sense_key) {
+		case NO_SENSE: case RECOVERED_ERROR:
+			break;
+		case NOT_READY:
+			/*
+			 * don't care about tray state messages for
+			 * e.g. capacity commands or in-progress or
+			 * becoming ready
+			 */
+			if (sense->asc == 0x3a || sense->asc == 0x04)
+				break;
+			log = 1;
+			break;
+		case ILLEGAL_REQUEST:
+			/*
+			 * don't log START_STOP unit with LoEj set, since
+			 * we cannot reliably check if drive can auto-close
+			 */
+			if (rq->cmd[0] == GPCMD_START_STOP_UNIT && sense->asc == 0x24)
+				log = 0;
+			break;
+		case UNIT_ATTENTION:
+			/*
+			 * Make good and sure we've seen this potential media
+			 * change. Some drives (i.e. Creative) fail to present
+			 * the correct sense key in the error register.
+			 */
+			cdrom_saw_media_change(drive);
+			break;
+		default:
+			log = 1;
+			break;
+	}
+	return log;
+}
+
+static
+void cdrom_analyze_sense_data(ide_drive_t *drive,
+			      struct request *failed_command,
+			      struct request_sense *sense)
+{
+	if (!cdrom_log_sense(drive, failed_command, sense))
+		return;
+
+	/*
+	 * If a read toc is executed for a CD-R or CD-RW medium where
+	 * the first toc has not been recorded yet, it will fail with
+	 * 05/24/00 (which is a confusing error)
+	 */
+	if (failed_command && failed_command->cmd[0] == GPCMD_READ_TOC_PMA_ATIP)
+		if (sense->sense_key == 0x05 && sense->asc == 0x24)
+			return;
+
+#if VERBOSE_IDE_CD_ERRORS
+	{
+		int i;
+		const char *s = "bad sense key!";
+		char buf[80];
+
+		printk ("ATAPI device %s:\n", drive->name);
+		if (sense->error_code==0x70)
+			printk("  Error: ");
+		else if (sense->error_code==0x71)
+			printk("  Deferred Error: ");
+		else if (sense->error_code == 0x7f)
+			printk("  Vendor-specific Error: ");
+		else
+			printk("  Unknown Error Type: ");
+
+		if (sense->sense_key < ARY_LEN(sense_key_texts))
+			s = sense_key_texts[sense->sense_key];
+
+		printk("%s -- (Sense key=0x%02x)\n", s, sense->sense_key);
+
+		if (sense->asc == 0x40) {
+			sprintf(buf, "Diagnostic failure on component 0x%02x",
+				 sense->ascq);
+			s = buf;
+		} else {
+			int lo = 0, mid, hi = ARY_LEN(sense_data_texts);
+			unsigned long key = (sense->sense_key << 16);
+			key |= (sense->asc << 8);
+			if (!(sense->ascq >= 0x80 && sense->ascq <= 0xdd))
+				key |= sense->ascq;
+			s = NULL;
+
+			while (hi > lo) {
+				mid = (lo + hi) / 2;
+				if (sense_data_texts[mid].asc_ascq == key ||
+				    sense_data_texts[mid].asc_ascq == (0xff0000|key)) {
+					s = sense_data_texts[mid].text;
+					break;
+				}
+				else if (sense_data_texts[mid].asc_ascq > key)
+					hi = mid;
+				else
+					lo = mid+1;
+			}
+		}
+
+		if (s == NULL) {
+			if (sense->asc > 0x80)
+				s = "(vendor-specific error)";
+			else
+				s = "(reserved error code)";
+		}
+
+		printk(KERN_ERR "  %s -- (asc=0x%02x, ascq=0x%02x)\n",
+			s, sense->asc, sense->ascq);
+
+		if (failed_command != NULL) {
+
+			int lo=0, mid, hi= ARY_LEN (packet_command_texts);
+			s = NULL;
+
+			while (hi > lo) {
+				mid = (lo + hi) / 2;
+				if (packet_command_texts[mid].packet_command ==
+				    failed_command->cmd[0]) {
+					s = packet_command_texts[mid].text;
+					break;
+				}
+				if (packet_command_texts[mid].packet_command >
+				    failed_command->cmd[0])
+					hi = mid;
+				else
+					lo = mid+1;
+			}
+
+			printk (KERN_ERR "  The failed \"%s\" packet command was: \n  \"", s);
+			for (i=0; i<sizeof (failed_command->cmd); i++)
+				printk ("%02x ", failed_command->cmd[i]);
+			printk ("\"\n");
+		}
+
+		/* The SKSV bit specifies validity of the sense_key_specific
+		 * in the next two commands. It is bit 7 of the first byte.
+		 * In the case of NOT_READY, if SKSV is set the drive can
+		 * give us nice ETA readings.
+		 */
+		if (sense->sense_key == NOT_READY && (sense->sks[0] & 0x80)) {
+			int progress = (sense->sks[1] << 8 | sense->sks[2]) * 100;
+			printk(KERN_ERR "  Command is %02d%% complete\n", progress / 0xffff);
+
+		}
+
+		if (sense->sense_key == ILLEGAL_REQUEST &&
+		    (sense->sks[0] & 0x80) != 0) {
+			printk(KERN_ERR "  Error in %s byte %d",
+				(sense->sks[0] & 0x40) != 0 ?
+				"command packet" : "command data",
+				(sense->sks[1] << 8) + sense->sks[2]);
+
+			if ((sense->sks[0] & 0x40) != 0)
+				printk (" bit %d", sense->sks[0] & 0x07);
+
+			printk ("\n");
+		}
+	}
+
+#else /* not VERBOSE_IDE_CD_ERRORS */
+
+	/* Suppress printing unit attention and `in progress of becoming ready'
+	   errors when we're not being verbose. */
+
+	if (sense->sense_key == UNIT_ATTENTION ||
+	    (sense->sense_key == NOT_READY && (sense->asc == 4 ||
+						sense->asc == 0x3a)))
+		return;
+
+	printk(KERN_ERR "%s: error code: 0x%02x  sense_key: 0x%02x  asc: 0x%02x  ascq: 0x%02x\n",
+		drive->name,
+		sense->error_code, sense->sense_key,
+		sense->asc, sense->ascq);
+#endif /* not VERBOSE_IDE_CD_ERRORS */
+}
+
+/*
+ * Initialize a ide-cd packet command request
+ */
+static void cdrom_prepare_request(ide_drive_t *drive, struct request *rq)
+{
+	struct cdrom_info *cd = drive->driver_data;
+
+	ide_init_drive_cmd(rq);
+	rq->flags = REQ_PC;
+	rq->rq_disk = cd->disk;
+}
+
+static void cdrom_queue_request_sense(ide_drive_t *drive, void *sense,
+				      struct request *failed_command)
+{
+	struct cdrom_info *info		= drive->driver_data;
+	struct request *rq		= &info->request_sense_request;
+
+	if (sense == NULL)
+		sense = &info->sense_data;
+
+	/* stuff the sense request in front of our current request */
+	cdrom_prepare_request(drive, rq);
+
+	rq->data = sense;
+	rq->cmd[0] = GPCMD_REQUEST_SENSE;
+	rq->cmd[4] = rq->data_len = 18;
+
+	rq->flags = REQ_SENSE;
+
+	/* NOTE! Save the failed command in "rq->buffer" */
+	rq->buffer = (void *) failed_command;
+
+	(void) ide_do_drive_cmd(drive, rq, ide_preempt);
+}
+
+static void cdrom_end_request (ide_drive_t *drive, int uptodate)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	int nsectors = rq->hard_cur_sectors;
+
+	if ((rq->flags & REQ_SENSE) && uptodate) {
+		/*
+		 * For REQ_SENSE, "rq->buffer" points to the original failed
+		 * request
+		 */
+		struct request *failed = (struct request *) rq->buffer;
+		struct cdrom_info *info = drive->driver_data;
+		void *sense = &info->sense_data;
+		unsigned long flags;
+
+		if (failed) {
+			if (failed->sense) {
+				sense = failed->sense;
+				failed->sense_len = rq->sense_len;
+			}
+
+			/*
+			 * now end failed request
+			 */
+			spin_lock_irqsave(&ide_lock, flags);
+			end_that_request_chunk(failed, 0, failed->data_len);
+			end_that_request_last(failed);
+			spin_unlock_irqrestore(&ide_lock, flags);
+		}
+
+		cdrom_analyze_sense_data(drive, failed, sense);
+	}
+
+	if (!rq->current_nr_sectors && blk_fs_request(rq))
+		uptodate = 1;
+	/* make sure it's fully ended */
+	if (blk_pc_request(rq))
+		nsectors = (rq->data_len + 511) >> 9;
+	if (!nsectors)
+		nsectors = 1;
+
+	ide_end_request(drive, uptodate, nsectors);
+}
+
+/* Returns 0 if the request should be continued.
+   Returns 1 if the request was ended. */
+static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	int stat, err, sense_key;
+	
+	/* Check for errors. */
+	stat = HWIF(drive)->INB(IDE_STATUS_REG);
+	if (stat_ret)
+		*stat_ret = stat;
+
+	if (OK_STAT(stat, good_stat, BAD_R_STAT))
+		return 0;
+
+	/* Get the IDE error register. */
+	err = HWIF(drive)->INB(IDE_ERROR_REG);
+	sense_key = err >> 4;
+
+	if (rq == NULL) {
+		printk("%s: missing rq in cdrom_decode_status\n", drive->name);
+		return 1;
+	}
+
+	if (rq->flags & REQ_SENSE) {
+		/* We got an error trying to get sense info
+		   from the drive (probably while trying
+		   to recover from a former error).  Just give up. */
+
+		rq->flags |= REQ_FAILED;
+		cdrom_end_request(drive, 0);
+		ide_error(drive, "request sense failure", stat);
+		return 1;
+
+	} else if (rq->flags & (REQ_PC | REQ_BLOCK_PC)) {
+		/* All other functions, except for READ. */
+		unsigned long flags;
+
+		/*
+		 * if we have an error, pass back CHECK_CONDITION as the
+		 * scsi status byte
+		 */
+		if ((rq->flags & REQ_BLOCK_PC) && !rq->errors)
+			rq->errors = SAM_STAT_CHECK_CONDITION;
+
+		/* Check for tray open. */
+		if (sense_key == NOT_READY) {
+			cdrom_saw_media_change (drive);
+		} else if (sense_key == UNIT_ATTENTION) {
+			/* Check for media change. */
+			cdrom_saw_media_change (drive);
+			/*printk("%s: media changed\n",drive->name);*/
+			return 0;
+		} else if (!(rq->flags & REQ_QUIET)) {
+			/* Otherwise, print an error. */
+			ide_dump_status(drive, "packet command error", stat);
+		}
+		
+		rq->flags |= REQ_FAILED;
+
+		/*
+		 * instead of playing games with moving completions around,
+		 * remove failed request completely and end it when the
+		 * request sense has completed
+		 */
+		if (stat & ERR_STAT) {
+			spin_lock_irqsave(&ide_lock, flags);
+			blkdev_dequeue_request(rq);
+			HWGROUP(drive)->rq = NULL;
+			spin_unlock_irqrestore(&ide_lock, flags);
+
+			cdrom_queue_request_sense(drive, rq->sense, rq);
+		} else
+			cdrom_end_request(drive, 0);
+
+	} else if (blk_fs_request(rq)) {
+		int do_end_request = 0;
+
+		/* Handle errors from READ and WRITE requests. */
+
+		if (blk_noretry_request(rq))
+			do_end_request = 1;
+
+		if (sense_key == NOT_READY) {
+			/* Tray open. */
+			if (rq_data_dir(rq) == READ) {
+				cdrom_saw_media_change (drive);
+
+				/* Fail the request. */
+				printk ("%s: tray open\n", drive->name);
+				do_end_request = 1;
+			} else {
+				struct cdrom_info *info = drive->driver_data;
+
+				/* allow the drive 5 seconds to recover, some
+				 * devices will return this error while flushing
+				 * data from cache */
+				if (!rq->errors)
+					info->write_timeout = jiffies + ATAPI_WAIT_WRITE_BUSY;
+				rq->errors = 1;
+				if (time_after(jiffies, info->write_timeout))
+					do_end_request = 1;
+				else {
+					unsigned long flags;
+
+					/*
+					 * take a breather relying on the
+					 * unplug timer to kick us again
+					 */
+					spin_lock_irqsave(&ide_lock, flags);
+					blk_plug_device(drive->queue);
+					spin_unlock_irqrestore(&ide_lock,flags);
+					return 1;
+				}
+			}
+		} else if (sense_key == UNIT_ATTENTION) {
+			/* Media change. */
+			cdrom_saw_media_change (drive);
+
+			/* Arrange to retry the request.
+			   But be sure to give up if we've retried
+			   too many times. */
+			if (++rq->errors > ERROR_MAX)
+				do_end_request = 1;
+		} else if (sense_key == ILLEGAL_REQUEST ||
+			   sense_key == DATA_PROTECT) {
+			/* No point in retrying after an illegal
+			   request or data protect error.*/
+			ide_dump_status (drive, "command error", stat);
+			do_end_request = 1;
+		} else if (sense_key == MEDIUM_ERROR) {
+			/* No point in re-trying a zillion times on a bad 
+			 * sector...  If we got here the error is not correctable */
+			ide_dump_status (drive, "media error (bad sector)", stat);
+			do_end_request = 1;
+		} else if (sense_key == BLANK_CHECK) {
+			/* Disk appears blank ?? */
+			ide_dump_status (drive, "media error (blank)", stat);
+			do_end_request = 1;
+		} else if ((err & ~ABRT_ERR) != 0) {
+			/* Go to the default handler
+			   for other errors. */
+			ide_error(drive, "cdrom_decode_status", stat);
+			return 1;
+		} else if ((++rq->errors > ERROR_MAX)) {
+			/* We've racked up too many retries.  Abort. */
+			do_end_request = 1;
+		}
+
+		if (do_end_request)
+			cdrom_end_request(drive, 0);
+
+		/* If we got a CHECK_CONDITION status,
+		   queue a request sense command. */
+		if ((stat & ERR_STAT) != 0)
+			cdrom_queue_request_sense(drive, NULL, NULL);
+	} else {
+		blk_dump_rq_flags(rq, "ide-cd: bad rq");
+		cdrom_end_request(drive, 0);
+	}
+
+	/* Retry, or handle the next request. */
+	return 1;
+}
+
+static int cdrom_timer_expiry(ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	unsigned long wait = 0;
+
+	/*
+	 * Some commands are *slow* and normally take a long time to
+	 * complete. Usually we can use the ATAPI "disconnect" to bypass
+	 * this, but not all commands/drives support that. Let
+	 * ide_timer_expiry keep polling us for these.
+	 */
+	switch (rq->cmd[0]) {
+		case GPCMD_BLANK:
+		case GPCMD_FORMAT_UNIT:
+		case GPCMD_RESERVE_RZONE_TRACK:
+		case GPCMD_CLOSE_TRACK:
+		case GPCMD_FLUSH_CACHE:
+			wait = ATAPI_WAIT_PC;
+			break;
+		default:
+			if (!(rq->flags & REQ_QUIET))
+				printk(KERN_INFO "ide-cd: cmd 0x%x timed out\n", rq->cmd[0]);
+			wait = 0;
+			break;
+	}
+	return wait;
+}
+
+/* Set up the device registers for transferring a packet command on DEV,
+   expecting to later transfer XFERLEN bytes.  HANDLER is the routine
+   which actually transfers the command to the drive.  If this is a
+   drq_interrupt device, this routine will arrange for HANDLER to be
+   called when the interrupt from the drive arrives.  Otherwise, HANDLER
+   will be called immediately after the drive is prepared for the transfer. */
+
+static ide_startstop_t cdrom_start_packet_command(ide_drive_t *drive,
+						  int xferlen,
+						  ide_handler_t *handler)
+{
+	ide_startstop_t startstop;
+	struct cdrom_info *info = drive->driver_data;
+	ide_hwif_t *hwif = drive->hwif;
+
+	/* Wait for the controller to be idle. */
+	if (ide_wait_stat(&startstop, drive, 0, BUSY_STAT, WAIT_READY))
+		return startstop;
+
+	if (info->dma)
+		info->dma = !hwif->dma_setup(drive);
+
+	/* Set up the controller registers. */
+	/* FIXME: for Virtual DMA we must check harder */
+	HWIF(drive)->OUTB(info->dma, IDE_FEATURE_REG);
+	HWIF(drive)->OUTB(0, IDE_IREASON_REG);
+	HWIF(drive)->OUTB(0, IDE_SECTOR_REG);
+
+	HWIF(drive)->OUTB(xferlen & 0xff, IDE_BCOUNTL_REG);
+	HWIF(drive)->OUTB(xferlen >> 8  , IDE_BCOUNTH_REG);
+	if (IDE_CONTROL_REG)
+		HWIF(drive)->OUTB(drive->ctl, IDE_CONTROL_REG);
+ 
+	if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
+		/* packet command */
+		ide_execute_command(drive, WIN_PACKETCMD, handler, ATAPI_WAIT_PC, cdrom_timer_expiry);
+		return ide_started;
+	} else {
+		unsigned long flags;
+
+		/* packet command */
+		spin_lock_irqsave(&ide_lock, flags);
+		hwif->OUTBSYNC(drive, WIN_PACKETCMD, IDE_COMMAND_REG);
+		ndelay(400);
+		spin_unlock_irqrestore(&ide_lock, flags);
+
+		return (*handler) (drive);
+	}
+}
+
+/* Send a packet command to DRIVE described by CMD_BUF and CMD_LEN.
+   The device registers must have already been prepared
+   by cdrom_start_packet_command.
+   HANDLER is the interrupt handler to call when the command completes
+   or there's data ready. */
+/*
+ * changed 5 parameters to 3 for dvd-ram
+ * struct packet_command *pc; now packet_command_t *pc;
+ */
+#define ATAPI_MIN_CDB_BYTES 12
+static ide_startstop_t cdrom_transfer_packet_command (ide_drive_t *drive,
+					  struct request *rq,
+					  ide_handler_t *handler)
+{
+	ide_hwif_t *hwif = drive->hwif;
+	int cmd_len;
+	struct cdrom_info *info = drive->driver_data;
+	ide_startstop_t startstop;
+
+	if (CDROM_CONFIG_FLAGS(drive)->drq_interrupt) {
+		/* Here we should have been called after receiving an interrupt
+		   from the device.  DRQ should how be set. */
+
+		/* Check for errors. */
+		if (cdrom_decode_status(drive, DRQ_STAT, NULL))
+			return ide_stopped;
+	} else {
+		/* Otherwise, we must wait for DRQ to get set. */
+		if (ide_wait_stat(&startstop, drive, DRQ_STAT,
+				BUSY_STAT, WAIT_READY))
+			return startstop;
+	}
+
+	/* Arm the interrupt handler. */
+	ide_set_handler(drive, handler, rq->timeout, cdrom_timer_expiry);
+
+	/* ATAPI commands get padded out to 12 bytes minimum */
+	cmd_len = COMMAND_SIZE(rq->cmd[0]);
+	if (cmd_len < ATAPI_MIN_CDB_BYTES)
+		cmd_len = ATAPI_MIN_CDB_BYTES;
+
+	/* Send the command to the device. */
+	HWIF(drive)->atapi_output_bytes(drive, rq->cmd, cmd_len);
+
+	/* Start the DMA if need be */
+	if (info->dma)
+		hwif->dma_start(drive);
+
+	return ide_started;
+}
+
+/****************************************************************************
+ * Block read functions.
+ */
+
+/*
+ * Buffer up to SECTORS_TO_TRANSFER sectors from the drive in our sector
+ * buffer.  Once the first sector is added, any subsequent sectors are
+ * assumed to be continuous (until the buffer is cleared).  For the first
+ * sector added, SECTOR is its sector number.  (SECTOR is then ignored until
+ * the buffer is cleared.)
+ */
+static void cdrom_buffer_sectors (ide_drive_t *drive, unsigned long sector,
+                                  int sectors_to_transfer)
+{
+	struct cdrom_info *info = drive->driver_data;
+
+	/* Number of sectors to read into the buffer. */
+	int sectors_to_buffer = min_t(int, sectors_to_transfer,
+				     (SECTOR_BUFFER_SIZE >> SECTOR_BITS) -
+				       info->nsectors_buffered);
+
+	char *dest;
+
+	/* If we couldn't get a buffer, don't try to buffer anything... */
+	if (info->buffer == NULL)
+		sectors_to_buffer = 0;
+
+	/* If this is the first sector in the buffer, remember its number. */
+	if (info->nsectors_buffered == 0)
+		info->sector_buffered = sector;
+
+	/* Read the data into the buffer. */
+	dest = info->buffer + info->nsectors_buffered * SECTOR_SIZE;
+	while (sectors_to_buffer > 0) {
+		HWIF(drive)->atapi_input_bytes(drive, dest, SECTOR_SIZE);
+		--sectors_to_buffer;
+		--sectors_to_transfer;
+		++info->nsectors_buffered;
+		dest += SECTOR_SIZE;
+	}
+
+	/* Throw away any remaining data. */
+	while (sectors_to_transfer > 0) {
+		static char dum[SECTOR_SIZE];
+		HWIF(drive)->atapi_input_bytes(drive, dum, sizeof (dum));
+		--sectors_to_transfer;
+	}
+}
+
+/*
+ * Check the contents of the interrupt reason register from the cdrom
+ * and attempt to recover if there are problems.  Returns  0 if everything's
+ * ok; nonzero if the request has been terminated.
+ */
+static inline
+int cdrom_read_check_ireason (ide_drive_t *drive, int len, int ireason)
+{
+	if (ireason == 2)
+		return 0;
+	else if (ireason == 0) {
+		/* Whoops... The drive is expecting to receive data from us! */
+		printk(KERN_ERR "%s: read_intr: Drive wants to transfer data the "
+						"wrong way!\n", drive->name);
+
+		/* Throw some data at the drive so it doesn't hang
+		   and quit this request. */
+		while (len > 0) {
+			int dum = 0;
+			HWIF(drive)->atapi_output_bytes(drive, &dum, sizeof (dum));
+			len -= sizeof (dum);
+		}
+	} else  if (ireason == 1) {
+		/* Some drives (ASUS) seem to tell us that status
+		 * info is available. just get it and ignore.
+		 */
+		(void) HWIF(drive)->INB(IDE_STATUS_REG);
+		return 0;
+	} else {
+		/* Drive wants a command packet, or invalid ireason... */
+		printk(KERN_ERR "%s: read_intr: bad interrupt reason %x\n", drive->name,
+								ireason);
+	}
+
+	cdrom_end_request(drive, 0);
+	return -1;
+}
+
+/*
+ * Interrupt routine.  Called when a read request has completed.
+ */
+static ide_startstop_t cdrom_read_intr (ide_drive_t *drive)
+{
+	int stat;
+	int ireason, len, sectors_to_transfer, nskip;
+	struct cdrom_info *info = drive->driver_data;
+	u8 lowcyl = 0, highcyl = 0;
+	int dma = info->dma, dma_error = 0;
+
+	struct request *rq = HWGROUP(drive)->rq;
+
+	/*
+	 * handle dma case
+	 */
+	if (dma) {
+		info->dma = 0;
+		if ((dma_error = HWIF(drive)->ide_dma_end(drive)))
+			__ide_dma_off(drive);
+	}
+
+	if (cdrom_decode_status(drive, 0, &stat))
+		return ide_stopped;
+
+	if (dma) {
+		if (!dma_error) {
+			ide_end_request(drive, 1, rq->nr_sectors);
+			return ide_stopped;
+		} else
+			return ide_error(drive, "dma error", stat);
+	}
+
+	/* Read the interrupt reason and the transfer length. */
+	ireason = HWIF(drive)->INB(IDE_IREASON_REG) & 0x3;
+	lowcyl  = HWIF(drive)->INB(IDE_BCOUNTL_REG);
+	highcyl = HWIF(drive)->INB(IDE_BCOUNTH_REG);
+
+	len = lowcyl + (256 * highcyl);
+
+	/* If DRQ is clear, the command has completed. */
+	if ((stat & DRQ_STAT) == 0) {
+		/* If we're not done filling the current buffer, complain.
+		   Otherwise, complete the command normally. */
+		if (rq->current_nr_sectors > 0) {
+			printk (KERN_ERR "%s: cdrom_read_intr: data underrun (%d blocks)\n",
+				drive->name, rq->current_nr_sectors);
+			rq->flags |= REQ_FAILED;
+			cdrom_end_request(drive, 0);
+		} else
+			cdrom_end_request(drive, 1);
+		return ide_stopped;
+	}
+
+	/* Check that the drive is expecting to do the same thing we are. */
+	if (cdrom_read_check_ireason (drive, len, ireason))
+		return ide_stopped;
+
+	/* Assume that the drive will always provide data in multiples
+	   of at least SECTOR_SIZE, as it gets hairy to keep track
+	   of the transfers otherwise. */
+	if ((len % SECTOR_SIZE) != 0) {
+		printk (KERN_ERR "%s: cdrom_read_intr: Bad transfer size %d\n",
+			drive->name, len);
+		if (CDROM_CONFIG_FLAGS(drive)->limit_nframes)
+			printk (KERN_ERR "  This drive is not supported by this version of the driver\n");
+		else {
+			printk (KERN_ERR "  Trying to limit transfer sizes\n");
+			CDROM_CONFIG_FLAGS(drive)->limit_nframes = 1;
+		}
+		cdrom_end_request(drive, 0);
+		return ide_stopped;
+	}
+
+	/* The number of sectors we need to read from the drive. */
+	sectors_to_transfer = len / SECTOR_SIZE;
+
+	/* First, figure out if we need to bit-bucket
+	   any of the leading sectors. */
+	nskip = min_t(int, rq->current_nr_sectors - bio_cur_sectors(rq->bio), sectors_to_transfer);
+
+	while (nskip > 0) {
+		/* We need to throw away a sector. */
+		static char dum[SECTOR_SIZE];
+		HWIF(drive)->atapi_input_bytes(drive, dum, sizeof (dum));
+
+		--rq->current_nr_sectors;
+		--nskip;
+		--sectors_to_transfer;
+	}
+
+	/* Now loop while we still have data to read from the drive. */
+	while (sectors_to_transfer > 0) {
+		int this_transfer;
+
+		/* If we've filled the present buffer but there's another
+		   chained buffer after it, move on. */
+		if (rq->current_nr_sectors == 0 && rq->nr_sectors)
+			cdrom_end_request(drive, 1);
+
+		/* If the buffers are full, cache the rest of the data in our
+		   internal buffer. */
+		if (rq->current_nr_sectors == 0) {
+			cdrom_buffer_sectors(drive, rq->sector, sectors_to_transfer);
+			sectors_to_transfer = 0;
+		} else {
+			/* Transfer data to the buffers.
+			   Figure out how many sectors we can transfer
+			   to the current buffer. */
+			this_transfer = min_t(int, sectors_to_transfer,
+					     rq->current_nr_sectors);
+
+			/* Read this_transfer sectors
+			   into the current buffer. */
+			while (this_transfer > 0) {
+				HWIF(drive)->atapi_input_bytes(drive, rq->buffer, SECTOR_SIZE);
+				rq->buffer += SECTOR_SIZE;
+				--rq->nr_sectors;
+				--rq->current_nr_sectors;
+				++rq->sector;
+				--this_transfer;
+				--sectors_to_transfer;
+			}
+		}
+	}
+
+	/* Done moving data!  Wait for another interrupt. */
+	ide_set_handler(drive, &cdrom_read_intr, ATAPI_WAIT_PC, NULL);
+	return ide_started;
+}
+
+/*
+ * Try to satisfy some of the current read request from our cached data.
+ * Returns nonzero if the request has been completed, zero otherwise.
+ */
+static int cdrom_read_from_buffer (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct request *rq = HWGROUP(drive)->rq;
+	unsigned short sectors_per_frame;
+
+	sectors_per_frame = queue_hardsect_size(drive->queue) >> SECTOR_BITS;
+
+	/* Can't do anything if there's no buffer. */
+	if (info->buffer == NULL) return 0;
+
+	/* Loop while this request needs data and the next block is present
+	   in our cache. */
+	while (rq->nr_sectors > 0 &&
+	       rq->sector >= info->sector_buffered &&
+	       rq->sector < info->sector_buffered + info->nsectors_buffered) {
+		if (rq->current_nr_sectors == 0)
+			cdrom_end_request(drive, 1);
+
+		memcpy (rq->buffer,
+			info->buffer +
+			(rq->sector - info->sector_buffered) * SECTOR_SIZE,
+			SECTOR_SIZE);
+		rq->buffer += SECTOR_SIZE;
+		--rq->current_nr_sectors;
+		--rq->nr_sectors;
+		++rq->sector;
+	}
+
+	/* If we've satisfied the current request,
+	   terminate it successfully. */
+	if (rq->nr_sectors == 0) {
+		cdrom_end_request(drive, 1);
+		return -1;
+	}
+
+	/* Move on to the next buffer if needed. */
+	if (rq->current_nr_sectors == 0)
+		cdrom_end_request(drive, 1);
+
+	/* If this condition does not hold, then the kluge i use to
+	   represent the number of sectors to skip at the start of a transfer
+	   will fail.  I think that this will never happen, but let's be
+	   paranoid and check. */
+	if (rq->current_nr_sectors < bio_cur_sectors(rq->bio) &&
+	    (rq->sector & (sectors_per_frame - 1))) {
+		printk(KERN_ERR "%s: cdrom_read_from_buffer: buffer botch (%ld)\n",
+			drive->name, (long)rq->sector);
+		cdrom_end_request(drive, 0);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Routine to send a read packet command to the drive.
+ * This is usually called directly from cdrom_start_read.
+ * However, for drq_interrupt devices, it is called from an interrupt
+ * when the drive is ready to accept the command.
+ */
+static ide_startstop_t cdrom_start_read_continuation (ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	unsigned short sectors_per_frame;
+	int nskip;
+
+	sectors_per_frame = queue_hardsect_size(drive->queue) >> SECTOR_BITS;
+
+	/* If the requested sector doesn't start on a cdrom block boundary,
+	   we must adjust the start of the transfer so that it does,
+	   and remember to skip the first few sectors.
+	   If the CURRENT_NR_SECTORS field is larger than the size
+	   of the buffer, it will mean that we're to skip a number
+	   of sectors equal to the amount by which CURRENT_NR_SECTORS
+	   is larger than the buffer size. */
+	nskip = rq->sector & (sectors_per_frame - 1);
+	if (nskip > 0) {
+		/* Sanity check... */
+		if (rq->current_nr_sectors != bio_cur_sectors(rq->bio) &&
+			(rq->sector & (sectors_per_frame - 1))) {
+			printk(KERN_ERR "%s: cdrom_start_read_continuation: buffer botch (%u)\n",
+				drive->name, rq->current_nr_sectors);
+			cdrom_end_request(drive, 0);
+			return ide_stopped;
+		}
+		rq->current_nr_sectors += nskip;
+	}
+
+	/* Set up the command */
+	rq->timeout = ATAPI_WAIT_PC;
+
+	/* Send the command to the drive and return. */
+	return cdrom_transfer_packet_command(drive, rq, &cdrom_read_intr);
+}
+
+
+#define IDECD_SEEK_THRESHOLD	(1000)			/* 1000 blocks */
+#define IDECD_SEEK_TIMER	(5 * WAIT_MIN_SLEEP)	/* 100 ms */
+#define IDECD_SEEK_TIMEOUT	(2 * WAIT_CMD)		/* 20 sec */
+
+static ide_startstop_t cdrom_seek_intr (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	int stat;
+	static int retry = 10;
+
+	if (cdrom_decode_status(drive, 0, &stat))
+		return ide_stopped;
+	CDROM_CONFIG_FLAGS(drive)->seeking = 1;
+
+	if (retry && time_after(jiffies, info->start_seek + IDECD_SEEK_TIMER)) {
+		if (--retry == 0) {
+			/*
+			 * this condition is far too common, to bother
+			 * users about it
+			 */
+			/* printk("%s: disabled DSC seek overlap\n", drive->name);*/ 
+			drive->dsc_overlap = 0;
+		}
+	}
+	return ide_stopped;
+}
+
+static ide_startstop_t cdrom_start_seek_continuation (ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	sector_t frame = rq->sector;
+
+	sector_div(frame, queue_hardsect_size(drive->queue) >> SECTOR_BITS);
+
+	memset(rq->cmd, 0, sizeof(rq->cmd));
+	rq->cmd[0] = GPCMD_SEEK;
+	put_unaligned(cpu_to_be32(frame), (unsigned int *) &rq->cmd[2]);
+
+	rq->timeout = ATAPI_WAIT_PC;
+	return cdrom_transfer_packet_command(drive, rq, &cdrom_seek_intr);
+}
+
+static ide_startstop_t cdrom_start_seek (ide_drive_t *drive, unsigned int block)
+{
+	struct cdrom_info *info = drive->driver_data;
+
+	info->dma = 0;
+	info->start_seek = jiffies;
+	return cdrom_start_packet_command(drive, 0, cdrom_start_seek_continuation);
+}
+
+/* Fix up a possibly partially-processed request so that we can
+   start it over entirely, or even put it back on the request queue. */
+static void restore_request (struct request *rq)
+{
+	if (rq->buffer != bio_data(rq->bio)) {
+		sector_t n = (rq->buffer - (char *) bio_data(rq->bio)) / SECTOR_SIZE;
+
+		rq->buffer = bio_data(rq->bio);
+		rq->nr_sectors += n;
+		rq->sector -= n;
+	}
+	rq->hard_cur_sectors = rq->current_nr_sectors = bio_cur_sectors(rq->bio);
+	rq->hard_nr_sectors = rq->nr_sectors;
+	rq->hard_sector = rq->sector;
+	rq->q->prep_rq_fn(rq->q, rq);
+}
+
+/*
+ * Start a read request from the CD-ROM.
+ */
+static ide_startstop_t cdrom_start_read (ide_drive_t *drive, unsigned int block)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct request *rq = HWGROUP(drive)->rq;
+	unsigned short sectors_per_frame;
+
+	sectors_per_frame = queue_hardsect_size(drive->queue) >> SECTOR_BITS;
+
+	/* We may be retrying this request after an error.  Fix up
+	   any weirdness which might be present in the request packet. */
+	restore_request(rq);
+
+	/* Satisfy whatever we can of this request from our cached sector. */
+	if (cdrom_read_from_buffer(drive))
+		return ide_stopped;
+
+	blk_attempt_remerge(drive->queue, rq);
+
+	/* Clear the local sector buffer. */
+	info->nsectors_buffered = 0;
+
+	/* use dma, if possible. */
+	info->dma = drive->using_dma;
+	if ((rq->sector & (sectors_per_frame - 1)) ||
+	    (rq->nr_sectors & (sectors_per_frame - 1)))
+		info->dma = 0;
+
+	/* Start sending the read request to the drive. */
+	return cdrom_start_packet_command(drive, 32768, cdrom_start_read_continuation);
+}
+
+/****************************************************************************
+ * Execute all other packet commands.
+ */
+
+/* Interrupt routine for packet command completion. */
+static ide_startstop_t cdrom_pc_intr (ide_drive_t *drive)
+{
+	int ireason, len, thislen;
+	struct request *rq = HWGROUP(drive)->rq;
+	u8 lowcyl = 0, highcyl = 0;
+	int stat;
+
+	/* Check for errors. */
+	if (cdrom_decode_status(drive, 0, &stat))
+		return ide_stopped;
+
+	/* Read the interrupt reason and the transfer length. */
+	ireason = HWIF(drive)->INB(IDE_IREASON_REG);
+	lowcyl  = HWIF(drive)->INB(IDE_BCOUNTL_REG);
+	highcyl = HWIF(drive)->INB(IDE_BCOUNTH_REG);
+
+	len = lowcyl + (256 * highcyl);
+
+	/* If DRQ is clear, the command has completed.
+	   Complain if we still have data left to transfer. */
+	if ((stat & DRQ_STAT) == 0) {
+		/* Some of the trailing request sense fields are optional, and
+		   some drives don't send them.  Sigh. */
+		if (rq->cmd[0] == GPCMD_REQUEST_SENSE &&
+		    rq->data_len > 0 &&
+		    rq->data_len <= 5) {
+			while (rq->data_len > 0) {
+				*(unsigned char *)rq->data++ = 0;
+				--rq->data_len;
+			}
+		}
+
+		if (rq->data_len == 0)
+			cdrom_end_request(drive, 1);
+		else {
+			/* Comment this out, because this always happens 
+			   right after a reset occurs, and it is annoying to 
+			   always print expected stuff.  */
+			/*
+			printk ("%s: cdrom_pc_intr: data underrun %d\n",
+				drive->name, pc->buflen);
+			*/
+			rq->flags |= REQ_FAILED;
+			cdrom_end_request(drive, 0);
+		}
+		return ide_stopped;
+	}
+
+	/* Figure out how much data to transfer. */
+	thislen = rq->data_len;
+	if (thislen > len) thislen = len;
+
+	/* The drive wants to be written to. */
+	if ((ireason & 3) == 0) {
+		if (!rq->data) {
+			blk_dump_rq_flags(rq, "cdrom_pc_intr, write");
+			goto confused;
+		}
+		/* Transfer the data. */
+		HWIF(drive)->atapi_output_bytes(drive, rq->data, thislen);
+
+		/* If we haven't moved enough data to satisfy the drive,
+		   add some padding. */
+		while (len > thislen) {
+			int dum = 0;
+			HWIF(drive)->atapi_output_bytes(drive, &dum, sizeof(dum));
+			len -= sizeof(dum);
+		}
+
+		/* Keep count of how much data we've moved. */
+		rq->data += thislen;
+		rq->data_len -= thislen;
+	}
+
+	/* Same drill for reading. */
+	else if ((ireason & 3) == 2) {
+		if (!rq->data) {
+			blk_dump_rq_flags(rq, "cdrom_pc_intr, write");
+			goto confused;
+		}
+		/* Transfer the data. */
+		HWIF(drive)->atapi_input_bytes(drive, rq->data, thislen);
+
+		/* If we haven't moved enough data to satisfy the drive,
+		   add some padding. */
+		while (len > thislen) {
+			int dum = 0;
+			HWIF(drive)->atapi_input_bytes(drive, &dum, sizeof(dum));
+			len -= sizeof(dum);
+		}
+
+		/* Keep count of how much data we've moved. */
+		rq->data += thislen;
+		rq->data_len -= thislen;
+
+		if (rq->flags & REQ_SENSE)
+			rq->sense_len += thislen;
+	} else {
+confused:
+		printk (KERN_ERR "%s: cdrom_pc_intr: The drive "
+			"appears confused (ireason = 0x%02x)\n",
+			drive->name, ireason);
+		rq->flags |= REQ_FAILED;
+	}
+
+	/* Now we wait for another interrupt. */
+	ide_set_handler(drive, &cdrom_pc_intr, ATAPI_WAIT_PC, cdrom_timer_expiry);
+	return ide_started;
+}
+
+static ide_startstop_t cdrom_do_pc_continuation (ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+
+	if (!rq->timeout)
+		rq->timeout = ATAPI_WAIT_PC;
+
+	/* Send the command to the drive and return. */
+	return cdrom_transfer_packet_command(drive, rq, &cdrom_pc_intr);
+}
+
+
+static ide_startstop_t cdrom_do_packet_command (ide_drive_t *drive)
+{
+	int len;
+	struct request *rq = HWGROUP(drive)->rq;
+	struct cdrom_info *info = drive->driver_data;
+
+	info->dma = 0;
+	rq->flags &= ~REQ_FAILED;
+	len = rq->data_len;
+
+	/* Start sending the command to the drive. */
+	return cdrom_start_packet_command(drive, len, cdrom_do_pc_continuation);
+}
+
+
+static
+int cdrom_queue_packet_command(ide_drive_t *drive, struct request *rq)
+{
+	struct request_sense sense;
+	int retries = 10;
+	unsigned int flags = rq->flags;
+
+	if (rq->sense == NULL)
+		rq->sense = &sense;
+
+	/* Start of retry loop. */
+	do {
+		int error;
+		unsigned long time = jiffies;
+		rq->flags = flags;
+
+		error = ide_do_drive_cmd(drive, rq, ide_wait);
+		time = jiffies - time;
+
+		/* FIXME: we should probably abort/retry or something 
+		 * in case of failure */
+		if (rq->flags & REQ_FAILED) {
+			/* The request failed.  Retry if it was due to a unit
+			   attention status
+			   (usually means media was changed). */
+			struct request_sense *reqbuf = rq->sense;
+
+			if (reqbuf->sense_key == UNIT_ATTENTION)
+				cdrom_saw_media_change(drive);
+			else if (reqbuf->sense_key == NOT_READY &&
+				 reqbuf->asc == 4 && reqbuf->ascq != 4) {
+				/* The drive is in the process of loading
+				   a disk.  Retry, but wait a little to give
+				   the drive time to complete the load. */
+				ssleep(2);
+			} else {
+				/* Otherwise, don't retry. */
+				retries = 0;
+			}
+			--retries;
+		}
+
+		/* End of retry loop. */
+	} while ((rq->flags & REQ_FAILED) && retries >= 0);
+
+	/* Return an error if the command failed. */
+	return (rq->flags & REQ_FAILED) ? -EIO : 0;
+}
+
+/*
+ * Write handling
+ */
+static inline int cdrom_write_check_ireason(ide_drive_t *drive, int len, int ireason)
+{
+	/* Two notes about IDE interrupt reason here - 0 means that
+	 * the drive wants to receive data from us, 2 means that
+	 * the drive is expecting to transfer data to us.
+	 */
+	if (ireason == 0)
+		return 0;
+	else if (ireason == 2) {
+		/* Whoops... The drive wants to send data. */
+		printk(KERN_ERR "%s: write_intr: wrong transfer direction!\n",
+							drive->name);
+
+		while (len > 0) {
+			int dum = 0;
+			HWIF(drive)->atapi_input_bytes(drive, &dum, sizeof(dum));
+			len -= sizeof(dum);
+		}
+	} else {
+		/* Drive wants a command packet, or invalid ireason... */
+		printk(KERN_ERR "%s: write_intr: bad interrupt reason %x\n",
+							drive->name, ireason);
+	}
+
+	cdrom_end_request(drive, 0);
+	return 1;
+}
+
+static void post_transform_command(struct request *req)
+{
+	u8 *c = req->cmd;
+	char *ibuf;
+
+	if (!blk_pc_request(req))
+		return;
+
+	if (req->bio)
+		ibuf = bio_data(req->bio);
+	else
+		ibuf = req->data;
+
+	if (!ibuf)
+		return;
+
+	/*
+	 * set ansi-revision and response data as atapi
+	 */
+	if (c[0] == GPCMD_INQUIRY) {
+		ibuf[2] |= 2;
+		ibuf[3] = (ibuf[3] & 0xf0) | 2;
+	}
+}
+
+typedef void (xfer_func_t)(ide_drive_t *, void *, u32);
+
+/*
+ * best way to deal with dma that is not sector aligned right now... note
+ * that in this path we are not using ->data or ->buffer at all. this irs
+ * can replace cdrom_pc_intr, cdrom_read_intr, and cdrom_write_intr in the
+ * future.
+ */
+static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct request *rq = HWGROUP(drive)->rq;
+	int dma_error, dma, stat, ireason, len, thislen;
+	u8 lowcyl, highcyl;
+	xfer_func_t *xferfunc;
+	unsigned long flags;
+
+	/* Check for errors. */
+	dma_error = 0;
+	dma = info->dma;
+	if (dma) {
+		info->dma = 0;
+		dma_error = HWIF(drive)->ide_dma_end(drive);
+	}
+
+	if (cdrom_decode_status(drive, 0, &stat))
+		return ide_stopped;
+
+	/*
+	 * using dma, transfer is complete now
+	 */
+	if (dma) {
+		if (dma_error) {
+			printk(KERN_ERR "ide-cd: dma error\n");
+			__ide_dma_off(drive);
+			return ide_error(drive, "dma error", stat);
+		}
+
+		end_that_request_chunk(rq, 1, rq->data_len);
+		rq->data_len = 0;
+		goto end_request;
+	}
+
+	/*
+	 * ok we fall to pio :/
+	 */
+	ireason = HWIF(drive)->INB(IDE_IREASON_REG) & 0x3;
+	lowcyl  = HWIF(drive)->INB(IDE_BCOUNTL_REG);
+	highcyl = HWIF(drive)->INB(IDE_BCOUNTH_REG);
+
+	len = lowcyl + (256 * highcyl);
+	thislen = rq->data_len;
+	if (thislen > len)
+		thislen = len;
+
+	/*
+	 * If DRQ is clear, the command has completed.
+	 */
+	if ((stat & DRQ_STAT) == 0)
+		goto end_request;
+
+	/*
+	 * check which way to transfer data
+	 */
+	if (rq_data_dir(rq) == WRITE) {
+		/*
+		 * write to drive
+		 */
+		if (cdrom_write_check_ireason(drive, len, ireason))
+			return ide_stopped;
+
+		xferfunc = HWIF(drive)->atapi_output_bytes;
+	} else  {
+		/*
+		 * read from drive
+		 */
+		if (cdrom_read_check_ireason(drive, len, ireason))
+			return ide_stopped;
+
+		xferfunc = HWIF(drive)->atapi_input_bytes;
+	}
+
+	/*
+	 * transfer data
+	 */
+	while (thislen > 0) {
+		int blen = blen = rq->data_len;
+		char *ptr = rq->data;
+
+		/*
+		 * bio backed?
+		 */
+		if (rq->bio) {
+			ptr = bio_data(rq->bio);
+			blen = bio_iovec(rq->bio)->bv_len;
+		}
+
+		if (!ptr) {
+			printk(KERN_ERR "%s: confused, missing data\n", drive->name);
+			break;
+		}
+
+		if (blen > thislen)
+			blen = thislen;
+
+		xferfunc(drive, ptr, blen);
+
+		thislen -= blen;
+		len -= blen;
+		rq->data_len -= blen;
+
+		if (rq->bio)
+			end_that_request_chunk(rq, 1, blen);
+		else
+			rq->data += blen;
+	}
+
+	/*
+	 * pad, if necessary
+	 */
+	if (len > 0) {
+		while (len > 0) {
+			int pad = 0;
+
+			xferfunc(drive, &pad, sizeof(pad));
+			len -= sizeof(pad);
+		}
+	}
+
+	if (HWGROUP(drive)->handler != NULL)
+		BUG();
+
+	ide_set_handler(drive, cdrom_newpc_intr, rq->timeout, NULL);
+	return ide_started;
+
+end_request:
+	if (!rq->data_len)
+		post_transform_command(rq);
+
+	spin_lock_irqsave(&ide_lock, flags);
+	blkdev_dequeue_request(rq);
+	end_that_request_last(rq);
+	HWGROUP(drive)->rq = NULL;
+	spin_unlock_irqrestore(&ide_lock, flags);
+	return ide_stopped;
+}
+
+static ide_startstop_t cdrom_write_intr(ide_drive_t *drive)
+{
+	int stat, ireason, len, sectors_to_transfer, uptodate;
+	struct cdrom_info *info = drive->driver_data;
+	int dma_error = 0, dma = info->dma;
+	u8 lowcyl = 0, highcyl = 0;
+
+	struct request *rq = HWGROUP(drive)->rq;
+
+	/* Check for errors. */
+	if (dma) {
+		info->dma = 0;
+		if ((dma_error = HWIF(drive)->ide_dma_end(drive))) {
+			printk(KERN_ERR "ide-cd: write dma error\n");
+			__ide_dma_off(drive);
+		}
+	}
+
+	if (cdrom_decode_status(drive, 0, &stat))
+		return ide_stopped;
+
+	/*
+	 * using dma, transfer is complete now
+	 */
+	if (dma) {
+		if (dma_error)
+			return ide_error(drive, "dma error", stat);
+
+		ide_end_request(drive, 1, rq->nr_sectors);
+		return ide_stopped;
+	}
+
+	/* Read the interrupt reason and the transfer length. */
+	ireason = HWIF(drive)->INB(IDE_IREASON_REG);
+	lowcyl  = HWIF(drive)->INB(IDE_BCOUNTL_REG);
+	highcyl = HWIF(drive)->INB(IDE_BCOUNTH_REG);
+
+	len = lowcyl + (256 * highcyl);
+
+	/* If DRQ is clear, the command has completed. */
+	if ((stat & DRQ_STAT) == 0) {
+		/* If we're not done writing, complain.
+		 * Otherwise, complete the command normally.
+		 */
+		uptodate = 1;
+		if (rq->current_nr_sectors > 0) {
+			printk(KERN_ERR "%s: write_intr: data underrun (%d blocks)\n",
+			drive->name, rq->current_nr_sectors);
+			uptodate = 0;
+		}
+		cdrom_end_request(drive, uptodate);
+		return ide_stopped;
+	}
+
+	/* Check that the drive is expecting to do the same thing we are. */
+	if (cdrom_write_check_ireason(drive, len, ireason))
+		return ide_stopped;
+
+	sectors_to_transfer = len / SECTOR_SIZE;
+
+	/*
+	 * now loop and write out the data
+	 */
+	while (sectors_to_transfer > 0) {
+		int this_transfer;
+
+		if (!rq->current_nr_sectors) {
+			printk(KERN_ERR "ide-cd: write_intr: oops\n");
+			break;
+		}
+
+		/*
+		 * Figure out how many sectors we can transfer
+		 */
+		this_transfer = min_t(int, sectors_to_transfer, rq->current_nr_sectors);
+
+		while (this_transfer > 0) {
+			HWIF(drive)->atapi_output_bytes(drive, rq->buffer, SECTOR_SIZE);
+			rq->buffer += SECTOR_SIZE;
+			--rq->nr_sectors;
+			--rq->current_nr_sectors;
+			++rq->sector;
+			--this_transfer;
+			--sectors_to_transfer;
+		}
+
+		/*
+		 * current buffer complete, move on
+		 */
+		if (rq->current_nr_sectors == 0 && rq->nr_sectors)
+			cdrom_end_request(drive, 1);
+	}
+
+	/* re-arm handler */
+	ide_set_handler(drive, &cdrom_write_intr, ATAPI_WAIT_PC, NULL);
+	return ide_started;
+}
+
+static ide_startstop_t cdrom_start_write_cont(ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+
+#if 0	/* the immediate bit */
+	rq->cmd[1] = 1 << 3;
+#endif
+	rq->timeout = ATAPI_WAIT_PC;
+
+	return cdrom_transfer_packet_command(drive, rq, cdrom_write_intr);
+}
+
+static ide_startstop_t cdrom_start_write(ide_drive_t *drive, struct request *rq)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct gendisk *g = info->disk;
+	unsigned short sectors_per_frame = queue_hardsect_size(drive->queue) >> SECTOR_BITS;
+
+	/*
+	 * writes *must* be hardware frame aligned
+	 */
+	if ((rq->nr_sectors & (sectors_per_frame - 1)) ||
+	    (rq->sector & (sectors_per_frame - 1))) {
+		cdrom_end_request(drive, 0);
+		return ide_stopped;
+	}
+
+	/*
+	 * disk has become write protected
+	 */
+	if (g->policy) {
+		cdrom_end_request(drive, 0);
+		return ide_stopped;
+	}
+
+	/*
+	 * for dvd-ram and such media, it's a really big deal to get
+	 * big writes all the time. so scour the queue and attempt to
+	 * remerge requests, often the plugging will not have had time
+	 * to do this properly
+	 */
+	blk_attempt_remerge(drive->queue, rq);
+
+	info->nsectors_buffered = 0;
+
+	/* use dma, if possible. we don't need to check more, since we
+	 * know that the transfer is always (at least!) frame aligned */
+	info->dma = drive->using_dma ? 1 : 0;
+
+	info->devinfo.media_written = 1;
+
+	/* Start sending the write request to the drive. */
+	return cdrom_start_packet_command(drive, 32768, cdrom_start_write_cont);
+}
+
+static ide_startstop_t cdrom_do_newpc_cont(ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+
+	if (!rq->timeout)
+		rq->timeout = ATAPI_WAIT_PC;
+
+	return cdrom_transfer_packet_command(drive, rq, cdrom_newpc_intr);
+}
+
+static ide_startstop_t cdrom_do_block_pc(ide_drive_t *drive, struct request *rq)
+{
+	struct cdrom_info *info = drive->driver_data;
+
+	rq->flags |= REQ_QUIET;
+
+	info->dma = 0;
+
+	/*
+	 * sg request
+	 */
+	if (rq->bio) {
+		int mask = drive->queue->dma_alignment;
+		unsigned long addr = (unsigned long) page_address(bio_page(rq->bio));
+
+		info->dma = drive->using_dma;
+
+		/*
+		 * check if dma is safe
+		 *
+		 * NOTE! The "len" and "addr" checks should possibly have
+		 * separate masks.
+		 */
+		if ((rq->data_len & 15) || (addr & mask))
+			info->dma = 0;
+	}
+
+	/* Start sending the command to the drive. */
+	return cdrom_start_packet_command(drive, rq->data_len, cdrom_do_newpc_cont);
+}
+
+/****************************************************************************
+ * cdrom driver request routine.
+ */
+static ide_startstop_t
+ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, sector_t block)
+{
+	ide_startstop_t action;
+	struct cdrom_info *info = drive->driver_data;
+
+	if (blk_fs_request(rq)) {
+		if (CDROM_CONFIG_FLAGS(drive)->seeking) {
+			unsigned long elapsed = jiffies - info->start_seek;
+			int stat = HWIF(drive)->INB(IDE_STATUS_REG);
+
+			if ((stat & SEEK_STAT) != SEEK_STAT) {
+				if (elapsed < IDECD_SEEK_TIMEOUT) {
+					ide_stall_queue(drive, IDECD_SEEK_TIMER);
+					return ide_stopped;
+				}
+				printk (KERN_ERR "%s: DSC timeout\n", drive->name);
+			}
+			CDROM_CONFIG_FLAGS(drive)->seeking = 0;
+		}
+		if ((rq_data_dir(rq) == READ) && IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) {
+			action = cdrom_start_seek(drive, block);
+		} else {
+			if (rq_data_dir(rq) == READ)
+				action = cdrom_start_read(drive, block);
+			else
+				action = cdrom_start_write(drive, rq);
+		}
+		info->last_block = block;
+		return action;
+	} else if (rq->flags & (REQ_PC | REQ_SENSE)) {
+		return cdrom_do_packet_command(drive);
+	} else if (rq->flags & REQ_BLOCK_PC) {
+		return cdrom_do_block_pc(drive, rq);
+	} else if (rq->flags & REQ_SPECIAL) {
+		/*
+		 * right now this can only be a reset...
+		 */
+		cdrom_end_request(drive, 1);
+		return ide_stopped;
+	}
+
+	blk_dump_rq_flags(rq, "ide-cd bad flags");
+	cdrom_end_request(drive, 0);
+	return ide_stopped;
+}
+
+
+
+/****************************************************************************
+ * Ioctl handling.
+ *
+ * Routines which queue packet commands take as a final argument a pointer
+ * to a request_sense struct.  If execution of the command results
+ * in an error with a CHECK CONDITION status, this structure will be filled
+ * with the results of the subsequent request sense command.  The pointer
+ * can also be NULL, in which case no sense information is returned.
+ */
+
+#if ! STANDARD_ATAPI
+static inline
+int bin2bcd (int x)
+{
+	return (x%10) | ((x/10) << 4);
+}
+
+
+static inline
+int bcd2bin (int x)
+{
+	return (x >> 4) * 10 + (x & 0x0f);
+}
+
+static
+void msf_from_bcd (struct atapi_msf *msf)
+{
+	msf->minute = bcd2bin (msf->minute);
+	msf->second = bcd2bin (msf->second);
+	msf->frame  = bcd2bin (msf->frame);
+}
+
+#endif /* not STANDARD_ATAPI */
+
+
+static inline
+void lba_to_msf (int lba, byte *m, byte *s, byte *f)
+{
+	lba += CD_MSF_OFFSET;
+	lba &= 0xffffff;  /* negative lbas use only 24 bits */
+	*m = lba / (CD_SECS * CD_FRAMES);
+	lba %= (CD_SECS * CD_FRAMES);
+	*s = lba / CD_FRAMES;
+	*f = lba % CD_FRAMES;
+}
+
+
+static inline
+int msf_to_lba (byte m, byte s, byte f)
+{
+	return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET;
+}
+
+static int cdrom_check_status(ide_drive_t *drive, struct request_sense *sense)
+{
+	struct request req;
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
+
+	cdrom_prepare_request(drive, &req);
+
+	req.sense = sense;
+	req.cmd[0] = GPCMD_TEST_UNIT_READY;
+	req.flags |= REQ_QUIET;
+
+#if ! STANDARD_ATAPI
+        /* the Sanyo 3 CD changer uses byte 7 of TEST_UNIT_READY to 
+           switch CDs instead of supporting the LOAD_UNLOAD opcode   */
+
+	req.cmd[7] = cdi->sanyo_slot % 3;
+#endif /* not STANDARD_ATAPI */
+
+	return cdrom_queue_packet_command(drive, &req);
+}
+
+
+/* Lock the door if LOCKFLAG is nonzero; unlock it otherwise. */
+static int
+cdrom_lockdoor(ide_drive_t *drive, int lockflag, struct request_sense *sense)
+{
+	struct request_sense my_sense;
+	struct request req;
+	int stat;
+
+	if (sense == NULL)
+		sense = &my_sense;
+
+	/* If the drive cannot lock the door, just pretend. */
+	if (CDROM_CONFIG_FLAGS(drive)->no_doorlock) {
+		stat = 0;
+	} else {
+		cdrom_prepare_request(drive, &req);
+		req.sense = sense;
+		req.cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
+		req.cmd[4] = lockflag ? 1 : 0;
+		stat = cdrom_queue_packet_command(drive, &req);
+	}
+
+	/* If we got an illegal field error, the drive
+	   probably cannot lock the door. */
+	if (stat != 0 &&
+	    sense->sense_key == ILLEGAL_REQUEST &&
+	    (sense->asc == 0x24 || sense->asc == 0x20)) {
+		printk (KERN_ERR "%s: door locking not supported\n",
+			drive->name);
+		CDROM_CONFIG_FLAGS(drive)->no_doorlock = 1;
+		stat = 0;
+	}
+	
+	/* no medium, that's alright. */
+	if (stat != 0 && sense->sense_key == NOT_READY && sense->asc == 0x3a)
+		stat = 0;
+
+	if (stat == 0)
+		CDROM_STATE_FLAGS(drive)->door_locked = lockflag;
+
+	return stat;
+}
+
+
+/* Eject the disk if EJECTFLAG is 0.
+   If EJECTFLAG is 1, try to reload the disk. */
+static int cdrom_eject(ide_drive_t *drive, int ejectflag,
+		       struct request_sense *sense)
+{
+	struct request req;
+	char loej = 0x02;
+
+	if (CDROM_CONFIG_FLAGS(drive)->no_eject && !ejectflag)
+		return -EDRIVE_CANT_DO_THIS;
+	
+	/* reload fails on some drives, if the tray is locked */
+	if (CDROM_STATE_FLAGS(drive)->door_locked && ejectflag)
+		return 0;
+
+	cdrom_prepare_request(drive, &req);
+
+	/* only tell drive to close tray if open, if it can do that */
+	if (ejectflag && !CDROM_CONFIG_FLAGS(drive)->close_tray)
+		loej = 0;
+
+	req.sense = sense;
+	req.cmd[0] = GPCMD_START_STOP_UNIT;
+	req.cmd[4] = loej | (ejectflag != 0);
+	return cdrom_queue_packet_command(drive, &req);
+}
+
+static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity,
+			       unsigned long *sectors_per_frame,
+			       struct request_sense *sense)
+{
+	struct {
+		__u32 lba;
+		__u32 blocklen;
+	} capbuf;
+
+	int stat;
+	struct request req;
+
+	cdrom_prepare_request(drive, &req);
+
+	req.sense = sense;
+	req.cmd[0] = GPCMD_READ_CDVD_CAPACITY;
+	req.data = (char *)&capbuf;
+	req.data_len = sizeof(capbuf);
+
+	stat = cdrom_queue_packet_command(drive, &req);
+	if (stat == 0) {
+		*capacity = 1 + be32_to_cpu(capbuf.lba);
+		*sectors_per_frame =
+			be32_to_cpu(capbuf.blocklen) >> SECTOR_BITS;
+	}
+
+	return stat;
+}
+
+static int cdrom_read_tocentry(ide_drive_t *drive, int trackno, int msf_flag,
+				int format, char *buf, int buflen,
+				struct request_sense *sense)
+{
+	struct request req;
+
+	cdrom_prepare_request(drive, &req);
+
+	req.sense = sense;
+	req.data =  buf;
+	req.data_len = buflen;
+	req.flags |= REQ_QUIET;
+	req.cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
+	req.cmd[6] = trackno;
+	req.cmd[7] = (buflen >> 8);
+	req.cmd[8] = (buflen & 0xff);
+	req.cmd[9] = (format << 6);
+
+	if (msf_flag)
+		req.cmd[1] = 2;
+
+	return cdrom_queue_packet_command(drive, &req);
+}
+
+
+/* Try to read the entire TOC for the disk into our internal buffer. */
+static int cdrom_read_toc(ide_drive_t *drive, struct request_sense *sense)
+{
+	int stat, ntracks, i;
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
+	struct atapi_toc *toc = info->toc;
+	struct {
+		struct atapi_toc_header hdr;
+		struct atapi_toc_entry  ent;
+	} ms_tmp;
+	long last_written;
+	unsigned long sectors_per_frame = SECTORS_PER_FRAME;
+
+	if (toc == NULL) {
+		/* Try to allocate space. */
+		toc = kmalloc(sizeof(struct atapi_toc), GFP_KERNEL);
+		if (toc == NULL) {
+			printk (KERN_ERR "%s: No cdrom TOC buffer!\n", drive->name);
+			return -ENOMEM;
+		}
+		info->toc = toc;
+	}
+
+	/* Check to see if the existing data is still valid.
+	   If it is, just return. */
+	(void) cdrom_check_status(drive, sense);
+
+	if (CDROM_STATE_FLAGS(drive)->toc_valid)
+		return 0;
+
+	/* Try to get the total cdrom capacity and sector size. */
+	stat = cdrom_read_capacity(drive, &toc->capacity, &sectors_per_frame,
+				   sense);
+	if (stat)
+		toc->capacity = 0x1fffff;
+
+	set_capacity(info->disk, toc->capacity * sectors_per_frame);
+	blk_queue_hardsect_size(drive->queue,
+				sectors_per_frame << SECTOR_BITS);
+
+	/* First read just the header, so we know how long the TOC is. */
+	stat = cdrom_read_tocentry(drive, 0, 1, 0, (char *) &toc->hdr,
+				    sizeof(struct atapi_toc_header), sense);
+	if (stat)
+		return stat;
+
+#if ! STANDARD_ATAPI
+	if (CDROM_CONFIG_FLAGS(drive)->toctracks_as_bcd) {
+		toc->hdr.first_track = bcd2bin(toc->hdr.first_track);
+		toc->hdr.last_track  = bcd2bin(toc->hdr.last_track);
+	}
+#endif  /* not STANDARD_ATAPI */
+
+	ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
+	if (ntracks <= 0)
+		return -EIO;
+	if (ntracks > MAX_TRACKS)
+		ntracks = MAX_TRACKS;
+
+	/* Now read the whole schmeer. */
+	stat = cdrom_read_tocentry(drive, toc->hdr.first_track, 1, 0,
+				  (char *)&toc->hdr,
+				   sizeof(struct atapi_toc_header) +
+				   (ntracks + 1) *
+				   sizeof(struct atapi_toc_entry), sense);
+
+	if (stat && toc->hdr.first_track > 1) {
+		/* Cds with CDI tracks only don't have any TOC entries,
+		   despite of this the returned values are
+		   first_track == last_track = number of CDI tracks + 1,
+		   so that this case is indistinguishable from the same
+		   layout plus an additional audio track.
+		   If we get an error for the regular case, we assume
+		   a CDI without additional audio tracks. In this case
+		   the readable TOC is empty (CDI tracks are not included)
+		   and only holds the Leadout entry. Heiko Eißfeldt */
+		ntracks = 0;
+		stat = cdrom_read_tocentry(drive, CDROM_LEADOUT, 1, 0,
+					   (char *)&toc->hdr,
+					   sizeof(struct atapi_toc_header) +
+					   (ntracks + 1) *
+					   sizeof(struct atapi_toc_entry),
+					   sense);
+		if (stat) {
+			return stat;
+		}
+#if ! STANDARD_ATAPI
+		if (CDROM_CONFIG_FLAGS(drive)->toctracks_as_bcd) {
+			toc->hdr.first_track = bin2bcd(CDROM_LEADOUT);
+			toc->hdr.last_track = bin2bcd(CDROM_LEADOUT);
+		} else
+#endif  /* not STANDARD_ATAPI */
+		{
+			toc->hdr.first_track = CDROM_LEADOUT;
+			toc->hdr.last_track = CDROM_LEADOUT;
+		}
+	}
+
+	if (stat)
+		return stat;
+
+	toc->hdr.toc_length = ntohs (toc->hdr.toc_length);
+
+#if ! STANDARD_ATAPI
+	if (CDROM_CONFIG_FLAGS(drive)->toctracks_as_bcd) {
+		toc->hdr.first_track = bcd2bin(toc->hdr.first_track);
+		toc->hdr.last_track  = bcd2bin(toc->hdr.last_track);
+	}
+#endif  /* not STANDARD_ATAPI */
+
+	for (i=0; i<=ntracks; i++) {
+#if ! STANDARD_ATAPI
+		if (CDROM_CONFIG_FLAGS(drive)->tocaddr_as_bcd) {
+			if (CDROM_CONFIG_FLAGS(drive)->toctracks_as_bcd)
+				toc->ent[i].track = bcd2bin(toc->ent[i].track);
+			msf_from_bcd(&toc->ent[i].addr.msf);
+		}
+#endif  /* not STANDARD_ATAPI */
+		toc->ent[i].addr.lba = msf_to_lba (toc->ent[i].addr.msf.minute,
+						   toc->ent[i].addr.msf.second,
+						   toc->ent[i].addr.msf.frame);
+	}
+
+	/* Read the multisession information. */
+	if (toc->hdr.first_track != CDROM_LEADOUT) {
+		/* Read the multisession information. */
+		stat = cdrom_read_tocentry(drive, 0, 0, 1, (char *)&ms_tmp,
+					   sizeof(ms_tmp), sense);
+		if (stat)
+			return stat;
+
+		toc->last_session_lba = be32_to_cpu(ms_tmp.ent.addr.lba);
+	} else {
+		ms_tmp.hdr.first_track = ms_tmp.hdr.last_track = CDROM_LEADOUT;
+		toc->last_session_lba = msf_to_lba(0, 2, 0); /* 0m 2s 0f */
+	}
+
+#if ! STANDARD_ATAPI
+	if (CDROM_CONFIG_FLAGS(drive)->tocaddr_as_bcd) {
+		/* Re-read multisession information using MSF format */
+		stat = cdrom_read_tocentry(drive, 0, 1, 1, (char *)&ms_tmp,
+					   sizeof(ms_tmp), sense);
+		if (stat)
+			return stat;
+
+		msf_from_bcd (&ms_tmp.ent.addr.msf);
+		toc->last_session_lba = msf_to_lba(ms_tmp.ent.addr.msf.minute,
+					  	   ms_tmp.ent.addr.msf.second,
+						   ms_tmp.ent.addr.msf.frame);
+	}
+#endif  /* not STANDARD_ATAPI */
+
+	toc->xa_flag = (ms_tmp.hdr.first_track != ms_tmp.hdr.last_track);
+
+	/* Now try to get the total cdrom capacity. */
+	stat = cdrom_get_last_written(cdi, &last_written);
+	if (!stat && (last_written > toc->capacity)) {
+		toc->capacity = last_written;
+		set_capacity(info->disk, toc->capacity * sectors_per_frame);
+	}
+
+	/* Remember that we've read this stuff. */
+	CDROM_STATE_FLAGS(drive)->toc_valid = 1;
+
+	return 0;
+}
+
+
+static int cdrom_read_subchannel(ide_drive_t *drive, int format, char *buf,
+				 int buflen, struct request_sense *sense)
+{
+	struct request req;
+
+	cdrom_prepare_request(drive, &req);
+
+	req.sense = sense;
+	req.data = buf;
+	req.data_len = buflen;
+	req.cmd[0] = GPCMD_READ_SUBCHANNEL;
+	req.cmd[1] = 2;     /* MSF addressing */
+	req.cmd[2] = 0x40;  /* request subQ data */
+	req.cmd[3] = format;
+	req.cmd[7] = (buflen >> 8);
+	req.cmd[8] = (buflen & 0xff);
+	return cdrom_queue_packet_command(drive, &req);
+}
+
+/* ATAPI cdrom drives are free to select the speed you request or any slower
+   rate :-( Requesting too fast a speed will _not_ produce an error. */
+static int cdrom_select_speed(ide_drive_t *drive, int speed,
+			      struct request_sense *sense)
+{
+	struct request req;
+	cdrom_prepare_request(drive, &req);
+
+	req.sense = sense;
+	if (speed == 0)
+		speed = 0xffff; /* set to max */
+	else
+		speed *= 177;   /* Nx to kbytes/s */
+
+	req.cmd[0] = GPCMD_SET_SPEED;
+	/* Read Drive speed in kbytes/second MSB */
+	req.cmd[2] = (speed >> 8) & 0xff;	
+	/* Read Drive speed in kbytes/second LSB */
+	req.cmd[3] = speed & 0xff;
+	if (CDROM_CONFIG_FLAGS(drive)->cd_r ||
+	    CDROM_CONFIG_FLAGS(drive)->cd_rw ||
+	    CDROM_CONFIG_FLAGS(drive)->dvd_r) {
+		/* Write Drive speed in kbytes/second MSB */
+		req.cmd[4] = (speed >> 8) & 0xff;
+		/* Write Drive speed in kbytes/second LSB */
+		req.cmd[5] = speed & 0xff;
+       }
+
+	return cdrom_queue_packet_command(drive, &req);
+}
+
+static int cdrom_play_audio(ide_drive_t *drive, int lba_start, int lba_end)
+{
+	struct request_sense sense;
+	struct request req;
+
+	cdrom_prepare_request(drive, &req);
+
+	req.sense = &sense;
+	req.cmd[0] = GPCMD_PLAY_AUDIO_MSF;
+	lba_to_msf(lba_start, &req.cmd[3], &req.cmd[4], &req.cmd[5]);
+	lba_to_msf(lba_end-1, &req.cmd[6], &req.cmd[7], &req.cmd[8]);
+
+	return cdrom_queue_packet_command(drive, &req);
+}
+
+static int cdrom_get_toc_entry(ide_drive_t *drive, int track,
+				struct atapi_toc_entry **ent)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct atapi_toc *toc = info->toc;
+	int ntracks;
+
+	/*
+	 * don't serve cached data, if the toc isn't valid
+	 */
+	if (!CDROM_STATE_FLAGS(drive)->toc_valid)
+		return -EINVAL;
+
+	/* Check validity of requested track number. */
+	ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
+	if (toc->hdr.first_track == CDROM_LEADOUT) ntracks = 0;
+	if (track == CDROM_LEADOUT)
+		*ent = &toc->ent[ntracks];
+	else if (track < toc->hdr.first_track ||
+		 track > toc->hdr.last_track)
+		return -EINVAL;
+	else
+		*ent = &toc->ent[track - toc->hdr.first_track];
+
+	return 0;
+}
+
+/* the generic packet interface to cdrom.c */
+static int ide_cdrom_packet(struct cdrom_device_info *cdi,
+			    struct packet_command *cgc)
+{
+	struct request req;
+	ide_drive_t *drive = cdi->handle;
+
+	if (cgc->timeout <= 0)
+		cgc->timeout = ATAPI_WAIT_PC;
+
+	/* here we queue the commands from the uniform CD-ROM
+	   layer. the packet must be complete, as we do not
+	   touch it at all. */
+	cdrom_prepare_request(drive, &req);
+	memcpy(req.cmd, cgc->cmd, CDROM_PACKET_SIZE);
+	if (cgc->sense)
+		memset(cgc->sense, 0, sizeof(struct request_sense));
+	req.data = cgc->buffer;
+	req.data_len = cgc->buflen;
+	req.timeout = cgc->timeout;
+
+	if (cgc->quiet)
+		req.flags |= REQ_QUIET;
+
+	req.sense = cgc->sense;
+	cgc->stat = cdrom_queue_packet_command(drive, &req);
+	if (!cgc->stat)
+		cgc->buflen -= req.data_len;
+	return cgc->stat;
+}
+
+static
+int ide_cdrom_dev_ioctl (struct cdrom_device_info *cdi,
+			 unsigned int cmd, unsigned long arg)
+{
+	struct packet_command cgc;
+	char buffer[16];
+	int stat;
+
+	init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_UNKNOWN);
+
+	/* These will be moved into the Uniform layer shortly... */
+	switch (cmd) {
+ 	case CDROMSETSPINDOWN: {
+ 		char spindown;
+ 
+ 		if (copy_from_user(&spindown, (void __user *) arg, sizeof(char)))
+			return -EFAULT;
+ 
+                if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0)))
+			return stat;
+
+ 		buffer[11] = (buffer[11] & 0xf0) | (spindown & 0x0f);
+
+ 		return cdrom_mode_select(cdi, &cgc);
+ 	} 
+ 
+ 	case CDROMGETSPINDOWN: {
+ 		char spindown;
+ 
+                if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0)))
+			return stat;
+ 
+ 		spindown = buffer[11] & 0x0f;
+ 
+		if (copy_to_user((void __user *) arg, &spindown, sizeof (char)))
+			return -EFAULT;
+ 
+ 		return 0;
+ 	}
+  
+	default:
+		return -EINVAL;
+	}
+
+}
+
+static
+int ide_cdrom_audio_ioctl (struct cdrom_device_info *cdi,
+			   unsigned int cmd, void *arg)
+			   
+{
+	ide_drive_t *drive = cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
+	int stat;
+
+	switch (cmd) {
+	/*
+	 * emulate PLAY_AUDIO_TI command with PLAY_AUDIO_10, since
+	 * atapi doesn't support it
+	 */
+	case CDROMPLAYTRKIND: {
+		unsigned long lba_start, lba_end;
+		struct cdrom_ti *ti = arg;
+		struct atapi_toc_entry *first_toc, *last_toc;
+
+		stat = cdrom_get_toc_entry(drive, ti->cdti_trk0, &first_toc);
+		if (stat)
+			return stat;
+
+		stat = cdrom_get_toc_entry(drive, ti->cdti_trk1, &last_toc);
+		if (stat)
+			return stat;
+
+		if (ti->cdti_trk1 != CDROM_LEADOUT)
+			++last_toc;
+		lba_start = first_toc->addr.lba;
+		lba_end   = last_toc->addr.lba;
+
+		if (lba_end <= lba_start)
+			return -EINVAL;
+
+		return cdrom_play_audio(drive, lba_start, lba_end);
+	}
+
+	case CDROMREADTOCHDR: {
+		struct cdrom_tochdr *tochdr = arg;
+		struct atapi_toc *toc;
+
+		/* Make sure our saved TOC is valid. */
+		stat = cdrom_read_toc(drive, NULL);
+		if (stat)
+			return stat;
+
+		toc = info->toc;
+		tochdr->cdth_trk0 = toc->hdr.first_track;
+		tochdr->cdth_trk1 = toc->hdr.last_track;
+
+		return 0;
+	}
+
+	case CDROMREADTOCENTRY: {
+		struct cdrom_tocentry *tocentry = arg;
+		struct atapi_toc_entry *toce;
+
+		stat = cdrom_get_toc_entry(drive, tocentry->cdte_track, &toce);
+		if (stat)
+			return stat;
+
+		tocentry->cdte_ctrl = toce->control;
+		tocentry->cdte_adr  = toce->adr;
+		if (tocentry->cdte_format == CDROM_MSF) {
+			lba_to_msf (toce->addr.lba,
+				   &tocentry->cdte_addr.msf.minute,
+				   &tocentry->cdte_addr.msf.second,
+				   &tocentry->cdte_addr.msf.frame);
+		} else
+			tocentry->cdte_addr.lba = toce->addr.lba;
+
+		return 0;
+	}
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static
+int ide_cdrom_reset (struct cdrom_device_info *cdi)
+{
+	ide_drive_t *drive = cdi->handle;
+	struct request_sense sense;
+	struct request req;
+	int ret;
+
+	cdrom_prepare_request(drive, &req);
+	req.flags = REQ_SPECIAL | REQ_QUIET;
+	ret = ide_do_drive_cmd(drive, &req, ide_wait);
+
+	/*
+	 * A reset will unlock the door. If it was previously locked,
+	 * lock it again.
+	 */
+	if (CDROM_STATE_FLAGS(drive)->door_locked)
+		(void) cdrom_lockdoor(drive, 1, &sense);
+
+	return ret;
+}
+
+
+static
+int ide_cdrom_tray_move (struct cdrom_device_info *cdi, int position)
+{
+	ide_drive_t *drive = cdi->handle;
+	struct request_sense sense;
+
+	if (position) {
+		int stat = cdrom_lockdoor(drive, 0, &sense);
+		if (stat)
+			return stat;
+	}
+
+	return cdrom_eject(drive, !position, &sense);
+}
+
+static
+int ide_cdrom_lock_door (struct cdrom_device_info *cdi, int lock)
+{
+	ide_drive_t *drive = cdi->handle;
+	return cdrom_lockdoor(drive, lock, NULL);
+}
+
+static
+int ide_cdrom_get_capabilities(ide_drive_t *drive, struct atapi_capabilities_page *cap)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
+	struct packet_command cgc;
+	int stat, attempts = 3, size = sizeof(*cap);
+
+	/*
+	 * ACER50 (and others?) require the full spec length mode sense
+	 * page capabilities size, but older drives break.
+	 */
+	if (!(!strcmp(drive->id->model, "ATAPI CD ROM DRIVE 50X MAX") ||
+	    !strcmp(drive->id->model, "WPI CDS-32X")))
+		size -= sizeof(cap->pad);
+
+	init_cdrom_command(&cgc, cap, size, CGC_DATA_UNKNOWN);
+	do { /* we seem to get stat=0x01,err=0x00 the first time (??) */
+		stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
+		if (!stat)
+			break;
+	} while (--attempts);
+	return stat;
+}
+
+static
+void ide_cdrom_update_speed (ide_drive_t *drive, struct atapi_capabilities_page *cap)
+{
+	/* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */
+	if (!drive->id->model[0] &&
+	    !strncmp(drive->id->fw_rev, "241N", 4)) {
+		CDROM_STATE_FLAGS(drive)->current_speed  =
+			(((unsigned int)cap->curspeed) + (176/2)) / 176;
+		CDROM_CONFIG_FLAGS(drive)->max_speed =
+			(((unsigned int)cap->maxspeed) + (176/2)) / 176;
+	} else {
+		CDROM_STATE_FLAGS(drive)->current_speed  =
+			(ntohs(cap->curspeed) + (176/2)) / 176;
+		CDROM_CONFIG_FLAGS(drive)->max_speed =
+			(ntohs(cap->maxspeed) + (176/2)) / 176;
+	}
+}
+
+static
+int ide_cdrom_select_speed (struct cdrom_device_info *cdi, int speed)
+{
+	ide_drive_t *drive = cdi->handle;
+	struct request_sense sense;
+	struct atapi_capabilities_page cap;
+	int stat;
+
+	if ((stat = cdrom_select_speed(drive, speed, &sense)) < 0)
+		return stat;
+
+	if (!ide_cdrom_get_capabilities(drive, &cap)) {
+		ide_cdrom_update_speed(drive, &cap);
+		cdi->speed = CDROM_STATE_FLAGS(drive)->current_speed;
+	}
+        return 0;
+}
+
+/*
+ * add logic to try GET_EVENT command first to check for media and tray
+ * status. this should be supported by newer cd-r/w and all DVD etc
+ * drives
+ */
+static
+int ide_cdrom_drive_status (struct cdrom_device_info *cdi, int slot_nr)
+{
+	ide_drive_t *drive = cdi->handle;
+	struct media_event_desc med;
+	struct request_sense sense;
+	int stat;
+
+	if (slot_nr != CDSL_CURRENT)
+		return -EINVAL;
+
+	stat = cdrom_check_status(drive, &sense);
+	if (!stat || sense.sense_key == UNIT_ATTENTION)
+		return CDS_DISC_OK;
+
+	if (!cdrom_get_media_event(cdi, &med)) {
+		if (med.media_present)
+			return CDS_DISC_OK;
+		else if (med.door_open)
+			return CDS_TRAY_OPEN;
+		else
+			return CDS_NO_DISC;
+	}
+
+	if (sense.sense_key == NOT_READY && sense.asc == 0x04 && sense.ascq == 0x04)
+		return CDS_DISC_OK;
+
+	/*
+	 * If not using Mt Fuji extended media tray reports,
+	 * just return TRAY_OPEN since ATAPI doesn't provide
+	 * any other way to detect this...
+	 */
+	if (sense.sense_key == NOT_READY) {
+		if (sense.asc == 0x3a) {
+			if (sense.ascq == 1)
+				return CDS_NO_DISC;
+			else if (sense.ascq == 0 || sense.ascq == 2)
+				return CDS_TRAY_OPEN;
+		}
+	}
+
+	return CDS_DRIVE_NOT_READY;
+}
+
+static
+int ide_cdrom_get_last_session (struct cdrom_device_info *cdi,
+				struct cdrom_multisession *ms_info)
+{
+	struct atapi_toc *toc;
+	ide_drive_t *drive = cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
+	struct request_sense sense;
+	int ret;
+
+	if (!CDROM_STATE_FLAGS(drive)->toc_valid || info->toc == NULL)
+		if ((ret = cdrom_read_toc(drive, &sense)))
+			return ret;
+
+	toc = info->toc;
+	ms_info->addr.lba = toc->last_session_lba;
+	ms_info->xa_flag = toc->xa_flag;
+
+	return 0;
+}
+
+static
+int ide_cdrom_get_mcn (struct cdrom_device_info *cdi,
+		       struct cdrom_mcn *mcn_info)
+{
+	int stat;
+	char mcnbuf[24];
+	ide_drive_t *drive = cdi->handle;
+
+/* get MCN */
+	if ((stat = cdrom_read_subchannel(drive, 2, mcnbuf, sizeof (mcnbuf), NULL)))
+		return stat;
+
+	memcpy (mcn_info->medium_catalog_number, mcnbuf+9,
+		sizeof (mcn_info->medium_catalog_number)-1);
+	mcn_info->medium_catalog_number[sizeof (mcn_info->medium_catalog_number)-1]
+		= '\0';
+
+	return 0;
+}
+
+
+
+/****************************************************************************
+ * Other driver requests (open, close, check media change).
+ */
+
+static
+int ide_cdrom_check_media_change_real (struct cdrom_device_info *cdi,
+				       int slot_nr)
+{
+	ide_drive_t *drive = cdi->handle;
+	int retval;
+	
+	if (slot_nr == CDSL_CURRENT) {
+		(void) cdrom_check_status(drive, NULL);
+		retval = CDROM_STATE_FLAGS(drive)->media_changed;
+		CDROM_STATE_FLAGS(drive)->media_changed = 0;
+		return retval;
+	} else {
+		return -EINVAL;
+	}
+}
+
+
+static
+int ide_cdrom_open_real (struct cdrom_device_info *cdi, int purpose)
+{
+	return 0;
+}
+
+/*
+ * Close down the device.  Invalidate all cached blocks.
+ */
+
+static
+void ide_cdrom_release_real (struct cdrom_device_info *cdi)
+{
+	ide_drive_t *drive = cdi->handle;
+
+	if (!cdi->use_count)
+		CDROM_STATE_FLAGS(drive)->toc_valid = 0;
+}
+
+
+
+/****************************************************************************
+ * Device initialization.
+ */
+static struct cdrom_device_ops ide_cdrom_dops = {
+	.open			= ide_cdrom_open_real,
+	.release		= ide_cdrom_release_real,
+	.drive_status		= ide_cdrom_drive_status,
+	.media_changed		= ide_cdrom_check_media_change_real,
+	.tray_move		= ide_cdrom_tray_move,
+	.lock_door		= ide_cdrom_lock_door,
+	.select_speed		= ide_cdrom_select_speed,
+	.get_last_session	= ide_cdrom_get_last_session,
+	.get_mcn		= ide_cdrom_get_mcn,
+	.reset			= ide_cdrom_reset,
+	.audio_ioctl		= ide_cdrom_audio_ioctl,
+	.dev_ioctl		= ide_cdrom_dev_ioctl,
+	.capability		= CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK |
+				CDC_SELECT_SPEED | CDC_SELECT_DISC |
+				CDC_MULTI_SESSION | CDC_MCN |
+				CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET |
+				CDC_IOCTLS | CDC_DRIVE_STATUS | CDC_CD_R |
+				CDC_CD_RW | CDC_DVD | CDC_DVD_R| CDC_DVD_RAM |
+				CDC_GENERIC_PACKET | CDC_MO_DRIVE | CDC_MRW |
+				CDC_MRW_W | CDC_RAM,
+	.generic_packet		= ide_cdrom_packet,
+};
+
+static int ide_cdrom_register (ide_drive_t *drive, int nslots)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *devinfo = &info->devinfo;
+
+	devinfo->ops = &ide_cdrom_dops;
+	devinfo->mask = 0;
+	devinfo->speed = CDROM_STATE_FLAGS(drive)->current_speed;
+	devinfo->capacity = nslots;
+	devinfo->handle = drive;
+	strcpy(devinfo->name, drive->name);
+	
+	/* set capability mask to match the probe. */
+	if (!CDROM_CONFIG_FLAGS(drive)->cd_r)
+		devinfo->mask |= CDC_CD_R;
+	if (!CDROM_CONFIG_FLAGS(drive)->cd_rw)
+		devinfo->mask |= CDC_CD_RW;
+	if (!CDROM_CONFIG_FLAGS(drive)->dvd)
+		devinfo->mask |= CDC_DVD;
+	if (!CDROM_CONFIG_FLAGS(drive)->dvd_r)
+		devinfo->mask |= CDC_DVD_R;
+	if (!CDROM_CONFIG_FLAGS(drive)->dvd_ram)
+		devinfo->mask |= CDC_DVD_RAM;
+	if (!CDROM_CONFIG_FLAGS(drive)->is_changer)
+		devinfo->mask |= CDC_SELECT_DISC;
+	if (!CDROM_CONFIG_FLAGS(drive)->audio_play)
+		devinfo->mask |= CDC_PLAY_AUDIO;
+	if (!CDROM_CONFIG_FLAGS(drive)->close_tray)
+		devinfo->mask |= CDC_CLOSE_TRAY;
+	if (!CDROM_CONFIG_FLAGS(drive)->mo_drive)
+		devinfo->mask |= CDC_MO_DRIVE;
+
+	devinfo->disk = info->disk;
+	return register_cdrom(devinfo);
+}
+
+static
+int ide_cdrom_probe_capabilities (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
+	struct atapi_capabilities_page cap;
+	int nslots = 1;
+
+	if (drive->media == ide_optical) {
+		CDROM_CONFIG_FLAGS(drive)->mo_drive = 1;
+		CDROM_CONFIG_FLAGS(drive)->ram = 1;
+		printk(KERN_ERR "%s: ATAPI magneto-optical drive\n", drive->name);
+		return nslots;
+	}
+
+	if (CDROM_CONFIG_FLAGS(drive)->nec260 ||
+	    !strcmp(drive->id->model,"STINGRAY 8422 IDE 8X CD-ROM 7-27-95")) {
+		CDROM_CONFIG_FLAGS(drive)->no_eject = 0;
+		CDROM_CONFIG_FLAGS(drive)->audio_play = 1;
+		return nslots;
+	}
+
+	/*
+	 * we have to cheat a little here. the packet will eventually
+	 * be queued with ide_cdrom_packet(), which extracts the
+	 * drive from cdi->handle. Since this device hasn't been
+	 * registered with the Uniform layer yet, it can't do this.
+	 * Same goes for cdi->ops.
+	 */
+	cdi->handle = drive;
+	cdi->ops = &ide_cdrom_dops;
+
+	if (ide_cdrom_get_capabilities(drive, &cap))
+		return 0;
+
+	if (cap.lock == 0)
+		CDROM_CONFIG_FLAGS(drive)->no_doorlock = 1;
+	if (cap.eject)
+		CDROM_CONFIG_FLAGS(drive)->no_eject = 0;
+	if (cap.cd_r_write)
+		CDROM_CONFIG_FLAGS(drive)->cd_r = 1;
+	if (cap.cd_rw_write) {
+		CDROM_CONFIG_FLAGS(drive)->cd_rw = 1;
+		CDROM_CONFIG_FLAGS(drive)->ram = 1;
+	}
+	if (cap.test_write)
+		CDROM_CONFIG_FLAGS(drive)->test_write = 1;
+	if (cap.dvd_ram_read || cap.dvd_r_read || cap.dvd_rom)
+		CDROM_CONFIG_FLAGS(drive)->dvd = 1;
+	if (cap.dvd_ram_write) {
+		CDROM_CONFIG_FLAGS(drive)->dvd_ram = 1;
+		CDROM_CONFIG_FLAGS(drive)->ram = 1;
+	}
+	if (cap.dvd_r_write)
+		CDROM_CONFIG_FLAGS(drive)->dvd_r = 1;
+	if (cap.audio_play)
+		CDROM_CONFIG_FLAGS(drive)->audio_play = 1;
+	if (cap.mechtype == mechtype_caddy || cap.mechtype == mechtype_popup)
+		CDROM_CONFIG_FLAGS(drive)->close_tray = 0;
+
+	/* Some drives used by Apple don't advertise audio play
+	 * but they do support reading TOC & audio datas
+	 */
+	if (strcmp(drive->id->model, "MATSHITADVD-ROM SR-8187") == 0 ||
+	    strcmp(drive->id->model, "MATSHITADVD-ROM SR-8186") == 0 ||
+	    strcmp(drive->id->model, "MATSHITADVD-ROM SR-8176") == 0 ||
+	    strcmp(drive->id->model, "MATSHITADVD-ROM SR-8174") == 0)
+		CDROM_CONFIG_FLAGS(drive)->audio_play = 1;
+
+#if ! STANDARD_ATAPI
+	if (cdi->sanyo_slot > 0) {
+		CDROM_CONFIG_FLAGS(drive)->is_changer = 1;
+		nslots = 3;
+	}
+
+	else
+#endif /* not STANDARD_ATAPI */
+	if (cap.mechtype == mechtype_individual_changer ||
+	    cap.mechtype == mechtype_cartridge_changer) {
+		if ((nslots = cdrom_number_of_slots(cdi)) > 1) {
+			CDROM_CONFIG_FLAGS(drive)->is_changer = 1;
+			CDROM_CONFIG_FLAGS(drive)->supp_disc_present = 1;
+		}
+	}
+
+	ide_cdrom_update_speed(drive, &cap);
+	/* don't print speed if the drive reported 0.
+	 */
+	printk(KERN_INFO "%s: ATAPI", drive->name);
+	if (CDROM_CONFIG_FLAGS(drive)->max_speed)
+		printk(" %dX", CDROM_CONFIG_FLAGS(drive)->max_speed);
+	printk(" %s", CDROM_CONFIG_FLAGS(drive)->dvd ? "DVD-ROM" : "CD-ROM");
+
+	if (CDROM_CONFIG_FLAGS(drive)->dvd_r|CDROM_CONFIG_FLAGS(drive)->dvd_ram)
+        	printk(" DVD%s%s", 
+        	(CDROM_CONFIG_FLAGS(drive)->dvd_r)? "-R" : "", 
+        	(CDROM_CONFIG_FLAGS(drive)->dvd_ram)? "-RAM" : "");
+
+        if (CDROM_CONFIG_FLAGS(drive)->cd_r|CDROM_CONFIG_FLAGS(drive)->cd_rw) 
+        	printk(" CD%s%s", 
+        	(CDROM_CONFIG_FLAGS(drive)->cd_r)? "-R" : "", 
+        	(CDROM_CONFIG_FLAGS(drive)->cd_rw)? "/RW" : "");
+
+        if (CDROM_CONFIG_FLAGS(drive)->is_changer) 
+        	printk(" changer w/%d slots", nslots);
+        else 	
+        	printk(" drive");
+
+	printk(", %dkB Cache", be16_to_cpu(cap.buffer_size));
+
+	if (drive->using_dma)
+		ide_dma_verbose(drive);
+
+	printk("\n");
+
+	return nslots;
+}
+
+static void ide_cdrom_add_settings(ide_drive_t *drive)
+{
+	ide_add_setting(drive,	"dsc_overlap",		SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1,	1, &drive->dsc_overlap, NULL);
+}
+
+/*
+ * standard prep_rq_fn that builds 10 byte cmds
+ */
+static int ide_cdrom_prep_fs(request_queue_t *q, struct request *rq)
+{
+	int hard_sect = queue_hardsect_size(q);
+	long block = (long)rq->hard_sector / (hard_sect >> 9);
+	unsigned long blocks = rq->hard_nr_sectors / (hard_sect >> 9);
+
+	memset(rq->cmd, 0, sizeof(rq->cmd));
+
+	if (rq_data_dir(rq) == READ)
+		rq->cmd[0] = GPCMD_READ_10;
+	else
+		rq->cmd[0] = GPCMD_WRITE_10;
+
+	/*
+	 * fill in lba
+	 */
+	rq->cmd[2] = (block >> 24) & 0xff;
+	rq->cmd[3] = (block >> 16) & 0xff;
+	rq->cmd[4] = (block >>  8) & 0xff;
+	rq->cmd[5] = block & 0xff;
+
+	/*
+	 * and transfer length
+	 */
+	rq->cmd[7] = (blocks >> 8) & 0xff;
+	rq->cmd[8] = blocks & 0xff;
+	rq->cmd_len = 10;
+	return BLKPREP_OK;
+}
+
+/*
+ * Most of the SCSI commands are supported directly by ATAPI devices.
+ * This transform handles the few exceptions.
+ */
+static int ide_cdrom_prep_pc(struct request *rq)
+{
+	u8 *c = rq->cmd;
+
+	/*
+	 * Transform 6-byte read/write commands to the 10-byte version
+	 */
+	if (c[0] == READ_6 || c[0] == WRITE_6) {
+		c[8] = c[4];
+		c[5] = c[3];
+		c[4] = c[2];
+		c[3] = c[1] & 0x1f;
+		c[2] = 0;
+		c[1] &= 0xe0;
+		c[0] += (READ_10 - READ_6);
+		rq->cmd_len = 10;
+		return BLKPREP_OK;
+	}
+
+	/*
+	 * it's silly to pretend we understand 6-byte sense commands, just
+	 * reject with ILLEGAL_REQUEST and the caller should take the
+	 * appropriate action
+	 */
+	if (c[0] == MODE_SENSE || c[0] == MODE_SELECT) {
+		rq->errors = ILLEGAL_REQUEST;
+		return BLKPREP_KILL;
+	}
+	
+	return BLKPREP_OK;
+}
+
+static int ide_cdrom_prep_fn(request_queue_t *q, struct request *rq)
+{
+	if (rq->flags & REQ_CMD)
+		return ide_cdrom_prep_fs(q, rq);
+	else if (rq->flags & REQ_BLOCK_PC)
+		return ide_cdrom_prep_pc(rq);
+
+	return 0;
+}
+
+static
+int ide_cdrom_setup (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
+	int nslots;
+
+	blk_queue_prep_rq(drive->queue, ide_cdrom_prep_fn);
+	blk_queue_dma_alignment(drive->queue, 31);
+	drive->queue->unplug_delay = (1 * HZ) / 1000;
+	if (!drive->queue->unplug_delay)
+		drive->queue->unplug_delay = 1;
+
+	drive->special.all	= 0;
+
+	CDROM_STATE_FLAGS(drive)->media_changed = 1;
+	CDROM_STATE_FLAGS(drive)->toc_valid     = 0;
+	CDROM_STATE_FLAGS(drive)->door_locked   = 0;
+
+#if NO_DOOR_LOCKING
+	CDROM_CONFIG_FLAGS(drive)->no_doorlock = 1;
+#else
+	CDROM_CONFIG_FLAGS(drive)->no_doorlock = 0;
+#endif
+
+	CDROM_CONFIG_FLAGS(drive)->drq_interrupt = ((drive->id->config & 0x0060) == 0x20);
+	CDROM_CONFIG_FLAGS(drive)->is_changer = 0;
+	CDROM_CONFIG_FLAGS(drive)->cd_r = 0;
+	CDROM_CONFIG_FLAGS(drive)->cd_rw = 0;
+	CDROM_CONFIG_FLAGS(drive)->test_write = 0;
+	CDROM_CONFIG_FLAGS(drive)->dvd = 0;
+	CDROM_CONFIG_FLAGS(drive)->dvd_r = 0;
+	CDROM_CONFIG_FLAGS(drive)->dvd_ram = 0;
+	CDROM_CONFIG_FLAGS(drive)->no_eject = 1;
+	CDROM_CONFIG_FLAGS(drive)->supp_disc_present = 0;
+	CDROM_CONFIG_FLAGS(drive)->audio_play = 0;
+	CDROM_CONFIG_FLAGS(drive)->close_tray = 1;
+	
+	/* limit transfer size per interrupt. */
+	CDROM_CONFIG_FLAGS(drive)->limit_nframes = 0;
+	/* a testament to the nice quality of Samsung drives... */
+	if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-2430"))
+		CDROM_CONFIG_FLAGS(drive)->limit_nframes = 1;
+	else if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-2432"))
+		CDROM_CONFIG_FLAGS(drive)->limit_nframes = 1;
+	/* the 3231 model does not support the SET_CD_SPEED command */
+	else if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-3231"))
+		cdi->mask |= CDC_SELECT_SPEED;
+
+#if ! STANDARD_ATAPI
+	/* by default Sanyo 3 CD changer support is turned off and
+           ATAPI Rev 2.2+ standard support for CD changers is used */
+	cdi->sanyo_slot = 0;
+
+	CDROM_CONFIG_FLAGS(drive)->nec260 = 0;
+	CDROM_CONFIG_FLAGS(drive)->toctracks_as_bcd = 0;
+	CDROM_CONFIG_FLAGS(drive)->tocaddr_as_bcd = 0;
+	CDROM_CONFIG_FLAGS(drive)->playmsf_as_bcd = 0;
+	CDROM_CONFIG_FLAGS(drive)->subchan_as_bcd = 0;
+
+	if (strcmp (drive->id->model, "V003S0DS") == 0 &&
+	    drive->id->fw_rev[4] == '1' &&
+	    drive->id->fw_rev[6] <= '2') {
+		/* Vertos 300.
+		   Some versions of this drive like to talk BCD. */
+		CDROM_CONFIG_FLAGS(drive)->toctracks_as_bcd = 1;
+		CDROM_CONFIG_FLAGS(drive)->tocaddr_as_bcd = 1;
+		CDROM_CONFIG_FLAGS(drive)->playmsf_as_bcd = 1;
+		CDROM_CONFIG_FLAGS(drive)->subchan_as_bcd = 1;
+	}
+
+	else if (strcmp (drive->id->model, "V006E0DS") == 0 &&
+	    drive->id->fw_rev[4] == '1' &&
+	    drive->id->fw_rev[6] <= '2') {
+		/* Vertos 600 ESD. */
+		CDROM_CONFIG_FLAGS(drive)->toctracks_as_bcd = 1;
+	}
+	else if (strcmp(drive->id->model, "NEC CD-ROM DRIVE:260") == 0 &&
+		 strncmp(drive->id->fw_rev, "1.01", 4) == 0) { /* FIXME */
+		/* Old NEC260 (not R).
+		   This drive was released before the 1.2 version
+		   of the spec. */
+		CDROM_CONFIG_FLAGS(drive)->tocaddr_as_bcd = 1;
+		CDROM_CONFIG_FLAGS(drive)->playmsf_as_bcd = 1;
+		CDROM_CONFIG_FLAGS(drive)->subchan_as_bcd = 1;
+		CDROM_CONFIG_FLAGS(drive)->nec260         = 1;
+	}
+	else if (strcmp(drive->id->model, "WEARNES CDD-120") == 0 &&
+		 strncmp(drive->id->fw_rev, "A1.1", 4) == 0) { /* FIXME */
+		/* Wearnes */
+		CDROM_CONFIG_FLAGS(drive)->playmsf_as_bcd = 1;
+		CDROM_CONFIG_FLAGS(drive)->subchan_as_bcd = 1;
+	}
+        /* Sanyo 3 CD changer uses a non-standard command
+           for CD changing */
+        else if ((strcmp(drive->id->model, "CD-ROM CDR-C3 G") == 0) ||
+                 (strcmp(drive->id->model, "CD-ROM CDR-C3G") == 0) ||
+                 (strcmp(drive->id->model, "CD-ROM CDR_C36") == 0)) {
+                 /* uses CD in slot 0 when value is set to 3 */
+                 cdi->sanyo_slot = 3;
+        }
+#endif /* not STANDARD_ATAPI */
+
+	info->toc		= NULL;
+	info->buffer		= NULL;
+	info->sector_buffered	= 0;
+	info->nsectors_buffered	= 0;
+	info->changer_info      = NULL;
+	info->last_block	= 0;
+	info->start_seek	= 0;
+
+	nslots = ide_cdrom_probe_capabilities (drive);
+
+	/*
+	 * set correct block size
+	 */
+	blk_queue_hardsect_size(drive->queue, CD_FRAMESIZE);
+
+	if (drive->autotune == IDE_TUNE_DEFAULT ||
+	    drive->autotune == IDE_TUNE_AUTO)
+		drive->dsc_overlap = (drive->next != drive);
+#if 0
+	drive->dsc_overlap = (HWIF(drive)->no_dsc) ? 0 : 1;
+	if (HWIF(drive)->no_dsc) {
+		printk(KERN_INFO "ide-cd: %s: disabling DSC overlap\n",
+			drive->name);
+		drive->dsc_overlap = 0;
+	}
+#endif
+
+	if (ide_cdrom_register(drive, nslots)) {
+		printk (KERN_ERR "%s: ide_cdrom_setup failed to register device with the cdrom driver.\n", drive->name);
+		info->devinfo.handle = NULL;
+		return 1;
+	}
+	ide_cdrom_add_settings(drive);
+	return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+static
+sector_t ide_cdrom_capacity (ide_drive_t *drive)
+{
+	unsigned long capacity, sectors_per_frame;
+
+	if (cdrom_read_capacity(drive, &capacity, &sectors_per_frame, NULL))
+		return 0;
+
+	return capacity * sectors_per_frame;
+}
+#endif
+
+static int ide_cd_remove(struct device *dev)
+{
+	ide_drive_t *drive = to_ide_device(dev);
+	struct cdrom_info *info = drive->driver_data;
+
+	ide_unregister_subdriver(drive, info->driver);
+
+	del_gendisk(info->disk);
+
+	ide_cd_put(info);
+
+	return 0;
+}
+
+static void ide_cd_release(struct kref *kref)
+{
+	struct cdrom_info *info = to_ide_cd(kref);
+	struct cdrom_device_info *devinfo = &info->devinfo;
+	ide_drive_t *drive = info->drive;
+	struct gendisk *g = info->disk;
+
+	kfree(info->buffer);
+	kfree(info->toc);
+	kfree(info->changer_info);
+	if (devinfo->handle == drive && unregister_cdrom(devinfo))
+		printk(KERN_ERR "%s: %s failed to unregister device from the cdrom "
+				"driver.\n", __FUNCTION__, drive->name);
+	drive->dsc_overlap = 0;
+	drive->driver_data = NULL;
+	blk_queue_prep_rq(drive->queue, NULL);
+	g->private_data = NULL;
+	put_disk(g);
+	kfree(info);
+}
+
+static int ide_cd_probe(struct device *);
+
+#ifdef CONFIG_PROC_FS
+static int proc_idecd_read_capacity
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	ide_drive_t *drive = data;
+	int len;
+
+	len = sprintf(page,"%llu\n", (long long)ide_cdrom_capacity(drive));
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static ide_proc_entry_t idecd_proc[] = {
+	{ "capacity", S_IFREG|S_IRUGO, proc_idecd_read_capacity, NULL },
+	{ NULL, 0, NULL, NULL }
+};
+#else
+# define idecd_proc	NULL
+#endif
+
+static ide_driver_t ide_cdrom_driver = {
+	.gen_driver = {
+		.owner		= THIS_MODULE,
+		.name		= "ide-cdrom",
+		.bus		= &ide_bus_type,
+		.probe		= ide_cd_probe,
+		.remove		= ide_cd_remove,
+	},
+	.version		= IDECD_VERSION,
+	.media			= ide_cdrom,
+	.supports_dsc_overlap	= 1,
+	.do_request		= ide_do_rw_cdrom,
+	.end_request		= ide_end_request,
+	.error			= __ide_error,
+	.abort			= __ide_abort,
+	.proc			= idecd_proc,
+};
+
+static int idecd_open(struct inode * inode, struct file * file)
+{
+	struct gendisk *disk = inode->i_bdev->bd_disk;
+	struct cdrom_info *info;
+	ide_drive_t *drive;
+	int rc = -ENOMEM;
+
+	if (!(info = ide_cd_get(disk)))
+		return -ENXIO;
+
+	drive = info->drive;
+
+	drive->usage++;
+
+	if (!info->buffer)
+		info->buffer = kmalloc(SECTOR_BUFFER_SIZE,
+					GFP_KERNEL|__GFP_REPEAT);
+        if (!info->buffer || (rc = cdrom_open(&info->devinfo, inode, file)))
+		drive->usage--;
+
+	if (rc < 0)
+		ide_cd_put(info);
+
+	return rc;
+}
+
+static int idecd_release(struct inode * inode, struct file * file)
+{
+	struct gendisk *disk = inode->i_bdev->bd_disk;
+	struct cdrom_info *info = ide_cd_g(disk);
+	ide_drive_t *drive = info->drive;
+
+	cdrom_release (&info->devinfo, file);
+	drive->usage--;
+
+	ide_cd_put(info);
+
+	return 0;
+}
+
+static int idecd_ioctl (struct inode *inode, struct file *file,
+			unsigned int cmd, unsigned long arg)
+{
+	struct block_device *bdev = inode->i_bdev;
+	struct cdrom_info *info = ide_cd_g(bdev->bd_disk);
+	int err;
+
+	err  = generic_ide_ioctl(info->drive, file, bdev, cmd, arg);
+	if (err == -EINVAL)
+		err = cdrom_ioctl(file, &info->devinfo, inode, cmd, arg);
+
+	return err;
+}
+
+static int idecd_media_changed(struct gendisk *disk)
+{
+	struct cdrom_info *info = ide_cd_g(disk);
+	return cdrom_media_changed(&info->devinfo);
+}
+
+static int idecd_revalidate_disk(struct gendisk *disk)
+{
+	struct cdrom_info *info = ide_cd_g(disk);
+	struct request_sense sense;
+	cdrom_read_toc(info->drive, &sense);
+	return  0;
+}
+
+static struct block_device_operations idecd_ops = {
+	.owner		= THIS_MODULE,
+	.open		= idecd_open,
+	.release	= idecd_release,
+	.ioctl		= idecd_ioctl,
+	.media_changed	= idecd_media_changed,
+	.revalidate_disk= idecd_revalidate_disk
+};
+
+/* options */
+static char *ignore = NULL;
+
+module_param(ignore, charp, 0400);
+MODULE_DESCRIPTION("ATAPI CD-ROM Driver");
+
+static int ide_cd_probe(struct device *dev)
+{
+	ide_drive_t *drive = to_ide_device(dev);
+	struct cdrom_info *info;
+	struct gendisk *g;
+	struct request_sense sense;
+
+	if (!strstr("ide-cdrom", drive->driver_req))
+		goto failed;
+	if (!drive->present)
+		goto failed;
+	if (drive->media != ide_cdrom && drive->media != ide_optical)
+		goto failed;
+	/* skip drives that we were told to ignore */
+	if (ignore != NULL) {
+		if (strstr(ignore, drive->name)) {
+			printk(KERN_INFO "ide-cd: ignoring drive %s\n", drive->name);
+			goto failed;
+		}
+	}
+	if (drive->scsi) {
+		printk(KERN_INFO "ide-cd: passing drive %s to ide-scsi emulation.\n", drive->name);
+		goto failed;
+	}
+	info = kzalloc(sizeof(struct cdrom_info), GFP_KERNEL);
+	if (info == NULL) {
+		printk(KERN_ERR "%s: Can't allocate a cdrom structure\n", drive->name);
+		goto failed;
+	}
+
+	g = alloc_disk(1 << PARTN_BITS);
+	if (!g)
+		goto out_free_cd;
+
+	ide_init_disk(g, drive);
+
+	ide_register_subdriver(drive, &ide_cdrom_driver);
+
+	kref_init(&info->kref);
+
+	info->drive = drive;
+	info->driver = &ide_cdrom_driver;
+	info->disk = g;
+
+	g->private_data = &info->driver;
+
+	drive->driver_data = info;
+
+	g->minors = 1;
+	snprintf(g->devfs_name, sizeof(g->devfs_name),
+			"%s/cd", drive->devfs_name);
+	g->driverfs_dev = &drive->gendev;
+	g->flags = GENHD_FL_CD | GENHD_FL_REMOVABLE;
+	if (ide_cdrom_setup(drive)) {
+		struct cdrom_device_info *devinfo = &info->devinfo;
+		ide_unregister_subdriver(drive, &ide_cdrom_driver);
+		kfree(info->buffer);
+		kfree(info->toc);
+		kfree(info->changer_info);
+		if (devinfo->handle == drive && unregister_cdrom(devinfo))
+			printk (KERN_ERR "%s: ide_cdrom_cleanup failed to unregister device from the cdrom driver.\n", drive->name);
+		kfree(info);
+		drive->driver_data = NULL;
+		goto failed;
+	}
+
+	cdrom_read_toc(drive, &sense);
+	g->fops = &idecd_ops;
+	g->flags |= GENHD_FL_REMOVABLE;
+	add_disk(g);
+	return 0;
+
+out_free_cd:
+	kfree(info);
+failed:
+	return -ENODEV;
+}
+
+static void __exit ide_cdrom_exit(void)
+{
+	driver_unregister(&ide_cdrom_driver.gen_driver);
+}
+
+static int __init ide_cdrom_init(void)
+{
+	return driver_register(&ide_cdrom_driver.gen_driver);
+}
+
+module_init(ide_cdrom_init);
+module_exit(ide_cdrom_exit);
+MODULE_LICENSE("GPL");
diff -urN oldtree/drivers/ide/ide-iops.c newtree/drivers/ide/ide-iops.c
--- oldtree/drivers/ide/ide-iops.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/ide/ide-iops.c	2006-02-13 19:19:59.900527424 -0500
@@ -1243,6 +1243,7 @@
 		 */
 		if (stat == 0xff)
 			return -ENODEV;
+		touch_softlockup_watchdog();
 	}
 	return -EBUSY;
 }
diff -urN oldtree/drivers/ide/pci/aec62xx.c newtree/drivers/ide/pci/aec62xx.c
--- oldtree/drivers/ide/pci/aec62xx.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/ide/pci/aec62xx.c	2006-02-13 19:19:59.901527272 -0500
@@ -262,6 +262,21 @@
 	else
 		pci_set_drvdata(dev, (void *) aec6xxx_34_base);
 
+	/* These are necessary to get AEC6280 Macintosh cards to work */
+	if ((dev->device == PCI_DEVICE_ID_ARTOP_ATP865) ||
+	    (dev->device == PCI_DEVICE_ID_ARTOP_ATP865R)) {
+		u8 reg49h = 0, reg4ah = 0;
+		/* Clear reset and test bits.  */
+		pci_read_config_byte(dev, 0x49, &reg49h);
+		pci_write_config_byte(dev, 0x49, reg49h & ~0x30);
+		/* Enable chip interrupt output.  */
+		pci_read_config_byte(dev, 0x4a, &reg4ah);
+		pci_write_config_byte(dev, 0x4a, reg4ah & ~0x01);
+		/* Enable burst mode. */
+		pci_read_config_byte(dev, 0x4a, &reg4ah);
+		pci_write_config_byte(dev, 0x4a, reg4ah | 0x80);
+	}
+
 	return dev->irq;
 }
 
diff -urN oldtree/drivers/ide/pci/hpt366.c newtree/drivers/ide/pci/hpt366.c
--- oldtree/drivers/ide/pci/hpt366.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/ide/pci/hpt366.c	2006-02-13 19:19:59.904526816 -0500
@@ -1288,6 +1288,10 @@
 				goto init_hpt37X_done;
 			}
 		}
+		if (!pci_get_drvdata(dev)) {
+			printk("No Clock Stabilization!!!\n");
+			return;
+		}
 pll_recal:
 		if (adjust & 1)
 			pll -= (adjust >> 1);
diff -urN oldtree/drivers/ide/pci/sis5513.c newtree/drivers/ide/pci/sis5513.c
--- oldtree/drivers/ide/pci/sis5513.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/ide/pci/sis5513.c	2006-02-13 19:19:59.904526816 -0500
@@ -112,6 +112,7 @@
 
 	{ "SiS5596",	PCI_DEVICE_ID_SI_5596,	ATA_16   },
 	{ "SiS5571",	PCI_DEVICE_ID_SI_5571,	ATA_16   },
+	{ "SiS5517",	PCI_DEVICE_ID_SI_5517,	ATA_16   },
 	{ "SiS551x",	PCI_DEVICE_ID_SI_5511,	ATA_16   },
 };
 
@@ -524,6 +525,7 @@
 			case 3:		test1 = 0x30|0x03; break;
 			case 2:		test1 = 0x40|0x04; break;
 			case 1:		test1 = 0x60|0x07; break;
+			case 0:		test1 = 0x00; break;
 			default:	break;
 		}
 		pci_write_config_byte(dev, drive_pci, test1);
diff -urN oldtree/drivers/infiniband/Kconfig newtree/drivers/infiniband/Kconfig
--- oldtree/drivers/infiniband/Kconfig	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/infiniband/Kconfig	2006-02-13 19:19:59.905526664 -0500
@@ -31,6 +31,8 @@
 
 source "drivers/infiniband/hw/mthca/Kconfig"
 
+source "drivers/infiniband/hw/ehca/Kconfig"
+
 source "drivers/infiniband/ulp/ipoib/Kconfig"
 
 source "drivers/infiniband/ulp/srp/Kconfig"
diff -urN oldtree/drivers/infiniband/Makefile newtree/drivers/infiniband/Makefile
--- oldtree/drivers/infiniband/Makefile	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/infiniband/Makefile	2006-02-13 19:19:59.905526664 -0500
@@ -1,4 +1,5 @@
 obj-$(CONFIG_INFINIBAND)		+= core/
 obj-$(CONFIG_INFINIBAND_MTHCA)		+= hw/mthca/
+obj-$(CONFIG_INFINIBAND_EHCA)		+= hw/ehca/
 obj-$(CONFIG_INFINIBAND_IPOIB)		+= ulp/ipoib/
 obj-$(CONFIG_INFINIBAND_SRP)		+= ulp/srp/
diff -urN oldtree/drivers/infiniband/hw/ehca/Kconfig newtree/drivers/infiniband/hw/ehca/Kconfig
--- oldtree/drivers/infiniband/hw/ehca/Kconfig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/Kconfig	2006-02-13 19:19:59.907526360 -0500
@@ -0,0 +1,6 @@
+config INFINIBAND_EHCA
+       tristate "eHCA support"
+       depends on IBMEBUS && INFINIBAND
+       ---help---
+       This is a low level device driver for the IBM
+       GX based Host channel adapters (HCAs)
diff -urN oldtree/drivers/infiniband/hw/ehca/Makefile newtree/drivers/infiniband/hw/ehca/Makefile
--- oldtree/drivers/infiniband/hw/ehca/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/Makefile	2006-02-13 19:19:59.907526360 -0500
@@ -0,0 +1,8 @@
+obj-$(CONFIG_INFINIBAND_EHCA) += hcad_mod.o 
+
+hcad_mod-y = ehca_main.o ehca_hca.o ipz_pt_fn.o ehca_classes.o ehca_av.o \
+	ehca_pd.o ehca_mrmw.o ehca_cq.o ehca_sqp.o ehca_qp.o hcp_sense.o \
+	ehca_eq.o ehca_irq.o hcp_phyp.o ehca_mcast.o ehca_reqs.o \
+	ehca_uverbs.o 
+
+CFLAGS +=-DP_SERIES -DEHCA_USE_HCALL -DEHCA_USE_HCALL_KERNEL
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_asm.h newtree/drivers/infiniband/hw/ehca/ehca_asm.h
--- oldtree/drivers/infiniband/hw/ehca/ehca_asm.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_asm.h	2006-02-13 19:19:59.907526360 -0500
@@ -0,0 +1,58 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  Some helper macros with assembler instructions
+ *
+ *  Authors: Khadija Souissi <souissik@de.ibm.com>
+ *           Christoph Raisch <raisch@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_asm.h,v 1.6 2005/11/03 08:47:54 schickhj Exp $
+ */
+
+
+#ifndef __EHCA_ASM_H__
+#define __EHCA_ASM_H__
+
+#if defined(CONFIG_PPC_PSERIES) || defined (__PPC64__) || defined (__PPC__)
+
+#define clear_cacheline(adr) __asm__ __volatile("dcbz 0,%0"::"r"(adr))
+
+#elif defined(CONFIG_ARCH_S390)
+#error "unsupported yet"
+#else
+#error "invalid platform"
+#endif
+
+#endif /* __EHCA_ASM_H__ */
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_av.c newtree/drivers/infiniband/hw/ehca/ehca_av.c
--- oldtree/drivers/infiniband/hw/ehca/ehca_av.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_av.c	2006-02-13 19:19:59.908526208 -0500
@@ -0,0 +1,258 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  adress vector functions
+ *
+ *  Authors: Reinhard Ernst <rernst@de.ibm.com> 
+ *           Christoph Raisch <raisch@de.ibm.com> 
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_av.c,v 1.26 2005/12/12 12:20:04 nguyen Exp $
+ */
+
+
+#define DEB_PREFIX "ehav"
+
+#include "ehca_kernel.h"
+#include "ehca_tools.h"
+#include "ehca_iverbs.h"
+#include "hcp_if.h"
+
+struct ib_ah *ehca_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr)
+{
+        extern int ehca_static_rate;
+	int retcode = 0;
+	struct ehca_av *av = NULL;
+
+	EHCA_CHECK_PD_P(pd);
+	EHCA_CHECK_ADR_P(ah_attr);
+
+	EDEB_EN(7,"pd=%p ah_attr=%p", pd, ah_attr);
+
+	av = ehca_av_new();
+	if (!av) {
+		EDEB_ERR(4,"Out of memory pd=%p ah_attr=%p", pd, ah_attr);
+		retcode = -ENOMEM;
+		goto create_ah_exit0;
+	}
+
+	av->av.sl = ah_attr->sl;
+	av->av.dlid = ntohs(ah_attr->dlid);
+	av->av.slid_path_bits = ah_attr->src_path_bits;
+
+	if (ehca_static_rate < 0) {
+	        av->av.ipd = ah_attr->static_rate;
+	} else {
+	        av->av.ipd = ehca_static_rate;
+	}
+
+	av->av.lnh = ah_attr->ah_flags;
+	av->av.grh.word_0 |= EHCA_BMASK_SET(GRH_IPVERSION_MASK, 6);
+	av->av.grh.word_0 |= EHCA_BMASK_SET(GRH_TCLASS_MASK,
+					    ah_attr->grh.traffic_class);
+	av->av.grh.word_0 |= EHCA_BMASK_SET(GRH_FLOWLABEL_MASK,
+					    ah_attr->grh.flow_label);
+	av->av.grh.word_0 |= EHCA_BMASK_SET(GRH_HOPLIMIT_MASK,
+					    ah_attr->grh.hop_limit);
+	av->av.grh.word_0 |= EHCA_BMASK_SET(GRH_NEXTHEADER_MASK, 0x1B);
+	/* IB transport */
+	av->av.grh.word_0 = be64_to_cpu(av->av.grh.word_0);
+	/* set sgid in grh.word_1 */
+	if (ah_attr->ah_flags & IB_AH_GRH) {
+		int rc = 0;
+		struct ib_port_attr port_attr;
+		union ib_gid gid;
+		memset(&port_attr, 0, sizeof(port_attr));
+		rc = ehca_query_port(pd->device, ah_attr->port_num, 
+				     &port_attr);
+		if (rc != 0) { /* invalid port number */
+			retcode = -EINVAL;
+			EDEB_ERR(4, "Invalid port number "
+				 "ehca_query_port() returned %x "
+				 "pd=%p ah_attr=%p", rc, pd, ah_attr);
+			goto create_ah_exit1;
+		}
+		memset(&gid, 0, sizeof(gid));
+		rc = ehca_query_gid(pd->device,
+				    ah_attr->port_num,
+				    ah_attr->grh.sgid_index, &gid);
+		if (rc != 0) {
+			retcode = -EINVAL;
+			EDEB_ERR(4, "Failed to retrieve sgid "
+				 "ehca_query_gid() returned %x "
+				 "pd=%p ah_attr=%p", rc, pd, ah_attr);
+			goto create_ah_exit1;
+		}
+		memcpy(&av->av.grh.word_1, &gid, sizeof(gid));
+	}
+	/* for the time beeing we use a hard coded PMTU of 2048 Bytes */
+	av->av.pmtu = 4; /* TODO */
+
+	/* dgid comes in grh.word_3 */
+	memcpy(&av->av.grh.word_3, &ah_attr->grh.dgid,
+	       sizeof(ah_attr->grh.dgid));
+
+	EHCA_REGISTER_AV(device, pd);
+
+	EDEB_EX(7,"pd=%p ah_attr=%p av=%p", pd, ah_attr, av);
+	return (&av->ib_ah);
+
+ create_ah_exit1:
+	ehca_av_delete(av);
+
+ create_ah_exit0:
+	EDEB_EX(7,"retcode=%x pd=%p ah_attr=%p", retcode, pd, ah_attr);
+	return ERR_PTR(retcode);
+}
+
+int ehca_modify_ah(struct ib_ah *ah, struct ib_ah_attr *ah_attr)
+{
+	struct ehca_av *av = NULL;
+	struct ehca_ud_av new_ehca_av;
+	int ret = 0;
+
+	EHCA_CHECK_AV(ah);
+	EHCA_CHECK_ADR(ah_attr);
+
+	EDEB_EN(7,"ah=%p ah_attr=%p", ah, ah_attr);
+
+	memset(&new_ehca_av, 0, sizeof(new_ehca_av));
+	new_ehca_av.sl = ah_attr->sl;
+	new_ehca_av.dlid = ntohs(ah_attr->dlid);
+	new_ehca_av.slid_path_bits = ah_attr->src_path_bits;
+	new_ehca_av.ipd = ah_attr->static_rate;
+	new_ehca_av.lnh = EHCA_BMASK_SET(GRH_FLAG_MASK,
+					 ((ah_attr->ah_flags & IB_AH_GRH) > 0));
+	new_ehca_av.grh.word_0 = EHCA_BMASK_SET(GRH_TCLASS_MASK,
+						ah_attr->grh.traffic_class);
+	new_ehca_av.grh.word_0 |= EHCA_BMASK_SET(GRH_FLOWLABEL_MASK,
+						 ah_attr->grh.flow_label);
+	new_ehca_av.grh.word_0 |= EHCA_BMASK_SET(GRH_HOPLIMIT_MASK,
+						 ah_attr->grh.hop_limit);
+	new_ehca_av.grh.word_0 |= EHCA_BMASK_SET(GRH_NEXTHEADER_MASK, 0x1b);
+	new_ehca_av.grh.word_0 = be64_to_cpu(new_ehca_av.grh.word_0);
+
+	/* set sgid in grh.word_1 */
+	if (ah_attr->ah_flags & IB_AH_GRH) {
+		int rc = 0;
+		struct ib_port_attr port_attr;
+		union ib_gid gid;
+		memset(&port_attr, 0, sizeof(port_attr));
+		rc = ehca_query_port(ah->device, ah_attr->port_num, 
+				     &port_attr);
+		if (rc != 0) { /* invalid port number */
+			ret = -EINVAL;
+			EDEB_ERR(4, "Invalid port number "
+				 "ehca_query_port() returned %x "
+				 "ah=%p ah_attr=%p port_num=%x", 
+				 rc, ah, ah_attr, ah_attr->port_num);
+			goto modify_ah_exit1;
+		}
+		memset(&gid, 0, sizeof(gid));
+		rc = ehca_query_gid(ah->device,
+				    ah_attr->port_num,
+				    ah_attr->grh.sgid_index, &gid);
+		if (rc != 0) {
+			ret = -EINVAL;
+			EDEB_ERR(4, 
+				 "Failed to retrieve sgid "
+				 "ehca_query_gid() returned %x "
+				 "ah=%p ah_attr=%p port_num=%x "
+				 "sgid_index=%x", 
+				 rc, ah, ah_attr, ah_attr->port_num, 
+				 ah_attr->grh.sgid_index);
+			goto modify_ah_exit1;
+		}
+		memcpy(&new_ehca_av.grh.word_1, &gid, sizeof(gid));
+	}
+
+	new_ehca_av.pmtu = 4; /* TODO: see comment in create_ah() */
+
+	memcpy(&new_ehca_av.grh.word_3, &ah_attr->grh.dgid,
+	       sizeof(ah_attr->grh.dgid));
+
+	av = container_of(ah, struct ehca_av, ib_ah);
+	av->av = new_ehca_av;
+
+ modify_ah_exit1:
+	EDEB_EX(7,"ret=%x ah=%p ah_attr=%p", ret, ah, ah_attr);
+
+	return ret;
+}
+
+int ehca_query_ah(struct ib_ah *ah, struct ib_ah_attr *ah_attr)
+{
+	int ret = 0;
+	struct ehca_av *av = NULL;
+
+	EHCA_CHECK_AV(ah);
+	EHCA_CHECK_ADR(ah_attr);
+
+	EDEB_EN(7,"ah=%p ah_attr=%p", ah, ah_attr);
+
+	av = container_of(ah, struct ehca_av, ib_ah);
+	memcpy(&ah_attr->grh.dgid, &av->av.grh.word_3,
+	       sizeof(ah_attr->grh.dgid));
+	ah_attr->sl = av->av.sl;
+
+	ah_attr->dlid = av->av.dlid;
+
+	ah_attr->src_path_bits = av->av.slid_path_bits;
+	ah_attr->static_rate = av->av.ipd;
+	ah_attr->ah_flags = EHCA_BMASK_GET(GRH_FLAG_MASK, av->av.lnh);
+	ah_attr->grh.traffic_class = EHCA_BMASK_GET(GRH_TCLASS_MASK,
+						    av->av.grh.word_0);
+	ah_attr->grh.hop_limit = EHCA_BMASK_GET(GRH_HOPLIMIT_MASK,
+						av->av.grh.word_0);
+	ah_attr->grh.flow_label = EHCA_BMASK_GET(GRH_FLOWLABEL_MASK,
+						 av->av.grh.word_0);
+
+	EDEB_EX(7,"ah=%p ah_attr=%p ret=%x", ah, ah_attr, ret);
+	return ret;
+}
+
+int ehca_destroy_ah(struct ib_ah *ah)
+{
+	int ret = 0;
+
+	EHCA_CHECK_AV(ah);
+	EHCA_DEREGISTER_AV(ah);
+
+	EDEB_EN(7,"ah=%p", ah);
+
+	ehca_av_delete(container_of(ah, struct ehca_av, ib_ah));
+
+	EDEB_EX(7,"ret=%x ah=%p", ret, ah);
+	return ret;
+}
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_classes.c newtree/drivers/infiniband/hw/ehca/ehca_classes.c
--- oldtree/drivers/infiniband/hw/ehca/ehca_classes.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_classes.c	2006-02-13 19:19:59.908526208 -0500
@@ -0,0 +1,204 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  struct initialisations and allocation
+ *
+ *  Authors: Christoph Raisch <raisch@de.ibm.com> 
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_classes.c,v 1.18 2005/11/03 07:48:38 schickhj Exp $
+ */
+
+#define DEB_PREFIX "clas"
+#include "ehca_kernel.h"
+
+#include "ehca_classes.h"
+
+int ehca_eq_ctor(struct ehca_eq *eq)
+{
+	spin_lock_init(&eq->spinlock);
+
+	return H_Success;
+}
+
+int ehca_eq_dtor(struct ehca_eq *eq)
+{
+
+	return H_Success;
+}
+
+struct ehca_pd *ehca_pd_new(void)
+{
+	extern struct ehca_module ehca_module;
+	struct ehca_pd *me; 
+
+	me = kmem_cache_alloc(ehca_module.cache_pd, SLAB_KERNEL);
+	if (me == NULL)
+		return NULL;
+
+	memset(me, 0, sizeof(struct ehca_pd));
+
+	return me;
+}
+
+void ehca_pd_delete(struct ehca_pd *me)
+{
+	extern struct ehca_module ehca_module;
+
+	kmem_cache_free(ehca_module.cache_pd, me);
+}
+
+struct ehca_cq *ehca_cq_new(void)
+{
+	extern struct ehca_module ehca_module;
+	struct ehca_cq *me; 
+	
+	me = kmem_cache_alloc(ehca_module.cache_cq, SLAB_KERNEL);
+	if (me == NULL)
+		return NULL;
+
+	memset(me, 0, sizeof(struct ehca_cq));
+	spin_lock_init(&me->spinlock);
+	spin_lock_init(&me->cb_lock);
+
+	return me;
+}
+
+int ehca_cq_delete(struct ehca_cq *me)
+{
+	extern struct ehca_module ehca_module;
+
+	kmem_cache_free(ehca_module.cache_cq, me);
+
+	return H_Success;
+}
+
+struct ehca_qp *ehca_qp_new(void)
+{
+	extern struct ehca_module ehca_module;
+	struct ehca_qp *me; 
+	
+	me = kmem_cache_alloc(ehca_module.cache_qp, SLAB_KERNEL);
+	if (me == NULL)
+		return NULL;
+
+	memset(me, 0, sizeof(struct ehca_qp));
+	spin_lock_init(&me->spinlock_s);
+	spin_lock_init(&me->spinlock_r);
+
+	return me;
+}
+
+int ehca_qp_delete(struct ehca_qp *me)
+{
+	extern struct ehca_module ehca_module;
+
+	kmem_cache_free(ehca_module.cache_qp, me);
+
+	return H_Success;
+}
+
+struct ehca_av *ehca_av_new(void)
+{
+	extern struct ehca_module ehca_module;
+	struct ehca_av *me; 
+
+	me  = kmem_cache_alloc(ehca_module.cache_av, SLAB_KERNEL);
+	if (me == NULL)
+		return NULL;
+
+	memset(me, 0, sizeof(struct ehca_av));
+
+	return me;
+}
+
+int ehca_av_delete(struct ehca_av *me)
+{
+	extern struct ehca_module ehca_module;
+
+	kmem_cache_free(ehca_module.cache_av, me);
+	
+	return H_Success;
+}
+
+struct ehca_mr *ehca_mr_new(void)
+{
+	extern struct ehca_module ehca_module;
+	struct ehca_mr *me;
+
+	me = kmem_cache_alloc(ehca_module.cache_mr, SLAB_KERNEL);
+	if (me) {
+		memset(me, 0, sizeof(struct ehca_mr));
+		spin_lock_init(&me->mrlock);
+		EDEB_EX(7, "ehca_mr=%p sizeof(ehca_mr_t)=%x", me,
+			(u32) sizeof(struct ehca_mr));
+	} else {
+		EDEB_ERR(3, "alloc failed");
+	}
+
+	return me;
+}
+
+void ehca_mr_delete(struct ehca_mr *me)
+{
+	extern struct ehca_module ehca_module;
+
+	kmem_cache_free(ehca_module.cache_mr, me);
+}
+
+struct ehca_mw *ehca_mw_new(void)
+{
+	extern struct ehca_module ehca_module;
+	struct ehca_mw *me;
+
+	me = kmem_cache_alloc(ehca_module.cache_mw, SLAB_KERNEL);
+	if (me) {
+		memset(me, 0, sizeof(struct ehca_mw));
+		spin_lock_init(&me->mwlock);
+		EDEB_EX(7, "ehca_mw=%p sizeof(ehca_mw_t)=%x", me,
+			(u32) sizeof(struct ehca_mw));
+	} else {
+		EDEB_ERR(3, "alloc failed");
+	}
+
+	return me;
+}
+
+void ehca_mw_delete(struct ehca_mw *me)
+{
+	extern struct ehca_module ehca_module;
+
+	kmem_cache_free(ehca_module.cache_mw, me);
+}
+
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_classes.h newtree/drivers/infiniband/hw/ehca/ehca_classes.h
--- oldtree/drivers/infiniband/hw/ehca/ehca_classes.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_classes.h	2006-02-13 19:19:59.911525752 -0500
@@ -0,0 +1,375 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  struct definitions for hcad internal structures
+ *
+ *  Authors: Christoph Raisch <raisch@de.ibm.com> 
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_classes.h,v 1.74 2005/11/28 17:36:58 nguyen Exp $
+ */
+
+#ifndef __EHCA_CLASSES_H__
+#define __EHCA_CLASSES_H__
+
+#include "ehca_kernel.h"
+#include "ipz_pt_fn.h"
+
+#include <linux/list.h>
+
+struct ehca_module;
+struct ehca_qp;
+struct ehca_cq;
+struct ehca_eq;
+struct ehca_mr;
+struct ehca_mw;
+struct ehca_pd;
+struct ehca_av;
+
+#ifndef CONFIG_PPC64
+#ifndef Z_SERIES
+#error "no series defined"
+#endif
+#endif
+
+#ifdef CONFIG_PPC64
+#include "ehca_classes_pSeries.h"
+#endif
+
+#ifdef Z_SERIES
+#include "ehca_classes_zSeries.h"
+#endif
+
+#include <rdma/ib_verbs.h>
+#include <rdma/ib_user_verbs.h>
+
+#include "ehca_irq.h"
+
+#include "ehca_classes_core.h"
+
+/** @brief HCAD class
+ *
+ * contains HCAD specific data 
+ *
+ */
+struct ehca_module {
+	struct list_head shca_list;
+	kmem_cache_t *cache_pd;
+	kmem_cache_t *cache_cq;
+	kmem_cache_t *cache_qp;
+	kmem_cache_t *cache_av;
+	kmem_cache_t *cache_mr;
+	kmem_cache_t *cache_mw;
+
+	struct ehca_pfmodule pf; /* plattform specific part of HCA */
+};
+
+/** @brief EQ class
+ */
+struct ehca_eq {
+	u32 length;		    /* length of EQ */
+	struct ipz_queue ipz_queue; /* EQ in kv     */
+	struct ipz_eq_handle ipz_eq_handle;
+	struct ehca_irq_info irq_info;
+	struct work_struct work;
+	struct h_galpas galpas;
+	int    is_initialized;
+
+	struct ehca_pfeq pf; /* plattform specific part of EQ */
+
+	spinlock_t spinlock;
+};
+
+/** static port
+ */
+struct ehca_sport {
+	struct ib_cq *ibcq_aqp1; /* CQ for AQP1 */
+	struct ib_qp *ibqp_aqp1; /* QP for AQP1 */
+	enum ib_port_state port_state;
+};
+
+/** @brief HCA class "static HCA"
+ *
+ * contains HCA specific data per HCA (or vHCA?)
+ * per instance reported by firmware
+ *
+ */
+struct ehca_shca {
+	struct ib_device ib_device;
+	struct ibmebus_dev *ibmebus_dev;
+	u8 num_ports;
+	int hw_level;
+	struct list_head shca_list;
+	struct ipz_adapter_handle ipz_hca_handle; /* firmware HCA handle     */
+	struct ehca_bridge_handle bridge;
+	struct ehca_sport sport[2];
+	struct ehca_eq eq;	/* event queue                        */
+	struct ehca_eq neq;	/* notification event queue           */
+	struct ehca_mr *maxmr;	/* internal max MR (for kernel users) */
+	struct ehca_pd *pd;	/* internal pd (for kernel users)     */
+	struct ehca_pfshca pf;	/* plattform specific part of HCA     */
+	struct h_galpas galpas;
+};
+
+/** @brief protection domain
+ */
+struct ehca_pd {
+	struct ib_pd ib_pd;	/* gen2 qp, must always be first in ehca_pd */
+	struct ipz_pd fw_pd;
+	struct ehca_pfpd pf;
+};
+
+/** @brief QP class
+ */
+struct ehca_qp {
+	struct ib_qp ib_qp;	/* gen2 qp, must always be first in ehca_qp */
+	struct ehca_qp_core ehca_qp_core;	/* common fields for 
+						   user/kernel space */
+	u32 token;
+	spinlock_t spinlock_s;
+	spinlock_t spinlock_r;
+	u32 sq_max_inline_data_size; /* max # of inline data can be send */
+	struct ipz_qp_handle ipz_qp_handle; /* QP handle for h-calls            */
+	struct ehca_pfqp pf;	     /* plattform specific part of QP    */
+	struct ib_qp_init_attr init_attr;
+	/* adr mapping for s/r queues and fw handle bw kernel&user space */
+	u64 uspace_squeue; 
+	u64 uspace_rqueue; 
+	u64 uspace_fwh;
+	struct ehca_cq* send_cq;
+	unsigned int sqerr_purgeflag;
+	struct list_head list_entries;
+};
+
+#define QP_HASHTAB_LEN 7
+/** @brief CQ class
+ */
+struct ehca_cq {
+	struct ib_cq ib_cq;	/* gen2 cq, must always be first 
+				   in ehca_cq */
+	struct ehca_cq_core ehca_cq_core; /* common fields for 
+                                             user/kernel space */
+	spinlock_t spinlock;
+	u32 cq_number;
+	u32 token;
+	u32 nr_of_entries;
+	/* fw specific data common for p+z */
+	struct ipz_cq_handle ipz_cq_handle;	/* CQ handle for h-calls */
+	/* pf specific code */
+	struct ehca_pfcq pf;            /* platform specific part of CQ */
+	spinlock_t cb_lock;             /* completion event handler */
+	/* adr mapping for queue and fw handle bw kernel&user space */
+	u64 uspace_queue;
+	u64 uspace_fwh;
+	struct list_head qp_hashtab[QP_HASHTAB_LEN];
+};
+
+
+/** @brief MR flags
+ */
+enum ehca_mr_flag {
+	EHCA_MR_FLAG_FMR = 0x80000000,	 /* FMR, created with ehca_alloc_fmr */
+	EHCA_MR_FLAG_MAXMR = 0x40000000, /* max-MR                           */
+	EHCA_MR_FLAG_USER = 0x20000000	 /* user space TODO...necessary????. */
+};
+
+/** @brief MR class
+ */
+struct ehca_mr {
+	union {
+		struct ib_mr ib_mr;	/* must always be first in ehca_mr */
+		struct ib_fmr ib_fmr;	/* must always be first in ehca_mr */
+	} ib;
+
+	spinlock_t mrlock;
+
+	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+	 * !!! ehca_mr_deletenew() memsets from flags to end of structure
+	 * !!! DON'T move flags or insert another field before.
+	 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+	enum ehca_mr_flag flags;
+	u32 num_pages;		/* number of MR pages */
+	int acl;		/* ACL (stored here for usage in reregister) */
+	u64 *start;		/* virtual start address (stored here for */
+	/* usage in reregister) */
+	u64 size;		/* size (stored here for usage in reregister) */
+	u32 fmr_page_size;	/* page size for FMR */
+	u32 fmr_max_pages;	/* max pages for FMR */
+	u32 fmr_max_maps;	/* max outstanding maps for FMR */
+	u32 fmr_map_cnt;	/* map counter for FMR */
+	/* fw specific data */
+	struct ipz_mrmw_handle ipz_mr_handle;	/* MR handle for h-calls */
+	struct h_galpas galpas;
+	/* data for userspace bridge */
+	u32 nr_of_pages;
+	void *pagearray;
+
+	struct ehca_pfmr pf;	/* platform specific part of MR */
+};
+
+/** @brief MW class
+ */
+struct ehca_mw {
+	struct ib_mw ib_mw;	/* gen2 mw, must always be first in ehca_mw */
+	spinlock_t mwlock;
+
+	u8 never_bound;		/* indication MW was never bound */
+	struct ipz_mrmw_handle ipz_mw_handle;	/* MW handle for h-calls */
+	struct h_galpas galpas;
+
+	struct ehca_pfmw pf;	/* platform specific part of MW */
+};
+
+/** @brief MR page info type
+ */
+enum ehca_mr_pgi_type {
+	EHCA_MR_PGI_PHYS   = 1,  /* type of ehca_reg_phys_mr, 
+				  * ehca_rereg_phys_mr,
+				  * ehca_reg_internal_maxmr */
+	EHCA_MR_PGI_USER   = 2,  /* type of ehca_reg_user_mr */
+	EHCA_MR_PGI_FMR    = 3   /* type of ehca_map_phys_fmr */
+};
+
+/** @brief MR page info
+ */
+struct ehca_mr_pginfo {
+	enum ehca_mr_pgi_type type;
+	u64 num_pages;
+	u64 page_count;
+
+	/* type EHCA_MR_PGI_PHYS section */
+	int num_phys_buf;
+	struct ib_phys_buf *phys_buf_array;
+	u64 next_buf;
+	u64 next_page;
+
+	/* type EHCA_MR_PGI_USER section */
+	struct ib_umem *region;
+	struct ib_umem_chunk *next_chunk;
+	u64 next_nmap;
+
+	/* type EHCA_MR_PGI_FMR section */
+	u64 *page_list;
+	u64 next_listelem;
+};
+
+
+/** @brief addres vector suitable for a ud enqueue request
+ */
+struct ehca_av {
+	struct ib_ah ib_ah;	/* gen2 ah, must always be first in ehca_ah */
+	struct ehca_ud_av av;
+};
+
+/** @brief user context
+ */
+struct ehca_ucontext {
+	struct ib_ucontext ib_ucontext;
+};
+
+struct ehca_module *ehca_module_new(void);
+
+int ehca_module_delete(struct ehca_module *me);
+
+int ehca_eq_ctor(struct ehca_eq *eq);
+
+int ehca_eq_dtor(struct ehca_eq *eq);
+
+struct ehca_shca *ehca_shca_new(void);
+
+int ehca_shca_delete(struct ehca_shca *me);
+
+struct ehca_sport *ehca_sport_new(struct ehca_shca *anchor);	/*anchor?? */
+
+struct ehca_cq *ehca_cq_new(void);
+
+int ehca_cq_delete(struct ehca_cq *me);
+
+struct ehca_av *ehca_av_new(void);
+
+int ehca_av_delete(struct ehca_av *me);
+
+struct ehca_pd *ehca_pd_new(void);
+
+void ehca_pd_delete(struct ehca_pd *me);
+
+struct ehca_qp *ehca_qp_new(void);
+
+int ehca_qp_delete(struct ehca_qp *me);
+
+struct ehca_mr *ehca_mr_new(void);
+
+void ehca_mr_delete(struct ehca_mr *me);
+
+struct ehca_mw *ehca_mw_new(void);
+
+void ehca_mw_delete(struct ehca_mw *me);
+
+extern struct rw_semaphore ehca_qp_idr_sem;
+extern struct rw_semaphore ehca_cq_idr_sem;
+extern struct idr ehca_qp_idr;
+extern struct idr ehca_cq_idr;
+
+/*
+ * cmd and resp structs for comm bw user and kernel space
+ */
+struct ehca_create_cq {
+	u64 respbuf;
+};
+
+struct ehca_create_cq_resp {
+	u32 cq_number;
+	u32 token;
+	struct ehca_cq_core ehca_cq_core;
+};
+
+struct ehca_create_qp {
+	u64 respbuf;
+};
+
+struct ehca_create_qp_resp {
+	u32 qp_num;
+	u32 token;
+	struct ehca_qp_core ehca_qp_core;
+};
+
+/*
+ * helper funcs to link send cq and qp
+ */
+int ehca_cq_assign_qp(struct ehca_cq *cq, struct ehca_qp *qp);
+int ehca_cq_unassign_qp(struct ehca_cq *cq, unsigned int qp_num);
+struct ehca_qp* ehca_cq_get_qp(struct ehca_cq *cq, int qp_num);
+
+#endif				/* __EHCA_CLASSES_H__ */
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_classes_core.h newtree/drivers/infiniband/hw/ehca/ehca_classes_core.h
--- oldtree/drivers/infiniband/hw/ehca/ehca_classes_core.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_classes_core.h	2006-02-13 19:19:59.911525752 -0500
@@ -0,0 +1,73 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  core struct definitions for hcad internal structures and 
+ *  to be used/compiled commonly in user and kernel space
+ *
+ *  Authors: Christoph Raisch <raisch@de.ibm.com> 
+ *           Hoang-Nam Nguyen <hnguyen@de.ibm.com> 
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_classes_core.h,v 1.10 2005/12/02 15:33:49 nguyen Exp $
+ */
+
+#ifndef __EHCA_CLASSES_CORE_H__
+#define __EHCA_CLASSES_CORE_H__
+
+#include "ipz_pt_fn_core.h"
+#include "ehca_galpa.h"
+
+/** @brief qp core contains common fields for user/kernel space
+ */
+struct ehca_qp_core {
+	/* kernel space: enum ib_qp_type, user space: enum ibv_qp_type */
+	int qp_type;
+	int dummy1; /* 8 byte alignment */
+	struct ipz_queue ipz_squeue;
+	struct ipz_queue ipz_rqueue;
+	struct h_galpas galpas;
+	unsigned int qkey;
+	int dummy2; /* 8 byte alignment */
+	/* qp_num assigned by ehca: sqp0/1 may have got different numbers */
+	unsigned int real_qp_num; 
+};
+
+/** @brief cq core contains common fields for user/kernel space
+ */
+struct ehca_cq_core {
+	struct ipz_queue ipz_queue;
+	struct h_galpas galpas;
+};
+
+#endif /* __EHCA_CLASSES_CORE_H__ */
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_classes_pSeries.h newtree/drivers/infiniband/hw/ehca/ehca_classes_pSeries.h
--- oldtree/drivers/infiniband/hw/ehca/ehca_classes_pSeries.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_classes_pSeries.h	2006-02-13 19:19:59.912525600 -0500
@@ -0,0 +1,256 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  pSeries interface definitions
+ *
+ *  Authors: Waleri Fomin <fomin@de.ibm.com>
+ *           Christoph Raisch <raisch@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_classes_pSeries.h,v 1.22 2005/11/03 10:40:21 nguyen Exp $
+ */
+
+#ifndef __EHCA_CLASSES_PSERIES_H__
+#define __EHCA_CLASSES_PSERIES_H__
+
+#include "ehca_galpa.h"
+#include "ipz_pt_fn.h"
+
+
+struct ehca_pfmodule {
+};
+
+struct ehca_pfshca {
+};
+
+struct ehca_pfqp {
+	struct ipz_qpt sqpt;
+	struct ipz_qpt rqpt;
+	struct ehca_bridge_handle bridge;
+};
+
+struct ehca_pfcq {
+	struct ipz_qpt qpt;
+	struct ehca_bridge_handle bridge;
+	u32 cqnr;
+};
+
+struct ehca_pfeq {
+	struct ipz_qpt qpt;
+	struct ehca_bridge_handle bridge;
+	struct h_galpa galpa;
+	u32 eqnr;
+};
+
+struct ehca_pfpd {
+};
+
+struct ehca_pfmr {
+	struct ehca_bridge_handle bridge;
+};
+struct ehca_pfmw {
+};
+
+struct ipz_adapter_handle {
+	u64 handle;
+};
+
+struct ipz_cq_handle {
+	u64 handle;
+};
+
+struct ipz_eq_handle {
+	u64 handle;
+};
+
+struct ipz_qp_handle {
+	u64 handle;
+};
+struct ipz_mrmw_handle {
+	u64 handle;
+};
+
+struct ipz_pd {
+	u32 value;
+};
+
+struct hcp_modify_qp_control_block {
+	u32 qkey;                      /* 00 */
+	u32 rdd;                       /* reliable datagram domain */
+	u32 send_psn;                  /* 02 */
+	u32 receive_psn;               /* 03 */
+	u32 prim_phys_port;            /* 04 */
+	u32 alt_phys_port;             /* 05 */
+	u32 prim_p_key_idx;            /* 06 */
+	u32 alt_p_key_idx;             /* 07 */
+	u32 rdma_atomic_ctrl;          /* 08 */
+	u32 qp_state;                  /* 09 */
+	u32 reserved_10;               /* 10 */
+	u32 rdma_nr_atomic_resp_res;   /* 11 */
+	u32 path_migration_state;      /* 12 */
+	u32 rdma_atomic_outst_dest_qp; /* 13 */
+	u32 dest_qp_nr;                /* 14 */
+	u32 min_rnr_nak_timer_field;   /* 15 */
+	u32 service_level;             /* 16 */
+	u32 send_grh_flag;             /* 17 */
+	u32 retry_count;               /* 18 */
+	u32 timeout;                   /* 19 */
+	u32 path_mtu;                  /* 20 */
+	u32 max_static_rate;           /* 21 */
+	u32 dlid;                      /* 22 */
+	u32 rnr_retry_count;           /* 23 */
+	u32 source_path_bits;          /* 24 */
+	u32 traffic_class;             /* 25 */
+	u32 hop_limit;                 /* 26 */
+	u32 source_gid_idx;            /* 27 */
+	u32 flow_label;                /* 28 */
+	u32 reserved_29;               /* 29 */
+	union {                        /* 30 */
+		u64 dw[2];
+		u8 byte[16];
+	} dest_gid;
+	u32 service_level_al;          /* 34 */
+	u32 send_grh_flag_al;          /* 35 */
+	u32 retry_count_al;            /* 36 */
+	u32 timeout_al;                /* 37 */
+	u32 max_static_rate_al;        /* 38 */
+	u32 dlid_al;                   /* 39 */
+	u32 rnr_retry_count_al;        /* 40 */
+	u32 source_path_bits_al;       /* 41 */
+	u32 traffic_class_al;          /* 42 */
+	u32 hop_limit_al;              /* 43 */
+	u32 source_gid_idx_al;         /* 44 */
+	u32 flow_label_al;             /* 45 */
+	u32 reserved_46;               /* 46 */
+	u32 reserved_47;               /* 47 */
+	union {                        /* 48 */
+		u64 dw[2];
+		u8 byte[16];
+	} dest_gid_al;
+	u32 max_nr_outst_send_wr;      /* 52 */
+	u32 max_nr_outst_recv_wr;      /* 53 */
+	u32 disable_ete_credit_check;  /* 54 */
+	u32 qp_number;                 /* 55 */
+	u64 send_queue_handle;         /* 56 */
+	u64 recv_queue_handle;         /* 58 */
+	u32 actual_nr_sges_in_sq_wqe;  /* 60 */
+	u32 actual_nr_sges_in_rq_wqe;  /* 61 */
+	u32 qp_enable;                 /* 62 */
+	u32 curr_srq_limit;            /* 63 */
+	u64 qp_aff_asyn_ev_log_reg;    /* 64 */
+	u64 shared_rq_hndl;            /* 66 */
+	u64 trigg_doorbell_qp_hndl;    /* 68 */
+	u32 reserved_70_127[58];       /* 70 */
+};
+
+#define MQPCB_MASK_QKEY                         EHCA_BMASK_IBM(0,0)
+#define MQPCB_MASK_SEND_PSN                     EHCA_BMASK_IBM(2,2)
+#define MQPCB_MASK_RECEIVE_PSN                  EHCA_BMASK_IBM(3,3)
+#define MQPCB_MASK_PRIM_PHYS_PORT               EHCA_BMASK_IBM(4,4)
+#define MQPCB_PRIM_PHYS_PORT                    EHCA_BMASK_IBM(24,31)
+#define MQPCB_MASK_ALT_PHYS_PORT                EHCA_BMASK_IBM(5,5)
+#define MQPCB_MASK_PRIM_P_KEY_IDX               EHCA_BMASK_IBM(6,6)
+#define MQPCB_PRIM_P_KEY_IDX                    EHCA_BMASK_IBM(24,31)
+#define MQPCB_MASK_ALT_P_KEY_IDX                EHCA_BMASK_IBM(7,7)
+#define MQPCB_MASK_RDMA_ATOMIC_CTRL             EHCA_BMASK_IBM(8,8)
+#define MQPCB_MASK_QP_STATE                     EHCA_BMASK_IBM(9,9)
+#define MQPCB_QP_STATE                          EHCA_BMASK_IBM(24,31)
+#define MQPCB_MASK_RDMA_NR_ATOMIC_RESP_RES      EHCA_BMASK_IBM(11,11)
+#define MQPCB_MASK_PATH_MIGRATION_STATE         EHCA_BMASK_IBM(12,12)
+#define MQPCB_MASK_RDMA_ATOMIC_OUTST_DEST_QP    EHCA_BMASK_IBM(13,13)
+#define MQPCB_MASK_DEST_QP_NR                   EHCA_BMASK_IBM(14,14)
+#define MQPCB_MASK_MIN_RNR_NAK_TIMER_FIELD      EHCA_BMASK_IBM(15,15)
+#define MQPCB_MASK_SERVICE_LEVEL                EHCA_BMASK_IBM(16,16)
+#define MQPCB_MASK_SEND_GRH_FLAG                EHCA_BMASK_IBM(17,17)
+#define MQPCB_MASK_RETRY_COUNT                  EHCA_BMASK_IBM(18,18)
+#define MQPCB_MASK_TIMEOUT                      EHCA_BMASK_IBM(19,19)
+#define MQPCB_MASK_PATH_MTU                     EHCA_BMASK_IBM(20,20)
+#define MQPCB_PATH_MTU                          EHCA_BMASK_IBM(24,31)
+#define MQPCB_MASK_MAX_STATIC_RATE              EHCA_BMASK_IBM(21,21)
+#define MQPCB_MAX_STATIC_RATE                   EHCA_BMASK_IBM(24,31)
+#define MQPCB_MASK_DLID                         EHCA_BMASK_IBM(22,22)
+#define MQPCB_DLID                              EHCA_BMASK_IBM(16,31)
+#define MQPCB_MASK_RNR_RETRY_COUNT              EHCA_BMASK_IBM(23,23)
+#define MQPCB_RNR_RETRY_COUNT                   EHCA_BMASK_IBM(29,31)
+#define MQPCB_MASK_SOURCE_PATH_BITS             EHCA_BMASK_IBM(24,24)
+#define MQPCB_SOURCE_PATH_BITS                  EHCA_BMASK_IBM(25,31)
+#define MQPCB_MASK_TRAFFIC_CLASS                EHCA_BMASK_IBM(25,25)
+#define MQPCB_TRAFFIC_CLASS                     EHCA_BMASK_IBM(24,31)
+#define MQPCB_MASK_HOP_LIMIT                    EHCA_BMASK_IBM(26,26)
+#define MQPCB_HOP_LIMIT                         EHCA_BMASK_IBM(24,31)
+#define MQPCB_MASK_SOURCE_GID_IDX               EHCA_BMASK_IBM(27,27)
+#define MQPCB_SOURCE_GID_IDX                    EHCA_BMASK_IBM(24,31)
+#define MQPCB_MASK_FLOW_LABEL                   EHCA_BMASK_IBM(28,28)
+#define MQPCB_FLOW_LABEL                        EHCA_BMASK_IBM(12,31)
+#define MQPCB_MASK_DEST_GID                     EHCA_BMASK_IBM(30,30)
+#define MQPCB_MASK_SERVICE_LEVEL_AL             EHCA_BMASK_IBM(31,31)
+#define MQPCB_SERVICE_LEVEL_AL                  EHCA_BMASK_IBM(28,31)
+#define MQPCB_MASK_SEND_GRH_FLAG_AL             EHCA_BMASK_IBM(32,32)
+#define MQPCB_SEND_GRH_FLAG_AL                  EHCA_BMASK_IBM(31,31)
+#define MQPCB_MASK_RETRY_COUNT_AL               EHCA_BMASK_IBM(33,33)
+#define MQPCB_RETRY_COUNT_AL                    EHCA_BMASK_IBM(29,31)
+#define MQPCB_MASK_TIMEOUT_AL                   EHCA_BMASK_IBM(34,34)
+#define MQPCB_TIMEOUT_AL                        EHCA_BMASK_IBM(27,31)
+#define MQPCB_MASK_MAX_STATIC_RATE_AL           EHCA_BMASK_IBM(35,35)
+#define MQPCB_MAX_STATIC_RATE_AL                EHCA_BMASK_IBM(24,31)
+#define MQPCB_MASK_DLID_AL                      EHCA_BMASK_IBM(36,36)
+#define MQPCB_DLID_AL                           EHCA_BMASK_IBM(16,31)
+#define MQPCB_MASK_RNR_RETRY_COUNT_AL           EHCA_BMASK_IBM(37,37)
+#define MQPCB_RNR_RETRY_COUNT_AL                EHCA_BMASK_IBM(29,31)
+#define MQPCB_MASK_SOURCE_PATH_BITS_AL          EHCA_BMASK_IBM(38,38)
+#define MQPCB_SOURCE_PATH_BITS_AL               EHCA_BMASK_IBM(25,31)
+#define MQPCB_MASK_TRAFFIC_CLASS_AL             EHCA_BMASK_IBM(39,39)
+#define MQPCB_TRAFFIC_CLASS_AL                  EHCA_BMASK_IBM(24,31)
+#define MQPCB_MASK_HOP_LIMIT_AL                 EHCA_BMASK_IBM(40,40)
+#define MQPCB_HOP_LIMIT_AL                      EHCA_BMASK_IBM(24,31)
+#define MQPCB_MASK_SOURCE_GID_IDX_AL            EHCA_BMASK_IBM(41,41)
+#define MQPCB_SOURCE_GID_IDX_AL                 EHCA_BMASK_IBM(24,31)
+#define MQPCB_MASK_FLOW_LABEL_AL                EHCA_BMASK_IBM(42,42)
+#define MQPCB_FLOW_LABEL_AL                     EHCA_BMASK_IBM(12,31)
+#define MQPCB_MASK_DEST_GID_AL                  EHCA_BMASK_IBM(44,44)
+#define MQPCB_MASK_MAX_NR_OUTST_SEND_WR         EHCA_BMASK_IBM(45,45)
+#define MQPCB_MAX_NR_OUTST_SEND_WR              EHCA_BMASK_IBM(16,31)
+#define MQPCB_MASK_MAX_NR_OUTST_RECV_WR         EHCA_BMASK_IBM(46,46)
+#define MQPCB_MAX_NR_OUTST_RECV_WR              EHCA_BMASK_IBM(16,31)
+#define MQPCB_MASK_DISABLE_ETE_CREDIT_CHECK     EHCA_BMASK_IBM(47,47)
+#define MQPCB_DISABLE_ETE_CREDIT_CHECK          EHCA_BMASK_IBM(31,31)
+#define MQPCB_QP_NUMBER                         EHCA_BMASK_IBM(8,31)
+#define MQPCB_MASK_QP_ENABLE                    EHCA_BMASK_IBM(48,48)
+#define MQPCB_QP_ENABLE                         EHCA_BMASK_IBM(31,31)
+#define MQPCB_MASK_CURR_SQR_LIMIT               EHCA_BMASK_IBM(49,49)
+#define MQPCB_CURR_SQR_LIMIT                    EHCA_BMASK_IBM(15,31)
+#define MQPCB_MASK_QP_AFF_ASYN_EV_LOG_REG       EHCA_BMASK_IBM(50,50)
+#define MQPCB_MASK_SHARED_RQ_HNDL               EHCA_BMASK_IBM(51,51)
+
+#endif /* __EHCA_CLASSES_PSERIES_H__ */
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_common.h newtree/drivers/infiniband/hw/ehca/ehca_common.h
--- oldtree/drivers/infiniband/hw/ehca/ehca_common.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_common.h	2006-02-13 19:19:59.912525600 -0500
@@ -0,0 +1,115 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  hcad local defines
+ *
+ *  Authors:  Christoph Raisch <raisch@de.ibm.com> 
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_common.h,v 1.12 2005/11/16 14:54:10 schickhj Exp $
+ */
+
+#ifndef __EHCA_COMMON_H__
+#define __EHCA_COMMON_H__
+
+#ifdef CONFIG_PPC64
+#include <asm/hvcall.h>
+
+#define H_PARTIAL_STORE   16
+#define H_PAGE_REGISTERED 15
+#define H_IN_PROGRESS     14
+#define H_PARTIAL          5
+#define H_NOT_AVAILABLE    3
+#define H_Closed           2
+#define H_ADAPTER_PARM         -17
+#define H_RH_PARM              -18
+#define H_RCQ_PARM             -19
+#define H_SCQ_PARM             -20
+#define H_EQ_PARM              -21
+#define H_RT_PARM              -22
+#define H_ST_PARM              -23
+#define H_SIGT_PARM            -24
+#define H_TOKEN_PARM           -25
+#define H_MLENGTH_PARM         -27
+#define H_MEM_PARM             -28
+#define H_MEM_ACCESS_PARM      -29
+#define H_ATTR_PARM            -30
+#define H_PORT_PARM            -31
+#define H_MCG_PARM             -32
+#define H_VL_PARM              -33
+#define H_TSIZE_PARM           -34
+#define H_TRACE_PARM           -35
+
+#define H_MASK_PARM            -37
+#define H_MCG_FULL             -38
+#define H_ALIAS_EXIST          -39
+#define H_P_COUNTER            -40
+#define H_TABLE_FULL           -41
+#define H_ALT_TABLE            -42
+#define H_MR_CONDITION         -43
+#define H_NOT_ENOUGH_RESOURCES -44
+#define H_R_STATE              -45
+#define H_RESCINDEND           -46
+
+/* H call defines to be moved to kernel */
+#define H_RESET_EVENTS         0x15C
+#define H_ALLOC_RESOURCE       0x160
+#define H_FREE_RESOURCE        0x164
+#define H_MODIFY_QP            0x168
+#define H_QUERY_QP             0x16C
+#define H_REREGISTER_PMR       0x170
+#define H_REGISTER_SMR         0x174
+#define H_QUERY_MR             0x178
+#define H_QUERY_MW             0x17C
+#define H_QUERY_HCA            0x180
+#define H_QUERY_PORT           0x184
+#define H_MODIFY_PORT          0x188
+#define H_DEFINE_AQP1          0x18C
+#define H_GET_TRACE_BUFFER     0x190
+#define H_DEFINE_AQP0          0x194
+#define H_RESIZE_MR            0x198
+#define H_ATTACH_MCQP          0x19C
+#define H_DETACH_MCQP          0x1A0
+#define H_CREATE_RPT           0x1A4
+#define H_REMOVE_RPT           0x1A8
+#define H_REGISTER_RPAGES      0x1AC
+#define H_DISABLE_AND_GETC     0x1B0
+#define H_ERROR_DATA           0x1B4
+#define H_GET_HCA_INFO         0x1B8
+#define H_GET_PERF_COUNT       0x1BC
+#define H_MANAGE_TRACE         0x1C0
+#define H_QUERY_INT_STATE      0x1E4
+#endif
+
+#endif /* __EHCA_COMMON_H__ */
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_cq.c newtree/drivers/infiniband/hw/ehca/ehca_cq.c
--- oldtree/drivers/infiniband/hw/ehca/ehca_cq.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_cq.c	2006-02-13 19:19:59.914525296 -0500
@@ -0,0 +1,424 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  Completion queue handling
+ *
+ *  Authors: Waleri Fomin <fomin@de.ibm.com>
+ *           Reinhard Ernst <rernst@de.ibm.com>
+ *           Heiko J Schick <schickhj@de.ibm.com>
+ *           Hoang-Nam Nguyen <hnguyen@de.ibm.com>
+ *            
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_cq.c,v 1.55 2005/12/02 15:36:00 nguyen Exp $
+ */
+
+#define DEB_PREFIX "e_cq"
+
+#include "ehca_kernel.h"
+#include "ehca_common.h"
+#include "ehca_iverbs.h"
+#include "ehca_classes.h"
+#include "ehca_irq.h"
+#include "hcp_if.h"
+#include <linux/err.h>
+#include <asm/uaccess.h>
+
+#define HIPZ_CQ_REGISTER_ORIG 0
+
+int ehca_cq_assign_qp(struct ehca_cq *cq, struct ehca_qp *qp)
+{
+	unsigned int qp_num = qp->ehca_qp_core.real_qp_num;
+	unsigned int key = qp_num%QP_HASHTAB_LEN;
+	unsigned long spin_flags = 0;
+	spin_lock_irqsave(&cq->spinlock, spin_flags);
+	list_add(&qp->list_entries, &cq->qp_hashtab[key]);
+	spin_unlock_irqrestore(&cq->spinlock, spin_flags);
+	EDEB(7, "cq_num=%x real_qp_num=%x", cq->cq_number, qp_num);
+	return 0;
+}
+
+int ehca_cq_unassign_qp(struct ehca_cq *cq, unsigned int real_qp_num)
+{
+	int ret = -EINVAL;
+	unsigned int key = real_qp_num%QP_HASHTAB_LEN;
+	struct list_head *iter = NULL;
+	struct ehca_qp *qp = NULL;
+	unsigned long spin_flags = 0;
+	spin_lock_irqsave(&cq->spinlock, spin_flags);
+	list_for_each(iter, &cq->qp_hashtab[key]) {
+		qp = list_entry(iter, struct ehca_qp, list_entries);
+		if (qp->ehca_qp_core.real_qp_num == real_qp_num) {
+			list_del(iter);
+			EDEB(7, "removed qp from cq .cq_num=%x real_qp_num=%x", 
+			     cq->cq_number, real_qp_num);
+			ret = 0;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&cq->spinlock, spin_flags);
+	if (ret!=0) {
+		EDEB_ERR(4, "qp not found cq_num=%x real_qp_num=%x", 
+			 cq->cq_number, real_qp_num);
+	}
+	return ret;
+}
+
+struct ehca_qp* ehca_cq_get_qp(struct ehca_cq *cq, int real_qp_num)
+{
+	struct ehca_qp *ret = NULL;
+	unsigned int key = real_qp_num%QP_HASHTAB_LEN;
+	struct list_head *iter = NULL;
+	struct ehca_qp *qp = NULL;
+	list_for_each(iter, &cq->qp_hashtab[key]) {
+		qp = list_entry(iter, struct ehca_qp, list_entries);
+		if (qp->ehca_qp_core.real_qp_num == real_qp_num) {
+			ret = qp;
+			break;
+		}
+	}
+	return ret;
+}
+
+struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe,
+			     struct ib_ucontext *context,
+			     struct ib_udata *udata)
+{
+	struct ib_cq *cq = NULL;
+	struct ehca_cq *my_cq = NULL;
+	u32 number_of_entries = cqe;
+	struct ehca_shca *shca = NULL;
+	struct ipz_adapter_handle adapter_handle;
+	struct ipz_eq_handle eq_handle;
+	struct ipz_cq_handle *cq_handle_ref = NULL;
+	u32 act_nr_of_entries = 0;
+	u32 act_pages = 0;
+	u32 counter = 0;
+	void *vpage = NULL;
+	u64 rpage = 0;
+	struct h_galpa gal;
+	u64 CQx_FEC = 0;
+	u64 hipz_rc = H_Success;
+	int ipz_rc = 0;
+	int ret = 0;
+	struct ehca_create_cq ucmd;
+	const u32 additional_cqe=20;
+	int i= 0;
+
+	EHCA_CHECK_DEVICE_P(device);
+	EDEB_EN(7,  "device=%p cqe=%x context=%p",
+		device, cqe, context);
+	/* cq's maximum depth is 4GB-64
+	 * but we need additional 20 as buffer for receiving errors cqes
+	 */
+	if (cqe>=0xFFFFFFFF-64-additional_cqe) {
+		return ERR_PTR(-EINVAL);
+	}
+	number_of_entries += additional_cqe;
+
+	if (context) {
+		if (ib_copy_from_udata(&ucmd, udata, sizeof ucmd)) {
+			EDEB_ERR(4,  "Copy from udata failed.");
+			return ERR_PTR(-EFAULT);
+		}
+	}
+
+	my_cq = ehca_cq_new();
+	if (my_cq == NULL) {
+		cq = ERR_PTR(-ENOMEM);
+		EDEB_ERR(4, 
+			 "Out of memory for ehca_cq struct "
+			 "device=%p", device);
+		goto create_cq_exit0;
+	}
+	cq = &my_cq->ib_cq;
+
+	shca = container_of(device, struct ehca_shca, ib_device);
+	adapter_handle = shca->ipz_hca_handle;
+	eq_handle = shca->eq.ipz_eq_handle;
+	cq_handle_ref = &my_cq->ipz_cq_handle;
+
+	do {
+		if (!idr_pre_get(&ehca_cq_idr, GFP_KERNEL)) {
+			cq = ERR_PTR(-ENOMEM);
+			EDEB_ERR(4, 
+				 "Can't reserve idr resources. "
+				 "device=%p", device);
+			goto create_cq_exit1;
+		}
+
+		down_write(&ehca_cq_idr_sem);
+		ret = idr_get_new(&ehca_cq_idr, my_cq, &my_cq->token);
+		up_write(&ehca_cq_idr_sem);
+
+	} while (ret == -EAGAIN);
+
+	if (ret) {
+		cq = ERR_PTR(-ENOMEM);
+		EDEB_ERR(4, 
+			 "Can't allocate new idr entry. "
+			 "device=%p", device);
+		goto create_cq_exit1;
+	}
+
+	hipz_rc = hipz_h_alloc_resource_cq(adapter_handle,
+					   &my_cq->pf,
+					   eq_handle,
+					   my_cq->token,
+					   number_of_entries,
+					   cq_handle_ref,
+					   &act_nr_of_entries,
+					   &act_pages,
+					   &my_cq->ehca_cq_core.galpas);
+	if (hipz_rc != H_Success) {
+		EDEB_ERR(4, 
+			 "hipz_h_alloc_resource_cq() failed "
+			 "hipz_rc=%lx device=%p", hipz_rc, device);
+		cq = ERR_PTR(ehca2ib_return_code(hipz_rc));
+		goto create_cq_exit2;
+	}
+
+	ipz_rc =
+		ipz_queue_ctor(&my_cq->ehca_cq_core.ipz_queue, act_pages,
+			       EHCA_PAGESIZE, sizeof(struct ehca_cqe), 0);
+	if (!ipz_rc) {
+		EDEB_ERR(4, 
+			 "ipz_queue_ctor() failed "
+			 "ipz_rc=%x device=%p", ipz_rc, device);
+		cq = ERR_PTR(-EINVAL);
+		goto create_cq_exit3;
+	}
+
+	for (counter = 0; counter < act_pages; counter++) {
+		vpage = ipz_QPageit_get_inc(&my_cq->ehca_cq_core.ipz_queue);
+		if (!vpage) {
+			EDEB_ERR(4, "ipz_QPageit_get_inc() "
+				 "returns NULL device=%p", device);
+			cq = ERR_PTR(-EAGAIN);
+			goto create_cq_exit4;
+		}
+		rpage = ehca_kv_to_g(vpage);
+
+		hipz_rc = hipz_h_register_rpage_cq(adapter_handle,
+						   my_cq->ipz_cq_handle,
+						   &my_cq->pf,
+						   0,
+						   HIPZ_CQ_REGISTER_ORIG,
+						   rpage,
+						   1,
+						   my_cq->ehca_cq_core.galpas.
+						   kernel);
+
+		if (hipz_rc < H_Success) { 
+			EDEB_ERR(4, "hipz_h_register_rpage_cq() failed "
+				 "ehca_cq=%p cq_num=%x hipz_rc=%lx "
+				 "counter=%i act_pages=%i",
+				 my_cq, my_cq->cq_number,
+				 hipz_rc, counter, act_pages);
+			cq = ERR_PTR(-EINVAL);
+			goto create_cq_exit4;
+		}
+
+		if (counter == (act_pages - 1)) {
+			vpage = ipz_QPageit_get_inc(
+				&my_cq->ehca_cq_core.ipz_queue);
+			if ((hipz_rc != H_Success) || (vpage != 0)) {
+				EDEB_ERR(4, "Registration of pages not "
+					 "complete ehca_cq=%p cq_num=%x "
+					 "hipz_rc=%lx",
+					 my_cq, my_cq->cq_number, hipz_rc);
+				cq = ERR_PTR(-EAGAIN);
+				goto create_cq_exit4;
+			}
+		} else {
+			if (hipz_rc != H_PAGE_REGISTERED) {
+				EDEB_ERR(4, "Registration of page failed "
+					 "ehca_cq=%p cq_num=%x hipz_rc=%lx"
+					 "counter=%i act_pages=%i",
+					 my_cq, my_cq->cq_number,
+					 hipz_rc, counter, act_pages);
+				cq = ERR_PTR(-ENOMEM);
+				goto create_cq_exit4;
+			}
+		}
+	}
+
+	ipz_QEit_reset(&my_cq->ehca_cq_core.ipz_queue);
+
+	gal = my_cq->ehca_cq_core.galpas.kernel;
+	CQx_FEC = hipz_galpa_load(gal, CQTEMM_OFFSET(CQx_FEC));
+	EDEB(8, "ehca_cq=%p cq_num=%x CQx_FEC=%lx",
+	     my_cq, my_cq->cq_number, CQx_FEC);
+
+	my_cq->ib_cq.cqe = my_cq->nr_of_entries = 
+		act_nr_of_entries-additional_cqe;
+	my_cq->cq_number = (my_cq->ipz_cq_handle.handle) & 0xffff;
+
+	for (i=0; i<QP_HASHTAB_LEN; i++) {
+		INIT_LIST_HEAD(&my_cq->qp_hashtab[i]);
+	}
+
+	if (context) {
+		struct ehca_create_cq_resp resp;
+		struct vm_area_struct * vma;
+		resp.cq_number = my_cq->cq_number;
+		resp.token = my_cq->token;
+		resp.ehca_cq_core = my_cq->ehca_cq_core;
+
+		ehca_mmap_nopage(((u64) (my_cq->token) << 32) | 0x12000000,
+				 my_cq->ehca_cq_core.ipz_queue.queue_length,
+				 ((void**)&resp.ehca_cq_core.ipz_queue.queue),
+				 &vma);
+		my_cq->uspace_queue = (u64)resp.ehca_cq_core.ipz_queue.queue;
+		ehca_mmap_register(my_cq->ehca_cq_core.galpas.user.fw_handle,
+				   ((void**)&resp.ehca_cq_core.galpas.kernel.fw_handle),
+				   &vma);
+		my_cq->uspace_fwh = (u64)resp.ehca_cq_core.galpas.kernel.fw_handle;
+		if (ib_copy_to_udata(udata, &resp, sizeof(resp))) {
+			EDEB_ERR(4,  "Copy to udata failed.");
+			goto create_cq_exit4;
+		}
+	}
+
+	EDEB_EX(7,"retcode=%p ehca_cq=%p cq_num=%x cq_size=%x",
+		cq, my_cq, my_cq->cq_number, act_nr_of_entries);
+	return cq;
+
+ create_cq_exit4:
+	ipz_queue_dtor(&my_cq->ehca_cq_core.ipz_queue);
+
+ create_cq_exit3:
+	hipz_rc = hipz_h_destroy_cq(adapter_handle, my_cq, 1);
+	EDEB(3, "hipz_h_destroy_cq() failed ehca_cq=%p cq_num=%x hipz_rc=%lx",
+	     my_cq, my_cq->cq_number, hipz_rc);
+
+ create_cq_exit2:
+	/* dereg idr */
+	down_write(&ehca_cq_idr_sem);
+	idr_remove(&ehca_cq_idr, my_cq->token);
+	up_write(&ehca_cq_idr_sem);
+
+ create_cq_exit1:
+	/* free cq struct */
+	ehca_cq_delete(my_cq);
+
+ create_cq_exit0:
+	EDEB_EX(7,  "An error has occured retcode=%p ", cq);
+	return cq;
+}
+
+int ehca_destroy_cq(struct ib_cq *cq)
+{
+	u64 hipz_rc = H_Success;
+	int retcode = 0;
+	struct ehca_cq *my_cq = NULL;
+	int cq_num = 0;
+	struct ib_device *device = NULL;
+	struct ehca_shca *shca = NULL;
+	struct ipz_adapter_handle adapter_handle;
+
+	EHCA_CHECK_CQ(cq);
+	my_cq = container_of(cq, struct ehca_cq, ib_cq);
+	cq_num = my_cq->cq_number;
+	device = cq->device;
+	EHCA_CHECK_DEVICE(device);
+	shca = container_of(device, struct ehca_shca, ib_device);
+	adapter_handle = shca->ipz_hca_handle;
+	EDEB_EN(7, "ehca_cq=%p cq_num=%x",
+		my_cq, my_cq->cq_number);
+
+	down_write(&ehca_cq_idr_sem);
+	idr_remove(&ehca_cq_idr, my_cq->token);
+	up_write(&ehca_cq_idr_sem);
+
+	/* un-mmap if vma alloc */
+	if (my_cq->uspace_queue!=0) {
+		struct ehca_cq_core *cq_core = &my_cq->ehca_cq_core;
+		retcode = ehca_munmap(my_cq->uspace_queue, 
+				      cq_core->ipz_queue.queue_length);
+		retcode = ehca_munmap(my_cq->uspace_fwh, 4096);
+	}
+
+	hipz_rc = hipz_h_destroy_cq(adapter_handle, my_cq, 0);
+	if (hipz_rc == H_R_STATE) {
+		/* cq in err: read err data and destroy it forcibly */
+		EDEB(4, "ehca_cq=%p cq_num=%x ressource=%lx in err state. "
+		     "Try to delete it forcibly.",
+		     my_cq, my_cq->cq_number, my_cq->ipz_cq_handle.handle);
+		ehca_error_data(shca, my_cq->ipz_cq_handle.handle);
+		hipz_rc = hipz_h_destroy_cq(adapter_handle, my_cq, 1);
+		if (hipz_rc == H_Success) {
+			EDEB(4, "ehca_cq=%p cq_num=%x deleted successfully.",
+			     my_cq, my_cq->cq_number);
+		}
+	}
+	if (hipz_rc != H_Success) {
+		EDEB_ERR(4,"hipz_h_destroy_cq() failed "
+			 "hipz_rc=%lx ehca_cq=%p cq_num=%x",
+			 hipz_rc, my_cq, my_cq->cq_number);
+		retcode = ehca2ib_return_code(hipz_rc);
+		goto destroy_cq_exit0;/*@TODO*/
+	}
+	ipz_queue_dtor(&my_cq->ehca_cq_core.ipz_queue);
+	ehca_cq_delete(my_cq);
+
+ destroy_cq_exit0:
+	EDEB_EX(7, "ehca_cq=%p cq_num=%x retcode=%x ",
+		my_cq, cq_num, retcode);
+	return retcode;
+}
+
+int ehca_resize_cq(struct ib_cq *cq, int cqe)
+{
+	int retcode = 0;
+	struct ehca_cq *my_cq = NULL;
+
+	if (unlikely(NULL == cq)) {
+		EDEB_ERR(4, "cq is NULL");
+		return -EFAULT;
+	}
+
+	my_cq = container_of(cq, struct ehca_cq, ib_cq);
+	EDEB_EN(7, "ehca_cq=%p cq_num=%x",
+		my_cq, my_cq->cq_number);
+	/*TODO proper resize still needs to be done*/
+	if (cqe > cq->cqe) {
+		retcode = -EINVAL;
+	}
+	EDEB_EX(7, "ehca_cq=%p cq_num=%x",
+		my_cq, my_cq->cq_number);
+	return retcode;
+}
+
+/* eof ehca_cq.c */
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_eq.c newtree/drivers/infiniband/hw/ehca/ehca_eq.c
--- oldtree/drivers/infiniband/hw/ehca/ehca_eq.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_eq.c	2006-02-13 19:19:59.915525144 -0500
@@ -0,0 +1,235 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  Event queue handling
+ *
+ *  Authors: Waleri Fomin <fomin@de.ibm.com>
+ *           Reinhard Ernst <rernst@de.ibm.com>
+ *           Heiko J Schick <schickhj@de.ibm.com>
+ *           Hoang-Nam Nguyen <hnguyen@de.ibm.com>
+ *            
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_eq.c,v 1.37 2005/11/14 13:58:55 schickhj Exp $
+ */
+
+#define DEB_PREFIX "e_eq"
+
+#include "ehca_eq.h"
+#include "ehca_kernel.h"
+#include "ehca_classes.h"
+#include "hcp_if.h"
+#include "ehca_iverbs.h"
+#include "ipz_pt_fn.h"
+#include "ehca_qes.h"
+#include "ehca_irq.h"
+
+/* TODO: should be defined in ehca_classes_pSeries.h */
+#define HIPZ_EQ_REGISTER_ORIG 0
+
+int ehca_create_eq(struct ehca_shca *shca,
+		   struct ehca_eq *eq,
+		   const enum ehca_eq_type type, const u32 length)
+{
+	extern struct workqueue_struct *ehca_wq;
+	u64 ret = H_Success;
+	u32 nr_pages = 0;
+	u32 i;
+	void *vpage = NULL;
+
+	EDEB_EN(7, "shca=%p eq=%p length=%x", shca, eq, length);
+	EHCA_CHECK_ADR(shca);
+	EHCA_CHECK_ADR(eq);
+	if (type!=EHCA_EQ && type!=EHCA_NEQ) {
+		EDEB_ERR(4, "Invalid EQ type %x. eq=%p", type, eq);
+		return -EINVAL;
+	}
+	if (length==0) {
+		EDEB_ERR(4, "EQ length must not be zero. eq=%p", eq);
+		return -EINVAL;
+	}
+
+	ret = hipz_h_alloc_resource_eq(shca->ipz_hca_handle,
+				       &eq->pf,
+				       type,
+				       length,
+				       &eq->ipz_eq_handle,
+				       &eq->length,
+				       &nr_pages, &eq->irq_info.ist);
+
+	if (ret != H_Success) {
+		EDEB_ERR(4, "Can't allocate EQ / NEQ. eq=%p", eq);
+		return -EINVAL;
+	}
+
+	ret = ipz_queue_ctor(&eq->ipz_queue, nr_pages,
+			     EHCA_PAGESIZE, sizeof(struct ehca_eqe), 0);
+	if (!ret) {
+		EDEB_ERR(4, "Can't allocate EQ pages. eq=%p", eq);
+		goto create_eq_exit1;
+	}
+
+	for (i = 0; i < nr_pages; i++) {
+		u64 rpage;
+
+		if (!(vpage = ipz_QPageit_get_inc(&eq->ipz_queue))) {
+			ret = H_Resource;
+			goto create_eq_exit2;
+		}
+
+		rpage = ehca_kv_to_g(vpage);
+		ret = hipz_h_register_rpage_eq(shca->ipz_hca_handle,
+					       eq->ipz_eq_handle,
+					       &eq->pf,
+					       0,
+					       HIPZ_EQ_REGISTER_ORIG, rpage, 1);
+
+		if (i == (nr_pages - 1)) {
+			/* last page */
+			vpage = ipz_QPageit_get_inc(&eq->ipz_queue);
+			if ((ret != H_Success) || (vpage != 0)) {
+				goto create_eq_exit2;
+			}
+		} else {
+			if ((ret != H_PAGE_REGISTERED) || (vpage == 0)) {
+				goto create_eq_exit2;
+			}
+		}
+	}
+
+	ipz_QEit_reset(&eq->ipz_queue);
+
+#ifndef EHCA_USERDRIVER
+	{
+	        pid_t pid = 0;
+		(eq->irq_info).pid = pid;
+		(eq->irq_info).eq = eq;
+		(eq->irq_info).wq = ehca_wq;
+		(eq->irq_info).work = &(eq->work);
+	}
+#endif
+
+	/* register interrupt handlers and initialize work queues */
+	if (type == EHCA_EQ) {
+		INIT_WORK(&(eq->work),
+			  ehca_interrupt_eq, (void *)&(eq->irq_info));
+		eq->is_initialized = 1;
+		hipz_request_interrupt(&(eq->irq_info), ehca_interrupt);
+	} else if (type == EHCA_NEQ) {
+		INIT_WORK(&(eq->work),
+			  ehca_interrupt_neq, (void *)&(eq->irq_info));
+		hipz_request_interrupt(&(eq->irq_info), ehca_interrupt);
+	}
+
+	EDEB_EX(7, "ret=%lx", ret);
+
+	return 0;
+
+      create_eq_exit2:
+	ipz_queue_dtor(&eq->ipz_queue);
+
+      create_eq_exit1:
+	hipz_h_destroy_eq(shca->ipz_hca_handle, eq);
+
+	EDEB_EX(7, "ret=%lx", ret);
+
+	return -EINVAL;
+}
+
+void *ehca_poll_eq(struct ehca_shca *shca, struct ehca_eq *eq)
+{
+	unsigned long flags = 0;
+	void *eqe = NULL;
+
+	EDEB_EN(7, "shca=%p  eq=%p", shca, eq);
+	EHCA_CHECK_ADR_P(shca);
+	EHCA_CHECK_EQ_P(eq);
+
+	spin_lock_irqsave(&eq->spinlock, flags);
+	eqe = ipz_QEit_EQ_get_inc_valid(&eq->ipz_queue);
+	spin_unlock_irqrestore(&eq->spinlock, flags);
+
+	EDEB_EX(7, "eq=%p eqe=%p", eq, eqe);
+
+	return eqe;
+}
+
+int ehca_poll_eqs(void *data)
+{
+	extern struct workqueue_struct *ehca_wq;
+	struct ehca_shca *shca;
+	struct ehca_module* module = data;
+	struct list_head *entry;
+
+	do {
+		list_for_each(entry, &module->shca_list) {
+			shca = list_entry(entry, struct ehca_shca, shca_list);
+			if(shca->eq.is_initialized && !kthread_should_stop())
+				queue_work(ehca_wq, &shca->eq.work);		
+		}
+
+		msleep_interruptible(1000);
+	}
+	while(!kthread_should_stop());
+
+	return 0;
+}
+
+int ehca_destroy_eq(struct ehca_shca *shca, struct ehca_eq *eq)
+{
+	unsigned long flags = 0;
+	u64 retcode = H_Success;
+
+	EDEB_EN(7, "shca=%p  eq=%p", shca, eq);
+	EHCA_CHECK_ADR(shca);
+	EHCA_CHECK_EQ(eq);
+
+	spin_lock_irqsave(&eq->spinlock, flags);
+	hipz_free_interrupt(&(eq->irq_info));
+
+	retcode = hipz_h_destroy_eq(shca->ipz_hca_handle, eq);
+
+	spin_unlock_irqrestore(&eq->spinlock, flags);
+
+	if (retcode != H_Success) {
+		EDEB_ERR(4, "Can't free EQ resources.");
+		return -EINVAL;
+	}
+	ipz_queue_dtor(&eq->ipz_queue);
+
+	EDEB_EX(7, "retcode=%lx", retcode);
+
+	return 0;
+}
+
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_eq.h newtree/drivers/infiniband/hw/ehca/ehca_eq.h
--- oldtree/drivers/infiniband/hw/ehca/ehca_eq.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_eq.h	2006-02-13 19:19:59.915525144 -0500
@@ -0,0 +1,78 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  Completion queue, event queue handling helper functions
+ *
+ *  Authors: Waleri Fomin <fomin@de.ibm.com>
+ *           Reinhard Ernst <rernst@de.ibm.com>
+ *           Heiko J Schick <schickhj@de.ibm.com>
+ *           Hoang-Nam Nguyen <hnguyen@de.ibm.com>
+ *            
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_eq.h,v 1.8 2005/09/26 14:54:17 souissi Exp $
+ */
+
+#ifndef EHCA_EQ_H
+#define EHCA_EQ_H
+
+#include "ehca_classes.h"
+#include "ehca_common.h"
+
+enum ehca_eq_type {
+	EHCA_EQ = 0, /* event queue              */
+	EHCA_NEQ     /* notification event queue */
+};
+
+/** @brief hcad internal create EQ
+ */
+int ehca_create_eq(struct ehca_shca *shca,
+		   struct ehca_eq *eq, /* struct contains eq to create */
+		   enum ehca_eq_type type,
+		   const u32 length);
+
+/** @brief destroy the eq
+ */
+int ehca_destroy_eq(struct ehca_shca *shca, struct ehca_eq *eq);
+
+/** @brief hcad internal poll EQ
+  - check if new EQE available,
+  - if yes, increment EQE pointer
+  - otherwise return 0
+  @returns pointer to EQE if new valid EQEavailable
+ */
+void *ehca_poll_eq(struct ehca_shca *shca, struct ehca_eq *eq);
+
+#endif /* EHCA_EQ_H */
+
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_flightrecorder.h newtree/drivers/infiniband/hw/ehca/ehca_flightrecorder.h
--- oldtree/drivers/infiniband/hw/ehca/ehca_flightrecorder.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_flightrecorder.h	2006-02-13 19:19:59.915525144 -0500
@@ -0,0 +1,74 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  flightrecorder macros
+ *
+ *  Authors: Christoph Raisch <raisch@de.ibm.com> 
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_flightrecorder.h,v 1.4 2005/09/26 14:54:17 souissi Exp $
+ */
+/*****************************************************************************/
+#ifndef EHCA_FLIGHTRECORDER_H
+#define EHCA_FLIGHTRECORDER_H
+
+#define ED_EXTEND1(x,ar1...) \
+        unsigned long __EDEB_R2=(const unsigned long)x-0;ED_EXTEND2(ar1)
+#define ED_EXTEND2(x,ar1...) \
+        unsigned long __EDEB_R3=(const unsigned long)x-0;ED_EXTEND3(ar1)
+#define ED_EXTEND3(x,ar1...) \
+        unsigned long __EDEB_R4=(const unsigned long)x-0;ED_EXTEND4(ar1)
+#define ED_EXTEND4(x,ar1...)
+
+#define EHCA_FLIGHTRECORDER_SIZE 65536
+
+extern atomic_t ehca_flightrecorder_index;
+extern unsigned long ehca_flightrecorder[EHCA_FLIGHTRECORDER_SIZE];
+
+/* Not nice, but -O2 optimized */
+
+#define ED_FLIGHT_LOG(x,ar1...) {                                            \
+        u32 flight_offset = ((u32)                                           \
+        atomic_add_return(4, &ehca_flightrecorder_index))                    \
+        % EHCA_FLIGHTRECORDER_SIZE;                                          \
+        unsigned long *flight_trline = &ehca_flightrecorder[flight_offset];  \
+        unsigned long __EDEB_R1 = (unsigned long) x-0; ED_EXTEND1(ar1)       \
+        flight_trline[0]=__EDEB_R1,flight_trline[1]=__EDEB_R2,               \
+        flight_trline[2]=__EDEB_R3,flight_trline[3]=__EDEB_R4; }
+
+#define EHCA_FLIGHTRECORDER_BACKLOG 60
+
+void ehca_flight_to_printk(void);
+
+#endif
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_galpa.h newtree/drivers/infiniband/hw/ehca/ehca_galpa.h
--- oldtree/drivers/infiniband/hw/ehca/ehca_galpa.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_galpa.h	2006-02-13 19:19:59.917524840 -0500
@@ -0,0 +1,74 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  pSeries interface definitions
+ *
+ *  Authors: Waleri Fomin <fomin@de.ibm.com>
+ *           Christoph Raisch <raisch@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_galpa.h,v 1.5 2005/09/26 14:54:17 souissi Exp $
+ */
+
+#ifndef __EHCA_GALPA_H__
+#define __EHCA_GALPA_H__
+
+/* eHCA page (mapped into p-memory)
+    resource to access eHCA register pages in CPU address space  
+*/
+struct h_galpa {
+	u64 fw_handle;
+	/* for pSeries this is a 64bit memory address where 
+	   I/O memory is mapped into CPU address space (kv) */
+};
+
+/**
+   resource to access eHCA address space registers, all types  
+*/
+struct h_galpas {
+	u32 pid;		/*PID of userspace galpa checking */
+	struct h_galpa user;	/* user space accessible resource, 
+				   set to 0 if unused */
+	struct h_galpa kernel;	/* kernel space accessible resource, 
+				   set to 0 if unused */
+};
+/** @brief store value at offset into galpa, will be inline function
+ */
+void hipz_galpa_store(struct h_galpa galpa, u32 offset, u64 value);
+
+/** @brief return value from offset in galpa, will be inline function
+ */
+u64 hipz_galpa_load(struct h_galpa galpa, u32 offset);
+
+#endif				/* __EHCA_GALPA_H__ */
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_hca.c newtree/drivers/infiniband/hw/ehca/ehca_hca.c
--- oldtree/drivers/infiniband/hw/ehca/ehca_hca.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_hca.c	2006-02-13 19:19:59.918524688 -0500
@@ -0,0 +1,323 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  HCA query functions
+ *
+ *  Authors: Heiko J Schick <schickhj@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_hca.c,v 1.43 2005/11/17 08:14:02 schickhj Exp $
+ */
+
+#undef DEB_PREFIX
+#define DEB_PREFIX "shca"
+
+#include "ehca_kernel.h"
+#include "ehca_tools.h"
+
+#include "hcp_if.h"		/* TODO: later via hipz_* header file */
+
+#define TO_MAX_INT(dest, src)  \
+        if (src >= INT_MAX)    \
+               dest = INT_MAX; \
+        else                   \
+               dest = src        
+
+int ehca_query_device(struct ib_device *ibdev, struct ib_device_attr *props)
+{
+	int ret = 0;
+	struct ehca_shca *shca;
+	struct query_hca_rblock *rblock;
+
+	EDEB_EN(7, "");
+	EHCA_CHECK_DEVICE(ibdev);
+
+	memset(props, 0, sizeof(struct ib_device_attr));
+	shca = container_of(ibdev, struct ehca_shca, ib_device);
+
+	rblock = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (rblock == NULL) {
+		EDEB_ERR(4, "Can't allocate rblock memory.");
+		ret = -ENOMEM;
+		goto query_device0;
+	}
+
+	memset(rblock, 0, PAGE_SIZE);
+
+	if (hipz_h_query_hca(shca->ipz_hca_handle, rblock) != H_Success) {
+		EDEB_ERR(4, "Can't query device properties");
+		ret = -EINVAL;
+		goto query_device1;
+	}
+
+	memcpy(&props->node_guid, &rblock->node_guid, (sizeof(u64)));
+	props->fw_ver              = rblock->hw_ver;
+        /* TODO: memcpy(&props->sys_image_guid, ...); */
+	props->max_mr_size         = rblock->max_mr_size;
+	/* TODO: props->page_size_cap        */
+	props->vendor_id           = rblock->vendor_id >> 8;
+        props->vendor_part_id      = rblock->vendor_part_id >> 16;
+	props->hw_ver              = rblock->hw_ver;
+	TO_MAX_INT(props->max_qp, (rblock->max_qp - rblock->cur_qp));
+	/* TODO: props->max_qp_wr  =         */
+	/* TODO: props->device_cap_flags     */
+	props->max_sge             = rblock->max_sge;
+	props->max_sge_rd          = rblock->max_sge_rd;
+	TO_MAX_INT(props->max_qp, (rblock->max_cq - rblock->cur_cq));
+        props->max_cqe             = rblock->max_cqe;
+	TO_MAX_INT(props->max_mr, (rblock->max_cq - rblock->cur_mr));
+	TO_MAX_INT(props->max_pd, rblock->max_pd);
+        /* TODO: props->max_qp_rd_atom       */
+	/* TODO: props->max_qp_init_rd_atom  */
+	/* TODO: props->atomic_cap           */
+	/* TODO: props->max_ee               */
+	/* TODO: props->max_rdd              */
+	props->max_mw              = rblock->max_mw;
+	TO_MAX_INT(props->max_mr, (rblock->max_mw - rblock->cur_mw));
+	props->max_raw_ipv6_qp     = rblock->max_raw_ipv6_qp;
+	props->max_raw_ethy_qp     = rblock->max_raw_ethy_qp;
+	props->max_mcast_grp       = rblock->max_mcast_grp;
+	props->max_mcast_qp_attach = rblock->max_qps_attached_mcast_grp;
+	props->max_total_mcast_qp_attach = rblock->max_qps_attached_all_mcast_grp;
+
+	TO_MAX_INT(props->max_ah, rblock->max_ah);
+
+	props->max_fmr             = rblock->max_mr;
+	/* TODO: props->max_map_per_fmr      */
+
+	/* TODO: props->max_srq              */
+	/* TODO: props->max_srq_wr           */
+	/* TODO: props->max_srq_sge          */
+	props->max_srq             = 0;
+	props->max_srq_wr          = 0;
+	props->max_srq_sge         = 0;
+
+	/* TODO: props->max_pkeys            */
+	props->max_pkeys           = 16;
+
+        props->local_ca_ack_delay  = rblock->local_ca_ack_delay;
+
+      query_device1:
+	kfree(rblock);
+
+      query_device0:
+	EDEB_EX(7, "ret=%x", ret);
+
+	return ret;
+}
+
+int ehca_query_port(struct ib_device *ibdev,
+		    u8 port, struct ib_port_attr *props)
+{
+	int ret = 0;
+	struct ehca_shca *shca;
+	struct query_port_rblock *rblock;
+
+	EDEB_EN(7, "port=%x", port);
+	EHCA_CHECK_DEVICE(ibdev);
+
+	memset(props, 0, sizeof(struct ib_port_attr));
+	shca = container_of(ibdev, struct ehca_shca, ib_device);
+
+	rblock = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (rblock == NULL) {
+		EDEB_ERR(4, "Can't allocate rblock memory.");
+		ret = -ENOMEM;
+		goto query_port0;
+	}
+
+	memset(rblock, 0, PAGE_SIZE);
+
+	if (hipz_h_query_port(shca->ipz_hca_handle, port, rblock) != H_Success) {
+		EDEB_ERR(4, "Can't query port properties");
+		ret = -EINVAL;
+		goto query_port1;
+	}
+
+	props->state = rblock->state;
+
+	switch (rblock->max_mtu) {
+	case 0x1:
+		props->active_mtu = props->max_mtu = IB_MTU_256;
+		break;
+	case 0x2:
+		props->active_mtu = props->max_mtu = IB_MTU_512;
+		break;
+	case 0x3:
+		props->active_mtu = props->max_mtu = IB_MTU_1024;
+		break;
+	case 0x4:
+		props->active_mtu = props->max_mtu = IB_MTU_2048;
+		break;
+	case 0x5:
+		props->active_mtu = props->max_mtu = IB_MTU_4096;
+		break;
+	default:
+		EDEB_ERR(4, "Unknown MTU size: %x.", rblock->max_mtu);
+	}
+
+	props->gid_tbl_len     = rblock->gid_tbl_len;
+	/* TODO: props->port_cap_flags */
+	props->max_msg_sz      = rblock->max_msg_sz;
+	props->bad_pkey_cntr   = rblock->bad_pkey_cntr;
+	props->qkey_viol_cntr  = rblock->qkey_viol_cntr;
+	props->pkey_tbl_len    = rblock->pkey_tbl_len;
+	props->lid             = rblock->lid;
+	props->sm_lid          = rblock->sm_lid;
+	props->lmc             = rblock->lmc;
+	/* TODO: max_vl_num            */
+	props->sm_sl           = rblock->sm_sl;
+	props->subnet_timeout  = rblock->subnet_timeout;
+	props->init_type_reply = rblock->init_type_reply;
+
+	/* TODO: props->active_width   */
+	props->active_width    = IB_WIDTH_12X;
+	/* TODO: props->active_speed   */
+
+	/* TODO: props->phys_state     */
+
+      query_port1:
+	kfree(rblock);
+
+      query_port0:
+	EDEB_EX(7, "ret=%x", ret);
+
+	return ret;
+}
+
+int ehca_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 *pkey)
+{
+	int ret = 0;
+	struct ehca_shca *shca;
+	struct query_port_rblock *rblock;
+
+	EDEB_EN(7, "port=%x index=%x", port, index);
+	EHCA_CHECK_DEVICE(ibdev);
+
+	if (index > 16) {
+		EDEB_ERR(4, "Invalid index: %x.", index);
+		ret = -EINVAL;
+		goto query_pkey0;
+	}
+
+	shca = container_of(ibdev, struct ehca_shca, ib_device);
+
+	rblock = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (rblock == NULL) {
+		EDEB_ERR(4,  "Can't allocate rblock memory.");
+		ret = -ENOMEM;
+		goto query_pkey0;
+	}
+
+	memset(rblock, 0, PAGE_SIZE);
+
+	if (hipz_h_query_port(shca->ipz_hca_handle, port, rblock) != H_Success) {
+		EDEB_ERR(4, "Can't query port properties");
+		ret = -EINVAL;
+		goto query_pkey1;
+	}
+
+	memcpy(pkey, &rblock->pkey_entries + index, sizeof(u16));
+
+      query_pkey1:
+	kfree(rblock);
+
+      query_pkey0:
+	EDEB_EX(7, "ret=%x", ret);
+
+	return ret;
+}
+
+int ehca_query_gid(struct ib_device *ibdev, u8 port,
+		   int index, union ib_gid *gid)
+{
+	int ret = 0;
+	struct ehca_shca *shca;
+	struct query_port_rblock *rblock;
+
+	EDEB_EN(7, "port=%x index=%x", port, index);
+	EHCA_CHECK_DEVICE(ibdev);
+
+	if (index > 255) {
+		EDEB_ERR(4, "Invalid index: %x.", index);
+		ret = -EINVAL;
+		goto query_gid0;
+	}
+
+	shca = container_of(ibdev, struct ehca_shca, ib_device);
+
+	rblock = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (rblock == NULL) {
+		EDEB_ERR(4, "Can't allocate rblock memory.");
+		ret = -ENOMEM;
+		goto query_gid0;
+	}
+
+	memset(rblock, 0, PAGE_SIZE);
+
+	if (hipz_h_query_port(shca->ipz_hca_handle, port, rblock) != H_Success) {
+		EDEB_ERR(4, "Can't query port properties");
+		ret = -EINVAL;
+		goto query_gid1;
+	}
+
+	memcpy(&gid->raw[0], &rblock->gid_prefix, sizeof(u64));
+	memcpy(&gid->raw[8], &rblock->guid_entries[index], sizeof(u64));
+
+      query_gid1:
+	kfree(rblock);
+
+      query_gid0:
+	EDEB_EX(7, "ret=%x GID=%lx%lx", ret, 
+		*(u64 *) & gid->raw[0], 
+		*(u64 *) & gid->raw[8]);
+
+	return ret;
+}
+
+int ehca_modify_port(struct ib_device *ibdev,
+		     u8 port, int port_modify_mask,
+		     struct ib_port_modify *props)
+{
+	int ret = 0;
+
+	EDEB_EN(7, "port=%x", port);
+	EHCA_CHECK_DEVICE(ibdev);
+
+	/* TODO: implementation */
+
+	EDEB_EX(7, "ret=%x", ret);
+
+	return ret;
+}
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_irq.c newtree/drivers/infiniband/hw/ehca/ehca_irq.c
--- oldtree/drivers/infiniband/hw/ehca/ehca_irq.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_irq.c	2006-02-13 19:19:59.919524536 -0500
@@ -0,0 +1,446 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  Functions for EQs, NEQs and interrupts
+ *
+ *  Authors: Heiko J Schick <schickhj@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_irq.c,v 1.55 2005/11/22 06:55:58 schickhj Exp $
+ */
+
+#include "ehca_kernel.h"
+#include "ehca_irq.h"
+
+#define DEB_PREFIX "eirq"
+
+#include "ehca_kernel.h"
+#include "ehca_classes.h"
+#include "ehca_tools.h"
+#include "ehca_eq.h"
+#include "ehca_irq.h"
+#include "hcp_if.h"
+
+#define EQE_COMPLETION_EVENT   EHCA_BMASK_IBM(1,1)
+#define EQE_CQ_QP_NUMBER       EHCA_BMASK_IBM(8,31)
+#define EQE_EE_IDENTIFIER      EHCA_BMASK_IBM(2,7)
+#define EQE_CQ_NUMBER          EHCA_BMASK_IBM(8,31)
+#define EQE_QP_NUMBER          EHCA_BMASK_IBM(8,31)
+#define EQE_QP_TOKEN           EHCA_BMASK_IBM(32,63)
+#define EQE_CQ_TOKEN           EHCA_BMASK_IBM(32,63)
+
+#define NEQE_COMPLETION_EVENT  EHCA_BMASK_IBM(1,1)
+#define NEQE_EVENT_CODE        EHCA_BMASK_IBM(2,7)
+#define NEQE_PORT_NUMBER       EHCA_BMASK_IBM(8,15)
+#define NEQE_PORT_AVAILABILITY EHCA_BMASK_IBM(16,16)
+
+#define ERROR_DATA_LENGTH      EHCA_BMASK_IBM(52,63)
+
+static inline void comp_event_callback(struct ehca_cq *cq)
+{
+	EDEB_EN(7, "cq=%p", cq);
+
+	if (cq->ib_cq.comp_handler == NULL)
+		return;
+
+	spin_lock(&cq->cb_lock);
+	cq->ib_cq.comp_handler(&cq->ib_cq, cq->ib_cq.cq_context);
+	spin_unlock(&cq->cb_lock);
+
+	EDEB_EX(7, "cq=%p", cq);
+
+	return;
+}
+
+int ehca_error_data(struct ehca_shca *shca,
+				  u64 ressource)
+{
+
+	unsigned long ret = 0;
+	u64 *rblock;
+	unsigned long block_count;
+
+	EDEB_EN(7, "ressource=%lx", ressource);
+
+	rblock = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (rblock == NULL) {
+		EDEB_ERR(4, "Cannot allocate rblock memory.");
+		ret = -ENOMEM;
+		goto error_data1;
+	}
+	
+	memset(rblock, 0, PAGE_SIZE);
+	
+	ret = hipz_h_error_data(shca->ipz_hca_handle, 
+				ressource,
+				rblock,
+				&block_count);
+
+	if (ret == H_R_STATE) {
+		EDEB_ERR(4, "No error data is available: %lx.", ressource);
+	}
+	else if (ret == H_Success) {
+		int length;
+
+		length = EHCA_BMASK_GET(ERROR_DATA_LENGTH, rblock[0]);
+
+		if (length > PAGE_SIZE)
+			length = PAGE_SIZE;
+
+		EDEB_ERR(4, "Error data is available: %lx.", ressource);
+		EDEB_ERR(4, "EHCA ----- error data begin "
+			 "---------------------------------------------------");
+		EDEB_DMP(4, rblock, length, "ressource=%lx", ressource);
+		EDEB_ERR(4, "EHCA ----- error data end "
+			 "-----------------------------------------------------");
+	}
+	else {
+		EDEB_ERR(4, "Error data could not be fetched: %lx", ressource);
+	}
+
+	kfree(rblock);
+
+      error_data1:
+	return ret;
+
+}
+
+static void qp_event_callback(struct ehca_shca *shca,
+					  u64 eqe,
+					  enum ib_event_type event_type)
+{
+	struct ib_event event;
+	struct ehca_qp *qp;
+	u32 token = EHCA_BMASK_GET(EQE_QP_TOKEN, eqe);
+
+	EDEB_EN(7, "eqe=%lx", eqe);
+
+	down_read(&ehca_qp_idr_sem);
+	qp = idr_find(&ehca_qp_idr, token);
+	up_read(&ehca_qp_idr_sem);
+
+	if (qp == NULL)
+		return;
+
+	if (event_type == IB_EVENT_QP_FATAL)
+		EDEB_ERR(4, "QP 0x%x (ressource=%lx) has errors.", 
+			 qp->ib_qp.qp_num, qp->ipz_qp_handle.handle);
+
+	ehca_error_data(shca, qp->ipz_qp_handle.handle);
+
+	if (qp->ib_qp.event_handler == NULL)
+		return;
+
+	event.device     = &shca->ib_device;
+	event.event      = event_type;
+	event.element.qp = &qp->ib_qp;
+
+	qp->ib_qp.event_handler(&event, qp->ib_qp.qp_context);
+
+	EDEB_EX(7, "qp=%p", qp);
+
+	return;
+}
+
+static void cq_event_callback(struct ehca_shca *shca,
+					  u64 eqe)
+{
+	struct ehca_cq *cq;	
+	u32 token = EHCA_BMASK_GET(EQE_CQ_TOKEN, eqe);
+
+	EDEB_EN(7, "eqe=%lx", eqe);
+
+	down_read(&ehca_cq_idr_sem);
+	cq = idr_find(&ehca_cq_idr, token);
+	up_read(&ehca_cq_idr_sem);
+
+	if (cq == NULL)
+		return;
+
+	EDEB_ERR(4, "CQ 0x%x (ressource=%lx) has errors.", 
+		 cq->cq_number, cq->ipz_cq_handle.handle);
+	
+	ehca_error_data(shca, cq->ipz_cq_handle.handle);
+
+	EDEB_EX(7, "cq=%p", cq);
+
+	return;
+}
+
+static void parse_identifier(struct ehca_shca *shca, u64 eqe)
+{
+	u8 identifier = EHCA_BMASK_GET(EQE_EE_IDENTIFIER, eqe);
+
+	EDEB_EN(7, "shca=%p eqe=%lx", shca, eqe);
+
+	switch (identifier) {
+	case 0x02:		/* path migrated */
+		qp_event_callback(shca, eqe, IB_EVENT_PATH_MIG);
+		break;
+	case 0x03:		/* communication established */
+		qp_event_callback(shca, eqe, IB_EVENT_COMM_EST);
+		break;
+	case 0x04:		/* send queue drained */
+		qp_event_callback(shca, eqe, IB_EVENT_SQ_DRAINED);
+		break;
+	case 0x05:		/* QP error */
+	case 0x06:		/* QP error */
+		qp_event_callback(shca, eqe, IB_EVENT_QP_FATAL);
+		break;
+	case 0x07:		/* CQ error */
+	case 0x08:		/* CQ error */
+		cq_event_callback(shca, eqe);
+		break;
+	case 0x09:		/* MRMWPTE error */
+	case 0x0A:		/* port event */
+	case 0x0B:		/* MR access error */
+	case 0x0C:		/* EQ error */
+	case 0x0D:		/* P/Q_Key mismatch */
+	case 0x10:		/* sampling complete */
+	case 0x11:		/* unaffiliated access error */
+	case 0x12:		/* path migrating error */
+	case 0x13:		/* interface trace stopped */
+	case 0x14:		/* first error capture info available */
+	default:
+		EDEB_ERR(4, "Unknown identifier: %x.", identifier);
+		break;
+	}
+
+	EDEB_EN(7, "eqe=%lx identifier=%x", eqe, identifier);
+
+	return;
+}
+
+static void parse_ec(struct ehca_shca *shca, u64 eqe)
+{
+	struct ib_event event;
+	u8 ec   = EHCA_BMASK_GET(NEQE_EVENT_CODE, eqe);
+	u8 port = EHCA_BMASK_GET(NEQE_PORT_NUMBER, eqe);	
+
+	EDEB_EN(7, "shca=%p eqe=%lx", shca, eqe);
+
+	switch (ec) {
+	case 0x30:		/* port availability change */
+		if (EHCA_BMASK_GET(NEQE_PORT_AVAILABILITY, eqe)) {
+			EDEB(4, "EHCA port %x is active.", port);
+			event.device = &shca->ib_device;
+			event.event = IB_EVENT_PORT_ACTIVE;
+			event.element.port_num = port;
+			shca->sport[port - 1].port_state = IB_PORT_ACTIVE;
+			ib_dispatch_event(&event);
+		} else {
+			EDEB(4, "EHCA port %x is inactive.", port);
+			event.device = &shca->ib_device;
+			event.event = IB_EVENT_PORT_ERR;
+			event.element.port_num = port;
+			shca->sport[port - 1].port_state = IB_PORT_DOWN;
+			ib_dispatch_event(&event);
+		}
+		break;
+	case 0x31:		
+                /* port configuration change      */
+		/* disruptive change is caused by */
+                /* LID, PKEY or SM change         */
+		EDEB(4, "EHCA disruptive port %x "
+		     "configuration change.", port);
+
+		EDEB(4, "EHCA port %x is inactive.", port);
+		event.device = &shca->ib_device;
+		event.event = IB_EVENT_PORT_ERR;
+		event.element.port_num = port;
+		shca->sport[port - 1].port_state = IB_PORT_DOWN;
+		ib_dispatch_event(&event);
+
+		EDEB(4, "EHCA port %x is active.", port);
+		event.device = &shca->ib_device;
+		event.event = IB_EVENT_PORT_ACTIVE;
+		event.element.port_num = port;
+		shca->sport[port - 1].port_state = IB_PORT_ACTIVE;
+		ib_dispatch_event(&event);
+		break;
+	case 0x32:		/* adapter malfunction */
+	case 0x33:		/* trace stopped */
+	default:
+		EDEB_ERR(4, "Unknown event code: %x.", ec);
+		break;
+	}
+
+	EDEB_EN(7, "eqe=%lx ec=%x", eqe, ec);
+
+	return;
+}
+
+static inline void reset_eq_pending(struct ehca_cq *cq)
+{
+	u64 CQx_EP = 0;
+	struct h_galpa gal = cq->ehca_cq_core.galpas.kernel;
+
+	EDEB_EN(7, "cq=%p", cq);
+
+	hipz_galpa_store_cq(gal, CQx_EP, 0x0);
+	CQx_EP = hipz_galpa_load(gal, CQTEMM_OFFSET(CQx_EP));
+	EDEB(7, "CQx_EP=%lx", CQx_EP);
+
+	EDEB_EX(7, "cq=%p", cq);
+
+	return;
+}
+
+/**
+ * ehca_interrupt_eq - On a HCA interrupt, we call this function, to poll the
+ * event queue and parse the received event queue entry if it was a completion 
+ * or an error event.
+ *
+ * @data: Interrupt information which EQ we must poll.
+ */
+void ehca_interrupt_eq(void *data)
+{
+	struct ehca_irq_info *irq_info;
+	struct ehca_shca *shca;
+	struct ehca_eqe *eqe;
+	int int_state;
+
+	EDEB_EN(7, "data=%p", data);
+
+	irq_info = (struct ehca_irq_info *)data;
+	shca = to_shca(eq);
+
+	do {
+		eqe = (struct ehca_eqe *)ehca_poll_eq(shca, &shca->eq);
+
+		if ((shca->hw_level >= 2) && (eqe != NULL))
+			int_state = 1;
+		else
+			int_state = 0;
+		
+		while ((int_state == 1) || (eqe != 0)) {
+			while (eqe) {
+				u64 eqe_value = eqe->entry;
+
+				EDEB(7, "eqe_value=%lx", eqe_value);
+
+				/* TODO: better structure */
+				if (EHCA_BMASK_GET(EQE_COMPLETION_EVENT,
+						   eqe_value)) {
+					extern struct idr ehca_cq_idr;
+					u32 token;
+					struct ehca_cq *cq;
+
+					EDEB(6, "... completion event");
+					token =
+					    EHCA_BMASK_GET(EQE_CQ_TOKEN,
+							   eqe_value);
+					down_read(&ehca_cq_idr_sem);
+					cq = idr_find(&ehca_cq_idr, token);
+					up_read(&ehca_cq_idr_sem);
+
+					reset_eq_pending(cq);
+					comp_event_callback(cq);
+				} else {
+					EDEB(6, "... non completion event");
+					parse_identifier(shca, eqe_value);
+				}
+				eqe =
+				    (struct ehca_eqe *)ehca_poll_eq(shca,
+								    &shca->eq);
+			}
+
+			/* TODO: do we need hw_level  */
+			if (shca->hw_level >= 2)
+				int_state =
+				    hipz_h_query_int_state(shca->ipz_hca_handle,
+							   irq_info);
+			eqe = (struct ehca_eqe *)ehca_poll_eq(shca, &shca->eq);
+
+		}
+	} while (int_state != 0);
+
+	EDEB_EX(7, "shca=%p", shca);
+
+	return;
+}
+
+/**
+ * ehca_interrupt_neq - On a an NEQ interrupt, we call this function to poll the
+ * notification event queue and parse the received event queue entry.
+ *
+ * @data:  Interrupt information which EQ we must poll.
+ */
+void ehca_interrupt_neq(void *data)
+{
+	struct ehca_irq_info *irq_info;
+	struct ehca_shca *shca;
+	struct ehca_eqe *eqe;
+	u64 ret = H_Success;
+
+	EDEB_EN(7, "data=%p", data);
+
+	irq_info = (struct ehca_irq_info *)data;
+	shca = to_shca(neq);
+	eqe = (struct ehca_eqe *)ehca_poll_eq(shca, &shca->neq);
+
+	while (eqe) {
+		if (!EHCA_BMASK_GET(NEQE_COMPLETION_EVENT, eqe->entry))
+			parse_ec(shca, eqe->entry);
+
+		eqe = (struct ehca_eqe *)ehca_poll_eq(shca, &shca->neq);
+	}
+
+	ret = hipz_h_reset_event(shca->ipz_hca_handle,
+				 shca->neq.ipz_eq_handle, 0xFFFFFFFFFFFFFFFF);
+
+	if (ret != H_Success)
+		EDEB_ERR(4, "Can't clear notification events.");
+
+	EDEB_EX(7, "shca=%p", shca);
+
+	return;
+}
+
+/**
+ * ehca_primary_interrupt - Takes an EQ or NEQ interrupt and
+ * puts the corresponding element into the work queue.
+ */
+irqreturn_t ehca_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct ehca_irq_info *info = (struct ehca_irq_info *)dev_id;
+
+	EDEB_EN(7, "dev_id=%p", dev_id);
+
+	queue_work(info->wq, info->work);
+
+	EDEB_EX(7, "");
+
+	return IRQ_HANDLED;
+}
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_irq.h newtree/drivers/infiniband/hw/ehca/ehca_irq.h
--- oldtree/drivers/infiniband/hw/ehca/ehca_irq.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_irq.h	2006-02-13 19:19:59.920524384 -0500
@@ -0,0 +1,90 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  Function definitions and structs for EQs, NEQs and interrupts
+ *
+ *  Authors: Heiko J Schick <schickhj@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_irq.h,v 1.20 2005/11/03 07:48:38 schickhj Exp $
+ */
+
+#ifndef __EHCA_IRQ_H
+#define __EHCA_IRQ_H
+
+
+struct ehca_shca;
+
+#include <asm/atomic.h>
+#include <asm/types.h>
+
+#ifndef EHCA_USERDRIVER
+#include <linux/interrupt.h>
+#endif
+
+#ifndef __KERNEL__
+#define NO_IRQ (-1)
+#include <linux/version.h>
+#include <errno.h>
+#endif
+
+#ifndef EHCA_USERDRIVER
+#define to_shca(queue) container_of(irq_info->eq,     \
+                                    struct ehca_shca, \
+                                    queue)
+#else
+extern struct ehca_module ehca_module;
+#define to_shca(queue) list_entry(ehca_module.shca_list.next, \
+                                  struct ehca_shca, shca_list)
+#endif
+
+struct ehca_irq_info {
+	__u32 ist;
+	__u32 irq;
+	void *eq;
+
+	atomic_t irq_count;
+	struct workqueue_struct *wq;
+	struct work_struct *work;
+
+	pid_t pid;
+};
+
+void ehca_interrupt_eq(void *data);
+void ehca_interrupt_neq(void *data);
+irqreturn_t ehca_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+irqreturn_t ehca_interrupt_direct(int irq, void *dev_id, struct pt_regs *regs);
+int ehca_error_data(struct ehca_shca *shca, u64 ressource);
+
+#endif
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_iverbs.h newtree/drivers/infiniband/hw/ehca/ehca_iverbs.h
--- oldtree/drivers/infiniband/hw/ehca/ehca_iverbs.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_iverbs.h	2006-02-13 19:19:59.921524232 -0500
@@ -0,0 +1,163 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  Function definitions for internal functions 
+ *
+ *  Authors: Heiko J Schick <schickhj@de.ibm.com>
+ *           Khadija Souissi <souissik@de.ibm.com> 
+ *           Christoph Raisch <raisch@de.ibm.com>
+ *           Hoang-Nam Nguyen <hnguyen@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_iverbs.h,v 1.30 2005/11/03 09:16:49 fomin Exp $
+ */
+
+#ifndef __EHCA_IVERBS_H__
+#define __EHCA_IVERBS_H__
+
+#include "ehca_classes.h"
+/** ehca internal verb for testuse
+ */
+void ehca_init_module(void);
+
+int ehca_query_device(struct ib_device *ibdev, struct ib_device_attr *props);
+int ehca_query_port(struct ib_device *ibdev,
+		    u8 port, struct ib_port_attr *props);
+int ehca_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 * pkey);
+int ehca_query_gid(struct ib_device *ibdev, u8 port,
+		   int index, union ib_gid *gid);
+int ehca_modify_port(struct ib_device *ibdev,
+		     u8 port, int port_modify_mask,
+		     struct ib_port_modify *props);
+
+struct ib_pd *ehca_alloc_pd(struct ib_device *device,
+			    struct ib_ucontext *context,
+			    struct ib_udata *udata);
+
+int ehca_dealloc_pd(struct ib_pd *pd);
+
+struct ib_ah *ehca_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr);
+int ehca_modify_ah(struct ib_ah *ah, struct ib_ah_attr *ah_attr);
+int ehca_query_ah(struct ib_ah *ah, struct ib_ah_attr *ah_attr);
+int ehca_destroy_ah(struct ib_ah *ah);
+
+struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe,
+			     struct ib_ucontext *context,
+			     struct ib_udata *udata);
+int ehca_resize_cq(struct ib_cq *cq, int cqe);
+
+int ehca_destroy_cq(struct ib_cq *cq);
+
+int ehca_poll_cq(struct ib_cq *cq, int num_entries, struct ib_wc *wc);
+
+int ehca_peek_cq(struct ib_cq *cq, int wc_cnt);
+
+int ehca_req_notify_cq(struct ib_cq *cq, enum ib_cq_notify cq_notify);
+
+struct ib_qp *ehca_create_qp(struct ib_pd *pd,
+			     struct ib_qp_init_attr *init_attr,
+			     struct ib_udata *udata);
+
+u64 ehca_define_sqp(struct ehca_shca *shca, struct ehca_qp *ibqp,
+			       struct ib_qp_init_attr *qp_init_attr);
+
+int ehca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask);
+
+int ehca_query_qp(struct ib_qp *qp, struct ib_qp_attr *qp_attr,
+		  int qp_attr_mask, struct ib_qp_init_attr *qp_init_attr);
+
+int ehca_destroy_qp(struct ib_qp *qp);
+
+int ehca_post_send(struct ib_qp *qp,
+		   struct ib_send_wr *send_wr, struct ib_send_wr **bad_send_wr);
+
+int ehca_post_recv(struct ib_qp *qp,
+		   struct ib_recv_wr *recv_wr, struct ib_recv_wr **bad_recv_wr);
+
+struct ib_mr *ehca_get_dma_mr(struct ib_pd *pd, int mr_access_flags);
+
+struct ib_mr *ehca_reg_phys_mr(struct ib_pd *pd,
+			       struct ib_phys_buf *phys_buf_array,
+			       int num_phys_buf,
+			       int mr_access_flags, u64 *iova_start);
+
+struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd,
+			       struct ib_umem *region,
+			       int mr_access_flags, struct ib_udata *udata);
+
+int ehca_rereg_phys_mr(struct ib_mr *mr,
+		       int mr_rereg_mask,
+		       struct ib_pd *pd,
+		       struct ib_phys_buf *phys_buf_array,
+		       int num_phys_buf, int mr_access_flags, u64 *iova_start);
+
+int ehca_query_mr(struct ib_mr *mr, struct ib_mr_attr *mr_attr);
+
+int ehca_dereg_mr(struct ib_mr *mr);
+
+struct ib_mw *ehca_alloc_mw(struct ib_pd *pd);
+
+int ehca_bind_mw(struct ib_qp *qp,
+		 struct ib_mw *mw, struct ib_mw_bind *mw_bind);
+
+int ehca_dealloc_mw(struct ib_mw *mw);
+
+struct ib_fmr *ehca_alloc_fmr(struct ib_pd *pd,
+			      int mr_access_flags,
+			      struct ib_fmr_attr *fmr_attr);
+
+int ehca_map_phys_fmr(struct ib_fmr *fmr,
+		      u64 *page_list, int list_len, u64 iova);
+
+int ehca_unmap_fmr(struct list_head *fmr_list);
+
+int ehca_dealloc_fmr(struct ib_fmr *fmr);
+
+int ehca_attach_mcast(struct ib_qp *qp, union ib_gid *gid, u16 lid);
+
+int ehca_detach_mcast(struct ib_qp *qp, union ib_gid *gid, u16 lid);
+
+struct ib_ucontext *ehca_alloc_ucontext(struct ib_device *device,
+					struct ib_udata *udata);
+int ehca_dealloc_ucontext(struct ib_ucontext *context);
+
+int ehca_mmap(struct ib_ucontext *context, struct vm_area_struct *vma);
+
+int ehca_poll_eqs(void *data);
+
+int ehca_mmap_nopage(u64 foffset,u64 length,void ** mapped,struct vm_area_struct ** vma);
+int ehca_mmap_register(u64 physical,void ** mapped,struct vm_area_struct ** vma); 
+int ehca_munmap(unsigned long addr, size_t len);
+
+#endif
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_kernel.h newtree/drivers/infiniband/hw/ehca/ehca_kernel.h
--- oldtree/drivers/infiniband/hw/ehca/ehca_kernel.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_kernel.h	2006-02-13 19:19:59.921524232 -0500
@@ -0,0 +1,135 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  generalized functions for code shared between kernel and userspace
+ *
+ *  Authors: Christoph Raisch <raisch@de.ibm.com> 
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_kernel.h,v 1.34 2005/11/15 13:52:26 schickhj Exp $
+ */
+
+#ifndef _EHCA_KERNEL_H_
+#define _EHCA_KERNEL_H_
+
+#define FALSE (1==0)
+#define TRUE (1==1)
+
+#include <linux/types.h>
+#include "ehca_common.h"
+#include "ehca_kernel.h"
+
+/** 
+ * Handle to be used for adress translation mechanisms, currently a placeholder.
+ */
+struct ehca_bridge_handle {
+	int handle;
+};
+
+inline static int ehca_adr_bad(void *adr)
+{
+	return (adr == 0);
+};
+
+#ifdef EHCA_USERDRIVER
+/* userspace replacement for kernel functions */
+#include "ehca_usermain.h"
+#else				/* USERDRIVER */
+/* kernel includes */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <asm/current.h>
+#include <asm/io.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/kthread.h>
+#include <linux/mman.h>
+#include <linux/delay.h>
+#include <asm/processor.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
+#include <asm/ibmebus.h>
+#else
+#include "ebus/ibmebus.h"
+#endif
+#include <linux/pci.h>
+#include <linux/idr.h>
+#include <linux/rwsem.h>
+
+struct ehca_cache {
+	kmem_cache_t *cache;
+	int size;
+};
+
+#ifdef __powerpc64__
+#include <linux/spinlock.h>
+#include <asm/abs_addr.h>
+#include <asm/prom.h>
+#else
+#endif
+
+#include "ehca_tools.h"
+
+#include <asm/pgtable.h>
+
+/**
+ * ehca_kv_to_g - Converts a kernel virtual address to visible addresses
+ * (i.e. a physical/absolute address).
+ */
+inline static u64 ehca_kv_to_g(void *adr)
+{
+	u64 raddr;
+#ifndef CONFIG_PPC64
+	raddr = virt_to_phys((u64)adr);
+#else 
+	/* we need to find not only the physical address
+	 * but the absolute to account for memory segmentation */
+	raddr = virt_to_abs((u64)adr);
+#endif
+	if (((u64)adr & VMALLOC_START) == VMALLOC_START) {
+		raddr = phys_to_abs((page_to_pfn(vmalloc_to_page(adr)) <<
+				     PAGE_SHIFT));
+	}
+	return (raddr);
+}
+
+#endif /* USERDRIVER */
+#include <linux/types.h>
+
+
+#endif /* _EHCA_KERNEL_H_ */
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_main.c newtree/drivers/infiniband/hw/ehca/ehca_main.c
--- oldtree/drivers/infiniband/hw/ehca/ehca_main.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_main.c	2006-02-13 19:19:59.922524080 -0500
@@ -0,0 +1,996 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  module start stop, hca detection
+ *
+ *  Authors: Heiko J Schick <schickhj@de.ibm.com>
+ *           Christoph Raisch <raisch@de.ibm.com> 
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_main.c,v 1.130 2005/12/15 13:18:02 nguyen Exp $
+ */
+
+#define DEB_PREFIX "shca"
+
+#include "ehca_kernel.h"
+#include "ehca_tools.h"
+#include "ehca_classes.h"
+#include "ehca_iverbs.h"
+#include "ehca_eq.h"
+#include "ehca_mrmw.h"
+
+#include "hcp_sense.h"		/* TODO: later via hipz_* header file */
+#include "hcp_if.h"		/* TODO: later via hipz_* header file */
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Christoph Raisch <raisch@de.ibm.com>");
+MODULE_DESCRIPTION("IBM eServer HCA Driver");
+MODULE_VERSION("EHCA2_0042");
+
+#ifdef EHCA_USERDRIVER
+int ehca_open_aqp1     = 1;
+#else
+int ehca_open_aqp1     = 0;
+#endif
+int ehca_tracelevel    = -1;
+int ehca_hw_level      = 0;
+int ehca_nr_ports      = 2;
+int ehca_use_hp_mr     = 0;
+int ehca_port_act_time = 30;
+int ehca_poll_all_eqs  = 1;
+int ehca_static_rate   = -1;
+
+module_param_named(open_aqp1,     ehca_open_aqp1,     int, 0);
+module_param_named(tracelevel,    ehca_tracelevel,    int, 0);
+module_param_named(hw_level,      ehca_hw_level,      int, 0);
+module_param_named(nr_ports,      ehca_nr_ports,      int, 0);
+module_param_named(use_hp_mr,     ehca_use_hp_mr,     int, 0);
+module_param_named(port_act_time, ehca_port_act_time, int, 0);
+module_param_named(poll_all_eqs,  ehca_poll_all_eqs,  int, 0);
+module_param_named(static_rate,   ehca_static_rate,   int, 0);
+
+MODULE_PARM_DESC(open_aqp1,     "0 no define AQP1 on startup (default),"
+			        "1 define AQP1 on startup");
+MODULE_PARM_DESC(tracelevel,    "0 maximum performance (no messages)," 
+		                "9 maximum messages (no performance)");
+MODULE_PARM_DESC(hw_level,      "0 autosensing,"
+                                "1 v. 0.20,"
+	                        "2 v. 0.21");
+MODULE_PARM_DESC(nr_ports,      "number of connected ports (default: 2)");
+MODULE_PARM_DESC(use_hp_mr,     "use high performance MRs,"
+		                "0 no (default),"
+                                "1 yes");
+MODULE_PARM_DESC(port_act_time, "time to wait for port activation" 
+		                "(default: 30 sec.)");
+MODULE_PARM_DESC(poll_all_eqs,  "polls all event queues periodically"
+	                        "0 no,"
+                                "1 yes (default)");
+MODULE_PARM_DESC(static_rate,   "set permanent static rate (default: disabled)");
+
+/* This external trace mask controls what will end up in the 
+ * kernel ring buffer. Number 6 means, that everything between
+ * 0 and 5 will be stored.
+ */
+u8 ehca_edeb_mask[EHCA_EDEB_TRACE_MASK_SIZE]={6,6,6,6,
+                                              6,6,6,6,
+                                              6,6,6,6,
+                                              6,6,6,6,
+                                              6,6,6,6,
+                                              6,6,6,6,
+                                              6,6,6,6,
+                                              6,6,1,0}; 
+                      /* offset 0x1e is flightrecorder */
+EXPORT_SYMBOL(ehca_edeb_mask);
+
+atomic_t ehca_flightrecorder_index = ATOMIC_INIT(1);
+unsigned long ehca_flightrecorder[EHCA_FLIGHTRECORDER_SIZE];
+EXPORT_SYMBOL(ehca_flightrecorder_index);
+EXPORT_SYMBOL(ehca_flightrecorder);
+
+DECLARE_RWSEM(ehca_qp_idr_sem);
+DECLARE_RWSEM(ehca_cq_idr_sem);
+DEFINE_IDR(ehca_qp_idr);
+DEFINE_IDR(ehca_cq_idr);
+
+struct ehca_module ehca_module;
+struct workqueue_struct *ehca_wq;
+struct task_struct  *ehca_kthread_eq;
+
+/**
+ * ehca_init_trace - TODO
+ */
+void ehca_init_trace(void)
+{
+	EDEB_EN(7, "");
+
+	if (ehca_tracelevel != -1) {
+		int i;
+		for (i = 0; i < EHCA_EDEB_TRACE_MASK_SIZE; i++)
+			ehca_edeb_mask[i] = ehca_tracelevel;
+	}
+
+	EDEB_EX(7, "");
+}
+
+/**
+ * ehca_init_flight - TODO
+ */
+void ehca_init_flight(void)
+{
+	EDEB_EN(7, "");
+
+	memset(ehca_flightrecorder, 0xFA,
+	       sizeof(unsigned long) * EHCA_FLIGHTRECORDER_SIZE);
+	atomic_set(&ehca_flightrecorder_index, 0);
+	ehca_flightrecorder[0] = 0x12345678abcdef0;
+
+	EDEB_EX(7, "");
+}
+
+/**
+ * ehca_flight_to_printk - TODO
+ */
+void ehca_flight_to_printk(void)
+{
+	int cur_offset = atomic_read(&ehca_flightrecorder_index);
+	int new_offset = cur_offset - (EHCA_FLIGHTRECORDER_BACKLOG * 4);
+	u32 flight_offset;
+	int i;
+
+	if (new_offset < 0)
+		new_offset = EHCA_FLIGHTRECORDER_SIZE + new_offset - 4;
+
+	printk(KERN_ERR
+	       "EHCA ----- flight recorder begin "
+	       "-------------------------------------------\n");
+
+	for (i = 0; i < EHCA_FLIGHTRECORDER_BACKLOG; i++) {
+		new_offset += 4;
+		flight_offset = (u32) new_offset % EHCA_FLIGHTRECORDER_SIZE;
+
+		printk(KERN_ERR "EHCA %02d: %.16lX %.16lX %.16lX %.16lX\n",
+		       i + 1,
+		       ehca_flightrecorder[flight_offset],
+		       ehca_flightrecorder[flight_offset + 1],
+		       ehca_flightrecorder[flight_offset + 2],
+		       ehca_flightrecorder[flight_offset + 3]);
+	}
+
+	printk(KERN_ERR
+	       "EHCA ----- flight recorder end "
+	       "---------------------------------------------\n");
+}
+
+#define EHCA_CACHE_CREATE(name)                                   \
+	ehca_module->cache_##name =                               \
+		kmem_cache_create("ehca_cache_"#name,             \
+				  sizeof(struct ehca_##name),     \
+				  0, SLAB_HWCACHE_ALIGN,          \
+				  NULL, NULL);                    \
+	if (ehca_module->cache_##name == NULL) {                  \
+		EDEB_ERR(4, "Cannot create "#name" SLAB cache."); \
+		return -ENOMEM;                                   \
+	}                                                         \
+
+/**
+ * ehca_caches_create: TODO
+ */
+int ehca_caches_create(struct ehca_module *ehca_module)
+{
+	EDEB_EN(7, "");
+
+	EHCA_CACHE_CREATE(pd);
+	EHCA_CACHE_CREATE(cq);
+	EHCA_CACHE_CREATE(qp);
+	EHCA_CACHE_CREATE(av);
+	EHCA_CACHE_CREATE(mw);
+	EHCA_CACHE_CREATE(mr);
+
+	EDEB_EX(7, "");
+
+	return 0;
+}
+
+#define EHCA_CACHE_DESTROY(name)                                               \
+	ret = kmem_cache_destroy(ehca_module->cache_##name);                   \
+	if (ret != 0) {                                                        \
+		EDEB_ERR(4, "Cannot destroy "#name" SLAB cache. ret=%x", ret); \
+		return ret;                                                    \
+	}                                                                      \
+
+/** 
+ * ehca_caches_destroy - TODO
+ */
+int ehca_caches_destroy(struct ehca_module *ehca_module)
+{
+	int ret;
+
+	EDEB_EN(7, "");
+
+	EHCA_CACHE_DESTROY(pd);
+	EHCA_CACHE_DESTROY(cq);
+	EHCA_CACHE_DESTROY(qp);
+	EHCA_CACHE_DESTROY(av);
+	EHCA_CACHE_DESTROY(mw);
+	EHCA_CACHE_DESTROY(mr);
+
+	EDEB_EX(7, "");
+
+	return 0;
+}
+
+#define EHCA_HCAAVER  EHCA_BMASK_IBM(32,39)
+#define EHCA_REVID    EHCA_BMASK_IBM(40,63)
+
+/**
+ * ehca_num_ports - TODO
+ */
+int ehca_sense_attributes(struct ehca_shca *shca)
+{
+	int ret = -EINVAL;
+	u64 rc = H_Success;
+	struct query_hca_rblock *rblock;
+
+	EDEB_EN(7, "shca=%p", shca);
+
+	rblock = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (rblock == NULL) {
+		EDEB_ERR(4, "Cannot allocate rblock memory.");
+		ret = -ENOMEM;
+		goto num_ports0;
+	}
+
+	memset(rblock, 0, PAGE_SIZE);
+
+	rc = hipz_h_query_hca(shca->ipz_hca_handle, rblock);
+	if (rc != H_Success) {
+		EDEB_ERR(4, "Cannot query device properties.rc=%lx", rc);
+		ret = -EPERM;
+		goto num_ports1;
+	}
+
+	if (ehca_nr_ports == 1)
+		shca->num_ports = 1;
+	else
+		shca->num_ports = (u8) rblock->num_ports;
+
+	EDEB(6, " ... found %x ports", rblock->num_ports);
+
+	if (ehca_hw_level == 0) {
+		u32 hcaaver;
+		u32 revid;
+
+		hcaaver = EHCA_BMASK_GET(EHCA_HCAAVER, rblock->hw_ver);
+		revid   = EHCA_BMASK_GET(EHCA_REVID, rblock->hw_ver);
+
+		EDEB(6, " ... hardware version=%x:%x",
+		     hcaaver, revid);
+
+		if ((hcaaver == 1) && (revid == 0))
+			shca->hw_level = 0;
+		else if ((hcaaver == 1) && (revid == 1))
+			shca->hw_level = 1;
+		else if ((hcaaver == 1) && (revid == 2))
+			shca->hw_level = 2;
+	}
+	EDEB(6, " ... hardware level=%x", shca->hw_level);
+
+	ret = 0;
+
+      num_ports1:
+	kfree(rblock);
+
+      num_ports0:
+	EDEB_EX(7, "ret=%x", ret);
+
+	return ret;
+}
+
+/**
+ * ehca_register_device - TODO
+ */
+int ehca_register_device(struct ehca_shca *shca)
+{
+	int ret = 0;
+
+	EDEB_EN(7, "shca=%p", shca);
+
+	strlcpy(shca->ib_device.name, "ehca%d", IB_DEVICE_NAME_MAX);
+	shca->ib_device.owner               = THIS_MODULE;
+
+	/* TODO: ABI ver later with define */
+	shca->ib_device.uverbs_abi_ver	    = 1;
+	shca->ib_device.uverbs_cmd_mask	    =
+		(1ull << IB_USER_VERBS_CMD_GET_CONTEXT)		|
+		(1ull << IB_USER_VERBS_CMD_QUERY_DEVICE)	|
+		(1ull << IB_USER_VERBS_CMD_QUERY_PORT)		|
+		(1ull << IB_USER_VERBS_CMD_ALLOC_PD)		|
+		(1ull << IB_USER_VERBS_CMD_DEALLOC_PD)		|
+		(1ull << IB_USER_VERBS_CMD_REG_MR)		|
+		(1ull << IB_USER_VERBS_CMD_DEREG_MR)		|
+		(1ull << IB_USER_VERBS_CMD_CREATE_COMP_CHANNEL)	|
+		(1ull << IB_USER_VERBS_CMD_CREATE_CQ)		|
+		(1ull << IB_USER_VERBS_CMD_DESTROY_CQ)		|
+		(1ull << IB_USER_VERBS_CMD_CREATE_QP)		|
+		(1ull << IB_USER_VERBS_CMD_MODIFY_QP)		|
+		(1ull << IB_USER_VERBS_CMD_DESTROY_QP)		|
+		(1ull << IB_USER_VERBS_CMD_ATTACH_MCAST)	|
+		(1ull << IB_USER_VERBS_CMD_DETACH_MCAST);
+
+        shca->ib_device.node_type           = IB_NODE_CA;
+        shca->ib_device.phys_port_cnt       = shca->num_ports;
+	shca->ib_device.dma_device          = &shca->ibmebus_dev->ofdev.dev;
+        shca->ib_device.query_device        = ehca_query_device;
+        shca->ib_device.query_port          = ehca_query_port;
+	shca->ib_device.query_gid           = ehca_query_gid;
+	shca->ib_device.query_pkey          = ehca_query_pkey;        
+	/* shca->in_device.modify_device    = ehca_modify_device    */
+        shca->ib_device.modify_port         = ehca_modify_port;
+	shca->ib_device.alloc_ucontext      = ehca_alloc_ucontext;   
+	shca->ib_device.dealloc_ucontext    = ehca_dealloc_ucontext; 
+        shca->ib_device.alloc_pd            = ehca_alloc_pd;
+        shca->ib_device.dealloc_pd          = ehca_dealloc_pd;
+        shca->ib_device.create_ah           = ehca_create_ah;
+	/* shca->ib_device.modify_ah        = ehca_modify_ah;       */
+	shca->ib_device.query_ah            = ehca_query_ah;
+        shca->ib_device.destroy_ah          = ehca_destroy_ah;
+        shca->ib_device.create_qp           = ehca_create_qp;
+        shca->ib_device.modify_qp           = ehca_modify_qp;
+	shca->ib_device.query_qp            = ehca_query_qp;
+        shca->ib_device.destroy_qp          = ehca_destroy_qp;
+	shca->ib_device.post_send           = ehca_post_send;
+	shca->ib_device.post_recv           = ehca_post_recv;
+        shca->ib_device.create_cq           = ehca_create_cq;
+        shca->ib_device.destroy_cq          = ehca_destroy_cq;
+
+        /* TODO: disabled due to func signature conflict */
+	/* shca->ib_device.resize_cq        = ehca_resize_cq;       */
+
+        shca->ib_device.poll_cq             = ehca_poll_cq;
+	/* shca->ib_device.peek_cq          = ehca_peek_cq;         */
+	shca->ib_device.req_notify_cq       = ehca_req_notify_cq;
+	/* shca->ib_device.req_ncomp_notif  = ehca_req_ncomp_notif; */
+        shca->ib_device.get_dma_mr          = ehca_get_dma_mr;
+        shca->ib_device.reg_phys_mr         = ehca_reg_phys_mr;
+	shca->ib_device.reg_user_mr         = ehca_reg_user_mr;
+	shca->ib_device.query_mr            = ehca_query_mr;
+        shca->ib_device.dereg_mr            = ehca_dereg_mr;
+	shca->ib_device.rereg_phys_mr       = ehca_rereg_phys_mr;
+        shca->ib_device.alloc_mw            = ehca_alloc_mw;
+	shca->ib_device.bind_mw             = ehca_bind_mw;
+	shca->ib_device.dealloc_mw          = ehca_dealloc_mw;
+        shca->ib_device.alloc_fmr           = ehca_alloc_fmr;
+	shca->ib_device.map_phys_fmr        = ehca_map_phys_fmr;
+        shca->ib_device.unmap_fmr           = ehca_unmap_fmr;
+        shca->ib_device.dealloc_fmr         = ehca_dealloc_fmr;
+        shca->ib_device.attach_mcast        = ehca_attach_mcast;
+	shca->ib_device.detach_mcast        = ehca_detach_mcast;
+	/* shca->ib_device.process_mad      = ehca_process_mad;     */
+	shca->ib_device.mmap                = ehca_mmap;             
+
+	ret = ib_register_device(&shca->ib_device);
+
+	EDEB_EX(7, "ret=%x", ret);
+
+	return ret;
+}
+
+/**
+ * ehca_create_aqp1 - TODO
+ *
+ * @shca: TODO
+ */
+static int ehca_create_aqp1(struct ehca_shca *shca, u32 port)
+{
+	struct ehca_sport *sport;
+	struct ib_cq *ibcq;
+	struct ib_qp *ibqp;
+	struct ib_qp_init_attr qp_init_attr;
+	int ret = 0;
+
+	EDEB_EN(7, "shca=%p port=%x", shca, port);
+
+	sport = &shca->sport[port - 1];
+
+	if (sport->ibcq_aqp1 != NULL) {
+		EDEB_ERR(4, "AQP1 CQ is already created.");
+		return -EPERM;
+	}
+
+	ibcq = ib_create_cq(&shca->ib_device, NULL, NULL, (void*)(-1), 10);
+	if (IS_ERR(ibcq)) {
+		EDEB_ERR(4, "Cannot create AQP1 CQ.");
+		return PTR_ERR(ibcq);
+	}
+	sport->ibcq_aqp1 = ibcq;
+
+	if (sport->ibqp_aqp1 != NULL) {
+		EDEB_ERR(4, "AQP1 QP is already created.");
+		ret = -EPERM;
+		goto create_aqp1;
+	}
+	
+	memset(&qp_init_attr, 0, sizeof(struct ib_qp_init_attr));
+	qp_init_attr.send_cq          = ibcq;
+	qp_init_attr.recv_cq          = ibcq;
+	qp_init_attr.sq_sig_type      = IB_SIGNAL_ALL_WR;
+	qp_init_attr.cap.max_send_wr  = 100;
+	qp_init_attr.cap.max_recv_wr  = 100;
+	qp_init_attr.cap.max_send_sge = 2;
+	qp_init_attr.cap.max_recv_sge = 1;
+	qp_init_attr.qp_type          = IB_QPT_GSI;
+	qp_init_attr.port_num         = port;
+	qp_init_attr.qp_context       = NULL;
+	qp_init_attr.event_handler    = NULL;
+	qp_init_attr.srq              = NULL;
+
+	ibqp = ib_create_qp(&shca->pd->ib_pd, &qp_init_attr);
+	if (IS_ERR(ibqp)) {
+		EDEB_ERR(4, "Cannot create AQP1 QP.");
+		ret = PTR_ERR(ibqp);
+		goto create_aqp1;
+	}
+	sport->ibqp_aqp1 = ibqp;
+
+	EDEB_EX(7, "ret=%x", ret);
+
+	return ret;
+
+      create_aqp1:
+	ib_destroy_cq(sport->ibcq_aqp1);
+
+	EDEB_EX(7, "ret=%x", ret);
+
+	return ret;
+}
+
+/**
+ * ehca_destroy_aqp1 - TODO
+ */
+static int ehca_destroy_aqp1(struct ehca_sport *sport)
+{
+	int ret = 0;
+
+	EDEB_EN(7, "sport=%p", sport);
+
+	ret = ib_destroy_qp(sport->ibqp_aqp1);
+	if (ret != 0) {
+		EDEB_ERR(4, "Cannot destroy AQP1 QP. ret=%x", ret);
+		goto destroy_aqp1;
+	}
+
+	ret = ib_destroy_cq(sport->ibcq_aqp1);
+	if (ret != 0)
+		EDEB_ERR(4, "Cannot destroy AQP1 CQ. ret=%x", ret);
+
+      destroy_aqp1:
+	EDEB_EX(7, "ret=%x", ret);
+
+	return ret;
+}
+
+static ssize_t ehca_show_debug_level(struct device_driver *ddp, char *buf)
+{
+	int f;
+	int total = 0;
+	total += snprintf(buf + total, PAGE_SIZE - total, "%d",
+			  ehca_edeb_mask[0]);
+	for (f = 1; f < EHCA_EDEB_TRACE_MASK_SIZE; f++) {
+		total += snprintf(buf + total, PAGE_SIZE - total, ",%d",
+				  ehca_edeb_mask[f]);
+	}
+
+	total += snprintf(buf + total, PAGE_SIZE - total, "\n");
+
+	return total;
+}
+
+static ssize_t ehca_store_debug_level(struct device_driver *ddp,
+				      const char *buf, size_t count)
+{
+	int f;
+	for (f = 0; f < EHCA_EDEB_TRACE_MASK_SIZE; f++) {
+		char value = buf[f * 2] - '0';
+		if ((value <= 9) && (count >= f * 2)) {
+			ehca_edeb_mask[f] = value;
+		}
+	}
+	return count;
+}
+DRIVER_ATTR(debug_level, S_IRUSR | S_IWUSR,
+	    ehca_show_debug_level, ehca_store_debug_level);
+
+static ssize_t ehca_show_flightrecorder(struct device_driver *ddp,
+					char *buf)
+{
+	/* this is not style compliant, but snprintf is not fast enough */
+	u64 *lbuf = (u64 *) buf;
+	lbuf[0] = (u64) & ehca_flightrecorder;
+	lbuf[1] = EHCA_FLIGHTRECORDER_SIZE;
+	lbuf[2] = atomic_read(&ehca_flightrecorder_index);
+	return sizeof(u64) * 3;
+}
+DRIVER_ATTR(flightrecorder, S_IRUSR, ehca_show_flightrecorder, 0);
+
+void ehca_create_driver_sysfs(struct ibmebus_driver *drv)
+{
+	driver_create_file(&drv->driver, &driver_attr_debug_level);
+	driver_create_file(&drv->driver, &driver_attr_flightrecorder);
+}
+
+void ehca_remove_driver_sysfs(struct ibmebus_driver *drv)
+{
+	driver_remove_file(&drv->driver, &driver_attr_debug_level);
+	driver_remove_file(&drv->driver, &driver_attr_flightrecorder);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
+#define EHCA_RESOURCE_ATTR_H(name)                                         \
+static ssize_t  ehca_show_##name(struct device *dev,                       \
+				 struct device_attribute *attr,            \
+				 char *buf)
+#else
+#define EHCA_RESOURCE_ATTR_H(name)                                         \
+static ssize_t  ehca_show_##name(struct device *dev,                       \
+				 char *buf)
+#endif
+
+#define EHCA_RESOURCE_ATTR(name)                                           \
+EHCA_RESOURCE_ATTR_H(name)                                                 \
+{									   \
+        struct ehca_shca *shca;                                            \
+        struct query_hca_rblock *rblock;                                   \
+	int len;							   \
+                                                                           \
+        shca = dev->driver_data;                                           \
+									   \
+        rblock = kmalloc(PAGE_SIZE, GFP_KERNEL);                           \
+	if (rblock == NULL) {                                              \
+		EDEB_ERR(4, "Can't allocate rblock memory.");              \
+		return 0;                                                  \
+	}                                                                  \
+                                                                           \
+        memset(rblock, 0, PAGE_SIZE);                                      \
+                                                                           \
+	if (hipz_h_query_hca(shca->ipz_hca_handle, rblock) != H_Success) { \
+			EDEB_ERR(4, "Can't query device properties");      \
+			kfree(rblock);                                     \
+			return 0;                                          \
+	}                                                                  \
+	                                                                   \
+        if ((strcmp(#name, "num_ports") == 0) && (ehca_nr_ports == 1))     \
+                len = snprintf(buf, 256, "1");                             \
+        else                                                               \
+		len = snprintf(buf, 256, "%d", rblock->name);              \
+	                                                                   \
+        if (len < 0)							   \
+		return 0;						   \
+	buf[len] = '\n';						   \
+	buf[len+1] = 0;							   \
+	                                                                   \
+        kfree(rblock);                                                     \
+                                                                           \
+        return len+1;							   \
+}								           \
+static DEVICE_ATTR(name, S_IRUGO, ehca_show_##name, NULL);
+
+EHCA_RESOURCE_ATTR(num_ports);
+EHCA_RESOURCE_ATTR(hw_ver);
+EHCA_RESOURCE_ATTR(max_eq);
+EHCA_RESOURCE_ATTR(cur_eq);
+EHCA_RESOURCE_ATTR(max_cq);
+EHCA_RESOURCE_ATTR(cur_cq);
+EHCA_RESOURCE_ATTR(max_qp);
+EHCA_RESOURCE_ATTR(cur_qp);
+EHCA_RESOURCE_ATTR(max_mr);
+EHCA_RESOURCE_ATTR(cur_mr);
+EHCA_RESOURCE_ATTR(max_mw);
+EHCA_RESOURCE_ATTR(cur_mw);
+EHCA_RESOURCE_ATTR(max_pd);
+EHCA_RESOURCE_ATTR(max_ah);
+
+static ssize_t ehca_show_adapter_handle(struct device *dev, 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
+					struct device_attribute *attr, 
+#endif
+					char *buf)
+{
+	struct ehca_shca *shca = dev->driver_data;
+
+	return sprintf(buf, "%lx\n", shca->ipz_hca_handle.handle);
+
+}
+static DEVICE_ATTR(adapter_handle, S_IRUGO, ehca_show_adapter_handle, NULL);
+
+
+
+void ehca_create_device_sysfs(struct ibmebus_dev *dev)
+{
+	device_create_file(&dev->ofdev.dev, &dev_attr_adapter_handle);
+	device_create_file(&dev->ofdev.dev, &dev_attr_num_ports);
+	device_create_file(&dev->ofdev.dev, &dev_attr_hw_ver);
+	device_create_file(&dev->ofdev.dev, &dev_attr_max_eq);
+	device_create_file(&dev->ofdev.dev, &dev_attr_cur_eq);
+	device_create_file(&dev->ofdev.dev, &dev_attr_max_cq);
+	device_create_file(&dev->ofdev.dev, &dev_attr_cur_cq);
+	device_create_file(&dev->ofdev.dev, &dev_attr_max_qp);
+	device_create_file(&dev->ofdev.dev, &dev_attr_cur_qp);
+	device_create_file(&dev->ofdev.dev, &dev_attr_max_mr);
+	device_create_file(&dev->ofdev.dev, &dev_attr_cur_mr);
+	device_create_file(&dev->ofdev.dev, &dev_attr_max_mw);
+	device_create_file(&dev->ofdev.dev, &dev_attr_cur_mw);
+	device_create_file(&dev->ofdev.dev, &dev_attr_max_pd);
+	device_create_file(&dev->ofdev.dev, &dev_attr_max_ah);
+}
+
+void ehca_remove_device_sysfs(struct ibmebus_dev *dev)
+{
+	device_remove_file(&dev->ofdev.dev, &dev_attr_adapter_handle);
+	device_remove_file(&dev->ofdev.dev, &dev_attr_num_ports);
+	device_remove_file(&dev->ofdev.dev, &dev_attr_hw_ver);
+	device_remove_file(&dev->ofdev.dev, &dev_attr_max_eq);
+	device_remove_file(&dev->ofdev.dev, &dev_attr_cur_eq);
+	device_remove_file(&dev->ofdev.dev, &dev_attr_max_cq);
+	device_remove_file(&dev->ofdev.dev, &dev_attr_cur_cq);
+	device_remove_file(&dev->ofdev.dev, &dev_attr_max_qp);
+	device_remove_file(&dev->ofdev.dev, &dev_attr_cur_qp);
+	device_remove_file(&dev->ofdev.dev, &dev_attr_max_mr);
+	device_remove_file(&dev->ofdev.dev, &dev_attr_cur_mr);
+	device_remove_file(&dev->ofdev.dev, &dev_attr_max_mw);
+	device_remove_file(&dev->ofdev.dev, &dev_attr_cur_mw);
+	device_remove_file(&dev->ofdev.dev, &dev_attr_max_pd);
+	device_remove_file(&dev->ofdev.dev, &dev_attr_max_ah);
+}
+
+/**
+ * ehca_probe - TODO
+ */
+static int __devinit ehca_probe(struct ibmebus_dev *dev, 
+				const struct of_device_id *id)
+{
+	struct ehca_shca *shca;
+	u64 *handle;
+	struct ib_pd *ibpd;
+	int ret = 0;
+
+	EDEB_EN(7, "name=%s", dev->name);
+
+	handle = (u64 *)get_property(dev->ofdev.node, "ibm,hca-handle", NULL);
+	if (!handle) {
+		EDEB_ERR(4, "Cannot get eHCA handle for adapter: %s.", 
+			 dev->ofdev.node->full_name);
+		return -ENODEV;
+	}
+
+	if (!(*handle)) {
+		EDEB_ERR(4, "Wrong eHCA handle for adapter: %s.", 
+			 dev->ofdev.node->full_name);
+		return -ENODEV;
+	}
+
+	shca = (struct ehca_shca *)ib_alloc_device(sizeof(*shca));
+	if (shca == NULL) {
+		EDEB_ERR(4, "Cannot allocate shca memory.");
+		return -ENOMEM;
+	}
+
+	shca->ibmebus_dev = dev;
+	shca->ipz_hca_handle.handle = *handle;
+	dev->ofdev.dev.driver_data = shca;
+
+	ret = ehca_sense_attributes(shca);
+	if (ret < 0) {
+		EDEB_ERR(4, "Cannot sense eHCA attributes.");
+		goto probe1;
+	}
+
+	/* create event queues */
+	ret = ehca_create_eq(shca, &shca->eq, EHCA_EQ, 2048);
+	if (ret != 0) {
+		EDEB_ERR(4, "Cannot create EQ.");
+		goto probe1;
+	}
+
+	ret = ehca_create_eq(shca, &shca->neq, EHCA_NEQ, 513);
+	if (ret != 0) {
+		EDEB_ERR(4, "Cannot create NEQ.");
+		goto probe2;
+	}
+
+	/* create internal protection domain */
+	ibpd = ehca_alloc_pd(&shca->ib_device, (void*)(-1), 0);
+	if (IS_ERR(ibpd)) {
+		EDEB_ERR(4, "Cannot create internal PD.");
+		ret = PTR_ERR(ibpd);
+		goto probe3;
+	}
+
+	shca->pd = container_of(ibpd, struct ehca_pd, ib_pd);
+	shca->pd->ib_pd.device = &shca->ib_device;
+
+	/* create internal max MR */
+	if (shca->maxmr == 0) {
+		struct ehca_mr *e_maxmr = 0;
+		ret = ehca_reg_internal_maxmr(shca, shca->pd, &e_maxmr);
+		if (ret != 0) {
+			EDEB_ERR(4, "Cannot create internal MR. ret=%x", ret);
+			goto probe4;
+		}
+		shca->maxmr = e_maxmr;
+	}
+
+	ret = ehca_register_device(shca);
+	if (ret != 0) {
+		EDEB_ERR(4, "Cannot register Infiniband device.");
+		goto probe5;
+	}
+
+	/* create AQP1 for port 1 */
+	if (ehca_open_aqp1 == 1) {
+		shca->sport[0].port_state = IB_PORT_DOWN;
+		ret = ehca_create_aqp1(shca, 1);
+		if (ret != 0) {
+			EDEB_ERR(4, "Cannot create AQP1 for port 1.");
+			goto probe6;
+		}
+	}
+
+	/* create AQP1 for port 2 */
+	if ((ehca_open_aqp1 == 1) && (shca->num_ports == 2)) {
+		shca->sport[1].port_state = IB_PORT_DOWN;
+		ret = ehca_create_aqp1(shca, 2);
+		if (ret != 0) {
+			EDEB_ERR(4, "Cannot create AQP1 for port 2.");
+			goto probe7;
+		}
+	}
+
+	ehca_create_device_sysfs(dev);
+
+	list_add(&shca->shca_list, &ehca_module.shca_list);
+
+	EDEB_EX(7, "ret=%x", ret);
+
+	return 0;
+
+ probe7:
+	ret = ehca_destroy_aqp1(&shca->sport[0]);
+	if (ret != 0)
+		EDEB_ERR(4, "Cannot destroy AQP1 for port 1. ret=%x", ret);
+
+ probe6:
+	ib_unregister_device(&shca->ib_device);
+
+ probe5:
+	ret = ehca_dereg_internal_maxmr(shca);
+	if (ret != 0)
+		EDEB_ERR(4, "Cannot destroy internal MR. ret=%x", ret);
+
+ probe4:
+	ret = ehca_dealloc_pd(&shca->pd->ib_pd);
+	if (ret != 0)
+		EDEB_ERR(4, "Cannot destroy internal PD. ret=%x", ret);
+
+ probe3:
+	ret = ehca_destroy_eq(shca, &shca->neq);
+	if (ret != 0)
+		EDEB_ERR(4, "Cannot destroy NEQ. ret=%x", ret);
+
+ probe2:
+	ret = ehca_destroy_eq(shca, &shca->eq);
+	if (ret != 0)
+		EDEB_ERR(4, "Cannot destroy EQ. ret=%x", ret);
+
+ probe1:
+	ib_dealloc_device(&shca->ib_device);
+
+	EDEB_EX(4, "ret=%x", ret);
+
+	return -EINVAL;
+}
+
+static int __devexit ehca_remove(struct ibmebus_dev *dev)
+{
+	struct ehca_shca *shca = dev->ofdev.dev.driver_data;
+	int ret;
+
+	EDEB_EN(7, "shca=%p", shca);
+
+	ehca_remove_device_sysfs(dev);
+
+	if (ehca_open_aqp1 == 1) {
+		int i;
+
+		for (i = 0; i < shca->num_ports; i++) {
+			ret = ehca_destroy_aqp1(&shca->sport[i]);
+			if (ret != 0)
+				EDEB_ERR(4, "Cannot destroy AQP1 for port %x."
+					 " ret=%x", ret, i);
+		}
+	}
+
+	ib_unregister_device(&shca->ib_device);
+
+	ret = ehca_dereg_internal_maxmr(shca);
+	if (ret != 0)
+		EDEB_ERR(4, "Cannot destroy internal MR. ret=%x", ret);
+
+	ret = ehca_dealloc_pd(&shca->pd->ib_pd);
+	if (ret != 0)
+		EDEB_ERR(4, "Cannot destroy internal PD. ret=%x", ret);
+
+	ret = ehca_destroy_eq(shca, &shca->eq);
+	if (ret != 0)
+		EDEB_ERR(4, "Cannot destroy EQ. ret=%x", ret);
+
+	ret = ehca_destroy_eq(shca, &shca->neq);
+	if (ret != 0)
+		EDEB_ERR(4, "Canot destroy NEQ. ret=%x", ret);
+
+	ib_dealloc_device(&shca->ib_device);
+
+	list_del(&shca->shca_list);
+
+	EDEB_EX(7, "ret=%x", ret);
+
+	return ret;
+}
+
+static struct of_device_id ehca_device_table[] =
+{
+	{
+		.name       = "lhca",
+		.compatible = "IBM,lhca",
+	},
+	{},
+};
+
+static struct ibmebus_driver ehca_driver = {
+	.name     = "ehca",
+	.id_table = ehca_device_table,
+	.probe    = ehca_probe,
+	.remove   = ehca_remove,
+};
+
+/**
+ * ehca_module_init - eHCA initialization routine.
+ */
+int __init ehca_module_init(void)
+{
+	int ret = 0;
+
+	printk(KERN_INFO "eHCA Infiniband Device Driver "
+	                 "(Rel.: EHCA2_0042)\n");
+	EDEB_EN(7, "");
+       
+	idr_init(&ehca_qp_idr);
+	idr_init(&ehca_cq_idr);
+
+	INIT_LIST_HEAD(&ehca_module.shca_list);
+
+	ehca_init_trace();
+	ehca_init_flight();
+
+	ehca_wq = create_workqueue("ehca");
+	if (ehca_wq == NULL) {
+		EDEB_ERR(4, "Cannot create workqueue.");
+		ret = -ENOMEM;
+		goto module_init1;
+	}
+
+	if ((ret = ehca_caches_create(&ehca_module)) != 0) {
+		ehca_catastrophic("Cannot create SLAB caches");
+		ret = -ENOMEM;
+		goto module_init2;
+	}
+
+	if ((ret = ibmebus_register_driver(&ehca_driver)) != 0) {
+		ehca_catastrophic("Cannot register eHCA device driver");
+		ret = -EINVAL;
+		goto module_init3;
+	}
+
+	ehca_create_driver_sysfs(&ehca_driver);
+
+	if (ehca_poll_all_eqs != 1) {
+		EDEB_ERR(4, "WARNING!!!");
+		EDEB_ERR(4, "It is possible to lose interrupts.");
+		
+		return 0;
+	}
+
+	ehca_kthread_eq = kthread_create(ehca_poll_eqs, &ehca_module, 
+					 "ehca_poll_eqs");
+	if (IS_ERR(ehca_kthread_eq)) {
+		EDEB_ERR(4, "Cannot create kthread_eq");
+		ret = PTR_ERR(ehca_kthread_eq);
+		goto module_init4;
+	}
+
+	wake_up_process(ehca_kthread_eq);
+	
+	EDEB_EX(7, "ret=%x", ret);
+
+	return 0;
+
+ module_init4:
+	ehca_remove_driver_sysfs(&ehca_driver);
+	ibmebus_unregister_driver(&ehca_driver);
+
+ module_init3:
+        ehca_caches_destroy(&ehca_module);
+ 
+ module_init2:
+	destroy_workqueue(ehca_wq);
+
+ module_init1:
+	EDEB_EX(7, "ret=%x", ret);
+
+	return ret;
+};
+
+/**
+ * ehca_module_exit - eHCA exit routine.
+ */
+void __exit ehca_module_exit(void)
+{
+	EDEB_EN(7, "");
+
+	if (ehca_poll_all_eqs == 1)
+		kthread_stop(ehca_kthread_eq);
+
+	ehca_remove_driver_sysfs(&ehca_driver);
+	ibmebus_unregister_driver(&ehca_driver);
+
+	if (ehca_caches_destroy(&ehca_module) != 0)
+		ehca_catastrophic("Cannot destroy SLAB caches");
+
+	destroy_workqueue(ehca_wq);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
+	printk(KERN_INFO "idr_destroy() for cq\n");
+	idr_destroy_ext(&ehca_cq_idr);
+	printk(KERN_INFO "idr_destroy() for qp\n");
+	idr_destroy_ext(&ehca_qp_idr);
+	printk(KERN_INFO "idr_destroy() OK\n");
+#else
+	idr_destroy(&ehca_cq_idr);
+	idr_destroy(&ehca_qp_idr);
+#endif
+
+	EDEB_EX(7, "");
+};
+
+module_init(ehca_module_init);
+module_exit(ehca_module_exit);
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_mcast.c newtree/drivers/infiniband/hw/ehca/ehca_mcast.c
--- oldtree/drivers/infiniband/hw/ehca/ehca_mcast.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_mcast.c	2006-02-13 19:19:59.924523776 -0500
@@ -0,0 +1,194 @@
+
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  mcast  functions
+ *
+ *  Authors: Waleri Fomin <fomin@de.ibm.com>
+ *           Reinhard Ernst <rernst@de.ibm.com> 
+ *           Hoang-Nam Nguyen <hnguyen@de.ibm.com> 
+ *           Heiko J Schick <schickhj@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_mcast.c,v 1.17 2005/11/03 09:16:02 fomin Exp $
+ */
+
+#define DEB_PREFIX "mcas"
+
+#include "ehca_kernel.h"
+#include "ehca_classes.h"
+#include "ehca_tools.h"
+#include "hcp_if.h"
+#include "ehca_qes.h"
+#include <linux/module.h>
+#include <linux/err.h>
+#include "ehca_iverbs.h"
+
+#define MAX_MC_LID 0xFFFE
+#define MIN_MC_LID 0xC000	/* Multicast limits */
+#define EHCA_VALID_MULTICAST_GID(gid)  ((gid)[0] == 0xFF)
+#define EHCA_VALID_MULTICAST_LID(lid)  (((lid) >= MIN_MC_LID) && ((lid) <= MIN_MC_LID))
+
+int ehca_attach_mcast(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
+{
+	struct ehca_qp *my_qp = NULL;
+	struct ehca_shca *shca = NULL;
+	union ib_gid my_gid;
+	u64 hipz_rc = H_Success;
+	int retcode = 0;
+
+	EHCA_CHECK_ADR(ibqp);
+	EHCA_CHECK_ADR(gid);
+
+	my_qp = container_of(ibqp, struct ehca_qp, ib_qp);
+
+	EHCA_CHECK_QP(my_qp);
+	if (ibqp->qp_type != IB_QPT_UD) {
+		EDEB_ERR(4, "invalid qp_type %x gid, retcode=%x",
+			 ibqp->qp_type, EINVAL);
+		return (-EINVAL);
+	}
+
+	shca = container_of(ibqp->pd->device, struct ehca_shca, ib_device);
+	EHCA_CHECK_ADR(shca);
+
+	if (!(EHCA_VALID_MULTICAST_GID(gid->raw))) {
+		EDEB_ERR(4, "gid is not valid mulitcast gid retcode=%x",
+			 EINVAL);
+		return (-EINVAL);
+	} else if ((lid < MIN_MC_LID) || (lid > MAX_MC_LID)) {
+		EDEB_ERR(4, "lid=%x is not valid mulitcast lid retcode=%x",
+			 lid, EINVAL);
+		return (-EINVAL);
+	}
+
+	memcpy(&my_gid.raw, gid->raw, sizeof(union ib_gid));
+
+	hipz_rc = hipz_h_attach_mcqp(shca->ipz_hca_handle,
+				     my_qp->ipz_qp_handle,
+				     my_qp->ehca_qp_core.galpas.kernel,
+				     lid, my_gid);
+	if (H_Success != hipz_rc) {
+		EDEB_ERR(4, 
+			 "ehca_qp=%p qp_num=%x hipz_h_attach_mcqp() failed "
+			 "hipz_rc=%lx", my_qp, ibqp->qp_num, hipz_rc);
+	}
+	retcode = ehca2ib_return_code(hipz_rc);
+
+	EDEB_EX(7, "mcast attach retcode=%x\n"
+		   "ehca_qp=%p qp_num=%x  lid=%x\n" 
+		   "my_gid=  %x %x %x %x\n"
+		   "         %x %x %x %x\n"
+		   "         %x %x %x %x\n"
+		   "         %x %x %x %x\n",
+		   retcode, my_qp, ibqp->qp_num, lid,
+		   my_gid.raw[0], my_gid.raw[1],
+		   my_gid.raw[2], my_gid.raw[3],
+		   my_gid.raw[4], my_gid.raw[5],
+		   my_gid.raw[6], my_gid.raw[7],
+		   my_gid.raw[8], my_gid.raw[9],
+		   my_gid.raw[10], my_gid.raw[11],
+		   my_gid.raw[12], my_gid.raw[13],
+		   my_gid.raw[14], my_gid.raw[15]);
+
+	return retcode;
+}
+
+int ehca_detach_mcast(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
+{
+	struct ehca_qp *my_qp = NULL;
+	struct ehca_shca *shca = NULL;
+	union ib_gid my_gid;
+	u64 hipz_rc = H_Success;
+	int retcode = 0;
+
+	EHCA_CHECK_ADR(ibqp);
+	EHCA_CHECK_ADR(gid);
+
+	my_qp = container_of(ibqp, struct ehca_qp, ib_qp);
+
+	EHCA_CHECK_QP(my_qp);
+	if (ibqp->qp_type != IB_QPT_UD) {
+		EDEB_ERR(4, "invalid qp_type %x gid, retcode=%x",
+			 ibqp->qp_type, EINVAL);
+		return (-EINVAL);
+	}
+
+	shca = container_of(ibqp->pd->device, struct ehca_shca, ib_device);
+	EHCA_CHECK_ADR(shca);
+
+	if (!(EHCA_VALID_MULTICAST_GID(gid->raw))) {
+		EDEB_ERR(4, "gid is not valid mulitcast gid retcode=%x",
+			 EINVAL);
+		return (-EINVAL);
+	} else if ((lid < MIN_MC_LID) || (lid > MAX_MC_LID)) {
+		EDEB_ERR(4, "lid=%x is not valid mulitcast lid retcode=%x",
+			 lid, EINVAL);
+		return (-EINVAL);
+	}
+
+	EDEB_EN(7, "dgid=%p qp_numl=%x lid=%x",
+		gid, ibqp->qp_num, lid);
+
+	memcpy(&my_gid.raw, gid->raw, sizeof(union ib_gid));
+
+	hipz_rc = hipz_h_detach_mcqp(shca->ipz_hca_handle,
+				     my_qp->ipz_qp_handle,
+				     my_qp->ehca_qp_core.galpas.kernel,
+				     lid, my_gid);
+	if (H_Success != hipz_rc) {
+		EDEB_ERR(4, 
+			 "ehca_qp=%p qp_num=%x hipz_h_detach_mcqp() failed "
+			 "hipz_rc=%lx", my_qp, ibqp->qp_num, hipz_rc);
+	}
+	retcode = ehca2ib_return_code(hipz_rc);
+	
+	EDEB_EX(7, "mcast detach retcode=%x\n"
+		"ehca_qp=%p qp_num=%x  lid=%x\n" 
+		"my_gid=  %x %x %x %x\n"
+		"         %x %x %x %x\n"
+		"         %x %x %x %x\n"
+		"         %x %x %x %x\n",
+		retcode, my_qp, ibqp->qp_num, lid,
+		my_gid.raw[0], my_gid.raw[1],
+		my_gid.raw[2], my_gid.raw[3],
+		my_gid.raw[4], my_gid.raw[5],
+		my_gid.raw[6], my_gid.raw[7],
+		my_gid.raw[8], my_gid.raw[9],
+		my_gid.raw[10], my_gid.raw[11],
+		my_gid.raw[12], my_gid.raw[13],
+		my_gid.raw[14], my_gid.raw[15]);
+
+	return retcode;
+}
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_mrmw.c newtree/drivers/infiniband/hw/ehca/ehca_mrmw.c
--- oldtree/drivers/infiniband/hw/ehca/ehca_mrmw.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_mrmw.c	2006-02-13 19:19:59.928523168 -0500
@@ -0,0 +1,1711 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  MR/MW functions
+ *
+ *  Authors: Dietmar Decker <ddecker@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_mrmw.c,v 1.82 2005/11/14 13:58:55 schickhj Exp $
+ */
+
+#undef DEB_PREFIX
+#define DEB_PREFIX "mrmw"
+
+#include "ehca_kernel.h"
+#include "ehca_iverbs.h"
+#include "hcp_if.h"
+#include "ehca_mrmw.h"
+
+extern int ehca_use_hp_mr;
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+struct ib_mr *ehca_get_dma_mr(struct ib_pd *pd, int mr_access_flags)
+{
+	struct ib_mr *ib_mr;
+	int retcode = 0;
+	struct ehca_mr *e_maxmr = 0;
+	struct ehca_pd *e_pd;
+	struct ehca_shca *shca;
+
+	EDEB_EN(7, "pd=%p mr_access_flags=%x", pd, mr_access_flags);
+
+	EHCA_CHECK_PD_P(pd);
+	e_pd = container_of(pd, struct ehca_pd, ib_pd);
+	shca = container_of(pd->device, struct ehca_shca, ib_device);
+
+	if (shca->maxmr) {
+		e_maxmr = ehca_mr_new();
+		if (!e_maxmr) {
+			EDEB_ERR(4, "out of memory");
+			ib_mr = ERR_PTR(-ENOMEM);
+			goto get_dma_mr_exit0;
+		}
+
+		retcode = ehca_reg_maxmr(shca, e_maxmr,
+					 (u64 *)KERNELBASE,
+					 mr_access_flags, e_pd,
+					 &e_maxmr->ib.ib_mr.lkey,
+					 &e_maxmr->ib.ib_mr.rkey);
+		if (retcode != 0) {
+			ib_mr = ERR_PTR(retcode);
+			goto get_dma_mr_exit0;
+		}
+		ib_mr = &e_maxmr->ib.ib_mr;
+	} else {
+		EDEB_ERR(4, "no internal max-MR exist!");
+		ib_mr = ERR_PTR(-EINVAL);
+		goto get_dma_mr_exit0;
+	}
+
+      get_dma_mr_exit0:
+	if (IS_ERR(ib_mr) == 0)
+		EDEB_EX(7, "ib_mr=%p lkey=%x rkey=%x",
+			ib_mr, ib_mr->lkey, ib_mr->rkey);
+	else
+		EDEB_EX(4, "rc=%lx pd=%p mr_access_flags=%x ",
+			PTR_ERR(ib_mr), pd, mr_access_flags);
+	return (ib_mr);
+} /* end ehca_get_dma_mr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+struct ib_mr *ehca_reg_phys_mr(struct ib_pd *pd,
+			       struct ib_phys_buf *phys_buf_array,
+			       int num_phys_buf,
+			       int mr_access_flags,
+			       u64 *iova_start)
+{
+	struct ib_mr *ib_mr = 0;
+	int retcode = 0;
+	struct ehca_mr *e_mr = 0;
+	struct ehca_shca *shca = 0;
+	struct ehca_pd *e_pd = 0;
+	u64 size = 0;
+	struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,0,0,0,0,0};
+	u32 num_pages_mr = 0;
+
+	EDEB_EN(7, "pd=%p phys_buf_array=%p num_phys_buf=%x "
+		"mr_access_flags=%x iova_start=%p", pd, phys_buf_array,
+		num_phys_buf, mr_access_flags, iova_start);
+
+	EHCA_CHECK_PD_P(pd);
+	if ((num_phys_buf <= 0) || ehca_adr_bad(phys_buf_array)) {
+		EDEB_ERR(4, "bad input values: num_phys_buf=%x "
+			 "phys_buf_array=%p", num_phys_buf, phys_buf_array);
+		ib_mr = ERR_PTR(-EINVAL);
+		goto reg_phys_mr_exit0;
+	}
+	if (((mr_access_flags & IB_ACCESS_REMOTE_WRITE) &&
+	     !(mr_access_flags & IB_ACCESS_LOCAL_WRITE)) ||
+	    ((mr_access_flags & IB_ACCESS_REMOTE_ATOMIC) &&
+	     !(mr_access_flags & IB_ACCESS_LOCAL_WRITE))) {
+		/* Remote Write Access requires Local Write Access */
+		/* Remote Atomic Access requires Local Write Access */
+		EDEB_ERR(4, "bad input values: mr_access_flags=%x", 
+			 mr_access_flags);
+		ib_mr = ERR_PTR(-EINVAL);
+		goto reg_phys_mr_exit0;
+	}
+
+	/* check physical buffer list and calculate size */
+	retcode = ehca_mr_chk_buf_and_calc_size(phys_buf_array, num_phys_buf, 
+						iova_start, &size);
+	if (retcode != 0) {
+		ib_mr = ERR_PTR(retcode);
+		goto reg_phys_mr_exit0;
+	}
+	if ((size == 0) ||
+	    ((0xFFFFFFFFFFFFFFFF - size) < (u64)iova_start)) {
+		EDEB_ERR(4, "bad input values: size=%lx iova_start=%p", 
+			 size, iova_start);
+		ib_mr = ERR_PTR(-EINVAL);
+		goto reg_phys_mr_exit0;
+	}
+
+	e_pd = container_of(pd, struct ehca_pd, ib_pd);
+	shca = container_of(pd->device, struct ehca_shca, ib_device);
+
+	e_mr = ehca_mr_new();
+	if (!e_mr) {
+		EDEB_ERR(4, "out of memory");
+		ib_mr = ERR_PTR(-ENOMEM);
+		goto reg_phys_mr_exit0;
+	}
+
+	/* determine number of MR pages */
+	/* pagesize currently hardcoded to 4k ... TODO.. */
+	num_pages_mr =
+	    ((((u64)iova_start % PAGE_SIZE) + size +
+	      PAGE_SIZE - 1) / PAGE_SIZE);
+
+	/* register MR on HCA */
+	if (ehca_mr_is_maxmr(size, iova_start)) {
+		e_mr->flags |= EHCA_MR_FLAG_MAXMR;
+		retcode = ehca_reg_maxmr(shca, e_mr, iova_start,
+					 mr_access_flags, e_pd,
+					 &e_mr->ib.ib_mr.lkey,
+					 &e_mr->ib.ib_mr.rkey);
+		if (retcode != 0) {
+			ib_mr = ERR_PTR(retcode);
+			goto reg_phys_mr_exit1;
+		}
+	} else {
+		pginfo.type           = EHCA_MR_PGI_PHYS;
+		pginfo.num_pages      = num_pages_mr;
+		pginfo.num_phys_buf   = num_phys_buf;
+		pginfo.phys_buf_array = phys_buf_array;
+
+		retcode = ehca_reg_mr(shca, e_mr, iova_start, size, 
+				      mr_access_flags, e_pd, &pginfo, 
+				      &e_mr->ib.ib_mr.lkey, 
+				      &e_mr->ib.ib_mr.rkey);
+		if (retcode != 0) {
+			ib_mr = ERR_PTR(retcode);
+			goto reg_phys_mr_exit1;
+		}
+	}
+
+	/* successful registration of all pages */
+	ib_mr = &e_mr->ib.ib_mr;
+	goto reg_phys_mr_exit0;
+
+      reg_phys_mr_exit1:
+	ehca_mr_delete(e_mr);
+      reg_phys_mr_exit0:
+	if (IS_ERR(ib_mr) == 0)
+		EDEB_EX(7, "ib_mr=%p lkey=%x rkey=%x",
+			ib_mr, ib_mr->lkey, ib_mr->rkey);
+	else
+		EDEB_EX(4, "rc=%lx pd=%p phys_buf_array=%p "
+			"num_phys_buf=%x mr_access_flags=%x iova_start=%p",
+			PTR_ERR(ib_mr), pd, phys_buf_array,
+			num_phys_buf, mr_access_flags, iova_start);
+	return (ib_mr);
+} /* end ehca_reg_phys_mr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd,
+			       struct ib_umem *region,
+			       int mr_access_flags,
+			       struct ib_udata *udata)
+{
+	struct ib_mr *ib_mr = 0;
+	struct ehca_mr *e_mr = 0;
+	struct ehca_shca *shca = 0;
+	struct ehca_pd *e_pd = 0;
+	struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,0,0,0,0,0};
+	int retcode = 0;
+	u32 num_pages_mr = 0;
+
+	EDEB_EN(7, "pd=%p region=%p mr_access_flags=%x udata=%p",
+		pd, region, mr_access_flags, udata);
+
+	EHCA_CHECK_PD_P(pd);
+	if (ehca_adr_bad(region)) {
+		EDEB_ERR(4, "bad input values: region=%p", region);
+		ib_mr = ERR_PTR(-EINVAL);
+		goto reg_user_mr_exit0;
+	}
+	if (((mr_access_flags & IB_ACCESS_REMOTE_WRITE) &&
+	     !(mr_access_flags & IB_ACCESS_LOCAL_WRITE)) ||
+	    ((mr_access_flags & IB_ACCESS_REMOTE_ATOMIC) &&
+	     !(mr_access_flags & IB_ACCESS_LOCAL_WRITE))) {
+		/* Remote Write Access requires Local Write Access */
+		/* Remote Atomic Access requires Local Write Access */
+		EDEB_ERR(4, "bad input values: mr_access_flags=%x",
+			 mr_access_flags);
+		ib_mr = ERR_PTR(-EINVAL);
+		goto reg_user_mr_exit0;
+	}
+	EDEB(7, "user_base=%lx virt_base=%lx length=%lx offset=%x page_size=%x "
+	     "chunk_list.next=%p", 
+	     region->user_base, region->virt_base, region->length,
+	     region->offset, region->page_size, region->chunk_list.next);
+	if (region->page_size != PAGE_SIZE) {
+		/* @TODO large page support */
+		EDEB_ERR(4, "large pages not supported, region->page_size=%x", 
+			 region->page_size);
+		ib_mr = ERR_PTR(-EINVAL);
+		goto reg_user_mr_exit0;
+	}
+
+	if ((region->length == 0) ||
+	    ((0xFFFFFFFFFFFFFFFF - region->length) < region->virt_base)) {
+		EDEB_ERR(4, "bad input values: length=%lx virt_base=%lx", 
+			 region->length, region->virt_base);
+		ib_mr = ERR_PTR(-EINVAL);
+		goto reg_user_mr_exit0;
+	}
+
+	e_pd = container_of(pd, struct ehca_pd, ib_pd);
+	shca = container_of(pd->device, struct ehca_shca, ib_device);
+
+	e_mr = ehca_mr_new();
+	if (!e_mr) {
+		EDEB_ERR(4, "out of memory");
+		ib_mr = ERR_PTR(-ENOMEM);
+		goto reg_user_mr_exit0;
+	}
+
+	/* determine number of MR pages */
+	/* pagesize currently hardcoded to 4k ...TODO... */
+	num_pages_mr =
+	    (((region->virt_base % PAGE_SIZE) + region->length +
+	      PAGE_SIZE - 1) / PAGE_SIZE);
+
+	/* register MR on HCA */
+	pginfo.type       = EHCA_MR_PGI_USER;
+	pginfo.num_pages  = num_pages_mr;
+	pginfo.region     = region;
+	pginfo.next_chunk = list_prepare_entry(pginfo.next_chunk,
+					       (&region->chunk_list), 
+					       list);
+
+	retcode = ehca_reg_mr(shca, e_mr, (u64 *)region->virt_base,
+			      region->length, mr_access_flags, e_pd, &pginfo,
+			      &e_mr->ib.ib_mr.lkey, &e_mr->ib.ib_mr.rkey);
+	if (retcode != 0) {
+		ib_mr = ERR_PTR(retcode);
+		goto reg_user_mr_exit1;
+	}
+	
+	/* successful registration of all pages */
+	ib_mr = &e_mr->ib.ib_mr;
+	goto reg_user_mr_exit0;
+
+      reg_user_mr_exit1:
+	ehca_mr_delete(e_mr);
+      reg_user_mr_exit0:
+	if (IS_ERR(ib_mr) == 0)
+		EDEB_EX(7, "ib_mr=%p lkey=%x rkey=%x",
+			ib_mr, ib_mr->lkey, ib_mr->rkey);
+	else
+		EDEB_EX(4, "rc=%lx pd=%p region=%p mr_access_flags=%x "
+			"udata=%p",
+			PTR_ERR(ib_mr), pd, region, mr_access_flags, udata);
+	return (ib_mr);
+} /* end ehca_reg_user_mr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+int ehca_rereg_phys_mr(struct ib_mr *mr,
+		       int mr_rereg_mask,
+		       struct ib_pd *pd,
+		       struct ib_phys_buf *phys_buf_array,
+		       int num_phys_buf,
+		       int mr_access_flags,
+		       u64 *iova_start)
+{
+	int retcode = 0;
+	struct ehca_shca *shca = 0;
+	struct ehca_mr *e_mr = 0;
+	u64 new_size = 0;
+	u64 *new_start = 0;
+	u32 new_acl = 0;
+	struct ehca_pd *new_pd = 0;
+	u32 tmp_lkey = 0;
+	u32 tmp_rkey = 0;
+	unsigned long sl_flags;
+	u64 num_pages_mr = 0;
+	struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,0,0,0,0,0};
+
+	EDEB_EN(7, "mr=%p mr_rereg_mask=%x pd=%p phys_buf_array=%p "
+		"num_phys_buf=%x mr_access_flags=%x iova_start=%p",
+		mr, mr_rereg_mask, pd, phys_buf_array, num_phys_buf,
+		mr_access_flags, iova_start);
+
+	if (!(mr_rereg_mask & IB_MR_REREG_TRANS)) {
+		/*@TODO not supported, because PHYP rereg hCall needs pages*/
+		/*@TODO: We will follow this with Tom ....*/
+		EDEB_ERR(4, "rereg without IB_MR_REREG_TRANS not supported yet,"
+			 " mr_rereg_mask=%x", mr_rereg_mask);
+		retcode = -EINVAL;
+		goto rereg_phys_mr_exit0;
+	}
+
+	EHCA_CHECK_MR(mr);
+	e_mr = container_of(mr, struct ehca_mr, ib.ib_mr);
+	if (mr_rereg_mask & IB_MR_REREG_PD) {
+		EHCA_CHECK_PD(pd);
+	}
+
+	if ((mr_rereg_mask &
+	     ~(IB_MR_REREG_TRANS | IB_MR_REREG_PD | IB_MR_REREG_ACCESS)) ||
+	    (mr_rereg_mask == 0)) {
+		retcode = -EINVAL;
+		goto rereg_phys_mr_exit0;
+	}
+
+	shca = container_of(mr->device, struct ehca_shca, ib_device);
+
+	/* check other parameters */
+	if (e_mr == shca->maxmr) {
+		/* should be impossible, however reject to be sure */
+		EDEB_ERR(3, "rereg internal max-MR impossible, mr=%p "
+			 "shca->maxmr=%p mr->lkey=%x", 
+			 mr, shca->maxmr, mr->lkey);
+		retcode = -EINVAL;
+		goto rereg_phys_mr_exit0;
+	}
+	if (mr_rereg_mask & IB_MR_REREG_TRANS) { /* transl., i.e. addr/size */
+		if (e_mr->flags & EHCA_MR_FLAG_FMR) {
+			EDEB_ERR(4, "not supported for FMR, mr=%p flags=%x", 
+				 mr, e_mr->flags);
+			retcode = -EINVAL;
+			goto rereg_phys_mr_exit0;
+		}
+		if (ehca_adr_bad(phys_buf_array) || num_phys_buf <= 0) {
+			EDEB_ERR(4, "bad input values: mr_rereg_mask=%x "
+				 "phys_buf_array=%p num_phys_buf=%x", 
+				 mr_rereg_mask, phys_buf_array, num_phys_buf);
+			retcode = -EINVAL;
+			goto rereg_phys_mr_exit0;
+		}
+	}
+	if ((mr_rereg_mask & IB_MR_REREG_ACCESS) &&	/* change ACL */
+	    (((mr_access_flags & IB_ACCESS_REMOTE_WRITE) &&
+	      !(mr_access_flags & IB_ACCESS_LOCAL_WRITE)) ||
+	     ((mr_access_flags & IB_ACCESS_REMOTE_ATOMIC) &&
+	      !(mr_access_flags & IB_ACCESS_LOCAL_WRITE)))) {
+		/* Remote Write Access requires Local Write Access */
+		/* Remote Atomic Access requires Local Write Access */
+		EDEB_ERR(4, "bad input values: mr_rereg_mask=%x "
+			 "mr_access_flags=%x", mr_rereg_mask, mr_access_flags);
+		retcode = -EINVAL;
+		goto rereg_phys_mr_exit0;
+	}
+
+	/* set requested values dependent on rereg request */
+	spin_lock_irqsave(&e_mr->mrlock, sl_flags); /* get lock @TODO for MR*/
+	new_start = e_mr->start;  /* new == old address */
+	new_size  = e_mr->size;	  /* new == old length */
+	new_acl   = e_mr->acl;	  /* new == old access control */
+	new_pd    = container_of(mr->pd,struct ehca_pd,ib_pd); /*new == old PD*/
+
+	if (mr_rereg_mask & IB_MR_REREG_TRANS) {
+		new_start = iova_start;	/* change address */
+		/* check physical buffer list and calculate size */
+		retcode = ehca_mr_chk_buf_and_calc_size(phys_buf_array, 
+							num_phys_buf,
+							iova_start, &new_size);
+		if (retcode != 0)
+			goto rereg_phys_mr_exit1;
+		if ((new_size == 0) ||
+		    ((0xFFFFFFFFFFFFFFFF - new_size) < (u64)iova_start)) {
+			EDEB_ERR(4, "bad input values: new_size=%lx "
+				 "iova_start=%p", new_size, iova_start);
+			retcode = -EINVAL;
+			goto rereg_phys_mr_exit1;
+		}
+		num_pages_mr = ((((u64)new_start % PAGE_SIZE) +
+				 new_size + PAGE_SIZE - 1) / PAGE_SIZE);
+		pginfo.type           = EHCA_MR_PGI_PHYS;
+		pginfo.num_pages      = num_pages_mr;
+		pginfo.num_phys_buf   = num_phys_buf;
+		pginfo.phys_buf_array = phys_buf_array;
+	}
+	if (mr_rereg_mask & IB_MR_REREG_ACCESS)
+		new_acl = mr_access_flags;
+	if (mr_rereg_mask & IB_MR_REREG_PD)
+		new_pd = container_of(pd, struct ehca_pd, ib_pd);
+	
+	EDEB(7, "mr=%p new_start=%p new_size=%lx new_acl=%x new_pd=%p "
+	     "num_pages_mr=%lx", 
+	     e_mr, new_start, new_size, new_acl, new_pd, num_pages_mr);
+	
+	retcode = ehca_rereg_mr(shca, e_mr, new_start, new_size, new_acl, 
+				new_pd, &pginfo, &tmp_lkey, &tmp_rkey);
+	if (retcode != 0)
+		goto rereg_phys_mr_exit1;
+
+	/* successful reregistration */
+	if (mr_rereg_mask & IB_MR_REREG_PD)
+		mr->pd = pd;
+	mr->lkey = tmp_lkey;
+	mr->rkey = tmp_rkey;
+
+      rereg_phys_mr_exit1:
+	spin_unlock_irqrestore(&e_mr->mrlock, sl_flags); /* free spin lock */
+      rereg_phys_mr_exit0:
+	if (retcode == 0)
+		EDEB_EX(7, "mr=%p mr_rereg_mask=%x pd=%p phys_buf_array=%p "
+			"num_phys_buf=%x mr_access_flags=%x iova_start=%p",
+			mr, mr_rereg_mask, pd, phys_buf_array, num_phys_buf,
+			mr_access_flags, iova_start);
+	else
+		EDEB_EX(4, "retcode=%x mr=%p mr_rereg_mask=%x pd=%p "
+			"phys_buf_array=%p num_phys_buf=%x mr_access_flags=%x "
+			"iova_start=%p", 
+			retcode, mr, mr_rereg_mask, pd, phys_buf_array, 
+			num_phys_buf, mr_access_flags, iova_start);
+
+	return (retcode);
+} /* end ehca_rereg_phys_mr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+int ehca_query_mr(struct ib_mr *mr, struct ib_mr_attr *mr_attr)
+{
+	int retcode = 0;
+	u64 rc = H_Success;
+	struct ehca_shca *shca = 0;
+	struct ehca_mr *e_mr = 0;
+	struct ipz_pd fwpd;		/* Firmware PD */
+	u32 access_ctrl = 0;
+	u64 tmp_remote_size = 0;
+	u64 tmp_remote_len = 0;
+
+	unsigned long sl_flags;
+
+	EDEB_EN(7, "mr=%p mr_attr=%p", mr, mr_attr);
+
+	EHCA_CHECK_MR(mr);
+	e_mr = container_of(mr, struct ehca_mr, ib.ib_mr);
+	if (ehca_adr_bad(mr_attr)) {
+		EDEB_ERR(4, "bad input values: mr_attr=%p", mr_attr);
+		retcode = -EINVAL;
+		goto query_mr_exit0;
+	}
+	if ((e_mr->flags & EHCA_MR_FLAG_FMR)) {
+		EDEB_ERR(4, "not supported for FMR, mr=%p e_mr=%p "
+			 "e_mr->flags=%x", mr, e_mr, e_mr->flags);
+		retcode = -EINVAL;
+		goto query_mr_exit0;
+	}
+
+	shca = container_of(mr->device, struct ehca_shca, ib_device);
+	memset(mr_attr, 0, sizeof(struct ib_mr_attr));
+	spin_lock_irqsave(&e_mr->mrlock, sl_flags); /* get spin lock @TODO?? */
+
+	rc = hipz_h_query_mr(shca->ipz_hca_handle, &e_mr->pf, 
+			     &e_mr->ipz_mr_handle, &mr_attr->size, 
+			     &mr_attr->device_virt_addr, &tmp_remote_size, 
+			     &tmp_remote_len, &access_ctrl, &fwpd, 
+			     &mr_attr->lkey, &mr_attr->rkey);
+	if (rc != H_Success) {
+		EDEB_ERR(4, "hipz_mr_query failed, rc=%lx mr=%p "
+			 "hca_hndl=%lx mr_hndl=%lx lkey=%x", 
+			 rc, mr, shca->ipz_hca_handle.handle,
+			 e_mr->ipz_mr_handle.handle, mr->lkey);
+		retcode = ehca_mrmw_map_rc_query_mr(rc);
+		goto query_mr_exit1;
+	}
+	ehca_mrmw_reverse_map_acl(&access_ctrl, &mr_attr->mr_access_flags);
+	mr_attr->pd = mr->pd;
+
+      query_mr_exit1:
+	spin_unlock_irqrestore(&e_mr->mrlock, sl_flags); /* free spin lock */
+      query_mr_exit0:
+	if (retcode == 0)
+		EDEB_EX(7, "pd=%p device_virt_addr=%lx size=%lx " 
+			"mr_access_flags=%x lkey=%x rkey=%x",
+			mr_attr->pd, mr_attr->device_virt_addr,
+			mr_attr->size, mr_attr->mr_access_flags,
+			mr_attr->lkey, mr_attr->rkey);
+	else
+		EDEB_EX(4, "retcode=%x mr=%p mr_attr=%p", retcode, mr, mr_attr);
+	return (retcode);
+} /* end ehca_query_mr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+int ehca_dereg_mr(struct ib_mr *mr)
+{
+	int retcode = 0;
+	u64 rc = H_Success;
+	struct ehca_shca *shca = 0;
+	struct ehca_mr *e_mr = 0;
+
+	EDEB_EN(7, "mr=%p", mr);
+
+	EHCA_CHECK_MR(mr);
+	e_mr = container_of(mr, struct ehca_mr, ib.ib_mr);
+	shca = container_of(mr->device, struct ehca_shca, ib_device);
+
+	if ((e_mr->flags & EHCA_MR_FLAG_FMR)) {
+		EDEB_ERR(4, "not supported for FMR, mr=%p e_mr=%p "
+			 "e_mr->flags=%x", mr, e_mr, e_mr->flags);
+		retcode = -EINVAL;
+		goto dereg_mr_exit0;
+	} else if (e_mr == shca->maxmr) {
+		/* should be impossible, however reject to be sure */
+		EDEB_ERR(3, "dereg internal max-MR impossible, mr=%p "
+			 "shca->maxmr=%p mr->lkey=%x", 
+			 mr, shca->maxmr, mr->lkey);
+		retcode = -EINVAL;
+		goto dereg_mr_exit0;
+	}
+
+	/*@TODO: BUSY: MR still has bound window(s) */
+	rc = hipz_h_free_resource_mr(shca->ipz_hca_handle, &e_mr->pf,
+				     &e_mr->ipz_mr_handle);
+	if (rc != H_Success) {
+		EDEB_ERR(4, "hipz_free_mr failed, rc=%lx shca=%p e_mr=%p"
+			 " hca_hndl=%lx mr_hndl=%lx mr->lkey=%x",
+			 rc, shca, e_mr, shca->ipz_hca_handle.handle,
+			 e_mr->ipz_mr_handle.handle, mr->lkey);
+		retcode = ehca_mrmw_map_rc_free_mr(rc);
+		goto dereg_mr_exit0;
+	}
+
+	/* successful deregistration */
+	ehca_mr_delete(e_mr);
+
+      dereg_mr_exit0:
+	if (retcode == 0)
+		EDEB_EX(7, "");
+	else
+		EDEB_EX(4, "retcode=%x mr=%p", retcode, mr);
+	return (retcode);
+} /* end ehca_dereg_mr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+struct ib_mw *ehca_alloc_mw(struct ib_pd *pd)
+{
+	struct ib_mw *ib_mw = 0;
+	u64 rc = H_Success;
+	struct ehca_shca *shca = 0;
+	struct ehca_mw *e_mw = 0;
+	struct ehca_pd *e_pd = 0;
+
+	EDEB_EN(7, "pd=%p", pd);
+
+	EHCA_CHECK_PD_P(pd);
+	e_pd = container_of(pd, struct ehca_pd, ib_pd);
+	shca = container_of(pd->device, struct ehca_shca, ib_device);
+
+	e_mw = ehca_mw_new();
+	if (!e_mw) {
+		ib_mw = ERR_PTR(-ENOMEM);
+		goto alloc_mw_exit0;
+	}
+
+	rc = hipz_h_alloc_resource_mw(shca->ipz_hca_handle, &e_mw->pf, 
+				      &shca->pf, e_pd->fw_pd, 
+				      &e_mw->ipz_mw_handle, &e_mw->ib_mw.rkey);
+	if (rc != H_Success) {
+		EDEB_ERR(4, "hipz_mw_allocate failed, rc=%lx shca=%p "
+			 "hca_hndl=%lx mw=%p", rc, shca,
+			 shca->ipz_hca_handle.handle, e_mw);
+		ib_mw = ERR_PTR(ehca_mrmw_map_rc_alloc(rc));
+		goto alloc_mw_exit1;
+	}
+	/* save R_Key in local copy */
+	/*@TODO?????    mw->rkey = *rkey_p; */
+
+	/* successful MW allocation */
+	ib_mw = &e_mw->ib_mw;
+	goto alloc_mw_exit0;
+
+      alloc_mw_exit1:
+	ehca_mw_delete(e_mw);
+      alloc_mw_exit0:
+	if (IS_ERR(ib_mw) == 0)
+		EDEB_EX(7, "ib_mw=%p rkey=%x", ib_mw, ib_mw->rkey);
+	else
+		EDEB_EX(4, "rc=%lx pd=%p", PTR_ERR(ib_mw), pd);
+	return (ib_mw);
+} /* end ehca_alloc_mw() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+int ehca_bind_mw(struct ib_qp *qp,
+		 struct ib_mw *mw,
+		 struct ib_mw_bind *mw_bind)
+{
+	int retcode = 0;
+
+	/*@TODO: not supported up to now */
+	EDEB_ERR(4, "bind MW currently not supported by HCAD");
+	retcode = -EPERM;
+	goto bind_mw_exit0;
+
+      bind_mw_exit0:
+	if (retcode == 0)
+		EDEB_EX(7, "qp=%p mw=%p mw_bind=%p", qp, mw, mw_bind);
+	else
+		EDEB_EX(4, "rc=%x qp=%p mw=%p mw_bind=%p", 
+			retcode, qp, mw, mw_bind);
+	return (retcode);
+} /* end ehca_bind_mw() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+int ehca_dealloc_mw(struct ib_mw *mw)
+{
+	int retcode = 0;
+	u64 rc = H_Success;
+	struct ehca_shca *shca = 0;
+	struct ehca_mw *e_mw = 0;
+
+	EDEB_EN(7, "mw=%p", mw);
+
+	EHCA_CHECK_MW(mw);
+	e_mw = container_of(mw, struct ehca_mw, ib_mw);
+	shca = container_of(mw->device, struct ehca_shca, ib_device);
+
+	rc = hipz_h_free_resource_mw(shca->ipz_hca_handle, &e_mw->pf,
+				     &e_mw->ipz_mw_handle);
+	if (rc != H_Success) {
+		EDEB_ERR(4, "hipz_free_mw failed, rc=%lx shca=%p mw=%p "
+			 "rkey=%x hca_hndl=%lx mw_hndl=%lx", 
+			 rc, shca, mw, mw->rkey, shca->ipz_hca_handle.handle,
+			 e_mw->ipz_mw_handle.handle);
+		retcode = ehca_mrmw_map_rc_free_mw(rc);
+		goto dealloc_mw_exit0;
+	}
+	/* successful deallocation */
+	ehca_mw_delete(e_mw);
+
+      dealloc_mw_exit0:
+	if (retcode == 0)
+		EDEB_EX(7, "");
+	else
+		EDEB_EX(4, "retcode=%x mw=%p", retcode, mw);
+	return (retcode);
+} /* end ehca_dealloc_mw() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+struct ib_fmr *ehca_alloc_fmr(struct ib_pd *pd,
+			      int mr_access_flags,
+			      struct ib_fmr_attr *fmr_attr)
+{
+	struct ib_fmr *ib_fmr = 0;
+	struct ehca_shca *shca = 0;
+	struct ehca_mr *e_fmr = 0;
+	int retcode = 0;
+	struct ehca_pd *e_pd = 0;
+	u32 tmp_lkey = 0;
+	u32 tmp_rkey = 0;
+	struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,0,0,0,0,0};
+
+	EDEB_EN(7, "pd=%p mr_access_flags=%x fmr_attr=%p",
+		pd, mr_access_flags, fmr_attr);
+
+	EHCA_CHECK_PD_P(pd);
+	if (ehca_adr_bad(fmr_attr)) {
+		EDEB_ERR(4, "bad input values: fmr_attr=%p", fmr_attr);
+		ib_fmr = ERR_PTR(-EINVAL);
+		goto alloc_fmr_exit0;
+	}
+
+	EDEB(7, "max_pages=%x max_maps=%x page_size=%x",
+	     fmr_attr->max_pages, fmr_attr->max_maps, fmr_attr->page_size);
+
+	/* check other parameters */
+	if (((mr_access_flags & IB_ACCESS_REMOTE_WRITE) &&
+	     !(mr_access_flags & IB_ACCESS_LOCAL_WRITE)) ||
+	    ((mr_access_flags & IB_ACCESS_REMOTE_ATOMIC) &&
+	     !(mr_access_flags & IB_ACCESS_LOCAL_WRITE))) {
+		/* Remote Write Access requires Local Write Access */
+		/* Remote Atomic Access requires Local Write Access */
+		EDEB_ERR(4, "bad input values: mr_access_flags=%x", 
+			 mr_access_flags);
+		ib_fmr = ERR_PTR(-EINVAL);
+		goto alloc_fmr_exit0;
+	}
+	if (mr_access_flags & IB_ACCESS_MW_BIND) {
+		EDEB_ERR(4, "bad input values: mr_access_flags=%x",
+			 mr_access_flags);
+		ib_fmr = ERR_PTR(-EINVAL);
+		goto alloc_fmr_exit0;
+	}
+	if ((fmr_attr->max_pages == 0) || (fmr_attr->max_maps == 0)) {
+		EDEB_ERR(4, "bad input values: fmr_attr->max_pages=%x "
+			 "fmr_attr->max_maps=%x fmr_attr->page_size=%x",
+			 fmr_attr->max_pages, fmr_attr->max_maps,
+			 fmr_attr->page_size);
+		ib_fmr = ERR_PTR(-EINVAL);
+		goto alloc_fmr_exit0;
+	}
+	if ((1 << fmr_attr->page_size) != PAGE_SIZE) {
+		/* pagesize currently hardcoded to 4k ... */
+		EDEB_ERR(4, "unsupported fmr_attr->page_size=%x",
+			 fmr_attr->page_size);
+		ib_fmr = ERR_PTR(-EINVAL);
+		goto alloc_fmr_exit0;
+	}
+
+	e_pd = container_of(pd, struct ehca_pd, ib_pd);
+	shca = container_of(pd->device, struct ehca_shca, ib_device);
+
+	e_fmr = ehca_mr_new();
+	if (e_fmr == 0) {
+		ib_fmr = ERR_PTR(-ENOMEM);
+		goto alloc_fmr_exit0;
+	}
+	e_fmr->flags |= EHCA_MR_FLAG_FMR;
+
+	/* register MR on HCA */
+	retcode = ehca_reg_mr(shca, e_fmr, 0,
+			      fmr_attr->max_pages * PAGE_SIZE,
+			      mr_access_flags, e_pd, &pginfo, 
+			      &tmp_lkey, &tmp_rkey);
+	if (retcode != 0) {
+		ib_fmr = ERR_PTR(retcode);
+		goto alloc_fmr_exit1;
+	}
+
+	/* successful registration of all pages */
+	e_fmr->fmr_page_size = 1 << fmr_attr->page_size;
+	e_fmr->fmr_max_pages = fmr_attr->max_pages; /* pagesize hardcoded 4k */
+	e_fmr->fmr_max_maps = fmr_attr->max_maps;
+	e_fmr->fmr_map_cnt = 0;
+	ib_fmr = &e_fmr->ib.ib_fmr;
+	goto alloc_fmr_exit0;
+
+      alloc_fmr_exit1:
+	ehca_mr_delete(e_fmr);
+      alloc_fmr_exit0:
+	if (IS_ERR(ib_fmr) == 0)
+		EDEB_EX(7, "ib_fmr=%p tmp_lkey=%x tmp_rkey=%x",
+			ib_fmr, tmp_lkey, tmp_rkey);
+	else
+		EDEB_EX(4, "rc=%lx pd=%p mr_access_flags=%x "
+			"fmr_attr=%p", PTR_ERR(ib_fmr), pd,
+			mr_access_flags, fmr_attr);
+	return (ib_fmr);
+} /* end ehca_alloc_fmr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+int ehca_map_phys_fmr(struct ib_fmr *fmr,
+		      u64 *page_list, 
+		      int list_len,
+		      u64 iova)
+{
+	int retcode = 0;
+	struct ehca_shca *shca = 0;
+	struct ehca_mr *e_fmr = 0;
+	struct ehca_pd *e_pd = 0;
+	struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,0,0,0,0,0};
+	u32 tmp_lkey = 0;
+	u32 tmp_rkey = 0;
+	/*@TODO unsigned long sl_flags; */
+
+	EDEB_EN(7, "fmr=%p page_list=%p list_len=%x iova=%lx",
+		fmr, page_list, list_len, iova);
+
+	EHCA_CHECK_FMR(fmr);
+	e_fmr = container_of(fmr, struct ehca_mr, ib.ib_fmr);
+	shca = container_of(fmr->device, struct ehca_shca, ib_device);
+	e_pd = container_of(fmr->pd, struct ehca_pd, ib_pd);
+
+	if (!(e_fmr->flags & EHCA_MR_FLAG_FMR)) {
+		EDEB_ERR(4, "not a FMR, e_fmr=%p e_fmr->flags=%x",
+			 e_fmr, e_fmr->flags);
+		retcode = -EINVAL;
+		goto map_phys_fmr_exit0;
+	}
+	retcode = ehca_fmr_check_page_list(e_fmr, page_list, list_len);
+	if (retcode != 0)
+		goto map_phys_fmr_exit0;
+	if (iova % PAGE_SIZE) {
+		/* only whole-numbered pages */
+		EDEB_ERR(4, "bad iova, iova=%lx", iova);
+		retcode = -EINVAL;
+		goto map_phys_fmr_exit0;
+	}
+	if (e_fmr->fmr_map_cnt >= e_fmr->fmr_max_maps) {
+		/* HCAD does not limit the maps, however trace this anyway */
+		EDEB(6, "map limit exceeded, fmr=%p e_fmr->fmr_map_cnt=%x "
+		     "e_fmr->fmr_max_maps=%x",
+		     fmr, e_fmr->fmr_map_cnt, e_fmr->fmr_max_maps);
+	}
+
+	pginfo.type      = EHCA_MR_PGI_FMR;
+	pginfo.num_pages = list_len;
+	pginfo.page_list = page_list;
+
+	/* @TODO spin_lock_irqsave(&e_fmr->mrlock, sl_flags); */
+
+	retcode = ehca_rereg_mr(shca, e_fmr, (u64 *)iova,
+				list_len * PAGE_SIZE,
+				e_fmr->acl, e_pd, &pginfo, 
+				&tmp_lkey, &tmp_rkey);
+	if (retcode != 0) {
+		/* @TODO spin_unlock_irqrestore(&fmr->mrlock, sl_flags); */
+		goto map_phys_fmr_exit0;
+	}
+	/* successful reregistration */
+	e_fmr->fmr_map_cnt++;
+	/* @TODO spin_unlock_irqrestore(&fmr->mrlock, sl_flags); */
+
+	e_fmr->ib.ib_fmr.lkey = tmp_lkey;
+	e_fmr->ib.ib_fmr.rkey = tmp_rkey;
+
+      map_phys_fmr_exit0:
+	if (retcode == 0)
+		EDEB_EX(7, "lkey=%x rkey=%x",
+			e_fmr->ib.ib_fmr.lkey, e_fmr->ib.ib_fmr.rkey);
+	else
+		EDEB_EX(4, "retcode=%x fmr=%p page_list=%p list_len=%x  "
+			"iova=%lx", 
+			retcode, fmr, page_list, list_len, iova);
+	return (retcode);
+} /* end ehca_map_phys_fmr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+int ehca_unmap_fmr(struct list_head *fmr_list)
+{
+	int retcode = 0;
+	struct ib_fmr *ib_fmr;
+	struct ehca_shca *shca = 0;
+	struct ehca_shca *prev_shca = 0;
+	struct ehca_mr *e_fmr = 0;
+	u32 num_fmr = 0;
+	u32 unmap_fmr_cnt = 0;
+	/* @TODO unsigned long sl_flags; */
+
+	EDEB_EN(7, "fmr_list=%p", fmr_list);
+
+	/* check all FMR belong to same SHCA, and check internal flag */
+	list_for_each_entry(ib_fmr, fmr_list, list) {
+		prev_shca = shca;
+		shca = container_of(ib_fmr->device, struct ehca_shca,
+				    ib_device);
+		EHCA_CHECK_FMR(ib_fmr);
+		e_fmr = container_of(ib_fmr, struct ehca_mr, ib.ib_fmr);
+		if ((shca != prev_shca) && (prev_shca != 0)) {
+			EDEB_ERR(4, "SHCA mismatch, shca=%p prev_shca=%p "
+				 "e_fmr=%p", shca, prev_shca, e_fmr);
+			retcode = -EINVAL;
+			goto unmap_fmr_exit0;
+		}
+		if (!(e_fmr->flags & EHCA_MR_FLAG_FMR)) {
+			EDEB_ERR(4, "not a FMR, e_fmr=%p e_fmr->flags=%x", 
+				 e_fmr, e_fmr->flags);
+			retcode = -EINVAL;
+			goto unmap_fmr_exit0;
+		}
+		num_fmr++;
+	}
+
+	/* loop over all FMRs to unmap */
+	list_for_each_entry(ib_fmr, fmr_list, list) {
+		unmap_fmr_cnt++;
+		e_fmr = container_of(ib_fmr, struct ehca_mr, ib.ib_fmr);
+		shca = container_of(ib_fmr->device, struct ehca_shca,
+				    ib_device);
+		/*@TODO??? spin_lock_irqsave(&fmr->mrlock, sl_flags); */
+		retcode = ehca_unmap_one_fmr(shca, e_fmr);
+		/*@TODO???? spin_unlock_irqrestore(&fmr->mrlock, sl_flags); */
+		if (retcode != 0) {
+			/* unmap failed, stop unmapping of rest of FMRs */
+			EDEB_ERR(4, "unmap of one FMR failed, stop rest, "
+				 "e_fmr=%p num_fmr=%x unmap_fmr_cnt=%x lkey=%x",
+				 e_fmr, num_fmr, unmap_fmr_cnt, 
+				 e_fmr->ib.ib_fmr.lkey);
+			goto unmap_fmr_exit0;
+		}
+	}
+
+      unmap_fmr_exit0:
+	if (retcode == 0)
+		EDEB_EX(7, "num_fmr=%x", num_fmr);
+	else
+		EDEB_EX(4, "retcode=%x fmr_list=%p num_fmr=%x unmap_fmr_cnt=%x",
+			retcode, fmr_list, num_fmr, unmap_fmr_cnt);
+	return (retcode);
+} /* end ehca_unmap_fmr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+int ehca_dealloc_fmr(struct ib_fmr *fmr)
+{
+	int retcode = 0;
+	u64 rc = H_Success;
+	struct ehca_shca *shca = 0;
+	struct ehca_mr *e_fmr = 0;
+
+	EDEB_EN(7, "fmr=%p", fmr);
+
+	EHCA_CHECK_FMR(fmr);
+	e_fmr = container_of(fmr, struct ehca_mr, ib.ib_fmr);
+	shca = container_of(fmr->device, struct ehca_shca, ib_device);
+
+	if (!(e_fmr->flags & EHCA_MR_FLAG_FMR)) {
+		EDEB_ERR(4, "not a FMR, e_fmr=%p e_fmr->flags=%x", 
+			 e_fmr, e_fmr->flags);
+		retcode = -EINVAL;
+		goto free_fmr_exit0;
+	}
+
+	rc = hipz_h_free_resource_mr(shca->ipz_hca_handle, &e_fmr->pf,
+				     &e_fmr->ipz_mr_handle);
+	if (rc != H_Success) {
+		EDEB_ERR(4, "hipz_free_mr failed, rc=%lx e_fmr=%p "
+			 "hca_hndl=%lx fmr_hndl=%lx fmr->lkey=%x",
+			 rc, e_fmr, shca->ipz_hca_handle.handle,
+			 e_fmr->ipz_mr_handle.handle, fmr->lkey);
+		ehca_mrmw_map_rc_free_mr(rc);
+		goto free_fmr_exit0;
+	}
+	/* successful deregistration */
+	ehca_mr_delete(e_fmr);
+
+      free_fmr_exit0:
+	if (retcode == 0)
+		EDEB_EX(7, "");
+	else
+		EDEB_EX(4, "retcode=%x fmr=%p", retcode, fmr);
+	return (retcode);
+} /* end ehca_dealloc_fmr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+int ehca_reg_mr(struct ehca_shca *shca,
+		struct ehca_mr *e_mr,
+		u64 *iova_start,
+		u64 size,
+		int acl,
+		struct ehca_pd *e_pd,
+		struct ehca_mr_pginfo *pginfo,
+		u32 *lkey,
+		u32 *rkey)
+{
+	int retcode = 0;
+	u64 rc = H_Success;
+	struct ehca_pfmr *pfmr = &e_mr->pf;
+	u32 hipz_acl = 0;
+
+	EDEB_EN(7, "shca=%p e_mr=%p iova_start=%p size=%lx acl=%x e_pd=%p "
+		"pginfo=%p num_pages=%lx", shca, e_mr, iova_start, size, acl, 
+		e_pd, pginfo, pginfo->num_pages);
+
+	ehca_mrmw_map_acl(acl, &hipz_acl);
+	ehca_mrmw_set_pgsize_hipz_acl(&hipz_acl);
+	if(ehca_use_hp_mr == 1)
+	        hipz_acl |= 0x00000001;
+
+	rc = hipz_h_alloc_resource_mr(shca->ipz_hca_handle, pfmr, &shca->pf,
+				      (u64)iova_start, size, hipz_acl,
+				      e_pd->fw_pd, &e_mr->ipz_mr_handle,
+				      lkey, rkey);
+	if (rc != H_Success) {
+		EDEB_ERR(4, "hipz_alloc_mr failed, rc=%lx hca_hndl=%lx " 
+			 "mr_hndl=%lx", rc, shca->ipz_hca_handle.handle, 
+			 e_mr->ipz_mr_handle.handle);
+		retcode = ehca_mrmw_map_rc_alloc(rc);
+		goto ehca_reg_mr_exit0;
+	}
+
+	retcode = ehca_reg_mr_rpages(shca, e_mr, pginfo);
+	if (retcode != 0)
+		goto ehca_reg_mr_exit1;
+
+	/* successful registration */
+	e_mr->num_pages = pginfo->num_pages;
+	e_mr->start = iova_start;
+	e_mr->size = size;
+	e_mr->acl = acl;
+ 	goto ehca_reg_mr_exit0;
+
+      ehca_reg_mr_exit1:
+	rc = hipz_h_free_resource_mr(shca->ipz_hca_handle, pfmr,
+				     &e_mr->ipz_mr_handle);
+	if (rc != H_Success) {
+		EDEB(1, "rc=%lx shca=%p e_mr=%p iova_start=%p "
+		     "size=%lx acl=%x e_pd=%p lkey=%x pginfo=%p num_pages=%lx",
+		     rc, shca, e_mr, iova_start, size, acl, 
+		     e_pd, *lkey, pginfo, pginfo->num_pages);
+		ehca_catastrophic("internal error in ehca_reg_mr, "
+				  "not recoverable");
+	}
+      ehca_reg_mr_exit0:
+	if (retcode == 0)
+		EDEB_EX(7, "retcode=%x lkey=%x rkey=%x", retcode, *lkey, *rkey);
+	else
+		EDEB_EX(4, "retcode=%x shca=%p e_mr=%p iova_start=%p "
+			"size=%lx acl=%x e_pd=%p pginfo=%p num_pages=%lx", 
+			retcode, shca, e_mr, iova_start,
+			size, acl, e_pd, pginfo, pginfo->num_pages);
+	return (retcode);
+} /* end ehca_reg_mr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+int ehca_reg_mr_rpages(struct ehca_shca *shca,
+		       struct ehca_mr *e_mr,
+		       struct ehca_mr_pginfo *pginfo)
+{
+	int retcode = 0;
+	u64 rc = H_Success;
+	struct ehca_pfmr *pfmr = &e_mr->pf;
+	u32 rnum = 0;
+	u64 rpage = 0;
+	u32 i;
+        u64 *kpage = 0;
+
+	EDEB_EN(7, "shca=%p e_mr=%p pginfo=%p num_pages=%lx",
+		shca, e_mr, pginfo, pginfo->num_pages);
+
+	kpage = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (kpage == 0) {
+		EDEB_ERR(4, "kpage alloc failed");
+		retcode = -ENOMEM;
+		goto ehca_reg_mr_rpages_exit0;
+	}
+	memset(kpage, 0, PAGE_SIZE);
+
+	/* max 512 pages per shot */
+	for (i = 0; i < ((pginfo->num_pages + 512 - 1) / 512); i++) {
+
+		if (i == ((pginfo->num_pages + 512 - 1) / 512) - 1) {
+			rnum = pginfo->num_pages % 512; /* last shot */
+			if (rnum == 0)
+				rnum = 512;      /* last shot is full */
+		} else
+			rnum = 512;
+
+		if (rnum > 1) {
+			retcode = ehca_set_pagebuf(e_mr, pginfo, rnum, kpage);
+			if (retcode) {
+				EDEB_ERR(4, "ehca_set_pagebuf bad rc, "
+					 "retcode=%x rnum=%x kpage=%p", 
+					 retcode, rnum, kpage);
+				retcode = -EFAULT;
+				goto ehca_reg_mr_rpages_exit1;
+			}
+			rpage = ehca_kv_to_g(kpage);
+			if (rpage == 0) {
+				EDEB_ERR(4, "kpage=%p i=%x", kpage, i);
+				retcode = -EFAULT;
+				goto ehca_reg_mr_rpages_exit1;
+			}
+		} else {  /* rnum==1 */
+			retcode = ehca_set_pagebuf_1(e_mr, pginfo, &rpage);
+			if (retcode) {
+				EDEB_ERR(4, "ehca_set_pagebuf_1 bad rc, "
+					 "retcode=%x i=%x", retcode, i); 
+				retcode = -EFAULT;
+				goto ehca_reg_mr_rpages_exit1;
+			}
+		}
+
+		EDEB(9, "i=%x rnum=%x rpage=%lx", i, rnum, rpage);
+
+		rc = hipz_h_register_rpage_mr(shca->ipz_hca_handle, 
+					      &e_mr->ipz_mr_handle, pfmr, 
+					      &shca->pf, 
+					      0, /* pagesize hardcoded to 4k */
+					      0, rpage, rnum);
+
+		if (i == ((pginfo->num_pages + 512 - 1) / 512) - 1) {
+			/* check for 'registration complete'==H_Success */
+			/* and for 'page registered'==H_PAGE_REGISTERED */
+			if (rc != H_Success) {
+				EDEB_ERR(4, "last hipz_reg_rpage_mr failed, "
+					 "rc=%lx e_mr=%p i=%x hca_hndl=%lx "
+					 "mr_hndl=%lx lkey=%x", rc, e_mr, i, 
+					 shca->ipz_hca_handle.handle,
+					 e_mr->ipz_mr_handle.handle,
+					 e_mr->ib.ib_mr.lkey);
+				retcode = ehca_mrmw_map_rc_rrpg_last(rc);
+				break;
+			} else
+				retcode = 0;
+		} else if (rc != H_PAGE_REGISTERED) {
+			EDEB_ERR(4, "hipz_reg_rpage_mr failed, rc=%lx e_mr=%p " 
+				 "i=%x lkey=%x hca_hndl=%lx mr_hndl=%lx", 
+				 rc, e_mr, i, e_mr->ib.ib_mr.lkey, 
+				 shca->ipz_hca_handle.handle,
+				 e_mr->ipz_mr_handle.handle);
+			retcode = ehca_mrmw_map_rc_rrpg_notlast(rc);
+			break;
+		} else
+			retcode = 0;
+	} /* end for(i) */
+
+
+       ehca_reg_mr_rpages_exit1:
+	kfree(kpage);
+       ehca_reg_mr_rpages_exit0:
+	if (retcode == 0)
+		EDEB_EX(7, "retcode=%x", retcode);
+	else
+		EDEB_EX(4, "retcode=%x shca=%p e_mr=%p pginfo=%p "
+			"num_pages=%lx", 
+			retcode, shca, e_mr, pginfo, pginfo->num_pages);
+	return (retcode);
+} /* end ehca_reg_mr_rpages() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+inline int ehca_rereg_mr_rereg1(struct ehca_shca *shca,
+				struct ehca_mr *e_mr,
+				u64 *iova_start,
+				u64 size,
+				u32 acl,
+				struct ehca_pd *e_pd,
+				struct ehca_mr_pginfo *pginfo,
+				u32 *lkey,
+				u32 *rkey)
+{
+	int retcode = 0;
+	u64 rc = H_Success;
+	struct ehca_pfmr *pfmr = &e_mr->pf;
+	u64 iova_start_out = 0;
+	u32 hipz_acl = 0;
+        u64 *kpage = 0;
+	u64 rpage = 0;
+	struct ehca_mr_pginfo pginfo_save;
+
+	EDEB_EN(7, "shca=%p e_mr=%p iova_start=%p size=%lx acl=%x "
+		"e_pd=%p pginfo=%p num_pages=%lx", shca, e_mr,
+		iova_start, size, acl, e_pd, pginfo, pginfo->num_pages);
+	
+	ehca_mrmw_map_acl(acl, &hipz_acl);
+	ehca_mrmw_set_pgsize_hipz_acl(&hipz_acl);
+
+	kpage = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (kpage == 0) {
+		EDEB_ERR(4, "kpage alloc failed");
+		retcode = -ENOMEM;
+		goto ehca_rereg_mr_rereg1_exit0;
+	}
+	memset(kpage, 0, PAGE_SIZE);
+
+	pginfo_save = *pginfo;
+	retcode = ehca_set_pagebuf(e_mr, pginfo, pginfo->num_pages, kpage);
+	if (retcode != 0) {
+		EDEB_ERR(4, "set pagebuf failed, e_mr=%p pginfo=%p type=%x "
+			 "num_pages=%lx kpage=%p", 
+			 e_mr, pginfo, pginfo->type, pginfo->num_pages, kpage);
+		goto ehca_rereg_mr_rereg1_exit1;
+	}
+	rpage = ehca_kv_to_g(kpage);
+	if (rpage == 0) {
+		EDEB_ERR(4, "kpage=%p", kpage);
+		retcode = -EFAULT;
+		goto ehca_rereg_mr_rereg1_exit1;
+	}
+	rc = hipz_h_reregister_pmr(shca->ipz_hca_handle, pfmr, &shca->pf,
+				   &e_mr->ipz_mr_handle, (u64)iova_start,
+				   size, hipz_acl, e_pd->fw_pd, rpage,
+				   &iova_start_out, lkey, rkey);
+	if (rc != H_Success) {
+		/* reregistration unsuccessful,                 */
+		/* try it again with the 3 hCalls,              */
+		/* e.g. this is required in case H_MR_CONDITION */
+		/* (MW bound or MR is shared)                   */
+		EDEB(6, "hipz_h_reregister_pmr failed (Rereg1), rc=%lx "
+		     "e_mr=%p", rc, e_mr);
+		*pginfo = pginfo_save;
+		retcode = -EAGAIN;
+	} else if ((u64 *)iova_start_out != iova_start) {
+		EDEB_ERR(4, "PHYP changed iova_start in rereg_pmr, "
+			 "iova_start=%p iova_start_out=%lx e_mr=%p "
+			 "mr_handle=%lx lkey=%x", iova_start, iova_start_out, 
+			 e_mr, e_mr->ipz_mr_handle.handle, e_mr->ib.ib_mr.lkey);
+		retcode = -EFAULT;
+	} else {
+		/* successful reregistration */
+		/* note: start and start_out are identical for eServer HCAs */
+		e_mr->num_pages = pginfo->num_pages;
+		e_mr->start     = iova_start;
+		e_mr->size      = size;
+		e_mr->acl       = acl;
+	}
+
+       ehca_rereg_mr_rereg1_exit1:
+	kfree(kpage);
+       ehca_rereg_mr_rereg1_exit0:
+	if ((retcode == 0) || (retcode == -EAGAIN))
+		EDEB_EX(7, "retcode=%x rc=%lx lkey=%x rkey=%x pginfo=%p "
+			"num_pages=%lx",
+			retcode, rc, *lkey, *rkey, pginfo, pginfo->num_pages);
+	else
+		EDEB_EX(4, "retcode=%x rc=%lx lkey=%x rkey=%x pginfo=%p "
+			"num_pages=%lx",
+			retcode, rc, *lkey, *rkey, pginfo, pginfo->num_pages);
+	return (retcode);
+} /* end ehca_rereg_mr_rereg1() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+int ehca_rereg_mr(struct ehca_shca *shca,
+		  struct ehca_mr *e_mr,
+		  u64 *iova_start,
+		  u64 size,
+		  int acl,
+		  struct ehca_pd *e_pd,
+		  struct ehca_mr_pginfo *pginfo,
+		  u32 *lkey,
+		  u32 *rkey)
+{
+	int retcode = 0;
+	u64 rc = H_Success;
+	struct ehca_pfmr *pfmr = &e_mr->pf;
+	int Rereg1Hcall = TRUE;	 /* TRUE: use hipz_h_reregister_pmr directly */
+	int Rereg3Hcall = FALSE; /* TRUE: use 3 hipz calls for reregistration */
+	struct ehca_bridge_handle save_bridge;
+
+	EDEB_EN(7, "shca=%p e_mr=%p iova_start=%p size=%lx acl=%x "
+		"e_pd=%p pginfo=%p num_pages=%lx", shca, e_mr, 
+		iova_start, size, acl, e_pd, pginfo, pginfo->num_pages);
+
+	/* first determine reregistration hCall(s) */
+	if ((pginfo->num_pages > 512) || (e_mr->num_pages > 512) ||
+	    (pginfo->num_pages > e_mr->num_pages)) {
+		EDEB(7, "Rereg3 case, pginfo->num_pages=%lx "
+		     "e_mr->num_pages=%x", pginfo->num_pages, e_mr->num_pages);
+		Rereg1Hcall = FALSE;
+		Rereg3Hcall = TRUE;
+	}
+
+	if (e_mr->flags & EHCA_MR_FLAG_MAXMR) {	/* check for max-MR */
+		Rereg1Hcall = FALSE;
+		Rereg3Hcall = TRUE;
+		e_mr->flags &= ~EHCA_MR_FLAG_MAXMR;
+		EDEB(4, "Rereg MR for max-MR! e_mr=%p", e_mr);
+	}
+
+	if (Rereg1Hcall) {
+		retcode = ehca_rereg_mr_rereg1(shca, e_mr, iova_start, size,
+					       acl, e_pd, pginfo, lkey, rkey);
+		if (retcode != 0) {
+			if (retcode == -EAGAIN)
+				Rereg3Hcall = TRUE;
+			else
+				goto ehca_rereg_mr_exit0;
+		}
+	}
+
+	if (Rereg3Hcall) {
+		struct ehca_mr save_mr;
+
+		/* first deregister old MR */
+		rc = hipz_h_free_resource_mr(shca->ipz_hca_handle, pfmr,
+					     &e_mr->ipz_mr_handle);
+		if (rc != H_Success) {
+			EDEB_ERR(4, "hipz_free_mr failed, rc=%lx e_mr=%p "
+				 "hca_hndl=%lx mr_hndl=%lx mr->lkey=%x", 
+				 rc, e_mr, shca->ipz_hca_handle.handle,
+				 e_mr->ipz_mr_handle.handle,
+				 e_mr->ib.ib_mr.lkey);
+			retcode = ehca_mrmw_map_rc_free_mr(rc);
+			goto ehca_rereg_mr_exit0;
+		}
+		/* clean ehca_mr_t, without changing struct ib_mr and lock */
+		save_bridge = pfmr->bridge;
+		save_mr = *e_mr;
+		ehca_mr_deletenew(e_mr);
+
+		/* set some MR values */
+		e_mr->flags = save_mr.flags;
+		pfmr->bridge = save_bridge;
+		e_mr->fmr_page_size = save_mr.fmr_page_size;
+		e_mr->fmr_max_pages = save_mr.fmr_max_pages;
+		e_mr->fmr_max_maps = save_mr.fmr_max_maps;
+		e_mr->fmr_map_cnt = save_mr.fmr_map_cnt;
+
+		retcode = ehca_reg_mr(shca, e_mr, iova_start, size, acl,
+				      e_pd, pginfo, lkey, rkey);
+		if (retcode != 0) {
+			u32 offset = (u64)(&e_mr->flags) - (u64)e_mr;
+			memcpy(&e_mr->flags, &(save_mr.flags),
+			       sizeof(struct ehca_mr) - offset);
+			goto ehca_rereg_mr_exit0;
+		}
+	}
+
+      ehca_rereg_mr_exit0:
+	if (retcode == 0)
+		EDEB_EX(7, "retcode=%x shca=%p e_mr=%p iova_start=%p size=%lx "
+			"acl=%x e_pd=%p pginfo=%p num_pages=%lx lkey=%x "
+			"rkey=%x Rereg1Hcall=%x Rereg3Hcall=%x", 
+			retcode, shca, e_mr, iova_start, size, acl, e_pd, 
+			pginfo, pginfo->num_pages, *lkey, *rkey, Rereg1Hcall,
+			Rereg3Hcall);
+	else
+		EDEB_EX(4, "retcode=%x shca=%p e_mr=%p iova_start=%p size=%lx "
+			"acl=%x e_pd=%p pginfo=%p num_pages=%lx lkey=%x "
+			"rkey=%x Rereg1Hcall=%x Rereg3Hcall=%x", 
+			retcode, shca, e_mr, iova_start, size, acl, e_pd, 
+			pginfo, pginfo->num_pages, *lkey, *rkey, Rereg1Hcall,
+			Rereg3Hcall);
+
+	return (retcode);
+} /* end ehca_rereg_mr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+int ehca_unmap_one_fmr(struct ehca_shca *shca,
+		       struct ehca_mr *e_fmr)
+{
+	int retcode = 0;
+	u64 rc = H_Success;
+	struct ehca_pfmr *pfmr = &e_fmr->pf;
+	int Rereg1Hcall = TRUE;	 /* TRUE: use hipz_mr_reregister directly */
+	int Rereg3Hcall = FALSE; /* TRUE: use 3 hipz calls for unmapping */
+	struct ehca_bridge_handle save_bridge;
+	struct ehca_pd *e_pd = 0;
+	struct ehca_mr save_fmr;
+	u32 tmp_lkey = 0;
+	u32 tmp_rkey = 0;
+	struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,0,0,0,0,0};
+
+	EDEB_EN(7, "shca=%p e_fmr=%p", shca, e_fmr);
+
+	/* first check if reregistration hCall can be used for unmap */
+	if (e_fmr->fmr_max_pages > 512) {
+		Rereg1Hcall = FALSE;
+		Rereg3Hcall = TRUE;
+	}
+
+	e_pd = container_of(e_fmr->ib.ib_fmr.pd, struct ehca_pd, ib_pd);
+
+	if (Rereg1Hcall) {
+		/* note: after using rereg hcall with len=0,            */
+		/* rereg hcall must be used again for registering pages */
+		u64 start_out = 0;
+		rc = hipz_h_reregister_pmr(shca->ipz_hca_handle, pfmr,
+					   &shca->pf, &e_fmr->ipz_mr_handle, 0,
+					   0, 0, e_pd->fw_pd, 0, &start_out,
+					   &tmp_lkey, &tmp_rkey);
+		if (rc != H_Success) {
+			/* should not happen, because length checked above, */
+			/* FMRs are not shared and no MW bound to FMRs      */
+			EDEB_ERR(4, "hipz_reregister_pmr failed (Rereg1), "
+				 "rc=%lx e_fmr=%p hca_hndl=%lx mr_hndl=%lx "
+				 "lkey=%x", rc, e_fmr,
+				 shca->ipz_hca_handle.handle,
+				 e_fmr->ipz_mr_handle.handle,
+				 e_fmr->ib.ib_fmr.lkey);
+			Rereg3Hcall = TRUE;
+		} else {
+			/* successful reregistration */
+			e_fmr->start = 0;
+			e_fmr->size = 0;
+		}
+	}
+
+	if (Rereg3Hcall) {
+		struct ehca_mr save_mr;
+
+		/* first free old FMR */
+		rc = hipz_h_free_resource_mr(shca->ipz_hca_handle, pfmr,
+					     &e_fmr->ipz_mr_handle);
+		if (rc != H_Success) {
+			EDEB_ERR(4, "hipz_free_mr failed, rc=%lx e_fmr=%p "
+				 "hca_hndl=%lx mr_hndl=%lx lkey=%x", rc, e_fmr,
+				 shca->ipz_hca_handle.handle,
+				 e_fmr->ipz_mr_handle.handle,
+				 e_fmr->ib.ib_fmr.lkey);
+			retcode = ehca_mrmw_map_rc_free_mr(rc);
+			goto ehca_unmap_one_fmr_exit0;
+		}
+		/* clean ehca_mr_t, without changing lock */
+		save_bridge = pfmr->bridge;
+		save_fmr = *e_fmr;
+		ehca_mr_deletenew(e_fmr);
+
+		/* set some MR values */
+		e_fmr->flags = save_fmr.flags;
+		pfmr->bridge = save_bridge;
+		e_fmr->fmr_page_size = save_fmr.fmr_page_size;
+		e_fmr->fmr_max_pages = save_fmr.fmr_max_pages;
+		e_fmr->fmr_max_maps = save_fmr.fmr_max_maps;
+		e_fmr->fmr_map_cnt = save_fmr.fmr_map_cnt;
+		e_fmr->acl = save_fmr.acl;
+
+		pginfo.type      = EHCA_MR_PGI_FMR;
+		pginfo.num_pages = 0;
+		retcode = ehca_reg_mr(shca, e_fmr, 0,
+				      (e_fmr->fmr_max_pages *
+				       e_fmr->fmr_page_size),
+				      e_fmr->acl, e_pd, &pginfo, &tmp_lkey,
+				      &tmp_rkey);
+		if (retcode != 0) {
+			u32 offset = (u64)(&e_fmr->flags) - (u64)e_fmr;
+			memcpy(&e_fmr->flags, &(save_mr.flags),
+			       sizeof(struct ehca_mr) - offset);
+			goto ehca_unmap_one_fmr_exit0;
+		}
+	}
+
+      ehca_unmap_one_fmr_exit0:
+	EDEB_EX(7, "retcode=%x tmp_lkey=%x tmp_rkey=%x fmr_max_pages=%x "
+		"Rereg1Hcall=%x Rereg3Hcall=%x", retcode, tmp_lkey, tmp_rkey, 
+		e_fmr->fmr_max_pages, Rereg1Hcall, Rereg3Hcall);
+	return (retcode);
+} /* end ehca_unmap_one_fmr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+int ehca_reg_smr(struct ehca_shca *shca,
+		 struct ehca_mr *e_origmr,
+		 struct ehca_mr *e_newmr,
+		 u64 *iova_start,
+		 int acl,
+		 struct ehca_pd *e_pd,
+		 u32 *lkey,
+		 u32 *rkey)
+{
+	int retcode = 0;
+	u64 rc = H_Success;
+	struct ehca_pfmr *pfmr = &e_newmr->pf;
+	u32 hipz_acl = 0;
+
+	EDEB_EN(7,"shca=%p e_origmr=%p e_newmr=%p iova_start=%p acl=%x e_pd=%p",
+		shca, e_origmr, e_newmr, iova_start, acl, e_pd);
+
+	ehca_mrmw_map_acl(acl, &hipz_acl);
+	ehca_mrmw_set_pgsize_hipz_acl(&hipz_acl);
+
+	rc = hipz_h_register_smr(shca->ipz_hca_handle, pfmr, &e_origmr->pf,
+				 &shca->pf, &e_origmr->ipz_mr_handle,
+				 (u64)iova_start, hipz_acl, e_pd->fw_pd,
+				 &e_newmr->ipz_mr_handle, lkey, rkey);
+	if (rc != H_Success) {
+		EDEB_ERR(4, "hipz_reg_smr failed, rc=%lx shca=%p e_origmr=%p "
+			 "e_newmr=%p iova_start=%p acl=%x e_pd=%p hca_hndl=%lx "
+			 "mr_hndl=%lx lkey=%x", rc, shca, e_origmr, e_newmr,
+			 iova_start, acl, e_pd, shca->ipz_hca_handle.handle, 
+			 e_origmr->ipz_mr_handle.handle,
+			 e_origmr->ib.ib_mr.lkey);
+		retcode = ehca_mrmw_map_rc_reg_smr(rc);
+		goto ehca_reg_smr_exit0;
+	}
+	/* successful registration */
+	e_newmr->num_pages = e_origmr->num_pages;
+	e_newmr->start = iova_start;
+	e_newmr->size = e_origmr->size;
+	e_newmr->acl = acl;
+	goto ehca_reg_smr_exit0;
+
+      ehca_reg_smr_exit0:
+	if (retcode == 0)
+		EDEB_EX(7, "retcode=%x lkey=%x rkey=%x",
+			retcode, *lkey, *rkey);
+	else
+		EDEB_EX(4, "retcode=%x shca=%p e_origmr=%p e_newmr=%p "
+			"iova_start=%p acl=%x e_pd=%p", retcode,
+			shca, e_origmr, e_newmr, iova_start, acl, e_pd);
+	return (retcode);
+} /* end ehca_reg_smr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+int ehca_reg_internal_maxmr(
+	struct ehca_shca *shca,
+	struct ehca_pd *e_pd,
+	struct ehca_mr **e_maxmr)
+{
+	int retcode = 0;
+	struct ehca_mr *e_mr = 0;
+	u64 *iova_start = 0;
+	u64 size_maxmr = 0;
+	struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,0,0,0,0,0};
+	struct ib_phys_buf ib_pbuf;
+	u32 num_pages_mr = 0;
+
+	EDEB_EN(7, "shca=%p e_pd=%p e_maxmr=%p", shca, e_pd, e_maxmr);
+
+	if (ehca_adr_bad(shca) || ehca_adr_bad(e_pd) || ehca_adr_bad(e_maxmr)) {
+		EDEB_ERR(4, "bad input values: shca=%p e_pd=%p e_maxmr=%p", 
+			 shca, e_pd, e_maxmr);
+		retcode = -EINVAL;
+		goto ehca_reg_internal_maxmr_exit0;
+	}
+
+	e_mr = ehca_mr_new();
+	if (!e_mr) {
+		EDEB_ERR(4, "out of memory");
+		retcode = -ENOMEM;
+		goto ehca_reg_internal_maxmr_exit0;
+	}
+	e_mr->flags |= EHCA_MR_FLAG_MAXMR;
+
+	/* register internal max-MR on HCA */
+	size_maxmr = (u64)high_memory - PAGE_OFFSET;
+	EDEB(9, "high_memory=%p PAGE_OFFSET=%lx", high_memory, PAGE_OFFSET);
+	iova_start = (u64 *)KERNELBASE;
+	ib_pbuf.addr = 0;
+	ib_pbuf.size = size_maxmr;
+	num_pages_mr = 
+		((((u64)iova_start % PAGE_SIZE) + size_maxmr + 
+		  PAGE_SIZE - 1) / PAGE_SIZE);
+
+	pginfo.type           = EHCA_MR_PGI_PHYS;
+	pginfo.num_pages      = num_pages_mr;
+	pginfo.num_phys_buf   = 1;
+	pginfo.phys_buf_array = &ib_pbuf;
+
+	retcode = ehca_reg_mr(shca, e_mr, iova_start, size_maxmr, 0, e_pd,
+			      &pginfo, &e_mr->ib.ib_mr.lkey, 
+			      &e_mr->ib.ib_mr.rkey);
+	if (retcode != 0) {
+		EDEB_ERR(4, "reg of internal max MR failed, e_mr=%p "
+			 "iova_start=%p size_maxmr=%lx num_pages_mr=%x",
+			 e_mr, iova_start, size_maxmr, num_pages_mr);
+		goto ehca_reg_internal_maxmr_exit1;
+	}
+
+	/* successful registration of all pages */
+	e_mr->ib.ib_mr.device = e_pd->ib_pd.device;
+	e_mr->ib.ib_mr.pd = &e_pd->ib_pd;
+	e_mr->ib.ib_mr.uobject = NULL;
+	atomic_inc(&(e_pd->ib_pd.usecnt));
+	atomic_set(&(e_mr->ib.ib_mr.usecnt), 0);
+	*e_maxmr = e_mr;
+	goto ehca_reg_internal_maxmr_exit0;
+
+      ehca_reg_internal_maxmr_exit1:
+	ehca_mr_delete(e_mr);
+      ehca_reg_internal_maxmr_exit0:
+	if (retcode == 0)
+		EDEB_EX(7, "*e_maxmr=%p lkey=%x rkey=%x",
+			*e_maxmr, (*e_maxmr)->ib.ib_mr.lkey,
+			(*e_maxmr)->ib.ib_mr.rkey);
+	else
+		EDEB_EX(4, "retcode=%x shca=%p e_pd=%p e_maxmr=%p",
+			retcode, shca, e_pd, e_maxmr);
+	return (retcode);
+} /* end ehca_reg_internal_maxmr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+int ehca_reg_maxmr(struct ehca_shca *shca,
+		   struct ehca_mr *e_newmr,
+		   u64 *iova_start,
+		   int acl,
+		   struct ehca_pd *e_pd,
+		   u32 *lkey,
+		   u32 *rkey)
+{
+	int retcode = 0;
+	u64 rc = H_Success;
+	struct ehca_pfmr *pfmr = &e_newmr->pf;
+	struct ehca_mr *e_origmr = shca->maxmr;
+	u32 hipz_acl = 0;
+
+	EDEB_EN(7,"shca=%p e_origmr=%p e_newmr=%p iova_start=%p acl=%x e_pd=%p",
+		shca, e_origmr, e_newmr, iova_start, acl, e_pd);
+
+	ehca_mrmw_map_acl(acl, &hipz_acl);
+	ehca_mrmw_set_pgsize_hipz_acl(&hipz_acl);
+
+	rc = hipz_h_register_smr(shca->ipz_hca_handle, pfmr, &e_origmr->pf,
+				 &shca->pf, &e_origmr->ipz_mr_handle,
+				 (u64)iova_start, hipz_acl, e_pd->fw_pd,
+				 &e_newmr->ipz_mr_handle, lkey, rkey);
+	if (rc != H_Success) {
+		EDEB_ERR(4, "hipz_reg_smr failed, rc=%lx e_origmr=%p "
+			 "hca_hndl=%lx mr_hndl=%lx lkey=%x",
+			 rc, e_origmr, shca->ipz_hca_handle.handle,
+			 e_origmr->ipz_mr_handle.handle,
+			 e_origmr->ib.ib_mr.lkey);
+		retcode = ehca_mrmw_map_rc_reg_smr(rc);
+		goto ehca_reg_maxmr_exit0;
+	}
+	/* successful registration */
+	e_newmr->num_pages = e_origmr->num_pages;
+	e_newmr->start = iova_start;
+	e_newmr->size = e_origmr->size;
+	e_newmr->acl = acl;
+
+      ehca_reg_maxmr_exit0:
+	EDEB_EX(7, "retcode=%x lkey=%x rkey=%x", retcode, *lkey, *rkey);
+	return (retcode);
+} /* end ehca_reg_maxmr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+int ehca_dereg_internal_maxmr(struct ehca_shca *shca)
+{
+	int retcode = 0;
+	struct ehca_mr *e_maxmr = 0;
+	struct ib_pd *ib_pd = 0;
+
+	EDEB_EN(7, "shca=%p shca->maxmr=%p", shca, shca->maxmr);
+
+	if (shca->maxmr == 0) {
+		EDEB_ERR(4, "bad call, shca=%p", shca);
+		retcode = -EINVAL;
+		goto ehca_dereg_internal_maxmr_exit0;
+	}
+
+	e_maxmr = shca->maxmr;
+	ib_pd = e_maxmr->ib.ib_mr.pd;
+	shca->maxmr = 0; /* remove internal max-MR indication from SHCA */
+
+	retcode = ehca_dereg_mr(&e_maxmr->ib.ib_mr);
+	if (retcode != 0) {
+		EDEB_ERR(3, "dereg internal max-MR failed, "
+			 "retcode=%x e_maxmr=%p shca=%p lkey=%x",
+			 retcode, e_maxmr, shca, e_maxmr->ib.ib_mr.lkey);
+		shca->maxmr = e_maxmr;
+		goto ehca_dereg_internal_maxmr_exit0;
+	}
+
+	atomic_dec(&ib_pd->usecnt);
+
+      ehca_dereg_internal_maxmr_exit0:
+	if (retcode == 0)
+		EDEB_EX(7, "");
+	else
+		EDEB_EX(4, "retcode=%x shca=%p shca->maxmr=%p",
+			retcode, shca, shca->maxmr);
+	return (retcode);
+} /* end ehca_dereg_internal_maxmr() */
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_mrmw.h newtree/drivers/infiniband/hw/ehca/ehca_mrmw.h
--- oldtree/drivers/infiniband/hw/ehca/ehca_mrmw.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_mrmw.h	2006-02-13 19:19:59.930522864 -0500
@@ -0,0 +1,739 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  MR/MW declarations and inline functions
+ *
+ *  Authors: Dietmar Decker <ddecker@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_mrmw.h,v 1.57 2005/11/14 13:58:55 schickhj Exp $
+ */
+
+#ifndef _EHCA_MRMW_H_
+#define _EHCA_MRMW_H_
+
+#undef DEB_PREFIX
+#define DEB_PREFIX "mrmw"
+
+#include "hipz_structs.h"
+
+
+int ehca_reg_mr(struct ehca_shca *shca,
+		struct ehca_mr *e_mr,
+		u64 *iova_start,
+		u64 size,
+		int acl,
+		struct ehca_pd *e_pd,
+		struct ehca_mr_pginfo *pginfo,
+		u32 *lkey,  /**<OUT*/
+		u32 *rkey); /**<OUT*/
+
+int ehca_reg_mr_rpages(struct ehca_shca *shca,
+		       struct ehca_mr *e_mr,
+		       struct ehca_mr_pginfo *pginfo);
+
+int ehca_rereg_mr(struct ehca_shca *shca,
+		  struct ehca_mr *e_mr,
+		  u64 *iova_start,
+		  u64 size,
+		  int mr_access_flags,
+		  struct ehca_pd *e_pd,
+		  struct ehca_mr_pginfo *pginfo,
+		  u32 *lkey,  /**<OUT*/
+		  u32 *rkey); /**<OUT*/
+
+int ehca_unmap_one_fmr(struct ehca_shca *shca,
+		       struct ehca_mr *e_fmr);
+
+int ehca_reg_smr(struct ehca_shca *shca,
+		 struct ehca_mr *e_origmr,
+		 struct ehca_mr *e_newmr,
+		 u64 *iova_start,
+		 int acl,
+		 struct ehca_pd *e_pd,
+		 u32 *lkey,  /**<OUT*/
+		 u32 *rkey); /**<OUT*/
+
+/** @brief register internal max-MR to internal SHCA
+ */
+int ehca_reg_internal_maxmr(struct ehca_shca *shca,  /**<IN*/
+			    struct ehca_pd *e_pd,    /**<IN*/
+			    struct ehca_mr **maxmr); /**<OUT*/
+
+int ehca_reg_maxmr(struct ehca_shca *shca,
+		   struct ehca_mr *e_newmr,
+		   u64 *iova_start,
+		   int acl,
+		   struct ehca_pd *e_pd,
+		   u32 *lkey,
+		   u32 *rkey);
+
+int ehca_dereg_internal_maxmr(struct ehca_shca *shca);
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+/** @brief check physical buffer array of MR verbs for validness and 
+ calculates MR size 
+*/
+static inline int ehca_mr_chk_buf_and_calc_size(
+	struct ib_phys_buf *phys_buf_array, /**<IN*/
+	int num_phys_buf,                   /**<IN*/
+	u64 *iova_start,                    /**<IN*/
+	u64 *size)                          /**<OUT*/
+{
+	struct ib_phys_buf *pbuf = phys_buf_array;
+	u64 size_count = 0;
+	u32 i;
+
+	if (num_phys_buf == 0) {
+		EDEB_ERR(4, "bad phys buf array len, num_phys_buf=0");
+		return (-EINVAL);
+	}
+	/* check first buffer */
+	if (((u64)iova_start & ~PAGE_MASK) != (pbuf->addr & ~PAGE_MASK)) {
+		EDEB_ERR(4, "iova_start/addr mismatch, iova_start=%p "
+			 "pbuf->addr=%lx pbuf->size=%lx",
+			 iova_start, pbuf->addr, pbuf->size);
+		return (-EINVAL);
+	}
+	if (((pbuf->addr + pbuf->size) % PAGE_SIZE) &&
+	    (num_phys_buf > 1)) {
+		EDEB_ERR(4, "addr/size mismatch in 1st buf, pbuf->addr=%lx "
+			 "pbuf->size=%lx", pbuf->addr, pbuf->size);
+		return (-EINVAL);
+	}
+
+	for (i = 0; i < num_phys_buf; i++) {
+		if ((i > 0) && (pbuf->addr % PAGE_SIZE)) {
+			EDEB_ERR(4, "bad address, i=%x pbuf->addr=%lx "
+				 "pbuf->size=%lx", i, pbuf->addr, pbuf->size);
+			return (-EINVAL);
+		}
+		if (((i > 0) &&	/* not 1st */
+		     (i < (num_phys_buf - 1)) &&	/* not last */
+		     (pbuf->size % PAGE_SIZE)) || (pbuf->size == 0)) {
+			EDEB_ERR(4, "bad size, i=%x pbuf->size=%lx", 
+				 i, pbuf->size);
+			return (-EINVAL);
+		}
+		size_count += pbuf->size;
+		pbuf++;
+	}
+
+	*size = size_count;
+	return (0);
+} /* end ehca_mr_chk_buf_and_calc_size() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+/** @brief check page list of map FMR verb for validness
+*/
+static inline int ehca_fmr_check_page_list(
+	struct ehca_mr *e_fmr, /**<IN*/
+	u64 *page_list,        /**<IN*/
+	int list_len)          /**<IN*/
+{
+	u32 i;
+	u64 *page = 0;
+
+	if (ehca_adr_bad(page_list)) {
+		EDEB_ERR(4, "bad page_list, page_list=%p fmr=%p", 
+			 page_list, e_fmr);
+		return (-EINVAL);
+	}
+
+	if ((list_len == 0) || (list_len > e_fmr->fmr_max_pages)) {
+		EDEB_ERR(4, "bad list_len, list_len=%x e_fmr->fmr_max_pages=%x "
+			 "fmr=%p", list_len, e_fmr->fmr_max_pages, e_fmr);
+		return (-EINVAL);
+	}
+	
+	/* each page must be aligned */
+	page = page_list;
+	for (i = 0; i < list_len; i++) {
+		if (*page % PAGE_SIZE) {
+			EDEB_ERR(4, "bad page, i=%x *page=%lx page=%p "
+				 "fmr=%p", i, *page, page, e_fmr);
+			return (-EINVAL);
+		}
+		page++;
+	}
+
+	return (0);
+} /* end ehca_fmr_check_page_list() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+/** @brief setup page buffer from page info
+ */
+static inline int ehca_set_pagebuf(struct ehca_mr *e_mr,
+				   struct ehca_mr_pginfo *pginfo,
+				   u32 number,
+				   u64 *kpage) /**<OUT*/
+{
+	int retcode = 0;
+	struct ib_umem_chunk *prev_chunk = NULL;
+	struct ib_umem_chunk *chunk      = NULL;
+	struct ib_phys_buf *pbuf         = NULL;
+	u64 *fmrlist = NULL;
+	u64 numpg  = 0;
+	u64 pgaddr = 0;
+	u32 i = 0;
+	u32 j = 0;
+
+
+	EDEB_EN(7, "pginfo=%p type=%x num_pages=%lx next_buf=%lx next_page=%lx "
+		"number=%x kpage=%p page_count=%lx next_listelem=%lx "
+		"region=%p next_chunk=%p next_nmap=%lx",
+		pginfo, pginfo->type, pginfo->num_pages, pginfo->next_buf,
+		pginfo->next_page, number, kpage, pginfo->page_count,
+		pginfo->next_listelem, pginfo->region, pginfo->next_chunk, 
+		pginfo->next_nmap);
+
+	if (pginfo->type == EHCA_MR_PGI_PHYS) {
+		/* loop over desired phys_buf_array entries */
+		while (i < number) {
+			pbuf  = pginfo->phys_buf_array + pginfo->next_buf;
+			numpg = ((pbuf->size + PAGE_SIZE - 1) / PAGE_SIZE);
+			while (pginfo->next_page < numpg) {
+				/* sanity check */
+				if (pginfo->page_count >= pginfo->num_pages) {
+					EDEB_ERR(4, "page_count >= num_pages, "
+						 "page_count=%lx num_pages=%lx "
+						 "i=%x", pginfo->page_count,
+						 pginfo->num_pages, i);
+					retcode = -EFAULT;
+					goto ehca_set_pagebuf_exit0;
+				}
+				*kpage = phys_to_abs((pbuf->addr & PAGE_MASK)
+						     + (pginfo->next_page * 
+							PAGE_SIZE));
+				if ((*kpage == 0) && (pbuf->addr != 0)) {
+					EDEB_ERR(4, "pbuf->addr=%lx" 
+						 " pbuf->size=%lx" 
+						 " next_page=%lx",
+						 pbuf->addr, pbuf->size,
+						 pginfo->next_page);
+					retcode = -EFAULT;
+					goto ehca_set_pagebuf_exit0;
+				}
+				(pginfo->next_page)++;
+				(pginfo->page_count)++;
+				kpage++;
+				i++;
+				if (i >= number) break;
+			}
+			if (pginfo->next_page >= numpg) {
+				(pginfo->next_buf)++;
+				pginfo->next_page = 0;
+			}
+		}
+	} else if (pginfo->type == EHCA_MR_PGI_USER) {
+		/* loop over desired chunk entries */
+		/* (@TODO: add support for large pages) */
+		chunk      = pginfo->next_chunk;
+		prev_chunk = pginfo->next_chunk;
+		list_for_each_entry_continue(chunk, 
+					     (&(pginfo->region->chunk_list)), 
+					     list) {
+			EDEB(9, "chunk->page_list[0]=%lx",
+			     (u64)sg_dma_address(&chunk->page_list[0]));
+			for (i = pginfo->next_nmap; i < chunk->nmap; i++) {
+				pgaddr = ( page_to_pfn(chunk->page_list[i].page)
+					   << PAGE_SHIFT );
+				*kpage = phys_to_abs(pgaddr);
+				EDEB(9,"pgaddr=%lx *kpage=%lx", pgaddr, *kpage);
+				if (*kpage == 0) {
+					EDEB_ERR(4, "chunk->page_list[i]=%lx"
+                                                 " i=%x mr=%p",
+						 (u64)sg_dma_address(
+							 &chunk->page_list[i]),
+						 i, e_mr);
+					retcode = -EFAULT;
+					goto ehca_set_pagebuf_exit0;
+				}
+				(pginfo->page_count)++;
+				(pginfo->next_nmap)++;
+				kpage++;
+				j++;
+				if (j >= number) break;
+			}
+			if ( (pginfo->next_nmap >= chunk->nmap) &&
+			     (j >= number) ) {
+				pginfo->next_nmap = 0;
+				prev_chunk = chunk;
+				break;
+			} else if (pginfo->next_nmap >= chunk->nmap) {
+				pginfo->next_nmap = 0;
+				prev_chunk = chunk;
+			} else if (j >= number)
+				break;
+			else
+				prev_chunk = chunk;
+		}
+		pginfo->next_chunk = 
+			list_prepare_entry(prev_chunk,
+					   (&(pginfo->region->chunk_list)),
+					   list);
+	} else if (pginfo->type == EHCA_MR_PGI_FMR) {
+		/* loop over desired page_list entries */
+		fmrlist = pginfo->page_list + pginfo->next_listelem;
+		for (i = 0; i < number; i++) {
+			*kpage = phys_to_abs(*fmrlist);
+			if (*kpage == 0) {
+				EDEB_ERR(4, "*fmrlist=%lx fmrlist=%p" 
+					 " next_listelem=%lx", *fmrlist,
+					 fmrlist, pginfo->next_listelem);
+				retcode = -EFAULT;
+				goto ehca_set_pagebuf_exit0;
+			}
+			(pginfo->next_listelem)++;
+			(pginfo->page_count)++;
+			fmrlist++;
+			kpage++;
+		}
+	} else {
+		EDEB_ERR(4, "bad pginfo->type=%x", pginfo->type);
+		retcode = -EFAULT;
+		goto ehca_set_pagebuf_exit0;
+	}
+
+      ehca_set_pagebuf_exit0:
+	if (retcode == 0)
+		EDEB_EX(7, "retcode=%x e_mr=%p pginfo=%p type=%x num_pages=%lx "
+			"next_buf=%lx next_page=%lx number=%x kpage=%p "
+			"page_count=%lx i=%x next_listelem=%lx region=%p "
+			"next_chunk=%p next_nmap=%lx",
+			retcode, e_mr, pginfo, pginfo->type, pginfo->num_pages, 
+			pginfo->next_buf, pginfo->next_page, number, kpage, 
+			pginfo->page_count, i, pginfo->next_listelem, 
+			pginfo->region, pginfo->next_chunk, pginfo->next_nmap);
+	else
+		EDEB_EX(4, "retcode=%x e_mr=%p pginfo=%p type=%x num_pages=%lx "
+			"next_buf=%lx next_page=%lx number=%x kpage=%p "
+			"page_count=%lx i=%x next_listelem=%lx region=%p "
+			"next_chunk=%p next_nmap=%lx",
+			retcode, e_mr, pginfo, pginfo->type, pginfo->num_pages, 
+			pginfo->next_buf, pginfo->next_page, number, kpage,
+			pginfo->page_count, i, pginfo->next_listelem,
+			pginfo->region, pginfo->next_chunk, pginfo->next_nmap);
+	return (retcode);
+} /* end ehca_set_pagebuf() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+/** @brief setup 1 page from page info page buffer
+ */
+static inline int ehca_set_pagebuf_1(struct ehca_mr *e_mr,
+				     struct ehca_mr_pginfo *pginfo,
+				     u64 *rpage) /**<OUT*/
+{
+	int retcode = 0;
+	struct ib_phys_buf *tmp_pbuf = 0;
+	u64 *tmp_fmrlist = 0;
+	struct ib_umem_chunk *chunk = 0;
+	struct ib_umem_chunk *prev_chunk = 0;
+	u64 pgaddr = 0;
+
+	EDEB_EN(7, "pginfo=%p type=%x num_pages=%lx next_buf=%lx next_page=%lx "
+		"rpage=%p page_count=%lx next_listelem=%lx region=%p "
+		"next_chunk=%p next_nmap=%lx",
+		pginfo, pginfo->type, pginfo->num_pages, pginfo->next_buf,
+		pginfo->next_page, rpage, pginfo->page_count, 
+		pginfo->next_listelem, pginfo->region, pginfo->next_chunk, 
+		pginfo->next_nmap);
+	
+	if (pginfo->type == EHCA_MR_PGI_PHYS) {
+		/* sanity check */
+		if (pginfo->page_count >= pginfo->num_pages) {
+			EDEB_ERR(4, "page_count >= num_pages, "
+				 "page_count=%lx num_pages=%lx",
+				 pginfo->page_count, pginfo->num_pages);
+			retcode = -EFAULT;
+			goto ehca_set_pagebuf_1_exit0;
+		}
+		tmp_pbuf = pginfo->phys_buf_array + pginfo->next_buf;
+		*rpage = phys_to_abs(((tmp_pbuf->addr & PAGE_MASK) +
+				      (pginfo->next_page * PAGE_SIZE)));
+		if ((*rpage == 0) && (tmp_pbuf->addr != 0)) {
+			EDEB_ERR(4, "tmp_pbuf->addr=%lx"
+				 " tmp_pbuf->size=%lx next_page=%lx",
+				 tmp_pbuf->addr, tmp_pbuf->size,
+				 pginfo->next_page);
+			retcode = -EFAULT;
+			goto ehca_set_pagebuf_1_exit0;
+		}
+		(pginfo->next_page)++;
+		(pginfo->page_count)++;
+		if (pginfo->next_page >= tmp_pbuf->size / PAGE_SIZE) {
+			(pginfo->next_buf)++;
+			pginfo->next_page = 0;
+		}
+	} else if (pginfo->type == EHCA_MR_PGI_USER) {
+		chunk      = pginfo->next_chunk;
+		prev_chunk = pginfo->next_chunk;
+		list_for_each_entry_continue(chunk, 
+					     (&(pginfo->region->chunk_list)), 
+					     list) {
+			pgaddr = ( page_to_pfn(chunk->page_list[
+						       pginfo->next_nmap].page)
+				   << PAGE_SHIFT );
+			*rpage = phys_to_abs(pgaddr);
+			EDEB(9,"pgaddr=%lx *rpage=%lx", pgaddr, *rpage);
+			if (*rpage == 0) {
+				EDEB_ERR(4, "chunk->page_list[]=%lx next_nmap=%lx "
+					 "mr=%p", (u64)sg_dma_address(
+						 &chunk->page_list[
+							 pginfo->next_nmap]),
+					 pginfo->next_nmap, e_mr);
+				retcode = -EFAULT;
+				goto ehca_set_pagebuf_1_exit0;
+			}
+			(pginfo->page_count)++;
+			(pginfo->next_nmap)++;
+			if (pginfo->next_nmap >= chunk->nmap) {
+				pginfo->next_nmap = 0;
+				prev_chunk = chunk;
+			}
+			break;
+		}
+		pginfo->next_chunk = 
+			list_prepare_entry(prev_chunk,
+					   (&(pginfo->region->chunk_list)),
+					   list);
+	} else if (pginfo->type == EHCA_MR_PGI_FMR) {
+		tmp_fmrlist = pginfo->page_list + pginfo->next_listelem;
+		*rpage = phys_to_abs(*tmp_fmrlist);
+		if (*rpage == 0) {
+			EDEB_ERR(4, "*tmp_fmrlist=%lx tmp_fmrlist=%p" 
+				 " next_listelem=%lx", *tmp_fmrlist,
+				 tmp_fmrlist, pginfo->next_listelem);
+			retcode = -EFAULT;
+			goto ehca_set_pagebuf_1_exit0;
+		}
+		(pginfo->next_listelem)++;
+		(pginfo->page_count)++;
+	} else {
+		EDEB_ERR(4, "bad pginfo->type=%x", pginfo->type);
+		retcode = -EFAULT;
+		goto ehca_set_pagebuf_1_exit0;
+	}
+
+      ehca_set_pagebuf_1_exit0:
+	if (retcode == 0)
+		EDEB_EX(7, "retcode=%x e_mr=%p pginfo=%p type=%x num_pages=%lx "
+			"next_buf=%lx next_page=%lx rpage=%p page_count=%lx "
+			"next_listelem=%lx region=%p next_chunk=%p "
+			"next_nmap=%lx",
+			retcode, e_mr, pginfo, pginfo->type, pginfo->num_pages, 
+			pginfo->next_buf, pginfo->next_page, rpage, 
+			pginfo->page_count, pginfo->next_listelem, 
+			pginfo->region, pginfo->next_chunk, pginfo->next_nmap);
+	else
+		EDEB_EX(4, "retcode=%x e_mr=%p pginfo=%p type=%x num_pages=%lx "
+			"next_buf=%lx next_page=%lx rpage=%p page_count=%lx "
+			"next_listelem=%lx region=%p next_chunk=%p "
+			"next_nmap=%lx",
+			retcode, e_mr, pginfo, pginfo->type, pginfo->num_pages, 
+			pginfo->next_buf, pginfo->next_page, rpage, 
+			pginfo->page_count, pginfo->next_listelem, 
+			pginfo->region, pginfo->next_chunk, pginfo->next_nmap);
+	return (retcode);
+} /* end ehca_set_pagebuf_1() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+/** @brief check MR if it is a max-MR, i.e. uses whole memory
+    in case it's a max-MR TRUE is returned, else FALSE
+*/
+static inline int ehca_mr_is_maxmr(u64 size, 
+				   u64 *iova_start)
+{
+	/* a MR is treated as max-MR only if it fits following: */
+	if ((size == ((u64)high_memory - PAGE_OFFSET)) &&
+	    (iova_start == (void*)KERNELBASE)) {
+		EDEB(6, "this is a max-MR");
+		return (TRUE);
+	} else
+		return (FALSE);
+} /* end ehca_mr_is_maxmr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+/** @brief map access control for MR/MW.
+    This routine is used for MR and MW.
+*/
+static inline void ehca_mrmw_map_acl(int ib_acl,    /**<IN*/
+				     u32 *hipz_acl) /**<OUT*/
+{
+	*hipz_acl = 0;
+	if (ib_acl & IB_ACCESS_REMOTE_READ)
+		*hipz_acl |= HIPZ_ACCESSCTRL_R_READ;
+	if (ib_acl & IB_ACCESS_REMOTE_WRITE)
+		*hipz_acl |= HIPZ_ACCESSCTRL_R_WRITE;
+	if (ib_acl & IB_ACCESS_REMOTE_ATOMIC)
+		*hipz_acl |= HIPZ_ACCESSCTRL_R_ATOMIC;
+	if (ib_acl & IB_ACCESS_LOCAL_WRITE)
+		*hipz_acl |= HIPZ_ACCESSCTRL_L_WRITE;
+	if (ib_acl & IB_ACCESS_MW_BIND)
+		*hipz_acl |= HIPZ_ACCESSCTRL_MW_BIND;
+} /* end ehca_mrmw_map_acl() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+/** @brief sets page size in hipz access control for MR/MW.
+ */
+static inline void ehca_mrmw_set_pgsize_hipz_acl(
+	u32 *hipz_acl) /**<INOUT HIPZ access control */
+{
+	/* @TODO page size of 4k currently hardcoded ... */
+	return;
+} /* end ehca_mrmw_set_pgsize_hipz_acl() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+/** @brief reverse map access control for MR/MW.
+    This routine is used for MR and MW.
+*/
+static inline void ehca_mrmw_reverse_map_acl(
+	const u32 *hipz_acl, /**<IN*/
+	int *ib_acl)	     /**<OUT*/
+{
+	*ib_acl = 0;
+	if (*hipz_acl & HIPZ_ACCESSCTRL_R_READ)
+		*ib_acl |= IB_ACCESS_REMOTE_READ;
+	if (*hipz_acl & HIPZ_ACCESSCTRL_R_WRITE)
+		*ib_acl |= IB_ACCESS_REMOTE_WRITE;
+	if (*hipz_acl & HIPZ_ACCESSCTRL_R_ATOMIC)
+		*ib_acl |= IB_ACCESS_REMOTE_ATOMIC;
+	if (*hipz_acl & HIPZ_ACCESSCTRL_L_WRITE)
+		*ib_acl |= IB_ACCESS_LOCAL_WRITE;
+	if (*hipz_acl & HIPZ_ACCESSCTRL_MW_BIND)
+		*ib_acl |= IB_ACCESS_MW_BIND;
+} /* end ehca_mrmw_reverse_map_acl() */
+
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+/** @brief map HIPZ rc to IB retcodes for MR/MW allocations
+    Used for hipz_mr_reg_alloc and hipz_mw_alloc.
+*/
+static inline int ehca_mrmw_map_rc_alloc(const u64 rc)
+{
+	switch (rc) {
+	case H_Success:	             /* successful completion */
+		return (0);
+	case H_ADAPTER_PARM:         /* invalid adapter handle */
+	case H_RT_PARM:              /* invalid resource type */
+	case H_NOT_ENOUGH_RESOURCES: /* insufficient resources */
+	case H_MLENGTH_PARM:         /* invalid memory length */
+	case H_MEM_ACCESS_PARM:      /* invalid access controls */
+	case H_Constrained:          /* resource constraint */
+		return (-EINVAL);
+	case H_Busy:                 /* long busy */
+		return (-EBUSY);
+	default:
+		return (-EINVAL);
+	}
+} /* end ehca_mrmw_map_rc_alloc() */ 
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+/** @brief map HIPZ rc to IB retcodes for MR register rpage
+    Used for hipz_h_register_rpage_mr at registering last page
+*/
+static inline int ehca_mrmw_map_rc_rrpg_last(const u64 rc)
+{
+	switch (rc) {
+	case H_Success:         /* registration complete */
+		return (0);
+	case H_PAGE_REGISTERED:	/* page registered */
+	case H_ADAPTER_PARM:    /* invalid adapter handle */
+	case H_RH_PARM:         /* invalid resource handle */
+/*	case H_QT_PARM:            invalid queue type */
+	case H_Parameter:       /* invalid logical address, */
+		                /* or count zero or greater 512 */
+	case H_TABLE_FULL:      /* page table full */
+	case H_Hardware:        /* HCA not operational */
+		return (-EINVAL);
+	case H_Busy:            /* long busy */
+		return (-EBUSY);
+	default:
+		return (-EINVAL);
+	}
+} /* end ehca_mrmw_map_rc_rrpg_last() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+/** @brief map HIPZ rc to IB retcodes for MR register rpage
+    Used for hipz_h_register_rpage_mr at registering one page, but not last page
+*/
+static inline int ehca_mrmw_map_rc_rrpg_notlast(const u64 rc)
+{
+	switch (rc) {
+	case H_PAGE_REGISTERED:	/* page registered */
+		return (0);
+	case H_Success:         /* registration complete */
+	case H_ADAPTER_PARM:    /* invalid adapter handle */
+	case H_RH_PARM:         /* invalid resource handle */
+/*	case H_QT_PARM:            invalid queue type */
+	case H_Parameter:       /* invalid logical address, */
+		                /* or count zero or greater 512 */
+	case H_TABLE_FULL:      /* page table full */
+	case H_Hardware:        /* HCA not operational */
+		return (-EINVAL);
+	case H_Busy:            /* long busy */
+		return (-EBUSY);
+	default:
+		return (-EINVAL);
+	}
+} /* end ehca_mrmw_map_rc_rrpg_notlast() */ 
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+/** @brief map HIPZ rc to IB retcodes for MR query
+    Used for hipz_mr_query.
+*/
+static inline int ehca_mrmw_map_rc_query_mr(const u64 rc)
+{
+	switch (rc) {
+	case H_Success:	             /* successful completion */
+		return (0);
+	case H_ADAPTER_PARM:         /* invalid adapter handle */
+	case H_RH_PARM:              /* invalid resource handle */
+		return (-EINVAL);
+	case H_Busy:                 /* long busy */
+		return (-EBUSY);
+	default:
+		return (-EINVAL);
+	}
+} /* end ehca_mrmw_map_rc_query_mr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+/** @brief map HIPZ rc to IB retcodes for freeing MR resource
+    Used for hipz_h_free_resource_mr
+*/
+static inline int ehca_mrmw_map_rc_free_mr(const u64 rc)
+{
+	switch (rc) {
+	case H_Success:	     /* resource freed */
+		return (0);
+	case H_ADAPTER_PARM: /* invalid adapter handle */
+	case H_RH_PARM:      /* invalid resource handle */
+	case H_R_STATE:      /* invalid resource state */
+	case H_Hardware:     /* HCA not operational */
+		return (-EINVAL);
+	case H_Resource:     /* Resource in use */
+	case H_Busy:         /* long busy */
+		return (-EBUSY);
+	default:
+		return (-EINVAL);
+	}
+} /* end ehca_mrmw_map_rc_free_mr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+/** @brief map HIPZ rc to IB retcodes for freeing MW resource
+    Used for hipz_h_free_resource_mw
+*/
+static inline int ehca_mrmw_map_rc_free_mw(const u64 rc)
+{
+	switch (rc) {
+	case H_Success:	     /* resource freed */
+		return (0);
+	case H_ADAPTER_PARM: /* invalid adapter handle */
+	case H_RH_PARM:      /* invalid resource handle */
+	case H_R_STATE:      /* invalid resource state */
+	case H_Hardware:     /* HCA not operational */
+		return (-EINVAL);
+	case H_Resource:     /* Resource in use */
+	case H_Busy:         /* long busy */
+		return (-EBUSY);
+	default:
+		return (-EINVAL);
+	}
+} /* end ehca_mrmw_map_rc_free_mw() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+/** @brief map HIPZ rc to IB retcodes for SMR registrations
+    Used for hipz_h_register_smr.
+*/
+static inline int ehca_mrmw_map_rc_reg_smr(const u64 rc)
+{
+	switch (rc) {
+	case H_Success:	             /* successful completion */
+		return (0);
+	case H_ADAPTER_PARM:         /* invalid adapter handle */
+	case H_RH_PARM:              /* invalid resource handle */
+	case H_MEM_PARM:             /* invalid MR virtual address */
+	case H_MEM_ACCESS_PARM:      /* invalid access controls */
+	case H_NOT_ENOUGH_RESOURCES: /* insufficient resources */
+		return (-EINVAL);
+	case H_Busy:                 /* long busy */
+		return (-EBUSY);
+	default:
+		return (-EINVAL);
+	}
+} /* end ehca_mrmw_map_rc_reg_smr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+/** @brief MR destructor and constructor
+    used in Reregister MR verb, memsets ehca_mr_t to 0, 
+    except struct ib_mr and spinlock
+ */
+static inline void ehca_mr_deletenew(struct ehca_mr *mr)
+{
+	u32 offset = (u64)(&mr->flags) - (u64)mr;
+	memset(&mr->flags, 0, sizeof(*mr) - offset);
+} /* end ehca_mr_deletenew() */
+
+#endif /*_EHCA_MRMW_H_*/
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_pd.c newtree/drivers/infiniband/hw/ehca/ehca_pd.c
--- oldtree/drivers/infiniband/hw/ehca/ehca_pd.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_pd.c	2006-02-13 19:19:59.931522712 -0500
@@ -0,0 +1,100 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  PD functions
+ *
+ *  Authors: Christoph Raisch <raisch@de.ibm.com> 
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_pd.c,v 1.23 2005/09/26 15:36:53 souissi Exp $
+ */
+
+
+#define DEB_PREFIX "vpd "
+
+#include "ehca_kernel.h"
+#include "ehca_tools.h"
+#include "ehca_iverbs.h"
+
+struct ib_pd *ehca_alloc_pd(struct ib_device *device,
+			    struct ib_ucontext *context, struct ib_udata *udata)
+{
+	struct ib_pd *mypd = NULL;
+	struct ehca_pd *pd = NULL;
+
+	EDEB_EN(7, "device=%p context=%p udata=%p", device, context, udata);
+
+	EHCA_CHECK_DEVICE_P(device);
+
+	pd = ehca_pd_new();
+	if (!pd) {
+		EDEB_ERR(4, "ERROR device=%p context=%p pd=%p "
+			 "out of memory", device, context, mypd);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	/* kernel pd when (device,-1,0)
+         * user pd only if context != -1  */
+	if (context == NULL) {
+		/* kernel pds after init reuses always 
+		 * the one created in ehca_shca_reopen()
+		 */
+		struct ehca_shca *shca = container_of(device, struct ehca_shca,
+						      ib_device);
+		pd->fw_pd.value = shca->pd->fw_pd.value;
+	} else {
+		pd->fw_pd.value = (u64)pd;
+	}
+
+	mypd = &pd->ib_pd;
+
+	EHCA_REGISTER_PD(device, pd);
+
+	EDEB_EX(7, "device=%p context=%p pd=%p", device, context, mypd);
+
+	return (mypd);
+}
+
+int ehca_dealloc_pd(struct ib_pd *pd)
+{
+	int ret = 0;
+	EDEB_EN(7, "pd=%p", pd);
+
+	EHCA_CHECK_PD(pd);
+	EHCA_DEREGISTER_PD(pd);
+	ehca_pd_delete(container_of(pd, struct ehca_pd, ib_pd));
+
+	EDEB_EX(7, "pd=%p", pd);
+	return ret;
+}
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_qes.h newtree/drivers/infiniband/hw/ehca/ehca_qes.h
--- oldtree/drivers/infiniband/hw/ehca/ehca_qes.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_qes.h	2006-02-13 19:19:59.932522560 -0500
@@ -0,0 +1,274 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  Hardware request structures
+ *
+ *  Authors: Waleri Fomin <fomin@de.ibm.com>
+ *           Reinhard Ernst <rernst@de.ibm.com> 
+ *           Christoph Raisch <raisch@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_qes.h,v 1.8 2005/11/29 17:11:10 nguyen Exp $
+ */
+
+
+#ifndef _EHCA_QES_H_
+#define _EHCA_QES_H_
+
+/** DON'T include any kernel related files here!!!
+ * This file is used commonly in user and kernel space!!!
+ */
+
+/**
+ * virtual scatter gather entry to specify remote adresses with length
+ */
+struct ehca_vsgentry {
+	u64 vaddr;
+	u32 lkey;
+	u32 length;
+};
+
+#define GRH_FLAG_MASK        EHCA_BMASK_IBM(7,7)
+#define GRH_IPVERSION_MASK   EHCA_BMASK_IBM(0,3)
+#define GRH_TCLASS_MASK      EHCA_BMASK_IBM(4,12)
+#define GRH_FLOWLABEL_MASK   EHCA_BMASK_IBM(13,31)
+#define GRH_PAYLEN_MASK      EHCA_BMASK_IBM(32,47)
+#define GRH_NEXTHEADER_MASK  EHCA_BMASK_IBM(48,55)
+#define GRH_HOPLIMIT_MASK    EHCA_BMASK_IBM(56,63)
+
+/** 
+ * Unreliable Datagram Address Vector Format
+ * see IBTA Vol1 chapter 8.3 Global Routing Header
+ */
+struct ehca_ud_av {
+	u8 sl;
+	u8 lnh;
+	u16 dlid;
+	u8 reserved1;
+	u8 reserved2;
+	u8 reserved3;
+	u8 slid_path_bits;
+	u8 reserved4;
+	u8 ipd;
+	u8 reserved5;
+	u8 pmtu;
+	u32 reserved6;
+	u64 reserved7;
+	union {
+		struct {
+			u64 word_0; /* always set to 6  */
+			/*should be 0x1B for IB transport */
+			u64 word_1;
+			u64 word_2;
+			u64 word_3;
+			u64 word_4;
+		} grh;
+		struct {
+			u32 wd_0;
+			u32 wd_1;
+			/* DWord_1 --> SGID */
+
+			u32 sgid_wd3;
+			/* bits 127 - 96       */
+
+			u32 sgid_wd2;
+			/* bits  95 - 64 */
+			/* DWord_2 */
+
+			u32 sgid_wd1;
+			/* bits  63 - 32 */
+
+			u32 sgid_wd0;
+			/* bits  31 -  0 */
+			/* DWord_3 --> DGID */
+
+			u32 dgid_wd3;
+			/* bits 127 - 96
+			 **/
+			u32 dgid_wd2;
+			/* bits  95 - 64
+			 DWord_4 */
+			u32 dgid_wd1;
+			/* bits  63 - 32 */
+
+			u32 dgid_wd0;
+			/* bits  31 -  0    */
+		} grh_l;
+	};
+};
+
+/* maximum number of sg entries allowed in a WQE */
+#define MAX_WQE_SG_ENTRIES 252	
+
+#define  WQE_OPTYPE_SEND             0x80
+#define  WQE_OPTYPE_RDMAREAD         0x40
+#define  WQE_OPTYPE_RDMAWRITE        0x20
+#define  WQE_OPTYPE_CMPSWAP          0x10
+#define  WQE_OPTYPE_FETCHADD         0x08
+#define  WQE_OPTYPE_BIND             0x04
+
+#define  WQE_WRFLAG_REQ_SIGNAL_COM   0x80
+#define  WQE_WRFLAG_FENCE            0x40
+#define  WQE_WRFLAG_IMM_DATA_PRESENT 0x20
+#define  WQE_WRFLAG_SOLIC_EVENT      0x10
+
+#define  WQEF_CACHE_HINT             0x80
+#define  WQEF_CACHE_HINT_RD_WR       0x40
+#define  WQEF_TIMED_WQE              0x20
+#define  WQEF_PURGE                  0x08
+
+#define MW_BIND_ACCESSCTRL_R_WRITE   0x40
+#define MW_BIND_ACCESSCTRL_R_READ    0x20
+#define MW_BIND_ACCESSCTRL_R_ATOMIC  0x10
+
+struct ehca_wqe {
+	u64 work_request_id;
+	u8 optype;
+	u8 wr_flag;
+	u16 pkeyi;
+	u8 wqef;
+	u8 nr_of_data_seg;
+	u16 wqe_provided_slid;
+	u32 destination_qp_number;
+	u32 resync_psn_sqp;
+	u32 local_ee_context_qkey;
+	u32 immediate_data;
+	union {
+		struct {
+			u64 remote_virtual_adress;
+			u32 rkey;
+			u32 reserved;
+			u64 atomic_1st_op_dma_len;
+			u64 atomic_2nd_op;
+			struct ehca_vsgentry sg_list[MAX_WQE_SG_ENTRIES];
+
+		} nud;
+		struct {
+			u64 ehca_ud_av_ptr;
+			u64 reserved1;
+			u64 reserved2;
+			u64 reserved3;
+			struct ehca_vsgentry sg_list[MAX_WQE_SG_ENTRIES];
+		} ud_avp;
+		struct {
+			struct ehca_ud_av ud_av;
+			struct ehca_vsgentry sg_list[MAX_WQE_SG_ENTRIES -
+						     2];
+		} ud_av;
+		struct {
+			u64 reserved0;
+			u64 reserved1;
+			u64 reserved2;
+			u64 reserved3;
+			struct ehca_vsgentry sg_list[MAX_WQE_SG_ENTRIES];
+		} all_rcv;
+
+		struct {
+			u64 reserved;
+			u32 rkey;
+			u32 old_rkey;
+			u64 reserved1;
+			u64 reserved2;
+			u64 virtual_address;
+			u32 reserved3;
+			u32 length;
+			u32 reserved4;
+			u16 reserved5;
+			u8 reserved6;
+			u8 lr_ctl;
+			u32 lkey;
+			u32 reserved7;
+			u64 reserved8;
+			u64 reserved9;
+			u64 reserved10;
+			u64 reserved11;
+		} bind;
+		struct {
+			u64 reserved12;
+			u64 reserved13;
+			u32 size;
+			u32 start;
+		} inline_data;
+	} u;
+
+};
+
+#define WC_SEND_RECEIVE EHCA_BMASK_IBM(0,0)
+#define WC_IMM_DATA     EHCA_BMASK_IBM(1,1)
+#define WC_GRH_PRESENT  EHCA_BMASK_IBM(2,2)
+#define WC_SE_BIT       EHCA_BMASK_IBM(3,3)
+
+struct ehca_cqe {
+	u64 work_request_id;
+	u8 optype;
+	u8 w_completion_flags;
+	u16 reserved1;
+	u32 nr_bytes_transferred;
+	u32 immediate_data;
+	u32 local_qp_number;
+	u8 freed_resource_count;
+	u8 service_level;
+	u16 wqe_count;
+	u32 qp_token;
+	u32 qkey_ee_token;
+	u32 remote_qp_number;
+	u16 dlid;
+	u16 rlid;
+	u16 reserved2;
+	u16 pkey_index;
+	u32 cqe_timestamp;
+	u32 wqe_timestamp;
+	u8 wqe_timestamp_valid;
+	u8 reserved3;
+	u8 reserved4;
+	u8 cqe_flags;
+	u32 status;
+};
+
+struct ehca_eqe {
+	u64 entry;
+};
+
+struct ehca_mrte {
+	u64 starting_va;
+	u64 length; /* length of memory region in bytes*/
+	u32 pd;
+	u8 key_instance;
+	u8 pagesize;
+	u8 mr_control;
+	u8 local_remote_access_ctrl;
+	u8 reserved[0x20 - 0x18];
+	u64 at_pointer[4];
+};
+#endif /*_EHCA_QES_H_*/
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_qp.c newtree/drivers/infiniband/hw/ehca/ehca_qp.c
--- oldtree/drivers/infiniband/hw/ehca/ehca_qp.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_qp.c	2006-02-13 19:19:59.936521952 -0500
@@ -0,0 +1,1776 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  QP functions
+ *
+ *  Authors: Waleri Fomin <fomin@de.ibm.com>
+ *           Reinhard Ernst <rernst@de.ibm.com> 
+ *           Hoang-Nam Nguyen <hnguyen@de.ibm.com> 
+ *           Heiko J Schick <schickhj@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_qp.c,v 1.147 2005/12/06 11:53:32 nguyen Exp $
+ */
+
+
+#define DEB_PREFIX "e_qp"
+
+#include "ehca_kernel.h"
+
+#include "ehca_classes.h"
+#include "ehca_tools.h"
+#include "hcp_if.h"
+#include "ehca_qes.h"
+
+#include "ehca_iverbs.h"
+#include <linux/module.h>
+#include <linux/err.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+/** @brief attributes not supported by query qp
+ */
+#define QP_ATTR_QUERY_NOT_SUPPORTED (IB_QP_MAX_DEST_RD_ATOMIC | \
+                                     IB_QP_MAX_QP_RD_ATOMIC   | \
+                                     IB_QP_ACCESS_FLAGS       | \
+                                     IB_QP_EN_SQD_ASYNC_NOTIFY)
+
+/** @brief ehca (internal) qp state values
+ */
+enum ehca_qp_state {
+	EHCA_QPS_RESET = 1,
+	EHCA_QPS_INIT = 2,
+	EHCA_QPS_RTR = 3,
+	EHCA_QPS_RTS = 5,
+	EHCA_QPS_SQD = 6,
+	EHCA_QPS_SQE = 8,
+	EHCA_QPS_ERR = 128
+};
+
+/** @brief qp state transitions as defined by IB Arch Rel 1.1 page 431
+ */
+enum ib_qp_statetrans {
+	IB_QPST_ANY2RESET,
+	IB_QPST_ANY2ERR,
+	IB_QPST_RESET2INIT,
+	IB_QPST_INIT2RTR,
+	IB_QPST_INIT2INIT,
+	IB_QPST_RTR2RTS,
+	IB_QPST_RTS2SQD,
+	IB_QPST_RTS2RTS,
+	IB_QPST_SQD2RTS,
+	IB_QPST_SQE2RTS,
+	IB_QPST_SQD2SQD,
+	IB_QPST_MAX	/* nr of transitions, this must be last!!! */
+};
+
+/** @brief returns ehca qp state corresponding to given ib qp state
+ */
+static inline enum ehca_qp_state ib2ehca_qp_state(enum ib_qp_state ib_qp_state)
+{
+	switch (ib_qp_state) {
+	case IB_QPS_RESET:
+		return EHCA_QPS_RESET;
+	case IB_QPS_INIT:
+		return EHCA_QPS_INIT;
+	case IB_QPS_RTR:
+		return EHCA_QPS_RTR;
+	case IB_QPS_RTS:
+		return EHCA_QPS_RTS;
+	case IB_QPS_SQD:
+		return EHCA_QPS_SQD;
+	case IB_QPS_SQE:
+		return EHCA_QPS_SQE;
+	case IB_QPS_ERR:
+		return EHCA_QPS_ERR;
+	default:
+		EDEB_ERR(4, "invalid ib_qp_state=%x", ib_qp_state);
+		return -EINVAL;
+	}
+}
+
+/** @brief returns ib qp state corresponding to given ehca qp state
+ */
+static inline enum ib_qp_state ehca2ib_qp_state(enum ehca_qp_state
+						ehca_qp_state)
+{
+	switch (ehca_qp_state) {
+	case EHCA_QPS_RESET:
+		return IB_QPS_RESET;
+	case EHCA_QPS_INIT:
+		return IB_QPS_INIT;
+	case EHCA_QPS_RTR:
+		return IB_QPS_RTR;
+	case EHCA_QPS_RTS:
+		return IB_QPS_RTS;
+	case EHCA_QPS_SQD:
+		return IB_QPS_SQD;
+	case EHCA_QPS_SQE:
+		return IB_QPS_SQE;
+	case EHCA_QPS_ERR:
+		return IB_QPS_ERR;
+	default:
+		EDEB_ERR(4,"invalid ehca_qp_state=%x",ehca_qp_state);
+		return -EINVAL;
+	}
+}
+
+/** @brief qp type
+ * used as index for req_attr and opt_attr of struct ehca_modqp_statetrans
+ */
+enum ehca_qp_type {
+	QPT_RC = 0,
+	QPT_UC = 1,
+	QPT_UD = 2,
+	QPT_SQP = 3,
+	QPT_MAX
+};
+
+/** @brief returns ehca qp type corresponding to ib qp type
+ */
+static inline enum ehca_qp_type ib2ehcaqptype(enum ib_qp_type ibqptype)
+{
+	switch (ibqptype) {
+	case IB_QPT_SMI:
+	case IB_QPT_GSI:
+		return QPT_SQP;
+	case IB_QPT_RC:
+		return QPT_RC;
+	case IB_QPT_UC:
+		return QPT_UC;
+	case IB_QPT_UD:
+		return QPT_UD;
+	default:
+		EDEB_ERR(4,"Invalid ibqptype=%x", ibqptype);
+		return -EINVAL;
+	}
+}
+
+/** @brief struct describes a state transition via modify qp
+ */
+struct ehca_modqp_statetrans {
+	enum ib_qp_statetrans statetrans;
+	int req_attr[QPT_MAX];
+	int opt_attr[QPT_MAX];
+};
+
+/** @brief state transition table used by modify qp
+ * the order is defined by transitions listed in enum ib_qp_statetrans
+ */
+static const struct ehca_modqp_statetrans modqp_statetrans_table[IB_QPST_MAX] = {
+	[IB_QPST_ANY2RESET] = {.statetrans = IB_QPST_ANY2RESET},
+	[IB_QPST_ANY2ERR] = {.statetrans = IB_QPST_ANY2ERR},
+	[IB_QPST_RESET2INIT] = {
+		.statetrans = IB_QPST_RESET2INIT,
+		.req_attr = {
+			[QPT_RC] = (IB_QP_STATE |
+				    IB_QP_PKEY_INDEX |
+				    IB_QP_PORT |
+				    IB_QP_ACCESS_FLAGS),
+			[QPT_UC] = (IB_QP_STATE |
+				    IB_QP_PKEY_INDEX |
+				    IB_QP_PORT |
+				    IB_QP_ACCESS_FLAGS),
+			[QPT_UD] = (IB_QP_STATE |
+				    IB_QP_PKEY_INDEX |
+				    IB_QP_PORT |
+				    IB_QP_QKEY),
+			[QPT_SQP] = (IB_QP_STATE |
+				     IB_QP_PKEY_INDEX |
+				     IB_QP_PORT |
+				     IB_QP_QKEY)
+		}
+	},
+	[IB_QPST_INIT2RTR] = {
+		.statetrans = IB_QPST_INIT2RTR,
+		.req_attr = {
+			[QPT_RC] = (IB_QP_STATE |
+				    IB_QP_RQ_PSN |
+				    IB_QP_PATH_MTU |
+				    IB_QP_MAX_DEST_RD_ATOMIC |
+				    IB_QP_DEST_QPN |
+				    IB_QP_AV |
+				    IB_QP_MIN_RNR_TIMER),
+			[QPT_UC] = (IB_QP_STATE |
+				    IB_QP_RQ_PSN |
+				    IB_QP_PATH_MTU |
+				    IB_QP_DEST_QPN |
+				    IB_QP_AV),
+			[QPT_UD] = (IB_QP_STATE),
+			[QPT_SQP] = (IB_QP_STATE)
+		},
+		.opt_attr = {
+			[QPT_RC] = (IB_QP_ALT_PATH |
+				    IB_QP_ACCESS_FLAGS |
+				    IB_QP_PKEY_INDEX),
+			[QPT_UC] = (IB_QP_ALT_PATH |
+				    IB_QP_ACCESS_FLAGS |
+				    IB_QP_PKEY_INDEX),
+			[QPT_UD] = (IB_QP_PKEY_INDEX |
+				    IB_QP_QKEY),
+			[QPT_SQP] = (IB_QP_PKEY_INDEX |
+				     IB_QP_QKEY)
+		}
+	},
+	[IB_QPST_INIT2INIT] = {
+		.statetrans = IB_QPST_INIT2INIT,
+		.opt_attr = {
+			[QPT_RC] = (IB_QP_PKEY_INDEX |
+				    IB_QP_ACCESS_FLAGS |
+				    IB_QP_PORT),
+			[QPT_UC] = (IB_QP_PKEY_INDEX |
+				    IB_QP_ACCESS_FLAGS |
+				    IB_QP_PORT),
+			[QPT_UD] = (IB_QP_QKEY |
+				    IB_QP_PKEY_INDEX |
+				    IB_QP_PORT),
+			[QPT_SQP] = (IB_QP_QKEY |
+				     IB_QP_PKEY_INDEX |
+				     IB_QP_PORT)
+		}
+	},
+	[IB_QPST_RTR2RTS] = {
+		.statetrans = IB_QPST_RTR2RTS,
+		.req_attr = {
+			[QPT_RC] = (IB_QP_STATE |
+				    IB_QP_SQ_PSN |
+				    IB_QP_MAX_QP_RD_ATOMIC |
+				    IB_QP_RNR_RETRY |
+				    IB_QP_TIMEOUT |
+				    IB_QP_RETRY_CNT),
+			[QPT_UC] = (IB_QP_STATE |
+				    IB_QP_SQ_PSN),
+			[QPT_UD] = (IB_QP_STATE |
+				    IB_QP_SQ_PSN),
+			[QPT_SQP] = (IB_QP_STATE |
+				     IB_QP_SQ_PSN)
+		},
+		.opt_attr = {
+			[QPT_RC] = (IB_QP_PATH_MIG_STATE |
+				    IB_QP_ALT_PATH |
+				    IB_QP_MIN_RNR_TIMER |
+				    IB_QP_ACCESS_FLAGS |
+				    IB_QP_PKEY_INDEX |
+				    IB_QP_CUR_STATE),
+			[QPT_UC] = (IB_QP_PATH_MIG_STATE |
+				    IB_QP_ALT_PATH |
+				    IB_QP_ACCESS_FLAGS |
+				    IB_QP_PKEY_INDEX |
+				    IB_QP_CUR_STATE),
+			[QPT_UD] = (IB_QP_QKEY |
+				    IB_QP_CUR_STATE),
+			[QPT_SQP] = (IB_QP_QKEY |
+				     IB_QP_CUR_STATE)
+		}
+	},
+	[IB_QPST_RTS2SQD] = {
+		.statetrans = IB_QPST_RTS2SQD,
+		.req_attr = {
+			[QPT_RC] = (IB_QP_STATE),
+			[QPT_UC] = (IB_QP_STATE),
+			[QPT_UD] = (IB_QP_STATE),
+			[QPT_SQP] = (IB_QP_STATE)
+		},
+	},
+	[IB_QPST_RTS2RTS] = {
+		.statetrans = IB_QPST_RTS2RTS,
+		.opt_attr = {
+			[QPT_RC] = (IB_QP_PATH_MIG_STATE |
+				    IB_QP_ALT_PATH |
+				    IB_QP_MIN_RNR_TIMER |
+				    IB_QP_ACCESS_FLAGS),
+			[QPT_UC] = (IB_QP_PATH_MIG_STATE |
+				    IB_QP_ALT_PATH |
+				    IB_QP_ACCESS_FLAGS),
+			[QPT_UD] = (IB_QP_QKEY |
+				    IB_QP_CUR_STATE),
+			[QPT_SQP] = (IB_QP_QKEY |
+				     IB_QP_CUR_STATE)
+		}
+	},
+	[IB_QPST_SQD2RTS] = {
+		.statetrans = IB_QPST_SQD2RTS,
+		.req_attr = {
+			[QPT_RC] = (IB_QP_STATE),
+			[QPT_UC] = (IB_QP_STATE),
+			[QPT_UD] = (IB_QP_STATE),
+			[QPT_SQP] = (IB_QP_STATE)
+		},
+		.opt_attr = {
+			[QPT_RC]  = (IB_QP_ALT_PATH |
+				     IB_QP_ACCESS_FLAGS |
+				     IB_QP_MIN_RNR_TIMER |
+				     IB_QP_PATH_MIG_STATE |
+				     IB_QP_CUR_STATE),
+			[QPT_UC]  = (IB_QP_ALT_PATH |
+				     IB_QP_ACCESS_FLAGS |
+				     IB_QP_PATH_MIG_STATE |
+				     IB_QP_CUR_STATE),
+			[QPT_UD]  = (IB_QP_QKEY |
+				     IB_QP_CUR_STATE),
+			[QPT_SQP]  = (IB_QP_QKEY |
+				      IB_QP_CUR_STATE)
+		}
+	},
+	[IB_QPST_SQE2RTS] = {
+		.statetrans = IB_QPST_SQE2RTS,
+		.req_attr = {
+			[QPT_RC] = (IB_QP_STATE),
+			[QPT_UC] = (IB_QP_STATE),
+			[QPT_UD] = (IB_QP_STATE),
+			[QPT_SQP] = (IB_QP_STATE)
+		},
+		.opt_attr = {
+			[QPT_RC] = (IB_QP_MIN_RNR_TIMER |
+				    IB_QP_CUR_STATE),
+			[QPT_UC] = (IB_QP_CUR_STATE),
+			[QPT_UD] = (IB_QP_QKEY |
+				    IB_QP_CUR_STATE),
+			[QPT_SQP] = (IB_QP_QKEY |
+				     IB_QP_CUR_STATE)
+		}
+	},
+	[IB_QPST_SQD2SQD] = {
+		.statetrans = IB_QPST_SQD2SQD,
+		.opt_attr = {
+			[QPT_RC]  = (IB_QP_ALT_PATH |
+				     IB_QP_ACCESS_FLAGS |
+				     IB_QP_MIN_RNR_TIMER |
+				     IB_QP_PATH_MIG_STATE |
+				     IB_QP_AV |
+				     IB_QP_MAX_QP_RD_ATOMIC |
+				     IB_QP_MAX_DEST_RD_ATOMIC |
+				     IB_QP_CUR_STATE |
+				     IB_QP_PKEY_INDEX |
+				     IB_QP_TIMEOUT |
+				     IB_QP_RETRY_CNT |
+				     IB_QP_RNR_RETRY),
+			[QPT_UC]  = (IB_QP_ALT_PATH |
+				     IB_QP_ACCESS_FLAGS |
+				     IB_QP_PATH_MIG_STATE |
+				     IB_QP_AV |
+				     IB_QP_CUR_STATE |
+				     IB_QP_PKEY_INDEX),
+			[QPT_UD]  = (IB_QP_QKEY |
+				     IB_QP_PKEY_INDEX),
+			[QPT_SQP]  = (IB_QP_QKEY |
+				      IB_QP_PKEY_INDEX)
+		}
+	},
+};
+
+/** @brief validates  qp state transition  from/to state
+ * output: req and opt attr masks in statetrans
+ * returns: 0 if transition  valid 
+ *         -EINVAL if not
+ */
+static inline int get_modqp_statetrans(int ib_fromstate, int ib_tostate,
+				       struct ehca_modqp_statetrans *statetrans)
+{
+	int index = -EINVAL;
+	switch (ib_tostate) {
+	case IB_QPS_RESET:
+		index = IB_QPST_ANY2RESET;
+		break;
+	case IB_QPS_INIT:
+		if (ib_fromstate == IB_QPS_RESET) {
+			index = IB_QPST_RESET2INIT;
+		} else if (ib_fromstate == IB_QPS_INIT) {
+			index = IB_QPST_INIT2INIT;
+		}
+		break;
+	case IB_QPS_RTR:
+		if (ib_fromstate == IB_QPS_INIT) {
+			index = IB_QPST_INIT2RTR;
+		}
+		break;
+	case IB_QPS_RTS:
+		if (ib_fromstate == IB_QPS_RTR) {
+			index = IB_QPST_RTR2RTS;
+		} else if (ib_fromstate == IB_QPS_RTS) {
+			index = IB_QPST_RTS2RTS;
+		} else if (ib_fromstate == IB_QPS_SQD) {
+			index = IB_QPST_SQD2RTS;
+		} else if (ib_fromstate == IB_QPS_SQE) {
+			index = IB_QPST_SQE2RTS;
+		}
+		break;
+	case IB_QPS_SQD:
+		if (ib_fromstate == IB_QPS_RTS) {
+			index = IB_QPST_RTS2SQD;
+		}
+		break;
+	case IB_QPS_SQE:
+		/* not allowed via mod qp */
+		break;
+	case IB_QPS_ERR:
+		index = IB_QPST_ANY2ERR;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (index >= 0)
+		*statetrans = modqp_statetrans_table[index];
+
+	return index;
+}
+
+/** @brief ehca service types
+ */
+enum ehca_service_type {
+	ST_RC = 0,
+	ST_UC = 1,
+	ST_RD = 2,
+	ST_UD = 3
+};
+
+/** @brief returns hcp service type corresponding to given ib qp type
+ * used by create_qp()
+ */
+static inline int ibqptype2servicetype(enum ib_qp_type ibqptype)
+{
+	switch (ibqptype) {
+	case IB_QPT_SMI:
+	case IB_QPT_GSI:
+		return ST_UD;
+	case IB_QPT_RC:
+		return ST_RC;
+	case IB_QPT_UC:
+		return ST_UC;
+	case IB_QPT_UD:
+		return ST_UD;
+	case IB_QPT_RAW_IPV6:
+		return -EINVAL;
+	case IB_QPT_RAW_ETY:
+		return -EINVAL;
+	default:
+		EDEB_ERR(4, "Invalid ibqptype=%x", ibqptype);
+		return -EINVAL;
+	}
+}
+
+/* init_qp_queues - Initializes/constructs r/squeue and registers queue pages.
+ * returns 0  if successful, 
+ *        -EXXXX if not
+ */
+static inline int init_qp_queues(struct ipz_adapter_handle ipz_hca_handle,
+				 struct ehca_qp *my_qp,
+				 int nr_sq_pages,
+				 int nr_rq_pages,
+				 int swqe_size,
+				 int rwqe_size,
+				 int nr_send_sges, int nr_receive_sges)
+{
+	int ret = -EINVAL;
+	int cnt = 0;
+	void *vpage = NULL;
+	u64 rpage = 0;
+	int ipz_rc = -1;
+	u64 hipz_rc = H_Parameter;
+
+	ipz_rc = ipz_queue_ctor(&my_qp->ehca_qp_core.ipz_squeue,
+				nr_sq_pages,
+				EHCA_PAGESIZE, swqe_size, nr_send_sges);
+	if (!ipz_rc) {
+		EDEB_ERR(4, "Cannot allocate page for squeue. ipz_rc=%x",
+			 ipz_rc);
+		ret = -EBUSY;
+		return ret;
+	}
+
+	ipz_rc = ipz_queue_ctor(&my_qp->ehca_qp_core.ipz_rqueue,
+				nr_rq_pages,
+				EHCA_PAGESIZE, rwqe_size, nr_receive_sges);
+	if (!ipz_rc) {
+		EDEB_ERR(4, "Cannot allocate page for rqueue. ipz_rc=%x",
+			 ipz_rc);
+		ret = -EBUSY;
+		goto init_qp_queues0;
+	}
+	/* register SQ pages */
+	for (cnt = 0; cnt < nr_sq_pages; cnt++) {
+		vpage = ipz_QPageit_get_inc(&my_qp->ehca_qp_core.ipz_squeue);
+		if (!vpage) {
+			EDEB_ERR(4, "SQ ipz_QPageit_get_inc() "
+				 "failed p_vpage= %p", vpage);
+			ret = -EINVAL;
+			goto init_qp_queues1;
+		}
+		rpage = ehca_kv_to_g(vpage);
+
+		hipz_rc = hipz_h_register_rpage_qp(ipz_hca_handle, 
+						   my_qp->ipz_qp_handle, 
+						   &my_qp->pf, 0, 0, /*TODO*/
+						   rpage, 1,
+						   my_qp->ehca_qp_core.galpas.kernel);
+		if (hipz_rc < H_Success) {
+			EDEB_ERR(4,"SQ  hipz_qp_register_rpage() faield "
+				 " rc=%lx", hipz_rc);
+			ret = ehca2ib_return_code(hipz_rc);
+			goto init_qp_queues1;
+		}
+		/* for sq no need to check hipz_rc against 
+		   e.g. H_PAGE_REGISTERED */
+	}
+
+	ipz_QEit_reset(&my_qp->ehca_qp_core.ipz_squeue);
+
+	/* register RQ pages */
+	for (cnt = 0; cnt < nr_rq_pages; cnt++) {
+		vpage = ipz_QPageit_get_inc(&my_qp->ehca_qp_core.ipz_rqueue);
+		if (!vpage) {
+			EDEB_ERR(4,"RQ ipz_QPageit_get_inc() "
+				 "failed p_vpage = %p", vpage);
+			hipz_rc = H_Resource;
+			ret = -EINVAL;
+			goto init_qp_queues1;
+		}
+
+		rpage = ehca_kv_to_g(vpage);
+
+		hipz_rc = hipz_h_register_rpage_qp(ipz_hca_handle, 
+						   my_qp->ipz_qp_handle, 
+						   &my_qp->pf, 0, 1, /*TODO*/
+						   rpage, 1,
+						   my_qp->ehca_qp_core.galpas.
+						   kernel);
+		if (hipz_rc < H_Success) {
+			EDEB_ERR(4, "RQ hipz_qp_register_rpage() failed "
+			     "rc=%lx", hipz_rc);
+			ret = ehca2ib_return_code(hipz_rc);
+			goto init_qp_queues1;
+		}
+		if (cnt == (nr_rq_pages - 1)) {	/* last page! */
+			if (hipz_rc != H_Success) {
+				EDEB_ERR(4,"RQ hipz_qp_register_rpage() "
+					 "hipz_rc= %lx ", hipz_rc);
+				ret = ehca2ib_return_code(hipz_rc);
+				goto init_qp_queues1;
+			}
+			vpage = ipz_QPageit_get_inc(&my_qp->ehca_qp_core.ipz_rqueue);
+			if (vpage != NULL) {
+				EDEB_ERR(4,"ipz_QPageit_get_inc() "
+					 "should not succeed vpage=%p",
+					 vpage);
+				ret = -EINVAL;
+				goto init_qp_queues1;
+			}
+		} else {
+			if (hipz_rc != H_PAGE_REGISTERED) {
+				EDEB_ERR(4,"RQ hipz_qp_register_rpage() "
+					 "hipz_rc= %lx ", hipz_rc);
+				ret = ehca2ib_return_code(hipz_rc);
+				goto init_qp_queues1;
+			}
+		}
+	}
+
+	ipz_QEit_reset(&my_qp->ehca_qp_core.ipz_rqueue);
+
+	return 0;
+
+ init_qp_queues1:
+	ipz_queue_dtor(&my_qp->ehca_qp_core.ipz_rqueue);
+ init_qp_queues0:
+	ipz_queue_dtor(&my_qp->ehca_qp_core.ipz_squeue);
+	return ret;
+}
+
+
+struct ib_qp *ehca_create_qp(struct ib_pd *pd,
+			     struct ib_qp_init_attr *init_attr,
+			     struct ib_udata *udata)
+{
+        static int da_msg_size[]={ 128, 256, 512, 1024, 2048, 4096 };
+	int ret = -EINVAL;
+	int servicetype = 0;
+	int sigtype = 0;
+
+	struct ehca_qp *my_qp = NULL;
+	struct ehca_pd *my_pd = NULL;
+	struct ehca_shca *shca = NULL;
+	struct ehca_cq *recv_ehca_cq = NULL;
+	struct ehca_cq *send_ehca_cq = NULL;
+	struct ib_ucontext *context = NULL;
+	struct ehca_create_qp ucmd;
+	u64 hipz_rc = H_Parameter;
+	int max_send_sge;
+	int max_recv_sge;
+	/* h_call's out parameters */
+	u16 act_nr_send_wqes = 0, act_nr_receive_wqes = 0;
+	u8 act_nr_send_sges = 0, act_nr_receive_sges = 0;
+	u32 qp_nr = 0,
+		nr_sq_pages = 0, swqe_size = 0, rwqe_size = 0, nr_rq_pages = 0;
+	u8 daqp_completion;
+	u8 isdaqp;
+	EDEB_EN(7,"pd=%p init_attr=%p", pd, init_attr);
+
+	EHCA_CHECK_PD_P(pd);
+	EHCA_CHECK_ADR_P(init_attr);
+
+	if (init_attr->sq_sig_type != IB_SIGNAL_REQ_WR &&
+	    init_attr->sq_sig_type != IB_SIGNAL_ALL_WR) {
+		EDEB_ERR(4, "init_attr->sg_sig_type=%x not allowed", 
+			 init_attr->sq_sig_type);
+		return ERR_PTR(-EINVAL);
+	}
+
+	/* save daqp completion bits */
+	daqp_completion = init_attr->qp_type & 0x60;
+	/* save daqp bit */
+	isdaqp = (init_attr->qp_type & 0x80) ? 1 : 0;
+	init_attr->qp_type = init_attr->qp_type & 0x1F;
+
+	if (init_attr->qp_type != IB_QPT_UD &&
+	    init_attr->qp_type != IB_QPT_SMI &&
+	    init_attr->qp_type != IB_QPT_GSI &&
+	    init_attr->qp_type != IB_QPT_UC &&
+	    init_attr->qp_type != IB_QPT_RC) {
+		EDEB_ERR(4,"wrong QP Type=%x",init_attr->qp_type);
+		return ERR_PTR(-EINVAL);
+	}
+	if (init_attr->qp_type!=IB_QPT_RC && isdaqp!=0) {
+		EDEB_ERR(4,"unsupported LL QP Type=%x",init_attr->qp_type);
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (pd->uobject && udata != NULL) {
+		context = pd->uobject->context;
+	}
+	if (context != NULL) {
+		if (ib_copy_from_udata(&ucmd, udata, sizeof ucmd)) {
+			EDEB_ERR(4, "copy_from_user() failed udata=%p ", 
+				 udata);
+			return ERR_PTR(-EFAULT);
+		}
+	}
+
+	my_qp = ehca_qp_new();
+	if (!my_qp) {
+		EDEB_ERR(4, "pd=%p not enough memory to alloc qp", pd);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	my_pd = container_of(pd, struct ehca_pd, ib_pd);
+
+	shca = container_of(pd->device, struct ehca_shca, ib_device);
+	recv_ehca_cq = container_of(init_attr->recv_cq, struct ehca_cq, ib_cq);
+	send_ehca_cq = container_of(init_attr->send_cq, struct ehca_cq, ib_cq);
+
+	my_qp->init_attr = *init_attr;
+
+	do {
+		if (!idr_pre_get(&ehca_qp_idr, GFP_KERNEL)) {
+			ret = -ENOMEM;
+			EDEB_ERR(4, "Can't reserve idr resources.");
+			goto create_qp_exit0;
+		}
+
+		down_write(&ehca_qp_idr_sem);
+		ret = idr_get_new(&ehca_qp_idr, my_qp, &my_qp->token);
+		up_write(&ehca_qp_idr_sem);
+
+	} while (ret == -EAGAIN);
+
+	if (ret) {
+		ret = -ENOMEM;
+		EDEB_ERR(4, "Can't allocate new idr entry.");
+		goto create_qp_exit0;
+	}
+
+	servicetype = ibqptype2servicetype(init_attr->qp_type);
+	if (servicetype < 0) {
+		ret = -EINVAL;
+		EDEB_ERR(4, "Invalid qp_type=%x", init_attr->qp_type);
+		goto create_qp_exit0;
+	}
+
+	if (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR) {
+		sigtype = HCALL_SIGT_EVERY;
+	} else {
+		sigtype = HCALL_SIGT_BY_WQE;
+	}
+
+	/* UD_AV CIRCUMVENTION */
+	max_send_sge=init_attr->cap.max_send_sge;
+	max_recv_sge=init_attr->cap.max_recv_sge;
+	if (IB_QPT_UD == init_attr->qp_type ||
+	    IB_QPT_GSI == init_attr->qp_type ||
+	    IB_QPT_SMI == init_attr->qp_type) {
+		max_send_sge += 2;
+		max_recv_sge += 2;
+	}
+
+	EDEB(7, "isdaqp=%x daqp_completion=%x", isdaqp, daqp_completion);
+
+	hipz_rc = hipz_h_alloc_resource_qp(shca->ipz_hca_handle, 
+					   &my_qp->pf,
+					   servicetype, 
+					   isdaqp | daqp_completion,
+					   sigtype, 0, /* no ud ad lkey ctrl */
+					   send_ehca_cq->ipz_cq_handle, 
+					   recv_ehca_cq->ipz_cq_handle, 
+					   shca->eq.ipz_eq_handle, 
+					   my_qp->token, 
+					   my_pd->fw_pd, 
+					   (u16) init_attr->cap.max_send_wr + 1, /* fixme(+1 ??) */
+					   (u16) init_attr->cap.max_recv_wr + 1, /* fixme(+1 ??) */
+					   (u8) max_send_sge,
+					   (u8) max_recv_sge,
+					   0, /* ignored if ud ad lkey ctrl is 0 */
+					   &my_qp->ipz_qp_handle,
+					   &qp_nr,
+					   &act_nr_send_wqes,
+					   &act_nr_receive_wqes,
+					   &act_nr_send_sges,
+					   &act_nr_receive_sges,
+					   &nr_sq_pages,
+					   &nr_rq_pages,
+					   &my_qp->ehca_qp_core.galpas);
+	if (hipz_rc != H_Success) {
+		EDEB_ERR(4, "h_alloc_resource_qp() failed rc=%lx", hipz_rc);
+		ret = ehca2ib_return_code(hipz_rc);
+		goto create_qp_exit1;
+	}
+
+	/* store real qp_num as we got from ehca */
+	my_qp->ehca_qp_core.real_qp_num = qp_nr;
+
+	switch (init_attr->qp_type) {
+	case IB_QPT_RC:
+	        if (isdaqp==0) {
+		        swqe_size = offsetof(struct ehca_wqe,
+					     u.nud.sg_list[(act_nr_send_sges)]);
+			rwqe_size = offsetof(struct ehca_wqe,
+					     u.nud.sg_list[(act_nr_receive_sges)]);
+		} else { /* for daqp we need to use msg size, not wqe size */
+		        swqe_size = da_msg_size[max_send_sge];
+			rwqe_size = da_msg_size[max_recv_sge];
+			act_nr_send_sges=1;
+			act_nr_receive_sges=1;
+		}
+		break;
+	case IB_QPT_UC:
+		swqe_size = offsetof(struct ehca_wqe,
+				     u.nud.sg_list[(act_nr_send_sges)]);
+		rwqe_size = offsetof(struct ehca_wqe,
+				     u.nud.sg_list[(act_nr_receive_sges)]);
+		break;
+
+	case IB_QPT_UD:
+	case IB_QPT_GSI:
+	case IB_QPT_SMI:
+		/* UD circumvention */
+	        act_nr_receive_sges -= 2;
+		act_nr_send_sges -= 2;
+		swqe_size = offsetof(struct ehca_wqe,
+				     u.ud_av.sg_list[(act_nr_send_sges)]);
+		rwqe_size = offsetof(struct ehca_wqe,
+				     u.ud_av.sg_list[(act_nr_receive_sges)]);
+
+		if (IB_QPT_GSI == init_attr->qp_type ||
+		    IB_QPT_SMI == init_attr->qp_type) {
+			act_nr_send_wqes = init_attr->cap.max_send_wr;
+			act_nr_receive_wqes = init_attr->cap.max_recv_wr;
+			act_nr_send_sges = init_attr->cap.max_send_sge;
+			act_nr_receive_sges = init_attr->cap.max_recv_sge;
+			qp_nr = (init_attr->qp_type == IB_QPT_SMI) ? 0 : 1;
+		}
+
+		break;
+
+	default:
+		break;
+	}
+
+	/* initializes r/squeue and registers queue pages */
+	ret = init_qp_queues(shca->ipz_hca_handle, my_qp,
+			     nr_sq_pages, nr_rq_pages,
+			     swqe_size, rwqe_size,
+			     act_nr_send_sges, act_nr_receive_sges);
+	if (ret != 0) {
+		EDEB_ERR(4,"Couldn't initialize r/squeue and pages ret=%x",
+			 ret);
+		goto create_qp_exit2;
+	}
+
+	my_qp->ib_qp.pd = &my_pd->ib_pd;
+	my_qp->ib_qp.device = my_pd->ib_pd.device;
+
+	my_qp->ib_qp.recv_cq = init_attr->recv_cq;
+	my_qp->ib_qp.send_cq = init_attr->send_cq;
+
+	my_qp->ib_qp.qp_num = qp_nr;
+	my_qp->ib_qp.qp_type = init_attr->qp_type;
+
+	my_qp->ehca_qp_core.qp_type = init_attr->qp_type;
+	my_qp->ib_qp.srq = init_attr->srq;
+
+	my_qp->ib_qp.qp_context = init_attr->qp_context;
+	my_qp->ib_qp.event_handler = init_attr->event_handler;
+
+	init_attr->cap.max_inline_data = 0; /* not supported? */
+	init_attr->cap.max_recv_sge = act_nr_receive_sges;
+	init_attr->cap.max_recv_wr = act_nr_receive_wqes;
+	init_attr->cap.max_send_sge = act_nr_send_sges;
+	init_attr->cap.max_send_wr = act_nr_send_wqes;
+
+	/* TODO : define_apq0() not supported yet */
+	if (init_attr->qp_type == IB_QPT_GSI) {
+		if ((hipz_rc = ehca_define_sqp(shca, my_qp, init_attr))) {
+			EDEB_ERR(4,  "ehca_define_sqp() failed rc=%lx", hipz_rc);
+			ret = ehca2ib_return_code(hipz_rc);
+			goto create_qp_exit3;
+		}
+	}
+
+	if (init_attr->send_cq!=NULL) {
+		struct ehca_cq *cq = container_of(init_attr->send_cq, 
+						  struct ehca_cq, ib_cq);
+		ret = ehca_cq_assign_qp(cq, my_qp);
+		if (ret !=0) {
+			EDEB_ERR(4, "Couldn't assign qp to send_cq ret=%x", ret);
+			goto create_qp_exit3;
+		}
+		my_qp->send_cq = cq;
+	}
+
+	/* copy queues, galpa data to user space */
+	if (context != NULL && udata != NULL) {
+		struct ehca_create_qp_resp resp;
+		struct vm_area_struct * vma;
+		resp.qp_num = qp_nr;
+		resp.token = my_qp->token;
+		resp.ehca_qp_core = my_qp->ehca_qp_core;
+
+		ehca_mmap_nopage(((u64) (my_qp->token) << 32) | 0x22000000,
+				 my_qp->ehca_qp_core.ipz_rqueue.queue_length,
+				 ((void**)&resp.ehca_qp_core.ipz_rqueue.queue),
+				 &vma);
+		my_qp->uspace_rqueue = (u64)resp.ehca_qp_core.ipz_rqueue.queue;
+		ehca_mmap_nopage(((u64) (my_qp->token) << 32) | 0x23000000,
+				 my_qp->ehca_qp_core.ipz_squeue.queue_length,
+				 ((void**)&resp.ehca_qp_core.ipz_squeue.queue),
+				 &vma);
+		my_qp->uspace_squeue = (u64)resp.ehca_qp_core.ipz_squeue.queue;
+		ehca_mmap_register(my_qp->ehca_qp_core.galpas.user.fw_handle,
+				   ((void**)&resp.ehca_qp_core.galpas.kernel.fw_handle),
+				   &vma);
+		my_qp->uspace_fwh = (u64)resp.ehca_qp_core.galpas.kernel.fw_handle;
+
+
+		if (copy_to_user
+		    ((u64 __user *) (unsigned long)ucmd.respbuf, &resp,
+		     sizeof(resp))) {
+			EDEB_ERR(4, "copy_to_user() failed");
+			ret = -EINVAL;
+			goto create_qp_exit3;
+		}
+	}
+
+	EDEB_EX(7, "ehca_qp=%p qp_num=%x, token=%x", 
+		my_qp, qp_nr, my_qp->token);
+	return (&my_qp->ib_qp);
+
+ create_qp_exit3:
+	ipz_queue_dtor(&my_qp->ehca_qp_core.ipz_rqueue);
+	ipz_queue_dtor(&my_qp->ehca_qp_core.ipz_squeue);
+
+ create_qp_exit2:
+	hipz_h_destroy_qp(shca->ipz_hca_handle, my_qp);
+
+ create_qp_exit1:
+	down_write(&ehca_qp_idr_sem);
+	idr_remove(&ehca_qp_idr, my_qp->token);
+	up_write(&ehca_qp_idr_sem);
+
+ create_qp_exit0:
+	ehca_qp_delete(my_qp);
+	EDEB_EX(4, "failed ret=%x", ret);
+	return ERR_PTR(ret);
+
+}
+
+/** called by internal_modify_qp() at trans sqe -> rts:
+ * set purge bit of bad wqe and subsequent wqes to avoid reentering sqe
+ * @return total number of bad wqes in bad_wqe_cnt
+ */
+static int prepare_sqe_rts(struct ehca_qp *my_qp, struct ehca_shca *shca, 
+			   int *bad_wqe_cnt)
+{
+	int ret = 0;
+	u64 hipz_rc = H_Success;
+	struct ipz_queue *squeue = NULL;
+	void *bad_send_wqe_p = NULL;
+	void *bad_send_wqe_v = NULL;
+	void *squeue_start_p = NULL;
+	void *squeue_end_p = NULL;
+	void *squeue_start_v = NULL;
+	void *squeue_end_v = NULL;
+	struct ehca_wqe *wqe = NULL;
+	int qp_num = my_qp->ib_qp.qp_num;
+	
+	EDEB_EN(7, "ehca_qp=%p qp_num=%x ", my_qp, qp_num);
+
+	/* get send wqe pointer */
+	hipz_rc = hipz_h_disable_and_get_wqe(shca->ipz_hca_handle,
+					     my_qp->ipz_qp_handle, &my_qp->pf,
+					     &bad_send_wqe_p, NULL, 2);
+	if (hipz_rc != H_Success) {
+		EDEB_ERR(4, "hipz_h_disable_and_get_wqe() failed "
+			 "ehca_qp=%p qp_num=%x hipz_rc=%lx",
+			 my_qp, qp_num, hipz_rc);
+		ret = ehca2ib_return_code(hipz_rc);
+		goto prepare_sqe_rts_exit1;
+	}
+	bad_send_wqe_p = (void*)((u64)bad_send_wqe_p & (~(1L<<63)));
+	EDEB(7, "qp_num=%x bad_send_wqe_p=%p", qp_num, bad_send_wqe_p);
+	/* convert wqe pointer to vadr */
+	bad_send_wqe_v = abs_to_virt((u64)bad_send_wqe_p);
+	EDEB_DMP(6, bad_send_wqe_v, 32, "qp_num=%x bad_wqe", qp_num);
+
+	squeue = &my_qp->ehca_qp_core.ipz_squeue;
+	squeue_start_p = (void*)ehca_kv_to_g(squeue->queue);
+	squeue_end_p = squeue_start_p+squeue->queue_length;
+	squeue_start_v = abs_to_virt((u64)squeue_start_p);
+	squeue_end_v = abs_to_virt((u64)squeue_end_p);
+	EDEB(6, "qp_num=%x squeue_start_v=%p squeue_end_v=%p", 
+	     qp_num, squeue_start_v, squeue_end_v);
+
+	/* loop sets wqe's purge bit */
+	wqe = (struct ehca_wqe*)bad_send_wqe_v;
+	*bad_wqe_cnt = 0;
+	while (wqe->optype!=0xff && wqe->wqef!=0xff) {
+		EDEB_DMP(6, wqe, 32, "qp_num=%x wqe", qp_num);
+		wqe->nr_of_data_seg = 0; /* suppress data access */
+		wqe->wqef = WQEF_PURGE; /* WQE to be purged */
+		wqe = (struct ehca_wqe*)((u8*)wqe+squeue->qe_size);
+		*bad_wqe_cnt = (*bad_wqe_cnt)+1;
+		if ((void*)wqe>=squeue_end_v) {
+			wqe = squeue_start_v;
+		}
+	} /* eof while wqe */
+	/* bad wqe will be reprocessed and ignored when pol_cq() is called,
+	   i.e. nr of wqes with flush error status is one less */
+	EDEB(6, "qp_num=%x flusherr_wqe_cnt=%x", qp_num, (*bad_wqe_cnt)-1);
+	wqe->wqef = 0;
+	
+ prepare_sqe_rts_exit1:
+
+	EDEB_EX(7, "ehca_qp=%p qp_num=%x ret=%x", my_qp, qp_num, ret);
+	return ret;
+}
+
+/** @brief internal modify qp with circumvention to handle aqp0 properly
+ * smi_reset2init indicates if this is an internal reset-to-init-call for
+ * smi. This flag must always be zero if called from ehca_modify_qp()!
+ * This internal func was intorduced to avoid recursion of ehca_modify_qp()!
+ */
+static int internal_modify_qp(struct ib_qp *ibqp,
+			      struct ib_qp_attr *attr,
+			      int attr_mask, int smi_reset2init)
+{
+	enum ib_qp_state qp_cur_state = 0, qp_new_state = 0;
+	int req_qp_attr_mask = 0,
+		opt_qp_attr_mask = 0, cnt = 0, qp_attr_idx = 0, retcode = 0;
+
+	struct ehca_modqp_statetrans qp_state_xsit={.statetrans=0};
+	struct hcp_modify_qp_control_block *mqpcb = NULL;
+	struct ehca_qp *my_qp = NULL;
+	struct ehca_shca *shca = NULL;
+	u64 update_mask = 0;
+	u64 hipz_rc = H_Success;
+	int bad_wqe_cnt = 0;
+	int squeue_locked = 0;
+	unsigned long spin_flags = 0;
+
+	my_qp = container_of(ibqp, struct ehca_qp, ib_qp);
+	shca = container_of(ibqp->pd->device, struct ehca_shca, ib_device);
+
+	EDEB_EN(7, "ehca_qp=%p qp_num=%x ibqp_type=%x "
+		"new qp_state=%x attribute_mask=%x",
+		my_qp, ibqp->qp_num, ibqp->qp_type, 
+		attr->qp_state, attr_mask);
+
+	/* do query_qp to obtain current attr values */
+	mqpcb = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (mqpcb == NULL) {
+		retcode = -ENOMEM;
+		EDEB_ERR(4, "Could not get zeroed page for mqpcb "
+			 "ehca_qp=%p qp_num=%x ", my_qp, ibqp->qp_num);
+		goto modify_qp_exit0;
+	}
+	memset(mqpcb, 0, PAGE_SIZE);
+
+	hipz_rc = hipz_h_query_qp(shca->ipz_hca_handle,
+                                  my_qp->ipz_qp_handle,
+				  &my_qp->pf,
+				  mqpcb, my_qp->ehca_qp_core.galpas.kernel);
+	if (hipz_rc != H_Success) {
+		EDEB_ERR(4, "hipz_h_query_qp() failed "
+			 "ehca_qp=%p qp_num=%x hipz_rc=%lx",
+			 my_qp, ibqp->qp_num, hipz_rc);
+		retcode = ehca2ib_return_code(hipz_rc);
+		goto modify_qp_exit1;
+	}
+	EDEB(7, "ehca_qp=%p qp_num=%x ehca_qp_state=%x",
+	     my_qp, ibqp->qp_num, mqpcb->qp_state);
+
+	qp_cur_state = ehca2ib_qp_state(mqpcb->qp_state);
+
+	if (qp_cur_state == -EINVAL) {	/* invalid qp state */
+		retcode = -EINVAL;
+		EDEB_ERR(4, "Invalid current ehca_qp_state=%x "
+			 "ehca_qp=%p qp_num=%x",
+			 mqpcb->qp_state, my_qp, ibqp->qp_num);
+		goto modify_qp_exit1;
+	}
+	/* circumvention to set aqp0 initial state to init 
+	   as expected by IB spec */
+	if (smi_reset2init == 0 && 
+	    ibqp->qp_type == IB_QPT_SMI && 
+	    qp_cur_state == IB_QPS_RESET && 
+	    (attr_mask & IB_QP_STATE)
+	    && attr->qp_state == IB_QPS_INIT) { /* RESET -> INIT */
+		int port = my_qp->init_attr.port_num;
+		struct ib_qp_attr smiqp_attr = {
+			.qp_state = IB_QPS_INIT,
+			.pkey_index = 0,
+			.port_num = port,
+			.qkey = 0
+		};
+		int smiqp_attr_mask = IB_QP_STATE |
+			IB_QP_PKEY_INDEX | IB_QP_PORT | IB_QP_QKEY;
+		int smirc = internal_modify_qp(
+			ibqp, &smiqp_attr, smiqp_attr_mask, 1);
+		if (smirc != 0) {
+			EDEB_ERR(4, "SMI RESET -> INIT failed. "
+				 "ehca_modify_qp() rc=%x", smirc);
+			retcode = H_Parameter;
+			goto modify_qp_exit1;
+		}
+		qp_cur_state = IB_QPS_INIT;
+		EDEB(7, "SMI RESET -> INIT succeeded");
+	}
+	/* is transmitted current state  equal to "real" current state */
+	if (attr_mask & IB_QP_CUR_STATE) {
+		if (qp_cur_state != attr->cur_qp_state) {
+			retcode = -EINVAL;
+			EDEB_ERR(4, "Invalid IB_QP_CUR_STATE "
+				 "attr->curr_qp_state=%x <> "
+				 "actual cur_qp_state=%x. "
+				 "ehca_qp=%p qp_num=%x",
+				 attr->cur_qp_state, qp_cur_state,
+				 my_qp, ibqp->qp_num);
+			goto modify_qp_exit1;
+		}
+	}
+
+	EDEB(7,	"ehca_qp=%p qp_num=%x current qp_state=%x "
+	     "new qp_state=%x attribute_mask=%x",
+	     my_qp, ibqp->qp_num, qp_cur_state, attr->qp_state, attr_mask);
+
+	if (attr_mask & IB_QP_STATE) {
+		if (attr->qp_state < IB_QPS_RESET ||
+		    attr->qp_state > IB_QPS_ERR) {
+			retcode = -EINVAL;
+			EDEB_ERR(4, "Invalid new qp state attr->qp_state=%x "
+				 "ehca_qp=%p qp_num=%x",
+				 attr->qp_state, my_qp, ibqp->qp_num);
+			goto modify_qp_exit1;
+		}
+		qp_new_state = attr->qp_state;
+	} else
+		qp_new_state = qp_cur_state;
+
+	if ((mqpcb->qp_state = ib2ehca_qp_state(qp_new_state))) {
+		update_mask = EHCA_BMASK_SET(MQPCB_MASK_QP_STATE, 1);
+	} else {
+		retcode = -EINVAL;
+		EDEB_ERR(4, "Invalid new qp state=%x "
+			 "ehca_qp=%p qp_num=%x",
+			 qp_new_state, my_qp, ibqp->qp_num);
+		goto modify_qp_exit1;
+	}
+
+	/* retrieve state transition struct to get req and opt attrs */
+	if (get_modqp_statetrans(qp_cur_state,
+				 qp_new_state, &qp_state_xsit) < 0) {
+		retcode = -EINVAL;
+		EDEB_ERR(4, "<INVALID STATE CHANGE> qp_cur_state=%x "
+			 "new_qp_state=%x State_xsition=%x "
+			 "ehca_qp=%p qp_num=%x",
+			 qp_cur_state, qp_new_state,
+			 qp_state_xsit.statetrans, my_qp, ibqp->qp_num);
+		goto modify_qp_exit1;
+	}
+
+	qp_attr_idx = ib2ehcaqptype(ibqp->qp_type);
+
+	if (qp_attr_idx < 0) {
+		retcode = qp_attr_idx;
+		EDEB_ERR(4, "Invalid QP type=%x ehca_qp=%p qp_num=%x",
+			 ibqp->qp_type, my_qp, ibqp->qp_num);
+		goto modify_qp_exit1;
+	}
+
+	req_qp_attr_mask = qp_state_xsit.req_attr[qp_attr_idx];
+	opt_qp_attr_mask = qp_state_xsit.opt_attr[qp_attr_idx];
+
+	if ((attr_mask & req_qp_attr_mask) != req_qp_attr_mask) {
+		retcode = -EINVAL;
+		EDEB_ERR(4, "<INVALID required MASK> ehca_qp=%p qp_num=%x "
+			 "req_mask=%x opt_mask=%x submitted_mask=%x "
+			 "qp_type=%x",
+			 my_qp, ibqp->qp_num,
+			 req_qp_attr_mask, opt_qp_attr_mask,
+			 attr_mask, ibqp->qp_type);
+		goto modify_qp_exit1;
+	} else if ((attr_mask & ~(req_qp_attr_mask | opt_qp_attr_mask))) {
+		EDEB(7, "<optional MASK> more attributes "
+		     "specified than allowed!!! req_mask=%x opt_mask=%x "
+		     "submitted_mask=%x",
+		     req_qp_attr_mask, opt_qp_attr_mask, attr_mask);
+	}
+
+	EDEB(7, "ehca_qp=%p qp_num=%x <VALID STATE CHANGE> "
+	     "req_mask=%x opt_mask=%x qp_state_xsit=%x ",
+	     my_qp, ibqp->qp_num, req_qp_attr_mask, 
+	     opt_qp_attr_mask, qp_state_xsit.statetrans);
+
+	/* sqe -> rts: set purge bit of bad wqe before actual trans */
+	if ((my_qp->ehca_qp_core.qp_type== IB_QPT_UD
+	     || my_qp->ehca_qp_core.qp_type== IB_QPT_GSI
+	     || my_qp->ehca_qp_core.qp_type== IB_QPT_SMI)
+	    && qp_state_xsit.statetrans == IB_QPST_SQE2RTS) {
+		/* mark next free wqe if kernel */
+		if (my_qp->uspace_squeue==0) {
+			struct ehca_wqe *wqe = NULL;
+			/* lock send queue */
+			spin_lock_irqsave(&my_qp->spinlock_s, spin_flags);
+			squeue_locked = 1;
+			/* mark next free wqe */
+			wqe=(struct ehca_wqe*)
+				my_qp->ehca_qp_core.ipz_squeue.current_q_addr;
+			wqe->optype = wqe->wqef = 0xff;
+			EDEB(7, "qp_num=%x next_free_wqe=%p", 
+			     ibqp->qp_num, wqe);
+		}
+		retcode = prepare_sqe_rts(my_qp, shca, &bad_wqe_cnt);
+		if (retcode != 0) {
+			EDEB_ERR(4, "prepare_sqe_rts() failed "
+				 "ehca_qp=%p qp_num=%x ret=%x",
+				 my_qp, ibqp->qp_num, retcode);
+			goto modify_qp_exit2;
+		}
+	}
+
+	/* enable RDMA_Atomic_Control if reset->init und reliable con
+	   this is necessary since gen2 does not provide that flag,
+	   but pHyp requires it */
+	if (qp_state_xsit.statetrans == IB_QPST_RESET2INIT &&
+	    (ibqp->qp_type == IB_QPT_RC || ibqp->qp_type == IB_QPT_UC)) {
+		mqpcb->rdma_atomic_ctrl = 3;
+		update_mask |= EHCA_BMASK_SET(MQPCB_MASK_RDMA_ATOMIC_CTRL, 1);
+	}
+	/* circ. pHyp requires #RDMA/Atomic Responder Resources for UC INIT -> RTR */
+	if (qp_state_xsit.statetrans == IB_QPST_INIT2RTR &&
+	    (ibqp->qp_type == IB_QPT_UC) &&
+	    !(attr_mask & IB_QP_MAX_DEST_RD_ATOMIC)) {
+		mqpcb->rdma_nr_atomic_resp_res = 1; /* default to 1 */
+		update_mask |=
+			EHCA_BMASK_SET(MQPCB_MASK_RDMA_NR_ATOMIC_RESP_RES, 1);
+	}
+
+	if (attr_mask & IB_QP_PKEY_INDEX) {
+		mqpcb->prim_p_key_idx = attr->pkey_index;
+		update_mask |= EHCA_BMASK_SET(MQPCB_MASK_PRIM_P_KEY_IDX, 1);
+		EDEB(7, "ehca_qp=%p qp_num=%x "
+		     "IB_QP_PKEY_INDEX update_mask=%lx",
+		     my_qp, ibqp->qp_num, update_mask);
+	}
+	if (attr_mask & IB_QP_PORT) {
+		mqpcb->prim_phys_port = attr->port_num;
+		update_mask |= EHCA_BMASK_SET(MQPCB_MASK_PRIM_PHYS_PORT, 1);
+		EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_PORT update_mask=%lx",
+		     my_qp, ibqp->qp_num, update_mask);
+	}
+	if (attr_mask & IB_QP_QKEY) {
+		mqpcb->qkey = attr->qkey;
+		update_mask |= EHCA_BMASK_SET(MQPCB_MASK_QKEY, 1);
+		EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_QKEY update_mask=%lx",
+		     my_qp, ibqp->qp_num, update_mask);
+	}
+	if (attr_mask & IB_QP_AV) {
+		mqpcb->dlid = attr->ah_attr.dlid;
+		update_mask |= EHCA_BMASK_SET(MQPCB_MASK_DLID, 1);
+		mqpcb->source_path_bits = attr->ah_attr.src_path_bits;
+		update_mask |= EHCA_BMASK_SET(MQPCB_MASK_SOURCE_PATH_BITS, 1);
+		mqpcb->service_level = attr->ah_attr.sl;
+		update_mask |= EHCA_BMASK_SET(MQPCB_MASK_SERVICE_LEVEL, 1);
+		mqpcb->max_static_rate = attr->ah_attr.static_rate;
+		update_mask |= EHCA_BMASK_SET(MQPCB_MASK_MAX_STATIC_RATE, 1);
+
+		/* only if GRH is TRUE we might consider SOURCE_GID_IDX and DEST_GID
+		 * otherwise phype will return H_ATTR_PARM!!!
+		 */
+		if (attr->ah_attr.ah_flags == IB_AH_GRH) {
+			mqpcb->send_grh_flag = 1 << 31;
+			update_mask |=
+				EHCA_BMASK_SET(MQPCB_MASK_SEND_GRH_FLAG, 1);
+			mqpcb->source_gid_idx = attr->ah_attr.grh.sgid_index;
+			update_mask |=
+				EHCA_BMASK_SET(MQPCB_MASK_SOURCE_GID_IDX, 1);
+
+			for (cnt = 0; cnt < 16; cnt++) {
+				mqpcb->dest_gid.byte[cnt] =
+					attr->ah_attr.grh.dgid.raw[cnt];
+			}
+
+			update_mask |= EHCA_BMASK_SET(MQPCB_MASK_DEST_GID, 1);
+			mqpcb->flow_label = attr->ah_attr.grh.flow_label;
+			update_mask |= EHCA_BMASK_SET(MQPCB_MASK_FLOW_LABEL, 1);
+			mqpcb->hop_limit = attr->ah_attr.grh.hop_limit;
+			update_mask |= EHCA_BMASK_SET(MQPCB_MASK_HOP_LIMIT, 1);
+			mqpcb->traffic_class = attr->ah_attr.grh.traffic_class;
+			update_mask |=
+				EHCA_BMASK_SET(MQPCB_MASK_TRAFFIC_CLASS, 1);
+		}
+
+		EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_AV update_mask=%lx",
+		     my_qp, ibqp->qp_num, update_mask);
+	}
+
+	if (attr_mask & IB_QP_PATH_MTU) {
+		mqpcb->path_mtu = attr->path_mtu;
+		update_mask |= EHCA_BMASK_SET(MQPCB_MASK_PATH_MTU, 1);
+		EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_PATH_MTU update_mask=%lx",
+		     my_qp, ibqp->qp_num, update_mask);
+	}
+	if (attr_mask & IB_QP_TIMEOUT) {
+		mqpcb->timeout = attr->timeout;
+		update_mask |= EHCA_BMASK_SET(MQPCB_MASK_TIMEOUT, 1);
+		EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_TIMEOUT update_mask=%lx",
+		     my_qp, ibqp->qp_num, update_mask);
+	}
+	if (attr_mask & IB_QP_RETRY_CNT) {
+		mqpcb->retry_count = attr->retry_cnt;
+		update_mask |= EHCA_BMASK_SET(MQPCB_MASK_RETRY_COUNT, 1);
+		EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_RETRY_CNT update_mask=%lx",
+		     my_qp, ibqp->qp_num, update_mask);
+	}
+	if (attr_mask & IB_QP_RNR_RETRY) {
+		mqpcb->rnr_retry_count = attr->rnr_retry;
+		update_mask |= EHCA_BMASK_SET(MQPCB_MASK_RNR_RETRY_COUNT, 1);
+		EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_RNR_RETRY update_mask=%lx",
+		     my_qp, ibqp->qp_num, update_mask);
+	}
+	if (attr_mask & IB_QP_RQ_PSN) {
+		mqpcb->receive_psn = attr->rq_psn;
+		update_mask |= EHCA_BMASK_SET(MQPCB_MASK_RECEIVE_PSN, 1);
+		EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_RQ_PSN update_mask=%lx",
+		     my_qp, ibqp->qp_num, update_mask);
+	}
+	if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) {
+	        /*  @TODO CHECK THIS with our spec */
+		mqpcb->rdma_nr_atomic_resp_res = attr->max_dest_rd_atomic;
+		update_mask |=
+			EHCA_BMASK_SET(MQPCB_MASK_RDMA_NR_ATOMIC_RESP_RES, 1);
+		EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_MAX_DEST_RD_ATOMIC "
+		     "update_mask=%lx", my_qp, ibqp->qp_num, update_mask);
+	}
+	if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC) {
+	        /*  @TODO CHECK THIS with our spec */
+		mqpcb->rdma_atomic_outst_dest_qp = attr->max_rd_atomic;	
+		update_mask |=
+			EHCA_BMASK_SET
+			(MQPCB_MASK_RDMA_ATOMIC_OUTST_DEST_QP, 1);
+		EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_MAX_QP_RD_ATOMIC "
+		     "update_mask=%lx", my_qp, ibqp->qp_num, update_mask);
+	}
+	if (attr_mask & IB_QP_ALT_PATH) {
+		mqpcb->dlid_al = attr->alt_ah_attr.dlid;
+		update_mask |= EHCA_BMASK_SET(MQPCB_MASK_DLID_AL, 1);
+		mqpcb->source_path_bits_al = attr->alt_ah_attr.src_path_bits;
+		update_mask |=
+			EHCA_BMASK_SET(MQPCB_MASK_SOURCE_PATH_BITS_AL, 1);
+		mqpcb->service_level_al = attr->alt_ah_attr.sl;
+		update_mask |= EHCA_BMASK_SET(MQPCB_MASK_SERVICE_LEVEL_AL, 1);
+		mqpcb->max_static_rate_al = attr->alt_ah_attr.static_rate;
+		update_mask |= EHCA_BMASK_SET(MQPCB_MASK_MAX_STATIC_RATE_AL, 1);
+
+		/* only if GRH is TRUE we might consider SOURCE_GID_IDX and DEST_GID
+		 * otherwise phype will return H_ATTR_PARM!!!
+		 */
+		if (attr->alt_ah_attr.ah_flags == IB_AH_GRH) {
+			mqpcb->send_grh_flag_al = 1 << 31;
+			update_mask |=
+				EHCA_BMASK_SET(MQPCB_MASK_SEND_GRH_FLAG_AL, 1);
+			mqpcb->source_gid_idx_al =
+				attr->alt_ah_attr.grh.sgid_index;
+			update_mask |=
+				EHCA_BMASK_SET(MQPCB_MASK_SOURCE_GID_IDX_AL, 1);
+
+			for (cnt = 0; cnt < 16; cnt++) {
+				mqpcb->dest_gid_al.byte[cnt] =
+					attr->alt_ah_attr.grh.dgid.raw[cnt];
+			}
+
+			update_mask |=
+				EHCA_BMASK_SET(MQPCB_MASK_DEST_GID_AL, 1);
+			mqpcb->flow_label_al = attr->alt_ah_attr.grh.flow_label;
+			update_mask |=
+				EHCA_BMASK_SET(MQPCB_MASK_FLOW_LABEL_AL, 1);
+			mqpcb->hop_limit_al = attr->alt_ah_attr.grh.hop_limit;
+			update_mask |=
+				EHCA_BMASK_SET(MQPCB_MASK_HOP_LIMIT_AL, 1);
+			mqpcb->traffic_class_al =
+				attr->alt_ah_attr.grh.traffic_class;
+			update_mask |=
+				EHCA_BMASK_SET(MQPCB_MASK_TRAFFIC_CLASS_AL, 1);
+		}
+
+		EDEB(7, "ehca_qp=%p qp_num=%x "
+		     "IB_QP_ALT_PATH update_mask=%lx",
+		     my_qp, ibqp->qp_num, update_mask);
+	}
+
+	if (attr_mask & IB_QP_MIN_RNR_TIMER) {
+		mqpcb->min_rnr_nak_timer_field = attr->min_rnr_timer;
+		update_mask |=
+			EHCA_BMASK_SET(MQPCB_MASK_MIN_RNR_NAK_TIMER_FIELD, 1);
+		EDEB(7, "ehca_qp=%p qp_num=%x "
+		     "IB_QP_MIN_RNR_TIMER update_mask=%lx",
+		     my_qp, ibqp->qp_num, update_mask);
+	}
+
+	if (attr_mask & IB_QP_SQ_PSN) {
+		mqpcb->send_psn = attr->sq_psn;
+		update_mask |= EHCA_BMASK_SET(MQPCB_MASK_SEND_PSN, 1);
+		EDEB(7, "ehca_qp=%p qp_num=%x "
+		     "IB_QP_SQ_PSN update_mask=%lx",
+		     my_qp, ibqp->qp_num, update_mask);
+	}
+
+	if (attr_mask & IB_QP_DEST_QPN) {
+		mqpcb->dest_qp_nr = attr->dest_qp_num;
+		update_mask |= EHCA_BMASK_SET(MQPCB_MASK_DEST_QP_NR, 1);
+		EDEB(7, "ehca_qp=%p qp_num=%x "
+		     "IB_QP_DEST_QPN update_mask=%lx",
+		     my_qp, ibqp->qp_num, update_mask);
+	}
+
+	if (attr_mask & IB_QP_PATH_MIG_STATE) {
+		mqpcb->path_migration_state = attr->path_mig_state;
+		update_mask |=
+			EHCA_BMASK_SET(MQPCB_MASK_PATH_MIGRATION_STATE, 1);
+		EDEB(7, "ehca_qp=%p qp_num=%x "
+		     "IB_QP_PATH_MIG_STATE update_mask=%lx", my_qp,
+		     ibqp->qp_num, update_mask);
+	}
+
+	if (attr_mask & IB_QP_CAP) {
+		mqpcb->max_nr_outst_send_wr = attr->cap.max_send_wr+1;
+		update_mask |=
+			EHCA_BMASK_SET(MQPCB_MASK_MAX_NR_OUTST_SEND_WR, 1);
+		mqpcb->max_nr_outst_recv_wr = attr->cap.max_recv_wr+1;
+		update_mask |=
+			EHCA_BMASK_SET(MQPCB_MASK_MAX_NR_OUTST_RECV_WR, 1);
+		EDEB(7, "ehca_qp=%p qp_num=%x "
+		     "IB_QP_CAP update_mask=%lx",
+		     my_qp, ibqp->qp_num, update_mask);
+		/* TODO no support for max_send/recv_sge??? */
+	}
+
+	EDEB_DMP(7, mqpcb, 4*70, "ehca_qp=%p qp_num=%x", my_qp, ibqp->qp_num);
+
+	hipz_rc = hipz_h_modify_qp(shca->ipz_hca_handle,
+				   my_qp->ipz_qp_handle,
+				   &my_qp->pf,
+				   update_mask,
+				   mqpcb, my_qp->ehca_qp_core.galpas.kernel);
+
+	if (hipz_rc != H_Success) {
+		retcode = ehca2ib_return_code(hipz_rc);
+		EDEB_ERR(4, "hipz_h_modify_qp() failed rc=%lx "
+			 "ehca_qp=%p qp_num=%x",
+			 hipz_rc, my_qp, ibqp->qp_num);
+		goto modify_qp_exit2;
+	}
+
+	if ((my_qp->ehca_qp_core.qp_type== IB_QPT_UD
+	     || my_qp->ehca_qp_core.qp_type== IB_QPT_GSI
+	     || my_qp->ehca_qp_core.qp_type== IB_QPT_SMI)
+	    && qp_state_xsit.statetrans == IB_QPST_SQE2RTS) {
+		/* doorbell to reprocessing wqes */
+		iosync(); /* serialize GAL register access */
+		hipz_update_SQA(&my_qp->ehca_qp_core, bad_wqe_cnt-1);
+		EDEB(6, "doorbell for %x wqes", bad_wqe_cnt);
+	}
+
+	if (qp_state_xsit.statetrans == IB_QPST_RESET2INIT ||
+	    qp_state_xsit.statetrans == IB_QPST_INIT2INIT) {
+		mqpcb->qp_enable = TRUE;
+		mqpcb->qp_state = EHCA_QPS_INIT;
+		update_mask = 0;
+		update_mask = EHCA_BMASK_SET(MQPCB_MASK_QP_ENABLE, 1);
+
+		EDEB(7, "ehca_qp=%p qp_num=%x "
+		     "RESET_2_INIT needs an additional enable "
+		     "-> update_mask=%lx", my_qp, ibqp->qp_num, update_mask);
+
+		hipz_rc = hipz_h_modify_qp(shca->ipz_hca_handle,
+					   my_qp->ipz_qp_handle,
+					   &my_qp->pf,
+					   update_mask,
+					   mqpcb,
+					   my_qp->ehca_qp_core.galpas.kernel);
+
+		if (hipz_rc != H_Success) {
+			retcode = ehca2ib_return_code(hipz_rc);
+			EDEB_ERR(4, "ENABLE in context of "
+				 "RESET_2_INIT failed! "
+				 "Maybe you didn't get a LID"
+				 "hipz_rc=%lx ehca_qp=%p qp_num=%x",
+				 hipz_rc, my_qp, ibqp->qp_num);
+			goto modify_qp_exit2;
+		}
+	}
+
+	if (qp_state_xsit.statetrans == IB_QPST_ANY2RESET) {
+		ipz_QEit_reset(&my_qp->ehca_qp_core.ipz_rqueue);
+		ipz_QEit_reset(&my_qp->ehca_qp_core.ipz_squeue);
+	}
+
+	if (attr_mask & IB_QP_QKEY) {
+		my_qp->ehca_qp_core.qkey = attr->qkey;
+	}
+
+ modify_qp_exit2:
+	if (squeue_locked) { /* this means: sqe -> rts */
+		spin_unlock_irqrestore(&my_qp->spinlock_s, spin_flags);
+		my_qp->sqerr_purgeflag = 1;
+	}
+
+ modify_qp_exit1:
+	kfree(mqpcb);
+
+ modify_qp_exit0:
+	EDEB_EX(7, "ehca_qp=%p qp_num=%x ibqp_type=%x retcode=%x",
+		my_qp, ibqp->qp_num, ibqp->qp_type, retcode);
+	return retcode;
+}
+
+int ehca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask)
+{
+	int ret = 0;
+	struct ehca_qp *my_qp = NULL;
+
+	EHCA_CHECK_ADR(ibqp);
+	EHCA_CHECK_ADR(attr);
+	EHCA_CHECK_ADR(ibqp->device);
+
+	my_qp = container_of(ibqp, struct ehca_qp, ib_qp);
+
+	EDEB_EN(7, "ehca_qp=%p qp_num=%x ibqp_type=%x attr_mask=%x",
+		my_qp, ibqp->qp_num, ibqp->qp_type, attr_mask);
+
+	ret = internal_modify_qp(ibqp, attr, attr_mask, 0);
+
+	EDEB_EX(7, "ehca_qp=%p qp_num=%x ibqp_type=%x ret=%x",
+		my_qp, ibqp->qp_num, ibqp->qp_type, ret);
+	return ret;
+}
+
+int ehca_query_qp(struct ib_qp *qp,
+		  struct ib_qp_attr *qp_attr,
+		  int qp_attr_mask, struct ib_qp_init_attr *qp_init_attr)
+{
+	struct ehca_qp *my_qp = NULL;
+	struct ehca_shca *shca = NULL;
+	struct hcp_modify_qp_control_block *qpcb = NULL;
+
+	struct ipz_adapter_handle adapter_handle;
+	int cnt = 0, retcode = 0;
+	u64 hipz_rc = H_Success;
+
+	EHCA_CHECK_ADR(qp);
+	EHCA_CHECK_ADR(qp_attr);
+	EHCA_CHECK_DEVICE(qp->device);
+
+	my_qp = container_of(qp, struct ehca_qp, ib_qp);
+
+	EDEB_EN(7, "ehca_qp=%p qp_num=%x "
+		"qp_attr=%p qp_attr_mask=%x qp_init_attr=%p",
+		my_qp, qp->qp_num, qp_attr, qp_attr_mask, qp_init_attr);
+
+	shca = container_of(qp->device, struct ehca_shca, ib_device);
+	adapter_handle = shca->ipz_hca_handle;
+
+	if (qp_attr_mask & QP_ATTR_QUERY_NOT_SUPPORTED) {
+		retcode = -EINVAL;
+		EDEB_ERR(4,"Invalid attribute mask "
+			 "ehca_qp=%p qp_num=%x qp_attr_mask=%x ",
+			 my_qp, qp->qp_num, qp_attr_mask);
+		goto query_qp_exit0;
+	}
+
+	qpcb = kmalloc(EHCA_PAGESIZE, GFP_KERNEL );
+
+	if (qpcb == NULL) {
+		retcode = -ENOMEM;
+		EDEB_ERR(4,"Out of memory for qpcb "
+			 "ehca_qp=%p qp_num=%x", my_qp, qp->qp_num);
+		goto query_qp_exit0;
+	}
+	memset(qpcb, 0, sizeof(*qpcb));
+
+	hipz_rc = hipz_h_query_qp(adapter_handle,
+				  my_qp->ipz_qp_handle,
+				  &my_qp->pf,
+				  qpcb, my_qp->ehca_qp_core.galpas.kernel);
+
+	if (hipz_rc != H_Success) {
+		retcode = ehca2ib_return_code(hipz_rc);
+		EDEB_ERR(4,"hipz_h_query_qp() failed "
+			 "ehca_qp=%p qp_num=%x hipz_rc=%lx",
+			 my_qp, qp->qp_num, hipz_rc);
+		goto query_qp_exit1;
+	}
+
+	qp_attr->cur_qp_state = ehca2ib_qp_state(qpcb->qp_state);
+	qp_attr->qp_state = qp_attr->cur_qp_state;
+	if (qp_attr->cur_qp_state == -EINVAL) {	
+		retcode = -EINVAL;
+		EDEB_ERR(4,"Got invalid ehca_qp_state=%x "
+			 "ehca_qp=%p qp_num=%x",
+			 qpcb->qp_state, my_qp, qp->qp_num);
+		goto query_qp_exit1;
+	}
+
+	if (qp_attr->qp_state == IB_QPS_SQD) {
+		qp_attr->sq_draining = TRUE;
+	}
+
+	qp_attr->qkey = qpcb->qkey;
+	qp_attr->path_mtu = qpcb->path_mtu;
+	qp_attr->path_mig_state = qpcb->path_migration_state;
+	qp_attr->rq_psn = qpcb->receive_psn;
+	qp_attr->sq_psn = qpcb->send_psn;
+	qp_attr->min_rnr_timer = qpcb->min_rnr_nak_timer_field;
+	qp_attr->cap.max_send_wr = qpcb->max_nr_outst_send_wr-1;
+	qp_attr->cap.max_recv_wr = qpcb->max_nr_outst_recv_wr-1;
+	/* UD_AV CIRCUMVENTION */
+	if (my_qp->ehca_qp_core.qp_type == IB_QPT_UD) {
+		qp_attr->cap.max_send_sge =
+			qpcb->actual_nr_sges_in_sq_wqe - 2;
+		qp_attr->cap.max_recv_sge =
+			qpcb->actual_nr_sges_in_rq_wqe - 2;
+	} else {
+		qp_attr->cap.max_send_sge =
+			qpcb->actual_nr_sges_in_sq_wqe;
+		qp_attr->cap.max_recv_sge =
+			qpcb->actual_nr_sges_in_rq_wqe;
+	}
+
+	qp_attr->cap.max_inline_data = my_qp->sq_max_inline_data_size;
+	qp_attr->dest_qp_num = qpcb->dest_qp_nr;
+
+	qp_attr->pkey_index =
+		EHCA_BMASK_GET(MQPCB_PRIM_P_KEY_IDX, qpcb->prim_p_key_idx);
+
+	qp_attr->port_num =
+		EHCA_BMASK_GET(MQPCB_PRIM_PHYS_PORT, qpcb->prim_phys_port);
+
+	qp_attr->timeout = qpcb->timeout;
+	qp_attr->retry_cnt = qpcb->retry_count;
+	qp_attr->rnr_retry = qpcb->rnr_retry_count;
+
+	qp_attr->alt_pkey_index =
+		EHCA_BMASK_GET(MQPCB_PRIM_P_KEY_IDX, qpcb->alt_p_key_idx);
+
+	qp_attr->alt_port_num = qpcb->alt_phys_port;
+	qp_attr->alt_timeout = qpcb->timeout_al;
+
+	/* primary av */
+	qp_attr->ah_attr.sl = qpcb->service_level;
+
+	if (qpcb->send_grh_flag) {
+		qp_attr->ah_attr.ah_flags = IB_AH_GRH;
+	}
+
+	qp_attr->ah_attr.static_rate = qpcb->max_static_rate;
+	qp_attr->ah_attr.dlid = qpcb->dlid;
+	qp_attr->ah_attr.src_path_bits = qpcb->source_path_bits;
+	qp_attr->ah_attr.port_num = qp_attr->port_num;
+
+	/* primary GRH */
+	qp_attr->ah_attr.grh.traffic_class = qpcb->traffic_class;
+	qp_attr->ah_attr.grh.hop_limit = qpcb->hop_limit;
+	qp_attr->ah_attr.grh.sgid_index = qpcb->source_gid_idx;
+	qp_attr->ah_attr.grh.flow_label = qpcb->flow_label;
+
+	for (cnt = 0; cnt < 16; cnt++) {
+		qp_attr->ah_attr.grh.dgid.raw[cnt] =
+			qpcb->dest_gid.byte[cnt];
+	}
+
+	/* alternate AV */
+	qp_attr->alt_ah_attr.sl = qpcb->service_level_al;
+	if (qpcb->send_grh_flag_al) {
+		qp_attr->alt_ah_attr.ah_flags = IB_AH_GRH;
+	}
+
+	qp_attr->alt_ah_attr.static_rate = qpcb->max_static_rate_al;
+	qp_attr->alt_ah_attr.dlid = qpcb->dlid_al;
+	qp_attr->alt_ah_attr.src_path_bits = qpcb->source_path_bits_al;
+
+	/* alternate GRH */
+	qp_attr->alt_ah_attr.grh.traffic_class = qpcb->traffic_class_al;
+	qp_attr->alt_ah_attr.grh.hop_limit = qpcb->hop_limit_al;
+	qp_attr->alt_ah_attr.grh.sgid_index = qpcb->source_gid_idx_al;
+	qp_attr->alt_ah_attr.grh.flow_label = qpcb->flow_label_al;
+
+	for (cnt = 0; cnt < 16; cnt++) {
+		qp_attr->alt_ah_attr.grh.dgid.raw[cnt] =
+			qpcb->dest_gid_al.byte[cnt];
+	}
+
+	/* return init attributes given in ehca_create_qp */
+	if (qp_init_attr != NULL) {
+		*qp_init_attr = my_qp->init_attr;
+	}
+
+	EDEB(7,	"ehca_qp=%p qp_number=%x dest_qp_number=%x "
+	     "dlid=%x path_mtu=%x dest_gid=%lx_%lx "
+	     "service_level=%x qp_state=%x",
+	     my_qp, qpcb->qp_number, qpcb->dest_qp_nr,
+	     qpcb->dlid, qpcb->path_mtu,
+	     qpcb->dest_gid.dw[0], qpcb->dest_gid.dw[1],
+	     qpcb->service_level, qpcb->qp_state);
+
+	EDEB_DMP(7, qpcb, 4*70, "ehca_qp=%p qp_num=%x", my_qp, qp->qp_num);
+
+ query_qp_exit1:
+	kfree(qpcb);
+
+ query_qp_exit0:
+	EDEB_EX(7, "ehca_qp=%p qp_num=%x retcode=%x",
+		my_qp, qp->qp_num, retcode);
+	return retcode;
+}
+
+int ehca_destroy_qp(struct ib_qp *ibqp)
+{
+	struct ehca_qp *my_qp = NULL;
+	struct ehca_shca *shca = NULL;
+	struct ehca_pfqp *qp_pf = NULL;
+	u32 qp_num = 0;
+	int retcode = 0;
+	u64 hipz_ret = H_Success;
+	u8 port_num = 0;
+	enum ib_qp_type	qp_type;
+
+	EHCA_CHECK_ADR(ibqp);
+
+	my_qp = container_of(ibqp, struct ehca_qp, ib_qp);
+	qp_num = ibqp->qp_num;
+	qp_pf = &my_qp->pf;
+
+	shca = container_of(ibqp->device, struct ehca_shca, ib_device);
+
+	EDEB_EN(7, "ehca_qp=%p qp_num=%x", my_qp, ibqp->qp_num);
+
+	if (my_qp->send_cq!=NULL) {
+		retcode = ehca_cq_unassign_qp(my_qp->send_cq, 
+					      my_qp->ehca_qp_core.real_qp_num);
+		if (retcode !=0) {
+			EDEB_ERR(4, "Couldn't unassign qp from send_cq "
+				 "ret=%x qp_num=%x cq_num=%x", 
+				 retcode, my_qp->ib_qp.qp_num, 
+				 my_qp->send_cq->cq_number);
+			goto destroy_qp_exit0;
+		}
+	}
+
+	down_write(&ehca_qp_idr_sem);
+	idr_remove(&ehca_qp_idr, my_qp->token);
+	up_write(&ehca_qp_idr_sem);
+
+	/* un-mmap if vma alloc */
+	if (my_qp->uspace_rqueue!=0) {
+		struct ehca_qp_core *qp_core = &my_qp->ehca_qp_core;
+		retcode = ehca_munmap(my_qp->uspace_rqueue, 
+				      qp_core->ipz_rqueue.queue_length);
+		retcode = ehca_munmap(my_qp->uspace_squeue, 
+				      qp_core->ipz_squeue.queue_length);
+		retcode = ehca_munmap(my_qp->uspace_fwh, 4096);
+	}
+
+	hipz_ret = hipz_h_destroy_qp(shca->ipz_hca_handle, my_qp);
+	if (hipz_ret != H_Success) {
+		EDEB_ERR(4, "hipz_h_destroy_qp() failed "
+			 "rc=%lx ehca_qp=%p qp_num=%x",
+			 hipz_ret, qp_pf, qp_num);
+		goto destroy_qp_exit0;
+	}
+
+	port_num = my_qp->init_attr.port_num;
+	qp_type  = my_qp->init_attr.qp_type;
+
+	/* TODO: later with IB_QPT_SMI */
+	if (qp_type == IB_QPT_GSI) {
+		struct ib_event event;
+
+		EDEB(4, "EHCA port %x is inactive.", port_num);
+		event.device = &shca->ib_device;
+		event.event = IB_EVENT_PORT_ERR;
+		event.element.port_num = port_num;
+		shca->sport[port_num - 1].port_state = IB_PORT_DOWN;
+		ib_dispatch_event(&event);
+	}
+
+	ipz_queue_dtor(&my_qp->ehca_qp_core.ipz_rqueue);
+	ipz_queue_dtor(&my_qp->ehca_qp_core.ipz_squeue);
+	ehca_qp_delete(my_qp);       
+
+ destroy_qp_exit0:
+	retcode = ehca2ib_return_code(hipz_ret);
+	EDEB_EX(7,"ret=%x", retcode);
+	return retcode;
+}
+
+/* eof ehca_qp.c */
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_reqs.c newtree/drivers/infiniband/hw/ehca/ehca_reqs.c
--- oldtree/drivers/infiniband/hw/ehca/ehca_reqs.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_reqs.c	2006-02-13 19:19:59.937521800 -0500
@@ -0,0 +1,401 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  post_send/recv, poll_cq, req_notify
+ *
+ *  Authors: Waleri Fomin <fomin@de.ibm.com>
+ *           Reinhard Ernst <rernst@de.ibm.com> 
+ *           Hoang-Nam Nguyen <hnguyen@de.ibm.com> 
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_reqs.c,v 1.37 2005/12/02 15:38:19 nguyen Exp $
+ */
+
+
+#define DEB_PREFIX "reqs"
+
+#include "ehca_kernel.h"
+#include "ehca_classes.h"
+#include "ehca_tools.h"
+#include "hcp_if.h"
+#include "ehca_qes.h"
+#include "ehca_iverbs.h"
+
+/* include some inline service routines */
+#include "ehca_asm.h"
+#include "ehca_reqs_core.c"
+
+int ehca_post_send(struct ib_qp *qp,
+		   struct ib_send_wr *send_wr, 
+		   struct ib_send_wr **bad_send_wr)
+{
+	struct ehca_qp *my_qp = NULL;
+	unsigned long spin_flags = 0;
+	struct ib_send_wr *cur_send_wr = NULL;
+	struct ehca_wqe *wqe_p = NULL;
+	int wqe_cnt = 0;
+	int retcode = 0;
+
+	EHCA_CHECK_ADR(qp);
+	my_qp = container_of(qp, struct ehca_qp, ib_qp);
+	EHCA_CHECK_QP(my_qp);
+	EHCA_CHECK_ADR(send_wr);
+	EDEB_EN(7, "ehca_qp=%p qp_num=%x send_wr=%p bad_send_wr=%p",
+		my_qp, qp->qp_num, send_wr, bad_send_wr);
+
+	/* LOCK the QUEUE */
+	spin_lock_irqsave(&my_qp->spinlock_s, spin_flags);
+
+	/* loop processes list of send reqs */
+	for (cur_send_wr = send_wr; cur_send_wr != NULL;
+	     cur_send_wr = cur_send_wr->next) {
+		void *start_addr =
+			&my_qp->ehca_qp_core.ipz_squeue.current_q_addr;
+		/* get pointer next to free WQE */
+		wqe_p = ipz_QEit_get_inc(&my_qp->ehca_qp_core.ipz_squeue);
+		if (unlikely(wqe_p == NULL)) {
+			/* too many posted work requests: queue overflow */
+			if (bad_send_wr != NULL) {
+				*bad_send_wr = cur_send_wr;
+			}
+			if (wqe_cnt==0) {
+				retcode = -ENOMEM;
+				EDEB_ERR(4, "Too many posted WQEs qp_num=%x", 
+					 qp->qp_num);
+			}
+			goto post_send_exit0;
+		}
+		/* write a SEND WQE into the QUEUE */
+		retcode = ehca_write_swqe(&my_qp->ehca_qp_core, 
+					  wqe_p, cur_send_wr);
+		/* if something failed, 
+		   reset the free entry pointer to the start value
+		*/
+		if (unlikely(retcode != 0)) {
+			my_qp->ehca_qp_core.ipz_squeue.current_q_addr =
+				start_addr;
+			*bad_send_wr = cur_send_wr;
+			if (wqe_cnt==0) {
+				retcode = -EINVAL;
+				EDEB_ERR(4, "Could not write WQE qp_num=%x", 
+					 qp->qp_num);
+			}
+			goto post_send_exit0;
+		}
+		wqe_cnt++;
+		EDEB(7, "ehca_qp=%p qp_num=%x wqe_cnt=%d", 
+		     my_qp, qp->qp_num, wqe_cnt);
+	} /* eof for cur_send_wr */
+
+ post_send_exit0:
+	/* UNLOCK the QUEUE */
+	spin_unlock_irqrestore(&my_qp->spinlock_s, spin_flags);
+	iosync(); /* serialize GAL register access */
+	hipz_update_SQA(&my_qp->ehca_qp_core, wqe_cnt);
+	EDEB_EX(7, "ehca_qp=%p qp_num=%x ret=%x wqe_cnt=%d",
+		my_qp, qp->qp_num, retcode, wqe_cnt);
+	return retcode;
+}
+
+int ehca_post_recv(struct ib_qp *qp,
+		   struct ib_recv_wr *recv_wr, 
+		   struct ib_recv_wr **bad_recv_wr)
+{
+	struct ehca_qp *my_qp = NULL;
+	unsigned long spin_flags = 0;
+	struct ib_recv_wr *cur_recv_wr = NULL;
+	struct ehca_wqe *wqe_p = NULL;
+	int wqe_cnt = 0;
+	int retcode = 0;
+
+	EHCA_CHECK_ADR(qp);
+	my_qp = container_of(qp, struct ehca_qp, ib_qp);
+	EHCA_CHECK_QP(my_qp);
+	EHCA_CHECK_ADR(recv_wr);
+	EDEB_EN(7, "ehca_qp=%p qp_num=%x recv_wr=%p bad_recv_wr=%p", 
+		my_qp, qp->qp_num, recv_wr, bad_recv_wr);
+
+	/* LOCK the QUEUE */
+	spin_lock_irqsave(&my_qp->spinlock_r, spin_flags);
+
+	/* loop processes list of send reqs */
+	for (cur_recv_wr = recv_wr; cur_recv_wr != NULL;
+	     cur_recv_wr = cur_recv_wr->next) {
+		void *start_addr =
+			&my_qp->ehca_qp_core.ipz_rqueue.current_q_addr;
+		/* get pointer next to free WQE */
+		wqe_p = ipz_QEit_get_inc(&my_qp->ehca_qp_core.ipz_rqueue);
+		if (unlikely(wqe_p == NULL)) {
+			/* too many posted work requests: queue overflow */
+			if (bad_recv_wr != NULL) {
+				*bad_recv_wr = cur_recv_wr;
+			}
+			if (wqe_cnt==0) {
+				retcode = -ENOMEM;
+				EDEB_ERR(4, "Too many posted WQEs qp_num=%x", 
+					 qp->qp_num);
+			}
+			goto post_recv_exit0;
+		}
+		/* write a RECV WQE into the QUEUE */
+		retcode =
+			ehca_write_rwqe(&my_qp->ehca_qp_core, wqe_p, cur_recv_wr);
+		/* if something failed, 
+		   reset the free entry pointer to the start value
+		*/
+		if (unlikely(retcode != 0)) {
+			my_qp->ehca_qp_core.ipz_rqueue.current_q_addr =
+				start_addr;
+			*bad_recv_wr = cur_recv_wr;
+			if (wqe_cnt==0) {
+				retcode = -EINVAL;
+				EDEB_ERR(4, "Could not write WQE qp_num=%x", 
+					 qp->qp_num);
+			}
+			goto post_recv_exit0;
+		}
+		wqe_cnt++;
+		EDEB(7, "ehca_qp=%p qp_num=%x wqe_cnt=%d",
+		     my_qp, qp->qp_num, wqe_cnt);
+	} /* eof for cur_recv_wr */
+
+ post_recv_exit0:
+	spin_unlock_irqrestore(&my_qp->spinlock_r, spin_flags);
+	iosync(); /* serialize GAL register access */
+	hipz_update_RQA(&my_qp->ehca_qp_core, wqe_cnt);
+	EDEB_EX(7, "ehca_qp=%p qp_num=%x ret=%x wqe_cnt=%d",
+		my_qp, qp->qp_num, retcode, wqe_cnt);
+	return retcode;
+}
+
+/**
+ * Table converts ehca wc opcode to ib
+ * Since we use zero to indicate invalid opcode, the actual ib opcode must 
+ * be decremented!!!
+ */
+static const u8 ib_wc_opcode[255] = {
+	[0x01] = IB_WC_RECV+1,
+	[0x02] = IB_WC_RECV_RDMA_WITH_IMM+1,
+	[0x04] = IB_WC_BIND_MW+1,
+	[0x08] = IB_WC_FETCH_ADD+1,
+	[0x10] = IB_WC_COMP_SWAP+1,
+	[0x20] = IB_WC_RDMA_WRITE+1,
+	[0x40] = IB_WC_RDMA_READ+1,
+	[0x80] = IB_WC_SEND+1
+};
+
+/** @brief internal function to poll one entry of cq
+ */
+static inline int ehca_poll_cq_one(struct ib_cq *cq, struct ib_wc *wc)
+{
+	int retcode = 0;
+	struct ehca_cq *my_cq = container_of(cq, struct ehca_cq, ib_cq);
+	struct ehca_cqe *cqe = NULL;
+	int cqe_count = 0;
+
+	EDEB_EN(7, "ehca_cq=%p cq_num=%x wc=%p", my_cq, my_cq->cq_number, wc);
+
+ poll_cq_one_read_cqe:
+	cqe = (struct ehca_cqe *)
+		ipz_QEit_get_inc_valid(&my_cq->ehca_cq_core.ipz_queue);
+	if (cqe == NULL) {
+		retcode = -EAGAIN;
+		EDEB(7, "Completion queue is empty ehca_cq=%p cq_num=%x "
+		     "retcode=%x", my_cq, my_cq->cq_number, retcode);
+		goto  poll_cq_one_exit0;
+	}
+        cqe_count++;
+        if (unlikely(cqe->status & 0x10)) { /* purge bit set */
+                struct ehca_qp *qp=ehca_cq_get_qp(my_cq, cqe->local_qp_number);
+		int purgeflag = 0;
+		unsigned long spin_flags = 0;
+		if (qp==NULL) { /* should not happen */
+			EDEB_ERR(4, "cq_num=%x qp_num=%x "
+				 "could not find qp -> ignore cqe", 
+				 my_cq->cq_number, cqe->local_qp_number);
+			EDEB_DMP(4, cqe, 64, "cq_num=%x qp_num=%x",
+				 my_cq->cq_number, cqe->local_qp_number);
+			/* ignore this purged cqe */
+                        goto poll_cq_one_read_cqe;
+		}
+		spin_lock_irqsave(&qp->spinlock_s, spin_flags);
+		purgeflag = qp->sqerr_purgeflag;
+		spin_unlock_irqrestore(&qp->spinlock_s, spin_flags);
+                if (purgeflag!=0) { 
+			EDEB(6, "Got CQE with purged bit qp_num=%x src_qp=%x",
+			     cqe->local_qp_number, cqe->remote_qp_number);
+			EDEB_DMP(6, cqe, 64, "qp_num=%x src_qp=%x",
+				 cqe->local_qp_number, cqe->remote_qp_number);
+			/* ignore this to avoid double cqes of bad wqe
+			   that caused sqe and turn off purge flag */
+                        qp->sqerr_purgeflag = 0;
+                        goto poll_cq_one_read_cqe;
+                }
+        }
+
+	/* tracing cqe */
+	if (IS_EDEB_ON(7)) {
+		EDEB(7, "Received COMPLETION ehca_cq=%p cq_num=%x -----",
+		     my_cq, my_cq->cq_number);
+		EDEB_DMP(7, cqe, 64, "ehca_cq=%p cq_num=%x", 
+			 my_cq, my_cq->cq_number);
+		EDEB(7, "ehca_cq=%p cq_num=%x -------------------------",
+		     my_cq, my_cq->cq_number);
+	}
+
+	/* we got a completion! */
+	wc->wr_id = cqe->work_request_id;
+
+	/* eval ib_wc_opcode */
+	wc->opcode = ib_wc_opcode[cqe->optype]-1;
+	if (unlikely(wc->opcode == -1)) {
+		EDEB_ERR(4, "Invalid cqe->OPType=%x cqe->status=%x "
+			 "ehca_cq=%p cq_num=%x",
+			 cqe->optype, cqe->status, my_cq, my_cq->cq_number);
+		/* dump cqe for other infos */
+		EDEB_DMP(4, cqe, 64, "ehca_cq=%p cq_num=%x", my_cq, my_cq->cq_number);
+		/* update also queue adder to throw away this entry!!! */
+		goto poll_cq_one_exit0;
+	}
+	/* eval ib_wc_status */
+	if (unlikely(cqe->status & 0x80000000)) { /* complete with errors */
+		map_ib_wc_status(cqe->status, &wc->status);
+		wc->vendor_err = wc->status;
+	} else {
+		wc->status = IB_WC_SUCCESS;
+	}
+
+	wc->qp_num = cqe->local_qp_number;
+	wc->byte_len = ntohl(cqe->nr_bytes_transferred);
+	wc->pkey_index = cqe->pkey_index;
+	wc->slid = cqe->rlid;
+	wc->dlid_path_bits = cqe->dlid;
+	wc->src_qp = cqe->remote_qp_number;
+	wc->wc_flags = cqe->w_completion_flags;
+	wc->imm_data = cqe->immediate_data;
+	wc->sl = cqe->service_level;
+
+	if (wc->status != IB_WC_SUCCESS) {
+		EDEB(6, "ehca_cq=%p cq_num=%x WARNING unsuccessful cqe "
+		     "OPType=%x status=%x qp_num=%x src_qp=%x wr_id=%lx cqe=%p", 
+		     my_cq, my_cq->cq_number, cqe->optype, cqe->status, 
+		     cqe->local_qp_number, cqe->remote_qp_number,
+		     cqe->work_request_id, cqe);
+	}
+
+ poll_cq_one_exit0:
+	if (cqe_count>0) {
+		hipz_update_FECA(&my_cq->ehca_cq_core, cqe_count);
+	}
+
+	EDEB_EX(7, "retcode=%x ehca_cq=%p cq_number=%x wc=%p "
+		"status=%x opcode=%x qp_num=%x byte_len=%x",
+		retcode, my_cq, my_cq->cq_number, wc, wc->status,
+		wc->opcode, wc->qp_num, wc->byte_len);
+	return (retcode);
+}
+
+int ehca_poll_cq(struct ib_cq *cq, int num_entries, struct ib_wc *wc)
+{
+	struct ehca_cq *my_cq = NULL;
+	int nr = 0;
+	struct ib_wc *current_wc = NULL;
+	unsigned long spin_flags = 0;
+	int retcode = 0;
+
+	EHCA_CHECK_CQ(cq);
+	EHCA_CHECK_ADR(wc);
+
+	my_cq = container_of(cq, struct ehca_cq, ib_cq);
+	EHCA_CHECK_CQ(my_cq);
+
+	EDEB_EN(7, "ehca_cq=%p cq_num=%x num_entries=%d wc=%p",
+		my_cq, my_cq->cq_number, num_entries, wc);
+
+	if (num_entries < 1) {
+		EDEB_ERR(4, "Invalid num_entries=%d ehca_cq=%p cq_num=%x",
+			 num_entries, my_cq, my_cq->cq_number);
+		retcode = -EINVAL;
+		goto poll_cq_exit0;
+	}
+
+	current_wc = wc;
+	spin_lock_irqsave(&my_cq->spinlock, spin_flags);
+	for (nr = 0; nr < num_entries; nr++) {
+		retcode = ehca_poll_cq_one(cq, current_wc);
+		if (0 != retcode) {
+			break;
+		}
+		current_wc++;
+	} /* eof for nr */
+	spin_unlock_irqrestore(&my_cq->spinlock, spin_flags);
+	if (-EAGAIN == retcode || 0 == retcode) {
+		retcode = nr;
+	}
+
+ poll_cq_exit0:
+	EDEB_EX(7, "ehca_cq=%p cq_num=%x retcode=%x wc=%p nr_entries=%d",
+		my_cq, my_cq->cq_number, retcode, wc, nr);
+	return (retcode);
+}
+
+int ehca_req_notify_cq(struct ib_cq *cq, enum ib_cq_notify cq_notify)
+{
+	struct ehca_cq *my_cq = NULL;
+	int retcode = 0;
+
+	EHCA_CHECK_CQ(cq);
+	my_cq = container_of(cq, struct ehca_cq, ib_cq);
+	EHCA_CHECK_CQ(my_cq);
+	EDEB_EN(7, "ehca_cq=%p cq_num=%x cq_notif=%x", 
+		my_cq, my_cq->cq_number, cq_notify);
+
+	switch (cq_notify) {
+	case IB_CQ_SOLICITED:
+		hipz_set_CQx_N0(&my_cq->ehca_cq_core, 1);
+		break;
+	case IB_CQ_NEXT_COMP:
+		hipz_set_CQx_N1(&my_cq->ehca_cq_core, 1);
+		break;
+	default:
+		retcode = -EINVAL;
+	}
+
+	EDEB_EX(7, "ehca_cq=%p cq_num=%x retcode=%x",
+		my_cq, my_cq->cq_number, retcode);
+
+	return (retcode);
+}
+
+/* eof ehca_reqs.c */
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_reqs_core.c newtree/drivers/infiniband/hw/ehca/ehca_reqs_core.c
--- oldtree/drivers/infiniband/hw/ehca/ehca_reqs_core.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_reqs_core.c	2006-02-13 19:19:59.937521800 -0500
@@ -0,0 +1,416 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  post_send/recv, poll_cq, req_notify
+ *  Common code to be included statically in respective user/kernel
+ *  modules, i.e. ehca_ureqs.c/ehca_reqs.c
+ *  This module contains C code only. Including modules must include
+ *  all required header files.
+ *
+ *  Authors: Waleri Fomin <fomin@de.ibm.com>
+ *           Reinhard Ernst <rernst@de.ibm.com> 
+ *           Hoang-Nam Nguyen <hnguyen@de.ibm.com> 
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_reqs_core.c,v 1.36 2005/12/09 15:39:03 nguyen Exp $
+ */
+
+/** THIS following block of defines
+ * replaces ib types of kernel space to corresponding ones in user space,
+ * so that the implemented inline functions below can be compiled and
+ * work in both user and kernel space.
+ * However this ASSUMES that there is no functional differences between ib 
+ * types in kernel e.g. ib_send_wr and user space e.g. ibv_send_wr.
+ */
+
+#ifndef __KERNEL__
+#define ib_recv_wr ibv_recv_wr
+#define ib_send_wr ibv_send_wr
+#define ehca_av ehcau_av
+/* ib_wr_opcode */
+#define IB_WR_SEND IBV_WR_SEND
+#define IB_WR_SEND_WITH_IMM IBV_WR_SEND_WITH_IMM
+#define IB_WR_RDMA_WRITE IBV_WR_RDMA_WRITE
+#define IB_WR_RDMA_WRITE_WITH_IMM IBV_WR_RDMA_WRITE_WITH_IMM
+#define IB_WR_RDMA_READ IBV_WR_RDMA_READ
+/* ib_qp_type */
+#define IB_QPT_RC IBV_QPT_RC
+#define IB_QPT_UC IBV_QPT_UC
+#define IB_QPT_UD IBV_QPT_UD
+/* ib_wc_opcode */
+#define ib_wc_opcode ibv_wc_opcode
+#define IB_WC_SEND IBV_WC_SEND
+#define IB_WC_RDMA_WRITE IBV_WC_RDMA_WRITE
+#define IB_WC_RDMA_READ IBV_WC_RDMA_READ
+#define IB_WC_COMP_SWAP IBV_WC_COMP_SWAP
+#define IB_WC_FETCH_ADD IBV_WC_FETCH_ADD
+#define IB_WC_BIND_MW IBV_WC_BIND_MW
+#define IB_WC_RECV IBV_WC_RECV
+#define IB_WC_RECV_RDMA_WITH_IMM IBV_WC_RECV_RDMA_WITH_IMM
+/* ib_wc_status */
+#define ib_wc_status ibv_wc_status
+#define IB_WC_LOC_LEN_ERR IBV_WC_LOC_LEN_ERR
+#define IB_WC_LOC_QP_OP_ERR IBV_WC_LOC_QP_OP_ERR
+#define IB_WC_LOC_EEC_OP_ERR IBV_WC_LOC_EEC_OP_ERR
+#define IB_WC_LOC_PROT_ERR IBV_WC_LOC_PROT_ERR
+#define IB_WC_WR_FLUSH_ERR IBV_WC_WR_FLUSH_ERR
+#define IB_WC_MW_BIND_ERR IBV_WC_MW_BIND_ERR
+#define IB_WC_GENERAL_ERR IBV_WC_GENERAL_ERR
+#define IB_WC_REM_INV_REQ_ERR IBV_WC_REM_INV_REQ_ERR
+#define IB_WC_REM_ACCESS_ERR IBV_WC_REM_ACCESS_ERR
+#define IB_WC_REM_OP_ERR IBV_WC_REM_OP_ERR
+#define IB_WC_REM_INV_RD_REQ_ERR IBV_WC_REM_INV_RD_REQ_ERR
+#define IB_WC_RETRY_EXC_ERR IBV_WC_RETRY_EXC_ERR
+#define IB_WC_RNR_RETRY_EXC_ERR IBV_WC_RNR_RETRY_EXC_ERR
+#define IB_WC_REM_ABORT_ERR IBV_WC_REM_ABORT_ERR
+#define IB_WC_INV_EECN_ERR IBV_WC_INV_EECN_ERR
+#define IB_WC_INV_EEC_STATE_ERR IBV_WC_INV_EEC_STATE_ERR
+#define IB_WC_BAD_RESP_ERR IBV_WC_BAD_RESP_ERR
+#define IB_WC_FATAL_ERR IBV_WC_FATAL_ERR
+#define IB_WC_SUCCESS IBV_WC_SUCCESS
+/* ib_send_flags */
+#define IB_SEND_FENCE IBV_SEND_FENCE
+#define IB_SEND_SIGNALED IBV_SEND_SIGNALED
+#define IB_SEND_SOLICITED IBV_SEND_SOLICITED
+#define IB_SEND_INLINE IBV_SEND_INLINE
+#endif
+
+static inline int ehca_write_rwqe(struct ehca_qp_core *qp_core,
+				  struct ehca_wqe *wqe_p,
+				  struct ib_recv_wr *recv_wr)
+{
+	u8 cnt_ds;
+	if (unlikely((recv_wr->num_sge < 0) ||
+		     (recv_wr->num_sge > qp_core->ipz_rqueue.act_nr_of_sg))) {
+		EDEB_ERR(4, "Invalid number of WQE SGE. "
+			 "num_sqe=%x max_nr_of_sg=%x",
+			 recv_wr->num_sge, qp_core->ipz_rqueue.act_nr_of_sg);
+		return (-EINVAL); /* invalid SG list length */
+	}
+
+	clear_cacheline(wqe_p);
+	clear_cacheline((u8 *) wqe_p + 32);
+	clear_cacheline((u8 *) wqe_p + 64);
+
+	wqe_p->work_request_id = be64_to_cpu(recv_wr->wr_id);
+	wqe_p->nr_of_data_seg = recv_wr->num_sge;
+
+	for (cnt_ds = 0; cnt_ds < recv_wr->num_sge; cnt_ds++) {
+		wqe_p->u.all_rcv.sg_list[cnt_ds].vaddr =
+		    be64_to_cpu(recv_wr->sg_list[cnt_ds].addr);
+		wqe_p->u.all_rcv.sg_list[cnt_ds].lkey =
+		    ntohl(recv_wr->sg_list[cnt_ds].lkey);
+		wqe_p->u.all_rcv.sg_list[cnt_ds].length =
+		    ntohl(recv_wr->sg_list[cnt_ds].length);
+	}
+
+	if (IS_EDEB_ON(7)) {
+		EDEB(7, "RECEIVE WQE written into queue qp_core=%p", qp_core);
+		EDEB_DMP(7, wqe_p, 16*(6 + wqe_p->nr_of_data_seg),
+			 "qp_core=%p", qp_core);
+	}
+
+	return (0);
+}
+
+/* internal use only
+   uncomment this line to enable trace output of GSI send wr */
+/* #define DEBUG_GSI_SEND_WR 1 */
+#if defined(__KERNEL__) && defined(DEBUG_GSI_SEND_WR)
+
+/* need ib_mad struct */
+#include <ib_mad.h>
+
+static void trace_send_wr_ud(const struct ib_send_wr *send_wr)
+{
+	int idx = 0;
+	int j = 0;
+	while (send_wr != NULL) {
+		struct ib_mad_hdr *mad_hdr = send_wr->wr.ud.mad_hdr;
+		struct ib_sge *sge = send_wr->sg_list;
+		EDEB(4, "send_wr#%x wr_id=%lx num_sge=%x "
+		     "send_flags=%x opcode=%x",idx, send_wr->wr_id, 
+		     send_wr->num_sge, send_wr->send_flags, send_wr->opcode);
+		if (mad_hdr != NULL) {
+			EDEB(4, "send_wr#%x mad_hdr base_version=%x "
+			     "mgmt_class=%x class_version=%x method=%x "
+			     "status=%x class_specific=%x tid=%lx attr_id=%x "
+			     "resv=%x attr_mod=%x",
+			     idx, mad_hdr->base_version, mad_hdr->mgmt_class,
+			     mad_hdr->class_version, mad_hdr->method,
+			     mad_hdr->status, mad_hdr->class_specific,
+			     mad_hdr->tid, mad_hdr->attr_id, mad_hdr->resv,
+			     mad_hdr->attr_mod);
+		}
+		for (j = 0; j < send_wr->num_sge; j++) {
+#ifdef EHCA_USERDRIVER
+			u8 *data = (u8 *) sge->addr;
+#else
+			u8 *data = (u8 *) abs_to_virt(sge->addr);
+#endif
+			EDEB(4, "send_wr#%x sge#%x addr=%p length=%x lkey=%x",
+			     idx, j, data, sge->length, sge->lkey);
+			/* assume length is n*16 */
+			EDEB_DMP(4, data, sge->length, "send_wr#%x sge#%x", idx, j);
+			sge++;
+		} /* eof for j */
+		idx++;
+		send_wr = send_wr->next;
+	} /* eof while send_wr */
+}
+
+#endif /* __KERNEL__ && DEBUG_GSI_SEND_WR */
+
+static inline int ehca_write_swqe(struct ehca_qp_core *qp_core,
+				  struct ehca_wqe *wqe_p,
+				  const struct ib_send_wr *send_wr)
+{
+	u32 idx;
+	u64 dma_length;
+        struct ehca_av *my_av;
+	u32 remote_qkey = send_wr->wr.ud.remote_qkey;
+
+        clear_cacheline(wqe_p);
+	clear_cacheline((u8 *) wqe_p + 32);
+
+	if (unlikely((send_wr->num_sge < 0) ||
+		     (send_wr->num_sge > qp_core->ipz_squeue.act_nr_of_sg))) {
+		EDEB_ERR(4, "Invalid number of WQE SGE. "
+			 "num_sqe=%x max_nr_of_sg=%x",
+			 send_wr->num_sge, qp_core->ipz_rqueue.act_nr_of_sg);
+		return (-EINVAL); /* invalid SG list length */
+	}
+
+	wqe_p->work_request_id = be64_to_cpu(send_wr->wr_id);
+
+	switch (send_wr->opcode) {
+	case IB_WR_SEND:
+	case IB_WR_SEND_WITH_IMM:
+		wqe_p->optype = WQE_OPTYPE_SEND;
+		break;
+	case IB_WR_RDMA_WRITE:
+	case IB_WR_RDMA_WRITE_WITH_IMM:
+		wqe_p->optype = WQE_OPTYPE_RDMAWRITE;
+		break;
+	case IB_WR_RDMA_READ:
+		wqe_p->optype = WQE_OPTYPE_RDMAREAD;
+		break;
+	default:
+		EDEB_ERR(4, "Invalid opcode=%x", send_wr->opcode);
+		return (-EINVAL); /* invalid opcode */
+	}
+
+	wqe_p->wqef = (send_wr->opcode) & 0xF0;
+
+	wqe_p->wr_flag = 0;
+	if (send_wr->send_flags & IB_SEND_SIGNALED) {
+		wqe_p->wr_flag |= WQE_WRFLAG_REQ_SIGNAL_COM;
+	}
+
+	if (send_wr->opcode == IB_WR_SEND_WITH_IMM ||
+	    send_wr->opcode == IB_WR_RDMA_WRITE_WITH_IMM) {
+		/* this might not work as long as HW does not support it */
+		wqe_p->immediate_data = send_wr->imm_data;
+		wqe_p->wr_flag |= WQE_WRFLAG_IMM_DATA_PRESENT;
+	}
+
+	wqe_p->nr_of_data_seg = send_wr->num_sge;
+
+	switch (qp_core->qp_type) {
+#ifdef __KERNEL__
+	case IB_QPT_SMI:
+	case IB_QPT_GSI:
+#endif /* __KERNEL__ */
+		/* no break is intential here */
+	case IB_QPT_UD:
+		/* IB 1.2 spec C10-15 compliance */
+		if (send_wr->wr.ud.remote_qkey & 0x80000000) {
+			remote_qkey = qp_core->qkey;
+		}
+		wqe_p->destination_qp_number =
+		    ntohl(send_wr->wr.ud.remote_qpn << 8);
+		wqe_p->local_ee_context_qkey = ntohl(remote_qkey);
+		my_av = container_of(send_wr->wr.ud.ah, struct ehca_av, ib_ah);
+		wqe_p->u.ud_av.ud_av = my_av->av;
+
+		/* omitted check of IB_SEND_INLINE 
+		   since HW does not support it */
+		for (idx = 0; idx < send_wr->num_sge; idx++) {
+			wqe_p->u.ud_av.sg_list[idx].vaddr =
+			    be64_to_cpu(send_wr->sg_list[idx].addr);
+			wqe_p->u.ud_av.sg_list[idx].lkey =
+			    ntohl(send_wr->sg_list[idx].lkey);
+			wqe_p->u.ud_av.sg_list[idx].length =
+			    ntohl(send_wr->sg_list[idx].length);
+		} /* eof for idx */
+#ifdef __KERNEL__
+		if (qp_core->qp_type == IB_QPT_SMI ||
+		    qp_core->qp_type == IB_QPT_GSI) {
+			wqe_p->u.ud_av.ud_av.pmtu = 1;
+		}
+		if (qp_core->qp_type == IB_QPT_GSI) {
+			wqe_p->pkeyi =
+			    ntohs(send_wr->wr.ud.pkey_index);
+#ifdef DEBUG_GSI_SEND_WR
+			trace_send_wr_ud(send_wr);
+#endif /* DEBUG_GSI_SEND_WR */
+		}
+#endif /* __KERNEL__ */
+		break;
+
+	case IB_QPT_UC:
+		if (send_wr->send_flags & IB_SEND_FENCE) {
+			wqe_p->wr_flag |= WQE_WRFLAG_FENCE;
+		}
+		/* no break is intential here */
+	case IB_QPT_RC:
+		/*@@TODO atomic???*/
+		wqe_p->u.nud.remote_virtual_adress =
+		    be64_to_cpu(send_wr->wr.rdma.remote_addr);
+		wqe_p->u.nud.rkey = ntohl(send_wr->wr.rdma.rkey);
+
+		/* omitted checking of IB_SEND_INLINE 
+		   since HW does not support it */
+		dma_length = 0;
+		for (idx = 0; idx < send_wr->num_sge; idx++) {
+			wqe_p->u.nud.sg_list[idx].vaddr =
+			    be64_to_cpu(send_wr->sg_list[idx].addr);
+			wqe_p->u.nud.sg_list[idx].lkey =
+			    ntohl(send_wr->sg_list[idx].lkey);
+			wqe_p->u.nud.sg_list[idx].length =
+			    ntohl(send_wr->sg_list[idx].length);
+			dma_length += send_wr->sg_list[idx].length;
+		} /* eof idx */
+		wqe_p->u.nud.atomic_1st_op_dma_len = be64_to_cpu(dma_length);
+
+		break;
+
+	default:
+		EDEB_ERR(4, "Invalid qptype=%x", qp_core->qp_type);
+		return (-EINVAL);
+	}
+
+	if (IS_EDEB_ON(7)) {
+		EDEB(7, "SEND WQE written into queue qp_core=%p ", qp_core);
+		EDEB_DMP(7, wqe_p, 16*(6 + wqe_p->nr_of_data_seg),
+			 "qp_core=%p", qp_core);
+	}
+	return (0);
+}
+
+/** @brief convert cqe_status to ib_wc_status
+ */
+static inline void map_ib_wc_status(u32 cqe_status,
+				    enum ib_wc_status *wc_status)
+{
+	if (unlikely(cqe_status & 0x80000000)) { /* complete with errors */
+		switch (cqe_status & 0x0000003F) {
+		case 0x01:
+		case 0x21:
+			*wc_status = IB_WC_LOC_LEN_ERR;
+			break;
+		case 0x02:
+		case 0x22:
+			*wc_status = IB_WC_LOC_QP_OP_ERR;
+			break;
+		case 0x03:
+		case 0x23:
+			*wc_status = IB_WC_LOC_EEC_OP_ERR;
+			break;
+		case 0x04:
+		case 0x24:
+			*wc_status = IB_WC_LOC_PROT_ERR;
+			break;
+		case 0x05:
+		case 0x25:
+			*wc_status = IB_WC_WR_FLUSH_ERR;
+			break;
+		case 0x06:
+			*wc_status = IB_WC_MW_BIND_ERR;
+			break;
+		case 0x07: /* remote error - look into bits 20:24 */
+			switch ((cqe_status & 0x0000F800) >> 11) {
+			case 0x0:
+				/* PSN Sequence Error! 
+				   couldn't find a matching VAPI status! */
+				*wc_status = IB_WC_GENERAL_ERR;
+				break;
+			case 0x1:
+				*wc_status = IB_WC_REM_INV_REQ_ERR;
+				break;
+			case 0x2:
+				*wc_status = IB_WC_REM_ACCESS_ERR;
+				break;
+			case 0x3:
+				*wc_status = IB_WC_REM_OP_ERR;
+				break;
+			case 0x4:
+				*wc_status = IB_WC_REM_INV_RD_REQ_ERR;
+				break;
+			}
+			break;
+		case 0x08:
+			*wc_status = IB_WC_RETRY_EXC_ERR;
+			break;
+		case 0x09:
+			*wc_status = IB_WC_RNR_RETRY_EXC_ERR;
+			break;
+		case 0x0A:
+		case 0x2D:
+			*wc_status = IB_WC_REM_ABORT_ERR;
+			break;
+		case 0x0B:
+		case 0x2E:
+			*wc_status = IB_WC_INV_EECN_ERR;
+			break;
+		case 0x0C:
+		case 0x2F:
+			*wc_status = IB_WC_INV_EEC_STATE_ERR;
+			break;
+		case 0x0D:
+			*wc_status = IB_WC_BAD_RESP_ERR;
+			break;
+		case 0x10:
+			/* WQE purged */
+			*wc_status = IB_WC_WR_FLUSH_ERR;
+			break;
+		default:
+			*wc_status = IB_WC_FATAL_ERR;
+
+		}
+	} else {
+		*wc_status = IB_WC_SUCCESS;
+	}
+}
+
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_sqp.c newtree/drivers/infiniband/hw/ehca/ehca_sqp.c
--- oldtree/drivers/infiniband/hw/ehca/ehca_sqp.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_sqp.c	2006-02-13 19:19:59.939521496 -0500
@@ -0,0 +1,135 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  SQP functions
+ *
+ *  Authors: Khadija Souissi <souissi@de.ibm.com>
+ *           Heiko J Schick <schickhj@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_sqp.c,v 1.32 2005/11/03 09:14:38 fomin Exp $
+ */
+
+
+#define DEB_PREFIX "e_qp"
+
+#include "ehca_kernel.h"
+#include "ehca_classes.h"
+#include "ehca_tools.h"
+#include "hcp_if.h"
+#include "ehca_qes.h"
+#include "ehca_iverbs.h"
+
+#include <linux/module.h>
+#include <linux/err.h>
+
+extern int ehca_create_aqp1(struct ehca_shca *shca, struct ehca_sport *sport);
+extern int ehca_destroy_aqp1(struct ehca_sport *sport);
+
+extern int ehca_port_act_time;
+
+/**
+ * ehca_define_aqp0 - TODO
+ * 
+ * @ehca_qp:     : TODO adapter_handle, ipz_qp_handle, galpas.kernel
+ * @qp_init_attr : TODO for port number
+ */
+u64 ehca_define_sqp(struct ehca_shca *shca,
+			       struct ehca_qp *ehca_qp,
+			       struct ib_qp_init_attr *qp_init_attr)
+{
+
+	u32 pma_qp_nr = 0;
+	u32 bma_qp_nr = 0;
+	u64 ret = H_Success;
+	u8 port = qp_init_attr->port_num;
+	int counter = 0;
+
+	EDEB_EN(7, "port=%x qp_type=%x",
+		port, qp_init_attr->qp_type);
+
+	shca->sport[port - 1].port_state = IB_PORT_DOWN;
+
+	switch (qp_init_attr->qp_type) {
+	case IB_QPT_SMI:
+		/* TODO: function not supported yet */
+		/*
+		   ret = hipz_h_define_aqp0(shca->ipz_hca_handle,
+		   ehca_qp->ipz_qp_handle,
+		   ehca_qp->galpas.kernel,
+		   (u32)qp_init_attr->port_num);
+		 */
+		break;
+	case IB_QPT_GSI:
+		ret = hipz_h_define_aqp1(shca->ipz_hca_handle,
+					 ehca_qp->ipz_qp_handle,
+					 ehca_qp->ehca_qp_core.galpas.kernel,
+					 (u32) qp_init_attr->port_num,
+					 &pma_qp_nr, &bma_qp_nr);
+
+		if (ret != H_Success) {
+			EDEB_ERR(4, "Can't define AQP1 for port %x. rc=%lx",
+				    port, ret);
+			goto ehca_define_aqp1;
+		}
+		break;
+	default:
+		ret = H_Parameter;
+		goto ehca_define_aqp1;
+	}
+
+#ifndef EHCA_USERDRIVER
+	while ((shca->sport[port - 1].port_state != IB_PORT_ACTIVE) &&
+	       (counter < ehca_port_act_time)) {
+		EDEB(6, "... wait until port %x is active",
+			port);
+		msleep_interruptible(1000);
+		counter++;
+	}
+
+	if (counter == ehca_port_act_time) {
+		EDEB_ERR(4, "Port %x is not active.", port);
+		ret = H_Hardware;	
+	}
+#else
+	if (shca->sport[port - 1].port_state != IB_PORT_ACTIVE) {
+		sleep(20);
+	}
+#endif
+
+      ehca_define_aqp1:
+	EDEB_EX(7, "ret=%lx", ret);
+
+	return ret;
+}
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_tools.h newtree/drivers/infiniband/hw/ehca/ehca_tools.h
--- oldtree/drivers/infiniband/hw/ehca/ehca_tools.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_tools.h	2006-02-13 19:19:59.940521344 -0500
@@ -0,0 +1,434 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  auxiliary functions
+ *
+ *  Authors: Christoph Raisch <raisch@de.ibm.com>
+ *           Khadija Souissi <souissik@de.ibm.com> 
+ *           Waleri Fomin <fomin@de.ibm.com>
+ *           Heiko J Schick <schickhj@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_tools.h,v 1.40 2005/11/03 13:26:55 nguyen Exp $
+ */
+
+
+#ifndef EHCA_TOOLS_H
+#define EHCA_TOOLS_H
+
+#include "ehca_flightrecorder.h"
+#include "ehca_common.h"
+
+#define flightlog_value() mftb()
+
+#ifndef sizeofmember
+#define sizeofmember(TYPE, MEMBER) (sizeof( ((TYPE *)0)->MEMBER))
+#endif
+
+#define EHCA_EDEB_TRACE_MASK_SIZE 32
+extern u8 ehca_edeb_mask[EHCA_EDEB_TRACE_MASK_SIZE];
+#define EDEB_ID_TO_U32(str4) (str4[3] | (str4[2] << 8) | (str4[1] << 16) | \
+                              (str4[0] << 24))
+
+inline static u64 ehca_edeb_filter(const u32 level,
+				   const u32 id, const u32 line)
+{
+	u64 ret = 0;
+	u32 filenr = 0;
+	u32 filter_level = 9;
+	u32 dynamic_level = 0;
+	/* This is code written for the gcc -O2 optimizer which should colapse 
+	 * to two single ints filter_level is the first level kicked out by 
+         * compiler means trace everythin below 6. */
+	if (id == EDEB_ID_TO_U32("ehav")) {
+		filenr = 0x01;
+		filter_level = 8;
+	}
+	if (id == EDEB_ID_TO_U32("clas")) {
+		filenr = 0x02;
+		filter_level = 8;
+	}
+	if (id == EDEB_ID_TO_U32("cqeq")) {
+		filenr = 0x03;
+		filter_level = 8;
+	}
+	if (id == EDEB_ID_TO_U32("shca")) {
+		filenr = 0x05;
+		filter_level = 8;
+	}
+	if (id == EDEB_ID_TO_U32("eirq")) {
+		filenr = 0x06;
+		filter_level = 8;
+	}
+	if (id == EDEB_ID_TO_U32("lMad")) {
+		filenr = 0x07;
+		filter_level = 8;
+	}
+	if (id == EDEB_ID_TO_U32("mcas")) {
+		filenr = 0x08;
+		filter_level = 8;
+	}
+	if (id == EDEB_ID_TO_U32("mrmw")) {
+		filenr = 0x09;
+		filter_level = 8;
+	}
+	if (id == EDEB_ID_TO_U32("vpd ")) {
+		filenr = 0x0a;
+		filter_level = 8;
+	}
+	if (id == EDEB_ID_TO_U32("e_qp")) {
+		filenr = 0x0b;
+		filter_level = 8;
+	}
+	if (id == EDEB_ID_TO_U32("uqes")) {
+		filenr = 0x0c;
+		filter_level = 8;
+	}
+	if (id == EDEB_ID_TO_U32("PHYP")) {
+		filenr = 0x0d;
+		filter_level = 8;
+	}
+	if (id == EDEB_ID_TO_U32("snse")) {
+		filenr = 0x0e;
+		filter_level = 8;
+	}
+	if (id == EDEB_ID_TO_U32("iptz")) {
+		filenr = 0x0f;
+		filter_level = 8;
+	}
+	if (id == EDEB_ID_TO_U32("spta")) {
+		filenr = 0x10;
+		filter_level = 8;
+	}
+	if (id == EDEB_ID_TO_U32("simp")) {
+		filenr = 0x11;
+		filter_level = 8;
+	}
+	if (id == EDEB_ID_TO_U32("reqs")) {
+		filenr = 0x12;
+		filter_level = 8;
+	}
+
+	if ((filenr - 1) > sizeof(ehca_edeb_mask)) {
+		filenr = 0;
+	}
+
+	if (filenr == 0) {
+		filter_level = 9;
+	}			/* default */
+	ret = filenr * 0x10000 + line;
+	if (filter_level <= level) {
+		return (ret | 0x100000000); /* this is the flag to not trace */
+	}
+	dynamic_level = ehca_edeb_mask[filenr];
+	if (likely(dynamic_level <= level)) {
+		ret = ret | 0x100000000;
+	};
+	return ret;
+}
+
+#ifdef EHCA_USE_HCALL_KERNEL
+#ifdef CONFIG_PPC_PSERIES
+
+#include <asm/paca.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+#define paca_index xPacaIndex
+#endif
+
+/**
+ * IS_EDEB_ON - Checks if debug is on for the given level.
+ */
+#define IS_EDEB_ON(level) \
+    ((ehca_edeb_filter(level, EDEB_ID_TO_U32(DEB_PREFIX), __LINE__) & 0x100000000)==0)
+
+#define EDEB_P_GENERIC(level,idstring,format,args...) \
+do { \
+        u64 ehca_edeb_filterresult = \
+		ehca_edeb_filter(level, EDEB_ID_TO_U32(DEB_PREFIX), __LINE__);\
+        if ((ehca_edeb_filterresult & 0x100000000) == 0) \
+                printk("PU%04x %08x:%s " idstring " "format "\n", \
+			get_paca()->paca_index, (u32)(ehca_edeb_filterresult),\
+			__func__,  ##args); \
+        if (unlikely(ehca_edeb_mask[0x1e]!=0)) \
+                ED_FLIGHT_LOG((((u64)(get_paca()->paca_index)<< 32) | \
+                      ((u64)(ehca_edeb_filterresult & 0xffffffff)) << 40 | \
+                      (flightlog_value()&0xffffffff)), args); \
+} while (1==0)
+
+#elif CONFIG_ARCH_S390 
+
+#include <asm/smp.h>
+#define EDEB_P_GENERIC(level,idstring,format,args...) \
+do { \
+        u64 ehca_edeb_filterresult = \
+		ehca_edeb_filter(level, EDEB_ID_TO_U32(DEB_PREFIX), __LINE__);\
+        if ((ehca_edeb_filterresult & 0x100000000) == 0) \
+                printk("PU%04x %08x:%s " idstring " "format "\n", \
+			smp_processor_id(), (u32)(ehca_edeb_filterresult), \
+			__func__,  ##args); \
+} while (1==0)
+
+#elif REAL_HCALL
+
+#define EDEB_P_GENERIC(level,idstring,format,args...) \
+do { \
+        u64 ehca_edeb_filterresult = \
+		ehca_edeb_filter(level, EDEB_ID_TO_U32(DEB_PREFIX), __LINE__);\
+        if ((ehca_edeb_filterresult & 0x100000000) == 0) \
+                printk("%08x:%s " idstring " "format "\n", \
+			(u32)(ehca_edeb_filterresult), \
+			__func__,  ##args); \
+} while (1==0)
+
+#endif
+#else
+
+#define IS_EDEB_ON(level) (1)
+
+#define EDEB_P_GENERIC(level,idstring,format,args...) \
+do { \
+                printk("%s " idstring " "format "\n", \
+			__func__,  ##args); \
+} while (1==0)
+
+#endif
+
+/**
+ * EDEB - Trace output macro.
+ * @level tracelevel
+ * @format optional format string, use "" if not desired
+ * @args printf like arguments for trace, use %Lx for u64, %x for u32 
+ *       %p for pointer
+ */
+#define EDEB(level,format,args...) \
+        EDEB_P_GENERIC(level,"",format,##args)
+#define EDEB_ERR(level,format,args...) \
+        EDEB_P_GENERIC(level,"HCAD_ERROR ",format,##args)
+#define EDEB_EN(level,format,args...) \
+        EDEB_P_GENERIC(level,">>>",format,##args)
+#define EDEB_EX(level,format,args...) \
+        EDEB_P_GENERIC(level,"<<<",format,##args)
+
+/**
+ * EDEB macro to dump a memory block, whose length is n*8 bytes.
+ * Each line has the following layout:
+ * <format string> adr=X ofs=Y <8 bytes hex> <8 bytes hex>
+ */
+
+#define EDEB_DMP(level,adr,len,format,args...) \
+        do { \
+                unsigned int x; \
+		unsigned int l = (unsigned int)(len); \
+                unsigned char *deb = (unsigned char*)(adr); \
+		for (x = 0; x < l; x += 16) { \
+		        EDEB(level, format " adr=%p ofs=%04x %016lx %016lx", \
+			     ##args, deb, x, *((u64 *)&deb[0]), *((u64 *)&deb[8])); \
+			deb += 16; \
+		} \
+        } while (0)
+
+#define LOCATION __FILE__  " "
+
+/* define a bitmask, little endian version */
+#define EHCA_BMASK(pos,length) (((pos)<<16)+(length))
+/* define a bitmask, the ibm way... */
+#define EHCA_BMASK_IBM(from,to) (((63-to)<<16)+((to)-(from)+1))
+/* internal function, don't use */
+#define EHCA_BMASK_SHIFTPOS(mask) (((mask)>>16)&0xffff)
+/* internal function, don't use */
+#define EHCA_BMASK_MASK(mask) (0xffffffffffffffffULL >> ((64-(mask))&0xffff))
+/* return value shifted and masked by mask\n
+ * variable|=HCA_BMASK_SET(MY_MASK,0x4711) ORs the bits in variable\n
+ * variable&=~HCA_BMASK_SET(MY_MASK,-1) clears the bits from the mask 
+ * in variable
+ */
+#define EHCA_BMASK_SET(mask,value) \
+        ((EHCA_BMASK_MASK(mask) & ((u64)(value)))<<EHCA_BMASK_SHIFTPOS(mask))
+/* extract a parameter from value by mask\n
+ * param=EHCA_BMASK_GET(MY_MASK,value)
+ */
+#define EHCA_BMASK_GET(mask,value) \
+        ( EHCA_BMASK_MASK(mask)& (((u64)(value))>>EHCA_BMASK_SHIFTPOS(mask)))
+
+/**
+ * ehca_fixme - Dummy function which will be removed in production code 
+ * to find all todos by compiler.
+ */
+void ehca_fixme(void);
+
+extern void exit(int);
+inline static void ehca_catastrophic(char *str)
+{
+#ifndef  EHCA_USERDRIVER
+	printk(KERN_ERR "HCAD_ERROR %s\n", str);
+	ehca_flight_to_printk();
+#else
+	exit(1);
+#endif
+}
+
+#define PARANOIA_MODE
+#ifdef PARANOIA_MODE
+
+#define EHCA_CHECK_ADR_P(adr)  \
+        if (unlikely(adr==0)) {\
+               EDEB_ERR(4, "adr=%p check failed line %i", adr,\
+                 __LINE__); \
+               return ERR_PTR(-EFAULT); }
+
+#define EHCA_CHECK_ADR(adr)  \
+        if (unlikely(adr==0)) {\
+               EDEB_ERR(4, "adr=%p check failed line %i", adr,\
+               __LINE__); \
+               return -EFAULT; }
+
+#define EHCA_CHECK_DEVICE_P(device)  \
+        if (unlikely(device==0)) {\
+               EDEB_ERR(4, "device=%p check failed", device); \
+               return ERR_PTR(-EFAULT); }
+
+#define EHCA_CHECK_DEVICE(device)  \
+        if (unlikely(device==0)) {\
+               EDEB_ERR(4, "device=%p check failed", device); \
+               return -EFAULT; }
+
+#define EHCA_CHECK_PD(pd)  \
+        if (unlikely(pd==0)) {\
+               EDEB_ERR(4, "pd=%p check failed", pd); \
+               return -EFAULT; }
+
+#define EHCA_CHECK_PD_P(pd)  \
+        if (unlikely(pd==0)) {\
+               EDEB_ERR(4, "pd=%p check failed", pd); \
+               return ERR_PTR(-EFAULT); }
+
+#define EHCA_CHECK_AV(av)  \
+        if (unlikely(av==0)) {\
+               EDEB_ERR(4, "av=%p check failed", av); \
+               return -EFAULT; }
+
+#define EHCA_CHECK_AV_P(av)  \
+        if (unlikely(av==0)) {\
+               EDEB_ERR(4, "av=%p check failed", av); \
+               return ERR_PTR(-EFAULT); }
+
+#define EHCA_CHECK_CQ(cq)  \
+        if (unlikely(cq==0)) {\
+               EDEB_ERR(4, "cq=%p check failed", cq); \
+               return -EFAULT; }
+
+#define EHCA_CHECK_CQ_P(cq)  \
+        if (unlikely(cq==0)) {\
+               EDEB_ERR(4, "cq=%p check failed", cq); \
+               return ERR_PTR(-EFAULT); }
+
+#define EHCA_CHECK_EQ(eq)  \
+        if (unlikely(eq==0)) {\
+               EDEB_ERR(4, "eq=%p check failed", eq); \
+               return -EFAULT; }
+
+#define EHCA_CHECK_EQ_P(eq)  \
+        if (unlikely(eq==0)) {\
+               EDEB_ERR(4, "eq=%p check failed", eq); \
+               return ERR_PTR(-EFAULT); }
+
+#define EHCA_CHECK_QP(qp)  \
+        if (unlikely(qp==0)) {\
+               EDEB_ERR(4, "qp=%p check failed", qp); \
+               return -EFAULT; }
+
+#define EHCA_CHECK_QP_P(qp)  \
+        if (unlikely(qp==0)) {\
+               EDEB_ERR(4, "qp=%p check failed", qp); \
+               return ERR_PTR(-EFAULT); }
+
+#define EHCA_CHECK_MR(mr)  \
+        if (unlikely(mr==0)) {\
+               EDEB_ERR(4, "mr=%p check failed", mr); \
+               return -EFAULT; }
+
+#define EHCA_CHECK_MR_P(mr)  \
+        if (unlikely(mr==0)) {\
+               EDEB_ERR(4, "mr=%p check failed", mr); \
+               return ERR_PTR(-EFAULT); }
+
+#define EHCA_CHECK_MW(mw)  \
+        if (unlikely(mw==0)) {\
+               EDEB_ERR(4, "mw=%p check failed", mw); \
+               return -EFAULT; }
+
+#define EHCA_CHECK_MW_P(mw)  \
+        if (unlikely(mw==0)) {\
+               EDEB_ERR(4, "mw=%p check failed", mw); \
+               return ERR_PTR(-EFAULT); }
+
+#define EHCA_CHECK_FMR(fmr)  \
+        if (unlikely(fmr==0)) {\
+               EDEB_ERR(4, "fmr=%p check failed", fmr); \
+               return -EFAULT; }
+
+#define EHCA_CHECK_FMR_P(fmr)  \
+        if (unlikely(fmr==0)) {\
+               EDEB_ERR(4, "fmr=%p check failed", fmr); \
+               return ERR_PTR(-EFAULT); }
+
+#define EHCA_REGISTER_PD(device,pd)
+#define EHCA_REGISTER_AV(pd,av)
+#define EHCA_DEREGISTER_PD(PD)
+#define EHCA_DEREGISTER_AV(av)
+#else
+#define EHCA_CHECK_DEVICE_P(device)
+
+#define EHCA_CHECK_PD(pd)
+#define EHCA_REGISTER_PD(device,pd)
+#define EHCA_DEREGISTER_PD(PD)
+#endif
+
+/**
+ * ehca2ib_return_code - Returns ib return code corresponding to the given 
+ * ehca return code.
+ */
+static inline int ehca2ib_return_code(u64 ehca_rc)
+{
+	switch (ehca_rc) {
+	case H_Success:
+		return 0;
+	case H_Busy:
+		return -EBUSY;
+	case H_NoMem:
+		return -ENOMEM;
+	default:
+		return -EINVAL;
+	}
+}
+
+#endif /* EHCA_TOOLS_H */
diff -urN oldtree/drivers/infiniband/hw/ehca/ehca_uverbs.c newtree/drivers/infiniband/hw/ehca/ehca_uverbs.c
--- oldtree/drivers/infiniband/hw/ehca/ehca_uverbs.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ehca_uverbs.c	2006-02-13 19:19:59.941521192 -0500
@@ -0,0 +1,376 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  userspace support verbs
+ *
+ *  Authors: Heiko J Schick <schickhj@de.ibm.com>
+ *           Christoph Raisch <raisch@de.ibm.com> 
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ehca_uverbs.c,v 1.27 2005/10/21 07:18:45 craisch Exp $
+ */
+
+#undef DEB_PREFIX
+#define DEB_PREFIX "uver"
+
+#include "ehca_kernel.h"
+#include "ehca_tools.h"
+#include "ehca_classes.h"
+#include "ehca_iverbs.h"
+#include "ehca_eq.h"
+#include "ehca_mrmw.h"
+
+#include "hcp_sense.h"		/* TODO: later via hipz_* header file */
+#include "hcp_if.h"		/* TODO: later via hipz_* header file */
+
+struct ib_ucontext *ehca_alloc_ucontext(struct ib_device *device,
+					struct ib_udata *udata)
+{
+	struct ehca_ucontext *my_context = NULL;
+	EHCA_CHECK_ADR_P(device);
+	EDEB_EN(7, "device=%p name=%s", device, device->name);
+	my_context = kmalloc(sizeof *my_context, GFP_KERNEL);
+	if (NULL == my_context) {
+		EDEB_ERR(4, "Out of memory device=%p", device);
+		return ERR_PTR(-ENOMEM);
+	}
+	memset(my_context, 0, sizeof(*my_context));
+	EDEB_EX(7, "device=%p ucontext=%p", device, my_context);
+	return &my_context->ib_ucontext;
+}
+
+int ehca_dealloc_ucontext(struct ib_ucontext *context)
+{
+	struct ehca_ucontext *my_context = NULL;
+	EHCA_CHECK_ADR(context);
+	EDEB_EN(7, "ucontext=%p", context);
+	my_context = container_of(context, struct ehca_ucontext, ib_ucontext);
+	kfree(my_context);
+	EDEB_EN(7, "ucontext=%p", context);
+	return 0;
+}
+
+struct page *ehca_nopage(struct vm_area_struct *vma,
+			 unsigned long address, int *type)
+{
+	struct page *mypage = 0;
+	u64 fileoffset = vma->vm_pgoff << PAGE_SHIFT;
+	u32 idr_handle = fileoffset >> 32;
+	u32 q_type = (fileoffset >> 28) & 0xF;	  /* CQ, QP,...        */
+	u32 rsrc_type = (fileoffset >> 24) & 0xF; /* sq,rq,cmnd_window */
+
+	EDEB_EN(7, 
+		"vm_start=%lx vm_end=%lx vm_page_prot=%lx vm_fileoff=%lx",
+		vma->vm_start, vma->vm_end, vma->vm_page_prot, fileoffset);
+
+
+	if (q_type == 1) { /* CQ */
+		struct ehca_cq *cq;
+		
+		down_read(&ehca_cq_idr_sem);
+		cq = idr_find(&ehca_cq_idr, idr_handle);
+		up_read(&ehca_cq_idr_sem);
+		
+		/* make sure this mmap really belongs to the authorized user */
+		if (cq == 0) {
+			EDEB_ERR(4, "cq is NULL ret=NOPAGE_SIGBUS");
+			return NOPAGE_SIGBUS;
+		}
+		if (rsrc_type == 2) {
+                        void *vaddr;
+			EDEB(6, "cq=%p cq queuearea", cq);
+			vaddr = address - vma->vm_start
+			    + cq->ehca_cq_core.ipz_queue.queue;
+			EDEB(6, "queue=%p vaddr=%p",
+			     cq->ehca_cq_core.ipz_queue.queue, vaddr);
+			mypage = vmalloc_to_page(vaddr);
+		}
+	} else if (q_type == 2) { /* QP */
+		struct ehca_qp *qp;
+
+		down_read(&ehca_qp_idr_sem);
+		qp = idr_find(&ehca_qp_idr, idr_handle);
+		up_read(&ehca_qp_idr_sem);
+
+		/* make sure this mmap really belongs to the authorized user */
+		if (qp == NULL) {
+			EDEB_ERR(4, "qp is NULL ret=NOPAGE_SIGBUS");
+			return NOPAGE_SIGBUS;
+		}
+		if (rsrc_type == 2) {	/* rqueue */
+                        void *vaddr;
+			EDEB(6, "qp=%p qp rqueuearea", qp);
+			vaddr = address - vma->vm_start
+			    + qp->ehca_qp_core.ipz_rqueue.queue;
+			EDEB(6, "rqueue=%p vaddr=%p",
+			     qp->ehca_qp_core.ipz_rqueue.queue, vaddr);
+			mypage = vmalloc_to_page(vaddr);
+		} else if (rsrc_type == 3) {	/* squeue */
+                        void *vaddr;
+			EDEB(6, "qp=%p qp squeuearea", qp);
+			vaddr = address - vma->vm_start
+			    + qp->ehca_qp_core.ipz_squeue.queue;
+			EDEB(6, "squeue=%p vaddr=%p",
+			     qp->ehca_qp_core.ipz_squeue.queue, vaddr);
+			mypage = vmalloc_to_page(vaddr);
+		}
+	}
+	if (mypage == 0) {
+		EDEB_ERR(4, "Invalid page adr==NULL ret=NOPAGE_SIGBUS");
+		return NOPAGE_SIGBUS;
+	}
+	get_page(mypage);
+	EDEB_EX(7, "page adr=%p", mypage);
+	return mypage;
+}
+
+static struct vm_operations_struct ehcau_vm_ops = {
+	.nopage = ehca_nopage,
+};
+
+/* TODO: better error output messages !!! 
+   NO RETURN WITHOUT ERROR
+ */
+int ehca_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
+{
+	u64 fileoffset = vma->vm_pgoff << PAGE_SHIFT;
+
+
+	u32 idr_handle = fileoffset >> 32;
+	u32 q_type = (fileoffset >> 28) & 0xF;	  /* CQ, QP,...        */
+	u32 rsrc_type = (fileoffset >> 24) & 0xF; /* sq,rq,cmnd_window */
+	u32 ret = -EFAULT;	/* assume the worst             */
+	u64 vsize = 0;		/* must be calculated/set below */
+	u64 physical = 0;	/* must be calculated/set below */
+
+	EDEB_EN(7, "vm_start=%lx vm_end=%lx vm_page_prot=%lx vm_fileoff=%lx",
+		vma->vm_start, vma->vm_end, vma->vm_page_prot, fileoffset);
+
+	if (q_type == 1) { /* CQ */
+		struct ehca_cq *cq;
+
+		down_read(&ehca_cq_idr_sem);
+		cq = idr_find(&ehca_cq_idr, idr_handle);
+		up_read(&ehca_cq_idr_sem);
+
+		/* make sure this mmap really belongs to the authorized user */
+		if (cq == 0)
+			return -EINVAL;
+		if (cq->ib_cq.uobject == 0)
+			return -EINVAL;
+		if (cq->ib_cq.uobject->context != context)
+			return -EINVAL;
+		if (rsrc_type == 1) {	/* galpa fw handle */
+			EDEB(6, "cq=%p cq triggerarea", cq);
+			vma->vm_flags |= VM_RESERVED;
+			vsize = vma->vm_end - vma->vm_start;
+			if (vsize != 4096) {
+				EDEB_ERR(4, "invalid vsize=%lx",
+					 vma->vm_end - vma->vm_start);
+				ret = -EINVAL;
+				goto mmap_exit0;
+			}
+
+			physical = cq->ehca_cq_core.galpas.user.fw_handle;
+			vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+			vma->vm_flags |= VM_IO | VM_RESERVED;
+
+			EDEB(6, "vsize=%lx physical=%lx", vsize,
+			     physical);
+			ret =
+			    remap_pfn_range(vma, vma->vm_start,
+					    physical >> PAGE_SHIFT, vsize,
+					    vma->vm_page_prot);
+			if (ret != 0) {
+				EDEB_ERR(4, 
+					 "Error: remap_pfn_range() returned %x!",
+					 ret);
+				ret = -ENOMEM;
+			}
+			goto mmap_exit0;
+		} else if (rsrc_type == 2) {	/* cq queue_addr */
+			EDEB(6, "cq=%p cq q_addr", cq);
+			/* vma->vm_page_prot = 
+			 * pgprot_noncached(vma->vm_page_prot); */
+			vma->vm_flags |= VM_RESERVED;
+			vma->vm_ops = &ehcau_vm_ops;
+			ret = 0;
+			goto mmap_exit0;
+		} else {
+			EDEB_ERR(6, "bad resource type %x", rsrc_type);
+			ret = -EINVAL;
+			goto mmap_exit0;
+		}
+	} else if (q_type == 2) { /* QP */
+		struct ehca_qp *qp;
+
+		down_read(&ehca_qp_idr_sem);
+		qp = idr_find(&ehca_qp_idr, idr_handle);
+		up_read(&ehca_qp_idr_sem);
+
+		/* make sure this mmap really belongs to the authorized user */
+		if (qp == NULL || qp->ib_qp.uobject == NULL ||
+		    qp->ib_qp.uobject->context != context) {
+			EDEB(6, "qp=%p, uobject=%p, context=%p",
+			     qp, qp->ib_qp.uobject, qp->ib_qp.uobject->context);
+			ret = -EINVAL;
+			goto mmap_exit0;
+		}
+		if (rsrc_type == 1) {	/* galpa fw handle */
+			EDEB(6, "qp=%p qp triggerarea", qp);
+			vma->vm_flags |= VM_RESERVED;
+			vsize = vma->vm_end - vma->vm_start;
+			if (vsize != 4096) {
+				EDEB_ERR(4, "invalid vsize=%lx",
+					 vma->vm_end - vma->vm_start);
+				ret = -EINVAL;
+				goto mmap_exit0;
+			}
+
+			physical = qp->ehca_qp_core.galpas.user.fw_handle;
+			vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+			vma->vm_flags |= VM_IO | VM_RESERVED;
+
+			EDEB(6, "vsize=%lx physical=%lx", vsize,
+			     physical);
+			ret =
+			    remap_pfn_range(vma, vma->vm_start,
+					    physical >> PAGE_SHIFT, vsize,
+					    vma->vm_page_prot);
+			if (ret != 0) {
+				EDEB_ERR(4, 
+					 "Error: remap_pfn_range() returned %x!",
+					 ret);
+				ret = -ENOMEM;
+			}
+			goto mmap_exit0;
+		} else if (rsrc_type == 2) {	/* qp rqueue_addr */
+			EDEB(6, "qp=%p qp rqueue_addr", qp);
+			vma->vm_flags |= VM_RESERVED;
+			vma->vm_ops = &ehcau_vm_ops;
+			ret = 0;
+			goto mmap_exit0;
+		} else if (rsrc_type == 3) {	/* qp squeue_addr */
+			EDEB(6, "qp=%p qp squeue_addr", qp);
+			vma->vm_flags |= VM_RESERVED;
+			vma->vm_ops = &ehcau_vm_ops;
+			ret = 0;
+			goto mmap_exit0;
+		} else {
+			EDEB_ERR(4, "bad resource type %x",
+				 rsrc_type);
+			ret = -EINVAL;
+			goto mmap_exit0;
+		}
+	} else {
+		EDEB_ERR(4, "bad queue type %x", q_type);
+		ret = -EINVAL;
+		goto mmap_exit0;
+	}
+
+      mmap_exit0:
+	EDEB_EX(7, "ret=%x", ret);
+	return ret;
+}
+
+int ehca_mmap_nopage(u64 foffset,u64 length,void ** mapped,struct vm_area_struct ** vma) 
+{
+	down_write(&current->mm->mmap_sem);
+	*mapped=(void*)
+		do_mmap(NULL,0,
+			length,
+			PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS,
+			foffset);
+	up_write(&current->mm->mmap_sem);
+	if (*mapped) {
+		*vma = find_vma(current->mm,(u64)*mapped);
+		if (*vma) {
+			(*vma)->vm_flags |= VM_RESERVED;
+			(*vma)->vm_ops = &ehcau_vm_ops;	
+		} else {
+			EDEB_ERR(4,"couldn't find queue vma queue=%p",
+				 *mapped);
+		}	
+	} else {
+		EDEB_ERR(4,"couldn't create mmap length=%lx",length);
+	}
+	EDEB(7,"mapped=%p",*mapped);
+	return 0;
+}
+
+int ehca_mmap_register(u64 physical,void ** mapped,struct vm_area_struct ** vma) 
+{
+	int ret;
+        unsigned long vsize;
+	ehca_mmap_nopage(0,4096,mapped,vma);
+	(*vma)->vm_flags |= VM_RESERVED;
+	vsize = (*vma)->vm_end - (*vma)->vm_start;
+	if (vsize != 4096) {
+		EDEB_ERR(4, "invalid vsize=%lx",
+			 (*vma)->vm_end - (*vma)->vm_start);
+		ret = -EINVAL;
+		return ret;
+	}
+	
+	(*vma)->vm_page_prot = pgprot_noncached((*vma)->vm_page_prot);
+	(*vma)->vm_flags |= VM_IO | VM_RESERVED;
+	
+	EDEB(6, "vsize=%lx physical=%lx", vsize,
+	     physical);
+	ret =
+		remap_pfn_range((*vma), (*vma)->vm_start,
+				physical >> PAGE_SHIFT, vsize,
+				(*vma)->vm_page_prot);
+	if (ret != 0) {
+		EDEB_ERR(4, 
+			 "Error: remap_pfn_range() returned %x!",
+			 ret);
+		ret = -ENOMEM;
+	}
+	return ret;
+
+}
+
+int ehca_munmap(unsigned long addr, size_t len) {
+	int ret=0;
+	struct mm_struct *mm = current->mm;
+	if (mm!=0) {
+		down_write(&mm->mmap_sem);
+		ret = do_munmap(mm, addr, len);
+		up_write(&mm->mmap_sem);
+	}
+	return ret;
+}
+
+/* eof ehca_uverbs.c */
diff -urN oldtree/drivers/infiniband/hw/ehca/hcp_if.h newtree/drivers/infiniband/hw/ehca/hcp_if.h
--- oldtree/drivers/infiniband/hw/ehca/hcp_if.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/hcp_if.h	2006-02-13 19:19:59.945520584 -0500
@@ -0,0 +1,2022 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  Firmware Infiniband Interface code for POWER
+ *
+ *  Authors: Gerd Bayer <gerd.bayer@de.ibm.com>
+ *           Christoph Raisch <raisch@de.ibm.com>
+ *           Waleri Fomin <fomin@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: hcp_if.h,v 1.60 2005/11/15 13:52:26 schickhj Exp $
+ */
+
+#ifndef __HCP_IF_H__
+#define __HCP_IF_H__
+
+#include "ehca_tools.h"
+#include "hipz_structs.h"
+#include "ehca_classes.h"
+
+#ifndef EHCA_USE_HCALL
+#include "hcz_queue.h"
+#include "hcz_mrmw.h"
+#include "hcz_emmio.h"
+#include <sim_prom.h>
+#endif
+#include "hipz_fns.h"
+#include "hcp_sense.h"
+#include "ehca_irq.h"
+
+#ifndef CONFIG_PPC64
+#ifndef Z_SERIES
+#warning "included with wrong target, this is a p file"
+#endif
+#endif
+
+#ifdef EHCA_USE_HCALL
+
+#ifndef EHCA_USERDRIVER
+#include "hcp_phyp.h"
+#else
+#include <testbench/hcallbridge.h>
+#endif
+#endif
+
+inline static int hcp_galpas_ctor(struct h_galpas *galpas,
+				  u64 paddr_kernel, u64 paddr_user)
+{
+	int rc = 0;
+
+	rc = hcall_map_page(paddr_kernel, &galpas->kernel.fw_handle);
+	if (rc != 0)
+		return (rc);
+
+	galpas->user.fw_handle = paddr_user;
+
+	EDEB(7, "paddr_kernel=%lx paddr_user=%lx galpas->kernel=%lx"
+	     " galpas->user=%lx",
+	     paddr_kernel, paddr_user, galpas->kernel.fw_handle, 
+	     galpas->user.fw_handle);
+
+	return (rc);
+}
+
+inline static int hcp_galpas_dtor(struct h_galpas *galpas)
+{
+	int rc = 0;
+
+	if (galpas->kernel.fw_handle != 0)
+		rc = hcall_unmap_page(galpas->kernel.fw_handle);
+	
+	if (rc != 0)
+		return (rc);
+
+	galpas->user.fw_handle = galpas->kernel.fw_handle = 0;
+
+	return rc;
+}
+
+/**
+ * hipz_h_alloc_resource_eq - Allocate EQ resources in HW and FW, initalize 
+ * resources, create the empty EQPT (ring).
+ *
+ * @eq_handle:         eq handle for this queue
+ * @act_nr_of_entries: actual number of queue entries
+ * @act_pages:         actual number of queue pages
+ * @eq_ist:            used by hcp_H_XIRR() call
+ */
+inline static u64 hipz_h_alloc_resource_eq(const struct
+						      ipz_adapter_handle
+						      hcp_adapter_handle,
+						      struct ehca_pfeq *pfeq,
+						      const u32 neq_control,
+						      const u32
+						      number_of_entries,
+						      struct ipz_eq_handle
+						      *eq_handle,
+						      u32 * act_nr_of_entries,
+						      u32 * act_pages,
+						      u32 * eq_ist)
+{
+	u64 retcode;
+	u64 dummy;
+	u64 act_nr_of_entries_out = 0;
+	u64 act_pages_out         = 0;
+	u64 eq_ist_out            = 0;
+	u64 allocate_controls     = 0;
+	u32 x = (u64)(&x);
+
+	EDEB_EN(7, "pfeq=%p hcp_adapter_handle=%lx  new_control=%x"
+		   " number_of_entries=%x",
+		   pfeq, hcp_adapter_handle.handle, neq_control, 
+		   number_of_entries);
+
+#ifndef EHCA_USE_HCALL
+	retcode = simp_h_alloc_resource_eq(hcp_adapter_handle, pfeq,
+					   neq_control,
+					   number_of_entries,
+					   eq_handle,
+					   act_nr_of_entries,
+					   act_pages, eq_ist);
+#else
+
+	/* resource type */
+	allocate_controls = 3ULL;
+
+	/* ISN is associated */
+	if (neq_control != 1) {
+		allocate_controls = (1ULL << (63 - 7)) | allocate_controls;
+	}
+
+	/* notification event queue */
+	if (neq_control == 1) {
+		allocate_controls = (1ULL << 63) | allocate_controls;
+	}
+
+	retcode = plpar_hcall_7arg_7ret(H_ALLOC_RESOURCE, 
+					hcp_adapter_handle.handle, /* r4 */
+					allocate_controls,	   /* r5 */
+					number_of_entries,	   /* r6 */
+					0, 0, 0, 0,
+					&eq_handle->handle,	   /* r4 */
+					&dummy,	                   /* r5 */
+					&dummy,	                   /* r6 */
+					&act_nr_of_entries_out,	   /* r7 */
+					&act_pages_out,	           /* r8 */
+					&eq_ist_out,               /* r8 */
+					&dummy);
+
+	*act_nr_of_entries = (u32) act_nr_of_entries_out;
+	*act_pages         = (u32) act_pages_out;
+	*eq_ist            = (u32) eq_ist_out;
+
+#endif /* EHCA_USE_HCALL */
+
+	if (retcode == H_NOT_ENOUGH_RESOURCES) {
+		EDEB_ERR(4, "Not enough resource - retcode=%lx ", retcode);
+	}
+
+	EDEB_EX(7, "act_nr_of_entries=%x act_pages=%x eq_ist=%x", 
+		*act_nr_of_entries, *act_pages, *eq_ist);
+
+	return retcode;
+}
+
+static inline u64 hipz_h_reset_event(const struct ipz_adapter_handle
+						hcp_adapter_handle,
+						struct ipz_eq_handle eq_handle,
+						const u64 event_mask)
+{
+	u64 retcode = 0;
+	u64 dummy;
+
+	EDEB_EN(7, "eq_handle=%lx, adapter_handle=%lx  event_mask=%lx",
+		   eq_handle.handle, hcp_adapter_handle.handle, event_mask);
+	
+#ifndef EHCA_USE_HCALL
+	/* TODO: Not implemented yet */
+#else
+
+	retcode = plpar_hcall_7arg_7ret(H_RESET_EVENTS, 
+					hcp_adapter_handle.handle, /* r4 */
+					eq_handle.handle,	   /* r5 */
+					event_mask,	           /* r6 */
+					0, 0, 0, 0,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy);
+#endif
+	EDEB(7, "retcode=%lx", retcode);
+
+	return retcode;
+}
+
+/**
+ * hipz_h_allocate_resource_cq - Allocate CQ resources in HW and FW, initialize 
+ * resources, create the empty CQPT (ring).
+ *
+ * @eq_handle:         eq handle to use for this cq
+ * @cq_handle:         cq handle for this queue 
+ * @act_nr_of_entries: actual number of queue entries
+ * @act_pages:         actual number of queue pages
+ * @galpas:            contain logical adress of priv. storage and 
+ *                     log_user_storage 
+ */
+static inline u64 hipz_h_alloc_resource_cq(const struct
+						      ipz_adapter_handle
+						      hcp_adapter_handle,
+						      struct ehca_pfcq *pfcq,
+						      const struct ipz_eq_handle
+						      eq_handle,
+						      const u32 cq_token,
+						      const u32
+						      number_of_entries,
+						      struct ipz_cq_handle
+						      *cq_handle,
+						      u32 * act_nr_of_entries,
+						      u32 * act_pages,
+						      struct h_galpas *galpas)
+{
+	u64 retcode = 0;
+	u64 dummy;
+	u64 act_nr_of_entries_out;
+	u64 act_pages_out;
+	u64 g_la_privileged_out;
+	u64 g_la_user_out;
+	/* stack location is a unique identifier for a process from beginning 
+	 * to end of this frame */
+	u32 x = (u64)(&x);
+	
+	EDEB_EN(7, "pfcq=%p hcp_adapter_handle=%lx eq_handle=%lx cq_token=%x"
+		" number_of_entries=%x", 
+		pfcq, hcp_adapter_handle.handle, eq_handle.handle, 
+		cq_token, number_of_entries);
+
+#ifndef EHCA_USE_HCALL
+	retcode = simp_h_alloc_resource_cq(hcp_adapter_handle,
+					   pfcq,
+					   eq_handle,
+					   cq_token,
+					   number_of_entries,
+					   cq_handle,
+					   act_nr_of_entries,
+					   act_pages, galpas);
+#else
+	retcode = plpar_hcall_7arg_7ret(H_ALLOC_RESOURCE, 
+					hcp_adapter_handle.handle, /* r4  */
+					2,	                   /* r5  */
+					eq_handle.handle,	   /* r6  */
+					cq_token,	           /* r7  */
+					number_of_entries,	   /* r8  */
+					0, 0,
+					&cq_handle->handle,	   /* r4  */
+					&dummy,	                   /* r5  */
+					&dummy,	                   /* r6  */
+					&act_nr_of_entries_out,	   /* r7  */
+					&act_pages_out,	           /* r8  */
+					&g_la_privileged_out,	   /* r9  */
+					&g_la_user_out);	   /* r10 */
+
+	*act_nr_of_entries = (u32) act_nr_of_entries_out;
+	*act_pages = (u32) act_pages_out;
+
+	if (retcode == 0) {
+		hcp_galpas_ctor(galpas, g_la_privileged_out, g_la_user_out);
+	}
+#endif /* EHCA_US_HCALL */
+
+	if (retcode == H_NOT_ENOUGH_RESOURCES) {
+		EDEB_ERR(4, "Not enough resources. retcode=%lx", retcode);
+	}
+
+	EDEB_EX(7, "cq_handle=%lx act_nr_of_entries=%x act_pages=%x",
+		cq_handle->handle, *act_nr_of_entries, *act_pages);
+
+	return retcode;
+}
+
+#define H_ALL_RES_QP_Enhanced_QP_Operations EHCA_BMASK_IBM(9,11)
+#define H_ALL_RES_QP_QP_PTE_Pin EHCA_BMASK_IBM(12,12)
+#define H_ALL_RES_QP_Service_Type EHCA_BMASK_IBM(13,15)
+#define H_ALL_RES_QP_LL_RQ_CQE_Posting EHCA_BMASK_IBM(18,18)
+#define H_ALL_RES_QP_LL_SQ_CQE_Posting EHCA_BMASK_IBM(19,21)
+#define H_ALL_RES_QP_Signalling_Type EHCA_BMASK_IBM(22,23)
+#define H_ALL_RES_QP_UD_Address_Vector_L_Key_Control EHCA_BMASK_IBM(31,31)
+#define H_ALL_RES_QP_Resource_Type EHCA_BMASK_IBM(56,63)
+
+#define H_ALL_RES_QP_Max_Outstanding_Send_Work_Requests EHCA_BMASK_IBM(0,15)
+#define H_ALL_RES_QP_Max_Outstanding_Receive_Work_Requests EHCA_BMASK_IBM(16,31)
+#define H_ALL_RES_QP_Max_Send_SG_Elements EHCA_BMASK_IBM(32,39)
+#define H_ALL_RES_QP_Max_Receive_SG_Elements EHCA_BMASK_IBM(40,47)
+
+#define H_ALL_RES_QP_Act_Outstanding_Send_Work_Requests EHCA_BMASK_IBM(16,31)
+#define H_ALL_RES_QP_Act_Outstanding_Receive_Work_Requests EHCA_BMASK_IBM(48,63)
+#define H_ALL_RES_QP_Act_Send_SG_Elements EHCA_BMASK_IBM(8,15)
+#define H_ALL_RES_QP_Act_Receeive_SG_Elements EHCA_BMASK_IBM(24,31)
+
+#define H_ALL_RES_QP_Send_Queue_Size_pages EHCA_BMASK_IBM(0,31)
+#define H_ALL_RES_QP_Receive_Queue_Size_pages EHCA_BMASK_IBM(32,63)
+
+/* direct access qp controls */
+#define DAQP_CTRL_ENABLE 0x01
+#define DAQP_CTRL_SEND_COMPLETION 0x20
+#define DAQP_CTRL_RECV_COMPLETION 0x40
+
+/** 
+ * hipz_h_alloc_resource_qp - Allocate QP resources in HW and FW, 
+ * initialize resources, create empty QPPTs (2 rings).
+ *
+ * @h_galpas to access HCA resident QP attributes
+ */
+static inline u64 hipz_h_alloc_resource_qp(const struct
+						      ipz_adapter_handle
+						      adapter_handle,
+						      struct ehca_pfqp *pfqp,
+						      const u8 servicetype, 
+						      const u8 daqp_ctrl,
+						      const u8 signalingtype, 
+						      const u8 ud_av_l_key_ctl, 
+						      const struct ipz_cq_handle send_cq_handle, 
+						      const struct ipz_cq_handle receive_cq_handle, 
+						      const struct ipz_eq_handle async_eq_handle, 
+						      const u32 qp_token, 
+						      const struct ipz_pd pd, 
+						      const u16 max_nr_send_wqes, 
+						      const u16 max_nr_receive_wqes, 
+						      const u8 max_nr_send_sges, 
+						      const u8 max_nr_receive_sges, 
+						      const u32 ud_av_l_key, 
+						      struct ipz_qp_handle *qp_handle,
+						      u32 * qp_nr,
+						      u16 * act_nr_send_wqes,
+						      u16 * act_nr_receive_wqes,
+						      u8 * act_nr_send_sges,
+						      u8 * act_nr_receive_sges,
+						      u32 * nr_sq_pages,
+						      u32 * nr_rq_pages,
+						      struct h_galpas *h_galpas)
+{
+	u64 retcode = H_Success;
+	u64 allocate_controls;
+	u64 max_r10_reg;
+	u64 dummy         = 0;
+	u64 qp_nr_out     = 0;
+	u64 r6_out        = 0;
+	u64 r7_out        = 0;
+	u64 r8_out        = 0;
+	u64 g_la_user_out = 0;
+	u64 r11_out       = 0;
+
+	EDEB_EN(7, "pfqp=%p adapter_handle=%lx servicetype=%x signalingtype=%x"
+		" ud_av_l_key=%x send_cq_handle=%lx receive_cq_handle=%lx"
+		" async_eq_handle=%lx qp_token=%x  pd=%x max_nr_send_wqes=%x"
+		" max_nr_receive_wqes=%x max_nr_send_sges=%x"
+		" max_nr_receive_sges=%x ud_av_l_key=%x galpa.pid=%x",
+		pfqp, adapter_handle.handle, servicetype, signalingtype, 
+		ud_av_l_key, send_cq_handle.handle, 
+		receive_cq_handle.handle, async_eq_handle.handle, qp_token,
+		pd.value, max_nr_send_wqes, max_nr_receive_wqes,
+		max_nr_send_sges, max_nr_receive_sges, ud_av_l_key,
+		h_galpas->pid);
+
+#ifndef EHCA_USE_HCALL
+	retcode = simp_h_alloc_resource_qp(adapter_handle,
+					   pfqp,
+					   servicetype,
+					   signalingtype,
+					   ud_av_l_key_ctl,
+					   send_cq_handle,
+					   receive_cq_handle,
+					   async_eq_handle,
+					   qp_token,
+					   pd,
+					   max_nr_send_wqes,
+					   max_nr_receive_wqes,
+					   max_nr_send_sges,
+					   max_nr_receive_sges,
+					   ud_av_l_key,
+					   qp_handle,
+					   qp_nr,
+					   act_nr_send_wqes,
+					   act_nr_receive_wqes,
+					   act_nr_send_sges,
+					   act_nr_receive_sges,
+					   nr_sq_pages, nr_rq_pages, h_galpas);
+
+#else
+	allocate_controls =
+		EHCA_BMASK_SET(H_ALL_RES_QP_Enhanced_QP_Operations, 
+			       (daqp_ctrl & DAQP_CTRL_ENABLE) ? 1 : 0)
+		| EHCA_BMASK_SET(H_ALL_RES_QP_QP_PTE_Pin, 0)
+		| EHCA_BMASK_SET(H_ALL_RES_QP_Service_Type, servicetype)
+		| EHCA_BMASK_SET(H_ALL_RES_QP_Signalling_Type, signalingtype)
+		| EHCA_BMASK_SET(H_ALL_RES_QP_LL_RQ_CQE_Posting, 
+				 (daqp_ctrl & DAQP_CTRL_RECV_COMPLETION) ? 1 : 0)
+		| EHCA_BMASK_SET(H_ALL_RES_QP_LL_SQ_CQE_Posting, 
+				 (daqp_ctrl & DAQP_CTRL_SEND_COMPLETION) ? 1 : 0)
+		| EHCA_BMASK_SET(H_ALL_RES_QP_UD_Address_Vector_L_Key_Control,
+				 ud_av_l_key_ctl)
+		| EHCA_BMASK_SET(H_ALL_RES_QP_Resource_Type, 1);
+	
+	max_r10_reg =
+		EHCA_BMASK_SET(H_ALL_RES_QP_Max_Outstanding_Send_Work_Requests,
+			       max_nr_send_wqes)
+		| EHCA_BMASK_SET(H_ALL_RES_QP_Max_Outstanding_Receive_Work_Requests,
+				 max_nr_receive_wqes)
+		| EHCA_BMASK_SET(H_ALL_RES_QP_Max_Send_SG_Elements,
+				 max_nr_send_sges)
+		| EHCA_BMASK_SET(H_ALL_RES_QP_Max_Receive_SG_Elements,
+				 max_nr_receive_sges);
+	
+	
+	retcode = plpar_hcall_9arg_9ret(H_ALLOC_RESOURCE, 
+					adapter_handle.handle,	 /* r4  */
+					allocate_controls,	 /* r5  */
+					send_cq_handle.handle,	 /* r6  */
+					receive_cq_handle.handle,/* r7  */
+					async_eq_handle.handle,	 /* r8  */
+					((u64) qp_token << 32) 
+					| pd.value,              /* r9  */
+					max_r10_reg,	         /* r10 */
+					ud_av_l_key,	         /* r11 */
+					0,
+					&qp_handle->handle,	 /* r4  */
+					&qp_nr_out,	         /* r5  */
+					&r6_out,	         /* r6  */
+					&r7_out,	         /* r7  */
+					&r8_out,	         /* r8  */
+					&dummy,	                 /* r9  */
+					&g_la_user_out,	         /* r10 */
+					&r11_out, 
+					&dummy);
+
+	/* extract outputs */
+	*qp_nr = (u32) qp_nr_out;
+	*act_nr_send_wqes = (u16)
+		EHCA_BMASK_GET(H_ALL_RES_QP_Act_Outstanding_Send_Work_Requests,
+			       r6_out);
+	*act_nr_receive_wqes = (u16)
+		EHCA_BMASK_GET(H_ALL_RES_QP_Act_Outstanding_Receive_Work_Requests,
+			       r6_out);
+	*act_nr_send_sges =
+		(u8) EHCA_BMASK_GET(H_ALL_RES_QP_Act_Send_SG_Elements, 
+				    r7_out);
+	*act_nr_receive_sges =
+		(u8) EHCA_BMASK_GET(H_ALL_RES_QP_Act_Receeive_SG_Elements, 
+				    r7_out);
+	*nr_sq_pages =
+		(u32) EHCA_BMASK_GET(H_ALL_RES_QP_Send_Queue_Size_pages, 
+				     r8_out);
+	*nr_rq_pages =
+		(u32) EHCA_BMASK_GET(H_ALL_RES_QP_Receive_Queue_Size_pages, 
+				     r8_out);
+	if (retcode == 0) {
+		hcp_galpas_ctor(h_galpas, g_la_user_out, g_la_user_out);
+	}
+#endif /* EHCA_USE_HCALL */
+	
+	if (retcode == H_NOT_ENOUGH_RESOURCES) {
+		EDEB_ERR(4, "Not enough resources. retcode=%lx",
+			 retcode);
+	}
+	
+	EDEB_EX(7, "qp_nr=%x act_nr_send_wqes=%x"
+		" act_nr_receive_wqes=%x act_nr_send_sges=%x"
+		" act_nr_receive_sges=%x nr_sq_pages=%x"
+		" nr_rq_pages=%x galpa.user=%lx galpa.kernel=%lx",
+		*qp_nr, *act_nr_send_wqes, *act_nr_receive_wqes,
+		*act_nr_send_sges, *act_nr_receive_sges, *nr_sq_pages,
+		*nr_rq_pages, h_galpas->user.fw_handle,
+		h_galpas->kernel.fw_handle);
+	
+	return (retcode);
+}
+
+static inline u64 hipz_h_query_port(const struct ipz_adapter_handle 
+					       hcp_adapter_handle,
+					       const u8 port_id, 
+					       struct query_port_rblock 
+					       *query_port_response_block)
+{
+	u64 retcode = H_Success;
+	u64 dummy;
+	u64 r_cb;
+
+	EDEB_EN(7, "hcp_adapter_handle=%lx port_id %x",
+		hcp_adapter_handle.handle, port_id);
+
+	if ((((u64)query_port_response_block) & 0xfff) != 0) {
+		EDEB_ERR(4, "response block not page aligned");
+		retcode = H_Parameter;
+		return (retcode);
+	}
+
+#ifndef EHCA_USE_HCALL
+	retcode = 0;
+#else
+	r_cb = ehca_kv_to_g(query_port_response_block);
+	
+	retcode = plpar_hcall_7arg_7ret(H_QUERY_PORT, 
+					hcp_adapter_handle.handle, /* r4 */
+					port_id,	           /* r5 */
+					r_cb,	                   /* r6 */
+					0, 0, 0, 0,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy);
+#endif /* EHCA_USE_HCALL */
+
+	EDEB(7, "offset0=%x offset1=%x offset2=%x offset3=%x",
+	     ((u32 *) query_port_response_block)[0],
+	     ((u32 *) query_port_response_block)[1],
+	     ((u32 *) query_port_response_block)[2],
+	     ((u32 *) query_port_response_block)[3]);
+	EDEB(7, "offset4=%x offset5=%x offset6=%x offset7=%x",
+	     ((u32 *) query_port_response_block)[4],
+	     ((u32 *) query_port_response_block)[5],
+	     ((u32 *) query_port_response_block)[6],
+	     ((u32 *) query_port_response_block)[7]);
+	EDEB(7, "offset8=%x offset9=%x offseta=%x offsetb=%x",
+	     ((u32 *) query_port_response_block)[8],
+	     ((u32 *) query_port_response_block)[9],
+	     ((u32 *) query_port_response_block)[10],
+	     ((u32 *) query_port_response_block)[11]);
+	EDEB(7, "offsetc=%x offsetd=%x offsete=%x offsetf=%x",
+	     ((u32 *) query_port_response_block)[12],
+	     ((u32 *) query_port_response_block)[13],
+	     ((u32 *) query_port_response_block)[14],
+	     ((u32 *) query_port_response_block)[15]);
+	EDEB(7, "offset31=%x offset35=%x offset36=%x",
+	     ((u32 *) query_port_response_block)[32],
+	     ((u32 *) query_port_response_block)[36],
+	     ((u32 *) query_port_response_block)[37]);
+	EDEB(7, "offset200=%x offset201=%x offset202=%x "
+	     "offset203=%x",
+	     ((u32 *) query_port_response_block)[0x200],
+	     ((u32 *) query_port_response_block)[0x201],
+	     ((u32 *) query_port_response_block)[0x202],
+	     ((u32 *) query_port_response_block)[0x203]);
+
+	EDEB_EX(7, "retcode=%lx", retcode);
+
+	return retcode;
+}
+
+static inline u64 hipz_h_query_hca(const struct ipz_adapter_handle 
+					      hcp_adapter_handle,
+					      struct query_hca_rblock 
+					      *query_hca_rblock)
+{
+	u64 retcode = 0;
+	u64 dummy;
+	u64 r_cb;
+	EDEB_EN(7, "hcp_adapter_handle=%lx", hcp_adapter_handle.handle);
+	
+	if ((((u64)query_hca_rblock) & 0xfff) != 0) {
+		EDEB_ERR(4, "response block not page aligned");
+		retcode = H_Parameter;
+		return (retcode);
+	}
+
+#ifndef EHCA_USE_HCALL
+	retcode = 0;
+#else
+	r_cb = ehca_kv_to_g(query_hca_rblock);
+	
+	retcode = plpar_hcall_7arg_7ret(H_QUERY_HCA, 
+					hcp_adapter_handle.handle, /* r4 */
+					r_cb,                      /* r5 */
+					0, 0, 0, 0, 0,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy);
+#endif /* EHCA_USE_HCALL */
+
+	EDEB(7, "offset0=%x offset1=%x offset2=%x offset3=%x",
+	     ((u32 *) query_hca_rblock)[0],
+	     ((u32 *) query_hca_rblock)[1],
+	     ((u32 *) query_hca_rblock)[2], ((u32 *) query_hca_rblock)[3]);
+	EDEB(7, "offset4=%x offset5=%x offset6=%x offset7=%x",
+	     ((u32 *) query_hca_rblock)[4],
+	     ((u32 *) query_hca_rblock)[5],
+	     ((u32 *) query_hca_rblock)[6], ((u32 *) query_hca_rblock)[7]);
+	EDEB(7, "offset8=%x offset9=%x offseta=%x offsetb=%x",
+	     ((u32 *) query_hca_rblock)[8],
+	     ((u32 *) query_hca_rblock)[9],
+	     ((u32 *) query_hca_rblock)[10], ((u32 *) query_hca_rblock)[11]);
+	EDEB(7, "offsetc=%x offsetd=%x offsete=%x offsetf=%x",
+	     ((u32 *) query_hca_rblock)[12],
+	     ((u32 *) query_hca_rblock)[13],
+	     ((u32 *) query_hca_rblock)[14], ((u32 *) query_hca_rblock)[15]);
+	EDEB(7, "offset136=%x offset192=%x offset204=%x",
+	     ((u32 *) query_hca_rblock)[32],
+	     ((u32 *) query_hca_rblock)[48], ((u32 *) query_hca_rblock)[51]);
+	EDEB(7, "offset231=%x offset235=%x",
+	     ((u32 *) query_hca_rblock)[57], ((u32 *) query_hca_rblock)[58]);
+	EDEB(7, "offset200=%x offset201=%x offset202=%x offset203=%x",
+	     ((u32 *) query_hca_rblock)[0x201],
+	     ((u32 *) query_hca_rblock)[0x202],
+	     ((u32 *) query_hca_rblock)[0x203],
+	     ((u32 *) query_hca_rblock)[0x204]);
+
+	EDEB_EX(7, "retcode=%lx hcp_adapter_handle=%lx", 
+		retcode, hcp_adapter_handle.handle);
+
+	return retcode;
+}
+
+/**
+ * hipz_h_register_rpage - hcp_if.h internal function for all 
+ * hcp_H_REGISTER_RPAGE calls.
+ *
+ * @logical_address_of_page: kv transformation to GX address in this routine
+ */
+static inline u64 hipz_h_register_rpage(const struct
+						   ipz_adapter_handle
+						   hcp_adapter_handle,
+						   const u8 pagesize,
+						   const u8 queue_type,
+						   const u64 resource_handle,
+						   const u64
+						   logical_address_of_page,
+						   u64 count)
+{
+	u64 retcode = 0;
+	u64 dummy;
+
+	EDEB_EN(7, "hcp_adapter_handle=%lx pagesize=%x queue_type=%x"
+		" resource_handle=%lx logical_address_of_page=%lx count=%lx", 
+		hcp_adapter_handle.handle, pagesize, queue_type, 
+		resource_handle, logical_address_of_page, count);
+	
+#ifndef EHCA_USE_HCALL
+	EDEB_ERR(4, "Not implemented");
+#else
+	retcode = plpar_hcall_7arg_7ret(H_REGISTER_RPAGES,
+					hcp_adapter_handle.handle,  /* r4  */
+					queue_type | pagesize << 8, /* r5  */
+					resource_handle,	    /* r6  */
+					logical_address_of_page,    /* r7  */
+					count,	                    /* r8  */
+					0, 0,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy);
+#endif /* EHCA_USE_HCALL */
+
+	EDEB_EX(7, "retcode=%lx", retcode);
+
+	return retcode;
+}
+
+static inline u64 hipz_h_register_rpage_eq(const struct
+						      ipz_adapter_handle
+						      hcp_adapter_handle,
+						      const struct ipz_eq_handle
+						      eq_handle,
+						      struct ehca_pfeq *pfeq,
+						      const u8 pagesize, 
+						      const u8 queue_type,
+						      const u64
+						      logical_address_of_page,
+						      const u64 count)
+{
+	u64 retcode = 0;
+
+	EDEB_EN(7, "pfeq=%p hcp_adapter_handle=%lx eq_handle=%lx pagesize=%x"
+		" queue_type=%x  logical_address_of_page=%lx count=%lx",
+		pfeq, hcp_adapter_handle.handle, eq_handle.handle, pagesize,
+		queue_type,logical_address_of_page, count);
+	
+#ifndef EHCA_USE_HCALL
+	retcode =
+		simp_h_register_rpage_eq(hcp_adapter_handle, eq_handle, pfeq,
+					 pagesize, queue_type,
+					 logical_address_of_page, count);
+#else
+	if (count != 1) {
+		EDEB_ERR(4, "Ppage counter=%lx", count);
+		return (H_Parameter);
+	}
+	retcode = hipz_h_register_rpage(hcp_adapter_handle,
+					pagesize,
+					queue_type,
+					eq_handle.handle,
+					logical_address_of_page, count);
+#endif /* EHCA_USE_HCALL */
+
+	EDEB_EX(7, "retcode=%lx", retcode);
+
+	return retcode;
+}
+
+static inline u32 hipz_request_interrupt(struct ehca_irq_info *irq_info,
+					 irqreturn_t(*handler)
+					 (int, void *, struct pt_regs *))
+{
+	
+	int ret = 0;
+
+	EDEB_EN(7, "ist=0x%x", irq_info->ist);
+	
+#ifdef EHCA_USE_HCALL
+#ifndef EHCA_USERDRIVER
+	ret = ibmebus_request_irq(NULL, irq_info->ist, handler,
+				  SA_INTERRUPT, "ehca", (void *)irq_info);
+	
+	if (ret < 0)
+		EDEB_ERR(4, "Can't map interrupt handler.");
+#else
+	struct hcall_irq_info hirq = {.irq = irq_info->irq,
+				      .ist = irq_info->ist,
+				      .pid = irq_info->pid};
+
+	hirq = hirq;
+	ret = hcall_reg_eqh(&hirq, ehca_interrupt_eq);
+#endif /* EHCA_USERDRIVER */
+#endif /* EHCA_USE_HCALL  */
+	
+	EDEB_EX(7, "ret=%x", ret);
+	
+	return ret;
+}
+
+static inline void hipz_free_interrupt(struct ehca_irq_info *irq_info)
+{
+#ifdef EHCA_USE_HCALL
+#ifndef EHCA_USERDRIVER
+	ibmebus_free_irq(NULL, irq_info->ist, (void *)irq_info);
+#endif
+#endif
+}
+
+static inline u32 hipz_h_query_int_state(const struct ipz_adapter_handle
+					 hcp_adapter_handle,
+					 struct ehca_irq_info *irq_info)
+{
+	u32 rc = 0;
+	u64 dummy = 0;
+
+	EDEB_EN(7, "ist=0x%x", irq_info->ist);
+
+#ifdef EHCA_USE_HCALL
+#ifdef EHCA_USERDRIVER
+	/* TODO: Not implemented yet */
+#else
+	rc = plpar_hcall_7arg_7ret(H_QUERY_INT_STATE, 
+				   hcp_adapter_handle.handle, /* r4 */
+				   irq_info->ist,             /* r5 */
+				   0, 0, 0, 0, 0,
+				   &dummy,
+				   &dummy,
+				   &dummy,
+				   &dummy,
+				   &dummy,
+				   &dummy,
+				   &dummy);
+
+	if ((rc != H_Success) && (rc != H_Busy))
+		EDEB_ERR(4, "Could not query interrupt state.");
+#endif
+#endif       
+	EDEB_EX(7, "interrupt state: %x", rc);
+	
+	return rc;
+}
+
+static inline u64 hipz_h_register_rpage_cq(const struct
+						      ipz_adapter_handle
+						      hcp_adapter_handle,
+						      const struct ipz_cq_handle
+						      cq_handle,
+						      struct ehca_pfcq *pfcq,
+						      const u8 pagesize, 
+						      const u8 queue_type,
+						      const u64
+						      logical_address_of_page,
+						      const u64 count,
+						      const struct h_galpa gal)
+{
+	u64 retcode = 0;
+
+	EDEB_EN(7, "pfcq=%p hcp_adapter_handle=%lx cq_handle=%lx pagesize=%x"
+		" queue_type=%x  logical_address_of_page=%lx count=%lx", 
+		pfcq, hcp_adapter_handle.handle, cq_handle.handle, pagesize, 
+		queue_type, logical_address_of_page, count);
+
+#ifndef EHCA_USE_HCALL
+	retcode =
+		simp_h_register_rpage_cq(hcp_adapter_handle, cq_handle, pfcq,
+					 pagesize, queue_type,
+					 logical_address_of_page, count, gal);
+#else
+	if (count != 1) {
+		EDEB_ERR(4, "Page counter=%lx", count);
+		return (H_Parameter);
+	}
+	
+	retcode =
+		hipz_h_register_rpage(hcp_adapter_handle, pagesize, queue_type,
+				      cq_handle.handle, logical_address_of_page,
+				      count);
+#endif /* EHCA_USE_HCALL */
+	EDEB_EX(7, "retcode=%lx", retcode);
+
+	return retcode;
+}
+
+static inline u64 hipz_h_register_rpage_qp(const struct
+						      ipz_adapter_handle
+						      hcp_adapter_handle,
+						      const struct ipz_qp_handle
+						      qp_handle,
+						      struct ehca_pfqp *pfqp,
+						      const u8 pagesize,
+						      const u8 queue_type,
+						      const u64
+						      logical_address_of_page,
+						      const u64 count,
+						      const struct h_galpa
+						      galpa)
+{
+	u64 retcode = 0;
+	
+	EDEB_EN(7, "pfqp=%p hcp_adapter_handle=%lx qp_handle=%lx pagesize=%x"
+		" queue_type=%x  logical_address_of_page=%lx count=%lx", 
+		pfqp, hcp_adapter_handle.handle, qp_handle.handle, pagesize, 
+		queue_type, logical_address_of_page, count);
+
+#ifndef EHCA_USE_HCALL
+	retcode = simp_h_register_rpage_qp(hcp_adapter_handle,
+					   qp_handle,
+					   pfqp,
+					   pagesize,
+					   queue_type,
+					   logical_address_of_page,
+					   count, galpa);
+#else
+	if (count != 1) {
+		EDEB_ERR(4, "Page counter=%lx", count);
+		return (H_Parameter);
+	}
+	
+	retcode = hipz_h_register_rpage(hcp_adapter_handle,
+					pagesize,
+					queue_type,
+					qp_handle.handle,
+					logical_address_of_page, count);
+#endif /* EHCA_USE_HCALL */
+	EDEB_EX(7, "retcode=%lx", retcode);
+	
+	return retcode;
+}
+
+static inline u64 hipz_h_remove_rpt_cq(const struct
+						  ipz_adapter_handle
+						  hcp_adapter_handle,
+						  const struct ipz_cq_handle
+						  cq_handle,
+						  struct ehca_pfcq *pfcq)
+{
+	u64 retcode = 0;
+
+	EDEB_EN(7, "pfcq=%p hcp_adapter_handle=%lx  cq_handle=%lx",
+		pfcq, hcp_adapter_handle.handle, cq_handle.handle);
+
+#ifndef EHCA_USE_HCALL
+	retcode = simp_h_remove_rpt_cq(hcp_adapter_handle, cq_handle, pfcq);
+#else
+	/* TODO: hcall not implemented */
+#endif
+	EDEB_EX(7, "retcode=%lx", retcode);
+	
+	return 0;
+}
+
+static inline u64 hipz_h_remove_rpt_eq(const struct
+						  ipz_adapter_handle
+						  hcp_adapter_handle,
+						  const struct ipz_eq_handle
+						  eq_handle,
+						  struct ehca_pfeq *pfeq)
+{
+	u64 retcode = 0;
+
+	EDEB_EX(7, "hcp_adapter_handle=%lx eq_handle=%lx",
+		hcp_adapter_handle.handle, eq_handle.handle);
+
+#ifndef EHCA_USE_HCALL
+	retcode = simp_h_remove_rpt_eq(hcp_adapter_handle, eq_handle, pfeq);
+#else
+	/* TODO: hcall not implemented */
+#endif
+	EDEB_EX(7, "retcode=%lx", retcode);
+	
+	return 0;
+}
+
+static inline u64 hipz_h_remove_rpt_qp(const struct
+						  ipz_adapter_handle
+						  hcp_adapter_handle,
+						  const struct ipz_qp_handle
+						  qp_handle,
+						  struct ehca_pfqp *pfqp)
+{
+	u64 retcode = 0;
+
+	EDEB_EN(7, "pfqp=%p hcp_adapter_handle=%lx qp_handle=%lx",
+		pfqp, hcp_adapter_handle.handle, qp_handle.handle);
+
+#ifndef EHCA_USE_HCALL
+	retcode = simp_h_remove_rpt_qp(hcp_adapter_handle, qp_handle, pfqp);
+#else
+	/* TODO: hcall not implemented */
+#endif
+	EDEB_EX(7, "retcode=%lx", retcode);
+	
+	return 0;
+}
+
+static inline u64 hipz_h_disable_and_get_wqe(const struct
+							ipz_adapter_handle
+							hcp_adapter_handle,
+							const struct
+							ipz_qp_handle qp_handle,
+							struct ehca_pfqp *pfqp,
+							void **log_addr_next_sq_wqe_tb_processed,
+							void **log_addr_next_rq_wqe_tb_processed,
+							int dis_and_get_function_code)
+{
+	u64 retcode = 0;
+	u8 function_code = 1;
+	u64 dummy, dummy1, dummy2;
+
+	EDEB_EN(7, "pfqp=%p hcp_adapter_handle=%lx function=%x qp_handle=%lx",
+		pfqp, hcp_adapter_handle.handle, function_code, qp_handle.handle);
+
+	if (log_addr_next_sq_wqe_tb_processed==NULL) {
+		log_addr_next_sq_wqe_tb_processed = (void**)&dummy1;
+	}
+	if (log_addr_next_rq_wqe_tb_processed==NULL) {
+		log_addr_next_rq_wqe_tb_processed = (void**)&dummy2;
+	}
+#ifndef EHCA_USE_HCALL
+	retcode =
+		simp_h_disable_and_get_wqe(hcp_adapter_handle, qp_handle, pfqp,
+					   log_addr_next_sq_wqe_tb_processed,
+					   log_addr_next_rq_wqe_tb_processed);
+#else
+	
+	retcode = plpar_hcall_7arg_7ret(H_DISABLE_AND_GETC, 
+					hcp_adapter_handle.handle, /* r4 */
+					dis_and_get_function_code, /* r5 */
+				        /* function code 1-disQP ret 
+					 * SQ RQ wqe ptr
+					 * 2- ret SQ wqe ptr 
+					 * 3- ret. RQ count */
+					qp_handle.handle,	   /* r6 */
+					0, 0, 0, 0,
+					(void*)log_addr_next_sq_wqe_tb_processed, /* r4 */
+					(void*)log_addr_next_rq_wqe_tb_processed, /* r5 */
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy);
+#endif /* EHCA_USE_HCALL */
+	EDEB_EX(7, "retcode=%lx  ladr_next_rq_wqe_out=%p"
+		" ladr_next_sq_wqe_out=%p", retcode,
+		*log_addr_next_sq_wqe_tb_processed,
+		*log_addr_next_rq_wqe_tb_processed);
+
+	return retcode;
+}
+
+enum hcall_sigt {
+	HCALL_SIGT_NO_CQE = 0,
+	HCALL_SIGT_BY_WQE = 1,
+	HCALL_SIGT_EVERY = 2
+};
+
+static inline u64 hipz_h_modify_qp(const struct ipz_adapter_handle
+					      hcp_adapter_handle,
+					      const struct ipz_qp_handle
+					      qp_handle, struct ehca_pfqp *pfqp,
+					      const u64 update_mask,
+					      struct hcp_modify_qp_control_block
+					      *mqpcb,				      
+					      struct h_galpa gal)
+{
+	u64 retcode = 0;
+	u64 invalid_attribute_identifier = 0;
+	u64 rc_attrib_mask = 0;
+	u64 dummy;
+	u64 r_cb;
+	EDEB_EN(7, "pfqp=%p hcp_adapter_handle=%lx qp_handle=%lx"
+		   " update_mask=%lx qp_state=%x mqpcb=%p",
+		   pfqp, hcp_adapter_handle.handle, qp_handle.handle,
+		   update_mask, mqpcb->qp_state, mqpcb);
+
+#ifndef EHCA_USE_HCALL
+	simp_h_modify_qp(hcp_adapter_handle, qp_handle, pfqp, update_mask,
+			 mqpcb, gal);
+#else
+	r_cb = ehca_kv_to_g(mqpcb);
+	retcode = plpar_hcall_7arg_7ret(H_MODIFY_QP, 
+					hcp_adapter_handle.handle,     /* r4 */
+					qp_handle.handle,	       /* r5 */
+					update_mask,	               /* r6 */
+					r_cb,	                       /* r7 */
+					0, 0, 0,
+					&invalid_attribute_identifier, /* r4 */
+					&dummy,	                       /* r5 */
+					&dummy,	                       /* r6 */
+					&dummy,                        /* r7 */
+					&dummy,	                       /* r8 */
+					&rc_attrib_mask,               /* r9 */
+					&dummy);
+#endif
+	if (retcode == H_NOT_ENOUGH_RESOURCES) {
+		EDEB_ERR(4, "Insufficient resources retcode=%lx", retcode);
+	}
+
+	EDEB_EX(7, "retcode=%lx invalid_attribute_identifier=%lx"
+		" invalid_attribute_MASK=%lx", retcode,
+		invalid_attribute_identifier, rc_attrib_mask);
+	
+	return retcode;
+}
+
+static inline u64 hipz_h_query_qp(const struct ipz_adapter_handle
+					     hcp_adapter_handle,
+					     const struct ipz_qp_handle
+					     qp_handle, struct ehca_pfqp *pfqp,
+					     struct hcp_modify_qp_control_block
+					     *qqpcb, struct h_galpa gal)
+{
+	u64 retcode = 0;
+	u64 dummy;
+	u64 r_cb;
+	EDEB_EN(7, "hcp_adapter_handle=%lx qp_handle=%lx",
+		hcp_adapter_handle.handle, qp_handle.handle);
+	
+#ifndef EHCA_USE_HCALL
+	simp_h_query_qp(hcp_adapter_handle, qp_handle, qqpcb, gal);
+#else
+	r_cb = ehca_kv_to_g(qqpcb);
+	EDEB(7, "r_cb=%lx", r_cb);
+
+	retcode = plpar_hcall_7arg_7ret(H_QUERY_QP, 
+					hcp_adapter_handle.handle, /* r4 */
+					qp_handle.handle,          /* r5 */
+					r_cb,	                   /* r6 */
+					0, 0, 0, 0,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy);
+
+#endif
+	EDEB_EX(7, "retcode=%lx", retcode);
+	
+	return retcode;
+}
+
+static inline u64 hipz_h_destroy_qp(const struct ipz_adapter_handle
+					       hcp_adapter_handle,
+					       struct ehca_qp *qp)
+{
+	u64 retcode = 0;
+	u64 dummy;
+	u64 ladr_next_sq_wqe_out;
+	u64 ladr_next_rq_wqe_out;
+
+	EDEB_EN(7, "qp = %p ,ipz_qp_handle=%lx adapter_handle=%lx",
+		qp, qp->ipz_qp_handle.handle, hcp_adapter_handle.handle);
+
+#ifndef EHCA_USE_HCALL
+	retcode =
+		simp_h_destroy_qp(hcp_adapter_handle, qp,
+				  qp->ehca_qp_core.galpas.user);
+#else
+	
+	retcode = hcp_galpas_dtor(&qp->ehca_qp_core.galpas);
+	
+	retcode = plpar_hcall_7arg_7ret(H_DISABLE_AND_GETC, 
+					hcp_adapter_handle.handle, /* r4 */
+					/* function code */
+					1,	                   /* r5 */
+					qp->ipz_qp_handle.handle,  /* r6 */
+					0, 0, 0, 0,
+					&ladr_next_sq_wqe_out,     /* r4 */
+					&ladr_next_rq_wqe_out,     /* r5 */
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy);
+	if (retcode == H_Hardware) {
+		EDEB_ERR(4, "HCA not operational. retcode=%lx", retcode);
+	}
+
+	retcode = plpar_hcall_7arg_7ret(H_FREE_RESOURCE, 
+					hcp_adapter_handle.handle, /* r4 */
+					qp->ipz_qp_handle.handle,  /* r5 */
+					0, 0, 0, 0, 0,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy);
+#endif /* EHCA_USE_HCALL */
+
+	if (retcode == H_Resource) {
+		EDEB_ERR(4, "Resource still in use. retcode=%lx", retcode);
+	}
+	EDEB_EX(7, "retcode=%lx", retcode);
+
+	return retcode;
+}
+
+static inline u64 hipz_h_define_aqp0(const struct ipz_adapter_handle
+						hcp_adapter_handle,
+						const struct ipz_qp_handle
+						qp_handle, struct h_galpa gal,
+						u32 port)
+{
+	u64 retcode = 0;
+	u64 dummy;
+
+	EDEB_EN(7, "port=%x ipz_qp_handle=%lx adapter_handle=%lx",
+		port, qp_handle.handle, hcp_adapter_handle.handle);
+
+#ifndef EHCA_USE_HCALL
+	/* TODO: not implemented yet */
+#else
+
+	retcode = plpar_hcall_7arg_7ret(H_DEFINE_AQP0, 
+					hcp_adapter_handle.handle, /* r4 */
+					qp_handle.handle,	   /* r5 */
+					port,                      /* r6 */
+					0, 0, 0, 0,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy);
+
+#endif /* EHCA_USE_HCALL */
+	EDEB_EX(7, "retcode=%lx", retcode);
+
+	return retcode;
+}
+
+static inline u64 hipz_h_define_aqp1(const struct ipz_adapter_handle
+						hcp_adapter_handle,
+						const struct ipz_qp_handle
+						qp_handle, struct h_galpa gal,
+						u32 port, u32 * pma_qp_nr,
+						u32 * bma_qp_nr)
+{
+	u64 retcode = 0;
+	u64 dummy;
+	u64 pma_qp_nr_out;
+	u64 bma_qp_nr_out;
+
+	EDEB_EN(7, "port=%x qp_handle=%lx adapter_handle=%lx",
+		port, qp_handle.handle, hcp_adapter_handle.handle);
+
+#ifndef EHCA_USE_HCALL
+	/* TODO: not implemented yet */
+#else
+	
+	retcode = plpar_hcall_7arg_7ret(H_DEFINE_AQP1, 
+					hcp_adapter_handle.handle, /* r4 */
+					qp_handle.handle,	   /* r5 */
+					port,	                   /* r6 */
+					0, 0, 0, 0,
+					&pma_qp_nr_out,            /* r4 */
+					&bma_qp_nr_out,            /* r5 */
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy);
+	*pma_qp_nr = (u32) pma_qp_nr_out;
+	*bma_qp_nr = (u32) bma_qp_nr_out;
+	
+#endif
+	if (retcode == H_ALIAS_EXIST) {
+		EDEB_ERR(4, "AQP1 already exists. retcode=%lx", retcode);
+	}
+
+	EDEB_EX(7, "retcode=%lx pma_qp_nr=%i bma_qp_nr=%i", 
+		retcode, (int)*pma_qp_nr, (int)*bma_qp_nr);
+
+	return retcode;
+}
+
+/* TODO: Don't use ib_* types in this file */
+static inline u64 hipz_h_attach_mcqp(const struct ipz_adapter_handle
+						hcp_adapter_handle,
+						const struct ipz_qp_handle
+						qp_handle, struct h_galpa gal,
+						u16 mcg_dlid, union ib_gid dgid)
+{
+	u64 retcode = 0;
+	u64 dummy;
+			       
+	EDEB_EN(7, "qp_handle=%lx adapter_handle=%lx\nMCG_DGID ="
+		" %d.%d.%d.%d.%d.%d.%d.%d."
+		" %d.%d.%d.%d.%d.%d.%d.%d\n",
+		qp_handle.handle, hcp_adapter_handle.handle,
+		dgid.raw[0], dgid.raw[1],
+		dgid.raw[2], dgid.raw[3],
+		dgid.raw[4], dgid.raw[5],
+		dgid.raw[6], dgid.raw[7],
+		dgid.raw[0 + 8], dgid.raw[1 + 8],
+		dgid.raw[2 + 8], dgid.raw[3 + 8],
+		dgid.raw[4 + 8], dgid.raw[5 + 8],
+		dgid.raw[6 + 8], dgid.raw[7 + 8]);
+	
+#ifndef EHCA_USE_HCALL
+	/* TODO: not implemented yet */
+#else
+	retcode = plpar_hcall_7arg_7ret(H_ATTACH_MCQP, 
+					hcp_adapter_handle.handle, /* r4 */
+					qp_handle.handle,          /* r5 */
+					mcg_dlid,                  /* r6 */     
+					dgid.global.interface_id,  /* r7 */
+					dgid.global.subnet_prefix, /* r8 */
+					0, 0,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy);
+#endif /* EHCA_USE_HCALL */
+	if (retcode == H_NOT_ENOUGH_RESOURCES) {
+		EDEB_ERR(4, "Not enough resources. retcode=%lx", retcode);
+	}
+
+	EDEB_EX(7, "retcode=%lx", retcode);
+
+	return retcode;
+}
+
+static inline u64 hipz_h_detach_mcqp(const struct ipz_adapter_handle
+						hcp_adapter_handle,
+						const struct ipz_qp_handle
+						qp_handle, struct h_galpa gal,
+						u16 mcg_dlid, union ib_gid dgid)
+{
+	u64 retcode = 0;
+	u64 dummy;
+
+	EDEB_EN(7, "qp_handle=%lx adapter_handle=%lx\nMCG_DGID ="
+		" %d.%d.%d.%d.%d.%d.%d.%d."
+		" %d.%d.%d.%d.%d.%d.%d.%d\n",
+		qp_handle.handle, hcp_adapter_handle.handle,
+		dgid.raw[0], dgid.raw[1],
+		dgid.raw[2], dgid.raw[3],
+		dgid.raw[4], dgid.raw[5],
+		dgid.raw[6], dgid.raw[7],
+		dgid.raw[0 + 8], dgid.raw[1 + 8],
+		dgid.raw[2 + 8], dgid.raw[3 + 8],
+		dgid.raw[4 + 8], dgid.raw[5 + 8],
+		dgid.raw[6 + 8], dgid.raw[7 + 8]);
+#ifndef EHCA_USE_HCALL
+	/* TODO: not implemented yet */
+#else
+	retcode = plpar_hcall_7arg_7ret(H_DETACH_MCQP, 
+					hcp_adapter_handle.handle, /* r4 */
+					qp_handle.handle,	   /* r5 */
+					mcg_dlid,	           /* r6 */
+					dgid.global.interface_id,  /* r7 */
+					dgid.global.subnet_prefix, /* r8 */
+					0, 0,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy);
+#endif /* EHCA_USE_HCALL */
+	EDEB(7, "retcode=%lx", retcode);
+
+	return retcode;
+}
+
+static inline u64 hipz_h_destroy_cq(const struct ipz_adapter_handle
+					       hcp_adapter_handle,
+					       struct ehca_cq *cq,
+					       u8 force_flag)
+{
+	u64 retcode = 0;
+	u64 dummy;
+
+	EDEB_EN(7, "cq->pf=%p cq=.%p ipz_cq_handle=%lx adapter_handle=%lx", 
+		&cq->pf, cq, cq->ipz_cq_handle.handle, hcp_adapter_handle.handle);
+	
+#ifndef EHCA_USE_HCALL
+	simp_h_destroy_cq(hcp_adapter_handle, cq,
+			  cq->ehca_cq_core.galpas.kernel);
+#else
+	retcode = hcp_galpas_dtor(&cq->ehca_cq_core.galpas);
+	if (retcode != 0) {
+		EDEB_ERR(4, "Could not destruct cp->galpas");
+		return (H_Resource);
+	}
+
+	retcode = plpar_hcall_7arg_7ret(H_FREE_RESOURCE, 
+					hcp_adapter_handle.handle, /* r4 */
+					cq->ipz_cq_handle.handle,  /* r5 */
+					force_flag!=0 ? 1L : 0L,   /* r6 */
+					0, 0, 0, 0,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy);
+#endif
+
+	if (retcode == H_Resource) {
+		EDEB(4, "retcode=%lx ", retcode);
+	}
+
+	EDEB_EX(7, "retcode=%lx", retcode);
+
+	return retcode;
+}
+
+static inline u64 hipz_h_destroy_eq(const struct ipz_adapter_handle
+					       hcp_adapter_handle,
+					       struct ehca_eq *eq)
+{
+	u64 retcode = 0;
+	u64 dummy;
+
+	EDEB_EN(7, "eq->pf=%p eq=%p ipz_eq_handle=%lx adapter_handle=%lx", 
+		&eq->pf, eq, eq->ipz_eq_handle.handle, 
+		hcp_adapter_handle.handle);
+
+#ifndef EHCA_USE_HCALL
+	/* TODO: not implemeted et */
+#else
+
+	retcode = hcp_galpas_dtor(&eq->galpas);
+	if (retcode != 0) {
+		EDEB_ERR(4, "Could not destruct ep->galpas");
+		return (H_Resource);
+	}
+
+	retcode = plpar_hcall_7arg_7ret(H_FREE_RESOURCE, 
+					hcp_adapter_handle.handle, /* r4 */
+					eq->ipz_eq_handle.handle,  /* r5 */
+					0, 0, 0, 0, 0,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy,
+					&dummy);
+
+#endif
+	if (retcode == H_Resource) {
+		EDEB_ERR(4, "Resource in use. retcode=%lx ", retcode);
+	}
+	EDEB_EX(7, "retcode=%lx", retcode);
+
+	return retcode;
+}
+
+/**
+ * hipz_h_alloc_resource_mr - Allocate MR resources in HW and FW, initialize 
+ * resources.
+ *
+ * @pfmr:        platform specific for MR
+ * pfshca:       platform specific for SHCA 
+ * vaddr:        Memory Region I/O Virtual Address
+ * @length:      Memory Region Length
+ * @access_ctrl: Memory Region Access Controls
+ * @pd:          Protection Domain
+ * @mr_handle:   Memory Region Handle
+ */
+static inline u64 hipz_h_alloc_resource_mr(const struct ipz_adapter_handle 
+						      hcp_adapter_handle, 
+						      struct ehca_pfmr *pfmr,	
+						      struct ehca_pfshca 
+						      *pfshca,
+						      const u64 vaddr,
+						      const u64 length,
+						      const u32 access_ctrl,
+						      const struct ipz_pd pd,
+						      struct ipz_mrmw_handle 
+						      *mr_handle,
+						      u32 * lkey,
+						      u32 * rkey)
+{
+	u64 rc = H_Success;
+	u64 dummy;
+	u64 lkey_out;
+	u64 rkey_out;
+
+	EDEB_EN(7, "hcp_adapter_handle=%lx pfmr=%p vaddr=%lx length=%lx"
+		" access_ctrl=%x pd=%x pfshca=%p",
+		hcp_adapter_handle.handle, pfmr, vaddr, length, access_ctrl,
+		pd.value, pfshca);
+
+#ifndef EHCA_USE_HCALL
+	rc = simp_hcz_h_alloc_resource_mr(hcp_adapter_handle,
+					  pfmr,
+					  pfshca,
+					  vaddr,
+					  length,
+					  access_ctrl,
+					  pd,
+					  (struct hcz_mrmw_handle *)mr_handle,
+					  lkey, rkey);
+	EDEB_EX(7, "rc=%lx mr_handle.mrwpte=%p mr_handle.page_index=%x"
+		" lkey=%x rkey=%x", 
+		rc, mr_handle->mrwpte, mr_handle->page_index, *lkey, *rkey);
+#else
+
+	rc = plpar_hcall_7arg_7ret(H_ALLOC_RESOURCE, 
+				   hcp_adapter_handle.handle,        /* r4 */
+				   5,                                /* r5 */
+				   vaddr,                            /* r6 */
+				   length,                           /* r7 */
+				   ((((u64) access_ctrl) << 32ULL)), /* r8 */
+				   pd.value,                         /* r9 */
+				   0, 
+				   &mr_handle->handle,               /* r4 */
+				   &dummy,                           /* r5 */
+				   &lkey_out,                        /* r6 */
+				   &rkey_out,                        /* r7 */
+				   &dummy,
+				   &dummy,
+				   &dummy);
+	*lkey = (u32) lkey_out;
+	*rkey = (u32) rkey_out;
+
+	EDEB_EX(7, "rc=%lx mr_handle=%lx lkey=%x rkey=%x",
+		rc, mr_handle->handle, *lkey, *rkey);
+#endif /* EHCA_USE_HCALL */
+
+	return rc;
+}
+
+/**
+ * hipz_h_register_rpage_mr - Register MR resource page in HW and FW .
+ *
+ * @pfmr:       platform specific for MR
+ * @pfshca:     platform specific for SHCA 
+ * @queue_type: must be zero for MR
+ */
+static inline u64 hipz_h_register_rpage_mr(const struct ipz_adapter_handle 
+						      hcp_adapter_handle, 
+						      const struct ipz_mrmw_handle 
+						      *mr_handle, 
+						      struct ehca_pfmr *pfmr,
+						      struct ehca_pfshca *pfshca,
+						      const u8 pagesize, 
+						      const u8 queue_type,
+						      const u64
+						      logical_address_of_page,
+						      const u64 count)
+{
+	u64 rc = H_Success;
+
+#ifndef EHCA_USE_HCALL
+	EDEB_EN(7, "hcp_adapter_handle=%lx pfmr=%p mr_handle.mrwpte=%p"
+		" mr_handle.page_index=%x pagesize=%x queue_type=%x "
+		" logical_address_of_page=%lx count=%lx pfshca=%p",
+		hcp_adapter_handle.handle, pfmr, mr_handle->mrwpte,
+		mr_handle->page_index, pagesize, queue_type,
+		logical_address_of_page, count, pfshca);
+
+	rc = simp_hcz_h_register_rpage_mr(hcp_adapter_handle,
+					  (struct hcz_mrmw_handle *)mr_handle,
+					  pfmr,
+					  pfshca,
+					  pagesize,
+					  queue_type,
+					  logical_address_of_page, count);
+#else
+	EDEB_EN(7, "hcp_adapter_handle=%lx pfmr=%p mr_handle=%lx pagesize=%x"
+		" queue_type=%x logical_address_of_page=%lx count=%lx", 
+		hcp_adapter_handle.handle, pfmr, mr_handle->handle, pagesize, 
+		queue_type, logical_address_of_page, count);
+
+	if ((count > 1) && (logical_address_of_page & 0xfff)) {
+		ehca_catastrophic("ERROR: logical_address_of_page "
+				  "not on a 4k boundary");
+		rc = H_Parameter;
+	} else {
+		rc = hipz_h_register_rpage(hcp_adapter_handle, pagesize,
+					   queue_type, mr_handle->handle,
+					   logical_address_of_page, count);
+	}
+#endif /* EHCA_USE_HCALL */
+	EDEB_EX(7, "rc=%lx", rc);
+
+	return rc;
+}
+
+/**
+ * hipz_h_query_mr - Query MR in HW and FW.
+ *
+ * @pfmr:             platform specific for MR
+ * @mr_handle:        Memory Region Handle
+ * @mr_local_length:  Local MR Length
+ * @mr_local_vaddr:   Local MR I/O Virtual Address
+ * @mr_remote_length: Remote MR Length
+ * @mr_remote_vaddr   Remote MR I/O Virtual Address
+ * @access_ctrl:      Memory Region Access Controls
+ * @pd:               Protection Domain 
+ * lkey:              L_Key
+ * rkey:              R_Key
+ */
+static inline u64 hipz_h_query_mr(const struct ipz_adapter_handle 
+					     hcp_adapter_handle, 
+					     struct ehca_pfmr *pfmr,
+					     const struct ipz_mrmw_handle 
+					     *mr_handle,
+					     u64 * mr_local_length,
+					     u64 * mr_local_vaddr,
+					     u64 * mr_remote_length,
+					     u64 * mr_remote_vaddr,
+					     u32 * access_ctrl,
+					     struct ipz_pd *pd,
+					     u32 * lkey,
+					     u32 * rkey)
+{
+	u64 rc = H_Success;
+	u64 dummy;
+	u64 acc_ctrl_pd_out;
+	u64 r9_out;
+
+#ifndef EHCA_USE_HCALL
+	EDEB_EN(7, "hcp_adapter_handle=%lx pfmr=%p mr_handle.mrwpte=%p"
+		" mr_handle.page_index=%x",
+		hcp_adapter_handle.handle, pfmr, mr_handle->mrwpte,
+		mr_handle->page_index);
+
+	rc = simp_hcz_h_query_mr(hcp_adapter_handle,
+				 pfmr,
+				 mr_handle,
+				 mr_local_length,
+				 mr_local_vaddr,
+				 mr_remote_length,
+				 mr_remote_vaddr, access_ctrl, pd, lkey, rkey);
+
+	EDEB_EX(7, "rc=%lx mr_local_length=%lx mr_local_vaddr=%lx"
+		" mr_remote_length=%lx mr_remote_vaddr=%lx access_ctrl=%x"
+		" pd=%x lkey=%x rkey=%x", 
+		rc, *mr_local_length, *mr_local_vaddr, *mr_remote_length,
+		*mr_remote_vaddr, *access_ctrl, pd->value, *lkey, *rkey);
+#else
+	EDEB_EN(7, "hcp_adapter_handle=%lx pfmr=%p mr_handle=%lx",
+		hcp_adapter_handle.handle, pfmr, mr_handle->handle);
+
+
+	rc = plpar_hcall_7arg_7ret(H_QUERY_MR, 
+				   hcp_adapter_handle.handle, /* r4 */
+				   mr_handle->handle,         /* r5 */
+				   0, 0, 0, 0, 0,
+				   mr_local_length,           /* r4 */
+				   mr_local_vaddr,            /* r5 */
+				   mr_remote_length,          /* r6 */
+				   mr_remote_vaddr,           /* r7 */
+				   &acc_ctrl_pd_out,          /* r8 */
+				   &r9_out,
+				   &dummy);
+
+	*access_ctrl = acc_ctrl_pd_out >> 32;
+	pd->value = (u32) acc_ctrl_pd_out;
+	*lkey = (u32) (r9_out >> 32);
+	*rkey = (u32) (r9_out & (0xffffffff));
+
+	EDEB_EX(7, "rc=%lx mr_local_length=%lx mr_local_vaddr=%lx"
+		" mr_remote_length=%lx mr_remote_vaddr=%lx access_ctrl=%x"
+		" pd=%x lkey=%x rkey=%x", 
+		rc, *mr_local_length, *mr_local_vaddr, *mr_remote_length,
+		*mr_remote_vaddr, *access_ctrl, pd->value, *lkey, *rkey);
+#endif /* EHCA_USE_HCALL */
+
+	return rc;
+}
+
+/**
+ * hipz_h_free_resource_mr - Free MR resources in HW and FW.
+ *
+ * @pfmr:      platform specific for MR
+ * @mr_handle: Memory Region Handle
+ */
+static inline u64 hipz_h_free_resource_mr(const struct ipz_adapter_handle 
+						     hcp_adapter_handle, 
+						     struct ehca_pfmr *pfmr,
+						     const struct ipz_mrmw_handle 
+						     *mr_handle)
+{
+	u64 rc = H_Success;
+	u64 dummy;
+
+#ifndef EHCA_USE_HCALL
+	EDEB_EN(7, "hcp_adapter_handle=%lx pfmr=%p mr_handle.mrwpte=%p"
+		" mr_handle.page_index=%x",
+		hcp_adapter_handle.handle, pfmr, mr_handle->mrwpte, 
+		mr_handle->page_index);
+	
+	rc = simp_hcz_h_free_resource_mr(hcp_adapter_handle, pfmr, mr_handle);
+#else
+	EDEB_EN(7, "hcp_adapter_handle=%lx pfmr=%p mr_handle=%lx",
+		hcp_adapter_handle.handle, pfmr, mr_handle->handle);
+	
+	rc = plpar_hcall_7arg_7ret(H_FREE_RESOURCE, 
+				   hcp_adapter_handle.handle, /* r4 */
+				   mr_handle->handle,         /* r5 */
+				   0, 0, 0, 0, 0,
+				   &dummy,
+				   &dummy,
+				   &dummy,
+				   &dummy,
+				   &dummy,
+				   &dummy,
+				   &dummy);
+#endif /* EHCA_USE_HCALL */
+	EDEB_EX(7, "rc=%lx", rc);
+
+	return rc;
+}
+
+/**
+ * hipz_h_reregister_pmr - Reregister MR in HW and FW.
+ *
+ * @pfmr:        platform specific for MR
+ * @pfshca:      platform specific for SHCA 
+ * @mr_handle:   Memory Region Handle
+ * @vaddr_in:    Memory Region I/O Virtual Address
+ * @length:      Memory Region Length
+ * @access_ctrl: Memory Region Access Controls
+ * @pd:          Protection Domain 
+ * @mr_addr_cb:  Logical Address of MR Control Block
+ * @vaddr_out:   Memory Region I/O Virtual Address
+ * lkey:         L_Key
+ * rkey:         R_Key
+ *
+ */
+static inline u64 hipz_h_reregister_pmr(const struct ipz_adapter_handle 
+						   hcp_adapter_handle, 
+						   struct ehca_pfmr *pfmr,
+						   struct ehca_pfshca *pfshca,
+						   const struct ipz_mrmw_handle 
+						   *mr_handle,
+						   const u64 vaddr_in,
+						   const u64 length,
+						   const u32 access_ctrl,
+						   const struct ipz_pd pd,
+						   const u64 mr_addr_cb,
+						   u64 * vaddr_out,
+						   u32 * lkey,
+						   u32 * rkey)
+{
+	u64 rc = H_Success;
+	u64 dummy;
+	u64 lkey_out;
+	u64 rkey_out;
+
+#ifndef EHCA_USE_HCALL
+	EDEB_EN(7, "hcp_adapter_handle=%lx pfmr=%p pfshca=%p"
+		" mr_handle.mrwpte=%p mr_handle.page_index=%x vaddr_in=%lx"
+		" length=%lx access_ctrl=%x pd=%x mr_addr_cb=", 
+		hcp_adapter_handle.handle, pfmr, pfshca, mr_handle->mrwpte, 
+		mr_handle->page_index, vaddr_in, length, access_ctrl, 
+		pd.value, mr_addr_cb);
+
+	rc = simp_hcz_h_reregister_pmr(hcp_adapter_handle, pfmr, pfshca,
+				       mr_handle, vaddr_in, length, access_ctrl,
+				       pd, mr_addr_cb, vaddr_out, lkey, rkey);
+#else
+	EDEB_EN(7, "hcp_adapter_handle=%lx pfmr=%p pfshca=%p mr_handle=%lx "
+		"vaddr_in=%lx length=%lx access_ctrl=%x pd=%x mr_addr_cb=%lx",
+		hcp_adapter_handle.handle, pfmr, pfshca, mr_handle->handle,
+		vaddr_in, length, access_ctrl, pd.value, mr_addr_cb);
+
+	rc = plpar_hcall_7arg_7ret(H_REREGISTER_PMR, 
+				   hcp_adapter_handle.handle, /* r4 */
+				   mr_handle->handle,	      /* r5 */
+				   vaddr_in,	              /* r6 */
+				   length,                    /* r7 */
+				   /* r8 */
+				   ((((u64) access_ctrl) << 32ULL) | pd.value),
+				   mr_addr_cb,                /* r9 */
+				   0,
+				   &dummy,                    /* r4 */
+				   vaddr_out,                 /* r5 */
+				   &lkey_out,                 /* r6 */
+				   &rkey_out,                 /* r7 */
+				   &dummy,
+				   &dummy,
+				   &dummy);
+	*lkey = (u32) lkey_out;
+	*rkey = (u32) rkey_out;
+#endif /* EHCA_USE_HCALL */
+
+	EDEB_EX(7, "rc=%lx vaddr_out=%lx lkey=%x rkey=%x",
+		rc, *vaddr_out, *lkey, *rkey);
+	return rc;
+}
+
+/**  @brief 
+     as defined in carols hcall document
+*/
+
+/**
+ * Register shared MR in HW and FW.
+ *
+ * @pfmr:           platform specific for new shared MR
+ * @orig_pfmr:      platform specific for original MR
+ * @pfshca:         platform specific for SHCA 
+ * @orig_mr_handle: Memory Region Handle of original MR
+ * @vaddr_in:       Memory Region I/O Virtual Address of new shared MR
+ * @access_ctrl:    Memory Region Access Controls of new shared MR
+ * @pd:             Protection Domain of new shared MR
+ * @mr_handle:      Memory Region Handle of new shared MR
+ * @lkey:           L_Key of new shared MR
+ * @rkey:           R_Key of new shared MR
+ */
+static inline u64 hipz_h_register_smr(const struct ipz_adapter_handle 
+						 hcp_adapter_handle, 
+						 struct ehca_pfmr *pfmr,
+						 struct ehca_pfmr *orig_pfmr,
+						 struct ehca_pfshca *pfshca,
+						 const struct ipz_mrmw_handle
+						 *orig_mr_handle,
+						 const u64 vaddr_in,
+						 const u32 access_ctrl,
+						 const struct ipz_pd pd,
+						 struct ipz_mrmw_handle
+						 *mr_handle,
+						 u32 * lkey,
+						 u32 * rkey)
+{
+	u64 rc = H_Success;
+	u64 dummy;
+	u64 lkey_out;
+	u64 rkey_out;
+
+#ifndef EHCA_USE_HCALL
+	EDEB_EN(7, "hcp_adapter_handle=%lx pfmr=%p orig_pfmr=%p pfshca=%p"
+		" orig_mr_handle.mrwpte=%p orig_mr_handle.page_index=%x"
+		" vaddr_in=%lx access_ctrl=%x pd=%x", 
+		hcp_adapter_handle.handle, pfmr, orig_pfmr, pfshca, 
+		orig_mr_handle->mrwpte, orig_mr_handle->page_index, 
+		vaddr_in, access_ctrl, pd.value);
+	
+	rc = simp_hcz_h_register_smr(hcp_adapter_handle, pfmr, orig_pfmr,
+				     pfshca, orig_mr_handle, vaddr_in,
+				     access_ctrl, pd,
+				     (struct hcz_mrmw_handle *)mr_handle, lkey,
+				     rkey);
+	EDEB_EX(7, "rc=%lx mr_handle.mrwpte=%p mr_handle.page_index=%x"
+		" lkey=%x rkey=%x",
+		rc, mr_handle->mrwpte, mr_handle->page_index, *lkey, *rkey);
+#else
+	EDEB_EN(7, "hcp_adapter_handle=%lx orig_pfmr=%p pfshca=%p"
+		" orig_mr_handle=%lx vaddr_in=%lx access_ctrl=%x pd=%x",
+		hcp_adapter_handle.handle, orig_pfmr, pfshca,
+		orig_mr_handle->handle, vaddr_in, access_ctrl, pd.value);
+	
+
+	rc = plpar_hcall_7arg_7ret(H_REGISTER_SMR, 
+				   hcp_adapter_handle.handle,        /* r4 */
+				   orig_mr_handle->handle,           /* r5 */
+				   vaddr_in,                         /* r6 */
+				   ((((u64) access_ctrl) << 32ULL)), /* r7 */
+				   pd.value,                         /* r8 */
+				   0, 0,
+				   &mr_handle->handle,               /* r4 */
+				   &dummy,                           /* r5 */
+				   &lkey_out,                        /* r6 */
+				   &rkey_out,                        /* r7 */
+				   &dummy,
+				   &dummy,
+				   &dummy);
+	*lkey = (u32) lkey_out;
+	*rkey = (u32) rkey_out;
+
+	EDEB_EX(7, "rc=%lx mr_handle=%lx lkey=%x rkey=%x",
+		rc, mr_handle->handle, *lkey, *rkey);
+#endif /* EHCA_USE_HCALL */
+
+	return rc;
+}
+
+static inline u64 hipz_h_alloc_resource_mw(const struct ipz_adapter_handle 
+						      hcp_adapter_handle,
+						      struct ehca_pfmw *pfmw,
+						      struct ehca_pfshca *pfshca,
+						      const struct ipz_pd pd,	
+						      struct ipz_mrmw_handle *mw_handle,
+						      u32 * rkey)
+{
+	u64 rc = H_Success;
+	u64 dummy;
+	u64 rkey_out;
+	
+	EDEB_EN(7, "hcp_adapter_handle=%lx pfmw=%p pd=%x pfshca=%p",
+		    hcp_adapter_handle.handle, pfmw, pd.value, pfshca);
+
+#ifndef EHCA_USE_HCALL
+
+	rc = simp_hcz_h_alloc_resource_mw(hcp_adapter_handle, pfmw, pfshca, pd,
+					  (struct hcz_mrmw_handle *)mw_handle,
+					  rkey);
+	EDEB_EX(7, "rc=%lx mw_handle.mrwpte=%p mw_handle.page_index=%x rkey=%x",
+		rc, mw_handle->mrwpte, mw_handle->page_index, *rkey);
+#else
+	rc = plpar_hcall_7arg_7ret(H_ALLOC_RESOURCE, 
+				   hcp_adapter_handle.handle, /* r4 */
+				   6,                         /* r5 */
+				   pd.value,                  /* r6 */
+				   0, 0, 0, 0,
+				   &mw_handle->handle,        /* r4 */
+				   &dummy,                    /* r5 */
+				   &dummy,                    /* r6 */
+				   &rkey_out,                 /* r7 */
+				   &dummy,
+				   &dummy,
+				   &dummy);
+	*rkey = (u32) rkey_out;
+
+	EDEB_EX(7, "rc=%lx mw_handle=%lx rkey=%x",
+		rc, mw_handle->handle, *rkey);
+#endif /* EHCA_USE_HCALL */
+	return rc;
+}
+
+static inline u64 hipz_h_query_mw(const struct ipz_adapter_handle 
+					     hcp_adapter_handle, 
+					     struct ehca_pfmw *pfmw,
+					     const struct ipz_mrmw_handle 
+					     *mw_handle,
+					     u32 * rkey,
+					     struct ipz_pd *pd)
+{
+	u64 rc = H_Success;
+	u64 dummy;
+	u64 pd_out;
+	u64 rkey_out;
+
+#ifndef EHCA_USE_HCALL
+	EDEB_EN(7, "hcp_adapter_handle=%lx pfmw=%p mw_handle.mrwpte=%p"
+		" mw_handle.page_index=%x",
+		hcp_adapter_handle.handle, pfmw, mw_handle->mrwpte,
+		mw_handle->page_index);
+
+	rc = simp_hcz_h_query_mw(hcp_adapter_handle, pfmw, mw_handle, rkey, pd);
+
+	EDEB_EX(7, "rc=%lx rkey=%x pd=%x", rc, *rkey, pd->value);
+#else
+	EDEB_EN(7, "hcp_adapter_handle=%lx pfmw=%p mw_handle=%lx", 
+		hcp_adapter_handle.handle, pfmw, mw_handle->handle);
+
+	rc = plpar_hcall_7arg_7ret(H_QUERY_MW, 
+				   hcp_adapter_handle.handle, /* r4 */
+				   mw_handle->handle,         /* r5 */
+				   0, 0, 0, 0, 0,
+				   &dummy,                    /* r4 */
+				   &dummy,                    /* r5 */
+				   &dummy,                    /* r6 */
+				   &rkey_out,                 /* r7 */
+				   &pd_out,                   /* r8 */
+				   &dummy,
+				   &dummy);
+	*rkey = (u32) rkey_out;
+	pd->value = (u32) pd_out;
+
+	EDEB_EX(7, "rc=%lx rkey=%x pd=%x", rc, *rkey, pd->value);
+#endif /* EHCA_USE_HCALL */
+
+	return rc;
+}
+
+static inline u64 hipz_h_free_resource_mw(const struct ipz_adapter_handle 
+						     hcp_adapter_handle, 
+						     struct ehca_pfmw *pfmw,
+						     const struct ipz_mrmw_handle 
+						     *mw_handle)
+{
+	u64 rc = H_Success;
+	u64 dummy;
+
+#ifndef EHCA_USE_HCALL
+	EDEB_EN(7, "hcp_adapter_handle=%lx pfmw=%p mw_handle.mrwpte=%p"
+		" mw_handle.page_index=%x",
+		hcp_adapter_handle.handle, pfmw, mw_handle->mrwpte,
+		mw_handle->page_index);
+
+	rc = simp_hcz_h_free_resource_mw(hcp_adapter_handle, pfmw, mw_handle);
+#else
+	EDEB_EN(7, "hcp_adapter_handle=%lx pfmw=%p mw_handle=%lx", 
+		hcp_adapter_handle.handle, pfmw, mw_handle->handle);
+
+	rc = plpar_hcall_7arg_7ret(H_FREE_RESOURCE, 
+				   hcp_adapter_handle.handle, /* r4 */
+				   mw_handle->handle,         /* r5 */
+				   0, 0, 0, 0, 0,
+				   &dummy,
+				   &dummy,
+				   &dummy,
+				   &dummy,
+				   &dummy,
+				   &dummy,
+				   &dummy);
+#endif /* EHCA_USE_HCALL */
+	EDEB_EX(7, "rc=%lx", rc);
+
+	return rc;
+}
+
+static inline u64 hipz_h_error_data(const struct ipz_adapter_handle 
+					       adapter_handle, 
+					       const u64 ressource_handle,
+					       void *rblock,
+					       unsigned long *byte_count)
+{
+	u64 rc = H_Success;
+	u64 dummy;
+	u64 r_cb;
+
+	EDEB_EN(7, "adapter_handle=%lx ressource_handle=%lx  rblock=%p",
+		adapter_handle.handle, ressource_handle, rblock);
+	
+	if ((((u64)rblock) & 0xfff) != 0) {
+		EDEB_ERR(4, "rblock not page aligned.");
+		rc = H_Parameter;
+		return rc;
+	}
+	
+	r_cb = ehca_kv_to_g(rblock);
+	
+	rc = plpar_hcall_7arg_7ret(H_ERROR_DATA, 
+				   adapter_handle.handle,
+				   ressource_handle,
+				   r_cb,
+				   0, 0, 0, 0,
+				   &dummy,
+				   &dummy,
+				   &dummy,
+				   &dummy,
+				   &dummy,
+				   &dummy,
+				   &dummy);
+
+	EDEB_EX(7, "rc=%lx", rc);
+      
+	return rc;
+}
+
+#endif /* __HCP_IF_H__ */
diff -urN oldtree/drivers/infiniband/hw/ehca/hcp_phyp.c newtree/drivers/infiniband/hw/ehca/hcp_phyp.c
--- oldtree/drivers/infiniband/hw/ehca/hcp_phyp.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/hcp_phyp.c	2006-02-13 19:19:59.945520584 -0500
@@ -0,0 +1,81 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *   load store abstraction for ehca register access
+ *
+ *  Authors:  Christoph Raisch <raisch@de.ibm.com> 
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: hcp_phyp.c,v 1.8 2005/10/21 07:18:45 craisch Exp $
+ */
+
+
+#define DEB_PREFIX "PHYP"
+
+#ifdef __KERNEL__
+#include "ehca_kernel.h"
+#include "hipz_hw.h"
+/* #include "hipz_structs.h" */ 
+/* TODO: still necessary */
+#include "ehca_classes.h"
+#else				/* !__KERNEL__ */
+#include "ehca_utools.h"
+#include "ehca_galpa.h"
+#endif
+
+#ifndef EHCA_USERDRIVER		/* TODO: is this correct */
+
+u64 hipz_galpa_load(struct h_galpa galpa, u32 offset)
+{
+	u64 addr = galpa.fw_handle + offset;
+	u64 out;
+	EDEB_EN(7, "addr=%lx offset=%x ", addr, offset);
+	out = *(u64 *) addr;
+	EDEB_EX(7, "addr=%lx value=%lx", addr, out);
+	return out;
+};
+
+void hipz_galpa_store(struct h_galpa galpa, u32 offset, u64 value)
+{
+	u64 addr = galpa.fw_handle + offset;
+	EDEB(7, "addr=%lx offset=%x value=%lx", addr,
+	     offset, value);
+	*(u64 *) addr = value;
+#ifdef EHCA_USE_HCALL
+	/* hipz_galpa_load(galpa, offset); */      
+        /* synchronize explicitly */
+#endif
+};
+
+#endif				/* EHCA_USERDRIVER */
diff -urN oldtree/drivers/infiniband/hw/ehca/hcp_phyp.h newtree/drivers/infiniband/hw/ehca/hcp_phyp.h
--- oldtree/drivers/infiniband/hw/ehca/hcp_phyp.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/hcp_phyp.h	2006-02-13 19:19:59.946520432 -0500
@@ -0,0 +1,338 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  Firmware calls
+ *
+ *  Authors: Christoph Raisch <raisch@de.ibm.com>
+ *           Waleri Fomin <fomin@de.ibm.com>
+ *           Gerd Bayer <gerd.bayer@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: hcp_phyp.h,v 1.15 2005/11/03 07:19:21 schickhj Exp $
+ */
+
+#ifndef __HCP_PHYP_H__
+#define __HCP_PHYP_H__
+
+#ifndef EHCA_USERDRIVER
+inline static int hcall_map_page(u64 physaddr, u64 * mapaddr)
+{
+	*mapaddr = (u64)(ioremap(physaddr, 4096));
+
+	EDEB(7, "ioremap physaddr=%lx mapaddr=%lx", physaddr, *mapaddr);
+	return 0;
+}
+
+inline static int hcall_unmap_page(u64 mapaddr)
+{
+	EDEB(7, "mapaddr=%lx", mapaddr);
+	iounmap((void *)(mapaddr));
+	return 0;
+}
+#else
+int hcall_map_page(u64 physaddr, u64 * mapaddr);
+int hcall_unmap_page(u64 mapaddr);
+#endif
+
+struct hcall {
+	u64 regs[11];
+};
+
+/** 
+ * @brief returns time to wait in secs for the given long busy error code
+ */
+inline static u32 getLongBusyTimeSecs(int longBusyRetCode)
+{
+	switch (longBusyRetCode) {
+	case H_LongBusyOrder1msec:
+		return 1;
+	case H_LongBusyOrder10msec:
+		return 10;
+	case H_LongBusyOrder100msec:
+		return 100;
+	case H_LongBusyOrder1sec:
+		return 1000;
+	case H_LongBusyOrder10sec:
+		return 10000;
+	case H_LongBusyOrder100sec:
+		return 100000;
+	default:
+		return 1;
+	}			/* eof switch */
+}
+
+inline static long plpar_hcall_7arg_7ret(unsigned long opcode, 
+					 unsigned long arg1,    /* <R4  */
+					 unsigned long arg2,	/* <R5  */
+					 unsigned long arg3,	/* <R6  */
+					 unsigned long arg4,	/* <R7  */
+					 unsigned long arg5,	/* <R8  */
+					 unsigned long arg6,	/* <R9  */
+					 unsigned long arg7,	/* <R10 */
+					 unsigned long *out1,	/* <R4  */
+					 unsigned long *out2,	/* <R5  */
+					 unsigned long *out3,	/* <R6  */
+					 unsigned long *out4,	/* <R7  */
+					 unsigned long *out5,	/* <R8  */
+					 unsigned long *out6,	/* <R9  */
+					 unsigned long *out7	/* <R10 */
+    )
+{
+	struct hcall hcall_in = {
+		.regs[0] = opcode,
+		.regs[1] = arg1,
+		.regs[2] = arg2,
+		.regs[3] = arg3,
+		.regs[4] = arg4,
+		.regs[5] = arg5,
+		.regs[6] = arg6,
+		.regs[7] = arg7	/*,
+				   .regs[8]=arg8 */
+	};
+	struct hcall hcall = hcall_in;
+	int i;
+	long ret;
+	int sleep_msecs;
+	EDEB(7, "HCALL77_IN r3=%lx r4=%lx r5=%lx r6=%lx r7=%lx r8=%lx"
+	     " r9=%lx r10=%lx r11=%lx", hcall.regs[0], hcall.regs[1],
+	     hcall.regs[2], hcall.regs[3], hcall.regs[4], hcall.regs[5],
+	     hcall.regs[6], hcall.regs[7], hcall.regs[8]);
+
+	/* if phype returns LongBusyXXX, 
+	 * we retry several times, but not forever */
+	for (i = 0; i < 5; i++) {
+		__asm__ __volatile__("mr 3,%10\n"
+				     "mr 4,%11\n"
+				     "mr 5,%12\n"
+				     "mr 6,%13\n"
+				     "mr 7,%14\n"
+				     "mr 8,%15\n"
+				     "mr 9,%16\n"
+				     "mr 10,%17\n"
+				     "mr 11,%18\n"
+				     "mr 12,%19\n"
+				     ".long 0x44000022\n"
+				     "mr %0,3\n"
+				     "mr %1,4\n"
+				     "mr %2,5\n"
+				     "mr %3,6\n"
+				     "mr %4,7\n"
+				     "mr %5,8\n"
+				     "mr %6,9\n"
+				     "mr %7,10\n"
+				     "mr %8,11\n"
+				     "mr %9,12\n":"=r"(hcall.regs[0]),
+				     "=r"(hcall.regs[1]), "=r"(hcall.regs[2]),
+				     "=r"(hcall.regs[3]), "=r"(hcall.regs[4]),
+				     "=r"(hcall.regs[5]), "=r"(hcall.regs[6]),
+				     "=r"(hcall.regs[7]), "=r"(hcall.regs[8]),
+				     "=r"(hcall.regs[9])
+				     :"r"(hcall.regs[0]), "r"(hcall.regs[1]),
+				     "r"(hcall.regs[2]), "r"(hcall.regs[3]),
+				     "r"(hcall.regs[4]), "r"(hcall.regs[5]),
+				     "r"(hcall.regs[6]), "r"(hcall.regs[7]),
+				     "r"(hcall.regs[8]), "r"(hcall.regs[9])
+				     :"r0", "r2", "r3", "r4", "r5", "r6", "r7",
+				     "r8", "r9", "r10", "r11", "r12", "cc",
+				     "xer", "ctr", "lr", "cr0", "cr1", "cr5",
+				     "cr6", "cr7");
+
+		EDEB(7, "HCALL77_OUT r3=%lx r4=%lx r5=%lx r6=%lx r7=%lx r8=%lx"
+		     "r9=%lx r10=%lx r11=%lx", hcall.regs[0], hcall.regs[1],
+		     hcall.regs[2], hcall.regs[3], hcall.regs[4], hcall.regs[5],
+		     hcall.regs[6], hcall.regs[7], hcall.regs[8]);
+		ret = hcall.regs[0];
+		*out1 = hcall.regs[1];
+		*out2 = hcall.regs[2];
+		*out3 = hcall.regs[3];
+		*out4 = hcall.regs[4];
+		*out5 = hcall.regs[5];
+		*out6 = hcall.regs[6];
+		*out7 = hcall.regs[7];
+
+		if (!H_isLongBusy(ret)) {
+			if (ret<0) {
+				EDEB_ERR(4, "HCALL77_IN r3=%lx r4=%lx r5=%lx r6=%lx " 
+					 "r7=%lx r8=%lx r9=%lx r10=%lx", 
+					 opcode, arg1, arg2, arg3, 
+					 arg4, arg5, arg6, arg7);
+				EDEB_ERR(4,  
+					 "HCALL77_OUT r3=%lx r4=%lx r5=%lx "
+					 "r6=%lx r7=%lx r8=%lx r9=%lx r10=%lx ", 
+					 hcall.regs[0], hcall.regs[1], 
+					 hcall.regs[2], hcall.regs[3], 
+					 hcall.regs[4], hcall.regs[5],
+					 hcall.regs[6], hcall.regs[7]);
+			}
+			return ret;
+		}
+		
+		sleep_msecs = getLongBusyTimeSecs(ret);
+		EDEB(7, "Got LongBusy return code from phype. "
+		       "Sleep %dmsecs and retry...", sleep_msecs);
+		msleep_interruptible(sleep_msecs);
+		hcall = hcall_in;
+	}			/* eof for */
+	EDEB_ERR(4, "HCALL77_OUT ret=H_Busy");
+	return H_Busy;
+}
+
+inline static long plpar_hcall_9arg_9ret(unsigned long opcode, 
+					 unsigned long arg1,	/* <R4  */
+					 unsigned long arg2,	/* <R5  */
+					 unsigned long arg3,	/* <R6  */
+					 unsigned long arg4,	/* <R7  */
+					 unsigned long arg5,	/* <R8  */
+					 unsigned long arg6,	/* <R9  */
+					 unsigned long arg7,	/* <R10 */
+					 unsigned long arg8,	/* <R11 */
+					 unsigned long arg9,	/* <R12 */
+					 unsigned long *out1,	/* <R4  */
+					 unsigned long *out2,	/* <R5  */
+					 unsigned long *out3,	/* <R6  */
+					 unsigned long *out4,	/* <R7  */
+					 unsigned long *out5,	/* <R8  */
+					 unsigned long *out6,	/* <R9  */
+					 unsigned long *out7,	/* <R10 */
+					 unsigned long *out8,	/* <R11 */
+					 unsigned long *out9	/* <R12 */
+    )
+{
+	struct hcall hcall_in = {
+		.regs[0] = opcode,
+		.regs[1] = arg1,
+		.regs[2] = arg2,
+		.regs[3] = arg3,
+		.regs[4] = arg4,
+		.regs[5] = arg5,
+		.regs[6] = arg6,
+		.regs[7] = arg7,
+		.regs[8] = arg8,
+		.regs[9] = arg9,
+	};
+	struct hcall hcall = hcall_in;
+	int i;
+	long ret;
+	int sleep_msecs;
+	EDEB(7,"HCALL99_IN  r3=%lx r4=%lx r5=%lx r6=%lx r7=%lx r8=%lx r9=%lx"
+	     " r10=%lx r11=%lx r12=%lx",
+	     hcall.regs[0], hcall.regs[1], hcall.regs[2], hcall.regs[3],
+	     hcall.regs[4], hcall.regs[5], hcall.regs[6], hcall.regs[7],
+	     hcall.regs[8], hcall.regs[9]);
+
+	/* if phype returns LongBusyXXX, we retry several times, but not forever */
+	for (i = 0; i < 5; i++) {
+		__asm__ __volatile__("mr 3,%10\n"
+				     "mr 4,%11\n"
+				     "mr 5,%12\n"
+				     "mr 6,%13\n"
+				     "mr 7,%14\n"
+				     "mr 8,%15\n"
+				     "mr 9,%16\n"
+				     "mr 10,%17\n"
+				     "mr 11,%18\n"
+				     "mr 12,%19\n"
+				     ".long 0x44000022\n"
+				     "mr %0,3\n"
+				     "mr %1,4\n"
+				     "mr %2,5\n"
+				     "mr %3,6\n"
+				     "mr %4,7\n"
+				     "mr %5,8\n"
+				     "mr %6,9\n"
+				     "mr %7,10\n"
+				     "mr %8,11\n"
+				     "mr %9,12\n":"=r"(hcall.regs[0]),
+				     "=r"(hcall.regs[1]), "=r"(hcall.regs[2]),
+				     "=r"(hcall.regs[3]), "=r"(hcall.regs[4]),
+				     "=r"(hcall.regs[5]), "=r"(hcall.regs[6]),
+				     "=r"(hcall.regs[7]), "=r"(hcall.regs[8]),
+				     "=r"(hcall.regs[9])
+				     :"r"(hcall.regs[0]), "r"(hcall.regs[1]),
+				     "r"(hcall.regs[2]), "r"(hcall.regs[3]),
+				     "r"(hcall.regs[4]), "r"(hcall.regs[5]),
+				     "r"(hcall.regs[6]), "r"(hcall.regs[7]),
+				     "r"(hcall.regs[8]), "r"(hcall.regs[9])
+				     :"r0", "r2", "r3", "r4", "r5", "r6", "r7",
+				     "r8", "r9", "r10", "r11", "r12", "cc",
+				     "xer", "ctr", "lr", "cr0", "cr1", "cr5",
+				     "cr6", "cr7");
+
+		EDEB(7,"HCALL99_OUT r3=%lx r4=%lx r5=%lx r6=%lx r7=%lx r8=%lx "
+		     "r9=%lx r10=%lx r11=%lx r12=%lx", hcall.regs[0],
+		     hcall.regs[1], hcall.regs[2], hcall.regs[3], hcall.regs[4],
+		     hcall.regs[5], hcall.regs[6], hcall.regs[7], hcall.regs[8],
+		     hcall.regs[9]);
+		ret = hcall.regs[0];
+		*out1 = hcall.regs[1];
+		*out2 = hcall.regs[2];
+		*out3 = hcall.regs[3];
+		*out4 = hcall.regs[4];
+		*out5 = hcall.regs[5];
+		*out6 = hcall.regs[6];
+		*out7 = hcall.regs[7];
+		*out8 = hcall.regs[8];
+		*out9 = hcall.regs[9];
+
+		if (!H_isLongBusy(ret)) {
+			if (ret<0) {
+				EDEB_ERR(4, "HCALL99_IN r3=%lx r4=%lx r5=%lx r6=%lx " 
+					 "r7=%lx r8=%lx r9=%lx r10=%lx " 
+					 "r11=%lx r12=%lx", 
+					 opcode, arg1, arg2, arg3, 
+					 arg4, arg5, arg6, arg7,
+					 arg8, arg9);
+				EDEB_ERR(4,  
+					 "HCALL99_OUT r3=%lx r4=%lx r5=%lx "
+					 "r6=%lx r7=%lx r8=%lx r9=%lx r10=%lx "
+					 "r11=%lx r12=lx", 
+					 hcall.regs[0], hcall.regs[1], 
+					 hcall.regs[2], hcall.regs[3], 
+					 hcall.regs[4], hcall.regs[5],
+					 hcall.regs[6], hcall.regs[7], 
+					 hcall.regs[8]);
+			}
+			return ret;
+		}
+		sleep_msecs = getLongBusyTimeSecs(ret);
+		EDEB(7, "Got LongBusy return code from phype. "
+		     "Sleep %dmsecs and retry...", sleep_msecs);
+		msleep_interruptible(sleep_msecs);
+		hcall = hcall_in;
+	}			/* eof for */
+	EDEB_ERR(4, "HCALL99_OUT ret=H_Busy");
+	return H_Busy;
+}
+
+#endif
diff -urN oldtree/drivers/infiniband/hw/ehca/hcp_sense.c newtree/drivers/infiniband/hw/ehca/hcp_sense.c
--- oldtree/drivers/infiniband/hw/ehca/hcp_sense.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/hcp_sense.c	2006-02-13 19:19:59.948520128 -0500
@@ -0,0 +1,144 @@
+/*
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ *  ehca detection and query code for POWER
+ *
+ *  Authors: Heiko J Schick <schickhj@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: hcp_sense.c,v 1.8 2005/11/14 14:47:28 schickhj Exp $
+ */
+
+#define DEB_PREFIX "snse"
+
+#include "ehca_kernel.h"
+#include "ehca_tools.h"
+
+int hipz_count_adapters(void)
+{
+	int num = 0;
+	struct device_node *dn = NULL;
+
+	EDEB_EN(7, "");
+
+	while ((dn = of_find_node_by_name(dn, "lhca"))) {
+		num++;
+	}
+
+	of_node_put(dn);
+
+	if (num == 0) {
+		EDEB_ERR(4, "No lhca node name was found in the"
+			 " Open Firmware device tree.");
+		return -ENODEV;
+	}
+
+	EDEB(6, " ... found %x adapter(s)", num);
+
+	EDEB_EX(7, "num=%x", num);
+
+	return num;
+}
+
+int hipz_probe_adapters(char **adapter_list)
+{
+	int ret = 0;
+	int num = 0;
+	struct device_node *dn = NULL;
+	char *loc;
+
+	EDEB_EN(7, "adapter_list=%p", adapter_list);
+
+	while ((dn = of_find_node_by_name(dn, "lhca"))) {
+		loc = get_property(dn, "ibm,loc-code", NULL);
+		if (loc == NULL) {
+			EDEB_ERR(4, "No ibm,loc-code property for"
+				 " lhca Open Firmware device tree node.");
+			ret = -ENODEV;
+			goto probe_adapters0;
+		}
+
+		adapter_list[num] = loc;
+		EDEB(6, " ... found adapter[%x] with loc-code: %s", num, loc);
+		num++;
+	}
+
+      probe_adapters0:
+	of_node_put(dn);
+
+	EDEB_EX(7, "ret=%x", ret);
+
+	return ret;
+}
+
+u64 hipz_get_adapter_handle(char *adapter)
+{
+	struct device_node *dn = NULL;
+	char *loc;
+	u64 *u64data = NULL;
+	u64 ret = 0;
+
+	EDEB_EN(7, "adapter=%p", adapter);
+
+	while ((dn = of_find_node_by_name(dn, "lhca"))) {
+		loc = get_property(dn, "ibm,loc-code", NULL);
+		if (loc == NULL) {
+			EDEB_ERR(4, "No ibm,loc-code property for"
+				 " lhca Open Firmware device tree node.");
+			goto get_adapter_handle0;
+		}
+
+		if (strcmp(loc, adapter) == 0) {
+			u64data =
+			    (u64 *) get_property(dn, "ibm,hca-handle", NULL);
+			break;
+		}
+	}
+
+	if (u64data == NULL) {
+		EDEB_ERR(4, "No ibm,hca-handle property for"
+			 " lhca Open Firmware device tree node with"
+			 " ibm,loc-code: %s.", adapter);
+		goto get_adapter_handle0;
+	}
+
+	ret = *u64data;
+
+      get_adapter_handle0:
+	of_node_put(dn);
+
+	EDEB_EX(7, "ret=%lx",ret);
+
+	return ret;
+}
diff -urN oldtree/drivers/infiniband/hw/ehca/hcp_sense.h newtree/drivers/infiniband/hw/ehca/hcp_sense.h
--- oldtree/drivers/infiniband/hw/ehca/hcp_sense.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/hcp_sense.h	2006-02-13 19:19:59.948520128 -0500
@@ -0,0 +1,136 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  ehca detection and query code for POWER
+ *
+ *  Authors: Heiko J Schick <schickhj@de.ibm.com> 
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: hcp_sense.h,v 1.10 2005/11/08 09:19:09 schickhj Exp $
+ */
+
+#ifndef HCP_SENSE_H
+#define HCP_SENSE_H
+
+int hipz_count_adapters(void);
+int hipz_probe_adapters(char **adapter_list);
+u64 hipz_get_adapter_handle(char *adapter);
+
+/* query hca response block */
+struct query_hca_rblock {
+	u32 cur_reliable_dg;
+	u32 cur_qp;
+	u32 cur_cq;
+	u32 cur_eq;
+	u32 cur_mr;
+	u32 cur_mw;
+	u32 cur_ee_context;
+	u32 cur_mcast_grp;
+	u32 cur_qp_attached_mcast_grp;
+	u32 reserved1;
+	u32 cur_ipv6_qp;
+	u32 cur_eth_qp;
+	u32 cur_hp_mr;
+	u32 reserved2[3];
+	u32 max_rd_domain;
+	u32 max_qp;
+	u32 max_cq;
+	u32 max_eq;
+	u32 max_mr;
+	u32 max_hp_mr;
+	u32 max_mw;
+	u32 max_mrwpte;
+	u32 max_special_mrwpte;
+	u32 max_rd_ee_context;
+	u32 max_mcast_grp;
+	u32 max_qps_attached_all_mcast_grp;
+	u32 max_qps_attached_mcast_grp;
+	u32 max_raw_ipv6_qp;
+	u32 max_raw_ethy_qp;
+	u32 internal_clock_frequency;
+	u32 max_pd;
+	u32 max_ah;
+	u32 max_cqe;
+	u32 max_wqes_wq;
+	u32 max_partitions;
+	u32 max_rr_ee_context;
+	u32 max_rr_qp;
+	u32 max_rr_hca;
+	u32 max_act_wqs_ee_context;
+	u32 max_act_wqs_qp;
+	u32 max_sge;
+	u32 max_sge_rd;
+	u32 memory_page_size_supported;
+	u64 max_mr_size;
+	u32 local_ca_ack_delay;
+	u32 num_ports;
+	u32 vendor_id;
+	u32 vendor_part_id;
+	u32 hw_ver;
+	u64 node_guid;
+	u64 hca_cap_indicators;
+	u32 data_counter_register_size;
+	u32 max_shared_rq;
+	u32 max_isns_eq;
+	u32 max_neq;
+} __attribute__ ((packed));
+
+/* query port response block */
+struct query_port_rblock {
+	u32 state;
+	u32 bad_pkey_cntr;
+	u32 lmc;
+	u32 lid;
+	u32 subnet_timeout;
+	u32 qkey_viol_cntr;
+	u32 sm_sl;
+	u32 sm_lid;
+	u32 capability_mask;
+	u32 init_type_reply;
+	u32 pkey_tbl_len;
+	u32 gid_tbl_len;
+	u64 gid_prefix;
+	u32 port_nr;
+	u16 pkey_entries[16];
+	u8  reserved1[32];
+	u32 trent_size;
+	u32 trbuf_size;
+	u64 max_msg_sz;
+	u32 max_mtu;
+	u32 vl_cap;
+	u8  reserved2[1900];
+	u64 guid_entries[255];
+} __attribute__ ((packed));
+
+#endif
diff -urN oldtree/drivers/infiniband/hw/ehca/hipz_fns.h newtree/drivers/infiniband/hw/ehca/hipz_fns.h
--- oldtree/drivers/infiniband/hw/ehca/hipz_fns.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/hipz_fns.h	2006-02-13 19:19:59.949519976 -0500
@@ -0,0 +1,83 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  HW abstraction register functions
+ *
+ *  Authors: Christoph Raisch <raisch@de.ibm.com> 
+ *           Reinhard Ernst <rernst@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: hipz_fns.h,v 1.13 2005/10/21 07:18:45 craisch Exp $
+ */
+
+#ifndef __HIPZ_FNS_H__
+#define __HIPZ_FNS_H__
+
+#include "hipz_structs.h"
+#include "ehca_classes.h"
+#include "hipz_hw.h"
+#ifndef EHCA_USE_HCALL
+#include <sim_gal.h>
+#endif
+
+#include "hipz_fns_core.h"
+
+#define hipz_galpa_store_eq(gal,offset,value)\
+        hipz_galpa_store(gal,EQTEMM_OFFSET(offset),value)
+#define hipz_galpa_load_eq(gal,offset)\
+        hipz_galpa_load(gal,EQTEMM_OFFSET(offset))
+
+#define hipz_galpa_store_qped(gal,offset,value)\
+        hipz_galpa_store(gal,QPEDMM_OFFSET(offset),value)
+#define hipz_galpa_load_qped(gal,offset)\
+        hipz_galpa_load(gal,QPEDMM_OFFSET(offset))
+
+#define hipz_galpa_store_mrmw(gal,offset,value)\
+        hipz_galpa_store(gal,MRMWMM_OFFSET(offset),value)
+#define hipz_galpa_load_mrmw(gal,offset)\
+        hipz_galpa_load(gal,MRMWMM_OFFSET(offset))
+
+inline static void hipz_load_FEC(struct ehca_cq_core *cq_core, u32 * count)
+{
+	uint64_t reg = 0;
+	EDEB_EN(7, "cq_core=%p", cq_core);
+	{
+	  struct h_galpa gal = cq_core->galpas.kernel;
+	  reg = hipz_galpa_load_cq(gal, CQx_FEC);
+	  *count = EHCA_BMASK_GET(CQx_FEC_CQE_cnt, reg);
+	}
+	EDEB_EX(7,"cq_core=%p CQx_FEC=%lx", cq_core,reg);
+}
+
+#endif /* __IPZ_IF_H__ */
diff -urN oldtree/drivers/infiniband/hw/ehca/hipz_fns_core.h newtree/drivers/infiniband/hw/ehca/hipz_fns_core.h
--- oldtree/drivers/infiniband/hw/ehca/hipz_fns_core.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/hipz_fns_core.h	2006-02-13 19:19:59.949519976 -0500
@@ -0,0 +1,123 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  HW abstraction register functions
+ *
+ *  Authors: Christoph Raisch <raisch@de.ibm.com> 
+ *           Reinhard Ernst <rernst@de.ibm.com>
+ *           Hoang-Nam Nguyen <hnguyen@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: hipz_fns_core.h,v 1.8 2005/10/17 08:37:30 craisch Exp $
+ */
+
+#ifndef __HIPZ_FNS_CORE_H__
+#define __HIPZ_FNS_CORE_H__
+
+#include "ehca_galpa.h"
+#include "hipz_hw.h"
+
+#define hipz_galpa_store_cq(gal,offset,value)\
+        hipz_galpa_store(gal,CQTEMM_OFFSET(offset),value)
+#define hipz_galpa_load_cq(gal,offset)\
+        hipz_galpa_load(gal,CQTEMM_OFFSET(offset))
+
+#define hipz_galpa_store_qp(gal,offset,value)\
+        hipz_galpa_store(gal,QPTEMM_OFFSET(offset),value)
+#define hipz_galpa_load_qp(gal,offset)\
+        hipz_galpa_load(gal,QPTEMM_OFFSET(offset))
+
+inline static void hipz_update_SQA(struct ehca_qp_core *qp_core, u16 nr_wqes)
+{
+	struct h_galpa gal;
+
+	EDEB_EN(7, "qp_core=%p", qp_core);
+	gal = qp_core->galpas.kernel;
+	/*  ringing doorbell :-) */
+	hipz_galpa_store_qp(gal, QPx_SQA, EHCA_BMASK_SET(QPx_SQAdder, nr_wqes));
+	EDEB_EX(7, "qp_core=%p QPx_SQA = %i", qp_core, nr_wqes);
+}
+
+inline static void hipz_update_RQA(struct ehca_qp_core *qp_core, u16 nr_wqes)
+{
+	struct h_galpa gal;
+
+	EDEB_EN(7, "qp_core=%p", qp_core);
+	gal = qp_core->galpas.kernel;
+	/*  ringing doorbell :-) */
+	hipz_galpa_store_qp(gal, QPx_RQA, EHCA_BMASK_SET(QPx_RQAdder, nr_wqes));
+	EDEB_EX(7, "qp_core=%p QPx_RQA = %i", qp_core, nr_wqes);
+}
+
+inline static void hipz_update_FECA(struct ehca_cq_core *cq_core, u32 nr_cqes)
+{
+	struct h_galpa gal;
+
+	EDEB_EN(7, "cq_core=%p", cq_core);
+	gal = cq_core->galpas.kernel;
+	hipz_galpa_store_cq(gal, CQx_FECA,
+			    EHCA_BMASK_SET(CQx_FECAdder, nr_cqes));
+	EDEB_EX(7, "cq_core=%p CQx_FECA = %i", cq_core, nr_cqes);
+}
+
+inline static void hipz_set_CQx_N0(struct ehca_cq_core *cq_core, u32 value)
+{
+	struct h_galpa gal;
+	u64 CQx_N0_reg = 0;
+
+	EDEB_EN(7, "cq_core=%p event on solicited completion -- write CQx_N0", 
+		cq_core);
+	gal = cq_core->galpas.kernel;
+	hipz_galpa_store_cq(gal, CQx_N0,
+			    EHCA_BMASK_SET(CQx_N0_generate_solicited_comp_event,
+					   value));
+	CQx_N0_reg = hipz_galpa_load_cq(gal, CQx_N0);
+	EDEB_EX(7, "cq_core=%p loaded CQx_N0=%lx", cq_core,(unsigned long)CQx_N0_reg);
+}
+
+inline static void hipz_set_CQx_N1(struct ehca_cq_core *cq_core, u32 value)
+{
+	struct h_galpa gal;
+	u64 CQx_N1_reg = 0;
+
+	EDEB_EN(7, "cq_core=%p event on completion -- write CQx_N1",
+		cq_core);
+	gal = cq_core->galpas.kernel;
+	hipz_galpa_store_cq(gal, CQx_N1,
+			    EHCA_BMASK_SET(CQx_N1_generate_comp_event, value));
+	CQx_N1_reg = hipz_galpa_load_cq(gal, CQx_N1);
+	EDEB_EX(7, "cq_core=%p loaded CQx_N1=%lx", cq_core,(unsigned long)CQx_N1_reg);
+}
+
+#endif /* __HIPZ_FNC_CORE_H__ */
diff -urN oldtree/drivers/infiniband/hw/ehca/hipz_hw.h newtree/drivers/infiniband/hw/ehca/hipz_hw.h
--- oldtree/drivers/infiniband/hw/ehca/hipz_hw.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/hipz_hw.h	2006-02-13 19:19:59.951519672 -0500
@@ -0,0 +1,382 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  eHCA register definitions
+ *
+ *  Authors: Christoph Raisch <raisch@de.ibm.com>
+ *           Reinhard Ernst <rernst@de.ibm.com>
+ *           Waleri Fomin <fomin@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: hipz_hw.h,v 1.5 2005/10/06 13:08:43 souissi Exp $
+ */
+
+#ifndef __HIPZ_HW_H__
+#define __HIPZ_HW_H__
+
+#ifdef __KERNEL__
+#include "ehca_tools.h"
+#include "ehca_kernel.h"
+#else				/*  !__KERNEL__ */
+#include "ehca_utools.h"
+#endif
+
+/** @brief Queue Pair Table Memory
+ */
+struct hipz_QPTEMM {
+	u64 QPx_HCR;
+#define QPx_HCR_PKEY_Mode EHCA_BMASK_IBM(1,2)
+#define QPx_HCR_Special_QP_Mode EHCA_BMASK_IBM(6,7)
+	u64 QPx_C;
+#define QPx_C_Enabled EHCA_BMASK_IBM(0,0)
+#define QPx_C_Disabled EHCA_BMASK_IBM(1,1)
+#define QPx_C_Req_State EHCA_BMASK_IBM(16,23)
+#define QPx_C_Res_State EHCA_BMASK_IBM(25,31)
+#define QPx_C_disable_ETE_check EHCA_BMASK_IBM(7,7)
+	u64 QPx_HERR;
+	u64 QPx_AER;
+/* 0x20*/
+	u64 QPx_SQA;
+#define QPx_SQAdder EHCA_BMASK_IBM(48,63)
+	u64 QPx_SQC;
+	u64 QPx_RQA;
+#define QPx_RQAdder EHCA_BMASK_IBM(48,63)
+	u64 QPx_RQC;
+/* 0x40*/
+	u64 QPx_ST;
+	u64 QPx_PMSTATE;
+#define  QPx_PMSTATE_BITS  EHCA_BMASK_IBM(30,31)
+	u64 QPx_PMFA;
+	u64 QPx_PKEY;
+#define QPx_PKEY_value EHCA_BMASK_IBM(48,63)
+/* 0x60*/
+	u64 QPx_PKEYA;
+#define QPx_PKEYA_index0 EHCA_BMASK_IBM(0,15)
+#define QPx_PKEYA_index1 EHCA_BMASK_IBM(16,31)
+#define QPx_PKEYA_index2 EHCA_BMASK_IBM(32,47)
+#define QPx_PKEYA_index3 EHCA_BMASK_IBM(48,63)
+	u64 QPx_PKEYB;
+#define QPx_PKEYB_index4 EHCA_BMASK_IBM(0,15)
+#define QPx_PKEYB_index5 EHCA_BMASK_IBM(16,31)
+#define QPx_PKEYB_index6 EHCA_BMASK_IBM(32,47)
+#define QPx_PKEYB_index7 EHCA_BMASK_IBM(48,63)
+	u64 QPx_PKEYC;
+#define QPx_PKEYC_index8 EHCA_BMASK_IBM(0,15)
+#define QPx_PKEYC_index9 EHCA_BMASK_IBM(16,31)
+#define QPx_PKEYC_index10 EHCA_BMASK_IBM(32,47)
+#define QPx_PKEYC_index11 EHCA_BMASK_IBM(48,63)
+	u64 QPx_PKEYD;
+#define QPx_PKEYD_index12 EHCA_BMASK_IBM(0,15)
+#define QPx_PKEYD_index13 EHCA_BMASK_IBM(16,31)
+#define QPx_PKEYD_index14 EHCA_BMASK_IBM(32,47)
+#define QPx_PKEYD_index15 EHCA_BMASK_IBM(48,63)
+/* 0x80*/
+	u64 QPx_QKEY;
+#define QPx_QKEY_value EHCA_BMASK_IBM(32,63)
+	u64 QPx_DQP;
+#define QPx_DQP_number EHCA_BMASK_IBM(40,63)
+	u64 QPx_DLIDP;
+#define QPx_DLID_PRIMARY EHCA_BMASK_IBM(48,63)
+#define QPx_DLIDP_GRH    EHCA_BMASK_IBM(31,31)
+	u64 QPx_PORTP;
+#define QPx_PORT_Primary EHCA_BMASK_IBM(57,63)
+/* 0xa0*/
+	u64 QPx_SLIDP;
+#define QPx_SLIDP_p_path EHCA_BMASK_IBM(48,63)
+#define QPx_SLIDP_lmc    EHCA_BMASK_IBM(37,39)
+	u64 QPx_SLIDPP;
+#define QPx_SLID_PRIM_PATH EHCA_BMASK_IBM(57,63)
+	u64 QPx_DLIDA;
+#define QPx_DLIDA_GRH    EHCA_BMASK_IBM(31,31)
+	u64 QPx_PORTA;
+#define QPx_PORT_Alternate EHCA_BMASK_IBM(57,63)
+/* 0xc0*/
+	u64 QPx_SLIDA;
+	u64 QPx_SLIDPA;
+	u64 QPx_SLVL;
+#define QPx_SLVL_BITS  EHCA_BMASK_IBM(56,59)
+#define QPx_SLVL_VL    EHCA_BMASK_IBM(60,63)
+	u64 QPx_IPD;
+#define QPx_IPD_max_static_rate EHCA_BMASK_IBM(56,63)
+/* 0xe0*/
+	u64 QPx_MTU;
+#define QPx_MTU_size EHCA_BMASK_IBM(56,63)
+	u64 QPx_LATO;
+#define QPx_LATO_BITS EHCA_BMASK_IBM(59,63)
+	u64 QPx_RLIMIT;
+#define QPx_RETRY_COUNT EHCA_BMASK_IBM(61,63)
+	u64 QPx_RNRLIMIT;
+#define QPx_RNR_RETRY_COUNT EHCA_BMASK_IBM(61,63)
+/* 0x100*/
+	u64 QPx_T;
+	u64 QPx_SQHP;
+	u64 QPx_SQPTP;
+	u64 QPx_NSPSN;
+#define QPx_NSPSN_value EHCA_BMASK_IBM(40,63)
+/* 0x120*/
+	u64 QPx_NSPSNHWM;
+#define QPx_NSPSNHWM_value EHCA_BMASK_IBM(40,63)
+	u64 reserved1;
+	u64 QPx_SDSI;
+	u64 QPx_SDSBC;
+/* 0x140*/
+	u64 QPx_SQWSIZE;
+#define QPx_SQWSIZE_value EHCA_BMASK_IBM(61,63)
+	u64 QPx_SQWTS;
+	u64 QPx_LSN;
+	u64 QPx_NSSN;
+/* 0x160 */
+	u64 QPx_MOR;
+#define QPx_MOR_value EHCA_BMASK_IBM(48,63)
+	u64 QPx_COR;
+	u64 QPx_SQSIZE;
+#define QPx_SQSIZE_value EHCA_BMASK_IBM(60,63)
+	u64 QPx_ERC;
+/* 0x180*/
+	u64 QPx_RNRRC;
+#define QPx_RNRRESP_value EHCA_BMASK_IBM(59,63)
+	u64 QPx_ERNRWT;
+	u64 QPx_RNRRESP;
+#define QPx_RNRRESP_WTR EHCA_BMASK_IBM(59,63)
+	u64 QPx_LMSNA;
+/* 0x1a0 */
+	u64 QPx_SQHPC;
+	u64 QPx_SQCPTP;
+	u64 QPx_SIGT;
+	u64 QPx_WQECNT;
+/* 0x1c0*/
+
+	u64 QPx_RQHP;
+	u64 QPx_RQPTP;
+	u64 QPx_RQSIZE;
+#define QPx_RQSIZE_value EHCA_BMASK_IBM(60,63)
+	u64 QPx_NRR;
+#define QPx_NRR_value EHCA_BMASK_IBM(61,63)
+/* 0x1e0*/
+	u64 QPx_RDMAC;
+#define QPx_RDMAC_value EHCA_BMASK_IBM(61,63)
+	u64 QPx_NRPSN;
+#define QPx_NRPSN_value EHCA_BMASK_IBM(40,63)
+	u64 QPx_LAPSN;
+#define QPx_LAPSN_value EHCA_BMASK_IBM(40,63)
+	u64 QPx_LCR;
+/* 0x200*/
+	u64 QPx_RWC;
+	u64 QPx_RWVA;
+	u64 QPx_RDSI;
+	u64 QPx_RDSBC;
+/* 0x220*/
+	u64 QPx_RQWSIZE;
+#define QPx_RQWSIZE_value EHCA_BMASK_IBM(61,63)
+	u64 QPx_CRMSN;
+	u64 QPx_RDD;
+#define QPx_RDD_VALUE  EHCA_BMASK_IBM(32,63)
+	u64 QPx_LARPSN;
+#define QPx_LARPSN_value EHCA_BMASK_IBM(40,63)
+/* 0x240*/
+	u64 QPx_PD;
+	u64 QPx_SCQN;
+	u64 QPx_RCQN;
+	u64 QPx_AEQN;
+/* 0x260*/
+	u64 QPx_AAELOG;
+	u64 QPx_RAM;
+	u64 QPx_RDMAQE0;
+	u64 QPx_RDMAQE1;
+/* 0x280*/
+	u64 QPx_RDMAQE2;
+	u64 QPx_RDMAQE3;
+	u64 QPx_NRPSNHWM;
+#define QPx_NRPSNHWM_value EHCA_BMASK_IBM(40,63)
+/* 0x298*/
+	u64 reserved[(0x400 - 0x298) / 8];
+/* 0x400 extended data */
+	u64 reserved_ext[(0x500 - 0x400) / 8];
+/* 0x500 */
+	u64 reserved2[(0x1000 - 0x500) / 8];
+/* 0x1000      */
+};
+
+#define QPTEMM_OFFSET(x) offsetof(struct hipz_QPTEMM,x)
+
+/** @brief MRMWPT Entry Memory Map
+ */
+struct hipz_MRMWMM {
+	/* 0x00 */
+	u64 MRx_HCR;
+#define MRx_HCR_LPARID_VALID EHCA_BMASK_IBM(0,0)
+
+	u64 MRx_C;
+	u64 MRx_HERR;
+	u64 MRx_AER;
+	/* 0x20 */
+	u64 MRx_PP;
+	u64 reserved1;
+	u64 reserved2;
+	u64 reserved3;
+	/* 0x40 */
+	u64 reserved4[(0x200 - 0x40) / 8];
+	/* 0x200 */
+	u64 MRx_CTL[64];
+
+};
+
+#define MRMWMM_OFFSET(x) offsetof(struct hipz_MRMWMM,x)
+
+/** @brief QPEDMM
+ */
+struct hipz_QPEDMM {
+	/* 0x00 */
+	u64 reserved0[(0x400) / 8];
+	/* 0x400 */
+	u64 QPEDx_PHH;
+#define QPEDx_PHH_TClass EHCA_BMASK_IBM(4,11)
+#define QPEDx_PHH_HopLimit EHCA_BMASK_IBM(56,63)
+#define QPEDx_PHH_FlowLevel EHCA_BMASK_IBM(12,31)
+	u64 QPEDx_PPSGP;
+#define QPEDx_PPSGP_PPPidx EHCA_BMASK_IBM(0,63)
+	/* 0x410 */
+	u64 QPEDx_PPSGU;
+#define QPEDx_PPSGU_PPPSGID EHCA_BMASK_IBM(0,63)
+	u64 QPEDx_PPDGP;
+	/* 0x420 */
+	u64 QPEDx_PPDGU;
+	u64 QPEDx_APH;
+	/* 0x430 */
+	u64 QPEDx_APSGP;
+	u64 QPEDx_APSGU;
+	/* 0x440 */
+	u64 QPEDx_APDGP;
+	u64 QPEDx_APDGU;
+	/* 0x450 */
+	u64 QPEDx_APAV;
+	u64 QPEDx_APSAV;
+	/* 0x460  */
+	u64 QPEDx_HCR;
+	u64 reserved1[4];
+	/* 0x488 */
+	u64 QPEDx_RRL0;
+	/* 0x490 */
+	u64 QPEDx_RRRKEY0;
+	u64 QPEDx_RRVA0;
+	/* 0x4A0 */
+	u64 reserved2;
+	u64 QPEDx_RRL1;
+	/* 0x4B0 */
+	u64 QPEDx_RRRKEY1;
+	u64 QPEDx_RRVA1;
+	/* 0x4C0 */
+	u64 reserved3;
+	u64 QPEDx_RRL2;
+	/* 0x4D0 */
+	u64 QPEDx_RRRKEY2;
+	u64 QPEDx_RRVA2;
+	/* 0x4E0 */
+	u64 reserved4;
+	u64 QPEDx_RRL3;
+	/* 0x4F0 */
+	u64 QPEDx_RRRKEY3;
+	u64 QPEDx_RRVA3;
+};
+
+#define QPEDMM_OFFSET(x) offsetof(struct hipz_QPEDMM,x)
+
+/** @brief CQ Table Entry Memory Map
+ */
+struct hipz_CQTEMM {
+	u64 CQx_HCR;
+#define CQx_HCR_LPARID_valid EHCA_BMASK_IBM(0,0)
+	u64 CQx_C;
+#define CQx_C_Enable EHCA_BMASK_IBM(0,0)
+#define CQx_C_Disable_Complete EHCA_BMASK_IBM(1,1)
+#define CQx_C_Error_Reset EHCA_BMASK_IBM(23,23)
+	u64 CQx_HERR;
+	u64 CQx_AER;
+/* 0x20  */
+	u64 CQx_PTP;
+	u64 CQx_TP;
+#define CQx_FEC_CQE_cnt EHCA_BMASK_IBM(32,63)
+	u64 CQx_FEC;
+	u64 CQx_FECA;
+#define CQx_FECAdder EHCA_BMASK_IBM(32,63)
+/* 0x40  */
+	u64 CQx_EP;
+#define CQx_EP_Event_Pending EHCA_BMASK_IBM(0,0)
+#define CQx_EQ_number EHCA_BMASK_IBM(0,15)
+#define CQx_EQ_CQtoken EHCA_BMASK_IBM(32,63)
+	u64 CQx_EQ;
+/* 0x50  */
+	u64 reserved1;
+	u64 CQx_N0;
+#define CQx_N0_generate_solicited_comp_event EHCA_BMASK_IBM(0,0)
+/* 0x60  */
+	u64 CQx_N1;
+#define CQx_N1_generate_comp_event EHCA_BMASK_IBM(0,0)
+	u64 reserved2[(0x1000 - 0x60) / 8];
+/* 0x1000 */     
+};
+
+#define CQTEMM_OFFSET(x) offsetof(struct hipz_CQTEMM,x)
+
+/** @brief EQ Table Entry Memory Map
+ */
+struct hipz_EQTEMM {
+	u64 EQx_HCR;
+#define EQx_HCR_LPARID_valid EHCA_BMASK_IBM(0,0)
+#define EQx_HCR_ENABLE_PSB EHCA_BMASK_IBM(8,8)
+	u64 EQx_C;
+#define EQx_C_Enable EHCA_BMASK_IBM(0,0)
+#define EQx_C_Error_Reset EHCA_BMASK_IBM(23,23)
+#define EQx_C_Comp_Event EHCA_BMASK_IBM(17,17)
+
+	u64 EQx_HERR;
+	u64 EQx_AER;
+/* 0x20 */
+	u64 EQx_PTP;
+	u64 EQx_TP;
+	u64 EQx_SSBA;
+	u64 EQx_PSBA;
+
+/* 0x40 */
+	u64 EQx_CEC;
+	u64 EQx_MEQL;
+	u64 EQx_XISBI;
+	u64 EQx_XISC;
+/* 0x60 */
+	u64 EQx_IT;
+
+};
+#define EQTEMM_OFFSET(x) offsetof(struct hipz_EQTEMM,x)
+
+#endif
diff -urN oldtree/drivers/infiniband/hw/ehca/hipz_structs.h newtree/drivers/infiniband/hw/ehca/hipz_structs.h
--- oldtree/drivers/infiniband/hw/ehca/hipz_structs.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/hipz_structs.h	2006-02-13 19:19:59.951519672 -0500
@@ -0,0 +1,54 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  Infiniband Firmware structure definition
+ *
+ *  Authors: Waleri Fomin <fomin@de.ibm.com>
+ *           Christoph Raisch <raisch@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: hipz_structs.h,v 1.7 2005/11/03 10:40:21 nguyen Exp $
+ */
+
+#ifndef __HIPZ_STRUCTS_H__
+#define __HIPZ_STRUCTS_H__
+
+/* access control defines for MR/MW */
+#define HIPZ_ACCESSCTRL_L_WRITE  0x00800000
+#define HIPZ_ACCESSCTRL_R_WRITE  0x00400000
+#define HIPZ_ACCESSCTRL_R_READ   0x00200000
+#define HIPZ_ACCESSCTRL_R_ATOMIC 0x00100000
+#define HIPZ_ACCESSCTRL_MW_BIND  0x00080000
+
+#endif /* __IPZ_IF_H__ */
diff -urN oldtree/drivers/infiniband/hw/ehca/ipz_pt_fn.c newtree/drivers/infiniband/hw/ehca/ipz_pt_fn.c
--- oldtree/drivers/infiniband/hw/ehca/ipz_pt_fn.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ipz_pt_fn.c	2006-02-13 19:19:59.952519520 -0500
@@ -0,0 +1,137 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  internal queue handling
+ *
+ *  Authors: Waleri Fomin <fomin@de.ibm.com>
+ *           Reinhard Ernst <rernst@de.ibm.com> 
+ *           Christoph Raisch <raisch@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ipz_pt_fn.c,v 1.14 2005/11/29 17:11:10 nguyen Exp $
+ */
+
+#define DEB_PREFIX "iptz"
+
+#include "ehca_kernel.h"
+#include "ehca_tools.h"
+#include "ipz_pt_fn.h"
+
+extern int ehca_hwlevel;
+
+void *ipz_QPageit_get_inc(struct ipz_queue *queue)
+{
+	void *retvalue = NULL;
+	u8 *EOF_last_page = queue->queue + queue->queue_length;
+
+	retvalue = queue->current_q_addr;
+	queue->current_q_addr += queue->pagesize;
+	if (queue->current_q_addr > EOF_last_page) {
+		queue->current_q_addr -= queue->pagesize;
+		retvalue = NULL;
+	}
+
+	if ((((u64)retvalue) % EHCA_PAGESIZE) != 0) {
+		EDEB(4,  "ERROR!! not at PAGE-Boundary");
+		return (NULL);
+	}
+	EDEB(7, "queue=%p retvalue=%p", queue, retvalue);
+	return (retvalue);
+}
+
+void *ipz_QEit_EQ_get_inc(struct ipz_queue *queue)
+{
+	void *retvalue = NULL;
+	u8 *last_entry_in_q = queue->queue + queue->queue_length
+	    - queue->qe_size;
+
+	retvalue = queue->current_q_addr;
+	queue->current_q_addr += queue->qe_size;
+	if (queue->current_q_addr > last_entry_in_q) {
+		queue->current_q_addr = queue->queue;
+		queue->toggle_state = (~queue->toggle_state) & 1;
+	}
+
+	EDEB(7, "queue=%p retvalue=%p new current_q_addr=%p qe_size=%x", 
+	     queue, retvalue, queue->current_q_addr, queue->qe_size);
+
+	return (retvalue);
+}
+
+int ipz_queue_ctor(struct ipz_queue *queue,
+		   const u32 nr_of_pages,
+		   const u32 pagesize, const u32 qe_size, const u32 nr_of_sg)
+{
+	EDEB_EN(7,  "nr_of_pages=%x pagesize=%x qe_size=%x",
+		nr_of_pages, pagesize, qe_size);
+	queue->queue_length = nr_of_pages * pagesize;
+	queue->queue = vmalloc(queue->queue_length);
+	if (queue->queue == 0) {
+		EDEB(4,  "ERROR!! didn't get the memory");
+		return (FALSE);
+	}
+	if ((((u64)queue->queue) & (EHCA_PAGESIZE - 1)) != 0) {
+		EDEB(4,  "ERROR!! QUEUE doesn't start at "
+		     "page boundary");
+		vfree(queue->queue);
+		return (FALSE);
+	}
+
+	memset(queue->queue, 0, queue->queue_length);
+	queue->current_q_addr = queue->queue;
+	queue->qe_size = qe_size;
+	queue->act_nr_of_sg = nr_of_sg;
+	queue->pagesize = pagesize;
+	queue->toggle_state = 1;
+	EDEB_EX(7,  "queue_length=%x queue=%p qe_size=%x"
+		" act_nr_of_sg=%x", queue->queue_length, queue->queue,
+		queue->qe_size, queue->act_nr_of_sg);
+	return TRUE;
+}
+
+int ipz_queue_dtor(struct ipz_queue *queue)
+{
+	EDEB_EN(7,  "ipz_queue pointer=%p", queue);
+	if (queue == NULL) {
+		return (FALSE);
+	}
+	if (queue->queue == NULL) {
+		return (FALSE);
+	}
+	EDEB(7,  "destructing a queue with the following "
+	     "properties:\n nr_of_pages=%x pagesize=%x qe_size=%x",
+	     queue->act_nr_of_sg, queue->pagesize, queue->qe_size);
+	vfree(queue->queue);
+
+	EDEB_EX(7,  "queue freed!");
+	return TRUE;
+}
diff -urN oldtree/drivers/infiniband/hw/ehca/ipz_pt_fn.h newtree/drivers/infiniband/hw/ehca/ipz_pt_fn.h
--- oldtree/drivers/infiniband/hw/ehca/ipz_pt_fn.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ipz_pt_fn.h	2006-02-13 19:19:59.952519520 -0500
@@ -0,0 +1,165 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  internal queue handling
+ *
+ *  Authors: Waleri Fomin <fomin@de.ibm.com>
+ *           Reinhard Ernst <rernst@de.ibm.com> 
+ *           Christoph Raisch <raisch@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ipz_pt_fn.h,v 1.9 2005/11/03 09:50:00 nguyen Exp $
+ */
+
+#ifndef __IPZ_PT_FN_H__
+#define __IPZ_PT_FN_H__
+
+#include "ipz_pt_fn_core.h"
+#include "ehca_qes.h"
+
+#define EHCA_PAGESIZE   4096UL
+#define EHCA_PT_ENTRIES 512UL
+
+/** @brief generic page table
+ */
+struct ipz_pt {
+	u64 entries[EHCA_PT_ENTRIES];
+};
+
+/** @brief generic page
+ */
+struct ipz_page {
+	u8 entries[EHCA_PAGESIZE];
+};
+
+/** @brief page table for a queue, only to be used in pf
+ */
+struct ipz_qpt {
+	/* queue page tables (kv), use u64 because we know the element length */
+	u64 *qpts;
+	u32 allocated_qpts_entries;
+	u32 nr_of_PTEs; /*  number of page table entries PTE iterators */
+	u64 *current_pte_addr;
+};
+
+/** @brief constructor for a ipz_queue_t, placement new for ipz_queue_t, 
+    new for all dependent datastructors
+
+    all QP Tables are the same
+    flow:
+    -# allocate+pin queue
+    @see ipz_qpt_ctor()
+    @returns true if ok, false if out of memory
+ */
+int ipz_queue_ctor(struct ipz_queue *queue, const u32 nr_of_pages,
+		   const u32 pagesize,
+		   const u32 qe_size, /* queue entry size*/
+		   const u32 nr_of_sg);
+
+/** @brief destructor for a ipz_queue_t 
+    -# free queue
+    @see ipz_queue_ctor()
+    @returns true if ok, false if queue was NULL-ptr of free failed 
+*/
+int ipz_queue_dtor(struct ipz_queue *queue);
+
+/** @brief constructor for a ipz_qpt_t, 
+ * placement new for struct ipz_queue, new for all dependent datastructors
+ *
+ *  all QP Tables are the same,
+ *  flow:
+ *  -# allocate+pin queue
+ *  -# initialise ptcb
+ *  -# allocate+pin PTs
+ *  -# link PTs to a ring, according to HCA Arch, set bit62 id needed
+ *  -# the ring must have room for exactly nr_of_PTEs
+ *  @see ipz_qpt_ctor()
+ */
+void ipz_qpt_ctor(struct ipz_qpt *qpt,
+		  struct ehca_bridge_handle bridge,
+		  const u32 nr_of_QEs,
+		  const u32 pagesize,
+		  const u32 qe_size,
+		  const u8 lowbyte, const u8 toggle, 
+		  u32 * act_nr_of_QEs,
+		  u32 * act_nr_of_pages);
+
+/**  @brief return current Queue Entry, increment Queue Entry iterator by one 
+     step in struct ipz_queue, will wrap in ringbuffer
+     @returns address (kv) of Queue Entry BEFORE increment
+     @warning don't use in parallel with ipz_QPageit_get_inc()
+     @warning unpredictable results may occur if steps>act_nr_of_queue_entries
+     
+     fix EQ page problems
+ */
+void *ipz_QEit_EQ_get_inc(struct ipz_queue *queue);
+
+/**  @brief return current Event Queue Entry, increment Queue Entry iterator 
+     by one step in struct ipz_queue if valid, will wrap in ringbuffer
+     @returns address (kv) of Queue Entry BEFORE increment
+     @returns 0 and does not increment, if wrong valid state
+     @warning don't use in parallel with ipz_queue_QPageit_get_inc()
+     @warning unpredictable results may occur if steps>act_nr_of_queue_entries
+ */
+inline static void *ipz_QEit_EQ_get_inc_valid(struct ipz_queue *queue)
+{
+	void *retvalue = ipz_QEit_get(queue);
+	u32 qe = *(u8 *) retvalue;
+	EDEB(7, "ipz_QEit_EQ_get_inc_valid qe=%x", qe);
+	if ((qe >> 7) == (queue->toggle_state & 1)) {
+		/* this is a good one */
+		ipz_QEit_EQ_get_inc(queue);
+	} else {
+		retvalue = NULL;
+	}
+	return (retvalue);
+}
+
+/**  
+     @returns address (GX) of first queue entry
+ */
+inline static u64 ipz_qpt_get_firstpage(struct ipz_qpt *qpt)
+{
+	return (be64_to_cpu(qpt->qpts[0]));
+}
+
+/**  
+     @returns address (kv) of first page of queue page table
+ */
+inline static void *ipz_qpt_get_qpt(struct ipz_qpt *qpt)
+{
+	return (qpt->qpts);
+}
+
+#endif /* __IPZ_PT_FN_H__ */
diff -urN oldtree/drivers/infiniband/hw/ehca/ipz_pt_fn_core.h newtree/drivers/infiniband/hw/ehca/ipz_pt_fn_core.h
--- oldtree/drivers/infiniband/hw/ehca/ipz_pt_fn_core.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/infiniband/hw/ehca/ipz_pt_fn_core.h	2006-02-13 19:19:59.954519216 -0500
@@ -0,0 +1,152 @@
+/* 
+ *  IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *   
+ *  internal queue handling
+ *
+ *  Authors: Waleri Fomin <fomin@de.ibm.com>
+ *           Reinhard Ernst <rernst@de.ibm.com> 
+ *           Christoph Raisch <raisch@de.ibm.com>
+ *
+ *  Copyright (c) 2005 IBM Corporation
+ *    
+ *  All rights reserved.
+ *
+ *  This source code is distributed under a dual license of GPL v2.0 and OpenIB 
+ *  BSD. 
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met: 
+ *
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials
+ * provided with the distribution. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  $Id: ipz_pt_fn_core.h,v 1.10 2005/11/29 17:11:10 nguyen Exp $
+ */
+
+#ifndef __IPZ_PT_FN_CORE_H__
+#define __IPZ_PT_FN_CORE_H__
+
+#ifdef __KERNEL__
+#include "ehca_tools.h"
+#else /* some replacements for kernel stuff */
+#include "ehca_utools.h"
+#endif
+
+#include "ehca_qes.h"
+
+/** @brief generic queue in linux kernel virtual memory (kv)
+ */
+struct ipz_queue {
+#ifndef __PPC64__
+	void * dummy1;              /* make sure we use the same thing on 32 bit */
+#endif
+	u8 *current_q_addr;         /* current queue entry */
+#ifndef __PPC64__
+	void * dummy2;
+#endif
+	u8 *queue;                  /* points to first queue entry */
+	u32 qe_size;                /* queue entry size */
+	u32 act_nr_of_sg;
+	u32 queue_length;           /* queue length allocated in bytes */
+	u32 pagesize;
+	u32 toggle_state;           /* toggle flag - per page */
+	u32 dummy3;                 /* 64 bit alignment*/
+};
+
+/**  @brief return current Queue Entry
+     @returns address (kv) of Queue Entry 
+ */
+static inline void *ipz_QEit_get(struct ipz_queue *queue)
+{
+	return (queue->current_q_addr);
+}
+
+/**  @brief return current Queue Page , increment Queue Page iterator from 
+     page to page in struct ipz_queue, last increment will return 0! and 
+     NOT wrap
+     @returns address (kv) of Queue Page
+     @warning don't use in parallel with ipz_QE_get_inc()
+ */
+void *ipz_QPageit_get_inc(struct ipz_queue *queue);
+
+/**  @brief return current Queue Entry, increment Queue Entry iterator by one 
+     step in struct ipz_queue, will wrap in ringbuffer
+     @returns address (kv) of Queue Entry BEFORE increment
+     @warning don't use in parallel with ipz_QPageit_get_inc()
+     @warning unpredictable results may occur if steps>act_nr_of_queue_entries
+ */
+static inline void *ipz_QEit_get_inc(struct ipz_queue *queue)
+{
+	void *retvalue = 0;
+	u8 *last_entry_in_q = queue->queue + queue->queue_length
+	    - queue->qe_size;
+
+	retvalue = queue->current_q_addr;
+	queue->current_q_addr += queue->qe_size;
+	if (queue->current_q_addr > last_entry_in_q) {
+		queue->current_q_addr = queue->queue;
+		/* toggle the valid flag */
+		queue->toggle_state = (~queue->toggle_state) & 1;
+	}
+
+	EDEB(7, "queue=%p retvalue=%p new current_q_addr=%p qe_size=%x", 
+	     queue, retvalue, queue->current_q_addr, queue->qe_size);
+
+	return (retvalue);
+}
+
+/**  @brief return current Queue Entry, increment Queue Entry iterator by one 
+     step in struct ipz_queue, will wrap in ringbuffer
+     @returns address (kv) of Queue Entry BEFORE increment
+     @returns 0 and does not increment, if wrong valid state
+     @warning don't use in parallel with ipz_QPageit_get_inc()
+     @warning unpredictable results may occur if steps>act_nr_of_queue_entries
+ */
+inline static void *ipz_QEit_get_inc_valid(struct ipz_queue *queue)
+{
+	void *retvalue = ipz_QEit_get(queue);
+#ifdef USERSPACE_DRIVER
+
+	u32 qe =
+	    ((struct ehca_cqe *)(ehca_ktou((struct ehca_cqe *)retvalue)))->
+	    cqe_flags;
+#else
+	u32 qe = ((struct ehca_cqe *)retvalue)->cqe_flags;
+#endif
+	if ((qe >> 7) == (queue->toggle_state & 1)) {
+		/* this is a good one */
+		ipz_QEit_get_inc(queue);
+	} else
+		retvalue = 0;
+	return (retvalue);
+}
+
+/**  @brief returns and resets Queue Entry iterator
+     @returns address (kv) of first Queue Entry 
+ */
+static inline void *ipz_QEit_reset(struct ipz_queue *queue)
+{
+	queue->current_q_addr = queue->queue;
+	return (queue->queue);
+}
+
+#endif /* __IPZ_PT_FN_CORE_H__ */
diff -urN oldtree/drivers/input/joystick/db9.c newtree/drivers/input/joystick/db9.c
--- oldtree/drivers/input/joystick/db9.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/input/joystick/db9.c	2006-02-13 19:20:29.707996000 -0500
@@ -275,68 +275,70 @@
 /*
  * db9_saturn_report() analyzes packet and reports.
  */
-static int db9_saturn_report(unsigned char id, unsigned char data[60], struct input_dev *dev, int n, int max_pads)
+static int db9_saturn_report(unsigned char id, unsigned char data[60], struct input_dev *devs[], int n, int max_pads)
 {
+	struct input_dev *dev;
 	int tmp, i, j;
 
 	tmp = (id == 0x41) ? 60 : 10;
-	for (j = 0; (j < tmp) && (n < max_pads); j += 10, n++) {
+	for (j = 0; j < tmp && n < max_pads; j += 10, n++) {
+		dev = devs[n];
 		switch (data[j]) {
 		case 0x16: /* multi controller (analog 4 axis) */
-			input_report_abs(dev + n, db9_abs[5], data[j + 6]);
+			input_report_abs(dev, db9_abs[5], data[j + 6]);
 		case 0x15: /* mission stick (analog 3 axis) */
-			input_report_abs(dev + n, db9_abs[3], data[j + 4]);
-			input_report_abs(dev + n, db9_abs[4], data[j + 5]);
+			input_report_abs(dev, db9_abs[3], data[j + 4]);
+			input_report_abs(dev, db9_abs[4], data[j + 5]);
 		case 0x13: /* racing controller (analog 1 axis) */
-			input_report_abs(dev + n, db9_abs[2], data[j + 3]);
+			input_report_abs(dev, db9_abs[2], data[j + 3]);
 		case 0x34: /* saturn keyboard (udlr ZXC ASD QE Esc) */
 		case 0x02: /* digital pad (digital 2 axis + buttons) */
-			input_report_abs(dev + n, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
-			input_report_abs(dev + n, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16));
+			input_report_abs(dev, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
+			input_report_abs(dev, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16));
 			for (i = 0; i < 9; i++)
-				input_report_key(dev + n, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]);
+				input_report_key(dev, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]);
 			break;
 		case 0x19: /* mission stick x2 (analog 6 axis + buttons) */
-			input_report_abs(dev + n, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
-			input_report_abs(dev + n, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16));
+			input_report_abs(dev, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
+			input_report_abs(dev, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16));
 			for (i = 0; i < 9; i++)
-				input_report_key(dev + n, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]);
-			input_report_abs(dev + n, db9_abs[2], data[j + 3]);
-			input_report_abs(dev + n, db9_abs[3], data[j + 4]);
-			input_report_abs(dev + n, db9_abs[4], data[j + 5]);
+				input_report_key(dev, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]);
+			input_report_abs(dev, db9_abs[2], data[j + 3]);
+			input_report_abs(dev, db9_abs[3], data[j + 4]);
+			input_report_abs(dev, db9_abs[4], data[j + 5]);
 			/*
-			input_report_abs(dev + n, db9_abs[8], (data[j + 6] & 128 ? 0 : 1) - (data[j + 6] & 64 ? 0 : 1));
-			input_report_abs(dev + n, db9_abs[9], (data[j + 6] & 32 ? 0 : 1) - (data[j + 6] & 16 ? 0 : 1));
+			input_report_abs(dev, db9_abs[8], (data[j + 6] & 128 ? 0 : 1) - (data[j + 6] & 64 ? 0 : 1));
+			input_report_abs(dev, db9_abs[9], (data[j + 6] & 32 ? 0 : 1) - (data[j + 6] & 16 ? 0 : 1));
 			*/
-			input_report_abs(dev + n, db9_abs[6], data[j + 7]);
-			input_report_abs(dev + n, db9_abs[7], data[j + 8]);
-			input_report_abs(dev + n, db9_abs[5], data[j + 9]);
+			input_report_abs(dev, db9_abs[6], data[j + 7]);
+			input_report_abs(dev, db9_abs[7], data[j + 8]);
+			input_report_abs(dev, db9_abs[5], data[j + 9]);
 			break;
 		case 0xd3: /* sankyo ff (analog 1 axis + stop btn) */
-			input_report_key(dev + n, BTN_A, data[j + 3] & 0x80);
-			input_report_abs(dev + n, db9_abs[2], data[j + 3] & 0x7f);
+			input_report_key(dev, BTN_A, data[j + 3] & 0x80);
+			input_report_abs(dev, db9_abs[2], data[j + 3] & 0x7f);
 			break;
 		case 0xe3: /* shuttle mouse (analog 2 axis + buttons. signed value) */
-			input_report_key(dev + n, BTN_START, data[j + 1] & 0x08);
-			input_report_key(dev + n, BTN_A, data[j + 1] & 0x04);
-			input_report_key(dev + n, BTN_C, data[j + 1] & 0x02);
-			input_report_key(dev + n, BTN_B, data[j + 1] & 0x01);
-			input_report_abs(dev + n, db9_abs[2], data[j + 2] ^ 0x80);
-			input_report_abs(dev + n, db9_abs[3], (0xff-(data[j + 3] ^ 0x80))+1); /* */
+			input_report_key(dev, BTN_START, data[j + 1] & 0x08);
+			input_report_key(dev, BTN_A, data[j + 1] & 0x04);
+			input_report_key(dev, BTN_C, data[j + 1] & 0x02);
+			input_report_key(dev, BTN_B, data[j + 1] & 0x01);
+			input_report_abs(dev, db9_abs[2], data[j + 2] ^ 0x80);
+			input_report_abs(dev, db9_abs[3], (0xff-(data[j + 3] ^ 0x80))+1); /* */
 			break;
 		case 0xff:
 		default: /* no pad */
-			input_report_abs(dev + n, db9_abs[0], 0);
-			input_report_abs(dev + n, db9_abs[1], 0);
+			input_report_abs(dev, db9_abs[0], 0);
+			input_report_abs(dev, db9_abs[1], 0);
 			for (i = 0; i < 9; i++)
-				input_report_key(dev + n, db9_cd32_btn[i], 0);
+				input_report_key(dev, db9_cd32_btn[i], 0);
 			break;
 		}
 	}
 	return n;
 }
 
-static int db9_saturn(int mode, struct parport *port, struct input_dev *dev)
+static int db9_saturn(int mode, struct parport *port, struct input_dev *devs[])
 {
 	unsigned char id, data[60];
 	int type, n, max_pads;
@@ -361,7 +363,7 @@
 	max_pads = min(db9_modes[mode].n_pads, DB9_MAX_DEVICES);
 	for (tmp = 0, i = 0; i < n; i++) {
 		id = db9_saturn_read_packet(port, data, type + i, 1);
-		tmp = db9_saturn_report(id, data, dev, tmp, max_pads);
+		tmp = db9_saturn_report(id, data, devs, tmp, max_pads);
 	}
 	return 0;
 }
@@ -489,7 +491,7 @@
 		case DB9_SATURN_DPP:
 		case DB9_SATURN_DPP_2:
 
-			db9_saturn(db9->mode, port, dev);
+			db9_saturn(db9->mode, port, db9->dev);
 			break;
 
 		case DB9_CD32_PAD:
diff -urN oldtree/drivers/input/joystick/grip.c newtree/drivers/input/joystick/grip.c
--- oldtree/drivers/input/joystick/grip.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/input/joystick/grip.c	2006-02-13 19:20:29.707996000 -0500
@@ -192,6 +192,9 @@
 	for (i = 0; i < 2; i++) {
 
 		dev = grip->dev[i];
+		if (!dev)
+			continue;
+
 		grip->reads++;
 
 		switch (grip->mode[i]) {
diff -urN oldtree/drivers/input/joystick/iforce/iforce-main.c newtree/drivers/input/joystick/iforce/iforce-main.c
--- oldtree/drivers/input/joystick/iforce/iforce-main.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/input/joystick/iforce/iforce-main.c	2006-02-13 19:20:29.708995848 -0500
@@ -345,7 +345,7 @@
 	int i;
 
 	input_dev = input_allocate_device();
-	if (input_dev)
+	if (!input_dev)
 		return -ENOMEM;
 
 	init_waitqueue_head(&iforce->wait);
diff -urN oldtree/drivers/input/joystick/iforce/iforce-packets.c newtree/drivers/input/joystick/iforce/iforce-packets.c
--- oldtree/drivers/input/joystick/iforce/iforce-packets.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/input/joystick/iforce/iforce-packets.c	2006-02-13 19:20:29.708995848 -0500
@@ -167,9 +167,9 @@
 		iforce->expect_packet = 0;
 		iforce->ecmd = cmd;
 		memcpy(iforce->edata, data, IFORCE_MAX_LENGTH);
-		wake_up(&iforce->wait);
 	}
 #endif
+	wake_up(&iforce->wait);
 
 	if (!iforce->type) {
 		being_used--;
@@ -264,7 +264,7 @@
 		wait_event_interruptible_timeout(iforce->wait,
 			iforce->ctrl->status != -EINPROGRESS, HZ);
 
-		if (iforce->ctrl->status != -EINPROGRESS) {
+		if (iforce->ctrl->status) {
 			usb_unlink_urb(iforce->ctrl);
 			return -1;
 		}
diff -urN oldtree/drivers/input/joystick/iforce/iforce-usb.c newtree/drivers/input/joystick/iforce/iforce-usb.c
--- oldtree/drivers/input/joystick/iforce/iforce-usb.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/input/joystick/iforce/iforce-usb.c	2006-02-13 19:20:29.710995544 -0500
@@ -95,7 +95,6 @@
 		goto exit;
 	}
 
-	wake_up(&iforce->wait);
 	iforce_process_packet(iforce,
 		(iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1, regs);
 
diff -urN oldtree/drivers/input/joystick/sidewinder.c newtree/drivers/input/joystick/sidewinder.c
--- oldtree/drivers/input/joystick/sidewinder.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/input/joystick/sidewinder.c	2006-02-13 19:20:29.710995544 -0500
@@ -736,7 +736,7 @@
 		sprintf(sw->name, "Microsoft SideWinder %s", sw_name[sw->type]);
 		sprintf(sw->phys[i], "%s/input%d", gameport->phys, i);
 
-		input_dev = input_allocate_device();
+		sw->dev[i] = input_dev = input_allocate_device();
 		if (!input_dev) {
 			err = -ENOMEM;
 			goto fail3;
diff -urN oldtree/drivers/input/mouse/Makefile newtree/drivers/input/mouse/Makefile
--- oldtree/drivers/input/mouse/Makefile	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/input/mouse/Makefile	2006-02-13 19:19:59.954519216 -0500
@@ -15,4 +15,5 @@
 obj-$(CONFIG_MOUSE_HIL)		+= hil_ptr.o
 obj-$(CONFIG_MOUSE_VSXXXAA)	+= vsxxxaa.o
 
-psmouse-objs  := psmouse-base.o alps.o logips2pp.o synaptics.o lifebook.o trackpoint.o
+psmouse-objs  := psmouse-base.o alps.o logips2pp.o synaptics.o lifebook.o \
+		trackpoint.o touchkit_ps2.o
diff -urN oldtree/drivers/input/mouse/alps.c newtree/drivers/input/mouse/alps.c
--- oldtree/drivers/input/mouse/alps.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/input/mouse/alps.c	2006-02-13 19:19:59.955519064 -0500
@@ -347,6 +347,39 @@
 	return 0;
 }
 
+/*
+ * alps_poll() - poll the touchpad for current motion packet.
+ * Used in resync.
+ */
+static int alps_poll(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned char buf[6];
+
+	if (priv->i->flags & ALPS_PASS)
+		alps_passthrough_mode(psmouse, 1);
+
+	if (ps2_command(&psmouse->ps2dev, buf, PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)))
+		return -1;
+
+	if ((buf[0] & priv->i->mask0) != priv->i->byte0)
+		return -1;
+
+	if (priv->i->flags & ALPS_PASS)
+		alps_passthrough_mode(psmouse, 0);
+
+	if ((psmouse->badbyte & 0xc8) == 0x08) {
+/*
+ * Poll the track stick ...
+ */
+		if (ps2_command(&psmouse->ps2dev, buf, PSMOUSE_CMD_POLL | (3 << 8)))
+			return -1;
+	}
+
+	memcpy(psmouse->packet, buf, sizeof(buf));
+	return 0;
+}
+
 static int alps_reconnect(struct psmouse *psmouse)
 {
 	struct alps_data *priv = psmouse->private;
@@ -450,10 +483,14 @@
 	input_register_device(priv->dev2);
 
 	psmouse->protocol_handler = alps_process_byte;
+	psmouse->poll = alps_poll;
 	psmouse->disconnect = alps_disconnect;
 	psmouse->reconnect = alps_reconnect;
 	psmouse->pktsize = 6;
 
+	/* We are having trouble resyncing ALPS touchpads so disable it for now */
+	psmouse->resync_time = 0;
+
 	return 0;
 
 init_fail:
diff -urN oldtree/drivers/input/mouse/logips2pp.c newtree/drivers/input/mouse/logips2pp.c
--- oldtree/drivers/input/mouse/logips2pp.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/input/mouse/logips2pp.c	2006-02-13 19:19:59.955519064 -0500
@@ -117,7 +117,7 @@
 	if (psmouse_sliced_command(psmouse, command))
 		return -1;
 
-	if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL))
+	if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL | 0x0300))
 		return -1;
 
 	return 0;
diff -urN oldtree/drivers/input/mouse/psmouse-base.c newtree/drivers/input/mouse/psmouse-base.c
--- oldtree/drivers/input/mouse/psmouse-base.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/input/mouse/psmouse-base.c	2006-02-13 19:19:59.958518608 -0500
@@ -26,6 +26,7 @@
 #include "alps.h"
 #include "lifebook.h"
 #include "trackpoint.h"
+#include "touchkit_ps2.h"
 
 #define DRIVER_DESC	"PS/2 mouse driver"
 
@@ -58,6 +59,10 @@
 module_param_named(resetafter, psmouse_resetafter, uint, 0644);
 MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never).");
 
+static unsigned int psmouse_resync_time = 5;
+module_param_named(resync_time, psmouse_resync_time, uint, 0644);
+MODULE_PARM_DESC(resync_time, "How long can mouse stay idle before forcing resync (in seconds, 0 = never).");
+
 PSMOUSE_DEFINE_ATTR(protocol, S_IWUSR | S_IRUGO,
 			NULL,
 			psmouse_attr_show_protocol, psmouse_attr_set_protocol);
@@ -70,12 +75,16 @@
 PSMOUSE_DEFINE_ATTR(resetafter, S_IWUSR | S_IRUGO,
 			(void *) offsetof(struct psmouse, resetafter),
 			psmouse_show_int_attr, psmouse_set_int_attr);
+PSMOUSE_DEFINE_ATTR(resync_time, S_IWUSR | S_IRUGO,
+			(void *) offsetof(struct psmouse, resync_time),
+			psmouse_show_int_attr, psmouse_set_int_attr);
 
 static struct attribute *psmouse_attributes[] = {
 	&psmouse_attr_protocol.dattr.attr,
 	&psmouse_attr_rate.dattr.attr,
 	&psmouse_attr_resolution.dattr.attr,
 	&psmouse_attr_resetafter.dattr.attr,
+	&psmouse_attr_resync_time.dattr.attr,
 	NULL
 };
 
@@ -98,6 +107,8 @@
  */
 static DECLARE_MUTEX(psmouse_sem);
 
+static struct workqueue_struct *kpsmoused_wq;
+
 struct psmouse_protocol {
 	enum psmouse_type type;
 	char *name;
@@ -178,15 +189,79 @@
 }
 
 /*
- * psmouse_interrupt() handles incoming characters, either gathering them into
- * packets or passing them to the command routine as command output.
+ * __psmouse_set_state() sets new psmouse state and resets all flags.
+ */
+
+static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
+{
+	psmouse->state = new_state;
+	psmouse->pktcnt = psmouse->out_of_sync = 0;
+	psmouse->ps2dev.flags = 0;
+	psmouse->last = jiffies;
+}
+
+
+/*
+ * psmouse_set_state() sets new psmouse state and resets all flags and
+ * counters while holding serio lock so fighting with interrupt handler
+ * is not a concern.
+ */
+
+static void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
+{
+	serio_pause_rx(psmouse->ps2dev.serio);
+	__psmouse_set_state(psmouse, new_state);
+	serio_continue_rx(psmouse->ps2dev.serio);
+}
+
+/*
+ * psmouse_handle_byte() processes one byte of the input data stream
+ * by calling corresponding protocol handler.
+ */
+
+static int psmouse_handle_byte(struct psmouse *psmouse, struct pt_regs *regs)
+{
+	psmouse_ret_t rc = psmouse->protocol_handler(psmouse, regs);
+
+	switch (rc) {
+		case PSMOUSE_BAD_DATA:
+			if (psmouse->state == PSMOUSE_ACTIVATED) {
+				printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n",
+					psmouse->name, psmouse->phys, psmouse->pktcnt);
+				if (++psmouse->out_of_sync == psmouse->resetafter) {
+					__psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+					printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n");
+					serio_reconnect(psmouse->ps2dev.serio);
+					return -1;
+				}
+			}
+			psmouse->pktcnt = 0;
+			break;
+
+		case PSMOUSE_FULL_PACKET:
+			psmouse->pktcnt = 0;
+			if (psmouse->out_of_sync) {
+				psmouse->out_of_sync = 0;
+				printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n",
+					psmouse->name, psmouse->phys);
+			}
+			break;
+
+		case PSMOUSE_GOOD_DATA:
+			break;
+	}
+	return 0;
+}
+
+/*
+ * psmouse_interrupt() handles incoming characters, either passing them
+ * for normal processing or gathering them as command response.
  */
 
 static irqreturn_t psmouse_interrupt(struct serio *serio,
 		unsigned char data, unsigned int flags, struct pt_regs *regs)
 {
 	struct psmouse *psmouse = serio_get_drvdata(serio);
-	psmouse_ret_t rc;
 
 	if (psmouse->state == PSMOUSE_IGNORE)
 		goto out;
@@ -208,67 +283,58 @@
 		if  (ps2_handle_response(&psmouse->ps2dev, data))
 			goto out;
 
-	if (psmouse->state == PSMOUSE_INITIALIZING)
+	if (psmouse->state <= PSMOUSE_RESYNCING)
 		goto out;
 
 	if (psmouse->state == PSMOUSE_ACTIVATED &&
 	    psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) {
-		printk(KERN_WARNING "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n",
+		printk(KERN_INFO "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n",
 		       psmouse->name, psmouse->phys, psmouse->pktcnt);
-		psmouse->pktcnt = 0;
+		psmouse->badbyte = psmouse->packet[0];
+		__psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
+		queue_work(kpsmoused_wq, &psmouse->resync_work);
+		goto out;
 	}
 
-	psmouse->last = jiffies;
 	psmouse->packet[psmouse->pktcnt++] = data;
-
-	if (psmouse->packet[0] == PSMOUSE_RET_BAT) {
+/*
+ * Check if this is a new device announcement (0xAA 0x00)
+ */
+	if (unlikely(psmouse->packet[0] == PSMOUSE_RET_BAT && psmouse->pktcnt <= 2)) {
 		if (psmouse->pktcnt == 1)
 			goto out;
 
-		if (psmouse->pktcnt == 2) {
-			if (psmouse->packet[1] == PSMOUSE_RET_ID) {
-				psmouse->state = PSMOUSE_IGNORE;
-				serio_reconnect(serio);
-				goto out;
-			}
-			if (psmouse->type == PSMOUSE_SYNAPTICS) {
-				/* neither 0xAA nor 0x00 are valid first bytes
-				 * for a packet in absolute mode
-				 */
-				psmouse->pktcnt = 0;
-				goto out;
-			}
+		if (psmouse->packet[1] == PSMOUSE_RET_ID) {
+			__psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+			serio_reconnect(serio);
+			goto out;
 		}
-	}
-
-	rc = psmouse->protocol_handler(psmouse, regs);
+/*
+ * Not a new device, try processing first byte normally
+ */
+		psmouse->pktcnt = 1;
+		if (psmouse_handle_byte(psmouse, regs))
+			goto out;
 
-	switch (rc) {
-		case PSMOUSE_BAD_DATA:
-			printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n",
-				psmouse->name, psmouse->phys, psmouse->pktcnt);
-			psmouse->pktcnt = 0;
+		psmouse->packet[psmouse->pktcnt++] = data;
+	}
 
-			if (++psmouse->out_of_sync == psmouse->resetafter) {
-				psmouse->state = PSMOUSE_IGNORE;
-				printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n");
-				serio_reconnect(psmouse->ps2dev.serio);
-			}
-			break;
+/*
+ * See if we need to force resync because mouse was idle for too long
+ */
+	if (psmouse->state == PSMOUSE_ACTIVATED &&
+	    psmouse->pktcnt == 1 && psmouse->resync_time &&
+	    time_after(jiffies, psmouse->last + psmouse->resync_time * HZ)) {
+		psmouse->badbyte = psmouse->packet[0];
+		__psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
+		queue_work(kpsmoused_wq, &psmouse->resync_work);
+		goto out;
+	}
 
-		case PSMOUSE_FULL_PACKET:
-			psmouse->pktcnt = 0;
-			if (psmouse->out_of_sync) {
-				psmouse->out_of_sync = 0;
-				printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n",
-					psmouse->name, psmouse->phys);
-			}
-			break;
+	psmouse->last = jiffies;
+	psmouse_handle_byte(psmouse, regs);
 
-		case PSMOUSE_GOOD_DATA:
-			break;
-	}
-out:
+ out:
 	return IRQ_HANDLED;
 }
 
@@ -545,6 +611,13 @@
 	if (max_proto > PSMOUSE_IMEX && trackpoint_detect(psmouse, set_properties) == 0)
 		return PSMOUSE_TRACKPOINT;
 
+	if (touchkit_ps2_detect(psmouse, set_properties) == 0) {
+		if (max_proto >= PSMOUSE_TOUCHKIT_PS2) {
+			if (!set_properties || touchkit_ps2_init(psmouse) == 0)
+				return PSMOUSE_TOUCHKIT_PS2;
+		}
+	}
+
 /*
  * Okay, all failed, we have a standard mouse here. The number of the buttons
  * is still a question, though. We assume 3.
@@ -632,6 +705,13 @@
 		.detect		= trackpoint_detect,
 	},
 	{
+		.type		= PSMOUSE_TOUCHKIT_PS2,
+		.name		= "touchkitPS/2",
+		.alias		= "touchkit",
+		.detect		= touchkit_ps2_detect,
+		.init		= touchkit_ps2_init,
+	},
+	{
 		.type		= PSMOUSE_AUTO,
 		.name		= "auto",
 		.alias		= "any",
@@ -755,21 +835,6 @@
 }
 
 /*
- * psmouse_set_state() sets new psmouse state and resets all flags and
- * counters while holding serio lock so fighting with interrupt handler
- * is not a concern.
- */
-
-static void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
-{
-	serio_pause_rx(psmouse->ps2dev.serio);
-	psmouse->state = new_state;
-	psmouse->pktcnt = psmouse->out_of_sync = 0;
-	psmouse->ps2dev.flags = 0;
-	serio_continue_rx(psmouse->ps2dev.serio);
-}
-
-/*
  * psmouse_activate() enables the mouse so that we get motion reports from it.
  */
 
@@ -797,6 +862,100 @@
 	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
 }
 
+/*
+ * psmouse_poll() - default poll hanlder. Everyone except for ALPS uses it.
+ */
+
+static int psmouse_poll(struct psmouse *psmouse)
+{
+	return ps2_command(&psmouse->ps2dev, psmouse->packet,
+			   PSMOUSE_CMD_POLL | (psmouse->pktsize << 8));
+}
+
+
+/*
+ * psmouse_resync() attempts to re-validate current protocol.
+ */
+
+static void psmouse_resync(void *p)
+{
+	struct psmouse *psmouse = p, *parent = NULL;
+	struct serio *serio = psmouse->ps2dev.serio;
+	psmouse_ret_t rc = PSMOUSE_GOOD_DATA;
+	int failed = 0, enabled = 0;
+	int i;
+
+	down(&psmouse_sem);
+
+	if (psmouse->state != PSMOUSE_RESYNCING)
+		goto out;
+
+	if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+		parent = serio_get_drvdata(serio->parent);
+		psmouse_deactivate(parent);
+	}
+
+/*
+ * Some mice don't ACK commands sent while they are in the middle of
+ * transmitting motion packet. To avoid delay we use ps2_sendbyte()
+ * instead of ps2_command() which would wait for 200ms for an ACK
+ * that may never come.
+ */
+	ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, 20);
+
+/*
+ * Poll the mouse. If it was reset the packet will be shorter than
+ * psmouse->pktsize and ps2_command will fail. We do not expect and
+ * do not handle scenario when mouse "upgrades" its protocol while
+ * disconnected since it would require additional delay. If we ever
+ * see a mouse that does it we'll adjust the code.
+ */
+	if (psmouse->poll(psmouse))
+		failed = 1;
+	else {
+		psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+		for (i = 0; i < psmouse->pktsize; i++) {
+			psmouse->pktcnt++;
+			rc = psmouse->protocol_handler(psmouse, NULL);
+			if (rc != PSMOUSE_GOOD_DATA)
+				break;
+		}
+		if (rc != PSMOUSE_FULL_PACKET)
+			failed = 1;
+		psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
+	}
+
+/*
+ * Now try to enable mouse. We try to do that even if poll failed and also
+ * repeat our attempts 5 times, otherwise we may be left out with disabled
+ * mouse.
+ */
+	for (i = 0; i < 5; i++) {
+		if (!ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
+			enabled = 1;
+			break;
+		}
+		msleep(200);
+	}
+
+	if (!enabled) {
+		printk(KERN_WARNING "psmouse.c: failed to re-enable mouse on %s\n",
+			psmouse->ps2dev.serio->phys);
+		failed = 1;
+	}
+
+	if (failed) {
+		psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+		printk(KERN_INFO "psmouse.c: resync failed, issuing reconnect request\n");
+		serio_reconnect(serio);
+	} else
+		psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
+
+	if (parent)
+		psmouse_activate(parent);
+ out:
+	up(&psmouse_sem);
+}
 
 /*
  * psmouse_cleanup() resets the mouse into power-on state.
@@ -825,6 +984,11 @@
 
 	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
 
+	/* make sure we don't have a resync in progress */
+	up(&psmouse_sem);
+	flush_workqueue(kpsmoused_wq);
+	down(&psmouse_sem);
+
 	if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
 		parent = serio_get_drvdata(serio->parent);
 		psmouse_deactivate(parent);
@@ -862,6 +1026,7 @@
 
 	psmouse->set_rate = psmouse_set_rate;
 	psmouse->set_resolution = psmouse_set_resolution;
+	psmouse->poll = psmouse_poll;
 	psmouse->protocol_handler = psmouse_process_byte;
 	psmouse->pktsize = 3;
 
@@ -917,6 +1082,7 @@
 		goto out;
 
 	ps2_init(&psmouse->ps2dev, serio);
+	INIT_WORK(&psmouse->resync_work, psmouse_resync, psmouse);
 	psmouse->dev = input_dev;
 	sprintf(psmouse->phys, "%s/input0", serio->phys);
 
@@ -937,6 +1103,7 @@
 	psmouse->rate = psmouse_rate;
 	psmouse->resolution = psmouse_resolution;
 	psmouse->resetafter = psmouse_resetafter;
+	psmouse->resync_time = parent ? 0 : psmouse_resync_time;
 	psmouse->smartscroll = psmouse_smartscroll;
 
 	psmouse_switch_protocol(psmouse, NULL);
@@ -1281,13 +1448,21 @@
 
 static int __init psmouse_init(void)
 {
+	kpsmoused_wq = create_singlethread_workqueue("kpsmoused");
+	if (!kpsmoused_wq) {
+		printk(KERN_ERR "psmouse: failed to create kpsmoused workqueue\n");
+		return -ENOMEM;
+	}
+
 	serio_register_driver(&psmouse_drv);
+
 	return 0;
 }
 
 static void __exit psmouse_exit(void)
 {
 	serio_unregister_driver(&psmouse_drv);
+	destroy_workqueue(kpsmoused_wq);
 }
 
 module_init(psmouse_init);
diff -urN oldtree/drivers/input/mouse/psmouse.h newtree/drivers/input/mouse/psmouse.h
--- oldtree/drivers/input/mouse/psmouse.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/input/mouse/psmouse.h	2006-02-13 19:19:59.959518456 -0500
@@ -7,7 +7,7 @@
 #define PSMOUSE_CMD_GETINFO	0x03e9
 #define PSMOUSE_CMD_SETSTREAM	0x00ea
 #define PSMOUSE_CMD_SETPOLL	0x00f0
-#define PSMOUSE_CMD_POLL	0x03eb
+#define PSMOUSE_CMD_POLL	0x00eb	/* caller sets number of bytes to receive */
 #define PSMOUSE_CMD_GETID	0x02f2
 #define PSMOUSE_CMD_SETRATE	0x10f3
 #define PSMOUSE_CMD_ENABLE	0x00f4
@@ -23,6 +23,7 @@
 enum psmouse_state {
 	PSMOUSE_IGNORE,
 	PSMOUSE_INITIALIZING,
+	PSMOUSE_RESYNCING,
 	PSMOUSE_CMD_MODE,
 	PSMOUSE_ACTIVATED,
 };
@@ -38,9 +39,11 @@
 	void *private;
 	struct input_dev *dev;
 	struct ps2dev ps2dev;
+	struct work_struct resync_work;
 	char *vendor;
 	char *name;
 	unsigned char packet[8];
+	unsigned char badbyte;
 	unsigned char pktcnt;
 	unsigned char pktsize;
 	unsigned char type;
@@ -54,6 +57,7 @@
 	unsigned int rate;
 	unsigned int resolution;
 	unsigned int resetafter;
+	unsigned int resync_time;
 	unsigned int smartscroll;	/* Logitech only */
 
 	psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse, struct pt_regs *regs);
@@ -62,6 +66,7 @@
 
 	int (*reconnect)(struct psmouse *psmouse);
 	void (*disconnect)(struct psmouse *psmouse);
+	int (*poll)(struct psmouse *psmouse);
 
 	void (*pt_activate)(struct psmouse *psmouse);
 	void (*pt_deactivate)(struct psmouse *psmouse);
@@ -79,6 +84,7 @@
 	PSMOUSE_ALPS,
 	PSMOUSE_LIFEBOOK,
 	PSMOUSE_TRACKPOINT,
+	PSMOUSE_TOUCHKIT_PS2,
 	PSMOUSE_AUTO		/* This one should always be last */
 };
 
diff -urN oldtree/drivers/input/mouse/synaptics.c newtree/drivers/input/mouse/synaptics.c
--- oldtree/drivers/input/mouse/synaptics.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/input/mouse/synaptics.c	2006-02-13 19:19:59.959518456 -0500
@@ -652,6 +652,8 @@
 	psmouse->disconnect = synaptics_disconnect;
 	psmouse->reconnect = synaptics_reconnect;
 	psmouse->pktsize = 6;
+	/* Synaptics can usually stay in sync without extra help */
+	psmouse->resync_time = 0;
 
 	if (SYN_CAP_PASS_THROUGH(priv->capabilities))
 		synaptics_pt_create(psmouse);
diff -urN oldtree/drivers/input/mouse/touchkit_ps2.c newtree/drivers/input/mouse/touchkit_ps2.c
--- oldtree/drivers/input/mouse/touchkit_ps2.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/input/mouse/touchkit_ps2.c	2006-02-13 19:19:59.962518000 -0500
@@ -0,0 +1,195 @@
+/* ----------------------------------------------------------------------------
+ * touchkit_ps2.c  --  Driver for eGalax TouchKit PS/2 Touchscreens
+ *
+ * Copyright (C) 2005 by Stefan Lucke
+ * Copyright (C) 2004 by Daniel Ritz
+ * Copyright (C) by Todd E. Johnson (mtouchusb.c)
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Based upon touchkitusb.c
+ *
+ * Vendor documentation is available in support section of:
+ * http://www.egalax.com.tw/
+ *
+ */
+
+/*
+ * Changelog:
+ *
+ * 2005-10-14:	whitespace & indentaion cleanup
+ *		touchkit commands defined
+ * initial version 0.1
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include <linux/dmi.h>
+
+#include "psmouse.h"
+#include "touchkit_ps2.h"
+
+#define TOUCHKIT_MIN_XC			0x0
+#define TOUCHKIT_MAX_XC			0x07ff
+#define TOUCHKIT_XC_FUZZ		0x0
+#define TOUCHKIT_XC_FLAT		0x0
+#define TOUCHKIT_MIN_YC			0x0
+#define TOUCHKIT_MAX_YC			0x07ff
+#define TOUCHKIT_YC_FUZZ		0x0
+#define TOUCHKIT_YC_FLAT		0x0
+#define TOUCHKIT_REPORT_DATA_SIZE	8
+
+#define TOUCHKIT_CMD			0x0A
+#define TOUCHKIT_CMD_LENGTH		1
+
+#define TOUCHKIT_CMD_ACTIVE		'A'
+#define TOUCHKIT_CMD_FIRMWARE_VERSION	'D'
+#define TOUCHKIT_CMD_CONTROLLER_TYPE	'E'
+
+#define TOUCHKIT_SEND_PARMS(s,r,c)	((s) << 12 | (r) << 8 | (c))
+
+#define TOUCHKIT_DOWN			0x01
+#define TOUCHKIT_POINT_TOUCH		0x81
+#define TOUCHKIT_POINT_NOTOUCH		0x80
+
+#define TOUCHKIT_GET_TOUCHED(dat)	((((dat)[0]) & TOUCHKIT_DOWN) ? 1 : 0)
+#define TOUCHKIT_GET_X(dat)		(((dat)[1] << 7) | (dat)[2])
+#define TOUCHKIT_GET_Y(dat)		(((dat)[3] << 7) | (dat)[4])
+
+#define DRIVER_VERSION			"v0.2"
+#define DRIVER_AUTHOR			"Stefan Lucke <stefan@lucke.in-berlin.de>"
+#define DRIVER_DESC			"eGalax TouchKit PS/2 Touchscreen Driver"
+
+static int xyswap = 0;
+module_param(xyswap, bool, 0644);
+MODULE_PARM_DESC(xyswap, "If set X and Y axes are swapped.");
+
+static int  xinvert = 0;
+module_param(xinvert, bool, 0644);
+MODULE_PARM_DESC(xinvert, "Invert direction of x axis.");
+
+static int  yinvert = 0;
+module_param(yinvert, bool, 0644);
+MODULE_PARM_DESC(yinvert, "Invert direction of y axis.");
+
+static int  xfuzz = 0;
+module_param(xfuzz, uint, 0644);
+MODULE_PARM_DESC(xinvert, "Fuzz value for x axis.");
+
+static int  yfuzz = 0;
+module_param(yfuzz, uint, 0644);
+MODULE_PARM_DESC(yfuzz, "Fuzz value for y axis.");
+
+static int  smartpad = 0;
+module_param(smartpad, bool, 0644);
+MODULE_PARM_DESC(smartpad, "Act as a smartpad device.");
+
+static int  mouse = 0;
+module_param(mouse, bool, 0644);
+MODULE_PARM_DESC(mouse, "Report mouse button");
+
+static psmouse_ret_t touchkit_ps2_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
+{
+	unsigned char 		*packet = psmouse->packet;
+	struct input_dev 	*dev = psmouse->dev;
+	int 			x,y;
+
+	if (psmouse->pktcnt != 5)
+		return PSMOUSE_GOOD_DATA;
+
+	input_regs(dev, regs);
+
+	if (xyswap) {
+		y = TOUCHKIT_GET_X(packet);
+		x = TOUCHKIT_GET_Y(packet);
+	} else {
+		x = TOUCHKIT_GET_X(packet);
+		y = TOUCHKIT_GET_Y(packet);
+	}
+
+	y = (yinvert) ? TOUCHKIT_MAX_YC - y : y;
+	x = (xinvert) ? TOUCHKIT_MAX_XC - x : x;
+
+	input_report_key(dev,
+			 (mouse) ? BTN_MOUSE : BTN_TOUCH,
+			 TOUCHKIT_GET_TOUCHED(packet));
+
+	if (smartpad)
+		input_report_key(dev, BTN_TOOL_FINGER, 1);
+
+	input_report_abs(dev, ABS_X, x);
+	input_report_abs(dev, ABS_Y, y);
+
+	input_sync(dev);
+
+	return PSMOUSE_FULL_PACKET;
+}
+
+int touchkit_ps2_detect(struct psmouse *psmouse, int set_properties)
+{
+	unsigned char	param[3];
+	int		command;
+
+	param[0] = TOUCHKIT_CMD_LENGTH;
+	param[1] = TOUCHKIT_CMD_ACTIVE;
+	command = TOUCHKIT_SEND_PARMS(2, 3, TOUCHKIT_CMD);
+
+	if (ps2_command(&psmouse->ps2dev, param, command) == 0 &&
+	    param[0] == TOUCHKIT_CMD &&
+	    param[1] == 0x01 &&
+	    param[2] == TOUCHKIT_CMD_ACTIVE){
+		printk(KERN_INFO "touchkit_ps2: device detected\n");
+		if (set_properties) {
+			psmouse->vendor = "eGalax";
+			psmouse->name = "Touchscreen";
+		}
+		return 0;
+	}
+	return -1;
+}
+
+int touchkit_ps2_init(struct psmouse *psmouse)
+{
+	psmouse->dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+	set_bit((mouse) ? BTN_MOUSE : BTN_TOUCH,psmouse->dev->keybit);
+	if (smartpad)
+		set_bit(BTN_TOOL_FINGER,psmouse->dev->keybit);
+
+	psmouse->dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
+
+	/* Used to Scale Compensated Data */
+	psmouse->dev->absmin[ABS_X] = TOUCHKIT_MIN_XC;
+	psmouse->dev->absmax[ABS_X] = TOUCHKIT_MAX_XC;
+	psmouse->dev->absfuzz[ABS_X] = xfuzz;
+	psmouse->dev->absflat[ABS_X] = TOUCHKIT_XC_FLAT;
+	psmouse->dev->absmin[ABS_Y] = TOUCHKIT_MIN_YC;
+	psmouse->dev->absmax[ABS_Y] = TOUCHKIT_MAX_YC;
+	psmouse->dev->absfuzz[ABS_Y] = yfuzz;
+	psmouse->dev->absflat[ABS_Y] = TOUCHKIT_YC_FLAT;
+
+	input_set_abs_params(psmouse->dev, ABS_X, 0, 0x07ff, xfuzz, 0);
+	input_set_abs_params(psmouse->dev, ABS_Y, 0, 0x07ff, yfuzz, 0);
+
+	psmouse->protocol_handler = touchkit_ps2_process_byte;
+	psmouse->pktsize = 5;
+
+	return 0;
+}
diff -urN oldtree/drivers/input/mouse/touchkit_ps2.h newtree/drivers/input/mouse/touchkit_ps2.h
--- oldtree/drivers/input/mouse/touchkit_ps2.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/input/mouse/touchkit_ps2.h	2006-02-13 19:19:59.962518000 -0500
@@ -0,0 +1,18 @@
+/* ----------------------------------------------------------------------------
+ * touchkit_ps2.h  --  Driver for eGalax TouchKit PS/2 Touchscreens
+ *
+ * Copyright (C) 2005 by Stefan Lucke
+ * Copyright (c) 2005 Vojtech Pavlik
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _TOUCHKIT_PS2_H
+#define _TOUCHKIT_PS2_H
+
+int touchkit_ps2_detect(struct psmouse *psmouse, int set_properties);
+int touchkit_ps2_init(struct psmouse *psmouse);
+
+#endif
diff -urN oldtree/drivers/input/mousedev.c newtree/drivers/input/mousedev.c
--- oldtree/drivers/input/mousedev.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/input/mousedev.c	2006-02-13 19:20:29.711995392 -0500
@@ -356,7 +356,7 @@
 	kfree(mousedev);
 }
 
-static int mixdev_release(void)
+static void mixdev_release(void)
 {
 	struct input_handle *handle;
 
@@ -370,8 +370,6 @@
 				mousedev_free(mousedev);
 		}
 	}
-
-	return 0;
 }
 
 static int mousedev_release(struct inode * inode, struct file * file)
@@ -384,9 +382,8 @@
 
 	if (!--list->mousedev->open) {
 		if (list->mousedev->minor == MOUSEDEV_MIX)
-			return mixdev_release();
-
-		if (!mousedev_mix.open) {
+			mixdev_release();
+		else if (!mousedev_mix.open) {
 			if (list->mousedev->exist)
 				input_close_device(&list->mousedev->handle);
 			else
diff -urN oldtree/drivers/md/dm-crypt.c newtree/drivers/md/dm-crypt.c
--- oldtree/drivers/md/dm-crypt.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/md/dm-crypt.c	2006-02-13 19:20:29.713995088 -0500
@@ -690,6 +690,8 @@
 bad2:
 	crypto_free_tfm(tfm);
 bad1:
+	/* Must zero key material before freeing */
+	memset(cc, 0, sizeof(*cc) + cc->key_size * sizeof(u8));
 	kfree(cc);
 	return -EINVAL;
 }
@@ -706,6 +708,9 @@
 		cc->iv_gen_ops->dtr(cc);
 	crypto_free_tfm(cc->tfm);
 	dm_put_device(ti, cc->dev);
+
+	/* Must zero key material before freeing */
+	memset(cc, 0, sizeof(*cc) + cc->key_size * sizeof(u8));
 	kfree(cc);
 }
 
diff -urN oldtree/drivers/md/dm-raid1.c newtree/drivers/md/dm-raid1.c
--- oldtree/drivers/md/dm-raid1.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/md/dm-raid1.c	2006-02-13 19:19:59.963517848 -0500
@@ -412,9 +412,21 @@
 
 	spin_lock_irqsave(&rh->region_lock, flags);
 	if (atomic_dec_and_test(&reg->pending)) {
+		/*
+		 * There is no pending I/O for this region.
+		 * We can move the region to corresponding list for next action.
+		 * At this point, the region is not yet connected to any list.
+		 *
+		 * If the state is RH_NOSYNC, the region should be kept off
+		 * from clean list.
+		 * The hash entry for RH_NOSYNC will remain in memory
+		 * until the region is recovered or the map is reloaded.
+		 */
+
+		/* do nothing for RH_NOSYNC */
 		if (reg->state == RH_RECOVERING) {
 			list_add_tail(&reg->list, &rh->quiesced_regions);
-		} else {
+		} else if (reg->state == RH_DIRTY) {
 			reg->state = RH_CLEAN;
 			list_add(&reg->list, &rh->clean_regions);
 		}
diff -urN oldtree/drivers/md/md.c newtree/drivers/md/md.c
--- oldtree/drivers/md/md.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/md/md.c	2006-02-13 19:20:29.714994936 -0500
@@ -1182,6 +1182,7 @@
 	mdk_rdev_t *same_pdev;
 	char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE];
 	struct kobject *ko;
+	char *s;
 
 	if (rdev->mddev) {
 		MD_BUG();
@@ -1213,6 +1214,8 @@
 	bdevname(rdev->bdev,b);
 	if (kobject_set_name(&rdev->kobj, "dev-%s", b) < 0)
 		return -ENOMEM;
+	while ( (s=strchr(rdev->kobj.k_name, '/')) != NULL)
+		*s = '!';
 			
 	list_add(&rdev->same_set, &mddev->disks);
 	rdev->mddev = mddev;
diff -urN oldtree/drivers/message/i2o/i2o_scsi.c newtree/drivers/message/i2o/i2o_scsi.c
--- oldtree/drivers/message/i2o/i2o_scsi.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/message/i2o/i2o_scsi.c	2006-02-13 19:20:29.716994632 -0500
@@ -729,7 +729,7 @@
 	       &msg->u.head[1]);
 	writel(i2o_cntxt_list_get_ptr(c, SCpnt), &msg->body[0]);
 
-	if (i2o_msg_post_wait(c, m, I2O_TIMEOUT_SCSI_SCB_ABORT))
+	if (!i2o_msg_post_wait(c, msg, I2O_TIMEOUT_SCSI_SCB_ABORT))
 		status = SUCCESS;
 
 	return status;
diff -urN oldtree/drivers/mmc/Kconfig newtree/drivers/mmc/Kconfig
--- oldtree/drivers/mmc/Kconfig	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/mmc/Kconfig	2006-02-13 19:19:59.963517848 -0500
@@ -29,6 +29,15 @@
 	  mount the filesystem. Almost everyone wishing MMC support
 	  should say Y or M here.
 
+config MMC_BULKTRANSFER
+	bool "Multi-block writes (EXPERIMENTAL)"
+	depends on MMC_BLOCK != n && EXPERIMENTAL
+	default n
+	help
+	  By default all writes are done one sector at a time. Enable
+	  this option to transfer as large blocks as the host supports.
+	  The transfer speed is in most cases doubled.
+
 config MMC_ARMMMCI
 	tristate "ARM AMBA Multimedia Card Interface support"
 	depends on ARM_AMBA && MMC
diff -urN oldtree/drivers/mmc/mmc_block.c newtree/drivers/mmc/mmc_block.c
--- oldtree/drivers/mmc/mmc_block.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/mmc/mmc_block.c	2006-02-13 19:19:59.965517544 -0500
@@ -177,9 +177,25 @@
 	struct mmc_card *card = md->queue.card;
 	int ret;
 
+#ifdef CONFIG_MMC_BULKTRANSFER
+	int failsafe;
+#endif
+
 	if (mmc_card_claim_host(card))
 		goto cmd_err;
 
+#ifdef CONFIG_MMC_BULKTRANSFER
+	/*
+	 * We first try transfering multiple blocks. If this fails
+	 * we fall back to single block transfers.
+	 *
+	 * This gives us good performance when all is well and the
+	 * possibility to determine which sector fails when all
+	 * is not well.
+	 */
+	failsafe = 0;
+#endif
+
 	do {
 		struct mmc_blk_request brq;
 		struct mmc_command cmd;
@@ -198,13 +214,31 @@
 		brq.stop.arg = 0;
 		brq.stop.flags = MMC_RSP_R1B;
 
+#ifdef CONFIG_MMC_BULKTRANSFER
+		/*
+		 * A multi-block transfer failed. Falling back to single
+		 * blocks.
+		 */
+		if (failsafe)
+			brq.data.blocks = 1;
+
+#else
+		/*
+		 * Writes are done one sector at a time.
+		 */
+		if (rq_data_dir(req) != READ)
+			brq.data.blocks = 1;
+#endif
+
+		ret = 1;
+
 		if (rq_data_dir(req) == READ) {
 			brq.cmd.opcode = brq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK;
 			brq.data.flags |= MMC_DATA_READ;
 		} else {
-			brq.cmd.opcode = MMC_WRITE_BLOCK;
+			brq.cmd.opcode = brq.data.blocks > 1 ?
+				MMC_WRITE_MULTIPLE_BLOCK : MMC_WRITE_BLOCK;
 			brq.data.flags |= MMC_DATA_WRITE;
-			brq.data.blocks = 1;
 		}
 		brq.mrq.stop = brq.data.blocks > 1 ? &brq.stop : NULL;
 
@@ -215,19 +249,19 @@
 		if (brq.cmd.error) {
 			printk(KERN_ERR "%s: error %d sending read/write command\n",
 			       req->rq_disk->disk_name, brq.cmd.error);
-			goto cmd_err;
+			goto cmd_fail;
 		}
 
 		if (brq.data.error) {
 			printk(KERN_ERR "%s: error %d transferring data\n",
 			       req->rq_disk->disk_name, brq.data.error);
-			goto cmd_err;
+			goto cmd_fail;
 		}
 
 		if (brq.stop.error) {
 			printk(KERN_ERR "%s: error %d sending stop command\n",
 			       req->rq_disk->disk_name, brq.stop.error);
-			goto cmd_err;
+			goto cmd_fail;
 		}
 
 		do {
@@ -240,7 +274,7 @@
 			if (err) {
 				printk(KERN_ERR "%s: error %d requesting status\n",
 				       req->rq_disk->disk_name, err);
-				goto cmd_err;
+				goto cmd_fail;
 			}
 		} while (!(cmd.resp[0] & R1_READY_FOR_DATA));
 
@@ -266,6 +300,27 @@
 			end_that_request_last(req);
 		}
 		spin_unlock_irq(&md->lock);
+
+#ifdef CONFIG_MMC_BULKTRANSFER
+		/*
+		 * Go back to bulk mode if in failsafe mode.
+		 */
+		failsafe = 0;
+#endif
+
+		continue;
+
+ cmd_fail:
+
+#ifdef CONFIG_MMC_BULKTRANSFER
+		if (failsafe)
+	 		goto cmd_err;
+	 	else
+	 		failsafe = 1;
+#else
+ 		goto cmd_err;
+#endif
+
 	} while (ret);
 
 	mmc_card_release_host(card);
diff -urN oldtree/drivers/mmc/mmc_block.c.orig newtree/drivers/mmc/mmc_block.c.orig
--- oldtree/drivers/mmc/mmc_block.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/mmc/mmc_block.c.orig	2006-02-13 19:19:59.966517392 -0500
@@ -0,0 +1,526 @@
+/*
+ * Block driver for media (i.e., flash cards)
+ *
+ * Copyright 2002 Hewlett-Packard Company
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ *
+ * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
+ * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
+ * FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ * Many thanks to Alessandro Rubini and Jonathan Corbet!
+ *
+ * Author:  Andrew Christian
+ *          28 May 2002
+ */
+#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/kdev_t.h>
+#include <linux/blkdev.h>
+#include <linux/devfs_fs_kernel.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/protocol.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "mmc_queue.h"
+
+/*
+ * max 8 partitions per card
+ */
+#define MMC_SHIFT	3
+
+static int major;
+
+/*
+ * There is one mmc_blk_data per slot.
+ */
+struct mmc_blk_data {
+	spinlock_t	lock;
+	struct gendisk	*disk;
+	struct mmc_queue queue;
+
+	unsigned int	usage;
+	unsigned int	block_bits;
+};
+
+static DECLARE_MUTEX(open_lock);
+
+static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk)
+{
+	struct mmc_blk_data *md;
+
+	down(&open_lock);
+	md = disk->private_data;
+	if (md && md->usage == 0)
+		md = NULL;
+	if (md)
+		md->usage++;
+	up(&open_lock);
+
+	return md;
+}
+
+static void mmc_blk_put(struct mmc_blk_data *md)
+{
+	down(&open_lock);
+	md->usage--;
+	if (md->usage == 0) {
+		put_disk(md->disk);
+		mmc_cleanup_queue(&md->queue);
+		kfree(md);
+	}
+	up(&open_lock);
+}
+
+static inline int mmc_blk_readonly(struct mmc_card *card)
+{
+	return mmc_card_readonly(card) ||
+	       !(card->csd.cmdclass & CCC_BLOCK_WRITE);
+}
+
+static int mmc_blk_open(struct inode *inode, struct file *filp)
+{
+	struct mmc_blk_data *md;
+	int ret = -ENXIO;
+
+	md = mmc_blk_get(inode->i_bdev->bd_disk);
+	if (md) {
+		if (md->usage == 2)
+			check_disk_change(inode->i_bdev);
+		ret = 0;
+
+		if ((filp->f_mode & FMODE_WRITE) &&
+			mmc_blk_readonly(md->queue.card))
+			ret = -EROFS;
+	}
+
+	return ret;
+}
+
+static int mmc_blk_release(struct inode *inode, struct file *filp)
+{
+	struct mmc_blk_data *md = inode->i_bdev->bd_disk->private_data;
+
+	mmc_blk_put(md);
+	return 0;
+}
+
+static int
+mmc_blk_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	struct block_device *bdev = inode->i_bdev;
+
+	if (cmd == HDIO_GETGEO) {
+		struct hd_geometry geo;
+
+		memset(&geo, 0, sizeof(struct hd_geometry));
+
+		geo.cylinders	= get_capacity(bdev->bd_disk) / (4 * 16);
+		geo.heads	= 4;
+		geo.sectors	= 16;
+		geo.start	= get_start_sect(bdev);
+
+		return copy_to_user((void __user *)arg, &geo, sizeof(geo))
+			? -EFAULT : 0;
+	}
+
+	return -ENOTTY;
+}
+
+static struct block_device_operations mmc_bdops = {
+	.open			= mmc_blk_open,
+	.release		= mmc_blk_release,
+	.ioctl			= mmc_blk_ioctl,
+	.owner			= THIS_MODULE,
+};
+
+struct mmc_blk_request {
+	struct mmc_request	mrq;
+	struct mmc_command	cmd;
+	struct mmc_command	stop;
+	struct mmc_data		data;
+};
+
+static int mmc_blk_prep_rq(struct mmc_queue *mq, struct request *req)
+{
+	struct mmc_blk_data *md = mq->data;
+	int stat = BLKPREP_OK;
+
+	/*
+	 * If we have no device, we haven't finished initialising.
+	 */
+	if (!md || !mq->card) {
+		printk(KERN_ERR "%s: killing request - no device/host\n",
+		       req->rq_disk->disk_name);
+		stat = BLKPREP_KILL;
+	}
+
+	return stat;
+}
+
+static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
+{
+	struct mmc_blk_data *md = mq->data;
+	struct mmc_card *card = md->queue.card;
+	int ret;
+
+	if (mmc_card_claim_host(card))
+		goto cmd_err;
+
+	do {
+		struct mmc_blk_request brq;
+		struct mmc_command cmd;
+
+		memset(&brq, 0, sizeof(struct mmc_blk_request));
+		brq.mrq.cmd = &brq.cmd;
+		brq.mrq.data = &brq.data;
+
+		brq.cmd.arg = req->sector << 9;
+		brq.cmd.flags = MMC_RSP_R1;
+		brq.data.timeout_ns = card->csd.tacc_ns * 10;
+		brq.data.timeout_clks = card->csd.tacc_clks * 10;
+		brq.data.blksz_bits = md->block_bits;
+		brq.data.blocks = req->nr_sectors >> (md->block_bits - 9);
+		brq.stop.opcode = MMC_STOP_TRANSMISSION;
+		brq.stop.arg = 0;
+		brq.stop.flags = MMC_RSP_R1B;
+
+		if (rq_data_dir(req) == READ) {
+			brq.cmd.opcode = brq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK;
+			brq.data.flags |= MMC_DATA_READ;
+		} else {
+			brq.cmd.opcode = MMC_WRITE_BLOCK;
+			brq.data.flags |= MMC_DATA_WRITE;
+			brq.data.blocks = 1;
+		}
+		brq.mrq.stop = brq.data.blocks > 1 ? &brq.stop : NULL;
+
+		brq.data.sg = mq->sg;
+		brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg);
+
+		mmc_wait_for_req(card->host, &brq.mrq);
+		if (brq.cmd.error) {
+			printk(KERN_ERR "%s: error %d sending read/write command\n",
+			       req->rq_disk->disk_name, brq.cmd.error);
+			goto cmd_err;
+		}
+
+		if (brq.data.error) {
+			printk(KERN_ERR "%s: error %d transferring data\n",
+			       req->rq_disk->disk_name, brq.data.error);
+			goto cmd_err;
+		}
+
+		if (brq.stop.error) {
+			printk(KERN_ERR "%s: error %d sending stop command\n",
+			       req->rq_disk->disk_name, brq.stop.error);
+			goto cmd_err;
+		}
+
+		do {
+			int err;
+
+			cmd.opcode = MMC_SEND_STATUS;
+			cmd.arg = card->rca << 16;
+			cmd.flags = MMC_RSP_R1;
+			err = mmc_wait_for_cmd(card->host, &cmd, 5);
+			if (err) {
+				printk(KERN_ERR "%s: error %d requesting status\n",
+				       req->rq_disk->disk_name, err);
+				goto cmd_err;
+			}
+		} while (!(cmd.resp[0] & R1_READY_FOR_DATA));
+
+#if 0
+		if (cmd.resp[0] & ~0x00000900)
+			printk(KERN_ERR "%s: status = %08x\n",
+			       req->rq_disk->disk_name, cmd.resp[0]);
+		if (mmc_decode_status(cmd.resp))
+			goto cmd_err;
+#endif
+
+		/*
+		 * A block was successfully transferred.
+		 */
+		spin_lock_irq(&md->lock);
+		ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered);
+		if (!ret) {
+			/*
+			 * The whole request completed successfully.
+			 */
+			add_disk_randomness(req->rq_disk);
+			blkdev_dequeue_request(req);
+			end_that_request_last(req);
+		}
+		spin_unlock_irq(&md->lock);
+	} while (ret);
+
+	mmc_card_release_host(card);
+
+	return 1;
+
+ cmd_err:
+	mmc_card_release_host(card);
+
+	/*
+	 * This is a little draconian, but until we get proper
+	 * error handling sorted out here, its the best we can
+	 * do - especially as some hosts have no idea how much
+	 * data was transferred before the error occurred.
+	 */
+	spin_lock_irq(&md->lock);
+	do {
+		ret = end_that_request_chunk(req, 0,
+				req->current_nr_sectors << 9);
+	} while (ret);
+
+	add_disk_randomness(req->rq_disk);
+	blkdev_dequeue_request(req);
+	end_that_request_last(req);
+	spin_unlock_irq(&md->lock);
+
+	return 0;
+}
+
+#define MMC_NUM_MINORS	(256 >> MMC_SHIFT)
+
+static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))];
+
+static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
+{
+	struct mmc_blk_data *md;
+	int devidx, ret;
+
+	devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS);
+	if (devidx >= MMC_NUM_MINORS)
+		return ERR_PTR(-ENOSPC);
+	__set_bit(devidx, dev_use);
+
+	md = kmalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);
+	if (md) {
+		memset(md, 0, sizeof(struct mmc_blk_data));
+
+		md->disk = alloc_disk(1 << MMC_SHIFT);
+		if (md->disk == NULL) {
+			kfree(md);
+			md = ERR_PTR(-ENOMEM);
+			goto out;
+		}
+
+		spin_lock_init(&md->lock);
+		md->usage = 1;
+
+		ret = mmc_init_queue(&md->queue, card, &md->lock);
+		if (ret) {
+			put_disk(md->disk);
+			kfree(md);
+			md = ERR_PTR(ret);
+			goto out;
+		}
+		md->queue.prep_fn = mmc_blk_prep_rq;
+		md->queue.issue_fn = mmc_blk_issue_rq;
+		md->queue.data = md;
+
+		md->disk->major	= major;
+		md->disk->first_minor = devidx << MMC_SHIFT;
+		md->disk->fops = &mmc_bdops;
+		md->disk->private_data = md;
+		md->disk->queue = md->queue.queue;
+		md->disk->driverfs_dev = &card->dev;
+
+		/*
+		 * As discussed on lkml, GENHD_FL_REMOVABLE should:
+		 *
+		 * - be set for removable media with permanent block devices
+		 * - be unset for removable block devices with permanent media
+		 *
+		 * Since MMC block devices clearly fall under the second
+		 * case, we do not set GENHD_FL_REMOVABLE.  Userspace
+		 * should use the block device creation/destruction hotplug
+		 * messages to tell when the card is present.
+		 */
+
+		sprintf(md->disk->disk_name, "mmcblk%d", devidx);
+		sprintf(md->disk->devfs_name, "mmc/blk%d", devidx);
+
+		md->block_bits = card->csd.read_blkbits;
+
+		blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits);
+
+		/*
+		 * The CSD capacity field is in units of read_blkbits.
+		 * set_capacity takes units of 512 bytes.
+		 */
+		set_capacity(md->disk, card->csd.capacity << (card->csd.read_blkbits - 9));
+	}
+ out:
+	return md;
+}
+
+static int
+mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
+{
+	struct mmc_command cmd;
+	int err;
+
+	mmc_card_claim_host(card);
+	cmd.opcode = MMC_SET_BLOCKLEN;
+	cmd.arg = 1 << md->block_bits;
+	cmd.flags = MMC_RSP_R1;
+	err = mmc_wait_for_cmd(card->host, &cmd, 5);
+	mmc_card_release_host(card);
+
+	if (err) {
+		printk(KERN_ERR "%s: unable to set block size to %d: %d\n",
+			md->disk->disk_name, cmd.arg, err);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mmc_blk_probe(struct mmc_card *card)
+{
+	struct mmc_blk_data *md;
+	int err;
+
+	/*
+	 * Check that the card supports the command class(es) we need.
+	 */
+	if (!(card->csd.cmdclass & CCC_BLOCK_READ))
+		return -ENODEV;
+
+	if (card->csd.read_blkbits < 9) {
+		printk(KERN_WARNING "%s: read blocksize too small (%u)\n",
+			mmc_card_id(card), 1 << card->csd.read_blkbits);
+		return -ENODEV;
+	}
+
+	md = mmc_blk_alloc(card);
+	if (IS_ERR(md))
+		return PTR_ERR(md);
+
+	err = mmc_blk_set_blksize(md, card);
+	if (err)
+		goto out;
+
+	printk(KERN_INFO "%s: %s %s %luKiB %s\n",
+		md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
+		get_capacity(md->disk) >> 1, mmc_blk_readonly(card)?"(ro)":"");
+
+	mmc_set_drvdata(card, md);
+	add_disk(md->disk);
+	return 0;
+
+ out:
+	mmc_blk_put(md);
+
+	return err;
+}
+
+static void mmc_blk_remove(struct mmc_card *card)
+{
+	struct mmc_blk_data *md = mmc_get_drvdata(card);
+
+	if (md) {
+		int devidx;
+
+		del_gendisk(md->disk);
+
+		/*
+		 * I think this is needed.
+		 */
+		md->disk->queue = NULL;
+
+		devidx = md->disk->first_minor >> MMC_SHIFT;
+		__clear_bit(devidx, dev_use);
+
+		mmc_blk_put(md);
+	}
+	mmc_set_drvdata(card, NULL);
+}
+
+#ifdef CONFIG_PM
+static int mmc_blk_suspend(struct mmc_card *card, pm_message_t state)
+{
+	struct mmc_blk_data *md = mmc_get_drvdata(card);
+
+	if (md) {
+		mmc_queue_suspend(&md->queue);
+	}
+	return 0;
+}
+
+static int mmc_blk_resume(struct mmc_card *card)
+{
+	struct mmc_blk_data *md = mmc_get_drvdata(card);
+
+	if (md) {
+		mmc_blk_set_blksize(md, card);
+		mmc_queue_resume(&md->queue);
+	}
+	return 0;
+}
+#else
+#define	mmc_blk_suspend	NULL
+#define mmc_blk_resume	NULL
+#endif
+
+static struct mmc_driver mmc_driver = {
+	.drv		= {
+		.name	= "mmcblk",
+	},
+	.probe		= mmc_blk_probe,
+	.remove		= mmc_blk_remove,
+	.suspend	= mmc_blk_suspend,
+	.resume		= mmc_blk_resume,
+};
+
+static int __init mmc_blk_init(void)
+{
+	int res = -ENOMEM;
+
+	res = register_blkdev(major, "mmc");
+	if (res < 0) {
+		printk(KERN_WARNING "Unable to get major %d for MMC media: %d\n",
+		       major, res);
+		goto out;
+	}
+	if (major == 0)
+		major = res;
+
+	devfs_mk_dir("mmc");
+	return mmc_register_driver(&mmc_driver);
+
+ out:
+	return res;
+}
+
+static void __exit mmc_blk_exit(void)
+{
+	mmc_unregister_driver(&mmc_driver);
+	devfs_remove("mmc");
+	unregister_blkdev(major, "mmc");
+}
+
+module_init(mmc_blk_init);
+module_exit(mmc_blk_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver");
+
+module_param(major, int, 0444);
+MODULE_PARM_DESC(major, "specify the major device number for MMC block driver");
diff -urN oldtree/drivers/mtd/chips/Kconfig newtree/drivers/mtd/chips/Kconfig
--- oldtree/drivers/mtd/chips/Kconfig	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/mtd/chips/Kconfig	2006-02-13 19:19:59.966517392 -0500
@@ -259,7 +259,7 @@
 	  with this driver will return -ENODEV upon access.
 
 config MTD_OBSOLETE_CHIPS
-	depends on MTD && BROKEN
+	depends on MTD
 	bool "Older (theoretically obsoleted now) drivers for non-CFI chips"
 	help
 	  This option does not enable any code directly, but will allow you to
@@ -272,7 +272,7 @@
 
 config MTD_AMDSTD
 	tristate "AMD compatible flash chip support (non-CFI)"
-	depends on MTD && MTD_OBSOLETE_CHIPS
+	depends on MTD && MTD_OBSOLETE_CHIPS && BROKEN
 	help
 	  This option enables support for flash chips using AMD-compatible
 	  commands, including some which are not CFI-compatible and hence
@@ -290,7 +290,7 @@
 
 config MTD_JEDEC
 	tristate "JEDEC device support"
-	depends on MTD && MTD_OBSOLETE_CHIPS
+	depends on MTD && MTD_OBSOLETE_CHIPS && BROKEN
 	help
 	  Enable older older JEDEC flash interface devices for self
 	  programming flash.  It is commonly used in older AMD chips.  It is
diff -urN oldtree/drivers/net/3c59x.c newtree/drivers/net/3c59x.c
--- oldtree/drivers/net/3c59x.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/3c59x.c	2006-02-13 19:19:59.969516936 -0500
@@ -789,7 +789,7 @@
 	int options;						/* User-settable misc. driver options. */
 	unsigned int media_override:4, 		/* Passed-in media type. */
 		default_media:4,				/* Read from the EEPROM/Wn3_Config. */
-		full_duplex:1, force_fd:1, autoselect:1,
+		full_duplex:1, autoselect:1,
 		bus_master:1,					/* Vortex can only do a fragment bus-m. */
 		full_bus_master_tx:1, full_bus_master_rx:2, /* Boomerang  */
 		flow_ctrl:1,					/* Use 802.3x flow control (PAUSE only) */
@@ -1326,7 +1326,7 @@
 			vp->enable_wol = 1;
 	}
 
-	vp->force_fd = vp->full_duplex;
+	vp->mii.force_media = vp->full_duplex;
 	vp->options = option;
 	/* Read the station address from the EEPROM. */
 	EL3WINDOW(0);
@@ -1616,6 +1616,48 @@
 }
 
 static void
+vortex_set_duplex(struct net_device *dev)
+{
+	int mii_reg1, mii_reg5;
+	struct vortex_private *vp = netdev_priv(dev);
+	long *ioaddr = vp->ioaddr;
+
+	EL3WINDOW(4);
+	mii_reg1 = mdio_read(dev, vp->phys[0], 1);
+	mii_reg5 = mdio_read(dev, vp->phys[0], 5);
+	vp->partner_flow_ctrl = ((mii_reg5 & 0x0400) != 0);
+	EL3WINDOW(3);
+	if (vortex_debug > 1)
+		printk(KERN_INFO "%s: MII #%d status %4.4x, link partner capability %4.4x,"
+			   " info1 %04x, setting %s-duplex.\n",
+				dev->name, vp->phys[0],
+				mii_reg1, mii_reg5,
+				vp->info1, ((vp->info1 & 0x8000) || vp->full_duplex) ? "full" : "half");
+	/* Set the full-duplex bit. */
+	iowrite16(	((vp->info1 & 0x8000) || vp->full_duplex ? 0x20 : 0) |
+		 	(vp->large_frames ? 0x40 : 0) |
+			((vp->full_duplex && vp->flow_ctrl && vp->partner_flow_ctrl) ? 0x100 : 0),
+			ioaddr + Wn3_MAC_Ctrl);
+	/* AKPM: bug: should reset Tx and Rx after setting Duplex.  Page 180 */
+}
+
+static void
+vortex_check_media(struct net_device *dev, unsigned int init)
+{
+	struct vortex_private *vp = netdev_priv(dev);
+	unsigned int ok_to_print = 0;
+
+	if (vortex_debug > 3)
+		ok_to_print = 1;
+
+	if (mii_check_media(&vp->mii, ok_to_print, init)) {
+		vp->full_duplex = vp->mii.full_duplex;
+		vortex_set_duplex(dev);
+	} else if (init)
+		vortex_set_duplex(dev);
+}
+
+static void
 vortex_up(struct net_device *dev)
 {
 	struct vortex_private *vp = netdev_priv(dev);
@@ -1675,55 +1717,15 @@
 		printk(KERN_DEBUG "%s: Initial media type %s.\n",
 			   dev->name, media_tbl[dev->if_port].name);
 
-	vp->full_duplex = vp->force_fd;
+	vp->full_duplex = vp->mii.force_media;
 	config = BFINS(config, dev->if_port, 20, 4);
 	if (vortex_debug > 6)
 		printk(KERN_DEBUG "vortex_up(): writing 0x%x to InternalConfig\n", config);
 	iowrite32(config, ioaddr + Wn3_Config);
 
-	if (dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY) {
-		int mii_reg1, mii_reg5;
-		EL3WINDOW(4);
-		/* Read BMSR (reg1) only to clear old status. */
-		mii_reg1 = mdio_read(dev, vp->phys[0], MII_BMSR);
-		mii_reg5 = mdio_read(dev, vp->phys[0], MII_LPA);
-		if (mii_reg5 == 0xffff  ||  mii_reg5 == 0x0000) {
-			netif_carrier_off(dev); /* No MII device or no link partner report */
-		} else {
-			mii_reg5 &= vp->advertising;
-			if ((mii_reg5 & 0x0100) != 0	/* 100baseTx-FD */
-				 || (mii_reg5 & 0x00C0) == 0x0040) /* 10T-FD, but not 100-HD */
-			vp->full_duplex = 1;
-			netif_carrier_on(dev);
-		}
-		vp->partner_flow_ctrl = ((mii_reg5 & 0x0400) != 0);
-		if (vortex_debug > 1)
-			printk(KERN_INFO "%s: MII #%d status %4.4x, link partner capability %4.4x,"
-				   " info1 %04x, setting %s-duplex.\n",
-					dev->name, vp->phys[0],
-					mii_reg1, mii_reg5,
-					vp->info1, ((vp->info1 & 0x8000) || vp->full_duplex) ? "full" : "half");
-		EL3WINDOW(3);
-	}
-
-	/* Set the full-duplex bit. */
-	iowrite16(	((vp->info1 & 0x8000) || vp->full_duplex ? 0x20 : 0) |
-		 	(vp->large_frames ? 0x40 : 0) |
-			((vp->full_duplex && vp->flow_ctrl && vp->partner_flow_ctrl) ? 0x100 : 0),
-			ioaddr + Wn3_MAC_Ctrl);
-
-	if (vortex_debug > 1) {
-		printk(KERN_DEBUG "%s: vortex_up() InternalConfig %8.8x.\n",
-			dev->name, config);
-	}
-
-	issue_and_wait(dev, TxReset);
-	/*
-	 * Don't reset the PHY - that upsets autonegotiation during DHCP operations.
-	 */
-	issue_and_wait(dev, RxReset|0x04);
-
-	iowrite16(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
+	netif_carrier_off(dev);
+	if (dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY)
+		vortex_check_media(dev, 1);
 
 	if (vortex_debug > 1) {
 		EL3WINDOW(4);
@@ -1883,7 +1885,7 @@
 	void __iomem *ioaddr = vp->ioaddr;
 	int next_tick = 60*HZ;
 	int ok = 0;
-	int media_status, mii_status, old_window;
+	int media_status, old_window;
 
 	if (vortex_debug > 2) {
 		printk(KERN_DEBUG "%s: Media selection timer tick happened, %s.\n",
@@ -1891,8 +1893,6 @@
 		printk(KERN_DEBUG "dev->watchdog_timeo=%d\n", dev->watchdog_timeo);
 	}
 
-	if (vp->medialock)
-		goto leave_media_alone;
 	disable_irq(dev->irq);
 	old_window = ioread16(ioaddr + EL3_CMD) >> 13;
 	EL3WINDOW(4);
@@ -1915,44 +1915,9 @@
 		break;
 	case XCVR_MII: case XCVR_NWAY:
 		{
-			spin_lock_bh(&vp->lock);
-			mii_status = mdio_read(dev, vp->phys[0], MII_BMSR);
-			if (!(mii_status & BMSR_LSTATUS)) {
-				/* Re-read to get actual link status */
-				mii_status = mdio_read(dev, vp->phys[0], MII_BMSR);
-			}
 			ok = 1;
-			if (vortex_debug > 2)
-				printk(KERN_DEBUG "%s: MII transceiver has status %4.4x.\n",
-					dev->name, mii_status);
-			if (mii_status & BMSR_LSTATUS) {
-				int mii_reg5 = mdio_read(dev, vp->phys[0], MII_LPA);
-				if (! vp->force_fd  &&  mii_reg5 != 0xffff) {
-					int duplex;
-
-					mii_reg5 &= vp->advertising;
-					duplex = (mii_reg5&0x0100) || (mii_reg5 & 0x01C0) == 0x0040;
-					if (vp->full_duplex != duplex) {
-						vp->full_duplex = duplex;
-						printk(KERN_INFO "%s: Setting %s-duplex based on MII "
-							"#%d link partner capability of %4.4x.\n",
-							dev->name, vp->full_duplex ? "full" : "half",
-							vp->phys[0], mii_reg5);
-						/* Set the full-duplex bit. */
-						EL3WINDOW(3);
-						iowrite16(	(vp->full_duplex ? 0x20 : 0) |
-								(vp->large_frames ? 0x40 : 0) |
-								((vp->full_duplex && vp->flow_ctrl && vp->partner_flow_ctrl) ? 0x100 : 0),
-								ioaddr + Wn3_MAC_Ctrl);
-						if (vortex_debug > 1)
-							printk(KERN_DEBUG "Setting duplex in Wn3_MAC_Ctrl\n");
-						/* AKPM: bug: should reset Tx and Rx after setting Duplex.  Page 180 */
-					}
-				}
-				netif_carrier_on(dev);
-			} else {
-				netif_carrier_off(dev);
-			}
+			spin_lock_bh(&vp->lock);
+			vortex_check_media(dev, 0);
 			spin_unlock_bh(&vp->lock);
 		}
 		break;
@@ -1962,7 +1927,14 @@
 				 dev->name, media_tbl[dev->if_port].name, media_status);
 		ok = 1;
 	}
-	if ( ! ok) {
+
+	if (!netif_carrier_ok(dev))
+		next_tick = 5*HZ;
+
+	if (vp->medialock)
+		goto leave_media_alone;
+
+	if (!ok) {
 		unsigned int config;
 
 		do {
@@ -1995,10 +1967,10 @@
 			printk(KERN_DEBUG "wrote 0x%08x to Wn3_Config\n", config);
 		/* AKPM: FIXME: Should reset Rx & Tx here.  P60 of 3c90xc.pdf */
 	}
-	EL3WINDOW(old_window);
-	enable_irq(dev->irq);
 
 leave_media_alone:
+	EL3WINDOW(old_window);
+	enable_irq(dev->irq);
 	if (vortex_debug > 2)
 	  printk(KERN_DEBUG "%s: Media selection timer finished, %s.\n",
 			 dev->name, media_tbl[dev->if_port].name);
@@ -2767,6 +2739,8 @@
 	del_timer_sync(&vp->rx_oom_timer);
 	del_timer_sync(&vp->timer);
 
+	netif_carrier_off(dev);
+
 	/* Turn off statistics ASAP.  We update vp->stats below. */
 	iowrite16(StatsDisable, ioaddr + EL3_CMD);
 
@@ -2967,20 +2941,6 @@
 	return rc;
 }
 
-static u32 vortex_get_link(struct net_device *dev)
-{
-	struct vortex_private *vp = netdev_priv(dev);
-	void __iomem *ioaddr = vp->ioaddr;
-	unsigned long flags;
-	int rc;
-
-	spin_lock_irqsave(&vp->lock, flags);
-	EL3WINDOW(4);
-	rc = mii_link_ok(&vp->mii);
-	spin_unlock_irqrestore(&vp->lock, flags);
-	return rc;
-}
-
 static int vortex_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 {
 	struct vortex_private *vp = netdev_priv(dev);
@@ -3080,7 +3040,7 @@
 	.get_stats_count        = vortex_get_stats_count,
 	.get_settings           = vortex_get_settings,
 	.set_settings           = vortex_set_settings,
-	.get_link               = vortex_get_link,
+	.get_link               = ethtool_op_get_link,
 	.nway_reset             = vortex_nway_reset,
 	.get_perm_addr			= ethtool_op_get_perm_addr,
 };
diff -urN oldtree/drivers/net/8139cp.c newtree/drivers/net/8139cp.c
--- oldtree/drivers/net/8139cp.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/8139cp.c	2006-02-13 19:19:59.971516632 -0500
@@ -1196,20 +1196,11 @@
 
 	cp_init_hw(cp);
 
-	rc = request_irq(dev->irq, cp_interrupt, SA_SHIRQ, dev->name, dev);
-	if (rc)
-		goto err_out_hw;
-
 	netif_carrier_off(dev);
 	mii_check_media(&cp->mii_if, netif_msg_link(cp), TRUE);
 	netif_start_queue(dev);
 
 	return 0;
-
-err_out_hw:
-	cp_stop_hw(cp);
-	cp_free_rings(cp);
-	return rc;
 }
 
 static int cp_close (struct net_device *dev)
@@ -1230,7 +1221,6 @@
 	spin_unlock_irqrestore(&cp->lock, flags);
 
 	synchronize_irq(dev->irq);
-	free_irq(dev->irq, dev);
 
 	cp_free_rings(cp);
 	return 0;
@@ -1814,6 +1804,10 @@
 	if (rc)
 		goto err_out_iomap;
 
+	rc = request_irq(dev->irq, cp_interrupt, SA_SHIRQ, dev->name, dev);
+	if (rc)
+		goto err_out_unreg;
+
 	printk (KERN_INFO "%s: RTL-8139C+ at 0x%lx, "
 		"%02x:%02x:%02x:%02x:%02x:%02x, "
 		"IRQ %d\n",
@@ -1833,6 +1827,8 @@
 
 	return 0;
 
+err_out_unreg:
+	unregister_netdev(dev);
 err_out_iomap:
 	iounmap(regs);
 err_out_res:
@@ -1853,6 +1849,7 @@
 
 	if (!dev)
 		BUG();
+	free_irq(dev->irq, dev);
 	unregister_netdev(dev);
 	iounmap(cp->regs);
 	if (cp->wol_enabled) pci_set_power_state (pdev, PCI_D0);
diff -urN oldtree/drivers/net/arcnet/arc-rawmode.c newtree/drivers/net/arcnet/arc-rawmode.c
--- oldtree/drivers/net/arcnet/arc-rawmode.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/arcnet/arc-rawmode.c	2006-02-13 19:19:59.971516632 -0500
@@ -42,7 +42,7 @@
 static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
 		      int bufnum);
 
-struct ArcProto rawmode_proto =
+static struct ArcProto rawmode_proto =
 {
 	.suffix		= 'r',
 	.mtu		= XMTU,
diff -urN oldtree/drivers/net/arcnet/arcnet.c newtree/drivers/net/arcnet/arcnet.c
--- oldtree/drivers/net/arcnet/arcnet.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/arcnet/arcnet.c	2006-02-13 19:19:59.973516328 -0500
@@ -61,6 +61,7 @@
 static int null_prepare_tx(struct net_device *dev, struct archdr *pkt,
 			   int length, int bufnum);
 
+static void arcnet_rx(struct net_device *dev, int bufnum);
 
 /*
  * one ArcProto per possible proto ID.  None of the elements of
@@ -71,7 +72,7 @@
  struct ArcProto *arc_proto_map[256], *arc_proto_default,
    *arc_bcast_proto, *arc_raw_proto;
 
-struct ArcProto arc_proto_null =
+static struct ArcProto arc_proto_null =
 {
 	.suffix		= '?',
 	.mtu		= XMTU,
@@ -90,7 +91,6 @@
 EXPORT_SYMBOL(arc_proto_default);
 EXPORT_SYMBOL(arc_bcast_proto);
 EXPORT_SYMBOL(arc_raw_proto);
-EXPORT_SYMBOL(arc_proto_null);
 EXPORT_SYMBOL(arcnet_unregister_proto);
 EXPORT_SYMBOL(arcnet_debug);
 EXPORT_SYMBOL(alloc_arcdev);
@@ -118,7 +118,7 @@
 
 	arcnet_debug = debug;
 
-	printk(VERSION);
+	printk("arcnet loaded.\n");
 
 #ifdef ALPHA_WARNING
 	BUGLVL(D_EXTRA) {
@@ -178,8 +178,8 @@
  * Dump the contents of an ARCnet buffer
  */
 #if (ARCNET_DEBUG_MAX & (D_RX | D_TX))
-void arcnet_dump_packet(struct net_device *dev, int bufnum, char *desc,
-			int take_arcnet_lock)
+static void arcnet_dump_packet(struct net_device *dev, int bufnum,
+			       char *desc, int take_arcnet_lock)
 {
 	struct arcnet_local *lp = dev->priv;
 	int i, length;
@@ -208,7 +208,10 @@
 
 }
 
-EXPORT_SYMBOL(arcnet_dump_packet);
+#else
+
+#define arcnet_dump_packet(dev, bufnum, desc,take_arcnet_lock) do { } while (0)
+
 #endif
 
 
@@ -996,7 +999,7 @@
  * This is a generic packet receiver that calls arcnet??_rx depending on the
  * protocol ID found.
  */
-void arcnet_rx(struct net_device *dev, int bufnum)
+static void arcnet_rx(struct net_device *dev, int bufnum)
 {
 	struct arcnet_local *lp = dev->priv;
 	struct archdr pkt;
diff -urN oldtree/drivers/net/arcnet/rfc1051.c newtree/drivers/net/arcnet/rfc1051.c
--- oldtree/drivers/net/arcnet/rfc1051.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/arcnet/rfc1051.c	2006-02-13 19:19:59.974516176 -0500
@@ -43,7 +43,7 @@
 		      int bufnum);
 
 
-struct ArcProto rfc1051_proto =
+static struct ArcProto rfc1051_proto =
 {
 	.suffix		= 's',
 	.mtu		= XMTU - RFC1051_HDR_SIZE,
diff -urN oldtree/drivers/net/arcnet/rfc1201.c newtree/drivers/net/arcnet/rfc1201.c
--- oldtree/drivers/net/arcnet/rfc1201.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/arcnet/rfc1201.c	2006-02-13 19:19:59.974516176 -0500
@@ -43,7 +43,7 @@
 		      int bufnum);
 static int continue_tx(struct net_device *dev, int bufnum);
 
-struct ArcProto rfc1201_proto =
+static struct ArcProto rfc1201_proto =
 {
 	.suffix		= 'a',
 	.mtu		= 1500,	/* could be more, but some receivers can't handle it... */
diff -urN oldtree/drivers/net/cassini.c newtree/drivers/net/cassini.c
--- oldtree/drivers/net/cassini.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/cassini.c	2006-02-13 19:19:59.978515568 -0500
@@ -1925,8 +1925,8 @@
 	u64 compwb = le64_to_cpu(cp->init_block->tx_compwb);
 #endif
 	if (netif_msg_intr(cp))
-		printk(KERN_DEBUG "%s: tx interrupt, status: 0x%x, %lx\n",
-			cp->dev->name, status, compwb);
+		printk(KERN_DEBUG "%s: tx interrupt, status: 0x%x, %llx\n",
+			cp->dev->name, status, (unsigned long long)compwb);
 	/* process all the rings */
 	for (ring = 0; ring < N_TX_RINGS; ring++) {
 #ifdef USE_TX_COMPWB
diff -urN oldtree/drivers/net/cassini.c.orig newtree/drivers/net/cassini.c.orig
--- oldtree/drivers/net/cassini.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/cassini.c.orig	2006-02-13 19:19:59.986514352 -0500
@@ -0,0 +1,5236 @@
+/* cassini.c: Sun Microsystems Cassini(+) ethernet driver.
+ *
+ * Copyright (C) 2004 Sun Microsystems Inc.
+ * Copyright (C) 2003 Adrian Sun (asun@darksunrising.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * This driver uses the sungem driver (c) David Miller
+ * (davem@redhat.com) as its basis.
+ *
+ * The cassini chip has a number of features that distinguish it from
+ * the gem chip:
+ *  4 transmit descriptor rings that are used for either QoS (VLAN) or
+ *      load balancing (non-VLAN mode)
+ *  batching of multiple packets
+ *  multiple CPU dispatching
+ *  page-based RX descriptor engine with separate completion rings
+ *  Gigabit support (GMII and PCS interface)
+ *  MIF link up/down detection works
+ *
+ * RX is handled by page sized buffers that are attached as fragments to
+ * the skb. here's what's done:
+ *  -- driver allocates pages at a time and keeps reference counts
+ *     on them.
+ *  -- the upper protocol layers assume that the header is in the skb
+ *     itself. as a result, cassini will copy a small amount (64 bytes)
+ *     to make them happy.
+ *  -- driver appends the rest of the data pages as frags to skbuffs
+ *     and increments the reference count
+ *  -- on page reclamation, the driver swaps the page with a spare page.
+ *     if that page is still in use, it frees its reference to that page,
+ *     and allocates a new page for use. otherwise, it just recycles the
+ *     the page. 
+ *
+ * NOTE: cassini can parse the header. however, it's not worth it
+ *       as long as the network stack requires a header copy.
+ *
+ * TX has 4 queues. currently these queues are used in a round-robin
+ * fashion for load balancing. They can also be used for QoS. for that
+ * to work, however, QoS information needs to be exposed down to the driver
+ * level so that subqueues get targetted to particular transmit rings.
+ * alternatively, the queues can be configured via use of the all-purpose
+ * ioctl.
+ *
+ * RX DATA: the rx completion ring has all the info, but the rx desc
+ * ring has all of the data. RX can conceivably come in under multiple
+ * interrupts, but the INT# assignment needs to be set up properly by
+ * the BIOS and conveyed to the driver. PCI BIOSes don't know how to do
+ * that. also, the two descriptor rings are designed to distinguish between
+ * encrypted and non-encrypted packets, but we use them for buffering 
+ * instead.
+ *
+ * by default, the selective clear mask is set up to process rx packets.  
+ */
+
+#include <linux/config.h>
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/compiler.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/list.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/crc32.h>
+#include <linux/random.h>
+#include <linux/mii.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+
+#include <net/checksum.h>
+
+#include <asm/atomic.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#define cas_page_map(x)      kmap_atomic((x), KM_SKB_DATA_SOFTIRQ)
+#define cas_page_unmap(x)    kunmap_atomic((x), KM_SKB_DATA_SOFTIRQ)
+#define CAS_NCPUS            num_online_cpus()
+
+#if defined(CONFIG_CASSINI_NAPI) && defined(HAVE_NETDEV_POLL)
+#define USE_NAPI
+#define cas_skb_release(x)  netif_receive_skb(x)
+#else
+#define cas_skb_release(x)  netif_rx(x)
+#endif
+
+/* select which firmware to use */
+#define USE_HP_WORKAROUND     
+#define HP_WORKAROUND_DEFAULT /* select which firmware to use as default */
+#define CAS_HP_ALT_FIRMWARE   cas_prog_null /* alternate firmware */
+
+#include "cassini.h"
+
+#define USE_TX_COMPWB      /* use completion writeback registers */
+#define USE_CSMA_CD_PROTO  /* standard CSMA/CD */
+#define USE_RX_BLANK       /* hw interrupt mitigation */
+#undef USE_ENTROPY_DEV     /* don't test for entropy device */
+
+/* NOTE: these aren't useable unless PCI interrupts can be assigned.
+ * also, we need to make cp->lock finer-grained.
+ */
+#undef  USE_PCI_INTB
+#undef  USE_PCI_INTC
+#undef  USE_PCI_INTD
+#undef  USE_QOS
+
+#undef  USE_VPD_DEBUG       /* debug vpd information if defined */
+
+/* rx processing options */
+#define USE_PAGE_ORDER      /* specify to allocate large rx pages */
+#define RX_DONT_BATCH  0    /* if 1, don't batch flows */
+#define RX_COPY_ALWAYS 0    /* if 0, use frags */
+#define RX_COPY_MIN    64   /* copy a little to make upper layers happy */
+#undef  RX_COUNT_BUFFERS    /* define to calculate RX buffer stats */
+
+#define DRV_MODULE_NAME		"cassini"
+#define PFX DRV_MODULE_NAME	": "
+#define DRV_MODULE_VERSION	"1.4"
+#define DRV_MODULE_RELDATE	"1 July 2004"
+
+#define CAS_DEF_MSG_ENABLE	  \
+	(NETIF_MSG_DRV		| \
+	 NETIF_MSG_PROBE	| \
+	 NETIF_MSG_LINK		| \
+	 NETIF_MSG_TIMER	| \
+	 NETIF_MSG_IFDOWN	| \
+	 NETIF_MSG_IFUP		| \
+	 NETIF_MSG_RX_ERR	| \
+	 NETIF_MSG_TX_ERR)
+
+/* length of time before we decide the hardware is borked,
+ * and dev->tx_timeout() should be called to fix the problem
+ */
+#define CAS_TX_TIMEOUT			(HZ)
+#define CAS_LINK_TIMEOUT                (22*HZ/10)
+#define CAS_LINK_FAST_TIMEOUT           (1)
+
+/* timeout values for state changing. these specify the number
+ * of 10us delays to be used before giving up.
+ */
+#define STOP_TRIES_PHY 1000
+#define STOP_TRIES     5000
+
+/* specify a minimum frame size to deal with some fifo issues 
+ * max mtu == 2 * page size - ethernet header - 64 - swivel =
+ *            2 * page_size - 0x50
+ */
+#define CAS_MIN_FRAME			97
+#define CAS_1000MB_MIN_FRAME            255
+#define CAS_MIN_MTU                     60
+#define CAS_MAX_MTU                     min(((cp->page_size << 1) - 0x50), 9000)
+
+#if 1
+/*
+ * Eliminate these and use separate atomic counters for each, to
+ * avoid a race condition.
+ */
+#else
+#define CAS_RESET_MTU                   1
+#define CAS_RESET_ALL                   2
+#define CAS_RESET_SPARE                 3
+#endif
+
+static char version[] __devinitdata =
+	DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
+
+MODULE_AUTHOR("Adrian Sun (asun@darksunrising.com)");
+MODULE_DESCRIPTION("Sun Cassini(+) ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_PARM(cassini_debug, "i");
+MODULE_PARM_DESC(cassini_debug, "Cassini bitmapped debugging message enable value");
+MODULE_PARM(link_mode, "i");
+MODULE_PARM_DESC(link_mode, "default link mode");
+
+/*
+ * Work around for a PCS bug in which the link goes down due to the chip
+ * being confused and never showing a link status of "up."
+ */
+#define DEFAULT_LINKDOWN_TIMEOUT 5
+/* 
+ * Value in seconds, for user input.
+ */
+static int linkdown_timeout = DEFAULT_LINKDOWN_TIMEOUT;
+MODULE_PARM(linkdown_timeout, "i");
+MODULE_PARM_DESC(linkdown_timeout,
+"min reset interval in sec. for PCS linkdown issue; disabled if not positive");
+
+/*
+ * value in 'ticks' (units used by jiffies). Set when we init the
+ * module because 'HZ' in actually a function call on some flavors of
+ * Linux.  This will default to DEFAULT_LINKDOWN_TIMEOUT * HZ.
+ */
+static int link_transition_timeout;
+
+
+static int cassini_debug = -1;	/* -1 == use CAS_DEF_MSG_ENABLE as value */
+static int link_mode;
+
+static u16 link_modes[] __devinitdata = {
+	BMCR_ANENABLE,			 /* 0 : autoneg */
+	0,				 /* 1 : 10bt half duplex */
+	BMCR_SPEED100,			 /* 2 : 100bt half duplex */
+	BMCR_FULLDPLX,			 /* 3 : 10bt full duplex */
+	BMCR_SPEED100|BMCR_FULLDPLX,	 /* 4 : 100bt full duplex */
+	CAS_BMCR_SPEED1000|BMCR_FULLDPLX /* 5 : 1000bt full duplex */
+};
+
+static struct pci_device_id cas_pci_tbl[] __devinitdata = {
+	{ PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_CASSINI,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+	{ PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SATURN,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, cas_pci_tbl);
+
+static void cas_set_link_modes(struct cas *cp);
+
+static inline void cas_lock_tx(struct cas *cp)
+{
+	int i;
+
+	for (i = 0; i < N_TX_RINGS; i++)  
+		spin_lock(&cp->tx_lock[i]);
+}
+
+static inline void cas_lock_all(struct cas *cp)
+{
+	spin_lock_irq(&cp->lock);
+	cas_lock_tx(cp);
+}
+
+/* WTZ: QA was finding deadlock problems with the previous
+ * versions after long test runs with multiple cards per machine.
+ * See if replacing cas_lock_all with safer versions helps. The
+ * symptoms QA is reporting match those we'd expect if interrupts
+ * aren't being properly restored, and we fixed a previous deadlock
+ * with similar symptoms by using save/restore versions in other
+ * places.
+ */
+#define cas_lock_all_save(cp, flags) \
+do { \
+	struct cas *xxxcp = (cp); \
+	spin_lock_irqsave(&xxxcp->lock, flags); \
+	cas_lock_tx(xxxcp); \
+} while (0)
+
+static inline void cas_unlock_tx(struct cas *cp)
+{
+	int i;
+
+	for (i = N_TX_RINGS; i > 0; i--)  
+		spin_unlock(&cp->tx_lock[i - 1]);  
+}
+
+static inline void cas_unlock_all(struct cas *cp)
+{
+	cas_unlock_tx(cp);
+	spin_unlock_irq(&cp->lock);
+}
+
+#define cas_unlock_all_restore(cp, flags) \
+do { \
+	struct cas *xxxcp = (cp); \
+	cas_unlock_tx(xxxcp); \
+	spin_unlock_irqrestore(&xxxcp->lock, flags); \
+} while (0)
+
+static void cas_disable_irq(struct cas *cp, const int ring)
+{
+	/* Make sure we won't get any more interrupts */
+	if (ring == 0) {
+		writel(0xFFFFFFFF, cp->regs + REG_INTR_MASK);
+		return;
+	}
+
+	/* disable completion interrupts and selectively mask */
+	if (cp->cas_flags & CAS_FLAG_REG_PLUS) {
+		switch (ring) {
+#if defined (USE_PCI_INTB) || defined(USE_PCI_INTC) || defined(USE_PCI_INTD)
+#ifdef USE_PCI_INTB
+		case 1:
+#endif
+#ifdef USE_PCI_INTC
+		case 2:
+#endif
+#ifdef USE_PCI_INTD
+		case 3:
+#endif
+			writel(INTRN_MASK_CLEAR_ALL | INTRN_MASK_RX_EN, 
+			       cp->regs + REG_PLUS_INTRN_MASK(ring));
+			break;
+#endif
+		default:
+			writel(INTRN_MASK_CLEAR_ALL, cp->regs +
+			       REG_PLUS_INTRN_MASK(ring));
+			break;
+		}
+	}
+}
+
+static inline void cas_mask_intr(struct cas *cp)
+{
+	int i;
+
+	for (i = 0; i < N_RX_COMP_RINGS; i++)
+		cas_disable_irq(cp, i);
+}
+
+static void cas_enable_irq(struct cas *cp, const int ring)
+{
+	if (ring == 0) { /* all but TX_DONE */
+		writel(INTR_TX_DONE, cp->regs + REG_INTR_MASK);
+		return;
+	}
+
+	if (cp->cas_flags & CAS_FLAG_REG_PLUS) {
+		switch (ring) {
+#if defined (USE_PCI_INTB) || defined(USE_PCI_INTC) || defined(USE_PCI_INTD)
+#ifdef USE_PCI_INTB
+		case 1:
+#endif
+#ifdef USE_PCI_INTC
+		case 2:
+#endif
+#ifdef USE_PCI_INTD
+		case 3:
+#endif
+			writel(INTRN_MASK_RX_EN, cp->regs +
+			       REG_PLUS_INTRN_MASK(ring));
+			break;
+#endif
+		default:
+			break;
+		}
+	}
+}
+
+static inline void cas_unmask_intr(struct cas *cp)
+{
+	int i;
+
+	for (i = 0; i < N_RX_COMP_RINGS; i++)
+		cas_enable_irq(cp, i);
+}
+
+static inline void cas_entropy_gather(struct cas *cp)
+{
+#ifdef USE_ENTROPY_DEV
+	if ((cp->cas_flags & CAS_FLAG_ENTROPY_DEV) == 0)
+		return;
+
+	batch_entropy_store(readl(cp->regs + REG_ENTROPY_IV),
+			    readl(cp->regs + REG_ENTROPY_IV),
+			    sizeof(uint64_t)*8);
+#endif
+}
+
+static inline void cas_entropy_reset(struct cas *cp)
+{
+#ifdef USE_ENTROPY_DEV
+	if ((cp->cas_flags & CAS_FLAG_ENTROPY_DEV) == 0)
+		return;
+
+	writel(BIM_LOCAL_DEV_PAD | BIM_LOCAL_DEV_PROM | BIM_LOCAL_DEV_EXT, 
+	       cp->regs + REG_BIM_LOCAL_DEV_EN);
+	writeb(ENTROPY_RESET_STC_MODE, cp->regs + REG_ENTROPY_RESET);
+	writeb(0x55, cp->regs + REG_ENTROPY_RAND_REG);
+
+	/* if we read back 0x0, we don't have an entropy device */
+	if (readb(cp->regs + REG_ENTROPY_RAND_REG) == 0)
+		cp->cas_flags &= ~CAS_FLAG_ENTROPY_DEV;
+#endif
+}
+
+/* access to the phy. the following assumes that we've initialized the MIF to 
+ * be in frame rather than bit-bang mode
+ */
+static u16 cas_phy_read(struct cas *cp, int reg)
+{
+	u32 cmd;
+	int limit = STOP_TRIES_PHY;
+
+	cmd = MIF_FRAME_ST | MIF_FRAME_OP_READ;
+	cmd |= CAS_BASE(MIF_FRAME_PHY_ADDR, cp->phy_addr);
+	cmd |= CAS_BASE(MIF_FRAME_REG_ADDR, reg);
+	cmd |= MIF_FRAME_TURN_AROUND_MSB;
+	writel(cmd, cp->regs + REG_MIF_FRAME);
+	
+	/* poll for completion */
+	while (limit-- > 0) {
+		udelay(10);
+		cmd = readl(cp->regs + REG_MIF_FRAME);
+		if (cmd & MIF_FRAME_TURN_AROUND_LSB)
+			return (cmd & MIF_FRAME_DATA_MASK);
+	}
+	return 0xFFFF; /* -1 */
+}
+
+static int cas_phy_write(struct cas *cp, int reg, u16 val)
+{
+	int limit = STOP_TRIES_PHY;
+	u32 cmd;
+
+	cmd = MIF_FRAME_ST | MIF_FRAME_OP_WRITE;
+	cmd |= CAS_BASE(MIF_FRAME_PHY_ADDR, cp->phy_addr);
+	cmd |= CAS_BASE(MIF_FRAME_REG_ADDR, reg);
+	cmd |= MIF_FRAME_TURN_AROUND_MSB;
+	cmd |= val & MIF_FRAME_DATA_MASK;
+	writel(cmd, cp->regs + REG_MIF_FRAME);
+	
+	/* poll for completion */
+	while (limit-- > 0) {
+		udelay(10);
+		cmd = readl(cp->regs + REG_MIF_FRAME);
+		if (cmd & MIF_FRAME_TURN_AROUND_LSB)
+			return 0;
+	}
+	return -1;
+}
+
+static void cas_phy_powerup(struct cas *cp)
+{
+	u16 ctl = cas_phy_read(cp, MII_BMCR);	
+
+	if ((ctl & BMCR_PDOWN) == 0)
+		return;
+	ctl &= ~BMCR_PDOWN;
+	cas_phy_write(cp, MII_BMCR, ctl);
+}
+
+static void cas_phy_powerdown(struct cas *cp)
+{
+	u16 ctl = cas_phy_read(cp, MII_BMCR);	
+
+	if (ctl & BMCR_PDOWN)
+		return;
+	ctl |= BMCR_PDOWN;
+	cas_phy_write(cp, MII_BMCR, ctl);
+}
+
+/* cp->lock held. note: the last put_page will free the buffer */
+static int cas_page_free(struct cas *cp, cas_page_t *page)
+{
+	pci_unmap_page(cp->pdev, page->dma_addr, cp->page_size, 
+		       PCI_DMA_FROMDEVICE);
+	__free_pages(page->buffer, cp->page_order);
+	kfree(page);
+	return 0;
+}
+
+#ifdef RX_COUNT_BUFFERS
+#define RX_USED_ADD(x, y)       ((x)->used += (y))
+#define RX_USED_SET(x, y)       ((x)->used  = (y))
+#else
+#define RX_USED_ADD(x, y) 
+#define RX_USED_SET(x, y)
+#endif
+
+/* local page allocation routines for the receive buffers. jumbo pages
+ * require at least 8K contiguous and 8K aligned buffers.
+ */
+static cas_page_t *cas_page_alloc(struct cas *cp, const gfp_t flags)
+{
+	cas_page_t *page;
+
+	page = kmalloc(sizeof(cas_page_t), flags);
+	if (!page)
+		return NULL;
+
+	INIT_LIST_HEAD(&page->list);
+	RX_USED_SET(page, 0);
+	page->buffer = alloc_pages(flags, cp->page_order);
+	if (!page->buffer)
+		goto page_err;
+	page->dma_addr = pci_map_page(cp->pdev, page->buffer, 0,
+				      cp->page_size, PCI_DMA_FROMDEVICE);
+	return page;
+
+page_err:
+	kfree(page);
+	return NULL;
+}
+
+/* initialize spare pool of rx buffers, but allocate during the open */
+static void cas_spare_init(struct cas *cp)
+{
+  	spin_lock(&cp->rx_inuse_lock);
+	INIT_LIST_HEAD(&cp->rx_inuse_list);
+	spin_unlock(&cp->rx_inuse_lock);
+
+	spin_lock(&cp->rx_spare_lock);
+	INIT_LIST_HEAD(&cp->rx_spare_list);
+	cp->rx_spares_needed = RX_SPARE_COUNT;
+	spin_unlock(&cp->rx_spare_lock);
+}
+
+/* used on close. free all the spare buffers. */
+static void cas_spare_free(struct cas *cp)
+{
+	struct list_head list, *elem, *tmp;
+
+	/* free spare buffers */
+	INIT_LIST_HEAD(&list);
+	spin_lock(&cp->rx_spare_lock);
+	list_splice(&cp->rx_spare_list, &list);
+	INIT_LIST_HEAD(&cp->rx_spare_list);
+	spin_unlock(&cp->rx_spare_lock);
+	list_for_each_safe(elem, tmp, &list) {
+		cas_page_free(cp, list_entry(elem, cas_page_t, list));
+	}
+
+	INIT_LIST_HEAD(&list);
+#if 1
+	/*
+	 * Looks like Adrian had protected this with a different
+	 * lock than used everywhere else to manipulate this list.
+	 */
+	spin_lock(&cp->rx_inuse_lock);
+	list_splice(&cp->rx_inuse_list, &list);
+	INIT_LIST_HEAD(&cp->rx_inuse_list);
+	spin_unlock(&cp->rx_inuse_lock);
+#else
+	spin_lock(&cp->rx_spare_lock);
+	list_splice(&cp->rx_inuse_list, &list);
+	INIT_LIST_HEAD(&cp->rx_inuse_list);
+	spin_unlock(&cp->rx_spare_lock);
+#endif
+	list_for_each_safe(elem, tmp, &list) {
+		cas_page_free(cp, list_entry(elem, cas_page_t, list));
+	}
+}
+
+/* replenish spares if needed */
+static void cas_spare_recover(struct cas *cp, const gfp_t flags)
+{
+	struct list_head list, *elem, *tmp;
+	int needed, i;
+
+	/* check inuse list. if we don't need any more free buffers,
+	 * just free it
+	 */
+
+	/* make a local copy of the list */
+	INIT_LIST_HEAD(&list);
+	spin_lock(&cp->rx_inuse_lock);
+	list_splice(&cp->rx_inuse_list, &list);
+	INIT_LIST_HEAD(&cp->rx_inuse_list);
+	spin_unlock(&cp->rx_inuse_lock);
+	
+	list_for_each_safe(elem, tmp, &list) {
+		cas_page_t *page = list_entry(elem, cas_page_t, list);
+
+		if (page_count(page->buffer) > 1) 
+			continue;
+
+		list_del(elem);
+		spin_lock(&cp->rx_spare_lock);
+		if (cp->rx_spares_needed > 0) {
+			list_add(elem, &cp->rx_spare_list);
+			cp->rx_spares_needed--;
+			spin_unlock(&cp->rx_spare_lock);
+		} else {
+			spin_unlock(&cp->rx_spare_lock);
+			cas_page_free(cp, page);
+		}
+	}
+
+	/* put any inuse buffers back on the list */
+	if (!list_empty(&list)) {
+		spin_lock(&cp->rx_inuse_lock);
+		list_splice(&list, &cp->rx_inuse_list);
+		spin_unlock(&cp->rx_inuse_lock);
+	}
+	
+	spin_lock(&cp->rx_spare_lock);
+	needed = cp->rx_spares_needed;
+	spin_unlock(&cp->rx_spare_lock);
+	if (!needed)
+		return;
+
+	/* we still need spares, so try to allocate some */
+	INIT_LIST_HEAD(&list);
+	i = 0;
+	while (i < needed) {
+		cas_page_t *spare = cas_page_alloc(cp, flags);
+		if (!spare) 
+			break;
+		list_add(&spare->list, &list);
+		i++;
+	}
+
+	spin_lock(&cp->rx_spare_lock);
+	list_splice(&list, &cp->rx_spare_list);
+	cp->rx_spares_needed -= i;
+	spin_unlock(&cp->rx_spare_lock);
+}
+
+/* pull a page from the list. */
+static cas_page_t *cas_page_dequeue(struct cas *cp)
+{
+	struct list_head *entry;
+	int recover;
+
+	spin_lock(&cp->rx_spare_lock);
+	if (list_empty(&cp->rx_spare_list)) {
+		/* try to do a quick recovery */
+		spin_unlock(&cp->rx_spare_lock);
+		cas_spare_recover(cp, GFP_ATOMIC);
+		spin_lock(&cp->rx_spare_lock);
+		if (list_empty(&cp->rx_spare_list)) {
+			if (netif_msg_rx_err(cp))
+				printk(KERN_ERR "%s: no spare buffers "
+				       "available.\n", cp->dev->name);
+			spin_unlock(&cp->rx_spare_lock);
+			return NULL;
+		}
+	}
+
+	entry = cp->rx_spare_list.next;
+	list_del(entry);
+	recover = ++cp->rx_spares_needed;
+	spin_unlock(&cp->rx_spare_lock);
+
+	/* trigger the timer to do the recovery */
+	if ((recover & (RX_SPARE_RECOVER_VAL - 1)) == 0) {
+#if 1
+		atomic_inc(&cp->reset_task_pending);
+		atomic_inc(&cp->reset_task_pending_spare);
+		schedule_work(&cp->reset_task);
+#else
+		atomic_set(&cp->reset_task_pending, CAS_RESET_SPARE);
+		schedule_work(&cp->reset_task);
+#endif
+	}
+	return list_entry(entry, cas_page_t, list);
+}
+
+
+static void cas_mif_poll(struct cas *cp, const int enable)
+{
+	u32 cfg;
+	
+	cfg  = readl(cp->regs + REG_MIF_CFG); 
+	cfg &= (MIF_CFG_MDIO_0 | MIF_CFG_MDIO_1);
+
+	if (cp->phy_type & CAS_PHY_MII_MDIO1)
+		cfg |= MIF_CFG_PHY_SELECT; 
+
+	/* poll and interrupt on link status change. */
+	if (enable) {
+		cfg |= MIF_CFG_POLL_EN;
+		cfg |= CAS_BASE(MIF_CFG_POLL_REG, MII_BMSR);
+		cfg |= CAS_BASE(MIF_CFG_POLL_PHY, cp->phy_addr);
+	}
+	writel((enable) ? ~(BMSR_LSTATUS | BMSR_ANEGCOMPLETE) : 0xFFFF, 
+	       cp->regs + REG_MIF_MASK); 
+	writel(cfg, cp->regs + REG_MIF_CFG);
+}
+
+/* Must be invoked under cp->lock */
+static void cas_begin_auto_negotiation(struct cas *cp, struct ethtool_cmd *ep)
+{
+	u16 ctl;
+#if 1
+	int lcntl;
+	int changed = 0;
+	int oldstate = cp->lstate;
+	int link_was_not_down = !(oldstate == link_down);
+#endif
+	/* Setup link parameters */
+	if (!ep)
+		goto start_aneg;
+	lcntl = cp->link_cntl;
+	if (ep->autoneg == AUTONEG_ENABLE)
+		cp->link_cntl = BMCR_ANENABLE;
+	else {
+		cp->link_cntl = 0;
+		if (ep->speed == SPEED_100)
+			cp->link_cntl |= BMCR_SPEED100;
+		else if (ep->speed == SPEED_1000)
+			cp->link_cntl |= CAS_BMCR_SPEED1000;
+		if (ep->duplex == DUPLEX_FULL)
+			cp->link_cntl |= BMCR_FULLDPLX;
+	}
+#if 1
+	changed = (lcntl != cp->link_cntl);
+#endif
+start_aneg:
+	if (cp->lstate == link_up) {
+		printk(KERN_INFO "%s: PCS link down.\n",
+		       cp->dev->name);
+	} else {
+		if (changed) {
+			printk(KERN_INFO "%s: link configuration changed\n",
+			       cp->dev->name);
+		}
+	}
+	cp->lstate = link_down;
+	cp->link_transition = LINK_TRANSITION_LINK_DOWN;
+	if (!cp->hw_running)
+		return;
+#if 1
+	/*
+	 * WTZ: If the old state was link_up, we turn off the carrier
+	 * to replicate everything we do elsewhere on a link-down
+	 * event when we were already in a link-up state..  
+	 */
+	if (oldstate == link_up)
+		netif_carrier_off(cp->dev);
+	if (changed  && link_was_not_down) {
+		/*
+		 * WTZ: This branch will simply schedule a full reset after
+		 * we explicitly changed link modes in an ioctl. See if this
+		 * fixes the link-problems we were having for forced mode. 
+		 */
+		atomic_inc(&cp->reset_task_pending);
+		atomic_inc(&cp->reset_task_pending_all);
+		schedule_work(&cp->reset_task);
+		cp->timer_ticks = 0;
+		mod_timer(&cp->link_timer, jiffies + CAS_LINK_TIMEOUT);
+		return;
+	}
+#endif
+	if (cp->phy_type & CAS_PHY_SERDES) {
+		u32 val = readl(cp->regs + REG_PCS_MII_CTRL);
+
+		if (cp->link_cntl & BMCR_ANENABLE) {
+			val |= (PCS_MII_RESTART_AUTONEG | PCS_MII_AUTONEG_EN);
+			cp->lstate = link_aneg;
+		} else {
+			if (cp->link_cntl & BMCR_FULLDPLX)
+				val |= PCS_MII_CTRL_DUPLEX;
+			val &= ~PCS_MII_AUTONEG_EN;
+			cp->lstate = link_force_ok;
+		}
+		cp->link_transition = LINK_TRANSITION_LINK_CONFIG;
+		writel(val, cp->regs + REG_PCS_MII_CTRL);
+
+	} else {
+		cas_mif_poll(cp, 0);
+		ctl = cas_phy_read(cp, MII_BMCR);
+		ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | 
+			 CAS_BMCR_SPEED1000 | BMCR_ANENABLE);
+		ctl |= cp->link_cntl;
+		if (ctl & BMCR_ANENABLE) {
+			ctl |= BMCR_ANRESTART;
+			cp->lstate = link_aneg;
+		} else {
+			cp->lstate = link_force_ok;
+		}
+		cp->link_transition = LINK_TRANSITION_LINK_CONFIG;
+		cas_phy_write(cp, MII_BMCR, ctl);
+		cas_mif_poll(cp, 1);
+	}
+
+	cp->timer_ticks = 0;
+	mod_timer(&cp->link_timer, jiffies + CAS_LINK_TIMEOUT);
+}
+
+/* Must be invoked under cp->lock. */
+static int cas_reset_mii_phy(struct cas *cp)
+{
+	int limit = STOP_TRIES_PHY;
+	u16 val;
+	
+	cas_phy_write(cp, MII_BMCR, BMCR_RESET);
+	udelay(100);
+	while (limit--) {
+		val = cas_phy_read(cp, MII_BMCR);
+		if ((val & BMCR_RESET) == 0)
+			break;
+		udelay(10);
+	}
+	return (limit <= 0);
+}
+
+static void cas_saturn_firmware_load(struct cas *cp)
+{
+	cas_saturn_patch_t *patch = cas_saturn_patch;
+
+	cas_phy_powerdown(cp);
+
+	/* expanded memory access mode */
+	cas_phy_write(cp, DP83065_MII_MEM, 0x0);
+
+	/* pointer configuration for new firmware */
+	cas_phy_write(cp, DP83065_MII_REGE, 0x8ff9);
+	cas_phy_write(cp, DP83065_MII_REGD, 0xbd);
+	cas_phy_write(cp, DP83065_MII_REGE, 0x8ffa);
+	cas_phy_write(cp, DP83065_MII_REGD, 0x82);
+	cas_phy_write(cp, DP83065_MII_REGE, 0x8ffb);
+	cas_phy_write(cp, DP83065_MII_REGD, 0x0);
+	cas_phy_write(cp, DP83065_MII_REGE, 0x8ffc);
+	cas_phy_write(cp, DP83065_MII_REGD, 0x39);
+
+	/* download new firmware */
+	cas_phy_write(cp, DP83065_MII_MEM, 0x1);
+	cas_phy_write(cp, DP83065_MII_REGE, patch->addr);
+	while (patch->addr) {
+		cas_phy_write(cp, DP83065_MII_REGD, patch->val);
+		patch++;
+	}
+
+	/* enable firmware */
+	cas_phy_write(cp, DP83065_MII_REGE, 0x8ff8);
+	cas_phy_write(cp, DP83065_MII_REGD, 0x1);
+}
+
+
+/* phy initialization */
+static void cas_phy_init(struct cas *cp)
+{
+	u16 val;
+
+	/* if we're in MII/GMII mode, set up phy */
+	if (CAS_PHY_MII(cp->phy_type)) {
+		writel(PCS_DATAPATH_MODE_MII,
+		       cp->regs + REG_PCS_DATAPATH_MODE);
+
+		cas_mif_poll(cp, 0);
+		cas_reset_mii_phy(cp); /* take out of isolate mode */
+
+		if (PHY_LUCENT_B0 == cp->phy_id) {
+			/* workaround link up/down issue with lucent */
+			cas_phy_write(cp, LUCENT_MII_REG, 0x8000);
+			cas_phy_write(cp, MII_BMCR, 0x00f1);
+			cas_phy_write(cp, LUCENT_MII_REG, 0x0);
+
+		} else if (PHY_BROADCOM_B0 == (cp->phy_id & 0xFFFFFFFC)) {
+			/* workarounds for broadcom phy */
+			cas_phy_write(cp, BROADCOM_MII_REG8, 0x0C20);
+			cas_phy_write(cp, BROADCOM_MII_REG7, 0x0012);
+			cas_phy_write(cp, BROADCOM_MII_REG5, 0x1804);
+			cas_phy_write(cp, BROADCOM_MII_REG7, 0x0013);
+			cas_phy_write(cp, BROADCOM_MII_REG5, 0x1204);
+			cas_phy_write(cp, BROADCOM_MII_REG7, 0x8006);
+			cas_phy_write(cp, BROADCOM_MII_REG5, 0x0132);
+			cas_phy_write(cp, BROADCOM_MII_REG7, 0x8006);
+			cas_phy_write(cp, BROADCOM_MII_REG5, 0x0232);
+			cas_phy_write(cp, BROADCOM_MII_REG7, 0x201F);
+			cas_phy_write(cp, BROADCOM_MII_REG5, 0x0A20);
+
+		} else if (PHY_BROADCOM_5411 == cp->phy_id) {
+			val = cas_phy_read(cp, BROADCOM_MII_REG4);
+			val = cas_phy_read(cp, BROADCOM_MII_REG4);
+			if (val & 0x0080) {
+				/* link workaround */
+				cas_phy_write(cp, BROADCOM_MII_REG4, 
+					      val & ~0x0080);
+			}
+			
+		} else if (cp->cas_flags & CAS_FLAG_SATURN) {
+			writel((cp->phy_type & CAS_PHY_MII_MDIO0) ? 
+			       SATURN_PCFG_FSI : 0x0, 
+			       cp->regs + REG_SATURN_PCFG);
+
+			/* load firmware to address 10Mbps auto-negotiation
+			 * issue. NOTE: this will need to be changed if the 
+			 * default firmware gets fixed.
+			 */
+			if (PHY_NS_DP83065 == cp->phy_id) {
+				cas_saturn_firmware_load(cp);
+			}
+			cas_phy_powerup(cp);
+		}
+
+		/* advertise capabilities */
+		val = cas_phy_read(cp, MII_BMCR);
+		val &= ~BMCR_ANENABLE;
+		cas_phy_write(cp, MII_BMCR, val);
+		udelay(10);
+
+		cas_phy_write(cp, MII_ADVERTISE,
+			      cas_phy_read(cp, MII_ADVERTISE) |
+			      (ADVERTISE_10HALF | ADVERTISE_10FULL |
+			       ADVERTISE_100HALF | ADVERTISE_100FULL |
+			       CAS_ADVERTISE_PAUSE | 
+			       CAS_ADVERTISE_ASYM_PAUSE));
+		
+		if (cp->cas_flags & CAS_FLAG_1000MB_CAP) {
+			/* make sure that we don't advertise half
+			 * duplex to avoid a chip issue
+			 */
+			val  = cas_phy_read(cp, CAS_MII_1000_CTRL);
+			val &= ~CAS_ADVERTISE_1000HALF;
+			val |= CAS_ADVERTISE_1000FULL;
+			cas_phy_write(cp, CAS_MII_1000_CTRL, val);
+		}
+
+	} else {
+		/* reset pcs for serdes */
+		u32 val;
+		int limit;
+
+		writel(PCS_DATAPATH_MODE_SERDES,
+		       cp->regs + REG_PCS_DATAPATH_MODE);
+
+		/* enable serdes pins on saturn */
+		if (cp->cas_flags & CAS_FLAG_SATURN)
+			writel(0, cp->regs + REG_SATURN_PCFG);
+
+		/* Reset PCS unit. */
+		val = readl(cp->regs + REG_PCS_MII_CTRL);
+		val |= PCS_MII_RESET;
+		writel(val, cp->regs + REG_PCS_MII_CTRL);
+
+		limit = STOP_TRIES;
+		while (limit-- > 0) {
+			udelay(10);
+			if ((readl(cp->regs + REG_PCS_MII_CTRL) & 
+			     PCS_MII_RESET) == 0)
+				break;
+		}
+		if (limit <= 0)
+			printk(KERN_WARNING "%s: PCS reset bit would not "
+			       "clear [%08x].\n", cp->dev->name,
+			       readl(cp->regs + REG_PCS_STATE_MACHINE));
+
+		/* Make sure PCS is disabled while changing advertisement
+		 * configuration.
+		 */
+		writel(0x0, cp->regs + REG_PCS_CFG);
+
+		/* Advertise all capabilities except half-duplex. */
+		val  = readl(cp->regs + REG_PCS_MII_ADVERT);
+		val &= ~PCS_MII_ADVERT_HD;
+		val |= (PCS_MII_ADVERT_FD | PCS_MII_ADVERT_SYM_PAUSE | 
+			PCS_MII_ADVERT_ASYM_PAUSE);
+		writel(val, cp->regs + REG_PCS_MII_ADVERT);
+
+		/* enable PCS */
+		writel(PCS_CFG_EN, cp->regs + REG_PCS_CFG);
+
+		/* pcs workaround: enable sync detect */
+		writel(PCS_SERDES_CTRL_SYNCD_EN,
+		       cp->regs + REG_PCS_SERDES_CTRL);
+	}
+}
+
+
+static int cas_pcs_link_check(struct cas *cp)
+{
+	u32 stat, state_machine;
+	int retval = 0;
+
+	/* The link status bit latches on zero, so you must
+	 * read it twice in such a case to see a transition
+	 * to the link being up.
+	 */
+	stat = readl(cp->regs + REG_PCS_MII_STATUS);
+	if ((stat & PCS_MII_STATUS_LINK_STATUS) == 0)
+		stat = readl(cp->regs + REG_PCS_MII_STATUS);
+
+	/* The remote-fault indication is only valid
+	 * when autoneg has completed.
+	 */
+	if ((stat & (PCS_MII_STATUS_AUTONEG_COMP |
+		     PCS_MII_STATUS_REMOTE_FAULT)) ==
+	    (PCS_MII_STATUS_AUTONEG_COMP | PCS_MII_STATUS_REMOTE_FAULT)) {
+		if (netif_msg_link(cp))
+			printk(KERN_INFO "%s: PCS RemoteFault\n", 
+			       cp->dev->name);
+	}
+
+	/* work around link detection issue by querying the PCS state
+	 * machine directly.
+	 */
+	state_machine = readl(cp->regs + REG_PCS_STATE_MACHINE);
+	if ((state_machine & PCS_SM_LINK_STATE_MASK) != SM_LINK_STATE_UP) {
+		stat &= ~PCS_MII_STATUS_LINK_STATUS;
+	} else if (state_machine & PCS_SM_WORD_SYNC_STATE_MASK) {
+		stat |= PCS_MII_STATUS_LINK_STATUS;
+	}
+
+	if (stat & PCS_MII_STATUS_LINK_STATUS) {
+		if (cp->lstate != link_up) {
+			if (cp->opened) {
+				cp->lstate = link_up;
+				cp->link_transition = LINK_TRANSITION_LINK_UP;
+				
+				cas_set_link_modes(cp);
+				netif_carrier_on(cp->dev);
+			}
+		}
+	} else if (cp->lstate == link_up) {
+		cp->lstate = link_down;
+		if (link_transition_timeout != 0 &&
+		    cp->link_transition != LINK_TRANSITION_REQUESTED_RESET &&
+		    !cp->link_transition_jiffies_valid) {
+			/*
+			 * force a reset, as a workaround for the 
+			 * link-failure problem. May want to move this to a 
+			 * point a bit earlier in the sequence. If we had
+			 * generated a reset a short time ago, we'll wait for
+			 * the link timer to check the status until a
+			 * timer expires (link_transistion_jiffies_valid is
+			 * true when the timer is running.)  Instead of using
+			 * a system timer, we just do a check whenever the
+			 * link timer is running - this clears the flag after
+			 * a suitable delay.
+			 */
+			retval = 1;
+			cp->link_transition = LINK_TRANSITION_REQUESTED_RESET;
+			cp->link_transition_jiffies = jiffies;
+			cp->link_transition_jiffies_valid = 1;
+		} else {
+			cp->link_transition = LINK_TRANSITION_ON_FAILURE;
+		}
+		netif_carrier_off(cp->dev);
+		if (cp->opened && netif_msg_link(cp)) {
+			printk(KERN_INFO "%s: PCS link down.\n",
+			       cp->dev->name);
+		}
+
+		/* Cassini only: if you force a mode, there can be
+		 * sync problems on link down. to fix that, the following
+		 * things need to be checked:
+		 * 1) read serialink state register
+		 * 2) read pcs status register to verify link down.
+		 * 3) if link down and serial link == 0x03, then you need
+		 *    to global reset the chip.
+		 */
+		if ((cp->cas_flags & CAS_FLAG_REG_PLUS) == 0) {
+			/* should check to see if we're in a forced mode */
+			stat = readl(cp->regs + REG_PCS_SERDES_STATE);
+			if (stat == 0x03)
+				return 1;
+		}
+	} else if (cp->lstate == link_down) {
+		if (link_transition_timeout != 0 &&
+		    cp->link_transition != LINK_TRANSITION_REQUESTED_RESET &&
+		    !cp->link_transition_jiffies_valid) {
+			/* force a reset, as a workaround for the
+			 * link-failure problem.  May want to move
+			 * this to a point a bit earlier in the
+			 * sequence.
+			 */
+			retval = 1;
+			cp->link_transition = LINK_TRANSITION_REQUESTED_RESET;
+			cp->link_transition_jiffies = jiffies;
+			cp->link_transition_jiffies_valid = 1;
+		} else {
+			cp->link_transition = LINK_TRANSITION_STILL_FAILED;
+		}
+	}
+
+	return retval;
+}
+
+static int cas_pcs_interrupt(struct net_device *dev, 
+			     struct cas *cp, u32 status)
+{
+	u32 stat = readl(cp->regs + REG_PCS_INTR_STATUS);
+
+	if ((stat & PCS_INTR_STATUS_LINK_CHANGE) == 0) 
+		return 0;
+	return cas_pcs_link_check(cp);
+}
+
+static int cas_txmac_interrupt(struct net_device *dev, 
+			       struct cas *cp, u32 status)
+{
+	u32 txmac_stat = readl(cp->regs + REG_MAC_TX_STATUS);
+
+	if (!txmac_stat)
+		return 0;
+
+	if (netif_msg_intr(cp))
+		printk(KERN_DEBUG "%s: txmac interrupt, txmac_stat: 0x%x\n",
+			cp->dev->name, txmac_stat);
+
+	/* Defer timer expiration is quite normal,
+	 * don't even log the event.
+	 */
+	if ((txmac_stat & MAC_TX_DEFER_TIMER) &&
+	    !(txmac_stat & ~MAC_TX_DEFER_TIMER))
+		return 0;
+
+	spin_lock(&cp->stat_lock[0]);
+	if (txmac_stat & MAC_TX_UNDERRUN) {
+		printk(KERN_ERR "%s: TX MAC xmit underrun.\n",
+		       dev->name);
+		cp->net_stats[0].tx_fifo_errors++;
+	}
+
+	if (txmac_stat & MAC_TX_MAX_PACKET_ERR) {
+		printk(KERN_ERR "%s: TX MAC max packet size error.\n",
+		       dev->name);
+		cp->net_stats[0].tx_errors++;
+	}
+
+	/* The rest are all cases of one of the 16-bit TX
+	 * counters expiring.
+	 */
+	if (txmac_stat & MAC_TX_COLL_NORMAL)
+		cp->net_stats[0].collisions += 0x10000;
+
+	if (txmac_stat & MAC_TX_COLL_EXCESS) {
+		cp->net_stats[0].tx_aborted_errors += 0x10000;
+		cp->net_stats[0].collisions += 0x10000;
+	}
+
+	if (txmac_stat & MAC_TX_COLL_LATE) {
+		cp->net_stats[0].tx_aborted_errors += 0x10000;
+		cp->net_stats[0].collisions += 0x10000;
+	}
+	spin_unlock(&cp->stat_lock[0]);
+
+	/* We do not keep track of MAC_TX_COLL_FIRST and
+	 * MAC_TX_PEAK_ATTEMPTS events.
+	 */
+	return 0;
+}
+
+static void cas_load_firmware(struct cas *cp, cas_hp_inst_t *firmware) 
+{
+	cas_hp_inst_t *inst;
+	u32 val;
+	int i;
+
+	i = 0;
+	while ((inst = firmware) && inst->note) {
+		writel(i, cp->regs + REG_HP_INSTR_RAM_ADDR);
+
+		val = CAS_BASE(HP_INSTR_RAM_HI_VAL, inst->val);
+		val |= CAS_BASE(HP_INSTR_RAM_HI_MASK, inst->mask);
+		writel(val, cp->regs + REG_HP_INSTR_RAM_DATA_HI);
+
+		val = CAS_BASE(HP_INSTR_RAM_MID_OUTARG, inst->outarg >> 10);
+		val |= CAS_BASE(HP_INSTR_RAM_MID_OUTOP, inst->outop);
+		val |= CAS_BASE(HP_INSTR_RAM_MID_FNEXT, inst->fnext);
+		val |= CAS_BASE(HP_INSTR_RAM_MID_FOFF, inst->foff);
+		val |= CAS_BASE(HP_INSTR_RAM_MID_SNEXT, inst->snext);
+		val |= CAS_BASE(HP_INSTR_RAM_MID_SOFF, inst->soff);
+		val |= CAS_BASE(HP_INSTR_RAM_MID_OP, inst->op);
+		writel(val, cp->regs + REG_HP_INSTR_RAM_DATA_MID);
+
+		val = CAS_BASE(HP_INSTR_RAM_LOW_OUTMASK, inst->outmask);
+		val |= CAS_BASE(HP_INSTR_RAM_LOW_OUTSHIFT, inst->outshift);
+		val |= CAS_BASE(HP_INSTR_RAM_LOW_OUTEN, inst->outenab);
+		val |= CAS_BASE(HP_INSTR_RAM_LOW_OUTARG, inst->outarg);
+		writel(val, cp->regs + REG_HP_INSTR_RAM_DATA_LOW);
+		++firmware;
+		++i;
+	}
+}
+
+static void cas_init_rx_dma(struct cas *cp)
+{
+	u64 desc_dma = cp->block_dvma; 
+	u32 val;
+	int i, size;
+
+	/* rx free descriptors */
+	val = CAS_BASE(RX_CFG_SWIVEL, RX_SWIVEL_OFF_VAL); 
+	val |= CAS_BASE(RX_CFG_DESC_RING, RX_DESC_RINGN_INDEX(0));
+	val |= CAS_BASE(RX_CFG_COMP_RING, RX_COMP_RINGN_INDEX(0));
+	if ((N_RX_DESC_RINGS > 1) &&
+	    (cp->cas_flags & CAS_FLAG_REG_PLUS))  /* do desc 2 */
+		val |= CAS_BASE(RX_CFG_DESC_RING1, RX_DESC_RINGN_INDEX(1));
+	writel(val, cp->regs + REG_RX_CFG);
+
+	val = (unsigned long) cp->init_rxds[0] - 
+		(unsigned long) cp->init_block;
+	writel((desc_dma + val) >> 32, cp->regs + REG_RX_DB_HI);
+	writel((desc_dma + val) & 0xffffffff, cp->regs + REG_RX_DB_LOW);
+	writel(RX_DESC_RINGN_SIZE(0) - 4, cp->regs + REG_RX_KICK);
+
+	if (cp->cas_flags & CAS_FLAG_REG_PLUS) {
+		/* rx desc 2 is for IPSEC packets. however, 
+		 * we don't it that for that purpose.
+		 */
+		val = (unsigned long) cp->init_rxds[1] - 
+			(unsigned long) cp->init_block;
+		writel((desc_dma + val) >> 32, cp->regs + REG_PLUS_RX_DB1_HI);
+		writel((desc_dma + val) & 0xffffffff, cp->regs + 
+		       REG_PLUS_RX_DB1_LOW);
+		writel(RX_DESC_RINGN_SIZE(1) - 4, cp->regs + 
+		       REG_PLUS_RX_KICK1);
+	}
+	
+	/* rx completion registers */
+	val = (unsigned long) cp->init_rxcs[0] - 
+		(unsigned long) cp->init_block;
+	writel((desc_dma + val) >> 32, cp->regs + REG_RX_CB_HI);
+	writel((desc_dma + val) & 0xffffffff, cp->regs + REG_RX_CB_LOW);
+
+	if (cp->cas_flags & CAS_FLAG_REG_PLUS) {
+		/* rx comp 2-4 */
+		for (i = 1; i < MAX_RX_COMP_RINGS; i++) {
+			val = (unsigned long) cp->init_rxcs[i] - 
+				(unsigned long) cp->init_block;
+			writel((desc_dma + val) >> 32, cp->regs + 
+			       REG_PLUS_RX_CBN_HI(i));
+			writel((desc_dma + val) & 0xffffffff, cp->regs + 
+			       REG_PLUS_RX_CBN_LOW(i));
+		}
+	}
+
+	/* read selective clear regs to prevent spurious interrupts
+	 * on reset because complete == kick.
+	 * selective clear set up to prevent interrupts on resets
+	 */
+	readl(cp->regs + REG_INTR_STATUS_ALIAS);
+	writel(INTR_RX_DONE | INTR_RX_BUF_UNAVAIL, cp->regs + REG_ALIAS_CLEAR);
+	if (cp->cas_flags & CAS_FLAG_REG_PLUS) {
+		for (i = 1; i < N_RX_COMP_RINGS; i++)
+			readl(cp->regs + REG_PLUS_INTRN_STATUS_ALIAS(i));
+
+		/* 2 is different from 3 and 4 */
+		if (N_RX_COMP_RINGS > 1)
+			writel(INTR_RX_DONE_ALT | INTR_RX_BUF_UNAVAIL_1, 
+			       cp->regs + REG_PLUS_ALIASN_CLEAR(1));
+
+		for (i = 2; i < N_RX_COMP_RINGS; i++) 
+			writel(INTR_RX_DONE_ALT, 
+			       cp->regs + REG_PLUS_ALIASN_CLEAR(i));
+	}
+
+	/* set up pause thresholds */
+	val  = CAS_BASE(RX_PAUSE_THRESH_OFF,
+			cp->rx_pause_off / RX_PAUSE_THRESH_QUANTUM);
+	val |= CAS_BASE(RX_PAUSE_THRESH_ON, 
+			cp->rx_pause_on / RX_PAUSE_THRESH_QUANTUM);
+	writel(val, cp->regs + REG_RX_PAUSE_THRESH);
+	
+	/* zero out dma reassembly buffers */
+	for (i = 0; i < 64; i++) {
+		writel(i, cp->regs + REG_RX_TABLE_ADDR);
+		writel(0x0, cp->regs + REG_RX_TABLE_DATA_LOW);
+		writel(0x0, cp->regs + REG_RX_TABLE_DATA_MID);
+		writel(0x0, cp->regs + REG_RX_TABLE_DATA_HI);
+	}
+
+	/* make sure address register is 0 for normal operation */
+	writel(0x0, cp->regs + REG_RX_CTRL_FIFO_ADDR);
+	writel(0x0, cp->regs + REG_RX_IPP_FIFO_ADDR);
+
+	/* interrupt mitigation */
+#ifdef USE_RX_BLANK
+	val = CAS_BASE(RX_BLANK_INTR_TIME, RX_BLANK_INTR_TIME_VAL);
+	val |= CAS_BASE(RX_BLANK_INTR_PKT, RX_BLANK_INTR_PKT_VAL);
+	writel(val, cp->regs + REG_RX_BLANK);
+#else
+	writel(0x0, cp->regs + REG_RX_BLANK);
+#endif
+
+	/* interrupt generation as a function of low water marks for
+	 * free desc and completion entries. these are used to trigger
+	 * housekeeping for rx descs. we don't use the free interrupt
+	 * as it's not very useful
+	 */
+	/* val = CAS_BASE(RX_AE_THRESH_FREE, RX_AE_FREEN_VAL(0)); */
+	val = CAS_BASE(RX_AE_THRESH_COMP, RX_AE_COMP_VAL);
+	writel(val, cp->regs + REG_RX_AE_THRESH);
+	if (cp->cas_flags & CAS_FLAG_REG_PLUS) {
+		val = CAS_BASE(RX_AE1_THRESH_FREE, RX_AE_FREEN_VAL(1));
+		writel(val, cp->regs + REG_PLUS_RX_AE1_THRESH);
+	}
+
+	/* Random early detect registers. useful for congestion avoidance.
+	 * this should be tunable.
+	 */
+	writel(0x0, cp->regs + REG_RX_RED);
+	
+	/* receive page sizes. default == 2K (0x800) */
+	val = 0;
+	if (cp->page_size == 0x1000)
+		val = 0x1;
+	else if (cp->page_size == 0x2000)
+		val = 0x2;
+	else if (cp->page_size == 0x4000)
+		val = 0x3;
+	
+	/* round mtu + offset. constrain to page size. */
+	size = cp->dev->mtu + 64;
+	if (size > cp->page_size)
+		size = cp->page_size;
+
+	if (size <= 0x400)
+		i = 0x0;
+	else if (size <= 0x800)
+		i = 0x1;
+	else if (size <= 0x1000)
+		i = 0x2;
+	else
+		i = 0x3;
+
+	cp->mtu_stride = 1 << (i + 10);
+	val  = CAS_BASE(RX_PAGE_SIZE, val);
+	val |= CAS_BASE(RX_PAGE_SIZE_MTU_STRIDE, i); 
+	val |= CAS_BASE(RX_PAGE_SIZE_MTU_COUNT, cp->page_size >> (i + 10));
+	val |= CAS_BASE(RX_PAGE_SIZE_MTU_OFF, 0x1);
+	writel(val, cp->regs + REG_RX_PAGE_SIZE);
+	
+	/* enable the header parser if desired */
+	if (CAS_HP_FIRMWARE == cas_prog_null)
+		return;
+
+	val = CAS_BASE(HP_CFG_NUM_CPU, CAS_NCPUS > 63 ? 0 : CAS_NCPUS);
+	val |= HP_CFG_PARSE_EN | HP_CFG_SYN_INC_MASK;
+	val |= CAS_BASE(HP_CFG_TCP_THRESH, HP_TCP_THRESH_VAL);
+	writel(val, cp->regs + REG_HP_CFG);
+}
+
+static inline void cas_rxc_init(struct cas_rx_comp *rxc)
+{
+	memset(rxc, 0, sizeof(*rxc));
+	rxc->word4 = cpu_to_le64(RX_COMP4_ZERO); 
+}
+
+/* NOTE: we use the ENC RX DESC ring for spares. the rx_page[0,1]
+ * flipping is protected by the fact that the chip will not
+ * hand back the same page index while it's being processed.
+ */
+static inline cas_page_t *cas_page_spare(struct cas *cp, const int index)
+{
+	cas_page_t *page = cp->rx_pages[1][index];
+	cas_page_t *new;
+
+	if (page_count(page->buffer) == 1)
+		return page;
+
+	new = cas_page_dequeue(cp);
+	if (new) {
+		spin_lock(&cp->rx_inuse_lock);
+		list_add(&page->list, &cp->rx_inuse_list);
+		spin_unlock(&cp->rx_inuse_lock);
+	}
+	return new;
+}
+				   
+/* this needs to be changed if we actually use the ENC RX DESC ring */
+static cas_page_t *cas_page_swap(struct cas *cp, const int ring, 
+				 const int index)
+{
+	cas_page_t **page0 = cp->rx_pages[0];
+	cas_page_t **page1 = cp->rx_pages[1];
+
+	/* swap if buffer is in use */
+	if (page_count(page0[index]->buffer) > 1) {
+		cas_page_t *new = cas_page_spare(cp, index);
+		if (new) {
+			page1[index] = page0[index];
+			page0[index] = new;
+		}
+	} 
+	RX_USED_SET(page0[index], 0);
+	return page0[index];
+}
+
+static void cas_clean_rxds(struct cas *cp)
+{
+	/* only clean ring 0 as ring 1 is used for spare buffers */
+        struct cas_rx_desc *rxd = cp->init_rxds[0];
+	int i, size;
+
+	/* release all rx flows */
+	for (i = 0; i < N_RX_FLOWS; i++) {
+		struct sk_buff *skb;
+		while ((skb = __skb_dequeue(&cp->rx_flows[i]))) {
+			cas_skb_release(skb);
+		}
+	}
+
+	/* initialize descriptors */
+	size = RX_DESC_RINGN_SIZE(0);
+	for (i = 0; i < size; i++) {
+		cas_page_t *page = cas_page_swap(cp, 0, i);
+		rxd[i].buffer = cpu_to_le64(page->dma_addr);
+		rxd[i].index  = cpu_to_le64(CAS_BASE(RX_INDEX_NUM, i) | 
+					    CAS_BASE(RX_INDEX_RING, 0));
+	}
+
+	cp->rx_old[0]  = RX_DESC_RINGN_SIZE(0) - 4; 
+	cp->rx_last[0] = 0;
+	cp->cas_flags &= ~CAS_FLAG_RXD_POST(0);
+}
+
+static void cas_clean_rxcs(struct cas *cp)
+{
+	int i, j;
+
+	/* take ownership of rx comp descriptors */
+	memset(cp->rx_cur, 0, sizeof(*cp->rx_cur)*N_RX_COMP_RINGS);
+	memset(cp->rx_new, 0, sizeof(*cp->rx_new)*N_RX_COMP_RINGS);
+	for (i = 0; i < N_RX_COMP_RINGS; i++) {
+		struct cas_rx_comp *rxc = cp->init_rxcs[i];
+		for (j = 0; j < RX_COMP_RINGN_SIZE(i); j++) {
+			cas_rxc_init(rxc + j);
+		}
+	}
+}
+
+#if 0
+/* When we get a RX fifo overflow, the RX unit is probably hung
+ * so we do the following.
+ *
+ * If any part of the reset goes wrong, we return 1 and that causes the
+ * whole chip to be reset.
+ */
+static int cas_rxmac_reset(struct cas *cp)
+{
+	struct net_device *dev = cp->dev;
+	int limit;
+	u32 val;
+
+	/* First, reset MAC RX. */
+	writel(cp->mac_rx_cfg & ~MAC_RX_CFG_EN, cp->regs + REG_MAC_RX_CFG);
+	for (limit = 0; limit < STOP_TRIES; limit++) {
+		if (!(readl(cp->regs + REG_MAC_RX_CFG) & MAC_RX_CFG_EN))
+			break;
+		udelay(10);
+	}
+	if (limit == STOP_TRIES) {
+		printk(KERN_ERR "%s: RX MAC will not disable, resetting whole "
+		       "chip.\n", dev->name);
+		return 1;
+	}
+
+	/* Second, disable RX DMA. */
+	writel(0, cp->regs + REG_RX_CFG);
+	for (limit = 0; limit < STOP_TRIES; limit++) {
+		if (!(readl(cp->regs + REG_RX_CFG) & RX_CFG_DMA_EN))
+			break;
+		udelay(10);
+	}
+	if (limit == STOP_TRIES) {
+		printk(KERN_ERR "%s: RX DMA will not disable, resetting whole "
+		       "chip.\n", dev->name);
+		return 1;
+	}
+
+	mdelay(5);
+
+	/* Execute RX reset command. */
+	writel(SW_RESET_RX, cp->regs + REG_SW_RESET);
+	for (limit = 0; limit < STOP_TRIES; limit++) {
+		if (!(readl(cp->regs + REG_SW_RESET) & SW_RESET_RX))
+			break;
+		udelay(10);
+	}
+	if (limit == STOP_TRIES) {
+		printk(KERN_ERR "%s: RX reset command will not execute, "
+		       "resetting whole chip.\n", dev->name);
+		return 1;
+	}
+
+	/* reset driver rx state */
+	cas_clean_rxds(cp);
+	cas_clean_rxcs(cp);
+
+	/* Now, reprogram the rest of RX unit. */
+	cas_init_rx_dma(cp);
+
+	/* re-enable */
+	val = readl(cp->regs + REG_RX_CFG);
+	writel(val | RX_CFG_DMA_EN, cp->regs + REG_RX_CFG);
+	writel(MAC_RX_FRAME_RECV, cp->regs + REG_MAC_RX_MASK);
+	val = readl(cp->regs + REG_MAC_RX_CFG);
+	writel(val | MAC_RX_CFG_EN, cp->regs + REG_MAC_RX_CFG);
+	return 0;
+}
+#endif
+
+static int cas_rxmac_interrupt(struct net_device *dev, struct cas *cp,
+			       u32 status)
+{
+	u32 stat = readl(cp->regs + REG_MAC_RX_STATUS);
+
+	if (!stat)
+		return 0;
+
+	if (netif_msg_intr(cp))
+		printk(KERN_DEBUG "%s: rxmac interrupt, stat: 0x%x\n",
+			cp->dev->name, stat);
+
+	/* these are all rollovers */
+	spin_lock(&cp->stat_lock[0]);
+	if (stat & MAC_RX_ALIGN_ERR) 
+		cp->net_stats[0].rx_frame_errors += 0x10000;
+
+	if (stat & MAC_RX_CRC_ERR)
+		cp->net_stats[0].rx_crc_errors += 0x10000;
+
+	if (stat & MAC_RX_LEN_ERR)
+		cp->net_stats[0].rx_length_errors += 0x10000;
+
+	if (stat & MAC_RX_OVERFLOW) {
+		cp->net_stats[0].rx_over_errors++;
+		cp->net_stats[0].rx_fifo_errors++;
+	}
+
+	/* We do not track MAC_RX_FRAME_COUNT and MAC_RX_VIOL_ERR
+	 * events.
+	 */
+	spin_unlock(&cp->stat_lock[0]);
+	return 0;
+}
+
+static int cas_mac_interrupt(struct net_device *dev, struct cas *cp,
+			     u32 status)
+{
+	u32 stat = readl(cp->regs + REG_MAC_CTRL_STATUS);
+
+	if (!stat)
+		return 0;
+
+	if (netif_msg_intr(cp))
+		printk(KERN_DEBUG "%s: mac interrupt, stat: 0x%x\n",
+			cp->dev->name, stat);
+
+	/* This interrupt is just for pause frame and pause
+	 * tracking.  It is useful for diagnostics and debug
+	 * but probably by default we will mask these events.
+	 */
+	if (stat & MAC_CTRL_PAUSE_STATE)
+		cp->pause_entered++;
+
+	if (stat & MAC_CTRL_PAUSE_RECEIVED)
+		cp->pause_last_time_recvd = (stat >> 16);
+
+	return 0;
+}
+
+	
+/* Must be invoked under cp->lock. */
+static inline int cas_mdio_link_not_up(struct cas *cp)
+{
+	u16 val;
+	
+	switch (cp->lstate) {
+	case link_force_ret:
+		if (netif_msg_link(cp))
+			printk(KERN_INFO "%s: Autoneg failed again, keeping"
+				" forced mode\n", cp->dev->name);
+		cas_phy_write(cp, MII_BMCR, cp->link_fcntl);
+		cp->timer_ticks = 5;
+		cp->lstate = link_force_ok;
+		cp->link_transition = LINK_TRANSITION_LINK_CONFIG;
+		break;
+		
+	case link_aneg:
+		val = cas_phy_read(cp, MII_BMCR);
+
+		/* Try forced modes. we try things in the following order:
+		 * 1000 full -> 100 full/half -> 10 half
+		 */
+		val &= ~(BMCR_ANRESTART | BMCR_ANENABLE);
+		val |= BMCR_FULLDPLX;
+		val |= (cp->cas_flags & CAS_FLAG_1000MB_CAP) ? 
+			CAS_BMCR_SPEED1000 : BMCR_SPEED100;
+		cas_phy_write(cp, MII_BMCR, val);
+		cp->timer_ticks = 5;
+		cp->lstate = link_force_try;
+		cp->link_transition = LINK_TRANSITION_LINK_CONFIG;
+		break;
+
+	case link_force_try:
+		/* Downgrade from 1000 to 100 to 10 Mbps if necessary. */
+		val = cas_phy_read(cp, MII_BMCR);
+		cp->timer_ticks = 5;
+		if (val & CAS_BMCR_SPEED1000) { /* gigabit */
+			val &= ~CAS_BMCR_SPEED1000;
+			val |= (BMCR_SPEED100 | BMCR_FULLDPLX);
+			cas_phy_write(cp, MII_BMCR, val);
+			break;
+		}
+
+		if (val & BMCR_SPEED100) {
+			if (val & BMCR_FULLDPLX) /* fd failed */
+				val &= ~BMCR_FULLDPLX;
+			else { /* 100Mbps failed */
+				val &= ~BMCR_SPEED100;
+			}
+			cas_phy_write(cp, MII_BMCR, val);
+			break;
+		}
+	default:
+		break;
+	}
+	return 0;
+}
+
+
+/* must be invoked with cp->lock held */
+static int cas_mii_link_check(struct cas *cp, const u16 bmsr)
+{
+	int restart;
+
+	if (bmsr & BMSR_LSTATUS) {
+		/* Ok, here we got a link. If we had it due to a forced
+		 * fallback, and we were configured for autoneg, we 
+		 * retry a short autoneg pass. If you know your hub is
+		 * broken, use ethtool ;)
+		 */
+		if ((cp->lstate == link_force_try) && 
+		    (cp->link_cntl & BMCR_ANENABLE)) {
+			cp->lstate = link_force_ret;
+			cp->link_transition = LINK_TRANSITION_LINK_CONFIG;
+			cas_mif_poll(cp, 0);
+			cp->link_fcntl = cas_phy_read(cp, MII_BMCR);
+			cp->timer_ticks = 5;
+			if (cp->opened && netif_msg_link(cp))
+				printk(KERN_INFO "%s: Got link after fallback, retrying"
+				       " autoneg once...\n", cp->dev->name);
+			cas_phy_write(cp, MII_BMCR,
+				      cp->link_fcntl | BMCR_ANENABLE |
+				      BMCR_ANRESTART);
+			cas_mif_poll(cp, 1);
+
+		} else if (cp->lstate != link_up) {
+			cp->lstate = link_up;
+			cp->link_transition = LINK_TRANSITION_LINK_UP;
+
+			if (cp->opened) {
+				cas_set_link_modes(cp);
+				netif_carrier_on(cp->dev);
+			}
+		}
+		return 0;
+	}
+
+	/* link not up. if the link was previously up, we restart the
+	 * whole process
+	 */
+	restart = 0;
+	if (cp->lstate == link_up) {
+		cp->lstate = link_down;
+		cp->link_transition = LINK_TRANSITION_LINK_DOWN;
+
+		netif_carrier_off(cp->dev);
+		if (cp->opened && netif_msg_link(cp))
+			printk(KERN_INFO "%s: Link down\n",
+			       cp->dev->name);
+		restart = 1;
+		
+	} else if (++cp->timer_ticks > 10)
+		cas_mdio_link_not_up(cp);
+		
+	return restart;
+}
+
+static int cas_mif_interrupt(struct net_device *dev, struct cas *cp,
+			     u32 status)
+{
+	u32 stat = readl(cp->regs + REG_MIF_STATUS);
+	u16 bmsr;
+
+	/* check for a link change */
+	if (CAS_VAL(MIF_STATUS_POLL_STATUS, stat) == 0)
+		return 0;
+
+	bmsr = CAS_VAL(MIF_STATUS_POLL_DATA, stat);
+	return cas_mii_link_check(cp, bmsr);
+}
+
+static int cas_pci_interrupt(struct net_device *dev, struct cas *cp,
+			     u32 status)
+{
+	u32 stat = readl(cp->regs + REG_PCI_ERR_STATUS);
+
+	if (!stat)
+		return 0;
+
+	printk(KERN_ERR "%s: PCI error [%04x:%04x] ", dev->name, stat,
+	       readl(cp->regs + REG_BIM_DIAG));
+
+	/* cassini+ has this reserved */
+	if ((stat & PCI_ERR_BADACK) &&
+	    ((cp->cas_flags & CAS_FLAG_REG_PLUS) == 0))
+		printk("<No ACK64# during ABS64 cycle> ");
+
+	if (stat & PCI_ERR_DTRTO)
+		printk("<Delayed transaction timeout> ");
+	if (stat & PCI_ERR_OTHER)
+		printk("<other> ");
+	if (stat & PCI_ERR_BIM_DMA_WRITE)
+		printk("<BIM DMA 0 write req> ");
+	if (stat & PCI_ERR_BIM_DMA_READ)
+		printk("<BIM DMA 0 read req> ");
+	printk("\n");
+
+	if (stat & PCI_ERR_OTHER) {
+		u16 cfg;
+
+		/* Interrogate PCI config space for the
+		 * true cause.
+		 */
+		pci_read_config_word(cp->pdev, PCI_STATUS, &cfg);
+		printk(KERN_ERR "%s: Read PCI cfg space status [%04x]\n",
+		       dev->name, cfg);
+		if (cfg & PCI_STATUS_PARITY)
+			printk(KERN_ERR "%s: PCI parity error detected.\n",
+			       dev->name);
+		if (cfg & PCI_STATUS_SIG_TARGET_ABORT)
+			printk(KERN_ERR "%s: PCI target abort.\n",
+			       dev->name);
+		if (cfg & PCI_STATUS_REC_TARGET_ABORT)
+			printk(KERN_ERR "%s: PCI master acks target abort.\n",
+			       dev->name);
+		if (cfg & PCI_STATUS_REC_MASTER_ABORT)
+			printk(KERN_ERR "%s: PCI master abort.\n", dev->name);
+		if (cfg & PCI_STATUS_SIG_SYSTEM_ERROR)
+			printk(KERN_ERR "%s: PCI system error SERR#.\n",
+			       dev->name);
+		if (cfg & PCI_STATUS_DETECTED_PARITY)
+			printk(KERN_ERR "%s: PCI parity error.\n",
+			       dev->name);
+
+		/* Write the error bits back to clear them. */
+		cfg &= (PCI_STATUS_PARITY |
+			PCI_STATUS_SIG_TARGET_ABORT |
+			PCI_STATUS_REC_TARGET_ABORT |
+			PCI_STATUS_REC_MASTER_ABORT |
+			PCI_STATUS_SIG_SYSTEM_ERROR |
+			PCI_STATUS_DETECTED_PARITY);
+		pci_write_config_word(cp->pdev, PCI_STATUS, cfg);
+	}
+
+	/* For all PCI errors, we should reset the chip. */
+	return 1;
+}
+
+/* All non-normal interrupt conditions get serviced here.
+ * Returns non-zero if we should just exit the interrupt
+ * handler right now (ie. if we reset the card which invalidates
+ * all of the other original irq status bits).
+ */
+static int cas_abnormal_irq(struct net_device *dev, struct cas *cp,
+			    u32 status)
+{
+	if (status & INTR_RX_TAG_ERROR) {
+		/* corrupt RX tag framing */
+		if (netif_msg_rx_err(cp))
+			printk(KERN_DEBUG "%s: corrupt rx tag framing\n",
+				cp->dev->name);
+		spin_lock(&cp->stat_lock[0]);
+		cp->net_stats[0].rx_errors++;
+		spin_unlock(&cp->stat_lock[0]);
+		goto do_reset;
+	}
+
+	if (status & INTR_RX_LEN_MISMATCH) {
+		/* length mismatch. */
+		if (netif_msg_rx_err(cp))
+			printk(KERN_DEBUG "%s: length mismatch for rx frame\n",
+				cp->dev->name);
+		spin_lock(&cp->stat_lock[0]);
+		cp->net_stats[0].rx_errors++;
+		spin_unlock(&cp->stat_lock[0]);
+		goto do_reset;
+	}
+
+	if (status & INTR_PCS_STATUS) {
+		if (cas_pcs_interrupt(dev, cp, status))
+			goto do_reset;
+	}
+
+	if (status & INTR_TX_MAC_STATUS) {
+		if (cas_txmac_interrupt(dev, cp, status))
+			goto do_reset;
+	}
+
+	if (status & INTR_RX_MAC_STATUS) {
+		if (cas_rxmac_interrupt(dev, cp, status))
+			goto do_reset;
+	}
+
+	if (status & INTR_MAC_CTRL_STATUS) {
+		if (cas_mac_interrupt(dev, cp, status))
+			goto do_reset;
+	}
+
+	if (status & INTR_MIF_STATUS) {
+		if (cas_mif_interrupt(dev, cp, status))
+			goto do_reset;
+	}
+
+	if (status & INTR_PCI_ERROR_STATUS) {
+		if (cas_pci_interrupt(dev, cp, status))
+			goto do_reset;
+	}
+	return 0;
+
+do_reset:
+#if 1
+	atomic_inc(&cp->reset_task_pending);
+	atomic_inc(&cp->reset_task_pending_all);
+	printk(KERN_ERR "%s:reset called in cas_abnormal_irq [0x%x]\n",
+	       dev->name, status);
+	schedule_work(&cp->reset_task);
+#else
+	atomic_set(&cp->reset_task_pending, CAS_RESET_ALL);
+	printk(KERN_ERR "reset called in cas_abnormal_irq\n");
+	schedule_work(&cp->reset_task);
+#endif
+	return 1;
+}
+
+/* NOTE: CAS_TABORT returns 1 or 2 so that it can be used when
+ *       determining whether to do a netif_stop/wakeup
+ */
+#define CAS_TABORT(x)      (((x)->cas_flags & CAS_FLAG_TARGET_ABORT) ? 2 : 1)
+#define CAS_ROUND_PAGE(x)  (((x) + PAGE_SIZE - 1) & PAGE_MASK)
+static inline int cas_calc_tabort(struct cas *cp, const unsigned long addr,
+				  const int len)
+{
+	unsigned long off = addr + len;
+
+	if (CAS_TABORT(cp) == 1)
+		return 0;
+	if ((CAS_ROUND_PAGE(off) - off) > TX_TARGET_ABORT_LEN)
+		return 0;
+	return TX_TARGET_ABORT_LEN;
+}
+
+static inline void cas_tx_ringN(struct cas *cp, int ring, int limit)
+{
+	struct cas_tx_desc *txds;
+	struct sk_buff **skbs;
+	struct net_device *dev = cp->dev;
+	int entry, count;
+
+	spin_lock(&cp->tx_lock[ring]);
+	txds = cp->init_txds[ring];
+	skbs = cp->tx_skbs[ring];
+	entry = cp->tx_old[ring];
+
+	count = TX_BUFF_COUNT(ring, entry, limit);
+	while (entry != limit) {
+		struct sk_buff *skb = skbs[entry];
+		dma_addr_t daddr;
+		u32 dlen;
+		int frag;
+
+		if (!skb) {
+			/* this should never occur */
+			entry = TX_DESC_NEXT(ring, entry);
+			continue;
+		}
+
+		/* however, we might get only a partial skb release. */
+		count -= skb_shinfo(skb)->nr_frags +
+			+ cp->tx_tiny_use[ring][entry].nbufs + 1;
+		if (count < 0)
+			break;
+
+		if (netif_msg_tx_done(cp))
+			printk(KERN_DEBUG "%s: tx[%d] done, slot %d\n",
+			       cp->dev->name, ring, entry);
+
+		skbs[entry] = NULL;
+		cp->tx_tiny_use[ring][entry].nbufs = 0;
+		
+		for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) {
+			struct cas_tx_desc *txd = txds + entry;
+
+			daddr = le64_to_cpu(txd->buffer);
+			dlen = CAS_VAL(TX_DESC_BUFLEN,
+				       le64_to_cpu(txd->control));
+			pci_unmap_page(cp->pdev, daddr, dlen,
+				       PCI_DMA_TODEVICE);
+			entry = TX_DESC_NEXT(ring, entry);
+
+			/* tiny buffer may follow */
+			if (cp->tx_tiny_use[ring][entry].used) {
+				cp->tx_tiny_use[ring][entry].used = 0;
+				entry = TX_DESC_NEXT(ring, entry);
+			} 
+		}
+
+		spin_lock(&cp->stat_lock[ring]);
+		cp->net_stats[ring].tx_packets++;
+		cp->net_stats[ring].tx_bytes += skb->len;
+		spin_unlock(&cp->stat_lock[ring]);
+		dev_kfree_skb_irq(skb);
+	}
+	cp->tx_old[ring] = entry;
+
+	/* this is wrong for multiple tx rings. the net device needs
+	 * multiple queues for this to do the right thing.  we wait
+	 * for 2*packets to be available when using tiny buffers
+	 */
+	if (netif_queue_stopped(dev) &&
+	    (TX_BUFFS_AVAIL(cp, ring) > CAS_TABORT(cp)*(MAX_SKB_FRAGS + 1)))
+		netif_wake_queue(dev);
+	spin_unlock(&cp->tx_lock[ring]);
+}
+
+static void cas_tx(struct net_device *dev, struct cas *cp,
+		   u32 status)
+{
+        int limit, ring;
+#ifdef USE_TX_COMPWB
+	u64 compwb = le64_to_cpu(cp->init_block->tx_compwb);
+#endif
+	if (netif_msg_intr(cp))
+		printk(KERN_DEBUG "%s: tx interrupt, status: 0x%x, %lx\n",
+			cp->dev->name, status, compwb);
+	/* process all the rings */
+	for (ring = 0; ring < N_TX_RINGS; ring++) {
+#ifdef USE_TX_COMPWB
+		/* use the completion writeback registers */
+		limit = (CAS_VAL(TX_COMPWB_MSB, compwb) << 8) |
+			CAS_VAL(TX_COMPWB_LSB, compwb);
+		compwb = TX_COMPWB_NEXT(compwb);
+#else
+		limit = readl(cp->regs + REG_TX_COMPN(ring));
+#endif
+		if (cp->tx_old[ring] != limit) 
+			cas_tx_ringN(cp, ring, limit);
+	}
+}
+
+
+static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, 
+			      int entry, const u64 *words, 
+			      struct sk_buff **skbref)
+{
+	int dlen, hlen, len, i, alloclen;
+	int off, swivel = RX_SWIVEL_OFF_VAL;
+	struct cas_page *page;
+	struct sk_buff *skb;
+	void *addr, *crcaddr;
+	char *p; 
+
+	hlen = CAS_VAL(RX_COMP2_HDR_SIZE, words[1]);
+	dlen = CAS_VAL(RX_COMP1_DATA_SIZE, words[0]);
+	len  = hlen + dlen;
+
+	if (RX_COPY_ALWAYS || (words[2] & RX_COMP3_SMALL_PKT)) 
+		alloclen = len;
+	else 
+		alloclen = max(hlen, RX_COPY_MIN);
+
+	skb = dev_alloc_skb(alloclen + swivel + cp->crc_size);
+	if (skb == NULL) 
+		return -1;
+
+	*skbref = skb;
+	skb->dev = cp->dev;
+	skb_reserve(skb, swivel);
+
+	p = skb->data;
+	addr = crcaddr = NULL;
+	if (hlen) { /* always copy header pages */
+		i = CAS_VAL(RX_COMP2_HDR_INDEX, words[1]);
+		page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)];
+		off = CAS_VAL(RX_COMP2_HDR_OFF, words[1]) * 0x100 + 
+			swivel;
+
+		i = hlen;
+		if (!dlen) /* attach FCS */
+			i += cp->crc_size;
+		pci_dma_sync_single_for_cpu(cp->pdev, page->dma_addr + off, i,
+				    PCI_DMA_FROMDEVICE);
+		addr = cas_page_map(page->buffer);
+		memcpy(p, addr + off, i);
+		pci_dma_sync_single_for_device(cp->pdev, page->dma_addr + off, i,
+				    PCI_DMA_FROMDEVICE);
+		cas_page_unmap(addr);
+		RX_USED_ADD(page, 0x100);
+		p += hlen;
+		swivel = 0;
+	} 
+
+
+	if (alloclen < (hlen + dlen)) {
+		skb_frag_t *frag = skb_shinfo(skb)->frags;
+
+		/* normal or jumbo packets. we use frags */
+		i = CAS_VAL(RX_COMP1_DATA_INDEX, words[0]);
+		page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)];
+		off = CAS_VAL(RX_COMP1_DATA_OFF, words[0]) + swivel;
+
+		hlen = min(cp->page_size - off, dlen);
+		if (hlen < 0) {
+			if (netif_msg_rx_err(cp)) {
+				printk(KERN_DEBUG "%s: rx page overflow: "
+				       "%d\n", cp->dev->name, hlen);
+			}
+			dev_kfree_skb_irq(skb);
+			return -1;
+		}
+		i = hlen;
+		if (i == dlen)  /* attach FCS */
+			i += cp->crc_size;
+		pci_dma_sync_single_for_cpu(cp->pdev, page->dma_addr + off, i,
+				    PCI_DMA_FROMDEVICE);
+
+		/* make sure we always copy a header */
+		swivel = 0;
+		if (p == (char *) skb->data) { /* not split */
+			addr = cas_page_map(page->buffer);
+			memcpy(p, addr + off, RX_COPY_MIN);
+			pci_dma_sync_single_for_device(cp->pdev, page->dma_addr + off, i,
+					PCI_DMA_FROMDEVICE);
+			cas_page_unmap(addr);
+			off += RX_COPY_MIN;
+			swivel = RX_COPY_MIN;
+			RX_USED_ADD(page, cp->mtu_stride);
+		} else {
+			RX_USED_ADD(page, hlen);
+		}
+		skb_put(skb, alloclen);
+
+		skb_shinfo(skb)->nr_frags++;
+		skb->data_len += hlen - swivel;
+		skb->len      += hlen - swivel;
+
+		get_page(page->buffer);
+		frag->page = page->buffer;
+		frag->page_offset = off;
+		frag->size = hlen - swivel;
+		
+		/* any more data? */
+		if ((words[0] & RX_COMP1_SPLIT_PKT) && ((dlen -= hlen) > 0)) {
+			hlen = dlen;
+			off = 0;
+
+			i = CAS_VAL(RX_COMP2_NEXT_INDEX, words[1]);
+			page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)];
+			pci_dma_sync_single_for_cpu(cp->pdev, page->dma_addr, 
+					    hlen + cp->crc_size, 
+					    PCI_DMA_FROMDEVICE);
+			pci_dma_sync_single_for_device(cp->pdev, page->dma_addr,
+					    hlen + cp->crc_size,
+					    PCI_DMA_FROMDEVICE);
+
+			skb_shinfo(skb)->nr_frags++;
+			skb->data_len += hlen;
+			skb->len      += hlen; 
+			frag++;
+
+			get_page(page->buffer);
+			frag->page = page->buffer;
+			frag->page_offset = 0;
+			frag->size = hlen;
+			RX_USED_ADD(page, hlen + cp->crc_size);
+		}
+
+		if (cp->crc_size) {
+			addr = cas_page_map(page->buffer);
+			crcaddr  = addr + off + hlen;
+		}
+
+	} else {
+		/* copying packet */
+		if (!dlen)
+			goto end_copy_pkt;
+
+		i = CAS_VAL(RX_COMP1_DATA_INDEX, words[0]);
+		page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)];
+		off = CAS_VAL(RX_COMP1_DATA_OFF, words[0]) + swivel;
+		hlen = min(cp->page_size - off, dlen);
+		if (hlen < 0) {
+			if (netif_msg_rx_err(cp)) {
+				printk(KERN_DEBUG "%s: rx page overflow: "
+				       "%d\n", cp->dev->name, hlen);
+			}
+			dev_kfree_skb_irq(skb);
+			return -1;
+		}
+		i = hlen;
+		if (i == dlen) /* attach FCS */
+			i += cp->crc_size;
+		pci_dma_sync_single_for_cpu(cp->pdev, page->dma_addr + off, i,
+				    PCI_DMA_FROMDEVICE);
+		addr = cas_page_map(page->buffer);
+		memcpy(p, addr + off, i);
+		pci_dma_sync_single_for_device(cp->pdev, page->dma_addr + off, i,
+				    PCI_DMA_FROMDEVICE);
+		cas_page_unmap(addr);
+		if (p == (char *) skb->data) /* not split */
+			RX_USED_ADD(page, cp->mtu_stride);
+		else
+			RX_USED_ADD(page, i);
+	
+		/* any more data? */
+		if ((words[0] & RX_COMP1_SPLIT_PKT) && ((dlen -= hlen) > 0)) {
+			p += hlen;
+			i = CAS_VAL(RX_COMP2_NEXT_INDEX, words[1]);
+			page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)];
+			pci_dma_sync_single_for_cpu(cp->pdev, page->dma_addr, 
+					    dlen + cp->crc_size, 
+					    PCI_DMA_FROMDEVICE);
+			addr = cas_page_map(page->buffer);
+			memcpy(p, addr, dlen + cp->crc_size);
+			pci_dma_sync_single_for_device(cp->pdev, page->dma_addr,
+					    dlen + cp->crc_size,
+					    PCI_DMA_FROMDEVICE);
+			cas_page_unmap(addr);
+			RX_USED_ADD(page, dlen + cp->crc_size); 
+		}
+end_copy_pkt:
+		if (cp->crc_size) {
+			addr    = NULL;
+			crcaddr = skb->data + alloclen;
+		}
+		skb_put(skb, alloclen);
+	}
+
+	i = CAS_VAL(RX_COMP4_TCP_CSUM, words[3]);
+	if (cp->crc_size) {
+		/* checksum includes FCS. strip it out. */
+		i = csum_fold(csum_partial(crcaddr, cp->crc_size, i));
+		if (addr)
+			cas_page_unmap(addr);
+	}
+	skb->csum = ntohs(i ^ 0xffff);
+	skb->ip_summed = CHECKSUM_HW;
+	skb->protocol = eth_type_trans(skb, cp->dev);
+	return len;
+}
+
+
+/* we can handle up to 64 rx flows at a time. we do the same thing
+ * as nonreassm except that we batch up the buffers. 
+ * NOTE: we currently just treat each flow as a bunch of packets that
+ *       we pass up. a better way would be to coalesce the packets
+ *       into a jumbo packet. to do that, we need to do the following:
+ *       1) the first packet will have a clean split between header and
+ *          data. save both.
+ *       2) each time the next flow packet comes in, extend the
+ *          data length and merge the checksums.
+ *       3) on flow release, fix up the header.
+ *       4) make sure the higher layer doesn't care.
+ * because packets get coalesced, we shouldn't run into fragment count 
+ * issues.
+ */
+static inline void cas_rx_flow_pkt(struct cas *cp, const u64 *words,
+				   struct sk_buff *skb)
+{
+	int flowid = CAS_VAL(RX_COMP3_FLOWID, words[2]) & (N_RX_FLOWS - 1);
+	struct sk_buff_head *flow = &cp->rx_flows[flowid];
+	
+	/* this is protected at a higher layer, so no need to 
+	 * do any additional locking here. stick the buffer
+	 * at the end.
+	 */
+	__skb_insert(skb, flow->prev, (struct sk_buff *) flow, flow);
+	if (words[0] & RX_COMP1_RELEASE_FLOW) {
+		while ((skb = __skb_dequeue(flow))) {
+			cas_skb_release(skb);
+		}
+	}
+}
+
+/* put rx descriptor back on ring. if a buffer is in use by a higher
+ * layer, this will need to put in a replacement.
+ */
+static void cas_post_page(struct cas *cp, const int ring, const int index)
+{
+	cas_page_t *new;
+	int entry;
+
+	entry = cp->rx_old[ring];
+
+	new = cas_page_swap(cp, ring, index);
+	cp->init_rxds[ring][entry].buffer = cpu_to_le64(new->dma_addr);
+	cp->init_rxds[ring][entry].index  =
+		cpu_to_le64(CAS_BASE(RX_INDEX_NUM, index) | 
+			    CAS_BASE(RX_INDEX_RING, ring));
+
+	entry = RX_DESC_ENTRY(ring, entry + 1);
+	cp->rx_old[ring] = entry;
+	
+	if (entry % 4)
+		return;
+
+	if (ring == 0)
+		writel(entry, cp->regs + REG_RX_KICK);
+	else if ((N_RX_DESC_RINGS > 1) &&
+		 (cp->cas_flags & CAS_FLAG_REG_PLUS)) 
+		writel(entry, cp->regs + REG_PLUS_RX_KICK1);
+}
+
+
+/* only when things are bad */
+static int cas_post_rxds_ringN(struct cas *cp, int ring, int num)
+{
+	unsigned int entry, last, count, released;
+	int cluster;
+	cas_page_t **page = cp->rx_pages[ring];
+
+	entry = cp->rx_old[ring];
+
+	if (netif_msg_intr(cp))
+		printk(KERN_DEBUG "%s: rxd[%d] interrupt, done: %d\n",
+		       cp->dev->name, ring, entry);
+
+	cluster = -1;
+	count = entry & 0x3; 
+	last = RX_DESC_ENTRY(ring, num ? entry + num - 4: entry - 4);
+	released = 0;
+	while (entry != last) {
+		/* make a new buffer if it's still in use */
+		if (page_count(page[entry]->buffer) > 1) {
+			cas_page_t *new = cas_page_dequeue(cp);
+			if (!new) {
+				/* let the timer know that we need to 
+				 * do this again
+				 */
+				cp->cas_flags |= CAS_FLAG_RXD_POST(ring);
+				if (!timer_pending(&cp->link_timer))
+					mod_timer(&cp->link_timer, jiffies + 
+						  CAS_LINK_FAST_TIMEOUT);
+				cp->rx_old[ring]  = entry;
+				cp->rx_last[ring] = num ? num - released : 0;
+				return -ENOMEM;
+			}
+			spin_lock(&cp->rx_inuse_lock);
+			list_add(&page[entry]->list, &cp->rx_inuse_list);
+			spin_unlock(&cp->rx_inuse_lock);
+			cp->init_rxds[ring][entry].buffer = 
+				cpu_to_le64(new->dma_addr);
+			page[entry] = new;
+			
+		}
+
+		if (++count == 4) {
+			cluster = entry;
+			count = 0;
+		}
+		released++;
+		entry = RX_DESC_ENTRY(ring, entry + 1);
+	}
+	cp->rx_old[ring] = entry;
+
+	if (cluster < 0) 
+		return 0;
+
+	if (ring == 0)
+		writel(cluster, cp->regs + REG_RX_KICK);
+	else if ((N_RX_DESC_RINGS > 1) &&
+		 (cp->cas_flags & CAS_FLAG_REG_PLUS)) 
+		writel(cluster, cp->regs + REG_PLUS_RX_KICK1);
+	return 0;
+}
+
+
+/* process a completion ring. packets are set up in three basic ways:
+ * small packets: should be copied header + data in single buffer.
+ * large packets: header and data in a single buffer.
+ * split packets: header in a separate buffer from data. 
+ *                data may be in multiple pages. data may be > 256
+ *                bytes but in a single page. 
+ *
+ * NOTE: RX page posting is done in this routine as well. while there's
+ *       the capability of using multiple RX completion rings, it isn't
+ *       really worthwhile due to the fact that the page posting will
+ *       force serialization on the single descriptor ring. 
+ */
+static int cas_rx_ringN(struct cas *cp, int ring, int budget)
+{
+	struct cas_rx_comp *rxcs = cp->init_rxcs[ring];
+	int entry, drops;
+	int npackets = 0;
+
+	if (netif_msg_intr(cp))
+		printk(KERN_DEBUG "%s: rx[%d] interrupt, done: %d/%d\n",
+		       cp->dev->name, ring,
+		       readl(cp->regs + REG_RX_COMP_HEAD), 
+		       cp->rx_new[ring]);
+
+	entry = cp->rx_new[ring];
+	drops = 0;
+	while (1) {
+		struct cas_rx_comp *rxc = rxcs + entry;
+		struct sk_buff *skb;
+		int type, len;
+		u64 words[4];
+		int i, dring;
+
+		words[0] = le64_to_cpu(rxc->word1);
+		words[1] = le64_to_cpu(rxc->word2);
+		words[2] = le64_to_cpu(rxc->word3);
+		words[3] = le64_to_cpu(rxc->word4);
+
+		/* don't touch if still owned by hw */
+		type = CAS_VAL(RX_COMP1_TYPE, words[0]);
+		if (type == 0)
+			break;
+
+		/* hw hasn't cleared the zero bit yet */
+		if (words[3] & RX_COMP4_ZERO) {
+			break;
+		}
+
+		/* get info on the packet */
+		if (words[3] & (RX_COMP4_LEN_MISMATCH | RX_COMP4_BAD)) {
+			spin_lock(&cp->stat_lock[ring]);
+			cp->net_stats[ring].rx_errors++;
+			if (words[3] & RX_COMP4_LEN_MISMATCH)
+				cp->net_stats[ring].rx_length_errors++;
+			if (words[3] & RX_COMP4_BAD)
+				cp->net_stats[ring].rx_crc_errors++;
+			spin_unlock(&cp->stat_lock[ring]);
+
+			/* We'll just return it to Cassini. */
+		drop_it:
+			spin_lock(&cp->stat_lock[ring]);
+			++cp->net_stats[ring].rx_dropped;
+			spin_unlock(&cp->stat_lock[ring]);
+			goto next;
+		}
+
+		len = cas_rx_process_pkt(cp, rxc, entry, words, &skb);
+		if (len < 0) {
+			++drops;
+			goto drop_it;
+		}
+
+		/* see if it's a flow re-assembly or not. the driver
+		 * itself handles release back up.
+		 */
+		if (RX_DONT_BATCH || (type == 0x2)) {
+			/* non-reassm: these always get released */
+			cas_skb_release(skb); 
+		} else {
+			cas_rx_flow_pkt(cp, words, skb);
+		}
+
+		spin_lock(&cp->stat_lock[ring]);
+		cp->net_stats[ring].rx_packets++;
+		cp->net_stats[ring].rx_bytes += len;
+		spin_unlock(&cp->stat_lock[ring]);
+		cp->dev->last_rx = jiffies;
+
+	next:
+		npackets++;
+
+		/* should it be released? */
+		if (words[0] & RX_COMP1_RELEASE_HDR) {
+			i = CAS_VAL(RX_COMP2_HDR_INDEX, words[1]);
+			dring = CAS_VAL(RX_INDEX_RING, i);
+			i = CAS_VAL(RX_INDEX_NUM, i);
+			cas_post_page(cp, dring, i);
+		}
+		
+		if (words[0] & RX_COMP1_RELEASE_DATA) {
+			i = CAS_VAL(RX_COMP1_DATA_INDEX, words[0]);
+			dring = CAS_VAL(RX_INDEX_RING, i);
+			i = CAS_VAL(RX_INDEX_NUM, i);
+			cas_post_page(cp, dring, i);
+		}
+
+		if (words[0] & RX_COMP1_RELEASE_NEXT) {
+			i = CAS_VAL(RX_COMP2_NEXT_INDEX, words[1]);
+			dring = CAS_VAL(RX_INDEX_RING, i);
+			i = CAS_VAL(RX_INDEX_NUM, i);
+			cas_post_page(cp, dring, i);
+		}
+
+		/* skip to the next entry */
+		entry = RX_COMP_ENTRY(ring, entry + 1 + 
+				      CAS_VAL(RX_COMP1_SKIP, words[0]));
+#ifdef USE_NAPI
+		if (budget && (npackets >= budget))
+			break;
+#endif
+	}
+	cp->rx_new[ring] = entry;
+
+	if (drops)
+		printk(KERN_INFO "%s: Memory squeeze, deferring packet.\n",
+		       cp->dev->name);
+	return npackets;
+}
+
+
+/* put completion entries back on the ring */
+static void cas_post_rxcs_ringN(struct net_device *dev,
+				struct cas *cp, int ring)
+{
+	struct cas_rx_comp *rxc = cp->init_rxcs[ring];
+	int last, entry;
+
+	last = cp->rx_cur[ring];
+	entry = cp->rx_new[ring]; 
+	if (netif_msg_intr(cp))
+		printk(KERN_DEBUG "%s: rxc[%d] interrupt, done: %d/%d\n",
+		       dev->name, ring, readl(cp->regs + REG_RX_COMP_HEAD),
+		       entry);
+	
+	/* zero and re-mark descriptors */
+	while (last != entry) {
+		cas_rxc_init(rxc + last);
+		last = RX_COMP_ENTRY(ring, last + 1);
+	}
+	cp->rx_cur[ring] = last;
+
+	if (ring == 0)
+		writel(last, cp->regs + REG_RX_COMP_TAIL);
+	else if (cp->cas_flags & CAS_FLAG_REG_PLUS) 
+		writel(last, cp->regs + REG_PLUS_RX_COMPN_TAIL(ring));
+}
+
+
+
+/* cassini can use all four PCI interrupts for the completion ring. 
+ * rings 3 and 4 are identical
+ */
+#if defined(USE_PCI_INTC) || defined(USE_PCI_INTD)
+static inline void cas_handle_irqN(struct net_device *dev, 
+				   struct cas *cp, const u32 status,
+				   const int ring)
+{
+	if (status & (INTR_RX_COMP_FULL_ALT | INTR_RX_COMP_AF_ALT)) 
+		cas_post_rxcs_ringN(dev, cp, ring);
+}
+
+static irqreturn_t cas_interruptN(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = dev_id;
+	struct cas *cp = netdev_priv(dev);
+	unsigned long flags;
+	int ring;
+	u32 status = readl(cp->regs + REG_PLUS_INTRN_STATUS(ring));
+
+	/* check for shared irq */
+	if (status == 0)
+		return IRQ_NONE;
+
+	ring = (irq == cp->pci_irq_INTC) ? 2 : 3;
+	spin_lock_irqsave(&cp->lock, flags);
+	if (status & INTR_RX_DONE_ALT) { /* handle rx separately */
+#ifdef USE_NAPI
+		cas_mask_intr(cp);
+		netif_rx_schedule(dev);
+#else
+		cas_rx_ringN(cp, ring, 0);
+#endif
+		status &= ~INTR_RX_DONE_ALT;
+	}
+
+	if (status)
+		cas_handle_irqN(dev, cp, status, ring);
+	spin_unlock_irqrestore(&cp->lock, flags);
+	return IRQ_HANDLED;
+}
+#endif
+
+#ifdef USE_PCI_INTB
+/* everything but rx packets */
+static inline void cas_handle_irq1(struct cas *cp, const u32 status)
+{
+	if (status & INTR_RX_BUF_UNAVAIL_1) {
+		/* Frame arrived, no free RX buffers available. 
+		 * NOTE: we can get this on a link transition. */
+		cas_post_rxds_ringN(cp, 1, 0);
+		spin_lock(&cp->stat_lock[1]);
+		cp->net_stats[1].rx_dropped++;
+		spin_unlock(&cp->stat_lock[1]);
+	}
+
+	if (status & INTR_RX_BUF_AE_1) 
+		cas_post_rxds_ringN(cp, 1, RX_DESC_RINGN_SIZE(1) - 
+				    RX_AE_FREEN_VAL(1));
+
+	if (status & (INTR_RX_COMP_AF | INTR_RX_COMP_FULL))
+		cas_post_rxcs_ringN(cp, 1);
+}
+
+/* ring 2 handles a few more events than 3 and 4 */
+static irqreturn_t cas_interrupt1(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = dev_id;
+	struct cas *cp = netdev_priv(dev);
+	unsigned long flags;
+	u32 status = readl(cp->regs + REG_PLUS_INTRN_STATUS(1));
+
+	/* check for shared interrupt */
+	if (status == 0)
+		return IRQ_NONE;
+
+	spin_lock_irqsave(&cp->lock, flags);
+	if (status & INTR_RX_DONE_ALT) { /* handle rx separately */
+#ifdef USE_NAPI
+		cas_mask_intr(cp);
+		netif_rx_schedule(dev);
+#else
+		cas_rx_ringN(cp, 1, 0);
+#endif
+		status &= ~INTR_RX_DONE_ALT;
+	}
+	if (status)
+		cas_handle_irq1(cp, status);
+	spin_unlock_irqrestore(&cp->lock, flags);
+	return IRQ_HANDLED;
+}
+#endif
+
+static inline void cas_handle_irq(struct net_device *dev,
+				  struct cas *cp, const u32 status)
+{
+	/* housekeeping interrupts */
+	if (status & INTR_ERROR_MASK)
+		cas_abnormal_irq(dev, cp, status);
+
+	if (status & INTR_RX_BUF_UNAVAIL) {
+		/* Frame arrived, no free RX buffers available. 
+		 * NOTE: we can get this on a link transition.
+		 */
+		cas_post_rxds_ringN(cp, 0, 0);
+		spin_lock(&cp->stat_lock[0]);
+		cp->net_stats[0].rx_dropped++;
+		spin_unlock(&cp->stat_lock[0]);
+	} else if (status & INTR_RX_BUF_AE) {
+		cas_post_rxds_ringN(cp, 0, RX_DESC_RINGN_SIZE(0) -
+				    RX_AE_FREEN_VAL(0));
+	}
+
+	if (status & (INTR_RX_COMP_AF | INTR_RX_COMP_FULL))
+		cas_post_rxcs_ringN(dev, cp, 0);
+}
+
+static irqreturn_t cas_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = dev_id;
+	struct cas *cp = netdev_priv(dev);
+	unsigned long flags;
+	u32 status = readl(cp->regs + REG_INTR_STATUS);
+
+	if (status == 0)
+		return IRQ_NONE;
+
+	spin_lock_irqsave(&cp->lock, flags);
+	if (status & (INTR_TX_ALL | INTR_TX_INTME)) {
+		cas_tx(dev, cp, status);
+		status &= ~(INTR_TX_ALL | INTR_TX_INTME);
+	}
+
+	if (status & INTR_RX_DONE) {
+#ifdef USE_NAPI
+		cas_mask_intr(cp);
+		netif_rx_schedule(dev);
+#else
+		cas_rx_ringN(cp, 0, 0);
+#endif
+		status &= ~INTR_RX_DONE;
+	}
+
+	if (status)
+		cas_handle_irq(dev, cp, status);
+	spin_unlock_irqrestore(&cp->lock, flags);
+	return IRQ_HANDLED;
+}
+
+
+#ifdef USE_NAPI
+static int cas_poll(struct net_device *dev, int *budget)
+{
+	struct cas *cp = netdev_priv(dev);
+	int i, enable_intr, todo, credits;
+	u32 status = readl(cp->regs + REG_INTR_STATUS);
+	unsigned long flags;
+
+	spin_lock_irqsave(&cp->lock, flags);
+	cas_tx(dev, cp, status);
+	spin_unlock_irqrestore(&cp->lock, flags);
+
+	/* NAPI rx packets. we spread the credits across all of the
+	 * rxc rings
+	 */
+	todo = min(*budget, dev->quota);
+
+	/* to make sure we're fair with the work we loop through each
+	 * ring N_RX_COMP_RING times with a request of 
+	 * todo / N_RX_COMP_RINGS
+	 */
+	enable_intr = 1;
+	credits = 0;
+	for (i = 0; i < N_RX_COMP_RINGS; i++) {
+		int j;
+		for (j = 0; j < N_RX_COMP_RINGS; j++) {
+			credits += cas_rx_ringN(cp, j, todo / N_RX_COMP_RINGS);
+			if (credits >= todo) {
+				enable_intr = 0;
+				goto rx_comp;
+			}
+		}
+	}
+
+rx_comp:
+	*budget    -= credits;
+	dev->quota -= credits;
+
+	/* final rx completion */
+	spin_lock_irqsave(&cp->lock, flags);
+	if (status)
+		cas_handle_irq(dev, cp, status);
+
+#ifdef USE_PCI_INTB
+	if (N_RX_COMP_RINGS > 1) {
+		status = readl(cp->regs + REG_PLUS_INTRN_STATUS(1));
+		if (status)
+			cas_handle_irq1(dev, cp, status);
+	}
+#endif
+
+#ifdef USE_PCI_INTC
+	if (N_RX_COMP_RINGS > 2) {
+		status = readl(cp->regs + REG_PLUS_INTRN_STATUS(2));
+		if (status)
+			cas_handle_irqN(dev, cp, status, 2);
+	}
+#endif
+
+#ifdef USE_PCI_INTD
+	if (N_RX_COMP_RINGS > 3) {
+		status = readl(cp->regs + REG_PLUS_INTRN_STATUS(3));
+		if (status)
+			cas_handle_irqN(dev, cp, status, 3);
+	}
+#endif
+	spin_unlock_irqrestore(&cp->lock, flags);
+	if (enable_intr) {
+		netif_rx_complete(dev);
+		cas_unmask_intr(cp);
+		return 0;
+	}
+	return 1;
+}
+#endif
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void cas_netpoll(struct net_device *dev)
+{
+	struct cas *cp = netdev_priv(dev);
+
+	cas_disable_irq(cp, 0);
+	cas_interrupt(cp->pdev->irq, dev, NULL);
+	cas_enable_irq(cp, 0);
+
+#ifdef USE_PCI_INTB
+	if (N_RX_COMP_RINGS > 1) {
+		/* cas_interrupt1(); */
+	}
+#endif
+#ifdef USE_PCI_INTC
+	if (N_RX_COMP_RINGS > 2) {
+		/* cas_interruptN(); */
+	}
+#endif
+#ifdef USE_PCI_INTD
+	if (N_RX_COMP_RINGS > 3) {
+		/* cas_interruptN(); */
+	}
+#endif
+}
+#endif
+
+static void cas_tx_timeout(struct net_device *dev)
+{
+	struct cas *cp = netdev_priv(dev);
+
+	printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
+	if (!cp->hw_running) {
+		printk("%s: hrm.. hw not running!\n", dev->name);
+		return;
+	}
+
+	printk(KERN_ERR "%s: MIF_STATE[%08x]\n",
+	       dev->name, readl(cp->regs + REG_MIF_STATE_MACHINE));
+
+	printk(KERN_ERR "%s: MAC_STATE[%08x]\n",
+	       dev->name, readl(cp->regs + REG_MAC_STATE_MACHINE));
+
+	printk(KERN_ERR "%s: TX_STATE[%08x:%08x:%08x] "
+	       "FIFO[%08x:%08x:%08x] SM1[%08x] SM2[%08x]\n",
+	       dev->name,
+	       readl(cp->regs + REG_TX_CFG),
+	       readl(cp->regs + REG_MAC_TX_STATUS),
+	       readl(cp->regs + REG_MAC_TX_CFG),
+	       readl(cp->regs + REG_TX_FIFO_PKT_CNT),
+	       readl(cp->regs + REG_TX_FIFO_WRITE_PTR),
+	       readl(cp->regs + REG_TX_FIFO_READ_PTR),
+	       readl(cp->regs + REG_TX_SM_1),
+	       readl(cp->regs + REG_TX_SM_2));
+
+	printk(KERN_ERR "%s: RX_STATE[%08x:%08x:%08x]\n",
+	       dev->name,
+	       readl(cp->regs + REG_RX_CFG),
+	       readl(cp->regs + REG_MAC_RX_STATUS),
+	       readl(cp->regs + REG_MAC_RX_CFG));
+
+	printk(KERN_ERR "%s: HP_STATE[%08x:%08x:%08x:%08x]\n",
+	       dev->name,
+	       readl(cp->regs + REG_HP_STATE_MACHINE),
+	       readl(cp->regs + REG_HP_STATUS0),
+	       readl(cp->regs + REG_HP_STATUS1),
+	       readl(cp->regs + REG_HP_STATUS2));
+
+#if 1
+	atomic_inc(&cp->reset_task_pending);
+	atomic_inc(&cp->reset_task_pending_all);
+	schedule_work(&cp->reset_task);
+#else
+	atomic_set(&cp->reset_task_pending, CAS_RESET_ALL);
+	schedule_work(&cp->reset_task);
+#endif
+}
+
+static inline int cas_intme(int ring, int entry)
+{
+	/* Algorithm: IRQ every 1/2 of descriptors. */
+	if (!(entry & ((TX_DESC_RINGN_SIZE(ring) >> 1) - 1)))
+		return 1;
+	return 0;
+}
+
+
+static void cas_write_txd(struct cas *cp, int ring, int entry,
+			  dma_addr_t mapping, int len, u64 ctrl, int last)
+{
+	struct cas_tx_desc *txd = cp->init_txds[ring] + entry;
+
+	ctrl |= CAS_BASE(TX_DESC_BUFLEN, len);
+	if (cas_intme(ring, entry))
+		ctrl |= TX_DESC_INTME;
+	if (last)
+		ctrl |= TX_DESC_EOF;
+	txd->control = cpu_to_le64(ctrl);
+	txd->buffer = cpu_to_le64(mapping);
+}
+
+static inline void *tx_tiny_buf(struct cas *cp, const int ring, 
+				const int entry)
+{
+	return cp->tx_tiny_bufs[ring] + TX_TINY_BUF_LEN*entry;
+}
+
+static inline dma_addr_t tx_tiny_map(struct cas *cp, const int ring, 
+				     const int entry, const int tentry)
+{
+	cp->tx_tiny_use[ring][tentry].nbufs++;
+	cp->tx_tiny_use[ring][entry].used = 1;
+	return cp->tx_tiny_dvma[ring] + TX_TINY_BUF_LEN*entry;
+}
+
+static inline int cas_xmit_tx_ringN(struct cas *cp, int ring, 
+				    struct sk_buff *skb)
+{
+	struct net_device *dev = cp->dev;
+	int entry, nr_frags, frag, tabort, tentry;
+	dma_addr_t mapping;
+	unsigned long flags;
+	u64 ctrl;
+	u32 len;
+
+	spin_lock_irqsave(&cp->tx_lock[ring], flags);
+
+	/* This is a hard error, log it. */
+	if (TX_BUFFS_AVAIL(cp, ring) <= 
+	    CAS_TABORT(cp)*(skb_shinfo(skb)->nr_frags + 1)) {
+		netif_stop_queue(dev);
+		spin_unlock_irqrestore(&cp->tx_lock[ring], flags);
+		printk(KERN_ERR PFX "%s: BUG! Tx Ring full when "
+		       "queue awake!\n", dev->name);
+		return 1;
+	}
+
+	ctrl = 0;
+	if (skb->ip_summed == CHECKSUM_HW) {
+		u64 csum_start_off, csum_stuff_off;
+
+		csum_start_off = (u64) (skb->h.raw - skb->data);
+		csum_stuff_off = (u64) ((skb->h.raw + skb->csum) - skb->data);
+
+		ctrl =  TX_DESC_CSUM_EN | 
+			CAS_BASE(TX_DESC_CSUM_START, csum_start_off) |
+			CAS_BASE(TX_DESC_CSUM_STUFF, csum_stuff_off);
+	}
+
+	entry = cp->tx_new[ring];
+	cp->tx_skbs[ring][entry] = skb;
+
+	nr_frags = skb_shinfo(skb)->nr_frags;
+	len = skb_headlen(skb);
+	mapping = pci_map_page(cp->pdev, virt_to_page(skb->data),
+			       offset_in_page(skb->data), len,
+			       PCI_DMA_TODEVICE);
+
+	tentry = entry;
+	tabort = cas_calc_tabort(cp, (unsigned long) skb->data, len);
+	if (unlikely(tabort)) {
+		/* NOTE: len is always >  tabort */
+		cas_write_txd(cp, ring, entry, mapping, len - tabort, 
+			      ctrl | TX_DESC_SOF, 0);
+		entry = TX_DESC_NEXT(ring, entry);
+
+		memcpy(tx_tiny_buf(cp, ring, entry), skb->data + 
+		       len - tabort, tabort);
+		mapping = tx_tiny_map(cp, ring, entry, tentry);
+		cas_write_txd(cp, ring, entry, mapping, tabort, ctrl,
+			      (nr_frags == 0));
+	} else {
+		cas_write_txd(cp, ring, entry, mapping, len, ctrl | 
+			      TX_DESC_SOF, (nr_frags == 0));
+	}
+	entry = TX_DESC_NEXT(ring, entry);
+
+	for (frag = 0; frag < nr_frags; frag++) {
+		skb_frag_t *fragp = &skb_shinfo(skb)->frags[frag];
+
+		len = fragp->size;
+		mapping = pci_map_page(cp->pdev, fragp->page,
+				       fragp->page_offset, len,
+				       PCI_DMA_TODEVICE);
+
+		tabort = cas_calc_tabort(cp, fragp->page_offset, len);
+		if (unlikely(tabort)) {
+			void *addr;
+
+			/* NOTE: len is always > tabort */
+			cas_write_txd(cp, ring, entry, mapping, len - tabort,
+				      ctrl, 0);
+			entry = TX_DESC_NEXT(ring, entry);
+			
+			addr = cas_page_map(fragp->page);
+			memcpy(tx_tiny_buf(cp, ring, entry),
+			       addr + fragp->page_offset + len - tabort, 
+			       tabort);
+			cas_page_unmap(addr);
+			mapping = tx_tiny_map(cp, ring, entry, tentry);
+			len     = tabort;
+		}
+
+		cas_write_txd(cp, ring, entry, mapping, len, ctrl,
+			      (frag + 1 == nr_frags));
+		entry = TX_DESC_NEXT(ring, entry);
+	}
+
+	cp->tx_new[ring] = entry;
+	if (TX_BUFFS_AVAIL(cp, ring) <= CAS_TABORT(cp)*(MAX_SKB_FRAGS + 1))
+		netif_stop_queue(dev);
+
+	if (netif_msg_tx_queued(cp))
+		printk(KERN_DEBUG "%s: tx[%d] queued, slot %d, skblen %d, "
+		       "avail %d\n",
+		       dev->name, ring, entry, skb->len, 
+		       TX_BUFFS_AVAIL(cp, ring));
+	writel(entry, cp->regs + REG_TX_KICKN(ring));
+	spin_unlock_irqrestore(&cp->tx_lock[ring], flags);
+	return 0;
+} 
+
+static int cas_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct cas *cp = netdev_priv(dev);
+
+	/* this is only used as a load-balancing hint, so it doesn't
+	 * need to be SMP safe
+	 */
+	static int ring; 
+
+	skb = skb_padto(skb, cp->min_frame_size);
+	if (!skb)
+		return 0;
+
+	/* XXX: we need some higher-level QoS hooks to steer packets to
+	 *      individual queues.
+	 */
+	if (cas_xmit_tx_ringN(cp, ring++ & N_TX_RINGS_MASK, skb))
+		return 1;
+	dev->trans_start = jiffies;
+	return 0;
+}
+
+static void cas_init_tx_dma(struct cas *cp)
+{
+	u64 desc_dma = cp->block_dvma;
+	unsigned long off;
+	u32 val;
+	int i;
+
+	/* set up tx completion writeback registers. must be 8-byte aligned */
+#ifdef USE_TX_COMPWB
+	off = offsetof(struct cas_init_block, tx_compwb);
+	writel((desc_dma + off) >> 32, cp->regs + REG_TX_COMPWB_DB_HI);
+	writel((desc_dma + off) & 0xffffffff, cp->regs + REG_TX_COMPWB_DB_LOW);
+#endif
+
+	/* enable completion writebacks, enable paced mode,
+	 * disable read pipe, and disable pre-interrupt compwbs
+	 */
+	val =   TX_CFG_COMPWB_Q1 | TX_CFG_COMPWB_Q2 | 
+		TX_CFG_COMPWB_Q3 | TX_CFG_COMPWB_Q4 |
+		TX_CFG_DMA_RDPIPE_DIS | TX_CFG_PACED_MODE | 
+		TX_CFG_INTR_COMPWB_DIS;
+
+	/* write out tx ring info and tx desc bases */
+	for (i = 0; i < MAX_TX_RINGS; i++) {
+		off = (unsigned long) cp->init_txds[i] - 
+			(unsigned long) cp->init_block;
+
+		val |= CAS_TX_RINGN_BASE(i);
+		writel((desc_dma + off) >> 32, cp->regs + REG_TX_DBN_HI(i));
+		writel((desc_dma + off) & 0xffffffff, cp->regs +
+		       REG_TX_DBN_LOW(i));
+		/* don't zero out the kick register here as the system
+		 * will wedge
+		 */
+	}
+	writel(val, cp->regs + REG_TX_CFG);
+
+	/* program max burst sizes. these numbers should be different
+	 * if doing QoS.
+	 */
+#ifdef USE_QOS
+	writel(0x800, cp->regs + REG_TX_MAXBURST_0);
+	writel(0x1600, cp->regs + REG_TX_MAXBURST_1);
+	writel(0x2400, cp->regs + REG_TX_MAXBURST_2);
+	writel(0x4800, cp->regs + REG_TX_MAXBURST_3);
+#else
+	writel(0x800, cp->regs + REG_TX_MAXBURST_0);
+	writel(0x800, cp->regs + REG_TX_MAXBURST_1);
+	writel(0x800, cp->regs + REG_TX_MAXBURST_2);
+	writel(0x800, cp->regs + REG_TX_MAXBURST_3);
+#endif
+}
+
+/* Must be invoked under cp->lock. */
+static inline void cas_init_dma(struct cas *cp)
+{
+	cas_init_tx_dma(cp);
+	cas_init_rx_dma(cp);
+}
+
+/* Must be invoked under cp->lock. */
+static u32 cas_setup_multicast(struct cas *cp)
+{
+	u32 rxcfg = 0;
+	int i;
+	
+	if (cp->dev->flags & IFF_PROMISC) {
+		rxcfg |= MAC_RX_CFG_PROMISC_EN;
+
+	} else if (cp->dev->flags & IFF_ALLMULTI) {
+	    	for (i=0; i < 16; i++)
+			writel(0xFFFF, cp->regs + REG_MAC_HASH_TABLEN(i));
+		rxcfg |= MAC_RX_CFG_HASH_FILTER_EN;
+
+	} else {
+		u16 hash_table[16];
+		u32 crc;
+		struct dev_mc_list *dmi = cp->dev->mc_list;
+		int i;
+
+		/* use the alternate mac address registers for the
+		 * first 15 multicast addresses
+		 */
+		for (i = 1; i <= CAS_MC_EXACT_MATCH_SIZE; i++) {
+			if (!dmi) {
+				writel(0x0, cp->regs + REG_MAC_ADDRN(i*3 + 0));
+				writel(0x0, cp->regs + REG_MAC_ADDRN(i*3 + 1));
+				writel(0x0, cp->regs + REG_MAC_ADDRN(i*3 + 2));
+				continue;
+			}
+			writel((dmi->dmi_addr[4] << 8) | dmi->dmi_addr[5], 
+			       cp->regs + REG_MAC_ADDRN(i*3 + 0));
+			writel((dmi->dmi_addr[2] << 8) | dmi->dmi_addr[3], 
+			       cp->regs + REG_MAC_ADDRN(i*3 + 1));
+			writel((dmi->dmi_addr[0] << 8) | dmi->dmi_addr[1], 
+			       cp->regs + REG_MAC_ADDRN(i*3 + 2));
+			dmi = dmi->next;
+		}
+
+		/* use hw hash table for the next series of 
+		 * multicast addresses
+		 */
+		memset(hash_table, 0, sizeof(hash_table));
+		while (dmi) {
+ 			crc = ether_crc_le(ETH_ALEN, dmi->dmi_addr);
+			crc >>= 24;
+			hash_table[crc >> 4] |= 1 << (15 - (crc & 0xf));
+			dmi = dmi->next;
+		}
+	    	for (i=0; i < 16; i++)
+			writel(hash_table[i], cp->regs + 
+			       REG_MAC_HASH_TABLEN(i));
+		rxcfg |= MAC_RX_CFG_HASH_FILTER_EN;
+	}
+
+	return rxcfg;
+}
+
+/* must be invoked under cp->stat_lock[N_TX_RINGS] */
+static void cas_clear_mac_err(struct cas *cp)
+{
+	writel(0, cp->regs + REG_MAC_COLL_NORMAL);
+	writel(0, cp->regs + REG_MAC_COLL_FIRST);
+	writel(0, cp->regs + REG_MAC_COLL_EXCESS);
+	writel(0, cp->regs + REG_MAC_COLL_LATE);
+	writel(0, cp->regs + REG_MAC_TIMER_DEFER);
+	writel(0, cp->regs + REG_MAC_ATTEMPTS_PEAK);
+	writel(0, cp->regs + REG_MAC_RECV_FRAME);
+	writel(0, cp->regs + REG_MAC_LEN_ERR);
+	writel(0, cp->regs + REG_MAC_ALIGN_ERR);
+	writel(0, cp->regs + REG_MAC_FCS_ERR);
+	writel(0, cp->regs + REG_MAC_RX_CODE_ERR);
+}
+
+
+static void cas_mac_reset(struct cas *cp)
+{
+	int i;
+
+	/* do both TX and RX reset */
+	writel(0x1, cp->regs + REG_MAC_TX_RESET);
+	writel(0x1, cp->regs + REG_MAC_RX_RESET);
+
+	/* wait for TX */
+	i = STOP_TRIES;
+	while (i-- > 0) {
+		if (readl(cp->regs + REG_MAC_TX_RESET) == 0)
+			break;
+		udelay(10);
+	}
+
+	/* wait for RX */
+	i = STOP_TRIES;
+	while (i-- > 0) {
+		if (readl(cp->regs + REG_MAC_RX_RESET) == 0)
+			break;
+		udelay(10);
+	}
+
+	if (readl(cp->regs + REG_MAC_TX_RESET) |
+	    readl(cp->regs + REG_MAC_RX_RESET))
+		printk(KERN_ERR "%s: mac tx[%d]/rx[%d] reset failed [%08x]\n",
+		       cp->dev->name, readl(cp->regs + REG_MAC_TX_RESET),
+		       readl(cp->regs + REG_MAC_RX_RESET),
+		       readl(cp->regs + REG_MAC_STATE_MACHINE));
+}
+
+
+/* Must be invoked under cp->lock. */
+static void cas_init_mac(struct cas *cp)
+{
+	unsigned char *e = &cp->dev->dev_addr[0];
+	int i;
+#ifdef CONFIG_CASSINI_MULTICAST_REG_WRITE
+	u32 rxcfg;
+#endif
+	cas_mac_reset(cp);
+
+	/* setup core arbitration weight register */
+	writel(CAWR_RR_DIS, cp->regs + REG_CAWR);
+
+	/* XXX Use pci_dma_burst_advice() */
+#if !defined(CONFIG_SPARC64) && !defined(CONFIG_ALPHA)
+	/* set the infinite burst register for chips that don't have
+	 * pci issues.
+	 */
+	if ((cp->cas_flags & CAS_FLAG_TARGET_ABORT) == 0)
+		writel(INF_BURST_EN, cp->regs + REG_INF_BURST);
+#endif
+
+	writel(0x1BF0, cp->regs + REG_MAC_SEND_PAUSE);
+
+	writel(0x00, cp->regs + REG_MAC_IPG0);
+	writel(0x08, cp->regs + REG_MAC_IPG1);
+	writel(0x04, cp->regs + REG_MAC_IPG2);
+	
+	/* change later for 802.3z */
+	writel(0x40, cp->regs + REG_MAC_SLOT_TIME); 
+
+	/* min frame + FCS */
+	writel(ETH_ZLEN + 4, cp->regs + REG_MAC_FRAMESIZE_MIN);
+
+	/* Ethernet payload + header + FCS + optional VLAN tag. NOTE: we
+	 * specify the maximum frame size to prevent RX tag errors on 
+	 * oversized frames.
+	 */
+	writel(CAS_BASE(MAC_FRAMESIZE_MAX_BURST, 0x2000) |
+	       CAS_BASE(MAC_FRAMESIZE_MAX_FRAME, 
+			(CAS_MAX_MTU + ETH_HLEN + 4 + 4)), 
+	       cp->regs + REG_MAC_FRAMESIZE_MAX);
+
+	/* NOTE: crc_size is used as a surrogate for half-duplex. 
+	 * workaround saturn half-duplex issue by increasing preamble
+	 * size to 65 bytes.
+	 */
+	if ((cp->cas_flags & CAS_FLAG_SATURN) && cp->crc_size)
+		writel(0x41, cp->regs + REG_MAC_PA_SIZE);
+	else
+		writel(0x07, cp->regs + REG_MAC_PA_SIZE);
+	writel(0x04, cp->regs + REG_MAC_JAM_SIZE);
+	writel(0x10, cp->regs + REG_MAC_ATTEMPT_LIMIT);
+	writel(0x8808, cp->regs + REG_MAC_CTRL_TYPE);
+
+	writel((e[5] | (e[4] << 8)) & 0x3ff, cp->regs + REG_MAC_RANDOM_SEED);
+
+	writel(0, cp->regs + REG_MAC_ADDR_FILTER0);
+	writel(0, cp->regs + REG_MAC_ADDR_FILTER1);
+	writel(0, cp->regs + REG_MAC_ADDR_FILTER2);
+	writel(0, cp->regs + REG_MAC_ADDR_FILTER2_1_MASK);
+	writel(0, cp->regs + REG_MAC_ADDR_FILTER0_MASK);
+
+	/* setup mac address in perfect filter array */
+	for (i = 0; i < 45; i++)
+		writel(0x0, cp->regs + REG_MAC_ADDRN(i));
+
+	writel((e[4] << 8) | e[5], cp->regs + REG_MAC_ADDRN(0));
+	writel((e[2] << 8) | e[3], cp->regs + REG_MAC_ADDRN(1));
+	writel((e[0] << 8) | e[1], cp->regs + REG_MAC_ADDRN(2));
+
+	writel(0x0001, cp->regs + REG_MAC_ADDRN(42));
+	writel(0xc200, cp->regs + REG_MAC_ADDRN(43));
+	writel(0x0180, cp->regs + REG_MAC_ADDRN(44));
+
+#ifndef CONFIG_CASSINI_MULTICAST_REG_WRITE
+	cp->mac_rx_cfg = cas_setup_multicast(cp);
+#else
+	/* WTZ: Do what Adrian did in cas_set_multicast. Doing
+	 * a writel does not seem to be necessary because Cassini
+	 * seems to preserve the configuration when we do the reset.
+	 * If the chip is in trouble, though, it is not clear if we
+	 * can really count on this behavior. cas_set_multicast uses
+	 * spin_lock_irqsave, but we are called only in cas_init_hw and
+	 * cas_init_hw is protected by cas_lock_all, which calls
+	 * spin_lock_irq (so it doesn't need to save the flags, and
+	 * we should be OK for the writel, as that is the only 
+	 * difference).
+	 */
+	cp->mac_rx_cfg = rxcfg = cas_setup_multicast(cp);
+	writel(rxcfg, cp->regs + REG_MAC_RX_CFG);
+#endif
+	spin_lock(&cp->stat_lock[N_TX_RINGS]);
+	cas_clear_mac_err(cp);
+	spin_unlock(&cp->stat_lock[N_TX_RINGS]);
+
+	/* Setup MAC interrupts.  We want to get all of the interesting
+	 * counter expiration events, but we do not want to hear about
+	 * normal rx/tx as the DMA engine tells us that.
+	 */
+	writel(MAC_TX_FRAME_XMIT, cp->regs + REG_MAC_TX_MASK);
+	writel(MAC_RX_FRAME_RECV, cp->regs + REG_MAC_RX_MASK);
+
+	/* Don't enable even the PAUSE interrupts for now, we
+	 * make no use of those events other than to record them.
+	 */
+	writel(0xffffffff, cp->regs + REG_MAC_CTRL_MASK);
+}
+
+/* Must be invoked under cp->lock. */
+static void cas_init_pause_thresholds(struct cas *cp)
+{
+	/* Calculate pause thresholds.  Setting the OFF threshold to the
+	 * full RX fifo size effectively disables PAUSE generation
+	 */
+	if (cp->rx_fifo_size <= (2 * 1024)) {
+		cp->rx_pause_off = cp->rx_pause_on = cp->rx_fifo_size;
+	} else {
+		int max_frame = (cp->dev->mtu + ETH_HLEN + 4 + 4 + 64) & ~63;
+		if (max_frame * 3 > cp->rx_fifo_size) {
+			cp->rx_pause_off = 7104;
+			cp->rx_pause_on  = 960;
+		} else {
+			int off = (cp->rx_fifo_size - (max_frame * 2));
+			int on = off - max_frame;
+			cp->rx_pause_off = off;
+			cp->rx_pause_on = on;
+		}
+	}
+}
+
+static int cas_vpd_match(const void __iomem *p, const char *str)
+{
+	int len = strlen(str) + 1;
+	int i;
+	
+	for (i = 0; i < len; i++) {
+		if (readb(p + i) != str[i])
+			return 0;
+	}
+	return 1;
+}
+
+
+/* get the mac address by reading the vpd information in the rom.
+ * also get the phy type and determine if there's an entropy generator.
+ * NOTE: this is a bit convoluted for the following reasons:
+ *  1) vpd info has order-dependent mac addresses for multinic cards
+ *  2) the only way to determine the nic order is to use the slot
+ *     number.
+ *  3) fiber cards don't have bridges, so their slot numbers don't
+ *     mean anything.
+ *  4) we don't actually know we have a fiber card until after 
+ *     the mac addresses are parsed.
+ */
+static int cas_get_vpd_info(struct cas *cp, unsigned char *dev_addr,
+			    const int offset)
+{
+	void __iomem *p = cp->regs + REG_EXPANSION_ROM_RUN_START;
+	void __iomem *base, *kstart;
+	int i, len;
+	int found = 0;
+#define VPD_FOUND_MAC        0x01
+#define VPD_FOUND_PHY        0x02
+
+	int phy_type = CAS_PHY_MII_MDIO0; /* default phy type */
+	int mac_off  = 0;
+
+	/* give us access to the PROM */
+	writel(BIM_LOCAL_DEV_PROM | BIM_LOCAL_DEV_PAD,
+	       cp->regs + REG_BIM_LOCAL_DEV_EN);
+
+	/* check for an expansion rom */
+	if (readb(p) != 0x55 || readb(p + 1) != 0xaa)
+		goto use_random_mac_addr;
+
+	/* search for beginning of vpd */
+	base = NULL;
+	for (i = 2; i < EXPANSION_ROM_SIZE; i++) {
+		/* check for PCIR */
+		if ((readb(p + i + 0) == 0x50) &&
+		    (readb(p + i + 1) == 0x43) &&
+		    (readb(p + i + 2) == 0x49) &&
+		    (readb(p + i + 3) == 0x52)) {
+			base = p + (readb(p + i + 8) | 
+				    (readb(p + i + 9) << 8));
+			break;
+		}		
+	}
+
+	if (!base || (readb(base) != 0x82))
+		goto use_random_mac_addr;
+	
+	i = (readb(base + 1) | (readb(base + 2) << 8)) + 3;
+	while (i < EXPANSION_ROM_SIZE) {
+		if (readb(base + i) != 0x90) /* no vpd found */
+			goto use_random_mac_addr;
+
+		/* found a vpd field */
+		len = readb(base + i + 1) | (readb(base + i + 2) << 8);
+
+		/* extract keywords */
+		kstart = base + i + 3;
+		p = kstart;
+		while ((p - kstart) < len) {
+			int klen = readb(p + 2);
+			int j;
+			char type;
+
+			p += 3;
+			
+			/* look for the following things:
+			 * -- correct length == 29
+			 * 3 (type) + 2 (size) + 
+			 * 18 (strlen("local-mac-address") + 1) + 
+			 * 6 (mac addr) 
+			 * -- VPD Instance 'I'
+			 * -- VPD Type Bytes 'B'
+			 * -- VPD data length == 6
+			 * -- property string == local-mac-address
+			 * 
+			 * -- correct length == 24
+			 * 3 (type) + 2 (size) + 
+			 * 12 (strlen("entropy-dev") + 1) + 
+			 * 7 (strlen("vms110") + 1)
+			 * -- VPD Instance 'I'
+			 * -- VPD Type String 'B'
+			 * -- VPD data length == 7
+			 * -- property string == entropy-dev
+			 *
+			 * -- correct length == 18
+			 * 3 (type) + 2 (size) + 
+			 * 9 (strlen("phy-type") + 1) + 
+			 * 4 (strlen("pcs") + 1)
+			 * -- VPD Instance 'I'
+			 * -- VPD Type String 'S'
+			 * -- VPD data length == 4
+			 * -- property string == phy-type
+			 * 
+			 * -- correct length == 23
+			 * 3 (type) + 2 (size) + 
+			 * 14 (strlen("phy-interface") + 1) + 
+			 * 4 (strlen("pcs") + 1)
+			 * -- VPD Instance 'I'
+			 * -- VPD Type String 'S'
+			 * -- VPD data length == 4
+			 * -- property string == phy-interface
+			 */
+			if (readb(p) != 'I')
+				goto next;
+
+			/* finally, check string and length */
+			type = readb(p + 3);
+			if (type == 'B') {
+				if ((klen == 29) && readb(p + 4) == 6 &&
+				    cas_vpd_match(p + 5, 
+						  "local-mac-address")) {
+					if (mac_off++ > offset) 
+						goto next;
+
+					/* set mac address */
+					for (j = 0; j < 6; j++) 
+						dev_addr[j] = 
+							readb(p + 23 + j);
+					goto found_mac;
+				}
+			}
+
+			if (type != 'S')
+				goto next;
+
+#ifdef USE_ENTROPY_DEV
+			if ((klen == 24) && 
+			    cas_vpd_match(p + 5, "entropy-dev") &&
+			    cas_vpd_match(p + 17, "vms110")) {
+				cp->cas_flags |= CAS_FLAG_ENTROPY_DEV;
+				goto next;
+			}
+#endif
+
+			if (found & VPD_FOUND_PHY)
+				goto next;
+
+			if ((klen == 18) && readb(p + 4) == 4 &&
+			    cas_vpd_match(p + 5, "phy-type")) {
+				if (cas_vpd_match(p + 14, "pcs")) {
+					phy_type = CAS_PHY_SERDES;
+					goto found_phy;
+				}
+			}
+			
+			if ((klen == 23) && readb(p + 4) == 4 &&
+			    cas_vpd_match(p + 5, "phy-interface")) {
+				if (cas_vpd_match(p + 19, "pcs")) {
+					phy_type = CAS_PHY_SERDES;
+					goto found_phy;
+				}
+			}
+found_mac:
+			found |= VPD_FOUND_MAC;
+			goto next;
+
+found_phy:
+			found |= VPD_FOUND_PHY;
+
+next:
+			p += klen;
+		}
+		i += len + 3;
+	}
+
+use_random_mac_addr:
+	if (found & VPD_FOUND_MAC)
+		goto done;
+
+	/* Sun MAC prefix then 3 random bytes. */
+	printk(PFX "MAC address not found in ROM VPD\n");
+	dev_addr[0] = 0x08;
+	dev_addr[1] = 0x00;
+	dev_addr[2] = 0x20;
+	get_random_bytes(dev_addr + 3, 3);
+
+done:
+	writel(0, cp->regs + REG_BIM_LOCAL_DEV_EN);
+	return phy_type;
+}
+
+/* check pci invariants */
+static void cas_check_pci_invariants(struct cas *cp)
+{
+	struct pci_dev *pdev = cp->pdev;
+	u8 rev;
+
+	cp->cas_flags = 0;
+	pci_read_config_byte(pdev, PCI_REVISION_ID, &rev);
+	if ((pdev->vendor == PCI_VENDOR_ID_SUN) &&
+	    (pdev->device == PCI_DEVICE_ID_SUN_CASSINI)) {
+		if (rev >= CAS_ID_REVPLUS)
+			cp->cas_flags |= CAS_FLAG_REG_PLUS;
+		if (rev < CAS_ID_REVPLUS02u)
+			cp->cas_flags |= CAS_FLAG_TARGET_ABORT;
+
+		/* Original Cassini supports HW CSUM, but it's not
+		 * enabled by default as it can trigger TX hangs.
+		 */
+		if (rev < CAS_ID_REV2)
+			cp->cas_flags |= CAS_FLAG_NO_HW_CSUM;
+	} else {
+		/* Only sun has original cassini chips.  */
+		cp->cas_flags |= CAS_FLAG_REG_PLUS;
+
+		/* We use a flag because the same phy might be externally
+		 * connected.
+		 */
+		if ((pdev->vendor == PCI_VENDOR_ID_NS) &&
+		    (pdev->device == PCI_DEVICE_ID_NS_SATURN))
+			cp->cas_flags |= CAS_FLAG_SATURN;
+	}
+}
+
+
+static int cas_check_invariants(struct cas *cp)
+{
+	struct pci_dev *pdev = cp->pdev;
+	u32 cfg;
+	int i;
+
+	/* get page size for rx buffers. */
+	cp->page_order = 0; 
+#ifdef USE_PAGE_ORDER
+	if (PAGE_SHIFT < CAS_JUMBO_PAGE_SHIFT) {
+		/* see if we can allocate larger pages */
+		struct page *page = alloc_pages(GFP_ATOMIC, 
+						CAS_JUMBO_PAGE_SHIFT - 
+						PAGE_SHIFT);
+		if (page) {
+			__free_pages(page, CAS_JUMBO_PAGE_SHIFT - PAGE_SHIFT);
+			cp->page_order = CAS_JUMBO_PAGE_SHIFT - PAGE_SHIFT;
+		} else {
+			printk(PFX "MTU limited to %d bytes\n", CAS_MAX_MTU);
+		}
+	}
+#endif
+	cp->page_size = (PAGE_SIZE << cp->page_order);
+
+	/* Fetch the FIFO configurations. */
+	cp->tx_fifo_size = readl(cp->regs + REG_TX_FIFO_SIZE) * 64;
+	cp->rx_fifo_size = RX_FIFO_SIZE;
+
+	/* finish phy determination. MDIO1 takes precedence over MDIO0 if 
+	 * they're both connected.
+	 */
+	cp->phy_type = cas_get_vpd_info(cp, cp->dev->dev_addr, 
+					PCI_SLOT(pdev->devfn));
+	if (cp->phy_type & CAS_PHY_SERDES) {
+		cp->cas_flags |= CAS_FLAG_1000MB_CAP;
+		return 0; /* no more checking needed */
+	} 
+
+	/* MII */
+	cfg = readl(cp->regs + REG_MIF_CFG);
+	if (cfg & MIF_CFG_MDIO_1) {
+		cp->phy_type = CAS_PHY_MII_MDIO1;
+	} else if (cfg & MIF_CFG_MDIO_0) {
+		cp->phy_type = CAS_PHY_MII_MDIO0;
+	}
+
+	cas_mif_poll(cp, 0);
+	writel(PCS_DATAPATH_MODE_MII, cp->regs + REG_PCS_DATAPATH_MODE);
+
+	for (i = 0; i < 32; i++) {
+		u32 phy_id;
+		int j;
+
+		for (j = 0; j < 3; j++) {
+			cp->phy_addr = i;
+			phy_id = cas_phy_read(cp, MII_PHYSID1) << 16;
+			phy_id |= cas_phy_read(cp, MII_PHYSID2);
+			if (phy_id && (phy_id != 0xFFFFFFFF)) {
+				cp->phy_id = phy_id;
+				goto done;
+			}
+		}
+	}
+	printk(KERN_ERR PFX "MII phy did not respond [%08x]\n",
+	       readl(cp->regs + REG_MIF_STATE_MACHINE));
+	return -1;
+
+done:
+	/* see if we can do gigabit */
+	cfg = cas_phy_read(cp, MII_BMSR);
+	if ((cfg & CAS_BMSR_1000_EXTEND) && 
+	    cas_phy_read(cp, CAS_MII_1000_EXTEND))
+		cp->cas_flags |= CAS_FLAG_1000MB_CAP;
+	return 0;
+}
+
+/* Must be invoked under cp->lock. */
+static inline void cas_start_dma(struct cas *cp)
+{
+	int i;
+	u32 val;
+	int txfailed = 0;
+	
+	/* enable dma */
+	val = readl(cp->regs + REG_TX_CFG) | TX_CFG_DMA_EN;
+	writel(val, cp->regs + REG_TX_CFG);
+	val = readl(cp->regs + REG_RX_CFG) | RX_CFG_DMA_EN;
+	writel(val, cp->regs + REG_RX_CFG);
+
+	/* enable the mac */
+	val = readl(cp->regs + REG_MAC_TX_CFG) | MAC_TX_CFG_EN;
+	writel(val, cp->regs + REG_MAC_TX_CFG);
+	val = readl(cp->regs + REG_MAC_RX_CFG) | MAC_RX_CFG_EN;
+	writel(val, cp->regs + REG_MAC_RX_CFG);
+
+	i = STOP_TRIES;
+	while (i-- > 0) {
+		val = readl(cp->regs + REG_MAC_TX_CFG);
+		if ((val & MAC_TX_CFG_EN))
+			break;
+		udelay(10);
+	}
+	if (i < 0) txfailed = 1;
+	i = STOP_TRIES;
+	while (i-- > 0) {
+		val = readl(cp->regs + REG_MAC_RX_CFG);
+		if ((val & MAC_RX_CFG_EN)) {
+			if (txfailed) {
+			  printk(KERN_ERR 
+				 "%s: enabling mac failed [tx:%08x:%08x].\n", 
+				 cp->dev->name,
+				 readl(cp->regs + REG_MIF_STATE_MACHINE),
+				 readl(cp->regs + REG_MAC_STATE_MACHINE));
+			}
+			goto enable_rx_done;
+		}
+		udelay(10);
+	}
+	printk(KERN_ERR "%s: enabling mac failed [%s:%08x:%08x].\n", 
+	       cp->dev->name,
+	       (txfailed? "tx,rx":"rx"),
+	       readl(cp->regs + REG_MIF_STATE_MACHINE),
+	       readl(cp->regs + REG_MAC_STATE_MACHINE));
+
+enable_rx_done:
+	cas_unmask_intr(cp); /* enable interrupts */
+	writel(RX_DESC_RINGN_SIZE(0) - 4, cp->regs + REG_RX_KICK);
+	writel(0, cp->regs + REG_RX_COMP_TAIL);
+
+	if (cp->cas_flags & CAS_FLAG_REG_PLUS) {
+		if (N_RX_DESC_RINGS > 1) 
+			writel(RX_DESC_RINGN_SIZE(1) - 4, 
+			       cp->regs + REG_PLUS_RX_KICK1);
+
+		for (i = 1; i < N_RX_COMP_RINGS; i++) 
+			writel(0, cp->regs + REG_PLUS_RX_COMPN_TAIL(i));
+	}
+}
+
+/* Must be invoked under cp->lock. */
+static void cas_read_pcs_link_mode(struct cas *cp, int *fd, int *spd,
+				   int *pause)
+{
+	u32 val = readl(cp->regs + REG_PCS_MII_LPA);
+	*fd     = (val & PCS_MII_LPA_FD) ? 1 : 0;
+	*pause  = (val & PCS_MII_LPA_SYM_PAUSE) ? 0x01 : 0x00;
+	if (val & PCS_MII_LPA_ASYM_PAUSE)
+		*pause |= 0x10;
+	*spd = 1000;
+}
+
+/* Must be invoked under cp->lock. */
+static void cas_read_mii_link_mode(struct cas *cp, int *fd, int *spd,
+				   int *pause)
+{
+	u32 val;
+
+	*fd = 0;
+	*spd = 10;
+	*pause = 0;
+	
+	/* use GMII registers */
+	val = cas_phy_read(cp, MII_LPA);
+	if (val & CAS_LPA_PAUSE)
+		*pause = 0x01;
+
+	if (val & CAS_LPA_ASYM_PAUSE)
+		*pause |= 0x10;
+
+	if (val & LPA_DUPLEX)
+		*fd = 1;
+	if (val & LPA_100)
+		*spd = 100;
+
+	if (cp->cas_flags & CAS_FLAG_1000MB_CAP) {
+		val = cas_phy_read(cp, CAS_MII_1000_STATUS);
+		if (val & (CAS_LPA_1000FULL | CAS_LPA_1000HALF))
+			*spd = 1000;
+		if (val & CAS_LPA_1000FULL)
+			*fd = 1;
+	}
+}
+
+/* A link-up condition has occurred, initialize and enable the
+ * rest of the chip.
+ *
+ * Must be invoked under cp->lock.
+ */
+static void cas_set_link_modes(struct cas *cp)
+{
+	u32 val;
+	int full_duplex, speed, pause;
+
+	full_duplex = 0;
+	speed = 10;
+	pause = 0;
+
+	if (CAS_PHY_MII(cp->phy_type)) {
+		cas_mif_poll(cp, 0);
+		val = cas_phy_read(cp, MII_BMCR);
+		if (val & BMCR_ANENABLE) {
+			cas_read_mii_link_mode(cp, &full_duplex, &speed, 
+					       &pause);
+		} else {
+			if (val & BMCR_FULLDPLX)
+				full_duplex = 1;
+
+			if (val & BMCR_SPEED100)
+				speed = 100;
+			else if (val & CAS_BMCR_SPEED1000)
+				speed = (cp->cas_flags & CAS_FLAG_1000MB_CAP) ?
+					1000 : 100;
+		}
+		cas_mif_poll(cp, 1);
+
+	} else {
+		val = readl(cp->regs + REG_PCS_MII_CTRL);
+		cas_read_pcs_link_mode(cp, &full_duplex, &speed, &pause);
+		if ((val & PCS_MII_AUTONEG_EN) == 0) {
+			if (val & PCS_MII_CTRL_DUPLEX)
+				full_duplex = 1;
+		}
+	}
+
+	if (netif_msg_link(cp))
+		printk(KERN_INFO "%s: Link up at %d Mbps, %s-duplex.\n",
+		       cp->dev->name, speed, (full_duplex ? "full" : "half"));
+
+	val = MAC_XIF_TX_MII_OUTPUT_EN | MAC_XIF_LINK_LED;
+	if (CAS_PHY_MII(cp->phy_type)) {
+		val |= MAC_XIF_MII_BUFFER_OUTPUT_EN;
+		if (!full_duplex)
+			val |= MAC_XIF_DISABLE_ECHO;
+	}
+	if (full_duplex) 
+		val |= MAC_XIF_FDPLX_LED;
+	if (speed == 1000)
+		val |= MAC_XIF_GMII_MODE;
+	writel(val, cp->regs + REG_MAC_XIF_CFG);
+
+	/* deal with carrier and collision detect. */
+	val = MAC_TX_CFG_IPG_EN;
+	if (full_duplex) {
+		val |= MAC_TX_CFG_IGNORE_CARRIER;
+		val |= MAC_TX_CFG_IGNORE_COLL;
+	} else {
+#ifndef USE_CSMA_CD_PROTO
+		val |= MAC_TX_CFG_NEVER_GIVE_UP_EN;
+		val |= MAC_TX_CFG_NEVER_GIVE_UP_LIM;
+#endif
+	}
+	/* val now set up for REG_MAC_TX_CFG */
+
+	/* If gigabit and half-duplex, enable carrier extension
+	 * mode.  increase slot time to 512 bytes as well. 
+	 * else, disable it and make sure slot time is 64 bytes.
+	 * also activate checksum bug workaround
+	 */
+	if ((speed == 1000) && !full_duplex) {
+		writel(val | MAC_TX_CFG_CARRIER_EXTEND, 
+		       cp->regs + REG_MAC_TX_CFG);
+
+		val = readl(cp->regs + REG_MAC_RX_CFG);
+		val &= ~MAC_RX_CFG_STRIP_FCS; /* checksum workaround */
+		writel(val | MAC_RX_CFG_CARRIER_EXTEND, 
+		       cp->regs + REG_MAC_RX_CFG);
+
+		writel(0x200, cp->regs + REG_MAC_SLOT_TIME);
+
+		cp->crc_size = 4;
+		/* minimum size gigabit frame at half duplex */
+		cp->min_frame_size = CAS_1000MB_MIN_FRAME;
+
+	} else {
+		writel(val, cp->regs + REG_MAC_TX_CFG);
+
+		/* checksum bug workaround. don't strip FCS when in 
+		 * half-duplex mode
+		 */
+		val = readl(cp->regs + REG_MAC_RX_CFG);
+		if (full_duplex) {
+			val |= MAC_RX_CFG_STRIP_FCS;
+			cp->crc_size = 0;
+			cp->min_frame_size = CAS_MIN_MTU;
+		} else {
+			val &= ~MAC_RX_CFG_STRIP_FCS;
+			cp->crc_size = 4;
+			cp->min_frame_size = CAS_MIN_FRAME;
+		}
+		writel(val & ~MAC_RX_CFG_CARRIER_EXTEND, 
+		       cp->regs + REG_MAC_RX_CFG);
+		writel(0x40, cp->regs + REG_MAC_SLOT_TIME);
+	}
+
+	if (netif_msg_link(cp)) {
+		if (pause & 0x01) {
+			printk(KERN_INFO "%s: Pause is enabled "
+			       "(rxfifo: %d off: %d on: %d)\n",
+			       cp->dev->name,
+			       cp->rx_fifo_size,
+			       cp->rx_pause_off,
+			       cp->rx_pause_on);
+		} else if (pause & 0x10) {
+			printk(KERN_INFO "%s: TX pause enabled\n",
+			       cp->dev->name);
+		} else {
+			printk(KERN_INFO "%s: Pause is disabled\n",
+			       cp->dev->name);
+		}
+	}
+
+	val = readl(cp->regs + REG_MAC_CTRL_CFG);
+	val &= ~(MAC_CTRL_CFG_SEND_PAUSE_EN | MAC_CTRL_CFG_RECV_PAUSE_EN);
+	if (pause) { /* symmetric or asymmetric pause */
+		val |= MAC_CTRL_CFG_SEND_PAUSE_EN;
+		if (pause & 0x01) { /* symmetric pause */
+			val |= MAC_CTRL_CFG_RECV_PAUSE_EN;
+		} 
+	}
+	writel(val, cp->regs + REG_MAC_CTRL_CFG);
+	cas_start_dma(cp);
+}
+
+/* Must be invoked under cp->lock. */
+static void cas_init_hw(struct cas *cp, int restart_link)
+{
+	if (restart_link)
+		cas_phy_init(cp);
+
+	cas_init_pause_thresholds(cp);
+	cas_init_mac(cp);
+	cas_init_dma(cp);
+
+	if (restart_link) {
+		/* Default aneg parameters */
+		cp->timer_ticks = 0;
+		cas_begin_auto_negotiation(cp, NULL);
+	} else if (cp->lstate == link_up) {
+		cas_set_link_modes(cp);
+		netif_carrier_on(cp->dev);
+	}
+}
+
+/* Must be invoked under cp->lock. on earlier cassini boards,
+ * SOFT_0 is tied to PCI reset. we use this to force a pci reset,
+ * let it settle out, and then restore pci state.
+ */
+static void cas_hard_reset(struct cas *cp)
+{
+	writel(BIM_LOCAL_DEV_SOFT_0, cp->regs + REG_BIM_LOCAL_DEV_EN); 
+	udelay(20);
+	pci_restore_state(cp->pdev);
+}
+
+
+static void cas_global_reset(struct cas *cp, int blkflag)
+{
+	int limit;
+
+	/* issue a global reset. don't use RSTOUT. */
+	if (blkflag && !CAS_PHY_MII(cp->phy_type)) {
+		/* For PCS, when the blkflag is set, we should set the
+		 * SW_REST_BLOCK_PCS_SLINK bit to prevent the results of
+		 * the last autonegotiation from being cleared.  We'll
+		 * need some special handling if the chip is set into a
+		 * loopback mode.
+		 */
+		writel((SW_RESET_TX | SW_RESET_RX | SW_RESET_BLOCK_PCS_SLINK), 
+		       cp->regs + REG_SW_RESET);
+	} else {
+		writel(SW_RESET_TX | SW_RESET_RX, cp->regs + REG_SW_RESET);
+	}
+
+	/* need to wait at least 3ms before polling register */
+	mdelay(3);
+
+	limit = STOP_TRIES;
+	while (limit-- > 0) {
+		u32 val = readl(cp->regs + REG_SW_RESET);
+		if ((val & (SW_RESET_TX | SW_RESET_RX)) == 0)
+			goto done;
+		udelay(10);
+	}
+	printk(KERN_ERR "%s: sw reset failed.\n", cp->dev->name);
+
+done:
+	/* enable various BIM interrupts */
+	writel(BIM_CFG_DPAR_INTR_ENABLE | BIM_CFG_RMA_INTR_ENABLE | 
+	       BIM_CFG_RTA_INTR_ENABLE, cp->regs + REG_BIM_CFG);
+
+	/* clear out pci error status mask for handled errors.
+	 * we don't deal with DMA counter overflows as they happen
+	 * all the time.
+	 */
+	writel(0xFFFFFFFFU & ~(PCI_ERR_BADACK | PCI_ERR_DTRTO | 
+			       PCI_ERR_OTHER | PCI_ERR_BIM_DMA_WRITE | 
+			       PCI_ERR_BIM_DMA_READ), cp->regs + 
+	       REG_PCI_ERR_STATUS_MASK);
+
+	/* set up for MII by default to address mac rx reset timeout
+	 * issue
+	 */
+	writel(PCS_DATAPATH_MODE_MII, cp->regs + REG_PCS_DATAPATH_MODE);
+}
+
+static void cas_reset(struct cas *cp, int blkflag)
+{
+	u32 val;
+
+	cas_mask_intr(cp);
+	cas_global_reset(cp, blkflag);
+	cas_mac_reset(cp);
+	cas_entropy_reset(cp);
+
+	/* disable dma engines. */
+	val = readl(cp->regs + REG_TX_CFG);
+	val &= ~TX_CFG_DMA_EN;
+	writel(val, cp->regs + REG_TX_CFG);
+
+	val = readl(cp->regs + REG_RX_CFG);
+	val &= ~RX_CFG_DMA_EN;
+	writel(val, cp->regs + REG_RX_CFG);
+
+	/* program header parser */
+	if ((cp->cas_flags & CAS_FLAG_TARGET_ABORT) ||
+	    (CAS_HP_ALT_FIRMWARE == cas_prog_null)) {
+		cas_load_firmware(cp, CAS_HP_FIRMWARE);
+	} else {
+		cas_load_firmware(cp, CAS_HP_ALT_FIRMWARE);
+	}
+
+	/* clear out error registers */
+	spin_lock(&cp->stat_lock[N_TX_RINGS]);
+	cas_clear_mac_err(cp);
+	spin_unlock(&cp->stat_lock[N_TX_RINGS]);
+}
+
+/* Shut down the chip, must be called with pm_sem held.  */
+static void cas_shutdown(struct cas *cp)
+{
+	unsigned long flags;
+
+	/* Make us not-running to avoid timers respawning */
+	cp->hw_running = 0;
+
+	del_timer_sync(&cp->link_timer);
+
+	/* Stop the reset task */
+#if 0
+	while (atomic_read(&cp->reset_task_pending_mtu) ||
+	       atomic_read(&cp->reset_task_pending_spare) ||
+	       atomic_read(&cp->reset_task_pending_all))
+		schedule();
+
+#else
+	while (atomic_read(&cp->reset_task_pending))
+		schedule();
+#endif	
+	/* Actually stop the chip */
+	cas_lock_all_save(cp, flags);
+	cas_reset(cp, 0);
+	if (cp->cas_flags & CAS_FLAG_SATURN)
+		cas_phy_powerdown(cp);
+	cas_unlock_all_restore(cp, flags);
+}
+
+static int cas_change_mtu(struct net_device *dev, int new_mtu)
+{
+	struct cas *cp = netdev_priv(dev);
+
+	if (new_mtu < CAS_MIN_MTU || new_mtu > CAS_MAX_MTU)
+		return -EINVAL;
+
+	dev->mtu = new_mtu;
+	if (!netif_running(dev) || !netif_device_present(dev))
+		return 0;
+
+	/* let the reset task handle it */
+#if 1
+	atomic_inc(&cp->reset_task_pending);
+	if ((cp->phy_type & CAS_PHY_SERDES)) {
+		atomic_inc(&cp->reset_task_pending_all);
+	} else {
+		atomic_inc(&cp->reset_task_pending_mtu);
+	}
+	schedule_work(&cp->reset_task);
+#else
+	atomic_set(&cp->reset_task_pending, (cp->phy_type & CAS_PHY_SERDES) ? 
+		   CAS_RESET_ALL : CAS_RESET_MTU);
+	printk(KERN_ERR "reset called in cas_change_mtu\n");
+	schedule_work(&cp->reset_task);
+#endif
+
+	flush_scheduled_work();
+	return 0;
+}
+
+static void cas_clean_txd(struct cas *cp, int ring)
+{
+	struct cas_tx_desc *txd = cp->init_txds[ring];
+	struct sk_buff *skb, **skbs = cp->tx_skbs[ring];
+	u64 daddr, dlen;
+	int i, size;
+
+	size = TX_DESC_RINGN_SIZE(ring);
+	for (i = 0; i < size; i++) {
+		int frag;
+
+		if (skbs[i] == NULL)
+			continue;
+
+		skb = skbs[i];
+		skbs[i] = NULL;
+
+		for (frag = 0; frag <= skb_shinfo(skb)->nr_frags;  frag++) {
+			int ent = i & (size - 1);
+
+			/* first buffer is never a tiny buffer and so
+			 * needs to be unmapped.
+			 */
+			daddr = le64_to_cpu(txd[ent].buffer);
+			dlen  =  CAS_VAL(TX_DESC_BUFLEN, 
+					 le64_to_cpu(txd[ent].control));
+			pci_unmap_page(cp->pdev, daddr, dlen,
+				       PCI_DMA_TODEVICE);
+
+			if (frag != skb_shinfo(skb)->nr_frags) {
+				i++;
+
+				/* next buffer might by a tiny buffer.
+				 * skip past it.
+				 */
+				ent = i & (size - 1);
+				if (cp->tx_tiny_use[ring][ent].used)
+					i++;
+			}
+		}
+		dev_kfree_skb_any(skb);
+	}
+
+	/* zero out tiny buf usage */
+	memset(cp->tx_tiny_use[ring], 0, size*sizeof(*cp->tx_tiny_use[ring]));
+}
+
+/* freed on close */
+static inline void cas_free_rx_desc(struct cas *cp, int ring)
+{
+	cas_page_t **page = cp->rx_pages[ring];
+	int i, size;
+
+	size = RX_DESC_RINGN_SIZE(ring);
+	for (i = 0; i < size; i++) {
+		if (page[i]) {
+			cas_page_free(cp, page[i]);
+			page[i] = NULL;
+		}
+	}
+}
+
+static void cas_free_rxds(struct cas *cp)
+{
+	int i;
+
+	for (i = 0; i < N_RX_DESC_RINGS; i++)
+		cas_free_rx_desc(cp, i);
+}
+
+/* Must be invoked under cp->lock. */
+static void cas_clean_rings(struct cas *cp)
+{
+	int i;
+
+	/* need to clean all tx rings */
+	memset(cp->tx_old, 0, sizeof(*cp->tx_old)*N_TX_RINGS);
+	memset(cp->tx_new, 0, sizeof(*cp->tx_new)*N_TX_RINGS);
+	for (i = 0; i < N_TX_RINGS; i++)
+		cas_clean_txd(cp, i);
+
+	/* zero out init block */
+	memset(cp->init_block, 0, sizeof(struct cas_init_block));
+	cas_clean_rxds(cp);
+	cas_clean_rxcs(cp);
+}
+
+/* allocated on open */
+static inline int cas_alloc_rx_desc(struct cas *cp, int ring)
+{
+	cas_page_t **page = cp->rx_pages[ring];
+	int size, i = 0;
+
+	size = RX_DESC_RINGN_SIZE(ring);
+	for (i = 0; i < size; i++) {
+		if ((page[i] = cas_page_alloc(cp, GFP_KERNEL)) == NULL) 
+			return -1;
+	}
+	return 0;
+}
+
+static int cas_alloc_rxds(struct cas *cp)
+{
+	int i;
+
+	for (i = 0; i < N_RX_DESC_RINGS; i++) {
+		if (cas_alloc_rx_desc(cp, i) < 0) {
+			cas_free_rxds(cp);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static void cas_reset_task(void *data)
+{
+	struct cas *cp = (struct cas *) data;
+#if 0
+	int pending = atomic_read(&cp->reset_task_pending);
+#else
+	int pending_all = atomic_read(&cp->reset_task_pending_all);
+	int pending_spare = atomic_read(&cp->reset_task_pending_spare);
+	int pending_mtu = atomic_read(&cp->reset_task_pending_mtu);
+
+	if (pending_all == 0 && pending_spare == 0 && pending_mtu == 0) {
+		/* We can have more tasks scheduled than actually
+		 * needed.
+		 */
+		atomic_dec(&cp->reset_task_pending);
+		return;
+	}
+#endif
+	/* The link went down, we reset the ring, but keep
+	 * DMA stopped. Use this function for reset
+	 * on error as well.
+	 */
+	if (cp->hw_running) {
+		unsigned long flags;
+
+		/* Make sure we don't get interrupts or tx packets */
+		netif_device_detach(cp->dev);
+		cas_lock_all_save(cp, flags);
+
+		if (cp->opened) {
+			/* We call cas_spare_recover when we call cas_open.
+			 * but we do not initialize the lists cas_spare_recover
+			 * uses until cas_open is called.
+			 */
+			cas_spare_recover(cp, GFP_ATOMIC);
+		}
+#if 1
+		/* test => only pending_spare set */
+		if (!pending_all && !pending_mtu)
+			goto done;
+#else
+		if (pending == CAS_RESET_SPARE)
+			goto done;
+#endif
+		/* when pending == CAS_RESET_ALL, the following
+		 * call to cas_init_hw will restart auto negotiation.
+		 * Setting the second argument of cas_reset to
+		 * !(pending == CAS_RESET_ALL) will set this argument
+		 * to 1 (avoiding reinitializing the PHY for the normal 
+		 * PCS case) when auto negotiation is not restarted.
+		 */
+#if 1
+		cas_reset(cp, !(pending_all > 0));
+		if (cp->opened)
+			cas_clean_rings(cp);
+		cas_init_hw(cp, (pending_all > 0));
+#else
+		cas_reset(cp, !(pending == CAS_RESET_ALL));
+		if (cp->opened)
+			cas_clean_rings(cp);
+		cas_init_hw(cp, pending == CAS_RESET_ALL);
+#endif
+
+done:
+		cas_unlock_all_restore(cp, flags);
+		netif_device_attach(cp->dev);
+	}
+#if 1
+	atomic_sub(pending_all, &cp->reset_task_pending_all);
+	atomic_sub(pending_spare, &cp->reset_task_pending_spare);
+	atomic_sub(pending_mtu, &cp->reset_task_pending_mtu);
+	atomic_dec(&cp->reset_task_pending);
+#else
+	atomic_set(&cp->reset_task_pending, 0);
+#endif
+}
+
+static void cas_link_timer(unsigned long data)
+{
+	struct cas *cp = (struct cas *) data;
+	int mask, pending = 0, reset = 0;
+	unsigned long flags;
+
+	if (link_transition_timeout != 0 &&
+	    cp->link_transition_jiffies_valid &&
+	    ((jiffies - cp->link_transition_jiffies) > 
+	      (link_transition_timeout))) {
+		/* One-second counter so link-down workaround doesn't 
+		 * cause resets to occur so fast as to fool the switch
+		 * into thinking the link is down.
+		 */
+		cp->link_transition_jiffies_valid = 0;
+	}
+
+	if (!cp->hw_running)
+		return;
+
+	spin_lock_irqsave(&cp->lock, flags);
+	cas_lock_tx(cp);
+	cas_entropy_gather(cp);
+
+	/* If the link task is still pending, we just
+	 * reschedule the link timer
+	 */
+#if 1
+	if (atomic_read(&cp->reset_task_pending_all) ||
+	    atomic_read(&cp->reset_task_pending_spare) ||
+	    atomic_read(&cp->reset_task_pending_mtu)) 
+		goto done;
+#else
+	if (atomic_read(&cp->reset_task_pending)) 
+		goto done;
+#endif
+
+	/* check for rx cleaning */
+	if ((mask = (cp->cas_flags & CAS_FLAG_RXD_POST_MASK))) {
+		int i, rmask;
+
+		for (i = 0; i < MAX_RX_DESC_RINGS; i++) {
+			rmask = CAS_FLAG_RXD_POST(i);
+			if ((mask & rmask) == 0)
+				continue;
+
+			/* post_rxds will do a mod_timer */
+			if (cas_post_rxds_ringN(cp, i, cp->rx_last[i]) < 0) {
+				pending = 1;
+				continue;
+			}
+			cp->cas_flags &= ~rmask;
+		}
+	}
+
+	if (CAS_PHY_MII(cp->phy_type)) {
+		u16 bmsr;
+		cas_mif_poll(cp, 0);
+		bmsr = cas_phy_read(cp, MII_BMSR);
+		/* WTZ: Solaris driver reads this twice, but that
+		 * may be due to the PCS case and the use of a
+		 * common implementation. Read it twice here to be
+		 * safe.
+		 */
+		bmsr = cas_phy_read(cp, MII_BMSR);
+		cas_mif_poll(cp, 1);
+		readl(cp->regs + REG_MIF_STATUS); /* avoid dups */
+		reset = cas_mii_link_check(cp, bmsr);
+	} else {
+		reset = cas_pcs_link_check(cp);
+	}
+
+	if (reset)
+		goto done;
+
+	/* check for tx state machine confusion */
+	if ((readl(cp->regs + REG_MAC_TX_STATUS) & MAC_TX_FRAME_XMIT) == 0) {
+		u32 val = readl(cp->regs + REG_MAC_STATE_MACHINE);
+		u32 wptr, rptr;
+		int tlm  = CAS_VAL(MAC_SM_TLM, val);
+
+		if (((tlm == 0x5) || (tlm == 0x3)) &&
+		    (CAS_VAL(MAC_SM_ENCAP_SM, val) == 0)) {
+			if (netif_msg_tx_err(cp))
+				printk(KERN_DEBUG "%s: tx err: "
+				       "MAC_STATE[%08x]\n",
+				       cp->dev->name, val);
+			reset = 1;
+			goto done;
+		}
+
+		val  = readl(cp->regs + REG_TX_FIFO_PKT_CNT);
+		wptr = readl(cp->regs + REG_TX_FIFO_WRITE_PTR);
+		rptr = readl(cp->regs + REG_TX_FIFO_READ_PTR);
+		if ((val == 0) && (wptr != rptr)) {
+			if (netif_msg_tx_err(cp))
+				printk(KERN_DEBUG "%s: tx err: "
+				       "TX_FIFO[%08x:%08x:%08x]\n",
+				       cp->dev->name, val, wptr, rptr);
+			reset = 1;
+		}
+
+		if (reset)
+			cas_hard_reset(cp);
+	}
+
+done:
+	if (reset) {
+#if 1
+		atomic_inc(&cp->reset_task_pending);
+		atomic_inc(&cp->reset_task_pending_all);
+		schedule_work(&cp->reset_task);
+#else
+		atomic_set(&cp->reset_task_pending, CAS_RESET_ALL);
+		printk(KERN_ERR "reset called in cas_link_timer\n");
+		schedule_work(&cp->reset_task);
+#endif
+	}
+
+	if (!pending)
+		mod_timer(&cp->link_timer, jiffies + CAS_LINK_TIMEOUT);
+	cas_unlock_tx(cp);
+	spin_unlock_irqrestore(&cp->lock, flags);
+}
+
+/* tiny buffers are used to avoid target abort issues with 
+ * older cassini's
+ */
+static void cas_tx_tiny_free(struct cas *cp)
+{
+	struct pci_dev *pdev = cp->pdev;
+	int i;
+
+	for (i = 0; i < N_TX_RINGS; i++) {
+		if (!cp->tx_tiny_bufs[i])
+			continue;
+
+		pci_free_consistent(pdev, TX_TINY_BUF_BLOCK, 
+				    cp->tx_tiny_bufs[i],
+				    cp->tx_tiny_dvma[i]);
+		cp->tx_tiny_bufs[i] = NULL;
+	}
+}
+
+static int cas_tx_tiny_alloc(struct cas *cp)
+{
+	struct pci_dev *pdev = cp->pdev;
+	int i;
+
+	for (i = 0; i < N_TX_RINGS; i++) {
+		cp->tx_tiny_bufs[i] = 
+			pci_alloc_consistent(pdev, TX_TINY_BUF_BLOCK,
+					     &cp->tx_tiny_dvma[i]);
+		if (!cp->tx_tiny_bufs[i]) {
+			cas_tx_tiny_free(cp);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+
+static int cas_open(struct net_device *dev)
+{
+	struct cas *cp = netdev_priv(dev);
+	int hw_was_up, err;
+	unsigned long flags;
+
+	down(&cp->pm_sem);
+
+	hw_was_up = cp->hw_running;
+
+	/* The power-management semaphore protects the hw_running
+	 * etc. state so it is safe to do this bit without cp->lock
+	 */
+	if (!cp->hw_running) {
+		/* Reset the chip */
+		cas_lock_all_save(cp, flags);
+		/* We set the second arg to cas_reset to zero
+		 * because cas_init_hw below will have its second 
+		 * argument set to non-zero, which will force
+		 * autonegotiation to start.
+		 */
+		cas_reset(cp, 0);
+		cp->hw_running = 1;
+		cas_unlock_all_restore(cp, flags);
+	}
+
+	if (cas_tx_tiny_alloc(cp) < 0)
+		return -ENOMEM;
+
+	/* alloc rx descriptors */
+	err = -ENOMEM;
+	if (cas_alloc_rxds(cp) < 0)
+		goto err_tx_tiny;
+	
+	/* allocate spares */
+	cas_spare_init(cp);
+	cas_spare_recover(cp, GFP_KERNEL);
+
+	/* We can now request the interrupt as we know it's masked
+	 * on the controller. cassini+ has up to 4 interrupts
+	 * that can be used, but you need to do explicit pci interrupt 
+	 * mapping to expose them
+	 */
+	if (request_irq(cp->pdev->irq, cas_interrupt,
+			SA_SHIRQ, dev->name, (void *) dev)) {
+		printk(KERN_ERR "%s: failed to request irq !\n", 
+		       cp->dev->name);
+		err = -EAGAIN;
+		goto err_spare;
+	}
+
+	/* init hw */
+	cas_lock_all_save(cp, flags);
+	cas_clean_rings(cp);
+	cas_init_hw(cp, !hw_was_up);
+	cp->opened = 1;
+	cas_unlock_all_restore(cp, flags);
+
+	netif_start_queue(dev);
+	up(&cp->pm_sem);
+	return 0;
+
+err_spare:
+	cas_spare_free(cp);
+	cas_free_rxds(cp);
+err_tx_tiny:
+	cas_tx_tiny_free(cp);
+	up(&cp->pm_sem);
+	return err;
+}
+
+static int cas_close(struct net_device *dev)
+{
+	unsigned long flags;
+	struct cas *cp = netdev_priv(dev);
+
+	/* Make sure we don't get distracted by suspend/resume */
+	down(&cp->pm_sem);
+
+	netif_stop_queue(dev);
+
+	/* Stop traffic, mark us closed */
+	cas_lock_all_save(cp, flags);
+	cp->opened = 0;	
+	cas_reset(cp, 0);
+	cas_phy_init(cp); 
+	cas_begin_auto_negotiation(cp, NULL);
+	cas_clean_rings(cp);
+	cas_unlock_all_restore(cp, flags);
+
+	free_irq(cp->pdev->irq, (void *) dev);
+	cas_spare_free(cp);
+	cas_free_rxds(cp);
+	cas_tx_tiny_free(cp);
+	up(&cp->pm_sem);
+	return 0;
+}
+
+static struct {
+	const char name[ETH_GSTRING_LEN];
+} ethtool_cassini_statnames[] = {
+	{"collisions"},
+	{"rx_bytes"},
+	{"rx_crc_errors"},
+	{"rx_dropped"},
+	{"rx_errors"},
+	{"rx_fifo_errors"},
+	{"rx_frame_errors"},
+	{"rx_length_errors"},
+	{"rx_over_errors"},
+	{"rx_packets"},
+	{"tx_aborted_errors"},
+	{"tx_bytes"},
+	{"tx_dropped"},
+	{"tx_errors"},
+	{"tx_fifo_errors"},
+	{"tx_packets"}
+};
+#define CAS_NUM_STAT_KEYS (sizeof(ethtool_cassini_statnames)/ETH_GSTRING_LEN)
+
+static struct {
+	const int offsets;	/* neg. values for 2nd arg to cas_read_phy */
+} ethtool_register_table[] = {
+	{-MII_BMSR},
+	{-MII_BMCR},
+	{REG_CAWR},
+	{REG_INF_BURST},
+	{REG_BIM_CFG},
+	{REG_RX_CFG},
+	{REG_HP_CFG},
+	{REG_MAC_TX_CFG},
+	{REG_MAC_RX_CFG},
+	{REG_MAC_CTRL_CFG},
+	{REG_MAC_XIF_CFG},
+	{REG_MIF_CFG},
+	{REG_PCS_CFG},
+	{REG_SATURN_PCFG},
+	{REG_PCS_MII_STATUS},
+	{REG_PCS_STATE_MACHINE},
+	{REG_MAC_COLL_EXCESS},
+	{REG_MAC_COLL_LATE}
+};
+#define CAS_REG_LEN 	(sizeof(ethtool_register_table)/sizeof(int))
+#define CAS_MAX_REGS 	(sizeof (u32)*CAS_REG_LEN)
+
+static void cas_read_regs(struct cas *cp, u8 *ptr, int len)
+{
+	u8 *p;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cp->lock, flags);
+	for (i = 0, p = ptr; i < len ; i ++, p += sizeof(u32)) {
+		u16 hval;
+		u32 val;
+		if (ethtool_register_table[i].offsets < 0) {
+			hval = cas_phy_read(cp,
+				    -ethtool_register_table[i].offsets);
+			val = hval;
+		} else {
+			val= readl(cp->regs+ethtool_register_table[i].offsets);
+		}
+		memcpy(p, (u8 *)&val, sizeof(u32));
+	}
+	spin_unlock_irqrestore(&cp->lock, flags);
+}
+
+static struct net_device_stats *cas_get_stats(struct net_device *dev)
+{
+	struct cas *cp = netdev_priv(dev);
+	struct net_device_stats *stats = cp->net_stats;
+	unsigned long flags;
+	int i;
+	unsigned long tmp;
+
+	/* we collate all of the stats into net_stats[N_TX_RING] */
+	if (!cp->hw_running)
+		return stats + N_TX_RINGS;
+	
+	/* collect outstanding stats */
+	/* WTZ: the Cassini spec gives these as 16 bit counters but
+	 * stored in 32-bit words.  Added a mask of 0xffff to be safe,
+	 * in case the chip somehow puts any garbage in the other bits.
+	 * Also, counter usage didn't seem to mach what Adrian did
+	 * in the parts of the code that set these quantities. Made
+	 * that consistent.
+	 */
+	spin_lock_irqsave(&cp->stat_lock[N_TX_RINGS], flags);
+	stats[N_TX_RINGS].rx_crc_errors += 
+	  readl(cp->regs + REG_MAC_FCS_ERR) & 0xffff;
+	stats[N_TX_RINGS].rx_frame_errors += 
+		readl(cp->regs + REG_MAC_ALIGN_ERR) &0xffff;
+	stats[N_TX_RINGS].rx_length_errors += 
+		readl(cp->regs + REG_MAC_LEN_ERR) & 0xffff;
+#if 1
+	tmp = (readl(cp->regs + REG_MAC_COLL_EXCESS) & 0xffff) +
+		(readl(cp->regs + REG_MAC_COLL_LATE) & 0xffff);
+	stats[N_TX_RINGS].tx_aborted_errors += tmp;
+	stats[N_TX_RINGS].collisions +=
+	  tmp + (readl(cp->regs + REG_MAC_COLL_NORMAL) & 0xffff);
+#else
+	stats[N_TX_RINGS].tx_aborted_errors += 
+		readl(cp->regs + REG_MAC_COLL_EXCESS);
+	stats[N_TX_RINGS].collisions += readl(cp->regs + REG_MAC_COLL_EXCESS) +
+		readl(cp->regs + REG_MAC_COLL_LATE);
+#endif
+	cas_clear_mac_err(cp);
+
+	/* saved bits that are unique to ring 0 */
+	spin_lock(&cp->stat_lock[0]);
+	stats[N_TX_RINGS].collisions        += stats[0].collisions;
+	stats[N_TX_RINGS].rx_over_errors    += stats[0].rx_over_errors;
+	stats[N_TX_RINGS].rx_frame_errors   += stats[0].rx_frame_errors;
+	stats[N_TX_RINGS].rx_fifo_errors    += stats[0].rx_fifo_errors;
+	stats[N_TX_RINGS].tx_aborted_errors += stats[0].tx_aborted_errors;
+	stats[N_TX_RINGS].tx_fifo_errors    += stats[0].tx_fifo_errors;
+	spin_unlock(&cp->stat_lock[0]);
+
+	for (i = 0; i < N_TX_RINGS; i++) {
+		spin_lock(&cp->stat_lock[i]);
+		stats[N_TX_RINGS].rx_length_errors += 
+			stats[i].rx_length_errors;
+		stats[N_TX_RINGS].rx_crc_errors += stats[i].rx_crc_errors;
+		stats[N_TX_RINGS].rx_packets    += stats[i].rx_packets;
+		stats[N_TX_RINGS].tx_packets    += stats[i].tx_packets;
+		stats[N_TX_RINGS].rx_bytes      += stats[i].rx_bytes;
+		stats[N_TX_RINGS].tx_bytes      += stats[i].tx_bytes;
+		stats[N_TX_RINGS].rx_errors     += stats[i].rx_errors;
+		stats[N_TX_RINGS].tx_errors     += stats[i].tx_errors;
+		stats[N_TX_RINGS].rx_dropped    += stats[i].rx_dropped;
+		stats[N_TX_RINGS].tx_dropped    += stats[i].tx_dropped;
+		memset(stats + i, 0, sizeof(struct net_device_stats));
+		spin_unlock(&cp->stat_lock[i]);
+	}
+	spin_unlock_irqrestore(&cp->stat_lock[N_TX_RINGS], flags);
+	return stats + N_TX_RINGS;
+}
+
+
+static void cas_set_multicast(struct net_device *dev)
+{
+	struct cas *cp = netdev_priv(dev);
+	u32 rxcfg, rxcfg_new;
+	unsigned long flags;
+	int limit = STOP_TRIES;
+	
+	if (!cp->hw_running)
+		return;
+		
+	spin_lock_irqsave(&cp->lock, flags);
+	rxcfg = readl(cp->regs + REG_MAC_RX_CFG);
+
+	/* disable RX MAC and wait for completion */
+	writel(rxcfg & ~MAC_RX_CFG_EN, cp->regs + REG_MAC_RX_CFG);
+	while (readl(cp->regs + REG_MAC_RX_CFG) & MAC_RX_CFG_EN) {
+		if (!limit--)
+			break;
+		udelay(10);
+	}
+
+	/* disable hash filter and wait for completion */
+	limit = STOP_TRIES;
+	rxcfg &= ~(MAC_RX_CFG_PROMISC_EN | MAC_RX_CFG_HASH_FILTER_EN);
+	writel(rxcfg & ~MAC_RX_CFG_EN, cp->regs + REG_MAC_RX_CFG);
+	while (readl(cp->regs + REG_MAC_RX_CFG) & MAC_RX_CFG_HASH_FILTER_EN) {
+		if (!limit--)
+			break;
+		udelay(10);
+	}
+
+	/* program hash filters */
+	cp->mac_rx_cfg = rxcfg_new = cas_setup_multicast(cp);
+	rxcfg |= rxcfg_new;
+	writel(rxcfg, cp->regs + REG_MAC_RX_CFG);
+	spin_unlock_irqrestore(&cp->lock, flags);
+}
+
+static void cas_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+	struct cas *cp = netdev_priv(dev);
+	strncpy(info->driver, DRV_MODULE_NAME, ETHTOOL_BUSINFO_LEN);
+	strncpy(info->version, DRV_MODULE_VERSION, ETHTOOL_BUSINFO_LEN);
+	info->fw_version[0] = '\0';
+	strncpy(info->bus_info, pci_name(cp->pdev), ETHTOOL_BUSINFO_LEN);
+	info->regdump_len = cp->casreg_len < CAS_MAX_REGS ?
+		cp->casreg_len : CAS_MAX_REGS;
+	info->n_stats = CAS_NUM_STAT_KEYS;
+}
+
+static int cas_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct cas *cp = netdev_priv(dev);
+	u16 bmcr;
+	int full_duplex, speed, pause;
+	unsigned long flags;
+	enum link_state linkstate = link_up;
+
+	cmd->advertising = 0;
+	cmd->supported = SUPPORTED_Autoneg;
+	if (cp->cas_flags & CAS_FLAG_1000MB_CAP) {
+		cmd->supported |= SUPPORTED_1000baseT_Full;
+		cmd->advertising |= ADVERTISED_1000baseT_Full;
+	}
+
+	/* Record PHY settings if HW is on. */
+	spin_lock_irqsave(&cp->lock, flags);
+	bmcr = 0;
+	linkstate = cp->lstate;
+	if (CAS_PHY_MII(cp->phy_type)) {
+		cmd->port = PORT_MII;
+		cmd->transceiver = (cp->cas_flags & CAS_FLAG_SATURN) ?
+			XCVR_INTERNAL : XCVR_EXTERNAL;
+		cmd->phy_address = cp->phy_addr;
+		cmd->advertising |= ADVERTISED_TP | ADVERTISED_MII |
+			ADVERTISED_10baseT_Half | 
+			ADVERTISED_10baseT_Full | 
+			ADVERTISED_100baseT_Half | 
+			ADVERTISED_100baseT_Full;
+
+		cmd->supported |=
+			(SUPPORTED_10baseT_Half | 
+			 SUPPORTED_10baseT_Full |
+			 SUPPORTED_100baseT_Half | 
+			 SUPPORTED_100baseT_Full |
+			 SUPPORTED_TP | SUPPORTED_MII);
+
+		if (cp->hw_running) {
+			cas_mif_poll(cp, 0);
+			bmcr = cas_phy_read(cp, MII_BMCR);
+			cas_read_mii_link_mode(cp, &full_duplex, 
+					       &speed, &pause);
+			cas_mif_poll(cp, 1);
+		}
+
+	} else {
+		cmd->port = PORT_FIBRE;
+		cmd->transceiver = XCVR_INTERNAL;
+		cmd->phy_address = 0;
+		cmd->supported   |= SUPPORTED_FIBRE;
+		cmd->advertising |= ADVERTISED_FIBRE;
+
+		if (cp->hw_running) {
+			/* pcs uses the same bits as mii */ 
+			bmcr = readl(cp->regs + REG_PCS_MII_CTRL);
+			cas_read_pcs_link_mode(cp, &full_duplex, 
+					       &speed, &pause);
+		}
+	}
+	spin_unlock_irqrestore(&cp->lock, flags);
+
+	if (bmcr & BMCR_ANENABLE) {
+		cmd->advertising |= ADVERTISED_Autoneg;
+		cmd->autoneg = AUTONEG_ENABLE;
+		cmd->speed = ((speed == 10) ?
+			      SPEED_10 :
+			      ((speed == 1000) ?
+			       SPEED_1000 : SPEED_100));
+		cmd->duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
+	} else {
+		cmd->autoneg = AUTONEG_DISABLE;
+		cmd->speed =
+			(bmcr & CAS_BMCR_SPEED1000) ?
+			SPEED_1000 : 
+			((bmcr & BMCR_SPEED100) ? SPEED_100: 
+			 SPEED_10);
+		cmd->duplex =
+			(bmcr & BMCR_FULLDPLX) ?
+			DUPLEX_FULL : DUPLEX_HALF;
+	}
+	if (linkstate != link_up) {
+		/* Force these to "unknown" if the link is not up and
+		 * autonogotiation in enabled. We can set the link 
+		 * speed to 0, but not cmd->duplex,
+		 * because its legal values are 0 and 1.  Ethtool will
+		 * print the value reported in parentheses after the
+		 * word "Unknown" for unrecognized values.
+		 *
+		 * If in forced mode, we report the speed and duplex
+		 * settings that we configured.
+		 */
+		if (cp->link_cntl & BMCR_ANENABLE) {
+			cmd->speed = 0;
+			cmd->duplex = 0xff;
+		} else {
+			cmd->speed = SPEED_10;
+			if (cp->link_cntl & BMCR_SPEED100) {
+				cmd->speed = SPEED_100;
+			} else if (cp->link_cntl & CAS_BMCR_SPEED1000) {
+				cmd->speed = SPEED_1000;
+			}
+			cmd->duplex = (cp->link_cntl & BMCR_FULLDPLX)?
+				DUPLEX_FULL : DUPLEX_HALF;
+		}
+	}
+	return 0;
+}
+
+static int cas_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct cas *cp = netdev_priv(dev);
+	unsigned long flags;
+
+	/* Verify the settings we care about. */
+	if (cmd->autoneg != AUTONEG_ENABLE &&
+	    cmd->autoneg != AUTONEG_DISABLE)
+		return -EINVAL;
+
+	if (cmd->autoneg == AUTONEG_DISABLE &&
+	    ((cmd->speed != SPEED_1000 &&
+	      cmd->speed != SPEED_100 &&
+	      cmd->speed != SPEED_10) ||
+	     (cmd->duplex != DUPLEX_HALF &&
+	      cmd->duplex != DUPLEX_FULL)))
+		return -EINVAL;
+
+	/* Apply settings and restart link process. */
+	spin_lock_irqsave(&cp->lock, flags);
+	cas_begin_auto_negotiation(cp, cmd);
+	spin_unlock_irqrestore(&cp->lock, flags);
+	return 0;
+}
+
+static int cas_nway_reset(struct net_device *dev)
+{
+	struct cas *cp = netdev_priv(dev);
+	unsigned long flags;
+
+	if ((cp->link_cntl & BMCR_ANENABLE) == 0)
+		return -EINVAL;
+
+	/* Restart link process. */
+	spin_lock_irqsave(&cp->lock, flags);
+	cas_begin_auto_negotiation(cp, NULL);
+	spin_unlock_irqrestore(&cp->lock, flags);
+
+	return 0;
+}
+
+static u32 cas_get_link(struct net_device *dev)
+{
+	struct cas *cp = netdev_priv(dev);
+	return cp->lstate == link_up;
+}
+
+static u32 cas_get_msglevel(struct net_device *dev)
+{
+	struct cas *cp = netdev_priv(dev);
+	return cp->msg_enable;
+}
+
+static void cas_set_msglevel(struct net_device *dev, u32 value)
+{
+	struct cas *cp = netdev_priv(dev);
+	cp->msg_enable = value;
+}
+
+static int cas_get_regs_len(struct net_device *dev)
+{
+	struct cas *cp = netdev_priv(dev);
+	return cp->casreg_len < CAS_MAX_REGS ? cp->casreg_len: CAS_MAX_REGS;
+}
+
+static void cas_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+			     void *p)
+{
+	struct cas *cp = netdev_priv(dev);
+	regs->version = 0;
+	/* cas_read_regs handles locks (cp->lock).  */
+	cas_read_regs(cp, p, regs->len / sizeof(u32));
+}
+
+static int cas_get_stats_count(struct net_device *dev)
+{
+	return CAS_NUM_STAT_KEYS;
+}
+
+static void cas_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+	 memcpy(data, &ethtool_cassini_statnames, 
+					 CAS_NUM_STAT_KEYS * ETH_GSTRING_LEN);
+}
+
+static void cas_get_ethtool_stats(struct net_device *dev,
+				      struct ethtool_stats *estats, u64 *data)
+{
+	struct cas *cp = netdev_priv(dev);
+	struct net_device_stats *stats = cas_get_stats(cp->dev);
+	int i = 0;
+	data[i++] = stats->collisions;
+	data[i++] = stats->rx_bytes;
+	data[i++] = stats->rx_crc_errors;
+	data[i++] = stats->rx_dropped;
+	data[i++] = stats->rx_errors;
+	data[i++] = stats->rx_fifo_errors;
+	data[i++] = stats->rx_frame_errors;
+	data[i++] = stats->rx_length_errors;
+	data[i++] = stats->rx_over_errors;
+	data[i++] = stats->rx_packets;
+	data[i++] = stats->tx_aborted_errors;
+	data[i++] = stats->tx_bytes;
+	data[i++] = stats->tx_dropped;
+	data[i++] = stats->tx_errors;
+	data[i++] = stats->tx_fifo_errors;
+	data[i++] = stats->tx_packets;
+	BUG_ON(i != CAS_NUM_STAT_KEYS);
+}
+
+static struct ethtool_ops cas_ethtool_ops = {
+	.get_drvinfo		= cas_get_drvinfo,
+	.get_settings		= cas_get_settings,
+	.set_settings		= cas_set_settings,
+	.nway_reset		= cas_nway_reset,
+	.get_link		= cas_get_link,
+	.get_msglevel		= cas_get_msglevel,
+	.set_msglevel		= cas_set_msglevel,
+	.get_regs_len		= cas_get_regs_len,
+	.get_regs		= cas_get_regs,
+	.get_stats_count	= cas_get_stats_count,
+	.get_strings		= cas_get_strings,
+	.get_ethtool_stats	= cas_get_ethtool_stats,
+};
+
+static int cas_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct cas *cp = netdev_priv(dev);
+	struct mii_ioctl_data *data = if_mii(ifr);
+	unsigned long flags;
+	int rc = -EOPNOTSUPP;
+	
+	/* Hold the PM semaphore while doing ioctl's or we may collide
+	 * with open/close and power management and oops.
+	 */
+	down(&cp->pm_sem);
+	switch (cmd) {
+	case SIOCGMIIPHY:		/* Get address of MII PHY in use. */
+		data->phy_id = cp->phy_addr;
+		/* Fallthrough... */
+
+	case SIOCGMIIREG:		/* Read MII PHY register. */
+		spin_lock_irqsave(&cp->lock, flags);
+		cas_mif_poll(cp, 0);
+		data->val_out = cas_phy_read(cp, data->reg_num & 0x1f);
+		cas_mif_poll(cp, 1);
+		spin_unlock_irqrestore(&cp->lock, flags);
+		rc = 0;
+		break;
+
+	case SIOCSMIIREG:		/* Write MII PHY register. */
+		if (!capable(CAP_NET_ADMIN)) {
+			rc = -EPERM;
+			break;
+		}
+		spin_lock_irqsave(&cp->lock, flags);
+		cas_mif_poll(cp, 0);
+		rc = cas_phy_write(cp, data->reg_num & 0x1f, data->val_in);
+		cas_mif_poll(cp, 1);
+		spin_unlock_irqrestore(&cp->lock, flags);
+		break;
+	default:
+		break;
+	};
+
+	up(&cp->pm_sem);
+	return rc;
+}
+
+static int __devinit cas_init_one(struct pci_dev *pdev,
+				  const struct pci_device_id *ent)
+{
+	static int cas_version_printed = 0;
+	unsigned long casreg_base, casreg_len;
+	struct net_device *dev;
+	struct cas *cp;
+	int i, err, pci_using_dac;
+	u16 pci_cmd;
+	u8 orig_cacheline_size = 0, cas_cacheline_size = 0;
+
+	if (cas_version_printed++ == 0)
+		printk(KERN_INFO "%s", version);
+
+	err = pci_enable_device(pdev);
+	if (err) {
+		printk(KERN_ERR PFX "Cannot enable PCI device, "
+		       "aborting.\n");
+		return err;
+	}
+
+	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
+		printk(KERN_ERR PFX "Cannot find proper PCI device "
+		       "base address, aborting.\n");
+		err = -ENODEV;
+		goto err_out_disable_pdev;
+	}
+
+	dev = alloc_etherdev(sizeof(*cp));
+	if (!dev) {
+		printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n");
+		err = -ENOMEM;
+		goto err_out_disable_pdev;
+	}
+	SET_MODULE_OWNER(dev);
+	SET_NETDEV_DEV(dev, &pdev->dev);
+
+	err = pci_request_regions(pdev, dev->name);
+	if (err) {
+		printk(KERN_ERR PFX "Cannot obtain PCI resources, "
+		       "aborting.\n");
+		goto err_out_free_netdev;
+	}
+	pci_set_master(pdev);
+
+	/* we must always turn on parity response or else parity
+	 * doesn't get generated properly. disable SERR/PERR as well.
+	 * in addition, we want to turn MWI on.
+	 */
+	pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd);
+	pci_cmd &= ~PCI_COMMAND_SERR;
+	pci_cmd |= PCI_COMMAND_PARITY;
+	pci_write_config_word(pdev, PCI_COMMAND, pci_cmd);
+	pci_set_mwi(pdev);
+	/*
+	 * On some architectures, the default cache line size set
+	 * by pci_set_mwi reduces perforamnce.  We have to increase
+	 * it for this case.  To start, we'll print some configuration
+	 * data.
+	 */
+#if 1
+	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE,
+			     &orig_cacheline_size);
+	if (orig_cacheline_size < CAS_PREF_CACHELINE_SIZE) {
+		cas_cacheline_size = 
+			(CAS_PREF_CACHELINE_SIZE < SMP_CACHE_BYTES) ? 
+			CAS_PREF_CACHELINE_SIZE : SMP_CACHE_BYTES;
+		if (pci_write_config_byte(pdev, 
+					  PCI_CACHE_LINE_SIZE, 
+					  cas_cacheline_size)) {
+			printk(KERN_ERR PFX "Could not set PCI cache "
+			       "line size\n");
+			goto err_write_cacheline;
+		}
+	}
+#endif
+
+
+	/* Configure DMA attributes. */
+	if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) {
+		pci_using_dac = 1;
+		err = pci_set_consistent_dma_mask(pdev,
+						  DMA_64BIT_MASK);
+		if (err < 0) {
+			printk(KERN_ERR PFX "Unable to obtain 64-bit DMA "
+			       "for consistent allocations\n");
+			goto err_out_free_res;
+		}
+
+	} else {
+		err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+		if (err) {
+			printk(KERN_ERR PFX "No usable DMA configuration, "
+			       "aborting.\n");
+			goto err_out_free_res;
+		}
+		pci_using_dac = 0;
+	}
+
+	casreg_base = pci_resource_start(pdev, 0);
+	casreg_len = pci_resource_len(pdev, 0);
+
+	cp = netdev_priv(dev);
+	cp->pdev = pdev;
+#if 1
+	/* A value of 0 indicates we never explicitly set it */
+	cp->orig_cacheline_size = cas_cacheline_size ? orig_cacheline_size: 0;
+#endif
+	cp->dev = dev;
+	cp->msg_enable = (cassini_debug < 0) ? CAS_DEF_MSG_ENABLE : 
+	  cassini_debug;
+
+	cp->link_transition = LINK_TRANSITION_UNKNOWN;
+	cp->link_transition_jiffies_valid = 0;
+
+	spin_lock_init(&cp->lock);
+	spin_lock_init(&cp->rx_inuse_lock);
+	spin_lock_init(&cp->rx_spare_lock);
+	for (i = 0; i < N_TX_RINGS; i++) {
+		spin_lock_init(&cp->stat_lock[i]);
+		spin_lock_init(&cp->tx_lock[i]);
+	}
+	spin_lock_init(&cp->stat_lock[N_TX_RINGS]);
+	init_MUTEX(&cp->pm_sem);
+
+	init_timer(&cp->link_timer);
+	cp->link_timer.function = cas_link_timer;
+	cp->link_timer.data = (unsigned long) cp;
+
+#if 1
+	/* Just in case the implementation of atomic operations
+	 * change so that an explicit initialization is necessary.
+	 */
+	atomic_set(&cp->reset_task_pending, 0);
+	atomic_set(&cp->reset_task_pending_all, 0);
+	atomic_set(&cp->reset_task_pending_spare, 0);
+	atomic_set(&cp->reset_task_pending_mtu, 0);
+#endif
+	INIT_WORK(&cp->reset_task, cas_reset_task, cp);
+
+	/* Default link parameters */
+	if (link_mode >= 0 && link_mode <= 6)
+		cp->link_cntl = link_modes[link_mode];
+	else
+		cp->link_cntl = BMCR_ANENABLE;
+	cp->lstate = link_down;
+	cp->link_transition = LINK_TRANSITION_LINK_DOWN;
+	netif_carrier_off(cp->dev);
+	cp->timer_ticks = 0;
+
+	/* give us access to cassini registers */
+	cp->regs = ioremap(casreg_base, casreg_len);
+	if (cp->regs == 0UL) {
+		printk(KERN_ERR PFX "Cannot map device registers, "
+		       "aborting.\n");
+		goto err_out_free_res;
+	}
+	cp->casreg_len = casreg_len;
+
+	pci_save_state(pdev);
+	cas_check_pci_invariants(cp);
+	cas_hard_reset(cp);
+	cas_reset(cp, 0);
+	if (cas_check_invariants(cp))
+		goto err_out_iounmap;
+
+	cp->init_block = (struct cas_init_block *)
+		pci_alloc_consistent(pdev, sizeof(struct cas_init_block),
+				     &cp->block_dvma);
+	if (!cp->init_block) {
+		printk(KERN_ERR PFX "Cannot allocate init block, "
+		       "aborting.\n");
+		goto err_out_iounmap;
+	}
+
+	for (i = 0; i < N_TX_RINGS; i++) 
+		cp->init_txds[i] = cp->init_block->txds[i];
+
+	for (i = 0; i < N_RX_DESC_RINGS; i++) 
+		cp->init_rxds[i] = cp->init_block->rxds[i];
+
+	for (i = 0; i < N_RX_COMP_RINGS; i++) 
+		cp->init_rxcs[i] = cp->init_block->rxcs[i];
+
+	for (i = 0; i < N_RX_FLOWS; i++)
+		skb_queue_head_init(&cp->rx_flows[i]);
+
+	dev->open = cas_open;
+	dev->stop = cas_close;
+	dev->hard_start_xmit = cas_start_xmit;
+	dev->get_stats = cas_get_stats;
+	dev->set_multicast_list = cas_set_multicast;
+	dev->do_ioctl = cas_ioctl;
+	dev->ethtool_ops = &cas_ethtool_ops;
+	dev->tx_timeout = cas_tx_timeout;
+	dev->watchdog_timeo = CAS_TX_TIMEOUT;
+	dev->change_mtu = cas_change_mtu;
+#ifdef USE_NAPI
+	dev->poll = cas_poll;
+	dev->weight = 64;
+#endif
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = cas_netpoll;
+#endif
+	dev->irq = pdev->irq;
+	dev->dma = 0;
+
+	/* Cassini features. */
+	if ((cp->cas_flags & CAS_FLAG_NO_HW_CSUM) == 0)
+		dev->features |= NETIF_F_HW_CSUM | NETIF_F_SG;
+
+	if (pci_using_dac)
+		dev->features |= NETIF_F_HIGHDMA;
+
+	if (register_netdev(dev)) {
+		printk(KERN_ERR PFX "Cannot register net device, "
+		       "aborting.\n");
+		goto err_out_free_consistent;
+	}
+
+	i = readl(cp->regs + REG_BIM_CFG);
+	printk(KERN_INFO "%s: Sun Cassini%s (%sbit/%sMHz PCI/%s) "
+	       "Ethernet[%d] ",  dev->name, 
+	       (cp->cas_flags & CAS_FLAG_REG_PLUS) ? "+" : "", 
+	       (i & BIM_CFG_32BIT) ? "32" : "64",
+	       (i & BIM_CFG_66MHZ) ? "66" : "33",
+	       (cp->phy_type == CAS_PHY_SERDES) ? "Fi" : "Cu", pdev->irq); 
+
+	for (i = 0; i < 6; i++)
+		printk("%2.2x%c", dev->dev_addr[i],
+		       i == 5 ? ' ' : ':');
+	printk("\n");
+
+	pci_set_drvdata(pdev, dev);
+	cp->hw_running = 1;
+	cas_entropy_reset(cp);
+	cas_phy_init(cp);
+	cas_begin_auto_negotiation(cp, NULL);
+	return 0;
+
+err_out_free_consistent:
+	pci_free_consistent(pdev, sizeof(struct cas_init_block),
+			    cp->init_block, cp->block_dvma);
+
+err_out_iounmap:
+	down(&cp->pm_sem);
+	if (cp->hw_running)
+		cas_shutdown(cp);
+	up(&cp->pm_sem);
+
+	iounmap(cp->regs);
+
+
+err_out_free_res:
+	pci_release_regions(pdev);
+
+err_write_cacheline:
+	/* Try to restore it in case the error occured after we
+	 * set it. 
+	 */
+	pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, orig_cacheline_size);
+
+err_out_free_netdev:
+	free_netdev(dev);
+
+err_out_disable_pdev:
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+	return -ENODEV;
+}
+
+static void __devexit cas_remove_one(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct cas *cp;
+	if (!dev)
+		return;
+
+	cp = netdev_priv(dev);
+	unregister_netdev(dev);
+
+	down(&cp->pm_sem);
+	flush_scheduled_work();
+	if (cp->hw_running)
+		cas_shutdown(cp);
+	up(&cp->pm_sem);
+
+#if 1
+	if (cp->orig_cacheline_size) {
+		/* Restore the cache line size if we had modified
+		 * it.
+		 */
+		pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 
+				      cp->orig_cacheline_size);
+	}
+#endif
+	pci_free_consistent(pdev, sizeof(struct cas_init_block),
+			    cp->init_block, cp->block_dvma);
+	iounmap(cp->regs);
+	free_netdev(dev);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+}
+
+#ifdef CONFIG_PM
+static int cas_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct cas *cp = netdev_priv(dev);
+	unsigned long flags;
+
+	/* We hold the PM semaphore during entire driver
+	 * sleep time
+	 */
+	down(&cp->pm_sem);
+	
+	/* If the driver is opened, we stop the DMA */
+	if (cp->opened) {
+		netif_device_detach(dev);
+
+		cas_lock_all_save(cp, flags);
+
+		/* We can set the second arg of cas_reset to 0
+		 * because on resume, we'll call cas_init_hw with
+		 * its second arg set so that autonegotiation is
+		 * restarted.
+		 */
+		cas_reset(cp, 0);
+		cas_clean_rings(cp);
+		cas_unlock_all_restore(cp, flags);
+	}
+
+	if (cp->hw_running)
+		cas_shutdown(cp);
+
+	return 0;
+}
+
+static int cas_resume(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct cas *cp = netdev_priv(dev);
+
+	printk(KERN_INFO "%s: resuming\n", dev->name);
+
+	cas_hard_reset(cp);
+	if (cp->opened) {
+		unsigned long flags;
+		cas_lock_all_save(cp, flags);
+		cas_reset(cp, 0);
+		cp->hw_running = 1;
+		cas_clean_rings(cp);
+		cas_init_hw(cp, 1);
+		cas_unlock_all_restore(cp, flags);
+
+		netif_device_attach(dev);
+	}
+	up(&cp->pm_sem);
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct pci_driver cas_driver = {
+	.name		= DRV_MODULE_NAME,
+	.id_table	= cas_pci_tbl,
+	.probe		= cas_init_one,
+	.remove		= __devexit_p(cas_remove_one),
+#ifdef CONFIG_PM
+	.suspend	= cas_suspend,
+	.resume		= cas_resume
+#endif
+};
+
+static int __init cas_init(void)
+{
+	if (linkdown_timeout > 0)
+		link_transition_timeout = linkdown_timeout * HZ;
+	else
+		link_transition_timeout = 0;
+
+	return pci_module_init(&cas_driver);
+}
+
+static void __exit cas_cleanup(void)
+{
+	pci_unregister_driver(&cas_driver);
+}
+
+module_init(cas_init);
+module_exit(cas_cleanup);
diff -urN oldtree/drivers/net/e100.c newtree/drivers/net/e100.c
--- oldtree/drivers/net/e100.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/e100.c	2006-02-13 19:19:59.987514200 -0500
@@ -276,12 +276,6 @@
 	rus_mask         = 0x3C,
 };
 
-enum ru_state  {
-	RU_SUSPENDED = 0,
-	RU_RUNNING	 = 1,
-	RU_UNINITIALIZED = -1,
-};
-
 enum scb_stat_ack {
 	stat_ack_not_ours    = 0x00,
 	stat_ack_sw_gen      = 0x04,
@@ -523,7 +517,6 @@
 	struct rx *rx_to_use;
 	struct rx *rx_to_clean;
 	struct rfd blank_rfd;
-	enum ru_state ru_running;
 
 	spinlock_t cb_lock			____cacheline_aligned;
 	spinlock_t cmd_lock;
@@ -923,7 +916,7 @@
 		((nic->mac >= mac_82558_D101_A4) ? cb_cid : cb_i));
 
 	/* Template for a freshly allocated RFD */
-	nic->blank_rfd.command = cpu_to_le16(cb_el);
+	nic->blank_rfd.command = cpu_to_le16(cb_el & cb_s);
 	nic->blank_rfd.rbd = 0xFFFFFFFF;
 	nic->blank_rfd.size = cpu_to_le16(VLAN_ETH_FRAME_LEN);
 
@@ -1687,19 +1680,11 @@
 	return 0;
 }
 
-static inline void e100_start_receiver(struct nic *nic, struct rx *rx)
+static inline void e100_start_receiver(struct nic *nic)
 {
-	if(!nic->rxs) return;
-	if(RU_SUSPENDED != nic->ru_running) return;
-
-	/* handle init time starts */
-	if(!rx) rx = nic->rxs;
-
-	/* (Re)start RU if suspended or idle and RFA is non-NULL */
-	if(rx->skb) {
-		e100_exec_cmd(nic, ruc_start, rx->dma_addr);
-		nic->ru_running = RU_RUNNING;
-	}
+	/* Start if RFA is non-NULL */
+	if(nic->rx_to_clean->skb)
+		e100_exec_cmd(nic, ruc_start, nic->rx_to_clean->dma_addr);
 }
 
 #define RFD_BUF_LEN (sizeof(struct rfd) + VLAN_ETH_FRAME_LEN)
@@ -1729,7 +1714,7 @@
 		put_unaligned(cpu_to_le32(rx->dma_addr),
 			(u32 *)&prev_rfd->link);
 		wmb();
-		prev_rfd->command &= ~cpu_to_le16(cb_el);
+		prev_rfd->command &= ~cpu_to_le16(cb_el & cb_s);
 		pci_dma_sync_single_for_device(nic->pdev, rx->prev->dma_addr,
 			sizeof(struct rfd), PCI_DMA_TODEVICE);
 	}
@@ -1767,10 +1752,6 @@
 	pci_unmap_single(nic->pdev, rx->dma_addr,
 		RFD_BUF_LEN, PCI_DMA_FROMDEVICE);
 
-	/* this allows for a fast restart without re-enabling interrupts */
-	if(le16_to_cpu(rfd->command) & cb_el)
-		nic->ru_running = RU_SUSPENDED;
-
 	/* Pull off the RFD and put the actual data (minus eth hdr) */
 	skb_reserve(skb, sizeof(struct rfd));
 	skb_put(skb, actual_size);
@@ -1801,45 +1782,18 @@
 	unsigned int work_to_do)
 {
 	struct rx *rx;
-	int restart_required = 0;
-	struct rx *rx_to_start = NULL;
-
-	/* are we already rnr? then pay attention!!! this ensures that
-	 * the state machine progression never allows a start with a 
-	 * partially cleaned list, avoiding a race between hardware
-	 * and rx_to_clean when in NAPI mode */
-	if(RU_SUSPENDED == nic->ru_running)
-		restart_required = 1;
 
 	/* Indicate newly arrived packets */
 	for(rx = nic->rx_to_clean; rx->skb; rx = nic->rx_to_clean = rx->next) {
-		int err = e100_rx_indicate(nic, rx, work_done, work_to_do);
-		if(-EAGAIN == err) {
-			/* hit quota so have more work to do, restart once
-			 * cleanup is complete */
-			restart_required = 0;
-			break;
-		} else if(-ENODATA == err)
+		if(e100_rx_indicate(nic, rx, work_done, work_to_do))
 			break; /* No more to clean */
 	}
 
-	/* save our starting point as the place we'll restart the receiver */
-	if(restart_required)
-		rx_to_start = nic->rx_to_clean;
-
 	/* Alloc new skbs to refill list */
 	for(rx = nic->rx_to_use; !rx->skb; rx = nic->rx_to_use = rx->next) {
 		if(unlikely(e100_rx_alloc_skb(nic, rx)))
 			break; /* Better luck next time (see watchdog) */
 	}
-
-	if(restart_required) {
-		// ack the rnr?
-		writeb(stat_ack_rnr, &nic->csr->scb.stat_ack);
-		e100_start_receiver(nic, rx_to_start);
-		if(work_done)
-			(*work_done)++;
-	}
 }
 
 static void e100_rx_clean_list(struct nic *nic)
@@ -1847,8 +1801,6 @@
 	struct rx *rx;
 	unsigned int i, count = nic->params.rfds.count;
 
-	nic->ru_running = RU_UNINITIALIZED;
-
 	if(nic->rxs) {
 		for(rx = nic->rxs, i = 0; i < count; rx++, i++) {
 			if(rx->skb) {
@@ -1870,7 +1822,6 @@
 	unsigned int i, count = nic->params.rfds.count;
 
 	nic->rx_to_use = nic->rx_to_clean = NULL;
-	nic->ru_running = RU_UNINITIALIZED;
 
 	if(!(nic->rxs = kmalloc(sizeof(struct rx) * count, GFP_ATOMIC)))
 		return -ENOMEM;
@@ -1886,7 +1837,6 @@
 	}
 
 	nic->rx_to_use = nic->rx_to_clean = nic->rxs;
-	nic->ru_running = RU_SUSPENDED;
 
 	return 0;
 }
@@ -1906,10 +1856,6 @@
 	/* Ack interrupt(s) */
 	writeb(stat_ack, &nic->csr->scb.stat_ack);
 
-	/* We hit Receive No Resource (RNR); restart RU after cleaning */
-	if(stat_ack & stat_ack_rnr)
-		nic->ru_running = RU_SUSPENDED;
-
 	if(likely(netif_rx_schedule_prep(netdev))) {
 		e100_disable_irq(nic);
 		__netif_rx_schedule(netdev);
@@ -2003,7 +1949,7 @@
 	if((err = e100_hw_init(nic)))
 		goto err_clean_cbs;
 	e100_set_multicast_list(nic->netdev);
-	e100_start_receiver(nic, NULL);
+	e100_start_receiver(nic);
 	mod_timer(&nic->watchdog, jiffies);
 	if((err = request_irq(nic->pdev->irq, e100_intr, SA_SHIRQ,
 		nic->netdev->name, nic->netdev)))
@@ -2083,7 +2029,7 @@
 		mdio_write(nic->netdev, nic->mii.phy_id, MII_BMCR,
 			BMCR_LOOPBACK);
 
-	e100_start_receiver(nic, NULL);
+	e100_start_receiver(nic);
 
 	if(!(skb = dev_alloc_skb(ETH_DATA_LEN))) {
 		err = -ENOMEM;
diff -urN oldtree/drivers/net/e100.c.orig newtree/drivers/net/e100.c.orig
--- oldtree/drivers/net/e100.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/e100.c.orig	2006-02-13 19:19:59.993513288 -0500
@@ -0,0 +1,2734 @@
+/*******************************************************************************
+
+  
+  Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
+  
+  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 the Free 
+  Software Foundation; either version 2 of the License, or (at your option) 
+  any later version.
+  
+  This program is distributed in the hope that it will be useful, but WITHOUT 
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+  more details.
+  
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc., 59 
+  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+  
+  The full GNU General Public License is included in this distribution in the
+  file called LICENSE.
+  
+  Contact Information:
+  Linux NICS <linux.nics@intel.com>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+/*
+ *	e100.c: Intel(R) PRO/100 ethernet driver
+ *
+ *	(Re)written 2003 by scott.feldman@intel.com.  Based loosely on
+ *	original e100 driver, but better described as a munging of
+ *	e100, e1000, eepro100, tg3, 8139cp, and other drivers.
+ *
+ *	References:
+ *		Intel 8255x 10/100 Mbps Ethernet Controller Family,
+ *		Open Source Software Developers Manual,
+ *		http://sourceforge.net/projects/e1000
+ *
+ *
+ *	                      Theory of Operation
+ *
+ *	I.   General
+ *
+ *	The driver supports Intel(R) 10/100 Mbps PCI Fast Ethernet
+ *	controller family, which includes the 82557, 82558, 82559, 82550,
+ *	82551, and 82562 devices.  82558 and greater controllers
+ *	integrate the Intel 82555 PHY.  The controllers are used in
+ *	server and client network interface cards, as well as in
+ *	LAN-On-Motherboard (LOM), CardBus, MiniPCI, and ICHx
+ *	configurations.  8255x supports a 32-bit linear addressing
+ *	mode and operates at 33Mhz PCI clock rate.
+ *
+ *	II.  Driver Operation
+ *
+ *	Memory-mapped mode is used exclusively to access the device's
+ *	shared-memory structure, the Control/Status Registers (CSR). All
+ *	setup, configuration, and control of the device, including queuing
+ *	of Tx, Rx, and configuration commands is through the CSR.
+ *	cmd_lock serializes accesses to the CSR command register.  cb_lock
+ *	protects the shared Command Block List (CBL).
+ *
+ *	8255x is highly MII-compliant and all access to the PHY go
+ *	through the Management Data Interface (MDI).  Consequently, the
+ *	driver leverages the mii.c library shared with other MII-compliant
+ *	devices.
+ *
+ *	Big- and Little-Endian byte order as well as 32- and 64-bit
+ *	archs are supported.  Weak-ordered memory and non-cache-coherent
+ *	archs are supported.
+ *
+ *	III. Transmit
+ *
+ *	A Tx skb is mapped and hangs off of a TCB.  TCBs are linked
+ *	together in a fixed-size ring (CBL) thus forming the flexible mode
+ *	memory structure.  A TCB marked with the suspend-bit indicates
+ *	the end of the ring.  The last TCB processed suspends the
+ *	controller, and the controller can be restarted by issue a CU
+ *	resume command to continue from the suspend point, or a CU start
+ *	command to start at a given position in the ring.
+ *
+ *	Non-Tx commands (config, multicast setup, etc) are linked
+ *	into the CBL ring along with Tx commands.  The common structure
+ *	used for both Tx and non-Tx commands is the Command Block (CB).
+ *
+ *	cb_to_use is the next CB to use for queuing a command; cb_to_clean
+ *	is the next CB to check for completion; cb_to_send is the first
+ *	CB to start on in case of a previous failure to resume.  CB clean
+ *	up happens in interrupt context in response to a CU interrupt.
+ *	cbs_avail keeps track of number of free CB resources available.
+ *
+ * 	Hardware padding of short packets to minimum packet size is
+ * 	enabled.  82557 pads with 7Eh, while the later controllers pad
+ * 	with 00h.
+ *
+ *	IV.  Recieve
+ *
+ *	The Receive Frame Area (RFA) comprises a ring of Receive Frame
+ *	Descriptors (RFD) + data buffer, thus forming the simplified mode
+ *	memory structure.  Rx skbs are allocated to contain both the RFD
+ *	and the data buffer, but the RFD is pulled off before the skb is
+ *	indicated.  The data buffer is aligned such that encapsulated
+ *	protocol headers are u32-aligned.  Since the RFD is part of the
+ *	mapped shared memory, and completion status is contained within
+ *	the RFD, the RFD must be dma_sync'ed to maintain a consistent
+ *	view from software and hardware.
+ *
+ *	Under typical operation, the  receive unit (RU) is start once,
+ *	and the controller happily fills RFDs as frames arrive.  If
+ *	replacement RFDs cannot be allocated, or the RU goes non-active,
+ *	the RU must be restarted.  Frame arrival generates an interrupt,
+ *	and Rx indication and re-allocation happen in the same context,
+ *	therefore no locking is required.  A software-generated interrupt
+ *	is generated from the watchdog to recover from a failed allocation
+ *	senario where all Rx resources have been indicated and none re-
+ *	placed.
+ *
+ *	V.   Miscellaneous
+ *
+ * 	VLAN offloading of tagging, stripping and filtering is not
+ * 	supported, but driver will accommodate the extra 4-byte VLAN tag
+ * 	for processing by upper layers.  Tx/Rx Checksum offloading is not
+ * 	supported.  Tx Scatter/Gather is not supported.  Jumbo Frames is
+ * 	not supported (hardware limitation).
+ *
+ * 	MagicPacket(tm) WoL support is enabled/disabled via ethtool.
+ *
+ * 	Thanks to JC (jchapman@katalix.com) for helping with
+ * 	testing/troubleshooting the development driver.
+ *
+ * 	TODO:
+ * 	o several entry points race with dev->close
+ * 	o check for tx-no-resources/stop Q races with tx clean/wake Q
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/mii.h>
+#include <linux/if_vlan.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/string.h>
+#include <asm/unaligned.h>
+
+
+#define DRV_NAME		"e100"
+#define DRV_EXT		"-NAPI"
+#define DRV_VERSION		"3.4.14-k4"DRV_EXT
+#define DRV_DESCRIPTION		"Intel(R) PRO/100 Network Driver"
+#define DRV_COPYRIGHT		"Copyright(c) 1999-2005 Intel Corporation"
+#define PFX			DRV_NAME ": "
+
+#define E100_WATCHDOG_PERIOD	(2 * HZ)
+#define E100_NAPI_WEIGHT	16
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_AUTHOR(DRV_COPYRIGHT);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+static int debug = 3;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
+#define DPRINTK(nlevel, klevel, fmt, args...) \
+	(void)((NETIF_MSG_##nlevel & nic->msg_enable) && \
+	printk(KERN_##klevel PFX "%s: %s: " fmt, nic->netdev->name, \
+		__FUNCTION__ , ## args))
+
+#define INTEL_8255X_ETHERNET_DEVICE(device_id, ich) {\
+	PCI_VENDOR_ID_INTEL, device_id, PCI_ANY_ID, PCI_ANY_ID, \
+	PCI_CLASS_NETWORK_ETHERNET << 8, 0xFFFF00, ich }
+static struct pci_device_id e100_id_table[] = {
+	INTEL_8255X_ETHERNET_DEVICE(0x1029, 0),
+	INTEL_8255X_ETHERNET_DEVICE(0x1030, 0),
+	INTEL_8255X_ETHERNET_DEVICE(0x1031, 3),
+	INTEL_8255X_ETHERNET_DEVICE(0x1032, 3),
+	INTEL_8255X_ETHERNET_DEVICE(0x1033, 3),
+	INTEL_8255X_ETHERNET_DEVICE(0x1034, 3),
+	INTEL_8255X_ETHERNET_DEVICE(0x1038, 3),
+	INTEL_8255X_ETHERNET_DEVICE(0x1039, 4),
+	INTEL_8255X_ETHERNET_DEVICE(0x103A, 4),
+	INTEL_8255X_ETHERNET_DEVICE(0x103B, 4),
+	INTEL_8255X_ETHERNET_DEVICE(0x103C, 4),
+	INTEL_8255X_ETHERNET_DEVICE(0x103D, 4),
+	INTEL_8255X_ETHERNET_DEVICE(0x103E, 4),
+	INTEL_8255X_ETHERNET_DEVICE(0x1050, 5),
+	INTEL_8255X_ETHERNET_DEVICE(0x1051, 5),
+	INTEL_8255X_ETHERNET_DEVICE(0x1052, 5),
+	INTEL_8255X_ETHERNET_DEVICE(0x1053, 5),
+	INTEL_8255X_ETHERNET_DEVICE(0x1054, 5),
+	INTEL_8255X_ETHERNET_DEVICE(0x1055, 5),
+	INTEL_8255X_ETHERNET_DEVICE(0x1056, 5),
+	INTEL_8255X_ETHERNET_DEVICE(0x1057, 5),
+	INTEL_8255X_ETHERNET_DEVICE(0x1059, 0),
+	INTEL_8255X_ETHERNET_DEVICE(0x1064, 6),
+	INTEL_8255X_ETHERNET_DEVICE(0x1065, 6),
+	INTEL_8255X_ETHERNET_DEVICE(0x1066, 6),
+	INTEL_8255X_ETHERNET_DEVICE(0x1067, 6),
+	INTEL_8255X_ETHERNET_DEVICE(0x1068, 6),
+	INTEL_8255X_ETHERNET_DEVICE(0x1069, 6),
+	INTEL_8255X_ETHERNET_DEVICE(0x106A, 6),
+	INTEL_8255X_ETHERNET_DEVICE(0x106B, 6),
+	INTEL_8255X_ETHERNET_DEVICE(0x1091, 7),
+	INTEL_8255X_ETHERNET_DEVICE(0x1092, 7),
+	INTEL_8255X_ETHERNET_DEVICE(0x1093, 7),
+	INTEL_8255X_ETHERNET_DEVICE(0x1094, 7),
+	INTEL_8255X_ETHERNET_DEVICE(0x1095, 7),
+	INTEL_8255X_ETHERNET_DEVICE(0x1209, 0),
+	INTEL_8255X_ETHERNET_DEVICE(0x1229, 0),
+	INTEL_8255X_ETHERNET_DEVICE(0x2449, 2),
+	INTEL_8255X_ETHERNET_DEVICE(0x2459, 2),
+	INTEL_8255X_ETHERNET_DEVICE(0x245D, 2),
+	INTEL_8255X_ETHERNET_DEVICE(0x27DC, 7),
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, e100_id_table);
+
+enum mac {
+	mac_82557_D100_A  = 0,
+	mac_82557_D100_B  = 1,
+	mac_82557_D100_C  = 2,
+	mac_82558_D101_A4 = 4,
+	mac_82558_D101_B0 = 5,
+	mac_82559_D101M   = 8,
+	mac_82559_D101S   = 9,
+	mac_82550_D102    = 12,
+	mac_82550_D102_C  = 13,
+	mac_82551_E       = 14,
+	mac_82551_F       = 15,
+	mac_82551_10      = 16,
+	mac_unknown       = 0xFF,
+};
+
+enum phy {
+	phy_100a     = 0x000003E0,
+	phy_100c     = 0x035002A8,
+	phy_82555_tx = 0x015002A8,
+	phy_nsc_tx   = 0x5C002000,
+	phy_82562_et = 0x033002A8,
+	phy_82562_em = 0x032002A8,
+	phy_82562_ek = 0x031002A8,
+	phy_82562_eh = 0x017002A8,
+	phy_unknown  = 0xFFFFFFFF,
+};
+
+/* CSR (Control/Status Registers) */
+struct csr {
+	struct {
+		u8 status;
+		u8 stat_ack;
+		u8 cmd_lo;
+		u8 cmd_hi;
+		u32 gen_ptr;
+	} scb;
+	u32 port;
+	u16 flash_ctrl;
+	u8 eeprom_ctrl_lo;
+	u8 eeprom_ctrl_hi;
+	u32 mdi_ctrl;
+	u32 rx_dma_count;
+};
+
+enum scb_status {
+	rus_ready        = 0x10,
+	rus_mask         = 0x3C,
+};
+
+enum ru_state  {
+	RU_SUSPENDED = 0,
+	RU_RUNNING	 = 1,
+	RU_UNINITIALIZED = -1,
+};
+
+enum scb_stat_ack {
+	stat_ack_not_ours    = 0x00,
+	stat_ack_sw_gen      = 0x04,
+	stat_ack_rnr         = 0x10,
+	stat_ack_cu_idle     = 0x20,
+	stat_ack_frame_rx    = 0x40,
+	stat_ack_cu_cmd_done = 0x80,
+	stat_ack_not_present = 0xFF,
+	stat_ack_rx = (stat_ack_sw_gen | stat_ack_rnr | stat_ack_frame_rx),
+	stat_ack_tx = (stat_ack_cu_idle | stat_ack_cu_cmd_done),
+};
+
+enum scb_cmd_hi {
+	irq_mask_none = 0x00,
+	irq_mask_all  = 0x01,
+	irq_sw_gen    = 0x02,
+};
+
+enum scb_cmd_lo {
+	cuc_nop        = 0x00,
+	ruc_start      = 0x01,
+	ruc_load_base  = 0x06,
+	cuc_start      = 0x10,
+	cuc_resume     = 0x20,
+	cuc_dump_addr  = 0x40,
+	cuc_dump_stats = 0x50,
+	cuc_load_base  = 0x60,
+	cuc_dump_reset = 0x70,
+};
+
+enum cuc_dump {
+	cuc_dump_complete       = 0x0000A005,
+	cuc_dump_reset_complete = 0x0000A007,
+};
+		
+enum port {
+	software_reset  = 0x0000,
+	selftest        = 0x0001,
+	selective_reset = 0x0002,
+};
+
+enum eeprom_ctrl_lo {
+	eesk = 0x01,
+	eecs = 0x02,
+	eedi = 0x04,
+	eedo = 0x08,
+};
+
+enum mdi_ctrl {
+	mdi_write = 0x04000000,
+	mdi_read  = 0x08000000,
+	mdi_ready = 0x10000000,
+};
+
+enum eeprom_op {
+	op_write = 0x05,
+	op_read  = 0x06,
+	op_ewds  = 0x10,
+	op_ewen  = 0x13,
+};
+
+enum eeprom_offsets {
+	eeprom_cnfg_mdix  = 0x03,
+	eeprom_id         = 0x0A,
+	eeprom_config_asf = 0x0D,
+	eeprom_smbus_addr = 0x90,
+};
+
+enum eeprom_cnfg_mdix {
+	eeprom_mdix_enabled = 0x0080,
+};
+
+enum eeprom_id {
+	eeprom_id_wol = 0x0020,
+};
+
+enum eeprom_config_asf {
+	eeprom_asf = 0x8000,
+	eeprom_gcl = 0x4000,
+};
+
+enum cb_status {
+	cb_complete = 0x8000,
+	cb_ok       = 0x2000,
+};
+
+enum cb_command {
+	cb_nop    = 0x0000,
+	cb_iaaddr = 0x0001,
+	cb_config = 0x0002,
+	cb_multi  = 0x0003,
+	cb_tx     = 0x0004,
+	cb_ucode  = 0x0005,
+	cb_dump   = 0x0006,
+	cb_tx_sf  = 0x0008,
+	cb_cid    = 0x1f00,
+	cb_i      = 0x2000,
+	cb_s      = 0x4000,
+	cb_el     = 0x8000,
+};
+
+struct rfd {
+	u16 status;
+	u16 command;
+	u32 link;
+	u32 rbd;
+	u16 actual_size;
+	u16 size;
+};
+
+struct rx {
+	struct rx *next, *prev;
+	struct sk_buff *skb;
+	dma_addr_t dma_addr;
+};
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+#define X(a,b)	b,a
+#else
+#define X(a,b)	a,b
+#endif
+struct config {
+/*0*/	u8 X(byte_count:6, pad0:2);
+/*1*/	u8 X(X(rx_fifo_limit:4, tx_fifo_limit:3), pad1:1);
+/*2*/	u8 adaptive_ifs;
+/*3*/	u8 X(X(X(X(mwi_enable:1, type_enable:1), read_align_enable:1),
+	   term_write_cache_line:1), pad3:4);
+/*4*/	u8 X(rx_dma_max_count:7, pad4:1);
+/*5*/	u8 X(tx_dma_max_count:7, dma_max_count_enable:1);
+/*6*/	u8 X(X(X(X(X(X(X(late_scb_update:1, direct_rx_dma:1),
+	   tno_intr:1), cna_intr:1), standard_tcb:1), standard_stat_counter:1),
+	   rx_discard_overruns:1), rx_save_bad_frames:1);
+/*7*/	u8 X(X(X(X(X(rx_discard_short_frames:1, tx_underrun_retry:2),
+	   pad7:2), rx_extended_rfd:1), tx_two_frames_in_fifo:1),
+	   tx_dynamic_tbd:1);
+/*8*/	u8 X(X(mii_mode:1, pad8:6), csma_disabled:1);
+/*9*/	u8 X(X(X(X(X(rx_tcpudp_checksum:1, pad9:3), vlan_arp_tco:1),
+	   link_status_wake:1), arp_wake:1), mcmatch_wake:1);
+/*10*/	u8 X(X(X(pad10:3, no_source_addr_insertion:1), preamble_length:2),
+	   loopback:2);
+/*11*/	u8 X(linear_priority:3, pad11:5);
+/*12*/	u8 X(X(linear_priority_mode:1, pad12:3), ifs:4);
+/*13*/	u8 ip_addr_lo;
+/*14*/	u8 ip_addr_hi;
+/*15*/	u8 X(X(X(X(X(X(X(promiscuous_mode:1, broadcast_disabled:1),
+	   wait_after_win:1), pad15_1:1), ignore_ul_bit:1), crc_16_bit:1),
+	   pad15_2:1), crs_or_cdt:1);
+/*16*/	u8 fc_delay_lo;
+/*17*/	u8 fc_delay_hi;
+/*18*/	u8 X(X(X(X(X(rx_stripping:1, tx_padding:1), rx_crc_transfer:1),
+	   rx_long_ok:1), fc_priority_threshold:3), pad18:1);
+/*19*/	u8 X(X(X(X(X(X(X(addr_wake:1, magic_packet_disable:1),
+	   fc_disable:1), fc_restop:1), fc_restart:1), fc_reject:1),
+	   full_duplex_force:1), full_duplex_pin:1);
+/*20*/	u8 X(X(X(pad20_1:5, fc_priority_location:1), multi_ia:1), pad20_2:1);
+/*21*/	u8 X(X(pad21_1:3, multicast_all:1), pad21_2:4);
+/*22*/	u8 X(X(rx_d102_mode:1, rx_vlan_drop:1), pad22:6);
+	u8 pad_d102[9];
+};
+
+#define E100_MAX_MULTICAST_ADDRS	64
+struct multi {
+	u16 count;
+	u8 addr[E100_MAX_MULTICAST_ADDRS * ETH_ALEN + 2/*pad*/];
+};
+
+/* Important: keep total struct u32-aligned */
+#define UCODE_SIZE			134
+struct cb {
+	u16 status;
+	u16 command;
+	u32 link;
+	union {
+		u8 iaaddr[ETH_ALEN];
+		u32 ucode[UCODE_SIZE];
+		struct config config;
+		struct multi multi;
+		struct {
+			u32 tbd_array;
+			u16 tcb_byte_count;
+			u8 threshold;
+			u8 tbd_count;
+			struct {
+				u32 buf_addr;
+				u16 size;
+				u16 eol;
+			} tbd;
+		} tcb;
+		u32 dump_buffer_addr;
+	} u;
+	struct cb *next, *prev;
+	dma_addr_t dma_addr;
+	struct sk_buff *skb;
+};
+
+enum loopback {
+	lb_none = 0, lb_mac = 1, lb_phy = 3,
+};
+
+struct stats {
+	u32 tx_good_frames, tx_max_collisions, tx_late_collisions,
+		tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions,
+		tx_multiple_collisions, tx_total_collisions;
+	u32 rx_good_frames, rx_crc_errors, rx_alignment_errors,
+		rx_resource_errors, rx_overrun_errors, rx_cdt_errors,
+		rx_short_frame_errors;
+	u32 fc_xmt_pause, fc_rcv_pause, fc_rcv_unsupported;
+	u16 xmt_tco_frames, rcv_tco_frames;
+	u32 complete;
+};
+
+struct mem {
+	struct {
+		u32 signature;
+		u32 result;
+	} selftest;
+	struct stats stats;
+	u8 dump_buf[596];
+};
+
+struct param_range {
+	u32 min;
+	u32 max;
+	u32 count;
+};
+
+struct params {
+	struct param_range rfds;
+	struct param_range cbs;
+};
+
+struct nic {
+	/* Begin: frequently used values: keep adjacent for cache effect */
+	u32 msg_enable				____cacheline_aligned;
+	struct net_device *netdev;
+	struct pci_dev *pdev;
+
+	struct rx *rxs				____cacheline_aligned;
+	struct rx *rx_to_use;
+	struct rx *rx_to_clean;
+	struct rfd blank_rfd;
+	enum ru_state ru_running;
+
+	spinlock_t cb_lock			____cacheline_aligned;
+	spinlock_t cmd_lock;
+	struct csr __iomem *csr;
+	enum scb_cmd_lo cuc_cmd;
+	unsigned int cbs_avail;
+	struct cb *cbs;
+	struct cb *cb_to_use;
+	struct cb *cb_to_send;
+	struct cb *cb_to_clean;
+	u16 tx_command;
+	/* End: frequently used values: keep adjacent for cache effect */
+
+	enum {
+		ich                = (1 << 0),
+		promiscuous        = (1 << 1),
+		multicast_all      = (1 << 2),
+		wol_magic          = (1 << 3),
+		ich_10h_workaround = (1 << 4),
+	} flags					____cacheline_aligned;
+
+	enum mac mac;
+	enum phy phy;
+	struct params params;
+	struct net_device_stats net_stats;
+	struct timer_list watchdog;
+	struct timer_list blink_timer;
+	struct mii_if_info mii;
+	struct work_struct tx_timeout_task;
+	enum loopback loopback;
+
+	struct mem *mem;
+	dma_addr_t dma_addr;
+
+	dma_addr_t cbs_dma_addr;
+	u8 adaptive_ifs;
+	u8 tx_threshold;
+	u32 tx_frames;
+	u32 tx_collisions;
+	u32 tx_deferred;
+	u32 tx_single_collisions;
+	u32 tx_multiple_collisions;
+	u32 tx_fc_pause;
+	u32 tx_tco_frames;
+
+	u32 rx_fc_pause;
+	u32 rx_fc_unsupported;
+	u32 rx_tco_frames;
+	u32 rx_over_length_errors;
+
+	u8 rev_id;
+	u16 leds;
+	u16 eeprom_wc;
+	u16 eeprom[256];
+};
+
+static inline void e100_write_flush(struct nic *nic)
+{
+	/* Flush previous PCI writes through intermediate bridges
+	 * by doing a benign read */
+	(void)readb(&nic->csr->scb.status);
+}
+
+static inline void e100_enable_irq(struct nic *nic)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&nic->cmd_lock, flags);
+	writeb(irq_mask_none, &nic->csr->scb.cmd_hi);
+	spin_unlock_irqrestore(&nic->cmd_lock, flags);
+	e100_write_flush(nic);
+}
+
+static inline void e100_disable_irq(struct nic *nic)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&nic->cmd_lock, flags);
+	writeb(irq_mask_all, &nic->csr->scb.cmd_hi);
+	spin_unlock_irqrestore(&nic->cmd_lock, flags);
+	e100_write_flush(nic);
+}
+
+static void e100_hw_reset(struct nic *nic)
+{
+	/* Put CU and RU into idle with a selective reset to get
+	 * device off of PCI bus */
+	writel(selective_reset, &nic->csr->port);
+	e100_write_flush(nic); udelay(20);
+
+	/* Now fully reset device */
+	writel(software_reset, &nic->csr->port);
+	e100_write_flush(nic); udelay(20);
+
+	/* Mask off our interrupt line - it's unmasked after reset */
+	e100_disable_irq(nic);
+}
+
+static int e100_self_test(struct nic *nic)
+{
+	u32 dma_addr = nic->dma_addr + offsetof(struct mem, selftest);
+
+	/* Passing the self-test is a pretty good indication
+	 * that the device can DMA to/from host memory */
+
+	nic->mem->selftest.signature = 0;
+	nic->mem->selftest.result = 0xFFFFFFFF;
+
+	writel(selftest | dma_addr, &nic->csr->port);
+	e100_write_flush(nic);
+	/* Wait 10 msec for self-test to complete */
+	msleep(10);
+
+	/* Interrupts are enabled after self-test */
+	e100_disable_irq(nic);
+
+	/* Check results of self-test */
+	if(nic->mem->selftest.result != 0) {
+		DPRINTK(HW, ERR, "Self-test failed: result=0x%08X\n",
+			nic->mem->selftest.result);
+		return -ETIMEDOUT;
+	}
+	if(nic->mem->selftest.signature == 0) {
+		DPRINTK(HW, ERR, "Self-test failed: timed out\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static void e100_eeprom_write(struct nic *nic, u16 addr_len, u16 addr, u16 data)
+{
+	u32 cmd_addr_data[3];
+	u8 ctrl;
+	int i, j;
+
+	/* Three cmds: write/erase enable, write data, write/erase disable */
+	cmd_addr_data[0] = op_ewen << (addr_len - 2);
+	cmd_addr_data[1] = (((op_write << addr_len) | addr) << 16) |
+		cpu_to_le16(data);
+	cmd_addr_data[2] = op_ewds << (addr_len - 2);
+
+	/* Bit-bang cmds to write word to eeprom */
+	for(j = 0; j < 3; j++) {
+
+		/* Chip select */
+		writeb(eecs | eesk, &nic->csr->eeprom_ctrl_lo);
+		e100_write_flush(nic); udelay(4);
+
+		for(i = 31; i >= 0; i--) {
+			ctrl = (cmd_addr_data[j] & (1 << i)) ?
+				eecs | eedi : eecs;
+			writeb(ctrl, &nic->csr->eeprom_ctrl_lo);
+			e100_write_flush(nic); udelay(4);
+
+			writeb(ctrl | eesk, &nic->csr->eeprom_ctrl_lo);
+			e100_write_flush(nic); udelay(4);
+		}
+		/* Wait 10 msec for cmd to complete */
+		msleep(10);
+
+		/* Chip deselect */
+		writeb(0, &nic->csr->eeprom_ctrl_lo);
+		e100_write_flush(nic); udelay(4);
+	}
+};
+
+/* General technique stolen from the eepro100 driver - very clever */
+static u16 e100_eeprom_read(struct nic *nic, u16 *addr_len, u16 addr)
+{
+	u32 cmd_addr_data;
+	u16 data = 0;
+	u8 ctrl;
+	int i;
+
+	cmd_addr_data = ((op_read << *addr_len) | addr) << 16;
+
+	/* Chip select */
+	writeb(eecs | eesk, &nic->csr->eeprom_ctrl_lo);
+	e100_write_flush(nic); udelay(4);
+
+	/* Bit-bang to read word from eeprom */
+	for(i = 31; i >= 0; i--) {
+		ctrl = (cmd_addr_data & (1 << i)) ? eecs | eedi : eecs;
+		writeb(ctrl, &nic->csr->eeprom_ctrl_lo);
+		e100_write_flush(nic); udelay(4);
+		
+		writeb(ctrl | eesk, &nic->csr->eeprom_ctrl_lo);
+		e100_write_flush(nic); udelay(4);
+		
+		/* Eeprom drives a dummy zero to EEDO after receiving
+		 * complete address.  Use this to adjust addr_len. */
+		ctrl = readb(&nic->csr->eeprom_ctrl_lo);
+		if(!(ctrl & eedo) && i > 16) {
+			*addr_len -= (i - 16);
+			i = 17;
+		}
+		
+		data = (data << 1) | (ctrl & eedo ? 1 : 0);
+	}
+
+	/* Chip deselect */
+	writeb(0, &nic->csr->eeprom_ctrl_lo);
+	e100_write_flush(nic); udelay(4);
+
+	return le16_to_cpu(data);
+};
+
+/* Load entire EEPROM image into driver cache and validate checksum */
+static int e100_eeprom_load(struct nic *nic)
+{
+	u16 addr, addr_len = 8, checksum = 0;
+
+	/* Try reading with an 8-bit addr len to discover actual addr len */
+	e100_eeprom_read(nic, &addr_len, 0);
+	nic->eeprom_wc = 1 << addr_len;
+
+	for(addr = 0; addr < nic->eeprom_wc; addr++) {
+		nic->eeprom[addr] = e100_eeprom_read(nic, &addr_len, addr);
+		if(addr < nic->eeprom_wc - 1)
+			checksum += cpu_to_le16(nic->eeprom[addr]);
+	}
+
+	/* The checksum, stored in the last word, is calculated such that
+	 * the sum of words should be 0xBABA */
+	checksum = le16_to_cpu(0xBABA - checksum);
+	if(checksum != nic->eeprom[nic->eeprom_wc - 1]) {
+		DPRINTK(PROBE, ERR, "EEPROM corrupted\n");
+		return -EAGAIN;
+	}
+
+	return 0;
+}
+
+/* Save (portion of) driver EEPROM cache to device and update checksum */
+static int e100_eeprom_save(struct nic *nic, u16 start, u16 count)
+{
+	u16 addr, addr_len = 8, checksum = 0;
+
+	/* Try reading with an 8-bit addr len to discover actual addr len */
+	e100_eeprom_read(nic, &addr_len, 0);
+	nic->eeprom_wc = 1 << addr_len;
+
+	if(start + count >= nic->eeprom_wc)
+		return -EINVAL;
+
+	for(addr = start; addr < start + count; addr++)
+		e100_eeprom_write(nic, addr_len, addr, nic->eeprom[addr]);
+
+	/* The checksum, stored in the last word, is calculated such that
+	 * the sum of words should be 0xBABA */
+	for(addr = 0; addr < nic->eeprom_wc - 1; addr++)
+		checksum += cpu_to_le16(nic->eeprom[addr]);
+	nic->eeprom[nic->eeprom_wc - 1] = le16_to_cpu(0xBABA - checksum);
+	e100_eeprom_write(nic, addr_len, nic->eeprom_wc - 1,
+		nic->eeprom[nic->eeprom_wc - 1]);
+
+	return 0;
+}
+
+#define E100_WAIT_SCB_TIMEOUT 20000 /* we might have to wait 100ms!!! */
+#define E100_WAIT_SCB_FAST 20       /* delay like the old code */
+static inline int e100_exec_cmd(struct nic *nic, u8 cmd, dma_addr_t dma_addr)
+{
+	unsigned long flags;
+	unsigned int i;
+	int err = 0;
+
+	spin_lock_irqsave(&nic->cmd_lock, flags);
+
+	/* Previous command is accepted when SCB clears */
+	for(i = 0; i < E100_WAIT_SCB_TIMEOUT; i++) {
+		if(likely(!readb(&nic->csr->scb.cmd_lo)))
+			break;
+		cpu_relax();
+		if(unlikely(i > E100_WAIT_SCB_FAST))
+			udelay(5);
+	}
+	if(unlikely(i == E100_WAIT_SCB_TIMEOUT)) {
+		err = -EAGAIN;
+		goto err_unlock;
+	}
+
+	if(unlikely(cmd != cuc_resume))
+		writel(dma_addr, &nic->csr->scb.gen_ptr);
+	writeb(cmd, &nic->csr->scb.cmd_lo);
+
+err_unlock:
+	spin_unlock_irqrestore(&nic->cmd_lock, flags);
+
+	return err;
+}
+
+static inline int e100_exec_cb(struct nic *nic, struct sk_buff *skb,
+	void (*cb_prepare)(struct nic *, struct cb *, struct sk_buff *))
+{
+	struct cb *cb;
+	unsigned long flags;
+	int err = 0;
+
+	spin_lock_irqsave(&nic->cb_lock, flags);
+
+	if(unlikely(!nic->cbs_avail)) {
+		err = -ENOMEM;
+		goto err_unlock;
+	}
+
+	cb = nic->cb_to_use;
+	nic->cb_to_use = cb->next;
+	nic->cbs_avail--;
+	cb->skb = skb;
+
+	if(unlikely(!nic->cbs_avail))
+		err = -ENOSPC;
+
+	cb_prepare(nic, cb, skb);
+
+	/* Order is important otherwise we'll be in a race with h/w:
+	 * set S-bit in current first, then clear S-bit in previous. */
+	cb->command |= cpu_to_le16(cb_s);
+	wmb();
+	cb->prev->command &= cpu_to_le16(~cb_s);
+
+	while(nic->cb_to_send != nic->cb_to_use) {
+		if(unlikely(e100_exec_cmd(nic, nic->cuc_cmd,
+			nic->cb_to_send->dma_addr))) {
+			/* Ok, here's where things get sticky.  It's
+			 * possible that we can't schedule the command
+			 * because the controller is too busy, so
+			 * let's just queue the command and try again
+			 * when another command is scheduled. */
+			if(err == -ENOSPC) {
+				//request a reset
+				schedule_work(&nic->tx_timeout_task);
+			}
+			break;
+		} else {
+			nic->cuc_cmd = cuc_resume;
+			nic->cb_to_send = nic->cb_to_send->next;
+		}
+	}
+
+err_unlock:
+	spin_unlock_irqrestore(&nic->cb_lock, flags);
+
+	return err;
+}
+
+static u16 mdio_ctrl(struct nic *nic, u32 addr, u32 dir, u32 reg, u16 data)
+{
+	u32 data_out = 0;
+	unsigned int i;
+
+	writel((reg << 16) | (addr << 21) | dir | data, &nic->csr->mdi_ctrl);
+
+	for(i = 0; i < 100; i++) {
+		udelay(20);
+		if((data_out = readl(&nic->csr->mdi_ctrl)) & mdi_ready)
+			break;
+	}
+
+	DPRINTK(HW, DEBUG,
+		"%s:addr=%d, reg=%d, data_in=0x%04X, data_out=0x%04X\n",
+		dir == mdi_read ? "READ" : "WRITE", addr, reg, data, data_out);
+	return (u16)data_out;
+}
+
+static int mdio_read(struct net_device *netdev, int addr, int reg)
+{
+	return mdio_ctrl(netdev_priv(netdev), addr, mdi_read, reg, 0);
+}
+
+static void mdio_write(struct net_device *netdev, int addr, int reg, int data)
+{
+	mdio_ctrl(netdev_priv(netdev), addr, mdi_write, reg, data);
+}
+
+static void e100_get_defaults(struct nic *nic)
+{
+	struct param_range rfds = { .min = 16, .max = 256, .count = 256 };
+	struct param_range cbs  = { .min = 64, .max = 256, .count = 128 };
+
+	pci_read_config_byte(nic->pdev, PCI_REVISION_ID, &nic->rev_id);
+	/* MAC type is encoded as rev ID; exception: ICH is treated as 82559 */
+	nic->mac = (nic->flags & ich) ? mac_82559_D101M : nic->rev_id;
+	if(nic->mac == mac_unknown)
+		nic->mac = mac_82557_D100_A;
+
+	nic->params.rfds = rfds;
+	nic->params.cbs = cbs;
+
+	/* Quadwords to DMA into FIFO before starting frame transmit */
+	nic->tx_threshold = 0xE0;
+
+	/* no interrupt for every tx completion, delay = 256us if not 557*/
+	nic->tx_command = cpu_to_le16(cb_tx | cb_tx_sf |
+		((nic->mac >= mac_82558_D101_A4) ? cb_cid : cb_i));
+
+	/* Template for a freshly allocated RFD */
+	nic->blank_rfd.command = cpu_to_le16(cb_el);
+	nic->blank_rfd.rbd = 0xFFFFFFFF;
+	nic->blank_rfd.size = cpu_to_le16(VLAN_ETH_FRAME_LEN);
+
+	/* MII setup */
+	nic->mii.phy_id_mask = 0x1F;
+	nic->mii.reg_num_mask = 0x1F;
+	nic->mii.dev = nic->netdev;
+	nic->mii.mdio_read = mdio_read;
+	nic->mii.mdio_write = mdio_write;
+}
+
+static void e100_configure(struct nic *nic, struct cb *cb, struct sk_buff *skb)
+{
+	struct config *config = &cb->u.config;
+	u8 *c = (u8 *)config;
+
+	cb->command = cpu_to_le16(cb_config);
+
+	memset(config, 0, sizeof(struct config));
+
+	config->byte_count = 0x16;		/* bytes in this struct */
+	config->rx_fifo_limit = 0x8;		/* bytes in FIFO before DMA */
+	config->direct_rx_dma = 0x1;		/* reserved */
+	config->standard_tcb = 0x1;		/* 1=standard, 0=extended */
+	config->standard_stat_counter = 0x1;	/* 1=standard, 0=extended */
+	config->rx_discard_short_frames = 0x1;	/* 1=discard, 0=pass */
+	config->tx_underrun_retry = 0x3;	/* # of underrun retries */
+	config->mii_mode = 0x1;			/* 1=MII mode, 0=503 mode */
+	config->pad10 = 0x6;
+	config->no_source_addr_insertion = 0x1;	/* 1=no, 0=yes */
+	config->preamble_length = 0x2;		/* 0=1, 1=3, 2=7, 3=15 bytes */
+	config->ifs = 0x6;			/* x16 = inter frame spacing */
+	config->ip_addr_hi = 0xF2;		/* ARP IP filter - not used */
+	config->pad15_1 = 0x1;
+	config->pad15_2 = 0x1;
+	config->crs_or_cdt = 0x0;		/* 0=CRS only, 1=CRS or CDT */
+	config->fc_delay_hi = 0x40;		/* time delay for fc frame */
+	config->tx_padding = 0x1;		/* 1=pad short frames */
+	config->fc_priority_threshold = 0x7;	/* 7=priority fc disabled */
+	config->pad18 = 0x1;
+	config->full_duplex_pin = 0x1;		/* 1=examine FDX# pin */
+	config->pad20_1 = 0x1F;
+	config->fc_priority_location = 0x1;	/* 1=byte#31, 0=byte#19 */
+	config->pad21_1 = 0x5;
+
+	config->adaptive_ifs = nic->adaptive_ifs;
+	config->loopback = nic->loopback;
+
+	if(nic->mii.force_media && nic->mii.full_duplex)
+		config->full_duplex_force = 0x1;	/* 1=force, 0=auto */
+
+	if(nic->flags & promiscuous || nic->loopback) {
+		config->rx_save_bad_frames = 0x1;	/* 1=save, 0=discard */
+		config->rx_discard_short_frames = 0x0;	/* 1=discard, 0=save */
+		config->promiscuous_mode = 0x1;		/* 1=on, 0=off */
+	}
+
+	if(nic->flags & multicast_all)
+		config->multicast_all = 0x1;		/* 1=accept, 0=no */
+
+	/* disable WoL when up */
+	if(netif_running(nic->netdev) || !(nic->flags & wol_magic))
+		config->magic_packet_disable = 0x1;	/* 1=off, 0=on */
+
+	if(nic->mac >= mac_82558_D101_A4) {
+		config->fc_disable = 0x1;	/* 1=Tx fc off, 0=Tx fc on */
+		config->mwi_enable = 0x1;	/* 1=enable, 0=disable */
+		config->standard_tcb = 0x0;	/* 1=standard, 0=extended */
+		config->rx_long_ok = 0x1;	/* 1=VLANs ok, 0=standard */
+		if(nic->mac >= mac_82559_D101M)
+			config->tno_intr = 0x1;		/* TCO stats enable */
+		else
+			config->standard_stat_counter = 0x0;
+	}
+
+	DPRINTK(HW, DEBUG, "[00-07]=%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+		c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]);
+	DPRINTK(HW, DEBUG, "[08-15]=%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+		c[8], c[9], c[10], c[11], c[12], c[13], c[14], c[15]);
+	DPRINTK(HW, DEBUG, "[16-23]=%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+		c[16], c[17], c[18], c[19], c[20], c[21], c[22], c[23]);
+}
+
+/********************************************************/
+/*  Micro code for 8086:1229 Rev 8                      */
+/********************************************************/
+
+/*  Parameter values for the D101M B-step  */
+#define D101M_CPUSAVER_TIMER_DWORD		78
+#define D101M_CPUSAVER_BUNDLE_DWORD		65
+#define D101M_CPUSAVER_MIN_SIZE_DWORD		126
+
+#define D101M_B_RCVBUNDLE_UCODE \
+{\
+0x00550215, 0xFFFF0437, 0xFFFFFFFF, 0x06A70789, 0xFFFFFFFF, 0x0558FFFF, \
+0x000C0001, 0x00101312, 0x000C0008, 0x00380216, \
+0x0010009C, 0x00204056, 0x002380CC, 0x00380056, \
+0x0010009C, 0x00244C0B, 0x00000800, 0x00124818, \
+0x00380438, 0x00000000, 0x00140000, 0x00380555, \
+0x00308000, 0x00100662, 0x00100561, 0x000E0408, \
+0x00134861, 0x000C0002, 0x00103093, 0x00308000, \
+0x00100624, 0x00100561, 0x000E0408, 0x00100861, \
+0x000C007E, 0x00222C21, 0x000C0002, 0x00103093, \
+0x00380C7A, 0x00080000, 0x00103090, 0x00380C7A, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x0010009C, 0x00244C2D, 0x00010004, 0x00041000, \
+0x003A0437, 0x00044010, 0x0038078A, 0x00000000, \
+0x00100099, 0x00206C7A, 0x0010009C, 0x00244C48, \
+0x00130824, 0x000C0001, 0x00101213, 0x00260C75, \
+0x00041000, 0x00010004, 0x00130826, 0x000C0006, \
+0x002206A8, 0x0013C926, 0x00101313, 0x003806A8, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00080600, 0x00101B10, 0x00050004, 0x00100826, \
+0x00101210, 0x00380C34, 0x00000000, 0x00000000, \
+0x0021155B, 0x00100099, 0x00206559, 0x0010009C, \
+0x00244559, 0x00130836, 0x000C0000, 0x00220C62, \
+0x000C0001, 0x00101B13, 0x00229C0E, 0x00210C0E, \
+0x00226C0E, 0x00216C0E, 0x0022FC0E, 0x00215C0E, \
+0x00214C0E, 0x00380555, 0x00010004, 0x00041000, \
+0x00278C67, 0x00040800, 0x00018100, 0x003A0437, \
+0x00130826, 0x000C0001, 0x00220559, 0x00101313, \
+0x00380559, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00130831, 0x0010090B, 0x00124813, \
+0x000CFF80, 0x002606AB, 0x00041000, 0x00010004, \
+0x003806A8, 0x00000000, 0x00000000, 0x00000000, \
+}
+
+/********************************************************/
+/*  Micro code for 8086:1229 Rev 9                      */
+/********************************************************/
+
+/*  Parameter values for the D101S  */
+#define D101S_CPUSAVER_TIMER_DWORD		78
+#define D101S_CPUSAVER_BUNDLE_DWORD		67
+#define D101S_CPUSAVER_MIN_SIZE_DWORD		128
+
+#define D101S_RCVBUNDLE_UCODE \
+{\
+0x00550242, 0xFFFF047E, 0xFFFFFFFF, 0x06FF0818, 0xFFFFFFFF, 0x05A6FFFF, \
+0x000C0001, 0x00101312, 0x000C0008, 0x00380243, \
+0x0010009C, 0x00204056, 0x002380D0, 0x00380056, \
+0x0010009C, 0x00244F8B, 0x00000800, 0x00124818, \
+0x0038047F, 0x00000000, 0x00140000, 0x003805A3, \
+0x00308000, 0x00100610, 0x00100561, 0x000E0408, \
+0x00134861, 0x000C0002, 0x00103093, 0x00308000, \
+0x00100624, 0x00100561, 0x000E0408, 0x00100861, \
+0x000C007E, 0x00222FA1, 0x000C0002, 0x00103093, \
+0x00380F90, 0x00080000, 0x00103090, 0x00380F90, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x0010009C, 0x00244FAD, 0x00010004, 0x00041000, \
+0x003A047E, 0x00044010, 0x00380819, 0x00000000, \
+0x00100099, 0x00206FFD, 0x0010009A, 0x0020AFFD, \
+0x0010009C, 0x00244FC8, 0x00130824, 0x000C0001, \
+0x00101213, 0x00260FF7, 0x00041000, 0x00010004, \
+0x00130826, 0x000C0006, 0x00220700, 0x0013C926, \
+0x00101313, 0x00380700, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00080600, 0x00101B10, 0x00050004, 0x00100826, \
+0x00101210, 0x00380FB6, 0x00000000, 0x00000000, \
+0x002115A9, 0x00100099, 0x002065A7, 0x0010009A, \
+0x0020A5A7, 0x0010009C, 0x002445A7, 0x00130836, \
+0x000C0000, 0x00220FE4, 0x000C0001, 0x00101B13, \
+0x00229F8E, 0x00210F8E, 0x00226F8E, 0x00216F8E, \
+0x0022FF8E, 0x00215F8E, 0x00214F8E, 0x003805A3, \
+0x00010004, 0x00041000, 0x00278FE9, 0x00040800, \
+0x00018100, 0x003A047E, 0x00130826, 0x000C0001, \
+0x002205A7, 0x00101313, 0x003805A7, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00130831, \
+0x0010090B, 0x00124813, 0x000CFF80, 0x00260703, \
+0x00041000, 0x00010004, 0x00380700  \
+}
+
+/********************************************************/
+/*  Micro code for the 8086:1229 Rev F/10               */
+/********************************************************/
+
+/*  Parameter values for the D102 E-step  */
+#define D102_E_CPUSAVER_TIMER_DWORD		42
+#define D102_E_CPUSAVER_BUNDLE_DWORD		54
+#define D102_E_CPUSAVER_MIN_SIZE_DWORD		46
+
+#define     D102_E_RCVBUNDLE_UCODE \
+{\
+0x007D028F, 0x0E4204F9, 0x14ED0C85, 0x14FA14E9, 0x0EF70E36, 0x1FFF1FFF, \
+0x00E014B9, 0x00000000, 0x00000000, 0x00000000, \
+0x00E014BD, 0x00000000, 0x00000000, 0x00000000, \
+0x00E014D5, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00E014C1, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00E014C8, 0x00000000, 0x00000000, 0x00000000, \
+0x00200600, 0x00E014EE, 0x00000000, 0x00000000, \
+0x0030FF80, 0x00940E46, 0x00038200, 0x00102000, \
+0x00E00E43, 0x00000000, 0x00000000, 0x00000000, \
+0x00300006, 0x00E014FB, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00906E41, 0x00800E3C, 0x00E00E39, 0x00000000, \
+0x00906EFD, 0x00900EFD, 0x00E00EF8, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+0x00000000, 0x00000000, 0x00000000, 0x00000000, \
+}
+
+static void e100_load_ucode(struct nic *nic, struct cb *cb, struct sk_buff *skb)
+{
+/* *INDENT-OFF* */
+	static struct {
+		u32 ucode[UCODE_SIZE + 1];
+		u8 mac;
+		u8 timer_dword;
+		u8 bundle_dword;
+		u8 min_size_dword;
+	} ucode_opts[] = {
+		{ D101M_B_RCVBUNDLE_UCODE,
+		  mac_82559_D101M,
+		  D101M_CPUSAVER_TIMER_DWORD,
+		  D101M_CPUSAVER_BUNDLE_DWORD,
+		  D101M_CPUSAVER_MIN_SIZE_DWORD },
+		{ D101S_RCVBUNDLE_UCODE,
+		  mac_82559_D101S,
+		  D101S_CPUSAVER_TIMER_DWORD,
+		  D101S_CPUSAVER_BUNDLE_DWORD,
+		  D101S_CPUSAVER_MIN_SIZE_DWORD },
+		{ D102_E_RCVBUNDLE_UCODE,
+		  mac_82551_F,
+		  D102_E_CPUSAVER_TIMER_DWORD,
+		  D102_E_CPUSAVER_BUNDLE_DWORD,
+		  D102_E_CPUSAVER_MIN_SIZE_DWORD },
+		{ D102_E_RCVBUNDLE_UCODE,
+		  mac_82551_10,
+		  D102_E_CPUSAVER_TIMER_DWORD,
+		  D102_E_CPUSAVER_BUNDLE_DWORD,
+		  D102_E_CPUSAVER_MIN_SIZE_DWORD },
+		{ {0}, 0, 0, 0, 0}
+	}, *opts;
+/* *INDENT-ON* */
+
+/*************************************************************************
+*  CPUSaver parameters
+*
+*  All CPUSaver parameters are 16-bit literals that are part of a
+*  "move immediate value" instruction.  By changing the value of
+*  the literal in the instruction before the code is loaded, the
+*  driver can change the algorithm.
+*
+*  INTDELAY - This loads the dead-man timer with its inital value.
+*    When this timer expires the interrupt is asserted, and the 
+*    timer is reset each time a new packet is received.  (see
+*    BUNDLEMAX below to set the limit on number of chained packets)
+*    The current default is 0x600 or 1536.  Experiments show that
+*    the value should probably stay within the 0x200 - 0x1000.
+*
+*  BUNDLEMAX - 
+*    This sets the maximum number of frames that will be bundled.  In
+*    some situations, such as the TCP windowing algorithm, it may be
+*    better to limit the growth of the bundle size than let it go as
+*    high as it can, because that could cause too much added latency.
+*    The default is six, because this is the number of packets in the
+*    default TCP window size.  A value of 1 would make CPUSaver indicate
+*    an interrupt for every frame received.  If you do not want to put
+*    a limit on the bundle size, set this value to xFFFF.
+*
+*  BUNDLESMALL - 
+*    This contains a bit-mask describing the minimum size frame that
+*    will be bundled.  The default masks the lower 7 bits, which means
+*    that any frame less than 128 bytes in length will not be bundled,
+*    but will instead immediately generate an interrupt.  This does
+*    not affect the current bundle in any way.  Any frame that is 128
+*    bytes or large will be bundled normally.  This feature is meant
+*    to provide immediate indication of ACK frames in a TCP environment.
+*    Customers were seeing poor performance when a machine with CPUSaver
+*    enabled was sending but not receiving.  The delay introduced when
+*    the ACKs were received was enough to reduce total throughput, because
+*    the sender would sit idle until the ACK was finally seen.
+*
+*    The current default is 0xFF80, which masks out the lower 7 bits.
+*    This means that any frame which is x7F (127) bytes or smaller
+*    will cause an immediate interrupt.  Because this value must be a 
+*    bit mask, there are only a few valid values that can be used.  To
+*    turn this feature off, the driver can write the value xFFFF to the
+*    lower word of this instruction (in the same way that the other
+*    parameters are used).  Likewise, a value of 0xF800 (2047) would
+*    cause an interrupt to be generated for every frame, because all
+*    standard Ethernet frames are <= 2047 bytes in length.
+*************************************************************************/
+
+/* if you wish to disable the ucode functionality, while maintaining the 
+ * workarounds it provides, set the following defines to:
+ * BUNDLESMALL 0
+ * BUNDLEMAX 1
+ * INTDELAY 1
+ */
+#define BUNDLESMALL 1
+#define BUNDLEMAX (u16)6
+#define INTDELAY (u16)1536 /* 0x600 */
+
+	/* do not load u-code for ICH devices */
+	if (nic->flags & ich)
+		goto noloaducode;
+
+	/* Search for ucode match against h/w rev_id */
+	for (opts = ucode_opts; opts->mac; opts++) {
+		int i;
+		u32 *ucode = opts->ucode;
+		if (nic->mac != opts->mac)
+			continue;
+
+		/* Insert user-tunable settings */
+		ucode[opts->timer_dword] &= 0xFFFF0000;
+		ucode[opts->timer_dword] |= INTDELAY;
+		ucode[opts->bundle_dword] &= 0xFFFF0000;
+		ucode[opts->bundle_dword] |= BUNDLEMAX;
+		ucode[opts->min_size_dword] &= 0xFFFF0000;
+		ucode[opts->min_size_dword] |= (BUNDLESMALL) ? 0xFFFF : 0xFF80;
+
+		for (i = 0; i < UCODE_SIZE; i++)
+			cb->u.ucode[i] = cpu_to_le32(ucode[i]);
+		cb->command = cpu_to_le16(cb_ucode);
+		return;
+	}
+
+noloaducode:
+	cb->command = cpu_to_le16(cb_nop);
+}
+
+static void e100_setup_iaaddr(struct nic *nic, struct cb *cb,
+	struct sk_buff *skb)
+{
+	cb->command = cpu_to_le16(cb_iaaddr);
+	memcpy(cb->u.iaaddr, nic->netdev->dev_addr, ETH_ALEN);
+}
+
+static void e100_dump(struct nic *nic, struct cb *cb, struct sk_buff *skb)
+{
+	cb->command = cpu_to_le16(cb_dump);
+	cb->u.dump_buffer_addr = cpu_to_le32(nic->dma_addr +
+		offsetof(struct mem, dump_buf));
+}
+
+#define NCONFIG_AUTO_SWITCH	0x0080
+#define MII_NSC_CONG		MII_RESV1
+#define NSC_CONG_ENABLE		0x0100
+#define NSC_CONG_TXREADY	0x0400
+#define ADVERTISE_FC_SUPPORTED	0x0400
+static int e100_phy_init(struct nic *nic)
+{
+	struct net_device *netdev = nic->netdev;
+	u32 addr;
+	u16 bmcr, stat, id_lo, id_hi, cong;
+
+	/* Discover phy addr by searching addrs in order {1,0,2,..., 31} */
+	for(addr = 0; addr < 32; addr++) {
+		nic->mii.phy_id = (addr == 0) ? 1 : (addr == 1) ? 0 : addr;
+		bmcr = mdio_read(netdev, nic->mii.phy_id, MII_BMCR);
+		stat = mdio_read(netdev, nic->mii.phy_id, MII_BMSR);
+		stat = mdio_read(netdev, nic->mii.phy_id, MII_BMSR);
+		if(!((bmcr == 0xFFFF) || ((stat == 0) && (bmcr == 0))))
+			break;
+	}
+	DPRINTK(HW, DEBUG, "phy_addr = %d\n", nic->mii.phy_id);
+	if(addr == 32)
+		return -EAGAIN;
+
+	/* Selected the phy and isolate the rest */
+	for(addr = 0; addr < 32; addr++) {
+		if(addr != nic->mii.phy_id) {
+			mdio_write(netdev, addr, MII_BMCR, BMCR_ISOLATE);
+		} else {
+			bmcr = mdio_read(netdev, addr, MII_BMCR);
+			mdio_write(netdev, addr, MII_BMCR,
+				bmcr & ~BMCR_ISOLATE);
+		}
+	}
+
+	/* Get phy ID */
+	id_lo = mdio_read(netdev, nic->mii.phy_id, MII_PHYSID1);
+	id_hi = mdio_read(netdev, nic->mii.phy_id, MII_PHYSID2);
+	nic->phy = (u32)id_hi << 16 | (u32)id_lo;
+	DPRINTK(HW, DEBUG, "phy ID = 0x%08X\n", nic->phy);
+
+	/* Handle National tx phys */
+#define NCS_PHY_MODEL_MASK	0xFFF0FFFF
+	if((nic->phy & NCS_PHY_MODEL_MASK) == phy_nsc_tx) {
+		/* Disable congestion control */
+		cong = mdio_read(netdev, nic->mii.phy_id, MII_NSC_CONG);
+		cong |= NSC_CONG_TXREADY;
+		cong &= ~NSC_CONG_ENABLE;
+		mdio_write(netdev, nic->mii.phy_id, MII_NSC_CONG, cong);
+	}
+
+	if((nic->mac >= mac_82550_D102) || ((nic->flags & ich) && 
+	   (mdio_read(netdev, nic->mii.phy_id, MII_TPISTATUS) & 0x8000))) {
+		/* enable/disable MDI/MDI-X auto-switching.
+		   MDI/MDI-X auto-switching is disabled for 82551ER/QM chips */
+		if((nic->mac == mac_82551_E) || (nic->mac == mac_82551_F) ||
+		   (nic->mac == mac_82551_10) || (nic->mii.force_media) || 
+		   !(nic->eeprom[eeprom_cnfg_mdix] & eeprom_mdix_enabled)) 
+			mdio_write(netdev, nic->mii.phy_id, MII_NCONFIG, 0);
+		else
+			mdio_write(netdev, nic->mii.phy_id, MII_NCONFIG, NCONFIG_AUTO_SWITCH);
+	}
+
+	return 0;
+}
+
+static int e100_hw_init(struct nic *nic)
+{
+	int err;
+
+	e100_hw_reset(nic);
+
+	DPRINTK(HW, ERR, "e100_hw_init\n");
+	if(!in_interrupt() && (err = e100_self_test(nic)))
+		return err;
+
+	if((err = e100_phy_init(nic)))
+		return err;
+	if((err = e100_exec_cmd(nic, cuc_load_base, 0)))
+		return err;
+	if((err = e100_exec_cmd(nic, ruc_load_base, 0)))
+		return err;
+	if((err = e100_exec_cb(nic, NULL, e100_load_ucode)))
+		return err;
+	if((err = e100_exec_cb(nic, NULL, e100_configure)))
+		return err;
+	if((err = e100_exec_cb(nic, NULL, e100_setup_iaaddr)))
+		return err;
+	if((err = e100_exec_cmd(nic, cuc_dump_addr,
+		nic->dma_addr + offsetof(struct mem, stats))))
+		return err;
+	if((err = e100_exec_cmd(nic, cuc_dump_reset, 0)))
+		return err;
+
+	e100_disable_irq(nic);
+
+	return 0;
+}
+
+static void e100_multi(struct nic *nic, struct cb *cb, struct sk_buff *skb)
+{
+	struct net_device *netdev = nic->netdev;
+	struct dev_mc_list *list = netdev->mc_list;
+	u16 i, count = min(netdev->mc_count, E100_MAX_MULTICAST_ADDRS);
+
+	cb->command = cpu_to_le16(cb_multi);
+	cb->u.multi.count = cpu_to_le16(count * ETH_ALEN);
+	for(i = 0; list && i < count; i++, list = list->next)
+		memcpy(&cb->u.multi.addr[i*ETH_ALEN], &list->dmi_addr,
+			ETH_ALEN);
+}
+
+static void e100_set_multicast_list(struct net_device *netdev)
+{
+	struct nic *nic = netdev_priv(netdev);
+
+	DPRINTK(HW, DEBUG, "mc_count=%d, flags=0x%04X\n",
+		netdev->mc_count, netdev->flags);
+
+	if(netdev->flags & IFF_PROMISC)
+		nic->flags |= promiscuous;
+	else
+		nic->flags &= ~promiscuous;
+
+	if(netdev->flags & IFF_ALLMULTI ||
+		netdev->mc_count > E100_MAX_MULTICAST_ADDRS)
+		nic->flags |= multicast_all;
+	else
+		nic->flags &= ~multicast_all;
+
+	e100_exec_cb(nic, NULL, e100_configure);
+	e100_exec_cb(nic, NULL, e100_multi);
+}
+
+static void e100_update_stats(struct nic *nic)
+{
+	struct net_device_stats *ns = &nic->net_stats;
+	struct stats *s = &nic->mem->stats;
+	u32 *complete = (nic->mac < mac_82558_D101_A4) ? &s->fc_xmt_pause :
+		(nic->mac < mac_82559_D101M) ? (u32 *)&s->xmt_tco_frames :
+		&s->complete;
+
+	/* Device's stats reporting may take several microseconds to
+	 * complete, so where always waiting for results of the
+	 * previous command. */
+
+	if(*complete == le32_to_cpu(cuc_dump_reset_complete)) {
+		*complete = 0;
+		nic->tx_frames = le32_to_cpu(s->tx_good_frames);
+		nic->tx_collisions = le32_to_cpu(s->tx_total_collisions);
+		ns->tx_aborted_errors += le32_to_cpu(s->tx_max_collisions);
+		ns->tx_window_errors += le32_to_cpu(s->tx_late_collisions);
+		ns->tx_carrier_errors += le32_to_cpu(s->tx_lost_crs);
+		ns->tx_fifo_errors += le32_to_cpu(s->tx_underruns);
+		ns->collisions += nic->tx_collisions;
+		ns->tx_errors += le32_to_cpu(s->tx_max_collisions) +
+			le32_to_cpu(s->tx_lost_crs);
+		ns->rx_length_errors += le32_to_cpu(s->rx_short_frame_errors) +
+			nic->rx_over_length_errors;
+		ns->rx_crc_errors += le32_to_cpu(s->rx_crc_errors);
+		ns->rx_frame_errors += le32_to_cpu(s->rx_alignment_errors);
+		ns->rx_over_errors += le32_to_cpu(s->rx_overrun_errors);
+		ns->rx_fifo_errors += le32_to_cpu(s->rx_overrun_errors);
+		ns->rx_missed_errors += le32_to_cpu(s->rx_resource_errors);
+		ns->rx_errors += le32_to_cpu(s->rx_crc_errors) +
+			le32_to_cpu(s->rx_alignment_errors) +
+			le32_to_cpu(s->rx_short_frame_errors) +
+			le32_to_cpu(s->rx_cdt_errors);
+		nic->tx_deferred += le32_to_cpu(s->tx_deferred);
+		nic->tx_single_collisions +=
+			le32_to_cpu(s->tx_single_collisions);
+		nic->tx_multiple_collisions +=
+			le32_to_cpu(s->tx_multiple_collisions);
+		if(nic->mac >= mac_82558_D101_A4) {
+			nic->tx_fc_pause += le32_to_cpu(s->fc_xmt_pause);
+			nic->rx_fc_pause += le32_to_cpu(s->fc_rcv_pause);
+			nic->rx_fc_unsupported +=
+				le32_to_cpu(s->fc_rcv_unsupported);
+			if(nic->mac >= mac_82559_D101M) {
+				nic->tx_tco_frames +=
+					le16_to_cpu(s->xmt_tco_frames);
+				nic->rx_tco_frames +=
+					le16_to_cpu(s->rcv_tco_frames);
+			}
+		}
+	}
+
+	
+	if(e100_exec_cmd(nic, cuc_dump_reset, 0))
+		DPRINTK(TX_ERR, DEBUG, "exec cuc_dump_reset failed\n");
+}
+
+static void e100_adjust_adaptive_ifs(struct nic *nic, int speed, int duplex)
+{
+	/* Adjust inter-frame-spacing (IFS) between two transmits if
+	 * we're getting collisions on a half-duplex connection. */
+
+	if(duplex == DUPLEX_HALF) {
+		u32 prev = nic->adaptive_ifs;
+		u32 min_frames = (speed == SPEED_100) ? 1000 : 100;
+
+		if((nic->tx_frames / 32 < nic->tx_collisions) &&
+		   (nic->tx_frames > min_frames)) {
+			if(nic->adaptive_ifs < 60)
+				nic->adaptive_ifs += 5;
+		} else if (nic->tx_frames < min_frames) {
+			if(nic->adaptive_ifs >= 5)
+				nic->adaptive_ifs -= 5;
+		}
+		if(nic->adaptive_ifs != prev)
+			e100_exec_cb(nic, NULL, e100_configure);
+	}
+}
+
+static void e100_watchdog(unsigned long data)
+{
+	struct nic *nic = (struct nic *)data;
+	struct ethtool_cmd cmd;
+
+	DPRINTK(TIMER, DEBUG, "right now = %ld\n", jiffies);
+
+	/* mii library handles link maintenance tasks */
+
+	mii_ethtool_gset(&nic->mii, &cmd);
+
+	if(mii_link_ok(&nic->mii) && !netif_carrier_ok(nic->netdev)) {
+		DPRINTK(LINK, INFO, "link up, %sMbps, %s-duplex\n",
+			cmd.speed == SPEED_100 ? "100" : "10",
+			cmd.duplex == DUPLEX_FULL ? "full" : "half");
+	} else if(!mii_link_ok(&nic->mii) && netif_carrier_ok(nic->netdev)) {
+		DPRINTK(LINK, INFO, "link down\n");
+	}
+
+	mii_check_link(&nic->mii);
+
+	/* Software generated interrupt to recover from (rare) Rx
+	* allocation failure.
+	* Unfortunately have to use a spinlock to not re-enable interrupts
+	* accidentally, due to hardware that shares a register between the
+	* interrupt mask bit and the SW Interrupt generation bit */
+	spin_lock_irq(&nic->cmd_lock);
+	writeb(readb(&nic->csr->scb.cmd_hi) | irq_sw_gen,&nic->csr->scb.cmd_hi);
+	spin_unlock_irq(&nic->cmd_lock);
+	e100_write_flush(nic);
+
+	e100_update_stats(nic);
+	e100_adjust_adaptive_ifs(nic, cmd.speed, cmd.duplex);
+
+	if(nic->mac <= mac_82557_D100_C)
+		/* Issue a multicast command to workaround a 557 lock up */
+		e100_set_multicast_list(nic->netdev);
+
+	if(nic->flags & ich && cmd.speed==SPEED_10 && cmd.duplex==DUPLEX_HALF)
+		/* Need SW workaround for ICH[x] 10Mbps/half duplex Tx hang. */
+		nic->flags |= ich_10h_workaround;
+	else
+		nic->flags &= ~ich_10h_workaround;
+
+	mod_timer(&nic->watchdog, jiffies + E100_WATCHDOG_PERIOD);
+}
+
+static inline void e100_xmit_prepare(struct nic *nic, struct cb *cb,
+	struct sk_buff *skb)
+{
+	cb->command = nic->tx_command;
+	/* interrupt every 16 packets regardless of delay */
+	if((nic->cbs_avail & ~15) == nic->cbs_avail)
+		cb->command |= cpu_to_le16(cb_i);
+	cb->u.tcb.tbd_array = cb->dma_addr + offsetof(struct cb, u.tcb.tbd);
+	cb->u.tcb.tcb_byte_count = 0;
+	cb->u.tcb.threshold = nic->tx_threshold;
+	cb->u.tcb.tbd_count = 1;
+	cb->u.tcb.tbd.buf_addr = cpu_to_le32(pci_map_single(nic->pdev,
+		skb->data, skb->len, PCI_DMA_TODEVICE));
+	/* check for mapping failure? */
+	cb->u.tcb.tbd.size = cpu_to_le16(skb->len);
+}
+
+static int e100_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
+{
+	struct nic *nic = netdev_priv(netdev);
+	int err;
+
+	if(nic->flags & ich_10h_workaround) {
+		/* SW workaround for ICH[x] 10Mbps/half duplex Tx hang.
+		   Issue a NOP command followed by a 1us delay before
+		   issuing the Tx command. */
+		if(e100_exec_cmd(nic, cuc_nop, 0))
+			DPRINTK(TX_ERR, DEBUG, "exec cuc_nop failed\n");
+		udelay(1);
+	}
+
+	err = e100_exec_cb(nic, skb, e100_xmit_prepare);
+
+	switch(err) {
+	case -ENOSPC:
+		/* We queued the skb, but now we're out of space. */
+		DPRINTK(TX_ERR, DEBUG, "No space for CB\n");
+		netif_stop_queue(netdev);
+		break;
+	case -ENOMEM:
+		/* This is a hard error - log it. */
+		DPRINTK(TX_ERR, DEBUG, "Out of Tx resources, returning skb\n");
+		netif_stop_queue(netdev);
+		return 1;
+	}
+
+	netdev->trans_start = jiffies;
+	return 0;
+}
+
+static inline int e100_tx_clean(struct nic *nic)
+{
+	struct cb *cb;
+	int tx_cleaned = 0;
+
+	spin_lock(&nic->cb_lock);
+
+	DPRINTK(TX_DONE, DEBUG, "cb->status = 0x%04X\n",
+		nic->cb_to_clean->status);
+
+	/* Clean CBs marked complete */
+	for(cb = nic->cb_to_clean;
+	    cb->status & cpu_to_le16(cb_complete);
+	    cb = nic->cb_to_clean = cb->next) {
+		if(likely(cb->skb != NULL)) {
+			nic->net_stats.tx_packets++;
+			nic->net_stats.tx_bytes += cb->skb->len;
+
+			pci_unmap_single(nic->pdev,
+				le32_to_cpu(cb->u.tcb.tbd.buf_addr),
+				le16_to_cpu(cb->u.tcb.tbd.size),
+				PCI_DMA_TODEVICE);
+			dev_kfree_skb_any(cb->skb);
+			cb->skb = NULL;
+			tx_cleaned = 1;
+		}
+		cb->status = 0;
+		nic->cbs_avail++;
+	}
+
+	spin_unlock(&nic->cb_lock);
+
+	/* Recover from running out of Tx resources in xmit_frame */
+	if(unlikely(tx_cleaned && netif_queue_stopped(nic->netdev)))
+		netif_wake_queue(nic->netdev);
+
+	return tx_cleaned;
+}
+
+static void e100_clean_cbs(struct nic *nic)
+{
+	if(nic->cbs) {
+		while(nic->cbs_avail != nic->params.cbs.count) {
+			struct cb *cb = nic->cb_to_clean;
+			if(cb->skb) {
+				pci_unmap_single(nic->pdev,
+					le32_to_cpu(cb->u.tcb.tbd.buf_addr),
+					le16_to_cpu(cb->u.tcb.tbd.size),
+					PCI_DMA_TODEVICE);
+				dev_kfree_skb(cb->skb);
+			}
+			nic->cb_to_clean = nic->cb_to_clean->next;
+			nic->cbs_avail++;
+		}
+		pci_free_consistent(nic->pdev,
+			sizeof(struct cb) * nic->params.cbs.count,
+			nic->cbs, nic->cbs_dma_addr);
+		nic->cbs = NULL;
+		nic->cbs_avail = 0;
+	}
+	nic->cuc_cmd = cuc_start;
+	nic->cb_to_use = nic->cb_to_send = nic->cb_to_clean =
+		nic->cbs;
+}
+
+static int e100_alloc_cbs(struct nic *nic)
+{
+	struct cb *cb;
+	unsigned int i, count = nic->params.cbs.count;
+
+	nic->cuc_cmd = cuc_start;
+	nic->cb_to_use = nic->cb_to_send = nic->cb_to_clean = NULL;
+	nic->cbs_avail = 0;
+
+	nic->cbs = pci_alloc_consistent(nic->pdev,
+		sizeof(struct cb) * count, &nic->cbs_dma_addr);
+	if(!nic->cbs)
+		return -ENOMEM;
+
+	for(cb = nic->cbs, i = 0; i < count; cb++, i++) {
+		cb->next = (i + 1 < count) ? cb + 1 : nic->cbs;
+		cb->prev = (i == 0) ? nic->cbs + count - 1 : cb - 1;
+
+		cb->dma_addr = nic->cbs_dma_addr + i * sizeof(struct cb);
+		cb->link = cpu_to_le32(nic->cbs_dma_addr +
+			((i+1) % count) * sizeof(struct cb));
+		cb->skb = NULL;
+	}
+
+	nic->cb_to_use = nic->cb_to_send = nic->cb_to_clean = nic->cbs;
+	nic->cbs_avail = count;
+
+	return 0;
+}
+
+static inline void e100_start_receiver(struct nic *nic, struct rx *rx)
+{
+	if(!nic->rxs) return;
+	if(RU_SUSPENDED != nic->ru_running) return;
+
+	/* handle init time starts */
+	if(!rx) rx = nic->rxs;
+
+	/* (Re)start RU if suspended or idle and RFA is non-NULL */
+	if(rx->skb) {
+		e100_exec_cmd(nic, ruc_start, rx->dma_addr);
+		nic->ru_running = RU_RUNNING;
+	}
+}
+
+#define RFD_BUF_LEN (sizeof(struct rfd) + VLAN_ETH_FRAME_LEN)
+static inline int e100_rx_alloc_skb(struct nic *nic, struct rx *rx)
+{
+	if(!(rx->skb = dev_alloc_skb(RFD_BUF_LEN + NET_IP_ALIGN)))
+		return -ENOMEM;
+
+	/* Align, init, and map the RFD. */
+	rx->skb->dev = nic->netdev;
+	skb_reserve(rx->skb, NET_IP_ALIGN);
+	memcpy(rx->skb->data, &nic->blank_rfd, sizeof(struct rfd));
+	rx->dma_addr = pci_map_single(nic->pdev, rx->skb->data,
+		RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL);
+
+	if(pci_dma_mapping_error(rx->dma_addr)) {
+		dev_kfree_skb_any(rx->skb);
+		rx->skb = NULL;
+		rx->dma_addr = 0;
+		return -ENOMEM;
+	}
+
+	/* Link the RFD to end of RFA by linking previous RFD to
+	 * this one, and clearing EL bit of previous.  */
+	if(rx->prev->skb) {
+		struct rfd *prev_rfd = (struct rfd *)rx->prev->skb->data;
+		put_unaligned(cpu_to_le32(rx->dma_addr),
+			(u32 *)&prev_rfd->link);
+		wmb();
+		prev_rfd->command &= ~cpu_to_le16(cb_el);
+		pci_dma_sync_single_for_device(nic->pdev, rx->prev->dma_addr,
+			sizeof(struct rfd), PCI_DMA_TODEVICE);
+	}
+
+	return 0;
+}
+
+static inline int e100_rx_indicate(struct nic *nic, struct rx *rx,
+	unsigned int *work_done, unsigned int work_to_do)
+{
+	struct sk_buff *skb = rx->skb;
+	struct rfd *rfd = (struct rfd *)skb->data;
+	u16 rfd_status, actual_size;
+
+	if(unlikely(work_done && *work_done >= work_to_do))
+		return -EAGAIN;
+
+	/* Need to sync before taking a peek at cb_complete bit */
+	pci_dma_sync_single_for_cpu(nic->pdev, rx->dma_addr,
+		sizeof(struct rfd), PCI_DMA_FROMDEVICE);
+	rfd_status = le16_to_cpu(rfd->status);
+
+	DPRINTK(RX_STATUS, DEBUG, "status=0x%04X\n", rfd_status);
+
+	/* If data isn't ready, nothing to indicate */
+	if(unlikely(!(rfd_status & cb_complete)))
+		return -ENODATA;
+
+	/* Get actual data size */
+	actual_size = le16_to_cpu(rfd->actual_size) & 0x3FFF;
+	if(unlikely(actual_size > RFD_BUF_LEN - sizeof(struct rfd)))
+		actual_size = RFD_BUF_LEN - sizeof(struct rfd);
+
+	/* Get data */
+	pci_unmap_single(nic->pdev, rx->dma_addr,
+		RFD_BUF_LEN, PCI_DMA_FROMDEVICE);
+
+	/* this allows for a fast restart without re-enabling interrupts */
+	if(le16_to_cpu(rfd->command) & cb_el)
+		nic->ru_running = RU_SUSPENDED;
+
+	/* Pull off the RFD and put the actual data (minus eth hdr) */
+	skb_reserve(skb, sizeof(struct rfd));
+	skb_put(skb, actual_size);
+	skb->protocol = eth_type_trans(skb, nic->netdev);
+
+	if(unlikely(!(rfd_status & cb_ok))) {
+		/* Don't indicate if hardware indicates errors */
+		dev_kfree_skb_any(skb);
+	} else if(actual_size > ETH_DATA_LEN + VLAN_ETH_HLEN) {
+		/* Don't indicate oversized frames */
+		nic->rx_over_length_errors++;
+		dev_kfree_skb_any(skb);
+	} else {
+		nic->net_stats.rx_packets++;
+		nic->net_stats.rx_bytes += actual_size;
+		nic->netdev->last_rx = jiffies;
+		netif_receive_skb(skb);
+		if(work_done)
+			(*work_done)++;
+	}
+
+	rx->skb = NULL;
+
+	return 0;
+}
+
+static inline void e100_rx_clean(struct nic *nic, unsigned int *work_done,
+	unsigned int work_to_do)
+{
+	struct rx *rx;
+	int restart_required = 0;
+	struct rx *rx_to_start = NULL;
+
+	/* are we already rnr? then pay attention!!! this ensures that
+	 * the state machine progression never allows a start with a 
+	 * partially cleaned list, avoiding a race between hardware
+	 * and rx_to_clean when in NAPI mode */
+	if(RU_SUSPENDED == nic->ru_running)
+		restart_required = 1;
+
+	/* Indicate newly arrived packets */
+	for(rx = nic->rx_to_clean; rx->skb; rx = nic->rx_to_clean = rx->next) {
+		int err = e100_rx_indicate(nic, rx, work_done, work_to_do);
+		if(-EAGAIN == err) {
+			/* hit quota so have more work to do, restart once
+			 * cleanup is complete */
+			restart_required = 0;
+			break;
+		} else if(-ENODATA == err)
+			break; /* No more to clean */
+	}
+
+	/* save our starting point as the place we'll restart the receiver */
+	if(restart_required)
+		rx_to_start = nic->rx_to_clean;
+
+	/* Alloc new skbs to refill list */
+	for(rx = nic->rx_to_use; !rx->skb; rx = nic->rx_to_use = rx->next) {
+		if(unlikely(e100_rx_alloc_skb(nic, rx)))
+			break; /* Better luck next time (see watchdog) */
+	}
+
+	if(restart_required) {
+		// ack the rnr?
+		writeb(stat_ack_rnr, &nic->csr->scb.stat_ack);
+		e100_start_receiver(nic, rx_to_start);
+		if(work_done)
+			(*work_done)++;
+	}
+}
+
+static void e100_rx_clean_list(struct nic *nic)
+{
+	struct rx *rx;
+	unsigned int i, count = nic->params.rfds.count;
+
+	nic->ru_running = RU_UNINITIALIZED;
+
+	if(nic->rxs) {
+		for(rx = nic->rxs, i = 0; i < count; rx++, i++) {
+			if(rx->skb) {
+				pci_unmap_single(nic->pdev, rx->dma_addr,
+					RFD_BUF_LEN, PCI_DMA_FROMDEVICE);
+				dev_kfree_skb(rx->skb);
+			}
+		}
+		kfree(nic->rxs);
+		nic->rxs = NULL;
+	}
+
+	nic->rx_to_use = nic->rx_to_clean = NULL;
+}
+
+static int e100_rx_alloc_list(struct nic *nic)
+{
+	struct rx *rx;
+	unsigned int i, count = nic->params.rfds.count;
+
+	nic->rx_to_use = nic->rx_to_clean = NULL;
+	nic->ru_running = RU_UNINITIALIZED;
+
+	if(!(nic->rxs = kmalloc(sizeof(struct rx) * count, GFP_ATOMIC)))
+		return -ENOMEM;
+	memset(nic->rxs, 0, sizeof(struct rx) * count);
+
+	for(rx = nic->rxs, i = 0; i < count; rx++, i++) {
+		rx->next = (i + 1 < count) ? rx + 1 : nic->rxs;
+		rx->prev = (i == 0) ? nic->rxs + count - 1 : rx - 1;
+		if(e100_rx_alloc_skb(nic, rx)) {
+			e100_rx_clean_list(nic);
+			return -ENOMEM;
+		}
+	}
+
+	nic->rx_to_use = nic->rx_to_clean = nic->rxs;
+	nic->ru_running = RU_SUSPENDED;
+
+	return 0;
+}
+
+static irqreturn_t e100_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *netdev = dev_id;
+	struct nic *nic = netdev_priv(netdev);
+	u8 stat_ack = readb(&nic->csr->scb.stat_ack);
+
+	DPRINTK(INTR, DEBUG, "stat_ack = 0x%02X\n", stat_ack);
+
+	if(stat_ack == stat_ack_not_ours ||	/* Not our interrupt */
+	   stat_ack == stat_ack_not_present)	/* Hardware is ejected */
+		return IRQ_NONE;
+
+	/* Ack interrupt(s) */
+	writeb(stat_ack, &nic->csr->scb.stat_ack);
+
+	/* We hit Receive No Resource (RNR); restart RU after cleaning */
+	if(stat_ack & stat_ack_rnr)
+		nic->ru_running = RU_SUSPENDED;
+
+	if(likely(netif_rx_schedule_prep(netdev))) {
+		e100_disable_irq(nic);
+		__netif_rx_schedule(netdev);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int e100_poll(struct net_device *netdev, int *budget)
+{
+	struct nic *nic = netdev_priv(netdev);
+	unsigned int work_to_do = min(netdev->quota, *budget);
+	unsigned int work_done = 0;
+	int tx_cleaned;
+
+	e100_rx_clean(nic, &work_done, work_to_do);
+	tx_cleaned = e100_tx_clean(nic);
+
+	/* If no Rx and Tx cleanup work was done, exit polling mode. */
+	if((!tx_cleaned && (work_done == 0)) || !netif_running(netdev)) {
+		netif_rx_complete(netdev);
+		e100_enable_irq(nic);
+		return 0;
+	}
+
+	*budget -= work_done;
+	netdev->quota -= work_done;
+
+	return 1;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void e100_netpoll(struct net_device *netdev)
+{
+	struct nic *nic = netdev_priv(netdev);
+
+	e100_disable_irq(nic);
+	e100_intr(nic->pdev->irq, netdev, NULL);
+	e100_tx_clean(nic);
+	e100_enable_irq(nic);
+}
+#endif
+
+static struct net_device_stats *e100_get_stats(struct net_device *netdev)
+{
+	struct nic *nic = netdev_priv(netdev);
+	return &nic->net_stats;
+}
+
+static int e100_set_mac_address(struct net_device *netdev, void *p)
+{
+	struct nic *nic = netdev_priv(netdev);
+	struct sockaddr *addr = p;
+
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+	e100_exec_cb(nic, NULL, e100_setup_iaaddr);
+
+	return 0;
+}
+
+static int e100_change_mtu(struct net_device *netdev, int new_mtu)
+{
+	if(new_mtu < ETH_ZLEN || new_mtu > ETH_DATA_LEN)
+		return -EINVAL;
+	netdev->mtu = new_mtu;
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int e100_asf(struct nic *nic)
+{
+	/* ASF can be enabled from eeprom */
+	return((nic->pdev->device >= 0x1050) && (nic->pdev->device <= 0x1057) &&
+	   (nic->eeprom[eeprom_config_asf] & eeprom_asf) &&
+	   !(nic->eeprom[eeprom_config_asf] & eeprom_gcl) &&
+	   ((nic->eeprom[eeprom_smbus_addr] & 0xFF) != 0xFE));
+}
+#endif
+
+static int e100_up(struct nic *nic)
+{
+	int err;
+
+	if((err = e100_rx_alloc_list(nic)))
+		return err;
+	if((err = e100_alloc_cbs(nic)))
+		goto err_rx_clean_list;
+	if((err = e100_hw_init(nic)))
+		goto err_clean_cbs;
+	e100_set_multicast_list(nic->netdev);
+	e100_start_receiver(nic, NULL);
+	mod_timer(&nic->watchdog, jiffies);
+	if((err = request_irq(nic->pdev->irq, e100_intr, SA_SHIRQ,
+		nic->netdev->name, nic->netdev)))
+		goto err_no_irq;
+	netif_wake_queue(nic->netdev);
+	netif_poll_enable(nic->netdev);
+	/* enable ints _after_ enabling poll, preventing a race between
+	 * disable ints+schedule */
+	e100_enable_irq(nic);
+	return 0;
+
+err_no_irq:
+	del_timer_sync(&nic->watchdog);
+err_clean_cbs:
+	e100_clean_cbs(nic);
+err_rx_clean_list:
+	e100_rx_clean_list(nic);
+	return err;
+}
+
+static void e100_down(struct nic *nic)
+{
+	/* wait here for poll to complete */
+	netif_poll_disable(nic->netdev);
+	netif_stop_queue(nic->netdev);
+	e100_hw_reset(nic);
+	free_irq(nic->pdev->irq, nic->netdev);
+	del_timer_sync(&nic->watchdog);
+	netif_carrier_off(nic->netdev);
+	e100_clean_cbs(nic);
+	e100_rx_clean_list(nic);
+}
+
+static void e100_tx_timeout(struct net_device *netdev)
+{
+	struct nic *nic = netdev_priv(netdev);
+
+	/* Reset outside of interrupt context, to avoid request_irq 
+	 * in interrupt context */
+	schedule_work(&nic->tx_timeout_task);
+}
+
+static void e100_tx_timeout_task(struct net_device *netdev)
+{
+	struct nic *nic = netdev_priv(netdev);
+
+	DPRINTK(TX_ERR, DEBUG, "scb.status=0x%02X\n",
+		readb(&nic->csr->scb.status));
+	e100_down(netdev_priv(netdev));
+	e100_up(netdev_priv(netdev));
+}
+
+static int e100_loopback_test(struct nic *nic, enum loopback loopback_mode)
+{
+	int err;
+	struct sk_buff *skb;
+
+	/* Use driver resources to perform internal MAC or PHY
+	 * loopback test.  A single packet is prepared and transmitted
+	 * in loopback mode, and the test passes if the received
+	 * packet compares byte-for-byte to the transmitted packet. */
+
+	if((err = e100_rx_alloc_list(nic)))
+		return err;
+	if((err = e100_alloc_cbs(nic)))
+		goto err_clean_rx;
+
+	/* ICH PHY loopback is broken so do MAC loopback instead */
+	if(nic->flags & ich && loopback_mode == lb_phy)
+		loopback_mode = lb_mac;
+
+	nic->loopback = loopback_mode;
+	if((err = e100_hw_init(nic)))
+		goto err_loopback_none;
+
+	if(loopback_mode == lb_phy)
+		mdio_write(nic->netdev, nic->mii.phy_id, MII_BMCR,
+			BMCR_LOOPBACK);
+
+	e100_start_receiver(nic, NULL);
+
+	if(!(skb = dev_alloc_skb(ETH_DATA_LEN))) {
+		err = -ENOMEM;
+		goto err_loopback_none;
+	}
+	skb_put(skb, ETH_DATA_LEN);
+	memset(skb->data, 0xFF, ETH_DATA_LEN);
+	e100_xmit_frame(skb, nic->netdev);
+
+	msleep(10);
+
+	if(memcmp(nic->rx_to_clean->skb->data + sizeof(struct rfd),
+	   skb->data, ETH_DATA_LEN))
+		err = -EAGAIN;
+
+err_loopback_none:
+	mdio_write(nic->netdev, nic->mii.phy_id, MII_BMCR, 0);
+	nic->loopback = lb_none;
+	e100_hw_init(nic);
+	e100_clean_cbs(nic);
+err_clean_rx:
+	e100_rx_clean_list(nic);
+	return err;
+}
+
+#define MII_LED_CONTROL	0x1B
+static void e100_blink_led(unsigned long data)
+{
+	struct nic *nic = (struct nic *)data;
+	enum led_state {
+		led_on     = 0x01,
+		led_off    = 0x04,
+		led_on_559 = 0x05,
+		led_on_557 = 0x07,
+	};
+
+	nic->leds = (nic->leds & led_on) ? led_off :
+		(nic->mac < mac_82559_D101M) ? led_on_557 : led_on_559;
+	mdio_write(nic->netdev, nic->mii.phy_id, MII_LED_CONTROL, nic->leds);
+	mod_timer(&nic->blink_timer, jiffies + HZ / 4);
+}
+
+static int e100_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
+{
+	struct nic *nic = netdev_priv(netdev);
+	return mii_ethtool_gset(&nic->mii, cmd);
+}
+
+static int e100_set_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
+{
+	struct nic *nic = netdev_priv(netdev);
+	int err;
+
+	mdio_write(netdev, nic->mii.phy_id, MII_BMCR, BMCR_RESET);
+	err = mii_ethtool_sset(&nic->mii, cmd);
+	e100_exec_cb(nic, NULL, e100_configure);
+
+	return err;
+}
+
+static void e100_get_drvinfo(struct net_device *netdev,
+	struct ethtool_drvinfo *info)
+{
+	struct nic *nic = netdev_priv(netdev);
+	strcpy(info->driver, DRV_NAME);
+	strcpy(info->version, DRV_VERSION);
+	strcpy(info->fw_version, "N/A");
+	strcpy(info->bus_info, pci_name(nic->pdev));
+}
+
+static int e100_get_regs_len(struct net_device *netdev)
+{
+	struct nic *nic = netdev_priv(netdev);
+#define E100_PHY_REGS		0x1C
+#define E100_REGS_LEN		1 + E100_PHY_REGS + \
+	sizeof(nic->mem->dump_buf) / sizeof(u32)
+	return E100_REGS_LEN * sizeof(u32);
+}
+
+static void e100_get_regs(struct net_device *netdev,
+	struct ethtool_regs *regs, void *p)
+{
+	struct nic *nic = netdev_priv(netdev);
+	u32 *buff = p;
+	int i;
+
+	regs->version = (1 << 24) | nic->rev_id;
+	buff[0] = readb(&nic->csr->scb.cmd_hi) << 24 |
+		readb(&nic->csr->scb.cmd_lo) << 16 |
+		readw(&nic->csr->scb.status);
+	for(i = E100_PHY_REGS; i >= 0; i--)
+		buff[1 + E100_PHY_REGS - i] =
+			mdio_read(netdev, nic->mii.phy_id, i);
+	memset(nic->mem->dump_buf, 0, sizeof(nic->mem->dump_buf));
+	e100_exec_cb(nic, NULL, e100_dump);
+	msleep(10);
+	memcpy(&buff[2 + E100_PHY_REGS], nic->mem->dump_buf,
+		sizeof(nic->mem->dump_buf));
+}
+
+static void e100_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
+{
+	struct nic *nic = netdev_priv(netdev);
+	wol->supported = (nic->mac >= mac_82558_D101_A4) ?  WAKE_MAGIC : 0;
+	wol->wolopts = (nic->flags & wol_magic) ? WAKE_MAGIC : 0;
+}
+
+static int e100_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
+{
+	struct nic *nic = netdev_priv(netdev);
+
+	if(wol->wolopts != WAKE_MAGIC && wol->wolopts != 0)
+		return -EOPNOTSUPP;
+
+	if(wol->wolopts)
+		nic->flags |= wol_magic;
+	else
+		nic->flags &= ~wol_magic;
+
+	e100_exec_cb(nic, NULL, e100_configure);
+
+	return 0;
+}
+
+static u32 e100_get_msglevel(struct net_device *netdev)
+{
+	struct nic *nic = netdev_priv(netdev);
+	return nic->msg_enable;
+}
+
+static void e100_set_msglevel(struct net_device *netdev, u32 value)
+{
+	struct nic *nic = netdev_priv(netdev);
+	nic->msg_enable = value;
+}
+
+static int e100_nway_reset(struct net_device *netdev)
+{
+	struct nic *nic = netdev_priv(netdev);
+	return mii_nway_restart(&nic->mii);
+}
+
+static u32 e100_get_link(struct net_device *netdev)
+{
+	struct nic *nic = netdev_priv(netdev);
+	return mii_link_ok(&nic->mii);
+}
+
+static int e100_get_eeprom_len(struct net_device *netdev)
+{
+	struct nic *nic = netdev_priv(netdev);
+	return nic->eeprom_wc << 1;
+}
+
+#define E100_EEPROM_MAGIC	0x1234
+static int e100_get_eeprom(struct net_device *netdev,
+	struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+	struct nic *nic = netdev_priv(netdev);
+
+	eeprom->magic = E100_EEPROM_MAGIC;
+	memcpy(bytes, &((u8 *)nic->eeprom)[eeprom->offset], eeprom->len);
+
+	return 0;
+}
+
+static int e100_set_eeprom(struct net_device *netdev,
+	struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+	struct nic *nic = netdev_priv(netdev);
+
+	if(eeprom->magic != E100_EEPROM_MAGIC)
+		return -EINVAL;
+
+	memcpy(&((u8 *)nic->eeprom)[eeprom->offset], bytes, eeprom->len);
+
+	return e100_eeprom_save(nic, eeprom->offset >> 1,
+		(eeprom->len >> 1) + 1);
+}
+
+static void e100_get_ringparam(struct net_device *netdev,
+	struct ethtool_ringparam *ring)
+{
+	struct nic *nic = netdev_priv(netdev);
+	struct param_range *rfds = &nic->params.rfds;
+	struct param_range *cbs = &nic->params.cbs;
+
+	ring->rx_max_pending = rfds->max;
+	ring->tx_max_pending = cbs->max;
+	ring->rx_mini_max_pending = 0;
+	ring->rx_jumbo_max_pending = 0;
+	ring->rx_pending = rfds->count;
+	ring->tx_pending = cbs->count;
+	ring->rx_mini_pending = 0;
+	ring->rx_jumbo_pending = 0;
+}
+
+static int e100_set_ringparam(struct net_device *netdev,
+	struct ethtool_ringparam *ring)
+{
+	struct nic *nic = netdev_priv(netdev);
+	struct param_range *rfds = &nic->params.rfds;
+	struct param_range *cbs = &nic->params.cbs;
+
+	if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) 
+		return -EINVAL;
+
+	if(netif_running(netdev))
+		e100_down(nic);
+	rfds->count = max(ring->rx_pending, rfds->min);
+	rfds->count = min(rfds->count, rfds->max);
+	cbs->count = max(ring->tx_pending, cbs->min);
+	cbs->count = min(cbs->count, cbs->max);
+	DPRINTK(DRV, INFO, "Ring Param settings: rx: %d, tx %d\n",
+	        rfds->count, cbs->count);
+	if(netif_running(netdev))
+		e100_up(nic);
+
+	return 0;
+}
+
+static const char e100_gstrings_test[][ETH_GSTRING_LEN] = {
+	"Link test     (on/offline)",
+	"Eeprom test   (on/offline)",
+	"Self test        (offline)",
+	"Mac loopback     (offline)",
+	"Phy loopback     (offline)",
+};
+#define E100_TEST_LEN	sizeof(e100_gstrings_test) / ETH_GSTRING_LEN
+
+static int e100_diag_test_count(struct net_device *netdev)
+{
+	return E100_TEST_LEN;
+}
+
+static void e100_diag_test(struct net_device *netdev,
+	struct ethtool_test *test, u64 *data)
+{
+	struct ethtool_cmd cmd;
+	struct nic *nic = netdev_priv(netdev);
+	int i, err;
+
+	memset(data, 0, E100_TEST_LEN * sizeof(u64));
+	data[0] = !mii_link_ok(&nic->mii);
+	data[1] = e100_eeprom_load(nic);
+	if(test->flags & ETH_TEST_FL_OFFLINE) {
+
+		/* save speed, duplex & autoneg settings */
+		err = mii_ethtool_gset(&nic->mii, &cmd);
+
+		if(netif_running(netdev))
+			e100_down(nic);
+		data[2] = e100_self_test(nic);
+		data[3] = e100_loopback_test(nic, lb_mac);
+		data[4] = e100_loopback_test(nic, lb_phy);
+
+		/* restore speed, duplex & autoneg settings */
+		err = mii_ethtool_sset(&nic->mii, &cmd);
+
+		if(netif_running(netdev))
+			e100_up(nic);
+	}
+	for(i = 0; i < E100_TEST_LEN; i++)
+		test->flags |= data[i] ? ETH_TEST_FL_FAILED : 0;
+
+	msleep_interruptible(4 * 1000);
+}
+
+static int e100_phys_id(struct net_device *netdev, u32 data)
+{
+	struct nic *nic = netdev_priv(netdev);
+
+	if(!data || data > (u32)(MAX_SCHEDULE_TIMEOUT / HZ))
+		data = (u32)(MAX_SCHEDULE_TIMEOUT / HZ);
+	mod_timer(&nic->blink_timer, jiffies);
+	msleep_interruptible(data * 1000);
+	del_timer_sync(&nic->blink_timer);
+	mdio_write(netdev, nic->mii.phy_id, MII_LED_CONTROL, 0);
+
+	return 0;
+}
+
+static const char e100_gstrings_stats[][ETH_GSTRING_LEN] = {
+	"rx_packets", "tx_packets", "rx_bytes", "tx_bytes", "rx_errors",
+	"tx_errors", "rx_dropped", "tx_dropped", "multicast", "collisions",
+	"rx_length_errors", "rx_over_errors", "rx_crc_errors",
+	"rx_frame_errors", "rx_fifo_errors", "rx_missed_errors",
+	"tx_aborted_errors", "tx_carrier_errors", "tx_fifo_errors",
+	"tx_heartbeat_errors", "tx_window_errors",
+	/* device-specific stats */
+	"tx_deferred", "tx_single_collisions", "tx_multi_collisions",
+	"tx_flow_control_pause", "rx_flow_control_pause",
+	"rx_flow_control_unsupported", "tx_tco_packets", "rx_tco_packets",
+};
+#define E100_NET_STATS_LEN	21
+#define E100_STATS_LEN	sizeof(e100_gstrings_stats) / ETH_GSTRING_LEN
+
+static int e100_get_stats_count(struct net_device *netdev)
+{
+	return E100_STATS_LEN;
+}
+
+static void e100_get_ethtool_stats(struct net_device *netdev,
+	struct ethtool_stats *stats, u64 *data)
+{
+	struct nic *nic = netdev_priv(netdev);
+	int i;
+
+	for(i = 0; i < E100_NET_STATS_LEN; i++)
+		data[i] = ((unsigned long *)&nic->net_stats)[i];
+
+	data[i++] = nic->tx_deferred;
+	data[i++] = nic->tx_single_collisions;
+	data[i++] = nic->tx_multiple_collisions;
+	data[i++] = nic->tx_fc_pause;
+	data[i++] = nic->rx_fc_pause;
+	data[i++] = nic->rx_fc_unsupported;
+	data[i++] = nic->tx_tco_frames;
+	data[i++] = nic->rx_tco_frames;
+}
+
+static void e100_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
+{
+	switch(stringset) {
+	case ETH_SS_TEST:
+		memcpy(data, *e100_gstrings_test, sizeof(e100_gstrings_test));
+		break;
+	case ETH_SS_STATS:
+		memcpy(data, *e100_gstrings_stats, sizeof(e100_gstrings_stats));
+		break;
+	}
+}
+
+static struct ethtool_ops e100_ethtool_ops = {
+	.get_settings		= e100_get_settings,
+	.set_settings		= e100_set_settings,
+	.get_drvinfo		= e100_get_drvinfo,
+	.get_regs_len		= e100_get_regs_len,
+	.get_regs		= e100_get_regs,
+	.get_wol		= e100_get_wol,
+	.set_wol		= e100_set_wol,
+	.get_msglevel		= e100_get_msglevel,
+	.set_msglevel		= e100_set_msglevel,
+	.nway_reset		= e100_nway_reset,
+	.get_link		= e100_get_link,
+	.get_eeprom_len		= e100_get_eeprom_len,
+	.get_eeprom		= e100_get_eeprom,
+	.set_eeprom		= e100_set_eeprom,
+	.get_ringparam		= e100_get_ringparam,
+	.set_ringparam		= e100_set_ringparam,
+	.self_test_count	= e100_diag_test_count,
+	.self_test		= e100_diag_test,
+	.get_strings		= e100_get_strings,
+	.phys_id		= e100_phys_id,
+	.get_stats_count	= e100_get_stats_count,
+	.get_ethtool_stats	= e100_get_ethtool_stats,
+	.get_perm_addr		= ethtool_op_get_perm_addr,
+};
+
+static int e100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+	struct nic *nic = netdev_priv(netdev);
+
+	return generic_mii_ioctl(&nic->mii, if_mii(ifr), cmd, NULL);
+}
+
+static int e100_alloc(struct nic *nic)
+{
+	nic->mem = pci_alloc_consistent(nic->pdev, sizeof(struct mem),
+		&nic->dma_addr);
+	return nic->mem ? 0 : -ENOMEM;
+}
+
+static void e100_free(struct nic *nic)
+{
+	if(nic->mem) {
+		pci_free_consistent(nic->pdev, sizeof(struct mem),
+			nic->mem, nic->dma_addr);
+		nic->mem = NULL;
+	}
+}
+
+static int e100_open(struct net_device *netdev)
+{
+	struct nic *nic = netdev_priv(netdev);
+	int err = 0;
+
+	netif_carrier_off(netdev);
+	if((err = e100_up(nic)))
+		DPRINTK(IFUP, ERR, "Cannot open interface, aborting.\n");
+	return err;
+}
+
+static int e100_close(struct net_device *netdev)
+{
+	e100_down(netdev_priv(netdev));
+	return 0;
+}
+
+static int __devinit e100_probe(struct pci_dev *pdev,
+	const struct pci_device_id *ent)
+{
+	struct net_device *netdev;
+	struct nic *nic;
+	int err;
+
+	if(!(netdev = alloc_etherdev(sizeof(struct nic)))) {
+		if(((1 << debug) - 1) & NETIF_MSG_PROBE)
+			printk(KERN_ERR PFX "Etherdev alloc failed, abort.\n");
+		return -ENOMEM;
+	}
+
+	netdev->open = e100_open;
+	netdev->stop = e100_close;
+	netdev->hard_start_xmit = e100_xmit_frame;
+	netdev->get_stats = e100_get_stats;
+	netdev->set_multicast_list = e100_set_multicast_list;
+	netdev->set_mac_address = e100_set_mac_address;
+	netdev->change_mtu = e100_change_mtu;
+	netdev->do_ioctl = e100_do_ioctl;
+	SET_ETHTOOL_OPS(netdev, &e100_ethtool_ops);
+	netdev->tx_timeout = e100_tx_timeout;
+	netdev->watchdog_timeo = E100_WATCHDOG_PERIOD;
+	netdev->poll = e100_poll;
+	netdev->weight = E100_NAPI_WEIGHT;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	netdev->poll_controller = e100_netpoll;
+#endif
+	strcpy(netdev->name, pci_name(pdev));
+
+	nic = netdev_priv(netdev);
+	nic->netdev = netdev;
+	nic->pdev = pdev;
+	nic->msg_enable = (1 << debug) - 1;
+	pci_set_drvdata(pdev, netdev);
+
+	if((err = pci_enable_device(pdev))) {
+		DPRINTK(PROBE, ERR, "Cannot enable PCI device, aborting.\n");
+		goto err_out_free_dev;
+	}
+
+	if(!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
+		DPRINTK(PROBE, ERR, "Cannot find proper PCI device "
+			"base address, aborting.\n");
+		err = -ENODEV;
+		goto err_out_disable_pdev;
+	}
+
+	if((err = pci_request_regions(pdev, DRV_NAME))) {
+		DPRINTK(PROBE, ERR, "Cannot obtain PCI resources, aborting.\n");
+		goto err_out_disable_pdev;
+	}
+
+	if((err = pci_set_dma_mask(pdev, DMA_32BIT_MASK))) {
+		DPRINTK(PROBE, ERR, "No usable DMA configuration, aborting.\n");
+		goto err_out_free_res;
+	}
+
+	SET_MODULE_OWNER(netdev);
+	SET_NETDEV_DEV(netdev, &pdev->dev);
+
+	nic->csr = ioremap(pci_resource_start(pdev, 0), sizeof(struct csr));
+	if(!nic->csr) {
+		DPRINTK(PROBE, ERR, "Cannot map device registers, aborting.\n");
+		err = -ENOMEM;
+		goto err_out_free_res;
+	}
+
+	if(ent->driver_data)
+		nic->flags |= ich;
+	else
+		nic->flags &= ~ich;
+
+	e100_get_defaults(nic);
+
+	/* locks must be initialized before calling hw_reset */
+	spin_lock_init(&nic->cb_lock);
+	spin_lock_init(&nic->cmd_lock);
+
+	/* Reset the device before pci_set_master() in case device is in some
+	 * funky state and has an interrupt pending - hint: we don't have the
+	 * interrupt handler registered yet. */
+	e100_hw_reset(nic);
+
+	pci_set_master(pdev);
+
+	init_timer(&nic->watchdog);
+	nic->watchdog.function = e100_watchdog;
+	nic->watchdog.data = (unsigned long)nic;
+	init_timer(&nic->blink_timer);
+	nic->blink_timer.function = e100_blink_led;
+	nic->blink_timer.data = (unsigned long)nic;
+
+	INIT_WORK(&nic->tx_timeout_task,
+		(void (*)(void *))e100_tx_timeout_task, netdev);
+
+	if((err = e100_alloc(nic))) {
+		DPRINTK(PROBE, ERR, "Cannot alloc driver memory, aborting.\n");
+		goto err_out_iounmap;
+	}
+
+	if((err = e100_eeprom_load(nic)))
+		goto err_out_free;
+
+	e100_phy_init(nic);
+
+	memcpy(netdev->dev_addr, nic->eeprom, ETH_ALEN);
+	memcpy(netdev->perm_addr, nic->eeprom, ETH_ALEN);
+	if(!is_valid_ether_addr(netdev->perm_addr)) {
+		DPRINTK(PROBE, ERR, "Invalid MAC address from "
+			"EEPROM, aborting.\n");
+		err = -EAGAIN;
+		goto err_out_free;
+	}
+
+	/* Wol magic packet can be enabled from eeprom */
+	if((nic->mac >= mac_82558_D101_A4) &&
+	   (nic->eeprom[eeprom_id] & eeprom_id_wol))
+		nic->flags |= wol_magic;
+
+	/* ack any pending wake events, disable PME */
+	pci_enable_wake(pdev, 0, 0);
+
+	strcpy(netdev->name, "eth%d");
+	if((err = register_netdev(netdev))) {
+		DPRINTK(PROBE, ERR, "Cannot register net device, aborting.\n");
+		goto err_out_free;
+	}
+
+	DPRINTK(PROBE, INFO, "addr 0x%lx, irq %d, "
+		"MAC addr %02X:%02X:%02X:%02X:%02X:%02X\n",
+		pci_resource_start(pdev, 0), pdev->irq,
+		netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2],
+		netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]);
+
+	return 0;
+
+err_out_free:
+	e100_free(nic);
+err_out_iounmap:
+	iounmap(nic->csr);
+err_out_free_res:
+	pci_release_regions(pdev);
+err_out_disable_pdev:
+	pci_disable_device(pdev);
+err_out_free_dev:
+	pci_set_drvdata(pdev, NULL);
+	free_netdev(netdev);
+	return err;
+}
+
+static void __devexit e100_remove(struct pci_dev *pdev)
+{
+	struct net_device *netdev = pci_get_drvdata(pdev);
+
+	if(netdev) {
+		struct nic *nic = netdev_priv(netdev);
+		unregister_netdev(netdev);
+		e100_free(nic);
+		iounmap(nic->csr);
+		free_netdev(netdev);
+		pci_release_regions(pdev);
+		pci_disable_device(pdev);
+		pci_set_drvdata(pdev, NULL);
+	}
+}
+
+#ifdef CONFIG_PM
+static int e100_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct net_device *netdev = pci_get_drvdata(pdev);
+	struct nic *nic = netdev_priv(netdev);
+
+	if(netif_running(netdev))
+		e100_down(nic);
+	e100_hw_reset(nic);
+	netif_device_detach(netdev);
+
+	pci_save_state(pdev);
+	pci_enable_wake(pdev, pci_choose_state(pdev, state), nic->flags & (wol_magic | e100_asf(nic)));
+	pci_disable_device(pdev);
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+	return 0;
+}
+
+static int e100_resume(struct pci_dev *pdev)
+{
+	struct net_device *netdev = pci_get_drvdata(pdev);
+	struct nic *nic = netdev_priv(netdev);
+
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+	/* ack any pending wake events, disable PME */
+	pci_enable_wake(pdev, 0, 0);
+	if(e100_hw_init(nic))
+		DPRINTK(HW, ERR, "e100_hw_init failed\n");
+
+	netif_device_attach(netdev);
+	if(netif_running(netdev))
+		e100_up(nic);
+
+	return 0;
+}
+#endif
+
+
+static void e100_shutdown(struct pci_dev *pdev)
+{
+	struct net_device *netdev = pci_get_drvdata(pdev);
+	struct nic *nic = netdev_priv(netdev);
+
+#ifdef CONFIG_PM
+	pci_enable_wake(pdev, 0, nic->flags & (wol_magic | e100_asf(nic)));
+#else
+	pci_enable_wake(pdev, 0, nic->flags & (wol_magic));
+#endif
+}
+
+
+static struct pci_driver e100_driver = {
+	.name =         DRV_NAME,
+	.id_table =     e100_id_table,
+	.probe =        e100_probe,
+	.remove =       __devexit_p(e100_remove),
+#ifdef CONFIG_PM
+	.suspend =      e100_suspend,
+	.resume =       e100_resume,
+#endif
+	.shutdown =	e100_shutdown,
+};
+
+static int __init e100_init_module(void)
+{
+	if(((1 << debug) - 1) & NETIF_MSG_DRV) {
+		printk(KERN_INFO PFX "%s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
+		printk(KERN_INFO PFX "%s\n", DRV_COPYRIGHT);
+	}
+	return pci_module_init(&e100_driver);
+}
+
+static void __exit e100_cleanup_module(void)
+{
+	pci_unregister_driver(&e100_driver);
+}
+
+module_init(e100_init_module);
+module_exit(e100_cleanup_module);
diff -urN oldtree/drivers/net/hamradio/mkiss.c newtree/drivers/net/hamradio/mkiss.c
--- oldtree/drivers/net/hamradio/mkiss.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/hamradio/mkiss.c	2006-02-13 19:20:29.717994480 -0500
@@ -515,6 +515,7 @@
 			count = kiss_esc(p, (unsigned char *)ax->xbuff, len);
 		}
   	}
+	spin_unlock_bh(&ax->buflock);
 
 	set_bit(TTY_DO_WRITE_WAKEUP, &ax->tty->flags);
 	actual = ax->tty->driver->write(ax->tty, ax->xbuff, count);
diff -urN oldtree/drivers/net/mii.c newtree/drivers/net/mii.c
--- oldtree/drivers/net/mii.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/mii.c	2006-02-13 19:19:59.993513288 -0500
@@ -267,8 +267,10 @@
 	int lpa2 = 0;
 
 	/* if forced media, go no further */
-	if (mii->force_media)
+	if (mii->force_media) {
+		netif_carrier_on(mii->dev);
 		return 0; /* duplex did not change */
+	}
 
 	/* check current and old link status */
 	old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
diff -urN oldtree/drivers/net/ppp_generic.c newtree/drivers/net/ppp_generic.c
--- oldtree/drivers/net/ppp_generic.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/ppp_generic.c	2006-02-13 19:20:29.718994328 -0500
@@ -1610,6 +1610,8 @@
 		}
 		else if (!pskb_may_pull(skb, skb->len))
 			goto err;
+		else
+			skb->ip_summed = CHECKSUM_NONE;
 
 		len = slhc_uncompress(ppp->vj, skb->data + 2, skb->len - 2);
 		if (len <= 0) {
@@ -1690,6 +1692,7 @@
 			kfree_skb(skb);
 		} else {
 			skb_pull(skb, 2);	/* chop off protocol */
+			skb_postpull_rcsum(skb, skb->data - 2, 2);
 			skb->dev = ppp->dev;
 			skb->protocol = htons(npindex_to_ethertype[npi]);
 			skb->mac.raw = skb->data;
diff -urN oldtree/drivers/net/sk98lin/h/skaddr.h newtree/drivers/net/sk98lin/h/skaddr.h
--- oldtree/drivers/net/sk98lin/h/skaddr.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/sk98lin/h/skaddr.h	2006-02-13 19:19:59.994513136 -0500
@@ -236,18 +236,6 @@
 	SK_U32	PortNumber,
 	int	Flags);
 
-extern	int	SkAddrXmacMcClear(
-	SK_AC	*pAC,
-	SK_IOC	IoC,
-	SK_U32	PortNumber,
-	int	Flags);
-
-extern	int	SkAddrGmacMcClear(
-	SK_AC	*pAC,
-	SK_IOC	IoC,
-	SK_U32	PortNumber,
-	int	Flags);
-
 extern	int	SkAddrMcAdd(
 	SK_AC		*pAC,
 	SK_IOC		IoC,
@@ -255,35 +243,11 @@
 	SK_MAC_ADDR	*pMc,
 	int		Flags);
 
-extern	int	SkAddrXmacMcAdd(
-	SK_AC		*pAC,
-	SK_IOC		IoC,
-	SK_U32		PortNumber,
-	SK_MAC_ADDR	*pMc,
-	int		Flags);
-
-extern	int	SkAddrGmacMcAdd(
-	SK_AC		*pAC,
-	SK_IOC		IoC,
-	SK_U32		PortNumber,
-	SK_MAC_ADDR	*pMc,
-	int		Flags);
-
 extern	int	SkAddrMcUpdate(
 	SK_AC	*pAC,
 	SK_IOC	IoC,
 	SK_U32	PortNumber);
 
-extern	int	SkAddrXmacMcUpdate(
-	SK_AC	*pAC,
-	SK_IOC	IoC,
-	SK_U32	PortNumber);
-
-extern	int	SkAddrGmacMcUpdate(
-	SK_AC	*pAC,
-	SK_IOC	IoC,
-	SK_U32	PortNumber);
-
 extern	int	SkAddrOverride(
 	SK_AC		*pAC,
 	SK_IOC		IoC,
@@ -297,18 +261,6 @@
 	SK_U32	PortNumber,
 	int	NewPromMode);
 
-extern	int	SkAddrXmacPromiscuousChange(
-	SK_AC	*pAC,
-	SK_IOC	IoC,
-	SK_U32	PortNumber,
-	int	NewPromMode);
-
-extern	int	SkAddrGmacPromiscuousChange(
-	SK_AC	*pAC,
-	SK_IOC	IoC,
-	SK_U32	PortNumber,
-	int	NewPromMode);	
-
 #ifndef SK_SLIM
 extern	int	SkAddrSwap(
 	SK_AC	*pAC,
diff -urN oldtree/drivers/net/sk98lin/h/skcsum.h newtree/drivers/net/sk98lin/h/skcsum.h
--- oldtree/drivers/net/sk98lin/h/skcsum.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/sk98lin/h/skcsum.h	2006-02-13 19:19:59.994513136 -0500
@@ -203,12 +203,6 @@
 	unsigned	Checksum2,
 	int			NetNumber);
 
-extern void SkCsGetSendInfo(
-	SK_AC				*pAc,
-	void				*pIpHeader,
-	SKCS_PACKET_INFO	*pPacketInfo,
-	int					NetNumber);
-
 extern void SkCsSetReceiveFlags(
 	SK_AC		*pAc,
 	unsigned	ReceiveFlags,
diff -urN oldtree/drivers/net/sk98lin/h/skgeinit.h newtree/drivers/net/sk98lin/h/skgeinit.h
--- oldtree/drivers/net/sk98lin/h/skgeinit.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/sk98lin/h/skgeinit.h	2006-02-13 19:19:59.997512680 -0500
@@ -464,12 +464,6 @@
 /*
  * public functions in skgeinit.c
  */
-extern void	SkGePollRxD(
-	SK_AC	*pAC,
-	SK_IOC	IoC,
-	int		Port,
-	SK_BOOL	PollRxD);
-
 extern void	SkGePollTxD(
 	SK_AC	*pAC,
 	SK_IOC	IoC,
@@ -522,10 +516,6 @@
 	int		Led,
 	int		Mode);
 
-extern void	SkGeInitRamIface(
-	SK_AC	*pAC,
-	SK_IOC	IoC);
-
 extern int	SkGeInitAssignRamToQueues(
 	SK_AC	*pAC,
 	int		ActivePort,
@@ -549,11 +539,6 @@
 	SK_IOC	IoC,
 	int		Port);
 
-extern void	SkMacClearRst(
-	SK_AC	*pAC,
-	SK_IOC	IoC,
-	int		Port);
-
 extern void	SkXmInitMac(
 	SK_AC	*pAC,
 	SK_IOC	IoC,
@@ -580,11 +565,6 @@
 	SK_IOC	IoC,
 	int		Port);
 
-extern void	SkMacFlushRxFifo(
-	SK_AC	*pAC,
-	SK_IOC	IoC,
-	int		Port);
-
 extern void	SkMacIrq(
 	SK_AC	*pAC,
 	SK_IOC	IoC,
@@ -601,12 +581,6 @@
 	int		Port,
 	SK_U16	IStatus);
 
-extern void  SkMacSetRxTxEn(
-	SK_AC	*pAC,
-	SK_IOC	IoC,
-	int		Port,
-	int		Para);
-
 extern int  SkMacRxTxEnable(
 	SK_AC	*pAC,
 	SK_IOC	IoC,
@@ -659,16 +633,6 @@
 	int		StartNum,
 	int		StopNum);
 
-extern void	SkXmInitDupMd(
-	SK_AC	*pAC,
-	SK_IOC	IoC,
-	int		Port);
-
-extern void	SkXmInitPauseMd(
-	SK_AC	*pAC,
-	SK_IOC	IoC,
-	int		Port);
-
 extern void	SkXmAutoNegLipaXmac(
 	SK_AC	*pAC,
 	SK_IOC	IoC,
@@ -729,17 +693,6 @@
 	int		Port,
 	SK_BOOL	StartTest);
 
-extern int SkGmEnterLowPowerMode(
-	SK_AC	*pAC,
-	SK_IOC	IoC,
-	int		Port,
-	SK_U8	Mode);
-
-extern int SkGmLeaveLowPowerMode(
-	SK_AC	*pAC,
-	SK_IOC	IoC,
-	int		Port);
-
 #ifdef SK_DIAG
 extern void	SkGePhyRead(
 	SK_AC	*pAC,
@@ -782,7 +735,6 @@
 /*
  * public functions in skgeinit.c
  */
-extern void	SkGePollRxD();
 extern void	SkGePollTxD();
 extern void	SkGeYellowLED();
 extern int	SkGeCfgSync();
@@ -792,7 +744,6 @@
 extern void	SkGeDeInit();
 extern int	SkGeInitPort();
 extern void	SkGeXmitLED();
-extern void	SkGeInitRamIface();
 extern int	SkGeInitAssignRamToQueues();
 
 /*
@@ -801,18 +752,15 @@
 extern void SkMacRxTxDisable();
 extern void	SkMacSoftRst();
 extern void	SkMacHardRst();
-extern void	SkMacClearRst();
 extern void SkMacInitPhy();
 extern int  SkMacRxTxEnable();
 extern void SkMacPromiscMode();
 extern void SkMacHashing();
 extern void SkMacIrqDisable();
 extern void	SkMacFlushTxFifo();
-extern void	SkMacFlushRxFifo();
 extern void	SkMacIrq();
 extern int	SkMacAutoNegDone();
 extern void	SkMacAutoNegLipaPhy();
-extern void SkMacSetRxTxEn();
 extern void	SkXmInitMac();
 extern void	SkXmPhyRead();
 extern void	SkXmPhyWrite();
@@ -820,8 +768,6 @@
 extern void	SkGmPhyRead();
 extern void	SkGmPhyWrite();
 extern void	SkXmClrExactAddr();
-extern void	SkXmInitDupMd();
-extern void	SkXmInitPauseMd();
 extern void	SkXmAutoNegLipaXmac();
 extern int	SkXmUpdateStats();
 extern int	SkGmUpdateStats();
@@ -832,8 +778,6 @@
 extern int	SkXmOverflowStatus();
 extern int	SkGmOverflowStatus();
 extern int	SkGmCableDiagStatus();
-extern int	SkGmEnterLowPowerMode();
-extern int	SkGmLeaveLowPowerMode();
 
 #ifdef SK_DIAG
 extern void	SkGePhyRead();
diff -urN oldtree/drivers/net/sk98lin/h/skgepnmi.h newtree/drivers/net/sk98lin/h/skgepnmi.h
--- oldtree/drivers/net/sk98lin/h/skgepnmi.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/sk98lin/h/skgepnmi.h	2006-02-13 19:19:59.997512680 -0500
@@ -946,10 +946,6 @@
  * Function prototypes
  */
 extern int SkPnmiInit(SK_AC *pAC, SK_IOC IoC, int Level);
-extern int SkPnmiGetVar(SK_AC *pAC, SK_IOC IoC, SK_U32 Id, void* pBuf,
-	unsigned int* pLen, SK_U32 Instance, SK_U32 NetIndex);
-extern int SkPnmiPreSetVar(SK_AC *pAC, SK_IOC IoC, SK_U32 Id,
-	void* pBuf, unsigned int *pLen, SK_U32 Instance, SK_U32 NetIndex);
 extern int SkPnmiSetVar(SK_AC *pAC, SK_IOC IoC, SK_U32 Id, void* pBuf,
 	unsigned int *pLen, SK_U32 Instance, SK_U32 NetIndex);
 extern int SkPnmiGetStruct(SK_AC *pAC, SK_IOC IoC, void* pBuf,
diff -urN oldtree/drivers/net/sk98lin/h/skgesirq.h newtree/drivers/net/sk98lin/h/skgesirq.h
--- oldtree/drivers/net/sk98lin/h/skgesirq.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/sk98lin/h/skgesirq.h	2006-02-13 19:19:59.998512528 -0500
@@ -105,7 +105,6 @@
 
 extern void SkGeSirqIsr(SK_AC *pAC, SK_IOC IoC, SK_U32 Istatus);
 extern int  SkGeSirqEvent(SK_AC *pAC, SK_IOC IoC, SK_U32 Event, SK_EVPARA Para);
-extern void SkHWLinkUp(SK_AC *pAC, SK_IOC IoC, int Port);
 extern void SkHWLinkDown(SK_AC *pAC, SK_IOC IoC, int Port);
 
 #endif	/* _INC_SKGESIRQ_H_ */
diff -urN oldtree/drivers/net/sk98lin/h/ski2c.h newtree/drivers/net/sk98lin/h/ski2c.h
--- oldtree/drivers/net/sk98lin/h/ski2c.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/sk98lin/h/ski2c.h	2006-02-13 19:19:59.998512528 -0500
@@ -162,9 +162,6 @@
 } SK_I2C;
 
 extern int SkI2cInit(SK_AC *pAC, SK_IOC IoC, int Level);
-extern int SkI2cWrite(SK_AC *pAC, SK_IOC IoC, SK_U32 Data, int Dev, int Size,
-					   int Reg, int Burst);
-extern int SkI2cReadSensor(SK_AC *pAC, SK_IOC IoC, SK_SENSOR *pSen);
 #ifdef SK_DIAG
 extern	SK_U32 SkI2cRead(SK_AC *pAC, SK_IOC IoC, int Dev, int Size, int Reg,
 						 int Burst);
diff -urN oldtree/drivers/net/sk98lin/h/skvpd.h newtree/drivers/net/sk98lin/h/skvpd.h
--- oldtree/drivers/net/sk98lin/h/skvpd.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/sk98lin/h/skvpd.h	2006-02-13 19:19:59.998512528 -0500
@@ -191,14 +191,6 @@
 	int			addr);
 #endif	/* SKDIAG */
 
-extern int	VpdSetupPara(
-	SK_AC		*pAC,
-	const char	*key,
-	const char	*buf,
-	int			len,
-	int			type,
-	int			op);
-
 extern SK_VPD_STATUS	*VpdStat(
 	SK_AC		*pAC,
 	SK_IOC		IoC);
@@ -235,11 +227,6 @@
 	SK_AC		*pAC,
 	SK_IOC		IoC);
 
-extern void	VpdErrLog(
-	SK_AC		*pAC,
-	SK_IOC		IoC,
-	char		*msg);
-
 #ifdef	SKDIAG
 extern int	VpdReadBlock(
 	SK_AC		*pAC,
@@ -257,7 +244,6 @@
 #endif	/* SKDIAG */
 #else	/* SK_KR_PROTO */
 extern SK_U32	VpdReadDWord();
-extern int	VpdSetupPara();
 extern SK_VPD_STATUS	*VpdStat();
 extern int	VpdKeys();
 extern int	VpdRead();
@@ -265,7 +251,6 @@
 extern int	VpdWrite();
 extern int	VpdDelete();
 extern int	VpdUpdate();
-extern void	VpdErrLog();
 #endif	/* SK_KR_PROTO */
 
 #endif	/* __INC_SKVPD_H_ */
diff -urN oldtree/drivers/net/sk98lin/h/skvpd.h.orig newtree/drivers/net/sk98lin/h/skvpd.h.orig
--- oldtree/drivers/net/sk98lin/h/skvpd.h.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/sk98lin/h/skvpd.h.orig	2006-02-13 19:19:59.999512376 -0500
@@ -0,0 +1,271 @@
+/******************************************************************************
+ *
+ * Name:	skvpd.h
+ * Project:	GEnesis, PCI Gigabit Ethernet Adapter
+ * Version:	$Revision: 1.15 $
+ * Date:	$Date: 2003/01/13 10:39:38 $
+ * Purpose:	Defines and Macros for VPD handling
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ *	(C)Copyright 1998-2003 SysKonnect GmbH.
+ *
+ *	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
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+ * skvpd.h	contains Diagnostic specific defines for VPD handling
+ */
+
+#ifndef __INC_SKVPD_H_
+#define __INC_SKVPD_H_
+
+/*
+ * Define Resource Type Identifiers and VPD keywords
+ */
+#define	RES_ID		0x82	/* Resource Type ID String (Product Name) */
+#define RES_VPD_R	0x90	/* start of VPD read only area */
+#define RES_VPD_W	0x91	/* start of VPD read/write area */
+#define RES_END		0x78	/* Resource Type End Tag */
+
+#ifndef VPD_NAME
+#define VPD_NAME	"Name"	/* Product Name, VPD name of RES_ID */
+#endif	/* VPD_NAME */
+#define VPD_PN		"PN"	/* Adapter Part Number */
+#define	VPD_EC		"EC"	/* Adapter Engineering Level */
+#define VPD_MN		"MN"	/* Manufacture ID */
+#define VPD_SN		"SN"	/* Serial Number */
+#define VPD_CP		"CP"	/* Extended Capability */
+#define VPD_RV		"RV"	/* Checksum and Reserved */
+#define	VPD_YA		"YA"	/* Asset Tag Identifier */
+#define VPD_VL		"VL"	/* First Error Log Message (SK specific) */
+#define VPD_VF		"VF"	/* Second Error Log Message (SK specific) */
+#define VPD_RW		"RW"	/* Remaining Read / Write Area */
+
+/* 'type' values for vpd_setup_para() */
+#define VPD_RO_KEY	1	/* RO keys are "PN", "EC", "MN", "SN", "RV" */
+#define VPD_RW_KEY	2	/* RW keys are "Yx", "Vx", and "RW" */
+
+/* 'op' values for vpd_setup_para() */
+#define	ADD_KEY		1	/* add the key at the pos "RV" or "RW" */
+#define OWR_KEY		2	/* overwrite key if already exists */
+
+/*
+ * Define READ and WRITE Constants.
+ */
+
+#define VPD_DEV_ID_GENESIS 	0x4300
+
+#define	VPD_SIZE_YUKON		256
+#define	VPD_SIZE_GENESIS	512
+#define	VPD_SIZE			512
+#define VPD_READ	0x0000
+#define VPD_WRITE	0x8000
+
+#define VPD_STOP(pAC,IoC)	VPD_OUT16(pAC,IoC,PCI_VPD_ADR_REG,VPD_WRITE)
+
+#define VPD_GET_RES_LEN(p)	((unsigned int) \
+					(* (SK_U8 *)&(p)[1]) |\
+					((* (SK_U8 *)&(p)[2]) << 8))
+#define VPD_GET_VPD_LEN(p)	((unsigned int)(* (SK_U8 *)&(p)[2]))
+#define VPD_GET_VAL(p)		((char *)&(p)[3])
+
+#define VPD_MAX_LEN	50
+
+/* VPD status */
+	/* bit 7..1 reserved */
+#define VPD_VALID	(1<<0)	/* VPD data buffer, vpd_free_ro, */
+							/* and vpd_free_rw valid	 */
+
+/*
+ * VPD structs
+ */
+typedef	struct s_vpd_status {
+	unsigned short	Align01;			/* Alignment */
+	unsigned short	vpd_status;			/* VPD status, description see above */
+	int				vpd_free_ro;		/* unused bytes in read only area */
+	int				vpd_free_rw;		/* bytes available in read/write area */
+} SK_VPD_STATUS;
+
+typedef	struct s_vpd {
+	SK_VPD_STATUS	v;					/* VPD status structure */
+	char			vpd_buf[VPD_SIZE];	/* VPD buffer */
+	int				rom_size;			/* VPD ROM Size from PCI_OUR_REG_2 */
+	int				vpd_size;			/* saved VPD-size */
+} SK_VPD;
+
+typedef	struct s_vpd_para {
+	unsigned int	p_len;	/* parameter length */
+	char			*p_val;	/* points to the value */
+} SK_VPD_PARA;
+
+/*
+ * structure of Large Resource Type Identifiers
+ */
+
+/* was removed because of alignment problems */
+
+/*
+ * structure of VPD keywords
+ */
+typedef	struct s_vpd_key {
+	char			p_key[2];	/* 2 bytes ID string */
+	unsigned char	p_len;		/* 1 byte length */
+	char			p_val;		/* start of the value string */
+} SK_VPD_KEY;
+
+
+/*
+ * System specific VPD macros
+ */
+#ifndef SKDIAG
+#ifndef VPD_DO_IO
+#define VPD_OUT8(pAC,IoC,Addr,Val)	(void)SkPciWriteCfgByte(pAC,Addr,Val)
+#define VPD_OUT16(pAC,IoC,Addr,Val)	(void)SkPciWriteCfgWord(pAC,Addr,Val)
+#define VPD_OUT32(pAC,IoC,Addr,Val)	(void)SkPciWriteCfgDWord(pAC,Addr,Val)
+#define VPD_IN8(pAC,IoC,Addr,pVal)	(void)SkPciReadCfgByte(pAC,Addr,pVal)
+#define VPD_IN16(pAC,IoC,Addr,pVal)	(void)SkPciReadCfgWord(pAC,Addr,pVal)
+#define VPD_IN32(pAC,IoC,Addr,pVal)	(void)SkPciReadCfgDWord(pAC,Addr,pVal)
+#else	/* VPD_DO_IO */
+#define VPD_OUT8(pAC,IoC,Addr,Val)	SK_OUT8(IoC,PCI_C(Addr),Val)
+#define VPD_OUT16(pAC,IoC,Addr,Val)	SK_OUT16(IoC,PCI_C(Addr),Val)
+#define VPD_OUT32(pAC,IoC,Addr,Val)	SK_OUT32(IoC,PCI_C(Addr),Val)
+#define VPD_IN8(pAC,IoC,Addr,pVal)	SK_IN8(IoC,PCI_C(Addr),pVal)
+#define VPD_IN16(pAC,IoC,Addr,pVal)	SK_IN16(IoC,PCI_C(Addr),pVal)
+#define VPD_IN32(pAC,IoC,Addr,pVal)	SK_IN32(IoC,PCI_C(Addr),pVal)
+#endif	/* VPD_DO_IO */
+#else	/* SKDIAG */
+#define VPD_OUT8(pAC,Ioc,Addr,Val) {			\
+		if ((pAC)->DgT.DgUseCfgCycle)			\
+			SkPciWriteCfgByte(pAC,Addr,Val);	\
+		else									\
+			SK_OUT8(pAC,PCI_C(Addr),Val);		\
+		}
+#define VPD_OUT16(pAC,Ioc,Addr,Val) {			\
+		if ((pAC)->DgT.DgUseCfgCycle)			\
+			SkPciWriteCfgWord(pAC,Addr,Val);	\
+		else						\
+			SK_OUT16(pAC,PCI_C(Addr),Val);		\
+		}
+#define VPD_OUT32(pAC,Ioc,Addr,Val) {			\
+		if ((pAC)->DgT.DgUseCfgCycle)			\
+			SkPciWriteCfgDWord(pAC,Addr,Val);	\
+		else						\
+			SK_OUT32(pAC,PCI_C(Addr),Val); 		\
+		}
+#define VPD_IN8(pAC,Ioc,Addr,pVal) {			\
+		if ((pAC)->DgT.DgUseCfgCycle) 			\
+			SkPciReadCfgByte(pAC,Addr,pVal);	\
+		else						\
+			SK_IN8(pAC,PCI_C(Addr),pVal); 		\
+		}
+#define VPD_IN16(pAC,Ioc,Addr,pVal) {			\
+		if ((pAC)->DgT.DgUseCfgCycle) 			\
+			SkPciReadCfgWord(pAC,Addr,pVal);	\
+		else						\
+			SK_IN16(pAC,PCI_C(Addr),pVal); 		\
+		}
+#define VPD_IN32(pAC,Ioc,Addr,pVal) {			\
+		if ((pAC)->DgT.DgUseCfgCycle)			\
+			SkPciReadCfgDWord(pAC,Addr,pVal);	\
+		else						\
+			SK_IN32(pAC,PCI_C(Addr),pVal);		\
+		}
+#endif	/* nSKDIAG */
+
+/* function prototypes ********************************************************/
+
+#ifndef	SK_KR_PROTO
+#ifdef SKDIAG
+extern SK_U32	VpdReadDWord(
+	SK_AC		*pAC,
+	SK_IOC		IoC,
+	int			addr);
+#endif	/* SKDIAG */
+
+extern int	VpdSetupPara(
+	SK_AC		*pAC,
+	const char	*key,
+	const char	*buf,
+	int			len,
+	int			type,
+	int			op);
+
+extern SK_VPD_STATUS	*VpdStat(
+	SK_AC		*pAC,
+	SK_IOC		IoC);
+
+extern int	VpdKeys(
+	SK_AC		*pAC,
+	SK_IOC		IoC,
+	char		*buf,
+	int			*len,
+	int			*elements);
+
+extern int	VpdRead(
+	SK_AC		*pAC,
+	SK_IOC		IoC,
+	const char	*key,
+	char		*buf,
+	int			*len);
+
+extern SK_BOOL	VpdMayWrite(
+	char		*key);
+
+extern int	VpdWrite(
+	SK_AC		*pAC,
+	SK_IOC		IoC,
+	const char	*key,
+	const char	*buf);
+
+extern int	VpdDelete(
+	SK_AC		*pAC,
+	SK_IOC		IoC,
+	char		*key);
+
+extern int	VpdUpdate(
+	SK_AC		*pAC,
+	SK_IOC		IoC);
+
+extern void	VpdErrLog(
+	SK_AC		*pAC,
+	SK_IOC		IoC,
+	char		*msg);
+
+#ifdef	SKDIAG
+extern int	VpdReadBlock(
+	SK_AC		*pAC,
+	SK_IOC		IoC,
+	char		*buf,
+	int			addr,
+	int			len);
+
+extern int	VpdWriteBlock(
+	SK_AC		*pAC,
+	SK_IOC		IoC,
+	char		*buf,
+	int			addr,
+	int			len);
+#endif	/* SKDIAG */
+#else	/* SK_KR_PROTO */
+extern SK_U32	VpdReadDWord();
+extern int	VpdSetupPara();
+extern SK_VPD_STATUS	*VpdStat();
+extern int	VpdKeys();
+extern int	VpdRead();
+extern SK_BOOL	VpdMayWrite();
+extern int	VpdWrite();
+extern int	VpdDelete();
+extern int	VpdUpdate();
+extern void	VpdErrLog();
+#endif	/* SK_KR_PROTO */
+
+#endif	/* __INC_SKVPD_H_ */
diff -urN oldtree/drivers/net/sk98lin/skaddr.c newtree/drivers/net/sk98lin/skaddr.c
--- oldtree/drivers/net/sk98lin/skaddr.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/sk98lin/skaddr.c	2006-02-13 19:20:00.000512224 -0500
@@ -87,6 +87,21 @@
 static int	Next0[SK_MAX_MACS] = {0};
 #endif	/* DEBUG */
 
+static int SkAddrGmacMcAdd(SK_AC *pAC, SK_IOC IoC, SK_U32 PortNumber,
+			   SK_MAC_ADDR *pMc, int Flags);
+static int SkAddrGmacMcClear(SK_AC *pAC, SK_IOC IoC, SK_U32 PortNumber,
+			     int Flags);
+static int SkAddrGmacMcUpdate(SK_AC *pAC, SK_IOC IoC, SK_U32 PortNumber);
+static int SkAddrGmacPromiscuousChange(SK_AC *pAC, SK_IOC IoC,
+				       SK_U32 PortNumber, int NewPromMode);
+static int SkAddrXmacMcAdd(SK_AC *pAC, SK_IOC IoC, SK_U32 PortNumber,
+			   SK_MAC_ADDR *pMc, int Flags);
+static int SkAddrXmacMcClear(SK_AC *pAC, SK_IOC IoC, SK_U32 PortNumber,
+			     int Flags);
+static int SkAddrXmacMcUpdate(SK_AC *pAC, SK_IOC IoC, SK_U32 PortNumber);
+static int SkAddrXmacPromiscuousChange(SK_AC *pAC, SK_IOC IoC,
+				       SK_U32 PortNumber, int NewPromMode);
+
 /* functions ******************************************************************/
 
 /******************************************************************************
@@ -372,7 +387,7 @@
  *	SK_ADDR_SUCCESS
  *	SK_ADDR_ILLEGAL_PORT
  */
-int	SkAddrXmacMcClear(
+static int	SkAddrXmacMcClear(
 SK_AC	*pAC,		/* adapter context */
 SK_IOC	IoC,		/* I/O context */
 SK_U32	PortNumber,	/* Index of affected port */
@@ -429,7 +444,7 @@
  *	SK_ADDR_SUCCESS
  *	SK_ADDR_ILLEGAL_PORT
  */
-int	SkAddrGmacMcClear(
+static int	SkAddrGmacMcClear(
 SK_AC	*pAC,		/* adapter context */
 SK_IOC	IoC,		/* I/O context */
 SK_U32	PortNumber,	/* Index of affected port */
@@ -519,7 +534,7 @@
  * Returns:
  *	Hash value of multicast address.
  */
-SK_U32 SkXmacMcHash(
+static SK_U32 SkXmacMcHash(
 unsigned char *pMc)	/* Multicast address */
 {
 	SK_U32 Idx;
@@ -557,7 +572,7 @@
  * Returns:
  *	Hash value of multicast address.
  */
-SK_U32 SkGmacMcHash(
+static SK_U32 SkGmacMcHash(
 unsigned char *pMc)	/* Multicast address */
 {
 	SK_U32 Data;
@@ -672,7 +687,7 @@
  *	SK_MC_ILLEGAL_ADDRESS
  *	SK_MC_RLMT_OVERFLOW
  */
-int	SkAddrXmacMcAdd(
+static int	SkAddrXmacMcAdd(
 SK_AC		*pAC,		/* adapter context */
 SK_IOC		IoC,		/* I/O context */
 SK_U32		PortNumber,	/* Port Number */
@@ -778,7 +793,7 @@
  *	SK_MC_FILTERING_INEXACT
  *	SK_MC_ILLEGAL_ADDRESS
  */
-int	SkAddrGmacMcAdd(
+static int	SkAddrGmacMcAdd(
 SK_AC		*pAC,		/* adapter context */
 SK_IOC		IoC,		/* I/O context */
 SK_U32		PortNumber,	/* Port Number */
@@ -937,7 +952,7 @@
  *	SK_MC_FILTERING_INEXACT
  *	SK_ADDR_ILLEGAL_PORT
  */
-int	SkAddrXmacMcUpdate(
+static int	SkAddrXmacMcUpdate(
 SK_AC	*pAC,		/* adapter context */
 SK_IOC	IoC,		/* I/O context */
 SK_U32	PortNumber)	/* Port Number */
@@ -1082,7 +1097,7 @@
  *	SK_MC_FILTERING_INEXACT
  *	SK_ADDR_ILLEGAL_PORT
  */
-int	SkAddrGmacMcUpdate(
+static int	SkAddrGmacMcUpdate(
 SK_AC	*pAC,		/* adapter context */
 SK_IOC	IoC,		/* I/O context */
 SK_U32	PortNumber)	/* Port Number */
@@ -1468,7 +1483,7 @@
  *	SK_ADDR_SUCCESS
  *	SK_ADDR_ILLEGAL_PORT
  */
-int	SkAddrXmacPromiscuousChange(
+static int	SkAddrXmacPromiscuousChange(
 SK_AC	*pAC,			/* adapter context */
 SK_IOC	IoC,			/* I/O context */
 SK_U32	PortNumber,		/* port whose promiscuous mode changes */
@@ -1585,7 +1600,7 @@
  *	SK_ADDR_SUCCESS
  *	SK_ADDR_ILLEGAL_PORT
  */
-int	SkAddrGmacPromiscuousChange(
+static int	SkAddrGmacPromiscuousChange(
 SK_AC	*pAC,			/* adapter context */
 SK_IOC	IoC,			/* I/O context */
 SK_U32	PortNumber,		/* port whose promiscuous mode changes */
diff -urN oldtree/drivers/net/sk98lin/skgeinit.c newtree/drivers/net/sk98lin/skgeinit.c
--- oldtree/drivers/net/sk98lin/skgeinit.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/sk98lin/skgeinit.c	2006-02-13 19:20:00.001512072 -0500
@@ -59,34 +59,6 @@
 
 /******************************************************************************
  *
- *	SkGePollRxD() - Enable / Disable Descriptor Polling of RxD Ring
- *
- * Description:
- *	Enable or disable the descriptor polling of the receive descriptor
- *	ring (RxD) for port 'Port'.
- *	The new configuration is *not* saved over any SkGeStopPort() and
- *	SkGeInitPort() calls.
- *
- * Returns:
- *	nothing
- */
-void SkGePollRxD(
-SK_AC	*pAC,		/* adapter context */
-SK_IOC	IoC,		/* IO context */
-int		Port,		/* Port Index (MAC_1 + n) */
-SK_BOOL PollRxD)	/* SK_TRUE (enable pol.), SK_FALSE (disable pol.) */
-{
-	SK_GEPORT *pPrt;
-
-	pPrt = &pAC->GIni.GP[Port];
-
-	SK_OUT32(IoC, Q_ADDR(pPrt->PRxQOff, Q_CSR), (PollRxD) ?
-		CSR_ENA_POL : CSR_DIS_POL);
-}	/* SkGePollRxD */
-
-
-/******************************************************************************
- *
  *	SkGePollTxD() - Enable / Disable Descriptor Polling of TxD Rings
  *
  * Description:
@@ -952,7 +924,7 @@
  * Returns:
  *	nothing
  */
-void SkGeInitRamIface(
+static void SkGeInitRamIface(
 SK_AC	*pAC,		/* adapter context */
 SK_IOC	IoC)		/* IO context */
 {
@@ -1409,83 +1381,6 @@
 
 }	/* SkGeInit0*/
 
-#ifdef SK_PCI_RESET
-
-/******************************************************************************
- *
- *	SkGePciReset() - Reset PCI interface
- *
- * Description:
- *	o Read PCI configuration.
- *	o Change power state to 3.
- *	o Change power state to 0.
- *	o Restore PCI configuration.
- *
- * Returns:
- *	0:	Success.
- *	1:	Power state could not be changed to 3.
- */
-static int SkGePciReset(
-SK_AC	*pAC,		/* adapter context */
-SK_IOC	IoC)		/* IO context */
-{
-	int		i;
-	SK_U16	PmCtlSts;
-	SK_U32	Bp1;
-	SK_U32	Bp2;
-	SK_U16	PciCmd;
-	SK_U8	Cls;
-	SK_U8	Lat;
-	SK_U8	ConfigSpace[PCI_CFG_SIZE];
-
-	/*
-	 * Note: Switching to D3 state is like a software reset.
-	 *		 Switching from D3 to D0 is a hardware reset.
-	 *		 We have to save and restore the configuration space.
-	 */
-	for (i = 0; i < PCI_CFG_SIZE; i++) {
-		SkPciReadCfgDWord(pAC, i*4, &ConfigSpace[i]);
-	}
-
-	/* We know the RAM Interface Arbiter is enabled. */
-	SkPciWriteCfgWord(pAC, PCI_PM_CTL_STS, PCI_PM_STATE_D3);
-	SkPciReadCfgWord(pAC, PCI_PM_CTL_STS, &PmCtlSts);
-	
-	if ((PmCtlSts & PCI_PM_STATE_MSK) != PCI_PM_STATE_D3) {
-		return(1);
-	}
-
-	/* Return to D0 state. */
-	SkPciWriteCfgWord(pAC, PCI_PM_CTL_STS, PCI_PM_STATE_D0);
-
-	/* Check for D0 state. */
-	SkPciReadCfgWord(pAC, PCI_PM_CTL_STS, &PmCtlSts);
-	
-	if ((PmCtlSts & PCI_PM_STATE_MSK) != PCI_PM_STATE_D0) {
-		return(1);
-	}
-
-	/* Check PCI Config Registers. */
-	SkPciReadCfgWord(pAC, PCI_COMMAND, &PciCmd);
-	SkPciReadCfgByte(pAC, PCI_CACHE_LSZ, &Cls);
-	SkPciReadCfgDWord(pAC, PCI_BASE_1ST, &Bp1);
-	SkPciReadCfgDWord(pAC, PCI_BASE_2ND, &Bp2);
-	SkPciReadCfgByte(pAC, PCI_LAT_TIM, &Lat);
-	
-	if (PciCmd != 0 || Cls != (SK_U8)0 || Lat != (SK_U8)0 ||
-		(Bp1 & 0xfffffff0L) != 0 || Bp2 != 1) {
-		return(1);
-	}
-
-	/* Restore PCI Config Space. */
-	for (i = 0; i < PCI_CFG_SIZE; i++) {
-		SkPciWriteCfgDWord(pAC, i*4, ConfigSpace[i]);
-	}
-
-	return(0);
-}	/* SkGePciReset */
-
-#endif /* SK_PCI_RESET */
 
 /******************************************************************************
  *
@@ -1524,10 +1419,6 @@
 	/* save CLK_RUN bits (YUKON-Lite) */
 	SK_IN16(IoC, B0_CTST, &CtrlStat);
 
-#ifdef SK_PCI_RESET
-	(void)SkGePciReset(pAC, IoC);
-#endif /* SK_PCI_RESET */
-
 	/* do the SW-reset */
 	SK_OUT8(IoC, B0_CTST, CS_RST_SET);
 
@@ -1991,11 +1882,6 @@
 	int	i;
 	SK_U16	Word;
 
-#ifdef SK_PHY_LP_MODE
-	SK_U8	Byte;
-	SK_U16	PmCtlSts;
-#endif /* SK_PHY_LP_MODE */
-
 #if (!defined(SK_SLIM) && !defined(VCPU))
 	/* ensure I2C is ready */
 	SkI2cWaitIrq(pAC, IoC);
@@ -2010,38 +1896,6 @@
 		}
 	}
 
-#ifdef SK_PHY_LP_MODE
-    /*
-	 * for power saving purposes within mobile environments
-	 * we set the PHY to coma mode and switch to D3 power state.
-	 */
-	if (pAC->GIni.GIYukonLite &&
-		pAC->GIni.GIChipRev >= CHIP_REV_YU_LITE_A3) {
-
-		/* for all ports switch PHY to coma mode */
-		for (i = 0; i < pAC->GIni.GIMacsFound; i++) {
-			
-			SkGmEnterLowPowerMode(pAC, IoC, i, PHY_PM_DEEP_SLEEP);
-		}
-
-		if (pAC->GIni.GIVauxAvail) {
-			/* switch power to VAUX */
-			Byte = PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_ON | PC_VCC_OFF;
-
-			SK_OUT8(IoC, B0_POWER_CTRL, Byte);
-		}
-		
-		/* switch to D3 state */
-		SK_IN16(IoC, PCI_C(PCI_PM_CTL_STS), &PmCtlSts);
-
-		PmCtlSts |= PCI_PM_STATE_D3;
-
-		SK_OUT8(IoC, B2_TST_CTRL1, TST_CFG_WRITE_ON);
-
-		SK_OUT16(IoC, PCI_C(PCI_PM_CTL_STS), PmCtlSts);
-	}
-#endif /* SK_PHY_LP_MODE */
-
 	/* Reset all bits in the PCI STATUS register */
 	/*
 	 * Note: PCI Cfg cycles cannot be used, because they are not
diff -urN oldtree/drivers/net/sk98lin/skgemib.c newtree/drivers/net/sk98lin/skgemib.c
--- oldtree/drivers/net/sk98lin/skgemib.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/sk98lin/skgemib.c	2006-02-13 19:20:00.002511920 -0500
@@ -871,13 +871,6 @@
 		sizeof(SK_PNMI_CONF),
 		SK_PNMI_OFF(Conf) + SK_PNMI_CNF_OFF(ConfPhyType),
 		SK_PNMI_RO, MacPrivateConf, 0},
-#ifdef SK_PHY_LP_MODE
-		{OID_SKGE_PHY_LP_MODE,
-		SK_PNMI_MAC_ENTRIES,
-		sizeof(SK_PNMI_CONF),
-		SK_PNMI_OFF(Conf) + SK_PNMI_CNF_OFF(ConfPhyMode),
-		SK_PNMI_RW, MacPrivateConf, 0},
-#endif	
 	{OID_SKGE_LINK_CAP,
 		SK_PNMI_MAC_ENTRIES,
 		sizeof(SK_PNMI_CONF),
diff -urN oldtree/drivers/net/sk98lin/skgepnmi.c newtree/drivers/net/sk98lin/skgepnmi.c
--- oldtree/drivers/net/sk98lin/skgepnmi.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/sk98lin/skgepnmi.c	2006-02-13 19:20:00.005511464 -0500
@@ -56,10 +56,6 @@
  * Public Function prototypes
  */
 int SkPnmiInit(SK_AC *pAC, SK_IOC IoC, int level);
-int SkPnmiGetVar(SK_AC *pAC, SK_IOC IoC, SK_U32 Id, void *pBuf,
-	unsigned int *pLen, SK_U32 Instance, SK_U32 NetIndex);
-int SkPnmiPreSetVar(SK_AC *pAC, SK_IOC IoC, SK_U32 Id, void *pBuf,
-	unsigned int *pLen, SK_U32 Instance, SK_U32 NetIndex);
 int SkPnmiSetVar(SK_AC *pAC, SK_IOC IoC, SK_U32 Id, void *pBuf,
 	unsigned int *pLen, SK_U32 Instance, SK_U32 NetIndex);
 int SkPnmiGetStruct(SK_AC *pAC, SK_IOC IoC, void *pBuf,
@@ -587,7 +583,7 @@
  *                           exist (e.g. port instance 3 on a two port
  *	                         adapter.
  */
-int SkPnmiGetVar(
+static int SkPnmiGetVar(
 SK_AC *pAC,		/* Pointer to adapter context */
 SK_IOC IoC,		/* IO context handle */
 SK_U32 Id,		/* Object ID that is to be processed */
@@ -629,7 +625,7 @@
  *                           exist (e.g. port instance 3 on a two port
  *	                         adapter.
  */
-int SkPnmiPreSetVar(
+static int SkPnmiPreSetVar(
 SK_AC *pAC,		/* Pointer to adapter context */
 SK_IOC IoC,		/* IO context handle */
 SK_U32 Id,		/* Object ID that is to be processed */
@@ -5062,9 +5058,6 @@
 		case OID_SKGE_SPEED_CAP:
 		case OID_SKGE_SPEED_MODE:
 		case OID_SKGE_SPEED_STATUS:
-#ifdef SK_PHY_LP_MODE
-		case OID_SKGE_PHY_LP_MODE:
-#endif
 			if (*pLen < (Limit - LogPortIndex) * sizeof(SK_U8)) {
 
 				*pLen = (Limit - LogPortIndex) * sizeof(SK_U8);
@@ -5140,28 +5133,6 @@
 				Offset += sizeof(SK_U32);
 				break;
 
-#ifdef SK_PHY_LP_MODE
-			case OID_SKGE_PHY_LP_MODE:
-				if (!pAC->Pnmi.DualNetActiveFlag) { /* SingleNetMode */
-					if (LogPortIndex == 0) {
-						continue;
-					}
-					else {
-						/* Get value for physical ports */
-						PhysPortIndex = SK_PNMI_PORT_LOG2PHYS(pAC, LogPortIndex);
-						Val8 = (SK_U8) pAC->GIni.GP[PhysPortIndex].PPhyPowerState;
-						*pBufPtr = Val8;
-					}
-				}
-				else { /* DualNetMode */
-					
-					Val8 = (SK_U8) pAC->GIni.GP[PhysPortIndex].PPhyPowerState;
-					*pBufPtr = Val8;
-				}
-				Offset += sizeof(SK_U8);
-				break;
-#endif
-
 			case OID_SKGE_LINK_CAP:
 				if (!pAC->Pnmi.DualNetActiveFlag) { /* SingleNetMode */
 					if (LogPortIndex == 0) {
@@ -5478,16 +5449,6 @@
 		}
 		break;
 
-#ifdef SK_PHY_LP_MODE
-	case OID_SKGE_PHY_LP_MODE:
-		if (*pLen < Limit - LogPortIndex) {
-
-			*pLen = Limit - LogPortIndex;
-			return (SK_PNMI_ERR_TOO_SHORT);
-		}
-		break;
-#endif
-
 	case OID_SKGE_MTU:
 		if (*pLen < sizeof(SK_U32)) {
 
@@ -5845,116 +5806,6 @@
 			Offset += sizeof(SK_U32);
 			break;
 		
-#ifdef SK_PHY_LP_MODE
-		case OID_SKGE_PHY_LP_MODE:
-			/* The preset ends here */
-			if (Action == SK_PNMI_PRESET) {
-
-				return (SK_PNMI_ERR_OK);
-			}
-
-			if (!pAC->Pnmi.DualNetActiveFlag) { /* SingleNetMode */
-				if (LogPortIndex == 0) {
-					Offset = 0;
-					continue;
-				}
-				else {
-					/* Set value for physical ports */
-					PhysPortIndex = SK_PNMI_PORT_LOG2PHYS(pAC, LogPortIndex);
-
-					switch (*(pBuf + Offset)) {
-						case 0:
-							/* If LowPowerMode is active, we can leave it. */
-							if (pAC->GIni.GP[PhysPortIndex].PPhyPowerState) {
-
-								Val32 = SkGmLeaveLowPowerMode(pAC, IoC, PhysPortIndex);
-								
-								if (pAC->GIni.GP[PhysPortIndex].PPhyPowerState < 3)	{
-									
-									SkDrvInitAdapter(pAC);
-								}
-								break;
-							}
-							else {
-								*pLen = 0;
-								return (SK_PNMI_ERR_GENERAL);
-							}
-						case 1:
-						case 2:
-						case 3:
-						case 4:
-							/* If no LowPowerMode is active, we can enter it. */
-							if (!pAC->GIni.GP[PhysPortIndex].PPhyPowerState) {
-
-								if ((*(pBuf + Offset)) < 3)	{
-								
-									SkDrvDeInitAdapter(pAC);
-								}
-
-								Val32 = SkGmEnterLowPowerMode(pAC, IoC, PhysPortIndex, *pBuf);
-								break;
-							}
-							else {
-								*pLen = 0;
-								return (SK_PNMI_ERR_GENERAL);
-							}
-						default:
-							*pLen = 0;
-							return (SK_PNMI_ERR_BAD_VALUE);
-					}
-				}
-			}
-			else { /* DualNetMode */
-				
-				switch (*(pBuf + Offset)) {
-					case 0:
-						/* If we are in a LowPowerMode, we can leave it. */
-						if (pAC->GIni.GP[PhysPortIndex].PPhyPowerState) {
-
-							Val32 = SkGmLeaveLowPowerMode(pAC, IoC, PhysPortIndex);
-							
-							if (pAC->GIni.GP[PhysPortIndex].PPhyPowerState < 3)	{
-
-								SkDrvInitAdapter(pAC);
-							}
-							break;
-						}
-						else {
-							*pLen = 0;
-							return (SK_PNMI_ERR_GENERAL);
-						}
-					
-					case 1:
-					case 2:
-					case 3:
-					case 4:
-						/* If we are not already in LowPowerMode, we can enter it. */
-						if (!pAC->GIni.GP[PhysPortIndex].PPhyPowerState) {
-
-							if ((*(pBuf + Offset)) < 3)	{
-
-								SkDrvDeInitAdapter(pAC);
-							}
-							else {
-
-								Val32 = SkGmEnterLowPowerMode(pAC, IoC, PhysPortIndex, *pBuf);
-							}
-							break;
-						}
-						else {
-							*pLen = 0;
-							return (SK_PNMI_ERR_GENERAL);
-						}
-					
-					default:
-						*pLen = 0;
-						return (SK_PNMI_ERR_BAD_VALUE);
-				}
-			}
-			Offset += sizeof(SK_U8);
-			break;
-#endif
-
 		default:
             SK_DBG_MSG(pAC, SK_DBGMOD_PNMI, SK_DBGCAT_ERR,
                 ("MacPrivateConf: Unknown OID should be handled before set"));
diff -urN oldtree/drivers/net/sk98lin/skgesirq.c newtree/drivers/net/sk98lin/skgesirq.c
--- oldtree/drivers/net/sk98lin/skgesirq.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/sk98lin/skgesirq.c	2006-02-13 19:20:00.006511312 -0500
@@ -265,7 +265,7 @@
  *
  * Returns: N/A
  */
-void SkHWLinkUp(
+static void SkHWLinkUp(
 SK_AC	*pAC,	/* adapter context */
 SK_IOC	IoC,	/* IO context */
 int		Port)	/* Port Index (MAC_1 + n) */
@@ -612,14 +612,6 @@
 				 * we ignore those
 				 */
 				pPrt->HalfDupTimerActive = SK_TRUE;
-#ifdef XXX
-				Len = sizeof(SK_U64);
-				SkPnmiGetVar(pAC, IoC, OID_SKGE_STAT_TX_OCTETS, (char *)&Octets,
-					&Len, (SK_U32)SK_PNMI_PORT_PHYS2INST(pAC, 0),
-					pAC->Rlmt.Port[0].Net->NetNumber);
-				
-				pPrt->LastOctets = Octets;
-#endif /* XXX */
 				/* Snap statistic counters */
 				(void)SkXmUpdateStats(pAC, IoC, 0);
 
@@ -653,14 +645,6 @@
 				 pPrt->PLinkModeStatus == SK_LMODE_STAT_AUTOHALF) &&
 				!pPrt->HalfDupTimerActive) {
 				pPrt->HalfDupTimerActive = SK_TRUE;
-#ifdef XXX
-				Len = sizeof(SK_U64);
-				SkPnmiGetVar(pAC, IoC, OID_SKGE_STAT_TX_OCTETS, (char *)&Octets,
-					&Len, (SK_U32)SK_PNMI_PORT_PHYS2INST(pAC, 1),
-					pAC->Rlmt.Port[1].Net->NetNumber);
-				
-				pPrt->LastOctets = Octets;
-#endif /* XXX */
 				/* Snap statistic counters */
 				(void)SkXmUpdateStats(pAC, IoC, 1);
 
@@ -2085,12 +2069,6 @@
 			pPrt->HalfDupTimerActive = SK_FALSE;
 			if (pPrt->PLinkModeStatus == SK_LMODE_STAT_HALF ||
 				pPrt->PLinkModeStatus == SK_LMODE_STAT_AUTOHALF) {
-#ifdef XXX
-				Len = sizeof(SK_U64);
-				SkPnmiGetVar(pAC, IoC, OID_SKGE_STAT_TX_OCTETS, (char *)&Octets,
-					&Len, (SK_U32)SK_PNMI_PORT_PHYS2INST(pAC, Port),
-					pAC->Rlmt.Port[Port].Net->NetNumber);
-#endif /* XXX */
 				/* Snap statistic counters */
 				(void)SkXmUpdateStats(pAC, IoC, Port);
 
diff -urN oldtree/drivers/net/sk98lin/ski2c.c newtree/drivers/net/sk98lin/ski2c.c
--- oldtree/drivers/net/sk98lin/ski2c.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/sk98lin/ski2c.c	2006-02-13 19:20:00.007511160 -0500
@@ -396,7 +396,7 @@
  *			1:	error,	 transfer does not complete, I2C transfer
  *						 killed, wait loop terminated.
  */
-int	SkI2cWait(
+static int	SkI2cWait(
 SK_AC	*pAC,	/* Adapter Context */
 SK_IOC	IoC,	/* I/O Context */
 int		Event)	/* complete event to wait for (I2C_READ or I2C_WRITE) */
@@ -481,7 +481,7 @@
  * returns	0:	success
  *			1:	error
  */
-int SkI2cWrite(
+static int SkI2cWrite(
 SK_AC	*pAC,		/* Adapter Context */
 SK_IOC	IoC,		/* I/O Context */
 SK_U32	I2cData,	/* I2C Data to write */
@@ -538,7 +538,7 @@
  *		1 if the read is completed
  *		0 if the read must be continued (I2C Bus still allocated)
  */
-int	SkI2cReadSensor(
+static int	SkI2cReadSensor(
 SK_AC		*pAC,	/* Adapter Context */
 SK_IOC		IoC,	/* I/O Context */
 SK_SENSOR	*pSen)	/* Sensor to be read */
diff -urN oldtree/drivers/net/sk98lin/sklm80.c newtree/drivers/net/sk98lin/sklm80.c
--- oldtree/drivers/net/sk98lin/sklm80.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/sk98lin/sklm80.c	2006-02-13 19:20:00.007511160 -0500
@@ -34,79 +34,7 @@
 #include "h/lm80.h"
 #include "h/skdrv2nd.h"		/* Adapter Control- and Driver specific Def. */
 
-#ifdef	SK_DIAG
-#define	BREAK_OR_WAIT(pAC,IoC,Event)	SkI2cWait(pAC,IoC,Event)
-#else	/* nSK_DIAG */
 #define	BREAK_OR_WAIT(pAC,IoC,Event)	break
-#endif	/* nSK_DIAG */
-
-#ifdef	SK_DIAG
-/*
- * read the register 'Reg' from the device 'Dev'
- *
- * return 	read error	-1
- *		success		the read value
- */
-int	SkLm80RcvReg(
-SK_IOC	IoC,		/* Adapter Context */
-int		Dev,		/* I2C device address */
-int		Reg)		/* register to read */
-{
-	int	Val = 0;
-	int	TempExt;
-
-	/* Signal device number */
-	if (SkI2cSndDev(IoC, Dev, I2C_WRITE)) {
-		return(-1);
-	}
-
-	if (SkI2cSndByte(IoC, Reg)) {
-		return(-1);
-	}
-
-	/* repeat start */
-	if (SkI2cSndDev(IoC, Dev, I2C_READ)) {
-		return(-1);
-	}
-
-	switch (Reg) {
-	case LM80_TEMP_IN:
-		Val = (int)SkI2cRcvByte(IoC, 1);
-
-		/* First: correct the value: it might be negative */
-		if ((Val & 0x80) != 0) {
-			/* Value is negative */
-			Val = Val - 256;
-		}
-		Val = Val * SK_LM80_TEMP_LSB;
-		SkI2cStop(IoC);
-		
-		TempExt = (int)SkLm80RcvReg(IoC, LM80_ADDR, LM80_TEMP_CTRL);
-		
-		if (Val > 0) {
-			Val += ((TempExt >> 7) * SK_LM80_TEMPEXT_LSB);
-		}
-		else {
-			Val -= ((TempExt >> 7) * SK_LM80_TEMPEXT_LSB);
-		}
-		return(Val);
-		break;
-	case LM80_VT0_IN:
-	case LM80_VT1_IN:
-	case LM80_VT2_IN:
-	case LM80_VT3_IN:
-		Val = (int)SkI2cRcvByte(IoC, 1) * SK_LM80_VT_LSB;
-		break;
-	
-	default:
-		Val = (int)SkI2cRcvByte(IoC, 1);
-		break;
-	}
-
-	SkI2cStop(IoC);
-	return(Val);
-}
-#endif	/* SK_DIAG */
 
 /*
  * read a sensors value (LM80 specific)
diff -urN oldtree/drivers/net/sk98lin/skrlmt.c newtree/drivers/net/sk98lin/skrlmt.c
--- oldtree/drivers/net/sk98lin/skrlmt.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/sk98lin/skrlmt.c	2006-02-13 19:20:00.008511008 -0500
@@ -282,7 +282,6 @@
 
 SK_MAC_ADDR	SkRlmtMcAddr =	{{0x01,  0x00,  0x5A,  0x52,  0x4C,  0x4D}};
 SK_MAC_ADDR	BridgeMcAddr =	{{0x01,  0x80,  0xC2,  0x00,  0x00,  0x00}};
-SK_MAC_ADDR	BcAddr = 		{{0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF}};
 
 /* local variables ************************************************************/
 
diff -urN oldtree/drivers/net/sk98lin/skvpd.c newtree/drivers/net/sk98lin/skvpd.c
--- oldtree/drivers/net/sk98lin/skvpd.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/sk98lin/skvpd.c	2006-02-13 19:20:00.009510856 -0500
@@ -132,65 +132,6 @@
 
 #endif	/* SKDIAG */
 
-#if 0
-
-/*
-	Write the dword 'data' at address 'addr' into the VPD EEPROM, and
-	verify that the data is written.
-
- Needed Time:
-
-.				MIN		MAX
-. -------------------------------------------------------------------
-. write				1.8 ms		3.6 ms
-. internal write cyles		0.7 ms		7.0 ms
-. -------------------------------------------------------------------
-. over all program time	 	2.5 ms		10.6 ms
-. read				1.3 ms		2.6 ms
-. -------------------------------------------------------------------
-. over all 			3.8 ms		13.2 ms
-.
-
-
- Returns	0:	success
-			1:	error,	I2C transfer does not terminate
-			2:	error,	data verify error
-
- */
-static int VpdWriteDWord(
-SK_AC	*pAC,	/* pAC pointer */
-SK_IOC	IoC,	/* IO Context */
-int		addr,	/* VPD address */
-SK_U32	data)	/* VPD data to write */
-{
-	/* start VPD write */
-	/* Don't swap here, it's a data stream of bytes */
-	SK_DBG_MSG(pAC, SK_DBGMOD_VPD, SK_DBGCAT_CTRL,
-		("VPD write dword at addr 0x%x, data = 0x%x\n",addr,data));
-	VPD_OUT32(pAC, IoC, PCI_VPD_DAT_REG, (SK_U32)data);
-	/* But do it here */
-	addr |= VPD_WRITE;
-
-	VPD_OUT16(pAC, IoC, PCI_VPD_ADR_REG, (SK_U16)(addr | VPD_WRITE));
-
-	/* this may take up to 10,6 ms */
-	if (VpdWait(pAC, IoC, VPD_WRITE)) {
-		SK_DBG_MSG(pAC, SK_DBGMOD_VPD, SK_DBGCAT_ERR,
-			("Write Timed Out\n"));
-		return(1);
-	};
-
-	/* verify data */
-	if (VpdReadDWord(pAC, IoC, addr) != data) {
-		SK_DBG_MSG(pAC, SK_DBGMOD_VPD, SK_DBGCAT_ERR | SK_DBGCAT_FATAL,
-			("Data Verify Error\n"));
-		return(2);
-	}
-	return(0);
-}	/* VpdWriteDWord */
-
-#endif	/* 0 */
-
 /*
  *	Read one Stream of 'len' bytes of VPD data, starting at 'addr' from
  *	or to the I2C EEPROM.
@@ -728,7 +669,7 @@
  *		6:	fatal VPD error
  *
  */
-int	VpdSetupPara(
+static int	VpdSetupPara(
 SK_AC	*pAC,		/* common data base */
 const char	*key,	/* keyword to insert */
 const char	*buf,	/* buffer with the keyword value */
@@ -1148,50 +1089,3 @@
 	return(0);
 }
 
-
-
-/*
- *	Read the contents of the VPD EEPROM and copy it to the VPD buffer
- *	if not already done. If the keyword "VF" is not present it will be
- *	created and the error log message will be stored to this keyword.
- *	If "VF" is not present the error log message will be stored to the
- *	keyword "VL". "VL" will created or overwritten if "VF" is present.
- *	The VPD read/write area is saved to the VPD EEPROM.
- *
- * returns nothing, errors will be ignored.
- */
-void VpdErrLog(
-SK_AC	*pAC,	/* common data base */
-SK_IOC	IoC,	/* IO Context */
-char	*msg)	/* error log message */
-{
-	SK_VPD_PARA *v, vf;	/* VF */
-	int len;
-
-	SK_DBG_MSG(pAC, SK_DBGMOD_VPD, SK_DBGCAT_TX,
-		("VPD error log msg %s\n", msg));
-	if ((pAC->vpd.v.vpd_status & VPD_VALID) == 0) {
-		if (VpdInit(pAC, IoC) != 0) {
-			SK_DBG_MSG(pAC, SK_DBGMOD_VPD, SK_DBGCAT_ERR,
-				("VPD init error\n"));
-			return;
-		}
-	}
-
-	len = strlen(msg);
-	if (len > VPD_MAX_LEN) {
-		/* cut it */
-		len = VPD_MAX_LEN;
-	}
-	if ((v = vpd_find_para(pAC, VPD_VF, &vf)) != NULL) {
-		SK_DBG_MSG(pAC, SK_DBGMOD_VPD, SK_DBGCAT_TX, ("overwrite VL\n"));
-		(void)VpdSetupPara(pAC, VPD_VL, msg, len, VPD_RW_KEY, OWR_KEY);
-	}
-	else {
-		SK_DBG_MSG(pAC, SK_DBGMOD_VPD, SK_DBGCAT_TX, ("write VF\n"));
-		(void)VpdSetupPara(pAC, VPD_VF, msg, len, VPD_RW_KEY, ADD_KEY);
-	}
-
-	(void)VpdUpdate(pAC, IoC);
-}
-
diff -urN oldtree/drivers/net/sk98lin/skxmac2.c newtree/drivers/net/sk98lin/skxmac2.c
--- oldtree/drivers/net/sk98lin/skxmac2.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/sk98lin/skxmac2.c	2006-02-13 19:20:00.012510400 -0500
@@ -41,13 +41,13 @@
 #endif
 
 #ifdef GENESIS
-BCOM_HACK BcomRegA1Hack[] = {
+static BCOM_HACK BcomRegA1Hack[] = {
  { 0x18, 0x0c20 }, { 0x17, 0x0012 }, { 0x15, 0x1104 }, { 0x17, 0x0013 },
  { 0x15, 0x0404 }, { 0x17, 0x8006 }, { 0x15, 0x0132 }, { 0x17, 0x8006 },
  { 0x15, 0x0232 }, { 0x17, 0x800D }, { 0x15, 0x000F }, { 0x18, 0x0420 },
  { 0, 0 }
 };
-BCOM_HACK BcomRegC0Hack[] = {
+static BCOM_HACK BcomRegC0Hack[] = {
  { 0x18, 0x0c20 }, { 0x17, 0x0012 }, { 0x15, 0x1204 }, { 0x17, 0x0013 },
  { 0x15, 0x0A04 }, { 0x18, 0x0420 },
  { 0, 0 }
@@ -790,7 +790,7 @@
  * Returns:
  *	nothing
  */
-void SkMacFlushRxFifo(
+static void SkMacFlushRxFifo(
 SK_AC	*pAC,	/* adapter context */
 SK_IOC	IoC,	/* IO context */
 int		Port)	/* Port Index (MAC_1 + n) */
@@ -1231,38 +1231,6 @@
 }	/* SkMacHardRst */
 
 
-/******************************************************************************
- *
- *	SkMacClearRst() - Clear the MAC reset
- *
- * Description:	calls a clear MAC reset routine dep. on board type
- *
- * Returns:
- *	nothing
- */
-void SkMacClearRst(
-SK_AC	*pAC,	/* adapter context */
-SK_IOC	IoC,	/* IO context */
-int		Port)	/* Port Index (MAC_1 + n) */
-{
-	
-#ifdef GENESIS
-	if (pAC->GIni.GIGenesis) {
-		
-		SkXmClearRst(pAC, IoC, Port);
-	}
-#endif /* GENESIS */
-	
-#ifdef YUKON
-	if (pAC->GIni.GIYukon) {
-		
-		SkGmClearRst(pAC, IoC, Port);
-	}
-#endif /* YUKON */
-
-}	/* SkMacClearRst */
-
-
 #ifdef GENESIS
 /******************************************************************************
  *
@@ -1713,7 +1681,7 @@
  * Returns:
  *	nothing
  */
-void SkXmInitDupMd(
+static void SkXmInitDupMd(
 SK_AC	*pAC,		/* adapter context */
 SK_IOC	IoC,		/* IO context */
 int		Port)		/* Port Index (MAC_1 + n) */
@@ -1761,7 +1729,7 @@
  * Returns:
  *	nothing
  */
-void SkXmInitPauseMd(
+static void SkXmInitPauseMd(
 SK_AC	*pAC,		/* adapter context */
 SK_IOC	IoC,		/* IO context */
 int		Port)		/* Port Index (MAC_1 + n) */
@@ -2076,283 +2044,7 @@
 }	/* SkXmInitPhyBcom */
 #endif /* GENESIS */
 
-
 #ifdef YUKON
-#ifndef SK_SLIM
-/******************************************************************************
- *
- *	SkGmEnterLowPowerMode()
- *
- * Description:	
- *	This function sets the Marvell Alaska PHY to the low power mode
- *	given by parameter mode.
- *	The following low power modes are available:
- *		
- *		- Coma Mode (Deep Sleep):
- *			Power consumption: ~15 - 30 mW
- *			The PHY cannot wake up on its own.
- *
- *		- IEEE 22.2.4.1.5 compatible power down mode
- *			Power consumption: ~240 mW
- *			The PHY cannot wake up on its own.
- *
- *		- energy detect mode
- *			Power consumption: ~160 mW
- *			The PHY can wake up on its own by detecting activity
- *			on the CAT 5 cable.
- *
- *		- energy detect plus mode
- *			Power consumption: ~150 mW
- *			The PHY can wake up on its own by detecting activity
- *			on the CAT 5 cable.
- *			Connected devices can be woken up by sending normal link
- *			pulses every one second.
- *
- * Note:
- *
- * Returns:
- *		0: ok
- *		1: error
- */
-int SkGmEnterLowPowerMode(
-SK_AC	*pAC,		/* adapter context */
-SK_IOC	IoC,		/* IO context */
-int		Port,		/* Port Index (e.g. MAC_1) */
-SK_U8	Mode)		/* low power mode */
-{
-	SK_U16	Word;
-	SK_U32	DWord;
-	SK_U8	LastMode;
-	int		Ret = 0;
-
-	if (pAC->GIni.GIYukonLite &&
-	    pAC->GIni.GIChipRev >= CHIP_REV_YU_LITE_A3) {
-
-		/* save current power mode */
-		LastMode = pAC->GIni.GP[Port].PPhyPowerState;
-		pAC->GIni.GP[Port].PPhyPowerState = Mode;
-
-		switch (Mode) {
-			/* coma mode (deep sleep) */
-			case PHY_PM_DEEP_SLEEP:
-				/* setup General Purpose Control Register */
-				GM_OUT16(IoC, 0, GM_GP_CTRL, GM_GPCR_FL_PASS |
-					GM_GPCR_SPEED_100 | GM_GPCR_AU_ALL_DIS);
-
-				/* apply COMA mode workaround */
-				SkGmPhyWrite(pAC, IoC, Port, 29, 0x001f);
-				SkGmPhyWrite(pAC, IoC, Port, 30, 0xfff3);
-
-				SK_IN32(IoC, PCI_C(PCI_OUR_REG_1), &DWord);
-
-				SK_OUT8(IoC, B2_TST_CTRL1, TST_CFG_WRITE_ON);
-				
-				/* Set PHY to Coma Mode */
-				SK_OUT32(IoC, PCI_C(PCI_OUR_REG_1), DWord | PCI_PHY_COMA);
-				
-				SK_OUT8(IoC, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
-
-			break;
-			
-			/* IEEE 22.2.4.1.5 compatible power down mode */
-			case PHY_PM_IEEE_POWER_DOWN:
-				/*
-				 * - disable MAC 125 MHz clock
-				 * - allow MAC power down
-				 */
-				SkGmPhyRead(pAC, IoC, Port, PHY_MARV_PHY_CTRL, &Word);
-				Word |= PHY_M_PC_DIS_125CLK;
-				Word &=	~PHY_M_PC_MAC_POW_UP;
-				SkGmPhyWrite(pAC, IoC, Port, PHY_MARV_PHY_CTRL, Word);
-
-				/*
-				 * register changes must be followed by a software
-				 * reset to take effect
-				 */
-				SkGmPhyRead(pAC, IoC, Port, PHY_MARV_CTRL, &Word);
-				Word |= PHY_CT_RESET;
-				SkGmPhyWrite(pAC, IoC, Port, PHY_MARV_CTRL, Word);
-
-				/* switch IEEE compatible power down mode on */
-				SkGmPhyRead(pAC, IoC, Port, PHY_MARV_CTRL, &Word);
-				Word |= PHY_CT_PDOWN;
-				SkGmPhyWrite(pAC, IoC, Port, PHY_MARV_CTRL, Word);
-			break;
-
-			/* energy detect and energy detect plus mode */
-			case PHY_PM_ENERGY_DETECT:
-			case PHY_PM_ENERGY_DETECT_PLUS:
-				/*
-				 * - disable MAC 125 MHz clock
-				 */
-				SkGmPhyRead(pAC, IoC, Port, PHY_MARV_PHY_CTRL, &Word);
-				Word |= PHY_M_PC_DIS_125CLK;
-				SkGmPhyWrite(pAC, IoC, Port, PHY_MARV_PHY_CTRL, Word);
-				
-				/* activate energy detect mode 1 */
-				SkGmPhyRead(pAC, IoC, Port, PHY_MARV_PHY_CTRL, &Word);
-
-				/* energy detect mode */
-				if (Mode == PHY_PM_ENERGY_DETECT) {
-					Word |= PHY_M_PC_EN_DET;
-				}
-				/* energy detect plus mode */
-				else {
-					Word |= PHY_M_PC_EN_DET_PLUS;
-				}
-
-				SkGmPhyWrite(pAC, IoC, Port, PHY_MARV_PHY_CTRL, Word);
-
-				/*
-				 * reinitialize the PHY to force a software reset
-				 * which is necessary after the register settings
-				 * for the energy detect modes.
-				 * Furthermore reinitialisation prevents that the
-				 * PHY is running out of a stable state.
-				 */
-				SkGmInitPhyMarv(pAC, IoC, Port, SK_FALSE);
-			break;
-
-			/* don't change current power mode */
-			default:
-				pAC->GIni.GP[Port].PPhyPowerState = LastMode;
-				Ret = 1;
-			break;
-		}
-	}
-	/* low power modes are not supported by this chip */
-	else {
-		Ret = 1;
-	}
-
-	return(Ret);
-
-}	/* SkGmEnterLowPowerMode */
-
-/******************************************************************************
- *
- *	SkGmLeaveLowPowerMode()
- *
- * Description:	
- *	Leave the current low power mode and switch to normal mode
- *
- * Note:
- *
- * Returns:
- *		0:	ok
- *		1:	error
- */
-int SkGmLeaveLowPowerMode(
-SK_AC	*pAC,		/* adapter context */
-SK_IOC	IoC,		/* IO context */
-int		Port)		/* Port Index (e.g. MAC_1) */
-{
-	SK_U32	DWord;
-	SK_U16	Word;
-	SK_U8	LastMode;
-	int		Ret = 0;
-
-	if (pAC->GIni.GIYukonLite &&
-		pAC->GIni.GIChipRev >= CHIP_REV_YU_LITE_A3) {
-
-		/* save current power mode */
-		LastMode = pAC->GIni.GP[Port].PPhyPowerState;
-		pAC->GIni.GP[Port].PPhyPowerState = PHY_PM_OPERATIONAL_MODE;
-
-		switch (LastMode) {
-			/* coma mode (deep sleep) */
-			case PHY_PM_DEEP_SLEEP:
-				SK_IN32(IoC, PCI_C(PCI_OUR_REG_1), &DWord);
-
-				SK_OUT8(IoC, B2_TST_CTRL1, TST_CFG_WRITE_ON);
-				
-				/* Release PHY from Coma Mode */
-				SK_OUT32(IoC, PCI_C(PCI_OUR_REG_1), DWord & ~PCI_PHY_COMA);
-				
-				SK_OUT8(IoC, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
-				
-				SK_IN32(IoC, B2_GP_IO, &DWord);
-
-				/* set to output */
-				DWord |= (GP_DIR_9 | GP_IO_9);
-
-				/* set PHY reset */
-				SK_OUT32(IoC, B2_GP_IO, DWord);
-
-				DWord &= ~GP_IO_9; /* clear PHY reset (active high) */
-
-				/* clear PHY reset */
-				SK_OUT32(IoC, B2_GP_IO, DWord);
-			break;
-			
-			/* IEEE 22.2.4.1.5 compatible power down mode */
-			case PHY_PM_IEEE_POWER_DOWN:
-				/*
-				 * - enable MAC 125 MHz clock
-				 * - set MAC power up
-				 */
-				SkGmPhyRead(pAC, IoC, Port, PHY_MARV_PHY_CTRL, &Word);
-				Word &= ~PHY_M_PC_DIS_125CLK;
-				Word |=	PHY_M_PC_MAC_POW_UP;
-				SkGmPhyWrite(pAC, IoC, Port, PHY_MARV_PHY_CTRL, Word);
-
-				/*
-				 * register changes must be followed by a software
-				 * reset to take effect
-				 */
-				SkGmPhyRead(pAC, IoC, Port, PHY_MARV_CTRL, &Word);
-				Word |= PHY_CT_RESET;
-				SkGmPhyWrite(pAC, IoC, Port, PHY_MARV_CTRL, Word);
-
-				/* switch IEEE compatible power down mode off */
-				SkGmPhyRead(pAC, IoC, Port, PHY_MARV_CTRL, &Word);
-				Word &= ~PHY_CT_PDOWN;
-				SkGmPhyWrite(pAC, IoC, Port, PHY_MARV_CTRL, Word);
-			break;
-
-			/* energy detect and energy detect plus mode */
-			case PHY_PM_ENERGY_DETECT:
-			case PHY_PM_ENERGY_DETECT_PLUS:
-				/*
-				 * - enable MAC 125 MHz clock
-				 */
-				SkGmPhyRead(pAC, IoC, Port, PHY_MARV_PHY_CTRL, &Word);
-				Word &= ~PHY_M_PC_DIS_125CLK;
-				SkGmPhyWrite(pAC, IoC, Port, PHY_MARV_PHY_CTRL, Word);
-				
-				/* disable energy detect mode */
-				SkGmPhyRead(pAC, IoC, Port, PHY_MARV_PHY_CTRL, &Word);
-				Word &= ~PHY_M_PC_EN_DET_MSK;
-				SkGmPhyWrite(pAC, IoC, Port, PHY_MARV_PHY_CTRL, Word);
-
-				/*
-				 * reinitialize the PHY to force a software reset
-				 * which is necessary after the register settings
-				 * for the energy detect modes.
-				 * Furthermore reinitialisation prevents that the
-				 * PHY is running out of a stable state.
-				 */
-				SkGmInitPhyMarv(pAC, IoC, Port, SK_FALSE);
-			break;
-
-			/* don't change current power mode */
-			default:
-				pAC->GIni.GP[Port].PPhyPowerState = LastMode;
-				Ret = 1;
-			break;
-		}
-	}
-	/* low power modes are not supported by this chip */
-	else {
-		Ret = 1;
-	}
-
-	return(Ret);
-
-}	/* SkGmLeaveLowPowerMode */
-#endif /* !SK_SLIM */
-
-
 /******************************************************************************
  *
  *	SkGmInitPhyMarv() - Initialize the Marvell Phy registers
@@ -3420,145 +3112,6 @@
 }	/* SkMacAutoNegDone */
 
 
-#ifdef GENESIS
-/******************************************************************************
- *
- *	SkXmSetRxTxEn() - Special Set Rx/Tx Enable and some features in XMAC
- *
- * Description:
- *  sets MAC or PHY LoopBack and Duplex Mode in the MMU Command Reg.
- *  enables Rx/Tx
- *
- * Returns: N/A
- */
-static void SkXmSetRxTxEn(
-SK_AC	*pAC,		/* Adapter Context */
-SK_IOC	IoC,		/* IO context */
-int		Port,		/* Port Index (MAC_1 + n) */
-int		Para)		/* Parameter to set: MAC or PHY LoopBack, Duplex Mode */
-{
-	SK_U16	Word;
-
-	XM_IN16(IoC, Port, XM_MMU_CMD, &Word);
-
-	switch (Para & (SK_MAC_LOOPB_ON | SK_MAC_LOOPB_OFF)) {
-	case SK_MAC_LOOPB_ON:
-		Word |= XM_MMU_MAC_LB;
-		break;
-	case SK_MAC_LOOPB_OFF:
-		Word &= ~XM_MMU_MAC_LB;
-		break;
-	}
-
-	switch (Para & (SK_PHY_LOOPB_ON | SK_PHY_LOOPB_OFF)) {
-	case SK_PHY_LOOPB_ON:
-		Word |= XM_MMU_GMII_LOOP;
-		break;
-	case SK_PHY_LOOPB_OFF:
-		Word &= ~XM_MMU_GMII_LOOP;
-		break;
-	}
-	
-	switch (Para & (SK_PHY_FULLD_ON | SK_PHY_FULLD_OFF)) {
-	case SK_PHY_FULLD_ON:
-		Word |= XM_MMU_GMII_FD;
-		break;
-	case SK_PHY_FULLD_OFF:
-		Word &= ~XM_MMU_GMII_FD;
-		break;
-	}
-	
-	XM_OUT16(IoC, Port, XM_MMU_CMD, Word | XM_MMU_ENA_RX | XM_MMU_ENA_TX);
-
-	/* dummy read to ensure writing */
-	XM_IN16(IoC, Port, XM_MMU_CMD, &Word);
-
-}	/* SkXmSetRxTxEn */
-#endif /* GENESIS */
-
-
-#ifdef YUKON
-/******************************************************************************
- *
- *	SkGmSetRxTxEn() - Special Set Rx/Tx Enable and some features in GMAC
- *
- * Description:
- *  sets MAC LoopBack and Duplex Mode in the General Purpose Control Reg.
- *  enables Rx/Tx
- *
- * Returns: N/A
- */
-static void SkGmSetRxTxEn(
-SK_AC	*pAC,		/* Adapter Context */
-SK_IOC	IoC,		/* IO context */
-int		Port,		/* Port Index (MAC_1 + n) */
-int		Para)		/* Parameter to set: MAC LoopBack, Duplex Mode */
-{
-	SK_U16	Ctrl;
-	
-	GM_IN16(IoC, Port, GM_GP_CTRL, &Ctrl);
-
-	switch (Para & (SK_MAC_LOOPB_ON | SK_MAC_LOOPB_OFF)) {
-	case SK_MAC_LOOPB_ON:
-		Ctrl |= GM_GPCR_LOOP_ENA;
-		break;
-	case SK_MAC_LOOPB_OFF:
-		Ctrl &= ~GM_GPCR_LOOP_ENA;
-		break;
-	}
-
-	switch (Para & (SK_PHY_FULLD_ON | SK_PHY_FULLD_OFF)) {
-	case SK_PHY_FULLD_ON:
-		Ctrl |= GM_GPCR_DUP_FULL;
-		break;
-	case SK_PHY_FULLD_OFF:
-		Ctrl &= ~GM_GPCR_DUP_FULL;
-		break;
-	}
-	
-    GM_OUT16(IoC, Port, GM_GP_CTRL, (SK_U16)(Ctrl | GM_GPCR_RX_ENA |
-		GM_GPCR_TX_ENA));
-
-	/* dummy read to ensure writing */
-	GM_IN16(IoC, Port, GM_GP_CTRL, &Ctrl);
-
-}	/* SkGmSetRxTxEn */
-#endif /* YUKON */
-
-
-#ifndef SK_SLIM
-/******************************************************************************
- *
- *	SkMacSetRxTxEn() - Special Set Rx/Tx Enable and parameters
- *
- * Description:	calls the Special Set Rx/Tx Enable routines dep. on board type
- *
- * Returns: N/A
- */
-void SkMacSetRxTxEn(
-SK_AC	*pAC,		/* Adapter Context */
-SK_IOC	IoC,		/* IO context */
-int		Port,		/* Port Index (MAC_1 + n) */
-int		Para)
-{
-#ifdef GENESIS
-	if (pAC->GIni.GIGenesis) {
-		
-		SkXmSetRxTxEn(pAC, IoC, Port, Para);
-	}
-#endif /* GENESIS */
-	
-#ifdef YUKON
-	if (pAC->GIni.GIYukon) {
-		
-		SkGmSetRxTxEn(pAC, IoC, Port, Para);
-	}
-#endif /* YUKON */
-
-}	/* SkMacSetRxTxEn */
-#endif /* !SK_SLIM */
-
-
 /******************************************************************************
  *
  *	SkMacRxTxEnable() - Enable Rx/Tx activity if port is up
@@ -3976,7 +3529,7 @@
  * Returns:
  *	nothing
  */
-void SkXmIrq(
+static void SkXmIrq(
 SK_AC	*pAC,		/* adapter context */
 SK_IOC	IoC,		/* IO context */
 int		Port)		/* Port Index (MAC_1 + n) */
@@ -4112,7 +3665,7 @@
  * Returns:
  *	nothing
  */
-void SkGmIrq(
+static void SkGmIrq(
 SK_AC	*pAC,		/* adapter context */
 SK_IOC	IoC,		/* IO context */
 int		Port)		/* Port Index (MAC_1 + n) */
diff -urN oldtree/drivers/net/skge.c newtree/drivers/net/skge.c
--- oldtree/drivers/net/skge.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/skge.c	2006-02-13 19:20:29.721993872 -0500
@@ -43,7 +43,7 @@
 #include "skge.h"
 
 #define DRV_NAME		"skge"
-#define DRV_VERSION		"1.2"
+#define DRV_VERSION		"1.3"
 #define PFX			DRV_NAME " "
 
 #define DEFAULT_TX_RING_SIZE	128
@@ -88,15 +88,14 @@
 
 static int skge_up(struct net_device *dev);
 static int skge_down(struct net_device *dev);
+static void skge_phy_reset(struct skge_port *skge);
 static void skge_tx_clean(struct skge_port *skge);
 static int xm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val);
 static int gm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val);
 static void genesis_get_stats(struct skge_port *skge, u64 *data);
 static void yukon_get_stats(struct skge_port *skge, u64 *data);
 static void yukon_init(struct skge_hw *hw, int port);
-static void yukon_reset(struct skge_hw *hw, int port);
 static void genesis_mac_init(struct skge_hw *hw, int port);
-static void genesis_reset(struct skge_hw *hw, int port);
 static void genesis_link_up(struct skge_port *skge);
 
 /* Avoid conditionals by using array */
@@ -276,10 +275,9 @@
 	skge->autoneg = ecmd->autoneg;
 	skge->advertising = ecmd->advertising;
 
-	if (netif_running(dev)) {
-		skge_down(dev);
-		skge_up(dev);
-	}
+	if (netif_running(dev))
+		skge_phy_reset(skge);
+
 	return (0);
 }
 
@@ -399,6 +397,7 @@
 			       struct ethtool_ringparam *p)
 {
 	struct skge_port *skge = netdev_priv(dev);
+	int err;
 
 	if (p->rx_pending == 0 || p->rx_pending > MAX_RX_RING_SIZE ||
 	    p->tx_pending == 0 || p->tx_pending > MAX_TX_RING_SIZE)
@@ -409,7 +408,11 @@
 
 	if (netif_running(dev)) {
 		skge_down(dev);
-		skge_up(dev);
+		err = skge_up(dev);
+		if (err)
+			dev_close(dev);
+		else
+			dev->set_multicast_list(dev);
 	}
 
 	return 0;
@@ -430,21 +433,11 @@
 static int skge_nway_reset(struct net_device *dev)
 {
 	struct skge_port *skge = netdev_priv(dev);
-	struct skge_hw *hw = skge->hw;
-	int port = skge->port;
 
 	if (skge->autoneg != AUTONEG_ENABLE || !netif_running(dev))
 		return -EINVAL;
 
-	spin_lock_bh(&hw->phy_lock);
-	if (hw->chip_id == CHIP_ID_GENESIS) {
-		genesis_reset(hw, port);
-		genesis_mac_init(hw, port);
-	} else {
-		yukon_reset(hw, port);
-		yukon_init(hw, port);
-	}
-	spin_unlock_bh(&hw->phy_lock);
+	skge_phy_reset(skge);
 	return 0;
 }
 
@@ -516,10 +509,8 @@
 	else
 		skge->flow_control = FLOW_MODE_NONE;
 
-	if (netif_running(dev)) {
-		skge_down(dev);
-		skge_up(dev);
-	}
+	if (netif_running(dev))
+		skge_phy_reset(skge);
 	return 0;
 }
 
@@ -1935,7 +1926,6 @@
 
 	}
 
-	yukon_reset(hw, port);
 	skge_link_down(skge);
 
 	yukon_init(hw, port);
@@ -2019,6 +2009,22 @@
 	/* XXX restart autonegotiation? */
 }
 
+static void skge_phy_reset(struct skge_port *skge)
+{
+	struct skge_hw *hw = skge->hw;
+	int port = skge->port;
+
+	netif_stop_queue(skge->netdev);
+	netif_carrier_off(skge->netdev);
+
+	spin_lock_bh(&hw->phy_lock);
+	if (hw->chip_id == CHIP_ID_GENESIS)
+		genesis_mac_init(hw, port);
+	else
+		yukon_init(hw, port);
+	spin_unlock_bh(&hw->phy_lock);
+}
+
 /* Basic MII support */
 static int skge_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
@@ -2187,6 +2193,7 @@
 	kfree(skge->rx_ring.start);
  free_pci_mem:
 	pci_free_consistent(hw->pdev, skge->mem_size, skge->mem, skge->dma);
+	skge->mem = NULL;
 
 	return err;
 }
@@ -2197,6 +2204,9 @@
 	struct skge_hw *hw = skge->hw;
 	int port = skge->port;
 
+	if (skge->mem == NULL)
+		return 0;
+
 	if (netif_msg_ifdown(skge))
 		printk(KERN_INFO PFX "%s: disabling interface\n", dev->name);
 
@@ -2253,6 +2263,7 @@
 	kfree(skge->rx_ring.start);
 	kfree(skge->tx_ring.start);
 	pci_free_consistent(hw->pdev, skge->mem_size, skge->mem, skge->dma);
+	skge->mem = NULL;
 	return 0;
 }
 
@@ -2413,18 +2424,23 @@
 
 static int skge_change_mtu(struct net_device *dev, int new_mtu)
 {
-	int err = 0;
-	int running = netif_running(dev);
+	int err;
 
 	if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU)
 		return -EINVAL;
 
+	if (!netif_running(dev)) {
+		dev->mtu = new_mtu;
+		return 0;
+	}
+
+	skge_down(dev);
 
-	if (running)
-		skge_down(dev);
 	dev->mtu = new_mtu;
-	if (running)
-		skge_up(dev);
+
+	err = skge_up(dev);
+	if (err)
+		dev_close(dev);
 
 	return err;
 }
@@ -3398,8 +3414,8 @@
 		struct net_device *dev = hw->dev[i];
 		if (dev) {
 			netif_device_attach(dev);
-			if (netif_running(dev))
-				skge_up(dev);
+			if (netif_running(dev) && skge_up(dev))
+				dev_close(dev);
 		}
 	}
 	return 0;
diff -urN oldtree/drivers/net/sunhme.c newtree/drivers/net/sunhme.c
--- oldtree/drivers/net/sunhme.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/sunhme.c	2006-02-13 19:20:00.013510248 -0500
@@ -3013,7 +3013,7 @@
 }
 #endif /* !(__sparc__) */
 
-static int __init happy_meal_pci_init(struct pci_dev *pdev)
+static int __devinit happy_meal_pci_init(struct pci_dev *pdev)
 {
 	struct quattro *qp = NULL;
 #ifdef __sparc__
@@ -3073,6 +3073,7 @@
 	memset(hp, 0, sizeof(*hp));
 
 	hp->happy_dev = pdev;
+	pci_dev_get(pdev);
 
 	spin_lock_init(&hp->happy_lock);
 
@@ -3260,6 +3261,7 @@
 	pci_release_regions(pdev);
 
 err_out_clear_quattro:
+	pci_dev_put(pdev);
 	if (qp != NULL)
 		qp->happy_meals[qfe_slot] = NULL;
 
@@ -3304,21 +3306,58 @@
 #endif
 
 #ifdef CONFIG_PCI
-static int __init happy_meal_pci_probe(void)
+static int __devinit happy_meal_pci_probe(struct pci_dev *pdev,
+	const struct pci_device_id *id)
 {
-	struct pci_dev *pdev = NULL;
-	int cards = 0;
+	int retval;
+
+	retval = pci_enable_device(pdev);
+	if (retval < 0)
+		goto err;
+
+	pci_set_master(pdev);
+	happy_meal_pci_init(pdev);
+
+	return 0;
+err:
+	return retval;
+}
 
-	while ((pdev = pci_find_device(PCI_VENDOR_ID_SUN,
-				       PCI_DEVICE_ID_SUN_HAPPYMEAL, pdev)) != NULL) {
-		if (pci_enable_device(pdev))
-			continue;
-		pci_set_master(pdev);
-		cards++;
-		happy_meal_pci_init(pdev);
+static void __devexit happy_meal_pci_remove(struct pci_dev *pdev)
+{
+	struct quattro *tmp, *qp = qfe_pci_list;
+	struct pci_dev *bdev = pdev->bus->self;
+
+	if (qp->quattro_dev == bdev) { /* is it the 1st one? */
+		qfe_pci_list = qp->next;
+		kfree(qp);
+		goto end;
 	}
-	return cards;
+
+	for (; qp->next != NULL; qp = qp->next) /* some further? */
+		if (qp->next->quattro_dev == bdev)
+			break;
+
+	tmp = qp->next; /* kill it, but preserve list */
+	qp->next = qp->next->next;
+	kfree(tmp);
+end:
+	pci_dev_put(pdev);
 }
+
+static struct pci_device_id happy_meal_pci_tbl[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_HAPPYMEAL) },
+	{ 0 }
+};
+MODULE_DEVICE_TABLE(pci, happy_meal_pci_tbl);
+
+static struct pci_driver happy_meal_pci_driver = {
+	.name		= "happy_meal_pci",
+	.id_table	= happy_meal_pci_tbl,
+	.probe		= happy_meal_pci_probe,
+	.remove		= __devexit_p(happy_meal_pci_remove)
+};
+
 #endif
 
 static int __init happy_meal_probe(void)
@@ -3337,11 +3376,10 @@
 	cards += happy_meal_sbus_probe();
 #endif
 #ifdef CONFIG_PCI
-	cards += happy_meal_pci_probe();
+	return pci_register_driver(&happy_meal_pci_driver);
+#else
+	return cards ? 0 : -ENODEV;
 #endif
-	if (!cards)
-		return -ENODEV;
-	return 0;
 }
 
 
@@ -3408,14 +3446,7 @@
 	}
 #endif
 #ifdef CONFIG_PCI
-	while (qfe_pci_list) {
-		struct quattro *qfe = qfe_pci_list;
-		struct quattro *next = qfe->next;
-
-		kfree(qfe);
-
-		qfe_pci_list = next;
-	}
+	pci_unregister_driver(&happy_meal_pci_driver);
 #endif
 }
 
diff -urN oldtree/drivers/net/tulip/media.c newtree/drivers/net/tulip/media.c
--- oldtree/drivers/net/tulip/media.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/tulip/media.c	2006-02-13 19:20:00.014510096 -0500
@@ -44,8 +44,10 @@
 
 /* MII transceiver control section.
    Read and write the MII registers using software-generated serial
-   MDIO protocol.  See the MII specifications or DP83840A data sheet
-   for details. */
+   MDIO protocol.
+   See IEEE 802.3-2002.pdf (Section 2, Chapter "22.2.4 Management functions")
+   or DP83840A data sheet for more details.
+   */
 
 int tulip_mdio_read(struct net_device *dev, int phy_id, int location)
 {
@@ -272,13 +274,29 @@
 				int reset_length = p[2 + init_length];
 				misc_info = (u16*)(reset_sequence + reset_length);
 				if (startup) {
+					int timeout = 10;	/* max 1 ms */
 					iowrite32(mtable->csr12dir | 0x100, ioaddr + CSR12);
 					for (i = 0; i < reset_length; i++)
 						iowrite32(reset_sequence[i], ioaddr + CSR12);
+
+					/* flush posted writes */
+					ioread32(ioaddr + CSR12);
+
+					/* Sect 3.10.3 in DP83840A.pdf (p39) */
+					udelay(500);
+
+					/* Section 4.2 in DP83840A.pdf (p43) */
+					/* and IEEE 802.3 "22.2.4.1.1 Reset" */
+					while (timeout-- &&
+						(tulip_mdio_read (dev, phy_num, MII_BMCR) & BMCR_RESET))
+						udelay(100);
 				}
 				for (i = 0; i < init_length; i++)
 					iowrite32(init_sequence[i], ioaddr + CSR12);
+
+				ioread32(ioaddr + CSR12);	/* flush posted writes */
 			}
+
 			tmp_info = get_u16(&misc_info[1]);
 			if (tmp_info)
 				tp->advertising[phy_num] = tmp_info | 1;
diff -urN oldtree/drivers/net/tulip/tulip.h newtree/drivers/net/tulip/tulip.h
--- oldtree/drivers/net/tulip/tulip.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/tulip/tulip.h	2006-02-13 19:20:00.014510096 -0500
@@ -474,8 +474,11 @@
 			udelay(10);
 
 		if (!i)
-			printk(KERN_DEBUG "%s: tulip_stop_rxtx() failed\n",
-					pci_name(tp->pdev));
+			printk(KERN_DEBUG "%s: tulip_stop_rxtx() failed"
+					" (CSR5 0x%x CSR6 0x%x)\n",
+					pci_name(tp->pdev),
+					ioread32(ioaddr + CSR5),
+					ioread32(ioaddr + CSR6));
 	}
 }
 
diff -urN oldtree/drivers/net/tulip/tulip_core.c newtree/drivers/net/tulip/tulip_core.c
--- oldtree/drivers/net/tulip/tulip_core.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/tulip/tulip_core.c	2006-02-13 19:20:00.015509944 -0500
@@ -22,7 +22,7 @@
 #else
 #define DRV_VERSION	"1.1.13"
 #endif
-#define DRV_RELDATE	"May 11, 2002"
+#define DRV_RELDATE	"December 15, 2004"
 
 
 #include <linux/module.h>
diff -urN oldtree/drivers/net/wireless/Kconfig newtree/drivers/net/wireless/Kconfig
--- oldtree/drivers/net/wireless/Kconfig	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/wireless/Kconfig	2006-02-13 19:20:00.016509792 -0500
@@ -477,6 +477,8 @@
 
 source "drivers/net/wireless/hostap/Kconfig"
 
+source "drivers/net/wireless/tiacx/Kconfig"
+
 # yes, this works even when no drivers are selected
 config NET_WIRELESS
 	bool
diff -urN oldtree/drivers/net/wireless/Makefile newtree/drivers/net/wireless/Makefile
--- oldtree/drivers/net/wireless/Makefile	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/wireless/Makefile	2006-02-13 19:20:00.016509792 -0500
@@ -33,7 +33,7 @@
 obj-$(CONFIG_PCMCIA_ATMEL)      += atmel_cs.o
 
 obj-$(CONFIG_PRISM54)		+= prism54/
-
+obj-$(CONFIG_TIACX)		+= tiacx/
 obj-$(CONFIG_HOSTAP)		+= hostap/
 
 # 16-bit wireless PCMCIA client drivers
diff -urN oldtree/drivers/net/wireless/hostap/Kconfig newtree/drivers/net/wireless/hostap/Kconfig
--- oldtree/drivers/net/wireless/hostap/Kconfig	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/wireless/hostap/Kconfig	2006-02-13 19:20:29.723993568 -0500
@@ -61,7 +61,7 @@
 
 config HOSTAP_CS
 	tristate "Host AP driver for Prism2/2.5/3 PC Cards"
-	depends on PCMCIA!=n && HOSTAP
+	depends on PCMCIA && HOSTAP
 	---help---
 	Host AP driver's version for Prism2/2.5/3 PC Cards.
 
diff -urN oldtree/drivers/net/wireless/ipw2200.c newtree/drivers/net/wireless/ipw2200.c
--- oldtree/drivers/net/wireless/ipw2200.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/wireless/ipw2200.c	2006-02-13 19:20:00.020509184 -0500
@@ -1870,7 +1870,8 @@
 }
 
 #define HOST_COMPLETE_TIMEOUT HZ
-static int ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
+
+static int __ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
 {
 	int rc = 0;
 	unsigned long flags;
@@ -1899,7 +1900,7 @@
 		     priv->status);
 	printk_buf(IPW_DL_HOST_COMMAND, (u8 *) cmd->param, cmd->len);
 
-	rc = ipw_queue_tx_hcmd(priv, cmd->cmd, &cmd->param, cmd->len, 0);
+	rc = ipw_queue_tx_hcmd(priv, cmd->cmd, cmd->param, cmd->len, 0);
 	if (rc) {
 		priv->status &= ~STATUS_HCMD_ACTIVE;
 		IPW_ERROR("Failed to send %s: Reason %d\n",
@@ -1934,7 +1935,7 @@
 		goto exit;
 	}
 
-      exit:
+exit:
 	if (priv->cmdlog) {
 		priv->cmdlog[priv->cmdlog_pos++].retcode = rc;
 		priv->cmdlog_pos %= priv->cmdlog_len;
@@ -1942,61 +1943,62 @@
 	return rc;
 }
 
-static int ipw_send_host_complete(struct ipw_priv *priv)
+static int ipw_send_cmd_simple(struct ipw_priv *priv, u8 command)
+{
+	struct host_cmd cmd = {
+		.cmd = command,
+	};
+
+	return __ipw_send_cmd(priv, &cmd);
+}
+
+static int ipw_send_cmd_pdu(struct ipw_priv *priv, u8 command, u8 len,
+			    void *data)
 {
 	struct host_cmd cmd = {
-		.cmd = IPW_CMD_HOST_COMPLETE,
-		.len = 0
+		.cmd = command,
+		.len = len,
+		.param = data,
 	};
 
+	return __ipw_send_cmd(priv, &cmd);
+}
+
+static int ipw_send_host_complete(struct ipw_priv *priv)
+{
 	if (!priv) {
 		IPW_ERROR("Invalid args\n");
 		return -1;
 	}
 
-	return ipw_send_cmd(priv, &cmd);
+	return ipw_send_cmd_simple(priv, IPW_CMD_HOST_COMPLETE);
 }
 
 static int ipw_send_system_config(struct ipw_priv *priv,
 				  struct ipw_sys_config *config)
 {
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_SYSTEM_CONFIG,
-		.len = sizeof(*config)
-	};
-
 	if (!priv || !config) {
 		IPW_ERROR("Invalid args\n");
 		return -1;
 	}
 
-	memcpy(cmd.param, config, sizeof(*config));
-	return ipw_send_cmd(priv, &cmd);
+	return ipw_send_cmd_pdu(priv, IPW_CMD_SYSTEM_CONFIG, sizeof(*config),
+					config);
 }
 
 static int ipw_send_ssid(struct ipw_priv *priv, u8 * ssid, int len)
 {
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_SSID,
-		.len = min(len, IW_ESSID_MAX_SIZE)
-	};
-
 	if (!priv || !ssid) {
 		IPW_ERROR("Invalid args\n");
 		return -1;
 	}
 
-	memcpy(cmd.param, ssid, cmd.len);
-	return ipw_send_cmd(priv, &cmd);
+	return ipw_send_cmd_pdu(priv, IPW_CMD_SSID, min(len, IW_ESSID_MAX_SIZE),
+					ssid);
 }
 
 static int ipw_send_adapter_address(struct ipw_priv *priv, u8 * mac)
 {
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_ADAPTER_ADDRESS,
-		.len = ETH_ALEN
-	};
-
 	if (!priv || !mac) {
 		IPW_ERROR("Invalid args\n");
 		return -1;
@@ -2005,8 +2007,8 @@
 	IPW_DEBUG_INFO("%s: Setting MAC to " MAC_FMT "\n",
 		       priv->net_dev->name, MAC_ARG(mac));
 
-	memcpy(cmd.param, mac, ETH_ALEN);
-	return ipw_send_cmd(priv, &cmd);
+	return ipw_send_cmd_pdu(priv, IPW_CMD_ADAPTER_ADDRESS, ETH_ALEN,
+					mac);
 }
 
 /*
@@ -2065,51 +2067,40 @@
 static int ipw_send_scan_request_ext(struct ipw_priv *priv,
 				     struct ipw_scan_request_ext *request)
 {
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_SCAN_REQUEST_EXT,
-		.len = sizeof(*request)
-	};
-
-	memcpy(cmd.param, request, sizeof(*request));
-	return ipw_send_cmd(priv, &cmd);
+	return ipw_send_cmd_pdu(priv, IPW_CMD_SCAN_REQUEST_EXT,
+					sizeof(*request), request);
 }
 
 static int ipw_send_scan_abort(struct ipw_priv *priv)
 {
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_SCAN_ABORT,
-		.len = 0
-	};
-
 	if (!priv) {
 		IPW_ERROR("Invalid args\n");
 		return -1;
 	}
 
-	return ipw_send_cmd(priv, &cmd);
+	return ipw_send_cmd_simple(priv, IPW_CMD_SCAN_ABORT);
 }
 
 static int ipw_set_sensitivity(struct ipw_priv *priv, u16 sens)
 {
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_SENSITIVITY_CALIB,
-		.len = sizeof(struct ipw_sensitivity_calib)
+	struct ipw_sensitivity_calib calib = {
+		.beacon_rssi_raw = sens,
 	};
-	struct ipw_sensitivity_calib *calib = (struct ipw_sensitivity_calib *)
-	    &cmd.param;
-	calib->beacon_rssi_raw = sens;
-	return ipw_send_cmd(priv, &cmd);
+
+	return ipw_send_cmd_pdu(priv, IPW_CMD_SENSITIVITY_CALIB, sizeof(calib),
+					&calib);
 }
 
 static int ipw_send_associate(struct ipw_priv *priv,
 			      struct ipw_associate *associate)
 {
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_ASSOCIATE,
-		.len = sizeof(*associate)
-	};
-
 	struct ipw_associate tmp_associate;
+
+	if (!priv || !associate) {
+		IPW_ERROR("Invalid args\n");
+		return -1;
+	}
+
 	memcpy(&tmp_associate, associate, sizeof(*associate));
 	tmp_associate.policy_support =
 	    cpu_to_le16(tmp_associate.policy_support);
@@ -2122,80 +2113,56 @@
 	    cpu_to_le16(tmp_associate.beacon_interval);
 	tmp_associate.atim_window = cpu_to_le16(tmp_associate.atim_window);
 
-	if (!priv || !associate) {
-		IPW_ERROR("Invalid args\n");
-		return -1;
-	}
-
-	memcpy(cmd.param, &tmp_associate, sizeof(*associate));
-	return ipw_send_cmd(priv, &cmd);
+	return ipw_send_cmd_pdu(priv, IPW_CMD_ASSOCIATE, sizeof(tmp_associate),
+					&tmp_associate);
 }
 
 static int ipw_send_supported_rates(struct ipw_priv *priv,
 				    struct ipw_supported_rates *rates)
 {
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_SUPPORTED_RATES,
-		.len = sizeof(*rates)
-	};
-
 	if (!priv || !rates) {
 		IPW_ERROR("Invalid args\n");
 		return -1;
 	}
 
-	memcpy(cmd.param, rates, sizeof(*rates));
-	return ipw_send_cmd(priv, &cmd);
+	return ipw_send_cmd_pdu(priv, IPW_CMD_SUPPORTED_RATES, sizeof(*rates),
+					rates);
 }
 
 static int ipw_set_random_seed(struct ipw_priv *priv)
 {
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_SEED_NUMBER,
-		.len = sizeof(u32)
-	};
+	u32 val;
 
 	if (!priv) {
 		IPW_ERROR("Invalid args\n");
 		return -1;
 	}
 
-	get_random_bytes(&cmd.param, sizeof(u32));
+	get_random_bytes(&val, sizeof(val));
 
-	return ipw_send_cmd(priv, &cmd);
+	return ipw_send_cmd_pdu(priv, IPW_CMD_SEED_NUMBER, sizeof(val), &val);
 }
 
 static int ipw_send_card_disable(struct ipw_priv *priv, u32 phy_off)
 {
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_CARD_DISABLE,
-		.len = sizeof(u32)
-	};
-
 	if (!priv) {
 		IPW_ERROR("Invalid args\n");
 		return -1;
 	}
 
-	*((u32 *) & cmd.param) = phy_off;
-
-	return ipw_send_cmd(priv, &cmd);
+	return ipw_send_cmd_pdu(priv, IPW_CMD_CARD_DISABLE, sizeof(phy_off),
+					&phy_off);
 }
 
 static int ipw_send_tx_power(struct ipw_priv *priv, struct ipw_tx_power *power)
 {
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_TX_POWER,
-		.len = sizeof(*power)
-	};
-
 	if (!priv || !power) {
 		IPW_ERROR("Invalid args\n");
 		return -1;
 	}
 
-	memcpy(cmd.param, power, sizeof(*power));
-	return ipw_send_cmd(priv, &cmd);
+	return ipw_send_cmd_pdu(priv, IPW_CMD_TX_POWER, sizeof(*power),
+					power);
 }
 
 static int ipw_set_tx_power(struct ipw_priv *priv)
@@ -2247,18 +2214,14 @@
 	struct ipw_rts_threshold rts_threshold = {
 		.rts_threshold = rts,
 	};
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_RTS_THRESHOLD,
-		.len = sizeof(rts_threshold)
-	};
 
 	if (!priv) {
 		IPW_ERROR("Invalid args\n");
 		return -1;
 	}
 
-	memcpy(cmd.param, &rts_threshold, sizeof(rts_threshold));
-	return ipw_send_cmd(priv, &cmd);
+	return ipw_send_cmd_pdu(priv, IPW_CMD_RTS_THRESHOLD,
+				sizeof(rts_threshold), &rts_threshold);
 }
 
 static int ipw_send_frag_threshold(struct ipw_priv *priv, u16 frag)
@@ -2266,27 +2229,19 @@
 	struct ipw_frag_threshold frag_threshold = {
 		.frag_threshold = frag,
 	};
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_FRAG_THRESHOLD,
-		.len = sizeof(frag_threshold)
-	};
 
 	if (!priv) {
 		IPW_ERROR("Invalid args\n");
 		return -1;
 	}
 
-	memcpy(cmd.param, &frag_threshold, sizeof(frag_threshold));
-	return ipw_send_cmd(priv, &cmd);
+	return ipw_send_cmd_pdu(priv, IPW_CMD_FRAG_THRESHOLD,
+				sizeof(frag_threshold), &frag_threshold);
 }
 
 static int ipw_send_power_mode(struct ipw_priv *priv, u32 mode)
 {
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_POWER_MODE,
-		.len = sizeof(u32)
-	};
-	u32 *param = (u32 *) (&cmd.param);
+	u32 param;
 
 	if (!priv) {
 		IPW_ERROR("Invalid args\n");
@@ -2297,17 +2252,18 @@
 	 * level */
 	switch (mode) {
 	case IPW_POWER_BATTERY:
-		*param = IPW_POWER_INDEX_3;
+		param = IPW_POWER_INDEX_3;
 		break;
 	case IPW_POWER_AC:
-		*param = IPW_POWER_MODE_CAM;
+		param = IPW_POWER_MODE_CAM;
 		break;
 	default:
-		*param = mode;
+		param = mode;
 		break;
 	}
 
-	return ipw_send_cmd(priv, &cmd);
+	return ipw_send_cmd_pdu(priv, IPW_CMD_POWER_MODE, sizeof(param),
+					&param);
 }
 
 static int ipw_send_retry_limit(struct ipw_priv *priv, u8 slimit, u8 llimit)
@@ -2316,18 +2272,14 @@
 		.short_retry_limit = slimit,
 		.long_retry_limit = llimit
 	};
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_RETRY_LIMIT,
-		.len = sizeof(retry_limit)
-	};
 
 	if (!priv) {
 		IPW_ERROR("Invalid args\n");
 		return -1;
 	}
 
-	memcpy(cmd.param, &retry_limit, sizeof(retry_limit));
-	return ipw_send_cmd(priv, &cmd);
+	return ipw_send_cmd_pdu(priv, IPW_CMD_RETRY_LIMIT, sizeof(retry_limit),
+					&retry_limit);
 }
 
 /*
@@ -5672,54 +5624,44 @@
 
 static void ipw_send_tgi_tx_key(struct ipw_priv *priv, int type, int index)
 {
-	struct ipw_tgi_tx_key *key;
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_TGI_TX_KEY,
-		.len = sizeof(*key)
-	};
+	struct ipw_tgi_tx_key key;
 
 	if (!(priv->ieee->sec.flags & (1 << index)))
 		return;
 
-	key = (struct ipw_tgi_tx_key *)&cmd.param;
-	key->key_id = index;
-	memcpy(key->key, priv->ieee->sec.keys[index], SCM_TEMPORAL_KEY_LENGTH);
-	key->security_type = type;
-	key->station_index = 0;	/* always 0 for BSS */
-	key->flags = 0;
+	key.key_id = index;
+	memcpy(key.key, priv->ieee->sec.keys[index], SCM_TEMPORAL_KEY_LENGTH);
+	key.security_type = type;
+	key.station_index = 0;	/* always 0 for BSS */
+	key.flags = 0;
 	/* 0 for new key; previous value of counter (after fatal error) */
-	key->tx_counter[0] = 0;
-	key->tx_counter[1] = 0;
+	key.tx_counter[0] = 0;
+	key.tx_counter[1] = 0;
 
-	ipw_send_cmd(priv, &cmd);
+	ipw_send_cmd_pdu(priv, IPW_CMD_TGI_TX_KEY, sizeof(key), &key);
 }
 
 static void ipw_send_wep_keys(struct ipw_priv *priv, int type)
 {
-	struct ipw_wep_key *key;
+	struct ipw_wep_key key;
 	int i;
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_WEP_KEY,
-		.len = sizeof(*key)
-	};
 
-	key = (struct ipw_wep_key *)&cmd.param;
-	key->cmd_id = DINO_CMD_WEP_KEY;
-	key->seq_num = 0;
+	key.cmd_id = DINO_CMD_WEP_KEY;
+	key.seq_num = 0;
 
 	/* Note: AES keys cannot be set for multiple times.
 	 * Only set it at the first time. */
 	for (i = 0; i < 4; i++) {
-		key->key_index = i | type;
+		key.key_index = i | type;
 		if (!(priv->ieee->sec.flags & (1 << i))) {
-			key->key_size = 0;
+			key.key_size = 0;
 			continue;
 		}
 
-		key->key_size = priv->ieee->sec.key_sizes[i];
-		memcpy(key->key, priv->ieee->sec.keys[i], key->key_size);
+		key.key_size = priv->ieee->sec.key_sizes[i];
+		memcpy(key.key, priv->ieee->sec.keys[i], key.key_size);
 
-		ipw_send_cmd(priv, &cmd);
+		ipw_send_cmd_pdu(priv, IPW_CMD_WEP_KEY, sizeof(key), &key);
 	}
 }
 
@@ -6216,15 +6158,10 @@
 static int ipw_set_rsn_capa(struct ipw_priv *priv,
 			    char *capabilities, int length)
 {
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_RSN_CAPABILITIES,
-		.len = length,
-	};
-
 	IPW_DEBUG_HC("HOST_CMD_RSN_CAPABILITIES\n");
 
-	memcpy(cmd.param, capabilities, length);
-	return ipw_send_cmd(priv, &cmd);
+	return ipw_send_cmd_pdu(priv, IPW_CMD_RSN_CAPABILITIES, length,
+					capabilities);
 }
 
 /*
@@ -7011,25 +6948,15 @@
 static int ipw_send_qos_params_command(struct ipw_priv *priv, struct ieee80211_qos_parameters
 				       *qos_param)
 {
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_QOS_PARAMETERS,
-		.len = (sizeof(struct ieee80211_qos_parameters) * 3)
-	};
-
-	memcpy(cmd.param, qos_param, sizeof(*qos_param) * 3);
-	return ipw_send_cmd(priv, &cmd);
+	return ipw_send_cmd_pdu(priv, IPW_CMD_QOS_PARAMETERS, qos_param,
+					sizeof(*qos_param) * 3);
 }
 
 static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos_information_element
 				     *qos_param)
 {
-	struct host_cmd cmd = {
-		.cmd = IPW_CMD_WME_INFO,
-		.len = sizeof(*qos_param)
-	};
-
-	memcpy(cmd.param, qos_param, sizeof(*qos_param));
-	return ipw_send_cmd(priv, &cmd);
+	return ipw_send_cmd_pdu(priv, IPW_CMD_WME_INFO, qos_param,
+					sizeof(*qos_param));
 }
 
 #endif				/* CONFIG_IPW_QOS */
@@ -9649,11 +9576,6 @@
 	u16 remaining_bytes;
 	int fc;
 
-	/* If there isn't room in the queue, we return busy and let the
-	 * network stack requeue the packet for us */
-	if (ipw_queue_space(q) < q->high_mark)
-		return NETDEV_TX_BUSY;
-
 	switch (priv->ieee->iw_mode) {
 	case IW_MODE_ADHOC:
 		hdr_len = IEEE80211_3ADDR_LEN;
@@ -9871,7 +9793,7 @@
 
       fail_unlock:
 	spin_unlock_irqrestore(&priv->lock, flags);
-	return 1;
+	return -1;
 }
 
 static struct net_device_stats *ipw_net_get_stats(struct net_device *dev)
diff -urN oldtree/drivers/net/wireless/ipw2200.h newtree/drivers/net/wireless/ipw2200.h
--- oldtree/drivers/net/wireless/ipw2200.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/net/wireless/ipw2200.h	2006-02-13 19:20:00.022508880 -0500
@@ -1860,7 +1860,7 @@
 	u8 cmd;
 	u8 len;
 	u16 reserved;
-	u32 param[TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH];
+	u32 *param;
 } __attribute__ ((packed));
 
 struct ipw_cmd_log {
diff -urN oldtree/drivers/net/wireless/tiacx/Changelog newtree/drivers/net/wireless/tiacx/Changelog
--- oldtree/drivers/net/wireless/tiacx/Changelog	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/Changelog	2006-02-13 19:20:00.022508880 -0500
@@ -0,0 +1,136 @@
+[20050823]
+* driver submitted for kernel inclusion
+* modules and config entries got TI prefix added to their names
+* #defines renamed: CONFIG_ACX_{PCI,USB} -> ACX_{PCI,USB}
+  (needed for correct compilation of two modules from the same source)
+* debug module parameter renamed to acx_debug (name clash in
+  non-modular build)
+* ACX_DEBUG lowered to 1, PARANOID_LOCKING is off by default now
+* version bump to 0.3.0
+* sizes:
+   text    data     bss     dec     hex filename
+  78242     264       8   78514   132b2 drivers/net/wireless/tiacx/tiacx_pci.o
+  60622     208      20   60850    edb2 drivers/net/wireless/tiacx/tiacx_usb.o
+
+[20050822]
+* idma.c is incorporated into helper.c
+* lots of cosmetic work: comments, struct member shuffling,
+  etc. TIWLAN_DC and other PCI data structs are #ifdef'ed
+  to be PCI-only. A few todos added. Extra #include's removed.
+* added sem locking debug printout code (for amd64 debug)
+* beacon tx rate and beacon ratevector is updated according to
+  "iwconfig rate" command immediately (required mode change before).
+
+[20050821]
+* Switch to wrapper functions for dealing with "tx buffers"
+  (memory areas where we create packets for tx. For USB, it's just
+  a part of wlandevice_t, for PCI it's a DMAable buffer pointed to
+  by txhostdesc).
+* Completely hide nature of PCI txdescs under opaque pointer type tx_t*
+* acx_l_ether_to_txdesc -> acx_l_ether_to_txbuf, not using knowledge
+  of PCI txdesc anymore.
+* Massive surgery on usb.c, cutting all paths into PCI code.
+* PCI code moved to pci.c - USB don't need these pieces anymore
+* acx111 and acx100 txhostdesc creation unified into single function
+
+[20050816]
+* PCI code switched to single-txdesc scheme
+* version bump to 0.2.5
+
+[20050815]
+* dev_kfree_skb -> dev_kfree_skb_any
+
+[20050814]
+* Auto rate was reset to lowest rate by scanning code
+  (AP beacons did it every tenth of a second!). Fixed.
+* USB rx is no longer uses PCI-style rx descriptor ring.
+  tx ring elimination needs 'single-descriptor' setup
+  to be developed for acx100 (patch exists for acx111).
+* Very strange sem locking problems are reported on amd64.
+  Code which misbehaves looks fine. I do not know what's going on.
+  Workaround: turn off preemption.
+
+[20050812]
+* acx100 was failing to find out radio module #,
+  and wanted to load radio module 00. Must be fixed now.
+* USB: more simplifications
+
+[20050810]
+* USB: simplified command submission code, removed some
+  wlandevice_t fields (now unused)
+
+[20050808]
+* USB changes: nuked global statics, simplified issue_cmd,
+  shortened wlandevice_t. Added some TODOs :)
+
+[20050807]
+* restart scan if AP is not found
+* remove use_eth_name parameter. It was deprecated
+* disable legacy firmware loader for 2.6. Driver printed a warning
+  about need to switch to hotplug.
+* WARNING: new names for firmware images!
+  Driver will try to load the following images:
+  PCI driver:
+    'tiacxNNNcMM' (NNN=100/111, MM=radio module ID (in uppercase hex)):
+    combined firmware for specified chipset and radio.
+    failing that, it will try to load images named
+    'tiacxNNN' (NNN=100/111): main firmware for specified chipset
+    and 'tiacxNNNrMM': corresponding radio module.
+    For example, my firmware is in file named 'tiacx111c16'.
+    Alternatively, I may remove it and use pair of files
+    'tiacx111' and 'tiacx111r16' instead.
+  USB driver: loads image named 'tiacx100usb'
+  Hint: you can keep both old and new image names (via symlinks
+  or just by copying files) if you need to run older driver.
+* fix "Debug: sleeping function called from invalid context..." for USB
+* ACX_{PCI,USB} -> CONFIG_ACX_{PCI,USB} in preparation to 2.6 merge
+* version bump to 0.2.4
+
+[20050804]
+* 'Fixed' deadlock on flush_scheduled_work (need a better fix eventually)
+* We didn't completely disable IRQs on ifdown -> "Disabling IRQ#NN"
+  on second modprobe. Fixed.
+
+[20050802]
+* removed some // comments in order to please Andreas
+* moved a field to PCI-only part of wlandevice_t
+* Random Version Bump (tm) to 0.2.3
+* no code changes
+
+[20050730]
+* Basically just incorporating acx-20050722.acx100fixed.patch
+  This is a bit of a backward step, because instead of figuring out
+  why active scanning doesn't work for acx100, we just disable it.
+  acx100 owners encouraged to try to make it work
+
+[20050729]
+* Added some IE IDs
+
+[20050726]
+* added probe request config to acx100_s_init_packet_templates,
+  maybe acx100 is working now
+
+[20050721]
+* del_timer_sync() sem lockup on SMP maybe fixed
+
+[20050720]
+* lots of amd64 warnings fixed, thanks to Matan Peled <chaosite@gmail.com>
+
+[20050710]
+* {tx,rx}hostdesc->desc_phy removed (was not used)
+* netdev->type of ARPHRD_IEEE80211_PRISM was sticking forever
+  after monitor mode. Fixed
+* monitor mode tx is working again
+* rate reporting added in monitor mode packet header
+
+[20050709]
+* moved PCI specific ioctls to pci.c
+* ioctl.c is PCI/USB independent now
+
+[20050708]
+* Fixed one apparent bug (wlandevice_t had different
+  layout for PCI and USB - conv.c would die horribly)
+* Massive code shuffling with only trivial code changes.
+  Mostly sorting out PCI/USB stuff into relevant files.
+* ihw.c eliminated
+* helper2.c is PCI/USB independent now
diff -urN oldtree/drivers/net/wireless/tiacx/Kconfig newtree/drivers/net/wireless/tiacx/Kconfig
--- oldtree/drivers/net/wireless/tiacx/Kconfig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/Kconfig	2006-02-13 19:20:00.022508880 -0500
@@ -0,0 +1,59 @@
+config TIACX
+	bool "TI acx100/acx111 802.11b/g wireless chipsets"
+	depends on NET_RADIO && EXPERIMENTAL
+	---help---
+	A driver for 802.11b/g wireless cards based on
+	Texas Instruments acx100 and acx111 chipsets.
+
+	This driver supports Host AP mode that allows
+	your computer to act as an IEEE 802.11 access point.
+	This driver is quite new and experimental.
+
+	These chipsets need their firmware loaded at startup.
+	You will need to provide a firmware image via hotplug.
+
+	Firmware may be in a form of single image 40-100kb in size
+	(a 'combined' firmware) or two images - main image
+	(again 40-100kb) and radio image (~10kb or less).
+
+	Firmware images are requested from hotplug using following names:
+
+	tiacx100 - main firmware image for acx100 chipset
+	tiacx100rNN - radio acx100 firmware for radio type NN
+	tiacx100cNN - combined acx100 firmware for radio type NN
+	tiacx111 - main acx111 firmware
+	tiacx111rNN - radio acx111 firmware for radio type NN
+	tiacx111cNN - combined acx111 firmware for radio type NN
+
+	Driver will attempt to load combined image first.
+	If no such image is found, it will try to load main image
+	and radio image instead.
+
+	Firmware files are not covered by GPL and are not distributed
+	with this driver for legal reasons.
+
+	Texas Instruments did not take part in development of this driver
+	in any way, shape or form.
+
+config TIACX_PCI
+	tristate "TI acx100/acx111 802.11b/g PCI"
+	depends on PCI && TIACX && FW_LOADER
+	---help---
+	A driver for PCI and CardBus incarnations of acx100 and acx111.
+
+	This driver is quite new and experimental.
+
+	The driver can be compiled as a module and will be named "tiacx_pci".
+
+config TIACX_USB
+	tristate "TI acx100/acx111 802.11b/g USB"
+	depends on USB && TIACX && FW_LOADER
+	---help---
+	A driver for USB incarnation of acx100 and acx111.
+
+	There is only one currently known device in this category,
+	D-Link DWL-120+, but newer devices seem to be on the horizon.
+
+	This driver is quite new and experimental.
+
+	The driver can be compiled as a module and will be named "tiacx_usb".
diff -urN oldtree/drivers/net/wireless/tiacx/Makefile newtree/drivers/net/wireless/tiacx/Makefile
--- oldtree/drivers/net/wireless/tiacx/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/Makefile	2006-02-13 19:20:00.023508728 -0500
@@ -0,0 +1,6 @@
+tiacx_pci-objs := wlan.o conv.o helper2.o ioctl.o pci.o pci_helper.o
+
+tiacx_usb-objs := wlan.o conv.o helper2.o ioctl.o usb.o usb_helper.o
+
+obj-$(CONFIG_TIACX_PCI) += tiacx_pci.o
+obj-$(CONFIG_TIACX_USB) += tiacx_usb.o
diff -urN oldtree/drivers/net/wireless/tiacx/acx.h newtree/drivers/net/wireless/tiacx/acx.h
--- oldtree/drivers/net/wireless/tiacx/acx.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/acx.h	2006-02-13 19:20:00.023508728 -0500
@@ -0,0 +1,6 @@
+#include "acx_config.h"
+#include "wlan_compat.h"
+#include "wlan_hdr.h"
+#include "wlan_mgmt.h"
+#include "acx_struct.h"
+#include "acx_func.h"
diff -urN oldtree/drivers/net/wireless/tiacx/acx_config.h newtree/drivers/net/wireless/tiacx/acx_config.h
--- oldtree/drivers/net/wireless/tiacx/acx_config.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/acx_config.h	2006-02-13 19:20:00.023508728 -0500
@@ -0,0 +1,40 @@
+#define WLAN_RELEASE "v0.3.0"
+
+/* set to 0 if you don't want any debugging code to be compiled in */
+/* set to 1 if you want some debugging */
+/* set to 2 if you want extensive debug log */
+#define ACX_DEBUG 1
+
+/* assume 32bit I/O width
+ * (16bit is also compatible with Compact Flash) */
+#define ACX_IO_WIDTH 32
+
+/* Set this to 1 if you want monitor mode to use
+ * phy header. Currently it is not useful anyway since we
+ * don't know what useful info (if any) is in phy header.
+ * If you want faster/smaller code, say 0 here */
+#define WANT_PHY_HDR 0
+
+/* whether to do Tx descriptor cleanup in softirq (i.e. not in IRQ
+ * handler) or not. Note that doing it later does slightly increase
+ * system load, so still do that stuff in the IRQ handler for now,
+ * even if that probably means worse latency */
+#define TX_CLEANUP_IN_SOFTIRQ 0
+
+/* set to 1 if you want to have 1 driver per card instead of 1 single driver
+ * managing all cards (of a particular bus type) in your system
+ * Useful e.g. if you need to reinitialize single cards from time to time
+ * LINUX 2.4.X ONLY!! (pci_for_each_dev()) Feel free to implement 2.6.x
+ * compatibility... */
+#define SEPARATE_DRIVER_INSTANCES 0
+
+/* Undefine if you want out-of-line acx_r/w_regNN,
+ * define to "static inline" if you want them inlined */
+#define INLINE_IO static inline
+
+/* Locking: */
+/* very talkative */
+/* #define PARANOID_LOCKING 1 */
+/* normal (use when bug-free) */
+#define DO_LOCKING 1
+/* else locking is disabled! */
diff -urN oldtree/drivers/net/wireless/tiacx/acx_func.h newtree/drivers/net/wireless/tiacx/acx_func.h
--- oldtree/drivers/net/wireless/tiacx/acx_func.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/acx_func.h	2006-02-13 19:20:00.024508576 -0500
@@ -0,0 +1,603 @@
+/***********************************************************************
+** Copyright (C) 2003  ACX100 Open Source Project
+**
+** The contents of this file are subject to the Mozilla Public
+** License Version 1.1 (the "License"); you may not use this file
+** except in compliance with the License. You may obtain a copy of
+** the License at http://www.mozilla.org/MPL/
+**
+** Software distributed under the License is distributed on an "AS
+** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+** implied. See the License for the specific language governing
+** rights and limitations under the License.
+**
+** Alternatively, the contents of this file may be used under the
+** terms of the GNU Public License version 2 (the "GPL"), in which
+** case the provisions of the GPL are applicable instead of the
+** above.  If you wish to allow the use of your version of this file
+** only under the terms of the GPL and not to allow others to use
+** your version of this file under the MPL, indicate your decision
+** by deleting the provisions above and replace them with the notice
+** and other provisions required by the GPL.  If you do not delete
+** the provisions above, a recipient may use your version of this
+** file under either the MPL or the GPL.
+** ---------------------------------------------------------------------
+** Inquiries regarding the ACX100 Open Source Project can be
+** made directly to:
+**
+** acx100-users@lists.sf.net
+** http://acx100.sf.net
+** ---------------------------------------------------------------------
+*/
+
+
+/***********************************************************************
+** LOGGING
+**
+** - Avoid SHOUTING needlessly. Avoid excessive verbosity.
+**   Gradually remove messages which are old debugging aids.
+**
+** - Use printk() for messages which are to be always logged.
+**   Supply either 'acx:' or '<devname>:' prefix so that user
+**   can figure out who's speaking among other kernel chatter.
+**   acx: is for general issues (e.g. "acx: no firmware image!")
+**   while <devname>: is related to a particular device
+**   (think about multi-card setup). Double check that message
+**   is not confusing to the average user.
+**
+** - use printk KERN_xxx level only if message is not a WARNING
+**   but is INFO, ERR etc.
+**
+** - Use printk_ratelimited() for messages which may flood
+**   (e.g. "rx DUP pkt!").
+**
+** - Use acxlog() for messages which may be omitted (and they
+**   _will_ be omitted in non-debug builds). Note that
+**   message levels may be disabled at compile-time selectively,
+**   thus select them wisely. Example: L_DEBUG is the lowest
+**   (most likely to be compiled out) -> use for less important stuff.
+**
+** - Do not print important stuff with acxlog(), or else people
+**   will never build non-debug driver.
+**
+** Style:
+** hex: capital letters, zero filled (e.g. 0x02AC)
+** str: dont start from capitals, no trailing periods ("tx: queue is stopped")
+*/
+#if ACX_DEBUG > 1
+
+void log_fn_enter(const char *funcname);
+void log_fn_exit(const char *funcname);
+void log_fn_exit_v(const char *funcname, int v);
+
+#define FN_ENTER \
+	do { \
+		if (unlikely(acx_debug & L_FUNC)) { \
+			log_fn_enter(__func__); \
+		} \
+	} while (0)
+
+#define FN_EXIT1(v) \
+	do { \
+		if (unlikely(acx_debug & L_FUNC)) { \
+			log_fn_exit_v(__func__, v); \
+		} \
+	} while (0)
+#define FN_EXIT0 \
+	do { \
+		if (unlikely(acx_debug & L_FUNC)) { \
+			log_fn_exit(__func__); \
+		} \
+	} while (0)
+
+#else
+
+#define FN_ENTER
+#define FN_EXIT1(v)
+#define FN_EXIT0
+
+#endif /* ACX_DEBUG > 1 */
+
+
+#if ACX_DEBUG
+
+#define acxlog(chan, args...) \
+	do { \
+		if (acx_debug & (chan)) \
+			printk(args); \
+	} while (0)
+#define printk_ratelimited(args...) printk(args)
+
+#else /* Non-debug build: */
+
+#define acxlog(chan, args...)
+/* Standard way of log flood prevention */
+#define printk_ratelimited(args...) \
+do { \
+	if (printk_ratelimit()) \
+		printk(args); \
+} while (0)
+
+#endif /* ACX_DEBUG */
+
+
+/* MAC logging code is big (relatively) */
+void acx_print_mac(const char *head, const u8 *mac, const char *tail);
+
+/* Optimized out to nothing in non-debug build */
+static inline void
+acxlog_mac(int level, const char *head, const u8 *mac, const char *tail)
+{
+	if (acx_debug & level) {
+		acx_print_mac(head, mac, tail);
+	}
+}
+
+
+/***********************************************************************
+** Low-level io routines
+*/
+#include "acx_inline.h"
+
+
+/***********************************************************************
+** MAC address helpers
+*/
+static inline void
+MAC_COPY(u8 *mac, const u8 *src)
+{
+	*(u32*)mac = *(u32*)src;
+	((u16*)mac)[2] = ((u16*)src)[2];
+	/* kernel's memcpy will do the same: memcpy(dst, src, ETH_ALEN); */
+}
+
+static inline void
+MAC_FILL(u8 *mac, u8 val)
+{
+	memset(mac, val, ETH_ALEN);
+}
+
+static inline void
+MAC_BCAST(u8 *mac)
+{
+	((u16*)mac)[2] = *(u32*)mac = -1;
+}
+
+static inline void
+MAC_ZERO(u8 *mac)
+{
+	((u16*)mac)[2] = *(u32*)mac = 0;
+}
+
+static inline int
+mac_is_equal(const u8 *a, const u8 *b)
+{
+	/* can't beat this */
+	return memcmp(a, b, ETH_ALEN) == 0;
+}
+
+static inline int
+mac_is_bcast(const u8 *mac)
+{
+	/* AND together 4 first bytes with sign-entended 2 last bytes
+	** Only bcast address gives 0xffffffff. +1 gives 0 */
+	return ( *(s32*)mac & ((s16*)mac)[2] ) + 1 == 0;
+}
+
+static inline int
+mac_is_zero(const u8 *mac)
+{
+	return ( *(u32*)mac | ((u16*)mac)[2] ) == 0;
+}
+
+static inline int
+mac_is_directed(const u8 *mac)
+{
+	return (mac[0] & 1)==0;
+}
+
+static inline int
+mac_is_mcast(const u8 *mac)
+{
+	return (mac[0] & 1) && !mac_is_bcast(mac);
+}
+
+#define MACSTR "%02X:%02X:%02X:%02X:%02X:%02X"
+#define MAC(bytevector) \
+	((unsigned char *)bytevector)[0], \
+	((unsigned char *)bytevector)[1], \
+	((unsigned char *)bytevector)[2], \
+	((unsigned char *)bytevector)[3], \
+	((unsigned char *)bytevector)[4], \
+	((unsigned char *)bytevector)[5]
+
+
+/***********************************************************************
+** Random helpers
+*/
+#define TO_STRING(x)	#x
+#define STRING(x)	TO_STRING(x)
+
+#define CLEAR_BIT(val, mask) ((val) &= ~(mask))
+#define SET_BIT(val, mask) ((val) |= (mask))
+
+/* undefined if v==0 */
+static inline unsigned int
+lowest_bit(u16 v)
+{
+	unsigned int n = 0;
+	while (!(v & 0xf)) { v>>=4; n+=4; }
+	while (!(v & 1)) { v>>=1; n++; }
+	return n;
+}
+
+/* undefined if v==0 */
+static inline unsigned int
+highest_bit(u16 v)
+{
+	unsigned int n = 0;
+	while (v>0xf) { v>>=4; n+=4; }
+	while (v>1) { v>>=1; n++; }
+	return n;
+}
+
+/* undefined if v==0 */
+static inline int
+has_only_one_bit(u16 v)
+{
+	return ((v-1) ^ v) >= v;
+}
+
+
+/***********************************************************************
+** LOCKING
+** We have priv->sem and priv->lock.
+**
+** We employ following naming convention in order to get locking right:
+**
+** acx_e_xxxx - external entry points called from process context.
+**	It is okay to sleep. priv->sem is to be taken on entry.
+** acx_i_xxxx - external entry points possibly called from atomic context.
+**	Sleeping is not allowed (and thus down(sem) is not legal!)
+** acx_s_xxxx - potentially sleeping functions. Do not ever call under lock!
+** acx_l_xxxx - functions which expect lock to be already taken.
+** rest       - non-sleeping functions which do not require locking
+**	but may be run inder lock
+**
+** Theory of operation:
+**
+** All process-context entry points (_e_ functions) take sem
+** immediately. IRQ handler and other 'atomic-context' entry points
+** (_i_ functions) take lock immediately on entry, but dont take sem
+** because that might sleep.
+**
+** Thus *all* code is either protected by sem or lock, or both.
+**
+** Code which must not run concurrently with IRQ takes lock
+** (since sem is already taken it runs under sem+lock then).
+** Such code is marked with _l_.
+**
+** This results in the following rules of thumb useful in code review:
+**
+** + If a function calls _s_ fn, it must be an _s_ itself.
+** + You can call _l_ fn only (a) from another _l_ fn
+**   or (b) from _s_, _e_ or _i_ fn by taking lock, calling _l_,
+**   and dropping lock.
+** + All IRQ code runs under lock.
+** + Any _s_ fn is running under sem.
+** + Code under sem can race only with IRQ code.
+** + Code under sem+lock cannot race with anything.
+*/
+
+/* These functions *must* be inline or they will break horribly on SPARC, due
+ * to its weird semantics for save/restore flags. extern inline should prevent
+ * the kernel from linking or module from loading if they are not inlined. */
+
+#if defined(PARANOID_LOCKING) /* Lock debugging */
+
+void acx_lock_debug(wlandevice_t *priv, const char* where);
+void acx_unlock_debug(wlandevice_t *priv, const char* where);
+void acx_down_debug(wlandevice_t *priv, const char* where);
+void acx_up_debug(wlandevice_t *priv, const char* where);
+void acx_lock_unhold(void);
+void acx_sem_unhold(void);
+
+extern inline void
+acx_lock_helper(wlandevice_t *priv, unsigned long *fp, const char* where)
+{
+	acx_lock_debug(priv, where);
+	spin_lock_irqsave(&priv->lock, *fp);
+}
+extern inline void
+acx_unlock_helper(wlandevice_t *priv, unsigned long *fp, const char* where)
+{
+	acx_unlock_debug(priv, where);
+	spin_unlock_irqrestore(&priv->lock, *fp);
+}
+extern inline void
+acx_down_helper(wlandevice_t *priv, const char* where)
+{
+	acx_down_debug(priv, where);
+}
+extern inline void
+acx_up_helper(wlandevice_t *priv, const char* where)
+{
+	acx_up_debug(priv, where);
+}
+#define acx_lock(priv, flags)	acx_lock_helper(priv, &(flags), __FILE__ ":" STRING(__LINE__))
+#define acx_unlock(priv, flags)	acx_unlock_helper(priv, &(flags), __FILE__ ":" STRING(__LINE__))
+#define acx_sem_lock(priv)	acx_down_helper(priv, __FILE__ ":" STRING(__LINE__))
+#define acx_sem_unlock(priv)	acx_up_helper(priv, __FILE__ ":" STRING(__LINE__))
+
+#elif defined(DO_LOCKING)
+
+#define acx_lock(priv, flags)	spin_lock_irqsave(&priv->lock, flags)
+#define acx_unlock(priv, flags)	spin_unlock_irqrestore(&priv->lock, flags)
+#define acx_sem_lock(priv)	down(&priv->sem)
+#define acx_sem_unlock(priv)	up(&priv->sem)
+#define acx_lock_unhold()	((void)0)
+#define acx_sem_unhold()	((void)0)
+
+#else /* no locking! :( */
+
+#define acx_lock(priv, flags)	((void)0)
+#define acx_unlock(priv, flags)	((void)0)
+#define acx_sem_lock(priv)	((void)0)
+#define acx_sem_unlock(priv)	((void)0)
+#define acx_lock_unhold()	((void)0)
+#define acx_sem_unhold()	((void)0)
+
+#endif
+
+
+/***********************************************************************
+** transitional define (before we go towards a real netdev_priv() layout)
+** DON'T erroneously use a netdev_priv() instead - it's different for now!
+**
+** BTW, the new netdev_priv() is available in >= 2.4.27, >= 2.6.3
+*/
+#define acx_netdev_priv(dev) (void *)((dev)->priv)
+
+static inline void
+acx_stop_queue(netdevice_t *dev, const char *msg)
+{
+	netif_stop_queue(dev);
+	if (msg)
+		acxlog(L_BUFT, "tx: stop queue %s\n", msg);
+}
+
+static inline int
+acx_queue_stopped(netdevice_t *dev)
+{
+	return netif_queue_stopped(dev);
+}
+
+static inline void
+acx_start_queue(netdevice_t *dev, const char *msg)
+{
+	netif_start_queue(dev);
+	if (msg)
+		acxlog(L_BUFT, "tx: start queue %s\n", msg);
+}
+
+static inline void
+acx_wake_queue(netdevice_t *dev, const char *msg)
+{
+	netif_wake_queue(dev);
+	if (msg)
+		acxlog(L_BUFT, "tx: wake queue %s\n", msg);
+}
+
+static inline void
+acx_carrier_off(netdevice_t *dev, const char *msg)
+{
+	netif_carrier_off(dev);
+	if (msg)
+		acxlog(L_BUFT, "tx: carrier off %s\n", msg);
+}
+
+static inline void
+acx_carrier_on(netdevice_t *dev, const char *msg)
+{
+	netif_carrier_on(dev);
+	if (msg)
+		acxlog(L_BUFT, "tx: carrier on %s\n", msg);
+}
+
+
+/***********************************************************************
+** Communication with firmware
+*/
+/* in 1/100 of ms */
+#define CMD_TIMEOUT_MS(n)	((n)*100)
+#define ACX_CMD_TIMEOUT_DEFAULT	CMD_TIMEOUT_MS(50)
+
+#if ACX_DEBUG
+
+/* We want to log cmd names */
+
+int acx_s_issue_cmd_timeo_debug(wlandevice_t *priv, unsigned cmd,
+		void *pcmdparam, unsigned paramlen, unsigned timeout,
+		const char* cmdstr);
+#define acx_s_issue_cmd(priv,cmd,param,len) \
+	acx_s_issue_cmd_timeo_debug(priv,cmd,param,len, \
+				ACX_CMD_TIMEOUT_DEFAULT,#cmd)
+#define acx_s_issue_cmd_timeo(priv,cmd,param,len,timeo) \
+	acx_s_issue_cmd_timeo_debug(priv,cmd,param,len, \
+				timeo,#cmd)
+int acx_s_configure_debug(wlandevice_t *priv, void *pdr,
+		int type, const char* str);
+#define acx_s_configure(priv,pdr,type) \
+	acx_s_configure_debug(priv,pdr,type,#type)
+int acx_s_interrogate_debug(wlandevice_t *priv, void *pdr,
+		int type, const char* str);
+#define acx_s_interrogate(priv,pdr,type) \
+	acx_s_interrogate_debug(priv,pdr,type,#type)
+
+#else
+
+int acx_s_issue_cmd_timeo(wlandevice_t *priv, unsigned cmd,
+		void *pcmdparam, unsigned paramlen, unsigned timeout);
+static inline int
+acx_s_issue_cmd(wlandevice_t *priv, unsigned cmd, void *param, unsigned len)
+{
+	return acx_s_issue_cmd_timeo(priv, cmd, param, len,
+			ACX_CMD_TIMEOUT_DEFAULT);
+}
+int acx_s_configure(wlandevice_t *priv, void *pdr, int type);
+int acx_s_interrogate(wlandevice_t *priv, void *pdr, int type);
+
+#endif
+
+void acx_s_cmd_join_bssid(wlandevice_t *priv, const u8 *bssid);
+void acx_s_cmd_start_scan(wlandevice_t *priv);
+int acx111_s_get_feature_config(wlandevice_t *priv,
+		u32 *feature_options, u32 *data_flow_options);
+int acx111_s_set_feature_config(wlandevice_t *priv,
+		u32 feature_options, u32 data_flow_options,
+		unsigned int mode /* 0 == remove, 1 == add, 2 == set */);
+static inline int
+acx111_s_feature_off(wlandevice_t *priv, u32 f, u32 d)
+{
+	return acx111_s_set_feature_config(priv, f, d, 0);
+}
+static inline int
+acx111_s_feature_on(wlandevice_t *priv, u32 f, u32 d)
+{
+	return acx111_s_set_feature_config(priv, f, d, 1);
+}
+static inline int
+acx111_s_feature_set(wlandevice_t *priv, u32 f, u32 d)
+{
+	return acx111_s_set_feature_config(priv, f, d, 2);
+}
+
+
+/***********************************************************************
+** Ioctls
+*/
+int acx_ioctl_dbg_get_io(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra);
+int acx_ioctl_dbg_set_io(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra);
+int acx111_ioctl_info(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra);
+int acx100_ioctl_set_phy_amp_bias(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra);
+
+
+/***********************************************************************
+** Unsorted yet :)
+*/
+void acx_s_msleep(int ms);
+int acx_s_init_mac(netdevice_t *dev);
+void acx_set_reg_domain(wlandevice_t *priv, unsigned char reg_dom_id);
+void acx_set_timer(wlandevice_t *priv, u32 time);
+void acx_update_capabilities(wlandevice_t *priv);
+int acx_read_eeprom_offset(wlandevice_t *priv, u32 addr, u8 *charbuf);
+int acx_s_read_phy_reg(wlandevice_t *priv, u32 reg, u8 *charbuf);
+int acx_s_write_phy_reg(wlandevice_t *priv, u32 reg, u8 value);
+void acx_s_start(wlandevice_t *priv);
+#if USE_FW_LOADER_26
+firmware_image_t *acx_s_read_fw(struct device *dev, const char *file, u32 *size);
+#else
+firmware_image_t *acx_s_read_fw(const char *file, u32 *size);
+#define acx_s_read_fw(dev, file, size) acx_s_read_fw(file, size)
+#endif
+void acx_s_initialize_rx_config(wlandevice_t *priv);
+void acx_s_update_card_settings(wlandevice_t *priv, int get_all, int set_all);
+void acx_init_task_scheduler(wlandevice_t *priv);
+void acx_schedule_after_interrupt_task(wlandevice_t *priv, unsigned int set_flag);
+int acx_s_upload_radio(wlandevice_t *priv);
+void acx_read_configoption(wlandevice_t *priv);
+int acx_proc_register_entries(const struct net_device *dev);
+int acx_proc_unregister_entries(const struct net_device *dev);
+void acx_l_update_ratevector(wlandevice_t *priv);
+
+int acx_s_recalib_radio(wlandevice_t *priv);
+int acx_e_ioctl_old(netdevice_t *dev, struct ifreq *ifr, int cmd);
+
+void acx_l_sta_list_init(wlandevice_t *priv);
+client_t *acx_l_sta_list_get(wlandevice_t *priv, const u8 *address);
+void acx_l_sta_list_del(wlandevice_t *priv, client_t *clt);
+
+void acx_set_status(wlandevice_t *priv, u16 status);
+int acx_l_rx_ieee802_11_frame(wlandevice_t *priv, rxbuffer_t *rxbuf);
+int acx_l_transmit_disassoc(wlandevice_t *priv, client_t *clt);
+void acx_i_timer(unsigned long a);
+int acx_s_complete_scan(wlandevice_t *priv);
+
+const char* acx_get_status_name(u16 status);
+
+static inline wlan_hdr_t*
+acx_get_wlan_hdr(wlandevice_t *priv, const rxbuffer_t *rxbuf)
+{
+	if (!(priv->rx_config_1 & RX_CFG1_INCLUDE_PHY_HDR))
+		return (wlan_hdr_t*)&rxbuf->hdr_a3;
+
+	/* take into account phy header in front of packet */
+	if (CHIPTYPE_ACX111 == priv->chip_type)
+		return (wlan_hdr_t*)((u8*)&rxbuf->hdr_a3 + 8);
+
+	return (wlan_hdr_t*)((u8*)&rxbuf->hdr_a3 + 4);
+}
+
+struct sk_buff *acx_rxbuf_to_ether(struct wlandevice *priv, rxbuffer_t *rxbuf);
+
+void acx_l_power_led(wlandevice_t *priv, int enable);
+
+int acx100_s_create_dma_regions(wlandevice_t *priv);
+int acx111_s_create_dma_regions(wlandevice_t *priv);
+unsigned int acx_l_clean_tx_desc(wlandevice_t *priv);
+void acx_l_clean_tx_desc_emergency(wlandevice_t *priv);
+
+u8 acx_signal_to_winlevel(u8 rawlevel);
+u8 acx_signal_determine_quality(u8 signal, u8 noise);
+
+void acx_l_process_rxbuf(wlandevice_t *priv, rxbuffer_t *rxbuf);
+void acx_l_process_rx_desc(wlandevice_t *priv);
+
+tx_t* acx_l_alloc_tx(wlandevice_t *priv);
+void* acx_l_get_txbuf(tx_t *tx_opaque);
+/* Misnamed. submit_prepared_tx() */
+void acx_l_dma_tx_data(wlandevice_t *priv, tx_t *tx_opaque, int len);
+
+void acx_dump_bytes(const void *, int);
+void acx_log_bad_eid(wlan_hdr_t* hdr, int len, wlan_ie_t* ie_ptr);
+
+u8 acx_rate111to100(u16);
+void acx_l_rx(wlandevice_t *priv, rxbuffer_t *rxbuf);
+
+void acx100usb_l_tx_data(wlandevice_t *priv, struct txdesc *desc);
+int acx_s_set_defaults(wlandevice_t *priv);
+void acx_init_mboxes(wlandevice_t *priv);
+
+int acx_l_ether_to_txbuf(wlandevice_t *priv, void *txbuf, const struct sk_buff *skb);
+
+#if !ACX_DEBUG
+static inline const char* acx_get_packet_type_string(u16 fc) { return ""; }
+#else
+const char* acx_get_packet_type_string(u16 fc);
+#endif
+
+int acx_i_start_xmit(struct sk_buff *skb, netdevice_t *dev);
+void acx_free_desc_queues(TIWLAN_DC *pDc);
+
+#ifdef ACX_PCI
+int acx_s_create_tx_host_desc_queue(TIWLAN_DC *pDc);
+int acx_s_create_rx_host_desc_queue(TIWLAN_DC *pDc);
+void acx_s_create_tx_desc_queue(TIWLAN_DC *pDc);
+void acx_create_rx_desc_queue(TIWLAN_DC *pDc);
+#endif
diff -urN oldtree/drivers/net/wireless/tiacx/acx_inline.h newtree/drivers/net/wireless/tiacx/acx_inline.h
--- oldtree/drivers/net/wireless/tiacx/acx_inline.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/acx_inline.h	2006-02-13 19:20:00.024508576 -0500
@@ -0,0 +1,119 @@
+/***********************************************************************
+** Copyright (C) 2003  ACX100 Open Source Project
+**
+** The contents of this file are subject to the Mozilla Public
+** License Version 1.1 (the "License"); you may not use this file
+** except in compliance with the License. You may obtain a copy of
+** the License at http://www.mozilla.org/MPL/
+**
+** Software distributed under the License is distributed on an "AS
+** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+** implied. See the License for the specific language governing
+** rights and limitations under the License.
+**
+** Alternatively, the contents of this file may be used under the
+** terms of the GNU Public License version 2 (the "GPL"), in which
+** case the provisions of the GPL are applicable instead of the
+** above.  If you wish to allow the use of your version of this file
+** only under the terms of the GPL and not to allow others to use
+** your version of this file under the MPL, indicate your decision
+** by deleting the provisions above and replace them with the notice
+** and other provisions required by the GPL.  If you do not delete
+** the provisions above, a recipient may use your version of this
+** file under either the MPL or the GPL.
+** ---------------------------------------------------------------------
+** Inquiries regarding the ACX100 Open Source Project can be
+** made directly to:
+**
+** acx100-users@lists.sf.net
+** http://acx100.sf.net
+** ---------------------------------------------------------------------
+*/
+
+/***********************************************************************
+** This file expects INLINE_IO to be:
+** a) #defined to 'static inline': will emit inlined functions (for .h file);
+** or
+** b) #defined to '': will emit non-inlined functions (for .c file);
+** or
+** c) not #defined at all: emit prototypes only
+*/
+#ifdef ACX_PCI
+
+#ifndef INLINE_IO
+
+u32 acx_read_reg32(wlandevice_t *priv, unsigned int offset);
+u16 acx_read_reg16(wlandevice_t *priv, unsigned int offset);
+u8 acx_read_reg8(wlandevice_t *priv, unsigned int offset);
+void acx_write_reg32(wlandevice_t *priv, unsigned int offset, u32 val);
+void acx_write_reg16(wlandevice_t *priv, unsigned int offset, u16 val);
+void acx_write_reg8(wlandevice_t *priv, unsigned int offset, u8 val);
+void acx_write_flush(wlandevice_t *priv);
+
+#else
+
+#include <linux/pci.h> /* readl() etc. */
+
+INLINE_IO u32
+acx_read_reg32(wlandevice_t *priv, unsigned int offset)
+{
+#if ACX_IO_WIDTH == 32
+	return readl((u8 *)priv->iobase + priv->io[offset]);
+#else
+	return readw((u8 *)priv->iobase + priv->io[offset])
+	    + (readw((u8 *)priv->iobase + priv->io[offset] + 2) << 16);
+#endif
+}
+
+INLINE_IO u16
+acx_read_reg16(wlandevice_t *priv, unsigned int offset)
+{
+	return readw((u8 *)priv->iobase + priv->io[offset]);
+}
+
+INLINE_IO u8
+acx_read_reg8(wlandevice_t *priv, unsigned int offset)
+{
+	return readb((u8 *)priv->iobase + priv->io[offset]);
+}
+
+INLINE_IO void
+acx_write_reg32(wlandevice_t *priv, unsigned int offset, u32 val)
+{
+#if ACX_IO_WIDTH == 32
+	writel(val, (u8 *)priv->iobase + priv->io[offset]);
+#else
+	writew(val & 0xffff, (u8 *)priv->iobase + priv->io[offset]);
+	writew(val >> 16, (u8 *)priv->iobase + priv->io[offset] + 2);
+#endif
+}
+
+INLINE_IO void
+acx_write_reg16(wlandevice_t *priv, unsigned int offset, u16 val)
+{
+	writew(val, (u8 *)priv->iobase + priv->io[offset]);
+}
+
+INLINE_IO void
+acx_write_reg8(wlandevice_t *priv, unsigned int offset, u8 val)
+{
+	writeb(val, (u8 *)priv->iobase + priv->io[offset]);
+}
+
+/* Handle PCI posting properly:
+ * Make sure that writes reach the adapter in case they require to be executed
+ * *before* the next write, by reading a random (and safely accessible) register.
+ * This call has to be made if there is no read following (which would flush the data
+ * to the adapter), yet the written data has to reach the adapter immediately. */
+INLINE_IO void
+acx_write_flush(wlandevice_t *priv)
+{
+	/* readb(priv->iobase + priv->io[IO_ACX_INFO_MAILBOX_OFFS]); */
+	/* faster version (accesses the first register, IO_ACX_SOFT_RESET,
+	 * which should also be safe): */
+	readb(priv->iobase);
+}
+
+#endif
+
+#endif /* CONFIG_TIACX_PCI */
diff -urN oldtree/drivers/net/wireless/tiacx/acx_struct.h newtree/drivers/net/wireless/tiacx/acx_struct.h
--- oldtree/drivers/net/wireless/tiacx/acx_struct.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/acx_struct.h	2006-02-13 19:20:00.271471032 -0500
@@ -0,0 +1,1803 @@
+/***********************************************************************
+** Copyright (C) 2003  ACX100 Open Source Project
+**
+** The contents of this file are subject to the Mozilla Public
+** License Version 1.1 (the "License"); you may not use this file
+** except in compliance with the License. You may obtain a copy of
+** the License at http://www.mozilla.org/MPL/
+**
+** Software distributed under the License is distributed on an "AS
+** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+** implied. See the License for the specific language governing
+** rights and limitations under the License.
+**
+** Alternatively, the contents of this file may be used under the
+** terms of the GNU Public License version 2 (the "GPL"), in which
+** case the provisions of the GPL are applicable instead of the
+** above.  If you wish to allow the use of your version of this file
+** only under the terms of the GPL and not to allow others to use
+** your version of this file under the MPL, indicate your decision
+** by deleting the provisions above and replace them with the notice
+** and other provisions required by the GPL.  If you do not delete
+** the provisions above, a recipient may use your version of this
+** file under either the MPL or the GPL.
+** ---------------------------------------------------------------------
+** Inquiries regarding the ACX100 Open Source Project can be
+** made directly to:
+**
+** acx100-users@lists.sf.net
+** http://acx100.sf.net
+** ---------------------------------------------------------------------
+*/
+
+/***********************************************************************
+** Forward declarations of types
+*/
+typedef struct tx tx_t;
+typedef struct wlandevice wlandevice_t;
+typedef struct client client_t;
+typedef struct rxdesc rxdesc_t;
+typedef struct txdesc txdesc_t;
+typedef struct rxhostdesc rxhostdesc_t;
+typedef struct txhostdesc txhostdesc_t;
+typedef struct TIWLAN_DC TIWLAN_DC;
+
+
+/***********************************************************************
+** Debug / log functionality
+*/
+enum {
+	L_LOCK		= (ACX_DEBUG>1)*0x0001,	/* locking debug log */
+	L_INIT		= (ACX_DEBUG>0)*0x0002,	/* special card initialization logging */
+	L_IRQ		= (ACX_DEBUG>0)*0x0004,	/* interrupt stuff */
+	L_ASSOC		= (ACX_DEBUG>0)*0x0008,	/* assocation (network join) and station log */
+	L_FUNC		= (ACX_DEBUG>1)*0x0020,	/* logging of function enter / leave */
+	L_XFER		= (ACX_DEBUG>1)*0x0080,	/* logging of transfers and mgmt */
+	L_DATA		= (ACX_DEBUG>1)*0x0100,	/* logging of transfer data */
+	L_DEBUG		= (ACX_DEBUG>1)*0x0200,	/* log of debug info */
+	L_IOCTL		= (ACX_DEBUG>0)*0x0400,	/* log ioctl calls */
+	L_CTL		= (ACX_DEBUG>1)*0x0800,	/* log of low-level ctl commands */
+	L_BUFR		= (ACX_DEBUG>1)*0x1000,	/* debug rx buffer mgmt (ring buffer etc.) */
+	L_XFER_BEACON	= (ACX_DEBUG>1)*0x2000,	/* also log beacon packets */
+	L_BUFT		= (ACX_DEBUG>1)*0x4000,	/* debug tx buffer mgmt (ring buffer etc.) */
+	L_USBRXTX	= (ACX_DEBUG>0)*0x8000,	/* debug USB rx/tx operations */
+	L_BUF		= L_BUFR + L_BUFT,
+	L_ANY		= 0xffff
+};
+
+#if ACX_DEBUG
+extern unsigned int acx_debug;
+#else
+enum { acx_debug = 0 };
+#endif
+
+
+/*============================================================================*
+ * Random helpers                                                             *
+ *============================================================================*/
+#define ACX_PACKED __WLAN_ATTRIB_PACK__
+
+#define VEC_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+/* Use worker_queues for 2.5/2.6 Kernels and queue tasks for 2.4 Kernels
+   (used for the 'bottom half' of the interrupt routine) */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,41)
+#include <linux/workqueue.h>
+/* #define NEWER_KERNELS_ONLY 1 */
+#define USE_WORKER_TASKS
+#define WORK_STRUCT struct work_struct
+#define SCHEDULE_WORK schedule_work
+#define FLUSH_SCHEDULED_WORK flush_scheduled_work
+
+#else
+#include <linux/tqueue.h>
+#define USE_QUEUE_TASKS
+#define WORK_STRUCT struct tq_struct
+#define SCHEDULE_WORK schedule_task
+#define INIT_WORK(work, func, ndev) \
+	do { \
+		(work)->routine = (func); \
+		(work)->data = (ndev); \
+	} while (0)
+#define FLUSH_SCHEDULED_WORK flush_scheduled_tasks
+
+#endif
+
+
+/*============================================================================*
+ * Constants                                                                  *
+ *============================================================================*/
+#define OK	0
+#define NOT_OK	1
+
+/* The supported chip models (taken from pci_device_id.driver_data) */
+#define CHIPTYPE_ACX100		1
+#define CHIPTYPE_ACX111		2
+
+/* Driver defaults */
+#define DEFAULT_DTIM_INTERVAL	10
+/* used to be 2048, but FreeBSD driver changed it to 4096 to work properly
+** in noisy wlans */
+#define DEFAULT_MSDU_LIFETIME	4096
+#define DEFAULT_RTS_THRESHOLD	2312	/* max. size: disable RTS mechanism */
+#define DEFAULT_BEACON_INTERVAL	100
+
+#define ACX100_BAP_DATALEN_MAX		4096
+#define ACX100_RID_GUESSING_MAXLEN	2048	/* I'm not really sure */
+#define ACX100_RIDDATA_MAXLEN		ACX100_RID_GUESSING_MAXLEN
+
+/* Support Constants */
+/* Radio type names, found in Win98 driver's TIACXLN.INF */
+#define RADIO_MAXIM_0D		0x0d
+#define RADIO_RFMD_11		0x11
+#define RADIO_RALINK_15		0x15
+/* used in ACX111 cards (WG311v2, WL-121, ...): */
+#define RADIO_RADIA_16		0x16
+/* most likely *sometimes* used in ACX111 cards: */
+#define RADIO_UNKNOWN_17	0x17
+/* FwRad19.bin was found in a Safecom driver; must be an ACX111 radio: */
+#define RADIO_UNKNOWN_19	0x19
+
+/* Controller Commands */
+/* can be found in table cmdTable in firmware "Rev. 1.5.0" (FW150) */
+#define ACX1xx_CMD_RESET		0x00
+#define ACX1xx_CMD_INTERROGATE		0x01
+#define ACX1xx_CMD_CONFIGURE		0x02
+#define ACX1xx_CMD_ENABLE_RX		0x03
+#define ACX1xx_CMD_ENABLE_TX		0x04
+#define ACX1xx_CMD_DISABLE_RX		0x05
+#define ACX1xx_CMD_DISABLE_TX		0x06
+#define ACX1xx_CMD_FLUSH_QUEUE		0x07
+#define ACX1xx_CMD_SCAN			0x08
+#define ACX1xx_CMD_STOP_SCAN		0x09
+#define ACX1xx_CMD_CONFIG_TIM		0x0a
+#define ACX1xx_CMD_JOIN			0x0b
+#define ACX1xx_CMD_WEP_MGMT		0x0c
+#if OLD_FIRMWARE_VERSIONS
+#define ACX100_CMD_HALT			0x0e	/* mapped to unknownCMD in FW150 */
+#else
+#define ACX1xx_CMD_MEM_READ		0x0d
+#define ACX1xx_CMD_MEM_WRITE		0x0e
+#endif
+#define ACX1xx_CMD_SLEEP		0x0f
+#define ACX1xx_CMD_WAKE			0x10
+#define ACX1xx_CMD_UNKNOWN_11		0x11	/* mapped to unknownCMD in FW150 */
+#define ACX100_CMD_INIT_MEMORY		0x12
+#define ACX1xx_CMD_CONFIG_BEACON	0x13
+#define ACX1xx_CMD_CONFIG_PROBE_RESPONSE	0x14
+#define ACX1xx_CMD_CONFIG_NULL_DATA	0x15
+#define ACX1xx_CMD_CONFIG_PROBE_REQUEST	0x16
+#define ACX1xx_CMD_TEST			0x17
+#define ACX1xx_CMD_RADIOINIT		0x18
+#define ACX111_CMD_RADIOCALIB		0x19
+
+/* 'After Interrupt' Commands */
+#define ACX_AFTER_IRQ_CMD_STOP_SCAN	0x01
+#define ACX_AFTER_IRQ_CMD_ASSOCIATE	0x02
+#define ACX_AFTER_IRQ_CMD_RADIO_RECALIB	0x04
+#define ACX_AFTER_IRQ_UPDATE_CARD_CFG	0x08
+#define ACX_AFTER_IRQ_TX_CLEANUP	0x10
+#define ACX_AFTER_IRQ_COMPLETE_SCAN	0x20
+#define ACX_AFTER_IRQ_RESTART_SCAN	0x40
+
+
+/*============================================================================*
+ * Record ID Constants                                                        *
+ *============================================================================*/
+
+/* Information Elements: Network Parameters, Static Configuration Entities */
+/* these are handled by real_cfgtable in firmware "Rev 1.5.0" (FW150) */
+#define ACX1xx_IE_UNKNOWN_00			0x0000	/* mapped to cfgInvalid in FW150 */
+#define ACX100_IE_ACX_TIMER			0x0001
+#define ACX1xx_IE_POWER_MGMT			0x0002
+#define ACX1xx_IE_QUEUE_CONFIG			0x0003
+#define ACX100_IE_BLOCK_SIZE			0x0004
+#define ACX1xx_IE_MEMORY_CONFIG_OPTIONS		0x0005
+#define ACX1xx_IE_RATE_FALLBACK			0x0006
+#define ACX100_IE_WEP_OPTIONS			0x0007
+#define ACX111_IE_RADIO_BAND			0x0007
+#define ACX1xx_IE_MEMORY_MAP			0x0008	/* huh? */
+#define ACX100_IE_SSID				0x0008	/* huh? */
+#define ACX1xx_IE_SCAN_STATUS			0x0009	/* mapped to cfgInvalid in FW150 */
+#define ACX1xx_IE_ASSOC_ID			0x000a
+#define ACX1xx_IE_UNKNOWN_0B			0x000b	/* mapped to cfgInvalid in FW150 */
+#define ACX100_IE_UNKNOWN_0C			0x000c	/* very small implementation in FW150! */
+#define ACX111_IE_CONFIG_OPTIONS		0x000c
+#define ACX1xx_IE_FWREV				0x000d
+#define ACX1xx_IE_FCS_ERROR_COUNT		0x000e
+#define ACX1xx_IE_MEDIUM_USAGE			0x000f
+#define ACX1xx_IE_RXCONFIG			0x0010
+#define ACX100_IE_UNKNOWN_11			0x0011	/* NONBINARY: large implementation in FW150! link quality readings or so? */
+#define ACX111_IE_QUEUE_THRESH			0x0011
+#define ACX100_IE_UNKNOWN_12			0x0012	/* NONBINARY: VERY large implementation in FW150!! */
+#define ACX111_IE_BSS_POWER_SAVE		0x0012
+#define ACX1xx_IE_FIRMWARE_STATISTICS		0x0013
+#define ACX1xx_IE_FEATURE_CONFIG		0x0015
+#define ACX111_IE_KEY_CHOOSE			0x0016	/* for rekeying */
+#define ACX1xx_IE_DOT11_STATION_ID		0x1001
+#define ACX100_IE_DOT11_UNKNOWN_1002		0x1002	/* mapped to cfgInvalid in FW150 */
+#define ACX111_IE_DOT11_FRAG_THRESH		0x1002	/* mapped to cfgInvalid in FW150 */
+#define ACX100_IE_DOT11_BEACON_PERIOD		0x1003	/* mapped to cfgInvalid in FW150 */
+#define ACX1xx_IE_DOT11_DTIM_PERIOD		0x1004	/* mapped to cfgInvalid in FW150 */
+#define ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT	0x1005
+#define ACX1xx_IE_DOT11_LONG_RETRY_LIMIT	0x1006
+#define ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE	0x1007 /* configure default keys */
+#define ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME	0x1008
+#define ACX1xx_IE_DOT11_GROUP_ADDR		0x1009
+#define ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN	0x100a
+#define ACX1xx_IE_DOT11_CURRENT_ANTENNA		0x100b
+#define ACX1xx_IE_DOT11_UNKNOWN_100C		0x100c	/* mapped to cfgInvalid in FW150 */
+#define ACX1xx_IE_DOT11_TX_POWER_LEVEL		0x100d
+#define ACX1xx_IE_DOT11_CURRENT_CCA_MODE	0x100e
+#define ACX100_IE_DOT11_ED_THRESHOLD		0x100f
+#define ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET	0x1010	/* set default key ID */
+#define ACX100_IE_DOT11_UNKNOWN_1011		0x1011	/* mapped to cfgInvalid in FW150 */
+#define ACX100_IE_DOT11_UNKNOWN_1012		0x1012	/* mapped to cfgInvalid in FW150 */
+#define ACX100_IE_DOT11_UNKNOWN_1013		0x1013	/* mapped to cfgInvalid in FW150 */
+
+/*--- Configuration RID Lengths: Network Params, Static Config Entities -----*/
+/* This is the length of JUST the DATA part of the RID (does not include the
+ * len or code fields) */
+
+/* TODO: fill in the rest of these */
+#define ACX100_IE_ACX_TIMER_LEN				0x10
+#define ACX1xx_IE_POWER_MGMT_LEN			0x06
+#define ACX1xx_IE_QUEUE_CONFIG_LEN			0x1c
+#define ACX100_IE_BLOCK_SIZE_LEN			0x02
+#define ACX1xx_IE_MEMORY_CONFIG_OPTIONS_LEN		0x14
+#define ACX1xx_IE_RATE_FALLBACK_LEN			0x01
+#define ACX100_IE_WEP_OPTIONS_LEN			0x03
+#define ACX1xx_IE_MEMORY_MAP_LEN			0x28
+#define ACX100_IE_SSID_LEN				0x20
+#define ACX1xx_IE_SCAN_STATUS_LEN			0x04
+#define ACX1xx_IE_ASSOC_ID_LEN				0x02
+#define ACX1xx_IE_CONFIG_OPTIONS_LEN			0x14c
+#define ACX1xx_IE_FWREV_LEN				0x18
+#define ACX1xx_IE_FCS_ERROR_COUNT_LEN			0x04
+#define ACX1xx_IE_MEDIUM_USAGE_LEN			0x08
+#define ACX1xx_IE_RXCONFIG_LEN				0x04
+#define ACX1xx_IE_FIRMWARE_STATISTICS_LEN		0x9c
+#define ACX1xx_IE_FEATURE_CONFIG_LEN			0x08
+#define ACX111_IE_KEY_CHOOSE_LEN			0x04	/* really 4?? */
+#define ACX1xx_IE_DOT11_STATION_ID_LEN			0x06
+#define ACX100_IE_DOT11_BEACON_PERIOD_LEN		0x02
+#define ACX1xx_IE_DOT11_DTIM_PERIOD_LEN			0x01
+#define ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN		0x01
+#define ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN		0x01
+#define ACX100_IE_DOT11_WEP_DEFAULT_KEY_LEN		0x20
+#define ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN	0x04
+#define ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN_LEN		0x02
+
+#ifdef ACX_USB
+#define ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN		0x02
+#else
+#define ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN		0x01
+#endif
+
+#define ACX1xx_IE_DOT11_TX_POWER_LEVEL_LEN		0x01
+
+#ifdef ACX_USB
+#define ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN		0x02
+#else
+#define ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN		0x01
+#endif
+
+//USB doesn't return anything - len==0?!
+#define ACX100_IE_DOT11_ED_THRESHOLD_LEN		0x04
+
+#define ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET_LEN		0x01
+
+
+/*============================================================================*
+ * Information Frames Structures                                              *
+ *============================================================================*/
+
+/* Used in beacon frames and the like */
+#define DOT11RATEBYTE_1		(1*2)
+#define DOT11RATEBYTE_2		(2*2)
+#define DOT11RATEBYTE_5_5	(5*2+1)
+#define DOT11RATEBYTE_11	(11*2)
+#define DOT11RATEBYTE_22	(22*2)
+#define DOT11RATEBYTE_6_G	(6*2)
+#define DOT11RATEBYTE_9_G	(9*2)
+#define DOT11RATEBYTE_12_G	(12*2)
+#define DOT11RATEBYTE_18_G	(18*2)
+#define DOT11RATEBYTE_24_G	(24*2)
+#define DOT11RATEBYTE_36_G	(36*2)
+#define DOT11RATEBYTE_48_G	(48*2)
+#define DOT11RATEBYTE_54_G	(54*2)
+#define DOT11RATEBYTE_BASIC	0x80	/* flags rates included in basic rate set */
+
+
+/***********************************************************************
+** rxbuffer_t
+**
+** This is the format of rx data returned by acx
+*/
+
+/* I've hoped it's a 802.11 PHY header, but no...
+ * so far, I've seen on acx111:
+ * 0000 3a00 0000 0000 IBBS Beacons
+ * 0000 3c00 0000 0000 ESS Beacons
+ * 0000 2700 0000 0000 Probe requests
+ * --vda
+ */
+typedef struct phy_hdr {
+	u8	unknown[4] ACX_PACKED;
+	u8	acx111_unknown[4] ACX_PACKED;
+} phy_hdr_t;
+
+/* seems to be a bit similar to hfa384x_rx_frame.
+ * These fields are still not quite obvious, though.
+ * Some seem to have different meanings... */
+
+#define RXBUF_HDRSIZE 12
+#define PHY_HDR(rxbuf) ((phy_hdr_t*)&rxbuf->hdr_a3)
+#define RXBUF_BYTES_RCVD(rxbuf) (le16_to_cpu(rxbuf->mac_cnt_rcvd) & 0xfff)
+#define RXBUF_BYTES_USED(rxbuf) \
+		((le16_to_cpu(rxbuf->mac_cnt_rcvd) & 0xfff) + RXBUF_HDRSIZE)
+/*
+mac_cnt_rcvd:
+    12 bits: length of frame from control field to last byte of FCS
+    4 bits: reserved
+
+mac_cnt_mblks:
+    6 bits: number of memory block used to store frame in adapter memory
+    1 bit: Traffic Indicator bit in TIM of received Beacon was set
+
+mac_status: 1 byte (bitmap):
+    7 Matching BSSID
+    6 Matching SSID
+    5 BDCST	Address 1 field is a broadcast
+    4 VBM	received beacon frame has more than one set bit (?!)
+    3 TIM Set	bit representing this station is set in TIM of received beacon
+    2 GROUP	Address 1 is a multicast
+    1 ADDR1	Address 1 matches our MAC
+    0 FCSGD	FSC is good
+
+phy_stat_baseband: 1 byte (bitmap):
+    7 Preamble		frame had a long preamble
+    6 PLCP Error	CRC16 error in PLCP header
+    5 Unsup_Mod		unsupported modulation
+    4 Selected Antenna	antenna 1 was used to receive this frame
+    3 PBCC/CCK		frame used: 1=PBCC, 0=CCK modulation
+    2 OFDM		frame used OFDM modulation
+    1 TI Protection	protection frame was detected
+    0 Reserved
+
+phy_plcp_signal: 1 byte:
+    Receive PLCP Signal field from the Baseband Processor
+
+phy_level: 1 byte:
+    receive AGC gain level (can be used to measure receive signal strength)
+
+phy_snr: 1 byte:
+    estimated noise power of equalized receive signal
+    at input of FEC decoder (can be used to measure receive signal quality)
+
+time: 4 bytes:
+    timestamp sampled from either the Access Manager TSF counter
+    or free-running microsecond counter when the MAC receives
+    first byte of PLCP header.
+*/
+
+typedef struct rxbuffer {
+	u16	mac_cnt_rcvd ACX_PACKED;	/* only 12 bits are len! (0xfff) */
+	u8	mac_cnt_mblks ACX_PACKED;
+	u8	mac_status ACX_PACKED;
+	u8	phy_stat_baseband ACX_PACKED;	/* bit 0x80: used LNA (Low-Noise Amplifier) */
+	u8	phy_plcp_signal ACX_PACKED;
+	u8	phy_level ACX_PACKED;		/* PHY stat */
+	u8	phy_snr ACX_PACKED;		/* PHY stat */
+	u32	time ACX_PACKED;		/* timestamp upon MAC rcv first byte */
+/* 4-byte (acx100) or 8-byte (acx111) phy header will be here
+** if RX_CFG1_INCLUDE_PHY_HDR is in effect:
+**	phy_hdr_t phy			*/
+	wlan_hdr_a3_t hdr_a3 ACX_PACKED;
+	/* maximally sized data part of wlan packet */
+	u8	data_a3[WLAN_A4FR_MAXLEN_WEP - WLAN_HDR_A3_LEN] ACX_PACKED;
+	/* can add hdr/data_a4 if needed */
+} rxbuffer_t;
+
+
+/*--- Firmware statistics ----------------------------------------------------*/
+typedef struct fw_stats {
+	u32	val0x0 ACX_PACKED;		/* hdr; */
+	u32	tx_desc_of ACX_PACKED;
+	u32	rx_oom ACX_PACKED;
+	u32	rx_hdr_of ACX_PACKED;
+	u32	rx_hdr_use_next ACX_PACKED;
+	u32	rx_dropped_frame ACX_PACKED;
+	u32	rx_frame_ptr_err ACX_PACKED;
+	u32	rx_xfr_hint_trig ACX_PACKED;
+
+	u32	rx_dma_req ACX_PACKED;
+	u32	rx_dma_err ACX_PACKED;
+	u32	tx_dma_req ACX_PACKED;
+	u32	tx_dma_err ACX_PACKED;
+
+	u32	cmd_cplt ACX_PACKED;
+	u32	fiq ACX_PACKED;
+	u32	rx_hdrs ACX_PACKED;
+	u32	rx_cmplt ACX_PACKED;
+	u32	rx_mem_of ACX_PACKED;
+	u32	rx_rdys ACX_PACKED;
+	u32	irqs ACX_PACKED;
+	u32	acx_trans_procs ACX_PACKED;
+	u32	decrypt_done ACX_PACKED;
+	u32	dma_0_done ACX_PACKED;
+	u32	dma_1_done ACX_PACKED;
+	u32	tx_exch_complet ACX_PACKED;
+	u32	commands ACX_PACKED;
+	u32	acx_rx_procs ACX_PACKED;
+	u32	hw_pm_mode_changes ACX_PACKED;
+	u32	host_acks ACX_PACKED;
+	u32	pci_pm ACX_PACKED;
+	u32	acm_wakeups ACX_PACKED;
+
+	u32	wep_key_count ACX_PACKED;
+	u32	wep_default_key_count ACX_PACKED;
+	u32	dot11_def_key_mib ACX_PACKED;
+	u32	wep_key_not_found ACX_PACKED;
+	u32	wep_decrypt_fail ACX_PACKED;
+} fw_stats_t;
+
+/* Firmware version struct */
+
+typedef struct fw_ver {
+	u16	cmd ACX_PACKED;
+	u16	size ACX_PACKED;
+	char	fw_id[20] ACX_PACKED;
+	u32	hw_id ACX_PACKED;
+} fw_ver_t;
+
+#define FW_ID_SIZE 20
+
+
+/*--- WEP stuff --------------------------------------------------------------*/
+#define DOT11_MAX_DEFAULT_WEP_KEYS	4
+#define MAX_KEYLEN			32
+
+#define HOSTWEP_DEFAULTKEY_MASK		(BIT1 | BIT0)
+#define HOSTWEP_DECRYPT			BIT4
+#define HOSTWEP_ENCRYPT			BIT5
+#define HOSTWEP_PRIVACYINVOKED		BIT6
+#define HOSTWEP_EXCLUDEUNENCRYPTED	BIT7
+
+/* non-firmware struct, no packing necessary */
+typedef struct wep_key {
+	size_t	size; /* most often used member first */
+	u8	index;
+	u8	key[29];
+	u16	strange_filler;
+} wep_key_t;			/* size = 264 bytes (33*8) */
+/* FIXME: We don't have size 264! Or is there 2 bytes beyond the key
+ * (strange_filler)? */
+
+/* non-firmware struct, no packing necessary */
+typedef struct key_struct {
+	u8	addr[ETH_ALEN];	/* 0x00 */
+	u16	filler1;	/* 0x06 */
+	u32	filler2;	/* 0x08 */
+	u32	index;		/* 0x0c */
+	u16	len;		/* 0x10 */
+	u8	key[29];	/* 0x12; is this long enough??? */
+} key_struct_t;			/* size = 276. FIXME: where is the remaining space?? */
+
+
+/*--- Client (peer) info -----------------------------------------------------*/
+/* priv->sta_list[] is used for:
+** accumulating and processing of scan results
+** keeping client info in AP mode
+** keeping AP info in STA mode (AP is the only one 'client')
+** keeping peer info in ad-hoc mode
+** non-firmware struct --> no packing necessary */
+enum {
+	CLIENT_EMPTY_SLOT_0 = 0,
+	CLIENT_EXIST_1 = 1,
+	CLIENT_AUTHENTICATED_2 = 2,
+	CLIENT_ASSOCIATED_3 = 3,
+	CLIENT_JOIN_CANDIDATE = 4
+};
+struct client {
+	struct client*	next;
+	unsigned long	mtime;		/* last time we heard it, in jiffies */
+	size_t	essid_len;		/* length of ESSID (without '\0') */
+	u32	sir;			/* Standard IR */
+	u32	snr;			/* Signal to Noise Ratio */
+	u16	aid;			/* association ID */
+	u16	seq;			/* from client's auth req */
+	u16	auth_alg;		/* from client's auth req */
+	u16	cap_info;		/* from client's assoc req */
+	u16	rate_cap;		/* what client supports (all rates) */
+	u16	rate_bas;		/* what client supports (basic rates) */
+	u16	rate_cfg;		/* what is allowed (by iwconfig etc) */
+	u16	rate_cur;		/* currently used rate mask */
+	u8	rate_100;		/* currently used rate byte (acx100 only) */
+	u8	used;			/* misnamed, more like 'status' */
+	u8	address[ETH_ALEN];
+	u8	bssid[ETH_ALEN];	/* ad-hoc hosts can have bssid != mac */
+	u8	channel;
+	u8	auth_step;
+	u8	ignore_count;
+	u8	fallback_count;
+	u8	stepup_count;
+	char	essid[IW_ESSID_MAX_SIZE + 1];	/* ESSID and trailing '\0'  */
+/* FIXME: this one is too damn big */
+	char	challenge_text[WLAN_CHALLENGE_LEN];
+};
+
+
+/*============================================================================*
+ * Hardware structures                                                        *
+ *============================================================================*/
+
+/* An opaque typesafe helper type
+ *
+ * Some hardware fields are actually pointers,
+ * but they have to remain u32, since using ptr instead
+ * (8 bytes on 64bit systems!) would disrupt the fixed descriptor
+ * format the acx firmware expects in the non-user area.
+ * Since we cannot cram an 8 byte ptr into 4 bytes, we need to
+ * enforce that pointed to data remains in low memory
+ * (address value needs to fit in 4 bytes) on 64bit systems.
+ *
+ * This is easy to get wrong, thus we are using a small struct
+ * and special macros to access it. Macros will check for
+ * attempts to overflow an acx_ptr with value > 0xffffffff.
+ *
+ * Attempts to use acx_ptr without macros result in compile-time errors */
+
+typedef struct {
+	u32	v;
+} acx_ptr;
+
+#if ACX_DEBUG
+#define CHECK32(n) BUG_ON(sizeof(n)>4 && (long)(n)>0xffffff00)
+#else
+#define CHECK32(n) ((void)0)
+#endif
+
+/* acx_ptr <-> integer conversion */
+#define cpu2acx(n) ({ CHECK32(n); ((acx_ptr){ .v = cpu_to_le32(n) }); })
+#define acx2cpu(a) (le32_to_cpu(a.v))
+
+/* acx_ptr <-> pointer conversion */
+#define ptr2acx(p) ({ CHECK32(p); ((acx_ptr){ .v = cpu_to_le32((u32)(long)(p)) }); })
+#define acx2ptr(a) ((void*)le32_to_cpu(a.v))
+
+/* Values for rate field (acx100 only) */
+#define RATE100_1		10
+#define RATE100_2		20
+#define RATE100_5		55
+#define RATE100_11		110
+#define RATE100_22		220
+/* This bit denotes use of PBCC:
+** (PBCC encoding is usable with 11 and 22 Mbps speeds only) */
+#define RATE100_PBCC511		0x80
+
+/* Bit values for rate111 field */
+#define RATE111_1		0x0001	/* DBPSK */
+#define RATE111_2		0x0002	/* DQPSK */
+#define RATE111_5		0x0004	/* CCK or PBCC */
+#define RATE111_6		0x0008	/* CCK-OFDM or OFDM */
+#define RATE111_9		0x0010	/* CCK-OFDM or OFDM */
+#define RATE111_11		0x0020	/* CCK or PBCC */
+#define RATE111_12		0x0040	/* CCK-OFDM or OFDM */
+#define RATE111_18		0x0080	/* CCK-OFDM or OFDM */
+#define RATE111_22		0x0100	/* PBCC */
+#define RATE111_24		0x0200	/* CCK-OFDM or OFDM */
+#define RATE111_36		0x0400	/* CCK-OFDM or OFDM */
+#define RATE111_48		0x0800	/* CCK-OFDM or OFDM */
+#define RATE111_54		0x1000	/* CCK-OFDM or OFDM */
+#define RATE111_RESERVED	0x2000
+#define RATE111_PBCC511		0x4000  /* PBCC mod at 5.5 or 11Mbit (else CCK) */
+#define RATE111_SHORTPRE	0x8000  /* short preamble */
+/* Special 'try everything' value */
+#define RATE111_ALL		0x1fff
+/* These bits denote acx100 compatible settings */
+#define RATE111_ACX100_COMPAT	0x0127
+/* These bits denote 802.11b compatible settings */
+#define RATE111_80211B_COMPAT	0x0027
+
+/* Descriptor Ctl field bits
+ * init value is 0x8e, "idle" value is 0x82 (in idle tx descs)
+ */
+#define DESC_CTL_SHORT_PREAMBLE	0x01	/* preamble type: 0 = long; 1 = short */
+#define DESC_CTL_FIRSTFRAG	0x02	/* this is the 1st frag of the frame */
+#define DESC_CTL_AUTODMA	0x04
+#define DESC_CTL_RECLAIM	0x08	/* ready to reuse */
+#define DESC_CTL_HOSTDONE	0x20	/* host has finished processing */
+#define DESC_CTL_ACXDONE	0x40	/* acx has finished processing */
+/* host owns the desc [has to be released last, AFTER modifying all other desc fields!] */
+#define DESC_CTL_HOSTOWN	0x80
+
+#define	DESC_CTL_INIT		(DESC_CTL_HOSTOWN | DESC_CTL_RECLAIM | \
+				 DESC_CTL_AUTODMA | DESC_CTL_FIRSTFRAG)
+#define	DESC_CTL_DONE		(DESC_CTL_ACXDONE | DESC_CTL_HOSTOWN)
+
+#define DESC_CTL_HOSTOWN_STR	"80"
+#define	DESC_CTL_DONE_STR	"C0"
+
+/* NB: some bits may be interesting for Monitor mode tx (aka Raw tx): */
+#define DESC_CTL2_SEQ		0x01	/* don't increase sequence field */
+#define DESC_CTL2_FCS		0x02	/* don't add the FCS */
+#define DESC_CTL2_MORE_FRAG	0x04
+#define DESC_CTL2_RETRY		0x08	/* don't increase retry field */
+#define DESC_CTL2_POWER		0x10	/* don't increase power mgmt. field */
+#define DESC_CTL2_RTS		0x20	/* do RTS/CTS magic before sending */
+#define DESC_CTL2_WEP		0x40	/* encrypt this frame */
+#define DESC_CTL2_DUR		0x80	/* don't increase duration field */
+
+/***************************************************************
+** PCI structures
+*/
+/* IRQ Constants
+** (outside of "#ifdef PCI" because USB (mis)uses HOST_INT_SCAN_COMPLETE */
+#define HOST_INT_RX_DATA	0x0001
+#define HOST_INT_TX_COMPLETE	0x0002
+#define HOST_INT_TX_XFER	0x0004
+#define HOST_INT_RX_COMPLETE	0x0008
+#define HOST_INT_DTIM		0x0010
+#define HOST_INT_BEACON		0x0020
+#define HOST_INT_TIMER		0x0040
+#define HOST_INT_KEY_NOT_FOUND	0x0080
+#define HOST_INT_IV_ICV_FAILURE	0x0100
+#define HOST_INT_CMD_COMPLETE	0x0200
+#define HOST_INT_INFO		0x0400
+#define HOST_INT_OVERFLOW	0x0800
+#define HOST_INT_PROCESS_ERROR	0x1000
+#define HOST_INT_SCAN_COMPLETE	0x2000
+#define HOST_INT_FCS_THRESHOLD	0x4000
+#define HOST_INT_UNKNOWN	0x8000
+
+#ifdef ACX_PCI
+
+/* Register I/O offsets */
+#define ACX100_EEPROM_ID_OFFSET	0x380
+
+/* please add further ACX hardware register definitions only when
+   it turns out you need them in the driver, and please try to use
+   firmware functionality instead, since using direct I/O access instead
+   of letting the firmware do it might confuse the firmware's state
+   machine */
+
+/* ***** ABSOLUTELY ALWAYS KEEP OFFSETS IN SYNC WITH THE INITIALIZATION
+** OF THE I/O ARRAYS!!!! (grep for '^IO_ACX') ***** */
+enum {
+	IO_ACX_SOFT_RESET = 0,
+
+	IO_ACX_SLV_MEM_ADDR,
+	IO_ACX_SLV_MEM_DATA,
+	IO_ACX_SLV_MEM_CTL,
+	IO_ACX_SLV_END_CTL,
+
+	IO_ACX_FEMR,		/* Function Event Mask */
+
+	IO_ACX_INT_TRIG,
+	IO_ACX_IRQ_MASK,
+	IO_ACX_IRQ_STATUS_NON_DES,
+	IO_ACX_IRQ_STATUS_CLEAR, /* CLEAR = clear on read */
+	IO_ACX_IRQ_ACK,
+	IO_ACX_HINT_TRIG,
+
+	IO_ACX_ENABLE,
+
+	IO_ACX_EEPROM_CTL,
+	IO_ACX_EEPROM_ADDR,
+	IO_ACX_EEPROM_DATA,
+	IO_ACX_EEPROM_CFG,
+
+	IO_ACX_PHY_ADDR,
+	IO_ACX_PHY_DATA,
+	IO_ACX_PHY_CTL,
+
+	IO_ACX_GPIO_OE,
+
+	IO_ACX_GPIO_OUT,
+
+	IO_ACX_CMD_MAILBOX_OFFS,
+	IO_ACX_INFO_MAILBOX_OFFS,
+	IO_ACX_EEPROM_INFORMATION,
+
+	IO_ACX_EE_START,
+	IO_ACX_SOR_CFG,
+	IO_ACX_ECPU_CTRL
+};
+/* ***** ABSOLUTELY ALWAYS KEEP OFFSETS IN SYNC WITH THE INITIALIZATION
+** OF THE I/O ARRAYS!!!! (grep for '^IO_ACX') ***** */
+
+/* Values for IO_ACX_INT_TRIG register: */
+/* inform hw that rxdesc in queue needs processing */
+#define INT_TRIG_RXPRC		0x08
+/* inform hw that txdesc in queue needs processing */
+#define INT_TRIG_TXPRC		0x04
+/* ack that we received info from info mailbox */
+#define INT_TRIG_INFOACK	0x02
+/* inform hw that we have filled command mailbox */
+#define INT_TRIG_CMD		0x01
+
+struct txhostdesc {
+	acx_ptr	data_phy ACX_PACKED;			/* 0x00 [u8 *] */
+	u16	data_offset ACX_PACKED;			/* 0x04 */
+	u16	reserved ACX_PACKED;			/* 0x06 */
+	u16	Ctl_16 ACX_PACKED;	/* 16bit value, endianness!! */
+	u16	length ACX_PACKED;			/* 0x0a */
+	acx_ptr	desc_phy_next ACX_PACKED;		/* 0x0c [txhostdesc *] */
+	acx_ptr	pNext ACX_PACKED;			/* 0x10 [txhostdesc *] */
+	u32	Status ACX_PACKED;			/* 0x14, unused on Tx */
+/* From here on you can use this area as you want (variable length, too!) */
+	u8	*data ACX_PACKED;
+};
+
+struct txdesc {
+	acx_ptr	pNextDesc ACX_PACKED;	/* pointer to next txdesc */
+	acx_ptr	HostMemPtr ACX_PACKED;			/* 0x04 */
+	acx_ptr	AcxMemPtr ACX_PACKED;			/* 0x08 */
+	u32	tx_time ACX_PACKED;			/* 0x0c */
+	u16	total_length ACX_PACKED;		/* 0x10 */
+	u16	Reserved ACX_PACKED;			/* 0x12 */
+	/* the following 16 bytes do not change when acx100 owns the descriptor */
+	/* we need to add a union here with a *fixed* size of 16,
+	 * since ptrlen AMD64 (8) != ptrlen x86 (4) */
+	union {						/* 0x14 */
+		struct {
+			struct client *txc ACX_PACKED;
+			struct txhostdesc *host_desc ACX_PACKED;
+		} s ACX_PACKED;
+		u32	dummy[4] ACX_PACKED;
+	} fixed_size ACX_PACKED;
+	u8	Ctl_8 ACX_PACKED;			/* 0x24, 8bit value */
+	u8	Ctl2_8 ACX_PACKED;			/* 0x25, 8bit value */
+	u8	error ACX_PACKED;			/* 0x26 */
+	u8	ack_failures ACX_PACKED;		/* 0x27 */
+	u8	rts_failures ACX_PACKED;		/* 0x28 */
+	u8	rts_ok ACX_PACKED;			/* 0x29 */
+	union {
+		struct {
+			u8	rate ACX_PACKED;	/* 0x2a */
+			u8	queue_ctrl ACX_PACKED;	/* 0x2b */
+		} r1 ACX_PACKED;
+		struct {
+			u16	rate111 ACX_PACKED;	/* 0x2a */
+		} r2 ACX_PACKED;
+	} u ACX_PACKED;
+	u32	queue_info ACX_PACKED;			/* 0x2c (acx100, reserved on acx111) */
+};		/* size : 48 = 0x30 */
+/* NB: acx111 txdesc structure is 4 byte larger */
+/* All these 4 extra bytes are reserved. tx alloc code takes them into account */
+
+struct rxhostdesc {
+	acx_ptr	data_phy ACX_PACKED;			/* 0x00 [rxbuffer_t *] */
+	u16	data_offset ACX_PACKED;			/* 0x04 */
+	u16	reserved ACX_PACKED;			/* 0x06 */
+	u16	Ctl_16 ACX_PACKED;			/* 0x08; 16bit value, endianness!! */
+	u16	length ACX_PACKED;			/* 0x0a */
+	acx_ptr	desc_phy_next ACX_PACKED;		/* 0x0c [rxhostdesc_t *] */
+	acx_ptr	pNext ACX_PACKED;			/* 0x10 [rxhostdesc_t *] */
+	u32	Status ACX_PACKED;			/* 0x14 */
+/* From here on you can use this area as you want (variable length, too!) */
+	rxbuffer_t *data ACX_PACKED;
+};
+
+struct rxdesc {
+	acx_ptr	pNextDesc ACX_PACKED;			/* 0x00 */
+	acx_ptr	HostMemPtr ACX_PACKED;			/* 0x04 */
+	acx_ptr	ACXMemPtr ACX_PACKED;			/* 0x08 */
+	u32	rx_time ACX_PACKED;			/* 0x0c */
+	u16	total_length ACX_PACKED;		/* 0x10 */
+	u16	WEP_length ACX_PACKED;			/* 0x12 */
+	u32	WEP_ofs ACX_PACKED;			/* 0x14 */
+
+/* the following 16 bytes do not change when acx100 owns the descriptor */
+	u8	driverWorkspace[16] ACX_PACKED;		/* 0x18 */
+
+	u8	Ctl_8 ACX_PACKED;
+	u8	rate ACX_PACKED;
+	u8	error ACX_PACKED;
+	u8	SNR ACX_PACKED;				/* Signal-to-Noise Ratio */
+	u8	RxLevel ACX_PACKED;
+	u8	queue_ctrl ACX_PACKED;
+	u16	unknown ACX_PACKED;
+	u32	unknown2 ACX_PACKED;
+};		/* size 52 = 0x34 */
+
+//TODO: incorporate into wlandevice_t
+struct TIWLAN_DC {
+	wlandevice_t	*priv;
+	/* pointer to the beginning of the card's tx queue pool.
+	   it is relative to the internal memory mapping of the card! */
+	u32		ui32ACXTxQueueStart;
+	/* pointer to the beginning of the card's rx queue pool.
+	   it is relative to the internal memory mapping of the card! */
+	u32		ui32ACXRxQueueStart;
+	/* pointers to tx buffers, tx host descriptors (in host memory)
+	** and tx descrs in device memory */
+	u8		*pTxBufferPool;
+	txhostdesc_t	*pTxHostDescQPool;
+	txdesc_t	*pTxDescQPool;	/* points to PCI-mapped memory */
+	/* same for rx */
+	rxbuffer_t	*pRxBufferPool;
+	rxhostdesc_t	*pRxHostDescQPool;
+	rxdesc_t	*pRxDescQPool;
+	/* physical addresses of above host memory areas */
+	dma_addr_t	RxBufferPoolPhyAddr;
+	dma_addr_t	RxHostDescQPoolPhyAddr;
+	dma_addr_t	TxBufferPoolPhyAddr;
+	dma_addr_t	TxHostDescQPoolPhyAddr;
+	/* sizes of above host memory areas */
+	unsigned int	TxBufferPoolSize;
+	unsigned int	TxHostDescQPoolSize;
+	unsigned int	RxBufferPoolSize;
+	unsigned int	RxHostDescQPoolSize;
+
+	unsigned int	TxDescrSize;		/* size per tx descr; ACX111 = ACX100 + 4 */
+	unsigned int	tx_pool_count;		/* indicates # of ring buffer pool entries */
+	unsigned int	tx_head;		/* current ring buffer pool member index */
+	unsigned int	tx_tail;
+	unsigned int	rx_pool_count;
+	unsigned int	rx_tail;
+};
+
+/* figure out tx descriptor pointer, depending on different acx100 or acx111
+ * tx descriptor length */
+#define GET_TX_DESC_PTR(dc, index) \
+	(struct txdesc *) (((u8 *)(dc)->pTxDescQPool) + ((index) * (dc)->TxDescrSize))
+#define GET_NEXT_TX_DESC_PTR(dc, tx_desc) \
+	(struct txdesc *) (((u8 *)(tx_desc)) + (dc)->TxDescrSize)
+
+#endif /* CONFIG_TIACX_PCI */
+
+/***************************************************************
+** USB structures and constants
+*/
+#ifdef ACX_USB
+
+/* Buffer size for fw upload */
+#define ACX100_USB_RWMEM_MAXLEN	2048
+
+/* Should be sent to the ctrlout endpoint */
+#define ACX100_USB_ENBULKIN	6
+
+/* The number of bulk URBs to use */
+#define ACX100_USB_NUM_BULK_URBS 8
+
+/* Should be sent to the bulkout endpoint */
+#define ACX_USB_REQ_UPLOAD_FW	0x10
+#define ACX_USB_REQ_ACK_CS	0x11
+#define ACX_USB_REQ_CMD		0x12
+
+/* Used for usb_txbuffer.desc field */
+#define USB_TXBUF_TXDESC	0xA
+/* Used for usb_txbuffer.hostData field */
+#define USB_TXBUF_HD_ISDATA     0x10000
+#define USB_TXBUF_HD_DIRECTED   0x20000
+#define USB_TXBUF_HD_BROADCAST  0x40000
+/* Size of header (everything up to data[]) */
+#define USB_TXBUF_HDRSIZE	14
+typedef struct usb_txbuffer {
+	u16	desc ACX_PACKED;
+	u16	MPDUlen ACX_PACKED;
+	u8	index ACX_PACKED;
+	u8	txRate ACX_PACKED;
+	u32	hostData ACX_PACKED;
+	u8	ctrl1 ACX_PACKED;
+	u8	ctrl2 ACX_PACKED;
+	u16	dataLength ACX_PACKED;
+	/* wlan packet content is placed here: */
+	u8	data[WLAN_A4FR_MAXLEN_WEP] ACX_PACKED;
+} usb_txbuffer_t;
+
+typedef struct {
+	void	*device;
+	int	number;
+} acx_usb_bulk_context_t;
+
+typedef struct usb_tx {
+	unsigned	busy:1;
+	struct urb	*urb;
+	wlandevice_t	*priv;
+	client_t	*txc;
+	/* actual USB bulk output data block is here: */
+	usb_txbuffer_t	bulkout;
+} usb_tx_t;
+
+#endif /* CONFIG_TIACX_USB */
+
+
+/*============================================================================*
+ * Main acx per-device data structure (netdev->priv)                          *
+ *============================================================================*/
+#define ACX_STATE_FW_LOADED	0x01
+#define ACX_STATE_IFACE_UP	0x02
+
+/* MAC mode (BSS type) defines
+ * Note that they shouldn't be redefined, since they are also used
+ * during communication with firmware */
+#define ACX_MODE_0_ADHOC	0
+#define ACX_MODE_1_UNUSED	1
+#define ACX_MODE_2_STA		2
+#define ACX_MODE_3_AP		3
+/* These are our own inventions. Sending these to firmware
+** makes it stop emitting beacons, which is exactly what we want
+** for these modes */
+#define ACX_MODE_MONITOR	0xfe
+#define ACX_MODE_OFF		0xff
+/* 'Submode': identifies exact status of ADHOC/STA host */
+#define ACX_STATUS_0_STOPPED		0
+#define ACX_STATUS_1_SCANNING		1
+#define ACX_STATUS_2_WAIT_AUTH		2
+#define ACX_STATUS_3_AUTHENTICATED	3
+#define ACX_STATUS_4_ASSOCIATED		4
+
+/* FIXME: this should be named something like struct acx_priv (typedef'd to
+ * acx_priv_t) */
+
+/* non-firmware struct, no packing necessary */
+struct wlandevice {
+	/*** Device chain ***/
+	struct wlandevice	*next;		/* link for list of devices */
+
+	/*** Linux network device ***/
+	struct net_device	*netdev;	/* pointer to linux netdevice */
+	struct net_device	*prev_nd;	/* FIXME: We should not chain via our
+						 * private struct wlandevice _and_
+						 * the struct net_device. */
+	/*** Device statistics ***/
+	struct net_device_stats	stats;		/* net device statistics */
+#ifdef WIRELESS_EXT
+	struct iw_statistics	wstats;		/* wireless statistics */
+#endif
+	/*** Power managment ***/
+	struct pm_dev		*pm;		/* PM crap */
+
+	/*** Management timer ***/
+	struct timer_list	mgmt_timer;
+
+	/*** Locking ***/
+	struct semaphore	sem;
+	spinlock_t		lock;
+#if defined(PARANOID_LOCKING) /* Lock debugging */
+	const char		*last_sem;
+	const char		*last_lock;
+	unsigned long		sem_time;
+	unsigned long		lock_time;
+#endif
+
+	/*** Hardware identification ***/
+	const char	*chip_name;
+	u8		chip_type;
+	u8		form_factor;
+	u8		radio_type;
+	u8		eeprom_version;
+
+	/*** Firmware identification ***/
+	char		firmware_version[FW_ID_SIZE+1];
+	u32		firmware_numver;
+	u32		firmware_id;
+
+	/*** Device state ***/
+	u16		dev_state_mask;
+	u8		led_power;		/* power LED status */
+	u32		get_mask;		/* mask of settings to fetch from the card */
+	u32		set_mask;		/* mask of settings to write to the card */
+
+	/* Barely used in USB case */
+	u16		irq_status;
+
+	u8		after_interrupt_jobs;	/* mini job list for doing actions after an interrupt occurred */
+	WORK_STRUCT	after_interrupt_task;	/* our task for after interrupt actions */
+
+	/*** scanning ***/
+	u16		scan_count;		/* number of times to do channel scan */
+	u8		scan_mode;		/* 0 == active, 1 == passive, 2 == background */
+	u8		scan_rate;
+	u16		scan_duration;
+	u16		scan_probe_delay;
+#if WIRELESS_EXT > 15
+	struct iw_spy_data	spy_data;	/* FIXME: needs to be implemented! */
+#endif
+
+	/*** Wireless network settings ***/
+	/* copy of the device address (ifconfig hw ether) that we actually use
+	** for 802.11; copied over from the network device's MAC address
+	** (ifconfig) when it makes sense only */
+	u8		dev_addr[MAX_ADDR_LEN];
+	u8		bssid[ETH_ALEN];	/* the BSSID after having joined */
+	u8		ap[ETH_ALEN];		/* The AP we want, FF:FF:FF:FF:FF:FF is any */
+	u16		aid;			/* The Association ID sent from the AP / last used AID if we're an AP */
+	u16		mode;			/* mode from iwconfig */
+	u16		status;			/* 802.11 association status */
+	u8		essid_active;		/* specific ESSID active, or select any? */
+	u8		essid_len;		/* to avoid dozens of strlen() */
+	/* INCLUDES \0 termination for easy printf - but many places
+	** simply want the string data memcpy'd plus a length indicator!
+	** Keep that in mind... */
+	char		essid[IW_ESSID_MAX_SIZE+1];
+	/* essid we are going to use for association, in case of "essid 'any'"
+	** and in case of hidden ESSID (use configured ESSID then) */
+	char		essid_for_assoc[IW_ESSID_MAX_SIZE+1];
+	char		nick[IW_ESSID_MAX_SIZE+1]; /* see essid! */
+	u8		channel;
+	u8		reg_dom_id;		/* reg domain setting */
+	u16		reg_dom_chanmask;
+	u16		auth_or_assoc_retries;
+	u16		scan_retries;
+	unsigned long	scan_start;		/* YES, jiffies is defined as "unsigned long" */
+
+	/* stations known to us (if we're an ap) */
+	client_t	sta_list[32];		/* tab is larger than list, so that */
+	client_t	*sta_hash_tab[64];	/* hash collisions are not likely */
+	client_t	*ap_client;		/* this one is our AP (STA mode only) */
+
+	unsigned long	dup_msg_expiry;
+	int		dup_count;
+	u16		last_seq_ctrl;		/* duplicate packet detection */
+
+	/* 802.11 power save mode */
+	u8		ps_wakeup_cfg;
+	u8		ps_listen_interval;
+	u8		ps_options;
+	u8		ps_hangover_period;
+	u16		ps_enhanced_transition_time;
+
+	/*** PHY settings ***/
+	u8		fallback_threshold;
+	u8		stepup_threshold;
+	u16		rate_basic;
+	u16		rate_oper;
+	u16		rate_bcast;
+	u16		rate_bcast100;
+	u8		rate_auto;		/* false if "iwconfig rate N" (WITHOUT 'auto'!) */
+	u8		preamble_mode;		/* 0 == Long Preamble, 1 == Short, 2 == Auto */
+	u8		preamble_cur;
+
+	u8		tx_disabled;
+	u8		tx_level_dbm;
+	u8		tx_level_val;
+	u8		tx_level_auto;		/* whether to do automatic power adjustment */
+
+	unsigned long	recalib_time_last_success;
+	unsigned long	recalib_time_last_attempt;
+	int		recalib_failure_count;
+	int		recalib_msg_ratelimit;
+	int		retry_errors_msg_ratelimit;
+
+	unsigned long	brange_time_last_state_change;	/* time the power LED was last changed */
+	u8		brange_last_state;	/* last state of the LED */
+	u8		brange_max_quality;	/* maximum quality that equates to full speed */
+
+	u8		sensitivity;
+	u8		antenna;		/* antenna settings */
+	u8		ed_threshold;		/* energy detect threshold */
+	u8		cca;			/* clear channel assessment */
+
+	u16		rts_threshold;
+	u32		short_retry;
+	u32		long_retry;
+	u16		msdu_lifetime;
+	u16		listen_interval;	/* given in units of beacon interval */
+	u32		beacon_interval;
+
+	u16		capabilities;
+	u8		capab_short;
+	u8		capab_pbcc;
+	u8		capab_agility;
+	u8		rate_supported_len;
+	u8		rate_supported[13];
+
+	/*** Encryption settings (WEP) ***/
+	u32		auth_alg;		/* used in transmit_authen1 */
+	u8		wep_enabled;
+	u8		wep_restricted;
+	u8		wep_current_index;
+	wep_key_t	wep_keys[DOT11_MAX_DEFAULT_WEP_KEYS];	/* the default WEP keys */
+	key_struct_t	wep_key_struct[10];
+
+	/*** Card Rx/Tx management ***/
+	u16		rx_config_1;
+	u16		rx_config_2;
+	u32		tx_cnt_done;
+	u16		memblocksize;
+	u32		RxQueueCnt;
+	u32		TxQueueCnt;
+	u32		TxQueueFree;
+
+	/*** Unknown ***/
+	u8		dtim_interval;
+
+/*** PCI/USB/... must be last or else hw agnostic code breaks horribly ***/
+
+	/*** PCI stuff ***/
+#ifdef ACX_PCI
+	TIWLAN_DC	dc;
+	/* Same as dc.pRxHostDescQPool, but possibly aligned to 4 bytes: */
+	rxhostdesc_t	*RxHostDescPoolStart;
+
+	u8		need_radio_fw;
+	u8		irqs_active;	/* whether irq sending is activated */
+
+	const u16	*io;		/* points to ACX100 or ACX111 I/O register address set */
+
+	struct pci_dev	*pdev;
+
+	unsigned long	membase;
+	unsigned long	membase2;
+	void		*iobase;
+	void		*iobase2;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10)
+	/* 2.6.9-rc3-mm2 (2.6.9-bk4, too) introduced a shorter API version,
+	   then it made its way into 2.6.10 */
+	u32		pci_state[16];		/* saved PCI state for suspend/resume */
+#endif
+	u16		irq_mask;		/* interrupt types to mask out (not wanted) with many IRQs activated */
+	u16		irq_mask_off;		/* interrupt types to mask out (not wanted) with IRQs off */
+	unsigned int	irq_loops_this_jiffy;
+	unsigned long	irq_last_jiffies;
+
+	/* command interface */
+	void		*cmd_area;	/* points to PCI mapped memory */
+	void		*info_area;	/* points to PCI mapped memory */
+	u16		cmd_type;
+	u16		cmd_status;
+	u16		info_type;
+	u16		info_status;
+#endif
+
+	/*** USB stuff ***/
+#ifdef ACX_USB
+	struct usb_device	*usbdev;
+
+	rxbuffer_t	rxtruncbuf;
+	/* TODO: convert (bulkins,bulkrx_urbs,rxcons) triple into
+	** struct usb_rx (see struct usb_tx for an example) */
+	rxbuffer_t	bulkins[ACX100_USB_NUM_BULK_URBS];
+	struct urb	*bulkrx_urbs[ACX100_USB_NUM_BULK_URBS];
+	/* Used by rx urb completion handler in order to find
+	** corresponding priv/index pair */
+	acx_usb_bulk_context_t	rxcons[ACX100_USB_NUM_BULK_URBS];
+	usb_tx_t	usb_tx[ACX100_USB_NUM_BULK_URBS];
+
+	int		bulkinep;	/* bulk-in endpoint */
+	int		bulkoutep;	/* bulk-out endpoint */
+	int		usb_free_tx;
+	int		rxtruncsize;
+#endif
+
+};
+
+/* For use with ACX1xx_IE_RXCONFIG */
+/*  bit     description
+ *    13   include additional header (length etc.) *required*
+ *		struct is defined in 'struct rxbuffer'
+ *		is this bit acx100 only? does acx111 always put the header,
+ *		and bit setting is irrelevant? --vda
+ *    10   receive frames only with SSID used in last join cmd
+ *     9   discard broadcast
+ *     8   receive packets for multicast address 1
+ *     7   receive packets for multicast address 0
+ *     6   discard all multicast packets
+ *     5   discard frames from foreign BSSID
+ *     4   discard frames with foreign destination MAC address
+ *     3   promiscuous mode (receive ALL frames, disable filter)
+ *     2   include FCS
+ *     1   include phy header
+ *     0   ???
+ */
+#define RX_CFG1_INCLUDE_RXBUF_HDR	0x2000 /* ACX100 only */
+#define RX_CFG1_FILTER_SSID		0x0400
+#define RX_CFG1_FILTER_BCAST		0x0200
+#define RX_CFG1_RCV_MC_ADDR1		0x0100
+#define RX_CFG1_RCV_MC_ADDR0		0x0080
+#define RX_CFG1_FILTER_ALL_MULTI	0x0040
+#define RX_CFG1_FILTER_BSSID		0x0020
+#define RX_CFG1_FILTER_MAC		0x0010
+#define RX_CFG1_RCV_PROMISCUOUS		0x0008
+#define RX_CFG1_INCLUDE_FCS		0x0004
+#define RX_CFG1_INCLUDE_PHY_HDR		(WANT_PHY_HDR ? 0x0002 : 0)
+/*  bit     description
+ *    11   receive association requests etc.
+ *    10   receive authentication frames
+ *     9   receive beacon frames
+ *     8   receive contention free packets
+ *     7   receive control frames
+ *     6   receive data frames
+ *     5   receive broken frames
+ *     4   receive management frames
+ *     3   receive probe requests
+ *     2   receive probe responses
+ *     1   receive RTS/CTS/ACK frames
+ *     0   receive other
+ */
+#define RX_CFG2_RCV_ASSOC_REQ		0x0800
+#define RX_CFG2_RCV_AUTH_FRAMES		0x0400
+#define RX_CFG2_RCV_BEACON_FRAMES	0x0200
+#define RX_CFG2_RCV_CONTENTION_FREE	0x0100
+#define RX_CFG2_RCV_CTRL_FRAMES		0x0080
+#define RX_CFG2_RCV_DATA_FRAMES		0x0040
+#define RX_CFG2_RCV_BROKEN_FRAMES	0x0020
+#define RX_CFG2_RCV_MGMT_FRAMES		0x0010
+#define RX_CFG2_RCV_PROBE_REQ		0x0008
+#define RX_CFG2_RCV_PROBE_RESP		0x0004
+#define RX_CFG2_RCV_ACK_FRAMES		0x0002
+#define RX_CFG2_RCV_OTHER		0x0001
+
+/* For use with ACX1xx_IE_FEATURE_CONFIG */
+#define FEATURE1_80MHZ_CLOCK	0x00000040L
+#define FEATURE1_4X		0x00000020L
+#define FEATURE1_LOW_RX		0x00000008L
+#define FEATURE1_EXTRA_LOW_RX	0x00000001L
+
+#define FEATURE2_SNIFFER	0x00000080L
+#define FEATURE2_NO_TXCRYPT	0x00000001L
+
+/*-- get and set mask values --*/
+#define GETSET_LED_POWER	0x00000001L
+#define GETSET_STATION_ID	0x00000002L
+#define SET_TEMPLATES		0x00000004L
+#define SET_STA_LIST		0x00000008L
+#define GETSET_TX		0x00000010L
+#define GETSET_RX		0x00000020L
+#define SET_RXCONFIG		0x00000040L
+#define GETSET_ANTENNA		0x00000080L
+#define GETSET_SENSITIVITY	0x00000100L
+#define GETSET_TXPOWER		0x00000200L
+#define GETSET_ED_THRESH	0x00000400L
+#define GETSET_CCA		0x00000800L
+#define GETSET_POWER_80211	0x00001000L
+#define GETSET_RETRY		0x00002000L
+#define GETSET_REG_DOMAIN	0x00004000L
+#define GETSET_CHANNEL		0x00008000L
+/* Used when ESSID changes etc and we need to scan for AP anew */
+#define GETSET_RESCAN		0x00010000L
+#define GETSET_MODE		0x00020000L
+#define GETSET_WEP		0x00040000L
+#define SET_WEP_OPTIONS		0x00080000L
+#define SET_MSDU_LIFETIME	0x00100000L
+#define SET_RATE_FALLBACK	0x00200000L
+#define GETSET_ALL		0x80000000L
+
+
+/*============================================================================*
+ * Firmware loading                                                           *
+ *============================================================================*/
+/* Doh, 2.4.x also has CONFIG_FW_LOADER_MODULE
+ * (but doesn't have the new device model yet which we require!)
+ * FIXME: exact version that introduced new device handling? */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
+#define USE_FW_LOADER_26 1
+#define USE_FW_LOADER_LEGACY 0
+#else
+#define USE_FW_LOADER_26 0
+#define USE_FW_LOADER_LEGACY 1
+#endif
+#endif
+
+#if USE_FW_LOADER_26
+#include <linux/firmware.h>	/* request_firmware() */
+#include <linux/pci.h>		/* struct pci_device */
+#endif
+
+#if USE_FW_LOADER_LEGACY
+extern char *firmware_dir;
+#endif
+
+
+/***********************************************************************
+*/
+typedef struct acx100_ie_memblocksize {
+	u16	type ACX_PACKED;
+	u16	len ACX_PACKED;
+	u16	size ACX_PACKED;
+} acx100_ie_memblocksize_t;
+
+typedef struct acx100_ie_queueconfig {
+	u16	type ACX_PACKED;
+	u16	len ACX_PACKED;
+	u32	AreaSize ACX_PACKED;
+	u32	RxQueueStart ACX_PACKED;
+	u8	QueueOptions ACX_PACKED; /* queue options */
+	u8	NumTxQueues ACX_PACKED;	 /* # tx queues */
+	u8	NumRxDesc ACX_PACKED;	 /* for USB only */
+	u8	padf2 ACX_PACKED;	 /* # rx buffers */
+	u32	QueueEnd ACX_PACKED;
+	u32	HostQueueEnd ACX_PACKED; /* QueueEnd2 */
+	u32	TxQueueStart ACX_PACKED;
+	u8	TxQueuePri ACX_PACKED;
+	u8	NumTxDesc ACX_PACKED;
+	u16	pad ACX_PACKED;
+} acx100_ie_queueconfig_t;
+
+typedef struct acx111_ie_queueconfig {
+	u16	type ACX_PACKED;
+	u16	len ACX_PACKED;
+	u32	tx_memory_block_address ACX_PACKED;
+	u32	rx_memory_block_address ACX_PACKED;
+	u32	rx1_queue_address ACX_PACKED;
+	u32	reserved1 ACX_PACKED;
+	u32	tx1_queue_address ACX_PACKED;
+	u8	tx1_attributes ACX_PACKED;
+	u16	reserved2 ACX_PACKED;
+	u8	reserved3 ACX_PACKED;
+} acx111_ie_queueconfig_t;
+
+typedef struct acx100_ie_memconfigoption {
+	u16	type ACX_PACKED;
+	u16	len ACX_PACKED;
+	u32	DMA_config ACX_PACKED;
+	u32	pRxHostDesc ACX_PACKED;
+	u32	rx_mem ACX_PACKED;
+	u32	tx_mem ACX_PACKED;
+	u16	RxBlockNum ACX_PACKED;
+	u16	TxBlockNum ACX_PACKED;
+} acx100_ie_memconfigoption_t;
+
+typedef struct acx111_ie_memoryconfig {
+	u16	type ACX_PACKED;
+	u16	len ACX_PACKED;
+	u16	no_of_stations ACX_PACKED;
+	u16	memory_block_size ACX_PACKED;
+	u8	tx_rx_memory_block_allocation ACX_PACKED;
+	u8	count_rx_queues ACX_PACKED;
+	u8	count_tx_queues ACX_PACKED;
+	u8	options ACX_PACKED;
+	u8	fragmentation ACX_PACKED;
+	u16	reserved1 ACX_PACKED;
+	u8	reserved2 ACX_PACKED;
+
+	/* start of rx1 block */
+	u8	rx_queue1_count_descs ACX_PACKED;
+	u8	rx_queue1_reserved1 ACX_PACKED;
+	u8	rx_queue1_type ACX_PACKED; /* must be set to 7 */
+	u8	rx_queue1_prio ACX_PACKED; /* must be set to 0 */
+	u32	rx_queue1_host_rx_start ACX_PACKED;
+	/* end of rx1 block */
+
+	/* start of tx1 block */
+	u8	tx_queue1_count_descs ACX_PACKED;
+	u8	tx_queue1_reserved1 ACX_PACKED;
+	u8	tx_queue1_reserved2 ACX_PACKED;
+	u8	tx_queue1_attributes ACX_PACKED;
+	/* end of tx1 block */
+} acx111_ie_memoryconfig_t;
+
+typedef struct acx_ie_memmap {
+	u16	type ACX_PACKED;
+	u16	len ACX_PACKED;
+	u32	CodeStart ACX_PACKED;
+	u32	CodeEnd ACX_PACKED;
+	u32	WEPCacheStart ACX_PACKED;
+	u32	WEPCacheEnd ACX_PACKED;
+	u32	PacketTemplateStart ACX_PACKED;
+	u32	PacketTemplateEnd ACX_PACKED;
+	u32	QueueStart ACX_PACKED;
+	u32	QueueEnd ACX_PACKED;
+	u32	PoolStart ACX_PACKED;
+	u32	PoolEnd ACX_PACKED;
+} acx_ie_memmap_t;
+
+typedef struct ACX111FeatureConfig {
+	u16	type ACX_PACKED;
+	u16	len ACX_PACKED;
+	u32	feature_options ACX_PACKED;
+	u32	data_flow_options ACX_PACKED;
+} ACX111FeatureConfig_t;
+
+typedef struct ACX111TxLevel {
+	u16	type ACX_PACKED;
+	u16	len ACX_PACKED;
+	u8	level ACX_PACKED;
+} ACX111TxLevel_t;
+
+////typedef struct associd {
+////	u16	vala ACX_PACKED;
+////} associd_t;
+
+#define PS_CFG_ENABLE		0x80
+#define PS_CFG_PENDING		0x40 /* status flag when entering PS */
+#define PS_CFG_WAKEUP_MODE_MASK	0x07
+#define PS_CFG_WAKEUP_BY_HOST	0x03
+#define PS_CFG_WAKEUP_EACH_ITVL	0x02
+#define PS_CFG_WAKEUP_ON_DTIM	0x01
+#define PS_CFG_WAKEUP_ALL_BEAC	0x00
+
+/* Enhanced PS mode: sleep until Rx Beacon w/ the STA's AID bit set
+** in the TIM; newer firmwares only(?) */
+#define PS_OPT_ENA_ENHANCED_PS	0x04
+#define PS_OPT_STILL_RCV_BCASTS	0x01
+
+typedef struct acx100_ie_powermgmt {
+	u32	type ACX_PACKED;
+	u32	len ACX_PACKED;
+	u8	wakeup_cfg ACX_PACKED;
+	u8	listen_interval ACX_PACKED; /* for EACH_ITVL: wake up every "beacon units" interval */
+	u8	options ACX_PACKED;
+	u8	hangover_period ACX_PACKED; /* remaining wake time after Tx MPDU w/ PS bit, in values of 1/1024 seconds */
+	u16	enhanced_ps_transition_time ACX_PACKED; /* rem. wake time for Enh. PS */
+} acx100_ie_powermgmt_t;
+
+typedef struct acx111_ie_powermgmt {
+	u32	type ACX_PACKED;
+	u32	len ACX_PACKED;
+	u8	wakeup_cfg ACX_PACKED;
+	u8	listen_interval ACX_PACKED; /* for EACH_ITVL: wake up every "beacon units" interval */
+	u8	options ACX_PACKED;
+	u8	hangover_period ACX_PACKED; /* remaining wake time after Tx MPDU w/ PS bit, in values of 1/1024 seconds */
+	u32	beaconRxTime ACX_PACKED;
+	u32	enhanced_ps_transition_time ACX_PACKED; /* rem. wake time for Enh. PS */
+} acx111_ie_powermgmt_t;
+
+
+/***********************************************************************
+** Commands and template structures
+*/
+
+/*
+** SCAN command structure
+**
+** even though acx100 scan rates match RATE100 constants,
+** acx111 ones do not match! Therefore we do not use RATE100 #defines */
+#define ACX_SCAN_RATE_1		10
+#define ACX_SCAN_RATE_2		20
+#define ACX_SCAN_RATE_5		55
+#define ACX_SCAN_RATE_11	110
+#define ACX_SCAN_RATE_22	220
+#define ACX_SCAN_OPT_ACTIVE	0x00	/* a bit mask */
+#define ACX_SCAN_OPT_PASSIVE	0x01
+/* Background scan: we go into Power Save mode (by transmitting
+** NULL data frame to AP with the power mgmt bit set), do the scan,
+** and then exit Power Save mode. A plus is that AP buffers frames
+** for us while we do background scan. Thus we avoid frame losses.
+** Background scan can be active or passive, just like normal one */
+#define ACX_SCAN_OPT_BACKGROUND	0x02
+typedef struct acx100_scan {
+	u16	count ACX_PACKED;	/* number of scans to do, 0xffff == continuous */
+	u16	start_chan ACX_PACKED;
+	u16	flags ACX_PACKED;	/* channel list mask; 0x8000 == all channels? */
+	u8	max_rate ACX_PACKED;	/* max. probe rate */
+	u8	options ACX_PACKED;	/* bit mask, see defines above */
+	u16	chan_duration ACX_PACKED;
+	u16	max_probe_delay ACX_PACKED;
+} acx100_scan_t;			/* length 0xc */
+
+#define ACX111_SCAN_RATE_6	0x0B
+#define ACX111_SCAN_RATE_9	0x0F
+#define ACX111_SCAN_RATE_12	0x0A
+#define ACX111_SCAN_RATE_18	0x0E
+#define ACX111_SCAN_RATE_24	0x09
+#define ACX111_SCAN_RATE_36	0x0D
+#define ACX111_SCAN_RATE_48	0x08
+#define ACX111_SCAN_RATE_54	0x0C
+#define ACX111_SCAN_OPT_5GHZ    0x04	/* else 2.4GHZ */
+#define ACX111_SCAN_MOD_SHORTPRE 0x01	/* you can combine SHORTPRE and PBCC */
+#define ACX111_SCAN_MOD_PBCC	0x80
+#define ACX111_SCAN_MOD_OFDM	0x40
+typedef struct acx111_scan {
+	u16	count ACX_PACKED;		/* number of scans to do */
+	u8	channel_list_select ACX_PACKED; /* 0: scan all channels, 1: from chan_list only */
+	u16	reserved1 ACX_PACKED;
+	u8	reserved2 ACX_PACKED;
+	u8	rate ACX_PACKED;		/* rate for probe requests (if active scan) */
+	u8	options ACX_PACKED;		/* bit mask, see defines above */
+	u16	chan_duration ACX_PACKED;	/* min time to wait for reply on one channel (in TU) */
+						/* (active scan only) (802.11 section 11.1.3.2.2) */
+	u16	max_probe_delay ACX_PACKED;	/* max time to wait for reply on one channel (active scan) */
+						/* time to listen on a channel (passive scan) */
+	u8	modulation ACX_PACKED;
+	u8	channel_list[26] ACX_PACKED;	/* bits 7:0 first byte: channels 8:1 */
+						/* bits 7:0 second byte: channels 16:9 */
+						/* 26 bytes is enough to cover 802.11a */
+} acx111_scan_t;
+
+
+/*
+** Radio calibration command structure
+*/
+typedef struct acx111_cmd_radiocalib {
+/* 0x80000000 == automatic calibration by firmware, according to interval;
+ * bits 0..3: select calibration methods to go through:
+ * calib based on DC, AfeDC, Tx mismatch, Tx equilization */
+	u32	methods ACX_PACKED;
+	u32	interval ACX_PACKED;
+} acx111_cmd_radiocalib_t;
+
+
+/*
+** Packet template structures
+**
+** Packet templates store contents of Beacon, Probe response, Probe request,
+** Null data frame, and TIM data frame. Firmware automatically transmits
+** contents of template at appropriate time:
+** - Beacon: when configured as AP or Ad-hoc
+** - Probe response: when configured as AP or Ad-hoc, whenever
+**   a Probe request frame is received
+** - Probe request: when host issues SCAN command (active)
+** - Null data frame: when entering 802.11 power save mode
+** - TIM data: at the end of Beacon frames (if no TIM template
+**   is configured, then transmits default TIM)
+** NB:
+** - size field must be set to size of actual template
+**   (NOT sizeof(struct) - templates are variable in length),
+**   size field is not itself counted.
+** - members flagged with an asterisk must be initialized with host,
+**   rest must be zero filled.
+** - variable length fields shown only in comments */
+typedef struct acx_template_tim {
+	u16	size ACX_PACKED;
+	u8	tim_eid ACX_PACKED;	/* 00 1 TIM IE ID * */
+	u8	len ACX_PACKED;		/* 01 1 Length * */
+	u8	dtim_cnt ACX_PACKED;	/* 02 1 DTIM Count */
+	u8	dtim_period ACX_PACKED;	/* 03 1 DTIM Period */
+	u8	bitmap_ctrl ACX_PACKED;	/* 04 1 Bitmap Control * (except bit0) */
+					/* 05 n Partial Virtual Bitmap * */
+	u8	variable[0x100 - 1-1-1-1-1] ACX_PACKED;
+} acx_template_tim_t;
+
+typedef struct acx100_template_probereq {
+	u16	size ACX_PACKED;
+	u16	fc ACX_PACKED;		/* 00 2 fc */
+	u16	dur ACX_PACKED;		/* 02 2 Duration */
+	u8	da[6] ACX_PACKED;	/* 04 6 Destination Address * */
+	u8	sa[6] ACX_PACKED;	/* 0A 6 Source Address * */
+	u8	bssid[6] ACX_PACKED;	/* 10 6 BSSID * */
+	u16	seq ACX_PACKED;		/* 16 2 Sequence Control */
+	u8	timestamp[8] ACX_PACKED;/* 18 8 Timestamp */
+	u16	beacon_interval ACX_PACKED; /* 20 2 Beacon Interval * */
+	u16	cap ACX_PACKED;		/* 22 2 Capability Information * */
+					/* 24 n SSID * */
+					/* nn n Supported Rates * */
+	u8	variable[0x44 - 2-2-6-6-6-2-8-2-2] ACX_PACKED;
+} acx100_template_probereq_t;
+
+typedef struct acx111_template_probereq {
+	u16	size ACX_PACKED;
+	u16	fc ACX_PACKED;		/* 00 2 fc * */
+	u16	dur ACX_PACKED;		/* 02 2 Duration */
+	u8	da[6] ACX_PACKED;	/* 04 6 Destination Address * */
+	u8	sa[6] ACX_PACKED;	/* 0A 6 Source Address * */
+	u8	bssid[6] ACX_PACKED;	/* 10 6 BSSID * */
+	u16	seq ACX_PACKED;		/* 16 2 Sequence Control */
+					/* 18 n SSID * */
+					/* nn n Supported Rates * */
+	u8	variable[0x44 - 2-2-6-6-6-2] ACX_PACKED;
+} acx111_template_probereq_t;
+
+typedef struct acx_template_proberesp {
+	u16	size ACX_PACKED;
+	u16	fc ACX_PACKED;		/* 00 2 fc * (bits [15:12] and [10:8] per 802.11 section 7.1.3.1) */
+	u16	dur ACX_PACKED;		/* 02 2 Duration */
+	u8	da[6] ACX_PACKED;	/* 04 6 Destination Address */
+	u8	sa[6] ACX_PACKED;	/* 0A 6 Source Address */
+	u8	bssid[6] ACX_PACKED;	/* 10 6 BSSID */
+	u16	seq ACX_PACKED;		/* 16 2 Sequence Control */
+	u8	timestamp[8] ACX_PACKED;/* 18 8 Timestamp */
+	u16	beacon_interval ACX_PACKED; /* 20 2 Beacon Interval * */
+	u16	cap ACX_PACKED;		/* 22 2 Capability Information * */
+					/* 24 n SSID * */
+					/* nn n Supported Rates * */
+					/* nn 1 DS Parameter Set * */
+	u8	variable[0x54 - 2-2-6-6-6-2-8-2-2] ACX_PACKED;
+} acx_template_proberesp_t;
+#define acx_template_beacon_t acx_template_proberesp_t
+#define acx_template_beacon acx_template_proberesp
+
+
+/*
+** JOIN command structure
+**
+** as opposed to acx100, acx111 dtim interval is AFTER rates_basic111.
+** NOTE: took me about an hour to get !@#$%^& packing right --> struct packing is eeeeevil... */
+typedef struct acx_joinbss {
+	u8	bssid[ETH_ALEN] ACX_PACKED;
+	u16	beacon_interval ACX_PACKED;
+	union {
+		struct {
+			u8	dtim_interval ACX_PACKED;
+			u8	rates_basic ACX_PACKED;
+			u8	rates_supported ACX_PACKED;
+		} acx100 ACX_PACKED;
+		struct {
+			u16	rates_basic ACX_PACKED;
+			u8	dtim_interval ACX_PACKED;
+		} acx111 ACX_PACKED;
+	} u ACX_PACKED;
+	u8	genfrm_txrate ACX_PACKED;	/* generated frame (bcn, proberesp, RTS, PSpoll) tx rate */
+	u8	genfrm_mod_pre ACX_PACKED;	/* generated frame modulation/preamble:
+						** bit7: PBCC, bit6: OFDM (else CCK/DQPSK/DBPSK)
+						** bit5: short pre */
+	u8	macmode ACX_PACKED;	/* BSS Type, must be one of ACX_MODE_xxx */
+	u8	channel ACX_PACKED;
+	u8	essid_len ACX_PACKED;
+	char	essid[IW_ESSID_MAX_SIZE] ACX_PACKED;
+} acx_joinbss_t;
+
+#define JOINBSS_RATES_1		0x01
+#define JOINBSS_RATES_2		0x02
+#define JOINBSS_RATES_5		0x04
+#define JOINBSS_RATES_11	0x08
+#define JOINBSS_RATES_22	0x10
+
+/* Looks like missing bits are used to indicate 11g rates!
+** (it follows from the fact that constants below match 1:1 to RATE111_nn)
+** This was actually seen! Look at that Assoc Request sent by acx111,
+** it _does_ contain 11g rates in basic set:
+01:30:20.070772 Beacon (xxx) [1.0* 2.0* 5.5* 11.0* 6.0* 9.0* 12.0* 18.0* 24.0* 36.0* 48.0* 54.0* Mbit] ESS CH: 1
+01:30:20.074425 Authentication (Open System)-1: Succesful
+01:30:20.076539 Authentication (Open System)-2:
+01:30:20.076620 Acknowledgment
+01:30:20.088546 Assoc Request (xxx) [1.0* 2.0* 5.5* 6.0* 9.0* 11.0* 12.0* 18.0* 24.0* 36.0* 48.0* 54.0* Mbit]
+01:30:20.122413 Assoc Response AID(1) :: Succesful
+01:30:20.122679 Acknowledgment
+01:30:20.173204 Beacon (xxx) [1.0* 2.0* 5.5* 11.0* 6.0* 9.0* 12.0* 18.0* 24.0* 36.0* 48.0* 54.0* Mbit] ESS CH: 1
+*/
+#define JOINBSS_RATES_BASIC111_1	0x0001
+#define JOINBSS_RATES_BASIC111_2	0x0002
+#define JOINBSS_RATES_BASIC111_5	0x0004
+#define JOINBSS_RATES_BASIC111_11	0x0020
+#define JOINBSS_RATES_BASIC111_22	0x0100
+
+
+/***********************************************************************
+*/
+typedef struct mem_read_write {
+	u16	addr ACX_PACKED;
+	u16	type ACX_PACKED; /* 0x0 int. RAM / 0xffff MAC reg. / 0x81 PHY RAM / 0x82 PHY reg. */
+	u32	len ACX_PACKED;
+	u32	data ACX_PACKED;
+} mem_read_write_t;
+
+typedef struct acxp80211_nullframe {
+	u16	size ACX_PACKED;
+	struct wlan_hdr_a3 hdr ACX_PACKED;
+} acxp80211_nullframe_t;
+
+typedef struct {
+	u32	chksum ACX_PACKED;
+	u32	size ACX_PACKED;
+	u8	data[1] ACX_PACKED; /* the byte array of the actual firmware... */
+} firmware_image_t;
+
+typedef struct {
+	u32	offset ACX_PACKED;
+	u32	len ACX_PACKED;
+} acx_cmd_radioinit_t;
+
+typedef struct acx100_ie_wep_options {
+	u16	type ACX_PACKED;
+	u16	len ACX_PACKED;
+	u16	NumKeys ACX_PACKED;	/* max # of keys */
+	u8	WEPOption ACX_PACKED;	/* 0 == decrypt default key only, 1 == override decrypt */
+	u8	Pad ACX_PACKED;		/* used only for acx111 */
+} acx100_ie_wep_options_t;
+
+typedef struct ie_dot11WEPDefaultKey {
+	u16	type ACX_PACKED;
+	u16	len ACX_PACKED;
+	u8	action ACX_PACKED;
+	u8	keySize ACX_PACKED;
+	u8	defaultKeyNum ACX_PACKED;
+	u8	key[29] ACX_PACKED;	/* check this! was Key[19]. */
+} ie_dot11WEPDefaultKey_t;
+
+typedef struct acx111WEPDefaultKey {
+	u8	MacAddr[ETH_ALEN] ACX_PACKED;
+	u16	action ACX_PACKED; /* NOTE: this is a u16, NOT a u8!! */
+	u16	reserved ACX_PACKED;
+	u8	keySize ACX_PACKED;
+	u8	type ACX_PACKED;
+	u8	index ACX_PACKED;
+	u8	defaultKeyNum ACX_PACKED;
+	u8	counter[6] ACX_PACKED;
+	u8	key[32] ACX_PACKED;	/* up to 32 bytes (for TKIP!) */
+} acx111WEPDefaultKey_t;
+
+typedef struct ie_dot11WEPDefaultKeyID {
+	u16	type ACX_PACKED;
+	u16	len ACX_PACKED;
+	u8	KeyID ACX_PACKED;
+} ie_dot11WEPDefaultKeyID_t;
+
+typedef struct acx100_cmd_wep_mgmt {
+	u8	MacAddr[ETH_ALEN] ACX_PACKED;
+	u16	Action ACX_PACKED;
+	u16	KeySize ACX_PACKED;
+	u8	Key[29] ACX_PACKED; /* 29*8 == 232bits == WEP256 */
+} acx100_cmd_wep_mgmt_t;
+
+typedef struct defaultkey {
+	u8	num;
+} defaultkey_t;
+
+typedef struct acx_ie_generic {
+	u16	type ACX_PACKED;
+	u16	len ACX_PACKED;
+	union {
+		/* struct wep wp ACX_PACKED; */
+		////struct associd asid ACX_PACKED;
+		/* Association ID IE: just a 16bit value: */
+		u16	aid;
+		/* UNUSED? struct defaultkey dkey ACX_PACKED; */
+		/* generic member for quick implementation of commands */
+		u8	bytes[32] ACX_PACKED;
+	} m ACX_PACKED;
+} acx_ie_generic_t;
+
+/* Config Option structs */
+
+typedef struct co_antennas {
+	u8	type ACX_PACKED;
+	u8	len ACX_PACKED;
+	u8	list[2] ACX_PACKED;
+} co_antennas_t;
+
+typedef struct co_powerlevels {
+	u8	type ACX_PACKED;
+	u8	len ACX_PACKED;
+	u16	list[8] ACX_PACKED;
+} co_powerlevels_t;
+
+typedef struct co_datarates {
+	u8	type ACX_PACKED;
+	u8	len ACX_PACKED;
+	u8	list[8] ACX_PACKED;
+} co_datarates_t;
+
+typedef struct co_domains {
+	u8	type ACX_PACKED;
+	u8	len ACX_PACKED;
+	u8	list[6] ACX_PACKED;
+} co_domains_t;
+
+typedef struct co_product_id {
+	u8	type ACX_PACKED;
+	u8	len ACX_PACKED;
+	u8	list[128] ACX_PACKED;
+} co_product_id_t;
+
+typedef struct co_manuf_id {
+	u8	type ACX_PACKED;
+	u8	len ACX_PACKED;
+	u8	list[128] ACX_PACKED;
+} co_manuf_t;
+
+typedef struct co_fixed {
+	u8	type ACX_PACKED;
+	u8	len ACX_PACKED;
+	char	NVSv[8] ACX_PACKED;
+	u8	MAC[6] ACX_PACKED;
+	u16	probe_delay ACX_PACKED;
+	u32	eof_memory ACX_PACKED;
+	u8	dot11CCAModes ACX_PACKED;
+	u8	dot11Diversity ACX_PACKED;
+	u8	dot11ShortPreambleOption ACX_PACKED;
+	u8	dot11PBCCOption ACX_PACKED;
+	u8	dot11ChannelAgility ACX_PACKED;
+	u8	dot11PhyType ACX_PACKED;
+/*	u8	dot11TempType ACX_PACKED;
+	u8	num_var ACX_PACKED;           seems to be erased     */
+} co_fixed_t;
+
+
+typedef struct acx111_ie_configoption {
+	co_fixed_t		configoption_fixed ACX_PACKED;
+	co_antennas_t		antennas ACX_PACKED;
+	co_powerlevels_t	power_levels ACX_PACKED;
+	co_datarates_t		data_rates ACX_PACKED;
+	co_domains_t		domains ACX_PACKED;
+	co_product_id_t		product_id ACX_PACKED;
+	co_manuf_t		manufacturer ACX_PACKED;
+} acx111_ie_configoption_t;
+
+
+/*============================================================================*
+ * Global data                                                                *
+ *============================================================================*/
+extern const u8 bitpos2ratebyte[];
+extern const u8 bitpos2rate100[];
+
+extern const struct iw_handler_def acx_ioctl_handler_def;
+
+#define MINFREE_TX 3
diff -urN oldtree/drivers/net/wireless/tiacx/conv.c newtree/drivers/net/wireless/tiacx/conv.c
--- oldtree/drivers/net/wireless/tiacx/conv.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/conv.c	2006-02-13 19:20:00.272470880 -0500
@@ -0,0 +1,523 @@
+/***********************************************************************
+** Copyright (C) 2003  ACX100 Open Source Project
+**
+** The contents of this file are subject to the Mozilla Public
+** License Version 1.1 (the "License"); you may not use this file
+** except in compliance with the License. You may obtain a copy of
+** the License at http://www.mozilla.org/MPL/
+**
+** Software distributed under the License is distributed on an "AS
+** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+** implied. See the License for the specific language governing
+** rights and limitations under the License.
+**
+** Alternatively, the contents of this file may be used under the
+** terms of the GNU Public License version 2 (the "GPL"), in which
+** case the provisions of the GPL are applicable instead of the
+** above.  If you wish to allow the use of your version of this file
+** only under the terms of the GPL and not to allow others to use
+** your version of this file under the MPL, indicate your decision
+** by deleting the provisions above and replace them with the notice
+** and other provisions required by the GPL.  If you do not delete
+** the provisions above, a recipient may use your version of this
+** file under either the MPL or the GPL.
+** ---------------------------------------------------------------------
+** Inquiries regarding the ACX100 Open Source Project can be
+** made directly to:
+**
+** acx100-users@lists.sf.net
+** http://acx100.sf.net
+** ---------------------------------------------------------------------
+*/
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#if WIRELESS_EXT >= 13
+#include <net/iw_handler.h>
+#endif
+
+#include "acx.h"
+
+
+/*----------------------------------------------------------------
+* proto_is_stt
+*
+* Searches the 802.1h Selective Translation Table for a given
+* protocol.
+*
+* Arguments:
+*	prottype	protocol number (in host order) to search for.
+*
+* Returns:
+*	1 - if the table is empty or a match is found.
+*	0 - if the table is non-empty and a match is not found.
+*
+* Comment:
+*	Based largely on p80211conv.c of the linux-wlan-ng project
+*----------------------------------------------------------------*/
+static inline int
+proto_is_stt(unsigned int proto)
+{
+	/* Always return found for now.  This is the behavior used by the */
+	/*  Zoom Win95 driver when 802.1h mode is selected */
+	/* TODO: If necessary, add an actual search we'll probably
+		 need this to match the CMAC's way of doing things.
+		 Need to do some testing to confirm.
+	*/
+
+	if (proto == 0x80f3)  /* APPLETALK */
+		return 1;
+
+	return 0;
+/*	return ((prottype == ETH_P_AARP) || (prottype == ETH_P_IPX)); */
+}
+
+/* Helpers */
+
+static inline void
+store_llc_snap(struct wlan_llc *llc)
+{
+	llc->dsap = 0xaa;	/* SNAP, see IEEE 802 */
+	llc->ssap = 0xaa;
+	llc->ctl = 0x03;
+}
+static inline int
+llc_is_snap(const struct wlan_llc *llc)
+{
+	return (llc->dsap == 0xaa)
+	&& (llc->ssap == 0xaa)
+	&& (llc->ctl == 0x03);
+}
+static inline void
+store_oui_rfc1042(struct wlan_snap *snap)
+{
+	snap->oui[0] = 0;
+	snap->oui[1] = 0;
+	snap->oui[2] = 0;
+}
+static inline int
+oui_is_rfc1042(const struct wlan_snap *snap)
+{
+	return (snap->oui[0] == 0)
+	&& (snap->oui[1] == 0)
+	&& (snap->oui[2] == 0);
+}
+static inline void
+store_oui_8021h(struct wlan_snap *snap)
+{
+	snap->oui[0] = 0;
+	snap->oui[1] = 0;
+	snap->oui[2] = 0xf8;
+}
+static inline int
+oui_is_8021h(const struct wlan_snap *snap)
+{
+	return (snap->oui[0] == 0)
+	&& (snap->oui[1] == 0)
+	&& (snap->oui[2] == 0xf8);
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_ether_to_txbuf
+*
+* Uses the contents of the ether frame to build the elements of
+* the 802.11 frame.
+*
+* We don't actually set up the frame header here.  That's the
+* MAC's job.  We're only handling conversion of DIXII or 802.3+LLC
+* frames to something that works with 802.11.
+*
+* Comment:
+*	Based largely on p80211conv.c of the linux-wlan-ng project
+*----------------------------------------------------------------*/
+int
+acx_l_ether_to_txbuf(wlandevice_t *priv, void *txbuf, const struct sk_buff *skb)
+{
+	struct wlan_hdr_a3 *w_hdr;
+	struct wlan_ethhdr *e_hdr;
+	struct wlan_llc *e_llc;
+	struct wlan_snap *e_snap;
+	const u8 *a1, *a3;
+	int header_len, payload_len;
+	int result = -1;
+	/* protocol type or data length, depending on whether
+	 * DIX or 802.3 ethernet format */
+	u16 proto;
+	u16 fc;
+
+	FN_ENTER;
+
+	if (unlikely(!skb->len)) {
+		acxlog(L_DEBUG, "zero-length skb!\n");
+		goto end;
+	}
+
+	w_hdr = (struct wlan_hdr_a3*)txbuf;
+
+	switch (priv->mode) {
+	case ACX_MODE_MONITOR:
+		/* NB: one day we might want to play with DESC_CTL2_FCS
+		** Will need to stop doing "- WLAN_CRC_LEN" here then */
+		if (skb->len >= WLAN_A4FR_MAXLEN_WEP - WLAN_CRC_LEN) {
+			printk("%s: can't tx oversized frame (%d bytes)\n",
+				priv->netdev->name, skb->len);
+			goto end;
+		}
+		memcpy(w_hdr, skb->data, skb->len);
+		result = skb->len;
+		goto end;
+	}
+
+	/* step 1: classify ether frame, DIX or 802.3? */
+	e_hdr = (wlan_ethhdr_t *)skb->data;
+	proto = ntohs(e_hdr->type);
+	if (proto <= 1500) {
+		acxlog(L_DEBUG, "tx: 802.3 len: %d\n", skb->len);
+		/* codes <= 1500 reserved for 802.3 lengths */
+		/* it's 802.3, pass ether payload unchanged, */
+		/* trim off ethernet header and copy payload to tx_desc */
+		header_len = WLAN_HDR_A3_LEN;
+		/* TODO: must be equal to skb->len - sizeof(wlan_ethhdr_t), no? */
+		/* then we can do payload_len = ... after this big if() */
+		payload_len = proto;
+	} else {
+		/* it's DIXII, time for some conversion */
+		/* Create 802.11 packet. Header also contains llc and snap. */
+
+		acxlog(L_DEBUG, "tx: DIXII len: %d\n", skb->len);
+
+		/* size of header is 802.11 header + llc + snap */
+		header_len = WLAN_HDR_A3_LEN + sizeof(wlan_llc_t) + sizeof(wlan_snap_t);
+		/* llc is located behind the 802.11 header */
+		e_llc = (wlan_llc_t*)(w_hdr + 1);
+		/* snap is located behind the llc */
+		e_snap = (wlan_snap_t*)(e_llc + 1);
+
+		/* setup the LLC header */
+		store_llc_snap(e_llc);
+
+		/* setup the SNAP header */
+		e_snap->type = htons(proto);
+		if (proto_is_stt(proto)) {
+			store_oui_8021h(e_snap);
+		} else {
+			store_oui_rfc1042(e_snap);
+		}
+		/* trim off ethernet header and copy payload to tx_desc */
+		payload_len = skb->len - sizeof(wlan_ethhdr_t);
+	}
+	/* TODO: can we just let acx DMA payload from skb instead? */
+	memcpy((u8*)txbuf + header_len, skb->data + sizeof(wlan_ethhdr_t), payload_len);
+	payload_len += header_len;
+	result = payload_len;
+
+	/* Set up the 802.11 header */
+	switch (priv->mode) {
+	case ACX_MODE_0_ADHOC:
+		fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi);
+		a1 = e_hdr->daddr;
+		a3 = priv->bssid;
+		break;
+	case ACX_MODE_2_STA:
+		fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi | WF_FC_TODSi);
+		a1 = priv->bssid;
+		a3 = e_hdr->daddr;
+		break;
+	case ACX_MODE_3_AP:
+		fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi | WF_FC_FROMDSi);
+		a1 = e_hdr->daddr;
+		a3 = e_hdr->saddr;
+		break;
+	default:
+		printk("%s: error - converting eth to wlan in unknown mode\n",
+				priv->netdev->name);
+		result = -1;
+		goto end;
+	}
+	if (priv->wep_enabled)
+		SET_BIT(fc, WF_FC_ISWEPi);
+
+	w_hdr->fc = fc;
+	w_hdr->dur = 0;
+	MAC_COPY(w_hdr->a1, a1);
+	MAC_COPY(w_hdr->a2, priv->dev_addr);
+	MAC_COPY(w_hdr->a3, a3);
+	w_hdr->seq = 0;
+
+#if DEBUG_CONVERT
+	if (acx_debug & L_DATA) {
+		printk("original eth frame [%d]: ", skb->len);
+		acx_dump_bytes(skb->data, skb->len);
+		printk("802.11 frame [%d]: ", payload_len);
+		acx_dump_bytes(w_hdr, payload_len);
+	}
+#endif
+
+end:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_rxbuf_to_ether
+*
+* Uses the contents of a received 802.11 frame to build an ether
+* frame.
+*
+* This function extracts the src and dest address from the 802.11
+* frame to use in the construction of the eth frame.
+*
+* Based largely on p80211conv.c of the linux-wlan-ng project
+*----------------------------------------------------------------*/
+struct sk_buff*
+acx_rxbuf_to_ether(wlandevice_t *priv, rxbuffer_t *rxbuf)
+{
+	struct wlan_hdr *w_hdr;
+	struct wlan_ethhdr *e_hdr;
+	struct wlan_llc *e_llc;
+	struct wlan_snap *e_snap;
+	struct sk_buff *skb;
+	const u8 *daddr;
+	const u8 *saddr;
+	const u8 *e_payload;
+	int buflen;
+	int payload_length;
+	unsigned int payload_offset;
+	u16 fc;
+
+	FN_ENTER;
+
+	/* This looks complex because it must handle possible
+	** phy header in rxbuff */
+	w_hdr = acx_get_wlan_hdr(priv, rxbuf);
+	payload_offset = WLAN_HDR_A3_LEN; /* it is relative to w_hdr */
+	payload_length = RXBUF_BYTES_USED(rxbuf) /* entire rxbuff... */
+		- ((u8*)w_hdr - (u8*)rxbuf) /* minus space before 802.11 frame */
+		- WLAN_HDR_A3_LEN; /* minus 802.11 header */
+
+	/* setup some vars for convenience */
+	fc = w_hdr->fc;
+	switch (WF_FC_FROMTODSi & fc) {
+	case 0:
+		daddr = w_hdr->a1;
+		saddr = w_hdr->a2;
+		break;
+	case WF_FC_FROMDSi:
+		daddr = w_hdr->a1;
+		saddr = w_hdr->a3;
+		break;
+	case WF_FC_TODSi:
+		daddr = w_hdr->a3;
+		saddr = w_hdr->a2;
+		break;
+	default: /* WF_FC_FROMTODSi */
+		payload_offset += (WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN);
+		payload_length -= (WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN);
+		if (unlikely(0 > payload_length)) {
+			acxlog(L_DEBUG, "A4 frame too short!\n");
+			goto ret_null;
+		}
+		daddr = w_hdr->a3;
+		saddr = w_hdr->a4;
+	}
+
+	if ((WF_FC_ISWEPi & fc) && (CHIPTYPE_ACX100 == priv->chip_type)) {
+		/* chop off the IV+ICV WEP header and footer */
+		acxlog(L_DATA | L_DEBUG, "rx: WEP packet, "
+			"chopping off IV and ICV\n");
+		payload_length -= 8;
+		payload_offset += 4;
+	}
+
+	e_hdr = (wlan_ethhdr_t*) ((u8*) w_hdr + payload_offset);
+
+	e_llc = (wlan_llc_t*) e_hdr;
+	e_snap = (wlan_snap_t*) (e_llc+1);
+	e_payload = (u8*) (e_snap+1);
+
+	acxlog(L_DATA, "rx: payload_offset %d, payload_length %d\n",
+		payload_offset, payload_length);
+	acxlog(L_XFER | L_DATA,
+		"rx: frame info: llc.dsap %X, llc.ssap %X, llc.ctl %X, "
+		"snap.oui %02X%02X%02X, snap.type %X\n",
+		e_llc->dsap, e_llc->ssap,
+		e_llc->ctl, e_snap->oui[0],
+		e_snap->oui[1], e_snap->oui[2],
+		e_snap->type);
+
+	/* Test for the various encodings */
+	if ((payload_length >= sizeof(wlan_ethhdr_t))
+	 && ((e_llc->dsap != 0xaa) || (e_llc->ssap != 0xaa))
+	 && (   (mac_is_equal(daddr, e_hdr->daddr))
+	     || (mac_is_equal(saddr, e_hdr->saddr))
+	    )
+	) {
+		acxlog(L_DEBUG | L_DATA, "rx: 802.3 ENCAP len: %d\n", payload_length);
+		/* 802.3 Encapsulated */
+		/* Test for an overlength frame */
+
+		if (unlikely(payload_length > WLAN_MAX_ETHFRM_LEN)) {
+			/* A bogus length ethfrm has been encap'd. */
+			/* Is someone trying an oflow attack? */
+			printk("%s: rx: ENCAP frame too large (%d > %d)\n",
+				priv->netdev->name, payload_length, WLAN_MAX_ETHFRM_LEN);
+			goto ret_null;
+		}
+
+		/* allocate space and setup host buffer */
+		buflen = payload_length;
+		/* FIXME: implement skb ring buffer similar to
+		 * xircom_tulip_cb.c? */
+		/* Attempt to align IP header (14 bytes eth header + 2 = 16) */
+		skb = dev_alloc_skb(buflen + 2);
+		if (unlikely(!skb))
+			goto no_skb;
+		skb_reserve(skb, 2);
+		skb_put(skb, buflen);		/* make room */
+
+		/* now copy the data from the 80211 frame */
+		memcpy(skb->data, e_hdr, payload_length);	/* copy the data */
+
+	} else if ( (payload_length >= sizeof(wlan_llc_t)+sizeof(wlan_snap_t))
+		 && llc_is_snap(e_llc) ) {
+		/* it's a SNAP */
+
+		if ( !oui_is_rfc1042(e_snap)
+		 || (proto_is_stt(ieee2host16(e_snap->type)) /* && (ethconv == WLAN_ETHCONV_8021h) */)) {
+			acxlog(L_DEBUG | L_DATA, "rx: SNAP+RFC1042 len: %d\n", payload_length);
+			/* it's a SNAP + RFC1042 frame && protocol is in STT */
+			/* build 802.3 + RFC1042 */
+
+			/* Test for an overlength frame */
+			if (unlikely(payload_length + WLAN_ETHHDR_LEN > WLAN_MAX_ETHFRM_LEN)) {
+				/* A bogus length ethfrm has been sent. */
+				/* Is someone trying an oflow attack? */
+				printk("%s: rx: SNAP frame too large (%d > %d)\n",
+					priv->netdev->name,
+					payload_length, WLAN_MAX_ETHFRM_LEN);
+				goto ret_null;
+			}
+
+			/* allocate space and setup host buffer */
+			buflen = payload_length + WLAN_ETHHDR_LEN;
+			skb = dev_alloc_skb(buflen + 2);
+			if (unlikely(!skb))
+				goto no_skb;
+			skb_reserve(skb, 2);
+			skb_put(skb, buflen);		/* make room */
+
+			/* create 802.3 header */
+			e_hdr = (wlan_ethhdr_t*) skb->data;
+			MAC_COPY(e_hdr->daddr, daddr);
+			MAC_COPY(e_hdr->saddr, saddr);
+			e_hdr->type = htons(payload_length);
+
+			/* Now copy the data from the 80211 frame.
+			   Make room in front for the eth header, and keep the
+			   llc and snap from the 802.11 payload */
+			memcpy(skb->data + WLAN_ETHHDR_LEN,
+			       e_llc,
+			       payload_length);
+
+		} else {
+			acxlog(L_DEBUG | L_DATA, "rx: 802.1h/RFC1042 len: %d\n",
+				payload_length);
+			/* it's an 802.1h frame (an RFC1042 && protocol is not in STT) */
+			/* build a DIXII + RFC894 */
+
+			/* Test for an overlength frame */
+			if (unlikely(payload_length - sizeof(wlan_llc_t) - sizeof(wlan_snap_t) + WLAN_ETHHDR_LEN > WLAN_MAX_ETHFRM_LEN)) {
+				/* A bogus length ethfrm has been sent. */
+				/* Is someone trying an oflow attack? */
+				printk("%s: rx: DIXII frame too large (%d > %d)\n",
+					priv->netdev->name,
+					(int)(payload_length - sizeof(wlan_llc_t) - sizeof(wlan_snap_t)),
+					WLAN_MAX_ETHFRM_LEN - WLAN_ETHHDR_LEN);
+				goto ret_null;
+			}
+
+			/* allocate space and setup host buffer */
+			buflen = payload_length + WLAN_ETHHDR_LEN
+				- sizeof(wlan_llc_t) - sizeof(wlan_snap_t);
+			skb = dev_alloc_skb(buflen + 2);
+			if (unlikely(!skb))
+				goto no_skb;
+			skb_reserve(skb, 2);
+			skb_put(skb, buflen);		/* make room */
+
+			/* create 802.3 header */
+			e_hdr = (wlan_ethhdr_t *) skb->data;
+			MAC_COPY(e_hdr->daddr, daddr);
+			MAC_COPY(e_hdr->saddr, saddr);
+			e_hdr->type = e_snap->type;
+
+			/* Now copy the data from the 80211 frame.
+			   Make room in front for the eth header, and cut off the
+			   llc and snap from the 802.11 payload */
+			memcpy(skb->data + WLAN_ETHHDR_LEN, e_payload,
+			       payload_length - sizeof(wlan_llc_t) - sizeof(wlan_snap_t));
+		}
+
+	} else {
+		acxlog(L_DEBUG | L_DATA, "rx: NON-ENCAP len: %d\n", payload_length);
+		/* any NON-ENCAP */
+		/* it's a generic 80211+LLC or IPX 'Raw 802.3' */
+		/*  build an 802.3 frame */
+		/* allocate space and setup hostbuf */
+
+		/* Test for an overlength frame */
+		if (unlikely(payload_length + WLAN_ETHHDR_LEN > WLAN_MAX_ETHFRM_LEN)) {
+			/* A bogus length ethfrm has been sent. */
+			/* Is someone trying an oflow attack? */
+			printk("%s: rx: OTHER frame too large (%d > %d)\n",
+				priv->netdev->name, payload_length,
+				WLAN_MAX_ETHFRM_LEN - WLAN_ETHHDR_LEN);
+			goto ret_null;
+		}
+
+		/* allocate space and setup host buffer */
+		buflen = payload_length + WLAN_ETHHDR_LEN;
+		skb = dev_alloc_skb(buflen + 2);
+		if (unlikely(!skb))
+			goto no_skb;
+		skb_reserve(skb, 2);
+		skb_put(skb, buflen);		/* make room */
+
+		/* set up the 802.3 header */
+		e_hdr = (wlan_ethhdr_t *) skb->data;
+		MAC_COPY(e_hdr->daddr, daddr);
+		MAC_COPY(e_hdr->saddr, saddr);
+		e_hdr->type = htons(payload_length);
+
+		/* now copy the data from the 80211 frame */
+		memcpy(skb->data + WLAN_ETHHDR_LEN, e_llc, payload_length);
+	}
+
+	skb->dev = priv->netdev;
+	skb->protocol = eth_type_trans(skb, priv->netdev);
+
+#if DEBUG_CONVERT
+	if (acx_debug & L_DATA) {
+		printk("p802.11 frame [%d]: ", RXBUF_BYTES_RCVD(rxbuf));
+		acx_dump_bytes(w_hdr, RXBUF_BYTES_RCVD(rxbuf));
+		printk("eth frame [%d]: ", skb->len);
+		acx_dump_bytes(skb->data, skb->len);
+	}
+#endif
+
+	FN_EXIT0;
+	return skb;
+
+no_skb:
+	printk("%s: rx: no memory for skb (%d bytes)\n",
+			priv->netdev->name, buflen + 2);
+ret_null:
+	FN_EXIT1((int)NULL);
+	return NULL;
+}
diff -urN oldtree/drivers/net/wireless/tiacx/helper.c newtree/drivers/net/wireless/tiacx/helper.c
--- oldtree/drivers/net/wireless/tiacx/helper.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/helper.c	2006-02-13 19:20:00.277470120 -0500
@@ -0,0 +1,4304 @@
+/***********************************************************************
+** Copyright (C) 2003  ACX100 Open Source Project
+**
+** The contents of this file are subject to the Mozilla Public
+** License Version 1.1 (the "License"); you may not use this file
+** except in compliance with the License. You may obtain a copy of
+** the License at http://www.mozilla.org/MPL/
+**
+** Software distributed under the License is distributed on an "AS
+** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+** implied. See the License for the specific language governing
+** rights and limitations under the License.
+**
+** Alternatively, the contents of this file may be used under the
+** terms of the GNU Public License version 2 (the "GPL"), in which
+** case the provisions of the GPL are applicable instead of the
+** above.  If you wish to allow the use of your version of this file
+** only under the terms of the GPL and not to allow others to use
+** your version of this file under the MPL, indicate your decision
+** by deleting the provisions above and replace them with the notice
+** and other provisions required by the GPL.  If you do not delete
+** the provisions above, a recipient may use your version of this
+** file under either the MPL or the GPL.
+** ---------------------------------------------------------------------
+** Inquiries regarding the ACX100 Open Source Project can be
+** made directly to:
+**
+** acx100-users@lists.sf.net
+** http://acx100.sf.net
+** ---------------------------------------------------------------------
+*/
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#ifdef ACX_PCI
+#include <linux/pci.h>
+#endif
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/wireless.h>
+#if WIRELESS_EXT >= 13
+#include <net/iw_handler.h>
+#endif
+#include <linux/pm.h>
+#include <linux/delay.h>
+
+#include "acx.h"
+
+
+/***********************************************************************
+*/
+
+/* Now that the pci_alloc_consistent() problem has been resolved,
+ * feel free to modify buffer count for ACX100 to 32, too.
+ * But it's not required since the card isn't too fast anyway */
+#define RXBUFFERCOUNT_ACX100 16
+#define TXBUFFERCOUNT_ACX100 16
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 53)
+/* dma_alloc_coherent() uses GFP_KERNEL, much less problematic than
+ * the pci_alloc_consistent() used below using GFP_ATOMIC (quite often causes
+ * a larger alloc to fail), so use less buffers there to be more successful */
+#define RXBUFFERCOUNT_ACX111 32
+#define TXBUFFERCOUNT_ACX111 32
+#else
+#define RXBUFFERCOUNT_ACX111 16
+#define TXBUFFERCOUNT_ACX111 16
+#endif
+#define TXBUFFERCOUNT_USB 10
+#define RXBUFFERCOUNT_USB 10
+
+
+/***********************************************************************
+*/
+
+/* minutes to wait until next radio recalibration: */
+#define RECALIB_PAUSE	5
+
+const u8 reg_domain_ids[] =
+	{ 0x10, 0x20, 0x30, 0x31, 0x32, 0x40, 0x41, 0x51 };
+/* stupid workaround for the fact that in C the size of an external array
+ * cannot be determined from within a second file */
+const u8 reg_domain_ids_len = sizeof(reg_domain_ids);
+const u16 reg_domain_channel_masks[] =
+	{ 0x07ff, 0x07ff, 0x1fff, 0x0600, 0x1e00, 0x2000, 0x3fff, 0x01fc };
+
+
+/***********************************************************************
+** Debugging support
+*/
+#ifdef PARANOID_LOCKING
+static unsigned max_lock_time;
+static unsigned max_sem_time;
+
+void
+acx_lock_unhold() { max_lock_time = 0; }
+void
+acx_sem_unhold() { max_sem_time = 0; }
+
+static inline const char*
+sanitize_str(const char *s)
+{
+	const char* t = strrchr(s, '/');
+	if (t) return t + 1;
+	return s;
+}
+
+void
+acx_lock_debug(wlandevice_t *priv, const char* where)
+{
+	int count = 100*1000*1000;
+	where = sanitize_str(where);
+	while (--count) {
+		if (!spin_is_locked(&priv->lock)) break;
+		cpu_relax();
+	}
+	if (!count) {
+		printk(KERN_EMERG "LOCKUP: already taken at %s!\n", priv->last_lock);
+		BUG();
+	}
+	priv->last_lock = where;
+	rdtscl(priv->lock_time);
+}
+void
+acx_unlock_debug(wlandevice_t *priv, const char* where)
+{
+	unsigned diff;
+#ifdef SMP
+	if (!spin_is_locked(&priv->lock)) {
+		where = sanitize_str(where);
+		printk(KERN_EMERG "STRAY UNLOCK at %s!\n", where);
+		BUG();
+	}
+#endif
+	rdtscl(diff);
+	diff -= priv->lock_time;
+	if (diff > max_lock_time) {
+		where = sanitize_str(where);
+		printk("max lock hold time %d CPU ticks "
+			"from %s to %s\n", diff, priv->last_lock, where);
+		max_lock_time = diff;
+	}
+}
+void
+acx_down_debug(wlandevice_t *priv, const char* where)
+{
+	int sem_count;
+	int count = 5000/5;
+	where = sanitize_str(where);
+
+	while (--count) {
+		sem_count = atomic_read(&priv->sem.count);
+		if (sem_count) break;
+		msleep(5);
+	}
+	if (!count) {
+		printk(KERN_EMERG "D STATE at %s! last sem at %s\n",
+			where, priv->last_sem);
+		dump_stack();
+	}
+	priv->last_sem = where;
+	priv->sem_time = jiffies;
+	down(&priv->sem);
+	if (acx_debug & L_LOCK) {
+		printk("%s: sem_down %d -> %d\n",
+			where, sem_count, atomic_read(&priv->sem.count));
+	}
+}
+void
+acx_up_debug(wlandevice_t *priv, const char* where)
+{
+	unsigned diff;
+	int sem_count = atomic_read(&priv->sem.count);
+	if (sem_count) {
+		where = sanitize_str(where);
+		printk(KERN_EMERG "STRAY UP at %s! sem.count=%d\n", where, sem_count);
+		dump_stack();
+	}
+	diff = jiffies - priv->sem_time;
+	if (diff > max_sem_time) {
+		where = sanitize_str(where);
+		printk("max sem hold time %d jiffies "
+			"from %s to %s\n", diff, priv->last_sem, where);
+		max_sem_time = diff;
+	}
+	up(&priv->sem);
+	if (acx_debug & L_LOCK) {
+		where = sanitize_str(where);
+		printk("%s: sem_up %d -> %d\n",
+			where, sem_count, atomic_read(&priv->sem.count));
+	}
+}
+#endif /* PARANOID_LOCKING */
+
+
+/***********************************************************************
+*/
+#if ACX_DEBUG > 1
+
+static int acx_debug_func_indent;
+#define DEBUG_TSC 0
+#define FUNC_INDENT_INCREMENT 2
+
+#if DEBUG_TSC
+#define TIMESTAMP(d) unsigned long d; rdtscl(d)
+#else
+#define TIMESTAMP(d) unsigned long d = jiffies
+#endif
+
+static const char
+spaces[] = "          " "          "; /* Nx10 spaces */
+
+void
+log_fn_enter(const char *funcname)
+{
+	int indent;
+	TIMESTAMP(d);
+
+	indent = acx_debug_func_indent;
+	if (indent >= sizeof(spaces))
+		indent = sizeof(spaces)-1;
+
+	printk("%08lx %s==> %s\n",
+		d,
+		spaces + (sizeof(spaces)-1) - indent,
+		funcname
+	);
+
+	acx_debug_func_indent += FUNC_INDENT_INCREMENT;
+}
+void
+log_fn_exit(const char *funcname)
+{
+	int indent;
+	TIMESTAMP(d);
+
+	acx_debug_func_indent -= FUNC_INDENT_INCREMENT;
+
+	indent = acx_debug_func_indent;
+	if (indent >= sizeof(spaces))
+		indent = sizeof(spaces)-1;
+
+	printk("%08lx %s<== %s\n",
+		d,
+		spaces + (sizeof(spaces)-1) - indent,
+		funcname
+	);
+}
+void
+log_fn_exit_v(const char *funcname, int v)
+{
+	int indent;
+	TIMESTAMP(d);
+
+	acx_debug_func_indent -= FUNC_INDENT_INCREMENT;
+
+	indent = acx_debug_func_indent;
+	if (indent >= sizeof(spaces))
+		indent = sizeof(spaces)-1;
+
+	printk("%08lx %s<== %s: %08X\n",
+		d,
+		spaces + (sizeof(spaces)-1) - indent,
+		funcname,
+		v
+	);
+}
+#endif /* ACX_DEBUG > 1 */
+
+
+/***********************************************************************
+** Basically a msleep with logging
+*/
+void
+acx_s_msleep(int ms)
+{
+	FN_ENTER;
+	msleep(ms);
+	FN_EXIT0;
+}
+
+
+/*----------------------------------------------------------------
+* acx_get_packet_type_string
+*----------------------------------------------------------------*/
+#if ACX_DEBUG
+const char*
+acx_get_packet_type_string(u16 fc)
+{
+	static const char * const mgmt_arr[] = {
+		"MGMT/AssocReq", "MGMT/AssocResp", "MGMT/ReassocReq",
+		"MGMT/ReassocResp", "MGMT/ProbeReq", "MGMT/ProbeResp",
+		"MGMT/UNKNOWN", "MGMT/UNKNOWN", "MGMT/Beacon", "MGMT/ATIM",
+		"MGMT/Disassoc", "MGMT/Authen", "MGMT/Deauthen"
+	};
+	static const char * const ctl_arr[] = {
+		"CTL/PSPoll", "CTL/RTS", "CTL/CTS", "CTL/Ack", "CTL/CFEnd",
+		"CTL/CFEndCFAck"
+	};
+	static const char * const data_arr[] = {
+		"DATA/DataOnly", "DATA/Data CFAck", "DATA/Data CFPoll",
+		"DATA/Data CFAck/CFPoll", "DATA/Null", "DATA/CFAck",
+		"DATA/CFPoll", "DATA/CFAck/CFPoll"
+	};
+	const char *str = "UNKNOWN";
+	u8 fstype = (WF_FC_FSTYPE & fc) >> 4;
+	u8 ctl;
+
+	FN_ENTER;
+	switch (WF_FC_FTYPE & fc) {
+	case WF_FTYPE_MGMT:
+		str = "MGMT/UNKNOWN";
+		if (fstype < VEC_SIZE(mgmt_arr))
+			str = mgmt_arr[fstype];
+		break;
+	case WF_FTYPE_CTL:
+		ctl = fstype - 0x0a;
+		str = "CTL/UNKNOWN";
+		if (ctl < VEC_SIZE(ctl_arr))
+			str = ctl_arr[ctl];
+		break;
+	case WF_FTYPE_DATA:
+		str = "DATA/UNKNOWN";
+		if (fstype < VEC_SIZE(data_arr))
+			str = data_arr[fstype];
+		break;
+	}
+	FN_EXIT0;
+	return str;
+}
+#endif
+
+
+/***********************************************************************
+** maps acx111 tx descr rate field to acx100 one
+*/
+const u8
+bitpos2rate100[] = {
+	RATE100_1	,/* 0 */
+	RATE100_2	,/* 1 */
+	RATE100_5	,/* 2 */
+	RATE100_2	,/* 3, should not happen */
+	RATE100_2	,/* 4, should not happen */
+	RATE100_11	,/* 5 */
+	RATE100_2	,/* 6, should not happen */
+	RATE100_2	,/* 7, should not happen */
+	RATE100_22	,/* 8 */
+	RATE100_2	,/* 9, should not happen */
+	RATE100_2	,/* 10, should not happen */
+	RATE100_2	,/* 11, should not happen */
+	RATE100_2	,/* 12, should not happen */
+	RATE100_2	,/* 13, should not happen */
+	RATE100_2	,/* 14, should not happen */
+	RATE100_2	,/* 15, should not happen */
+};
+
+u8
+acx_rate111to100(u16 r) {
+	return bitpos2rate100[highest_bit(r)];
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_rxmonitor
+* Called from IRQ context only
+*----------------------------------------------------------------*/
+static void
+acx_l_rxmonitor(wlandevice_t *priv, const rxbuffer_t *rxbuf)
+{
+	wlansniffrm_t *msg;
+	struct sk_buff *skb;
+	void *datap;
+	unsigned int skb_len;
+	int payload_offset;
+
+	FN_ENTER;
+
+	/* we are in big luck: the acx100 doesn't modify any of the fields */
+	/* in the 802.11 frame. just pass this packet into the PF_PACKET */
+	/* subsystem. yeah. */
+#if 0
+	if (!(priv->rx_config_1 & RX_CFG1_INCLUDE_RXBUF_HDR)) {
+		printk("rx_config_1 is missing RX_CFG1_INCLUDE_RXBUF_HDR\n");
+		goto end;
+	}
+#endif
+	payload_offset = ((u8*)acx_get_wlan_hdr(priv, rxbuf) - (u8*)rxbuf);
+	skb_len = RXBUF_BYTES_USED(rxbuf) - payload_offset;
+
+	/* sanity check */
+	if (skb_len > (WLAN_A4FR_MAXLEN_WEP)) {
+		printk("monitor mode panic: oversized frame!\n");
+		goto end;
+	}
+
+	if (priv->netdev->type == ARPHRD_IEEE80211_PRISM)
+		skb_len += sizeof(*msg);
+
+	/* allocate skb */
+	skb = dev_alloc_skb(skb_len);
+	if (!skb) {
+		printk("%s: no memory for skb (%u bytes)\n",
+				priv->netdev->name, skb_len);
+		goto end;
+	}
+
+	skb_put(skb, skb_len);
+
+		/* when in raw 802.11 mode, just copy frame as-is */
+	if (priv->netdev->type == ARPHRD_IEEE80211)
+		datap = skb->data;
+	else { /* otherwise, emulate prism header */
+		msg = (wlansniffrm_t*)skb->data;
+		datap = msg + 1;
+
+		msg->msgcode = WLANSNIFFFRM;
+		msg->msglen = sizeof(*msg);
+		strncpy(msg->devname, priv->netdev->name, sizeof(msg->devname)-1);
+		msg->devname[sizeof(msg->devname)-1] = '\0';
+
+		msg->hosttime.did = WLANSNIFFFRM_hosttime;
+		msg->hosttime.status = WLANITEM_STATUS_data_ok;
+		msg->hosttime.len = 4;
+		msg->hosttime.data = jiffies;
+
+		msg->mactime.did = WLANSNIFFFRM_mactime;
+		msg->mactime.status = WLANITEM_STATUS_data_ok;
+		msg->mactime.len = 4;
+		msg->mactime.data = rxbuf->time;
+
+		msg->channel.did = WLANSNIFFFRM_channel;
+		msg->channel.status = WLANITEM_STATUS_data_ok;
+		msg->channel.len = 4;
+		msg->channel.data = priv->channel;
+
+		msg->rssi.did = WLANSNIFFFRM_rssi;
+		msg->rssi.status = WLANITEM_STATUS_no_value;
+		msg->rssi.len = 4;
+		msg->rssi.data = 0;
+
+		msg->sq.did = WLANSNIFFFRM_sq;
+		msg->sq.status = WLANITEM_STATUS_no_value;
+		msg->sq.len = 4;
+		msg->sq.data = 0;
+
+		msg->signal.did = WLANSNIFFFRM_signal;
+		msg->signal.status = WLANITEM_STATUS_data_ok;
+		msg->signal.len = 4;
+		msg->signal.data = rxbuf->phy_snr;
+
+		msg->noise.did = WLANSNIFFFRM_noise;
+		msg->noise.status = WLANITEM_STATUS_data_ok;
+		msg->noise.len = 4;
+		msg->noise.data = rxbuf->phy_level;
+
+		msg->rate.did = WLANSNIFFFRM_rate;
+		msg->rate.status = WLANITEM_STATUS_data_ok;
+		msg->rate.len = 4;
+		msg->rate.data = rxbuf->phy_plcp_signal/5;
+
+		msg->istx.did = WLANSNIFFFRM_istx;
+		msg->istx.status = WLANITEM_STATUS_data_ok;
+		msg->istx.len = 4;
+		msg->istx.data = 0;	/* tx=0: it's not a tx packet */
+
+		skb_len -= sizeof(*msg);
+
+		msg->frmlen.did = WLANSNIFFFRM_signal;
+		msg->frmlen.status = WLANITEM_STATUS_data_ok;
+		msg->frmlen.len = 4;
+		msg->frmlen.data = skb_len;
+	}
+
+	memcpy(datap, ((unsigned char*)rxbuf)+payload_offset, skb_len);
+
+	skb->dev = priv->netdev;
+	skb->dev->last_rx = jiffies;
+
+	skb->mac.raw = skb->data;
+	skb->ip_summed = CHECKSUM_NONE;
+	skb->pkt_type = PACKET_OTHERHOST;
+	skb->protocol = htons(ETH_P_80211_RAW);
+	netif_rx(skb);
+
+	priv->stats.rx_packets++;
+	priv->stats.rx_bytes += skb->len;
+end:
+	FN_EXIT0;
+}
+
+
+/***********************************************************************
+*/
+/*
+ * Calculate level like the feb 2003 windows driver seems to do
+ */
+u8
+acx_signal_to_winlevel(u8 rawlevel)
+{
+	/* u8 winlevel = (u8) (0.5 + 0.625 * rawlevel); */
+	u8 winlevel = ((4 + (rawlevel * 5)) / 8);
+
+	if (winlevel > 100)
+		winlevel = 100;
+	return winlevel;
+}
+
+u8
+acx_signal_determine_quality(u8 signal, u8 noise)
+{
+	int qual;
+
+	qual = (((signal - 30) * 100 / 70) + (100 - noise * 4)) / 2;
+
+	if (qual > 100)
+		return 100;
+	if (qual < 0)
+		return 0;
+	return qual;
+}
+
+
+/***************************************************************
+** acx_l_process_rxbuf
+**
+** NB: used by USB code also
+*/
+void
+acx_l_process_rxbuf(wlandevice_t *priv, rxbuffer_t *rxbuf)
+{
+	struct wlan_hdr *hdr;
+	unsigned int buf_len;
+	unsigned int qual;
+	u16 fc;
+
+	hdr = acx_get_wlan_hdr(priv, rxbuf);
+	/* length of frame from control field to last byte of FCS */
+	buf_len = RXBUF_BYTES_RCVD(rxbuf);
+	fc = le16_to_cpu(hdr->fc);
+
+	if ( ((WF_FC_FSTYPE & fc) != WF_FSTYPE_BEACON)
+	  || (acx_debug & L_XFER_BEACON)
+	) {
+		acxlog(L_XFER|L_DATA, "rx: %s "
+			"time %u len %u signal %u SNR %u macstat %02X "
+			"phystat %02X phyrate %u status %u\n",
+			acx_get_packet_type_string(fc),
+			le32_to_cpu(rxbuf->time),
+			buf_len,
+			acx_signal_to_winlevel(rxbuf->phy_level),
+			acx_signal_to_winlevel(rxbuf->phy_snr),
+			rxbuf->mac_status,
+			rxbuf->phy_stat_baseband,
+			rxbuf->phy_plcp_signal,
+			priv->status);
+	}
+
+	if (unlikely(acx_debug & L_DATA)) {
+		printk("rx: 802.11 buf[%u]: ", buf_len);
+		acx_dump_bytes(hdr, buf_len);
+	}
+
+	/* FIXME: should check for Rx errors (rxbuf->mac_status?
+	 * discard broken packets - but NOT for monitor!)
+	 * and update Rx packet statistics here */
+
+	if (unlikely(priv->mode == ACX_MODE_MONITOR)) {
+		acx_l_rxmonitor(priv, rxbuf);
+	} else if (likely(buf_len >= WLAN_HDR_A3_LEN)) {
+		acx_l_rx_ieee802_11_frame(priv, rxbuf);
+	} else {
+		acxlog(L_DEBUG | L_XFER | L_DATA,
+		       "rx: NOT receiving packet (%s): "
+		       "size too small (%u)\n",
+		       acx_get_packet_type_string(fc),
+		       buf_len);
+	}
+
+	/* Now check Rx quality level, AFTER processing packet.
+	 * I tried to figure out how to map these levels to dBm
+	 * values, but for the life of me I really didn't
+	 * manage to get it. Either these values are not meant to
+	 * be expressed in dBm, or it's some pretty complicated
+	 * calculation. */
+
+#if FROM_SCAN_SOURCE_ONLY
+	/* only consider packets originating from the MAC
+	 * address of the device that's managing our BSSID.
+	 * Disable it for now, since it removes information (levels
+	 * from different peers) and slows the Rx path. */
+	if (priv->ap_client
+	 && mac_is_equal(hdr->a2, priv->ap_client->address)) {
+#endif
+		priv->wstats.qual.level = acx_signal_to_winlevel(rxbuf->phy_level);
+		priv->wstats.qual.noise = acx_signal_to_winlevel(rxbuf->phy_snr);
+#ifndef OLD_QUALITY
+		qual = acx_signal_determine_quality(priv->wstats.qual.level,
+				priv->wstats.qual.noise);
+#else
+		qual = (priv->wstats.qual.noise <= 100) ?
+				100 - priv->wstats.qual.noise : 0;
+#endif
+		priv->wstats.qual.qual = qual;
+		priv->wstats.qual.updated = 7; /* all 3 indicators updated */
+#if FROM_SCAN_SOURCE_ONLY
+	}
+#endif
+}
+
+
+/*----------------------------------------------------------------
+* acx100_s_init_memory_pools
+*
+* Comment:
+*	FIXME: This function still needs a cleanup
+*----------------------------------------------------------------*/
+static int
+acx100_s_init_memory_pools(wlandevice_t *priv, const acx_ie_memmap_t *mmt)
+{
+	acx100_ie_memblocksize_t MemoryBlockSize;
+	acx100_ie_memconfigoption_t MemoryConfigOption;
+	u32 TotalMemoryBlocks;
+
+	u32 RxBlockNum;
+	u32 TotalRxBlockSize;
+	u32 TxBlockNum;
+	u32 TotalTxBlockSize;
+
+	FN_ENTER;
+
+	/* Let's see if we can follow this:
+	   first we select our memory block size (which I think is
+	   completely arbitrary) */
+	MemoryBlockSize.size = cpu_to_le16(priv->memblocksize);
+
+	/* Then we alert the card to our decision of block size */
+	if (OK != acx_s_configure(priv, &MemoryBlockSize, ACX100_IE_BLOCK_SIZE)) {
+		printk("acx: MemoryBlockSizeWrite FAILED\n");
+		goto bad;
+	}
+
+	/* We figure out how many total blocks we can create, using
+	   the block size we chose, and the beginning and ending
+	   memory pointers, i.e.: end-start/size */
+	TotalMemoryBlocks = (le32_to_cpu(mmt->PoolEnd) - le32_to_cpu(mmt->PoolStart)) / priv->memblocksize;
+
+	acxlog(L_DEBUG, "TotalMemoryBlocks=%u (%u bytes)\n",
+		TotalMemoryBlocks, TotalMemoryBlocks*priv->memblocksize);
+
+	/* This one I have no idea on */
+	/* block-transfer=0x20000
+	 * indirect descriptors=0x10000
+	 */
+#ifdef ACX_USB
+	MemoryConfigOption.DMA_config = cpu_to_le32(0x20000);
+#else
+	MemoryConfigOption.DMA_config = cpu_to_le32(0x30000);
+
+	/* Declare start of the Rx host pool */
+//Should be of acx_ptr type, no?
+	MemoryConfigOption.pRxHostDesc = cpu_to_le32((u32)(long)priv->RxHostDescPoolStart);
+	acxlog(L_DEBUG, "pRxHostDesc 0x%08X, RxHostDescPoolStart 0x%p\n",
+		MemoryConfigOption.pRxHostDesc, priv->RxHostDescPoolStart);
+#endif
+
+	/* 50% of the allotment of memory blocks go to tx descriptors */
+	TxBlockNum = TotalMemoryBlocks / 2;
+	MemoryConfigOption.TxBlockNum = cpu_to_le16(TxBlockNum);
+
+	/* and 50% go to the rx descriptors */
+	RxBlockNum = TotalMemoryBlocks - TxBlockNum;
+	MemoryConfigOption.RxBlockNum = cpu_to_le16(RxBlockNum);
+
+	/* size of the tx and rx descriptor queues */
+	TotalTxBlockSize = TxBlockNum * priv->memblocksize;
+	TotalRxBlockSize = RxBlockNum * priv->memblocksize;
+	acxlog(L_DEBUG, "TxBlockNum %u RxBlockNum %u TotalTxBlockSize %u "
+		"TotalTxBlockSize %u\n", TxBlockNum, RxBlockNum,
+		TotalTxBlockSize, TotalRxBlockSize);
+
+
+	/* align the tx descriptor queue to an alignment of 0x20 (32 bytes) */
+	MemoryConfigOption.rx_mem =
+		cpu_to_le32((le32_to_cpu(mmt->PoolStart) + 0x1f) & ~0x1f);
+
+	/* align the rx descriptor queue to units of 0x20
+	 * and offset it by the tx descriptor queue */
+	MemoryConfigOption.tx_mem =
+		cpu_to_le32((le32_to_cpu(mmt->PoolStart) + TotalRxBlockSize + 0x1f) & ~0x1f);
+	acxlog(L_DEBUG, "rx_mem %08X rx_mem %08X\n",
+		MemoryConfigOption.tx_mem, MemoryConfigOption.rx_mem);
+
+	/* alert the device to our decision */
+	if (OK != acx_s_configure(priv, &MemoryConfigOption, ACX1xx_IE_MEMORY_CONFIG_OPTIONS)) {
+		printk("acx: configure memory config options FAILED\n");
+		goto bad;
+	}
+
+	/* and tell the device to kick it into gear */
+	if (OK != acx_s_issue_cmd(priv, ACX100_CMD_INIT_MEMORY, NULL, 0)) {
+		printk("acx: init memory FAILED\n");
+		goto bad;
+	}
+	FN_EXIT1(OK);
+	return OK;
+bad:
+	FN_EXIT1(NOT_OK);
+	return NOT_OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx100_s_create_dma_regions
+*
+* Note that this fn messes up heavily with hardware, but we cannot
+* lock it (we need to sleep). Not a problem since IRQs can't happen
+*----------------------------------------------------------------*/
+void BUG_acx100_ie_queueconfig_t_must_be_0x20_bytes(void);
+
+int
+acx100_s_create_dma_regions(wlandevice_t *priv)
+{
+	acx100_ie_queueconfig_t qcfg;
+	acx_ie_memmap_t MemMap;
+	int res = NOT_OK;
+#ifdef ACX_PCI
+	struct TIWLAN_DC *pDc;
+	pDc = &priv->dc;
+	pDc->priv = priv;
+#endif
+
+	FN_ENTER;
+
+	/* read out the acx100 physical start address for the queues */
+	if (OK != acx_s_interrogate(priv, &MemMap, ACX1xx_IE_MEMORY_MAP)) {
+		printk("acx: ctlMemoryMapRead FAILED\n");
+		goto fail;
+	}
+
+	/* # of items in Rx and Tx queues */
+	priv->TxQueueCnt = TXBUFFERCOUNT_ACX100;
+	priv->RxQueueCnt = RXBUFFERCOUNT_ACX100;
+
+#ifdef ACX_USB
+	qcfg.NumTxDesc = TXBUFFERCOUNT_USB;
+	qcfg.NumRxDesc = RXBUFFERCOUNT_USB;
+#endif
+
+#ifdef ACX_PCI
+	/* calculate size of queues */
+	qcfg.AreaSize = cpu_to_le32(
+			(sizeof(struct txdesc) * TXBUFFERCOUNT_ACX100 +
+			 sizeof(struct rxdesc) * RXBUFFERCOUNT_ACX100 + 8)
+			);
+#endif
+	qcfg.NumTxQueues = 1;  /* number of tx queues */
+
+	/* sets the beginning of the tx descriptor queue */
+	qcfg.TxQueueStart = MemMap.QueueStart;
+	qcfg.TxQueuePri = 0;
+#ifdef ACX_PCI
+	pDc->ui32ACXTxQueueStart = le32_to_cpu(MemMap.QueueStart);
+
+	/* sets the beginning of the rx descriptor queue, after the tx descrs */
+	pDc->ui32ACXRxQueueStart = pDc->ui32ACXTxQueueStart +
+				priv->TxQueueCnt * sizeof(txdesc_t);
+	qcfg.RxQueueStart = cpu_to_le32(pDc->ui32ACXRxQueueStart);
+#endif
+	qcfg.QueueOptions = 1;		/* auto reset descriptor */
+
+#ifdef ACX_PCI
+	/* sets the end of the rx descriptor queue */
+	qcfg.QueueEnd = cpu_to_le32(pDc->ui32ACXRxQueueStart +
+				priv->RxQueueCnt * sizeof(struct rxdesc));
+#endif
+
+	/* sets the beginning of the next queue */
+	qcfg.HostQueueEnd = cpu_to_le32(le32_to_cpu(qcfg.QueueEnd) + 8);
+
+	acxlog(L_DEBUG, "initializing Queue Indicator\n");
+
+	if (sizeof(qcfg) != 0x20)
+		BUG_acx100_ie_queueconfig_t_must_be_0x20_bytes();
+	if (OK != acx_s_configure(priv, &qcfg, ACX1xx_IE_QUEUE_CONFIG)) {
+		printk("acx: ctlQueueConfigurationWrite FAILED\n");
+		goto fail;
+	}
+
+#ifdef ACX_PCI
+	if (OK != acx_s_create_tx_host_desc_queue(pDc)) {
+		printk("acx: create_tx_host_desc_queue FAILED\n");
+		goto fail;
+	}
+	if (OK != acx_s_create_rx_host_desc_queue(pDc)) {
+		printk("acx: create_rx_host_desc_queue FAILED\n");
+		goto fail;
+	}
+
+	pDc->pTxDescQPool = NULL;
+	pDc->pRxDescQPool = NULL;
+
+	acx_s_create_tx_desc_queue(pDc);
+	acx_create_rx_desc_queue(pDc);
+#endif
+	if (OK != acx_s_interrogate(priv, &MemMap, ACX1xx_IE_MEMORY_MAP)) {
+		printk("acx: FAILED to read memory map\n");
+		goto fail;
+	}
+
+	/* FIXME: huh, why call ACX1xx_IE_MEMORY_MAP twice in the USB case?
+	   Is this needed?? */
+#ifdef ACX_USB
+	if (OK != acx_s_configure(priv, &MemMap, ACX1xx_IE_MEMORY_MAP)) {
+		printk("acx: FAILED to write memory map\n");
+		goto fail;
+	}
+#endif
+
+	MemMap.PoolStart = cpu_to_le32((le32_to_cpu(MemMap.QueueEnd) + 0x1f + 4) & ~0x1f);
+
+	if (OK != acx_s_configure(priv, &MemMap, ACX1xx_IE_MEMORY_MAP)) {
+		printk("acx: ctlMemoryMapWrite FAILED\n");
+		goto fail;
+	}
+
+	if (OK != acx100_s_init_memory_pools(priv, &MemMap)) {
+		printk("acx: acx100_init_memory_pools FAILED\n");
+		goto fail;
+	}
+
+	res = OK;
+	goto end;
+
+fail:
+	acx_s_msleep(1000); /* ? */
+#ifdef ACX_PCI
+	acx_free_desc_queues(pDc);
+#endif
+end:
+	FN_EXIT1(res);
+	return res;
+}
+
+
+/*----------------------------------------------------------------
+* acx111_s_create_dma_regions
+*
+* Note that this fn messes up heavily with hardware, but we cannot
+* lock it (we need to sleep). Not a problem since IRQs can't happen
+*----------------------------------------------------------------*/
+#define ACX111_PERCENT(percent) ((percent)/5)
+
+int
+acx111_s_create_dma_regions(wlandevice_t *priv)
+{
+#ifdef ACX_PCI
+	struct acx111_ie_memoryconfig memconf;
+	struct acx111_ie_queueconfig queueconf;
+	struct TIWLAN_DC *pDc;
+
+	FN_ENTER;
+
+	pDc = &priv->dc;
+	pDc->priv = priv;
+
+	/* Calculate memory positions and queue sizes */
+
+	priv->TxQueueCnt = TXBUFFERCOUNT_ACX111;
+	priv->RxQueueCnt = RXBUFFERCOUNT_ACX111;
+
+	/* Set up our host descriptor pool + data pool */
+	if (OK != acx_s_create_tx_host_desc_queue(pDc)) {
+		printk("acx: create_tx_host_desc_queue FAILED\n");
+		goto fail;
+	}
+	if (OK != acx_s_create_rx_host_desc_queue(pDc)) {
+		printk("acx: create_rx_host_desc_queue FAILED\n");
+		goto fail;
+	}
+
+	memset(&memconf, 0, sizeof(memconf));
+	/* the number of STAs (STA contexts) to support
+	** NB: was set to 1 and everything seemed to work nevertheless... */
+	memconf.no_of_stations = cpu_to_le16(VEC_SIZE(priv->sta_list));
+	/* specify the memory block size. Default is 256 */
+	memconf.memory_block_size = cpu_to_le16(priv->memblocksize);
+	/* let's use 50%/50% for tx/rx (specify percentage, units of 5%) */
+	memconf.tx_rx_memory_block_allocation = ACX111_PERCENT(50);
+	/* set the count of our queues
+	** NB: struct acx111_ie_memoryconfig shall be modified
+	** if we ever will switch to more than one rx and/or tx queue */
+	memconf.count_rx_queues = 1;
+	memconf.count_tx_queues = 1;
+	/* 0 == Busmaster Indirect Memory Organization, which is what we want
+	 * (using linked host descs with their allocated mem).
+	 * 2 == Generic Bus Slave */
+	/* done by memset: memconf.options = 0; */
+	/* let's use 25% for fragmentations and 75% for frame transfers
+	 * (specified in units of 5%) */
+	memconf.fragmentation = ACX111_PERCENT(75);
+	/* Rx descriptor queue config */
+	memconf.rx_queue1_count_descs = RXBUFFERCOUNT_ACX111;
+	memconf.rx_queue1_type = 7; /* must be set to 7 */
+	/* done by memset: memconf.rx_queue1_prio = 0; low prio */
+	memconf.rx_queue1_host_rx_start = cpu_to_le32(pDc->RxHostDescQPoolPhyAddr);
+	/* Tx descriptor queue config */
+	memconf.tx_queue1_count_descs = TXBUFFERCOUNT_ACX111;
+	/* done by memset: memconf.tx_queue1_attributes = 0; lowest priority */
+
+	/* NB1: this looks wrong: (memconf,ACX1xx_IE_QUEUE_CONFIG),
+	** (queueconf,ACX1xx_IE_MEMORY_CONFIG_OPTIONS) look swapped, eh?
+	** But it is actually correct wrt IE numbers.
+	** NB2: sizeof(memconf) == 28 == 0x1c but configure(ACX1xx_IE_QUEUE_CONFIG)
+	** writes 0x20 bytes (because same IE for acx100 uses struct acx100_ie_queueconfig
+	** wich is 4 bytes larger. what a mess...) */
+	if (OK != acx_s_configure(priv, &memconf, ACX1xx_IE_QUEUE_CONFIG)) {
+		printk("acx: setting acx111_ie_memoryconfig FAILED\n");
+		goto fail;
+	}
+
+	/* read out queueconf */
+	if (OK != acx_s_interrogate(priv, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS)) {
+		printk("acx: read queuehead FAILED\n");
+	}
+
+	pDc->ui32ACXRxQueueStart = le32_to_cpu(queueconf.rx1_queue_address);
+	pDc->ui32ACXTxQueueStart = le32_to_cpu(queueconf.tx1_queue_address);
+
+	acxlog(L_INIT, "dump queue head (from card):\n"
+		       "len: %u\n"
+		       "tx_memory_block_address: %X\n"
+		       "rx_memory_block_address: %X\n"
+		       "rx1_queue address: %X\n"
+		       "tx1_queue address: %X\n",
+		       le16_to_cpu(queueconf.len),
+		       le32_to_cpu(queueconf.tx_memory_block_address),
+		       le32_to_cpu(queueconf.rx_memory_block_address),
+		       pDc->ui32ACXRxQueueStart,
+		       pDc->ui32ACXTxQueueStart);
+	pDc->pRxDescQPool = (struct rxdesc *) ((u8 *)priv->iobase2 +
+				     pDc->ui32ACXRxQueueStart);
+
+	pDc->pTxDescQPool = (struct txdesc *) ((u8 *)priv->iobase2 +
+				     pDc->ui32ACXTxQueueStart);
+	acx_s_create_tx_desc_queue(pDc);
+	acx_create_rx_desc_queue(pDc);
+
+	FN_EXIT1(OK);
+	return OK;
+
+fail:
+	acx_free_desc_queues(pDc);
+
+	FN_EXIT1(NOT_OK);
+#endif /* CONFIG_TIACX_PCI */
+	return NOT_OK;
+}
+
+
+/***************************************************************
+*/
+void
+acx_log_bad_eid(wlan_hdr_t* hdr, int len, wlan_ie_t* ie_ptr)
+{
+	acxlog(L_ASSOC, "acx: unknown EID %d in mgmt frame at offset %d\n",
+				ie_ptr->eid, (int) ((u8*)ie_ptr - (u8*)hdr));
+	if (acx_debug & (L_DATA|L_ASSOC)) {
+		printk("frame (%s): ",
+			acx_get_packet_type_string(le16_to_cpu(hdr->fc)));
+		acx_dump_bytes(hdr, len);
+	}
+}
+
+
+/***************************************************************
+*/
+#if ACX_DEBUG
+void
+acx_dump_bytes(const void *data, int num)
+{
+	const u8* ptr = (const u8*)data;
+
+	if (num <= 0) {
+		printk("\n");
+		return;
+	}
+
+	while (num >= 16) {
+		printk( "%02X %02X %02X %02X %02X %02X %02X %02X "
+			"%02X %02X %02X %02X %02X %02X %02X %02X\n",
+			ptr[0], ptr[1], ptr[2], ptr[3],
+			ptr[4], ptr[5], ptr[6], ptr[7],
+			ptr[8], ptr[9], ptr[10], ptr[11],
+			ptr[12], ptr[13], ptr[14], ptr[15]);
+		num -= 16;
+		ptr += 16;
+	}
+	if (num > 0) {
+		while (--num > 0)
+			printk("%02X ", *ptr++);
+		printk("%02X\n", *ptr);
+	}
+}
+#endif
+
+
+/*----------------------------------------------------------------
+* acx_i_start_xmit
+*
+* Called by network core. Can be called outside of process context.
+*----------------------------------------------------------------*/
+int
+acx_i_start_xmit(struct sk_buff *skb, netdevice_t *dev)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	tx_t *tx;
+	void *txbuf;
+	unsigned long flags;
+	int txresult = NOT_OK;
+	int len;
+
+	FN_ENTER;
+
+	if (unlikely(!skb)) {
+		/* indicate success */
+		txresult = OK;
+		goto end_no_unlock;
+	}
+	if (unlikely(!priv)) {
+		goto end_no_unlock;
+	}
+
+/* Example from tg3.c. Do we need to do this similarly?
+	local_irq_save(flags);
+	if (!spin_trylock(&tp->lock)) {
+		local_irq_restore(flags);
+		return NETDEV_TX_LOCKED;
+	}
+	...
+	mmiowb();
+	spin_unlock_irqrestore(&tp->lock, flags);
+*/
+	acx_lock(priv, flags);
+
+	if (unlikely(!(priv->dev_state_mask & ACX_STATE_IFACE_UP))) {
+		goto end;
+	}
+	if (unlikely(priv->mode == ACX_MODE_OFF)) {
+		goto end;
+	}
+	if (unlikely(acx_queue_stopped(dev))) {
+		acxlog(L_DEBUG, "%s: called when queue stopped\n", __func__);
+		goto end;
+	}
+	if (unlikely(ACX_STATUS_4_ASSOCIATED != priv->status)) {
+		acxlog(L_XFER, "trying to xmit, but not associated yet: "
+			"aborting...\n");
+		/* silently drop the packet, since we're not connected yet */
+		txresult = OK;
+		/* ...but indicate an error nevertheless */
+		priv->stats.tx_errors++;
+		goto end;
+	}
+
+#if 0
+	/* we're going to transmit now, so stop another packet from entering.
+	 * FIXME: most likely we shouldn't do it like that, but instead:
+	 * stop the queue during card init, then wake the queue once
+	 * we're associated to the network, then stop the queue whenever
+	 * we don't have any free Tx buffers left, and wake it again once a
+	 * Tx buffer becomes free again. And of course also stop the
+	 * queue once we lose association to the network (since it
+	 * doesn't make sense to allow more user packets if we can't
+	 * forward them to a network).
+	 * FIXME: Hmm, seems this is all wrong. We SHOULD leave the
+	 * queue open from the beginning (as long as we're not full,
+	 * and also even before we're even associated),
+	 * otherwise we'll get NETDEV WATCHDOG transmit timeouts... */
+	acx_stop_queue(dev, "during tx");
+#endif
+	tx = acx_l_alloc_tx(priv);
+	if (unlikely(!tx)) {
+		printk("%s: start_xmit: txdesc ring is full, dropping tx\n",
+			dev->name);
+		txresult = NOT_OK;
+		goto end;
+	}
+
+	txbuf = acx_l_get_txbuf(tx);
+	if (!txbuf) {
+		/* Card was removed */
+		txresult = NOT_OK;
+		goto end;
+	}
+	len = acx_l_ether_to_txbuf(priv, txbuf, skb);
+	if (len < 0) {
+		/* Error in packet conversion */
+		txresult = NOT_OK;
+		goto end;
+	}
+	acx_l_dma_tx_data(priv, tx, len);
+	dev->trans_start = jiffies;
+
+	txresult = OK;
+	priv->stats.tx_packets++;
+	priv->stats.tx_bytes += skb->len;
+
+end:
+	acx_unlock(priv, flags);
+
+end_no_unlock:
+	if ((txresult == OK) && skb)
+		dev_kfree_skb_any(skb);
+
+	FN_EXIT1(txresult);
+	return txresult;
+}
+
+
+/***********************************************************************
+** Interrogate/configure commands
+*/
+static const u16
+CtlLength[] = {
+	0,
+	ACX100_IE_ACX_TIMER_LEN,
+	ACX1xx_IE_POWER_MGMT_LEN,
+	ACX1xx_IE_QUEUE_CONFIG_LEN,
+	ACX100_IE_BLOCK_SIZE_LEN,
+	ACX1xx_IE_MEMORY_CONFIG_OPTIONS_LEN,
+	ACX1xx_IE_RATE_FALLBACK_LEN,
+	ACX100_IE_WEP_OPTIONS_LEN,
+	ACX1xx_IE_MEMORY_MAP_LEN, /*	ACX1xx_IE_SSID_LEN, */
+	0,
+	ACX1xx_IE_ASSOC_ID_LEN,
+	0,
+	ACX1xx_IE_CONFIG_OPTIONS_LEN,
+	ACX1xx_IE_FWREV_LEN,
+	ACX1xx_IE_FCS_ERROR_COUNT_LEN,
+	ACX1xx_IE_MEDIUM_USAGE_LEN,
+	ACX1xx_IE_RXCONFIG_LEN,
+	0,
+	0,
+	ACX1xx_IE_FIRMWARE_STATISTICS_LEN,
+	0,
+	ACX1xx_IE_FEATURE_CONFIG_LEN,
+	ACX111_IE_KEY_CHOOSE_LEN,
+};
+
+static const u16
+CtlLengthDot11[] = {
+	0,
+	ACX1xx_IE_DOT11_STATION_ID_LEN,
+	0,
+	ACX100_IE_DOT11_BEACON_PERIOD_LEN,
+	ACX1xx_IE_DOT11_DTIM_PERIOD_LEN,
+	ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN,
+	ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN,
+	ACX100_IE_DOT11_WEP_DEFAULT_KEY_LEN,
+	ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN,
+	0,
+	ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN_LEN,
+	ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN,
+	0,
+	ACX1xx_IE_DOT11_TX_POWER_LEVEL_LEN,
+	ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN,
+	ACX100_IE_DOT11_ED_THRESHOLD_LEN,
+	ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET_LEN,
+	0,
+	0,
+	0,
+};
+
+
+#if !ACX_DEBUG
+int
+acx_s_configure(wlandevice_t *priv, void *pdr, int type)
+{
+#else
+int
+acx_s_configure_debug(wlandevice_t *priv, void *pdr, int type, const char* typestr)
+{
+#endif
+	u16 len;
+
+	/* TODO implement and check other acx111 commands */
+	if ((priv->chip_type == CHIPTYPE_ACX111)
+	 && (type == ACX1xx_IE_DOT11_CURRENT_ANTENNA)) {
+		/* acx111 has differing struct size */
+		acxlog(L_CTL, "Configure Command %s is not supported "
+			"under acx111 (yet)\n", typestr);
+		return NOT_OK;
+	}
+
+	if (type < 0x1000)
+		len = CtlLength[type];
+	else
+		len = CtlLengthDot11[type-0x1000];
+
+	acxlog(L_XFER, "configure(type:%s,len:%u)\n", typestr, len);
+	if (unlikely(!len)) {
+		acxlog(L_DEBUG, "zero-length type %s?!\n", typestr);
+	}
+
+	((acx_ie_generic_t *)pdr)->type = cpu_to_le16(type);
+	((acx_ie_generic_t *)pdr)->len = cpu_to_le16(len);
+	return acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIGURE, pdr, len + 4);
+}
+
+#if !ACX_DEBUG
+int
+acx_s_interrogate(wlandevice_t *priv, void *pdr, int type)
+{
+#else
+int
+acx_s_interrogate_debug(wlandevice_t *priv, void *pdr, int type,
+		const char* typestr)
+{
+#endif
+	u16 len;
+
+	if (type < 0x1000)
+		len = CtlLength[type];
+	else
+		len = CtlLengthDot11[type-0x1000];
+
+	((acx_ie_generic_t *)pdr)->type = cpu_to_le16(type);
+	((acx_ie_generic_t *)pdr)->len = cpu_to_le16(len);
+
+	acxlog(L_CTL, "interrogate(type:%s,len:%u)\n", typestr, len);
+
+	return acx_s_issue_cmd(priv, ACX1xx_CMD_INTERROGATE, pdr, len + 4);
+}
+
+
+/***********************************************************************
+** acx_l_update_ratevector
+**
+** Updates priv->rate_supported[_len] according to rate_{basic,oper}
+*/
+const u8
+bitpos2ratebyte[] = {
+	DOT11RATEBYTE_1,
+	DOT11RATEBYTE_2,
+	DOT11RATEBYTE_5_5,
+	DOT11RATEBYTE_6_G,
+	DOT11RATEBYTE_9_G,
+	DOT11RATEBYTE_11,
+	DOT11RATEBYTE_12_G,
+	DOT11RATEBYTE_18_G,
+	DOT11RATEBYTE_22,
+	DOT11RATEBYTE_24_G,
+	DOT11RATEBYTE_36_G,
+	DOT11RATEBYTE_48_G,
+	DOT11RATEBYTE_54_G,
+};
+
+void
+acx_l_update_ratevector(wlandevice_t *priv)
+{
+	u16 bcfg = priv->rate_basic;
+	u16 ocfg = priv->rate_oper;
+	u8 *supp = priv->rate_supported;
+	const u8 *dot11 = bitpos2ratebyte;
+
+	FN_ENTER;
+
+	while (ocfg) {
+		if (ocfg & 1) {
+			*supp = *dot11;
+			if (bcfg & 1) {
+				*supp |= 0x80;
+			}
+			supp++;
+		}
+		dot11++;
+		ocfg >>= 1;
+		bcfg >>= 1;
+	}
+	priv->rate_supported_len = supp - priv->rate_supported;
+	if (acx_debug & L_ASSOC) {
+		printk("new ratevector: ");
+		acx_dump_bytes(priv->rate_supported, priv->rate_supported_len);
+	}
+	FN_EXIT0;
+}
+
+
+#ifdef CONFIG_PROC_FS
+/***********************************************************************
+** /proc files
+*/
+/***********************************************************************
+** acx_l_proc_output
+** Generate content for our /proc entry
+**
+** Arguments:
+**	buf is a pointer to write output to
+**	priv is the usual pointer to our private struct wlandevice
+** Returns:
+**	number of bytes actually written to buf
+** Side effects:
+**	none
+*/
+static int
+acx_l_proc_output(char *buf, wlandevice_t *priv)
+{
+	char *p = buf;
+	int i;
+
+	FN_ENTER;
+
+	p += sprintf(p,
+		"acx driver version:\t\t" WLAN_RELEASE "\n"
+		"Wireless extension version:\t" STRING(WIRELESS_EXT) "\n"
+		"chip name:\t\t\t%s (0x%08X)\n"
+		"radio type:\t\t\t0x%02X\n"
+		/* TODO: add radio type string from acx_display_hardware_details */
+		"form factor:\t\t\t0x%02X\n"
+		/* TODO: add form factor string from acx_display_hardware_details */
+		"EEPROM version:\t\t\t0x%02X\n"
+		"firmware version:\t\t%s (0x%08X)\n",
+		priv->chip_name, priv->firmware_id,
+		priv->radio_type,
+		priv->form_factor,
+		priv->eeprom_version,
+		priv->firmware_version, priv->firmware_numver);
+
+	for (i = 0; i < VEC_SIZE(priv->sta_list); i++) {
+		struct client *bss = &priv->sta_list[i];
+		if (!bss->used) continue;
+		p += sprintf(p, "BSS %u BSSID "MACSTR" ESSID %s channel %u "
+			"Cap 0x%X SIR %u SNR %u\n",
+			i, MAC(bss->bssid), (char*)bss->essid, bss->channel,
+			bss->cap_info, bss->sir, bss->snr);
+	}
+	p += sprintf(p, "status:\t\t\t%u (%s)\n",
+			priv->status, acx_get_status_name(priv->status));
+	/* TODO: add more interesting stuff (essid, ...) here */
+
+	FN_EXIT1(p - buf);
+	return p - buf;
+}
+
+
+/***********************************************************************
+*/
+static int
+acx_s_proc_diag_output(char *buf, wlandevice_t *priv)
+{
+	char *p = buf;
+	fw_stats_t *fw_stats;
+	unsigned long flags;
+
+	FN_ENTER;
+
+	fw_stats = kmalloc(sizeof(fw_stats_t), GFP_KERNEL);
+	if (!fw_stats) {
+		FN_EXIT1(0);
+		return 0;
+	}
+	memset(fw_stats, 0, sizeof(fw_stats_t));
+
+	acx_lock(priv, flags);
+
+#ifdef ACX_PCI
+{
+	int i;
+	const char *rtl, *thd, *ttl;
+	const struct rxhostdesc *pRxDesc;
+	txdesc_t *pTxDesc;
+	TIWLAN_DC *pDc = &priv->dc;
+	p += sprintf(p, "** Rx buf **\n");
+	for (i = 0; i < pDc->rx_pool_count; i++) {
+		rtl = (i == pDc->rx_tail) ? " [tail]" : "";
+		pRxDesc = &pDc->pRxHostDescQPool[i];
+		if ((pRxDesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN))
+		 && (pRxDesc->Status & cpu_to_le32(BIT31)) )
+			p += sprintf(p, "%02u FULL%s\n", i, rtl);
+		else
+			p += sprintf(p, "%02u empty%s\n", i, rtl);
+	}
+	p += sprintf(p, "** Tx buf (free %d, Linux netqueue %s) **\n", priv->TxQueueFree,
+				acx_queue_stopped(priv->netdev) ? "STOPPED" : "running");
+	pTxDesc = pDc->pTxDescQPool;
+	for (i = 0; i < pDc->tx_pool_count; i++) {
+		thd = (i == pDc->tx_head) ? " [head]" : "";
+		ttl = (i == pDc->tx_tail) ? " [tail]" : "";
+		if (pTxDesc->Ctl_8 & DESC_CTL_ACXDONE)
+			p += sprintf(p, "%02u DONE   (%02X)%s%s\n", i, pTxDesc->Ctl_8, thd, ttl);
+		else
+		if (!(pTxDesc->Ctl_8 & DESC_CTL_HOSTOWN))
+			p += sprintf(p, "%02u TxWait (%02X)%s%s\n", i, pTxDesc->Ctl_8, thd, ttl);
+		else
+			p += sprintf(p, "%02u empty  (%02X)%s%s\n", i, pTxDesc->Ctl_8, thd, ttl);
+		pTxDesc = GET_NEXT_TX_DESC_PTR(pDc, pTxDesc);
+	}
+}
+#endif
+	p += sprintf(p,
+		"\n"
+		"** network status **\n"
+		"dev_state_mask 0x%04X\n"
+		"status %u (%s), "
+		"mode %u, channel %u, "
+		"reg_dom_id 0x%02X, reg_dom_chanmask 0x%04X, ",
+		priv->dev_state_mask,
+		priv->status, acx_get_status_name(priv->status),
+		priv->mode, priv->channel,
+		priv->reg_dom_id, priv->reg_dom_chanmask
+		);
+	p += sprintf(p,
+		"ESSID \"%s\", essid_active %d, essid_len %d, "
+		"essid_for_assoc \"%s\", nick \"%s\"\n"
+		"WEP ena %d, restricted %d, idx %d\n",
+		priv->essid, priv->essid_active, (int)priv->essid_len,
+		priv->essid_for_assoc, priv->nick,
+		priv->wep_enabled, priv->wep_restricted,
+		priv->wep_current_index);
+	p += sprintf(p, "dev_addr  "MACSTR"\n", MAC(priv->dev_addr));
+	p += sprintf(p, "bssid     "MACSTR"\n", MAC(priv->bssid));
+	p += sprintf(p, "ap_filter "MACSTR"\n", MAC(priv->ap));
+
+	p += sprintf(p,
+		"\n"
+		"** PHY status **\n"
+		"tx_disabled %d, tx_level_dbm %d, tx_level_val %d, tx_level_auto %d\n"
+		"sensitivity %d, antenna 0x%02X, ed_threshold %d, cca %d, preamble_mode %d\n"
+		"rts_threshold %d, short_retry %d, long_retry %d, msdu_lifetime %d, listen_interval %d, beacon_interval %d\n",
+		priv->tx_disabled, priv->tx_level_dbm, priv->tx_level_val, priv->tx_level_auto,
+		priv->sensitivity, priv->antenna, priv->ed_threshold, priv->cca, priv->preamble_mode,
+		priv->rts_threshold, priv->short_retry, priv->long_retry, priv->msdu_lifetime, priv->listen_interval, priv->beacon_interval);
+
+#ifdef ACX_PCI
+{
+	TIWLAN_DC *pDc = &priv->dc;
+	p += sprintf(p,
+		"\n"
+		"** TIWLAN_DC **\n"
+		"ui32ACXTxQueueStart %u, ui32ACXRxQueueStart %u\n"
+		"pTxBufferPool %p, TxBufferPoolSize %u, TxBufferPoolPhyAddr %08llx\n"
+		"TxDescrSize %u, pTxDescQPool %p, tx_pool_count %u\n"
+		"pTxHostDescQPool %p, TxHostDescQPoolSize %u, TxHostDescQPoolPhyAddr %08llx\n"
+		"pRxDescQPool %p, rx_pool_count %d\n"
+		"pRxHostDescQPool %p, RxHostDescQPoolSize %u, RxHostDescQPoolPhyAddr %08llx\n"
+		"pRxBufferPool %p, RxBufferPoolSize %u, RxBufferPoolPhyAddr %08llx\n",
+		pDc->ui32ACXTxQueueStart, pDc->ui32ACXRxQueueStart,
+		pDc->pTxBufferPool, pDc->TxBufferPoolSize, (u64)pDc->TxBufferPoolPhyAddr,
+		pDc->TxDescrSize, pDc->pTxDescQPool, pDc->tx_pool_count,
+		pDc->pTxHostDescQPool, pDc->TxHostDescQPoolSize, (u64)pDc->TxHostDescQPoolPhyAddr,
+		pDc->pRxDescQPool, pDc->rx_pool_count,
+		pDc->pRxHostDescQPool, pDc->RxHostDescQPoolSize, (u64)pDc->RxHostDescQPoolPhyAddr,
+		pDc->pRxBufferPool, pDc->RxBufferPoolSize, (u64)pDc->RxBufferPoolPhyAddr);
+}
+#endif
+
+	acx_unlock(priv, flags);
+
+	if (OK != acx_s_interrogate(priv, fw_stats, ACX1xx_IE_FIRMWARE_STATISTICS))
+		p += sprintf(p,
+			"\n"
+			"** Firmware **\n"
+			"QUERY FAILED!!\n");
+	else {
+		p += sprintf(p,
+			"\n"
+			"** Firmware **\n"
+			"version \"%s\"\n"
+			"tx_desc_overfl %u, rx_OutOfMem %u, rx_hdr_overfl %u, rx_hdr_use_next %u\n"
+			"rx_dropped_frame %u, rx_frame_ptr_err %u, rx_xfr_hint_trig %u, rx_dma_req %u\n"
+			"rx_dma_err %u, tx_dma_req %u, tx_dma_err %u, cmd_cplt %u, fiq %u\n"
+			"rx_hdrs %u, rx_cmplt %u, rx_mem_overfl %u, rx_rdys %u, irqs %u\n"
+			"acx_trans_procs %u, decrypt_done %u, dma_0_done %u, dma_1_done %u\n",
+			priv->firmware_version,
+			le32_to_cpu(fw_stats->tx_desc_of),
+			le32_to_cpu(fw_stats->rx_oom),
+			le32_to_cpu(fw_stats->rx_hdr_of),
+			le32_to_cpu(fw_stats->rx_hdr_use_next),
+			le32_to_cpu(fw_stats->rx_dropped_frame),
+			le32_to_cpu(fw_stats->rx_frame_ptr_err),
+			le32_to_cpu(fw_stats->rx_xfr_hint_trig),
+			le32_to_cpu(fw_stats->rx_dma_req),
+			le32_to_cpu(fw_stats->rx_dma_err),
+			le32_to_cpu(fw_stats->tx_dma_req),
+			le32_to_cpu(fw_stats->tx_dma_err),
+			le32_to_cpu(fw_stats->cmd_cplt),
+			le32_to_cpu(fw_stats->fiq),
+			le32_to_cpu(fw_stats->rx_hdrs),
+			le32_to_cpu(fw_stats->rx_cmplt),
+			le32_to_cpu(fw_stats->rx_mem_of),
+			le32_to_cpu(fw_stats->rx_rdys),
+			le32_to_cpu(fw_stats->irqs),
+			le32_to_cpu(fw_stats->acx_trans_procs),
+			le32_to_cpu(fw_stats->decrypt_done),
+			le32_to_cpu(fw_stats->dma_0_done),
+			le32_to_cpu(fw_stats->dma_1_done));
+		p += sprintf(p,
+			"tx_exch_complet %u, commands %u, acx_rx_procs %u\n"
+			"hw_pm_mode_changes %u, host_acks %u, pci_pm %u, acm_wakeups %u\n"
+			"wep_key_count %u, wep_default_key_count %u, dot11_def_key_mib %u\n"
+			"wep_key_not_found %u, wep_decrypt_fail %u\n",
+			le32_to_cpu(fw_stats->tx_exch_complet),
+			le32_to_cpu(fw_stats->commands),
+			le32_to_cpu(fw_stats->acx_rx_procs),
+			le32_to_cpu(fw_stats->hw_pm_mode_changes),
+			le32_to_cpu(fw_stats->host_acks),
+			le32_to_cpu(fw_stats->pci_pm),
+			le32_to_cpu(fw_stats->acm_wakeups),
+			le32_to_cpu(fw_stats->wep_key_count),
+			le32_to_cpu(fw_stats->wep_default_key_count),
+			le32_to_cpu(fw_stats->dot11_def_key_mib),
+			le32_to_cpu(fw_stats->wep_key_not_found),
+			le32_to_cpu(fw_stats->wep_decrypt_fail));
+	}
+
+	kfree(fw_stats);
+
+	FN_EXIT1(p - buf);
+	return p - buf;
+}
+
+
+/***********************************************************************
+*/
+static int
+acx_proc_eeprom_output(char *buf, wlandevice_t *priv)
+{
+	char *p = buf;
+#ifdef ACX_PCI
+	int i;
+
+	FN_ENTER;
+
+	for (i = 0; i < 0x400; i++) {
+		acx_read_eeprom_offset(priv, i, p++);
+	}
+
+	FN_EXIT1(p - buf);
+#endif
+	return p - buf;
+}
+
+
+/***********************************************************************
+*/
+static int
+acx_s_proc_phy_output(char *buf, wlandevice_t *priv)
+{
+	char *p = buf;
+	int i;
+
+	FN_ENTER;
+
+	/*
+	if (RADIO_RFMD_11 != priv->radio_type) {
+		printk("sorry, not yet adapted for radio types "
+			"other than RFMD, please verify "
+			"PHY size etc. first!\n");
+		goto end;
+	}
+	*/
+
+	/* The PHY area is only 0x80 bytes long; further pages after that
+	 * only have some page number registers with altered value,
+	 * all other registers remain the same. */
+	for (i = 0; i < 0x80; i++) {
+		acx_s_read_phy_reg(priv, i, p++);
+	}
+
+	FN_EXIT1(p - buf);
+	return p - buf;
+}
+
+
+/***********************************************************************
+** acx_e_read_proc_XXXX
+** Handle our /proc entry
+**
+** Arguments:
+**	standard kernel read_proc interface
+** Returns:
+**	number of bytes written to buf
+** Side effects:
+**	none
+*/
+static int
+acx_e_read_proc(char *buf, char **start, off_t offset, int count,
+		     int *eof, void *data)
+{
+	wlandevice_t *priv = (wlandevice_t *)data;
+	unsigned long flags;
+	int length;
+
+	FN_ENTER;
+
+	acx_sem_lock(priv);
+	acx_lock(priv, flags);
+	/* fill buf */
+	length = acx_l_proc_output(buf, priv);
+	acx_unlock(priv, flags);
+	acx_sem_unlock(priv);
+
+	/* housekeeping */
+	if (length <= offset + count)
+		*eof = 1;
+	*start = buf + offset;
+	length -= offset;
+	if (length > count)
+		length = count;
+	if (length < 0)
+		length = 0;
+	FN_EXIT1(length);
+	return length;
+}
+
+static int
+acx_e_read_proc_diag(char *buf, char **start, off_t offset, int count,
+		     int *eof, void *data)
+{
+	wlandevice_t *priv = (wlandevice_t *)data;
+	int length;
+
+	FN_ENTER;
+
+	acx_sem_lock(priv);
+	/* fill buf */
+	length = acx_s_proc_diag_output(buf, priv);
+	acx_sem_unlock(priv);
+
+	/* housekeeping */
+	if (length <= offset + count)
+		*eof = 1;
+	*start = buf + offset;
+	length -= offset;
+	if (length > count)
+		length = count;
+	if (length < 0)
+		length = 0;
+	FN_EXIT1(length);
+	return length;
+}
+
+static int
+acx_e_read_proc_eeprom(char *buf, char **start, off_t offset, int count,
+		     int *eof, void *data)
+{
+	wlandevice_t *priv = (wlandevice_t *)data;
+	int length;
+
+	FN_ENTER;
+
+	acx_sem_lock(priv);
+	/* fill buf */
+	length = acx_proc_eeprom_output(buf, priv);
+	acx_sem_unlock(priv);
+
+	/* housekeeping */
+	if (length <= offset + count)
+		*eof = 1;
+	*start = buf + offset;
+	length -= offset;
+	if (length > count)
+		length = count;
+	if (length < 0)
+		length = 0;
+	FN_EXIT1(length);
+	return length;
+}
+
+static int
+acx_e_read_proc_phy(char *buf, char **start, off_t offset, int count,
+		     int *eof, void *data)
+{
+	wlandevice_t *priv = (wlandevice_t *)data;
+	int length;
+
+	FN_ENTER;
+
+	acx_sem_lock(priv);
+	/* fill buf */
+	length = acx_s_proc_phy_output(buf, priv);
+	acx_sem_unlock(priv);
+
+	/* housekeeping */
+	if (length <= offset + count)
+		*eof = 1;
+	*start = buf + offset;
+	length -= offset;
+	if (length > count)
+		length = count;
+	if (length < 0)
+		length = 0;
+	FN_EXIT1(length);
+	return length;
+}
+
+
+/***********************************************************************
+** /proc files registration
+*/
+static const char * const
+proc_files[] = { "", "_diag", "_eeprom", "_phy" };
+
+static read_proc_t * const
+acx_proc_funcs[] = {
+	acx_e_read_proc,
+	acx_e_read_proc_diag,
+	acx_e_read_proc_eeprom,
+	acx_e_read_proc_phy
+};
+
+static int
+manage_proc_entries(const struct net_device *dev, int remove)
+{
+	/* doh, netdev_priv() doesn't have const! */
+	wlandevice_t *priv = acx_netdev_priv((struct net_device *)dev);
+	char procbuf[80];
+	int i;
+
+	for (i = 0; i < 4; i++)	{
+		sprintf(procbuf, "driver/acx_%s", dev->name);
+		strcat(procbuf, proc_files[i]);
+		if (!remove) {
+			acxlog(L_INIT, "creating /proc entry %s\n", procbuf);
+			if (!create_proc_read_entry(procbuf, 0, 0, acx_proc_funcs[i], priv))
+				return NOT_OK;
+		} else {
+			acxlog(L_INIT, "removing /proc entry %s\n", procbuf);
+			remove_proc_entry(procbuf, NULL);
+		}
+	}
+	return OK;
+}
+
+int
+acx_proc_register_entries(const struct net_device *dev)
+{
+	return manage_proc_entries(dev, 0);
+}
+
+int
+acx_proc_unregister_entries(const struct net_device *dev)
+{
+	return manage_proc_entries(dev, 1);
+}
+#endif /* CONFIG_PROC_FS */
+
+
+/***********************************************************************
+** acx_s_read_fw
+**
+** Loads a firmware image
+**
+** Returns:
+**  0				unable to load file
+**  pointer to firmware		success
+*/
+#if USE_FW_LOADER_26
+firmware_image_t*
+acx_s_read_fw(struct device *dev, const char *file, u32 *size)
+#else
+#undef acx_s_read_fw
+firmware_image_t*
+acx_s_read_fw(const char *file, u32 *size)
+#endif
+{
+	firmware_image_t *res = NULL;
+
+#if USE_FW_LOADER_LEGACY
+	mm_segment_t orgfs;
+	unsigned long page;
+	char *buffer;
+	struct file *inf;
+	int retval;
+	u32 offset = 0;
+	char *filename;
+#endif
+
+#if USE_FW_LOADER_26
+	const struct firmware *fw_entry;
+
+	acxlog(L_DEBUG, "requesting firmware image '%s'\n", file);
+	if (!request_firmware(&fw_entry, file, dev)) {
+		*size = 8 + le32_to_cpu(*(u32 *)(fw_entry->data + 4));
+		if (fw_entry->size != *size) {
+			printk("acx: firmware size does not match "
+				"firmware header: %d != %d\n",
+				(int) fw_entry->size, (int) *size);
+		}
+		res = vmalloc(*size);
+		if (!res) {
+			printk("acx: no memory for firmware "
+				"(%u bytes)\n", *size);
+			return NULL;
+		}
+		memcpy(res, fw_entry->data, fw_entry->size);
+		release_firmware(fw_entry);
+		return res;
+	}
+	printk("acx: no firmware image was provided. "
+		"Check your hotplug scripts\n");
+#endif
+
+#if USE_FW_LOADER_LEGACY
+	printk("acx: firmware upload via firmware_dir module parameter "
+		"is deprecated. Switch to using hotplug\n");
+
+	orgfs = get_fs(); /* store original fs */
+	set_fs(KERNEL_DS);
+
+	/* Read in whole file then check the size */
+	page = __get_free_page(GFP_KERNEL);
+	if (unlikely(0 == page)) {
+		printk("acx: no memory for firmware upload\n");
+		goto fail;
+	}
+
+	filename = kmalloc(PATH_MAX, GFP_KERNEL);
+	if (unlikely(!filename)) {
+		printk("acx: no memory for firmware upload\n");
+		goto fail;
+	}
+	if (!firmware_dir) {
+		firmware_dir = "/usr/share/acx";
+		acxlog(L_DEBUG, "no firmware directory specified "
+			"via module parameter firmware_dir, "
+			"using default %s\n", firmware_dir);
+	}
+	snprintf(filename, PATH_MAX, "%s/%s", firmware_dir, file);
+	acxlog(L_DEBUG, "reading firmware image '%s'\n", filename);
+
+	buffer = (char*)page;
+
+	/* Note that file must be given as absolute path:
+	 * a relative path works on first loading,
+	 * but any subsequent firmware loading during card
+	 * eject/insert will fail, most likely since the first
+	 * module loading happens in user space (and thus
+	 * filp_open can figure out the absolute path from a
+	 * relative path) whereas the card reinsert processing
+	 * probably happens in kernel space where you don't have
+	 * a current directory to be able to figure out an
+	 * absolute path from a relative path... */
+	inf = filp_open(filename, O_RDONLY, 0);
+	kfree(filename);
+	if (OK != IS_ERR(inf)) {
+		const char *err;
+
+		switch (-PTR_ERR(inf)) {
+			case 2: err = "file not found";
+				break;
+			default:
+				err = "unknown error";
+				break;
+		}
+		printk("acx: error %ld trying to open file '%s': %s\n",
+					-PTR_ERR(inf), file, err);
+		goto fail;
+	}
+
+	if (unlikely((NULL == inf->f_op) || (NULL == inf->f_op->read))) {
+		printk("acx: %s does not have a read method?!\n", file);
+		goto fail_close;
+	}
+
+	offset = 0;
+	do {
+		retval = inf->f_op->read(inf, buffer, PAGE_SIZE, &inf->f_pos);
+
+		if (unlikely(0 > retval)) {
+			printk("acx: error %d reading file '%s'\n",
+							-retval, file);
+			vfree(res);
+			res = NULL;
+		} else if (0 == retval) {
+			if (0 == offset) {
+				printk("acx: firmware image file "
+					"'%s' is empty?!\n", file);
+			}
+		} else if (0 < retval) {
+			/* allocate result buffer here if needed,
+			 * since we don't want to waste resources/time
+			 * (in case file opening/reading fails)
+			 * by doing allocation in front of the loop instead. */
+			if (NULL == res) {
+				*size = 8 + le32_to_cpu(*(u32 *)(4 + buffer));
+
+				res = vmalloc(*size);
+				if (NULL == res) {
+					printk("acx: unable to "
+						"allocate %u bytes for "
+						"firmware module upload\n",
+						*size);
+					goto fail_close;
+				}
+				acxlog(L_DEBUG, "allocated %u bytes "
+					"for firmware module loading\n",
+					*size);
+			}
+			if ((unlikely(offset + retval > *size))) {
+				printk("acx: ERROR: allocation "
+					"was less than firmware image size?!\n");
+				goto fail_close;
+			}
+			memcpy((u8*)res + offset, buffer, retval);
+			offset += retval;
+		}
+	} while (0 < retval);
+
+fail_close:
+	retval = filp_close(inf, NULL);
+
+	if (unlikely(retval)) {
+		printk("acx: error %d closing file '%s'\n", -retval, file);
+	}
+
+	if (unlikely((NULL != res) && (offset != le32_to_cpu(res->size) + 8))) {
+		printk("acx: firmware is reporting a different size "
+			"(0x%08X; 0x%08X was read)\n",
+			le32_to_cpu(res->size) + 8, offset);
+		vfree(res);
+		res = NULL;
+	}
+
+fail:
+	if (page)
+		free_page(page);
+	set_fs(orgfs);
+#endif
+
+	/* checksum will be verified in write_fw, so don't bother here */
+	return res;
+}
+
+
+/***********************************************************************
+** acx_s_set_wepkey
+*/
+static void
+acx100_s_set_wepkey(wlandevice_t *priv)
+{
+	ie_dot11WEPDefaultKey_t dk;
+	int i;
+
+	for (i = 0; i < DOT11_MAX_DEFAULT_WEP_KEYS; i++) {
+		if (priv->wep_keys[i].size != 0) {
+			acxlog(L_INIT, "setting WEP key: %d with "
+				"total size: %d\n", i, (int) priv->wep_keys[i].size);
+			dk.action = 1;
+			dk.keySize = priv->wep_keys[i].size;
+			dk.defaultKeyNum = i;
+			memcpy(dk.key, priv->wep_keys[i].key, dk.keySize);
+			acx_s_configure(priv, &dk, ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE);
+		}
+	}
+}
+
+static void
+acx111_s_set_wepkey(wlandevice_t *priv)
+{
+	acx111WEPDefaultKey_t dk;
+	int i;
+
+	for (i = 0; i < DOT11_MAX_DEFAULT_WEP_KEYS; i++) {
+		if (priv->wep_keys[i].size != 0) {
+			acxlog(L_INIT, "setting WEP key: %d with "
+				"total size: %d\n", i, (int) priv->wep_keys[i].size);
+			memset(&dk, 0, sizeof(dk));
+			dk.action = cpu_to_le16(1); /* "add key"; yes, that's a 16bit value */
+			dk.keySize = priv->wep_keys[i].size;
+
+			/* are these two lines necessary? */
+			dk.type = 0;              /* default WEP key */
+			dk.index = 0;             /* ignored when setting default key */
+
+			dk.defaultKeyNum = i;
+			memcpy(dk.key, priv->wep_keys[i].key, dk.keySize);
+			acx_s_issue_cmd(priv, ACX1xx_CMD_WEP_MGMT, &dk, sizeof(dk));
+		}
+	}
+}
+
+static void
+acx_s_set_wepkey(wlandevice_t *priv)
+{
+	if (priv->chip_type == CHIPTYPE_ACX111)
+		acx111_s_set_wepkey(priv);
+	else
+		acx100_s_set_wepkey(priv);
+}
+
+
+/***********************************************************************
+** acx100_s_init_wep
+**
+** FIXME: this should probably be moved into the new card settings
+** management, but since we're also modifying the memory map layout here
+** due to the WEP key space we want, we should take care...
+*/
+static int
+acx100_s_init_wep(wlandevice_t *priv)
+{
+/*	int i;
+	acx100_cmd_wep_mgmt_t wep_mgmt;           size = 37 bytes */
+	acx100_ie_wep_options_t options;
+	ie_dot11WEPDefaultKeyID_t dk;
+	acx_ie_memmap_t pt;
+	int res = NOT_OK;
+
+	FN_ENTER;
+
+	if (OK != acx_s_interrogate(priv, &pt, ACX1xx_IE_MEMORY_MAP)) {
+		printk("%s: ctlMemoryMapRead failed\n", priv->netdev->name);
+		goto fail;
+	}
+
+	acxlog(L_DEBUG, "CodeEnd:%X\n", pt.CodeEnd);
+
+	pt.WEPCacheStart = cpu_to_le32(le32_to_cpu(pt.CodeEnd) + 0x4);
+	pt.WEPCacheEnd   = cpu_to_le32(le32_to_cpu(pt.CodeEnd) + 0x4);
+
+	if (OK != acx_s_configure(priv, &pt, ACX1xx_IE_MEMORY_MAP)) {
+		printk("%s: ctlMemoryMapWrite FAILED\n", priv->netdev->name);
+		goto fail;
+	}
+
+	/* let's choose maximum setting: 4 default keys, plus 10 other keys: */
+	options.NumKeys = cpu_to_le16(DOT11_MAX_DEFAULT_WEP_KEYS + 10);
+	options.WEPOption = 0x00;
+
+	acxlog(L_ASSOC, "%s: writing WEP options\n", __func__);
+	acx_s_configure(priv, &options, ACX100_IE_WEP_OPTIONS);
+
+	acx100_s_set_wepkey(priv);
+
+	if (priv->wep_keys[priv->wep_current_index].size != 0) {
+		acxlog(L_ASSOC, "setting active default WEP key number: %d\n",
+				priv->wep_current_index);
+		dk.KeyID = priv->wep_current_index;
+		acx_s_configure(priv, &dk, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET); /* 0x1010 */
+	}
+	/* FIXME!!! wep_key_struct is filled nowhere! But priv
+	 * is initialized to 0, and we don't REALLY need those keys either */
+/*		for (i = 0; i < 10; i++) {
+		if (priv->wep_key_struct[i].len != 0) {
+			MAC_COPY(wep_mgmt.MacAddr, priv->wep_key_struct[i].addr);
+			wep_mgmt.KeySize = cpu_to_le16(priv->wep_key_struct[i].len);
+			memcpy(&wep_mgmt.Key, priv->wep_key_struct[i].key, le16_to_cpu(wep_mgmt.KeySize));
+			wep_mgmt.Action = cpu_to_le16(1);
+			acxlog(L_ASSOC, "writing WEP key %d (len %d)\n", i, le16_to_cpu(wep_mgmt.KeySize));
+			if (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_WEP_MGMT, &wep_mgmt, sizeof(wep_mgmt))) {
+				priv->wep_key_struct[i].index = i;
+			}
+		}
+	} */
+
+	/* now retrieve the updated WEPCacheEnd pointer... */
+	if (OK != acx_s_interrogate(priv, &pt, ACX1xx_IE_MEMORY_MAP)) {
+		printk("%s: ctlMemoryMapRead #2 FAILED\n", priv->netdev->name);
+		goto fail;
+	}
+	/* ...and tell it to start allocating templates at that location */
+	/* (no endianness conversion needed) */
+	pt.PacketTemplateStart = pt.WEPCacheEnd;
+
+	if (OK != acx_s_configure(priv, &pt, ACX1xx_IE_MEMORY_MAP)) {
+		printk("%s: ctlMemoryMapWrite #2 FAILED\n", priv->netdev->name);
+		goto fail;
+	}
+	res = OK;
+
+fail:
+	FN_EXIT1(res);
+	return res;
+}
+
+
+/***********************************************************************
+*/
+static int
+acx_s_init_max_null_data_template(wlandevice_t *priv)
+{
+	struct acxp80211_nullframe b;
+	int result;
+
+	FN_ENTER;
+	memset(&b, 0, sizeof(b));
+	b.size = cpu_to_le16(sizeof(b) - 2);
+	result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_NULL_DATA, &b, sizeof(b));
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/***********************************************************************
+** acx_s_init_max_beacon_template
+*/
+static int
+acx_s_init_max_beacon_template(wlandevice_t *priv)
+{
+	struct acx_template_beacon b;
+	int result;
+
+	FN_ENTER;
+	memset(&b, 0, sizeof(b));
+	b.size = cpu_to_le16(sizeof(b) - 2);
+	result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_BEACON, &b, sizeof(b));
+
+	FN_EXIT1(result);
+	return result;
+}
+
+/***********************************************************************
+** acx_s_init_max_tim_template
+*/
+static int
+acx_s_init_max_tim_template(wlandevice_t *priv)
+{
+	acx_template_tim_t t;
+
+	memset(&t, 0, sizeof(t));
+	t.size = cpu_to_le16(sizeof(t) - 2);
+	return acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_TIM, &t, sizeof(t));
+}
+
+
+/***********************************************************************
+** acx_s_init_max_probe_response_template
+*/
+static int
+acx_s_init_max_probe_response_template(wlandevice_t *priv)
+{
+	struct acx_template_proberesp pr;
+
+	memset(&pr, 0, sizeof(pr));
+	pr.size = cpu_to_le16(sizeof(pr) - 2);
+
+	return acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_RESPONSE, &pr, sizeof(pr));
+}
+
+
+/***********************************************************************
+** acx_s_init_max_probe_request_template
+*/
+static int
+acx_s_init_max_probe_request_template(wlandevice_t *priv)
+{
+	union {
+		acx100_template_probereq_t p100;
+		acx111_template_probereq_t p111;
+	} pr;
+	int res;
+
+	FN_ENTER;
+	memset(&pr, 0, sizeof(pr));
+	pr.p100.size = cpu_to_le16(sizeof(pr) - 2);
+	res = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_REQUEST, &pr, sizeof(pr));
+	FN_EXIT1(res);
+	return res;
+}
+
+
+/***********************************************************************
+** acx_s_set_tim_template
+**
+** In full blown driver we will regularly update partial virtual bitmap
+** by calling this function
+** (it can be done by irq handler on each DTIM irq or by timer...)
+
+[802.11 7.3.2.6] TIM information element:
+- 1 EID
+- 1 Length
+1 1 DTIM Count
+    indicates how many beacons (including this) appear before next DTIM
+    (0=this one is a DTIM)
+2 1 DTIM Period
+    number of beacons between successive DTIMs
+    (0=reserved, 1=all TIMs are DTIMs, 2=every other, etc)
+3 1 Bitmap Control
+    bit0: Traffic Indicator bit associated with Assoc ID 0 (Bcast AID?)
+    set to 1 in TIM elements with a value of 0 in the DTIM Count field
+    when one or more broadcast or multicast frames are buffered at the AP.
+    bit1-7: Bitmap Offset (logically Bitmap_Offset = Bitmap_Control & 0xFE).
+4 n Partial Virtual Bitmap
+    Visible part of traffic-indication bitmap.
+    Full bitmap consists of 2008 bits (251 octets) such that bit number N
+    (0<=N<=2007) in the bitmap corresponds to bit number (N mod 8)
+    in octet number N/8 where the low-order bit of each octet is bit0,
+    and the high order bit is bit7.
+    Each set bit in virtual bitmap corresponds to traffic buffered by AP
+    for a specific station (with corresponding AID?).
+    Partial Virtual Bitmap shows a part of bitmap which has non-zero.
+    Bitmap Offset is a number of skipped zero octets (see above).
+    'Missing' octets at the tail are also assumed to be zero.
+    Example: Length=6, Bitmap_Offset=2, Partial_Virtual_Bitmap=55 55 55
+    This means that traffic-indication bitmap is:
+    00000000 00000000 01010101 01010101 01010101 00000000 00000000...
+    (is bit0 in the map is always 0 and real value is in Bitmap Control bit0?)
+*/
+static int
+acx_s_set_tim_template(wlandevice_t *priv)
+{
+/* For now, configure smallish test bitmap, all zero ("no pending data") */
+	enum { bitmap_size = 5 };
+
+	acx_template_tim_t t;
+	int result;
+
+	FN_ENTER;
+
+	memset(&t, 0, sizeof(t));
+	t.size = 5 + bitmap_size; /* eid+len+count+period+bmap_ctrl + bmap */
+	t.tim_eid = WLAN_EID_TIM;
+	t.len = 3 + bitmap_size; /* count+period+bmap_ctrl + bmap */
+	result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_TIM, &t, sizeof(t));
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/***********************************************************************
+** acx_fill_beacon_or_proberesp_template
+**
+** For frame format info, please see 802.11-1999.pdf item 7.2.3.9 and below!!
+**
+** STATUS: done
+** WARNING/FIXME/TODO: this needs to be called (via SET_TEMPLATES) *whenever*
+** *any* of the parameters contained in it change!!!
+** fishy status fixed
+**
+** NB: we use the fact that
+** struct acx_template_proberesp and struct acx_template_beacon are the same
+** (well, almost...)
+**
+** [802.11] Beacon's body consist of these IEs:
+** 1 Timestamp
+** 2 Beacon interval
+** 3 Capability information
+** 4 SSID
+** 5 Supported rates (up to 8 rates)
+** 6 FH Parameter Set (frequency-hopping PHYs only)
+** 7 DS Parameter Set (direct sequence PHYs only)
+** 8 CF Parameter Set (only if PCF is supported)
+** 9 IBSS Parameter Set (ad-hoc only)
+**
+** Beacon only:
+** 10 TIM (AP only) (see 802.11 7.3.2.6)
+** 11 Country Information (802.11d)
+** 12 FH Parameters (802.11d)
+** 13 FH Pattern Table (802.11d)
+** ... (?!! did not yet find relevant PDF file... --vda)
+** 19 ERP Information (extended rate PHYs)
+** 20 Extended Supported Rates (if more than 8 rates)
+**
+** Proberesp only:
+** 10 Country information (802.11d)
+** 11 FH Parameters (802.11d)
+** 12 FH Pattern Table (802.11d)
+** 13-n Requested information elements (802.11d)
+** ????
+** 18 ERP Information (extended rate PHYs)
+** 19 Extended Supported Rates (if more than 8 rates)
+*/
+static int
+acx_fill_beacon_or_proberesp_template(wlandevice_t *priv,
+					struct acx_template_beacon *templ,
+					u16 fc /* in host order! */)
+{
+	int len;
+	u8 *p;
+
+	FN_ENTER;
+
+	memset(templ, 0, sizeof(*templ));
+	MAC_BCAST(templ->da);
+	MAC_COPY(templ->sa, priv->dev_addr);
+	MAC_COPY(templ->bssid, priv->bssid);
+
+	templ->beacon_interval = cpu_to_le16(priv->beacon_interval);
+	acx_update_capabilities(priv);
+	templ->cap = cpu_to_le16(priv->capabilities);
+
+	p = templ->variable;
+	p = wlan_fill_ie_ssid(p, priv->essid_len, priv->essid);
+	p = wlan_fill_ie_rates(p, priv->rate_supported_len, priv->rate_supported);
+	p = wlan_fill_ie_ds_parms(p, priv->channel);
+	/* NB: should go AFTER tim, but acx seem to keep tim last always */
+	p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, priv->rate_supported);
+
+	switch (priv->mode) {
+	case ACX_MODE_0_ADHOC:
+		/* ATIM window */
+		p = wlan_fill_ie_ibss_parms(p, 0); break;
+	case ACX_MODE_3_AP:
+		/* TIM IE is set up as separate template */
+		break;
+	}
+
+	len = p - (u8*)templ;
+	templ->fc = cpu_to_le16(WF_FTYPE_MGMT | fc);
+	/* - 2: do not count 'u16 size' field */
+	templ->size = cpu_to_le16(len - 2);
+
+	FN_EXIT1(len);
+	return len;
+}
+
+
+/***********************************************************************
+** acx_s_set_beacon_template
+*/
+static int
+acx_s_set_beacon_template(wlandevice_t *priv)
+{
+	struct acx_template_beacon bcn;
+	int len, result;
+
+	FN_ENTER;
+
+	len = acx_fill_beacon_or_proberesp_template(priv, &bcn, WF_FSTYPE_BEACON);
+	result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_BEACON, &bcn, len);
+
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/***********************************************************************
+** acx_s_set_probe_response_template
+*/
+static int
+acx_s_set_probe_response_template(wlandevice_t *priv)
+{
+	struct acx_template_proberesp pr;
+	int len, result;
+
+	FN_ENTER;
+
+	len = acx_fill_beacon_or_proberesp_template(priv, &pr, WF_FSTYPE_PROBERESP);
+	result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_RESPONSE, &pr, len);
+
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/***********************************************************************
+** acx100_s_init_packet_templates()
+**
+** NOTE: order is very important here, to have a correct memory layout!
+** init templates: max Probe Request (station mode), max NULL data,
+** max Beacon, max TIM, max Probe Response.
+**
+** acxInitPacketTemplates()
+** STATUS: almost ok, except for struct definitions.
+*/
+static int
+acx100_s_init_packet_templates(wlandevice_t *priv)
+{
+	acx_ie_memmap_t mm;
+	int result = NOT_OK;
+
+	FN_ENTER;
+
+	acxlog(L_DEBUG, "sizeof(memmap)=%d bytes\n", (int)sizeof(mm));
+
+	/* acx100 still do not emit probe requests, thus this call
+	** is sourt of not needed. But we want it to work someday */
+	if (OK != acx_s_init_max_probe_request_template(priv))
+		goto failed;
+
+#if NOT_WORKING_YET
+	/* FIXME: creating the NULL data template breaks
+	 * communication right now, needs further testing.
+	 * Also, need to set the template once we're joining a network. */
+	if (OK != acx_s_init_max_null_data_template(priv))
+		goto failed;
+#endif
+
+	if (OK != acx_s_init_max_beacon_template(priv))
+		goto failed;
+
+	/* TODO: beautify code by moving init_tim down just before set_tim */
+	if (OK != acx_s_init_max_tim_template(priv))
+		goto failed;
+
+	if (OK != acx_s_init_max_probe_response_template(priv))
+		goto failed;
+
+	if (OK != acx_s_set_tim_template(priv))
+		goto failed;
+
+	if (OK != acx_s_interrogate(priv, &mm, ACX1xx_IE_MEMORY_MAP)) {
+		printk("%s: memmap req FAILED\n", priv->netdev->name);
+		goto failed;
+	}
+
+	mm.QueueStart = cpu_to_le32(le32_to_cpu(mm.PacketTemplateEnd) + 4);
+	if (OK != acx_s_configure(priv, &mm, ACX1xx_IE_MEMORY_MAP)) {
+		printk("%s: memmap cfg FAILED\n", priv->netdev->name);
+		goto failed;
+	}
+
+	result = OK;
+	goto success;
+
+failed:
+	acxlog(L_DEBUG | L_INIT,
+		/* "cb=0x%X\n" */
+		"pACXMemoryMap:\n"
+		".CodeStart=0x%X\n"
+		".CodeEnd=0x%X\n"
+		".WEPCacheStart=0x%X\n"
+		".WEPCacheEnd=0x%X\n"
+		".PacketTemplateStart=0x%X\n"
+		".PacketTemplateEnd=0x%X\n",
+		/* len, */
+		le32_to_cpu(mm.CodeStart),
+		le32_to_cpu(mm.CodeEnd),
+		le32_to_cpu(mm.WEPCacheStart),
+		le32_to_cpu(mm.WEPCacheEnd),
+		le32_to_cpu(mm.PacketTemplateStart),
+		le32_to_cpu(mm.PacketTemplateEnd));
+
+success:
+	FN_EXIT1(result);
+	return result;
+}
+
+static int
+acx111_s_init_packet_templates(wlandevice_t *priv)
+{
+	int result = NOT_OK;
+
+	FN_ENTER;
+
+	acxlog(L_DEBUG | L_INIT, "initializing max packet templates\n");
+
+	if (OK != acx_s_init_max_probe_request_template(priv))
+		goto failed;
+
+	if (OK != acx_s_init_max_null_data_template(priv))
+		goto failed;
+
+	if (OK != acx_s_init_max_beacon_template(priv))
+		goto failed;
+
+	if (OK != acx_s_init_max_tim_template(priv))
+		goto failed;
+
+	if (OK != acx_s_init_max_probe_response_template(priv))
+		goto failed;
+
+	/* the other templates will be set later (acx_start) */
+	/*
+	if (OK != acx_s_set_tim_template(priv))
+		goto failed;*/
+
+	result = OK;
+	goto success;
+
+failed:
+	printk("%s: acx111_init_packet_templates() FAILED\n", priv->netdev->name);
+
+success:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/***********************************************************************
+*/
+static int
+acx100_s_set_probe_request_template(wlandevice_t *priv)
+{
+	struct acx100_template_probereq probereq;
+	char *p;
+	int res;
+	int frame_len;
+
+	FN_ENTER;
+
+	memset(&probereq, 0, sizeof(probereq));
+
+	probereq.fc = WF_FTYPE_MGMTi | WF_FSTYPE_PROBEREQi;
+	MAC_BCAST(probereq.da);
+	MAC_COPY(probereq.sa, priv->dev_addr);
+	MAC_BCAST(probereq.bssid);
+
+	probereq.beacon_interval = cpu_to_le16(priv->beacon_interval);
+	acx_update_capabilities(priv);
+	probereq.cap = cpu_to_le16(priv->capabilities);
+
+	p = probereq.variable;
+	acxlog(L_ASSOC, "SSID='%s' len=%d\n", priv->essid, priv->essid_len);
+	p = wlan_fill_ie_ssid(p, priv->essid_len, priv->essid);
+	p = wlan_fill_ie_rates(p, priv->rate_supported_len, priv->rate_supported);
+	/* FIXME: should these be here or AFTER ds_parms? */
+	p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, priv->rate_supported);
+	/* HUH?? who said it must be here? I've found nothing in 802.11! --vda*/
+	/* p = wlan_fill_ie_ds_parms(p, priv->channel); */
+	frame_len = p - (char*)&probereq;
+	probereq.size = frame_len - 2;
+
+	res = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_REQUEST, &probereq, frame_len);
+	FN_EXIT0;
+	return res;
+}
+
+static int
+acx111_s_set_probe_request_template(wlandevice_t *priv)
+{
+	struct acx111_template_probereq probereq;
+	char *p;
+	int res;
+	int frame_len;
+
+	FN_ENTER;
+
+	memset(&probereq, 0, sizeof(probereq));
+
+	probereq.fc = WF_FTYPE_MGMTi | WF_FSTYPE_PROBEREQi;
+	MAC_BCAST(probereq.da);
+	MAC_COPY(probereq.sa, priv->dev_addr);
+	MAC_BCAST(probereq.bssid);
+
+	p = probereq.variable;
+	p = wlan_fill_ie_ssid(p, priv->essid_len, priv->essid);
+	p = wlan_fill_ie_rates(p, priv->rate_supported_len, priv->rate_supported);
+	p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, priv->rate_supported);
+	frame_len = p - (char*)&probereq;
+	probereq.size = frame_len - 2;
+
+	res = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_REQUEST, &probereq, frame_len);
+	FN_EXIT0;
+	return res;
+}
+
+static int
+acx_s_set_probe_request_template(wlandevice_t *priv)
+{
+	if (priv->chip_type == CHIPTYPE_ACX111) {
+		return acx111_s_set_probe_request_template(priv);
+	} else {
+		return acx100_s_set_probe_request_template(priv);
+	}
+}
+
+
+/***********************************************************************
+** FIXME: this should be solved in a general way for all radio types
+** by decoding the radio firmware module,
+** since it probably has some standard structure describing how to
+** set the power level of the radio module which it controls.
+** Or maybe not, since the radio module probably has a function interface
+** instead which then manages Tx level programming :-\
+*/
+static int
+acx100_s_set_tx_level(wlandevice_t *priv, u8 level_dbm)
+{
+	/* since it can be assumed that at least the Maxim radio has a
+	 * maximum power output of 20dBm and since it also can be
+	 * assumed that these values drive the DAC responsible for
+	 * setting the linear Tx level, I'd guess that these values
+	 * should be the corresponding linear values for a dBm value,
+	 * in other words: calculate the values from that formula:
+	 * Y [dBm] = 10 * log (X [mW])
+	 * then scale the 0..63 value range onto the 1..100mW range (0..20 dBm)
+	 * and you're done...
+	 * Hopefully that's ok, but you never know if we're actually
+	 * right... (especially since Windows XP doesn't seem to show
+	 * actual Tx dBm values :-P) */
+#ifdef ACX_PCI
+	/* NOTE: on Maxim, value 30 IS 30mW, and value 10 IS 10mW - so the
+	 * values are EXACTLY mW!!! Not sure about RFMD and others,
+	 * though... */
+	static const u8 dbm2val_maxim[21] = {
+		63, 63, 63, 62,
+		61, 61, 60, 60,
+		59, 58, 57, 55,
+		53, 50, 47, 43,
+		38, 31, 23, 13,
+		0
+	};
+	static const u8 dbm2val_rfmd[21] = {
+		 0,  0,  0,  1,
+		 2,  2,  3,  3,
+		 4,  5,  6,  8,
+		10, 13, 16, 20,
+		25, 32, 41, 50,
+		63
+	};
+	const u8 *table;
+
+	switch (priv->radio_type) {
+		case RADIO_MAXIM_0D:
+			table = &dbm2val_maxim[0];
+			break;
+		case RADIO_RFMD_11:
+		case RADIO_RALINK_15:
+			table = &dbm2val_rfmd[0];
+			break;
+		default:
+			printk("%s: unknown/unsupported radio type, "
+				"cannot modify tx power level yet!\n",
+					priv->netdev->name);
+			return NOT_OK;
+	}
+	printk("%s: changing radio power level to %u dBm (%u)\n",
+			priv->netdev->name, level_dbm, table[level_dbm]);
+	acx_s_write_phy_reg(priv, 0x11, table[level_dbm]);
+#endif
+	return OK;
+}
+
+static int
+acx111_s_set_tx_level(wlandevice_t *priv, u8 level_dbm)
+{
+	struct ACX111TxLevel tx_level;
+
+	/* my acx111 card has two power levels in its configoptions (== EEPROM):
+	 * 1 (30mW) [15dBm]
+	 * 2 (10mW) [10dBm]
+	 * For now, just assume all other acx111 cards have the same.
+	 * Ideally we would query it here, but we first need a
+	 * standard way to query individual configoptions easily. */
+	if (level_dbm <= 12) {
+		tx_level.level = 2; /* 10 dBm */
+		priv->tx_level_dbm = 10;
+	} else {
+		tx_level.level = 1; /* 15 dBm */
+		priv->tx_level_dbm = 15;
+	}
+	if (level_dbm != priv->tx_level_dbm)
+		acxlog(L_INIT, "ACX111 firmware has specific "
+			"power levels only: adjusted %d dBm to %d dBm!\n",
+			level_dbm, priv->tx_level_dbm);
+
+	if (OK != acx_s_configure(priv, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL)) {
+		printk("%s: error setting acx111 tx level\n", priv->netdev->name);
+		return NOT_OK;
+	}
+	return OK;
+}
+
+static int
+acx_s_set_tx_level(wlandevice_t *priv, u8 level_dbm)
+{
+	if (priv->chip_type == CHIPTYPE_ACX111) {
+		return acx111_s_set_tx_level(priv, level_dbm);
+	}
+	return acx100_s_set_tx_level(priv, level_dbm);
+}
+
+
+/***********************************************************************
+*/
+#ifdef UNUSED
+/* Returns the current tx level (ACX111) */
+static u8
+acx111_s_get_tx_level(wlandevice_t *priv)
+{
+	struct ACX111TxLevel tx_level;
+
+	tx_level.level = 0;
+	if (OK != acx_s_interrogate(priv, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL)) {
+		printk("%s: error getting acx111 tx level\n", priv->netdev->name);
+	}
+	return tx_level.level;
+}
+#endif
+
+
+/***********************************************************************
+*/
+int
+acx111_s_get_feature_config(wlandevice_t *priv,
+		u32 *feature_options, u32 *data_flow_options)
+{
+	struct ACX111FeatureConfig fc;
+
+	if (priv->chip_type != CHIPTYPE_ACX111) {
+		return NOT_OK;
+	}
+
+	memset(&fc, 0, sizeof(struct ACX111FeatureConfig));
+
+	if (OK != acx_s_interrogate(priv, &fc, ACX1xx_IE_FEATURE_CONFIG)) {
+		printk("%s: error reading feature config\n",
+						priv->netdev->name);
+		return NOT_OK;
+	}
+	acxlog(L_DEBUG,
+		"got Feature option:0x%X, DataFlow option: 0x%X\n",
+		fc.feature_options,
+		fc.data_flow_options);
+
+	if (feature_options)
+		*feature_options = le32_to_cpu(fc.feature_options);
+	if (data_flow_options)
+		*data_flow_options = le32_to_cpu(fc.data_flow_options);
+
+	return OK;
+}
+
+int
+acx111_s_set_feature_config(wlandevice_t *priv,
+	u32 feature_options, u32 data_flow_options,
+	unsigned int mode /* 0 == remove, 1 == add, 2 == set */)
+{
+	struct ACX111FeatureConfig fc;
+
+	if (priv->chip_type != CHIPTYPE_ACX111) {
+		return NOT_OK;
+	}
+
+	if ((mode < 0) || (mode > 2))
+		return NOT_OK;
+
+	if (mode != 2)
+		/* need to modify old data */
+		acx111_s_get_feature_config(priv, &fc.feature_options, &fc.data_flow_options);
+	else {
+		/* need to set a completely new value */
+		fc.feature_options = 0;
+		fc.data_flow_options = 0;
+	}
+
+	if (mode == 0) { /* remove */
+		CLEAR_BIT(fc.feature_options, cpu_to_le32(feature_options));
+		CLEAR_BIT(fc.data_flow_options, cpu_to_le32(data_flow_options));
+	} else { /* add or set */
+		SET_BIT(fc.feature_options, cpu_to_le32(feature_options));
+		SET_BIT(fc.data_flow_options, cpu_to_le32(data_flow_options));
+	}
+
+	acxlog(L_DEBUG,
+		"old: feature 0x%08X dataflow 0x%08X. mode: %u\n"
+		"new: feature 0x%08X dataflow 0x%08X\n",
+		feature_options, data_flow_options, mode,
+		le32_to_cpu(fc.feature_options),
+		le32_to_cpu(fc.data_flow_options));
+
+	if (OK != acx_s_configure(priv, &fc, ACX1xx_IE_FEATURE_CONFIG)) {
+		printk("%s: error setting feature config\n", priv->netdev->name);
+		return NOT_OK;
+	}
+
+	return OK;
+}
+
+
+/***********************************************************************
+*/
+int
+acx_s_recalib_radio(wlandevice_t *priv)
+{
+	if (CHIPTYPE_ACX111 == priv->chip_type) {
+		acx111_cmd_radiocalib_t cal;
+
+		printk("%s: recalibrating radio\n", priv->netdev->name);
+		/* automatic recalibration, choose all methods: */
+		cal.methods = cpu_to_le32(0x8000000f);
+		/* automatic recalibration every 60 seconds (value in TUs)
+		 * FIXME: what is the firmware default here?? */
+		cal.interval = cpu_to_le32(58594);
+		return acx_s_issue_cmd_timeo(priv, ACX111_CMD_RADIOCALIB,
+			&cal, sizeof(cal), CMD_TIMEOUT_MS(100));
+	} else {
+		if (/* (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_TX, NULL, 0)) &&
+		    (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_RX, NULL, 0)) && */
+		    (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_TX, &(priv->channel), 1)) &&
+		    (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_RX, &(priv->channel), 1)) )
+			return OK;
+		return NOT_OK;
+	}
+}
+
+
+/***********************************************************************
+** acx_s_cmd_start_scan
+**
+** Issue scan command to the hardware
+*/
+static void
+acx100_s_scan_chan(wlandevice_t *priv)
+{
+	acx100_scan_t s;
+
+	FN_ENTER;
+
+	memset(&s, 0, sizeof(s));
+	s.count = cpu_to_le16(priv->scan_count);
+	s.start_chan = cpu_to_le16(1);
+	s.flags = cpu_to_le16(0x8000);
+	s.max_rate = priv->scan_rate;
+	s.options = priv->scan_mode;
+	s.chan_duration = cpu_to_le16(priv->scan_duration);
+	s.max_probe_delay = cpu_to_le16(priv->scan_probe_delay);
+
+	acx_s_issue_cmd(priv, ACX1xx_CMD_SCAN, &s, sizeof(s));
+	FN_EXIT0;
+}
+
+static void
+acx111_s_scan_chan(wlandevice_t *priv)
+{
+	acx111_scan_t s;
+
+	FN_ENTER;
+
+	memset(&s, 0, sizeof(s));
+	s.count = cpu_to_le16(priv->scan_count);
+	s.channel_list_select = 0; /* scan every allowed channel */
+	/*s.channel_list_select = 1;*/ /* scan given channels */
+	s.rate = priv->scan_rate;
+	s.options = priv->scan_mode;
+	s.chan_duration = cpu_to_le16(priv->scan_duration);
+	s.max_probe_delay = cpu_to_le16(priv->scan_probe_delay);
+	/*s.modulation = 0x40;*/ /* long preamble? OFDM? -> only for active scan */
+	s.modulation = 0;
+	/*s.channel_list[0] = 6;
+	s.channel_list[1] = 4;*/
+
+	acx_s_issue_cmd(priv, ACX1xx_CMD_SCAN, &s, sizeof(s));
+	FN_EXIT0;
+}
+
+void
+acx_s_cmd_start_scan(wlandevice_t *priv)
+{
+	/* time_before check is 'just in case' thing */
+	if (!(priv->irq_status & HOST_INT_SCAN_COMPLETE)
+	 && time_before(jiffies, priv->scan_start + 10*HZ)
+	) {
+		acxlog(L_INIT, "start_scan: seems like previous scan "
+		"is still running. Not starting anew. Please report\n");
+		return;
+	}
+
+	acxlog(L_INIT, "starting radio scan\n");
+	/* remember that fw is commanded to do scan */
+	priv->scan_start = jiffies;
+	CLEAR_BIT(priv->irq_status, HOST_INT_SCAN_COMPLETE);
+	/* issue it */
+	if (priv->chip_type == CHIPTYPE_ACX100) {
+		acx100_s_scan_chan(priv);
+	} else {
+		acx111_s_scan_chan(priv);
+	}
+}
+
+
+/***********************************************************************
+** acx_s_update_card_settings
+**
+** Applies accumulated changes in various priv->xxxx members
+** Called by ioctl commit handler, acx_start, acx_set_defaults,
+** acx_s_after_interrupt_task (if IRQ_CMD_UPDATE_CARD_CFG),
+*/
+static void
+acx111_s_sens_radio_16_17(wlandevice_t *priv)
+{
+	u32 feature1, feature2;
+
+	if ((priv->sensitivity < 1) || (priv->sensitivity > 3)) {
+		printk("%s: invalid sensitivity setting (1..3), "
+			"setting to 1\n", priv->netdev->name);
+		priv->sensitivity = 1;
+	}
+	acx111_s_get_feature_config(priv, &feature1, &feature2);
+	CLEAR_BIT(feature1, FEATURE1_LOW_RX|FEATURE1_EXTRA_LOW_RX);
+	if (priv->sensitivity > 1)
+		SET_BIT(feature1, FEATURE1_LOW_RX);
+	if (priv->sensitivity > 2)
+		SET_BIT(feature1, FEATURE1_EXTRA_LOW_RX);
+	acx111_s_feature_set(priv, feature1, feature2);
+}
+
+void
+acx_s_update_card_settings(wlandevice_t *priv, int get_all, int set_all)
+{
+	unsigned long flags;
+	unsigned int start_scan = 0;
+	int i;
+
+	FN_ENTER;
+
+	if (get_all)
+		SET_BIT(priv->get_mask, GETSET_ALL);
+	if (set_all)
+		SET_BIT(priv->set_mask, GETSET_ALL);
+	/* Why not just set masks to 0xffffffff? We can get rid of GETSET_ALL */
+
+	acxlog(L_INIT, "get_mask 0x%08X, set_mask 0x%08X\n",
+			priv->get_mask, priv->set_mask);
+
+	/* Track dependencies betweed various settings */
+
+	if (priv->set_mask & (GETSET_MODE|GETSET_RESCAN|GETSET_WEP)) {
+		acxlog(L_INIT, "important setting has been changed. "
+			"Need to update packet templates, too\n");
+		SET_BIT(priv->set_mask, SET_TEMPLATES);
+	}
+	if (priv->set_mask & (GETSET_CHANNEL|GETSET_ALL)) {
+		/* This will actually tune RX/TX to the channel */
+		SET_BIT(priv->set_mask, GETSET_RX|GETSET_TX);
+		switch (priv->mode) {
+		case ACX_MODE_0_ADHOC:
+		case ACX_MODE_3_AP:
+			/* Beacons contain channel# - update them */
+			SET_BIT(priv->set_mask, SET_TEMPLATES);
+		}
+		switch (priv->mode) {
+		case ACX_MODE_0_ADHOC:
+		case ACX_MODE_2_STA:
+			start_scan = 1;
+		}
+	}
+
+	/* Apply settings */
+
+#ifdef WHY_SHOULD_WE_BOTHER /* imagine we were just powered off */
+	/* send a disassoc request in case it's required */
+	if (priv->set_mask & (GETSET_MODE|GETSET_RESCAN|GETSET_CHANNEL|GETSET_WEP|GETSET_ALL)) {
+		if (ACX_MODE_2_STA == priv->mode) {
+			if (ACX_STATUS_4_ASSOCIATED == priv->status) {
+				acxlog(L_ASSOC, "we were ASSOCIATED - "
+					"sending disassoc request\n");
+				acx_lock(priv, flags);
+				acx_l_transmit_disassoc(priv, NULL);
+				/* FIXME: deauth? */
+				acx_unlock(priv, flags);
+			}
+			/* need to reset some other stuff as well */
+			acxlog(L_DEBUG, "resetting bssid\n");
+			MAC_ZERO(priv->bssid);
+			SET_BIT(priv->set_mask, SET_TEMPLATES|SET_STA_LIST);
+			/* FIXME: should start scanning */
+			start_scan = 1;
+		}
+	}
+#endif
+
+	if (priv->get_mask & (GETSET_STATION_ID|GETSET_ALL)) {
+		u8 stationID[4 + ACX1xx_IE_DOT11_STATION_ID_LEN];
+		const u8 *paddr;
+
+		acx_s_interrogate(priv, &stationID, ACX1xx_IE_DOT11_STATION_ID);
+		paddr = &stationID[4];
+		for (i = 0; i < ETH_ALEN; i++) {
+			/* we copy the MAC address (reversed in
+			 * the card) to the netdevice's MAC
+			 * address, and on ifup it will be
+			 * copied into iwpriv->dev_addr */
+			priv->netdev->dev_addr[ETH_ALEN - 1 - i] = paddr[i];
+		}
+		CLEAR_BIT(priv->get_mask, GETSET_STATION_ID);
+	}
+
+	if (priv->get_mask & (GETSET_SENSITIVITY|GETSET_ALL)) {
+		if ((RADIO_RFMD_11 == priv->radio_type)
+		|| (RADIO_MAXIM_0D == priv->radio_type)
+		|| (RADIO_RALINK_15 == priv->radio_type)) {
+			acx_s_read_phy_reg(priv, 0x30, &priv->sensitivity);
+		} else {
+			acxlog(L_INIT, "don't know how to get sensitivity "
+				"for radio type 0x%02X\n", priv->radio_type);
+			priv->sensitivity = 0;
+		}
+		acxlog(L_INIT, "got sensitivity value %u\n", priv->sensitivity);
+
+		CLEAR_BIT(priv->get_mask, GETSET_SENSITIVITY);
+	}
+
+	if (priv->get_mask & (GETSET_ANTENNA|GETSET_ALL)) {
+		u8 antenna[4 + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN];
+
+		memset(antenna, 0, sizeof(antenna));
+		acx_s_interrogate(priv, antenna, ACX1xx_IE_DOT11_CURRENT_ANTENNA);
+		priv->antenna = antenna[4];
+		acxlog(L_INIT, "got antenna value 0x%02X\n", priv->antenna);
+		CLEAR_BIT(priv->get_mask, GETSET_ANTENNA);
+	}
+
+	if (priv->get_mask & (GETSET_ED_THRESH|GETSET_ALL)) {
+		if (priv->chip_type == CHIPTYPE_ACX100)	{
+			u8 ed_threshold[4 + ACX100_IE_DOT11_ED_THRESHOLD_LEN];
+
+			memset(ed_threshold, 0, sizeof(ed_threshold));
+			acx_s_interrogate(priv, ed_threshold, ACX100_IE_DOT11_ED_THRESHOLD);
+			priv->ed_threshold = ed_threshold[4];
+		} else {
+			acxlog(L_INIT, "acx111 doesn't support ED\n");
+			priv->ed_threshold = 0;
+		}
+		acxlog(L_INIT, "got Energy Detect (ED) threshold %u\n", priv->ed_threshold);
+		CLEAR_BIT(priv->get_mask, GETSET_ED_THRESH);
+	}
+
+	if (priv->get_mask & (GETSET_CCA|GETSET_ALL)) {
+		if (priv->chip_type == CHIPTYPE_ACX100)	{
+			u8 cca[4 + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN];
+
+			memset(cca, 0, sizeof(priv->cca));
+			acx_s_interrogate(priv, cca, ACX1xx_IE_DOT11_CURRENT_CCA_MODE);
+			priv->cca = cca[4];
+		} else {
+			acxlog(L_INIT, "acx111 doesn't support CCA\n");
+			priv->cca = 0;
+		}
+		acxlog(L_INIT, "got Channel Clear Assessment (CCA) value %u\n", priv->cca);
+		CLEAR_BIT(priv->get_mask, GETSET_CCA);
+	}
+
+	if (priv->get_mask & (GETSET_REG_DOMAIN|GETSET_ALL)) {
+		acx_ie_generic_t dom;
+
+		acx_s_interrogate(priv, &dom, ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN);
+		priv->reg_dom_id = dom.m.bytes[0];
+		/* FIXME: should also set chanmask somehow */
+		acxlog(L_INIT, "got regulatory domain 0x%02X\n", priv->reg_dom_id);
+		CLEAR_BIT(priv->get_mask, GETSET_REG_DOMAIN);
+	}
+
+	if (priv->set_mask & (GETSET_STATION_ID|GETSET_ALL)) {
+		u8 stationID[4 + ACX1xx_IE_DOT11_STATION_ID_LEN];
+		u8 *paddr;
+
+		paddr = &stationID[4];
+		for (i = 0; i < ETH_ALEN; i++) {
+			/* copy the MAC address we obtained when we noticed
+			 * that the ethernet iface's MAC changed
+			 * to the card (reversed in
+			 * the card!) */
+			paddr[i] = priv->dev_addr[ETH_ALEN - 1 - i];
+		}
+		acx_s_configure(priv, &stationID, ACX1xx_IE_DOT11_STATION_ID);
+		CLEAR_BIT(priv->set_mask, GETSET_STATION_ID);
+	}
+
+	if (priv->set_mask & (SET_TEMPLATES|GETSET_ALL)) {
+		acxlog(L_INIT, "updating packet templates\n");
+		/* Doesn't work for acx100, do it only for acx111 for now */
+		if (priv->chip_type == CHIPTYPE_ACX111) {
+			switch (priv->mode) {
+			case ACX_MODE_0_ADHOC:
+			case ACX_MODE_2_STA:
+				if (OK != acx_s_set_probe_request_template(priv))
+					acxlog(L_INIT, "acx_set_probe_request_template FAILED\n");
+			}
+		}
+		switch (priv->mode) {
+		case ACX_MODE_0_ADHOC:
+		case ACX_MODE_3_AP:
+			/* FIXME: why only for AP? STA need probe req templates... */
+			if (OK != acx_s_set_beacon_template(priv))
+				acxlog(L_INIT, "acx_set_beacon_template FAILED\n");
+			if (OK != acx_s_set_tim_template(priv))
+				acxlog(L_INIT, "acx_set_tim_template FAILED\n");
+			/* BTW acx111 firmware would not send probe responses
+			** if probe request does not have all basic rates flagged
+			** by 0x80! Thus firmware does not conform to 802.11,
+			** it should ignore 0x80 bit in ratevector from STA.
+			** We can 'fix' it by not using this template and
+			** sending probe responses by hand. TODO --vda */
+			if (OK != acx_s_set_probe_response_template(priv))
+				acxlog(L_INIT, "acx_set_probe_response_template FAILED\n");
+		}
+		/* Needed if generated frames are to be emitted at different tx rate now */
+		acxlog(L_IRQ, "redoing cmd_join_bssid() after template cfg\n");
+		acx_s_cmd_join_bssid(priv, priv->bssid);
+		CLEAR_BIT(priv->set_mask, SET_TEMPLATES);
+	}
+	if (priv->set_mask & (SET_STA_LIST|GETSET_ALL)) {
+		/* TODO insert a sweet if here */
+		acx_lock(priv, flags);
+		acx_l_sta_list_init(priv);
+		CLEAR_BIT(priv->set_mask, SET_STA_LIST);
+		acx_unlock(priv, flags);
+	}
+	if (priv->set_mask & (SET_RATE_FALLBACK|GETSET_ALL)) {
+		u8 rate[4 + ACX1xx_IE_RATE_FALLBACK_LEN];
+
+		/* configure to not do fallbacks when not in auto rate mode */
+		rate[4] = (priv->rate_auto) ? /* priv->txrate_fallback_retries */ 1 : 0;
+		acxlog(L_INIT, "updating Tx fallback to %u retries\n", rate[4]);
+		acx_s_configure(priv, &rate, ACX1xx_IE_RATE_FALLBACK);
+		CLEAR_BIT(priv->set_mask, SET_RATE_FALLBACK);
+	}
+	if (priv->set_mask & (GETSET_TXPOWER|GETSET_ALL)) {
+		acxlog(L_INIT, "updating transmit power: %u dBm\n",
+					priv->tx_level_dbm);
+		acx_s_set_tx_level(priv, priv->tx_level_dbm);
+		CLEAR_BIT(priv->set_mask, GETSET_TXPOWER);
+	}
+
+	if (priv->set_mask & (GETSET_SENSITIVITY|GETSET_ALL)) {
+		acxlog(L_INIT, "updating sensitivity value: %u\n",
+					priv->sensitivity);
+		switch (priv->radio_type) {
+		case RADIO_RFMD_11:
+		case RADIO_MAXIM_0D:
+		case RADIO_RALINK_15:
+			acx_s_write_phy_reg(priv, 0x30, priv->sensitivity);
+			break;
+		case RADIO_RADIA_16:
+		case RADIO_UNKNOWN_17:
+			acx111_s_sens_radio_16_17(priv);
+			break;
+		default:
+			acxlog(L_INIT, "don't know how to modify sensitivity "
+				"for radio type 0x%02X\n", priv->radio_type);
+		}
+		CLEAR_BIT(priv->set_mask, GETSET_SENSITIVITY);
+	}
+
+	if (priv->set_mask & (GETSET_ANTENNA|GETSET_ALL)) {
+		/* antenna */
+		u8 antenna[4 + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN];
+
+		memset(antenna, 0, sizeof(antenna));
+		antenna[4] = priv->antenna;
+		acxlog(L_INIT, "updating antenna value: 0x%02X\n",
+					priv->antenna);
+		acx_s_configure(priv, &antenna, ACX1xx_IE_DOT11_CURRENT_ANTENNA);
+		CLEAR_BIT(priv->set_mask, GETSET_ANTENNA);
+	}
+
+	if (priv->set_mask & (GETSET_ED_THRESH|GETSET_ALL)) {
+		/* ed_threshold */
+		acxlog(L_INIT, "updating Energy Detect (ED) threshold: %u\n",
+					priv->ed_threshold);
+		if (CHIPTYPE_ACX100 == priv->chip_type) {
+			u8 ed_threshold[4 + ACX100_IE_DOT11_ED_THRESHOLD_LEN];
+
+			memset(ed_threshold, 0, sizeof(ed_threshold));
+			ed_threshold[4] = priv->ed_threshold;
+			acx_s_configure(priv, &ed_threshold, ACX100_IE_DOT11_ED_THRESHOLD);
+		}
+		else
+			acxlog(L_INIT, "ACX111 doesn't support ED!\n");
+		CLEAR_BIT(priv->set_mask, GETSET_ED_THRESH);
+	}
+
+	if (priv->set_mask & (GETSET_CCA|GETSET_ALL)) {
+		/* CCA value */
+		acxlog(L_INIT, "updating Channel Clear Assessment "
+				"(CCA) value: 0x%02X\n", priv->cca);
+		if (CHIPTYPE_ACX100 == priv->chip_type)	{
+			u8 cca[4 + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN];
+
+			memset(cca, 0, sizeof(cca));
+			cca[4] = priv->cca;
+			acx_s_configure(priv, &cca, ACX1xx_IE_DOT11_CURRENT_CCA_MODE);
+		}
+		else
+			acxlog(L_INIT, "acx111 doesn't support CCA!\n");
+		CLEAR_BIT(priv->set_mask, GETSET_CCA);
+	}
+
+	if (priv->set_mask & (GETSET_LED_POWER|GETSET_ALL)) {
+		/* Enable Tx */
+		acxlog(L_INIT, "updating power LED status: %u\n", priv->led_power);
+
+		acx_lock(priv, flags);
+		acx_l_power_led(priv, priv->led_power);
+		CLEAR_BIT(priv->set_mask, GETSET_LED_POWER);
+		acx_unlock(priv, flags);
+	}
+
+/* this seems to cause Tx lockup after some random time (Tx error 0x20),
+ * so let's disable it for now until further investigation */
+/* Maybe fixed now after locking is fixed. Need to retest */
+#if POWER_SAVE_80211
+	if (priv->set_mask & (GETSET_POWER_80211|GETSET_ALL)) {
+		acx100_ie_powermgmt_t pm;
+
+		/* change 802.11 power save mode settings */
+		acxlog(L_INIT, "updating 802.11 power save mode settings: "
+			"wakeup_cfg 0x%02X, listen interval %u, "
+			"options 0x%02X, hangover period %u, "
+			"enhanced_ps_transition_time %d\n",
+			priv->ps_wakeup_cfg, priv->ps_listen_interval,
+			priv->ps_options, priv->ps_hangover_period,
+			priv->ps_enhanced_transition_time);
+		acx_s_interrogate(priv, &pm, ACX100_IE_POWER_MGMT);
+		acxlog(L_INIT, "Previous PS mode settings: wakeup_cfg 0x%02X, "
+			"listen interval %u, options 0x%02X, "
+			"hangover period %u, "
+			"enhanced_ps_transition_time %d\n",
+			pm.wakeup_cfg, pm.listen_interval, pm.options,
+			pm.hangover_period, pm.enhanced_ps_transition_time);
+		pm.wakeup_cfg = priv->ps_wakeup_cfg;
+		pm.listen_interval = priv->ps_listen_interval;
+		pm.options = priv->ps_options;
+		pm.hangover_period = priv->ps_hangover_period;
+		pm.enhanced_ps_transition_time = cpu_to_le16(priv->ps_enhanced_transition_time);
+		acx_s_configure(priv, &pm, ACX100_IE_POWER_MGMT);
+		acx_s_interrogate(priv, &pm, ACX100_IE_POWER_MGMT);
+		acxlog(L_INIT, "wakeup_cfg: 0x%02X\n", pm.wakeup_cfg);
+		acx_s_msleep(40);
+		acx_s_interrogate(priv, &pm, ACX100_IE_POWER_MGMT);
+		acxlog(L_INIT, "power save mode change %s\n",
+			(pm.wakeup_cfg & PS_CFG_PENDING) ? "FAILED" : "was successful");
+		/* FIXME: maybe verify via PS_CFG_PENDING bit here
+		 * that power save mode change was successful. */
+		/* FIXME: we shouldn't trigger a scan immediately after
+		 * fiddling with power save mode (since the firmware is sending
+		 * a NULL frame then). Does this need locking?? */
+		CLEAR_BIT(priv->set_mask, GETSET_POWER_80211);
+	}
+#endif
+
+	if (priv->set_mask & (GETSET_CHANNEL|GETSET_ALL)) {
+		/* channel */
+		acxlog(L_INIT, "updating channel to: %u\n", priv->channel);
+		CLEAR_BIT(priv->set_mask, GETSET_CHANNEL);
+	}
+
+	if (priv->set_mask & (GETSET_TX|GETSET_ALL)) {
+		/* set Tx */
+		acxlog(L_INIT, "updating: %s Tx\n",
+				priv->tx_disabled ? "disable" : "enable");
+		if (priv->tx_disabled)
+			acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_TX, NULL, 0);
+			/*                                                 ^ */
+			/* FIXME: this used to be 1, but since we don't transfer a parameter... */
+		else
+			acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_TX, &(priv->channel), 1);
+		CLEAR_BIT(priv->set_mask, GETSET_TX);
+	}
+
+	if (priv->set_mask & (GETSET_RX|GETSET_ALL)) {
+		/* Enable Rx */
+		acxlog(L_INIT, "updating: enable Rx on channel: %u\n",
+				priv->channel);
+		acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_RX, &(priv->channel), 1);
+		CLEAR_BIT(priv->set_mask, GETSET_RX);
+	}
+
+	if (priv->set_mask & (GETSET_RETRY|GETSET_ALL)) {
+		u8 short_retry[4 + ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN];
+		u8 long_retry[4 + ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN];
+
+		acxlog(L_INIT, "updating short retry limit: %u, long retry limit: %u\n",
+					priv->short_retry, priv->long_retry);
+		short_retry[0x4] = priv->short_retry;
+		long_retry[0x4] = priv->long_retry;
+		acx_s_configure(priv, &short_retry, ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT);
+		acx_s_configure(priv, &long_retry, ACX1xx_IE_DOT11_LONG_RETRY_LIMIT);
+		CLEAR_BIT(priv->set_mask, GETSET_RETRY);
+	}
+
+	if (priv->set_mask & (SET_MSDU_LIFETIME|GETSET_ALL)) {
+		u8 xmt_msdu_lifetime[4 + ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN];
+
+		acxlog(L_INIT, "updating tx MSDU lifetime: %u\n",
+					priv->msdu_lifetime);
+		*(u32 *)&xmt_msdu_lifetime[4] = cpu_to_le32((u32)priv->msdu_lifetime);
+		acx_s_configure(priv, &xmt_msdu_lifetime, ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME);
+		CLEAR_BIT(priv->set_mask, SET_MSDU_LIFETIME);
+	}
+
+	if (priv->set_mask & (GETSET_REG_DOMAIN|GETSET_ALL)) {
+		/* reg_domain */
+		acx_ie_generic_t dom;
+		unsigned mask;
+
+		acxlog(L_INIT, "updating regulatory domain: 0x%02X\n",
+					priv->reg_dom_id);
+		for (i = 0; i < sizeof(reg_domain_ids); i++)
+			if (reg_domain_ids[i] == priv->reg_dom_id)
+				break;
+
+		if (sizeof(reg_domain_ids) == i) {
+			acxlog(L_INIT, "Invalid or unsupported regulatory "
+				"domain 0x%02X specified, falling back to "
+				"FCC (USA)! Please report if this sounds "
+				"fishy!\n", priv->reg_dom_id);
+			i = 0;
+			priv->reg_dom_id = reg_domain_ids[i];
+		}
+
+		priv->reg_dom_chanmask = reg_domain_channel_masks[i];
+		dom.m.bytes[0] = priv->reg_dom_id;
+		acx_s_configure(priv, &dom, ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN);
+
+		mask = (1 << (priv->channel - 1));
+		if (!(priv->reg_dom_chanmask & mask)) {
+		/* hmm, need to adjust our channel to reside within domain */
+			mask = 1;
+			for (i = 1; i <= 14; i++) {
+				if (priv->reg_dom_chanmask & mask) {
+					printk("%s: adjusting "
+						"selected channel from %d "
+						"to %d due to new regulatory "
+						"domain\n", priv->netdev->name,
+						priv->channel, i);
+					priv->channel = i;
+					break;
+				}
+				mask <<= 1;
+			}
+		}
+		CLEAR_BIT(priv->set_mask, GETSET_REG_DOMAIN);
+	}
+
+	if (priv->set_mask & (GETSET_MODE|GETSET_ALL)) {
+		priv->netdev->type = ARPHRD_ETHER;
+
+		switch (priv->mode) {
+		case ACX_MODE_3_AP:
+
+			acx_lock(priv, flags);
+			acx_l_sta_list_init(priv);
+			priv->aid = 0;
+			priv->ap_client = NULL;
+			MAC_COPY(priv->bssid, priv->dev_addr);
+			/* this basically says "we're connected" */
+			acx_set_status(priv, ACX_STATUS_4_ASSOCIATED);
+			acx_unlock(priv, flags);
+
+			acx111_s_feature_off(priv, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER);
+			/* start sending beacons */
+			acx_s_cmd_join_bssid(priv, priv->bssid);
+			break;
+		case ACX_MODE_MONITOR:
+			/* TODO: what exactly do we want here? */
+			/* priv->netdev->type = ARPHRD_ETHER; */
+			/* priv->netdev->type = ARPHRD_IEEE80211; */
+			priv->netdev->type = ARPHRD_IEEE80211_PRISM;
+			acx111_s_feature_on(priv, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER);
+			/* this stops beacons */
+			acx_s_cmd_join_bssid(priv, priv->bssid);
+			/* this basically says "we're connected" */
+			acx_set_status(priv, ACX_STATUS_4_ASSOCIATED);
+			SET_BIT(priv->set_mask, SET_RXCONFIG|SET_WEP_OPTIONS);
+			break;
+		case ACX_MODE_0_ADHOC:
+		case ACX_MODE_2_STA:
+			acx111_s_feature_off(priv, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER);
+			priv->aid = 0;
+			priv->ap_client = NULL;
+			/* we want to start looking for peer or AP */
+			start_scan = 1;
+			break;
+		case ACX_MODE_OFF:
+			/* TODO: disable RX/TX, stop any scanning activity etc: */
+			/* priv->tx_disabled = 1; */
+			/* SET_BIT(priv->set_mask, GETSET_RX|GETSET_TX); */
+
+			/* This stops beacons (invalid macmode...) */
+			acx_s_cmd_join_bssid(priv, priv->bssid);
+			acx_set_status(priv, ACX_STATUS_0_STOPPED);
+			break;
+		}
+		CLEAR_BIT(priv->set_mask, GETSET_MODE);
+	}
+
+	if (priv->set_mask & (SET_RXCONFIG|GETSET_ALL)) {
+		acx_s_initialize_rx_config(priv);
+		CLEAR_BIT(priv->set_mask, SET_RXCONFIG);
+	}
+
+	if (priv->set_mask & (GETSET_RESCAN|GETSET_ALL)) {
+		switch (priv->mode) {
+		case ACX_MODE_0_ADHOC:
+		case ACX_MODE_2_STA:
+			start_scan = 1;
+			break;
+		}
+		CLEAR_BIT(priv->set_mask, GETSET_RESCAN);
+	}
+
+	if (priv->set_mask & (GETSET_WEP|GETSET_ALL)) {
+		/* encode */
+
+		ie_dot11WEPDefaultKeyID_t dkey;
+#if DEBUG_WEP
+		struct {
+			u16 type ACX_PACKED;
+			u16 len ACX_PACKED;
+			u8  val ACX_PACKED;
+		} keyindic;
+#endif
+		acxlog(L_INIT, "updating WEP key settings\n");
+
+		acx_s_set_wepkey(priv);
+
+		dkey.KeyID = priv->wep_current_index;
+		acxlog(L_INIT, "setting WEP key %u as default\n", dkey.KeyID);
+		acx_s_configure(priv, &dkey, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET);
+#if DEBUG_WEP
+		keyindic.val = 3;
+		acx_s_configure(priv, &keyindic, ACX111_IE_KEY_CHOOSE);
+#endif
+		start_scan = 1;
+		CLEAR_BIT(priv->set_mask, GETSET_WEP);
+	}
+
+	if (priv->set_mask & (SET_WEP_OPTIONS|GETSET_ALL)) {
+		acx100_ie_wep_options_t options;
+
+		if (priv->chip_type == CHIPTYPE_ACX111) {
+			acxlog(L_DEBUG, "setting WEP Options for ACX111 is not supported\n");
+		} else {
+			acxlog(L_INIT, "setting WEP Options\n");
+
+			/* let's choose maximum setting: 4 default keys,
+			 * plus 10 other keys: */
+			options.NumKeys = cpu_to_le16(DOT11_MAX_DEFAULT_WEP_KEYS + 10);
+			/* don't decrypt default key only,
+			 * don't override decryption: */
+			options.WEPOption = 0;
+			if (priv->mode == ACX_MODE_MONITOR) {
+				/* don't decrypt default key only,
+				 * override decryption mechanism: */
+				options.WEPOption = 2;
+			}
+
+			acx_s_configure(priv, &options, ACX100_IE_WEP_OPTIONS);
+		}
+		CLEAR_BIT(priv->set_mask, SET_WEP_OPTIONS);
+	}
+
+	/* Rescan was requested */
+	if (start_scan) {
+		switch (priv->mode) {
+		case ACX_MODE_0_ADHOC:
+		case ACX_MODE_2_STA:
+			/* We can avoid clearing list if join code
+			** will be a bit more clever about not picking
+			** 'bad' AP over and over again */
+			acx_lock(priv, flags);
+			priv->ap_client = NULL;
+			acx_l_sta_list_init(priv);
+			acx_set_status(priv, ACX_STATUS_1_SCANNING);
+			acx_unlock(priv, flags);
+
+			acx_s_cmd_start_scan(priv);
+		}
+	}
+
+	/* debug, rate, and nick don't need any handling */
+	/* what about sniffing mode?? */
+
+	acxlog(L_INIT, "get_mask 0x%08X, set_mask 0x%08X - after update\n",
+			priv->get_mask, priv->set_mask);
+
+/* end: */
+	FN_EXIT0;
+}
+
+
+/***********************************************************************
+*/
+void
+acx_s_initialize_rx_config(wlandevice_t *priv)
+{
+	struct {
+		u16	id ACX_PACKED;
+		u16	len ACX_PACKED;
+		u16	rx_cfg1 ACX_PACKED;
+		u16	rx_cfg2 ACX_PACKED;
+	} cfg;
+
+	switch (priv->mode) {
+	case ACX_MODE_OFF:
+		priv->rx_config_1 = (u16) (0
+			/* | RX_CFG1_INCLUDE_RXBUF_HDR	*/
+			/* | RX_CFG1_FILTER_SSID	*/
+			/* | RX_CFG1_FILTER_BCAST	*/
+			/* | RX_CFG1_RCV_MC_ADDR1	*/
+			/* | RX_CFG1_RCV_MC_ADDR0	*/
+			/* | RX_CFG1_FILTER_ALL_MULTI	*/
+			/* | RX_CFG1_FILTER_BSSID	*/
+			/* | RX_CFG1_FILTER_MAC		*/
+			/* | RX_CFG1_RCV_PROMISCUOUS	*/
+			/* | RX_CFG1_INCLUDE_FCS	*/
+			/* | RX_CFG1_INCLUDE_PHY_HDR	*/
+			);
+		priv->rx_config_2 = (u16) (0
+			/*| RX_CFG2_RCV_ASSOC_REQ	*/
+			/*| RX_CFG2_RCV_AUTH_FRAMES	*/
+			/*| RX_CFG2_RCV_BEACON_FRAMES	*/
+			/*| RX_CFG2_RCV_CONTENTION_FREE	*/
+			/*| RX_CFG2_RCV_CTRL_FRAMES	*/
+			/*| RX_CFG2_RCV_DATA_FRAMES	*/
+			/*| RX_CFG2_RCV_BROKEN_FRAMES	*/
+			/*| RX_CFG2_RCV_MGMT_FRAMES	*/
+			/*| RX_CFG2_RCV_PROBE_REQ	*/
+			/*| RX_CFG2_RCV_PROBE_RESP	*/
+			/*| RX_CFG2_RCV_ACK_FRAMES	*/
+			/*| RX_CFG2_RCV_OTHER		*/
+			);
+		break;
+	case ACX_MODE_MONITOR:
+		priv->rx_config_1 = (u16) (0
+			/* | RX_CFG1_INCLUDE_RXBUF_HDR	*/
+			/* | RX_CFG1_FILTER_SSID	*/
+			/* | RX_CFG1_FILTER_BCAST	*/
+			/* | RX_CFG1_RCV_MC_ADDR1	*/
+			/* | RX_CFG1_RCV_MC_ADDR0	*/
+			/* | RX_CFG1_FILTER_ALL_MULTI	*/
+			/* | RX_CFG1_FILTER_BSSID	*/
+			/* | RX_CFG1_FILTER_MAC		*/
+			| RX_CFG1_RCV_PROMISCUOUS
+			/* | RX_CFG1_INCLUDE_FCS	*/
+			/* | RX_CFG1_INCLUDE_PHY_HDR	*/
+			);
+		priv->rx_config_2 = (u16) (0
+			| RX_CFG2_RCV_ASSOC_REQ
+			| RX_CFG2_RCV_AUTH_FRAMES
+			| RX_CFG2_RCV_BEACON_FRAMES
+			| RX_CFG2_RCV_CONTENTION_FREE
+			| RX_CFG2_RCV_CTRL_FRAMES
+			| RX_CFG2_RCV_DATA_FRAMES
+			| RX_CFG2_RCV_BROKEN_FRAMES
+			| RX_CFG2_RCV_MGMT_FRAMES
+			| RX_CFG2_RCV_PROBE_REQ
+			| RX_CFG2_RCV_PROBE_RESP
+			| RX_CFG2_RCV_ACK_FRAMES
+			| RX_CFG2_RCV_OTHER
+			);
+		break;
+	default:
+		priv->rx_config_1 = (u16) (0
+			/* | RX_CFG1_INCLUDE_RXBUF_HDR	*/
+			/* | RX_CFG1_FILTER_SSID	*/
+			/* | RX_CFG1_FILTER_BCAST	*/
+			/* | RX_CFG1_RCV_MC_ADDR1	*/
+			/* | RX_CFG1_RCV_MC_ADDR0	*/
+			/* | RX_CFG1_FILTER_ALL_MULTI	*/
+			/* | RX_CFG1_FILTER_BSSID	*/
+			| RX_CFG1_FILTER_MAC
+			/* | RX_CFG1_RCV_PROMISCUOUS	*/
+			/* | RX_CFG1_INCLUDE_FCS	*/
+			/* | RX_CFG1_INCLUDE_PHY_HDR	*/
+			);
+		priv->rx_config_2 = (u16) (0
+			| RX_CFG2_RCV_ASSOC_REQ
+			| RX_CFG2_RCV_AUTH_FRAMES
+			| RX_CFG2_RCV_BEACON_FRAMES
+			| RX_CFG2_RCV_CONTENTION_FREE
+			| RX_CFG2_RCV_CTRL_FRAMES
+			| RX_CFG2_RCV_DATA_FRAMES
+			/*| RX_CFG2_RCV_BROKEN_FRAMES	*/
+			| RX_CFG2_RCV_MGMT_FRAMES
+			| RX_CFG2_RCV_PROBE_REQ
+			| RX_CFG2_RCV_PROBE_RESP
+			/*| RX_CFG2_RCV_ACK_FRAMES	*/
+			| RX_CFG2_RCV_OTHER
+			);
+		break;
+	}
+#if DEBUG_WEP
+	if (CHIPTYPE_ACX100 == priv->chip_type)
+		/* only ACX100 supports that */
+#endif
+		priv->rx_config_1 |= RX_CFG1_INCLUDE_RXBUF_HDR;
+
+	acxlog(L_INIT, "setting RXconfig to %04X:%04X\n",
+			priv->rx_config_1, priv->rx_config_2);
+	cfg.rx_cfg1 = cpu_to_le16(priv->rx_config_1);
+	cfg.rx_cfg2 = cpu_to_le16(priv->rx_config_2);
+	acx_s_configure(priv, &cfg, ACX1xx_IE_RXCONFIG);
+}
+
+
+/***********************************************************************
+** acx_schedule_after_interrupt_task
+**
+** Schedule the call of the after_interrupt method after leaving
+** the interrupt context.
+*/
+void
+acx_schedule_after_interrupt_task(wlandevice_t *priv, unsigned int set_flag)
+{
+	SET_BIT(priv->after_interrupt_jobs, set_flag);
+	SCHEDULE_WORK(&priv->after_interrupt_task);
+}
+
+
+/***********************************************************************
+** Helper
+*/
+static void
+acx_s_after_interrupt_recalib(wlandevice_t *priv)
+{
+	int res;
+
+	/* this helps with ACX100 at least;
+	 * hopefully ACX111 also does a
+	 * recalibration here */
+
+	/* clear flag beforehand, since we want to make sure
+	 * it's cleared; then only set it again on specific circumstances */
+	CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
+
+	/* better wait a bit between recalibrations to
+	 * prevent overheating due to torturing the card
+	 * into working too long despite high temperature
+	 * (just a safety measure) */
+	if (priv->recalib_time_last_success
+	 && time_before(jiffies, priv->recalib_time_last_success
+					+ RECALIB_PAUSE * 60 * HZ)) {
+		priv->recalib_msg_ratelimit++;
+		if (priv->recalib_msg_ratelimit <= 5)
+			printk("%s: less than " STRING(RECALIB_PAUSE)
+				" minutes since last radio recalibration, "
+				"not recalibrating (maybe card is too hot?)\n",
+				priv->netdev->name);
+		if (priv->recalib_msg_ratelimit == 5)
+			printk("disabling above message\n");
+		return;
+	}
+
+	priv->recalib_msg_ratelimit = 0;
+
+	/* note that commands sometimes fail (card busy),
+	 * so only clear flag if we were fully successful */
+	res = acx_s_recalib_radio(priv);
+	if (res == OK) {
+		printk("%s: successfully recalibrated radio\n",
+						priv->netdev->name);
+		priv->recalib_time_last_success = jiffies;
+		priv->recalib_failure_count = 0;
+	} else {
+		/* failed: resubmit, but only limited
+		 * amount of times within some time range
+		 * to prevent endless loop */
+
+		priv->recalib_time_last_success = 0; /* we failed */
+
+		/* if some time passed between last
+		 * attempts, then reset failure retry counter
+		 * to be able to do next recalib attempt */
+		if (time_after(jiffies, priv->recalib_time_last_attempt + HZ))
+			priv->recalib_failure_count = 0;
+
+		if (++priv->recalib_failure_count <= 5) {
+			priv->recalib_time_last_attempt = jiffies;
+			acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
+		}
+	}
+}
+
+/***********************************************************************
+** acx_e_after_interrupt_task
+*/
+static void
+acx_e_after_interrupt_task(void *data)
+{
+	netdevice_t *dev = (netdevice_t *) data;
+	wlandevice_t *priv;
+
+	FN_ENTER;
+
+	priv = (struct wlandevice *) dev->priv;
+
+	acx_sem_lock(priv);
+
+	if (!priv->after_interrupt_jobs)
+		goto end; /* no jobs to do */
+
+#if TX_CLEANUP_IN_SOFTIRQ
+	if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_TX_CLEANUP) {
+		acx_lock(priv, flags);
+		acx_l_clean_tx_desc(priv);
+		CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_TX_CLEANUP);
+		acx_unlock(priv, flags);
+	}
+#endif
+	/* we see lotsa tx errors */
+	if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_RADIO_RECALIB) {
+		acx_s_after_interrupt_recalib(priv);
+	}
+
+	/* a poor interrupt code wanted to do update_card_settings() */
+	if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_UPDATE_CARD_CFG) {
+		if (ACX_STATE_IFACE_UP & priv->dev_state_mask)
+			acx_s_update_card_settings(priv, 0, 0);
+		CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_UPDATE_CARD_CFG);
+	}
+
+	/* 1) we detected that no Scan_Complete IRQ came from fw, or
+	** 2) we found too many STAs */
+	if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_STOP_SCAN) {
+		acxlog(L_IRQ, "sending a stop scan cmd...\n");
+		acx_s_issue_cmd(priv, ACX1xx_CMD_STOP_SCAN, NULL, 0);
+		/* HACK: set the IRQ bit, since we won't get a
+		 * scan complete IRQ any more on ACX111 (works on ACX100!),
+		 * since _we_, not a fw, have stopped the scan */
+		SET_BIT(priv->irq_status, HOST_INT_SCAN_COMPLETE);
+		CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_STOP_SCAN);
+	}
+
+	/* either fw sent Scan_Complete or we detected that
+	** no Scan_Complete IRQ came from fw. Finish scanning,
+	** pick join partner if any */
+	if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_COMPLETE_SCAN) {
+		if (priv->status == ACX_STATUS_1_SCANNING) {
+			if (OK != acx_s_complete_scan(priv)) {
+				SET_BIT(priv->after_interrupt_jobs,
+					ACX_AFTER_IRQ_RESTART_SCAN);
+			}
+		} else {
+			/* + scan kills current join status - restore it
+			**   (do we need it for STA?) */
+			/* + does it happen only with active scans?
+			**   active and passive scans? ALL scans including
+			**   background one? */
+			/* + was not verified that everything is restored
+			**   (but at least we start to emit beacons again) */
+			switch (priv->mode) {
+			case ACX_MODE_0_ADHOC:
+			case ACX_MODE_3_AP:
+				acxlog(L_IRQ, "redoing cmd_join_bssid() after scan\n");
+				acx_s_cmd_join_bssid(priv, priv->bssid);
+			}
+		}
+		CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_COMPLETE_SCAN);
+	}
+
+	/* STA auth or assoc timed out, start over again */
+	if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_RESTART_SCAN) {
+		acxlog(L_IRQ, "sending a start_scan cmd...\n");
+		acx_s_cmd_start_scan(priv);
+		CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_RESTART_SCAN);
+	}
+
+	/* whee, we got positive assoc response! 8) */
+	if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_ASSOCIATE) {
+		acx_ie_generic_t pdr;
+		/* tiny race window exists, checking that we still a STA */
+		switch (priv->mode) {
+		case ACX_MODE_2_STA:
+			pdr.m.aid = cpu_to_le16(priv->aid);
+			acx_s_configure(priv, &pdr, ACX1xx_IE_ASSOC_ID);
+			acx_set_status(priv, ACX_STATUS_4_ASSOCIATED);
+			acxlog(L_ASSOC | L_DEBUG, "ASSOCIATED!\n");
+			CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_ASSOCIATE);
+		}
+	}
+end:
+	acx_sem_unlock(priv);
+	FN_EXIT0;
+}
+
+
+/***********************************************************************
+*/
+void
+acx_init_task_scheduler(wlandevice_t *priv)
+{
+	/* configure task scheduler */
+	INIT_WORK(&priv->after_interrupt_task, acx_e_after_interrupt_task, priv->netdev);
+}
+
+
+/***********************************************************************
+** acx_cmd_join_bssid
+**
+** Common code for both acx100 and acx111.
+*/
+/* NB: does NOT match RATE100_nn but matches ACX[111]_SCAN_RATE_n */
+static const u8
+bitpos2genframe_txrate[] = {
+	10,	/*  0.  1 Mbit/s */
+	20,	/*  1.  2 Mbit/s */
+	55,	/*  2.  5.5 Mbit/s */
+	0x0B,	/*  3.  6 Mbit/s */
+	0x0F,	/*  4.  9 Mbit/s */
+	110,	/*  5. 11 Mbit/s */
+	0x0A,	/*  6. 12 Mbit/s */
+	0x0E,	/*  7. 18 Mbit/s */
+	220,	/*  8. 22 Mbit/s */
+	0x09,	/*  9. 24 Mbit/s */
+	0x0D,	/* 10. 36 Mbit/s */
+	0x08,	/* 11. 48 Mbit/s */
+	0x0C,	/* 12. 54 Mbit/s */
+	10,	/* 13.  1 Mbit/s, should never happen */
+	10,	/* 14.  1 Mbit/s, should never happen */
+	10,	/* 15.  1 Mbit/s, should never happen */
+};
+
+/* Looks scary, eh?
+** Actually, each one compiled into one AND and one SHIFT,
+** 31 bytes in x86 asm (more if uints are replaced by u16/u8) */
+static unsigned int
+rate111to5bits(unsigned int rate)
+{
+	return (rate & 0x7)
+	| ( (rate & RATE111_11) / (RATE111_11/JOINBSS_RATES_11) )
+	| ( (rate & RATE111_22) / (RATE111_22/JOINBSS_RATES_22) )
+	;
+}
+
+extern void BUG_joinbss_must_be_0x30_bytes_in_length(void);
+
+void
+acx_s_cmd_join_bssid(wlandevice_t *priv, const u8 *bssid)
+{
+	acx_joinbss_t tmp;
+	int dtim_interval;
+	int i;
+
+	/* compile-time check for proper size of fixed-size struct */
+	if (sizeof(acx_joinbss_t) != 0x30)
+		BUG_joinbss_must_be_0x30_bytes_in_length();
+
+	FN_ENTER;
+
+	dtim_interval =	(ACX_MODE_0_ADHOC == priv->mode) ?
+			1 : priv->dtim_interval;
+
+	memset(&tmp, 0, sizeof(tmp));
+
+	for (i = 0; i < ETH_ALEN; i++) {
+		tmp.bssid[i] = bssid[ETH_ALEN-1 - i];
+	}
+
+	tmp.beacon_interval = cpu_to_le16(priv->beacon_interval);
+
+	/* basic rate set. Control frame responses (such as ACK or CTS frames)
+	** are sent with one of these rates */
+	if (CHIPTYPE_ACX111 == priv->chip_type) {
+		/* It was experimentally determined that rates_basic
+		** can take 11g rates as well, not only rates
+		** defined with JOINBSS_RATES_BASIC111_nnn.
+		** Just use RATE111_nnn constants... */
+		tmp.u.acx111.dtim_interval = dtim_interval;
+		tmp.u.acx111.rates_basic = cpu_to_le16(priv->rate_basic);
+		acxlog(L_ASSOC, "%s rates_basic %04X, rates_supported %04X\n",
+			__func__, priv->rate_basic, priv->rate_oper);
+	} else {
+		tmp.u.acx100.dtim_interval = dtim_interval;
+		tmp.u.acx100.rates_basic = rate111to5bits(priv->rate_basic);
+		tmp.u.acx100.rates_supported = rate111to5bits(priv->rate_oper);
+		acxlog(L_ASSOC, "%s rates_basic %04X->%02X, "
+			"rates_supported %04X->%02X\n",
+			__func__,
+			priv->rate_basic, tmp.u.acx100.rates_basic,
+			priv->rate_oper, tmp.u.acx100.rates_supported);
+	}
+
+	/* Setting up how Beacon, Probe Response, RTS, and PS-Poll frames
+	** will be sent (rate/modulation/preamble) */
+	tmp.genfrm_txrate = bitpos2genframe_txrate[lowest_bit(priv->rate_basic)];
+	tmp.genfrm_mod_pre = 0; /* FIXME: was = priv->capab_short (which is always 0); */
+	/* we can use short pre *if* all peers can understand it */
+	/* FIXME #2: we need to correctly set PBCC/OFDM bits here too */
+
+	/* we switch fw to STA mode in MONITOR mode, it seems to be
+	** the only mode where fw does not emit beacons by itself
+	** but allows us to send anything (we really want to retain
+	** ability to tx arbitrary frames in MONITOR mode)
+	*/
+	tmp.macmode = (priv->mode != ACX_MODE_MONITOR ? priv->mode : ACX_MODE_2_STA);
+	tmp.channel = priv->channel;
+	tmp.essid_len = priv->essid_len;
+	/* NOTE: the code memcpy'd essid_len + 1 before, which is WRONG! */
+	memcpy(tmp.essid, priv->essid, tmp.essid_len);
+	acx_s_issue_cmd(priv, ACX1xx_CMD_JOIN, &tmp, tmp.essid_len + 0x11);
+
+	acxlog(L_ASSOC | L_DEBUG, "BSS_Type = %u\n", tmp.macmode);
+	acxlog_mac(L_ASSOC | L_DEBUG, "JoinBSSID MAC:", priv->bssid, "\n");
+
+	acx_update_capabilities(priv);
+	FN_EXIT0;
+}
+
+
+/***********************************************************************
+** acx_s_set_defaults
+** Called from acx_s_init_mac
+*/
+int
+acx_s_set_defaults(wlandevice_t *priv)
+{
+	unsigned long flags;
+
+	FN_ENTER;
+
+	/* query some settings from the card.
+	 * NOTE: for some settings, e.g. CCA and ED (ACX100!), an initial
+	 * query is REQUIRED, otherwise the card won't work correctly!! */
+	priv->get_mask = GETSET_ANTENNA|GETSET_SENSITIVITY|GETSET_STATION_ID|GETSET_REG_DOMAIN;
+	/* Only ACX100 supports ED and CCA */
+	if (CHIPTYPE_ACX100 == priv->chip_type)
+		priv->get_mask |= GETSET_CCA|GETSET_ED_THRESH;
+
+	acx_s_update_card_settings(priv, 0, 0);
+
+	acx_lock(priv, flags);
+
+#ifdef ACX_PCI
+	/* set our global interrupt mask */
+	if (priv->chip_type == CHIPTYPE_ACX111) {
+		priv->irq_mask = (u16) ~(0
+				/* | HOST_INT_RX_DATA        */
+				| HOST_INT_TX_COMPLETE
+				/* | HOST_INT_TX_XFER        */
+				| HOST_INT_RX_COMPLETE
+				/* | HOST_INT_DTIM           */
+				/* | HOST_INT_BEACON         */
+				/* | HOST_INT_TIMER          */
+				/* | HOST_INT_KEY_NOT_FOUND  */
+				| HOST_INT_IV_ICV_FAILURE
+				| HOST_INT_CMD_COMPLETE
+				| HOST_INT_INFO
+				/* | HOST_INT_OVERFLOW       */
+				/* | HOST_INT_PROCESS_ERROR  */
+				| HOST_INT_SCAN_COMPLETE
+				| HOST_INT_FCS_THRESHOLD
+				/* | HOST_INT_UNKNOWN        */
+				);
+		priv->irq_mask_off = (u16)~( HOST_INT_CMD_COMPLETE ); /* 0xfdff */
+	} else {
+		priv->irq_mask = (u16) ~(0
+				/* | HOST_INT_RX_DATA        */
+				| HOST_INT_TX_COMPLETE
+				/* | HOST_INT_TX_XFER        */
+				| HOST_INT_RX_COMPLETE
+				/* | HOST_INT_DTIM           */
+				/* | HOST_INT_BEACON         */
+				/* | HOST_INT_TIMER          */
+				/* | HOST_INT_KEY_NOT_FOUND  */
+				/* | HOST_INT_IV_ICV_FAILURE */
+				| HOST_INT_CMD_COMPLETE
+				| HOST_INT_INFO
+				/* | HOST_INT_OVERFLOW       */
+				/* | HOST_INT_PROCESS_ERROR  */
+				| HOST_INT_SCAN_COMPLETE
+				/* | HOST_INT_FCS_THRESHOLD  */
+				/* | HOST_INT_UNKNOWN        */
+				);
+		priv->irq_mask_off = (u16)~( HOST_INT_UNKNOWN ); /* 0x7fff */
+	}
+#endif
+
+	priv->led_power = 1; /* LED is active on startup */
+	priv->brange_max_quality = 60; /* LED blink max quality is 60 */
+	priv->brange_time_last_state_change = jiffies;
+
+	/* copy the MAC address we just got from the card
+	 * into our MAC address used during current 802.11 session */
+	MAC_COPY(priv->dev_addr, priv->netdev->dev_addr);
+	sprintf(priv->essid, "STA%02X%02X%02X",
+		priv->dev_addr[3], priv->dev_addr[4], priv->dev_addr[5]);
+	priv->essid_len = sizeof("STAxxxxxx") - 1; /* make sure to adapt if changed above! */
+	priv->essid_active = 1;
+
+	/* we have a nick field to waste, so why not abuse it
+	 * to announce the driver version? ;-) */
+	strncpy(priv->nick, "acx " WLAN_RELEASE, IW_ESSID_MAX_SIZE);
+
+#ifdef ACX_PCI
+	if (priv->chip_type == CHIPTYPE_ACX111) {
+		/* Hope this is correct, only tested with domain 0x30 */
+		acx_read_eeprom_offset(priv, 0x16F, &priv->reg_dom_id);
+	} else if (priv->eeprom_version < 5) {
+		acx_read_eeprom_offset(priv, 0x16F, &priv->reg_dom_id);
+	} else {
+		acx_read_eeprom_offset(priv, 0x171, &priv->reg_dom_id);
+	}
+#endif
+
+	priv->channel = 1;
+	/* 0xffff would be better, but then we won't get a "scan complete"
+	 * interrupt, so our current infrastructure will fail: */
+	priv->scan_count = 1;
+	priv->scan_mode = ACX_SCAN_OPT_PASSIVE;
+	/* Doesn't work for acx100, do it only for acx111 for now */
+	if (priv->chip_type == CHIPTYPE_ACX111) {
+		priv->scan_mode = ACX_SCAN_OPT_ACTIVE;
+	}
+	priv->scan_duration = 100;
+	priv->scan_probe_delay = 200;
+	priv->scan_rate = ACX_SCAN_RATE_1;
+
+	priv->auth_alg = WLAN_AUTH_ALG_OPENSYSTEM;
+	priv->preamble_mode = 2; /* auto */
+	priv->listen_interval = 100;
+	priv->beacon_interval = DEFAULT_BEACON_INTERVAL;
+	priv->mode = ACX_MODE_OFF;
+	priv->dtim_interval = DEFAULT_DTIM_INTERVAL;
+
+	priv->msdu_lifetime = DEFAULT_MSDU_LIFETIME;
+	SET_BIT(priv->set_mask, SET_MSDU_LIFETIME);
+
+	priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
+
+	/* use standard default values for retry limits */
+	priv->short_retry = 7; /* max. retries for (short) non-RTS packets */
+	priv->long_retry = 4; /* max. retries for long (RTS) packets */
+	SET_BIT(priv->set_mask, GETSET_RETRY);
+
+	priv->fallback_threshold = 3;
+	priv->stepup_threshold = 10;
+	priv->rate_bcast = RATE111_1;
+	priv->rate_bcast100 = RATE100_1;
+	priv->rate_basic = RATE111_1 | RATE111_2;
+	priv->rate_auto = 1;
+	if (priv->chip_type == CHIPTYPE_ACX111) {
+		priv->rate_oper = RATE111_ALL;
+	} else {
+		priv->rate_oper = RATE111_ACX100_COMPAT;
+	}
+
+	/* configure card to do rate fallback when in auto rate mode. */
+	SET_BIT(priv->set_mask, SET_RATE_FALLBACK);
+
+	/* Supported Rates element - the rates here are given in units of
+	 * 500 kbit/s, plus 0x80 added. See 802.11-1999.pdf item 7.3.2.2 */
+	acx_l_update_ratevector(priv);
+
+	priv->capab_short = 0;
+	priv->capab_pbcc = 1;
+	priv->capab_agility = 0;
+
+	SET_BIT(priv->set_mask, SET_RXCONFIG);
+
+	/* set some more defaults */
+	if (priv->chip_type == CHIPTYPE_ACX111) {
+		/* 30mW (15dBm) is default, at least in my acx111 card: */
+		priv->tx_level_dbm = 15;
+	} else {
+		/* don't use max. level, since it might be dangerous
+		 * (e.g. WRT54G people experience
+		 * excessive Tx power damage!) */
+		priv->tx_level_dbm = 18;
+	}
+	priv->tx_level_auto = 1;
+	SET_BIT(priv->set_mask, GETSET_TXPOWER);
+
+	if (priv->chip_type == CHIPTYPE_ACX111) {
+		/* start with sensitivity level 1 out of 3: */
+		priv->sensitivity = 1;
+	}
+
+	/* better re-init the antenna value we got above */
+	SET_BIT(priv->set_mask, GETSET_ANTENNA);
+
+	priv->ps_wakeup_cfg = 0;
+	priv->ps_listen_interval = 0;
+	priv->ps_options = 0;
+	priv->ps_hangover_period = 0;
+	priv->ps_enhanced_transition_time = 0;
+#if POWER_SAVE_80211
+	SET_BIT(priv->set_mask, GETSET_POWER_80211);
+#endif
+
+	MAC_BCAST(priv->ap);
+
+	acx_unlock(priv, flags);
+	acx_lock_unhold(); // hold time 844814 CPU ticks @2GHz
+
+	acx_s_initialize_rx_config(priv);
+
+	FN_EXIT1(OK);
+	return OK;
+}
+
+
+/***********************************************************************
+** acx_s_init_mac
+*/
+int
+acx_s_init_mac(netdevice_t *dev)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int result = NOT_OK;
+
+	FN_ENTER;
+
+#ifdef ACX_PCI
+	priv->memblocksize = 256; /* 256 is default */
+
+	acx_init_mboxes(priv);
+
+	/* try to load radio for both ACX100 and ACX111, since both
+	 * chips have at least some firmware versions making use of an
+	 * external radio module */
+	acx_s_upload_radio(priv);
+#else
+	priv->memblocksize = 128;
+#endif
+
+	if (priv->chip_type == CHIPTYPE_ACX111) {
+		/* for ACX111, the order is different from ACX100
+		   1. init packet templates
+		   2. create station context and create dma regions
+		   3. init wep default keys
+		*/
+		if (OK != acx111_s_init_packet_templates(priv))
+			goto fail;
+
+		if (OK != acx111_s_create_dma_regions(priv)) {
+			printk("%s: acx111_create_dma_regions FAILED\n",
+							dev->name);
+			goto fail;
+		}
+#if DEBUG_WEP
+		/* don't decrypt WEP in firmware */
+		if (OK != acx111_s_feature_on(priv, 0, FEATURE2_SNIFFER))
+			goto fail;
+#endif
+	} else {
+		if (OK != acx100_s_init_wep(priv))
+			goto fail;
+		acxlog(L_DEBUG, "between init_wep and init_packet_templates\n");
+		if (OK != acx100_s_init_packet_templates(priv))
+			goto fail;
+
+		if (OK != acx100_s_create_dma_regions(priv)) {
+			printk("%s: acx100_create_dma_regions FAILED\n",
+							dev->name);
+			goto fail;
+		}
+	}
+
+	MAC_COPY(dev->dev_addr, priv->dev_addr);
+	result = OK;
+
+fail:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/***********************************************************************
+** acx_s_start
+*/
+void
+acx_s_start(wlandevice_t *priv)
+{
+	FN_ENTER;
+
+	/*
+	 * Ok, now we do everything that can possibly be done with ioctl
+	 * calls to make sure that when it was called before the card
+	 * was up we get the changes asked for
+	 */
+
+	SET_BIT(priv->set_mask, SET_TEMPLATES|SET_STA_LIST|GETSET_WEP
+		|GETSET_TXPOWER|GETSET_ANTENNA|GETSET_ED_THRESH|GETSET_CCA
+		|GETSET_REG_DOMAIN|GETSET_MODE|GETSET_CHANNEL
+		|GETSET_TX|GETSET_RX);
+
+	acxlog(L_INIT, "updating initial settings on iface activation...\n");
+	acx_s_update_card_settings(priv, 0, 0);
+
+	FN_EXIT0;
+}
+
+
+/***********************************************************************
+** acx_update_capabilities
+*/
+void
+acx_update_capabilities(wlandevice_t *priv)
+{
+	u16 cap = 0;
+
+	switch (priv->mode) {
+	case ACX_MODE_3_AP:
+		SET_BIT(cap, WF_MGMT_CAP_ESS); break;
+	case ACX_MODE_0_ADHOC:
+		SET_BIT(cap, WF_MGMT_CAP_IBSS); break;
+	/* other types of stations do not emit beacons */
+	}
+
+	if (priv->wep_restricted) {
+		SET_BIT(cap, WF_MGMT_CAP_PRIVACY);
+	}
+	if (priv->capab_short) {
+		SET_BIT(cap, WF_MGMT_CAP_SHORT);
+	}
+	if (priv->capab_pbcc) {
+		SET_BIT(cap, WF_MGMT_CAP_PBCC);
+	}
+	if (priv->capab_agility) {
+		SET_BIT(cap, WF_MGMT_CAP_AGILITY);
+	}
+	acxlog(L_DEBUG, "caps updated from 0x%04X to 0x%04X\n",
+				priv->capabilities, cap);
+	priv->capabilities = cap;
+}
+
+#ifdef UNUSED
+/***********************************************************************
+** FIXME: check whether this function is indeed acx111 only,
+** rename ALL relevant definitions to indicate actual card scope!
+*/
+void
+acx111_s_read_configoption(wlandevice_t *priv)
+{
+	acx111_ie_configoption_t co, co2;
+	int i;
+	const u8 *pEle;
+
+	if (OK != acx_s_interrogate(priv, &co, ACX111_IE_CONFIG_OPTIONS) ) {
+		printk("%s: reading ConfigOption FAILED\n", priv->netdev->name);
+		return;
+	};
+	if (!(acx_debug & L_DEBUG))
+		return;
+
+	memcpy(&co2.configoption_fixed, &co.configoption_fixed,
+			sizeof(co.configoption_fixed));
+
+	pEle = (u8 *)&co.configoption_fixed + sizeof(co.configoption_fixed) - 4;
+
+	co2.antennas.type = pEle[0];
+	co2.antennas.len = pEle[1];
+	printk("AntennaID:%02X Len:%02X Data:",
+			co2.antennas.type, co2.antennas.len);
+	for (i = 0; i < pEle[1]; i++) {
+		co2.antennas.list[i] = pEle[i+2];
+		printk("%02X ", pEle[i+2]);
+	}
+	printk("\n");
+
+	pEle += pEle[1] + 2;
+	co2.power_levels.type = pEle[0];
+	co2.power_levels.len = pEle[1];
+	printk("PowerLevelID:%02X Len:%02X Data:",
+			co2.power_levels.type, co2.power_levels.len);
+	for (i = 0; i < pEle[1]*2; i++) {
+		co2.power_levels.list[i] = pEle[i+2];
+		printk("%02X ", pEle[i+2]);
+	}
+	printk("\n");
+
+	pEle += pEle[1]*2 + 2;
+	co2.data_rates.type = pEle[0];
+	co2.data_rates.len = pEle[1];
+	printk("DataRatesID:%02X Len:%02X Data:",
+			co2.data_rates.type, co2.data_rates.len);
+	for (i = 0; i < pEle[1]; i++) {
+		co2.data_rates.list[i] = pEle[i+2];
+		printk("%02X ", pEle[i+2]);
+	}
+	printk("\n");
+
+	pEle += pEle[1] + 2;
+	co2.domains.type = pEle[0];
+	co2.domains.len = pEle[1];
+	printk("DomainID:%02X Len:%02X Data:",
+			co2.domains.type, co2.domains.len);
+	for (i = 0; i < pEle[1]; i++) {
+		co2.domains.list[i] = pEle[i+2];
+		printk("%02X ", pEle[i+2]);
+	}
+	printk("\n");
+
+	pEle += pEle[1] + 2;
+	co2.product_id.type = pEle[0];
+	co2.product_id.len = pEle[1];
+	for (i = 0; i < pEle[1]; i++) {
+		co2.product_id.list[i] = pEle[i+2];
+	}
+	printk("ProductID:%02X Len:%02X Data:%.*s\n",
+			co2.product_id.type, co2.product_id.len,
+			co2.product_id.len, (char *)co2.product_id.list);
+
+	pEle += pEle[1] + 2;
+	co2.manufacturer.type = pEle[0];
+	co2.manufacturer.len = pEle[1];
+	for (i = 0; i < pEle[1]; i++) {
+		co2.manufacturer.list[i] = pEle[i+2];
+	}
+	printk("ManufacturerID:%02X Len:%02X Data:%.*s\n",
+			co2.manufacturer.type, co2.manufacturer.len,
+			co2.manufacturer.len, (char *)co2.manufacturer.list);
+/*
+	printk("EEPROM part:\n");
+	for (i=0; i<58; i++) {
+		printk("%02X =======>  0x%02X\n",
+			    i, (u8 *)co.configoption_fixed.NVSv[i-2]);
+	}
+*/
+}
+#endif
+
+
+/***********************************************************************
+** Not inlined: it's larger than it seems
+*/
+void
+acx_print_mac(const char *head, const u8 *mac, const char *tail)
+{
+	printk("%s"MACSTR"%s", head, MAC(mac), tail);
+}
diff -urN oldtree/drivers/net/wireless/tiacx/helper2.c newtree/drivers/net/wireless/tiacx/helper2.c
--- oldtree/drivers/net/wireless/tiacx/helper2.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/helper2.c	2006-02-13 19:20:00.280469664 -0500
@@ -0,0 +1,2422 @@
+/***********************************************************************
+** Copyright (C) 2003  ACX100 Open Source Project
+**
+** The contents of this file are subject to the Mozilla Public
+** License Version 1.1 (the "License"); you may not use this file
+** except in compliance with the License. You may obtain a copy of
+** the License at http://www.mozilla.org/MPL/
+**
+** Software distributed under the License is distributed on an "AS
+** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+** implied. See the License for the specific language governing
+** rights and limitations under the License.
+**
+** Alternatively, the contents of this file may be used under the
+** terms of the GNU Public License version 2 (the "GPL"), in which
+** case the provisions of the GPL are applicable instead of the
+** above.  If you wish to allow the use of your version of this file
+** only under the terms of the GPL and not to allow others to use
+** your version of this file under the MPL, indicate your decision
+** by deleting the provisions above and replace them with the notice
+** and other provisions required by the GPL.  If you do not delete
+** the provisions above, a recipient may use your version of this
+** file under either the MPL or the GPL.
+** ---------------------------------------------------------------------
+** Inquiries regarding the ACX100 Open Source Project can be
+** made directly to:
+**
+** acx100-users@lists.sf.net
+** http://acx100.sf.net
+** ---------------------------------------------------------------------
+*/
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/if_arp.h>
+#include <linux/rtnetlink.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#include <linux/pm.h>
+#if WIRELESS_EXT >= 13
+#include <net/iw_handler.h>
+#endif /* WE >= 13 */
+
+#include "acx.h"
+
+
+/***********************************************************************
+*/
+static client_t *acx_l_sta_list_alloc(wlandevice_t *priv);
+static client_t *acx_l_sta_list_get_from_hash(wlandevice_t *priv, const u8 *address);
+
+static int acx_l_process_data_frame_master(wlandevice_t *priv, rxbuffer_t *rxbuf);
+static int acx_l_process_data_frame_client(wlandevice_t *priv, rxbuffer_t *rxbuf);
+/* static int acx_l_process_NULL_frame(wlandevice_t *priv, rxbuffer_t *rxbuf, int vala); */
+static int acx_l_process_mgmt_frame(wlandevice_t *priv, rxbuffer_t *rxbuf);
+static void acx_l_process_disassoc_from_sta(wlandevice_t *priv, const wlan_fr_disassoc_t *req);
+static void acx_l_process_disassoc_from_ap(wlandevice_t *priv, const wlan_fr_disassoc_t *req);
+static void acx_l_process_deauth_from_sta(wlandevice_t *priv, const wlan_fr_deauthen_t *req);
+static void acx_l_process_deauth_from_ap(wlandevice_t *priv, const wlan_fr_deauthen_t *req);
+static int acx_l_process_probe_response(wlandevice_t *priv, wlan_fr_proberesp_t *req, const rxbuffer_t *rxbuf);
+static int acx_l_process_assocresp(wlandevice_t *priv, const wlan_fr_assocresp_t *req);
+static int acx_l_process_reassocresp(wlandevice_t *priv, const wlan_fr_reassocresp_t *req);
+static int acx_l_process_authen(wlandevice_t *priv, const wlan_fr_authen_t *req);
+static int acx_l_transmit_assocresp(wlandevice_t *priv, const wlan_fr_assocreq_t *req);
+static int acx_l_transmit_reassocresp(wlandevice_t *priv, const wlan_fr_reassocreq_t *req);
+static int acx_l_transmit_deauthen(wlandevice_t *priv, const u8 *addr, u16 reason);
+static int acx_l_transmit_authen1(wlandevice_t *priv);
+static int acx_l_transmit_authen2(wlandevice_t *priv, const wlan_fr_authen_t *req, client_t *clt);
+static int acx_l_transmit_authen3(wlandevice_t *priv, const wlan_fr_authen_t *req);
+static int acx_l_transmit_authen4(wlandevice_t *priv, const wlan_fr_authen_t *req);
+static int acx_l_transmit_assoc_req(wlandevice_t *priv);
+
+static void acx_s_activate_power_save_mode(wlandevice_t *priv, /*@unused@*/ int vala);
+
+
+/***********************************************************************
+*/
+const char*
+acx_get_status_name(u16 status)
+{
+	static const char * const str[] = {
+		"STOPPED", "SCANNING", "WAIT_AUTH",
+		"AUTHENTICATED", "ASSOCIATED", "INVALID??"
+	};
+	return str[(status < VEC_SIZE(str)) ? status : VEC_SIZE(str)-1];
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_sta_list_init
+*----------------------------------------------------------------*/
+void
+acx_l_sta_list_init(wlandevice_t *priv)
+{
+	FN_ENTER;
+	memset(priv->sta_hash_tab, 0, sizeof(priv->sta_hash_tab));
+	memset(priv->sta_list, 0, sizeof(priv->sta_list));
+	FN_EXIT0;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_sta_list_get_from_hash
+*----------------------------------------------------------------*/
+static inline client_t*
+acx_l_sta_list_get_from_hash(wlandevice_t *priv, const u8 *address)
+{
+	return priv->sta_hash_tab[address[5] % VEC_SIZE(priv->sta_hash_tab)];
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_sta_list_get
+*----------------------------------------------------------------*/
+client_t*
+acx_l_sta_list_get(wlandevice_t *priv, const u8 *address)
+{
+	client_t *client;
+	FN_ENTER;
+	client = acx_l_sta_list_get_from_hash(priv, address);
+	while (client) {
+		if (mac_is_equal(address, client->address)) {
+			client->mtime = jiffies;
+			break;
+		}
+		client = client->next;
+	}
+	FN_EXIT0;
+	return client;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_sta_list_del
+*----------------------------------------------------------------*/
+void
+acx_l_sta_list_del(wlandevice_t *priv, client_t *victim)
+{
+	client_t *client, *next;
+
+	client = acx_l_sta_list_get_from_hash(priv, victim->address);
+	next = client;
+	/* tricky. next = client on first iteration only,
+	** on all other iters next = client->next */
+	while (next) {
+		if (next == victim) {
+			client->next = victim->next;
+			/* Overkill */
+			memset(victim, 0, sizeof(*victim));
+			break;
+		}
+		client = next;
+		next = client->next;
+	}
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_sta_list_alloc
+*
+* Never fails - will evict oldest client if needed
+*----------------------------------------------------------------*/
+static client_t*
+acx_l_sta_list_alloc(wlandevice_t *priv)
+{
+	int i;
+	unsigned long age, oldest_age;
+	client_t *client, *oldest;
+
+	FN_ENTER;
+
+	oldest = &priv->sta_list[0];
+	oldest_age = 0;
+	for (i = 0; i < VEC_SIZE(priv->sta_list); i++) {
+		client = &priv->sta_list[i];
+
+		if (!client->used) {
+			goto found;
+		} else {
+			age = jiffies - client->mtime;
+			if (oldest_age < age) {
+				oldest_age = age;
+				oldest = client;
+			}
+		}
+	}
+	acx_l_sta_list_del(priv, oldest);
+	client = oldest;
+found:
+	memset(client, 0, sizeof(*client));
+	FN_EXIT0;
+	return client;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_sta_list_add
+*
+* Never fails - will evict oldest client if needed
+*----------------------------------------------------------------*/
+/* In case we will reimplement it differently... */
+#define STA_LIST_ADD_CAN_FAIL 0
+
+static client_t*
+acx_l_sta_list_add(wlandevice_t *priv, const u8 *address)
+{
+	client_t *client;
+	int index;
+
+	FN_ENTER;
+
+	client = acx_l_sta_list_alloc(priv);
+
+	client->mtime = jiffies;
+	MAC_COPY(client->address, address);
+	client->used = CLIENT_EXIST_1;
+	client->auth_alg = WLAN_AUTH_ALG_SHAREDKEY;
+	client->auth_step = 1;
+	/* give some tentative peer rate values
+	** (needed because peer may do auth without probing us first,
+	** thus we'll have no idea of peer's ratevector yet).
+	** Will be overwritten by scanning or assoc code */
+	client->rate_cap = priv->rate_basic;
+	client->rate_cfg = priv->rate_basic;
+	client->rate_cur = 1 << lowest_bit(priv->rate_basic);
+
+	index = address[5] % VEC_SIZE(priv->sta_hash_tab);
+	client->next = priv->sta_hash_tab[index];
+	priv->sta_hash_tab[index] = client;
+
+	acxlog_mac(L_ASSOC, "sta_list_add: sta=", address, "\n");
+
+	FN_EXIT0;
+	return client;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_sta_list_get_or_add
+*
+* Never fails - will evict oldest client if needed
+*----------------------------------------------------------------*/
+static client_t*
+acx_l_sta_list_get_or_add(wlandevice_t *priv, const u8 *address)
+{
+	client_t *client = acx_l_sta_list_get(priv, address);
+	if (!client)
+		client = acx_l_sta_list_add(priv, address);
+	return client;
+}
+
+
+/*----------------------------------------------------------------
+* acx_set_status
+*
+* This function is called in many atomic regions, must not sleep
+*----------------------------------------------------------------*/
+void
+acx_set_status(wlandevice_t *priv, u16 new_status)
+{
+#define QUEUE_OPEN_AFTER_ASSOC 1 /* this really seems to be needed now */
+	u16 old_status = priv->status;
+
+	FN_ENTER;
+
+	acxlog(L_ASSOC, "%s(%d):%s\n",
+	       __func__, new_status, acx_get_status_name(new_status));
+
+#if WIRELESS_EXT > 13 /* wireless_send_event() and SIOCGIWSCAN */
+	/* wireless_send_event never sleeps */
+	if (ACX_STATUS_4_ASSOCIATED == new_status) {
+		union iwreq_data wrqu;
+
+		wrqu.data.length = 0;
+		wrqu.data.flags = 0;
+		wireless_send_event(priv->netdev, SIOCGIWSCAN, &wrqu, NULL);
+
+		wrqu.data.length = 0;
+		wrqu.data.flags = 0;
+		MAC_COPY(wrqu.ap_addr.sa_data, priv->bssid);
+		wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+		wireless_send_event(priv->netdev, SIOCGIWAP, &wrqu, NULL);
+	} else {
+		union iwreq_data wrqu;
+
+		/* send event with empty BSSID to indicate we're not associated */
+		MAC_ZERO(wrqu.ap_addr.sa_data);
+		wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+		wireless_send_event(priv->netdev, SIOCGIWAP, &wrqu, NULL);
+	}
+#endif
+
+	priv->status = new_status;
+
+	switch (new_status) {
+	case ACX_STATUS_1_SCANNING:
+		priv->scan_retries = 0;
+		/* 2.5s initial scan time
+		 * (used to be 1.5s, but failed to find WEP APs!) */
+		acx_set_timer(priv, 1000000);
+		break;
+	case ACX_STATUS_2_WAIT_AUTH:
+	case ACX_STATUS_3_AUTHENTICATED:
+		priv->auth_or_assoc_retries = 0;
+		acx_set_timer(priv, 1500000); /* 1.5 s */
+		break;
+	}
+
+#if QUEUE_OPEN_AFTER_ASSOC
+	if (new_status == ACX_STATUS_4_ASSOCIATED)	{
+		if (old_status < ACX_STATUS_4_ASSOCIATED) {
+			/* ah, we're newly associated now,
+			 * so let's indicate carrier */
+			acx_carrier_on(priv->netdev, "after association");
+			acx_wake_queue(priv->netdev, "after association");
+		}
+	} else {
+		/* not associated any more, so let's kill carrier */
+		if (old_status >= ACX_STATUS_4_ASSOCIATED) {
+			acx_carrier_off(priv->netdev, "after losing association");
+			acx_stop_queue(priv->netdev, "after losing association");
+		}
+	}
+#endif
+	FN_EXIT0;
+}
+
+
+/*------------------------------------------------------------------------------
+ * acx_i_timer
+ *
+ * Fires up periodically. Used to kick scan/auth/assoc if something goes wrong
+ *----------------------------------------------------------------------------*/
+void
+acx_i_timer(unsigned long address)
+{
+	unsigned long flags;
+	wlandevice_t *priv = (wlandevice_t *)address;
+
+	FN_ENTER;
+
+	acx_lock(priv, flags);
+
+	acxlog(L_DEBUG|L_ASSOC, "%s: priv->status=%d (%s)\n",
+		__func__, priv->status, acx_get_status_name(priv->status));
+
+	switch (priv->status) {
+	case ACX_STATUS_1_SCANNING:
+		/* was set to 0 by set_status() */
+		if (++priv->scan_retries < 7) {
+			acx_set_timer(priv, 1000000);
+			/* used to interrogate for scan status.
+			** We rely on SCAN_COMPLETE IRQ instead */
+			acxlog(L_ASSOC, "continuing scan (%d sec)\n",
+					priv->scan_retries);
+		} else {
+			acxlog(L_ASSOC, "stopping scan\n");
+			/* send stop_scan cmd when we leave the interrupt context,
+			 * and make a decision what to do next (COMPLETE_SCAN) */
+			acx_schedule_after_interrupt_task(priv,
+				ACX_AFTER_IRQ_CMD_STOP_SCAN + ACX_AFTER_IRQ_COMPLETE_SCAN);
+		}
+		break;
+	case ACX_STATUS_2_WAIT_AUTH:
+		/* was set to 0 by set_status() */
+		if (++priv->auth_or_assoc_retries < 10) {
+			acxlog(L_ASSOC, "resend authen1 request (attempt %d)\n",
+					priv->auth_or_assoc_retries + 1);
+			acx_l_transmit_authen1(priv);
+		} else {
+			/* time exceeded: fall back to scanning mode */
+			acxlog(L_ASSOC,
+			       "authen1 request reply timeout, giving up\n");
+			/* we are a STA, need to find AP anyhow */
+			acx_set_status(priv, ACX_STATUS_1_SCANNING);
+			acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_RESTART_SCAN);
+		}
+		/* used to be 1500000, but some other driver uses 2.5s */
+		acx_set_timer(priv, 2500000);
+		break;
+	case ACX_STATUS_3_AUTHENTICATED:
+		/* was set to 0 by set_status() */
+		if (++priv->auth_or_assoc_retries < 10) {
+			acxlog(L_ASSOC,	"resend assoc request (attempt %d)\n",
+					priv->auth_or_assoc_retries + 1);
+			acx_l_transmit_assoc_req(priv);
+		} else {
+			/* time exceeded: give up */
+			acxlog(L_ASSOC,
+				"association request reply timeout, giving up\n");
+			/* we are a STA, need to find AP anyhow */
+			acx_set_status(priv, ACX_STATUS_1_SCANNING);
+			acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_RESTART_SCAN);
+		}
+		acx_set_timer(priv, 2500000); /* see above */
+		break;
+	case ACX_STATUS_4_ASSOCIATED:
+	default:
+		break;
+	}
+
+	acx_unlock(priv, flags);
+
+	FN_EXIT0;
+}
+
+
+/*------------------------------------------------------------------------------
+ * acx_set_timer
+ *
+ * Sets the 802.11 state management timer's timeout.
+ *----------------------------------------------------------------------------*/
+void
+acx_set_timer(wlandevice_t *priv, u32 timeout)
+{
+	FN_ENTER;
+
+	acxlog(L_DEBUG|L_IRQ, "%s(%u ms)\n", __func__, timeout/1000);
+	if (!(priv->dev_state_mask & ACX_STATE_IFACE_UP)) {
+		printk("attempt to set the timer "
+			"when the card interface is not up!\n");
+		goto end;
+	}
+
+	/* first check if the timer was already initialized, THEN modify it */
+	if (priv->mgmt_timer.function) {
+		mod_timer(&priv->mgmt_timer,
+				jiffies + (timeout * HZ / 1000000));
+	}
+end:
+	FN_EXIT0;
+}
+
+
+/*------------------------------------------------------------------------------
+ * acx_l_rx_ieee802_11_frame
+ *
+ * Called from IRQ context only
+ * STATUS: FINISHED, UNVERIFIED.
+ *----------------------------------------------------------------------------*/
+int
+acx_l_rx_ieee802_11_frame(wlandevice_t *priv, rxbuffer_t *rxbuf)
+{
+	unsigned int ftype, fstype;
+	const wlan_hdr_t *hdr;
+	int result = NOT_OK;
+
+	FN_ENTER;
+
+	hdr = acx_get_wlan_hdr(priv, rxbuf);
+
+	/* see IEEE 802.11-1999.pdf chapter 7 "MAC frame formats" */
+	if ((hdr->fc & WF_FC_PVERi) != 0) {
+		printk_ratelimited(KERN_INFO "rx: unsupported 802.11 protocol\n");
+		goto end;
+	}
+
+	ftype = hdr->fc & WF_FC_FTYPEi;
+	fstype = hdr->fc & WF_FC_FSTYPEi;
+
+	switch (ftype) {
+	/* check data frames first, for speed */
+	case WF_FTYPE_DATAi:
+		switch (fstype) {
+		case WF_FSTYPE_DATAONLYi:
+		/* FIXME: will fail if two peers send 2 streams of DUPs */
+			if (priv->dup_count
+			 && time_after(jiffies, priv->dup_msg_expiry)) {
+				printk(KERN_INFO "%s: rx: %d DUPs received in 10 secs\n",
+					priv->netdev->name, priv->dup_count);
+				priv->dup_count = 0;
+			}
+			if (unlikely(hdr->seq == priv->last_seq_ctrl)) {
+				if (!priv->dup_count++)
+					priv->dup_msg_expiry = jiffies + 10*HZ;
+				/* simply discard it and indicate error */
+				priv->stats.rx_errors++;
+				break;
+			}
+			priv->last_seq_ctrl = hdr->seq; /* le_to_cpu? */
+
+			/* TODO:
+			if (WF_FC_FROMTODSi == (hdr->fc & WF_FC_FROMTODSi)) {
+				result = acx_l_process_data_frame_wds(priv, rxbuf);
+				break;
+			}
+			*/
+
+			switch (priv->mode) {
+			case ACX_MODE_3_AP:
+				result = acx_l_process_data_frame_master(priv, rxbuf);
+				break;
+			case ACX_MODE_0_ADHOC:
+			case ACX_MODE_2_STA:
+				result = acx_l_process_data_frame_client(priv, rxbuf);
+				break;
+			}
+		case WF_FSTYPE_DATA_CFACKi:
+		case WF_FSTYPE_DATA_CFPOLLi:
+		case WF_FSTYPE_DATA_CFACK_CFPOLLi:
+		case WF_FSTYPE_CFPOLLi:
+		case WF_FSTYPE_CFACK_CFPOLLi:
+		/*   see above.
+			acx_process_class_frame(priv, rxbuf, 3); */
+			break;
+		case WF_FSTYPE_NULLi:
+			/* acx_l_process_NULL_frame(priv, rxbuf, 3); */
+			break;
+		/* FIXME: same here, see above */
+		case WF_FSTYPE_CFACKi:
+		default:
+			break;
+		}
+		break;
+	case WF_FTYPE_MGMTi:
+		result = acx_l_process_mgmt_frame(priv, rxbuf);
+		break;
+	case WF_FTYPE_CTLi:
+		if (fstype == WF_FSTYPE_PSPOLLi)
+			result = OK;
+		/*   this call is irrelevant, since
+		 *   acx_process_class_frame is a stub, so return
+		 *   immediately instead.
+		 * return acx_process_class_frame(priv, rxbuf, 3); */
+		break;
+	default:
+		break;
+	}
+end:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_transmit_assocresp
+*
+* We are an AP here
+*----------------------------------------------------------------*/
+static const u8
+dot11ratebyte[] = {
+	DOT11RATEBYTE_1,
+	DOT11RATEBYTE_2,
+	DOT11RATEBYTE_5_5,
+	DOT11RATEBYTE_6_G,
+	DOT11RATEBYTE_9_G,
+	DOT11RATEBYTE_11,
+	DOT11RATEBYTE_12_G,
+	DOT11RATEBYTE_18_G,
+	DOT11RATEBYTE_22,
+	DOT11RATEBYTE_24_G,
+	DOT11RATEBYTE_36_G,
+	DOT11RATEBYTE_48_G,
+	DOT11RATEBYTE_54_G,
+};
+
+static int
+find_pos(const u8 *p, int size, u8 v)
+{
+	int i;
+	for (i = 0; i < size; i++)
+		if (p[i] == v)
+			return i;
+	/* printk a message about strange byte? */
+	return 0;
+}
+
+static void
+add_bits_to_ratemasks(u8* ratevec, int len, u16* brate, u16* orate)
+{
+	while (len--) {
+		int n = 1 << find_pos(dot11ratebyte,
+				sizeof(dot11ratebyte), *ratevec & 0x7f);
+		if (*ratevec & 0x80)
+			*brate |= n;
+		*orate |= n;
+		ratevec++;
+	}
+}
+
+static int
+acx_l_transmit_assocresp(wlandevice_t *priv, const wlan_fr_assocreq_t *req)
+{
+	struct tx *tx;
+	struct wlan_hdr_mgmt *head;
+	struct assocresp_frame_body *body;
+	u8 *p;
+	const u8 *da;
+	/* const u8 *sa; */
+	const u8 *bssid;
+	client_t *clt;
+
+	FN_ENTER;
+
+	/* sa = req->hdr->a1; */
+	da = req->hdr->a2;
+	bssid = req->hdr->a3;
+
+	clt = acx_l_sta_list_get(priv, da);
+	if (!clt)
+		goto ok;
+
+	/* Assoc without auth is a big no-no */
+	/* Let's be liberal: if already assoc'ed STA sends assoc req again,
+	** we won't be rude */
+	if (clt->used != CLIENT_AUTHENTICATED_2
+	 && clt->used != CLIENT_ASSOCIATED_3) {
+		acx_l_transmit_deauthen(priv, da, WLAN_MGMT_REASON_CLASS2_NONAUTH);
+		goto bad;
+	}
+
+	clt->used = CLIENT_ASSOCIATED_3;
+
+	if (clt->aid == 0) {
+		clt->aid = ++priv->aid;
+	}
+	clt->cap_info = ieee2host16(*(req->cap_info));
+	/* We cheat here a bit. We don't really care which rates are flagged
+	** as basic by the client, so we stuff them in single ratemask */
+	clt->rate_cap = 0;
+	if (req->supp_rates)
+		add_bits_to_ratemasks(req->supp_rates->rates,
+			req->supp_rates->len, &clt->rate_cap, &clt->rate_cap);
+	if (req->ext_rates)
+		add_bits_to_ratemasks(req->ext_rates->rates,
+			req->ext_rates->len, &clt->rate_cap, &clt->rate_cap);
+	/* We can check that client supports all basic rates,
+	** and deny assoc if not. But let's be liberal, right? ;) */
+	clt->rate_cfg = clt->rate_cap & priv->rate_oper;
+	if (!clt->rate_cfg) clt->rate_cfg = 1 << lowest_bit(priv->rate_oper);
+	clt->rate_cur = 1 << lowest_bit(clt->rate_cfg);
+	clt->fallback_count = clt->stepup_count = 0;
+	clt->ignore_count = 16;
+
+	tx = acx_l_alloc_tx(priv);
+	if (!tx)
+		goto bad;
+	head = acx_l_get_txbuf(tx);
+	if (!head)
+		goto bad;
+	body = (void*)(head + 1);
+
+	head->fc = WF_FSTYPE_ASSOCRESPi;
+	head->dur = req->hdr->dur;
+	MAC_COPY(head->da, da);
+	/* MAC_COPY(head->sa, sa); */
+	MAC_COPY(head->sa, priv->dev_addr);
+	MAC_COPY(head->bssid, bssid);
+	head->seq = req->hdr->seq;
+
+	body->cap_info = host2ieee16(priv->capabilities);
+	body->status = host2ieee16(0);
+	body->aid = host2ieee16(clt->aid);
+	p = wlan_fill_ie_rates((u8*)&body->rates, priv->rate_supported_len,
+							priv->rate_supported);
+	p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len,
+							priv->rate_supported);
+
+	acx_l_dma_tx_data(priv, tx, p - (u8*)head);
+ok:
+	FN_EXIT1(OK);
+	return OK;
+bad:
+	FN_EXIT1(NOT_OK);
+	return NOT_OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_transmit_reassocresp
+
+You may be wondering, just like me, what a hell is ReAuth.
+In practice it was seen sent by STA when STA feels like losing connection.
+
+[802.11]
+
+5.4.2.3 Reassociation
+
+Association is sufficient for no-transition message delivery between
+IEEE 802.11 stations. Additional functionality is needed to support
+BSS-transition mobility. The additional required functionality
+is provided by the reassociation service. Reassociation is a DSS.
+The reassociation service is invoked to ÒmoveÓ a current association
+from one AP to another. This keeps the DS informed of the current
+mapping between AP and STA as the station moves from BSS to BSS within
+an ESS. Reassociation also enables changing association attributes
+of an established association while the STA remains associated with
+the same AP. Reassociation is always initiated by the mobile STA.
+
+5.4.3.1 Authentication
+...
+A STA may be authenticated with many other STAs at any given instant.
+
+5.4.3.1.1 Preauthentication
+
+Because the authentication process could be time-consuming (depending
+on the authentication protocol in use), the authentication service can
+be invoked independently of the association service. Preauthentication
+is typically done by a STA while it is already associated with an AP
+(with which it previously authenticated). IEEE 802.11 does not require
+that STAs preauthenticate with APs. However, authentication is required
+before an association can be established. If the authentication is left
+until reassociation time, this may impact the speed with which a STA can
+reassociate between APs, limiting BSS-transition mobility performance.
+The use of preauthentication takes the authentication service overhead
+out of the time-critical reassociation process.
+
+5.7.3 Reassociation
+
+For a STA to reassociate, the reassociation service causes the following
+message to occur:
+
+  Reassociation request
+
+* Message type: Management
+* Message subtype: Reassociation request
+* Information items:
+  - IEEE address of the STA
+  - IEEE address of the AP with which the STA will reassociate
+  - IEEE address of the AP with which the STA is currently associated
+  - ESSID
+* Direction of message: From STA to 'new' AP
+
+The address of the current AP is included for efficiency. The inclusion
+of the current AP address facilitates MAC reassociation to be independent
+of the DS implementation.
+
+  Reassociation response
+* Message type: Management
+* Message subtype: Reassociation response
+* Information items:
+  - Result of the requested reassociation. (success/failure)
+  - If the reassociation is successful, the response shall include the AID.
+* Direction of message: From AP to STA
+
+7.2.3.6 Reassociation Request frame format
+
+The frame body of a management frame of subtype Reassociation Request
+contains the information shown in Table 9.
+
+Table 9 Reassociation Request frame body
+Order Information
+1 Capability information
+2 Listen interval
+3 Current AP address
+4 SSID
+5 Supported rates
+
+7.2.3.7 Reassociation Response frame format
+
+The frame body of a management frame of subtype Reassociation Response
+contains the information shown in Table 10.
+
+Table 10 Reassociation Response frame body
+Order Information
+1 Capability information
+2 Status code
+3 Association ID (AID)
+4 Supported rates
+
+*----------------------------------------------------------------*/
+static int
+acx_l_transmit_reassocresp(wlandevice_t *priv, const wlan_fr_reassocreq_t *req)
+{
+	struct tx *tx;
+	struct wlan_hdr_mgmt *head;
+	struct reassocresp_frame_body *body;
+	u8 *p;
+	const u8 *da;
+	/* const u8 *sa; */
+	const u8 *bssid;
+	client_t *clt;
+
+	FN_ENTER;
+
+	/* sa = req->hdr->a1; */
+	da = req->hdr->a2;
+	bssid = req->hdr->a3;
+
+	/* Must be already authenticated, so it must be in the list */
+	clt = acx_l_sta_list_get(priv, da);
+	if (!clt)
+		goto ok;
+
+	/* Assoc without auth is a big no-no */
+	/* Already assoc'ed STAs sending ReAssoc req are ok per 802.11 */
+	if (clt->used != CLIENT_AUTHENTICATED_2
+	 && clt->used != CLIENT_ASSOCIATED_3) {
+		acx_l_transmit_deauthen(priv, da, WLAN_MGMT_REASON_CLASS2_NONAUTH);
+		goto bad;
+	}
+
+	clt->used = CLIENT_ASSOCIATED_3;
+	if (clt->aid == 0) {
+		clt->aid = ++priv->aid;
+	}
+	if (req->cap_info)
+		clt->cap_info = ieee2host16(*(req->cap_info));
+	/* We cheat here a bit. We don't really care which rates are flagged
+	** as basic by the client, so we stuff them in single ratemask */
+	clt->rate_cap = 0;
+	if (req->supp_rates)
+		add_bits_to_ratemasks(req->supp_rates->rates,
+			req->supp_rates->len, &clt->rate_cap, &clt->rate_cap);
+	if (req->ext_rates)
+		add_bits_to_ratemasks(req->ext_rates->rates,
+			req->ext_rates->len, &clt->rate_cap, &clt->rate_cap);
+	/* We can check that client supports all basic rates,
+	** and deny assoc if not. But let's be liberal, right? ;) */
+	clt->rate_cfg = clt->rate_cap & priv->rate_oper;
+	if (!clt->rate_cfg) clt->rate_cfg = 1 << lowest_bit(priv->rate_oper);
+	clt->rate_cur = 1 << lowest_bit(clt->rate_cfg);
+	clt->fallback_count = clt->stepup_count = 0;
+	clt->ignore_count = 16;
+
+	tx = acx_l_alloc_tx(priv);
+	if (!tx)
+		goto ok;
+	head = acx_l_get_txbuf(tx);
+	if (!head)
+		goto ok;
+	body = (void*)(head + 1);
+
+	head->fc = WF_FSTYPE_REASSOCRESPi;
+	head->dur = req->hdr->dur;
+	MAC_COPY(head->da, da);
+	/* MAC_COPY(head->sa, sa); */
+	MAC_COPY(head->sa, priv->dev_addr);
+	MAC_COPY(head->bssid, bssid);
+	head->seq = req->hdr->seq;
+
+	/* IEs: 1. caps */
+	body->cap_info = host2ieee16(priv->capabilities);
+	/* 2. status code */
+	body->status = host2ieee16(0);
+	/* 3. AID */
+	body->aid = host2ieee16(clt->aid);
+	/* 4. supp rates */
+	p = wlan_fill_ie_rates((u8*)&body->rates, priv->rate_supported_len,
+							priv->rate_supported);
+	/* 5. ext supp rates */
+	p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len,
+							priv->rate_supported);
+
+	acx_l_dma_tx_data(priv, tx, p - (u8*)head);
+ok:
+	FN_EXIT1(OK);
+	return OK;
+bad:
+	FN_EXIT1(NOT_OK);
+	return NOT_OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_process_disassoc_from_sta
+*----------------------------------------------------------------*/
+static void
+acx_l_process_disassoc_from_sta(wlandevice_t *priv, const wlan_fr_disassoc_t *req)
+{
+	const u8 *ta;
+	client_t *clt;
+
+	FN_ENTER;
+
+	ta = req->hdr->a2;
+	clt = acx_l_sta_list_get(priv, ta);
+	if (!clt)
+		goto end;
+
+	if (clt->used != CLIENT_ASSOCIATED_3
+	 && clt->used != CLIENT_AUTHENTICATED_2) {
+		/* it's disassociating, but it's
+		** not even authenticated! Let it know that */
+		acxlog_mac(L_ASSOC|L_XFER, "peer ", ta, "has sent disassoc "
+			"req but it is not even auth'ed! sending deauth\n");
+		acx_l_transmit_deauthen(priv, ta,
+			WLAN_MGMT_REASON_CLASS2_NONAUTH);
+		clt->used = CLIENT_EXIST_1;
+	} else {
+		/* mark it as auth'ed only */
+		clt->used = CLIENT_AUTHENTICATED_2;
+	}
+end:
+	FN_EXIT0;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_process_deauthen_from_sta
+*----------------------------------------------------------------*/
+static void
+acx_l_process_deauth_from_sta(wlandevice_t *priv, const wlan_fr_deauthen_t *req)
+{
+	const wlan_hdr_t *hdr;
+	client_t *client;
+
+	FN_ENTER;
+
+	hdr = req->hdr;
+
+	if (acx_debug & L_ASSOC) {
+		acx_print_mac("DEAUTHEN priv->addr=", priv->dev_addr, " ");
+		acx_print_mac("a1", hdr->a1, " ");
+		acx_print_mac("a2", hdr->a2, " ");
+		acx_print_mac("a3", hdr->a3, " ");
+		acx_print_mac("priv->bssid", priv->bssid, "\n");
+	}
+
+	if (!mac_is_equal(priv->dev_addr, hdr->a1)) {
+		goto end;
+	}
+
+	acxlog_mac(L_DEBUG, "STA ", hdr->a2, " sent us deauthen packet\n");
+
+	client = acx_l_sta_list_get(priv, hdr->a2);
+	if (!client) {
+		goto end;
+	}
+	client->used = CLIENT_EXIST_1;
+end:
+	FN_EXIT0;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_process_disassoc_from_ap
+*----------------------------------------------------------------*/
+static void
+acx_l_process_disassoc_from_ap(wlandevice_t *priv, const wlan_fr_disassoc_t *req)
+{
+	FN_ENTER;
+
+	if (!priv->ap_client) {
+		/* Hrm, we aren't assoc'ed yet anyhow... */
+		goto end;
+	}
+	if (mac_is_equal(priv->dev_addr, req->hdr->a1)) {
+		/* TODO: send a deauth... */
+		acx_l_transmit_deauthen(priv, priv->bssid,
+				WLAN_MGMT_REASON_DEAUTH_LEAVING);
+		/* Start scan anew */
+		SET_BIT(priv->set_mask, GETSET_RESCAN);
+		acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_UPDATE_CARD_CFG);
+	}
+end:
+	FN_EXIT0;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_process_deauth_from_ap
+*----------------------------------------------------------------*/
+static void
+acx_l_process_deauth_from_ap(wlandevice_t *priv, const wlan_fr_deauthen_t *req)
+{
+	FN_ENTER;
+
+	if (!priv->ap_client) {
+		/* Hrm, we aren't assoc'ed yet anyhow... */
+		goto end;
+	}
+	/* Chk: is ta is verified to be from our AP? */
+	if (mac_is_equal(priv->dev_addr, req->hdr->a1)) {
+		acxlog(L_DEBUG, "AP sent us deauth packet\n");
+		/* not needed: acx_set_status(priv, ACX_STATUS_1_SCANNING) */
+		SET_BIT(priv->set_mask, GETSET_RESCAN);
+		acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_UPDATE_CARD_CFG);
+	}
+end:
+	FN_EXIT0;
+}
+
+
+/*------------------------------------------------------------------------------
+ * acx_l_rx
+ *
+ * The end of the Rx path. Pulls data from a rxhostdesc into a socket
+ * buffer and feeds it to the network stack via netif_rx().
+ *
+ * Arguments:
+ *	rxdesc:	the rxhostdesc to pull the data from
+ *	priv:	the acx100 private struct of the interface
+ *----------------------------------------------------------------------------*/
+void
+acx_l_rx(wlandevice_t *priv, rxbuffer_t *rxbuf)
+{
+	FN_ENTER;
+	if (likely(priv->dev_state_mask & ACX_STATE_IFACE_UP)) {
+		struct sk_buff *skb;
+		skb = acx_rxbuf_to_ether(priv, rxbuf);
+		if (likely(skb)) {
+			netif_rx(skb);
+			priv->netdev->last_rx = jiffies;
+			priv->stats.rx_packets++;
+			priv->stats.rx_bytes += skb->len;
+		}
+	}
+	FN_EXIT0;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_process_data_frame_master
+*----------------------------------------------------------------*/
+static int
+acx_l_process_data_frame_master(wlandevice_t *priv, rxbuffer_t *rxbuf)
+{
+	struct wlan_hdr *hdr;
+	struct tx *tx;
+	void *txbuf;
+	int len;
+	int result = NOT_OK;
+
+	FN_ENTER;
+
+	hdr = acx_get_wlan_hdr(priv, rxbuf);
+
+	switch (WF_FC_FROMTODSi & hdr->fc) {
+	case 0:
+	case WF_FC_FROMDSi:
+		acxlog(L_DEBUG, "ap->sta or adhoc->adhoc data frame ignored\n");
+		goto done;
+	case WF_FC_TODSi:
+		break;
+	default: /* WF_FC_FROMTODSi */
+		acxlog(L_DEBUG, "wds data frame ignored (todo)\n");
+		goto done;
+	}
+
+	/* check if it is our BSSID, if not, leave */
+	if (!mac_is_equal(priv->bssid, hdr->a1)) {
+		goto done;
+	}
+
+	if (mac_is_equal(priv->dev_addr, hdr->a3)) {
+		/* this one is for us */
+		acx_l_rx(priv, rxbuf);
+	} else {
+		if (mac_is_bcast(hdr->a3)) {
+			/* this one is bcast, rx it too */
+			acx_l_rx(priv, rxbuf);
+		}
+		tx = acx_l_alloc_tx(priv);
+		if (!tx) {
+			goto fail;
+		}
+		/* repackage, tx, and hope it someday reaches its destination */
+		/* order is important, we do it in-place */
+		MAC_COPY(hdr->a1, hdr->a3);
+		MAC_COPY(hdr->a3, hdr->a2);
+		MAC_COPY(hdr->a2, priv->bssid);
+		/* To_DS = 0, From_DS = 1 */
+		hdr->fc = WF_FC_FROMDSi + WF_FTYPE_DATAi;
+
+		len = RXBUF_BYTES_RCVD(rxbuf);
+		txbuf = acx_l_get_txbuf(tx);
+		if (txbuf) {
+			memcpy(txbuf, &rxbuf->hdr_a3, len);
+			acx_l_dma_tx_data(priv, tx, len);
+		}
+	}
+done:
+	result = OK;
+fail:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_process_data_frame_client
+*----------------------------------------------------------------*/
+static int
+acx_l_process_data_frame_client(wlandevice_t *priv, rxbuffer_t *rxbuf)
+{
+	const u8 *da, *bssid;
+	const wlan_hdr_t *hdr;
+	netdevice_t *dev = priv->netdev;
+	int result = NOT_OK;
+
+	FN_ENTER;
+
+	if (ACX_STATUS_4_ASSOCIATED != priv->status) goto drop;
+
+	hdr = acx_get_wlan_hdr(priv, rxbuf);
+
+	switch (WF_FC_FROMTODSi & hdr->fc) {
+	case 0:
+		if (priv->mode != ACX_MODE_0_ADHOC) {
+			acxlog(L_DEBUG, "adhoc->adhoc data frame ignored\n");
+			goto drop;
+		}
+		bssid = hdr->a3;
+		break;
+	case WF_FC_FROMDSi:
+		if (priv->mode != ACX_MODE_2_STA) {
+			acxlog(L_DEBUG, "ap->sta data frame ignored\n");
+			goto drop;
+		}
+		bssid = hdr->a2;
+		break;
+	case WF_FC_TODSi:
+		acxlog(L_DEBUG, "sta->ap data frame ignored\n");
+		goto drop;
+	default: /* WF_FC_FROMTODSi: wds->wds */
+		acxlog(L_DEBUG, "wds data frame ignored (todo)\n");
+		goto drop;
+	}
+
+	da = hdr->a1;
+
+	if (unlikely(acx_debug & L_DEBUG)) {
+		acx_print_mac("rx: da=", da, "");
+		acx_print_mac(" bssid=", bssid, "");
+		acx_print_mac(" priv->bssid=", priv->bssid, "");
+		acx_print_mac(" priv->addr=", priv->dev_addr, "\n");
+	}
+
+	/* promiscuous mode --> receive all packets */
+	if (unlikely(dev->flags & IFF_PROMISC))
+		goto process;
+
+	/* FIRST, check if it is our BSSID */
+	if (!mac_is_equal(priv->bssid, bssid)) {
+		/* is not our BSSID, so bail out */
+		goto drop;
+	}
+
+	/* then, check if it is our address */
+	if (mac_is_equal(priv->dev_addr, da)) {
+		goto process;
+	}
+
+	/* then, check if it is broadcast */
+	if (mac_is_bcast(da)) {
+		goto process;
+	}
+
+	if (mac_is_mcast(da)) {
+		/* unconditionally receive all multicasts */
+		if (dev->flags & IFF_ALLMULTI)
+			goto process;
+
+		/* FIXME: check against the list of
+		 * multicast addresses that are configured
+		 * for the interface (ifconfig) */
+		acxlog(L_XFER, "FIXME: multicast packet, need to check "
+			"against a list of multicast addresses "
+			"(to be created!); accepting packet for now\n");
+		/* for now, just accept it here */
+		goto process;
+	}
+
+	acxlog(L_DEBUG, "rx: foreign packet, dropping\n");
+	goto drop;
+process:
+	/* receive packet */
+	acx_l_rx(priv, rxbuf);
+
+	result = OK;
+drop:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_process_mgmt_frame
+*
+* Theory of operation: mgmt packet gets parsed (to make it easy
+* to access variable-sized IEs), results stored in 'parsed'.
+* Then we react to the packet.
+* NB: wlan_mgmt_decode_XXX are dev-independent (shoudnt have been named acx_XXX)
+*----------------------------------------------------------------*/
+typedef union parsed_mgmt_req {
+	wlan_fr_mgmt_t mgmt;
+	wlan_fr_assocreq_t assocreq;
+	wlan_fr_reassocreq_t reassocreq;
+	wlan_fr_assocresp_t assocresp;
+	wlan_fr_reassocresp_t reassocresp;
+	wlan_fr_beacon_t beacon;
+	wlan_fr_disassoc_t disassoc;
+	wlan_fr_authen_t authen;
+	wlan_fr_deauthen_t deauthen;
+	wlan_fr_proberesp_t proberesp;
+} parsed_mgmt_req_t;
+
+extern void BUG_excessive_stack_usage(void);
+
+static int
+acx_l_process_mgmt_frame(wlandevice_t *priv, rxbuffer_t *rxbuf)
+{
+	parsed_mgmt_req_t parsed;	/* takes ~100 bytes of stack */
+	wlan_hdr_t *hdr;
+	int adhoc, sta_scan, sta, ap;
+	int len;
+
+	if (sizeof(parsed) > 256) BUG_excessive_stack_usage();
+
+	FN_ENTER;
+
+	hdr = acx_get_wlan_hdr(priv, rxbuf);
+
+	/* Management frames never have these set */
+	if (WF_FC_FROMTODSi & hdr->fc) {
+		FN_EXIT1(NOT_OK);
+		return NOT_OK;
+	}
+
+	len = RXBUF_BYTES_RCVD(rxbuf);
+	if (WF_FC_ISWEPi & hdr->fc)
+		len -= 0x10;
+
+	adhoc = (priv->mode == ACX_MODE_0_ADHOC);
+	sta_scan = ((priv->mode == ACX_MODE_2_STA)
+		 && (priv->status != ACX_STATUS_4_ASSOCIATED));
+	sta = ((priv->mode == ACX_MODE_2_STA)
+	    && (priv->status == ACX_STATUS_4_ASSOCIATED));
+	ap = (priv->mode == ACX_MODE_3_AP);
+
+	switch (WF_FC_FSTYPEi & hdr->fc) {
+	/* beacons first, for speed */
+	case WF_FSTYPE_BEACONi:
+		memset(&parsed.beacon, 0, sizeof(parsed.beacon));
+		parsed.beacon.hdr = hdr;
+		parsed.beacon.len = len;
+		if (acx_debug & L_DATA) {
+			printk("BCN len:%d fc:%04X dur:%04X seq:%04X\n",
+			       len, hdr->fc, hdr->dur, hdr->seq);
+			acx_print_mac("BCN a1:", hdr->a1, "\n");
+			acx_print_mac("BCN a2:", hdr->a2, "\n");
+			acx_print_mac("BCN a3:", hdr->a3, "\n");
+		}
+		wlan_mgmt_decode_beacon(&parsed.beacon);
+		/* beacon and probe response are very similar, so... */
+		acx_l_process_probe_response(priv, &parsed.beacon, rxbuf);
+		break;
+	case WF_FSTYPE_ASSOCREQi:
+		if (!ap)
+			break;
+		memset(&parsed.assocreq, 0, sizeof(parsed.assocreq));
+		parsed.assocreq.hdr = hdr;
+		parsed.assocreq.len = len;
+		wlan_mgmt_decode_assocreq(&parsed.assocreq);
+		if (mac_is_equal(hdr->a1, priv->bssid)
+		 && mac_is_equal(hdr->a3, priv->bssid)) {
+			acx_l_transmit_assocresp(priv, &parsed.assocreq);
+		}
+		break;
+	case WF_FSTYPE_REASSOCREQi:
+		if (!ap)
+			break;
+		memset(&parsed.assocreq, 0, sizeof(parsed.assocreq));
+		parsed.assocreq.hdr = hdr;
+		parsed.assocreq.len = len;
+		wlan_mgmt_decode_assocreq(&parsed.assocreq);
+		/* reassocreq and assocreq are equivalent */
+		acx_l_transmit_reassocresp(priv, &parsed.reassocreq);
+		break;
+	case WF_FSTYPE_ASSOCRESPi:
+		if (!sta_scan)
+			break;
+		memset(&parsed.assocresp, 0, sizeof(parsed.assocresp));
+		parsed.assocresp.hdr = hdr;
+		parsed.assocresp.len = len;
+		wlan_mgmt_decode_assocresp(&parsed.assocresp);
+		acx_l_process_assocresp(priv, &parsed.assocresp);
+		break;
+	case WF_FSTYPE_REASSOCRESPi:
+		if (!sta_scan)
+			break;
+		memset(&parsed.assocresp, 0, sizeof(parsed.assocresp));
+		parsed.assocresp.hdr = hdr;
+		parsed.assocresp.len = len;
+		wlan_mgmt_decode_assocresp(&parsed.assocresp);
+		acx_l_process_reassocresp(priv, &parsed.reassocresp);
+		break;
+	case WF_FSTYPE_PROBEREQi:
+		if (ap || adhoc) {
+			/* FIXME: since we're supposed to be an AP,
+			** we need to return a Probe Response packet.
+			** Currently firmware is doing it for us,
+			** but firmware is buggy! See comment elsewhere --vda */
+		}
+		break;
+	case WF_FSTYPE_PROBERESPi:
+		memset(&parsed.proberesp, 0, sizeof(parsed.proberesp));
+		parsed.proberesp.hdr = hdr;
+		parsed.proberesp.len = len;
+		wlan_mgmt_decode_proberesp(&parsed.proberesp);
+		acx_l_process_probe_response(priv, &parsed.proberesp, rxbuf);
+		break;
+	case 6:
+	case 7:
+		/* exit */
+		break;
+	case WF_FSTYPE_ATIMi:
+		/* exit */
+		break;
+	case WF_FSTYPE_DISASSOCi:
+		if (!sta && !ap)
+			break;
+		memset(&parsed.disassoc, 0, sizeof(parsed.disassoc));
+		parsed.disassoc.hdr = hdr;
+		parsed.disassoc.len = len;
+		wlan_mgmt_decode_disassoc(&parsed.disassoc);
+		if (sta)
+			acx_l_process_disassoc_from_ap(priv, &parsed.disassoc);
+		else
+			acx_l_process_disassoc_from_sta(priv, &parsed.disassoc);
+		break;
+	case WF_FSTYPE_AUTHENi:
+		if (!sta_scan && !ap)
+			break;
+		memset(&parsed.authen, 0, sizeof(parsed.authen));
+		parsed.authen.hdr = hdr;
+		parsed.authen.len = len;
+		wlan_mgmt_decode_authen(&parsed.authen);
+		acx_l_process_authen(priv, &parsed.authen);
+		break;
+	case WF_FSTYPE_DEAUTHENi:
+		if (!sta && !ap)
+			break;
+		memset(&parsed.deauthen, 0, sizeof(parsed.deauthen));
+		parsed.deauthen.hdr = hdr;
+		parsed.deauthen.len = len;
+		wlan_mgmt_decode_deauthen(&parsed.deauthen);
+		if (sta)
+			acx_l_process_deauth_from_ap(priv, &parsed.deauthen);
+		else
+			acx_l_process_deauth_from_sta(priv, &parsed.deauthen);
+		break;
+	}
+
+	FN_EXIT1(OK);
+	return OK;
+}
+
+
+#if UNUSED
+/*----------------------------------------------------------------
+* acx_process_class_frame
+*
+* Called from IRQ context only
+*----------------------------------------------------------------*/
+static int
+acx_process_class_frame(wlandevice_t *priv, rxbuffer_t *rxbuf, int vala)
+{
+	return OK;
+}
+#endif
+
+
+/*----------------------------------------------------------------
+* acx_l_process_NULL_frame
+*----------------------------------------------------------------*/
+#if BOGUS_ITS_NOT_A_NULL_FRAME_HANDLER_AT_ALL
+static int
+acx_l_process_NULL_frame(wlandevice_t *priv, rxbuffer_t *rxbuf, int vala)
+{
+	const signed char *esi;
+	const u8 *ebx;
+	const wlan_hdr_t *hdr;
+	const client_t *client;
+	int result = NOT_OK;
+
+	hdr = acx_get_wlan_hdr(priv, rxbuf);
+
+	switch (WF_FC_FROMTODSi & hdr->fc) {
+	case 0:
+		esi = hdr->a1;
+		ebx = hdr->a2;
+		break;
+	case WF_FC_FROMDSi:
+		esi = hdr->a1;
+		ebx = hdr->a3;
+		break;
+	case WF_FC_TODSi:
+		esi = hdr->a1;
+		ebx = hdr->a2;
+		break;
+	default: /* WF_FC_FROMTODSi */
+		esi = hdr->a1; /* added by me! --vda */
+		ebx = hdr->a2;
+	}
+
+	if (esi[0x0] < 0) {
+		result = OK;
+		goto done;
+	}
+
+	client = acx_l_sta_list_get(priv, ebx);
+	if (client)
+		result = NOT_OK;
+	else {
+#if IS_IT_BROKEN
+		acxlog(L_DEBUG | L_XFER, "<transmit_deauth 7>\n");
+		acx_l_transmit_deauthen(priv, ebx,
+			WLAN_MGMT_REASON_CLASS2_NONAUTH /* 6 */);
+#else
+		acxlog(L_DEBUG, "received NULL frame from unknown client! "
+			"We really shouldn't send deauthen here, right?\n");
+#endif
+		result = OK;
+	}
+done:
+	return result;
+}
+#endif
+
+
+/*----------------------------------------------------------------
+* acx_l_process_probe_response
+*----------------------------------------------------------------*/
+static int
+acx_l_process_probe_response(wlandevice_t *priv, wlan_fr_proberesp_t *req,
+			const rxbuffer_t *rxbuf)
+{
+	struct client *bss;
+	wlan_hdr_t *hdr;
+
+	FN_ENTER;
+
+	hdr = req->hdr;
+
+	if (mac_is_equal(hdr->a3, priv->dev_addr)) {
+		acxlog(L_ASSOC, "huh, scan found our own MAC!?\n");
+		goto ok; /* just skip this one silently */
+	}
+
+	bss = acx_l_sta_list_get_or_add(priv, hdr->a2);
+
+	/* NB: be careful modifying bss data! It may be one
+	** of already known clients (like our AP is we are a STA)
+	** Thus do not blindly modify e.g. current ratemask! */
+
+	if (STA_LIST_ADD_CAN_FAIL && !bss) {
+		/* uh oh, we found more sites/stations than we can handle with
+		 * our current setup: pull the emergency brake and stop scanning! */
+		acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_STOP_SCAN);
+		/* TODO: a nice comment what below call achieves --vda */
+		acx_set_status(priv, ACX_STATUS_2_WAIT_AUTH);
+		goto ok;
+	}
+	/* NB: get_or_add already filled bss->address = hdr->a2 */
+	MAC_COPY(bss->bssid, hdr->a3);
+
+	/* copy the ESSID element */
+	if (req->ssid && req->ssid->len <= IW_ESSID_MAX_SIZE) {
+		bss->essid_len = req->ssid->len;
+		memcpy(bss->essid, req->ssid->ssid, req->ssid->len);
+		bss->essid[req->ssid->len] = '\0';
+	} else {
+		/* Either no ESSID IE or oversized one */
+		printk("%s: received packet has bogus ESSID\n",
+						    priv->netdev->name);
+	}
+
+	if (req->ds_parms)
+		bss->channel = req->ds_parms->curr_ch;
+	if (req->cap_info)
+		bss->cap_info = ieee2host16(*req->cap_info);
+
+	bss->sir = acx_signal_to_winlevel(rxbuf->phy_level);
+	bss->snr = acx_signal_to_winlevel(rxbuf->phy_snr);
+
+	bss->rate_cap = 0;	/* operational mask */
+	bss->rate_bas = 0;	/* basic mask */
+	if (req->supp_rates)
+		add_bits_to_ratemasks(req->supp_rates->rates,
+			req->supp_rates->len, &bss->rate_bas, &bss->rate_cap);
+	if (req->ext_rates)
+		add_bits_to_ratemasks(req->ext_rates->rates,
+			req->ext_rates->len, &bss->rate_bas, &bss->rate_cap);
+	/* Fix up any possible bogosity - code elsewhere
+	 * is not expecting empty masks */
+	if (!bss->rate_cap)
+		bss->rate_cap = priv->rate_basic;
+	if (!bss->rate_bas)
+		bss->rate_bas = 1 << lowest_bit(bss->rate_cap);
+	if (!bss->rate_cur)
+		bss->rate_cur = 1 << lowest_bit(bss->rate_bas);
+
+	/* People moan about this being too noisy at L_ASSOC */
+	acxlog(L_DEBUG,
+		"found %s: ESSID='%s' ch=%d "
+		"BSSID="MACSTR" caps=0x%04X SIR=%d SNR=%d\n",
+		(bss->cap_info & WF_MGMT_CAP_IBSS) ? "Ad-Hoc peer" : "AP",
+		bss->essid, bss->channel, MAC(bss->bssid), bss->cap_info,
+		bss->sir, bss->snr);
+ok:
+	FN_EXIT0;
+	return OK;
+}
+
+
+/*----------------------------------------------------------------
+* get_status_string
+*----------------------------------------------------------------*/
+static const char*
+get_status_string(unsigned int status)
+{
+	/* A bit shortened, but hopefully still understandable */
+	static const char * const status_str[] = {
+	/* 0 */	"Successful",
+	/* 1 */	"Unspecified failure",
+	/* 2 */	"reserved",
+	/* 3 */	"reserved",
+	/* 4 */	"reserved",
+	/* 5 */	"reserved",
+	/* 6 */	"reserved",
+	/* 7 */	"reserved",
+	/* 8 */	"reserved",
+	/* 9 */	"reserved",
+	/*10 */	"Cannot support all requested capabilities in Capability Information field",
+	/*11 */	"Reassoc denied (reason outside of 802.11b scope)",
+	/*12 */	"Assoc denied (reason outside of 802.11b scope), maybe MAC filtering by peer?",
+	/*13 */	"Responding station doesnt support specified auth algorithm",
+	/*14 */	"Auth rejected: wrong transaction sequence number",
+	/*15 */	"Auth rejected: challenge failure",
+	/*16 */	"Auth rejected: timeout for next frame in sequence",
+	/*17 */	"Assoc denied: too many STAs on this AP",
+	/*18 */	"Assoc denied: requesting STA doesnt support all data rates in basic set",
+	/*19 */	"Assoc denied: requesting STA doesnt support Short Preamble",
+	/*20 */	"Assoc denied: requesting STA doesnt support PBCC Modulation",
+	/*21 */	"Assoc denied: requesting STA doesnt support Channel Agility"
+	/*22 */	"reserved",
+	/*23 */	"reserved",
+	/*24 */	"reserved",
+	/*25 */	"Assoc denied: requesting STA doesnt support Short Slot Time",
+	/*26 */	"Assoc denied: requesting STA doesnt support DSSS-OFDM"
+	};
+
+	return status_str[status < VEC_SIZE(status_str) ? status : 2];
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_process_assocresp
+*----------------------------------------------------------------*/
+static int
+acx_l_process_assocresp(wlandevice_t *priv, const wlan_fr_assocresp_t *req)
+{
+	const wlan_hdr_t *hdr;
+	int res = OK;
+
+	FN_ENTER;
+	hdr = req->hdr;
+
+	if ((ACX_MODE_2_STA == priv->mode)
+	 && mac_is_equal(priv->dev_addr, hdr->a1)) {
+		u16 st = ieee2host16(*(req->status));
+		if (WLAN_MGMT_STATUS_SUCCESS == st) {
+			priv->aid = ieee2host16(*(req->aid));
+			/* tell the card we are associated when we are out of interrupt context */
+			acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_ASSOCIATE);
+		} else {
+
+			/* TODO: we shall delete peer from sta_list, and try other candidates... */
+
+			printk("%s: association FAILED: peer sent "
+				"response code %d (%s)\n",
+				priv->netdev->name, st, get_status_string(st));
+			res = NOT_OK;
+		}
+	}
+
+	FN_EXIT1(res);
+	return res;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_process_reassocresp
+*----------------------------------------------------------------*/
+static int
+acx_l_process_reassocresp(wlandevice_t *priv, const wlan_fr_reassocresp_t *req)
+{
+	const wlan_hdr_t *hdr;
+	int result = 4;
+	u16 st;
+
+	FN_ENTER;
+	hdr = req->hdr;
+
+	if (!mac_is_equal(priv->dev_addr, hdr->a1)) {
+		result = 3;
+		goto end;
+	}
+	st = ieee2host16(*(req->status));
+	if (st == WLAN_MGMT_STATUS_SUCCESS) {
+		acx_set_status(priv, ACX_STATUS_4_ASSOCIATED);
+	} else {
+		printk("%s: reassociation FAILED: peer sent "
+			"response code %d (%s)\n",
+			priv->netdev->name, st, get_status_string(st));
+	}
+	result = OK;
+end:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_process_authen
+*
+* Called only in STA_SCAN or AP mode
+*----------------------------------------------------------------*/
+static int
+acx_l_process_authen(wlandevice_t *priv, const wlan_fr_authen_t *req)
+{
+	const wlan_hdr_t *hdr;
+	client_t *clt;
+	wlan_ie_challenge_t *chal;
+	u16 alg, seq, status;
+	int ap, result;
+
+	FN_ENTER;
+
+	hdr = req->hdr;
+
+	if (acx_debug & L_ASSOC) {
+		acx_print_mac("AUTHEN priv->addr=", priv->dev_addr, " ");
+		acx_print_mac("a1=", hdr->a1, " ");
+		acx_print_mac("a2=", hdr->a2, " ");
+		acx_print_mac("a3=", hdr->a3, " ");
+		acx_print_mac("priv->bssid=", priv->bssid, "\n");
+	}
+
+	/* TODO: move first check up in caller chain,
+	** it's not auth specific */
+	if (!mac_is_equal(priv->dev_addr, hdr->a1)
+	 || !mac_is_equal(priv->bssid, hdr->a3)) {
+		result = OK;
+		goto end;
+	}
+
+	alg = ieee2host16(*(req->auth_alg));
+	seq = ieee2host16(*(req->auth_seq));
+	status = ieee2host16(*(req->status));
+
+	ap = (priv->mode == ACX_MODE_3_AP);
+
+	if (priv->auth_alg <= 1) {
+		if (priv->auth_alg != alg) {
+			acxlog(L_ASSOC, "authentication algorithm mismatch: "
+				"want: %d, req: %d\n", priv->auth_alg, alg);
+			result = NOT_OK;
+			goto end;
+		}
+	}
+	acxlog(L_ASSOC, "algorithm is ok\n");
+
+	if (ap) {
+		clt = acx_l_sta_list_get_or_add(priv, hdr->a2);
+		if (STA_LIST_ADD_CAN_FAIL && !clt) {
+			acxlog(L_ASSOC, "could not allocate room for client\n");
+			result = NOT_OK;
+			goto end;
+		}
+	} else {
+		clt = priv->ap_client;
+		if (!mac_is_equal(clt->address, hdr->a2)) {
+			printk("%s: malformed auth frame from AP?!\n",
+					priv->netdev->name);
+			result = NOT_OK;
+			goto end;
+		}
+	}
+
+	/* now check which step in the authentication sequence we are
+	 * currently in, and act accordingly */
+	acxlog(L_ASSOC, "acx_process_authen auth seq step %d\n", seq);
+	switch (seq) {
+	case 1:
+		if (!ap)
+			break;
+		acx_l_transmit_authen2(priv, req, clt);
+		break;
+	case 2:
+		if (ap)
+			break;
+		if (status == WLAN_MGMT_STATUS_SUCCESS) {
+			if (alg == WLAN_AUTH_ALG_OPENSYSTEM) {
+				acx_set_status(priv, ACX_STATUS_3_AUTHENTICATED);
+				acx_l_transmit_assoc_req(priv);
+			} else
+			if (alg == WLAN_AUTH_ALG_SHAREDKEY) {
+				acx_l_transmit_authen3(priv, req);
+			}
+		} else {
+			printk("%s: auth FAILED: peer sent "
+				"response code %d (%s), "
+				"still waiting for authentication\n",
+				priv->netdev->name,
+				status,	get_status_string(status));
+			acx_set_status(priv, ACX_STATUS_2_WAIT_AUTH);
+		}
+		break;
+	case 3:
+		if (!ap)
+			break;
+		if ((clt->auth_alg != WLAN_AUTH_ALG_SHAREDKEY)
+		 || (alg != WLAN_AUTH_ALG_SHAREDKEY)
+		 || (clt->auth_step != 2))
+			break;
+		chal = req->challenge;
+		if (!chal
+		 || memcmp(chal->challenge, clt->challenge_text, WLAN_CHALLENGE_LEN)
+		 || (chal->eid != WLAN_EID_CHALLENGE)
+		 || (chal->len != WLAN_CHALLENGE_LEN)
+		)
+			break;
+		acx_l_transmit_authen4(priv, req);
+		MAC_COPY(clt->address, hdr->a2);
+		clt->used = CLIENT_AUTHENTICATED_2;
+		clt->auth_step = 4;
+		clt->seq = ieee2host16(hdr->seq);
+		break;
+	case 4:
+		if (ap)
+			break;
+		/* ok, we're through: we're authenticated. Woohoo!! */
+		acx_set_status(priv, ACX_STATUS_3_AUTHENTICATED);
+		acxlog(L_ASSOC, "Authenticated!\n");
+		/* now that we're authenticated, request association */
+		acx_l_transmit_assoc_req(priv);
+		break;
+	}
+	result = NOT_OK;
+end:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_gen_challenge
+*----------------------------------------------------------------*/
+static void
+acx_gen_challenge(wlan_ie_challenge_t* d)
+{
+	FN_ENTER;
+	d->eid = WLAN_EID_CHALLENGE;
+	d->len = WLAN_CHALLENGE_LEN;
+	get_random_bytes(d->challenge, WLAN_CHALLENGE_LEN);
+	FN_EXIT0;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_transmit_deauthen
+* STATUS: should be ok, but UNVERIFIED.
+*----------------------------------------------------------------*/
+static int
+acx_l_transmit_deauthen(wlandevice_t *priv, const u8 *addr, u16 reason)
+{
+	struct tx *tx;
+	struct wlan_hdr_mgmt *head;
+	struct deauthen_frame_body *body;
+
+	FN_ENTER;
+
+	tx = acx_l_alloc_tx(priv);
+	if (!tx)
+		goto bad;
+	head = acx_l_get_txbuf(tx);
+	if (!head)
+		goto bad;
+	body = (void*)(head + 1);
+
+	head->fc = (WF_FTYPE_MGMTi | WF_FSTYPE_DEAUTHENi);
+	head->dur = 0;
+	MAC_COPY(head->da, addr);
+	MAC_COPY(head->sa, priv->dev_addr);
+	MAC_COPY(head->bssid, priv->bssid);
+	head->seq = 0;
+
+	acxlog(L_DEBUG | L_ASSOC | L_XFER,
+		"sending deauthen to "MACSTR" for %d\n",
+		MAC(addr), reason);
+
+	body->reason = host2ieee16(reason);
+
+	/* body is fixed size here, but beware of cutting-and-pasting this -
+	** do not use sizeof(*body) for variable sized mgmt packets! */
+	acx_l_dma_tx_data(priv, tx, WLAN_HDR_A3_LEN + sizeof(*body));
+
+	FN_EXIT1(OK);
+	return OK;
+bad:
+	FN_EXIT1(NOT_OK);
+	return NOT_OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_transmit_authen1
+*----------------------------------------------------------------*/
+static int
+acx_l_transmit_authen1(wlandevice_t *priv)
+{
+	struct tx *tx;
+	struct wlan_hdr_mgmt *head;
+	struct auth_frame_body *body;
+
+	FN_ENTER;
+
+	acxlog(L_ASSOC, "Sending authentication1 request, "
+		"awaiting response!\n");
+
+	tx = acx_l_alloc_tx(priv);
+	if (!tx)
+		goto bad;
+	head = acx_l_get_txbuf(tx);
+	if (!head)
+		goto bad;
+	body = (void*)(head + 1);
+
+	head->fc = WF_FSTYPE_AUTHENi;
+	head->dur = host2ieee16(0x8000);
+	MAC_COPY(head->da, priv->bssid);
+	MAC_COPY(head->sa, priv->dev_addr);
+	MAC_COPY(head->bssid, priv->bssid);
+	head->seq = 0;
+
+	body->auth_alg = host2ieee16(priv->auth_alg);
+	body->auth_seq = host2ieee16(1);
+	body->status = host2ieee16(0);
+
+	acx_l_dma_tx_data(priv, tx, WLAN_HDR_A3_LEN + 2 + 2 + 2);
+
+	FN_EXIT1(OK);
+	return OK;
+bad:
+	FN_EXIT1(NOT_OK);
+	return NOT_OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_transmit_authen2
+*----------------------------------------------------------------*/
+static int
+acx_l_transmit_authen2(wlandevice_t *priv, const wlan_fr_authen_t *req,
+		      client_t *clt)
+{
+	struct tx *tx;
+	struct wlan_hdr_mgmt *head;
+	struct auth_frame_body *body;
+	unsigned int packet_len;
+
+	FN_ENTER;
+
+	if (!clt)
+		goto ok;
+
+	MAC_COPY(clt->address, req->hdr->a2);
+#if UNUSED
+	clt->ps = ((WF_FC_PWRMGTi & req->hdr->fc) != 0);
+#endif
+	clt->auth_alg = ieee2host16(*(req->auth_alg));
+	clt->auth_step = 2;
+	clt->seq = ieee2host16(req->hdr->seq);
+
+	tx = acx_l_alloc_tx(priv);
+	if (!tx)
+		goto bad;
+	head = acx_l_get_txbuf(tx);
+	if (!head)
+		goto bad;
+	body = (void*)(head + 1);
+
+	head->fc = WF_FSTYPE_AUTHENi; /* 0xb0 */
+	head->dur = req->hdr->dur;
+	MAC_COPY(head->da, req->hdr->a2);
+	/* MAC_COPY(head->sa, req->hdr->a1); */
+	MAC_COPY(head->sa, priv->dev_addr);
+	MAC_COPY(head->bssid, req->hdr->a3);
+	head->seq = req->hdr->seq;
+
+	/* already in IEEE format, no endianness conversion */
+	body->auth_alg = *(req->auth_alg);
+	body->auth_seq = host2ieee16(2);
+	body->status = host2ieee16(0);
+
+	packet_len = WLAN_HDR_A3_LEN + 2 + 2 + 2;
+	if (ieee2host16(*(req->auth_alg)) == WLAN_AUTH_ALG_OPENSYSTEM) {
+		clt->used = CLIENT_AUTHENTICATED_2;
+	} else {	/* shared key */
+		acx_gen_challenge(&body->challenge);
+		memcpy(&clt->challenge_text, body->challenge.challenge, WLAN_CHALLENGE_LEN);
+		packet_len += 2 + 2 + 2 + 1+1+WLAN_CHALLENGE_LEN;
+	}
+
+	acxlog_mac(L_ASSOC | L_XFER,
+		"transmit_auth2: BSSID=", head->bssid, "\n");
+
+	acx_l_dma_tx_data(priv, tx, packet_len);
+ok:
+	FN_EXIT1(OK);
+	return OK;
+bad:
+	FN_EXIT1(NOT_OK);
+	return NOT_OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_transmit_authen3
+*----------------------------------------------------------------*/
+static int
+acx_l_transmit_authen3(wlandevice_t *priv, const wlan_fr_authen_t *req)
+{
+	struct tx *tx;
+	struct wlan_hdr_mgmt *head;
+	struct auth_frame_body *body;
+	unsigned int packet_len;
+
+	FN_ENTER;
+
+	tx = acx_l_alloc_tx(priv);
+	if (!tx)
+		goto ok;
+	head = acx_l_get_txbuf(tx);
+	if (!head)
+		goto ok;
+	body = (void*)(head + 1);
+
+	head->fc = WF_FC_ISWEPi + WF_FSTYPE_AUTHENi;
+	/* FIXME: is this needed?? authen4 does it...
+	head->dur = req->hdr->dur;
+	head->seq = req->hdr->seq;
+	*/
+	MAC_COPY(head->da, priv->bssid);
+	MAC_COPY(head->sa, priv->dev_addr);
+	MAC_COPY(head->bssid, priv->bssid);
+
+	/* already in IEEE format, no endianness conversion */
+	body->auth_alg = *(req->auth_alg);
+	body->auth_seq = host2ieee16(3);
+	body->status = host2ieee16(0);
+	memcpy(&body->challenge, req->challenge, req->challenge->len + 2);
+	packet_len = WLAN_HDR_A3_LEN + 8 + req->challenge->len;
+
+	acxlog(L_ASSOC | L_XFER, "transmit_authen3!\n");
+
+	acx_l_dma_tx_data(priv, tx, packet_len);
+ok:
+	FN_EXIT1(OK);
+	return OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_transmit_authen4
+*----------------------------------------------------------------*/
+static int
+acx_l_transmit_authen4(wlandevice_t *priv, const wlan_fr_authen_t *req)
+{
+	struct tx *tx;
+	struct wlan_hdr_mgmt *head;
+	struct auth_frame_body *body;
+
+	FN_ENTER;
+
+	tx = acx_l_alloc_tx(priv);
+	if (!tx)
+		goto ok;
+	head = acx_l_get_txbuf(tx);
+	if (!head)
+		goto ok;
+	body = (void*)(head + 1);
+
+	head->fc = WF_FSTYPE_AUTHENi; /* 0xb0 */
+	head->dur = req->hdr->dur;
+	MAC_COPY(head->da, req->hdr->a2);
+	/* MAC_COPY(head->sa, req->hdr->a1); */
+	MAC_COPY(head->sa, priv->dev_addr);
+	MAC_COPY(head->bssid, req->hdr->a3);
+	head->seq = req->hdr->seq;
+
+	/* already in IEEE format, no endianness conversion */
+	body->auth_alg = *(req->auth_alg);
+	body->auth_seq = host2ieee16(4);
+	body->status = host2ieee16(0);
+
+	acx_l_dma_tx_data(priv, tx, WLAN_HDR_A3_LEN + 2 + 2 + 2);
+ok:
+	FN_EXIT1(OK);
+	return OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_transmit_assoc_req
+*
+* priv->ap_client is a current candidate AP here
+*----------------------------------------------------------------*/
+static int
+acx_l_transmit_assoc_req(wlandevice_t *priv)
+{
+	struct tx *tx;
+	struct wlan_hdr_mgmt *head;
+	u8 *body, *p, *prate;
+	unsigned int packet_len;
+	u16 cap;
+
+	FN_ENTER;
+
+	acxlog(L_ASSOC, "sending association request, "
+			"awaiting response. NOT ASSOCIATED YET\n");
+	tx = acx_l_alloc_tx(priv);
+	if (!tx)
+		goto bad;
+	head = acx_l_get_txbuf(tx);
+	if (!head)
+		goto bad;
+	body = (void*)(head + 1);
+
+	head->fc = WF_FSTYPE_ASSOCREQi;
+	head->dur = host2ieee16(0x8000);
+	MAC_COPY(head->da, priv->bssid);
+	MAC_COPY(head->sa, priv->dev_addr);
+	MAC_COPY(head->bssid, priv->bssid);
+	head->seq = 0;
+
+	p = body;
+	/* now start filling the AssocReq frame body */
+
+	/* since this assoc request will most likely only get
+	 * sent in the STA to AP case (and not when Ad-Hoc IBSS),
+	 * the cap combination indicated here will thus be
+	 * WF_MGMT_CAP_ESSi *always* (no IBSS ever)
+	 * The specs are more than non-obvious on all that:
+	 *
+	 * 802.11 7.3.1.4 Capability Information field
+	** APs set the ESS subfield to 1 and the IBSS subfield to 0 within
+	** Beacon or Probe Response management frames. STAs within an IBSS
+	** set the ESS subfield to 0 and the IBSS subfield to 1 in transmitted
+	** Beacon or Probe Response management frames
+	**
+	** APs set the Privacy subfield to 1 within transmitted Beacon,
+	** Probe Response, Association Response, and Reassociation Response
+	** if WEP is required for all data type frames within the BSS.
+	** STAs within an IBSS set the Privacy subfield to 1 in Beacon
+	** or Probe Response management frames if WEP is required
+	** for all data type frames within the IBSS */
+
+	/* note that returning 0 will be refused by several APs...
+	 * (so this indicates that you're probably supposed to
+	 * "confirm" the ESS mode) */
+	cap = WF_MGMT_CAP_ESSi;
+
+	/* this one used to be a check on wep_restricted,
+	 * but more likely it's wep_enabled instead */
+	if (priv->wep_enabled)
+		SET_BIT(cap, WF_MGMT_CAP_PRIVACYi);
+
+	/* Probably we can just set these always, because our hw is
+	** capable of shortpre and PBCC --vda */
+	/* only ask for short preamble if the peer station supports it */
+	if (priv->ap_client->cap_info & WF_MGMT_CAP_SHORT)
+		SET_BIT(cap, WF_MGMT_CAP_SHORTi);
+	/* only ask for PBCC support if the peer station supports it */
+	if (priv->ap_client->cap_info & WF_MGMT_CAP_PBCC)
+		SET_BIT(cap, WF_MGMT_CAP_PBCCi);
+
+	/* IEs: 1. caps */
+	*(u16*)p = cap;	p += 2;
+	/* 2. listen interval */
+	*(u16*)p = host2ieee16(priv->listen_interval); p += 2;
+	/* 3. ESSID */
+	p = wlan_fill_ie_ssid(p,
+			strlen(priv->essid_for_assoc), priv->essid_for_assoc);
+	/* 4. supp rates */
+	prate = p;
+	p = wlan_fill_ie_rates(p,
+			priv->rate_supported_len, priv->rate_supported);
+	/* 5. ext supp rates */
+	p = wlan_fill_ie_rates_ext(p,
+			priv->rate_supported_len, priv->rate_supported);
+
+	if (acx_debug & L_DEBUG) {
+		printk("association: rates element\n");
+		acx_dump_bytes(prate, p - prate);
+	}
+
+	/* calculate lengths */
+	packet_len = WLAN_HDR_A3_LEN + (p - body);
+
+	acxlog(L_ASSOC, "association: requesting caps 0x%04X, ESSID '%s'\n",
+		cap, priv->essid_for_assoc);
+
+	acx_l_dma_tx_data(priv, tx, packet_len);
+	FN_EXIT1(OK);
+	return OK;
+bad:
+	FN_EXIT1(NOT_OK);
+	return NOT_OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_transmit_disassoc
+*
+* FIXME: looks like incomplete implementation of a helper:
+* acx_l_transmit_disassoc(priv, clt) - kick this client (we're an AP)
+* acx_l_transmit_disassoc(priv, NULL) - leave BSSID (we're a STA)
+*----------------------------------------------------------------*/
+int
+acx_l_transmit_disassoc(wlandevice_t *priv, client_t *clt)
+{
+	struct tx *tx;
+	struct wlan_hdr_mgmt *head;
+	struct disassoc_frame_body *body;
+
+	FN_ENTER;
+/*	if (clt != NULL) { */
+		tx = acx_l_alloc_tx(priv);
+		if (!tx)
+			goto bad;
+		head = acx_l_get_txbuf(tx);
+		if (!head)
+			goto bad;
+		body = (void*)(head + 1);
+
+/*		clt->used = CLIENT_AUTHENTICATED_2; - not (yet?) associated */
+
+		head->fc = WF_FSTYPE_DISASSOCi;
+		head->dur = 0;
+		/* huh? It muchly depends on whether we're STA or AP...
+		** sta->ap: da=bssid, sa=own, bssid=bssid
+		** ap->sta: da=sta, sa=bssid, bssid=bssid. FIXME! */
+		MAC_COPY(head->da, priv->bssid);
+		MAC_COPY(head->sa, priv->dev_addr);
+		MAC_COPY(head->bssid, priv->dev_addr);
+		head->seq = 0;
+
+		/* "Class 3 frame received from nonassociated station." */
+		body->reason = host2ieee16(7);
+
+		/* fixed size struct, ok to sizeof */
+		acx_l_dma_tx_data(priv, tx, WLAN_HDR_A3_LEN + sizeof(*body));
+/*	} */
+	FN_EXIT1(OK);
+	return OK;
+bad:
+	FN_EXIT1(NOT_OK);
+	return NOT_OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_s_complete_scan
+*
+* Called either from after_interrupt_task() if:
+* 1) there was Scan_Complete IRQ, or
+* 2) scanning expired in timer()
+* We need to decide which ESS or IBSS to join.
+* Iterates thru priv->sta_list:
+*	if priv->ap is not bcast, will join only specified
+*	ESS or IBSS with this bssid
+*	checks peers' caps for ESS/IBSS bit
+*	checks peers' SSID, allows exact match or hidden SSID
+* If station to join is chosen:
+*	points priv->ap_client to the chosen struct client
+*	sets priv->essid_for_assoc for future assoc attempt
+* Auth/assoc is not yet performed
+* Returns OK if there is no need to restart scan
+*----------------------------------------------------------------*/
+int
+acx_s_complete_scan(wlandevice_t *priv)
+{
+	struct client *bss;
+	unsigned long flags;
+	u16 needed_cap;
+	int i;
+	int idx_found = -1;
+	int result = OK;
+
+	FN_ENTER;
+
+	switch (priv->mode) {
+	case ACX_MODE_0_ADHOC:
+		needed_cap = WF_MGMT_CAP_IBSS; /* 2, we require Ad-Hoc */
+		break;
+	case ACX_MODE_2_STA:
+		needed_cap = WF_MGMT_CAP_ESS; /* 1, we require Managed */
+		break;
+	default:
+		printk("acx: driver bug: mode=%d in complete_scan()\n", priv->mode);
+		dump_stack();
+		goto end;
+	}
+
+	acx_lock(priv, flags);
+
+	/* TODO: sta_iterator hiding implementation would be nice here... */
+
+	for (i = 0; i < VEC_SIZE(priv->sta_list); i++) {
+		bss = &priv->sta_list[i];
+		if (!bss->used) continue;
+
+		acxlog(L_ASSOC, "Scan Table: SSID='%s' CH=%d SIR=%d SNR=%d\n",
+			bss->essid, bss->channel, bss->sir, bss->snr);
+
+		if (!mac_is_bcast(priv->ap))
+			if (!mac_is_equal(bss->bssid, priv->ap))
+				continue; /* keep looking */
+
+		/* broken peer with no mode flags set? */
+		if (unlikely(!(bss->cap_info & (WF_MGMT_CAP_ESS | WF_MGMT_CAP_IBSS)))) {
+			printk("%s: strange peer "MACSTR" found with "
+				"neither ESS (AP) nor IBSS (Ad-Hoc) "
+				"capability - skipped\n",
+				priv->netdev->name, MAC(bss->address));
+			continue;
+		}
+		acxlog(L_ASSOC, "peer_cap 0x%04X, needed_cap 0x%04X\n",
+		       bss->cap_info, needed_cap);
+
+		/* does peer station support what we need? */
+		if ((bss->cap_info & needed_cap) != needed_cap)
+			continue; /* keep looking */
+
+		/* strange peer with NO basic rates?! */
+		if (unlikely(!bss->rate_bas)) {
+			printk("%s: strange peer "MACSTR" with empty rate set "
+				"- skipped\n",
+				priv->netdev->name, MAC(bss->address));
+			continue;
+		}
+
+		/* do we support all basic rates of this peer? */
+		if ((bss->rate_bas & priv->rate_oper) != bss->rate_bas)	{
+/* we probably need to have all rates as operational rates,
+   even in case of an 11M-only configuration */
+#if THIS_IS_TROUBLESOME
+			printk("%s: peer "MACSTR": incompatible basic rates "
+				"(AP requests 0x%04X, we have 0x%04X) "
+				"- skipped\n",
+				priv->netdev->name, MAC(bss->address),
+				bss->rate_bas, priv->rate_oper);
+			continue;
+#else
+			printk("%s: peer "MACSTR": incompatible basic rates "
+				"(AP requests 0x%04X, we have 0x%04X). "
+				"Considering anyway...\n",
+				priv->netdev->name, MAC(bss->address),
+				bss->rate_bas, priv->rate_oper);
+#endif
+		}
+
+		if ( !(priv->reg_dom_chanmask & (1<<(bss->channel-1))) ) {
+			printk("%s: warning: peer "MACSTR" is on channel %d "
+				"outside of channel range of current "
+				"regulatory domain - couldn't join "
+				"even if other settings match. "
+				"You might want to adapt your config\n",
+				priv->netdev->name, MAC(bss->address),
+				bss->channel);
+			continue; /* keep looking */
+		}
+
+		if (!priv->essid_active || !strcmp(bss->essid, priv->essid)) {
+			acxlog(L_ASSOC,
+			       "found station with matching ESSID! ('%s' "
+			       "station, '%s' config)\n",
+			       bss->essid,
+			       (priv->essid_active) ? priv->essid : "[any]");
+			/* TODO: continue looking for peer with better SNR */
+			bss->used = CLIENT_JOIN_CANDIDATE;
+			idx_found = i;
+
+			/* stop searching if this station is
+			 * on the current channel, otherwise
+			 * keep looking for an even better match */
+			if (bss->channel == priv->channel)
+				break;
+		} else
+		if (!bss->essid[0]
+		 || ((' ' == bss->essid[0]) && !bss->essid[1])
+		) {
+			/* hmm, station with empty or single-space SSID:
+			 * using hidden SSID broadcast?
+			 */
+			/* This behaviour is broken: which AP from zillion
+			** of APs with hidden SSID you'd try?
+			** We should use Probe requests to get Probe responses
+			** and check for real SSID (are those never hidden?) */
+			bss->used = CLIENT_JOIN_CANDIDATE;
+			if (idx_found == -1)
+				idx_found = i;
+			acxlog(L_ASSOC, "found station with empty or "
+				"single-space (hidden) SSID, considering "
+				"for assoc attempt\n");
+			/* ...and keep looking for better matches */
+		} else {
+			acxlog(L_ASSOC, "ESSID doesn't match! ('%s' "
+				"station, '%s' config)\n",
+				bss->essid,
+				(priv->essid_active) ? priv->essid : "[any]");
+		}
+	}
+
+	/* TODO: iterate thru join candidates instead */
+	/* TODO: rescan if not associated within some timeout */
+	if (idx_found != -1) {
+		char *essid_src;
+		size_t essid_len;
+
+		bss = &priv->sta_list[idx_found];
+		priv->ap_client = bss;
+
+		if (bss->essid[0] == '\0') {
+			/* if the ESSID of the station we found is empty
+			 * (no broadcast), then use user configured ESSID
+			 * instead */
+			essid_src = priv->essid;
+			essid_len = priv->essid_len;
+		} else {
+			essid_src = bss->essid;
+			essid_len = strlen(bss->essid);
+		}
+
+		acx_update_capabilities(priv);
+
+		memcpy(priv->essid_for_assoc, essid_src, essid_len);
+		priv->essid_for_assoc[essid_len] = '\0';
+		priv->channel = bss->channel;
+		MAC_COPY(priv->bssid, bss->bssid);
+
+		bss->rate_cfg = (bss->rate_cap & priv->rate_oper);
+		bss->rate_cur = 1 << lowest_bit(bss->rate_cfg);
+		bss->rate_100 = acx_rate111to100(bss->rate_cur);
+
+		acxlog_mac(L_ASSOC,
+			"matching station found: ", priv->bssid, ", joining\n");
+
+		/* TODO: do we need to switch to the peer's channel first? */
+
+		if (ACX_MODE_0_ADHOC == priv->mode) {
+			acx_set_status(priv, ACX_STATUS_4_ASSOCIATED);
+		} else {
+			acx_l_transmit_authen1(priv);
+			acx_set_status(priv, ACX_STATUS_2_WAIT_AUTH);
+		}
+	} else { /* idx_found == -1 */
+		/* uh oh, no station found in range */
+		if (ACX_MODE_0_ADHOC == priv->mode) {
+			printk("%s: no matching station found in range, "
+				"generating our own IBSS instead\n",
+				priv->netdev->name);
+			/* we do it hostap way: */
+			MAC_COPY(priv->bssid, priv->dev_addr);
+			priv->bssid[0] |= 0x02; /* 'local assigned addr' bit */
+			/* add IBSS bit to our caps... */
+			acx_update_capabilities(priv);
+			acx_set_status(priv, ACX_STATUS_4_ASSOCIATED);
+			/* In order to cmd_join be called below */
+			idx_found = 0;
+		} else {
+			/* we shall scan again, AP can be
+			** just temporarily powered off */
+			acxlog(L_ASSOC,
+				"no matching station found in range yet\n");
+			acx_set_status(priv, ACX_STATUS_1_SCANNING);
+			result = NOT_OK;
+		}
+	}
+
+	acx_unlock(priv, flags);
+
+	if (idx_found != -1) {
+		if (ACX_MODE_0_ADHOC == priv->mode) {
+			/* need to update channel in beacon template */
+			SET_BIT(priv->set_mask, SET_TEMPLATES);
+			if (ACX_STATE_IFACE_UP & priv->dev_state_mask)
+				acx_s_update_card_settings(priv, 0, 0);
+		}
+		/* Inform firmware on our decision to start or join BSS */
+		acx_s_cmd_join_bssid(priv, priv->bssid);
+	}
+
+end:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+#if (POWER_SAVE_80211 == 0)
+/*----------------------------------------------------------------
+* acx_s_activate_power_save_mode
+*----------------------------------------------------------------*/
+static void
+acx_s_activate_power_save_mode(wlandevice_t *priv, /*@unused@*/ int vala)
+{
+	acx100_ie_powermgmt_t pm;
+
+	FN_ENTER;
+
+	acx_s_interrogate(priv, &pm, ACX1xx_IE_POWER_MGMT);
+	if (pm.wakeup_cfg != 0x81)
+		goto end;
+
+	pm.wakeup_cfg = 0;
+	pm.options = 0;
+	pm.hangover_period = 0;
+	acx_s_configure(priv, &pm, ACX1xx_IE_POWER_MGMT);
+end:
+	FN_EXIT0;
+}
+#endif
diff -urN oldtree/drivers/net/wireless/tiacx/ioctl.c newtree/drivers/net/wireless/tiacx/ioctl.c
--- oldtree/drivers/net/wireless/tiacx/ioctl.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/ioctl.c	2006-02-13 19:20:00.284469056 -0500
@@ -0,0 +1,3033 @@
+/***********************************************************************
+** Copyright (C) 2003  ACX100 Open Source Project
+**
+** The contents of this file are subject to the Mozilla Public
+** License Version 1.1 (the "License"); you may not use this file
+** except in compliance with the License. You may obtain a copy of
+** the License at http://www.mozilla.org/MPL/
+**
+** Software distributed under the License is distributed on an "AS
+** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+** implied. See the License for the specific language governing
+** rights and limitations under the License.
+**
+** Alternatively, the contents of this file may be used under the
+** terms of the GNU Public License version 2 (the "GPL"), in which
+** case the provisions of the GPL are applicable instead of the
+** above.  If you wish to allow the use of your version of this file
+** only under the terms of the GPL and not to allow others to use
+** your version of this file under the MPL, indicate your decision
+** by deleting the provisions above and replace them with the notice
+** and other provisions required by the GPL.  If you do not delete
+** the provisions above, a recipient may use your version of this
+** file under either the MPL or the GPL.
+** ---------------------------------------------------------------------
+** Inquiries regarding the ACX100 Open Source Project can be
+** made directly to:
+**
+** acx100-users@lists.sf.net
+** http://acx100.sf.net
+** ---------------------------------------------------------------------
+*/
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+#include <asm/uaccess.h> /* required for 2.4.x kernels; verify_write() */
+
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+#if WIRELESS_EXT >= 13
+#include <net/iw_handler.h>
+#endif /* WE >= 13 */
+
+#include "acx.h"
+
+
+/*================================================================*/
+
+/* if you plan to reorder something, make sure to reorder all other places
+ * accordingly! */
+/* someone broke SET/GET convention: SETs must have even position, GETs odd */
+#define ACX100_IOCTL SIOCIWFIRSTPRIV
+enum {
+	ACX100_IOCTL_DEBUG = ACX100_IOCTL,
+	ACX100_IOCTL_GET__________UNUSED1,
+	ACX100_IOCTL_SET_PLED,
+	ACX100_IOCTL_GET_PLED,
+	ACX100_IOCTL_SET_RATES,
+	ACX100_IOCTL_LIST_DOM,
+	ACX100_IOCTL_SET_DOM,
+	ACX100_IOCTL_GET_DOM,
+	ACX100_IOCTL_SET_SCAN_PARAMS,
+	ACX100_IOCTL_GET_SCAN_PARAMS,
+	ACX100_IOCTL_SET_PREAMB,
+	ACX100_IOCTL_GET_PREAMB,
+	ACX100_IOCTL_SET_ANT,
+	ACX100_IOCTL_GET_ANT,
+	ACX100_IOCTL_RX_ANT,
+	ACX100_IOCTL_TX_ANT,
+	ACX100_IOCTL_SET_PHY_AMP_BIAS,
+	ACX100_IOCTL_GET_PHY_CHAN_BUSY,
+	ACX100_IOCTL_SET_ED,
+	ACX100_IOCTL_GET__________UNUSED3,
+	ACX100_IOCTL_SET_CCA,
+	ACX100_IOCTL_GET__________UNUSED4,
+	ACX100_IOCTL_MONITOR,
+	ACX100_IOCTL_TEST,
+	ACX100_IOCTL_DBG_SET_MASKS,
+	ACX111_IOCTL_INFO,
+	ACX100_IOCTL_DBG_SET_IO,
+	ACX100_IOCTL_DBG_GET_IO
+};
+
+/* channel frequencies
+ * TODO: Currently, every other 802.11 driver keeps its own copy of this. In
+ * the long run this should be integrated into ieee802_11.h or wireless.h or
+ * whatever IEEE802.11x framework evolves */
+static const u16 acx_channel_freq[] = {
+	2412, 2417, 2422, 2427, 2432, 2437, 2442,
+	2447, 2452, 2457, 2462, 2467, 2472, 2484,
+};
+
+static const struct iw_priv_args acx_ioctl_private_args[] = {
+#if ACX_DEBUG
+{ cmd : ACX100_IOCTL_DEBUG,
+	set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	get_args : 0,
+	name : "SetDebug" },
+#endif
+{ cmd : ACX100_IOCTL_SET_PLED,
+	set_args : IW_PRIV_TYPE_BYTE | 2,
+	get_args : 0,
+	name : "SetLEDPower" },
+{ cmd : ACX100_IOCTL_GET_PLED,
+	set_args : 0,
+	get_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 2,
+	name : "GetLEDPower" },
+{ cmd : ACX100_IOCTL_SET_RATES,
+	set_args : IW_PRIV_TYPE_CHAR | 256,
+	get_args : 0,
+	name : "SetRates" },
+{ cmd : ACX100_IOCTL_LIST_DOM,
+	set_args : 0,
+	get_args : 0,
+	name : "ListRegDomain" },
+{ cmd : ACX100_IOCTL_SET_DOM,
+	set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
+	get_args : 0,
+	name : "SetRegDomain" },
+{ cmd : ACX100_IOCTL_GET_DOM,
+	set_args : 0,
+	get_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
+	name : "GetRegDomain" },
+{ cmd : ACX100_IOCTL_SET_SCAN_PARAMS,
+	set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4,
+	get_args : 0,
+	name : "SetScanParams" },
+{ cmd : ACX100_IOCTL_GET_SCAN_PARAMS,
+	set_args : 0,
+	get_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4,
+	name : "GetScanParams" },
+{ cmd : ACX100_IOCTL_SET_PREAMB,
+	set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
+	get_args : 0,
+	name : "SetSPreamble" },
+{ cmd : ACX100_IOCTL_GET_PREAMB,
+	set_args : 0,
+	get_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
+	name : "GetSPreamble" },
+{ cmd : ACX100_IOCTL_SET_ANT,
+	set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
+	get_args : 0,
+	name : "SetAntenna" },
+{ cmd : ACX100_IOCTL_GET_ANT,
+	set_args : 0,
+	get_args : 0,
+	name : "GetAntenna" },
+{ cmd : ACX100_IOCTL_RX_ANT,
+	set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
+	get_args : 0,
+	name : "SetRxAnt" },
+{ cmd : ACX100_IOCTL_TX_ANT,
+	set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
+	get_args : 0,
+	name : "SetTxAnt" },
+{ cmd : ACX100_IOCTL_SET_PHY_AMP_BIAS,
+	set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
+	get_args : 0,
+	name : "SetPhyAmpBias"},
+{ cmd : ACX100_IOCTL_GET_PHY_CHAN_BUSY,
+	set_args : 0,
+	get_args : 0,
+	name : "GetPhyChanBusy" },
+{ cmd : ACX100_IOCTL_SET_ED,
+	set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	get_args : 0,
+	name : "SetED" },
+{ cmd : ACX100_IOCTL_SET_CCA,
+	set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
+	get_args : 0,
+	name : "SetCCA" },
+{ cmd : ACX100_IOCTL_MONITOR,
+	set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2,
+	get_args : 0,
+	name : "monitor" },
+{ cmd : ACX100_IOCTL_TEST,
+	set_args : 0,
+	get_args : 0,
+	name : "Test" },
+{ cmd : ACX100_IOCTL_DBG_SET_MASKS,
+	set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2,
+	get_args : 0,
+	name : "DbgSetMasks" },
+{ cmd : ACX111_IOCTL_INFO,
+	set_args : 0,
+	get_args : 0,
+	name : "GetAcx111Info" },
+{ cmd : ACX100_IOCTL_DBG_SET_IO,
+	set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4,
+	get_args : 0,
+	name : "DbgSetIO" },
+{ cmd : ACX100_IOCTL_DBG_GET_IO,
+	set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3,
+	get_args : 0,
+	name : "DbgGetIO" },
+};
+
+
+/*------------------------------------------------------------------------------
+ * acx_ioctl_commit
+ *----------------------------------------------------------------------------*/
+static int
+acx_ioctl_commit(struct net_device *dev,
+				      struct iw_request_info *info,
+				      void *zwrq, char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+
+	FN_ENTER;
+
+	acx_sem_lock(priv);
+	if (ACX_STATE_IFACE_UP & priv->dev_state_mask)
+		acx_s_update_card_settings(priv, 0, 0);
+	acx_sem_unlock(priv);
+
+	FN_EXIT0;
+	return OK;
+}
+
+
+/***********************************************************************
+*/
+static int
+acx_ioctl_get_name(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	char *cwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	static const char * const names[] = { "IEEE 802.11b+/g+", "IEEE 802.11b+" };
+
+	strcpy(cwrq, names[(CHIPTYPE_ACX111 == priv->chip_type) ? 0 : 1]);
+
+	return OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_set_freq
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_set_freq(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_freq *fwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int channel = -1;
+	unsigned int mult = 1;
+	int result;
+
+	FN_ENTER;
+
+	if (fwrq->e == 0 && fwrq->m <= 1000) {
+		/* Setting by channel number */
+		channel = fwrq->m;
+	} else {
+		/* If setting by frequency, convert to a channel */
+		int i;
+
+		for (i = 0; i < (6 - fwrq->e); i++)
+			mult *= 10;
+
+		for (i = 1; i <= 14; i++)
+			if (fwrq->m == acx_channel_freq[i - 1] * mult)
+				channel = i;
+	}
+
+	if (channel > 14) {
+		result = -EINVAL;
+		goto end;
+	}
+
+	acx_sem_lock(priv);
+
+	priv->channel = channel;
+	/* hmm, the following code part is strange, but this is how
+	 * it was being done before... */
+	acxlog(L_IOCTL, "Changing to channel %d\n", channel);
+	SET_BIT(priv->set_mask, GETSET_CHANNEL);
+
+	result = -EINPROGRESS; /* need to call commit handler */
+
+	acx_sem_unlock(priv);
+end:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/***********************************************************************
+*/
+static inline int
+acx_ioctl_get_freq(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_freq *fwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	fwrq->e = 0;
+	fwrq->m = priv->channel;
+	return OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_set_mode
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_set_mode(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	u32 *uwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int result;
+
+	FN_ENTER;
+
+	acx_sem_lock(priv);
+
+	switch (*uwrq) {
+	case IW_MODE_AUTO:
+		priv->mode = ACX_MODE_OFF;
+		break;
+#if WIRELESS_EXT > 14
+	case IW_MODE_MONITOR:
+		priv->mode = ACX_MODE_MONITOR;
+		break;
+#endif /* WIRELESS_EXT > 14 */
+	case IW_MODE_ADHOC:
+		priv->mode = ACX_MODE_0_ADHOC;
+		break;
+	case IW_MODE_INFRA:
+		priv->mode = ACX_MODE_2_STA;
+		break;
+	case IW_MODE_MASTER:
+		printk("acx: master mode (HostAP) is very, very "
+			"experimental! It might work partially, but "
+			"better get prepared for nasty surprises "
+			"at any time\n");
+		priv->mode = ACX_MODE_3_AP;
+		break;
+	case IW_MODE_REPEAT:
+	case IW_MODE_SECOND:
+	default:
+		result = -EOPNOTSUPP;
+		goto end_unlock;
+	}
+
+	SET_BIT(priv->set_mask, GETSET_MODE);
+	result = -EINPROGRESS;
+
+end_unlock:
+	acx_sem_unlock(priv);
+
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/***********************************************************************
+*/
+static int
+acx_ioctl_get_mode(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	u32 *uwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int result = 0;
+
+	switch (priv->mode) {
+	case ACX_MODE_OFF:
+		*uwrq = IW_MODE_AUTO; break;
+#if WIRELESS_EXT > 14
+	case ACX_MODE_MONITOR:
+		*uwrq = IW_MODE_MONITOR; break;
+#endif /* WIRELESS_EXT > 14 */
+	case ACX_MODE_0_ADHOC:
+		*uwrq = IW_MODE_ADHOC; break;
+	case ACX_MODE_2_STA:
+		*uwrq = IW_MODE_INFRA; break;
+	case ACX_MODE_3_AP:
+		*uwrq = IW_MODE_MASTER; break;
+	default:
+		result = -EOPNOTSUPP;
+	}
+	return result;
+}
+
+
+/***********************************************************************
+*/
+static int
+acx_ioctl_set_sens(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+
+	acx_sem_lock(priv);
+
+	priv->sensitivity = (1 == vwrq->disabled) ? 0 : vwrq->value;
+	SET_BIT(priv->set_mask, GETSET_SENSITIVITY);
+
+	acx_sem_unlock(priv);
+
+	return -EINPROGRESS;
+}
+
+
+/***********************************************************************
+*/
+static int
+acx_ioctl_get_sens(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+
+	/* acx_sem_lock(priv); */
+
+	vwrq->value = priv->sensitivity;
+	vwrq->disabled = (vwrq->value == 0);
+	vwrq->fixed = 1;
+
+	/* acx_sem_unlock(priv); */
+
+	return OK;
+}
+
+
+/*------------------------------------------------------------------------------
+ * acx_ioctl_set_ap
+ *
+ * Sets the MAC address of the AP to associate with
+ *
+ * Call context: Process
+ * STATUS: NEW
+ *----------------------------------------------------------------------------*/
+static int
+acx_ioctl_set_ap(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct sockaddr *awrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int result = 0;
+	const u8 *ap;
+
+	FN_ENTER;
+	if (NULL == awrq) {
+		result = -EFAULT;
+		goto end;
+	}
+	if (ARPHRD_ETHER != awrq->sa_family) {
+		result = -EINVAL;
+		goto end;
+	}
+
+	ap = awrq->sa_data;
+	acxlog_mac(L_IOCTL, "Set AP=", ap, "\n");
+
+	MAC_COPY(priv->ap, ap);
+
+	/* We want to start rescan in managed or ad-hoc mode,
+	** otherwise just set priv->ap.
+	** "iwconfig <if> ap <mac> mode managed": we must be able
+	** to set ap _first_ and _then_ set mode */
+	switch (priv->mode) {
+	case ACX_MODE_0_ADHOC:
+	case ACX_MODE_2_STA:
+		/* FIXME: if there is a convention on what zero AP means,
+		** please add a comment about that. I don't know of any --vda */
+		if (mac_is_zero(ap)) {
+			/* "off" == 00:00:00:00:00:00 */
+			MAC_BCAST(priv->ap);
+			acxlog(L_IOCTL, "Not reassociating\n");
+		} else {
+			acxlog(L_IOCTL, "Forcing reassociation\n");
+			SET_BIT(priv->set_mask, GETSET_RESCAN);
+		}
+		break;
+	}
+	result = -EINPROGRESS;
+end:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/***********************************************************************
+*/
+static int
+acx_ioctl_get_ap(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct sockaddr *awrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+
+	if (ACX_STATUS_4_ASSOCIATED == priv->status) {
+		/* as seen in Aironet driver, airo.c */
+		MAC_COPY(awrq->sa_data, priv->bssid);
+	} else {
+		MAC_ZERO(awrq->sa_data);
+	}
+	awrq->sa_family = ARPHRD_ETHER;
+	return OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_get_aplist
+*
+* Comment: deprecated in favour of iwscan.
+* We simply return the list of currently available stations in range,
+* don't do a new scan.
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_get_aplist(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	struct sockaddr *address = (struct sockaddr *) extra;
+	struct iw_quality qual[IW_MAX_AP];
+	int i, cur;
+	int result = OK;
+
+	FN_ENTER;
+
+	/* we have AP list only in STA mode */
+	if (ACX_MODE_2_STA != priv->mode) {
+		result = -EOPNOTSUPP;
+		goto end;
+	}
+
+	cur = 0;
+	for (i = 0; i < VEC_SIZE(priv->sta_list); i++) {
+		struct client *bss = &priv->sta_list[i];
+		if (!bss->used) continue;
+		MAC_COPY(address[cur].sa_data, bss->bssid);
+		address[cur].sa_family = ARPHRD_ETHER;
+		qual[cur].level = bss->sir;
+		qual[cur].noise = bss->snr;
+#ifndef OLD_QUALITY
+		qual[cur].qual = acx_signal_determine_quality(qual[cur].level,
+						    qual[cur].noise);
+#else
+		qual[cur].qual = (qual[cur].noise <= 100) ?
+			       100 - qual[cur].noise : 0;
+#endif
+		/* no scan: level/noise/qual not updated: */
+		qual[cur].updated = 0;
+		cur++;
+	}
+	if (cur) {
+		dwrq->flags = 1;
+		memcpy(extra + sizeof(struct sockaddr)*cur, &qual,
+				sizeof(struct iw_quality)*cur);
+	}
+	dwrq->length = cur;
+end:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/***********************************************************************
+*/
+static int
+acx_ioctl_set_scan(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int result;
+
+	FN_ENTER;
+
+	acx_sem_lock(priv);
+
+	/* don't start scan if device is not up yet */
+	if (!(priv->dev_state_mask & ACX_STATE_IFACE_UP)) {
+		result = -EAGAIN;
+		goto end_unlock;
+	}
+
+	/* This is NOT a rescan for new AP!
+	** Do not use SET_BIT(GETSET_RESCAN); */
+	acx_s_cmd_start_scan(priv);
+	result = OK;
+
+end_unlock:
+	acx_sem_unlock(priv);
+/* end: */
+	FN_EXIT1(result);
+	return result;
+}
+
+
+#if WIRELESS_EXT > 13
+/***********************************************************************
+** acx_s_scan_add_station
+*/
+/* helper. not sure wheter it's really a _s_leeping fn */
+static char*
+acx_s_scan_add_station(
+	wlandevice_t *priv,
+	char *ptr,
+	char *end_buf,
+	struct client *bss)
+{
+	struct iw_event iwe;
+	char *ptr_rate;
+
+	FN_ENTER;
+
+	/* MAC address has to be added first */
+	iwe.cmd = SIOCGIWAP;
+	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+	MAC_COPY(iwe.u.ap_addr.sa_data, bss->bssid);
+	acxlog_mac(L_IOCTL, "scan, station address: ", bss->bssid, "\n");
+	ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_ADDR_LEN);
+
+	/* Add ESSID */
+	iwe.cmd = SIOCGIWESSID;
+	iwe.u.data.length = bss->essid_len;
+	iwe.u.data.flags = 1;
+	acxlog(L_IOCTL, "scan, essid: %s\n", bss->essid);
+	ptr = iwe_stream_add_point(ptr, end_buf, &iwe, bss->essid);
+
+	/* Add mode */
+	iwe.cmd = SIOCGIWMODE;
+	if (bss->cap_info & (WF_MGMT_CAP_ESS | WF_MGMT_CAP_IBSS)) {
+		if (bss->cap_info & WF_MGMT_CAP_ESS)
+			iwe.u.mode = IW_MODE_MASTER;
+		else
+			iwe.u.mode = IW_MODE_ADHOC;
+		acxlog(L_IOCTL, "scan, mode: %d\n", iwe.u.mode);
+		ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_UINT_LEN);
+	}
+
+	/* Add frequency */
+	iwe.cmd = SIOCGIWFREQ;
+	iwe.u.freq.m = acx_channel_freq[bss->channel - 1] * 100000;
+	iwe.u.freq.e = 1;
+	acxlog(L_IOCTL, "scan, frequency: %d\n", iwe.u.freq.m);
+	ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_FREQ_LEN);
+
+	/* Add link quality */
+	iwe.cmd = IWEVQUAL;
+	/* FIXME: these values should be expressed in dBm, but we don't know
+	 * how to calibrate it yet */
+	iwe.u.qual.level = bss->sir;
+	iwe.u.qual.noise = bss->snr;
+#ifndef OLD_QUALITY
+	iwe.u.qual.qual = acx_signal_determine_quality(iwe.u.qual.level,
+							iwe.u.qual.noise);
+#else
+	iwe.u.qual.qual = (iwe.u.qual.noise <= 100) ?
+				100 - iwe.u.qual.noise : 0;
+#endif
+	iwe.u.qual.updated = 7;
+	acxlog(L_IOCTL, "scan, link quality: %d/%d/%d\n",
+			iwe.u.qual.level, iwe.u.qual.noise, iwe.u.qual.qual);
+	ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_QUAL_LEN);
+
+	/* Add encryption */
+	iwe.cmd = SIOCGIWENCODE;
+	if (bss->cap_info & WF_MGMT_CAP_PRIVACY)
+		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+	else
+		iwe.u.data.flags = IW_ENCODE_DISABLED;
+	iwe.u.data.length = 0;
+	acxlog(L_IOCTL, "scan, encryption flags: %X\n", iwe.u.data.flags);
+	ptr = iwe_stream_add_point(ptr, end_buf, &iwe, bss->essid);
+
+	/* add rates */
+	iwe.cmd = SIOCGIWRATE;
+	iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+	ptr_rate = ptr + IW_EV_LCP_LEN;
+
+	{
+	u16 rate = bss->rate_cap;
+	const u8* p = bitpos2ratebyte;
+	while (rate) {
+		if (rate & 1) {
+			iwe.u.bitrate.value = *p * 500000; /* units of 500kb/s */
+			acxlog(L_IOCTL, "scan, rate: %d\n", iwe.u.bitrate.value);
+			ptr = iwe_stream_add_value(ptr, ptr_rate, end_buf, &iwe, IW_EV_PARAM_LEN);
+		}
+		rate >>= 1;
+		p++;
+	}}
+
+	if ((ptr_rate - ptr) > (ptrdiff_t)IW_EV_LCP_LEN)
+		ptr = ptr_rate;
+
+	/* drop remaining station data items for now */
+
+	FN_EXIT0;
+	return ptr;
+}
+
+
+/***********************************************************************
+ * acx_ioctl_get_scan
+ */
+static int
+acx_ioctl_get_scan(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	char *ptr = extra;
+	int i;
+	int result = OK;
+
+	FN_ENTER;
+
+	acx_sem_lock(priv);
+
+	/* no scan available if device is not up yet */
+	if (!(priv->dev_state_mask & ACX_STATE_IFACE_UP)) {
+		acxlog(L_IOCTL, "iface not up yet\n");
+		result = -EAGAIN;
+		goto end_unlock;
+	}
+#if 0 /* Why is this needed? If needed, add a comment */
+	if (priv->scan_start && time_before(jiffies, priv->scan_start + 3*HZ)) {
+		acxlog(L_IOCTL, "scan in progress, no results yet\n");
+		result = -EAGAIN;
+		goto end_unlock;
+	}
+#endif
+
+#if ENODATA_TO_BE_USED_AFTER_SCAN_ERROR_ONLY
+	if (priv->bss_table_count == 0)	{
+		/* no stations found */
+		result = -ENODATA;
+		goto end_unlock;
+	}
+#endif
+
+	for (i = 0; i < VEC_SIZE(priv->sta_list); i++) {
+		struct client *bss = &priv->sta_list[i];
+		if (!bss->used) continue;
+		ptr = acx_s_scan_add_station(priv, ptr,
+			extra + IW_SCAN_MAX_DATA, bss);
+	}
+	dwrq->length = ptr - extra;
+	dwrq->flags = 0;
+
+end_unlock:
+	acx_sem_unlock(priv);
+/* end: */
+	FN_EXIT1(result);
+	return result;
+}
+#endif /* WIRELESS_EXT > 13 */
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_set_essid
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_set_essid(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int len = dwrq->length;
+	int result;
+
+	FN_ENTER;
+
+	acxlog(L_IOCTL, "Set ESSID '%*s', length %d, flags 0x%04X\n",
+					len, extra, len, dwrq->flags);
+
+	if (len < 0) {
+		result = -EINVAL;
+		goto end;
+	}
+
+	acx_sem_lock(priv);
+
+	/* ESSID disabled? */
+	if (0 == dwrq->flags) {
+		priv->essid_active = 0;
+
+	} else {
+		if (dwrq->length > IW_ESSID_MAX_SIZE+1)	{
+			result = -E2BIG;
+			goto end_unlock;
+		}
+
+		if (len > sizeof(priv->essid))
+			len = sizeof(priv->essid);
+		memcpy(priv->essid, extra, len-1);
+		priv->essid[len-1] = '\0';
+		/* Paranoia: just in case there is a '\0'... */
+		priv->essid_len = strlen(priv->essid);
+		priv->essid_active = 1;
+	}
+
+	SET_BIT(priv->set_mask, GETSET_RESCAN);
+
+	result = -EINPROGRESS;
+
+end_unlock:
+	acx_sem_unlock(priv);
+end:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/***********************************************************************
+*/
+static int
+acx_ioctl_get_essid(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+
+	dwrq->flags = priv->essid_active;
+	if (priv->essid_active)	{
+		memcpy(extra, priv->essid, priv->essid_len);
+		extra[priv->essid_len] = '\0';
+		dwrq->length = priv->essid_len + 1;
+		dwrq->flags = 1;
+	}
+	return OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_update_client_rates
+*----------------------------------------------------------------*/
+static void
+acx_l_update_client_rates(wlandevice_t *priv, u16 rate)
+{
+	int i;
+	for (i = 0; i < VEC_SIZE(priv->sta_list); i++) {
+		client_t *clt = &priv->sta_list[i];
+		if (!clt->used)	continue;
+		clt->rate_cfg = (clt->rate_cap & rate);
+		if (!clt->rate_cfg) {
+			/* no compatible rates left: kick client */
+			acxlog_mac(L_ASSOC, "client ",clt->address," kicked: "
+				"rates are not compatible anymore\n");
+			acx_l_sta_list_del(priv, clt);
+			continue;
+		}
+		clt->rate_cur &= clt->rate_cfg;
+		if (!clt->rate_cur) {
+			/* current rate become invalid, choose a valid one */
+			clt->rate_cur = 1 << lowest_bit(clt->rate_cfg);
+		}
+		clt->fallback_count = clt->stepup_count = 0;
+		clt->ignore_count = 16;
+	}
+	switch (priv->mode) {
+	case ACX_MODE_2_STA:
+		if (priv->ap_client && !priv->ap_client->used) {
+			/* Owwww... we kicked our AP!! :) */
+			SET_BIT(priv->set_mask, GETSET_RESCAN);
+		}
+	}
+}
+
+
+/***********************************************************************
+*/
+/* maps bits from acx111 rate to rate in Mbits */
+static const unsigned int
+acx111_rate_tbl[] = {
+     1000000, /* 0 */
+     2000000, /* 1 */
+     5500000, /* 2 */
+     6000000, /* 3 */
+     9000000, /* 4 */
+    11000000, /* 5 */
+    12000000, /* 6 */
+    18000000, /* 7 */
+    22000000, /* 8 */
+    24000000, /* 9 */
+    36000000, /* 10 */
+    48000000, /* 11 */
+    54000000, /* 12 */
+      500000, /* 13, should not happen */
+      500000, /* 14, should not happen */
+      500000, /* 15, should not happen */
+};
+
+/***********************************************************************
+ * acx_ioctl_set_rate
+ */
+static int
+acx_ioctl_set_rate(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	u16 txrate_cfg = 1;
+	unsigned long flags;
+	int autorate;
+	int result = -EINVAL;
+
+	FN_ENTER;
+	acxlog(L_IOCTL,
+	       "rate %d fixed 0x%X disabled 0x%X flags 0x%X\n",
+	       vwrq->value, vwrq->fixed, vwrq->disabled, vwrq->flags);
+
+	if ((0 == vwrq->fixed) || (1 == vwrq->fixed)) {
+		int i = VEC_SIZE(acx111_rate_tbl)-1;
+		if (vwrq->value == -1)
+			/* "iwconfig rate auto" --> choose highest */
+			vwrq->value = (CHIPTYPE_ACX100 == priv->chip_type) ?
+						22000000 : 54000000;
+		while (i >= 0) {
+			if (vwrq->value == acx111_rate_tbl[i]) {
+				txrate_cfg <<= i;
+				i = 0;
+				break;
+			}
+			i--;
+		}
+		if (i == -1) { /* no matching rate */
+			result = -EINVAL;
+			goto end;
+		}
+	} else {	/* rate N, N<1000 (driver specific): we don't use this */
+		result = -EOPNOTSUPP;
+		goto end;
+	}
+	/* now: only one bit is set in txrate_cfg, corresponding to
+	** indicated rate */
+
+	autorate = (vwrq->fixed == 0) && (RATE111_1 != txrate_cfg);
+	if (autorate) {
+		/* convert 00100000 -> 00111111 */
+		txrate_cfg = (txrate_cfg<<1)-1;
+	}
+
+	if (CHIPTYPE_ACX100 == priv->chip_type) {
+		txrate_cfg &= RATE111_ACX100_COMPAT;
+		if (!txrate_cfg) {
+			result = -ENOTSUPP; /* rate is not supported by acx100 */
+			goto end;
+		}
+	}
+
+	acx_sem_lock(priv);
+	acx_lock(priv, flags);
+
+	priv->rate_auto = autorate;
+	priv->rate_oper = txrate_cfg;
+	priv->rate_basic = txrate_cfg;
+	/* only do that in auto mode, non-auto will be able to use
+	 * one specific Tx rate only anyway */
+	if (autorate) {
+		/* only use 802.11b base rates, for standard 802.11b H/W
+		 * compatibility */
+		priv->rate_basic &= RATE111_80211B_COMPAT;
+	}
+	priv->rate_bcast = 1 << lowest_bit(txrate_cfg);
+	if (CHIPTYPE_ACX100 == priv->chip_type)
+		priv->rate_bcast100 = acx_rate111to100(priv->rate_bcast);
+	acx_l_update_ratevector(priv);
+	acx_l_update_client_rates(priv, txrate_cfg);
+
+	/* Do/don't do tx rate fallback; beacon contents and rate */
+	SET_BIT(priv->set_mask, SET_RATE_FALLBACK|SET_TEMPLATES);
+	result = -EINPROGRESS;
+
+	acx_unlock(priv, flags);
+	acx_sem_unlock(priv);
+end:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_get_rate
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_get_rate(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	/* TODO: remember rate of last tx, show it. think about multiple peers... */
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	vwrq->value = acx111_rate_tbl[highest_bit(priv->rate_oper)];
+	vwrq->fixed = !priv->rate_auto;
+	vwrq->disabled = 0;
+	return OK;
+}
+
+static int
+acx_ioctl_set_rts(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int val = vwrq->value;
+
+	if (vwrq->disabled)
+		val = 2312;
+	if ((val < 0) || (val > 2312))
+		return -EINVAL;
+
+	priv->rts_threshold = val;
+	return OK;
+}
+
+static inline int
+acx_ioctl_get_rts(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+
+	vwrq->value = priv->rts_threshold;
+	vwrq->disabled = (vwrq->value >= 2312);
+	vwrq->fixed = 1;
+	return OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_set_encode
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_set_encode(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int index;
+	int result;
+
+	FN_ENTER;
+	acxlog(L_IOCTL,
+	       "Set Encoding flags=0x%04X, size=%d, key: %s\n",
+	       dwrq->flags, dwrq->length, extra ? "set" : "No key");
+
+	acx_sem_lock(priv);
+
+	index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+
+	if (dwrq->length > 0) {
+		/* if index is 0 or invalid, use default key */
+		if ((index < 0) || (index > 3))
+			index = (int)priv->wep_current_index;
+
+		if (0 == (dwrq->flags & IW_ENCODE_NOKEY)) {
+			if (dwrq->length > 29)
+				dwrq->length = 29; /* restrict it */
+
+			if (dwrq->length > 13)
+				priv->wep_keys[index].size = 29; /* 29*8 == 232, WEP256 */
+			else
+			if (dwrq->length > 5)
+				priv->wep_keys[index].size = 13; /* 13*8 == 104bit, WEP128 */
+			else
+			if (dwrq->length > 0)
+				priv->wep_keys[index].size = 5; /* 5*8 == 40bit, WEP64 */
+			else
+				/* disable key */
+				priv->wep_keys[index].size = 0;
+
+			memset(priv->wep_keys[index].key, 0, sizeof(priv->wep_keys[index].key));
+			memcpy(priv->wep_keys[index].key, extra, dwrq->length);
+		}
+
+	} else {
+		/* set transmit key */
+		if ((index >= 0) && (index <= 3))
+			priv->wep_current_index = index;
+		else
+			if (0 == (dwrq->flags & IW_ENCODE_MODE)) {
+				/* complain if we were not just setting
+				 * the key mode */
+				result =  -EINVAL;
+				goto end_unlock;
+			}
+	}
+
+	priv->wep_enabled = !(dwrq->flags & IW_ENCODE_DISABLED);
+
+	if (dwrq->flags & IW_ENCODE_OPEN) {
+		priv->auth_alg = WLAN_AUTH_ALG_OPENSYSTEM;
+		priv->wep_restricted = 0;
+
+	} else if (dwrq->flags & IW_ENCODE_RESTRICTED) {
+		priv->auth_alg = WLAN_AUTH_ALG_SHAREDKEY;
+		priv->wep_restricted = 1;
+	}
+
+	/* set flag to make sure the card WEP settings get updated */
+	SET_BIT(priv->set_mask, GETSET_WEP);
+
+	acxlog(L_IOCTL, "len=%d, key at 0x%p, flags=0x%X\n",
+	       dwrq->length, extra,
+	       dwrq->flags);
+
+	for (index = 0; index <= 3; index++) {
+		if (priv->wep_keys[index].size) {
+			acxlog(L_IOCTL,
+				"index=%d, size=%d, key at 0x%p\n",
+				priv->wep_keys[index].index,
+				(int) priv->wep_keys[index].size,
+				priv->wep_keys[index].key);
+		}
+	}
+	result = -EINPROGRESS;
+
+end_unlock:
+	acx_sem_unlock(priv);
+
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_get_encode
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_get_encode(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+
+	if (priv->wep_enabled == 0) {
+		dwrq->flags = IW_ENCODE_DISABLED;
+
+	} else {
+		if ((index < 0) || (index > 3))
+			index = (int)priv->wep_current_index;
+
+		dwrq->flags =
+			(priv->wep_restricted == 1) ? IW_ENCODE_RESTRICTED : IW_ENCODE_OPEN;
+		dwrq->length = priv->wep_keys[index].size;
+
+		memcpy(extra,
+			     priv->wep_keys[index].key,
+			     priv->wep_keys[index].size);
+	}
+
+	/* set the current index */
+	SET_BIT(dwrq->flags, index + 1);
+
+	acxlog(L_IOCTL, "len=%d, key=%p, flags=0x%X\n",
+	       dwrq->length, dwrq->pointer,
+	       dwrq->flags);
+
+	return OK;
+}
+
+
+/***********************************************************************
+*/
+static int
+acx_ioctl_set_power(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+
+	acxlog(L_IOCTL, "Set 802.11 Power Save flags=0x%04X\n", vwrq->flags);
+	if (vwrq->disabled) {
+		CLEAR_BIT(priv->ps_wakeup_cfg, PS_CFG_ENABLE);
+		SET_BIT(priv->set_mask, GETSET_POWER_80211);
+		return -EINPROGRESS;
+	}
+	if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
+		u16 ps_timeout = (vwrq->value * 1024) / 1000;
+
+		if (ps_timeout > 255)
+			ps_timeout = 255;
+		acxlog(L_IOCTL, "setting PS timeout value to %d time units "
+				"due to %dus\n", ps_timeout, vwrq->value);
+		priv->ps_hangover_period = ps_timeout;
+	} else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
+		u16 ps_periods = vwrq->value / 1000000;
+
+		if (ps_periods > 255)
+			ps_periods = 255;
+		acxlog(L_IOCTL, "setting PS period value to %d periods "
+				"due to %dus\n", ps_periods, vwrq->value);
+		priv->ps_listen_interval = ps_periods;
+		CLEAR_BIT(priv->ps_wakeup_cfg, PS_CFG_WAKEUP_MODE_MASK);
+		SET_BIT(priv->ps_wakeup_cfg, PS_CFG_WAKEUP_EACH_ITVL);
+	}
+	switch (vwrq->flags & IW_POWER_MODE) {
+		/* FIXME: are we doing the right thing here? */
+		case IW_POWER_UNICAST_R:
+			CLEAR_BIT(priv->ps_options, PS_OPT_STILL_RCV_BCASTS);
+			break;
+		case IW_POWER_MULTICAST_R:
+			SET_BIT(priv->ps_options, PS_OPT_STILL_RCV_BCASTS);
+			break;
+		case IW_POWER_ALL_R:
+			SET_BIT(priv->ps_options, PS_OPT_STILL_RCV_BCASTS);
+			break;
+		case IW_POWER_ON:
+			break;
+		default:
+			acxlog(L_IOCTL, "unknown PS mode\n");
+			return -EINVAL;
+	}
+
+	SET_BIT(priv->ps_wakeup_cfg, PS_CFG_ENABLE);
+	SET_BIT(priv->set_mask, GETSET_POWER_80211);
+
+	return -EINPROGRESS;
+
+}
+
+
+/***********************************************************************
+*/
+static int
+acx_ioctl_get_power(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+
+	acxlog(L_IOCTL, "Get 802.11 Power Save flags = 0x%04X\n", vwrq->flags);
+	vwrq->disabled = ((priv->ps_wakeup_cfg & PS_CFG_ENABLE) == 0);
+	if (vwrq->disabled)
+		return OK;
+	if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
+		vwrq->value = priv->ps_hangover_period * 1000 / 1024;
+		vwrq->flags = IW_POWER_TIMEOUT;
+	} else {
+		vwrq->value = priv->ps_listen_interval * 1000000;
+		vwrq->flags = IW_POWER_PERIOD|IW_POWER_RELATIVE;
+	}
+	if (priv->ps_options & PS_OPT_STILL_RCV_BCASTS)
+		SET_BIT(vwrq->flags, IW_POWER_ALL_R);
+	else
+		SET_BIT(vwrq->flags, IW_POWER_UNICAST_R);
+
+	return OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_get_txpow
+*----------------------------------------------------------------*/
+static inline int
+acx_ioctl_get_txpow(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+
+	vwrq->flags = IW_TXPOW_DBM;
+	vwrq->disabled = 0;
+	vwrq->fixed = 1;
+	vwrq->value = priv->tx_level_dbm;
+
+	acxlog(L_IOCTL, "get txpower:%d dBm\n", priv->tx_level_dbm);
+
+	return OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_set_txpow
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_set_txpow(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int result;
+
+	FN_ENTER;
+	acxlog(L_IOCTL, "set txpower:%d, disabled:%d, flags:0x%04X\n",
+			vwrq->value, vwrq->disabled, vwrq->flags);
+
+	acx_sem_lock(priv);
+
+	if (vwrq->disabled != priv->tx_disabled) {
+		SET_BIT(priv->set_mask, GETSET_TX); /* Tx status needs update later */
+	}
+
+	priv->tx_disabled = vwrq->disabled;
+	if (vwrq->value == -1) {
+		if (vwrq->disabled) {
+			priv->tx_level_dbm = 0;
+			acxlog(L_IOCTL, "disable radio tx\n");
+		} else {
+			priv->tx_level_auto = 1;
+			acxlog(L_IOCTL, "set tx power auto (NIY)\n");
+		}
+	} else {
+		priv->tx_level_dbm = vwrq->value <= 20 ? vwrq->value : 20;
+		priv->tx_level_auto = 0;
+		acxlog(L_IOCTL, "set txpower=%d dBm\n", priv->tx_level_dbm);
+	}
+	SET_BIT(priv->set_mask, GETSET_TXPOWER);
+
+	result = -EINPROGRESS;
+
+	acx_sem_unlock(priv);
+
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_get_range
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_get_range(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra)
+{
+	if (dwrq->pointer != NULL) {
+		struct iw_range *range = (struct iw_range *)extra;
+		wlandevice_t *priv = acx_netdev_priv(dev);
+		unsigned int i;
+
+		dwrq->length = sizeof(struct iw_range);
+		memset(range, 0, sizeof(struct iw_range));
+		range->num_channels = 0;
+		for (i = 1; i <= 14; i++) {
+			if (priv->reg_dom_chanmask & (1 << (i - 1))) {
+				range->freq[range->num_channels].i = i;
+				range->freq[range->num_channels].m = acx_channel_freq[i - 1] * 100000;
+				range->freq[range->num_channels++].e = 1; /* MHz values */
+			}
+		}
+		range->num_frequency = range->num_channels;
+
+		range->min_rts = 0;
+		range->max_rts = 2312;
+		/* range->min_frag = 256;
+		 * range->max_frag = 2312;
+		 */
+
+		range->encoding_size[0] = 5;
+		range->encoding_size[1] = 13;
+		range->encoding_size[2] = 29;
+		range->num_encoding_sizes = 3;
+		range->max_encoding_tokens = 4;
+
+		range->min_pmp = 0;
+		range->max_pmp = 5000000;
+		range->min_pmt = 0;
+		range->max_pmt = 65535 * 1000;
+		range->pmp_flags = IW_POWER_PERIOD;
+		range->pmt_flags = IW_POWER_TIMEOUT;
+		range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;
+
+		for (i = 0; i <= IW_MAX_TXPOWER - 1; i++)
+			range->txpower[i] = 20 * i / (IW_MAX_TXPOWER - 1);
+		range->num_txpower = IW_MAX_TXPOWER;
+		range->txpower_capa = IW_TXPOW_DBM;
+
+		range->we_version_compiled = WIRELESS_EXT;
+		range->we_version_source = 0x9;
+
+		range->retry_capa = IW_RETRY_LIMIT;
+		range->retry_flags = IW_RETRY_LIMIT;
+		range->min_retry = 1;
+		range->max_retry = 255;
+
+		range->r_time_flags = IW_RETRY_LIFETIME;
+		range->min_r_time = 0;
+		/* FIXME: lifetime ranges and orders of magnitude are strange?? */
+		range->max_r_time = 65535;
+
+		if (CHIPTYPE_ACX111 == priv->chip_type)
+			range->sensitivity = 3;
+		else
+			range->sensitivity = 255;
+		/* TODO: USB really should have range->sensitivity = 0; */
+
+		for (i=0; i < priv->rate_supported_len; i++) {
+			range->bitrate[i] = (priv->rate_supported[i] & ~0x80) * 500000;
+			/* never happens, but keep it, to be safe: */
+			if (range->bitrate[i] == 0)
+				break;
+		}
+		range->num_bitrates = i;
+
+		range->max_qual.qual = 100;
+		range->max_qual.level = 100;
+		range->max_qual.noise = 100;
+		/* TODO: better values */
+		range->avg_qual.qual = 90;
+		range->avg_qual.level = 80;
+		range->avg_qual.noise = 2;
+	}
+
+	return OK;
+}
+
+
+/*================================================================*/
+/* Private functions						  */
+/*================================================================*/
+
+#if WIRELESS_EXT < 13
+/*----------------------------------------------------------------
+* acx_ioctl_get_iw_priv
+*
+*
+* STATUS: FINISHED
+* Comment: I added the monitor mode and changed the stuff below
+* to look more like the orinoco driver
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_get_iw_priv(struct iwreq *iwr)
+{
+	int result = -EINVAL;
+
+	if (!iwr->u.data.pointer)
+		return -EINVAL;
+	result = verify_area(VERIFY_WRITE, iwr->u.data.pointer,
+			sizeof(acx_ioctl_private_args));
+	if (result)
+		return result;
+
+	iwr->u.data.length = VEC_SIZE(acx_ioctl_private_args);
+	if (copy_to_user(iwr->u.data.pointer, acx_ioctl_private_args, sizeof(acx_ioctl_private_args)) != 0)
+		result = -EFAULT;
+
+	return result;
+}
+#endif
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_get_nick
+*----------------------------------------------------------------*/
+static inline int
+acx_ioctl_get_nick(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+
+	/* FIXME : consider spinlock here */
+	strcpy(extra, priv->nick);
+	/* FIXME : consider spinlock here */
+
+	dwrq->length = strlen(extra) + 1;
+
+	return OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_set_nick
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_set_nick(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_point *dwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int result;
+
+	FN_ENTER;
+
+	acx_sem_lock(priv);
+
+	if (dwrq->length > IW_ESSID_MAX_SIZE + 1) {
+		result = -E2BIG;
+		goto end_unlock;
+	}
+
+	/* extra includes trailing \0, so it's ok */
+	strcpy(priv->nick, extra);
+	result = OK;
+
+end_unlock:
+	acx_sem_unlock(priv);
+
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/*------------------------------------------------------------------------------
+ * acx_ioctl_get_retry
+ *----------------------------------------------------------------------------*/
+static int
+acx_ioctl_get_retry(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	unsigned int type = vwrq->flags & IW_RETRY_TYPE;
+	unsigned int modifier = vwrq->flags & IW_RETRY_MODIFIER;
+	int result;
+
+	acx_sem_lock(priv);
+
+	/* return the short retry number by default */
+	if (type == IW_RETRY_LIFETIME) {
+		vwrq->flags = IW_RETRY_LIFETIME;
+		vwrq->value = priv->msdu_lifetime;
+	} else if (modifier == IW_RETRY_MAX) {
+		vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+		vwrq->value = priv->long_retry;
+	} else {
+		vwrq->flags = IW_RETRY_LIMIT;
+		if (priv->long_retry != priv->short_retry)
+			SET_BIT(vwrq->flags, IW_RETRY_MIN);
+		vwrq->value = priv->short_retry;
+	}
+
+	/* can't be disabled */
+	vwrq->disabled = (u8)0;
+	result = OK;
+
+	acx_sem_unlock(priv);
+
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_set_retry
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_set_retry(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int result;
+
+	FN_ENTER;
+
+	if (!vwrq) {
+		result = -EFAULT;
+		goto end;
+	}
+	if (vwrq->disabled) {
+		result = -EINVAL;
+		goto end;
+	}
+
+	acx_sem_lock(priv);
+
+	result = -EINVAL;
+	if (IW_RETRY_LIMIT == (vwrq->flags & IW_RETRY_TYPE)) {
+		printk("old retry limits: short %d long %d\n",
+				priv->short_retry, priv->long_retry);
+		if (vwrq->flags & IW_RETRY_MAX) {
+			priv->long_retry = vwrq->value;
+		} else if (vwrq->flags & IW_RETRY_MIN) {
+			priv->short_retry = vwrq->value;
+		} else {
+			/* no modifier: set both */
+			priv->long_retry = vwrq->value;
+			priv->short_retry = vwrq->value;
+		}
+		printk("new retry limits: short %d long %d\n",
+				priv->short_retry, priv->long_retry);
+		SET_BIT(priv->set_mask, GETSET_RETRY);
+		result = -EINPROGRESS;
+	}
+	else if (vwrq->flags & IW_RETRY_LIFETIME) {
+		priv->msdu_lifetime = vwrq->value;
+		printk("new MSDU lifetime: %d\n", priv->msdu_lifetime);
+		SET_BIT(priv->set_mask, SET_MSDU_LIFETIME);
+		result = -EINPROGRESS;
+	}
+
+	acx_sem_unlock(priv);
+end:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/******************************* private ioctls ******************************/
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_set_debug
+*----------------------------------------------------------------*/
+#if ACX_DEBUG
+static int
+acx_ioctl_set_debug(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	unsigned int debug_new = *((unsigned int *)extra);
+	int result = -EINVAL;
+
+	acxlog(L_ANY, "setting debug from %04X to %04X\n", acx_debug, debug_new);
+	acx_debug = debug_new;
+
+	result = OK;
+	return result;
+
+}
+#endif
+
+/*----------------------------------------------------------------
+* acx_ioctl_list_reg_domain
+*----------------------------------------------------------------*/
+extern const u8 reg_domain_ids[];
+extern const u8 reg_domain_ids_len;
+static const char * const
+reg_domain_strings[] = {
+	" 1-11 FCC (USA)",
+	" 1-11 DOC/IC (Canada)",
+	/* BTW: WLAN use in ETSI is regulated by
+	 * ETSI standard EN 300 328-2 V1.1.2 */
+	" 1-13 ETSI (Europe)",
+	"10-11 Spain",
+	"10-13 France",
+	"   14 MKK (Japan)",
+	" 1-14 MKK1",
+	"  3-9 Israel (not all firmware versions)",
+	NULL /* needs to remain as last entry */
+};
+
+static int
+acx_ioctl_list_reg_domain(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+
+	int i = 1;
+	const char * const *entry = reg_domain_strings;
+
+	printk("dom# chan# domain/country\n");
+	while (*entry)
+		printk("%4d %s\n", i++, *entry++);
+	return OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_set_reg_domain
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_set_reg_domain(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int result;
+
+	FN_ENTER;
+
+	if ((*extra < 1) || ((size_t)*extra > reg_domain_ids_len)) {
+		result = -EINVAL;
+		goto end;
+	}
+
+	acx_sem_lock(priv);
+
+	priv->reg_dom_id = reg_domain_ids[*extra - 1];
+	SET_BIT(priv->set_mask, GETSET_REG_DOMAIN);
+
+	result = -EINPROGRESS;
+
+	acx_sem_unlock(priv);
+end:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_get_reg_domain
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_get_reg_domain(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int dom,i;
+
+	/* no locking */
+	dom = priv->reg_dom_id;
+
+	for (i=1; i <= 7; i++) {
+		if (reg_domain_ids[i-1] == dom) {
+			acxlog(L_IOCTL, "regulatory domain is currently set "
+				"to %d (0x%X): %s\n", i, dom,
+				reg_domain_strings[i-1]);
+			*extra = i;
+			break;
+		}
+	}
+
+	return OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_set_short_preamble
+*----------------------------------------------------------------*/
+static const char * const
+preamble_modes[] = {
+	"off",
+	"on",
+	"auto (peer capability dependent)",
+	"unknown mode, error"
+};
+
+static int
+acx_ioctl_set_short_preamble(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int i;
+	int result;
+
+	FN_ENTER;
+
+	if ((unsigned char)*extra > 2) {
+		result = -EINVAL;
+		goto end;
+	}
+
+	acx_sem_lock(priv);
+
+	priv->preamble_mode = (u8)*extra;
+	switch (priv->preamble_mode) {
+	case 0: /* long */
+		priv->preamble_cur = 0;
+		break;
+	case 1:
+		/* short, kick incapable peers */
+		priv->preamble_cur = 1;
+		for (i = 0; i < VEC_SIZE(priv->sta_list); i++) {
+			client_t *clt = &priv->sta_list[i];
+			if (!clt->used) continue;
+			if (!(clt->cap_info & WF_MGMT_CAP_SHORT)) {
+				clt->used = CLIENT_EMPTY_SLOT_0;
+			}
+		}
+		switch (priv->mode) {
+		case ACX_MODE_2_STA:
+			if (priv->ap_client && !priv->ap_client->used) {
+				/* We kicked our AP :) */
+				SET_BIT(priv->set_mask, GETSET_RESCAN);
+			}
+		}
+		break;
+	case 2: /* auto. short only if all peers are short-capable */
+		priv->preamble_cur = 1;
+		for (i = 0; i < VEC_SIZE(priv->sta_list); i++) {
+			client_t *clt = &priv->sta_list[i];
+			if (!clt->used) continue;
+			if (!(clt->cap_info & WF_MGMT_CAP_SHORT)) {
+				priv->preamble_cur = 0;
+				break;
+			}
+		}
+		break;
+	}
+	printk("new short preamble setting: configured %s, active %s\n",
+			preamble_modes[priv->preamble_mode],
+			preamble_modes[priv->preamble_cur]);
+	result = OK;
+
+	acx_sem_unlock(priv);
+end:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_get_short_preamble
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_get_short_preamble(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+
+	acx_sem_lock(priv);
+
+	printk("current short preamble setting: configured %s, active %s\n",
+			preamble_modes[priv->preamble_mode],
+			preamble_modes[priv->preamble_cur]);
+
+	*extra = (char)priv->preamble_mode;
+
+	acx_sem_unlock(priv);
+
+	return OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_set_antenna
+*
+* Comment: TX and RX antenna can be set separately but this function good
+*          for testing 0-4 bits
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_set_antenna(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+
+	acx_sem_lock(priv);
+
+	printk("old antenna value: 0x%02X (COMBINED bit mask)\n"
+		     "Rx antenna selection:\n"
+		     "0x00 ant. 1\n"
+		     "0x40 ant. 2\n"
+		     "0x80 full diversity\n"
+		     "0xc0 partial diversity\n"
+		     "0x0f dwell time mask (in units of us)\n"
+		     "Tx antenna selection:\n"
+		     "0x00 ant. 2\n" /* yep, those ARE reversed! */
+		     "0x20 ant. 1\n"
+		     "new antenna value: 0x%02X\n",
+		     priv->antenna, (u8)*extra);
+
+	priv->antenna = (u8)*extra;
+	SET_BIT(priv->set_mask, GETSET_ANTENNA);
+
+	acx_sem_unlock(priv);
+
+	return -EINPROGRESS;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_get_antenna
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_get_antenna(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+
+	/* no locking. it's pointless to lock a single load */
+	printk("current antenna value: 0x%02X (COMBINED bit mask)\n"
+		     "Rx antenna selection:\n"
+		     "0x00 ant. 1\n"
+		     "0x40 ant. 2\n"
+		     "0x80 full diversity\n"
+		     "0xc0 partial diversity\n"
+		     "Tx antenna selection:\n"
+		     "0x00 ant. 2\n" /* yep, those ARE reversed! */
+		     "0x20 ant. 1\n", priv->antenna);
+
+	return 0;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_set_rx_antenna
+*
+*
+* Arguments:
+*	0 = antenna1; 1 = antenna2; 2 = full diversity; 3 = partial diversity
+* Comment: Could anybody test which antenna is the external one
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_set_rx_antenna(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int result;
+
+	FN_ENTER;
+
+	if (*extra > 3) {
+		result = -EINVAL;
+		goto end;
+	}
+
+	printk("old antenna value: 0x%02X\n", priv->antenna);
+
+	acx_sem_lock(priv);
+
+	priv->antenna &= 0x3f;
+	SET_BIT(priv->antenna, (*extra << 6));
+	SET_BIT(priv->set_mask, GETSET_ANTENNA);
+	printk("new antenna value: 0x%02X\n", priv->antenna);
+	result = -EINPROGRESS;
+
+	acx_sem_unlock(priv);
+end:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_set_tx_antenna
+*
+*
+* Arguments: 0 == antenna2; 1 == antenna1;
+* Comment: Could anybody test which antenna is the external one
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_set_tx_antenna(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int result;
+
+	FN_ENTER;
+
+	if (*extra > 1) {
+		result = -EINVAL;
+		goto end;
+	}
+
+	printk("old antenna value: 0x%02X\n", priv->antenna);
+
+	acx_sem_lock(priv);
+
+	priv->antenna &= ~0x30;
+	SET_BIT(priv->antenna, ((*extra & 0x01) << 5));
+	SET_BIT(priv->set_mask, GETSET_ANTENNA);
+	printk("new antenna value: 0x%02X\n", priv->antenna);
+	result = -EINPROGRESS;
+
+	acx_sem_unlock(priv);
+end:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_wlansniff
+* STATUS: NEW
+* can we just remove this in favor of monitor mode? --vda
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_wlansniff(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	unsigned int *params = (unsigned int*)extra;
+	unsigned int enable = (unsigned int)(params[0] > 0);
+	int result;
+
+	FN_ENTER;
+
+	acx_sem_lock(priv);
+
+	/* not using printk() here, since it distorts kismet display
+	 * when printk messages activated */
+	acxlog(L_IOCTL, "setting monitor to: 0x%02X\n", params[0]);
+
+	switch (params[0]) {
+	case 0:
+		priv->netdev->type = ARPHRD_ETHER;
+		break;
+	case 1:
+		priv->netdev->type = ARPHRD_IEEE80211_PRISM;
+		break;
+	case 2:
+		priv->netdev->type = ARPHRD_IEEE80211;
+		break;
+	}
+
+	if (params[0]) {
+		priv->mode = ACX_MODE_MONITOR;
+		SET_BIT(priv->set_mask, GETSET_MODE);
+	}
+
+	if (enable) {
+		priv->channel = params[1];
+		SET_BIT(priv->set_mask, GETSET_RX);
+	}
+	result = -EINPROGRESS;
+
+	acx_sem_unlock(priv);
+
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_unknown11
+* FIXME: looks like some sort of "iwpriv kick_sta MAC" but it's broken
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_unknown11(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	unsigned long flags;
+	client_t client;
+	int result;
+
+	acx_sem_lock(priv);
+	acx_lock(priv, flags);
+
+	acx_l_transmit_disassoc(priv, &client);
+	result = OK;
+
+	acx_unlock(priv, flags);
+	acx_sem_unlock(priv);
+
+	return result;
+}
+
+
+/***********************************************************************
+** debug helper function to be able to debug various issues relatively easily
+*/
+static int
+acx_ioctl_dbg_set_masks(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	const unsigned int *params = (unsigned int*)extra;
+	int result;
+
+	acx_sem_lock(priv);
+
+	acxlog(L_IOCTL, "setting flags in settings mask: "
+			"get_mask %08X set_mask %08X\n"
+			"before: get_mask %08X set_mask %08X\n",
+			params[0], params[1],
+			priv->get_mask, priv->set_mask);
+	SET_BIT(priv->get_mask, params[0]);
+	SET_BIT(priv->set_mask, params[1]);
+	acxlog(L_IOCTL, "after: get_mask %08X set_mask %08X\n",
+			priv->get_mask, priv->set_mask);
+	result = -EINPROGRESS; /* immediately call commit handler */
+
+	acx_sem_unlock(priv);
+
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_set_rates
+*
+* This ioctl takes string parameter. Examples:
+* iwpriv wlan0 SetRates "1,2"
+*	use 1 and 2 Mbit rates, both are in basic rate set
+* iwpriv wlan0 SetRates "1,2 5,11"
+*	use 1,2,5.5,11 Mbit rates. 1 and 2 are basic
+* iwpriv wlan0 SetRates "1,2 5c,11c"
+*	same ('c' means 'CCK modulation' and it is a default for 5 and 11)
+* iwpriv wlan0 SetRates "1,2 5p,11p"
+*	use 1,2,5.5,11 Mbit, 1,2 are basic. 5 and 11 are using PBCC
+* iwpriv wlan0 SetRates "1,2,5,11 22p"
+*	use 1,2,5.5,11,22 Mbit. 1,2,5.5 and 11 are basic. 22 is using PBCC
+*	(this is the maximum acx100 can do (modulo x4 mode))
+* iwpriv wlan0 SetRates "1,2,5,11 22"
+*	same. 802.11 defines only PBCC modulation
+*	for 22 and 33 Mbit rates, so there is no ambiguity
+* iwpriv wlan0 SetRates "1,2,5,11 6o,9o,12o,18o,24o,36o,48o,54o"
+*	1,2,5.5 and 11 are basic. 11g OFDM rates are enabled but
+*	they are not in basic rate set.	22 Mbit is disabled.
+* iwpriv wlan0 SetRates "1,2,5,11 6,9,12,18,24,36,48,54"
+*	same. OFDM is default for 11g rates except 22 and 33 Mbit,
+*	thus 'o' is optional
+* iwpriv wlan0 SetRates "1,2,5,11 6d,9d,12d,18d,24d,36d,48d,54d"
+*	1,2,5.5 and 11 are basic. 11g CCK-OFDM rates are enabled
+*	(acx111 does not support CCK-OFDM, driver will reject this cmd)
+* iwpriv wlan0 SetRates "6,9,12 18,24,36,48,54"
+*	6,9,12 are basic, rest of 11g rates is enabled. Using OFDM
+*----------------------------------------------------------------*/
+#include "setrate.c"
+
+/* disallow: 33Mbit (unsupported by hw) */
+/* disallow: CCKOFDM (unsupported by hw) */
+static int
+acx111_supported(int mbit, int modulation, void *opaque)
+{
+	if (mbit==33) return -ENOTSUPP;
+	if (modulation==DOT11_MOD_CCKOFDM) return -ENOTSUPP;
+	return OK;
+}
+
+static const u16
+acx111mask[] = {
+	[DOT11_RATE_1 ] = RATE111_1 ,
+	[DOT11_RATE_2 ] = RATE111_2 ,
+	[DOT11_RATE_5 ] = RATE111_5 ,
+	[DOT11_RATE_11] = RATE111_11,
+	[DOT11_RATE_22] = RATE111_22,
+	/* [DOT11_RATE_33] = */
+	[DOT11_RATE_6 ] = RATE111_6 ,
+	[DOT11_RATE_9 ] = RATE111_9 ,
+	[DOT11_RATE_12] = RATE111_12,
+	[DOT11_RATE_18] = RATE111_18,
+	[DOT11_RATE_24] = RATE111_24,
+	[DOT11_RATE_36] = RATE111_36,
+	[DOT11_RATE_48] = RATE111_48,
+	[DOT11_RATE_54] = RATE111_54,
+};
+
+static u32
+acx111_gen_mask(int mbit, int modulation, void *opaque)
+{
+	/* lower 16 bits show selected 1, 2, CCK and OFDM rates */
+	/* upper 16 bits show selected PBCC rates */
+	u32 m = acx111mask[rate_mbit2enum(mbit)];
+	if (modulation==DOT11_MOD_PBCC)
+		return m<<16;
+	return m;
+}
+
+static int
+verify_rate(u32 rate, int chip_type)
+{
+	/* never happens. be paranoid */
+	if (!rate) return -EINVAL;
+
+	/* disallow: mixing PBCC and CCK at 5 and 11Mbit
+	** (can be supported, but needs complicated handling in tx code) */
+	if (( rate & ((RATE111_11+RATE111_5)<<16) )
+	&&  ( rate & (RATE111_11+RATE111_5) )
+	) {
+		return -ENOTSUPP;
+	}
+	if (CHIPTYPE_ACX100 == chip_type) {
+		if ( rate & ~(RATE111_ACX100_COMPAT+(RATE111_ACX100_COMPAT<<16)) )
+			return -ENOTSUPP;
+	}
+	return 0;
+}
+
+static int
+acx_ioctl_set_rates(struct net_device *dev, struct iw_request_info *info,
+		 struct iw_param *vwrq, char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	unsigned long flags;
+	int result;
+	u32 brate = 0, orate = 0; /* basic, operational rate set */
+
+	FN_ENTER;
+
+	acxlog(L_IOCTL, "set_rates %s\n", extra);
+	result = fill_ratemasks(extra, &brate, &orate,
+				acx111_supported, acx111_gen_mask, 0);
+	if (result) goto end;
+	SET_BIT(orate, brate);
+	acxlog(L_IOCTL, "brate %08X orate %08X\n", brate, orate);
+
+	result = verify_rate(brate, priv->chip_type);
+	if (result) goto end;
+	result = verify_rate(orate, priv->chip_type);
+	if (result) goto end;
+
+	acx_sem_lock(priv);
+	acx_lock(priv, flags);
+
+	priv->rate_basic = brate;
+	priv->rate_oper = orate;
+	/* TODO: ideally, we shall monitor highest basic rate
+	** which was successfully sent to every peer
+	** (say, last we checked, everybody could hear 5.5 Mbits)
+	** and use that for bcasts when we want to reach all peers.
+	** For beacons, we probably shall use lowest basic rate
+	** because we want to reach all *potential* new peers too */
+	priv->rate_bcast = 1 << lowest_bit(brate);
+	if (CHIPTYPE_ACX100 == priv->chip_type)
+		priv->rate_bcast100 = acx_rate111to100(priv->rate_bcast);
+	priv->rate_auto = !has_only_one_bit(orate);
+	acx_l_update_client_rates(priv, orate);
+	/* TODO: get rid of ratevector, build it only when needed */
+	acx_l_update_ratevector(priv);
+
+	/* Do/don't do tx rate fallback; beacon contents and rate */
+	SET_BIT(priv->set_mask, SET_RATE_FALLBACK|SET_TEMPLATES);
+	result = -EINPROGRESS;
+
+	acx_unlock(priv, flags);
+	acx_sem_unlock(priv);
+end:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_get_phy_chan_busy_percentage
+*----------------------------------------------------------------*/
+static int
+acx_ioctl_get_phy_chan_busy_percentage(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	struct { /* added ACX_PACKED, not tested --vda */
+		u16 type ACX_PACKED;
+		u16 len ACX_PACKED;
+		u32 busytime ACX_PACKED;
+		u32 totaltime ACX_PACKED;
+	} usage;
+
+	acx_sem_lock(priv);
+
+	if (OK != acx_s_interrogate(priv, &usage, ACX1xx_IE_MEDIUM_USAGE))
+		return NOT_OK;
+	printk("%s: average busy percentage since last invocation: %d%% "
+		"(microseconds: %u of %u)\n",
+		dev->name,
+		100 * (le32_to_cpu(usage.busytime) / 100) / (le32_to_cpu(usage.totaltime) / 100),
+		le32_to_cpu(usage.busytime), le32_to_cpu(usage.totaltime));
+		/* prevent calculation overflow */
+
+	acx_sem_unlock(priv);
+
+	return OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_set_ed_threshold
+*----------------------------------------------------------------*/
+static inline int
+acx_ioctl_set_ed_threshold(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+
+	acx_sem_lock(priv);
+
+	printk("old ED threshold value: %d\n", priv->ed_threshold);
+	priv->ed_threshold = (unsigned char)*extra;
+	printk("new ED threshold value: %d\n", (unsigned char)*extra);
+	SET_BIT(priv->set_mask, GETSET_ED_THRESH);
+
+	acx_sem_unlock(priv);
+
+	return -EINPROGRESS;
+}
+
+
+/*----------------------------------------------------------------
+* acx_ioctl_set_cca
+*----------------------------------------------------------------*/
+static inline int
+acx_ioctl_set_cca(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int result;
+
+	acx_sem_lock(priv);
+
+	printk("old CCA value: 0x%02X\n", priv->cca);
+	priv->cca = (unsigned char)*extra;
+	printk("new CCA value: 0x%02X\n", (unsigned char)*extra);
+	SET_BIT(priv->set_mask, GETSET_CCA);
+	result = -EINPROGRESS;
+
+	acx_sem_unlock(priv);
+
+	return result;
+}
+
+
+/***********************************************************************
+*/
+static const char * const
+scan_modes[] = { "active", "passive", "background" };
+
+static void
+acx_print_scan_params(wlandevice_t *priv, const char* head)
+{
+	printk("%s: %smode %d (%s), min chan time %dTU, "
+		"max chan time %dTU, max scan rate byte: %d\n",
+		priv->netdev->name, head,
+		priv->scan_mode, scan_modes[priv->scan_mode],
+		priv->scan_probe_delay, priv->scan_duration, priv->scan_rate);
+}
+
+static int
+acx_ioctl_set_scan_params(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int result;
+	const int *params = (int *)extra;
+
+	acx_sem_lock(priv);
+
+	acx_print_scan_params(priv, "old scan parameters: ");
+	if ((params[0] != -1) && (params[0] >= 0) && (params[0] <= 2))
+		priv->scan_mode = params[0];
+	if (params[1] != -1)
+		priv->scan_probe_delay = params[1];
+	if (params[2] != -1)
+		priv->scan_duration = params[2];
+	if ((params[3] != -1) && (params[3] <= 255))
+		priv->scan_rate = params[3];
+	acx_print_scan_params(priv, "new scan parameters: ");
+	SET_BIT(priv->set_mask, GETSET_RESCAN);
+	result = -EINPROGRESS;
+
+	acx_sem_unlock(priv);
+
+	return result;
+}
+
+static int
+acx_ioctl_get_scan_params(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int result;
+	int *params = (int *)extra;
+
+	acx_sem_lock(priv);
+
+	acx_print_scan_params(priv, "current scan parameters: ");
+	params[0] = priv->scan_mode;
+	params[1] = priv->scan_probe_delay;
+	params[2] = priv->scan_duration;
+	params[3] = priv->scan_rate;
+	result = OK;
+
+	acx_sem_unlock(priv);
+
+	return result;
+}
+
+
+/***********************************************************************
+*/
+static int
+acx100_ioctl_set_led_power(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	static const char * const led_modes[] = { "off", "on", "LinkQuality" };
+
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int result;
+
+	acx_sem_lock(priv);
+
+	printk("%s: power LED status: old %d (%s), ",
+			dev->name,
+			priv->led_power,
+			led_modes[priv->led_power]);
+	priv->led_power = extra[0];
+	if (priv->led_power > 2) priv->led_power = 2;
+	printk("new %d (%s)\n",
+			priv->led_power,
+			led_modes[priv->led_power]);
+
+	if (priv->led_power == 2) {
+		printk("%s: max link quality setting: old %d, ",
+			dev->name, priv->brange_max_quality);
+		if (extra[1])
+			priv->brange_max_quality = extra[1];
+		printk("new %d\n", priv->brange_max_quality);
+	}
+
+	SET_BIT(priv->set_mask, GETSET_LED_POWER);
+
+	result = -EINPROGRESS;
+
+	acx_sem_unlock(priv);
+
+	return result;
+}
+
+
+/***********************************************************************
+*/
+static inline int
+acx100_ioctl_get_led_power(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+
+	acx_sem_lock(priv);
+
+	extra[0] = priv->led_power;
+	if (priv->led_power == 2)
+		extra[1] = priv->brange_max_quality;
+	else
+		extra[1] = -1;
+
+	acx_sem_unlock(priv);
+
+	return OK;
+}
+
+
+/***********************************************************************
+*/
+#if WIRELESS_EXT >= 13
+static const iw_handler acx_ioctl_handler[] =
+{
+	(iw_handler) acx_ioctl_commit,		/* SIOCSIWCOMMIT */
+	(iw_handler) acx_ioctl_get_name,	/* SIOCGIWNAME */
+	(iw_handler) NULL,			/* SIOCSIWNWID */
+	(iw_handler) NULL,			/* SIOCGIWNWID */
+	(iw_handler) acx_ioctl_set_freq,	/* SIOCSIWFREQ */
+	(iw_handler) acx_ioctl_get_freq,	/* SIOCGIWFREQ */
+	(iw_handler) acx_ioctl_set_mode,	/* SIOCSIWMODE */
+	(iw_handler) acx_ioctl_get_mode,	/* SIOCGIWMODE */
+	(iw_handler) acx_ioctl_set_sens,	/* SIOCSIWSENS */
+	(iw_handler) acx_ioctl_get_sens,	/* SIOCGIWSENS */
+	(iw_handler) NULL,			/* SIOCSIWRANGE */
+	(iw_handler) acx_ioctl_get_range,	/* SIOCGIWRANGE */
+	(iw_handler) NULL,			/* SIOCSIWPRIV */
+	(iw_handler) NULL,			/* SIOCGIWPRIV */
+	(iw_handler) NULL,			/* SIOCSIWSTATS */
+	(iw_handler) NULL,			/* SIOCGIWSTATS */
+#if IW_HANDLER_VERSION > 4
+	iw_handler_set_spy,			/* SIOCSIWSPY */
+	iw_handler_get_spy,			/* SIOCGIWSPY */
+	iw_handler_set_thrspy,			/* SIOCSIWTHRSPY */
+	iw_handler_get_thrspy,			/* SIOCGIWTHRSPY */
+#else /* IW_HANDLER_VERSION > 4 */
+#ifdef WIRELESS_SPY
+	(iw_handler) NULL /* acx_ioctl_set_spy FIXME */,	/* SIOCSIWSPY */
+	(iw_handler) NULL /* acx_ioctl_get_spy FIXME */,	/* SIOCGIWSPY */
+#else /* WSPY */
+	(iw_handler) NULL,			/* SIOCSIWSPY */
+	(iw_handler) NULL,			/* SIOCGIWSPY */
+#endif /* WSPY */
+	(iw_handler) NULL,			/* [nothing] */
+	(iw_handler) NULL,			/* [nothing] */
+#endif /* IW_HANDLER_VERSION > 4 */
+	(iw_handler) acx_ioctl_set_ap,		/* SIOCSIWAP */
+	(iw_handler) acx_ioctl_get_ap,		/* SIOCGIWAP */
+	(iw_handler) NULL,			/* [nothing] */
+	(iw_handler) acx_ioctl_get_aplist,	/* SIOCGIWAPLIST */
+#if WIRELESS_EXT > 13
+	(iw_handler) acx_ioctl_set_scan,	/* SIOCSIWSCAN */
+	(iw_handler) acx_ioctl_get_scan,	/* SIOCGIWSCAN */
+#else /* WE > 13 */
+	(iw_handler) NULL,			/* SIOCSIWSCAN */
+	(iw_handler) NULL,			/* SIOCGIWSCAN */
+#endif /* WE > 13 */
+	(iw_handler) acx_ioctl_set_essid,	/* SIOCSIWESSID */
+	(iw_handler) acx_ioctl_get_essid,	/* SIOCGIWESSID */
+	(iw_handler) acx_ioctl_set_nick,	/* SIOCSIWNICKN */
+	(iw_handler) acx_ioctl_get_nick,	/* SIOCGIWNICKN */
+	(iw_handler) NULL,			/* [nothing] */
+	(iw_handler) NULL,			/* [nothing] */
+	(iw_handler) acx_ioctl_set_rate,	/* SIOCSIWRATE */
+	(iw_handler) acx_ioctl_get_rate,	/* SIOCGIWRATE */
+	(iw_handler) acx_ioctl_set_rts,		/* SIOCSIWRTS */
+	(iw_handler) acx_ioctl_get_rts,		/* SIOCGIWRTS */
+	(iw_handler) NULL /* acx_ioctl_set_frag FIXME */,	/* SIOCSIWFRAG */
+	(iw_handler) NULL /* acx_ioctl_get_frag FIXME */,	/* SIOCGIWFRAG */
+	(iw_handler) acx_ioctl_set_txpow,	/* SIOCSIWTXPOW */
+	(iw_handler) acx_ioctl_get_txpow,	/* SIOCGIWTXPOW */
+	(iw_handler) acx_ioctl_set_retry,	/* SIOCSIWRETRY */
+	(iw_handler) acx_ioctl_get_retry,	/* SIOCGIWRETRY */
+	(iw_handler) acx_ioctl_set_encode,	/* SIOCSIWENCODE */
+	(iw_handler) acx_ioctl_get_encode,	/* SIOCGIWENCODE */
+	(iw_handler) acx_ioctl_set_power,	/* SIOCSIWPOWER */
+	(iw_handler) acx_ioctl_get_power,	/* SIOCGIWPOWER */
+};
+
+static const iw_handler acx_ioctl_private_handler[] =
+{
+#if ACX_DEBUG
+[ACX100_IOCTL_DEBUG		- ACX100_IOCTL] = (iw_handler) acx_ioctl_set_debug,
+#else
+[ACX100_IOCTL_DEBUG		- ACX100_IOCTL] = (iw_handler) NULL,
+#endif
+[ACX100_IOCTL_SET_PLED		- ACX100_IOCTL] = (iw_handler) acx100_ioctl_set_led_power,
+[ACX100_IOCTL_GET_PLED		- ACX100_IOCTL] = (iw_handler) acx100_ioctl_get_led_power,
+[ACX100_IOCTL_SET_RATES		- ACX100_IOCTL] = (iw_handler) acx_ioctl_set_rates,
+[ACX100_IOCTL_LIST_DOM		- ACX100_IOCTL] = (iw_handler) acx_ioctl_list_reg_domain,
+[ACX100_IOCTL_SET_DOM		- ACX100_IOCTL] = (iw_handler) acx_ioctl_set_reg_domain,
+[ACX100_IOCTL_GET_DOM		- ACX100_IOCTL] = (iw_handler) acx_ioctl_get_reg_domain,
+[ACX100_IOCTL_SET_SCAN_PARAMS	- ACX100_IOCTL] = (iw_handler) acx_ioctl_set_scan_params,
+[ACX100_IOCTL_GET_SCAN_PARAMS	- ACX100_IOCTL] = (iw_handler) acx_ioctl_get_scan_params,
+[ACX100_IOCTL_SET_PREAMB	- ACX100_IOCTL] = (iw_handler) acx_ioctl_set_short_preamble,
+[ACX100_IOCTL_GET_PREAMB	- ACX100_IOCTL] = (iw_handler) acx_ioctl_get_short_preamble,
+[ACX100_IOCTL_SET_ANT		- ACX100_IOCTL] = (iw_handler) acx_ioctl_set_antenna,
+[ACX100_IOCTL_GET_ANT		- ACX100_IOCTL] = (iw_handler) acx_ioctl_get_antenna,
+[ACX100_IOCTL_RX_ANT		- ACX100_IOCTL] = (iw_handler) acx_ioctl_set_rx_antenna,
+[ACX100_IOCTL_TX_ANT		- ACX100_IOCTL] = (iw_handler) acx_ioctl_set_tx_antenna,
+[ACX100_IOCTL_SET_PHY_AMP_BIAS	- ACX100_IOCTL] = (iw_handler) acx100_ioctl_set_phy_amp_bias,
+[ACX100_IOCTL_GET_PHY_CHAN_BUSY	- ACX100_IOCTL] = (iw_handler) acx_ioctl_get_phy_chan_busy_percentage,
+[ACX100_IOCTL_SET_ED		- ACX100_IOCTL] = (iw_handler) acx_ioctl_set_ed_threshold,
+[ACX100_IOCTL_SET_CCA		- ACX100_IOCTL] = (iw_handler) acx_ioctl_set_cca,
+[ACX100_IOCTL_MONITOR		- ACX100_IOCTL] = (iw_handler) acx_ioctl_wlansniff,
+[ACX100_IOCTL_TEST		- ACX100_IOCTL] = (iw_handler) acx_ioctl_unknown11,
+[ACX100_IOCTL_DBG_SET_MASKS	- ACX100_IOCTL] = (iw_handler) acx_ioctl_dbg_set_masks,
+[ACX111_IOCTL_INFO		- ACX100_IOCTL] = (iw_handler) acx111_ioctl_info,
+[ACX100_IOCTL_DBG_SET_IO	- ACX100_IOCTL] = (iw_handler) acx_ioctl_dbg_set_io,
+[ACX100_IOCTL_DBG_GET_IO	- ACX100_IOCTL] = (iw_handler) acx_ioctl_dbg_get_io,
+};
+
+const struct iw_handler_def acx_ioctl_handler_def =
+{
+	.num_standard = VEC_SIZE(acx_ioctl_handler),
+	.num_private = VEC_SIZE(acx_ioctl_private_handler),
+	.num_private_args = VEC_SIZE(acx_ioctl_private_args),
+	.standard = (iw_handler *) acx_ioctl_handler,
+	.private = (iw_handler *) acx_ioctl_private_handler,
+	.private_args = (struct iw_priv_args *) acx_ioctl_private_args,
+#if WIRELESS_EXT > 15
+	.spy_offset = ((void *) (&((struct wlandevice *) NULL)->spy_data) - (void *) NULL),
+#endif /* WE > 15 */
+};
+
+#endif /* WE >= 13 */
+
+
+#if WIRELESS_EXT < 13
+/*================================================================*/
+/* Main function						  */
+/*================================================================*/
+/*----------------------------------------------------------------
+* acx_e_ioctl_old
+*
+*
+* STATUS: FINISHED
+*
+* Comment:
+* This is the *OLD* ioctl handler.
+* Make sure to not only place your additions here, but instead mainly
+* in the new one (acx_ioctl_handler[])!
+*----------------------------------------------------------------*/
+int
+acx_e_ioctl_old(netdevice_t *dev, struct ifreq *ifr, int cmd)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int result = 0;
+	struct iwreq *iwr = (struct iwreq *)ifr;
+
+	acxlog(L_IOCTL, "%s cmd = 0x%04X\n", __func__, cmd);
+
+	/* This is the way it is done in the orinoco driver.
+	 * Check to see if device is present.
+	 */
+	if (0 == netif_device_present(dev)) {
+		return -ENODEV;
+	}
+
+	switch (cmd) {
+/* WE 13 and higher will use acx_ioctl_handler_def */
+	case SIOCGIWNAME:
+		/* get name == wireless protocol */
+		result = acx_ioctl_get_name(dev, NULL,
+					       (char *)&(iwr->u.name), NULL);
+		break;
+
+	case SIOCSIWNWID: /* pre-802.11, */
+	case SIOCGIWNWID: /* not supported. */
+		result = -EOPNOTSUPP;
+		break;
+
+	case SIOCSIWFREQ:
+		/* set channel/frequency (Hz)
+		   data can be frequency or channel :
+		   0-1000 = channel
+		   > 1000 = frequency in Hz */
+		result = acx_ioctl_set_freq(dev, NULL, &(iwr->u.freq), NULL);
+		break;
+
+	case SIOCGIWFREQ:
+		/* get channel/frequency (Hz) */
+		result = acx_ioctl_get_freq(dev, NULL, &(iwr->u.freq), NULL);
+		break;
+
+	case SIOCSIWMODE:
+		/* set operation mode */
+		result = acx_ioctl_set_mode(dev, NULL, &(iwr->u.mode), NULL);
+		break;
+
+	case SIOCGIWMODE:
+		/* get operation mode */
+		result = acx_ioctl_get_mode(dev, NULL, &(iwr->u.mode), NULL);
+		break;
+
+	case SIOCSIWSENS:
+		/* Set sensitivity */
+		result = acx_ioctl_set_sens(dev, NULL, &(iwr->u.sens), NULL);
+		break;
+
+	case SIOCGIWSENS:
+		/* Get sensitivity */
+		result = acx_ioctl_get_sens(dev, NULL, &(iwr->u.sens), NULL);
+		break;
+
+#if WIRELESS_EXT > 10
+	case SIOCGIWRANGE:
+		/* Get range of parameters */
+		{
+			struct iw_range range;
+			result = acx_ioctl_get_range(dev, NULL,
+					&(iwr->u.data), (char *)&range);
+			if (copy_to_user(iwr->u.data.pointer, &range,
+					 sizeof(struct iw_range)))
+				result = -EFAULT;
+		}
+		break;
+#endif
+
+	case SIOCGIWPRIV:
+		result = acx_ioctl_get_iw_priv(iwr);
+		break;
+
+	/* FIXME: */
+	/* case SIOCSIWSPY: */
+	/* case SIOCGIWSPY: */
+	/* case SIOCSIWTHRSPY: */
+	/* case SIOCGIWTHRSPY: */
+
+	case SIOCSIWAP:
+		/* set access point by MAC address */
+		result = acx_ioctl_set_ap(dev, NULL, &(iwr->u.ap_addr),
+					     NULL);
+		break;
+
+	case SIOCGIWAP:
+		/* get access point MAC address */
+		result = acx_ioctl_get_ap(dev, NULL, &(iwr->u.ap_addr),
+					     NULL);
+		break;
+
+	case SIOCGIWAPLIST:
+		/* get list of access points in range */
+		result = acx_ioctl_get_aplist(dev, NULL, &(iwr->u.data),
+						 NULL);
+		break;
+
+#if NOT_FINISHED_YET
+	/* FIXME: do proper interfacing to activate that! */
+	case SIOCSIWSCAN:
+		/* start a station scan */
+		result = acx_ioctl_set_scan(iwr, priv);
+		break;
+
+	case SIOCGIWSCAN:
+		/* get list of stations found during scan */
+		result = acx_ioctl_get_scan(iwr, priv);
+		break;
+#endif
+
+	case SIOCSIWESSID:
+		/* set ESSID (network name) */
+		{
+			char essid[IW_ESSID_MAX_SIZE+1];
+
+			if (iwr->u.essid.length > IW_ESSID_MAX_SIZE)
+			{
+				result = -E2BIG;
+				break;
+			}
+			if (copy_from_user(essid, iwr->u.essid.pointer,
+						iwr->u.essid.length))
+			{
+				result = -EFAULT;
+				break;
+			}
+			result = acx_ioctl_set_essid(dev, NULL,
+					&(iwr->u.essid), essid);
+		}
+		break;
+
+	case SIOCGIWESSID:
+		/* get ESSID */
+		{
+			char essid[IW_ESSID_MAX_SIZE+1];
+			if (iwr->u.essid.pointer)
+				result = acx_ioctl_get_essid(dev, NULL,
+					&(iwr->u.essid), essid);
+			if (copy_to_user(iwr->u.essid.pointer, essid,
+						iwr->u.essid.length))
+				result = -EFAULT;
+		}
+		break;
+
+	case SIOCSIWNICKN:
+		/* set nick */
+		{
+			char nick[IW_ESSID_MAX_SIZE+1];
+
+			if (iwr->u.data.length > IW_ESSID_MAX_SIZE)
+			{
+				result = -E2BIG;
+				break;
+			}
+			if (copy_from_user(nick, iwr->u.data.pointer,
+						iwr->u.data.length))
+			{
+				result = -EFAULT;
+				break;
+			}
+			result = acx_ioctl_set_nick(dev, NULL,
+					&(iwr->u.data), nick);
+		}
+		break;
+
+	case SIOCGIWNICKN:
+		/* get nick */
+		{
+			char nick[IW_ESSID_MAX_SIZE+1];
+			if (iwr->u.data.pointer)
+				result = acx_ioctl_get_nick(dev, NULL,
+						&(iwr->u.data), nick);
+			if (copy_to_user(iwr->u.data.pointer, nick,
+						iwr->u.data.length))
+				result = -EFAULT;
+		}
+		break;
+
+	case SIOCSIWRATE:
+		/* set default bit rate (bps) */
+		result = acx_ioctl_set_rate(dev, NULL, &(iwr->u.bitrate),
+					       NULL);
+		break;
+
+	case SIOCGIWRATE:
+		/* get default bit rate (bps) */
+		result = acx_ioctl_get_rate(dev, NULL, &(iwr->u.bitrate),
+					       NULL);
+		break;
+
+	case  SIOCSIWRTS:
+		/* set RTS threshold value */
+		result = acx_ioctl_set_rts(dev, NULL, &(iwr->u.rts), NULL);
+		break;
+	case  SIOCGIWRTS:
+		/* get RTS threshold value */
+		result = acx_ioctl_get_rts(dev, NULL,  &(iwr->u.rts), NULL);
+		break;
+
+	/* FIXME: */
+	/* case  SIOCSIWFRAG: */
+	/* case  SIOCGIWFRAG: */
+
+#if WIRELESS_EXT > 9
+	case SIOCGIWTXPOW:
+		/* get tx power */
+		result = acx_ioctl_get_txpow(dev, NULL, &(iwr->u.txpower),
+						NULL);
+		break;
+
+	case SIOCSIWTXPOW:
+		/* set tx power */
+		result = acx_ioctl_set_txpow(dev, NULL, &(iwr->u.txpower),
+						NULL);
+		break;
+#endif
+
+	case SIOCSIWRETRY:
+		result = acx_ioctl_set_retry(dev, NULL, &(iwr->u.retry), NULL);
+		break;
+
+	case SIOCGIWRETRY:
+		result = acx_ioctl_get_retry(dev, NULL, &(iwr->u.retry), NULL);
+		break;
+
+	case SIOCSIWENCODE:
+		{
+			/* set encoding token & mode */
+			u8 key[29];
+			if (iwr->u.encoding.pointer) {
+				if (iwr->u.encoding.length > 29) {
+					result = -E2BIG;
+					break;
+				}
+				if (copy_from_user(key, iwr->u.encoding.pointer,
+						iwr->u.encoding.length)) {
+					result = -EFAULT;
+					break;
+				}
+			}
+			else
+			if (iwr->u.encoding.length) {
+				result = -EINVAL;
+				break;
+			}
+			result = acx_ioctl_set_encode(dev, NULL,
+					&(iwr->u.encoding), key);
+		}
+		break;
+
+	case SIOCGIWENCODE:
+		{
+			/* get encoding token & mode */
+			u8 key[29];
+
+			result = acx_ioctl_get_encode(dev, NULL,
+					&(iwr->u.encoding), key);
+			if (iwr->u.encoding.pointer) {
+				if (copy_to_user(iwr->u.encoding.pointer,
+						key, iwr->u.encoding.length))
+					result = -EFAULT;
+			}
+		}
+		break;
+
+	/******************** iwpriv ioctls below ********************/
+#if ACX_DEBUG
+	case ACX100_IOCTL_DEBUG:
+		acx_ioctl_set_debug(dev, NULL, NULL, iwr->u.name);
+		break;
+#endif
+
+	case ACX100_IOCTL_SET_PLED:
+		acx100_ioctl_set_led_power(dev, NULL, NULL, iwr->u.name);
+		break;
+
+	case ACX100_IOCTL_GET_PLED:
+		acx100_ioctl_get_led_power(dev, NULL, NULL, iwr->u.name);
+		break;
+
+	case ACX100_IOCTL_LIST_DOM:
+		acx_ioctl_list_reg_domain(dev, NULL, NULL, NULL);
+		break;
+
+	case ACX100_IOCTL_SET_DOM:
+		acx_ioctl_set_reg_domain(dev, NULL, NULL, iwr->u.name);
+		break;
+
+	case ACX100_IOCTL_GET_DOM:
+		acx_ioctl_get_reg_domain(dev, NULL, NULL, iwr->u.name);
+		break;
+
+	case ACX100_IOCTL_SET_SCAN_PARAMS:
+		acx_ioctl_set_scan_params(dev, NULL, NULL, iwr->u.name);
+		break;
+
+	case ACX100_IOCTL_GET_SCAN_PARAMS:
+		acx_ioctl_get_scan_params(dev, NULL, NULL, iwr->u.name);
+		break;
+
+	case ACX100_IOCTL_SET_PREAMB:
+		acx_ioctl_set_short_preamble(dev, NULL, NULL, iwr->u.name);
+		break;
+
+	case ACX100_IOCTL_GET_PREAMB:
+		acx_ioctl_get_short_preamble(dev, NULL, NULL, iwr->u.name);
+		break;
+
+	case ACX100_IOCTL_SET_ANT:
+		acx_ioctl_set_antenna(dev, NULL, NULL, iwr->u.name);
+		break;
+
+	case ACX100_IOCTL_GET_ANT:
+		acx_ioctl_get_antenna(dev, NULL, NULL, NULL);
+		break;
+
+	case ACX100_IOCTL_RX_ANT:
+		acx_ioctl_set_rx_antenna(dev, NULL, NULL, iwr->u.name);
+		break;
+
+	case ACX100_IOCTL_TX_ANT:
+		acx_ioctl_set_tx_antenna(dev, NULL, NULL, iwr->u.name);
+		break;
+
+	case ACX100_IOCTL_SET_ED:
+		acx_ioctl_set_ed_threshold(dev, NULL, NULL, iwr->u.name);
+		break;
+
+	case ACX100_IOCTL_SET_CCA:
+		acx_ioctl_set_cca(dev, NULL, NULL, iwr->u.name);
+		break;
+
+	case ACX100_IOCTL_MONITOR:	/* set sniff (monitor) mode */
+		acxlog(L_IOCTL, "%s: IWPRIV monitor\n", dev->name);
+
+		/* can only be done by admin */
+		if (!capable(CAP_NET_ADMIN)) {
+			result = -EPERM;
+			break;
+		}
+		result = acx_ioctl_wlansniff(dev, NULL, NULL, iwr->u.name);
+		break;
+
+	case ACX100_IOCTL_TEST:
+		acx_ioctl_unknown11(dev, NULL, NULL, NULL);
+		break;
+
+	case ACX111_IOCTL_INFO:
+		acx111_ioctl_info(dev, NULL, NULL, NULL);
+		break;
+
+	default:
+		acxlog(L_IOCTL, "wireless ioctl 0x%04X queried "
+				"but not implemented yet\n", cmd);
+		result = -EOPNOTSUPP;
+		break;
+	}
+
+	if ((priv->dev_state_mask & ACX_STATE_IFACE_UP) && priv->set_mask) {
+		acx_sem_lock(priv);
+		acx_s_update_card_settings(priv, 0, 0);
+		acx_sem_unlock(priv);
+	}
+
+	/* older WEs don't have a commit handler,
+	 * so we need to fix return code in this case */
+	if (-EINPROGRESS == result)
+		result = 0;
+
+	return result;
+}
+#endif /* WE < 13 */
diff -urN oldtree/drivers/net/wireless/tiacx/pci.c newtree/drivers/net/wireless/tiacx/pci.c
--- oldtree/drivers/net/wireless/tiacx/pci.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/pci.c	2006-02-13 19:20:00.542429840 -0500
@@ -0,0 +1,4777 @@
+/***********************************************************************
+** Copyright (C) 2003  ACX100 Open Source Project
+**
+** The contents of this file are subject to the Mozilla Public
+** License Version 1.1 (the "License"); you may not use this file
+** except in compliance with the License. You may obtain a copy of
+** the License at http://www.mozilla.org/MPL/
+**
+** Software distributed under the License is distributed on an "AS
+** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+** implied. See the License for the specific language governing
+** rights and limitations under the License.
+**
+** Alternatively, the contents of this file may be used under the
+** terms of the GNU Public License version 2 (the "GPL"), in which
+** case the provisions of the GPL are applicable instead of the
+** above.  If you wish to allow the use of your version of this file
+** only under the terms of the GPL and not to allow others to use
+** your version of this file under the MPL, indicate your decision
+** by deleting the provisions above and replace them with the notice
+** and other provisions required by the GPL.  If you do not delete
+** the provisions above, a recipient may use your version of this
+** file under either the MPL or the GPL.
+** ---------------------------------------------------------------------
+** Inquiries regarding the ACX100 Open Source Project can be
+** made directly to:
+**
+** acx100-users@lists.sf.net
+** http://acx100.sf.net
+** ---------------------------------------------------------------------
+*/
+#define ACX_PCI 1
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)
+#include <linux/moduleparam.h>
+#endif
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/if_arp.h>
+#include <linux/rtnetlink.h>
+#include <linux/wireless.h>
+#if WIRELESS_EXT >= 13
+#include <net/iw_handler.h>
+#endif
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/pm.h>
+
+#include "acx.h"
+
+
+/********************************************************************/
+/* Module information                                               */
+/********************************************************************/
+MODULE_AUTHOR("The ACX100 Open Source Driver development team");
+MODULE_DESCRIPTION("Driver for TI ACX1xx based wireless cards (CardBus/PCI/USB)");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual MPL/GPL");
+#endif
+
+
+/*================================================================*/
+/* Local Constants */
+#define PCI_TYPE		(PCI_USES_MEM | PCI_ADDR0 | PCI_NO_ACPI_WAKE)
+#define PCI_ACX100_REGION1		0x01
+#define PCI_ACX100_REGION1_SIZE		0x1000	/* Memory size - 4K bytes */
+#define PCI_ACX100_REGION2		0x02
+#define PCI_ACX100_REGION2_SIZE		0x10000 /* Memory size - 64K bytes */
+
+#define PCI_ACX111_REGION1		0x00
+#define PCI_ACX111_REGION1_SIZE		0x2000	/* Memory size - 8K bytes */
+#define PCI_ACX111_REGION2		0x01
+#define PCI_ACX111_REGION2_SIZE		0x20000 /* Memory size - 128K bytes */
+
+/* Texas Instruments Vendor ID */
+#define PCI_VENDOR_ID_TI		0x104c
+
+/* ACX100 22Mb/s WLAN controller */
+#define PCI_DEVICE_ID_TI_TNETW1100A	0x8400
+#define PCI_DEVICE_ID_TI_TNETW1100B	0x8401
+
+/* ACX111 54Mb/s WLAN controller */
+#define PCI_DEVICE_ID_TI_TNETW1130	0x9066
+
+/* PCI Class & Sub-Class code, Network-'Other controller' */
+#define PCI_CLASS_NETWORK_OTHERS	0x280
+
+
+/*================================================================*/
+/* Local Static Definitions */
+#define CARD_EEPROM_ID_SIZE 6
+#define MAX_IRQLOOPS_PER_JIFFY  (20000/HZ) /* a la orinoco.c */
+
+#if ACX_DEBUG
+unsigned int acx_debug = L_ASSOC|L_INIT;
+#endif
+
+#if SEPARATE_DRIVER_INSTANCES
+int card = 0;
+#endif /* SEPARATE_DRIVER_INSTANCES */
+
+#if USE_FW_LOADER_LEGACY
+char *firmware_dir;
+#endif
+
+static const char name_acx100[] = "ACX100";
+static const char name_tnetw1100a[] = "TNETW1100A";
+static const char name_tnetw1100b[] = "TNETW1100B";
+
+static const char name_acx111[] = "ACX111";
+static const char name_tnetw1130[] = "TNETW1130";
+
+static const struct pci_device_id
+acx_pci_id_tbl[] __devinitdata = {
+	{
+		.vendor = PCI_VENDOR_ID_TI,
+		.device = PCI_DEVICE_ID_TI_TNETW1100A,
+		.subvendor = PCI_ANY_ID,
+		.subdevice = PCI_ANY_ID,
+		.driver_data = CHIPTYPE_ACX100,
+	},
+	{
+		.vendor = PCI_VENDOR_ID_TI,
+		.device = PCI_DEVICE_ID_TI_TNETW1100B,
+		.subvendor = PCI_ANY_ID,
+		.subdevice = PCI_ANY_ID,
+		.driver_data = CHIPTYPE_ACX100,
+	},
+	{
+		.vendor = PCI_VENDOR_ID_TI,
+		.device = PCI_DEVICE_ID_TI_TNETW1130,
+		.subvendor = PCI_ANY_ID,
+		.subdevice = PCI_ANY_ID,
+		.driver_data = CHIPTYPE_ACX111,
+	},
+	{
+		.vendor = 0,
+		.device = 0,
+		.subvendor = 0,
+		.subdevice = 0,
+		.driver_data = 0,
+	}
+};
+
+MODULE_DEVICE_TABLE(pci, acx_pci_id_tbl);
+
+static void acx_l_disable_irq(wlandevice_t *priv);
+static void acx_l_enable_irq(wlandevice_t *priv);
+static int acx_e_probe_pci(struct pci_dev *pdev,
+			    const struct pci_device_id *id);
+static void acx_e_remove_pci(struct pci_dev *pdev);
+
+#ifdef CONFIG_PM
+static int acx_e_suspend(struct pci_dev *pdev, u32 state);
+static int acx_e_resume(struct pci_dev *pdev);
+#endif
+
+static void acx_i_tx_timeout(netdevice_t *dev);
+static struct net_device_stats *acx_e_get_stats(netdevice_t *dev);
+static struct iw_statistics *acx_e_get_wireless_stats(netdevice_t *dev);
+
+static irqreturn_t acx_i_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void acx_i_set_multicast_list(netdevice_t *dev);
+
+static int acx_e_open(netdevice_t *dev);
+static int acx_e_close(netdevice_t *dev);
+static void acx_s_up(netdevice_t *dev);
+static void acx_s_down(netdevice_t *dev);
+
+
+/* FIXME: checks should be removed once driver is included in the kernel */
+#ifndef __devexit_p
+#warning *** your kernel is EXTREMELY old since it does not even know about
+#warning __devexit_p - this driver could easily FAIL to work, so better
+#warning upgrade your kernel! ***
+#define __devexit_p(x) x
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11)
+/* pci_name() got introduced at start of 2.6.x,
+ * got mandatory (slot_name member removed) in 2.6.11-bk1 */
+#define pci_name(x) x->slot_name
+#endif
+
+static struct pci_driver acx_pci_drv_id = {
+	.name        = "acx_pci",
+	.id_table    = acx_pci_id_tbl,
+	.probe       = acx_e_probe_pci,
+	.remove      = __devexit_p(acx_e_remove_pci),
+#ifdef CONFIG_PM
+	.suspend     = acx_e_suspend,
+	.resume      = acx_e_resume
+#endif /* CONFIG_PM */
+};
+
+typedef struct acx_device {
+	netdevice_t *newest;
+} acx_device_t;
+
+/* if this driver was only about PCI devices, then we probably wouldn't
+ * need this linked list.
+ * But if we want to register ALL kinds of devices in one global list,
+ * then we need it and need to maintain it properly. */
+static struct acx_device root_acx_dev = {
+	.newest        = NULL,
+};
+DECLARE_MUTEX(root_acx_dev_sem);
+
+
+/***********************************************************************
+** Register access
+*/
+/* If INLINE_IO is not defined, it means user wants to have out-of-line
+** acx_r/w_regNN() functions. We are doing this with #define magic
+** in acx_inline.h: */
+#ifndef INLINE_IO
+#define INLINE_IO /* defined to nothing */
+#include "acx_inline.h"
+#endif
+
+
+/***********************************************************************
+** EEPROM and PHY read/write helpers
+*/
+/***********************************************************************
+** acx_read_eeprom_offset
+**
+** Function called to read an octet in the EEPROM.
+**
+** This function is used by acx_probe_pci to check if the
+** connected card is a legal one or not.
+**
+** Arguments:
+**	priv		ptr to wlandevice structure
+**	addr		address to read in the EEPROM
+**	charbuf		ptr to a char. This is where the read octet
+**			will be stored
+**
+** Returns:
+**	zero (0)	- failed
+**	one (1)		- success
+**
+** NOT ADAPTED FOR ACX111!!
+*/
+int
+acx_read_eeprom_offset(wlandevice_t *priv, u32 addr, u8 *charbuf)
+{
+	int result = NOT_OK;
+	int count;
+
+	acx_write_reg32(priv, IO_ACX_EEPROM_CFG, 0);
+	acx_write_reg32(priv, IO_ACX_EEPROM_ADDR, addr);
+	acx_write_flush(priv);
+	acx_write_reg32(priv, IO_ACX_EEPROM_CTL, 2);
+
+	count = 0xffff;
+	while (acx_read_reg16(priv, IO_ACX_EEPROM_CTL)) {
+		/* scheduling away instead of CPU burning loop
+		 * doesn't seem to work here at all:
+		 * awful delay, sometimes also failure.
+		 * Doesn't matter anyway (only small delay). */
+		if (unlikely(!--count)) {
+			printk("%s: timeout waiting for EEPROM read\n",
+							priv->netdev->name);
+			goto fail;
+		}
+	}
+
+	*charbuf = acx_read_reg8(priv, IO_ACX_EEPROM_DATA);
+	acxlog(L_DEBUG, "EEPROM at 0x%04X = 0x%02X\n", addr, *charbuf);
+	result = OK;
+
+fail:
+	return result;
+}
+
+
+/***********************************************************************
+** Dummy EEPROM read? why?!
+*/
+static int
+acx_read_eeprom_area(wlandevice_t *priv)
+{
+	int offs;
+	u8 tmp[0x3b];
+
+	for (offs = 0x8c; offs < 0xb9; offs++) {
+		acx_read_eeprom_offset(priv, offs, &tmp[offs - 0x8c]);
+	}
+	return OK;
+}
+
+
+/***********************************************************************
+** We don't lock hw accesses here since we never r/w eeprom in IRQ
+** Note: this function sleeps only because of GFP_KERNEL alloc
+*/
+#ifdef UNUSED
+int
+acx_s_write_eeprom_offset(wlandevice_t *priv, u32 addr, u32 len, const u8 *charbuf)
+{
+	u8 *data_verify = NULL;
+	unsigned long flags;
+	int count, i;
+	int result = NOT_OK;
+	u16 gpio_orig;
+
+	printk("acx: WARNING! I would write to EEPROM now. "
+		"Since I really DON'T want to unless you know "
+		"what you're doing (THIS CODE WILL PROBABLY "
+		"NOT WORK YET!), I will abort that now. And "
+		"definitely make sure to make a "
+		"/proc/driver/acx_wlan0_eeprom backup copy first!!! "
+		"(the EEPROM content includes the PCI config header!! "
+		"If you kill important stuff, then you WILL "
+		"get in trouble and people DID get in trouble already)\n");
+	return OK;
+
+	FN_ENTER;
+
+	data_verify = kmalloc(len, GFP_KERNEL);
+	if (!data_verify) {
+		goto end;
+	}
+
+	/* first we need to enable the OE (EEPROM Output Enable) GPIO line
+	 * to be able to write to the EEPROM.
+	 * NOTE: an EEPROM writing success has been reported,
+	 * but you probably have to modify GPIO_OUT, too,
+	 * and you probably need to activate a different GPIO
+	 * line instead! */
+	gpio_orig = acx_read_reg16(priv, IO_ACX_GPIO_OE);
+	acx_write_reg16(priv, IO_ACX_GPIO_OE, gpio_orig & ~1);
+	acx_write_flush(priv);
+
+	/* ok, now start writing the data out */
+	for (i = 0; i < len; i++) {
+		acx_write_reg32(priv, IO_ACX_EEPROM_CFG, 0);
+		acx_write_reg32(priv, IO_ACX_EEPROM_ADDR, addr + i);
+		acx_write_reg32(priv, IO_ACX_EEPROM_DATA, *(charbuf + i));
+		acx_write_flush(priv);
+		acx_write_reg32(priv, IO_ACX_EEPROM_CTL, 1);
+
+		while (acx_read_reg16(priv, IO_ACX_EEPROM_CTL)) {
+			if (unlikely(++count > 0xffff)) {
+				printk("WARNING, DANGER!!! "
+					"Timeout waiting for EEPROM write\n");
+				goto end;
+			}
+		}
+	}
+
+	/* disable EEPROM writing */
+	acx_write_reg16(priv, IO_ACX_GPIO_OE, gpio_orig);
+	acx_write_flush(priv);
+
+	/* now start a verification run */
+	count = 0xffff;
+	for (i = 0; i < len; i++) {
+		acx_write_reg32(priv, IO_ACX_EEPROM_CFG, 0);
+		acx_write_reg32(priv, IO_ACX_EEPROM_ADDR, addr + i);
+		acx_write_flush(priv);
+		acx_write_reg32(priv, IO_ACX_EEPROM_CTL, 2);
+
+		while (acx_read_reg16(priv, IO_ACX_EEPROM_CTL)) {
+			if (unlikely(!--count)) {
+				printk("timeout waiting for EEPROM read\n");
+				goto end;
+			}
+		}
+
+		data_verify[i] = acx_read_reg16(priv, IO_ACX_EEPROM_DATA);
+	}
+
+	if (0 == memcmp(charbuf, data_verify, len))
+		result = OK; /* read data matches, success */
+
+end:
+	kfree(data_verify);
+	FN_EXIT1(result);
+	return result;
+}
+#endif /* UNUSED */
+
+
+/***********************************************************************
+** acx_s_read_phy_reg
+**
+** Messing with rx/tx disabling and enabling here
+** (acx_write_reg32(priv, IO_ACX_ENABLE, 0b000000xx)) kills traffic
+*/
+int
+acx_s_read_phy_reg(wlandevice_t *priv, u32 reg, u8 *charbuf)
+{
+	int result = NOT_OK;
+	int count;
+
+	FN_ENTER;
+
+	acx_write_reg32(priv, IO_ACX_PHY_ADDR, reg);
+	acx_write_flush(priv);
+	acx_write_reg32(priv, IO_ACX_PHY_CTL, 2);
+
+	count = 0xffff;
+	while (acx_read_reg32(priv, IO_ACX_PHY_CTL)) {
+		/* scheduling away instead of CPU burning loop
+		 * doesn't seem to work here at all:
+		 * awful delay, sometimes also failure.
+		 * Doesn't matter anyway (only small delay). */
+		if (unlikely(!--count)) {
+			printk("%s: timeout waiting for phy read\n",
+							priv->netdev->name);
+			*charbuf = 0;
+			goto fail;
+		}
+	}
+
+	acxlog(L_DEBUG, "count was %u\n", count);
+	*charbuf = acx_read_reg8(priv, IO_ACX_PHY_DATA);
+
+	acxlog(L_DEBUG, "radio PHY at 0x%04X = 0x%02X\n", *charbuf, reg);
+	result = OK;
+	goto fail; /* silence compiler warning */
+fail:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/***********************************************************************
+*/
+int
+acx_s_write_phy_reg(wlandevice_t *priv, u32 reg, u8 value)
+{
+	FN_ENTER;
+
+	/* FIXME: we didn't use 32bit access here since mprusko said that
+	 * it results in distorted sensitivity on his card (huh!?!?
+	 * doesn't happen with my setup...)
+	 * But with the access reordering and flushing it
+	 * shouldn't happen any more...
+	 * FIXME: which radio is in the problematic card? My working one
+	 * is 0x11 */
+	acx_write_reg32(priv, IO_ACX_PHY_DATA, value);
+	acx_write_reg32(priv, IO_ACX_PHY_ADDR, reg);
+	acx_write_flush(priv);
+	acx_write_reg32(priv, IO_ACX_PHY_CTL, 1);
+	acx_write_flush(priv);
+	acxlog(L_DEBUG, "radio PHY write 0x%02X at 0x%04X\n", value, reg);
+
+	FN_EXIT1(OK);
+	return OK;
+}
+
+
+#define NO_AUTO_INCREMENT	1
+
+/***********************************************************************
+** acx_s_write_fw
+**
+** Write the firmware image into the card.
+**
+** Arguments:
+**	priv		wlan device structure
+**	apfw_image	firmware image.
+**
+** Returns:
+**	1	firmware image corrupted
+**	0	success
+*/
+static int
+acx_s_write_fw(wlandevice_t *priv, const firmware_image_t *apfw_image, u32 offset)
+{
+	int len, size;
+	u32 sum, v32;
+	/* we skip the first four bytes which contain the control sum */
+	const u8 *image = (u8*)apfw_image + 4;
+
+	/* start the image checksum by adding the image size value */
+	sum = image[0]+image[1]+image[2]+image[3];
+	image += 4;
+
+	acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0);
+
+#if NO_AUTO_INCREMENT
+	acxlog(L_INIT, "not using auto increment for firmware loading\n");
+	acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */
+#else
+	acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */
+	acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */
+	acx_write_flush(priv);
+#endif
+
+	len = 0;
+	size = le32_to_cpu(apfw_image->size) & (~3);
+
+	while (likely(len < size)) {
+		v32 = be32_to_cpu(*(u32*)image);
+		sum += image[0]+image[1]+image[2]+image[3];
+		image += 4;
+		len += 4;
+
+#if NO_AUTO_INCREMENT
+		acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset + len - 4);
+		acx_write_flush(priv);
+#endif
+		acx_write_reg32(priv, IO_ACX_SLV_MEM_DATA, v32);
+	}
+
+	acxlog(L_DEBUG, "%s: firmware written\n", __func__);
+
+	/* compare our checksum with the stored image checksum */
+	return (sum != le32_to_cpu(apfw_image->chksum));
+}
+
+
+/***********************************************************************
+** acx_s_validate_fw
+**
+** Compare the firmware image given with
+** the firmware image written into the card.
+**
+** Arguments:
+**	priv		wlan device structure
+**   apfw_image  firmware image.
+**
+** Returns:
+**	NOT_OK	firmware image corrupted or not correctly written
+**	OK	success
+*/
+static int
+acx_s_validate_fw(wlandevice_t *priv, const firmware_image_t *apfw_image,
+				u32 offset)
+{
+	u32 v32, w32, sum;
+	int len, size;
+	int result = OK;
+	/* we skip the first four bytes which contain the control sum */
+	const u8 *image = (u8*)apfw_image + 4;
+
+	/* start the image checksum by adding the image size value */
+	sum = image[0]+image[1]+image[2]+image[3];
+	image += 4;
+
+	acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0);
+
+#if NO_AUTO_INCREMENT
+	acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */
+#else
+	acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */
+	acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */
+#endif
+
+	len = 0;
+	size = le32_to_cpu(apfw_image->size) & (~3);
+
+	while (likely(len < size)) {
+		v32 = be32_to_cpu(*(u32*)image);
+		image += 4;
+		len += 4;
+
+#if NO_AUTO_INCREMENT
+		acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset + len - 4);
+#endif
+		w32 = acx_read_reg32(priv, IO_ACX_SLV_MEM_DATA);
+
+		if (unlikely(w32 != v32)) {
+			printk("acx: FATAL: firmware upload: "
+			"data parts at offset %d don't match (0x%08X vs. 0x%08X)! "
+			"I/O timing issues or defective memory, with DWL-xx0+? "
+			"ACX_IO_WIDTH=16 may help. Please report\n",
+				len, v32, w32);
+			result = NOT_OK;
+			break;
+		}
+
+		sum += (u8)w32 + (u8)(w32>>8) + (u8)(w32>>16) + (u8)(w32>>24);
+	}
+
+	/* sum control verification */
+	if (result != NOT_OK) {
+		if (sum != le32_to_cpu(apfw_image->chksum)) {
+			printk("acx: FATAL: firmware upload: "
+				"checksums don't match!\n");
+			result = NOT_OK;
+		}
+	}
+
+	return result;
+}
+
+
+/***********************************************************************
+** acx_s_upload_fw
+**
+** Arguments:
+**	wlandevice: private device that contains card device
+** Returns:
+**	NOT_OK: failed
+**	OK: success
+** Call context:
+**	acx_reset_dev
+*/
+static int
+acx_s_upload_fw(wlandevice_t *priv)
+{
+	firmware_image_t *apfw_image = NULL;
+	int res = NOT_OK;
+	int try;
+	u32 size;
+	char filename[sizeof("tiacx1NNcNN")];
+
+	FN_ENTER;
+
+	/* Try combined, then main image */
+	priv->need_radio_fw = 0;
+	sprintf(filename, "tiacx1%02dc%02X",
+		(priv->chip_type == CHIPTYPE_ACX111)*11,
+		priv->radio_type);
+	apfw_image = acx_s_read_fw(&priv->pdev->dev, filename, &size);
+	if (!apfw_image) {
+		priv->need_radio_fw = 1;
+		filename[sizeof("tiacx1NN")-1] = '\0';
+		apfw_image = acx_s_read_fw(&priv->pdev->dev, filename, &size);
+		if (!apfw_image) {
+			FN_EXIT1(NOT_OK);
+			return NOT_OK;
+		}
+	}
+
+	for (try = 1; try <= 5; try++) {
+		res = acx_s_write_fw(priv, apfw_image, 0);
+		acxlog(L_DEBUG|L_INIT, "acx_write_fw (main/combined):%d\n", res);
+		if (OK == res) {
+			res = acx_s_validate_fw(priv, apfw_image, 0);
+			acxlog(L_DEBUG|L_INIT, "acx_validate_fw "
+					"(main/combined):%d\n", res);
+		}
+
+		if (OK == res) {
+			SET_BIT(priv->dev_state_mask, ACX_STATE_FW_LOADED);
+			break;
+		}
+		printk("acx: firmware upload attempt #%d FAILED, "
+			"retrying...\n", try);
+		acx_s_msleep(1000); /* better wait for a while... */
+	}
+
+	vfree(apfw_image);
+
+	FN_EXIT1(res);
+	return res;
+}
+
+
+/***********************************************************************
+** acx_s_upload_radio
+**
+** Uploads the appropriate radio module firmware
+** into the card.
+*/
+int
+acx_s_upload_radio(wlandevice_t *priv)
+{
+	acx_ie_memmap_t mm;
+	firmware_image_t *radio_image = NULL;
+	acx_cmd_radioinit_t radioinit;
+	int res = NOT_OK;
+	int try;
+	u32 offset;
+	u32 size;
+	char filename[sizeof("tiacx1NNrNN")];
+
+	if (!priv->need_radio_fw) return OK;
+
+	FN_ENTER;
+
+	acx_s_interrogate(priv, &mm, ACX1xx_IE_MEMORY_MAP);
+	offset = le32_to_cpu(mm.CodeEnd);
+
+	sprintf(filename, "tiacx1%02dr%02X",
+		(priv->chip_type == CHIPTYPE_ACX111)*11,
+		priv->radio_type);
+	radio_image = acx_s_read_fw(&priv->pdev->dev, filename, &size);
+	if (!radio_image) {
+		printk("acx: can't load radio module '%s'\n", filename);
+		goto fail;
+	}
+
+	acx_s_issue_cmd(priv, ACX1xx_CMD_SLEEP, NULL, 0);
+
+	for (try = 1; try <= 5; try++) {
+		res = acx_s_write_fw(priv, radio_image, offset);
+		acxlog(L_DEBUG|L_INIT, "acx_write_fw (radio): %d\n", res);
+		if (OK == res) {
+			res = acx_s_validate_fw(priv, radio_image, offset);
+			acxlog(L_DEBUG|L_INIT, "acx_validate_fw (radio): %d\n", res);
+		}
+
+		if (OK == res)
+			break;
+		printk("acx: radio firmware upload attempt #%d FAILED, "
+			"retrying...\n", try);
+		acx_s_msleep(1000); /* better wait for a while... */
+	}
+
+	acx_s_issue_cmd(priv, ACX1xx_CMD_WAKE, NULL, 0);
+	radioinit.offset = cpu_to_le32(offset);
+	/* no endian conversion needed, remains in card CPU area: */
+	radioinit.len = radio_image->size;
+
+	vfree(radio_image);
+
+	if (OK != res)
+		goto fail;
+
+	/* will take a moment so let's have a big timeout */
+	acx_s_issue_cmd_timeo(priv, ACX1xx_CMD_RADIOINIT,
+		&radioinit, sizeof(radioinit), CMD_TIMEOUT_MS(1000));
+
+	res = acx_s_interrogate(priv, &mm, ACX1xx_IE_MEMORY_MAP);
+	if (OK != res) {
+		printk("%s: error reading memory map\n", priv->netdev->name);
+	}
+fail:
+	FN_EXIT1(res);
+	return res;
+}
+
+
+/***********************************************************************
+** acx_l_reset_mac
+**
+** Arguments:
+**	wlandevice: private device that contains card device
+** Side effects:
+**	MAC will be reset
+** Call context:
+**	acx_reset_dev
+** Comment:
+**	resets onboard acx100 MAC
+**
+** Requires lock to be taken
+*/
+static void
+acx_l_reset_mac(wlandevice_t *priv)
+{
+	u16 temp;
+
+	FN_ENTER;
+
+	/* halt eCPU */
+	temp = acx_read_reg16(priv, IO_ACX_ECPU_CTRL) | 0x1;
+	acx_write_reg16(priv, IO_ACX_ECPU_CTRL, temp);
+
+	/* now do soft reset of eCPU */
+	temp = acx_read_reg16(priv, IO_ACX_SOFT_RESET) | 0x1;
+	acxlog(L_DEBUG, "%s: enable soft reset...\n", __func__);
+	acx_write_reg16(priv, IO_ACX_SOFT_RESET, temp);
+	acx_write_flush(priv);
+
+	/* now reset bit again */
+	acxlog(L_DEBUG, "%s: disable soft reset and go to init mode...\n", __func__);
+	/* deassert eCPU reset */
+	acx_write_reg16(priv, IO_ACX_SOFT_RESET, temp & ~0x1);
+
+	/* now start a burst read from initial flash EEPROM */
+	temp = acx_read_reg16(priv, IO_ACX_EE_START) | 0x1;
+	acx_write_reg16(priv, IO_ACX_EE_START, temp);
+	acx_write_flush(priv);
+
+	FN_EXIT0;
+}
+
+
+/***********************************************************************
+** acx_s_verify_init
+*/
+static int
+acx_s_verify_init(wlandevice_t *priv)
+{
+	int result = NOT_OK;
+	int timer;
+
+	FN_ENTER;
+
+	for (timer = 40; timer > 0; timer--) {
+		u16 irqstat = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_NON_DES);
+		if (irqstat & HOST_INT_FCS_THRESHOLD) {
+			result = OK;
+			acx_write_reg16(priv, IO_ACX_IRQ_ACK, HOST_INT_FCS_THRESHOLD);
+			break;
+		}
+		/* HZ / 50 resulted in 24 schedules for ACX100 on my machine,
+		 * so better schedule away longer for greater efficiency,
+		 * decrease loop count */
+		acx_s_msleep(50);
+	}
+
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/***********************************************************************
+** acx_cmd_status_str
+*/
+static const char*
+acx_cmd_status_str(unsigned int state)
+{
+	static const char * const cmd_error_strings[] = {
+		"Idle",
+		"Success",
+		"Unknown Command",
+		"Invalid Information Element",
+		"Channel rejected",
+		"Channel invalid in current regulatory domain",
+		"MAC invalid",
+		"Command rejected (read-only information element)",
+		"Command rejected",
+		"Already asleep",
+		"TX in progress",
+		"Already awake",
+		"Write only",
+		"RX in progress",
+		"Invalid parameter",
+		"Scan in progress",
+		"Failed"
+	};
+	return state < VEC_SIZE(cmd_error_strings) ?
+			cmd_error_strings[state] : "UNKNOWN REASON";
+}
+
+
+/***********************************************************************
+** A few low-level helpers
+**
+** Note: these functions are not protected by lock
+** and thus are never allowed to be called from IRQ.
+** Also they must not race with fw upload which uses same hw regs
+*/
+
+/***********************************************************************
+** acx_read_info_status
+*/
+/* Info mailbox format:
+2 bytes: type
+2 bytes: status
+more bytes may follow
+    docs say about status:
+	0x0000 info available (set by hw)
+	0x0001 information received (must be set by host)
+	0x1000 info available, mailbox overflowed (messages lost) (set by hw)
+    but in practice we've seen:
+	0x9000 when we did not set status to 0x0001 on prev message
+	0x1001 when we did set it
+	0x0000 was never seen
+    conclusion: this is really a bitfield:
+    0x1000 is 'info available' bit
+    'mailbox overflowed' bit is 0x8000, not 0x1000
+    value of 0x0000 probably means that there is no message at all
+    P.S. I dunno how in hell hw is supposed to notice that messages are lost -
+    it does NOT clear bit 0x0001, and this bit will probably stay forever set
+    after we set it once. Let's hope this will be fixed in firmware someday
+*/
+static void
+acx_read_info_status(wlandevice_t *priv)
+{
+	u32 value;
+
+	acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0x0);
+	acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x1);
+
+	acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR,
+		acx_read_reg32(priv, IO_ACX_INFO_MAILBOX_OFFS));
+
+	/* make sure we only read the data once all cfg registers are written: */
+	acx_write_flush(priv);
+	value = acx_read_reg32(priv, IO_ACX_SLV_MEM_DATA);
+
+	priv->info_type = (u16)value;
+	priv->info_status = (value >> 16);
+
+	/* inform hw that we have read this info message */
+	acx_write_reg32(priv, IO_ACX_SLV_MEM_DATA, priv->info_type | 0x00010000);
+	acx_write_flush(priv);
+	/* now bother hw to notice it: */
+	acx_write_reg16(priv, IO_ACX_INT_TRIG, INT_TRIG_INFOACK);
+	acx_write_flush(priv);
+
+	acxlog(L_CTL, "info_type 0x%04X, info_status 0x%04X\n",
+			priv->info_type, priv->info_status);
+}
+
+
+/***********************************************************************
+** acx_write_cmd_type_or_status
+*/
+static void
+acx_write_cmd_type_or_status(wlandevice_t *priv, u32 val)
+{
+	acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0x0);
+	acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x1); /* FIXME: why auto increment?? */
+
+	acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR,
+		acx_read_reg32(priv, IO_ACX_CMD_MAILBOX_OFFS));
+
+	/* make sure we only write the data once all config registers are written */
+	acx_write_flush(priv);
+	acx_write_reg32(priv, IO_ACX_SLV_MEM_DATA, val);
+	acx_write_flush(priv);
+}
+static inline void
+acx_write_cmd_type(wlandevice_t *priv, u32 val)
+{
+	acx_write_cmd_type_or_status(priv, val);
+}
+static inline void
+acx_write_cmd_status(wlandevice_t *priv, u32 val)
+{
+	acx_write_cmd_type_or_status(priv, val<<16);
+}
+
+
+/***********************************************************************
+** acx_read_cmd_status
+*/
+static void
+acx_read_cmd_status(wlandevice_t *priv)
+{
+	u32 value;
+
+	acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0x0);
+	acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x1); /* FIXME: why auto increment?? */
+
+	acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR,
+		acx_read_reg32(priv, IO_ACX_CMD_MAILBOX_OFFS));
+
+	/* make sure we only read the data once all config registers are written */
+	acx_write_flush(priv);
+	value = acx_read_reg32(priv, IO_ACX_SLV_MEM_DATA);
+
+	priv->cmd_type = (u16)value;
+	priv->cmd_status = (value >> 16);
+
+	acxlog(L_CTL, "cmd_type 0x%04X, cmd_status 0x%04X [%s]\n",
+		priv->cmd_type, priv->cmd_status,
+		acx_cmd_status_str(priv->cmd_status));
+}
+
+
+/***********************************************************************
+** acx_s_reset_dev
+**
+** Arguments:
+**	netdevice that contains the wlandevice priv variable
+** Returns:
+**	NOT_OK on fail
+**	OK on success
+** Side effects:
+**	device is hard reset
+** Call context:
+**	acx_probe_pci
+** Comment:
+**	This resets the acx100 device using low level hardware calls
+**	as well as uploads and verifies the firmware to the card
+*/
+static int
+acx_s_reset_dev(netdevice_t *dev)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	const char* msg = "";
+	unsigned long flags;
+	int result = NOT_OK;
+	u16 hardware_info;
+	u16 ecpu_ctrl;
+
+	FN_ENTER;
+
+	/* we're doing a reset, so hardware is unavailable */
+
+	/* reset the device to make sure the eCPU is stopped
+	 * to upload the firmware correctly */
+
+	acx_lock(priv, flags);
+
+	acx_l_reset_mac(priv);
+
+	ecpu_ctrl = acx_read_reg16(priv, IO_ACX_ECPU_CTRL) & 1;
+	if (!ecpu_ctrl) {
+		msg = "eCPU is already running. ";
+		goto fail_unlock;
+	}
+
+#if WE_DONT_NEED_THAT_DO_WE
+	if (acx_read_reg16(priv, IO_ACX_SOR_CFG) & 2) {
+		/* eCPU most likely means "embedded CPU" */
+		msg = "eCPU did not start after boot from flash. ";
+		goto fail_unlock;
+	}
+
+	/* check sense on reset flags */
+	if (acx_read_reg16(priv, IO_ACX_SOR_CFG) & 0x10) {
+		printk("%s: eCPU did not start after boot (SOR), "
+			"is this fatal?\n", dev->name);
+	}
+#endif
+	/* scan, if any, is stopped now, setting corresponding IRQ bit */
+	priv->irq_status |= HOST_INT_SCAN_COMPLETE;
+
+	acx_unlock(priv, flags);
+
+	/* without this delay acx100 may fail to report hardware_info
+	** (see below). Most probably eCPU runs some init code */
+	acx_s_msleep(10);
+
+	/* Need to know radio type before fw load */
+	hardware_info = acx_read_reg16(priv, IO_ACX_EEPROM_INFORMATION);
+	priv->form_factor = hardware_info & 0xff;
+	priv->radio_type = hardware_info >> 8;
+
+	/* load the firmware */
+	if (OK != acx_s_upload_fw(priv))
+		goto fail;
+
+	acx_s_msleep(10);
+
+	/* now start eCPU by clearing bit */
+	acxlog(L_DEBUG, "booted eCPU up and waiting for completion...\n");
+	acx_write_reg16(priv, IO_ACX_ECPU_CTRL, ecpu_ctrl & ~0x1);
+
+	/* wait for eCPU bootup */
+	if (OK != acx_s_verify_init(priv)) {
+		msg = "timeout waiting for eCPU. ";
+		goto fail;
+	}
+
+	acxlog(L_DEBUG, "eCPU has woken up, card is ready to be configured\n");
+
+	if (priv->chip_type == CHIPTYPE_ACX111) {
+		acxlog(L_DEBUG, "cleaning up cmd mailbox access area\n");
+		acx_write_cmd_status(priv, 0);
+		acx_read_cmd_status(priv);
+		if (priv->cmd_status) {
+			msg = "error cleaning cmd mailbox area. ";
+			goto fail;
+		}
+	}
+
+	/* TODO what is this one doing ?? adapt for acx111 */
+	if ((OK != acx_read_eeprom_area(priv))
+	 && (CHIPTYPE_ACX100 == priv->chip_type)) {
+		/* does "CIS" mean "Card Information Structure"?
+		 * If so, then this would be a PCMCIA message...
+		 */
+		msg = "CIS error. ";
+		goto fail;
+	}
+
+	result = OK;
+	FN_EXIT1(result);
+	return result;
+
+/* Finish error message. Indicate which function failed */
+fail_unlock:
+	acx_unlock(priv, flags);
+fail:
+	printk("acx: %sreset_dev() FAILED\n", msg);
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/***********************************************************************
+** acx_init_mboxes
+*/
+void
+acx_init_mboxes(wlandevice_t *priv)
+{
+	u32 cmd_offs, info_offs;
+
+	FN_ENTER;
+
+	cmd_offs = acx_read_reg32(priv, IO_ACX_CMD_MAILBOX_OFFS);
+	info_offs = acx_read_reg32(priv, IO_ACX_INFO_MAILBOX_OFFS);
+	priv->cmd_area = (u8 *)priv->iobase2 + cmd_offs + 0x4;
+	priv->info_area = (u8 *)priv->iobase2 + info_offs + 0x4;
+	acxlog(L_DEBUG, "iobase2=%p\n"
+		"cmd_mbox_offset=%X cmd_area=%p\n"
+		"info_mbox_offset=%X info_area=%p\n",
+		priv->iobase2,
+		cmd_offs, priv->cmd_area,
+		info_offs, priv->info_area);
+
+	FN_EXIT0;
+}
+
+
+/*----------------------------------------------------------------
+* acx_s_issue_cmd_timeo
+* Excecutes a command in the command mailbox
+*
+* Arguments:
+*   *pcmdparam = an pointer to the data. The data mustn't include
+*                the 4 byte command header!
+*
+* NB: we do _not_ take lock inside, so be sure to not touch anything
+* which may interfere with IRQ handler operation
+*
+* TODO: busy wait is a bit silly, so:
+* 1) stop doing many iters - go to sleep after first
+* 2) go to waitqueue based approach: wait, not poll!
+*----------------------------------------------------------------*/
+#undef FUNC
+#define FUNC "issue_cmd"
+
+#if !ACX_DEBUG
+int
+acx_s_issue_cmd_timeo(
+	wlandevice_t *priv,
+	unsigned int cmd,
+	void *pcmdparam,
+	unsigned paramlen,
+	unsigned timeout)
+{
+#else
+int
+acx_s_issue_cmd_timeo_debug(
+	wlandevice_t *priv,
+	unsigned cmd,
+	void *pcmdparam,
+	unsigned paramlen,
+	unsigned timeout,
+	const char* cmdstr)
+{
+	unsigned long start = jiffies;
+#endif
+	unsigned counter;
+	int result = NOT_OK;
+	u16 irqtype;
+	u16 cmd_status;
+
+	FN_ENTER;
+
+	acxlog(L_CTL, FUNC"(cmd:%s,timeout:%ums)\n", cmdstr, timeout/100);
+
+	if (!(priv->dev_state_mask & ACX_STATE_FW_LOADED)) {
+		acxlog(L_CTL, "firmware not loaded yet, "
+				"cannot execute command!\n");
+		goto done;
+	}
+
+	if ((acx_debug & L_DEBUG) && (cmd != ACX1xx_CMD_INTERROGATE)) {
+		printk("input pdr (len=%u):\n", paramlen);
+		acx_dump_bytes(pcmdparam, paramlen);
+	}
+
+	/* wait for firmware to become idle for our command submission */
+	counter = 199; /* in ms */
+	do {
+		acx_read_cmd_status(priv);
+		/* Test for IDLE state */
+		if (!priv->cmd_status)
+			break;
+		if (counter % 10 == 0) {
+			/* we waited 10 iterations, no luck. Sleep 10 ms */
+			acx_s_msleep(10);
+		}
+	} while (--counter);
+
+	if (!counter) {
+		/* the card doesn't get idle, we're in trouble */
+		printk("%s: trying to issue firmware command  "
+			"but Command Register is not IDLE: 0x%04X\n",
+			priv->netdev->name, priv->cmd_status);
+		goto done;
+	} else if (counter < 199) { /* if waited >0ms... */
+		acxlog(L_CTL|L_DEBUG, FUNC"(): waited for idle %dms. "
+			"Please report\n", 199 - counter);
+	}
+
+	/* now write the parameters of the command if needed */
+	if (pcmdparam && paramlen) {
+		/* if it's an INTERROGATE command, just pass the length
+		 * of parameters to read, as data */
+		memcpy(priv->cmd_area, pcmdparam,
+			(cmd == ACX1xx_CMD_INTERROGATE) ? 4 : paramlen);
+	}
+	/* now write the actual command type */
+	priv->cmd_type = cmd;
+	acx_write_cmd_type(priv, cmd);
+	/* execute command */
+	acx_write_reg16(priv, IO_ACX_INT_TRIG, INT_TRIG_CMD);
+	acx_write_flush(priv);
+
+	/* wait for firmware to process command */
+
+	/* make sure we have at least *some* timeout value */
+	if (unlikely(timeout > 120000))
+		timeout = 120000;
+	timeout = ((timeout/100)-1) | 1; /* in ms, nonzero */
+	/* clear it. can be set only by IRQ handler: */
+	priv->irq_status &= ~HOST_INT_CMD_COMPLETE;
+
+	/* we schedule away sometimes (timeout can be large) */
+	counter = timeout;
+	do {
+		if (!priv->irqs_active) { /* IRQ disabled: poll */
+			irqtype = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_NON_DES);
+			if (irqtype & HOST_INT_CMD_COMPLETE) {
+				acx_write_reg16(priv, IO_ACX_IRQ_ACK, HOST_INT_CMD_COMPLETE);
+				break;
+			}
+		} else { /* Wait when IRQ will set the bit */
+			irqtype = priv->irq_status;
+			if (irqtype & HOST_INT_CMD_COMPLETE)
+				break;
+		}
+
+		if (counter % 10 == 0) {
+			/* we waited 10 iterations, no luck. Sleep 10 ms */
+			acx_s_msleep(10);
+		}
+	} while (--counter);
+
+	/* save state for debugging */
+	acx_read_cmd_status(priv);
+	cmd_status = priv->cmd_status;
+
+	/* put the card in IDLE state */
+	priv->cmd_status = 0;
+	acx_write_cmd_status(priv, 0);
+
+	if (!counter) {	/* timed out! */
+		printk("%s: "FUNC"(0x%04X) timed out %s for Cmd_Complete. "
+			"irq bits:0x%04X irq status:0x%04X timeout:%dms "
+			"cmd status:%d (%s). Bailing\n",
+			priv->netdev->name, cmd,
+			(priv->irqs_active) ? "waiting" : "polling",
+			irqtype, priv->irq_status, timeout,
+			cmd_status, acx_cmd_status_str(cmd_status));
+		if (acx_debug)
+			dump_stack();
+		goto done;
+	} else if (timeout - counter > 1) { /* if waited >1ms... */
+		acxlog(L_CTL|L_DEBUG, FUNC"(%s): %s for Cmd_Complete %dms. count:%d. "
+			"Please report\n", cmdstr,
+			(priv->irqs_active) ? "waited" : "polled",
+			timeout - counter, counter);
+	}
+
+	if (1 != cmd_status) { /* it is not a 'Success' */
+		printk("%s: "FUNC"(0x%04X) FAILED: %d (%s). Took %dms of %d\n",
+			priv->netdev->name, cmd,
+			cmd_status, acx_cmd_status_str(cmd_status),
+			timeout - counter, timeout);
+		if (acx_debug & L_CTL)
+			dump_stack();
+		/* zero out result buffer */
+		if (pcmdparam && paramlen)
+			memset(pcmdparam, 0, paramlen);
+		goto done;
+	}
+
+	/* read in result parameters if needed */
+	if (pcmdparam && paramlen && (cmd == ACX1xx_CMD_INTERROGATE)) {
+		memcpy(pcmdparam, priv->cmd_area, paramlen);
+		if (acx_debug & L_DEBUG) {
+			printk("output pdr (len=%u): ", paramlen);
+			acx_dump_bytes(pcmdparam, paramlen);
+		}
+	}
+	result = OK;
+done:
+	acxlog(L_CTL, FUNC"(%s): took %ld jiffies to complete\n",
+			 cmdstr, jiffies-start);
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_s_get_firmware_version
+*----------------------------------------------------------------*/
+static void
+acx_s_get_firmware_version(wlandevice_t *priv)
+{
+	fw_ver_t fw;
+	u8 hexarr[4] = { 0, 0, 0, 0 };
+	int hexidx = 0, val = 0;
+	const char *num;
+	char c;
+
+	FN_ENTER;
+
+	acx_s_interrogate(priv, &fw, ACX1xx_IE_FWREV);
+	memcpy(priv->firmware_version, fw.fw_id, FW_ID_SIZE);
+	priv->firmware_version[FW_ID_SIZE] = '\0';
+	acxlog(L_DEBUG, "fw_ver: fw_id='%s' hw_id=%08X\n",
+				priv->firmware_version, fw.hw_id);
+
+	if (strncmp(fw.fw_id, "Rev ", 4) != 0) {
+		printk("acx: strange firmware version string "
+			"'%s', please report\n", priv->firmware_version);
+		priv->firmware_numver = 0x01090407; /* assume 1.9.4.7 */
+	} else {
+		num = &fw.fw_id[4];
+		while (1) {
+			c = *num++;
+			if ((c == '.') || (c == '\0')) {
+				hexarr[hexidx++] = val;
+				if ((hexidx > 3) || (c == '\0')) /* end? */
+					break;
+				val = 0;
+				continue;
+			}
+			if ((c >= '0') && (c <= '9'))
+				c -= '0';
+			else
+				c = c - 'a' + (char)10;
+			val = val*16 + c;
+		}
+
+		priv->firmware_numver = (u32)(
+				(hexarr[0] << 24) + (hexarr[1] << 16)
+				+ (hexarr[2] << 8) + hexarr[3]);
+		acxlog(L_DEBUG, "firmware_numver 0x%08X\n", priv->firmware_numver);
+	}
+	if (priv->chip_type == CHIPTYPE_ACX111) {
+		if (priv->firmware_numver == 0x00010011) {
+			/* This one does not survive floodpinging */
+			printk("acx: firmware '%s' is known to be buggy, "
+				"please upgrade\n", priv->firmware_version);
+		}
+		if (priv->firmware_numver == 0x02030131) {
+			/* With this one, all rx packets look mangled
+			** Most probably we simply do not know how to use it
+			**  properly */
+			printk("acx: firmware '%s' does not work well "
+				"with this driver\n", priv->firmware_version);
+		}
+	}
+
+	priv->firmware_id = le32_to_cpu(fw.hw_id);
+
+	/* we're able to find out more detailed chip names now */
+	switch (priv->firmware_id & 0xffff0000) {
+		case 0x01010000:
+		case 0x01020000:
+			priv->chip_name = name_tnetw1100a;
+			break;
+		case 0x01030000:
+			priv->chip_name = name_tnetw1100b;
+			break;
+		case 0x03000000:
+		case 0x03010000:
+			priv->chip_name = name_tnetw1130;
+			break;
+		default:
+			printk("acx: unknown chip ID 0x%08X, "
+				"please report\n", priv->firmware_id);
+			break;
+	}
+
+	FN_EXIT0;
+}
+
+
+/*----------------------------------------------------------------
+* acx_display_hardware_details
+*
+* Arguments:
+*	priv: ptr to wlandevice that contains all the details
+*	  displayed by this function
+* Call context:
+*	acx_probe_pci
+* Comment:
+*	This function will display strings to the system log according
+* to device form_factor and radio type. It will needed to be
+*----------------------------------------------------------------*/
+static void
+acx_display_hardware_details(wlandevice_t *priv)
+{
+	const char *radio_str, *form_str;
+
+	FN_ENTER;
+
+	switch (priv->radio_type) {
+	case RADIO_MAXIM_0D:
+		/* hmm, the DWL-650+ seems to have two variants,
+		 * according to a windows driver changelog comment:
+		 * RFMD and Maxim. */
+		radio_str = "Maxim";
+		break;
+	case RADIO_RFMD_11:
+		radio_str = "RFMD";
+		break;
+	case RADIO_RALINK_15:
+		radio_str = "Ralink";
+		break;
+	case RADIO_RADIA_16:
+		radio_str = "Radia";
+		break;
+	case RADIO_UNKNOWN_17:
+		/* TI seems to have a radio which is
+		 * additionally 802.11a capable, too */
+		radio_str = "802.11a/b/g radio?! Please report";
+		break;
+	case RADIO_UNKNOWN_19:
+		radio_str = "A radio used by Safecom cards?! Please report";
+		break;
+	default:
+		radio_str = "UNKNOWN, please report the radio type name!";
+		break;
+	}
+
+	switch (priv->form_factor) {
+	case 0x00:
+		form_str = "unspecified";
+		break;
+	case 0x01:
+		form_str = "(mini-)PCI / CardBus";
+		break;
+	case 0x02:
+		form_str = "USB";
+		break;
+	case 0x03:
+		form_str = "Compact Flash";
+		break;
+	default:
+		form_str = "UNKNOWN, Please report";
+		break;
+	}
+
+	printk("acx: form factor 0x%02X (%s), "
+		"radio type 0x%02X (%s), EEPROM version 0x%02X, "
+		"uploaded firmware '%s' (0x%08X)\n",
+		priv->form_factor, form_str, priv->radio_type, radio_str,
+		priv->eeprom_version, priv->firmware_version,
+		priv->firmware_id);
+
+	FN_EXIT0;
+}
+
+/***********************************************************************
+*/
+#ifdef NONESSENTIAL_FEATURES
+typedef struct device_id {
+	unsigned char id[6];
+	char *descr;
+	char *type;
+} device_id_t;
+
+static const device_id_t
+device_ids[] =
+{
+	{
+		{'G', 'l', 'o', 'b', 'a', 'l'},
+		NULL,
+		NULL,
+	},
+	{
+		{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+		"uninitialized",
+		"SpeedStream SS1021 or Gigafast WF721-AEX"
+	},
+	{
+		{0x80, 0x81, 0x82, 0x83, 0x84, 0x85},
+		"non-standard",
+		"DrayTek Vigor 520"
+	},
+	{
+		{'?', '?', '?', '?', '?', '?'},
+		"non-standard",
+		"Level One WPC-0200"
+	},
+	{
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		"empty",
+		"DWL-650+ variant"
+	}
+};
+
+static void
+acx_show_card_eeprom_id(wlandevice_t *priv)
+{
+	unsigned char buffer[CARD_EEPROM_ID_SIZE];
+	int i;
+
+	memset(&buffer, 0, CARD_EEPROM_ID_SIZE);
+	/* use direct EEPROM access */
+	for (i = 0; i < CARD_EEPROM_ID_SIZE; i++) {
+		if (OK != acx_read_eeprom_offset(priv,
+					 ACX100_EEPROM_ID_OFFSET + i,
+					 &buffer[i]))
+		{
+			printk("acx: reading EEPROM FAILED\n");
+			break;
+		}
+	}
+
+	for (i = 0; i < VEC_SIZE(device_ids); i++) {
+		if (!memcmp(&buffer, device_ids[i].id, CARD_EEPROM_ID_SIZE)) {
+			if (device_ids[i].descr) {
+				printk("acx: EEPROM card ID string check "
+					"found %s card ID: is this %s?\n",
+					device_ids[i].descr, device_ids[i].type);
+			}
+			break;
+		}
+	}
+	if (i == VEC_SIZE(device_ids)) {
+		printk("acx: EEPROM card ID string check found "
+			"unknown card: expected 'Global', got '%.*s\'. "
+			"Please report\n", CARD_EEPROM_ID_SIZE, buffer);
+	}
+}
+#endif /* NONESSENTIAL_FEATURES */
+
+
+/***********************************************************************
+*/
+static void
+acx_s_device_chain_add(struct net_device *dev)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+
+	down(&root_acx_dev_sem);
+	priv->prev_nd = root_acx_dev.newest;
+	root_acx_dev.newest = dev;
+	priv->netdev = dev;
+	up(&root_acx_dev_sem);
+}
+
+static void
+acx_s_device_chain_remove(struct net_device *dev)
+{
+	struct net_device *querydev;
+	struct net_device *olderdev;
+	struct net_device *newerdev;
+
+	down(&root_acx_dev_sem);
+	querydev = root_acx_dev.newest;
+	newerdev = NULL;
+	while (NULL != querydev) {
+		olderdev = ((struct wlandevice *) querydev->priv)->prev_nd;
+		if (0 == strcmp(querydev->name, dev->name)) {
+			if (NULL == newerdev) {
+				/* if we were at the beginning of the
+				 * list, then it's the list head that
+				 * we need to update to point at the
+				 * next older device */
+				root_acx_dev.newest = olderdev;
+			} else {
+				/* it's the device that is newer than us
+				 * that we need to update to point at
+				 * the device older than us */
+				((struct wlandevice *) newerdev->priv)->
+				    prev_nd = olderdev;
+			}
+			break;
+		}
+		/* "newerdev" is actually the device of the old iteration,
+		 * but since the list starts (root_acx_dev.newest)
+		 * with the newest devices,
+		 * it's newer than the ones following.
+		 * Oh the joys of iterating from newest to oldest :-\ */
+		newerdev = querydev;
+
+		/* keep checking old devices for matches until we hit the end
+		 * of the list */
+		querydev = olderdev;
+	}
+	up(&root_acx_dev_sem);
+}
+
+
+/*----------------------------------------------------------------
+* acx_free_desc_queues
+*
+*	Releases the queues that have been allocated, the
+*	others have been initialised to NULL in acx100.c so this
+*	function can be used if only part of the queues were
+*	allocated.
+*----------------------------------------------------------------*/
+static inline void
+acx_free_coherent(struct pci_dev *hwdev, size_t size,
+			void *vaddr, dma_addr_t dma_handle)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 53)
+	dma_free_coherent(hwdev == NULL ? NULL : &hwdev->dev,
+			size, vaddr, dma_handle);
+#else
+	pci_free_consistent(hwdev, size, vaddr, dma_handle);
+#endif
+}
+
+void
+acx_free_desc_queues(TIWLAN_DC *pDc)
+{
+#define ACX_FREE_QUEUE(size, ptr, phyaddr) \
+	if (ptr) { \
+		acx_free_coherent(0, size, ptr, phyaddr); \
+		ptr = NULL; \
+		size = 0; \
+	}
+
+	FN_ENTER;
+
+	ACX_FREE_QUEUE(pDc->TxHostDescQPoolSize, pDc->pTxHostDescQPool, pDc->TxHostDescQPoolPhyAddr);
+	ACX_FREE_QUEUE(pDc->TxBufferPoolSize, pDc->pTxBufferPool, pDc->TxBufferPoolPhyAddr);
+
+	pDc->pTxDescQPool = NULL;
+	pDc->tx_pool_count = 0;
+
+	ACX_FREE_QUEUE(pDc->RxHostDescQPoolSize, pDc->pRxHostDescQPool, pDc->RxHostDescQPoolPhyAddr);
+	ACX_FREE_QUEUE(pDc->RxBufferPoolSize, pDc->pRxBufferPool, pDc->RxBufferPoolPhyAddr);
+
+	pDc->pRxDescQPool = NULL;
+	pDc->rx_pool_count = 0;
+
+	FN_EXIT0;
+}
+
+
+/*----------------------------------------------------------------
+* acx_s_delete_dma_regions
+*----------------------------------------------------------------*/
+static void
+acx_s_delete_dma_regions(wlandevice_t *priv)
+{
+	unsigned long flags;
+
+	FN_ENTER;
+	/* disable radio Tx/Rx. Shouldn't we use the firmware commands
+	 * here instead? Or are we that much down the road that it's no
+	 * longer possible here? */
+	acx_write_reg16(priv, IO_ACX_ENABLE, 0);
+
+	acx_s_msleep(100);
+
+	acx_lock(priv, flags);
+	acx_free_desc_queues(&priv->dc);
+	acx_unlock(priv, flags);
+
+	FN_EXIT0;
+}
+
+
+/*----------------------------------------------------------------
+* acx_e_probe_pci
+*
+* Probe routine called when a PCI device w/ matching ID is found.
+* Here's the sequence:
+*   - Allocate the PCI resources.
+*   - Read the PCMCIA attribute memory to make sure we have a WLAN card
+*   - Reset the MAC
+*   - Initialize the dev and wlan data
+*   - Initialize the MAC
+*
+* Arguments:
+*	pdev		ptr to pci device structure containing info about
+*			pci configuration.
+*	id		ptr to the device id entry that matched this device.
+*
+* Returns:
+*	zero		- success
+*	negative	- failed
+*
+* Call context:
+*	process thread
+----------------------------------------------------------------*/
+static const u16
+IO_ACX100[] =
+{
+	0x0000, /* IO_ACX_SOFT_RESET */
+
+	0x0014, /* IO_ACX_SLV_MEM_ADDR */
+	0x0018, /* IO_ACX_SLV_MEM_DATA */
+	0x001c, /* IO_ACX_SLV_MEM_CTL */
+	0x0020, /* IO_ACX_SLV_END_CTL */
+
+	0x0034, /* IO_ACX_FEMR */
+
+	0x007c, /* IO_ACX_INT_TRIG */
+	0x0098, /* IO_ACX_IRQ_MASK */
+	0x00a4, /* IO_ACX_IRQ_STATUS_NON_DES */
+	0x00a8, /* IO_ACX_IRQ_STATUS_CLEAR */
+	0x00ac, /* IO_ACX_IRQ_ACK */
+	0x00b0, /* IO_ACX_HINT_TRIG */
+
+	0x0104, /* IO_ACX_ENABLE */
+
+	0x0250, /* IO_ACX_EEPROM_CTL */
+	0x0254, /* IO_ACX_EEPROM_ADDR */
+	0x0258, /* IO_ACX_EEPROM_DATA */
+	0x025c, /* IO_ACX_EEPROM_CFG */
+
+	0x0268, /* IO_ACX_PHY_ADDR */
+	0x026c, /* IO_ACX_PHY_DATA */
+	0x0270, /* IO_ACX_PHY_CTL */
+
+	0x0290, /* IO_ACX_GPIO_OE */
+
+	0x0298, /* IO_ACX_GPIO_OUT */
+
+	0x02a4, /* IO_ACX_CMD_MAILBOX_OFFS */
+	0x02a8, /* IO_ACX_INFO_MAILBOX_OFFS */
+	0x02ac, /* IO_ACX_EEPROM_INFORMATION */
+
+	0x02d0, /* IO_ACX_EE_START */
+	0x02d4, /* IO_ACX_SOR_CFG */
+	0x02d8 /* IO_ACX_ECPU_CTRL */
+};
+
+static const u16
+IO_ACX111[] =
+{
+	0x0000, /* IO_ACX_SOFT_RESET */
+
+	0x0014, /* IO_ACX_SLV_MEM_ADDR */
+	0x0018, /* IO_ACX_SLV_MEM_DATA */
+	0x001c, /* IO_ACX_SLV_MEM_CTL */
+	0x0020, /* IO_ACX_SLV_END_CTL */
+
+	0x0034, /* IO_ACX_FEMR */
+
+	0x00b4, /* IO_ACX_INT_TRIG */
+	0x00d4, /* IO_ACX_IRQ_MASK */
+	/* we need NON_DES (0xf0), not NON_DES_MASK which is at 0xe0: */
+	0x00f0, /* IO_ACX_IRQ_STATUS_NON_DES */
+	0x00e4, /* IO_ACX_IRQ_STATUS_CLEAR */
+	0x00e8, /* IO_ACX_IRQ_ACK */
+	0x00ec, /* IO_ACX_HINT_TRIG */
+
+	0x01d0, /* IO_ACX_ENABLE */
+
+	0x0338, /* IO_ACX_EEPROM_CTL */
+	0x033c, /* IO_ACX_EEPROM_ADDR */
+	0x0340, /* IO_ACX_EEPROM_DATA */
+	0x0344, /* IO_ACX_EEPROM_CFG */
+
+	0x0350, /* IO_ACX_PHY_ADDR */
+	0x0354, /* IO_ACX_PHY_DATA */
+	0x0358, /* IO_ACX_PHY_CTL */
+
+	0x0374, /* IO_ACX_GPIO_OE */
+
+	0x037c, /* IO_ACX_GPIO_OUT */
+
+	0x0388, /* IO_ACX_CMD_MAILBOX_OFFS */
+	0x038c, /* IO_ACX_INFO_MAILBOX_OFFS */
+	0x0390, /* IO_ACX_EEPROM_INFORMATION */
+
+	0x0100, /* IO_ACX_EE_START */
+	0x0104, /* IO_ACX_SOR_CFG */
+	0x0108, /* IO_ACX_ECPU_CTRL */
+};
+
+static int __devinit
+acx_e_probe_pci(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	unsigned long mem_region1 = 0;
+	unsigned long mem_region2 = 0;
+	unsigned long mem_region1_size;
+	unsigned long mem_region2_size;
+	unsigned long phymem1;
+	unsigned long phymem2;
+	void *mem1 = NULL;
+	void *mem2 = NULL;
+	wlandevice_t *priv = NULL;
+	struct net_device *dev = NULL;
+	const char *chip_name;
+	int result = -EIO;
+	int err;
+	u8 chip_type;
+
+#if SEPARATE_DRIVER_INSTANCES
+	struct pci_dev *tdev;
+	unsigned int inited;
+	static int turn = 0;
+#endif /* SEPARATE_DRIVER_INSTANCES */
+
+	FN_ENTER;
+
+#if SEPARATE_DRIVER_INSTANCES
+	if (card) {
+		turn++;
+		inited = 0;
+		pci_for_each_dev(tdev) {
+			if (tdev->vendor != PCI_VENDOR_ID_TI)
+				continue;
+
+			if (tdev == pdev)
+				break;
+			if (pci_get_drvdata(tdev))
+				inited++;
+		}
+		if (inited + turn != card) {
+			FN_EXIT1(result);
+			return -ENODEV;
+		}
+	}
+#endif /* SEPARATE_DRIVER_INSTANCES */
+
+	/* Enable the PCI device */
+	if (pci_enable_device(pdev)) {
+		printk("acx: pci_enable_device() FAILED\n");
+		result = -ENODEV;
+		goto fail_pci_enable_device;
+	}
+
+	/* enable busmastering (required for CardBus) */
+	pci_set_master(pdev);
+
+	/* chiptype is u8 but id->driver_data is ulong
+	** Works for now (possible values are 1 and 2) */
+	chip_type = (u8)id->driver_data;
+	/* acx100 and acx111 have different PCI memory regions */
+	if (chip_type == CHIPTYPE_ACX100) {
+		chip_name = name_acx100;
+		mem_region1 = PCI_ACX100_REGION1;
+		mem_region1_size  = PCI_ACX100_REGION1_SIZE;
+
+		mem_region2 = PCI_ACX100_REGION2;
+		mem_region2_size  = PCI_ACX100_REGION2_SIZE;
+	} else if (chip_type == CHIPTYPE_ACX111) {
+		chip_name = name_acx111;
+		mem_region1 = PCI_ACX111_REGION1;
+		mem_region1_size  = PCI_ACX111_REGION1_SIZE;
+
+		mem_region2 = PCI_ACX111_REGION2;
+		mem_region2_size  = PCI_ACX111_REGION2_SIZE;
+	} else {
+		printk("acx: unknown chip type 0x%04X\n", chip_type);
+		result = -EIO;
+		goto fail_unknown_chiptype;
+	}
+
+	/* Figure out our resources */
+	phymem1 = pci_resource_start(pdev, mem_region1);
+	phymem2 = pci_resource_start(pdev, mem_region2);
+
+	if (!request_mem_region
+	    (phymem1, pci_resource_len(pdev, mem_region1), "ACX1xx_1")) {
+		printk("acx: cannot reserve PCI memory region 1 (are you sure "
+			"you have CardBus support in kernel?)\n");
+		result = -EIO;
+		goto fail_request_mem_region1;
+	}
+
+	if (!request_mem_region
+	    (phymem2, pci_resource_len(pdev, mem_region2), "ACX1xx_2")) {
+		printk("acx: cannot reserve PCI memory region 2\n");
+		result = -EIO;
+		goto fail_request_mem_region2;
+	}
+
+	mem1 = ioremap(phymem1, mem_region1_size);
+	if (NULL == mem1) {
+		printk("acx: ioremap() FAILED\n");
+		result = -EIO;
+		goto fail_ioremap1;
+	}
+
+	mem2 = ioremap(phymem2, mem_region2_size);
+	if (NULL == mem2) {
+		printk("acx: ioremap() #2 FAILED\n");
+		result = -EIO;
+		goto fail_ioremap2;
+	}
+
+	/* Log the device */
+	printk("acx: found %s-based wireless network card at %s, irq:%d, "
+		"phymem1:0x%lX, phymem2:0x%lX, mem1:0x%p, mem1_size:%ld, "
+		"mem2:0x%p, mem2_size:%ld\n",
+		chip_name, pci_name(pdev), pdev->irq, phymem1, phymem2,
+		mem1, mem_region1_size,
+		mem2, mem_region2_size);
+	acxlog(L_ANY, "initial debug setting is 0x%04X\n", acx_debug);
+
+	if (0 == pdev->irq) {
+		printk("acx: can't use IRQ 0\n");
+		result = -EIO;
+		goto fail_irq;
+	}
+
+	acxlog(L_DEBUG, "allocating %d (0x%X) bytes for wlandevice_t\n",
+			(int) sizeof(wlandevice_t), (int) sizeof(wlandevice_t));
+	priv = kmalloc(sizeof(wlandevice_t), GFP_KERNEL);
+	if (!priv) {
+		printk("acx: no memory for wlandevice_t\n");
+		result = -EIO;
+		goto fail_alloc_priv;
+	}
+
+	memset(priv, 0, sizeof(wlandevice_t));
+
+	spin_lock_init(&priv->lock);	/* initial state: unlocked */
+	/* We do not start with downed sem: we want PARANOID_LOCKING to work */
+	sema_init(&priv->sem, 1);	/* initial state: 1 (upped) */
+
+	/* since nobody can see new netdev yet, we can as well
+	** just _presume_ that we're under sem (instead of actually taking it): */
+	/* acx_sem_lock(priv); */
+
+	priv->pdev = pdev;
+	priv->chip_type = chip_type;
+	priv->chip_name = chip_name;
+	priv->io = (CHIPTYPE_ACX100 == chip_type) ? IO_ACX100 : IO_ACX111;
+
+	priv->membase = phymem1;
+	priv->iobase = mem1;
+
+	priv->membase2 = phymem2;
+	priv->iobase2 = mem2;
+
+	/* to find crashes due to weird driver access
+	 * to unconfigured interface (ifup) */
+	priv->mgmt_timer.function = (void (*)(unsigned long))0x0000dead;
+
+#ifdef NONESSENTIAL_FEATURES
+	acx_show_card_eeprom_id(priv);
+#endif /* NONESSENTIAL_FEATURES */
+
+	dev = kmalloc(sizeof(netdevice_t), GFP_KERNEL);
+	if (unlikely(NULL == dev)) {
+		printk("acx: no memory for netdevice_t\n");
+		result = -EIO;
+		goto fail_alloc_netdev;
+	}
+
+	memset(dev, 0, sizeof(netdevice_t));
+	ether_setup(dev);
+
+	/* now we have our device, so make sure the kernel doesn't try
+	 * to send packets even though we're not associated to a network yet */
+	acx_stop_queue(dev, "after setup");
+
+	dev->priv = priv;
+
+#ifdef SET_MODULE_OWNER
+	SET_MODULE_OWNER(dev);
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 70)
+	/* this define and its netdev member exist since 2.5.70 */
+	SET_NETDEV_DEV(dev, &pdev->dev);
+#endif
+
+	/* register new dev in linked list */
+	acx_s_device_chain_add(dev);
+
+	acxlog(L_IRQ | L_INIT, "using IRQ %d\n", pdev->irq);
+
+	dev->irq = pdev->irq;
+	/* TODO this is maybe incompatible to ACX111 */
+	dev->base_addr = pci_resource_start(pdev, 0);
+
+	/* need to be able to restore PCI state after a suspend */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)
+	/* 2.6.9-rc3-mm2 (2.6.9-bk4, too) introduced this shorter version,
+	   then it made its way into 2.6.10 */
+	pci_save_state(pdev);
+#else
+	pci_save_state(pdev, priv->pci_state);
+#endif
+
+	/* NB: acx_read_reg() reads may return bogus data before reset_dev().
+	** acx100 seems to be more affected than acx111 */
+
+	if (OK != acx_s_reset_dev(dev)) {
+		result = -EIO;
+		goto fail_reset;
+	}
+
+	if (dev_alloc_name(dev, "wlan%d") < 0) {
+		result = -EIO;
+		goto fail_alloc_name;
+	}
+	printk("acx: net device %s, driver compiled "
+		"against wireless extensions %d and Linux %s\n",
+		dev->name, WIRELESS_EXT, UTS_RELEASE);
+
+	/* now that device init was successful, fill remaining fields... */
+	dev->open = &acx_e_open;
+	dev->stop = &acx_e_close;
+	dev->hard_start_xmit = &acx_i_start_xmit;
+	dev->get_stats = &acx_e_get_stats;
+	dev->get_wireless_stats = &acx_e_get_wireless_stats;
+#if WIRELESS_EXT >= 13
+	dev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def;
+#else
+	dev->do_ioctl = &acx_e_ioctl_old;
+#endif
+	dev->set_multicast_list = &acx_i_set_multicast_list;
+	dev->tx_timeout = &acx_i_tx_timeout;
+	dev->watchdog_timeo = 4 * HZ;	/* 400 */
+
+	/* ok, basic setup is finished, now start initialising the card */
+
+	if (OK != acx_read_eeprom_offset(priv, 0x05, &priv->eeprom_version)) {
+		result = -EIO;
+		goto fail_read_eeprom_version;
+	}
+
+	if (OK != acx_s_init_mac(dev)) {
+		printk("acx: init_mac() FAILED\n");
+		result = -EIO;
+		goto fail_init_mac;
+	}
+	if (OK != acx_s_set_defaults(priv)) {
+		printk("%s: acx_set_defaults FAILED\n", dev->name);
+		goto fail_set_defaults;
+	}
+
+	/* needs to be after acx_s_init_mac() due to necessary init stuff */
+	acx_s_get_firmware_version(priv);
+
+	acx_display_hardware_details(priv);
+
+	pci_set_drvdata(pdev, dev);
+
+	/* ...and register the card, AFTER everything else has been set up,
+	 * since otherwise an ioctl could step on our feet due to
+	 * firmware operations happening in parallel or uninitialized data */
+	err = register_netdev(dev);
+	if (OK != err) {
+		printk("acx: register_netdev() FAILED: %d\n", err);
+		result = -EIO;
+		goto fail_register_netdev;
+	}
+
+	acx_carrier_off(dev, "on probe");
+
+#ifdef CONFIG_PROC_FS
+	if (OK != acx_proc_register_entries(dev)) {
+		result = -EIO;
+		goto fail_proc_register_entries;
+	}
+#endif
+
+	/* after register_netdev() userspace may start working with dev
+	 * (in particular, on other CPUs), we only need to up the sem */
+	/* acx_sem_unlock(priv); */
+
+	printk("acx_pci module " WLAN_RELEASE " loaded successfully\n");
+	result = OK;
+	goto done;
+
+	/* error paths: undo everything in reverse order... */
+
+#ifdef CONFIG_PROC_FS
+fail_proc_register_entries:
+
+	if (priv->dev_state_mask & ACX_STATE_IFACE_UP)
+		acx_s_down(dev);
+
+	unregister_netdev(dev);
+
+	/* after unregister_netdev() userspace is guaranteed to finish
+	 * working with it. netdev does not exist anymore.
+	 * For paranoid reasons I am taking sem anyway */
+	acx_sem_lock(priv);
+#endif
+
+fail_register_netdev:
+
+	acx_s_delete_dma_regions(priv);
+	pci_set_drvdata(pdev, NULL);
+
+fail_set_defaults:
+fail_init_mac:
+fail_read_eeprom_version:
+fail_alloc_name:
+fail_reset:
+
+	acx_s_device_chain_remove(dev);
+	kfree(dev);
+fail_alloc_netdev:
+
+	kfree(priv);
+fail_alloc_priv:
+fail_irq:
+
+	iounmap(mem2);
+fail_ioremap2:
+
+	iounmap(mem1);
+fail_ioremap1:
+
+	release_mem_region(pci_resource_start(pdev, mem_region2),
+			   pci_resource_len(pdev, mem_region2));
+fail_request_mem_region2:
+
+	release_mem_region(pci_resource_start(pdev, mem_region1),
+			   pci_resource_len(pdev, mem_region1));
+fail_request_mem_region1:
+fail_unknown_chiptype:
+
+	pci_disable_device(pdev);
+fail_pci_enable_device:
+
+	pci_set_power_state(pdev, 3);
+
+done:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_e_remove_pci
+*
+* Deallocate PCI resources for the ACX100 chip.
+*
+* This should NOT execute any other hardware operations on the card,
+* since the card might already be ejected. Instead, that should be done
+* in cleanup_module, since the card is most likely still available there.
+*
+* Arguments:
+*	pdev		ptr to PCI device structure containing info about
+*			PCI configuration.
+*
+* Call context:
+*	process thread
+----------------------------------------------------------------*/
+static void __devexit
+acx_e_remove_pci(struct pci_dev *pdev)
+{
+	struct net_device *dev;
+	wlandevice_t *priv;
+	unsigned long mem_region1 = 0, mem_region2 = 0;
+
+	FN_ENTER;
+
+	dev = (struct net_device *) pci_get_drvdata(pdev);
+	if (!dev) {
+		acxlog(L_DEBUG, "%s: card is unused. Skipping any release code\n",
+			__func__);
+		goto end;
+	}
+
+	priv = (struct wlandevice *) dev->priv;
+	if (!priv) {
+		acxlog(L_DEBUG, "%s: card is unused. Skipping any release code\n",
+			__func__);
+		goto end;
+	}
+
+	/* unregister the device to not let the kernel
+	 * (e.g. ioctls) access a half-deconfigured device
+	 * NB: this will cause acx_e_close() to be called,
+	 * thus we shouldn't call it under sem! */
+	acxlog(L_INIT, "removing device %s\n", dev->name);
+	unregister_netdev(dev);
+
+	/* unregister_netdev ensures that no references to us left.
+	 * For paranoid reasons we continue to follow the rules */
+	acx_sem_lock(priv);
+
+	if (priv->chip_type == CHIPTYPE_ACX100) {
+		mem_region1 = PCI_ACX100_REGION1;
+		mem_region2 = PCI_ACX100_REGION2;
+	} else {
+		mem_region1 = PCI_ACX111_REGION1;
+		mem_region2 = PCI_ACX111_REGION2;
+	}
+
+#ifdef CONFIG_PROC_FS
+	acx_proc_unregister_entries(dev);
+#endif
+
+	/* find our PCI device in the global acx list and remove it */
+	acx_s_device_chain_remove(dev);
+
+	if (priv->dev_state_mask & ACX_STATE_IFACE_UP)
+		acx_s_down(dev);
+
+	CLEAR_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP);
+
+	acx_s_delete_dma_regions(priv);
+
+	/* finally, clean up PCI bus state */
+	if (priv->iobase) iounmap(priv->iobase);
+	if (priv->iobase2) iounmap(priv->iobase2);
+
+	release_mem_region(pci_resource_start(pdev, mem_region1),
+			   pci_resource_len(pdev, mem_region1));
+
+	release_mem_region(pci_resource_start(pdev, mem_region2),
+			   pci_resource_len(pdev, mem_region2));
+
+	pci_disable_device(pdev);
+
+	/* remove dev registration */
+	pci_set_drvdata(pdev, NULL);
+
+	/* Free netdev (quite late,
+	 * since otherwise we might get caught off-guard
+	 * by a netdev timeout handler execution
+	 * expecting to see a working dev...)
+	 * But don't use free_netdev() here,
+	 * it's supported by newer kernels only */
+	kfree(priv);
+	kfree(dev);
+
+	/* put device into ACPI D3 mode (shutdown) */
+	pci_set_power_state(pdev, 3);
+
+end:
+	FN_EXIT0;
+}
+
+
+/***********************************************************************
+*/
+#ifdef CONFIG_PM
+static int if_was_up = 0; /* FIXME: HACK, do it correctly sometime instead */
+static int
+acx_e_suspend(struct pci_dev *pdev, /*@unused@*/ u32 state)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	wlandevice_t *priv = acx_netdev_priv(dev);
+
+	FN_ENTER;
+
+	acx_sem_lock(priv);
+
+	printk("acx: experimental suspend handler called for %p\n", priv);
+	if (netif_device_present(dev)) {
+		if_was_up = 1;
+		acx_s_down(dev);
+	}
+	else
+		if_was_up = 0;
+
+	netif_device_detach(dev);	/* This one cannot sleep */
+	acx_s_delete_dma_regions(priv);
+
+	acx_sem_unlock(priv);
+
+	FN_EXIT0;
+	return OK;
+}
+
+static int
+acx_e_resume(struct pci_dev *pdev)
+{
+	struct net_device *dev;
+	wlandevice_t *priv;
+
+	printk(KERN_WARNING "rsm: resume\n");
+	dev = pci_get_drvdata(pdev);
+	printk(KERN_WARNING "rsm: got dev\n");
+
+	if (!netif_running(dev))
+		return 0;
+
+	priv = dev->priv;
+
+	acx_sem_lock(priv);
+
+	printk(KERN_WARNING "rsm: got priv\n");
+	FN_ENTER;
+	printk("acx: experimental resume handler called for %p!\n", priv);
+	pci_set_power_state(pdev, 0);
+	acxlog(L_DEBUG, "rsm: power state set\n");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)
+	/* 2.6.9-rc3-mm2 (2.6.9-bk4, too) introduced this shorter version,
+	   then it made its way into 2.6.10 */
+	pci_restore_state(pdev);
+#else
+	pci_restore_state(pdev, priv->pci_state);
+#endif
+	acxlog(L_DEBUG, "rsm: PCI state restored\n");
+	acx_s_reset_dev(dev);
+	acxlog(L_DEBUG, "rsm: device reset done\n");
+
+	if (OK != acx_s_init_mac(dev)) {
+		printk("rsm: init_mac FAILED\n");
+		goto fail;
+	}
+	acxlog(L_DEBUG, "rsm: init MAC done\n");
+
+	if (1 == if_was_up)
+		acx_s_up(dev);
+	acxlog(L_DEBUG, "rsm: acx up\n");
+
+	/* now even reload all card parameters as they were before suspend,
+	 * and possibly be back in the network again already :-)
+	 * FIXME: should this be done in that scheduled task instead?? */
+	if (ACX_STATE_IFACE_UP & priv->dev_state_mask)
+		acx_s_update_card_settings(priv, 0, 1);
+	acxlog(L_DEBUG, "rsm: settings updated\n");
+	netif_device_attach(dev);
+	acxlog(L_DEBUG, "rsm: device attached\n");
+fail: /* we need to return OK here anyway, right? */
+	acx_sem_unlock(priv);
+	FN_EXIT0;
+	return OK;
+}
+#endif /* CONFIG_PM */
+
+
+/*----------------------------------------------------------------
+* acx_s_up
+*
+* Side effects:
+*	- Enables on-card interrupt requests
+*	- calls acx_start
+* Call context:
+*	- process thread
+* Comment:
+*	This function is called by acx_open (when ifconfig sets the
+*	device as up).
+*----------------------------------------------------------------*/
+static void
+acx_s_up(netdevice_t *dev)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	unsigned long flags;
+
+	FN_ENTER;
+
+	switch (priv->mode) {
+	case ACX_MODE_0_ADHOC:
+	case ACX_MODE_2_STA:
+		/* actual scan cmd will happen in start() */
+		acx_set_status(priv, ACX_STATUS_1_SCANNING); break;
+	case ACX_MODE_3_AP:
+	case ACX_MODE_MONITOR:
+		acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); break;
+	}
+
+	acx_lock(priv, flags);
+	acx_l_enable_irq(priv);
+	acx_unlock(priv, flags);
+
+	/* acx fw < 1.9.3.e has a hardware timer, and older drivers
+	** used to use it. But we don't do that anymore, our OS
+	** has reliable software timers */
+	init_timer(&priv->mgmt_timer);
+	priv->mgmt_timer.function = acx_i_timer;
+	priv->mgmt_timer.data = (unsigned long)priv;
+
+	SET_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP);
+	acx_s_start(priv);
+
+	/* acx_start_queue(dev, "on startup"); */
+
+	FN_EXIT0;
+}
+
+
+/*----------------------------------------------------------------
+* acx_s_down
+*
+* Side effects:
+*	- disables on-card interrupt request
+* Call context:
+*	process thread
+* Comment:
+*	this disables the netdevice
+*----------------------------------------------------------------*/
+static void
+acx_s_down(netdevice_t *dev)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	unsigned long flags;
+
+	FN_ENTER;
+
+	acx_stop_queue(dev, "during close");
+
+	/* we really don't want to have an asynchronous tasklet disturb us
+	** after something vital for its job has been shut down, so
+	** end all remaining work now.
+	**
+	** NB: carrier_off (done by set_status below) would lead to
+	** not yet fully understood deadlock in FLUSH_SCHEDULED_WORK().
+	** That's why we do FLUSH first.
+	**
+	** NB2: we have a bad locking bug here: FLUSH_SCHEDULED_WORK()
+	** waits for acx_e_after_interrupt_task to complete if it is running
+	** on another CPU, but acx_e_after_interrupt_task
+	** will sleep on sem forever, because it is taken by us!
+	** Work around that by temporary sem unlock.
+	** This will fail miserably if we'll be hit by concurrent
+	** iwconfig or something in between. TODO!
+	*/
+	acx_sem_unlock(priv);
+	FLUSH_SCHEDULED_WORK();
+	acx_sem_lock(priv);
+
+	acx_set_status(priv, ACX_STATUS_0_STOPPED);
+
+	/* kernel/timer.c says it's illegal to del_timer_sync()
+	** a timer which restarts itself. We guarantee this cannot
+	** ever happen because acx_i_timer() never does this if
+	** status is ACX_STATUS_0_STOPPED
+	*/
+	del_timer_sync(&priv->mgmt_timer);
+
+	acx_lock(priv, flags);
+	acx_l_disable_irq(priv);
+	acx_unlock(priv, flags);
+
+	FN_EXIT0;
+}
+
+
+/*----------------------------------------------------------------
+* acx_e_open
+*
+* WLAN device open method.  Called from p80211netdev when kernel
+* device open (start) method is called in response to the
+* SIOCSIFFLAGS ioctl changing the flags bit IFF_UP
+* from clear to set.
+*
+* Returns:
+*	0	success
+*	>0	f/w reported error
+*	<0	driver reported error
+*
+* Call context:
+*	process thread
+----------------------------------------------------------------*/
+static int
+acx_e_open(netdevice_t *dev)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int result = OK;
+
+	FN_ENTER;
+
+	acxlog(L_INIT, "module count++\n");
+	WLAN_MOD_INC_USE_COUNT;
+
+	acx_sem_lock(priv);
+
+	acx_init_task_scheduler(priv);
+
+	/* request shared IRQ handler */
+	if (request_irq(dev->irq, acx_i_interrupt, SA_SHIRQ, dev->name, dev)) {
+		printk("%s: request_irq FAILED\n", dev->name);
+		result = -EAGAIN;
+		goto done;
+	}
+	acxlog(L_DEBUG | L_IRQ, "request_irq %d successful\n", dev->irq);
+
+	/* ifup device */
+	acx_s_up(dev);
+
+	/* We don't currently have to do anything else.
+	 * The setup of the MAC should be subsequently completed via
+	 * the mlme commands.
+	 * Higher layers know we're ready from dev->start==1 and
+	 * dev->tbusy==0.  Our rx path knows to pass up received/
+	 * frames because of dev->flags&IFF_UP is true.
+	 */
+done:
+	acx_sem_unlock(priv);
+
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/*----------------------------------------------------------------
+* acx_e_close
+*
+* WLAN device close method.  Called from network core when kernel
+* device close method is called in response to the
+* SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP
+* from set to clear.
+* (i.e. called for "ifconfig DEV down")
+*
+* Returns:
+*	0	success
+*	>0	f/w reported error
+*	<0	driver reported error
+*
+* Call context:
+*	process thread
+----------------------------------------------------------------*/
+static int
+acx_e_close(netdevice_t *dev)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+
+	FN_ENTER;
+
+	acx_sem_lock(priv);
+
+	/* ifdown device */
+	CLEAR_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP);
+	if (netif_device_present(dev)) {
+		acx_s_down(dev);
+	}
+
+	/* disable all IRQs, release shared IRQ handler */
+	acx_write_reg16(priv, IO_ACX_IRQ_MASK, 0xffff);
+	acx_write_reg16(priv, IO_ACX_FEMR, 0x0);
+	free_irq(dev->irq, dev);
+
+	/* We currently don't have to do anything else.
+	 * Higher layers know we're not ready from dev->start==0 and
+	 * dev->tbusy==1.  Our rx path knows to not pass up received
+	 * frames because of dev->flags&IFF_UP is false.
+	 */
+	acxlog(L_INIT, "module count--\n");
+	WLAN_MOD_DEC_USE_COUNT;
+
+	acx_sem_unlock(priv);
+
+	acxlog(L_INIT, "closed device\n");
+	FN_EXIT0;
+	return OK;
+}
+
+
+/*----------------------------------------------------------------
+* acx_i_tx_timeout
+*
+* Called from network core. Must not sleep!
+*----------------------------------------------------------------*/
+static void
+acx_i_tx_timeout(netdevice_t *dev)
+{
+	wlandevice_t *priv;
+	unsigned long flags;
+	unsigned int tx_num_cleaned;
+
+	FN_ENTER;
+
+	priv = (wlandevice_t *)dev->priv;
+
+	acx_lock(priv, flags);
+
+	/* clean processed tx descs, they may have been completely full */
+	tx_num_cleaned = acx_l_clean_tx_desc(priv);
+
+	/* nothing cleaned, yet (almost) no free buffers available?
+	 * --> clean all tx descs, no matter which status!!
+	 * Note that I strongly suspect that doing emergency cleaning
+	 * may confuse the firmware. This is a last ditch effort to get
+	 * ANYTHING to work again...
+	 *
+	 * TODO: it's best to simply reset & reinit hw from scratch...
+	 */
+	if ((priv->TxQueueFree <= 2) && (tx_num_cleaned == 0)) {
+		printk("%s: FAILED to free any of the many full tx buffers. "
+			"Switching to emergency freeing. "
+			"Please report!\n", dev->name);
+		acx_l_clean_tx_desc_emergency(priv);
+	}
+
+	if (acx_queue_stopped(dev) && (ACX_STATUS_4_ASSOCIATED == priv->status))
+		acx_wake_queue(dev, "after tx timeout");
+
+	/* stall may have happened due to radio drift, so recalib radio */
+	acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
+
+	/* do unimportant work last */
+	printk("%s: tx timeout!\n", dev->name);
+	priv->stats.tx_errors++;
+
+	acx_unlock(priv, flags);
+
+	FN_EXIT0;
+}
+
+
+/*----------------------------------------------------------------
+* acx_e_get_stats
+*----------------------------------------------------------------*/
+static struct net_device_stats*
+acx_e_get_stats(netdevice_t *dev)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+#if ANNOYING_GETS_CALLED_TOO_OFTEN
+	FN_ENTER;
+	FN_EXIT1((int)&priv->stats);
+#endif
+	return &priv->stats;
+}
+
+
+/*----------------------------------------------------------------
+* acx_e_get_wireless_stats
+*----------------------------------------------------------------*/
+static struct iw_statistics*
+acx_e_get_wireless_stats(netdevice_t *dev)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	FN_ENTER;
+	FN_EXIT0;
+	return &priv->wstats;
+}
+
+
+/*----------------------------------------------------------------
+* acx_i_set_multicast_list
+*----------------------------------------------------------------*/
+static void
+acx_i_set_multicast_list(netdevice_t *dev)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	unsigned long flags;
+
+	FN_ENTER;
+
+	acx_lock(priv, flags);
+
+	printk("FIXME: most likely needs refinement, "
+		"first implementation version only...\n");
+
+	/* ACX firmwares don't have allmulti capability,
+	 * so just use promiscuous mode instead in this case. */
+	if (dev->flags & (IFF_PROMISC|IFF_ALLMULTI)) {
+		SET_BIT(priv->rx_config_1, RX_CFG1_RCV_PROMISCUOUS);
+		CLEAR_BIT(priv->rx_config_1, RX_CFG1_FILTER_ALL_MULTI);
+		SET_BIT(priv->set_mask, SET_RXCONFIG);
+		/* let kernel know in case *we* needed to set promiscuous */
+		dev->flags |= (IFF_PROMISC|IFF_ALLMULTI);
+	}
+	else {
+		CLEAR_BIT(priv->rx_config_1, RX_CFG1_RCV_PROMISCUOUS);
+		SET_BIT(priv->rx_config_1, RX_CFG1_FILTER_ALL_MULTI);
+		SET_BIT(priv->set_mask, SET_RXCONFIG);
+		dev->flags &= ~(IFF_PROMISC|IFF_ALLMULTI);
+	}
+
+	/* cannot update card settings directly here, atomic context */
+	acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_UPDATE_CARD_CFG);
+
+	acx_unlock(priv, flags);
+
+	FN_EXIT0;
+}
+
+static void
+acx_l_update_link_quality_led(wlandevice_t *priv)
+{
+	int qual;
+
+	qual = acx_signal_determine_quality(priv->wstats.qual.level, priv->wstats.qual.noise);
+	if (qual > priv->brange_max_quality)
+		qual = priv->brange_max_quality;
+
+	if (time_after(jiffies, priv->brange_time_last_state_change +
+				(HZ/2 - HZ/2 * (unsigned long) qual/priv->brange_max_quality ) )) {
+		acx_l_power_led(priv, (priv->brange_last_state == 0));
+		priv->brange_last_state ^= 1; /* toggle */
+		priv->brange_time_last_state_change = jiffies;
+	}
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_enable_irq
+*----------------------------------------------------------------*/
+static void
+acx_l_enable_irq(wlandevice_t *priv)
+{
+	FN_ENTER;
+	acx_write_reg16(priv, IO_ACX_IRQ_MASK, priv->irq_mask);
+	acx_write_reg16(priv, IO_ACX_FEMR, 0x8000);
+	priv->irqs_active = 1;
+	FN_EXIT0;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_disable_irq
+*----------------------------------------------------------------*/
+static void
+acx_l_disable_irq(wlandevice_t *priv)
+{
+	FN_ENTER;
+	acx_write_reg16(priv, IO_ACX_IRQ_MASK, priv->irq_mask_off);
+	acx_write_reg16(priv, IO_ACX_FEMR, 0x0);
+	priv->irqs_active = 0;
+	FN_EXIT0;
+}
+
+/* scan is complete. all frames now on the receive queue are valid */
+#define INFO_SCAN_COMPLETE      0x0001
+#define INFO_WEP_KEY_NOT_FOUND  0x0002
+/* hw has been reset as the result of a watchdog timer timeout */
+#define INFO_WATCH_DOG_RESET    0x0003
+/* failed to send out NULL frame from PS mode notification to AP */
+/* recommended action: try entering 802.11 PS mode again */
+#define INFO_PS_FAIL            0x0004
+/* encryption/decryption process on a packet failed */
+#define INFO_IV_ICV_FAILURE     0x0005
+
+static void
+acx_l_handle_info_irq(wlandevice_t *priv)
+{
+#if ACX_DEBUG
+	static const char * const info_type_msg[] = {
+		"(unknown)",
+		"scan complete",
+		"WEP key not found",
+		"internal watchdog reset was done",
+		"failed to send powersave (NULL frame) notification to AP",
+		"encrypt/decrypt on a packet has failed",
+		"TKIP tx keys disabled",
+		"TKIP rx keys disabled",
+		"TKIP rx: key ID not found",
+		"???",
+		"???",
+		"???",
+		"???",
+		"???",
+		"???",
+		"???",
+		"TKIP IV value exceeds thresh"
+	};
+#endif
+	acx_read_info_status(priv);
+	acxlog(L_IRQ, "got Info IRQ: status 0x%04X type 0x%04X: %s\n",
+		priv->info_status, priv->info_type,
+		info_type_msg[(priv->info_type >= VEC_SIZE(info_type_msg)) ?
+				0 : priv->info_type]
+	);
+}
+
+
+/*----------------------------------------------------------------
+* acx_i_interrupt
+*
+* IRQ handler (atomic context, must not sleep, blah, blah)
+*----------------------------------------------------------------*/
+static void
+acx_log_unusual_irq(u16 irqtype) {
+	/*
+	if (!printk_ratelimit())
+		return;
+	*/
+
+	printk("acx: got");
+	if (irqtype & HOST_INT_RX_DATA) {
+		printk(" Rx_Data");
+	}
+		/* HOST_INT_TX_COMPLETE   */
+	if (irqtype & HOST_INT_TX_XFER) {
+		printk(" Tx_Xfer");
+	}
+		/* HOST_INT_RX_COMPLETE   */
+	if (irqtype & HOST_INT_DTIM) {
+		printk(" DTIM");
+	}
+	if (irqtype & HOST_INT_BEACON) {
+		printk(" Beacon");
+	}
+	if (irqtype & HOST_INT_TIMER) {
+		acxlog(L_IRQ, " Timer");
+	}
+	if (irqtype & HOST_INT_KEY_NOT_FOUND) {
+		printk(" Key_Not_Found");
+	}
+	if (irqtype & HOST_INT_IV_ICV_FAILURE) {
+		printk(" IV_ICV_Failure");
+	}
+		/* HOST_INT_CMD_COMPLETE  */
+		/* HOST_INT_INFO          */
+	if (irqtype & HOST_INT_OVERFLOW) {
+		printk(" Overflow");
+	}
+	if (irqtype & HOST_INT_PROCESS_ERROR) {
+		printk(" Process_Error");
+	}
+		/* HOST_INT_SCAN_COMPLETE */
+	if (irqtype & HOST_INT_FCS_THRESHOLD) {
+		printk(" FCS_Threshold");
+	}
+	if (irqtype & HOST_INT_UNKNOWN) {
+		printk(" Unknown");
+	}
+	printk(" IRQ(s)\n");
+}
+
+static irqreturn_t
+acx_i_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	wlandevice_t *priv;
+	unsigned long flags;
+	unsigned int irqcount = MAX_IRQLOOPS_PER_JIFFY;
+	u16 irqtype, unmasked;
+
+	priv = (wlandevice_t *) (((netdevice_t *) dev_id)->priv);
+
+	/* LOCKING: can just spin_lock() since IRQs are disabled anyway.
+	 * I am paranoid */
+	acx_lock(priv, flags);
+
+	unmasked = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_CLEAR);
+	if (unlikely(0xffff == unmasked)) {
+		/* 0xffff value hints at missing hardware,
+		 * so don't do anything.
+		 * FIXME: that's not very clean - maybe we are able to
+		 * establish a flag which definitely tells us that some
+		 * hardware is absent and which we could check here?
+		 * Hmm, but other drivers do the very same thing... */
+		acxlog(L_IRQ, "IRQ type:FFFF - device removed? IRQ_NONE\n");
+		goto none;
+	}
+
+	/* We will check only "interesting" IRQ types */
+	irqtype = unmasked & ~priv->irq_mask;
+	if (!irqtype) {
+		/* We are on a shared IRQ line and it wasn't our IRQ */
+		acxlog(L_IRQ, "IRQ type:%04X, mask:%04X - all are masked, IRQ_NONE\n",
+			unmasked, priv->irq_mask);
+		goto none;
+	}
+
+	/* Done here because IRQ_NONEs taking three lines of log
+	** drive me crazy */
+	FN_ENTER;
+
+#define IRQ_ITERATE 1
+#if IRQ_ITERATE
+if (jiffies != priv->irq_last_jiffies) {
+	priv->irq_loops_this_jiffy = 0;
+	priv->irq_last_jiffies = jiffies;
+}
+
+/* safety condition; we'll normally abort loop below
+ * in case no IRQ type occurred */
+while (--irqcount) {
+#endif
+	/* ACK all IRQs asap */
+	acx_write_reg16(priv, IO_ACX_IRQ_ACK, 0xffff);
+
+	acxlog(L_IRQ, "IRQ type:%04X, mask:%04X, type & ~mask:%04X\n",
+				unmasked, priv->irq_mask, irqtype);
+
+	/* Handle most important IRQ types first */
+	if (irqtype & HOST_INT_RX_COMPLETE) {
+		acxlog(L_IRQ, "got Rx_Complete IRQ\n");
+		acx_l_process_rx_desc(priv);
+	}
+	if (irqtype & HOST_INT_TX_COMPLETE) {
+		acxlog(L_IRQ, "got Tx_Complete IRQ\n");
+		/* don't clean up on each Tx complete, wait a bit
+		 * unless we're going towards full, in which case
+		 * we do it immediately, too (otherwise we might lockup
+		 * with a full Tx buffer if we go into
+		 * acx_l_clean_tx_desc() at a time when we won't wakeup
+		 * the net queue in there for some reason...) */
+		if ((++priv->tx_cnt_done % (priv->TxQueueCnt >> 2) == 0) ||
+		    (priv->TxQueueFree < (priv->TxQueueCnt >> 1)))
+		{
+#if TX_CLEANUP_IN_SOFTIRQ
+			acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_TX_CLEANUP);
+#else
+			acx_l_clean_tx_desc(priv);
+#endif
+			/* no need to set tx_cnt_done back to 0 here, since
+			 * an overflow cannot cause counter misalignment on
+			 * check above */
+		}
+#if 0
+/* this shouldn't happen here generally, since we'd also enable user packet
+ * xmit for management packets, which we really DON'T want */
+/* BS: disabling this caused my card to stop working after a few
+ * seconds when floodpinging. This should be reinvestigated! */
+		if (acx_queue_stopped(dev_id)) {
+			acx_wake_queue(dev_id, "after Tx complete");
+		}
+#endif
+	}
+
+	/* Less frequent ones */
+	if (irqtype & (0
+		| HOST_INT_CMD_COMPLETE
+		| HOST_INT_INFO
+		| HOST_INT_SCAN_COMPLETE
+	)) {
+		if (irqtype & HOST_INT_CMD_COMPLETE) {
+			acxlog(L_IRQ, "got Command_Complete IRQ\n");
+			/* save the state for the running issue_cmd() */
+			SET_BIT(priv->irq_status, HOST_INT_CMD_COMPLETE);
+		}
+		if (irqtype & HOST_INT_INFO) {
+			acx_l_handle_info_irq(priv);
+		}
+		if (irqtype & HOST_INT_SCAN_COMPLETE) {
+			acxlog(L_IRQ, "got Scan_Complete IRQ\n");
+			/* need to do that in process context */
+			acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_COMPLETE_SCAN);
+			/* remember that fw is not scanning anymore */
+			SET_BIT(priv->irq_status, HOST_INT_SCAN_COMPLETE);
+		}
+	}
+
+	/* These we just log, but either they happen rarely
+	 * or we keep them masked out */
+	if (irqtype & (0
+		| HOST_INT_RX_DATA
+		/* | HOST_INT_TX_COMPLETE   */
+		| HOST_INT_TX_XFER
+		/* | HOST_INT_RX_COMPLETE   */
+		| HOST_INT_DTIM
+		| HOST_INT_BEACON
+		| HOST_INT_TIMER
+		| HOST_INT_KEY_NOT_FOUND
+		| HOST_INT_IV_ICV_FAILURE
+		/* | HOST_INT_CMD_COMPLETE  */
+		/* | HOST_INT_INFO          */
+		| HOST_INT_OVERFLOW
+		| HOST_INT_PROCESS_ERROR
+		/* | HOST_INT_SCAN_COMPLETE */
+		| HOST_INT_FCS_THRESHOLD
+		| HOST_INT_UNKNOWN
+	)) {
+		acx_log_unusual_irq(irqtype);
+	}
+
+#if IRQ_ITERATE
+	unmasked = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_CLEAR);
+	irqtype = unmasked & ~priv->irq_mask;
+	/* Bail out if no new IRQ bits or if all are masked out */
+	if (!irqtype)
+		break;
+
+	if (unlikely(++priv->irq_loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY)) {
+		printk(KERN_ERR "acx: too many interrupts per jiffy!\n");
+		/* Looks like card floods us with IRQs! Try to stop that */
+		acx_write_reg16(priv, IO_ACX_IRQ_MASK, 0xffff);
+		/* This will short-circuit all future attempts to handle IRQ.
+		 * We cant do much more... */
+		priv->irq_mask = 0;
+		break;
+	}
+}
+#endif
+	/* Routine to perform blink with range */
+	if (unlikely(priv->led_power == 2))
+		acx_l_update_link_quality_led(priv);
+
+/* handled: */
+	/* acx_write_flush(priv); - not needed, last op was read anyway */
+	acx_unlock(priv, flags);
+	FN_EXIT0;
+	return IRQ_HANDLED;
+
+none:
+	acx_unlock(priv, flags);
+	return IRQ_NONE;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_power_led
+*----------------------------------------------------------------*/
+void
+acx_l_power_led(wlandevice_t *priv, int enable)
+{
+	u16 gpio_pled =
+		(CHIPTYPE_ACX111 == priv->chip_type) ? 0x0040 : 0x0800;
+
+	/* A hack. Not moving message rate limiting to priv->xxx
+	 * (it's only a debug message after all) */
+	static int rate_limit = 0;
+
+	if (rate_limit++ < 3)
+		acxlog(L_IOCTL, "Please report in case toggling the power "
+				"LED doesn't work for your card!\n");
+	if (enable)
+		acx_write_reg16(priv, IO_ACX_GPIO_OUT,
+			acx_read_reg16(priv, IO_ACX_GPIO_OUT) & ~gpio_pled);
+	else
+		acx_write_reg16(priv, IO_ACX_GPIO_OUT,
+			acx_read_reg16(priv, IO_ACX_GPIO_OUT) | gpio_pled);
+}
+
+
+/***********************************************************************
+** Ioctls
+*/
+
+/***********************************************************************
+** Ioctls for easy debug of I/O things
+*/
+/* specialized register io for debug purposes, see ioctl below */
+static inline u32
+_acx_read_reg32(wlandevice_t *priv, unsigned int offset)
+{
+#if ACX_IO_WIDTH == 32
+	return readl(priv->iobase + offset);
+#else
+	return readw(priv->iobase + offset)
+	    + (readw(priv->iobase + offset + 2) << 16);
+#endif
+}
+
+static inline void
+_acx_write_reg32(wlandevice_t *priv, unsigned int offset, u32 val)
+{
+#if ACX_IO_WIDTH == 32
+	writel(val, priv->iobase + offset);
+#else
+	writew(val & 0xffff, priv->iobase + offset);
+	writew(val >> 16, priv->iobase + offset + 2);
+#endif
+}
+
+int
+acx_ioctl_dbg_get_io(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	int result = -EINVAL;
+#if ACX_DEBUG
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	unsigned int *params = (unsigned int*)extra;
+
+	/* expected value order: DbgGetIO type address magic */
+
+	if (params[2] != 0x1234) {
+		acxlog(L_IOCTL, "wrong magic: 0x%04X doesn't match 0x%04X! "
+			"If you don't know what you're doing, then please "
+			"stop NOW, this can be DANGEROUS!\n",
+			params[2], 0x1234);
+		goto end;
+	}
+
+	switch (params[0]) {
+	case 0x0: /* Internal RAM */
+		acxlog(L_IOCTL, "sorry, access to internal RAM "
+			"is not implemented yet\n");
+		break;
+	case 0xffff: /* MAC registers */
+		acxlog(L_IOCTL, "value at register 0x%04X is 0x%08X\n",
+			params[1], _acx_read_reg32(priv, params[1]));
+		break;
+	case 0x81: /* PHY RAM table */
+		acxlog(L_IOCTL, "sorry, access to PHY RAM "
+			"is not implemented yet\n");
+		break;
+	case 0x82: /* PHY registers */
+		acxlog(L_IOCTL, "sorry, access to PHY registers "
+			"is not implemented yet\n");
+		break;
+	default:
+		acxlog(L_IOCTL, "Invalid I/O type specified, aborting!\n");
+		goto end;
+	}
+	result = OK;
+end:
+#endif /* ACX_DEBUG */
+	return result;
+}
+
+int
+acx_ioctl_dbg_set_io(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	int result = -EINVAL;
+#if ACX_DEBUG
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	int *params = (int*)extra;
+
+	/* expected value order: DbgSetIO type address value magic */
+
+	if (params[3] != 0x1234) {
+		acxlog(L_ANY, "wrong magic: 0x%04X doesn't match 0x%04X! "
+			"If you don't know what you're doing, then please "
+			"stop, this can be DANGEROUS!\n",
+			params[3], 0x1234);
+		goto end;
+	}
+
+	switch (params[0]) {
+	case 0x0: /* Internal RAM */
+		acxlog(L_IOCTL, "sorry, access to internal RAM "
+			"is not implemented yet\n");
+		break;
+	case 0xffff: /* MAC registers */
+		acxlog(L_IOCTL, "setting value at register 0x%04X "
+			"to 0x%08X\n", params[1], params[2]);
+		_acx_write_reg32(priv, params[1], params[2]);
+		break;
+	case 0x81: /* PHY RAM table */
+		acxlog(L_IOCTL, "sorry, access to PHY RAM "
+			"is not implemented yet\n");
+		break;
+	case 0x82: /* PHY registers */
+		acxlog(L_IOCTL, "sorry, access to PHY registers "
+			"is not implemented yet\n");
+		break;
+	default:
+		acxlog(L_ANY, "Invalid I/O type specified, aborting!\n");
+		goto end;
+	}
+	result = OK;
+end:
+#endif /* ACX_DEBUG */
+	return result;
+}
+
+
+/***********************************************************************
+*/
+int
+acx111_ioctl_info(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+#if ACX_DEBUG
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	const TIWLAN_DC *pDc = &priv->dc;
+	const struct rxdesc *pRxDesc;
+	const struct rxhostdesc *rx_host_desc;
+	struct txdesc *tx_desc;
+	const struct txhostdesc *tx_host_desc;
+	struct acx111_ie_memoryconfig memconf;
+	struct acx111_ie_queueconfig queueconf;
+	unsigned long flags;
+	int i;
+	char memmap[0x34];
+	char rxconfig[0x8];
+	char fcserror[0x8];
+	char ratefallback[0x5];
+
+	if ( !(acx_debug & (L_IOCTL|L_DEBUG)) )
+		return OK;
+	/* using printk() since we checked debug flag already */
+
+	acx_sem_lock(priv);
+
+	if (CHIPTYPE_ACX111 != priv->chip_type) {
+		printk("acx111-specific function called "
+			"with non-acx111 chip, aborting\n");
+		goto end_ok;
+	}
+
+	/* get Acx111 Memory Configuration */
+	memset(&memconf, 0, sizeof(memconf));
+
+	/* BTW, fails with 12 (Write only) error code --vda */
+	if (OK != acx_s_interrogate(priv, &memconf, ACX1xx_IE_QUEUE_CONFIG)) {
+		printk("read memconf FAILED\n");
+	}
+
+	/* get Acx111 Queue Configuration */
+	memset(&queueconf, 0, sizeof(queueconf));
+
+	if (OK != acx_s_interrogate(priv, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS)) {
+		printk("read queuehead FAILED\n");
+	}
+
+	/* get Acx111 Memory Map */
+	memset(memmap, 0, sizeof(memmap));
+
+	if (OK != acx_s_interrogate(priv, &memmap, ACX1xx_IE_MEMORY_MAP)) {
+		printk("read mem map FAILED\n");
+	}
+
+	/* get Acx111 Rx Config */
+	memset(rxconfig, 0, sizeof(rxconfig));
+
+	if (OK != acx_s_interrogate(priv, &rxconfig, ACX1xx_IE_RXCONFIG)) {
+		printk("read rxconfig FAILED\n");
+	}
+
+	/* get Acx111 fcs error count */
+	memset(fcserror, 0, sizeof(fcserror));
+
+	if (OK != acx_s_interrogate(priv, &fcserror, ACX1xx_IE_FCS_ERROR_COUNT)) {
+		printk("read fcserror FAILED\n");
+	}
+
+	/* get Acx111 rate fallback */
+	memset(ratefallback, 0, sizeof(ratefallback));
+
+	if (OK != acx_s_interrogate(priv, &ratefallback, ACX1xx_IE_RATE_FALLBACK)) {
+		printk("read ratefallback FAILED\n");
+	}
+
+	/* force occurrence of a beacon interrupt */
+	/* TODO: comment why is this necessary */
+	acx_write_reg16(priv, IO_ACX_HINT_TRIG, HOST_INT_BEACON);
+
+	/* dump Acx111 Mem Configuration */
+	printk("dump mem config:\n"
+		"data read: %d, struct size: %d\n"
+		"Number of stations: %1X\n"
+		"Memory block size: %1X\n"
+		"tx/rx memory block allocation: %1X\n"
+		"count rx: %X / tx: %X queues\n"
+		"options %1X\n"
+		"fragmentation %1X\n"
+		"Rx Queue 1 Count Descriptors: %X\n"
+		"Rx Queue 1 Host Memory Start: %X\n"
+		"Tx Queue 1 Count Descriptors: %X\n"
+		"Tx Queue 1 Attributes: %X\n",
+		memconf.len, (int) sizeof(memconf),
+		memconf.no_of_stations,
+		memconf.memory_block_size,
+		memconf.tx_rx_memory_block_allocation,
+		memconf.count_rx_queues, memconf.count_tx_queues,
+		memconf.options,
+		memconf.fragmentation,
+		memconf.rx_queue1_count_descs,
+		memconf.rx_queue1_host_rx_start,
+		memconf.tx_queue1_count_descs,
+		memconf.tx_queue1_attributes);
+
+	/* dump Acx111 Queue Configuration */
+	printk("dump queue head:\n"
+		"data read: %d, struct size: %d\n"
+		"tx_memory_block_address (from card): %X\n"
+		"rx_memory_block_address (from card): %X\n"
+		"rx1_queue address (from card): %X\n"
+		"tx1_queue address (from card): %X\n"
+		"tx1_queue attributes (from card): %X\n",
+		queueconf.len, (int) sizeof(queueconf),
+		queueconf.tx_memory_block_address,
+		queueconf.rx_memory_block_address,
+		queueconf.rx1_queue_address,
+		queueconf.tx1_queue_address,
+		queueconf.tx1_attributes);
+
+	/* dump Acx111 Mem Map */
+	printk("dump mem map:\n"
+		"data read: %d, struct size: %d\n"
+		"Code start: %X\n"
+		"Code end: %X\n"
+		"WEP default key start: %X\n"
+		"WEP default key end: %X\n"
+		"STA table start: %X\n"
+		"STA table end: %X\n"
+		"Packet template start: %X\n"
+		"Packet template end: %X\n"
+		"Queue memory start: %X\n"
+		"Queue memory end: %X\n"
+		"Packet memory pool start: %X\n"
+		"Packet memory pool end: %X\n"
+		"iobase: %p\n"
+		"iobase2: %p\n",
+		*((u16 *)&memmap[0x02]), (int) sizeof(memmap),
+		*((u32 *)&memmap[0x04]),
+		*((u32 *)&memmap[0x08]),
+		*((u32 *)&memmap[0x0C]),
+		*((u32 *)&memmap[0x10]),
+		*((u32 *)&memmap[0x14]),
+		*((u32 *)&memmap[0x18]),
+		*((u32 *)&memmap[0x1C]),
+		*((u32 *)&memmap[0x20]),
+		*((u32 *)&memmap[0x24]),
+		*((u32 *)&memmap[0x28]),
+		*((u32 *)&memmap[0x2C]),
+		*((u32 *)&memmap[0x30]),
+		priv->iobase,
+		priv->iobase2);
+
+	/* dump Acx111 Rx Config */
+	printk("dump rx config:\n"
+		"data read: %d, struct size: %d\n"
+		"rx config: %X\n"
+		"rx filter config: %X\n",
+		*((u16 *)&rxconfig[0x02]), (int) sizeof(rxconfig),
+		*((u16 *)&rxconfig[0x04]),
+		*((u16 *)&rxconfig[0x06]));
+
+	/* dump Acx111 fcs error */
+	printk("dump fcserror:\n"
+		"data read: %d, struct size: %d\n"
+		"fcserrors: %X\n",
+		*((u16 *)&fcserror[0x02]), (int) sizeof(fcserror),
+		*((u32 *)&fcserror[0x04]));
+
+	/* dump Acx111 rate fallback */
+	printk("dump rate fallback:\n"
+		"data read: %d, struct size: %d\n"
+		"ratefallback: %X\n",
+		*((u16 *)&ratefallback[0x02]), (int) sizeof(ratefallback),
+		*((u8 *)&ratefallback[0x04]));
+
+	/* protect against IRQ */
+	acx_lock(priv, flags);
+
+	/* dump acx111 internal rx descriptor ring buffer */
+	pRxDesc = pDc->pRxDescQPool;
+
+	/* loop over complete receive pool */
+	for (i = 0; i < pDc->rx_pool_count; i++) {
+		printk("\ndump internal rxdesc %d:\n"
+			"mem pos %p\n"
+			"next 0x%X\n"
+			"acx mem pointer (dynamic) 0x%X\n"
+			"CTL (dynamic) 0x%X\n"
+			"Rate (dynamic) 0x%X\n"
+			"RxStatus (dynamic) 0x%X\n"
+			"Mod/Pre (dynamic) 0x%X\n",
+			i,
+			pRxDesc,
+			acx2cpu(pRxDesc->pNextDesc),
+			acx2cpu(pRxDesc->ACXMemPtr),
+			pRxDesc->Ctl_8,
+			pRxDesc->rate,
+			pRxDesc->error,
+			pRxDesc->SNR);
+		pRxDesc++;
+	}
+
+	/* dump host rx descriptor ring buffer */
+
+	rx_host_desc = pDc->pRxHostDescQPool;
+
+	/* loop over complete receive pool */
+	for (i = 0; i < pDc->rx_pool_count; i++) {
+		printk("\ndump host rxdesc %d:\n"
+			"mem pos %p\n"
+			"buffer mem pos 0x%X\n"
+			"buffer mem offset 0x%X\n"
+			"CTL 0x%X\n"
+			"Length 0x%X\n"
+			"next 0x%X\n"
+			"Status 0x%X\n",
+			i,
+			rx_host_desc,
+			acx2cpu(rx_host_desc->data_phy),
+			rx_host_desc->data_offset,
+			le16_to_cpu(rx_host_desc->Ctl_16),
+			le16_to_cpu(rx_host_desc->length),
+			acx2cpu(rx_host_desc->desc_phy_next),
+			rx_host_desc->Status);
+		rx_host_desc++;
+	}
+
+	/* dump acx111 internal tx descriptor ring buffer */
+	tx_desc = pDc->pTxDescQPool;
+
+	/* loop over complete transmit pool */
+	for (i = 0; i < pDc->tx_pool_count; i++) {
+		printk("\ndump internal txdesc %d:\n"
+			"size 0x%X\n"
+			"mem pos %p\n"
+			"next 0x%X\n"
+			"acx mem pointer (dynamic) 0x%X\n"
+			"host mem pointer (dynamic) 0x%X\n"
+			"length (dynamic) 0x%X\n"
+			"CTL (dynamic) 0x%X\n"
+			"CTL2 (dynamic) 0x%X\n"
+			"Status (dynamic) 0x%X\n"
+			"Rate (dynamic) 0x%X\n",
+			i,
+			(int) sizeof(struct txdesc),
+			tx_desc,
+			acx2cpu(tx_desc->pNextDesc),
+			acx2cpu(tx_desc->AcxMemPtr),
+			acx2cpu(tx_desc->HostMemPtr),
+			le16_to_cpu(tx_desc->total_length),
+			tx_desc->Ctl_8,
+			tx_desc->Ctl2_8, tx_desc->error,
+			tx_desc->u.r1.rate);
+		tx_desc = GET_NEXT_TX_DESC_PTR(pDc, tx_desc);
+	}
+
+	/* dump host tx descriptor ring buffer */
+
+	tx_host_desc = pDc->pTxHostDescQPool;
+
+	/* loop over complete host send pool */
+	for (i = 0; i < pDc->tx_pool_count * 2; i++) {
+		printk("\ndump host txdesc %d:\n"
+			"mem pos %p\n"
+			"buffer mem pos 0x%X\n"
+			"buffer mem offset 0x%X\n"
+			"CTL 0x%X\n"
+			"Length 0x%X\n"
+			"next 0x%X\n"
+			"Status 0x%X\n",
+			i,
+			tx_host_desc,
+			acx2cpu(tx_host_desc->data_phy),
+			tx_host_desc->data_offset,
+			le16_to_cpu(tx_host_desc->Ctl_16),
+			le16_to_cpu(tx_host_desc->length),
+			acx2cpu(tx_host_desc->desc_phy_next),
+			le32_to_cpu(tx_host_desc->Status));
+		tx_host_desc++;
+	}
+
+	/* acx_write_reg16(priv, 0xb4, 0x4); */
+
+	acx_unlock(priv, flags);
+end_ok:
+
+	acx_sem_unlock(priv);
+#endif /* ACX_DEBUG */
+	return OK;
+}
+
+
+/***********************************************************************
+*/
+int
+acx100_ioctl_set_phy_amp_bias(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	unsigned long flags;
+	u16 gpio_old;
+
+	if (priv->chip_type != CHIPTYPE_ACX100) {
+		/* WARNING!!!
+		 * Removing this check *might* damage
+		 * hardware, since we're tweaking GPIOs here after all!!!
+		 * You've been warned...
+		 * WARNING!!! */
+		printk("acx: sorry, setting bias level for non-acx100 "
+			"is not supported yet\n");
+		return OK;
+	}
+
+	if (*extra > 7)	{
+		printk("acx: invalid bias parameter, range is 0-7\n");
+		return -EINVAL;
+	}
+
+	acx_sem_lock(priv);
+
+	/* Need to lock accesses to [IO_ACX_GPIO_OUT]:
+	 * IRQ handler uses it to update LED */
+	acx_lock(priv, flags);
+	gpio_old = acx_read_reg16(priv, IO_ACX_GPIO_OUT);
+	acx_write_reg16(priv, IO_ACX_GPIO_OUT, (gpio_old & 0xf8ff) | ((u16)*extra << 8));
+	acx_unlock(priv, flags);
+
+	acxlog(L_DEBUG, "gpio_old: 0x%04X\n", gpio_old);
+	printk("%s: PHY power amplifier bias: old:%d, new:%d\n",
+		dev->name,
+		(gpio_old & 0x0700) >> 8, (unsigned char)*extra);
+
+	acx_sem_unlock(priv);
+
+	return OK;
+}
+
+
+/***************************************************************
+** acx_l_alloc_tx
+** Actually returns a txdesc_t* ptr
+*/
+tx_t*
+acx_l_alloc_tx(wlandevice_t* priv)
+{
+	struct TIWLAN_DC *pDc = &priv->dc;
+	struct txdesc *tx_desc;
+	u8 ctl8;
+
+	FN_ENTER;
+
+	tx_desc = GET_TX_DESC_PTR(pDc, pDc->tx_head);
+	/* why?! rmb(); */
+	ctl8 = tx_desc->Ctl_8;
+	if (unlikely(DESC_CTL_HOSTOWN != (ctl8 & DESC_CTL_DONE))) {
+		/* whoops, descr at current index is not free, so probably
+		 * ring buffer already full */
+		/* FIXME: this causes a deadlock situation (endless
+		 * loop) in case the current descriptor remains busy,
+		 * so handle it a bit better in the future!! */
+		printk("acx: BUG: tx_head->Ctl8=0x%02X, (0x%02X & "
+			"0x"DESC_CTL_DONE_STR") != 0x"DESC_CTL_HOSTOWN_STR
+			": failed to find free tx descr\n",
+			ctl8, ctl8);
+		tx_desc = NULL;
+		goto end;
+	}
+
+	priv->TxQueueFree--;
+	acxlog(L_BUFT, "tx: got desc %u, %u remain\n", pDc->tx_head, priv->TxQueueFree);
+
+/*
+ * This comment is probably not entirely correct, needs further discussion
+ * (restored commented-out code below to fix Tx ring buffer overflow,
+ * since it's much better to have a slightly less efficiently used ring
+ * buffer rather than one which easily overflows):
+ *
+ * This doesn't do anything other than limit our maximum number of
+ * buffers used at a single time (we might as well just declare
+ * MINFREE_TX less descriptors when we open up.) We should just let it
+ * slide here, and back off MINFREE_TX in acx_l_clean_tx_desc, when given the
+ * opportunity to let the queue start back up.
+ */
+	if (priv->TxQueueFree < MINFREE_TX) {
+		acxlog(L_BUF, "stop queue (%u tx desc left)\n",
+				priv->TxQueueFree);
+		acx_stop_queue(priv->netdev, NULL);
+	}
+
+	/* returning current descriptor, so advance to next free one */
+	pDc->tx_head = (pDc->tx_head + 1) % pDc->tx_pool_count;
+end:
+	FN_EXIT0;
+
+	return (tx_t*)tx_desc;
+}
+
+
+/***************************************************************
+*/
+void*
+acx_l_get_txbuf(tx_t* tx_opaque)
+{
+	txhostdesc_t *hostdesc = ((txdesc_t*)tx_opaque)->fixed_size.s.host_desc;
+	/* FIXME: happens on card eject; better method? */
+	if (unlikely((long)-1 == (long)hostdesc))
+		return NULL;
+	return hostdesc->data;
+}
+
+
+/***************************************************************
+** acx_l_dma_tx_data
+**
+** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx).
+** Can be called from acx_i_start_xmit (data frames from net core).
+*/
+void
+acx_l_dma_tx_data(wlandevice_t *priv, tx_t* tx_opaque, int hostdesc_len)
+{
+	struct txdesc *tx_desc = (struct txdesc*)tx_opaque;
+	struct txhostdesc *hostdesc;
+	client_t *clt;
+	u8 Ctl_8, Ctl2_8;
+
+	FN_ENTER;
+
+	hostdesc = tx_desc->fixed_size.s.host_desc;
+	/* FIXME: happens on card eject; better method? */
+	if (unlikely((long)-1 == (long)hostdesc))
+		goto end;
+
+	hostdesc->data_offset = 0;
+	tx_desc->total_length = hostdesc->length = cpu_to_le16(hostdesc_len);
+
+	/* modify flag status in separate variable to be able to write it back
+	 * in one big swoop later (also in order to have less device memory
+	 * accesses) */
+	Ctl_8 = tx_desc->Ctl_8;
+	Ctl2_8 = tx_desc->Ctl2_8;
+
+	/* DON'T simply set Ctl field to 0 here globally,
+	 * it needs to maintain a consistent flag status (those are state flags!!),
+	 * otherwise it may lead to severe disruption. Only set or reset particular
+	 * flags at the exact moment this is needed...
+	 * FIXME: what about Ctl2? Equally problematic? */
+
+
+	/* let chip do RTS/CTS handshaking before sending
+	 * in case packet size exceeds threshold */
+	if (le16_to_cpu(tx_desc->total_length) > priv->rts_threshold)
+		SET_BIT(Ctl2_8, DESC_CTL2_RTS);
+	else
+		CLEAR_BIT(Ctl2_8, DESC_CTL2_RTS);
+
+#if DEBUG_WEP
+	if (priv->wep_enabled)
+		SET_BIT(Ctl2_8, DESC_CTL2_WEP);
+	else
+		CLEAR_BIT(Ctl2_8, DESC_CTL2_WEP);
+#endif
+	switch (priv->mode) {
+	case ACX_MODE_0_ADHOC:
+	case ACX_MODE_3_AP:
+		clt = acx_l_sta_list_get(priv, ((wlan_hdr_t*)hostdesc->data)->a1);
+		break;
+	case ACX_MODE_2_STA:
+		clt = priv->ap_client;
+		break;
+	default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */
+		clt = NULL;
+		break;
+	}
+
+	if (unlikely(clt && !clt->rate_cur)) {
+		printk("acx: driver bug! bad ratemask\n");
+		goto end;
+	}
+
+	/* used in tx cleanup routine for auto rate and accounting: */
+	tx_desc->fixed_size.s.txc = clt;
+
+	if (CHIPTYPE_ACX111 == priv->chip_type) {
+		u16 rate_cur = clt ? clt->rate_cur : priv->rate_bcast;
+		/* note that if !tx_desc->do_auto, txrate->cur
+		** has only one nonzero bit */
+		tx_desc->u.r2.rate111 = cpu_to_le16(
+			rate_cur
+			/* WARNING: I was never able to make it work with prism54 AP.
+			** It was falling down to 1Mbit where shortpre is not applicable,
+			** and not working at all at "5,11 basic rates only" setting.
+			** I even didn't see tx packets in radio packet capture.
+			** Disabled for now --vda */
+			/*| ((peer->shortpre && txrate->cur!=RATE111_1) ? RATE111_SHORTPRE : 0) */
+			);
+#if TODO_FIGURE_OUT_WHEN_TO_SET_THIS
+			/* should add this to rate111 above as necessary */
+			| (txrate->pbcc511 ? RATE111_PBCC511 : 0)
+#endif
+	} else { /* ACX100 */
+		u8 rate_100 = clt ? clt->rate_100 : priv->rate_bcast100;
+		tx_desc->u.r1.rate = rate_100;
+#if TODO_FIGURE_OUT_WHEN_TO_SET_THIS
+		if (txrate->pbcc511) {
+			if (n == RATE100_5 || n == RATE100_11)
+				n |= RATE100_PBCC511;
+		}
+
+		if (peer->shortpre && (txrate->cur != RATE111_1))
+			SET_BIT(Ctl_8, DESC_CTL_SHORT_PREAMBLE); /* set Short Preamble */
+#endif
+		/* set autodma and reclaim and 1st mpdu */
+		SET_BIT(Ctl_8, DESC_CTL_AUTODMA | DESC_CTL_RECLAIM | DESC_CTL_FIRSTFRAG);
+	}
+	/* don't need to clean ack/rts statistics here, already
+	 * done on descr cleanup */
+
+	/* clears Ctl DESC_CTL_HOSTOWN bit, thus telling that the descriptors
+	 * are now owned by the acx100; do this as LAST operation */
+	CLEAR_BIT(Ctl_8, DESC_CTL_HOSTOWN);
+	wmb(); /* make sure everything else is written before we release our descriptor to the adapter here */
+	CLEAR_BIT(hostdesc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN));
+
+	/* write back modified flags */
+	tx_desc->Ctl2_8 = Ctl2_8;
+	tx_desc->Ctl_8 = Ctl_8;
+
+	tx_desc->tx_time = cpu_to_le32(jiffies);
+	wmb(); /* make sure all descr writes finish before we tell the adapter that it's its turn now */
+	acx_write_reg16(priv, IO_ACX_INT_TRIG, INT_TRIG_TXPRC);
+	acx_write_flush(priv);
+
+	/* log the packet content AFTER sending it,
+	 * in order to not delay sending any further than absolutely needed
+	 * Do separate logs for acx100/111 to have human-readable rates */
+	if (unlikely(acx_debug & (L_XFER | L_DATA))) {
+		u16 fc = ((wlan_hdr_t*)hostdesc->data)->fc;
+		if (CHIPTYPE_ACX111 == priv->chip_type)
+			printk("tx: pkt (%s): len %d "
+				"rate %04X%s status %u\n",
+				acx_get_packet_type_string(le16_to_cpu(fc)),
+				le16_to_cpu(tx_desc->total_length),
+				le16_to_cpu(tx_desc->u.r2.rate111),
+				(le16_to_cpu(tx_desc->u.r2.rate111) & RATE111_SHORTPRE) ? "(SPr)" : "",
+				priv->status);
+		else
+			printk("tx: pkt (%s): len %d rate %03u%s status %u\n",
+				acx_get_packet_type_string(fc),
+				le16_to_cpu(tx_desc->total_length),
+				tx_desc->u.r1.rate,
+				(Ctl_8 & DESC_CTL_SHORT_PREAMBLE) ? "(SPr)" : "",
+				priv->status);
+
+		if (acx_debug & L_DATA) {
+			printk("tx: 802.11 [%d]: ", hostdesc_len);
+			acx_dump_bytes(hostdesc->data, hostdesc_len);
+		}
+	}
+end:
+	FN_EXIT0;
+}
+
+
+/***********************************************************************
+*/
+static void
+acx_l_handle_tx_error(wlandevice_t *priv, u8 error, unsigned int finger)
+{
+	const char *err = "unknown error";
+
+	/* hmm, should we handle this as a mask
+	 * of *several* bits?
+	 * For now I think only caring about
+	 * individual bits is ok... */
+	switch (error) {
+	case 0x01:
+		err = "no Tx due to error in other fragment";
+		priv->wstats.discard.fragment++;
+		break;
+	case 0x02:
+		err = "Tx aborted";
+		priv->stats.tx_aborted_errors++;
+		break;
+	case 0x04:
+		err = "Tx desc wrong parameters";
+		priv->wstats.discard.misc++;
+		break;
+	case 0x08:
+		err = "WEP key not found";
+		priv->wstats.discard.misc++;
+		break;
+	case 0x10:
+		err = "MSDU lifetime timeout? - try changing "
+				"'iwconfig retry lifetime XXX'";
+		priv->wstats.discard.misc++;
+		break;
+	case 0x20:
+		err = "excessive Tx retries due to either distance "
+			"too high or unable to Tx or Tx frame error - "
+			"try changing 'iwconfig txpower XXX' or "
+			"'sens'itivity or 'retry'";
+		priv->wstats.discard.retries++;
+		/* FIXME: set (GETSET_TX|GETSET_RX) here
+		 * (this seems to recalib radio on ACX100)
+		 * after some more jiffies passed??
+		 * But OTOH Tx error 0x20 also seems to occur on
+		 * overheating, so I'm not sure whether we
+		 * actually want that, since people maybe won't notice
+		 * then that their hardware is slowly getting
+		 * cooked...
+		 * Or is it still a safe long distance from utter
+		 * radio non-functionality despite many radio
+		 * recalibs
+		 * to final destructive overheating of the hardware?
+		 * In this case we really should do recalib here...
+		 * I guess the only way to find out is to do a
+		 * potentially fatal self-experiment :-\
+		 * Or maybe only recalib in case we're using Tx
+		 * rate auto (on errors switching to lower speed
+		 * --> less heat?) or 802.11 power save mode? */
+
+		/* ok, just do it.
+		 * ENABLE_TX|ENABLE_RX helps, so even do
+		 * DISABLE_TX and DISABLE_RX in order to perhaps
+		 * have more impact. */
+		if (++priv->retry_errors_msg_ratelimit % 4 == 0) {
+			if (priv->retry_errors_msg_ratelimit <= 20)
+				printk("%s: several excessive Tx "
+					"retry errors occurred, attempting "
+					"to recalibrate radio. Radio "
+					"drift might be caused by increasing "
+					"card temperature, please check the card "
+					"before it's too late!\n",
+					priv->netdev->name);
+			if (priv->retry_errors_msg_ratelimit == 20)
+				printk("disabling above "
+					"notification message\n");
+
+			acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
+		}
+		break;
+	case 0x40:
+		err = "Tx buffer overflow";
+		priv->stats.tx_fifo_errors++;
+		break;
+	case 0x80:
+		err = "DMA error";
+		priv->wstats.discard.misc++;
+		break;
+	}
+	priv->stats.tx_errors++;
+	if (priv->stats.tx_errors <= 20)
+		printk("%s: tx error 0x%02X, buf %02u! (%s)\n",
+				priv->netdev->name, error, finger, err);
+	else
+		printk("%s: tx error 0x%02X, buf %02u!\n",
+				priv->netdev->name, error, finger);
+}
+
+
+/***********************************************************************
+*/
+/* Theory of operation:
+** client->rate_cap is a bitmask of rates client is capable of.
+** client->rate_cfg is a bitmask of allowed (configured) rates.
+** It is set as a result of iwconfig rate N [auto]
+** or iwpriv set_rates "N,N,N N,N,N" commands.
+** It can be fixed (e.g. 0x0080 == 18Mbit only),
+** auto (0x00ff == 18Mbit or any lower value),
+** and code handles any bitmask (0x1081 == try 54Mbit,18Mbit,1Mbit _only_).
+**
+** client->rate_cur is a value for rate111 field in tx descriptor.
+** It is always set to txrate_cfg sans zero or more most significant
+** bits. This routine handles selection of new rate_cur value depending on
+** outcome of last tx event.
+**
+** client->rate_100 is a precalculated rate value for acx100
+** (we can do without it, but will need to calculate it on each tx).
+**
+** You cannot configure mixed usage of 5.5 and/or 11Mbit rate
+** with PBCC and CCK modulation. Either both at CCK or both at PBCC.
+** In theory you can implement it, but so far it is considered not worth doing.
+**
+** 22Mbit, of course, is PBCC always. */
+
+/* maps acx100 tx descr rate field to acx111 one */
+static u16
+rate100to111(u8 r)
+{
+	switch (r) {
+	case RATE100_1:	return RATE111_1;
+	case RATE100_2:	return RATE111_2;
+	case RATE100_5:
+	case (RATE100_5 | RATE100_PBCC511):	return RATE111_5;
+	case RATE100_11:
+	case (RATE100_11 | RATE100_PBCC511):	return RATE111_11;
+	case RATE100_22:	return RATE111_22;
+	default:
+		printk("acx: unexpected acx100 txrate: %u! "
+			"Please report\n", r);
+		return RATE111_2;
+	}
+}
+
+
+static void
+acx_l_handle_txrate_auto(wlandevice_t *priv, struct client *txc,
+			unsigned int idx, u8 rate100, u16 rate111, u8 error)
+{
+	u16 sent_rate;
+	u16 cur = txc->rate_cur;
+	int slower_rate_was_used;
+
+	/* FIXME: need to implement some kind of rate success memory
+	 * which stores the success percentage per rate, to be taken
+	 * into account when considering allowing a new rate, since it
+	 * doesn't really help to stupidly count fallback/stepup,
+	 * since one invalid rate will spoil the party anyway
+	 * (such as 22M in case of 11M-only peers) */
+
+	/* vda: hmm. current code will do this:
+	** 1. send packets at 11 Mbit, stepup++
+	** 2. will try to send at 22Mbit. hardware will see no ACK,
+	**    retries at 11Mbit, success. code notes that used rate
+	**    is lower. stepup = 0, fallback++
+	** 3. repeat step 2 fallback_count times. Fall back to
+	**    11Mbit. go to step 1.
+	** If stepup_count is large (say, 16) and fallback_count
+	** is small (3), this wouldn't be too bad wrt throughput */
+
+	/* do some preparations, i.e. calculate the one rate that was
+	 * used to send this packet */
+	if (CHIPTYPE_ACX111 == priv->chip_type) {
+		sent_rate = 1 << highest_bit(rate111 & RATE111_ALL);
+	} else {
+		sent_rate = rate100to111(rate100);
+	}
+	/* sent_rate has only one bit set now, corresponding to tx rate
+	 * which was used by hardware to tx this particular packet */
+
+	/* now do the actual auto rate management */
+	acxlog(L_DEBUG, "tx: %sclient=%p/"MACSTR" used=%04X cur=%04X cfg=%04X "
+		"__=%u/%u ^^=%u/%u\n",
+		(txc->ignore_count > 0) ? "[IGN] " : "",
+		txc, MAC(txc->address), sent_rate, cur, txc->rate_cfg,
+		txc->fallback_count, priv->fallback_threshold,
+		txc->stepup_count, priv->stepup_threshold
+	);
+
+	/* we need to ignore old packets already in the tx queue since
+	 * they use older rate bytes configured before our last rate change,
+	 * otherwise our mechanism will get confused by interpreting old data.
+	 * Do it here only, in order to have the logging above */
+	if (txc->ignore_count) {
+		txc->ignore_count--;
+		return;
+	}
+
+	/* true only if the only nonzero bit in sent_rate is
+	** less significant than highest nonzero bit in cur */
+	slower_rate_was_used = ( cur > ((sent_rate<<1)-1) );
+
+	if (slower_rate_was_used || (error & 0x30)) {
+		txc->stepup_count = 0;
+		if (++txc->fallback_count <= priv->fallback_threshold)
+			return;
+		txc->fallback_count = 0;
+
+		/* clear highest 1 bit in cur */
+		sent_rate = RATE111_54;
+		while (!(cur & sent_rate)) sent_rate >>= 1;
+		CLEAR_BIT(cur, sent_rate);
+
+		if (cur) { /* we can't disable all rates! */
+			acxlog(L_XFER, "tx: falling back to ratemask %04X\n", cur);
+			txc->rate_cur = cur;
+			txc->ignore_count = priv->TxQueueCnt - priv->TxQueueFree;
+		}
+	} else if (!slower_rate_was_used) {
+		txc->fallback_count = 0;
+		if (++txc->stepup_count <= priv->stepup_threshold)
+			return;
+		txc->stepup_count = 0;
+
+		/* sanitize. Sort of not needed, but I dont trust hw that much...
+		** what if it can report bogus tx rates sometimes? */
+		while (!(cur & sent_rate)) sent_rate >>= 1;
+		/* try to find a higher sent_rate that isn't yet in our
+		 * current set, but is an allowed cfg */
+		while (1) {
+			sent_rate <<= 1;
+			if (sent_rate > txc->rate_cfg)
+				/* no higher rates allowed by config */
+				return;
+			if (!(cur & sent_rate) && (txc->rate_cfg & sent_rate))
+				/* found */
+				break;
+			/* not found, try higher one */
+		}
+		SET_BIT(cur, sent_rate);
+		acxlog(L_XFER, "tx: stepping up to ratemask %04X\n", cur);
+		txc->rate_cur = cur;
+		/* FIXME: totally bogus - we could be sending to many peers at once... */
+		txc->ignore_count = priv->TxQueueCnt - priv->TxQueueFree;
+	}
+
+	/* calculate acx100 style rate byte if needed */
+	if (CHIPTYPE_ACX100 == priv->chip_type) {
+		txc->rate_100 = bitpos2rate100[highest_bit(cur)];
+	}
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_log_txbuffer
+*----------------------------------------------------------------*/
+#if !ACX_DEBUG
+static inline void acx_l_log_txbuffer(const TIWLAN_DC *pDc) {}
+#else
+static void
+acx_l_log_txbuffer(const TIWLAN_DC *pDc)
+{
+	txdesc_t *pTxDesc;
+	int i;
+
+	/* no FN_ENTER here, we don't want that */
+	/* no locks here, since it's entirely non-critical code */
+	pTxDesc = pDc->pTxDescQPool;
+	for (i = 0; i < pDc->tx_pool_count; i++) {
+		if ((pTxDesc->Ctl_8 & DESC_CTL_DONE) == DESC_CTL_DONE)
+			printk("tx: buf %d done\n", i);
+		pTxDesc = GET_NEXT_TX_DESC_PTR(pDc, pTxDesc);
+	}
+}
+#endif
+
+
+/*----------------------------------------------------------------
+* acx_l_clean_tx_desc
+*
+* Comment:
+*	This function probably resets the txdescs' status when the ACX100
+*	signals the TX done IRQ (txdescs have been processed), starting with
+*	the pool index of the descriptor which we would use next,
+*	in order to make sure that we can be as fast as possible
+*	in filling new txdescs.
+*	Oops, now we have our own index, so everytime we get called we know
+*	where the next packet to be cleaned is.
+*	Hmm, still need to loop through the whole ring buffer now,
+*	since we lost sync for some reason when ping flooding or so...
+*	(somehow we don't get the IRQ for acx_l_clean_tx_desc any more when
+*	too many packets are being sent!)
+*	FIXME: currently we only process one packet, but this gets out of
+*	sync for some reason when ping flooding, so we need to loop,
+*	but the previous smart loop implementation causes the ping latency
+*	to rise dramatically (~3000 ms), at least on CardBus PheeNet WL-0022.
+*	Dunno what to do :-\
+*----------------------------------------------------------------*/
+unsigned int
+acx_l_clean_tx_desc(wlandevice_t *priv)
+{
+	TIWLAN_DC *pDc = &priv->dc;
+	txdesc_t *pTxDesc;
+	struct client *txc;
+	unsigned int watch, finger;
+	unsigned int num_cleaned = 0, num_processed = 0;
+	u16 r111;
+	u8 error, ack_failures, rts_failures, rts_ok, r100; /* keep old desc status */
+
+	FN_ENTER;
+
+	if (unlikely(acx_debug & L_DEBUG))
+		acx_l_log_txbuffer(pDc);
+
+	acxlog(L_BUFT, "tx: cleaning up bufs from %u\n", pDc->tx_tail);
+
+	watch = pDc->tx_tail;
+	finger = watch;
+
+	do {
+		pTxDesc = GET_TX_DESC_PTR(pDc, finger);
+
+		/* abort if txdesc is not marked as "Tx finished" and "owned" */
+		if ((pTxDesc->Ctl_8 & DESC_CTL_DONE) != DESC_CTL_DONE) {
+			/* we do need to have at least one cleaned,
+			 * otherwise we wouldn't get called in the first place.
+			 * So better stay around some more, unless
+			 * we already processed more descs than the ring
+			 * size. */
+			if ((num_cleaned == 0) && (num_processed < pDc->tx_pool_count))
+				goto next;
+			else
+				break;
+		}
+
+		/* remember descr values... */
+		error = pTxDesc->error;
+		ack_failures = pTxDesc->ack_failures;
+		rts_failures = pTxDesc->rts_failures;
+		rts_ok = pTxDesc->rts_ok;
+		r100 = pTxDesc->u.r1.rate;
+		r111 = pTxDesc->u.r2.rate111;
+
+#if WIRELESS_EXT > 13 /* wireless_send_event() and IWEVTXDROP are WE13 */
+		/* need to check for certain error conditions before we
+		 * clean the descriptor: we still need valid descr data here */
+		if (unlikely(0x30 & error)) {
+			/* only send IWEVTXDROP in case of retry or lifetime exceeded;
+			 * all other errors mean we screwed up locally */
+			union iwreq_data wrqu;
+			wlan_hdr_t *hdr;
+			txhostdesc_t *hostdesc;
+
+			hostdesc = pTxDesc->fixed_size.s.host_desc;
+			/* FIXME: happens on card eject; better method? */
+			if (unlikely((long)-1 == (long)hostdesc))
+				goto end;
+			hdr = (wlan_hdr_t *)hostdesc->data;
+			MAC_COPY(wrqu.addr.sa_data, hdr->a1);
+			wireless_send_event(priv->netdev, IWEVTXDROP, &wrqu, NULL);
+		}
+#endif
+		/* ...and free the descr */
+		pTxDesc->error = 0;
+		pTxDesc->ack_failures = 0;
+		pTxDesc->rts_failures = 0;
+		pTxDesc->rts_ok = 0;
+		/* signal host owning it LAST, since ACX already knows that this
+		 * descriptor is finished since it set Ctl_8 accordingly:
+		 * if _OWN is set at the beginning instead, our own get_tx
+		 * might choose a Tx desc that isn't fully cleared
+		 * (in case of bad locking). */
+		pTxDesc->Ctl_8 = DESC_CTL_HOSTOWN;
+		priv->TxQueueFree++;
+		num_cleaned++;
+
+		if ((priv->TxQueueFree >= MINFREE_TX + 3)
+		&& (priv->status == ACX_STATUS_4_ASSOCIATED)
+		&& (acx_queue_stopped(priv->netdev))
+		) {
+			acxlog(L_BUF, "tx: wake queue (avail. Tx desc %u)\n",
+					priv->TxQueueFree);
+			acx_wake_queue(priv->netdev, NULL);
+		}
+
+		/* do error checking, rate handling and logging
+		 * AFTER having done the work, it's faster */
+
+		/* do rate handling */
+		txc = pTxDesc->fixed_size.s.txc;
+		if (txc && priv->rate_auto) {
+			acx_l_handle_txrate_auto(priv, txc, finger, r100, r111, error);
+		}
+
+		if (unlikely(error))
+			acx_l_handle_tx_error(priv, error, finger);
+
+		if (CHIPTYPE_ACX111 == priv->chip_type)
+			acxlog(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u r111=%04X\n",
+				finger, ack_failures, rts_failures, rts_ok, r111);
+		else
+			acxlog(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u rate=%u\n",
+				finger, ack_failures, rts_failures, rts_ok, le16_to_cpu(r111) & 0xff);
+next:
+		/* update pointer for descr to be cleaned next */
+		finger = (finger + 1) % pDc->tx_pool_count;
+		num_processed++;
+	} while (watch != finger);
+
+	/* remember last position */
+	pDc->tx_tail = finger;
+end:
+	FN_EXIT1(num_cleaned);
+	return num_cleaned;
+}
+
+/* clean *all* Tx descriptors, and regardless of their previous state.
+ * Used for brute-force reset handling. */
+void
+acx_l_clean_tx_desc_emergency(wlandevice_t *priv)
+{
+	TIWLAN_DC *pDc = &priv->dc;
+	txdesc_t *pTxDesc;
+	unsigned int i;
+
+	FN_ENTER;
+
+	for (i = 0; i < pDc->tx_pool_count; i++) {
+		pTxDesc = GET_TX_DESC_PTR(pDc, i);
+
+		/* free it */
+		pTxDesc->ack_failures = 0;
+		pTxDesc->rts_failures = 0;
+		pTxDesc->rts_ok = 0;
+		pTxDesc->error = 0;
+		pTxDesc->Ctl_8 = DESC_CTL_HOSTOWN;
+	}
+
+	priv->TxQueueFree = pDc->tx_pool_count;
+
+	FN_EXIT0;
+}
+
+
+/*----------------------------------------------------------------
+* acx_l_log_rxbuffer
+*
+* Called from IRQ context only
+*----------------------------------------------------------------*/
+#if !ACX_DEBUG
+static inline void acx_l_log_rxbuffer(const TIWLAN_DC *pDc) {}
+#else
+static void
+acx_l_log_rxbuffer(const TIWLAN_DC *pDc)
+{
+	const struct rxhostdesc *pRxDesc;
+	int i;
+
+	/* no FN_ENTER here, we don't want that */
+
+	pRxDesc = pDc->pRxHostDescQPool;
+	for (i = 0; i < pDc->rx_pool_count; i++) {
+		if ((pRxDesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN))
+		 && (pRxDesc->Status & cpu_to_le32(BIT31)))
+			printk("rx: buf %d full\n", i);
+		pRxDesc++;
+	}
+}
+#endif
+
+
+/***************************************************************
+** acx_l_process_rx_desc
+**
+** Called directly and only from the IRQ handler
+*/
+void
+acx_l_process_rx_desc(wlandevice_t *priv)
+{
+	struct rxhostdesc *RxHostPool;
+	TIWLAN_DC *pDc;
+	struct rxhostdesc *pRxHostDesc;
+	unsigned int curr_idx;
+	unsigned int count = 0;
+
+	FN_ENTER;
+
+	pDc = &priv->dc;
+	if (unlikely(acx_debug & L_BUFR)) {
+		acx_l_log_rxbuffer(pDc);
+	}
+
+	RxHostPool = pDc->pRxHostDescQPool;
+
+	/* First, have a loop to determine the first descriptor that's
+	 * full, just in case there's a mismatch between our current
+	 * rx_tail and the full descriptor we're supposed to handle. */
+	while (1) {
+		curr_idx = pDc->rx_tail;
+		pRxHostDesc = &RxHostPool[pDc->rx_tail];
+		pDc->rx_tail = (pDc->rx_tail + 1) % pDc->rx_pool_count;
+		rmb();
+		if ((pRxHostDesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN))
+		 && (pRxHostDesc->Status & cpu_to_le32(BIT31))) {
+			/* found it! */
+			break;
+		}
+		count++;
+		if (unlikely(count > pDc->rx_pool_count)) {
+			/* hmm, no luck: all descriptors empty, bail out */
+			goto end;
+		}
+	}
+
+	/* now process descriptors, starting with the first we figured out */
+	while (1) {
+		acxlog(L_BUFR, "%s: using curr_idx %u, rx_tail is now %u\n",
+				__func__, curr_idx, pDc->rx_tail);
+
+		/* FIXME: comment needed - why rmb()? */
+		rmb();
+		acx_l_process_rxbuf(priv, pRxHostDesc->data);
+
+		pRxHostDesc->Status = 0;
+		/* make sure adapter sees CTL_HOSTOWN change only after
+		 * all other fields have been written */
+		wmb();
+		/* Host no longer owns this, needs to be LAST */
+		CLEAR_BIT(pRxHostDesc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN));
+
+		/* ok, descriptor is handled, now check the next descriptor */
+		curr_idx = pDc->rx_tail;
+		pRxHostDesc = &RxHostPool[pDc->rx_tail];
+
+		/* if next descriptor is empty, then bail out */
+		/* FIXME: is this check really entirely correct?? */
+		rmb();
+		/*
+		if (!(pRxHostDesc->Ctl & cpu_to_le16(DESC_CTL_HOSTOWN))
+		 && !(pRxHostDesc->Status & cpu_to_le32(BIT31))) */
+		if (!(pRxHostDesc->Status & cpu_to_le32(BIT31)))
+			break;
+		else
+			pDc->rx_tail = (pDc->rx_tail + 1) % pDc->rx_pool_count;
+	}
+end:
+	FN_EXIT0;
+}
+
+
+/*----------------------------------------------------------------
+* acx_s_create_tx_host_desc_queue
+*----------------------------------------------------------------*/
+static inline void*
+acx_alloc_coherent(struct pci_dev *hwdev, size_t size,
+			dma_addr_t *dma_handle, int flag)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 53)
+	return dma_alloc_coherent(hwdev == NULL ? NULL : &hwdev->dev,
+			size, dma_handle, flag);
+#else
+#warning Using old PCI-specific DMA allocation, may fail with out-of-mem!
+#warning Upgrade kernel if it does...
+	return pci_alloc_consistent(hwdev, size, dma_handle);
+#endif
+}
+
+static void*
+allocate(wlandevice_t *priv, size_t size, dma_addr_t *phy, const char *msg)
+{
+	void *ptr = acx_alloc_coherent(priv->pdev, size, phy, GFP_KERNEL);
+	if (ptr) {
+		acxlog(L_DEBUG, "%s sz=%d adr=0x%p phy=0x%08llx\n",
+				msg, (int)size, ptr, (unsigned long long)*phy);
+		memset(ptr, 0, size);
+		return ptr;
+	}
+	printk(KERN_ERR "acx: %s allocation FAILED (%d bytes)\n",
+					msg, (int)size);
+	return NULL;
+}
+
+int
+acx_s_create_tx_host_desc_queue(TIWLAN_DC *pDc)
+{
+	wlandevice_t *priv = pDc->priv;
+	struct txhostdesc *host_desc;
+	struct txhostdesc *host_desc_phy;
+	u8 *frame_buffer;
+	u8 *frame_buffer_phy;
+	unsigned int align_offs, alignment;
+	int i;
+
+	FN_ENTER;
+
+	/* allocate TX buffer */
+	pDc->TxBufferPoolSize = priv->TxQueueCnt * WLAN_A4FR_MAXLEN_WEP;
+	pDc->pTxBufferPool = allocate(priv, pDc->TxBufferPoolSize,
+			&pDc->TxBufferPoolPhyAddr, "pTxBufferPool");
+	if (!pDc->pTxBufferPool)
+		goto fail;
+
+	/* allocate the TX host descriptor queue pool */
+	pDc->TxHostDescQPoolSize = priv->TxQueueCnt * 2*sizeof(struct txhostdesc) + 3;
+	pDc->pTxHostDescQPool = allocate(priv, pDc->TxHostDescQPoolSize,
+			&pDc->TxHostDescQPoolPhyAddr, "pTxHostDescQPool");
+	if (!pDc->pTxHostDescQPool)
+		goto fail;
+
+	/* check for proper alignment of TX host descriptor pool */
+	alignment = (long) pDc->pTxHostDescQPool & 3;
+	if (alignment) {
+		printk("acx: TxHostDescQPool is not aligned properly\n");
+		align_offs = 4 - alignment;
+	} else {
+		align_offs = 0;
+	}
+
+/* Each tx frame buffer is accessed by hardware via
+** txdesc -> txhostdesc(s) -> framebuffer(s)
+** We use only one txhostdesc per txdesc, but it looks like
+** acx111 is buggy: it accesses second txhostdesc
+** (via hostdesc.desc_phy_next field) even if
+** txdesc->length == hostdesc->length and thus
+** entire packet was placed into first txhostdesc.
+** Due to this bug acx111 hangs unless second txhostdesc
+** has hostdesc.length = 3 (or larger)
+** Storing NULL into hostdesc.desc_phy_next
+** doesn't seem to help.
+*/
+/* It is not known whether we need to have 'extra' second
+** txhostdescs for acx100. Maybe it is acx111-only bug.
+*/
+	host_desc = (struct txhostdesc *) ((u8 *) pDc->pTxHostDescQPool + align_offs);
+	host_desc_phy = (struct txhostdesc *) ((u8 *) pDc->TxHostDescQPoolPhyAddr + align_offs);
+	frame_buffer = (u8 *) pDc->pTxBufferPool;
+	frame_buffer_phy = (u8 *) pDc->TxBufferPoolPhyAddr;
+
+	for (i = 0; i < priv->TxQueueCnt*2; i++) {
+		if (!(i & 1)) {
+			host_desc->data_phy = ptr2acx(frame_buffer_phy);
+			/* host_desc->data_offset = ... */
+			/* host_desc->reserved = ... */
+			host_desc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN);
+			host_desc->desc_phy_next = ptr2acx(host_desc_phy + 1);
+			host_desc->pNext = ptr2acx(NULL);
+			/* host_desc->Status = ... */
+			/* below: non-hardware fields */
+			host_desc->data = frame_buffer;
+
+			frame_buffer += WLAN_A4FR_MAXLEN_WEP;
+			frame_buffer_phy += WLAN_A4FR_MAXLEN_WEP;
+		} else {
+			/* host_desc->data_phy = ... */
+			/* host_desc->data_offset = ... */
+			/* host_desc->reserved = ... */
+			/* host_desc->Ctl_16 = ... */
+			host_desc->length = 3; /* bug workaround */
+			/* host_desc->desc_phy_next = ... */
+			/* host_desc->pNext = ... */
+			/* host_desc->Status = ... */
+			/* below: non-hardware fields */
+			/* host_desc->data = ... */
+		}
+		host_desc_phy++;
+		host_desc++;
+	}
+	FN_EXIT1(OK);
+	return OK;
+fail:
+	/* dealloc will be done by free function on error case */
+	FN_EXIT1(NOT_OK);
+	return NOT_OK;
+}
+
+
+/***************************************************************
+** acx_s_create_rx_host_desc_queue
+*/
+/* the whole size of a data buffer (header plus data body)
+ * plus 32 bytes safety offset at the end */
+#define RX_BUFFER_SIZE (sizeof(rxbuffer_t) + 32)
+
+int
+acx_s_create_rx_host_desc_queue(TIWLAN_DC *pDc)
+{
+	wlandevice_t *priv = pDc->priv;
+	struct rxhostdesc *host_desc;
+	rxbuffer_t *data;
+	struct rxhostdesc *host_desc_phy;
+	rxbuffer_t *data_phy;
+	unsigned int align_offs, alignment;
+	int result = NOT_OK;
+	int i;
+
+	FN_ENTER;
+
+	/* allocate the RX host descriptor queue pool */
+	pDc->RxHostDescQPoolSize = (priv->RxQueueCnt * sizeof(struct rxhostdesc)) + 0x3;
+
+	pDc->pRxHostDescQPool = allocate(priv, pDc->RxHostDescQPoolSize,
+			&pDc->RxHostDescQPoolPhyAddr, "pRxHostDescQPool");
+	if (!pDc->pRxHostDescQPool)
+		goto fail;
+
+	/* allocate Rx buffer pool which will be used by the acx
+	 * to store the whole content of the received frames in it */
+	pDc->RxBufferPoolSize = ( priv->RxQueueCnt * RX_BUFFER_SIZE );
+	pDc->pRxBufferPool = allocate(priv, pDc->RxBufferPoolSize,
+			&pDc->RxBufferPoolPhyAddr, "pRxBufferPool");
+	if (!pDc->pRxBufferPool)
+		goto fail;
+
+	data = pDc->pRxBufferPool;
+	data_phy = (rxbuffer_t *) pDc->RxBufferPoolPhyAddr;
+
+	/* check for proper alignment of RX host descriptor pool */
+	alignment = (long) pDc->pRxHostDescQPool & 3;
+	if (alignment) {
+		printk("acx: RxHostDescQPool is not aligned properly\n");
+		align_offs = 4 - alignment;
+	} else {
+		align_offs = 0;
+	}
+
+	host_desc = (struct rxhostdesc *) ((u8 *) pDc->pRxHostDescQPool + align_offs);
+	host_desc_phy = (struct rxhostdesc *) ((u8 *) pDc->RxHostDescQPoolPhyAddr + align_offs);
+	priv->RxHostDescPoolStart = host_desc_phy;
+
+	/* don't make any popular C programming pointer arithmetic mistakes
+	 * here, otherwise I'll kill you...
+	 * (and don't dare asking me why I'm warning you about that...) */
+	for (i = 0; i < priv->RxQueueCnt - 1; i++) {
+		host_desc->data = data;
+		data++; /* proceed to content of next buffer */
+		host_desc->data_phy = ptr2acx(data_phy);
+		data_phy++; /* proceed to content of next buffer */
+		host_desc->length = cpu_to_le16(RX_BUFFER_SIZE);
+		CLEAR_BIT(host_desc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN));
+		host_desc->desc_phy_next = ptr2acx(host_desc_phy + 1);
+		host_desc_phy++;
+		host_desc++;
+	}
+	host_desc->data = data;
+	host_desc->data_phy = ptr2acx(data_phy);
+	host_desc->length = cpu_to_le16(RX_BUFFER_SIZE);
+	CLEAR_BIT(host_desc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN));
+	host_desc->desc_phy_next = ptr2acx((u8 *) pDc->RxHostDescQPoolPhyAddr + align_offs);
+	result = OK;
+fail:
+	/* dealloc will be done by free function on error case */
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/***************************************************************
+** acx_s_create_tx_desc_queue
+*/
+extern void BUG_txdesc_must_be_0x30_bytes_in_length(void);
+
+void
+acx_s_create_tx_desc_queue(TIWLAN_DC *pDc)
+{
+	wlandevice_t *priv = pDc->priv;
+	struct txdesc *tx_desc;
+	struct txhostdesc *tx_hostdesc;
+	dma_addr_t hostmemptr; /* remains 0 in USB case */
+	u32 mem_offs;
+	int i;
+
+	FN_ENTER;
+
+	pDc->tx_pool_count = priv->TxQueueCnt;
+
+	pDc->TxDescrSize = sizeof(struct txdesc);
+
+	if (sizeof(struct txdesc) != 0x30)
+		BUG_txdesc_must_be_0x30_bytes_in_length();
+
+	if (priv->chip_type == CHIPTYPE_ACX111) {
+		/* the acx111 txdesc is 4 bytes larger */
+		pDc->TxDescrSize = sizeof(struct txdesc)+4;
+	}
+
+	if (pDc->pTxDescQPool == NULL) { /* calculate it */
+		pDc->pTxDescQPool = (struct txdesc *) (priv->iobase2 +
+					     pDc->ui32ACXTxQueueStart);
+	}
+
+	acxlog(L_DEBUG, "priv->iobase2 = 0x%p\n"
+			"pDc->ui32ACXTxQueueStart = 0x%08X\n"
+			"pDc->pTxDescQPool = 0x%p\n",
+			priv->iobase2,
+			pDc->ui32ACXTxQueueStart,
+			pDc->pTxDescQPool);
+
+	priv->TxQueueFree = priv->TxQueueCnt;
+	pDc->tx_head = 0;
+	pDc->tx_tail = 0;
+	tx_desc = pDc->pTxDescQPool;
+	mem_offs = pDc->ui32ACXTxQueueStart;
+	hostmemptr = pDc->TxHostDescQPoolPhyAddr;
+	tx_hostdesc = pDc->pTxHostDescQPool;
+
+	if (CHIPTYPE_ACX111 == priv->chip_type) {
+		/* ACX111 has a preinitialized Tx buffer! */
+		/* loop over whole send pool */
+		/* FIXME: do we have to do the hostmemptr stuff here?? */
+		for (i = 0; i < pDc->tx_pool_count; i++) {
+			tx_desc->HostMemPtr = ptr2acx(hostmemptr);
+			tx_desc->Ctl_8 = DESC_CTL_HOSTOWN;
+			tx_desc->fixed_size.s.host_desc = tx_hostdesc;
+
+			/* reserve two (hdr desc and payload desc) */
+			tx_hostdesc += 2;
+			hostmemptr += 2 * sizeof(struct txhostdesc);
+			tx_desc = (struct txdesc *)(((u8 *)tx_desc) + pDc->TxDescrSize);
+		}
+
+	} else {
+		/* ACX100 Tx buffer needs to be initialized by us */
+		/* clear whole send pool */
+		memset(pDc->pTxDescQPool, 0, pDc->tx_pool_count * pDc->TxDescrSize);
+
+		/* loop over whole send pool */
+		for (i = 0; i < pDc->tx_pool_count; i++) {
+			acxlog(L_DEBUG, "configure card tx descriptor: 0x%p, "
+				"size: 0x%X\n", tx_desc, pDc->TxDescrSize);
+
+			/* pointer to hostdesc memory */
+			/* FIXME: type-incorrect assignment, might cause trouble
+			 * in some cases */
+			tx_desc->HostMemPtr = ptr2acx(hostmemptr);
+			/* initialise ctl */
+			tx_desc->Ctl_8 = DESC_CTL_INIT;
+			tx_desc->Ctl2_8 = 0;
+			/* point to next txdesc */
+			tx_desc->pNextDesc = cpu2acx(mem_offs + pDc->TxDescrSize);
+			/* pointer to first txhostdesc */
+			tx_desc->fixed_size.s.host_desc = tx_hostdesc;
+
+			/* reserve two (hdr desc and payload desc) */
+			tx_hostdesc += 2;
+			hostmemptr += 2 * sizeof(struct txhostdesc);
+			/* go to the next one */
+			mem_offs += pDc->TxDescrSize;
+			tx_desc = (struct txdesc *)(((u8 *)tx_desc) + pDc->TxDescrSize);
+		}
+		/* go back to the last one */
+		tx_desc = (struct txdesc *)(((u8 *)tx_desc) - pDc->TxDescrSize);
+		/* and point to the first making it a ring buffer */
+		tx_desc->pNextDesc = cpu2acx(pDc->ui32ACXTxQueueStart);
+	}
+	FN_EXIT0;
+}
+
+
+/***************************************************************
+** acx_create_rx_desc_queue
+*/
+void
+acx_create_rx_desc_queue(TIWLAN_DC *pDc)
+{
+	wlandevice_t *priv = pDc->priv;
+	struct rxdesc *rx_desc;
+	u32 mem_offs;
+	int i;
+
+	FN_ENTER;
+
+	pDc->rx_pool_count = priv->RxQueueCnt;
+	pDc->rx_tail = 0;
+
+	/* ACX111 doesn't need any further config: preconfigures itself.
+	 * Simply print ring buffer for debugging */
+	if (CHIPTYPE_ACX111 == priv->chip_type) {
+		/* pRxDescQPool already set here */
+		rx_desc = pDc->pRxDescQPool;
+		for (i = 0; i < pDc->rx_pool_count; i++) {
+			acxlog(L_DEBUG, "rx descriptor %d @ 0x%p\n", i, rx_desc);
+			rx_desc = pDc->pRxDescQPool = (struct rxdesc *)
+				(priv->iobase2 + acx2cpu(rx_desc->pNextDesc));
+		}
+	} else {
+		/* we didn't pre-calculate pRxDescQPool in case of ACX100 */
+		/* pRxDescQPool should be right AFTER Tx pool */
+		pDc->pRxDescQPool = (struct rxdesc *)
+			((u8 *) pDc->pTxDescQPool + (priv->TxQueueCnt * sizeof(struct txdesc)));
+
+		memset(pDc->pRxDescQPool, 0, pDc->rx_pool_count * sizeof(struct rxdesc));
+
+		/* loop over whole receive pool */
+		rx_desc = pDc->pRxDescQPool;
+		mem_offs = pDc->ui32ACXRxQueueStart;
+		for (i = 0; i < pDc->rx_pool_count; i++) {
+			acxlog(L_DEBUG, "rx descriptor @ 0x%p\n", rx_desc);
+			rx_desc->Ctl_8 = DESC_CTL_RECLAIM | DESC_CTL_AUTODMA;
+			/* point to next rxdesc */
+			rx_desc->pNextDesc = cpu2acx(mem_offs + sizeof(struct rxdesc));
+			/* go to the next one */
+			mem_offs += sizeof(struct rxdesc);
+			rx_desc++;
+		}
+		/* go to the last one */
+		rx_desc--;
+
+		/* and point to the first making it a ring buffer */
+		rx_desc->pNextDesc = cpu2acx(pDc->ui32ACXRxQueueStart);
+	}
+	FN_EXIT0;
+}
+
+
+/*----------------------------------------------------------------
+* acx_e_init_module
+*
+* Module initialization routine, called once at module load time.
+*
+* Returns:
+*	0	- success
+*	~0	- failure, module is unloaded.
+*
+* Call context:
+*	process thread (insmod or modprobe)
+----------------------------------------------------------------*/
+/* introduced earlier than 2.6.10, but takes more memory, so don't use it
+ * if there's no compile warning by kernel */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)
+
+#if ACX_DEBUG
+module_param(acx_debug, uint, 0);
+#endif
+#if USE_FW_LOADER_LEGACY
+module_param(firmware_dir, charp, 0);
+#endif
+
+#else
+
+#if ACX_DEBUG
+/* doh, 2.6.x screwed up big time: here the define has its own ";"
+ * ("double ; detected"), yet in 2.4.x it DOESN'T (the sane thing to do),
+ * grrrrr! */
+MODULE_PARM(debug, "i");
+#endif
+#if USE_FW_LOADER_LEGACY
+MODULE_PARM(firmware_dir, "s");
+#endif
+
+#endif
+
+MODULE_PARM_DESC(debug, "Debug level mask (see L_xxx constants)");
+#if USE_FW_LOADER_LEGACY
+MODULE_PARM_DESC(firmware_dir, "Directory to load acx100 firmware files from");
+#endif
+#if SEPARATE_DRIVER_INSTANCES
+MODULE_PARM(card, "i");
+MODULE_PARM_DESC(card, "Associate only with card-th acx100 card from this "
+				"driver instance");
+#endif /* SEPARATE_DRIVER_INSTANCES */
+
+static int __init
+acx_e_init_module(void)
+{
+	int res;
+
+	FN_ENTER;
+
+	printk("acx: this driver is still EXPERIMENTAL\n"
+	       "acx: reading README file and/or Craig's HOWTO is "
+	       "recommended, visit http://acx100.sf.net in case "
+	       "of further questions/discussion\n");
+
+#if (ACX_IO_WIDTH==32)
+	printk("acx: compiled to use 32bit I/O access. "
+		"I/O timing issues might occur, such as "
+		"non-working firmware upload. Report them\n");
+#else
+	printk("acx: compiled to use 16bit I/O access only "
+		"(compatibility mode)\n");
+#endif
+
+#ifdef __LITTLE_ENDIAN
+	acxlog(L_INIT, "running on a little-endian CPU\n");
+#else
+	acxlog(L_INIT, "running on a BIG-ENDIAN CPU\n");
+#endif
+	acxlog(L_INIT, "acx_pci module " WLAN_RELEASE " initialized, "
+		"waiting for cards to probe...\n");
+
+	res = pci_module_init(&acx_pci_drv_id);
+	FN_EXIT1(res);
+	return res;
+}
+
+
+/*----------------------------------------------------------------
+* acx_e_cleanup_module
+*
+* Called at module unload time.  This is our last chance to
+* clean up after ourselves.
+*
+* Call context:
+*	process thread
+----------------------------------------------------------------*/
+static void __exit
+acx_e_cleanup_module(void)
+{
+	const struct net_device *dev;
+	unsigned long flags;
+
+	FN_ENTER;
+
+	/* Since the whole module is about to be unloaded,
+	 * we recursively shutdown all cards we handled instead
+	 * of doing it in remove_pci() (which will be activated by us
+	 * via pci_unregister_driver at the end).
+	 * remove_pci() might just get called after a card eject,
+	 * that's why hardware operations have to be done here instead
+	 * when the hardware is available. */
+
+	down(&root_acx_dev_sem);
+
+	dev = root_acx_dev.newest;
+	while (dev != NULL) {
+		/* doh, netdev_priv() doesn't have const! */
+		wlandevice_t *priv = acx_netdev_priv((struct net_device *)dev);
+
+		acx_sem_lock(priv);
+
+		/* disable both Tx and Rx to shut radio down properly */
+		acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_TX, NULL, 0);
+		acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_RX, NULL, 0);
+
+#if REDUNDANT
+		/* put the eCPU to sleep to save power
+		 * Halting is not possible currently,
+		 * since not supported by all firmware versions */
+		acx_s_issue_cmd(priv, ACX100_CMD_SLEEP, NULL, 0);
+#endif
+		acx_lock(priv, flags);
+
+		/* disable power LED to save power :-) */
+		acxlog(L_INIT, "switching off power LED to save power :-)\n");
+		acx_l_power_led(priv, 0);
+
+		/* stop our eCPU */
+		if (priv->chip_type == CHIPTYPE_ACX111) {
+			/* FIXME: does this actually keep halting the eCPU?
+			 * I don't think so...
+			 */
+			acx_l_reset_mac(priv);
+		} else {
+			u16 temp;
+
+			/* halt eCPU */
+			temp = acx_read_reg16(priv, IO_ACX_ECPU_CTRL) | 0x1;
+			acx_write_reg16(priv, IO_ACX_ECPU_CTRL, temp);
+			acx_write_flush(priv);
+		}
+
+		acx_unlock(priv, flags);
+
+		acx_sem_unlock(priv);
+
+		dev = priv->prev_nd;
+	}
+
+	up(&root_acx_dev_sem);
+
+	/* now let the PCI layer recursively remove
+	 * all PCI related things (acx_e_remove_pci()) */
+	pci_unregister_driver(&acx_pci_drv_id);
+
+	FN_EXIT0;
+}
+
+module_init(acx_e_init_module)
+module_exit(acx_e_cleanup_module)
+
+#if USE_FW_LOADER_LEGACY
+static int __init acx_get_firmware_dir(const char *str)
+{
+	/* I've seen other drivers just pass the string pointer,
+	 * so hopefully that's safe */
+	firmware_dir = str;
+	return OK;
+}
+
+__setup("acx_firmware_dir=", acx_get_firmware_dir);
+#endif
diff -urN oldtree/drivers/net/wireless/tiacx/pci_helper.c newtree/drivers/net/wireless/tiacx/pci_helper.c
--- oldtree/drivers/net/wireless/tiacx/pci_helper.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/pci_helper.c	2006-02-13 19:20:00.542429840 -0500
@@ -0,0 +1,2 @@
+#define ACX_PCI 1
+#include "helper.c"
diff -urN oldtree/drivers/net/wireless/tiacx/setrate.c newtree/drivers/net/wireless/tiacx/setrate.c
--- oldtree/drivers/net/wireless/tiacx/setrate.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/setrate.c	2006-02-13 19:20:00.543429688 -0500
@@ -0,0 +1,213 @@
+/* TODO: stop #including, move into wireless.c
+ * until then, keep in sync copies in prism54/ and acx/ dirs
+ * code+data size: less than 1k */
+
+enum {
+	DOT11_RATE_1,
+	DOT11_RATE_2,
+	DOT11_RATE_5,
+	DOT11_RATE_11,
+	DOT11_RATE_22,
+	DOT11_RATE_33,
+	DOT11_RATE_6,
+	DOT11_RATE_9,
+	DOT11_RATE_12,
+	DOT11_RATE_18,
+	DOT11_RATE_24,
+	DOT11_RATE_36,
+	DOT11_RATE_48,
+	DOT11_RATE_54
+};
+enum {
+	DOT11_MOD_DBPSK,
+	DOT11_MOD_DQPSK,
+	DOT11_MOD_CCK,
+	DOT11_MOD_OFDM,
+	DOT11_MOD_CCKOFDM,
+	DOT11_MOD_PBCC
+};
+static const u8 ratelist[] = { 1,2,5,11,22,33,6,9,12,18,24,36,48,54 };
+static const u8 dot11ratebyte[] = { 1*2,2*2,11,11*2,22*2,33*2,6*2,9*2,12*2,18*2,24*2,36*2,48*2,54*2 };
+static const u8 default_modulation[] = {
+	DOT11_MOD_DBPSK,
+	DOT11_MOD_DQPSK,
+	DOT11_MOD_CCK,
+	DOT11_MOD_CCK,
+	DOT11_MOD_PBCC,
+	DOT11_MOD_PBCC,
+	DOT11_MOD_OFDM,
+	DOT11_MOD_OFDM,
+	DOT11_MOD_OFDM,
+	DOT11_MOD_OFDM,
+	DOT11_MOD_OFDM,
+	DOT11_MOD_OFDM,
+	DOT11_MOD_OFDM,
+	DOT11_MOD_OFDM
+};
+
+static /* TODO: remove 'static' when moved to wireless.c */
+int
+rate_mbit2enum(int n) {
+	int i=0;
+	while(i<sizeof(ratelist)) {
+		if(n==ratelist[i]) return i;
+		i++;
+	}
+	return -EINVAL;
+}
+
+static int
+get_modulation(int r_enum, char suffix) {
+	if(suffix==',' || suffix==' ' || suffix=='\0') {
+		/* could shorten default_mod by 8 bytes:
+		if(r_enum>=DOT11_RATE_6) return DOT11_MOD_OFDM; */
+		return default_modulation[r_enum];
+	}
+	if(suffix=='c') {
+		if(r_enum<DOT11_RATE_5 || r_enum>DOT11_RATE_11) return -EINVAL;
+		return DOT11_MOD_CCK;
+	}
+	if(suffix=='p') {
+		if(r_enum<DOT11_RATE_5 || r_enum>DOT11_RATE_33) return -EINVAL;
+		return DOT11_MOD_PBCC;
+	}
+	if(suffix=='o') {
+		if(r_enum<DOT11_RATE_6) return -EINVAL;
+		return DOT11_MOD_OFDM;
+	}
+	if(suffix=='d') {
+		if(r_enum<DOT11_RATE_6) return -EINVAL;
+		return DOT11_MOD_CCKOFDM;
+	}
+	return -EINVAL;
+}
+
+#if UNUSED
+static int
+fill_ratevector(const char **pstr, u8 *vector, int size,
+		int (*supported)(int mbit, int mod, void *opaque), void *opaque, int or_mask)
+{
+	unsigned long rate_mbit;
+	int rate_enum,mod;
+	const char *str = *pstr;
+	char c;
+
+	do {
+		rate_mbit = simple_strtoul(str, (char**)&str, 10);
+		if(rate_mbit>INT_MAX) return -EINVAL;
+
+		rate_enum = rate_mbit2enum(rate_mbit);
+		if(rate_enum<0) return rate_enum;
+
+		c = *str;
+		mod = get_modulation(rate_enum, c);
+		if(mod<0) return mod;
+
+		if(c>='a' && c<='z') c = *++str;
+		if(c!=',' && c!=' ' && c!='\0') return -EINVAL;
+
+		if(supported) {
+			int r = supported(rate_mbit, mod, opaque);
+			if(r) return r;
+		}
+
+		*vector++ = dot11ratebyte[rate_enum] | or_mask;
+
+		size--;
+		str++;
+	} while(size>0 && c==',');
+
+	if(size<1) return -E2BIG;
+	*vector=0; /* TODO: sort, remove dups? */
+
+	*pstr = str-1;
+	return 0;
+}
+
+static /* TODO: remove 'static' when moved to wireless.c */
+int
+fill_ratevectors(const char *str, u8 *brate, u8 *orate, int size,
+		int (*supported)(int mbit, int mod, void *opaque), void *opaque)
+{
+	int r;
+
+	r = fill_ratevector(&str, brate, size, supported, opaque, 0x80);
+	if(r) return r;
+
+	orate[0] = 0;
+	if(*str==' ') {
+		str++;
+		r = fill_ratevector(&str, orate, size, supported, opaque, 0);
+		if(r) return r;
+		/* TODO: sanitize, e.g. remove/error on rates already in basic rate set? */
+	}
+	if(*str)
+		return -EINVAL;
+
+	return 0;
+}
+#endif
+
+/* TODO: use u64 masks? */
+
+static int
+fill_ratemask(const char **pstr, u32* mask,
+		int (*supported)(int mbit, int mod,void *opaque),
+		u32 (*gen_mask)(int mbit, int mod,void *opaque),
+		void *opaque)
+{
+	unsigned long rate_mbit;
+	int rate_enum,mod;
+	u32 m = 0;
+	const char *str = *pstr;
+	char c;
+
+	do {
+		rate_mbit = simple_strtoul(str, (char**)&str, 10);
+		if(rate_mbit>INT_MAX) return -EINVAL;
+
+		rate_enum = rate_mbit2enum(rate_mbit);
+		if(rate_enum<0) return rate_enum;
+
+		c = *str;
+		mod = get_modulation(rate_enum, c);
+		if(mod<0) return mod;
+
+		if(c>='a' && c<='z') c = *++str;
+		if(c!=',' && c!=' ' && c!='\0') return -EINVAL;
+
+		if(supported) {
+			int r = supported(rate_mbit, mod, opaque);
+			if(r) return r;
+		}
+
+		m |= gen_mask(rate_mbit, mod, opaque);
+		str++;
+	} while(c==',');
+
+	*pstr = str-1;
+	*mask |= m;
+	return 0;
+}
+
+static /* TODO: remove 'static' when moved to wireless.c */
+int
+fill_ratemasks(const char *str, u32 *bmask, u32 *omask,
+		int (*supported)(int mbit, int mod,void *opaque),
+		u32 (*gen_mask)(int mbit, int mod,void *opaque),
+		void *opaque)
+{
+	int r;
+
+	r = fill_ratemask(&str, bmask, supported, gen_mask, opaque);
+	if(r) return r;
+
+	if(*str==' ') {
+		str++;
+		r = fill_ratemask(&str, omask, supported, gen_mask, opaque);
+		if(r) return r;
+	}
+	if(*str)
+		return -EINVAL;
+	return 0;
+}
diff -urN oldtree/drivers/net/wireless/tiacx/usb.c newtree/drivers/net/wireless/tiacx/usb.c
--- oldtree/drivers/net/wireless/tiacx/usb.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/usb.c	2006-02-13 19:20:00.547429080 -0500
@@ -0,0 +1,1967 @@
+/***********************************************************************
+** Copyright (C) 2003  ACX100 Open Source Project
+**
+** The contents of this file are subject to the Mozilla Public
+** License Version 1.1 (the "License"); you may not use this file
+** except in compliance with the License. You may obtain a copy of
+** the License at http://www.mozilla.org/MPL/
+**
+** Software distributed under the License is distributed on an "AS
+** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+** implied. See the License for the specific language governing
+** rights and limitations under the License.
+**
+** Alternatively, the contents of this file may be used under the
+** terms of the GNU Public License version 2 (the "GPL"), in which
+** case the provisions of the GPL are applicable instead of the
+** above.  If you wish to allow the use of your version of this file
+** only under the terms of the GPL and not to allow others to use
+** your version of this file under the MPL, indicate your decision
+** by deleting the provisions above and replace them with the notice
+** and other provisions required by the GPL.  If you do not delete
+** the provisions above, a recipient may use your version of this
+** file under either the MPL or the GPL.
+** ---------------------------------------------------------------------
+** Inquiries regarding the ACX100 Open Source Project can be
+** made directly to:
+**
+** acx100-users@lists.sf.net
+** http://acx100.sf.net
+** ---------------------------------------------------------------------
+*/
+
+/***********************************************************************
+** USB support for TI ACX100 based devices. Many parts are taken from
+** the PCI driver.
+**
+** Authors:
+**  Martin Wawro <martin.wawro AT uni-dortmund.de>
+**  Andreas Mohr <andi AT lisas.de>
+**
+** Issues:
+**  - Note that this driver relies on a native little-endian byteformat
+**    at some points
+**
+** LOCKING
+** callback functions called by USB core are running in interrupt context
+** and thus have names with _i_.
+*/
+#define ACX_USB 1
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)
+#include <linux/moduleparam.h>
+#endif
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#if WIRELESS_EXT >= 13
+#include <net/iw_handler.h>
+#endif
+
+#include "acx.h"
+
+
+/***********************************************************************
+** try to make it compile for both 2.4.x and 2.6.x kernels
+*/
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
+
+/* number of endpoints of an interface */
+#define NUM_EP(intf) (intf)->altsetting[0].desc.bNumEndpoints
+#define EP(intf, nr) (intf)->altsetting[0].endpoint[(nr)].desc
+#define GET_DEV(udev) usb_get_dev((udev))
+#define PUT_DEV(udev) usb_put_dev((udev))
+#define SET_NETDEV_OWNER(ndev, owner) /* not needed anymore ??? */
+
+#define ASYNC_UNLINK	URB_ASYNC_UNLINK
+#define QUEUE_BULK	0
+#define ZERO_PACKET	URB_ZERO_PACKET
+
+static inline int
+submit_urb(struct urb *urb, int mem_flags)
+{
+	return usb_submit_urb(urb, mem_flags);
+}
+static inline struct urb*
+alloc_urb(int iso_pk, int mem_flags)
+{
+	return usb_alloc_urb(iso_pk, mem_flags);
+}
+
+#else
+
+/* 2.4.x kernels */
+#define USB_24	1
+
+#define NUM_EP(intf) (intf)->altsetting[0].bNumEndpoints
+#define EP(intf, nr) (intf)->altsetting[0].endpoint[(nr)]
+
+#define GET_DEV(udev) usb_inc_dev_use((udev))
+#define PUT_DEV(udev) usb_dec_dev_use((udev))
+
+#ifndef SET_NETDEV_DEV
+#define SET_NETDEV_DEV(x, y)
+#endif
+#define SET_NETDEV_OWNER(ndev, owner) ndev->owner = owner
+
+#define ASYNC_UNLINK	USB_ASYNC_UNLINK
+#define QUEUE_BULK	USB_QUEUE_BULK
+#define ZERO_PACKET	USB_ZERO_PACKET
+
+static inline int
+submit_urb(struct urb *urb, int mem_flags)
+{
+	return usb_submit_urb(urb);
+}
+static inline struct urb*
+alloc_urb(int iso_pk, int mem_flags)
+{
+	return usb_alloc_urb(iso_pk);
+}
+
+static inline void
+usb_set_intfdata(struct usb_interface *intf, void *data)
+{
+}
+
+#endif /* #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) */
+
+
+/***********************************************************************
+** Module Stuff
+*/
+#if ACX_DEBUG
+unsigned int acx_debug = L_ASSOC|L_INIT;
+#endif
+
+#if USE_FW_LOADER_LEGACY
+char *firmware_dir;
+#endif
+
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual MPL/GPL");
+#endif
+MODULE_AUTHOR("Martin Wawro <martin.wawro AT uni-dortmund.de>");
+MODULE_DESCRIPTION("TI ACX100 WLAN USB Driver");
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)
+
+#if ACX_DEBUG
+module_param(acx_debug, uint, 0);
+#endif
+#if USE_FW_LOADER_LEGACY
+module_param(firmware_dir, charp, 0);
+#endif
+
+#else
+
+#if ACX_DEBUG
+MODULE_PARM(debug, "i");
+#endif
+#if USE_FW_LOADER_LEGACY
+MODULE_PARM(firmware_dir, "s");
+#endif
+
+#endif
+
+MODULE_PARM_DESC(debug, "Debug level mask: 0x0000 - 0x3fff");
+#if USE_FW_LOADER_LEGACY
+MODULE_PARM_DESC(firmware_dir, "Directory to load acx100 firmware files from");
+#endif
+
+/***********************************************************************
+*/
+#define ACX100_VENDOR_ID 0x2001
+#define ACX100_PRODUCT_ID_UNBOOTED 0x3B01
+#define ACX100_PRODUCT_ID_BOOTED 0x3B00
+
+/* RX-Timeout: NONE (request waits forever) */
+#define ACX100_USB_RX_TIMEOUT (0)
+
+#define ACX100_USB_TX_TIMEOUT (4*HZ)
+
+#define USB_CTRL_HARD_TIMEOUT 5500   /* steps in ms */
+
+
+/***********************************************************************
+** Prototypes
+*/
+#if USB_24
+static void* acx100usb_e_probe(struct usb_device *, unsigned int, const struct usb_device_id *);
+static void acx100usb_e_disconnect(struct usb_device *, void *);
+static void acx100usb_i_complete_tx(struct urb *);
+static void acx100usb_i_complete_rx(struct urb *);
+#else
+static int acx100usb_e_probe(struct usb_interface *, const struct usb_device_id *);
+static void acx100usb_e_disconnect(struct usb_interface *);
+static void acx100usb_i_complete_tx(struct urb *, struct pt_regs *);
+static void acx100usb_i_complete_rx(struct urb *, struct pt_regs *);
+#endif
+static int acx100usb_e_open(struct net_device *);
+static int acx100usb_e_close(struct net_device *);
+static void acx100usb_i_set_rx_mode(struct net_device *);
+static int acx100usb_e_init_network_device(struct net_device *);
+static int acx100usb_boot(struct usb_device *);
+
+static struct net_device_stats * acx_e_get_stats(struct net_device *);
+static struct iw_statistics *acx_e_get_wireless_stats(struct net_device *);
+
+static void acx100usb_l_poll_rx(wlandevice_t *, int number);
+
+static void acx100usb_i_tx_timeout(struct net_device *);
+
+/* static void dump_device(struct usb_device *); */
+/* static void dump_device_descriptor(struct usb_device_descriptor *); */
+/* static void dump_config_descriptor(struct usb_config_descriptor *); */
+#if USB_24
+static void dump_endpoint_descriptor(struct usb_endpoint_descriptor *);
+static void dump_interface_descriptor(struct usb_interface_descriptor *);
+#endif
+
+
+/***********************************************************************
+** Module Data
+*/
+#define TXBUFSIZE sizeof(usb_txbuffer_t)
+//// Bogus! We CANNOT pretend that rxbuffer_t is larger than it is.
+/* make it a multiply of 64 */
+/* #define RXBUFSIZE ((sizeof(rxbuffer_t)+63) & ~63) */
+#define RXBUFSIZE sizeof(rxbuffer_t)
+
+static const struct usb_device_id
+acx100usb_ids[] = {
+	{ USB_DEVICE(ACX100_VENDOR_ID, ACX100_PRODUCT_ID_BOOTED) },
+	{ USB_DEVICE(ACX100_VENDOR_ID, ACX100_PRODUCT_ID_UNBOOTED) },
+	{}
+};
+
+
+/* USB driver data structure as required by the kernel's USB core */
+static struct usb_driver
+acx100usb_driver = {
+	.name = "acx_usb",
+	.probe = acx100usb_e_probe,
+	.disconnect = acx100usb_e_disconnect,
+	.id_table = acx100usb_ids
+};
+
+
+/***********************************************************************
+** USB helper
+*/
+static void
+acx_unlink_and_free_urb(struct urb* urb) {
+	if (!urb)
+		return;
+	if (urb->status == -EINPROGRESS) {
+		usb_unlink_urb(urb);
+//TODO: timeout!
+		while (urb->status == -EINPROGRESS) {
+			mdelay(2);
+		}
+	}
+	usb_free_urb(urb);
+}
+
+
+/***********************************************************************
+*/
+#if ACX_DEBUG
+#if USB_24
+static char*
+acx100usb_pstatus(int val)
+{
+#define CASE(status)	case status: return ""#status""
+	switch (val) {
+		CASE(USB_ST_NOERROR);
+		CASE(USB_ST_CRC);
+		CASE(USB_ST_BITSTUFF);
+		CASE(USB_ST_DATAOVERRUN);
+		CASE(USB_ST_BUFFEROVERRUN);
+		CASE(USB_ST_BUFFERUNDERRUN);
+		CASE(USB_ST_SHORT_PACKET);
+		CASE(USB_ST_URB_KILLED);
+		CASE(USB_ST_URB_PENDING);
+		CASE(USB_ST_REMOVED);
+		CASE(USB_ST_TIMEOUT);
+		CASE(USB_ST_NOTSUPPORTED);
+		CASE(USB_ST_BANDWIDTH_ERROR);
+		CASE(USB_ST_URB_INVALID_ERROR);
+		CASE(USB_ST_URB_REQUEST_ERROR);
+		CASE(USB_ST_STALL);
+	default:
+		return "UNKNOWN";
+	}
+}
+#else
+static char*
+acx100usb_pstatus(int val)
+{
+	static char status[20];
+
+	if (val < 0)
+		sprintf(status, "errno %d", -val);
+	else
+		sprintf(status, "length %d", val);
+
+	return status;
+}
+#endif /* USB_24 */
+#endif /* ACX_DEBUG */
+
+
+/***********************************************************************
+** EEPROM and PHY read/write helpers
+*/
+/***********************************************************************
+** acx_s_read_phy_reg
+*/
+int
+acx_s_read_phy_reg(wlandevice_t *priv, u32 reg, u8 *charbuf)
+{
+	int result = NOT_OK;
+	mem_read_write_t mem;
+
+	FN_ENTER;
+
+	mem.addr = cpu_to_le16(reg);
+	mem.type = cpu_to_le16(0x82);
+	mem.len = cpu_to_le32(4);
+	acx_s_issue_cmd(priv, ACX1xx_CMD_MEM_READ, &mem, 8);
+	*charbuf = mem.data;
+	acxlog(L_DEBUG, "radio PHY at 0x%04X = 0x%02X\n", *charbuf, reg);
+	result = OK;
+	goto fail; /* silence compiler warning */
+fail:
+	FN_EXIT1(result);
+	return result;
+}
+
+
+/***********************************************************************
+*/
+int
+acx_s_write_phy_reg(wlandevice_t *priv, u32 reg, u8 value)
+{
+	mem_read_write_t mem;
+
+	FN_ENTER;
+
+	mem.addr = cpu_to_le16(reg);
+	mem.type = cpu_to_le16(0x82);
+	mem.len = cpu_to_le32(4);
+	mem.data = value;
+	acx_s_issue_cmd(priv, ACX1xx_CMD_MEM_WRITE, &mem, sizeof(mem));
+	acxlog(L_DEBUG, "radio PHY write 0x%02X at 0x%04X\n", value, reg);
+
+	FN_EXIT1(OK);
+	return OK;
+}
+
+
+/***********************************************************************
+** acx_s_issue_cmd_timeo
+** Excecutes a command in the command mailbox
+**
+** Arguments:
+**   *pcmdparam = an pointer to the data. The data mustn't include
+**                the 4 byte command header!
+*/
+#undef FUNC
+#define FUNC "issue_cmd"
+
+#if !ACX_DEBUG
+int
+acx_s_issue_cmd_timeo(
+	wlandevice_t *priv,
+	unsigned cmd,
+	void *pdr,
+	unsigned paramlen,
+	unsigned timeout)
+{
+#else
+int
+acx_s_issue_cmd_timeo_debug(
+	wlandevice_t *priv,
+	unsigned cmd,
+	void *pdr,
+	unsigned paramlen,
+	unsigned timeout,
+	const char* cmdstr)
+{
+#endif
+	/* USB ignores timeout param */
+
+	struct usb_device *usbdev;
+	struct {
+		u16	cmd ACX_PACKED;
+		u16	status ACX_PACKED;
+		u8	data[3000]; //former boguspad. try [ACX100_USB_RWMEM_MAXLEN-4] later!
+	} *loc;
+	int acklen, blocklen, inpipe, outpipe;
+	int result;
+
+	FN_ENTER;
+
+	acxlog(L_CTL, FUNC"(cmd:%s,paramlen:%u,type:%d)\n",
+			cmdstr, paramlen,
+			pdr ? le16_to_cpu(((acx_ie_generic_t *)pdr)->type) : -1);
+
+	loc = kmalloc(sizeof(*loc), GFP_KERNEL);
+	if (!loc) {
+		printk("acx: "FUNC"() failed to alloc data buffer\n");
+		goto fail;
+	}
+
+	/* get context from wlandevice */
+	usbdev = priv->usbdev;
+
+	/* check which kind of command was issued */
+	loc->cmd = cpu_to_le16(cmd);
+	loc->status = 0;
+
+/* NB: paramlen == ((acx_ie_generic_t*)pdr)->len + 4 on entry
+**
+** Interrogate: write 8-byte (cmd,status,rid,frmlen),
+**	read data[frmlen+8] back
+** Configure: write (cmd,status,rid,frmlen,data[frmlen])
+**
+** Possibly bogus special handling of ACX1xx_IE_SCAN_STATUS removed
+*/
+
+	/* now write the parameters of the command if needed */
+	acklen = sizeof(loc->data);
+	blocklen = paramlen;
+	if (pdr && paramlen) {
+		/* if it's an INTERROGATE command, just pass the length
+		 * of parameters to read, as data */
+		if (cmd == ACX1xx_CMD_INTERROGATE) {
+			blocklen = 4;
+			acklen = paramlen + 4;
+		}
+		memcpy(loc->data, pdr, blocklen);
+	}
+	blocklen += 4; /* account for cmd,status */
+
+	/* obtain the I/O pipes */
+	outpipe = usb_sndctrlpipe(usbdev, 0);
+	inpipe = usb_rcvctrlpipe(usbdev, 0);
+	acxlog(L_CTL, "ctrl inpipe=0x%X outpipe=0x%X\n", inpipe, outpipe);
+	acxlog(L_CTL, "sending USB control msg (out) (blocklen=%d)\n", blocklen);
+	if (acx_debug & L_DATA)
+		acx_dump_bytes(loc, blocklen);
+
+	result = usb_control_msg(usbdev, outpipe,
+		ACX_USB_REQ_CMD, /* request */
+		USB_TYPE_VENDOR|USB_DIR_OUT, /* requesttype */
+		0, /* value */
+		0, /* index */
+		loc, /* dataptr */
+		blocklen, /* size */
+		USB_CTRL_HARD_TIMEOUT /* timeout in ms */
+	);
+	acxlog(L_CTL, "wrote %d bytes\n", result);
+	if (result < 0) {
+		goto fail;
+	}
+
+	/* check for device acknowledge */
+	acxlog(L_CTL, "sending USB control msg (in) (acklen=%d) "
+		"sizeof(loc->data)=%d\n", acklen, (int) sizeof(loc->data));
+	loc->status = 0; /* delete old status flag -> set to fail */
+//shall we zero out the rest?
+	result = usb_control_msg(usbdev, inpipe,
+		ACX_USB_REQ_CMD, /* request */
+		USB_TYPE_VENDOR|USB_DIR_IN, /* requesttype */
+		0, /* value */
+		0, /* index */
+		loc, /* dataptr */
+		acklen, /* size */
+		USB_CTRL_HARD_TIMEOUT /* timeout in ms */
+	);
+	acxlog(L_CTL, "read %d bytes\n", result);
+	if (result < 0) {
+		goto fail;
+	}
+
+//check for result==paramlen+4? Was seen:
+//interrogate(type:ACX100_IE_DOT11_ED_THRESHOLD,len:4)
+//issue_cmd(cmd:ACX1xx_CMD_INTERROGATE,paramlen:8,type:4111)
+//ctrl inpipe=0x80000280 outpipe=0x80000200
+//sending USB control msg (out) (blocklen=8)
+//01 00 00 00 0F 10 04 00
+//wrote 8 bytes
+//sending USB control msg (in) (acklen=12) sizeof(loc->data
+//read 4 bytes <==== MUST BE 12!!
+
+	if (le16_to_cpu(loc->status) != 1) {
+		printk("%s: warning: command returned status %u\n",
+			priv->netdev->name, le16_to_cpu(loc->status));
+	}
+	if ((cmd == ACX1xx_CMD_INTERROGATE) && pdr && paramlen) {
+		memcpy(pdr, loc->data, paramlen);
+		acxlog(L_CTL, "response frame: cmd=%u status=%u\n",
+			le16_to_cpu(loc->cmd),
+			le16_to_cpu(loc->status));
+		if (acx_debug & L_DATA) {
+			printk("incoming bytes (%d):\n", paramlen);
+			acx_dump_bytes(pdr, paramlen);
+		}
+	}
+	kfree(loc);
+	FN_EXIT1(OK);
+	return OK;
+fail:
+	kfree(loc);
+	FN_EXIT1(NOT_OK);
+	return NOT_OK;
+}
+
+
+/***********************************************************************
+** acx100usb_e_probe()
+**
+** Inputs:
+**    dev -> Pointer to usb_device structure that may or may not be claimed
+**  ifNum -> Interface number
+**  devID -> Device ID (vendor and product specific stuff)
+************************************************************************
+** Returns:
+**  (void *) Pointer to (custom) driver context or NULL if we are not interested
+**           or unable to handle the offered device.
+**
+** Description:
+**  This function is invoked by the kernel's USB core whenever a new device is
+**  attached to the system or the module is loaded. It is presented a usb_device
+**  structure from which information regarding the device is obtained and evaluated.
+**  In case this driver is able to handle one of the offered devices, it returns
+**  a non-null pointer to a driver context and thereby claims the device.
+*/
+#if USB_24
+#define OUTOFMEM	NULL
+static void*
+acx100usb_e_probe(struct usb_device *usbdev, unsigned int ifNum,
+				const struct usb_device_id *devID)
+{
+	void *res = NULL;
+#else
+#define OUTOFMEM	-ENOMEM
+static int
+acx100usb_e_probe(struct usb_interface *intf, const struct usb_device_id *devID)
+{
+	struct usb_device *usbdev = interface_to_usbdev(intf);
+	int res = 0;
+#endif
+	wlandevice_t *priv = NULL;
+	struct net_device *dev = NULL;
+	struct usb_config_descriptor *config;
+	struct usb_endpoint_descriptor *epdesc;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
+	struct usb_host_endpoint *ep;
+#endif
+	struct usb_interface_descriptor *ifdesc;
+	const char* msg;
+	int i, numep;
+	int numconfigs, numfaces, result = 0;
+
+	FN_ENTER;
+
+	/* First check if this is the "unbooted" hardware */
+	if ((usbdev->descriptor.idVendor == ACX100_VENDOR_ID)
+	 && (usbdev->descriptor.idProduct == ACX100_PRODUCT_ID_UNBOOTED)) {
+		/* Boot the device (i.e. upload the firmware) */
+		acx100usb_boot(usbdev);
+
+		/* OK, we are done with booting. Normally, the
+		** ID for the unbooted device should disappear
+		** and it will not need a driver anyway...so
+		** return a NULL
+		*/
+		acxlog(L_INIT, "finished booting, returning from probe()\n");
+#if USB_24
+		res = NULL;
+#else
+		res = 0; /* is that ok?? */
+#endif
+		goto end;
+	}
+
+	if ((usbdev->descriptor.idVendor != ACX100_VENDOR_ID)
+	 || (usbdev->descriptor.idProduct != ACX100_PRODUCT_ID_BOOTED)) {
+		goto end_nodev;
+	}
+
+	/* Ok, so it's our device and it is already booted */
+
+	/* allocate memory for the device driver context */
+	priv = kmalloc(sizeof(struct wlandevice), GFP_KERNEL);
+	if (!priv) {
+		result = sizeof(struct wlandevice);
+		msg = "acx: could not allocate %d bytes "
+			"for device driver context\n";
+		goto end_nomem;
+	}
+	memset(priv, 0, sizeof(wlandevice_t));
+	priv->chip_type = CHIPTYPE_ACX100;
+	/* FIXME: should be read from register (via firmware) using standard ACX code */
+	priv->radio_type = RADIO_MAXIM_0D;
+	priv->usbdev = usbdev;
+
+	spin_lock_init(&priv->lock);    /* initial state: unlocked */
+	sema_init(&priv->sem, 1);       /* initial state: 1 (upped) */
+
+	/* Initialize the device context and also check
+	** if this is really the hardware we know about.
+	** If not sure, at least notify the user that he
+	** may be in trouble...
+	*/
+	numconfigs = (int)usbdev->descriptor.bNumConfigurations;
+	if (numconfigs != 1)
+		printk("acx: number of configurations is %d, "
+			"this driver only knows how to handle 1, "
+			"be prepared for surprises\n", numconfigs);
+#if USB_24
+	config = usbdev->actconfig;
+#else
+	config = &usbdev->config->desc;
+#endif
+	numfaces = config->bNumInterfaces;
+	if (numfaces != 1)
+		printk("acx: number of interfaces is %d, "
+			"this driver only knows how to handle 1, "
+			"be prepared for surprises\n", numfaces);
+#if USB_24
+	ifdesc = config->interface->altsetting;
+#else
+	ifdesc = &intf->altsetting->desc;
+#endif
+	numep = ifdesc->bNumEndpoints;
+	acxlog(L_DEBUG, "# of endpoints: %d\n", numep);
+
+	/* obtain information about the endpoint
+	** addresses, begin with some default values
+	*/
+	priv->bulkoutep = 1;
+	priv->bulkinep = 1;
+	for (i = 0; i < numep; i++) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
+		ep = usbdev->ep_in[i];
+		if (!ep)
+			continue;
+		epdesc = &ep->desc;
+#else
+		epdesc = usb_epnum_to_ep_desc(usbdev, i);
+		if (!epdesc)
+			continue;
+#endif
+		if (epdesc->bmAttributes & USB_ENDPOINT_XFER_BULK) {
+			if (epdesc->bEndpointAddress & 0x80)
+				priv->bulkinep = epdesc->bEndpointAddress & 0xF;
+			else
+				priv->bulkoutep = epdesc->bEndpointAddress & 0xF;
+		}
+	}
+	acxlog(L_DEBUG, "bulkout ep: 0x%X\n", priv->bulkoutep);
+	acxlog(L_DEBUG, "bulkin ep: 0x%X\n", priv->bulkinep);
+
+	/* Set the packet-size equivalent to the buffer size */
+	/* already done by memset: priv->rxtruncsize = 0; */
+	acxlog(L_DEBUG, "TXBUFSIZE=%d RXBUFSIZE=%d\n",
+				(int) TXBUFSIZE, (int) RXBUFSIZE);
+
+	priv->usb_free_tx = ACX100_USB_NUM_BULK_URBS;
+	/* already done by memset:
+	for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) {
+		priv->usb_tx[i].busy = 0;
+	}
+	*/
+
+	/* Allocate memory for a network device */
+	dev = kmalloc(sizeof(struct net_device), GFP_ATOMIC);
+	if (!dev) {
+		msg = "acx: no memory for netdev\n";
+		goto end_nomem;
+	}
+
+	/* Setup network device */
+	memset(dev, 0, sizeof(struct net_device));
+	dev->init = (void *)&acx100usb_e_init_network_device;
+	dev->priv = priv;
+	priv->netdev = dev;
+
+	/* Setup URBs for bulk-in/out messages */
+	for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) {
+		priv->bulkrx_urbs[i] = alloc_urb(0, GFP_KERNEL);
+		if (!priv->bulkrx_urbs[i]) {
+			msg = "acx: no memory for input URB\n";
+			goto end_nomem;
+		}
+		priv->bulkrx_urbs[i]->status = 0;
+
+		priv->usb_tx[i].urb = alloc_urb(0, GFP_KERNEL);
+		if (!priv->usb_tx[i].urb) {
+			msg = "acx: no memory for output URB\n";
+			goto end_nomem;
+		}
+		priv->usb_tx[i].urb->status = 0;
+
+		priv->usb_tx[i].priv = priv;
+	}
+
+	/* Allocate a network device name */
+	acxlog(L_INIT, "allocating device name\n");
+	result = dev_alloc_name(dev, "wlan%d");
+	if (result < 0) {
+		msg = "acx: no memory for wlan device name\n";
+		goto end_nomem;
+	}
+#if USB_26
+	usb_set_intfdata(intf, priv);
+	SET_NETDEV_DEV(dev, &intf->dev);
+#endif
+	/* Register the network device */
+	acxlog(L_INIT, "registering network device\n");
+	result = register_netdev(dev);
+	if (result != 0) {
+		msg = "acx: failed to register network device "
+			"for USB WLAN (errcode=%d)\n";
+		goto end_nomem;
+	}
+#ifdef CONFIG_PROC_FS
+	if (OK != acx_proc_register_entries(dev)) {
+		printk("acx: /proc registration failed\n");
+	}
+#endif
+
+	/* Everything went OK, we are happy now	*/
+#if USB_24
+	res = priv;
+#else
+	res = 0;
+#endif
+	goto end;
+
+end_nomem:
+	printk(msg, result);
+
+	/* we rely on the fact that free(NULL) is harmless */
+	kfree(dev);
+	if (priv) {
+		for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) {
+			usb_free_urb(priv->bulkrx_urbs[i]);
+			usb_free_urb(priv->usb_tx[i].urb);
+		}
+		kfree(priv);
+	}
+	res = OUTOFMEM;
+	goto end;
+
+end_nodev:
+
+	/* no device we could handle, return NULL */
+#if USB_24
+	res = NULL;
+#else
+	res = -EIO;
+#endif
+end:
+	FN_EXIT1((int)res);
+	return res;
+}
+
+
+/***********************************************************************
+** acx100usb_e_disconnect():
+** Inputs:
+**         dev -> Pointer to usb_device structure handled by this module
+**  devContext -> Pointer to own device context (acx100usb_context)
+************************************************************************
+** Description:
+**  This function is invoked whenever the user pulls the plug from the USB
+**  device or the module is removed from the kernel. In these cases, the
+**  network devices have to be taken down and all allocated memory has
+**  to be freed.
+*/
+#if USB_24
+static void
+acx100usb_e_disconnect(struct usb_device *usbdev, void *devContext)
+{
+	wlandevice_t *priv = (wlandevice_t *)devContext;
+#else
+static void
+acx100usb_e_disconnect(struct usb_interface *intf)
+{
+	wlandevice_t *priv = usb_get_intfdata(intf);
+#endif
+	int i, result;
+
+	/* No WLAN device...no sense */
+	if (!priv)
+		return;
+
+	acx_sem_lock(priv);
+
+	/* stop the transmit queue */
+	if (priv->netdev) {
+		rtnl_lock();
+		if (!acx_queue_stopped(priv->netdev)) {
+			acx_stop_queue(priv->netdev, "on USB disconnect");
+		}
+		rtnl_unlock();
+#ifdef CONFIG_PROC_FS
+		acx_proc_unregister_entries(priv->netdev);
+#endif
+	}
+
+	/* now abort pending URBs and free them */
+	for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) {
+		acx_unlink_and_free_urb(priv->bulkrx_urbs[i]);
+		acx_unlink_and_free_urb(priv->usb_tx[i].urb);
+	}
+
+	/* Unregister the network devices */
+	if (priv->netdev) {
+		rtnl_lock();
+		result = unregister_netdevice(priv->netdev);
+		rtnl_unlock();
+		kfree(priv->netdev);
+	}
+
+	acx_sem_unlock(priv);
+
+	/* finally free the WLAN device */
+	kfree(priv);
+}
+
+
+/***********************************************************************
+** acx100usb_boot():
+** Inputs:
+**    usbdev -> Pointer to kernel's usb_device structure
+**  endpoint -> Address of the endpoint for control transfers
+************************************************************************
+** Returns:
+**  (int) Errorcode or 0 on success
+**
+** Description:
+**  This function triggers the loading of the firmware image from harddisk
+**  and then uploads the firmware to the USB device. After uploading the
+**  firmware and transmitting the checksum, the device resets and appears
+**  as a new device on the USB bus (the device we can finally deal with)
+*/
+static int
+acx100usb_boot(struct usb_device *usbdev)
+{
+	static const char filename[] = "tiacx100usb";
+
+	char *firmware = NULL;
+	char *usbbuf;
+	unsigned int offset;
+	unsigned int len, inpipe, outpipe;
+	u32 checksum;
+	u32 size;
+	int result;
+
+	usbbuf = kmalloc(ACX100_USB_RWMEM_MAXLEN, GFP_KERNEL);
+	if (!usbbuf) {
+		printk(KERN_ERR "acx: no memory for USB transfer buffer ("
+			STRING(ACX100_USB_RWMEM_MAXLEN)" bytes)\n");
+		result = -ENOMEM;
+		goto end;
+	}
+	firmware = (char *)acx_s_read_fw(&usbdev->dev, filename, &size);
+	if (!firmware) {
+		result = -EIO;
+		goto end;
+	}
+	acxlog(L_INIT, "firmware size: %d bytes\n", size);
+
+	/* Obtain the I/O pipes */
+	outpipe = usb_sndctrlpipe(usbdev, 0);
+	inpipe = usb_rcvctrlpipe(usbdev, 0);
+
+	/* now upload the firmware, slice the data into blocks */
+	offset = 8;
+	while (offset < size) {
+		len = size - offset;
+		if (len >= ACX100_USB_RWMEM_MAXLEN) {
+			len = ACX100_USB_RWMEM_MAXLEN;
+		}
+		acxlog(L_INIT, "uploading firmware (%d bytes, offset=%d)\n",
+						len, offset);
+		result = 0;
+		memcpy(usbbuf, firmware + offset, len);
+		result = usb_control_msg(usbdev, outpipe,
+			ACX_USB_REQ_UPLOAD_FW,
+			USB_TYPE_VENDOR|USB_DIR_OUT,
+			size - 8, /* value */
+			0, /* index */
+			usbbuf, /* dataptr */
+			len, /* size */
+			3000 /* timeout in ms */
+		);
+		offset += len;
+		if (result < 0) {
+#if ACX_DEBUG
+			printk(KERN_ERR "acx: error %d (%s) during upload "
+				"of firmware, aborting\n", result,
+				acx100usb_pstatus(result));
+#else
+			printk(KERN_ERR "acx: error %d during upload "
+				"of firmware, aborting\n", result);
+#endif
+			goto end;
+		}
+	}
+
+	/* finally, send the checksum and reboot the device */
+	checksum = le32_to_cpu(*(u32 *)firmware);
+	/* is this triggers the reboot? */
+	result = usb_control_msg(usbdev, outpipe,
+		ACX_USB_REQ_UPLOAD_FW,
+		USB_TYPE_VENDOR|USB_DIR_OUT,
+		checksum & 0xffff, /* value */
+		checksum >> 16, /* index */
+		NULL, /* dataptr */
+		0, /* size */
+		3000 /* timeout in ms */
+	);
+	if (result < 0) {
+		printk(KERN_ERR "acx: error %d during tx of checksum, "
+				"aborting\n", result);
+		goto end;
+	}
+	result = usb_control_msg(usbdev, inpipe,
+		ACX_USB_REQ_ACK_CS,
+		USB_TYPE_VENDOR|USB_DIR_IN,
+		checksum & 0xffff, /* value */
+		checksum >> 16, /* index */
+		usbbuf, /* dataptr */
+		8, /* size */
+		3000 /* timeout in ms */
+	);
+	if (result < 0) {
+		printk(KERN_ERR "acx: error %d during ACK of checksum, "
+				"aborting\n", result);
+		goto end;
+	}
+	if (*usbbuf != 0x10) {
+		kfree(usbbuf);
+		printk(KERN_ERR "acx: invalid checksum?\n");
+		result = -EINVAL;
+		goto end;
+	}
+	result = 0;
+end:
+	vfree(firmware);
+	kfree(usbbuf);
+	return result;
+}
+
+
+/***********************************************************************
+** acx100usb_e_init_network_device():
+** Inputs:
+**    dev -> Pointer to network device
+************************************************************************
+** Description:
+**  Basic setup of a network device for use with the WLAN device.
+*/
+static int
+acx100usb_e_init_network_device(struct net_device *dev) {
+	int result = 0;
+	wlandevice_t *priv;
+
+	/* Setup the device and stop the queue */
+	ether_setup(dev);
+	acx_stop_queue(dev, "on init");
+
+	priv = dev->priv;
+
+	acx_sem_lock(priv);
+
+	/* put the ACX100 out of sleep mode */
+	acx_s_issue_cmd(priv, ACX1xx_CMD_WAKE, NULL, 0);
+
+	/* Register the callbacks for the network device functions */
+	dev->open = &acx100usb_e_open;
+	dev->stop = &acx100usb_e_close;
+	dev->hard_start_xmit = (void *)&acx_i_start_xmit;
+	dev->get_stats = (void *)&acx_e_get_stats;
+	dev->get_wireless_stats = (void *)&acx_e_get_wireless_stats;
+#if WIRELESS_EXT >= 13
+	dev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def;
+#else
+	dev->do_ioctl = (void *)&acx_e_ioctl_old;
+#endif
+	dev->set_multicast_list = (void *)&acx100usb_i_set_rx_mode;
+#ifdef HAVE_TX_TIMEOUT
+	dev->tx_timeout = &acx100usb_i_tx_timeout;
+	dev->watchdog_timeo = 4 * HZ;
+#endif
+	result = acx_s_init_mac(dev);
+	if (OK != result)
+		goto end;
+	result = acx_s_set_defaults(priv);
+	if (OK != result) {
+		printk("%s: acx_set_defaults FAILED\n", dev->name);
+		goto end;
+	}
+
+	SET_MODULE_OWNER(dev);
+end:
+	acx_sem_unlock(priv);
+
+	return result;
+}
+
+
+/***********************************************************************
+** acx100usb_e_open
+** This function is called when the user sets up the network interface.
+** It initializes a management timer, sets up the USB card and starts
+** the network tx queue and USB receive.
+*/
+static int
+acx100usb_e_open(struct net_device *dev)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	unsigned long flags;
+	int i;
+
+	FN_ENTER;
+
+	acx_sem_lock(priv);
+
+	/* put the ACX100 out of sleep mode */
+	acx_s_issue_cmd(priv, ACX1xx_CMD_WAKE, NULL, 0);
+
+	acx_init_task_scheduler(priv);
+
+	init_timer(&priv->mgmt_timer);
+	priv->mgmt_timer.function = acx_i_timer;
+	priv->mgmt_timer.data = (unsigned long)priv;
+
+	/* set ifup to 1, since acx_start needs it */
+	SET_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP);
+	acx_s_start(priv);
+
+	acx_start_queue(dev, "on open");
+
+	acx_lock(priv, flags);
+	for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) {
+		acx100usb_l_poll_rx(priv, i);
+	}
+	acx_unlock(priv, flags);
+
+	WLAN_MOD_INC_USE_COUNT;
+
+	acx_sem_unlock(priv);
+
+	FN_EXIT0;
+	return 0;
+}
+
+
+/***********************************************************************
+** acx100usb_l_poll_rx
+** This function initiates a bulk-in USB transfer (in case the interface
+** is up).
+*/
+static void
+acx100usb_l_poll_rx(wlandevice_t *priv, int number)
+{
+	struct usb_device *usbdev;
+	rxbuffer_t *inbuf;
+	acx_usb_bulk_context_t *rxcon;
+	struct urb *rxurb;
+	int errcode;
+	unsigned int inpipe;
+
+	FN_ENTER;
+
+	if (!(priv->dev_state_mask & ACX_STATE_IFACE_UP)) {
+		goto end;
+	}
+
+	rxcon = &priv->rxcons[number];
+	inbuf = &priv->bulkins[number];
+	rxurb = priv->bulkrx_urbs[number];
+	usbdev = priv->usbdev;
+
+	rxcon->device = priv;
+	rxcon->number = number;
+	inpipe = usb_rcvbulkpipe(usbdev, priv->bulkinep);
+	if (rxurb->status == -EINPROGRESS) {
+		printk(KERN_ERR "acx: error, rx triggered while rx urb in progress\n");
+		/* FIXME: this is nasty, receive is being cancelled by this code
+		 * on the other hand, this should not happen anyway...
+		 */
+		usb_unlink_urb(rxurb);
+	}
+	rxurb->actual_length = 0;
+	usb_fill_bulk_urb(rxurb, usbdev, inpipe,
+		inbuf, /* dataptr */
+		RXBUFSIZE, /* size */
+		acx100usb_i_complete_rx, /* handler */
+		rxcon /* handler param */
+	);
+	rxurb->transfer_flags = ASYNC_UNLINK|QUEUE_BULK;
+#if USB_24
+	rxurb->timeout = 0;
+	rxurb->status = 0;
+#endif
+	/* ATOMIC: we may be called from complete_rx() usb callback */
+	errcode = submit_urb(rxurb, GFP_ATOMIC);
+	/* FIXME: evaluate the error code ! */
+	acxlog(L_USBRXTX, "SUBMIT RX (%d) inpipe=0x%X size=%d errcode=%d\n",
+			number, inpipe, (int) RXBUFSIZE, errcode);
+
+end:
+	FN_EXIT0;
+}
+
+
+/***********************************************************************
+** acx100usb_i_complete_rx():
+** Inputs:
+**     urb -> Pointer to USB request block
+**    regs -> Pointer to register-buffer for syscalls (see asm/ptrace.h)
+************************************************************************
+** Description:
+**  This function is invoked by USB subsystem whenever a bulk receive
+**  request returns.
+**  The received data is then committed to the network stack and the next
+**  USB receive is triggered.
+*/
+#if USB_24
+static void
+acx100usb_i_complete_rx(struct urb *urb)
+#else
+static void
+acx100usb_i_complete_rx(struct urb *urb, struct pt_regs *regs)
+#endif
+{
+	wlandevice_t *priv;
+	rxbuffer_t *ptr;
+	rxbuffer_t *inbuf;
+	unsigned long flags;
+	int size, number, remsize, packetsize;
+
+	FN_ENTER;
+
+	if (!urb->context) {
+		printk(KERN_ERR "acx: error, urb context was NULL\n");
+		goto end; /* at least try to prevent the worst */
+	}
+
+	priv = ((acx_usb_bulk_context_t *)urb->context)->device;
+	number = ((acx_usb_bulk_context_t *)urb->context)->number;
+	size = urb->actual_length;
+	remsize = size;
+
+	acxlog(L_USBRXTX, "RETURN RX (%d) status=%d size=%d\n",
+				number, urb->status, size);
+
+	acx_lock(priv, flags);
+
+	inbuf = &priv->bulkins[number];
+	ptr = inbuf;
+
+	/* check if the transfer was aborted */
+	switch (urb->status) {
+	case 0: /* No error */
+		break;
+	case -EOVERFLOW:
+		printk(KERN_ERR "acx: error in rx, data overrun -> emergency stop\n");
+		/* LOCKING BUG! acx100usb_e_close(priv->netdev); */
+		goto end_unlock;
+	case -ECONNRESET:
+		goto do_poll_rx;
+	default:
+		priv->stats.rx_errors++;
+		printk("acx: rx error (urb status=%d)\n", urb->status);
+		goto do_poll_rx;
+	}
+
+	if (!size)
+		printk("acx: warning, encountered zerolength rx packet\n");
+
+	if (urb->transfer_buffer != inbuf)
+		goto do_poll_rx;
+
+	/* check if previous frame was truncated
+	** FIXME: this code can only handle truncation
+	** of consecutive packets!
+	*/
+	if (priv->rxtruncsize) {
+		int tail_size;
+
+		ptr = &priv->rxtruncbuf;
+		packetsize = RXBUF_BYTES_RCVD(ptr) + RXBUF_HDRSIZE;
+		if (acx_debug & L_USBRXTX) {
+			printk("handling truncated frame (truncsize=%d size=%d "
+					"packetsize(from trunc)=%d)\n",
+					priv->rxtruncsize, size, packetsize);
+			acx_dump_bytes(ptr, RXBUF_HDRSIZE);
+			acx_dump_bytes(inbuf, RXBUF_HDRSIZE);
+		}
+
+		/* bytes needed for rxtruncbuf completion: */
+		tail_size = packetsize - priv->rxtruncsize;
+
+		if (size < tail_size) {
+			/* there is not enough data to complete this packet,
+			** simply append the stuff to the truncation buffer
+			*/
+			memcpy(((char *)ptr) + priv->rxtruncsize, inbuf, size);
+			priv->rxtruncsize += size;
+			remsize = 0;
+		} else {
+			/* ok, this data completes the previously
+			** truncated packet. copy it into a descriptor
+			** and give it to the rest of the stack	*/
+
+			/* append tail to previously truncated part
+			** NB: priv->rxtruncbuf (pointed to by ptr) can't
+			** overflow because this is already checked before
+			** truncation buffer was filled. See below,
+			** "if (packetsize > sizeof(rxbuffer_t))..." code */
+			memcpy(((char *)ptr) + priv->rxtruncsize, inbuf, tail_size);
+
+			if (acx_debug & L_USBRXTX) {
+				printk("full trailing packet + 12 bytes:\n");
+				acx_dump_bytes(inbuf, tail_size + RXBUF_HDRSIZE);
+			}
+			acx_l_process_rxbuf(priv, ptr);
+			priv->rxtruncsize = 0;
+			ptr = (rxbuffer_t *) (((char *)inbuf) + tail_size);
+			remsize -= tail_size;
+		}
+		acxlog(L_USBRXTX, "post-merge size=%d remsize=%d\n",
+						size, remsize);
+	}
+
+	/* size = USB data block size
+	** remsize = unprocessed USB bytes left
+	** ptr = current pos in USB data block
+	*/
+	while (remsize) {
+		if (remsize < RXBUF_HDRSIZE) {
+			printk("acx: truncated rx header (%d bytes)!\n",
+				remsize);
+			break;
+		}
+		packetsize = RXBUF_BYTES_RCVD(ptr) + RXBUF_HDRSIZE;
+		acxlog(L_USBRXTX, "packet with packetsize=%d\n", packetsize);
+		if (packetsize > sizeof(rxbuffer_t)) {
+			printk("acx: packet exceeds max wlan "
+				"frame size (%d > %d). size=%d\n",
+				packetsize, (int) sizeof(rxbuffer_t), size);
+			/* FIXME: put some real error-handling in here! */
+			break;
+		}
+
+		/* skip null packets (does this really happen?!) */
+		if (packetsize == RXBUF_HDRSIZE) {
+			remsize -= RXBUF_HDRSIZE;
+			if (acx_debug & L_USBRXTX) {
+				printk("acx: null packet, new remsize=%d. "
+					"header follows:\n", remsize);
+				acx_dump_bytes(ptr, RXBUF_HDRSIZE);
+			}
+			ptr = (rxbuffer_t *)(((char *)ptr) + RXBUF_HDRSIZE);
+			continue;
+		}
+
+		if (packetsize > remsize) {
+			/* frame truncation handling */
+			if (acx_debug & L_USBRXTX) {
+				printk("need to truncate packet, "
+					"packetsize=%d remsize=%d "
+					"size=%d\n",
+					packetsize, remsize, size);
+				acx_dump_bytes(ptr, RXBUF_HDRSIZE);
+			}
+			memcpy(&priv->rxtruncbuf, ptr, remsize);
+			priv->rxtruncsize = remsize;
+			break;
+		} else { /* packetsize <= remsize */
+			/* now handle the received data */
+			acx_l_process_rxbuf(priv, ptr);
+
+			ptr = (rxbuffer_t *)(((char *)ptr) + packetsize);
+			remsize -= packetsize;
+			if ((acx_debug & L_USBRXTX) && remsize) {
+				printk("more than one packet in buffer, "
+						"second packet hdr follows\n");
+				acx_dump_bytes(ptr, RXBUF_HDRSIZE);
+			}
+		}
+	}
+
+do_poll_rx:
+	/* look for the next rx */
+	if (priv->dev_state_mask & ACX_STATE_IFACE_UP) {
+		/* receive of frame completed, now look for the next one */
+		acx100usb_l_poll_rx(priv, number);
+	}
+
+end_unlock:
+	acx_unlock(priv, flags);
+end:
+	FN_EXIT0;
+}
+
+
+/***********************************************************************
+** acx100usb_i_complete_tx():
+** Inputs:
+**     urb -> Pointer to USB request block
+**    regs -> Pointer to register-buffer for syscalls (see asm/ptrace.h)
+************************************************************************
+** Description:
+**   This function is invoked upon termination of a USB transfer. As the
+**   USB device is only capable of sending a limited amount of bytes per
+**   transfer to the bulk-out endpoint, this routine checks if there are
+**   more bytes to send and triggers subsequent transfers. In case the
+**   transfer size exactly matches the maximum bulk-out size, it triggers
+**   a transfer of a null-frame, telling the card that this is it. Upon
+**   completion of a frame, it checks whether the Tx ringbuffer contains
+**   more data to send and invokes the Tx routines if this is the case.
+**   If there are no more occupied Tx descriptors, the Tx Mutex is unlocked
+**   and the network queue is switched back to life again.
+*/
+#if USB_24
+static void
+acx100usb_i_complete_tx(struct urb *urb)
+#else
+static void
+acx100usb_i_complete_tx(struct urb *urb, struct pt_regs *regs)
+#endif
+{
+	wlandevice_t *priv;
+	usb_tx_t *tx;
+	unsigned long flags;
+
+	FN_ENTER;
+
+	if (!urb->context) {
+		printk(KERN_ERR "acx: error, NULL context in tx completion callback\n");
+		/* FIXME: real error-handling code must go here! */
+		goto end;
+	}
+
+	tx = (usb_tx_t *)urb->context;
+	priv = tx->priv;
+
+	acxlog(L_USBRXTX, "RETURN TX (%p): status=%d size=%d\n",
+				tx, urb->status, urb->actual_length);
+
+	/* handle USB transfer errors */
+	switch (urb->status) {
+	case 0:	/* No error */
+		break;
+	case -ECONNRESET:
+		break;
+		/* FIXME: real error-handling code here please */
+	default:
+		printk(KERN_ERR "acx: tx error, urb status=%d\n", urb->status);
+		/* FIXME: real error-handling code here please */
+	}
+
+	acx_lock(priv, flags);
+
+	/* free the URB and check for more data	*/
+	priv->usb_free_tx++;
+	tx->busy = 0;
+
+	acx_unlock(priv, flags);
+end:
+	FN_EXIT0;
+}
+
+
+/***********************************************************************
+** acx100usb_e_close():
+** Inputs:
+**    dev -> Pointer to network device structure
+************************************************************************
+** Returns:
+**  (int) 0 on success, or error-code
+**
+** Description:
+**  This function stops the network functionality of the interface (invoked
+**  when the user calls ifconfig <wlan> down). The tx queue is halted and
+**  the device is marked as down. In case there were any pending USB bulk
+**  transfers, these are unlinked (asynchronously). The module in-use count
+**  is also decreased in this function.
+*/
+static int
+acx100usb_e_close(struct net_device *dev)
+{
+	wlandevice_t *priv;
+	int i, already_down;
+
+	priv = dev->priv;
+	if (!priv) {
+		printk(KERN_ERR "acx: dev->priv empty, FAILED\n");
+		return -ENODEV;
+	}
+
+	FN_ENTER;
+
+#if WE_STILL_DONT_CARE_ABOUT_IT
+	/* Transmit a disassociate frame */
+	lock
+	acx_l_transmit_disassoc(priv, &client);
+	unlock
+#endif
+
+	acx_sem_lock(priv);
+
+	/* stop the transmit queue */
+	if (!acx_queue_stopped(dev)) {
+		acx_stop_queue(dev, "on iface stop");
+	}
+
+//maybe LOCKING BUG, see pci.c if you wonder why
+	FLUSH_SCHEDULED_WORK();
+
+	/* mark the device as DOWN */
+	if (priv->dev_state_mask & ACX_STATE_IFACE_UP) {
+		already_down = 0;
+		CLEAR_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP);
+	} else {
+		/* I don't think it can happen */
+		printk("acx: double down, please report!\n");
+		already_down = 1;
+	}
+
+	/* wait until all tx are out */
+#if 0
+	for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) {
+		while (priv->usb_tx[i].urb->status == -EINPROGRESS) {
+			mdelay(5);
+		}
+	}
+#endif
+
+	/* stop pending rx/tx urb transfers */
+	for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) {
+		acx_unlink_and_free_urb(priv->bulkrx_urbs[i]);
+		acx_unlink_and_free_urb(priv->usb_tx[i].urb);
+	}
+
+	/* disable rx and tx */
+	acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_TX, NULL, 0);
+	acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_RX, NULL, 0);
+
+	/* power down the device */
+	acx_s_issue_cmd(priv, ACX1xx_CMD_SLEEP, NULL, 0);
+
+	acx_sem_unlock(priv);
+
+	/* decrease module-in-use count (if necessary) */
+	if (!already_down)
+		WLAN_MOD_DEC_USE_COUNT;
+
+	FN_EXIT1(0);
+	return 0;
+}
+
+
+/***************************************************************
+** acx_l_alloc_tx
+** Actually returns a usb_tx_t* ptr
+*/
+tx_t*
+acx_l_alloc_tx(wlandevice_t* priv)
+{
+	int i;
+	usb_tx_t *tx = NULL;
+
+	FN_ENTER;
+
+	for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) {
+		if (!priv->usb_tx[i].busy) {
+			tx = &priv->usb_tx[i];
+			tx->busy = 1;
+			break;
+		}
+	}
+	if (i >= ACX100_USB_NUM_BULK_URBS) {
+		printk("acx: tx buffers full\n");
+	}
+
+	FN_EXIT0;
+
+	return (tx_t*)tx;
+}
+
+
+/***************************************************************
+*/
+void*
+acx_l_get_txbuf(tx_t* tx_opaque)
+{
+	usb_tx_t* tx = (usb_tx_t*)tx_opaque;
+	return &tx->bulkout.data;
+}
+
+
+/***************************************************************
+** acx_l_dma_tx_data
+**
+** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx).
+** Can be called from acx_i_start_xmit (data frames from net core).
+*/
+void
+acx_l_dma_tx_data(wlandevice_t *priv, tx_t* tx_opaque, int wlanpkt_len)
+{
+	struct usb_device *usbdev;
+	struct urb* txurb;
+	usb_tx_t* tx;
+	usb_txbuffer_t* txbuf;
+	client_t *clt;
+	wlan_hdr_t* whdr;
+	unsigned int outpipe;
+	int ucode;
+	u8 rate100;
+
+	FN_ENTER;
+
+	tx = ((usb_tx_t *)tx_opaque);
+	txurb = tx->urb;
+	txbuf = &tx->bulkout;
+	whdr = (wlan_hdr_t *)txbuf->data;
+
+	priv->usb_free_tx--;
+	acxlog(L_DEBUG, "using buf#%d free=%d len=%d\n", tx - priv->usb_tx,
+			priv->usb_free_tx, wlanpkt_len);
+
+	switch (priv->mode) {
+	case ACX_MODE_0_ADHOC:
+	case ACX_MODE_3_AP:
+		clt = acx_l_sta_list_get(priv, whdr->a1);
+		break;
+	case ACX_MODE_2_STA:
+		clt = priv->ap_client;
+		break;
+	default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */
+		clt = NULL;
+		break;
+	}
+
+	if (unlikely(clt && !clt->rate_cur)) {
+		printk("acx: driver bug! bad ratemask\n");
+		goto end;
+	}
+
+	/* used in tx cleanup routine for auto rate and accounting: */
+//TODO: currently unused - fix that
+	tx->txc = clt;
+
+	rate100 = clt ? clt->rate_100 : priv->rate_bcast100;
+
+	/* fill the USB transfer header */
+	txbuf->desc = cpu_to_le16(USB_TXBUF_TXDESC);
+	txbuf->MPDUlen = cpu_to_le16(wlanpkt_len);
+	txbuf->ctrl1 = 0;
+	txbuf->ctrl2 = 0;
+	txbuf->hostData = cpu_to_le32(wlanpkt_len | (rate100 << 24));
+	if (1 == priv->preamble_cur)
+		SET_BIT(txbuf->ctrl1, DESC_CTL_SHORT_PREAMBLE);
+	SET_BIT(txbuf->ctrl1, DESC_CTL_FIRSTFRAG);
+	txbuf->txRate = rate100;
+	txbuf->index = 1;
+	txbuf->dataLength = cpu_to_le16(wlanpkt_len);
+
+	if ( (WF_FC_FTYPEi & whdr->fc) == WF_FTYPE_DATAi )
+		SET_BIT(txbuf->hostData, cpu_to_le32(USB_TXBUF_HD_ISDATA));
+	if (mac_is_directed(whdr->a1))
+		SET_BIT(txbuf->hostData, cpu_to_le32(USB_TXBUF_HD_DIRECTED));
+	else if (mac_is_bcast(whdr->a1))
+		SET_BIT(txbuf->hostData, cpu_to_le32(USB_TXBUF_HD_BROADCAST));
+
+	if (acx_debug & L_DATA) {
+		printk("dump of bulk out urb:\n");
+		acx_dump_bytes(txbuf, wlanpkt_len + USB_TXBUF_HDRSIZE);
+	}
+
+	if (txurb->status == -EINPROGRESS) {
+		printk("acx: trying to submit tx urb while already in progress\n");
+	}
+
+	/* now schedule the USB transfer */
+	usbdev = priv->usbdev;
+	outpipe = usb_sndbulkpipe(usbdev, priv->bulkoutep);
+//can be removed, please try & test:
+	tx->priv = priv;
+
+	usb_fill_bulk_urb(txurb, usbdev, outpipe,
+		txbuf, /* dataptr */
+		wlanpkt_len + USB_TXBUF_HDRSIZE, /* size */
+		acx100usb_i_complete_tx, /* handler */
+		tx /* handler param */
+	);
+
+	txurb->transfer_flags = ASYNC_UNLINK|QUEUE_BULK|ZERO_PACKET;
+#if USB_24
+	txurb->status = 0;
+	txurb->timeout = ACX100_USB_TX_TIMEOUT;
+#endif
+	ucode = submit_urb(txurb, GFP_ATOMIC);
+	acxlog(L_USBRXTX, "SUBMIT TX (%p): outpipe=0x%X buf=%p txsize=%d "
+		"errcode=%d\n", tx, outpipe, txbuf,
+		wlanpkt_len + USB_TXBUF_HDRSIZE, ucode);
+
+	if (ucode) {
+		printk(KERN_ERR "acx: submit_urb() error=%d txsize=%d\n",
+			ucode, wlanpkt_len + USB_TXBUF_HDRSIZE);
+
+		/* on error, just mark the frame as done and update
+		** the statistics
+		*/
+		priv->stats.tx_errors++;
+		tx->busy = 0;
+		priv->usb_free_tx++;
+	}
+end:
+	FN_EXIT0;
+}
+
+
+/***********************************************************************
+*/
+static struct net_device_stats*
+acx_e_get_stats(netdevice_t *dev)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	return &priv->stats;
+}
+
+
+/***********************************************************************
+*/
+static struct iw_statistics*
+acx_e_get_wireless_stats(netdevice_t *dev)
+{
+	wlandevice_t *priv = acx_netdev_priv(dev);
+	return &priv->wstats;
+}
+
+
+/***********************************************************************
+*/
+static void
+acx100usb_i_set_rx_mode(struct net_device *dev)
+{
+}
+
+
+/***********************************************************************
+*/
+#ifdef HAVE_TX_TIMEOUT
+static void
+acx100usb_i_tx_timeout(struct net_device *dev)
+{
+	wlandevice_t *priv;
+	unsigned long flags;
+	int i;
+
+	FN_ENTER;
+
+	priv = dev->priv;
+
+	acx_lock(priv, flags);
+	/* unlink the URBs */
+	for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) {
+		if (priv->usb_tx[i].urb->status == -EINPROGRESS)
+			usb_unlink_urb(priv->usb_tx[i].urb);
+	}
+	/* TODO: stats update */
+	acx_unlock(priv, flags);
+
+	FN_EXIT0;
+}
+#endif
+
+
+/***********************************************************************
+** acx_l_power_led
+*/
+void
+acx_l_power_led(wlandevice_t *priv, int enable)
+{
+	acxlog(L_IOCTL, "acx: power_led() is not supported on USB\n");
+}
+
+
+/***********************************************************************
+** Ioctls
+*/
+
+/***********************************************************************
+** Ioctls for easy debug of I/O things (stubs only for USB)
+*/
+int
+acx_ioctl_dbg_get_io(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	acxlog(L_IOCTL, "acx: dbg_get_io() is not supported on USB\n");
+	return -EINVAL;
+}
+
+int
+acx_ioctl_dbg_set_io(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	acxlog(L_IOCTL, "acx: dbg_set_io() is not supported on USB\n");
+	return -EINVAL;
+}
+
+
+/***********************************************************************
+*/
+int
+acx111_ioctl_info(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	return OK;
+}
+
+
+/***********************************************************************
+*/
+int
+acx100_ioctl_set_phy_amp_bias(
+	struct net_device *dev,
+	struct iw_request_info *info,
+	struct iw_param *vwrq,
+	char *extra)
+{
+	printk("acx: set_phy_amp_bias() is not supported on USB\n");
+	return OK;
+}
+
+
+/***********************************************************************
+** init_module():
+** Inputs:
+**  <NONE>
+************************************************************************
+** Returns:
+**  (int) Errorcode on failure, 0 on success
+**
+** Description:
+**  This function is invoked upon loading of the kernel module. It registers
+**  itself at the kernel's USB subsystem.
+*/
+int
+init_module(void)
+{
+	printk(KERN_INFO "Initializing acx100 WLAN USB kernel module\n");
+	return usb_register(&acx100usb_driver);
+}
+
+
+
+/***********************************************************************
+** cleanup_module():
+** Inputs:
+**  <NONE>
+************************************************************************
+** Description:
+**  This function is invoked as last step of the module unloading. It simply
+**  deregisters this module at the kernel's USB subsystem.
+*/
+void
+cleanup_module()
+{
+	usb_deregister(&acx100usb_driver);
+	printk(KERN_INFO "Cleaning up acx100 WLAN USB kernel module\n");
+}
+
+
+/***********************************************************************
+** DEBUG STUFF
+*/
+#if ACX_DEBUG
+
+#if UNUSED
+static void
+dump_device(struct usb_device *usbdev)
+{
+	int i;
+	struct usb_config_descriptor *cd;
+
+	printk("acx device dump:\n");
+	printk("  devnum: %d\n", usbdev->devnum);
+	printk("  speed: %d\n", usbdev->speed);
+	printk("  tt: 0x%X\n", (unsigned int)(usbdev->tt));
+	printk("  ttport: %d\n", (unsigned int)(usbdev->ttport));
+	printk("  toggle[0]: 0x%X  toggle[1]: 0x%X\n", (unsigned int)(usbdev->toggle[0]), (unsigned int)(usbdev->toggle[1]));
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 8)
+	/* halted removed in 2.6.9-rc1 */
+	/* DOH, Canbreak... err... Mandrake decided to do their very own very
+	 * special version "2.6.8.1" which already includes this change, so we
+	 * need to blacklist that version already (i.e. 2.6.8) */
+	printk("  halted[0]: 0x%X  halted[1]: 0x%X\n", usbdev->halted[0], usbdev->halted[1]);
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
+	/* This saw a change after 2.6.10 */
+	printk("  ep_in wMaxPacketSize: ");
+	for (i = 0; i < 16; ++i)
+		printk("%d ", usbdev->ep_in[i]->desc.wMaxPacketSize);
+	printk("\n");
+	printk("  ep_out wMaxPacketSize: ");
+	for (i = 0; i < 15; ++i)
+		printk("%d ", usbdev->ep_out[i]->desc.wMaxPacketSize);
+	printk("\n");
+#else
+	printk("  epmaxpacketin: ");
+	for (i = 0; i < 16; i++)
+		printk("%d ", usbdev->epmaxpacketin[i]);
+	printk("\n");
+	printk("  epmaxpacketout: ");
+	for (i = 0; i < 16; i++)
+		printk("%d ", usbdev->epmaxpacketout[i]);
+	printk("\n");
+#endif
+	printk("  parent: 0x%X\n", (unsigned int)usbdev->parent);
+	printk("  bus: 0x%X\n", (unsigned int)usbdev->bus);
+#if NO_DATATYPE
+	printk("  configs: ");
+	for (i = 0; i < usbdev->descriptor.bNumConfigurations; i++)
+		printk("0x%X ", usbdev->config[i]);
+	printk("\n");
+#endif
+	printk("  actconfig: %p\n", usbdev->actconfig);
+	dump_device_descriptor(&usbdev->descriptor);
+#if USB_24
+	cd = usbdev->actconfig;
+	dump_config_descriptor(cd);
+	{
+		struct usb_interface *ifc = cd->interface;
+		if (ifc) {
+			printk("iface: altsetting=%p act_altsetting=%d "
+				"num_altsetting=%d max_altsetting=%d\n",
+				ifc->altsetting, ifc->act_altsetting,
+				ifc->num_altsetting, ifc->max_altsetting);
+			dump_interface_descriptor(ifc->altsetting);
+			dump_endpoint_descriptor(ifc->altsetting->endpoint);
+		}
+	}
+#else
+	cd = &usbdev->config->desc;
+	dump_config_descriptor(cd);
+#endif
+}
+
+
+/***********************************************************************
+*/
+static void
+dump_config_descriptor(struct usb_config_descriptor *cd)
+{
+	printk("Configuration Descriptor:\n");
+	if (!cd) {
+		printk("NULL\n");
+		return;
+	}
+	printk("  bLength: %d (0x%X)\n", cd->bLength, cd->bLength);
+	printk("  bDescriptorType: %d (0x%X)\n", cd->bDescriptorType, cd->bDescriptorType);
+	printk("  bNumInterfaces: %d (0x%X)\n", cd->bNumInterfaces, cd->bNumInterfaces);
+	printk("  bConfigurationValue: %d (0x%X)\n", cd->bConfigurationValue, cd->bConfigurationValue);
+	printk("  iConfiguration: %d (0x%X)\n", cd->iConfiguration, cd->iConfiguration);
+	printk("  bmAttributes: %d (0x%X)\n", cd->bmAttributes, cd->bmAttributes);
+	/* printk("  MaxPower: %d (0x%X)\n", cd->bMaxPower, cd->bMaxPower); */
+}
+
+
+static void
+dump_device_descriptor(struct usb_device_descriptor *dd)
+{
+	printk("Device Descriptor:\n");
+	if (!dd) {
+		printk("NULL\n");
+		return;
+	}
+	printk("  bLength: %d (0x%X)\n", dd->bLength, dd->bLength);
+	printk("  bDescriptortype: %d (0x%X)\n", dd->bDescriptorType, dd->bDescriptorType);
+	printk("  bcdUSB: %d (0x%X)\n", dd->bcdUSB, dd->bcdUSB);
+	printk("  bDeviceClass: %d (0x%X)\n", dd->bDeviceClass, dd->bDeviceClass);
+	printk("  bDeviceSubClass: %d (0x%X)\n", dd->bDeviceSubClass, dd->bDeviceSubClass);
+	printk("  bDeviceProtocol: %d (0x%X)\n", dd->bDeviceProtocol, dd->bDeviceProtocol);
+	printk("  bMaxPacketSize0: %d (0x%X)\n", dd->bMaxPacketSize0, dd->bMaxPacketSize0);
+	printk("  idVendor: %d (0x%X)\n", dd->idVendor, dd->idVendor);
+	printk("  idProduct: %d (0x%X)\n", dd->idProduct, dd->idProduct);
+	printk("  bcdDevice: %d (0x%X)\n", dd->bcdDevice, dd->bcdDevice);
+	printk("  iManufacturer: %d (0x%X)\n", dd->iManufacturer, dd->iManufacturer);
+	printk("  iProduct: %d (0x%X)\n", dd->iProduct, dd->iProduct);
+	printk("  iSerialNumber: %d (0x%X)\n", dd->iSerialNumber, dd->iSerialNumber);
+	printk("  bNumConfigurations: %d (0x%X)\n", dd->bNumConfigurations, dd->bNumConfigurations);
+}
+#endif /* UNUSED */
+
+
+/***********************************************************************
+*/
+#if USB_24
+static void
+dump_endpoint_descriptor(struct usb_endpoint_descriptor *ep)
+{
+	printk("Endpoint Descriptor:\n");
+	if (!ep) {
+		printk("NULL\n");
+		return;
+	}
+	printk("  bLength: %d (0x%X)\n", ep->bLength, ep->bLength);
+	printk("  bDescriptorType: %d (0x%X)\n", ep->bDescriptorType, ep->bDescriptorType);
+	printk("  bEndpointAddress: %d (0x%X)\n", ep->bEndpointAddress, ep->bEndpointAddress);
+	printk("  bmAttributes: 0x%X\n", ep->bmAttributes);
+	printk("  wMaxPacketSize: %d (0x%X)\n", ep->wMaxPacketSize, ep->wMaxPacketSize);
+	printk("  bInterval: %d (0x%X)\n", ep->bInterval, ep->bInterval);
+	printk("  bRefresh: %d (0x%X)\n", ep->bRefresh, ep->bRefresh);
+	printk("  bSyncAdrress: %d (0x%X)\n", ep->bSynchAddress, ep->bSynchAddress);
+}
+
+static void
+dump_interface_descriptor(struct usb_interface_descriptor *id)
+{
+	printk("Interface Descriptor:\n");
+	if (!id) {
+		printk("NULL\n");
+		return;
+	}
+	printk("  bLength: %d (0x%X)\n", id->bLength, id->bLength);
+	printk("  bDescriptorType: %d (0x%X)\n", id->bDescriptorType, id->bDescriptorType);
+	printk("  bInterfaceNumber: %d (0x%X)\n", id->bInterfaceNumber, id->bInterfaceNumber);
+	printk("  bAlternateSetting: %d (0x%X)\n", id->bAlternateSetting, id->bAlternateSetting);
+	printk("  bNumEndpoints: %d (0x%X)\n", id->bNumEndpoints, id->bNumEndpoints);
+	printk("  bInterfaceClass: %d (0x%X)\n", id->bInterfaceClass, id->bInterfaceClass);
+	printk("  bInterfaceSubClass: %d (0x%X)\n", id->bInterfaceSubClass, id->bInterfaceSubClass);
+	printk("  bInterfaceProtocol: %d (0x%X)\n", id->bInterfaceProtocol, id->bInterfaceProtocol);
+	printk("  iInterface: %d (0x%X)\n", id->iInterface, id->iInterface);
+	printk("  endpoint: 0x%X\n", (unsigned int)(id->endpoint));
+}
+#endif /* USB_24 */
+
+#endif /* ACX_DEBUG */
diff -urN oldtree/drivers/net/wireless/tiacx/usb_helper.c newtree/drivers/net/wireless/tiacx/usb_helper.c
--- oldtree/drivers/net/wireless/tiacx/usb_helper.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/usb_helper.c	2006-02-13 19:20:00.548428928 -0500
@@ -0,0 +1,2 @@
+#define ACX_USB 1
+#include "helper.c"
diff -urN oldtree/drivers/net/wireless/tiacx/wlan.c newtree/drivers/net/wireless/tiacx/wlan.c
--- oldtree/drivers/net/wireless/tiacx/wlan.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/wlan.c	2006-02-13 19:20:00.548428928 -0500
@@ -0,0 +1,391 @@
+/***********************************************************************
+** Copyright (C) 2003  ACX100 Open Source Project
+**
+** The contents of this file are subject to the Mozilla Public
+** License Version 1.1 (the "License"); you may not use this file
+** except in compliance with the License. You may obtain a copy of
+** the License at http://www.mozilla.org/MPL/
+**
+** Software distributed under the License is distributed on an "AS
+** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+** implied. See the License for the specific language governing
+** rights and limitations under the License.
+**
+** Alternatively, the contents of this file may be used under the
+** terms of the GNU Public License version 2 (the "GPL"), in which
+** case the provisions of the GPL are applicable instead of the
+** above.  If you wish to allow the use of your version of this file
+** only under the terms of the GPL and not to allow others to use
+** your version of this file under the MPL, indicate your decision
+** by deleting the provisions above and replace them with the notice
+** and other provisions required by the GPL.  If you do not delete
+** the provisions above, a recipient may use your version of this
+** file under either the MPL or the GPL.
+** ---------------------------------------------------------------------
+** Inquiries regarding the ACX100 Open Source Project can be
+** made directly to:
+**
+** acx100-users@lists.sf.net
+** http://acx100.sf.net
+** ---------------------------------------------------------------------
+*/
+
+/***********************************************************************
+** This code is based on elements which are
+** Copyright (C) 1999 AbsoluteValue Systems, Inc.  All Rights Reserved.
+** info@linux-wlan.com
+** http://www.linux-wlan.com
+*/
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#include <linux/types.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+#if WIRELESS_EXT >= 13
+#include <net/iw_handler.h>
+#endif
+
+#include "acx.h"
+
+
+/***********************************************************************
+*/
+#define LOG_BAD_EID(hdr,len,ie_ptr) acx_log_bad_eid(hdr, len, ((wlan_ie_t*)ie_ptr))
+
+#define IE_EID(ie_ptr) (((wlan_ie_t*)(ie_ptr))->eid)
+#define IE_LEN(ie_ptr) (((wlan_ie_t*)(ie_ptr))->len)
+#define OFFSET(hdr,off) (WLAN_HDR_A3_DATAP(hdr) + (off))
+
+
+/***********************************************************************
+** wlan_mgmt_decode_XXX
+**
+** Given a complete frame in f->hdr, sets the pointers in f to
+** the areas that correspond to the parts of the frame.
+**
+** Assumptions:
+**	1) f->len and f->hdr are already set
+**	2) f->len is the length of the MAC header + data, the CRC
+**	   is NOT included
+**	3) all members except len and hdr are zero
+** Arguments:
+**	f	frame structure
+**
+** Returns:
+**	nothing
+**
+** Side effects:
+**	frame structure members are pointing at their
+**	respective portions of the frame buffer.
+*/
+void
+wlan_mgmt_decode_beacon(wlan_fr_beacon_t * f)
+{
+	u8 *ie_ptr;
+	u8 *end = (u8*)f->hdr + f->len;
+
+	f->type = WLAN_FSTYPE_BEACON;
+
+	/*-- Fixed Fields ----*/
+	f->ts = (u64 *) OFFSET(f->hdr, WLAN_BEACON_OFF_TS);
+	f->bcn_int = (u16 *) OFFSET(f->hdr, WLAN_BEACON_OFF_BCN_INT);
+	f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_BEACON_OFF_CAPINFO);
+
+	/*-- Information elements */
+	ie_ptr = OFFSET(f->hdr, WLAN_BEACON_OFF_SSID);
+	while (ie_ptr < end) {
+		switch (IE_EID(ie_ptr)) {
+		case WLAN_EID_SSID:
+			f->ssid = (wlan_ie_ssid_t *) ie_ptr;
+			break;
+		case WLAN_EID_SUPP_RATES:
+			f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr;
+			break;
+		case WLAN_EID_EXT_RATES:
+			f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr;
+			break;
+		case WLAN_EID_FH_PARMS:
+			f->fh_parms = (wlan_ie_fh_parms_t *) ie_ptr;
+			break;
+		case WLAN_EID_DS_PARMS:
+			f->ds_parms = (wlan_ie_ds_parms_t *) ie_ptr;
+			break;
+		case WLAN_EID_CF_PARMS:
+			f->cf_parms = (wlan_ie_cf_parms_t *) ie_ptr;
+			break;
+		case WLAN_EID_IBSS_PARMS:
+			f->ibss_parms = (wlan_ie_ibss_parms_t *) ie_ptr;
+			break;
+		case WLAN_EID_TIM:
+			f->tim = (wlan_ie_tim_t *) ie_ptr;
+			break;
+		case WLAN_EID_ERP_INFO:
+			f->erp = (wlan_ie_erp_t *) ie_ptr;
+			break;
+		case WLAN_EID_GENERIC:
+		/* WPA:
+			if (pos[1] >= 4 &&
+				pos[2] == 0x00 && pos[3] == 0x50 &&
+				pos[4] == 0xf2 && pos[5] == 1) {
+				wpa = pos;
+				wpa_len = pos[1] + 2;
+			}
+		TI x4 mode: last byte is probably 0/1 - disabled/enabled
+			if (pos[1] == 4 &&
+				pos[2] == 0x80 && pos[3] == 0x00 &&
+				pos[4] == 0x28 && pos[5] == 0/1) {
+			}
+		*/
+			break;
+		case WLAN_EID_RSN:
+		/*
+			rsn = pos;
+			rsn_len = pos[1] + 2;
+		*/
+			break;
+		default:
+			LOG_BAD_EID(f->hdr, f->len, ie_ptr);
+			break;
+		}
+		ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr);
+	}
+}
+
+
+#if UNUSED
+void wlan_mgmt_decode_ibssatim(wlan_fr_ibssatim_t * f)
+{
+	f->type = WLAN_FSTYPE_ATIM;
+	/*-- Fixed Fields ----*/
+	/*-- Information elements */
+}
+#endif /* UNUSED */
+
+void
+wlan_mgmt_decode_disassoc(wlan_fr_disassoc_t * f)
+{
+	f->type = WLAN_FSTYPE_DISASSOC;
+
+	/*-- Fixed Fields ----*/
+	f->reason = (u16 *) OFFSET(f->hdr, WLAN_DISASSOC_OFF_REASON);
+
+	/*-- Information elements */
+}
+
+
+void
+wlan_mgmt_decode_assocreq(wlan_fr_assocreq_t * f)
+{
+	u8 *ie_ptr;
+	u8 *end = (u8*)f->hdr + f->len;
+
+
+	f->type = WLAN_FSTYPE_ASSOCREQ;
+
+	/*-- Fixed Fields ----*/
+	f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_ASSOCREQ_OFF_CAP_INFO);
+	f->listen_int = (u16 *) OFFSET(f->hdr, WLAN_ASSOCREQ_OFF_LISTEN_INT);
+
+	/*-- Information elements */
+	ie_ptr = OFFSET(f->hdr, WLAN_ASSOCREQ_OFF_SSID);
+	while (ie_ptr < end) {
+		switch (IE_EID(ie_ptr)) {
+		case WLAN_EID_SSID:
+			f->ssid = (wlan_ie_ssid_t *) ie_ptr;
+			break;
+		case WLAN_EID_SUPP_RATES:
+			f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr;
+			break;
+		case WLAN_EID_EXT_RATES:
+			f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr;
+			break;
+		default:
+			LOG_BAD_EID(f->hdr, f->len, ie_ptr);
+			break;
+		}
+		ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr);
+	}
+}
+
+
+void
+wlan_mgmt_decode_assocresp(wlan_fr_assocresp_t * f)
+{
+	f->type = WLAN_FSTYPE_ASSOCRESP;
+
+	/*-- Fixed Fields ----*/
+	f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_CAP_INFO);
+	f->status = (u16 *) OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_STATUS);
+	f->aid = (u16 *) OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_AID);
+
+	/*-- Information elements */
+	f->supp_rates = (wlan_ie_supp_rates_t *)
+			OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_SUPP_RATES);
+}
+
+
+void
+wlan_mgmt_decode_reassocreq(wlan_fr_reassocreq_t * f)
+{
+	u8 *ie_ptr;
+	u8 *end = (u8*)f->hdr + f->len;
+
+	f->type = WLAN_FSTYPE_REASSOCREQ;
+
+	/*-- Fixed Fields ----*/
+	f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_CAP_INFO);
+	f->listen_int = (u16 *) OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_LISTEN_INT);
+	f->curr_ap = (u8 *) OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_CURR_AP);
+
+	/*-- Information elements */
+	ie_ptr = OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_SSID);
+	while (ie_ptr < end) {
+		switch (IE_EID(ie_ptr)) {
+		case WLAN_EID_SSID:
+			f->ssid = (wlan_ie_ssid_t *) ie_ptr;
+			break;
+		case WLAN_EID_SUPP_RATES:
+			f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr;
+			break;
+		case WLAN_EID_EXT_RATES:
+			f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr;
+			break;
+		default:
+			LOG_BAD_EID(f->hdr, f->len, ie_ptr);
+			break;
+		}
+		ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr);
+	}
+}
+
+
+void
+wlan_mgmt_decode_reassocresp(wlan_fr_reassocresp_t * f)
+{
+	f->type = WLAN_FSTYPE_REASSOCRESP;
+
+	/*-- Fixed Fields ----*/
+	f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_CAP_INFO);
+	f->status = (u16 *) OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_STATUS);
+	f->aid = (u16 *) OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_AID);
+
+	/*-- Information elements */
+	f->supp_rates = (wlan_ie_supp_rates_t *)
+			OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_SUPP_RATES);
+}
+
+
+void
+wlan_mgmt_decode_probereq(wlan_fr_probereq_t * f)
+{
+	u8 *ie_ptr;
+	u8 *end = (u8*)f->hdr + f->len;
+
+	f->type = WLAN_FSTYPE_PROBEREQ;
+
+	/*-- Fixed Fields ----*/
+
+	/*-- Information elements */
+	ie_ptr = OFFSET(f->hdr, WLAN_PROBEREQ_OFF_SSID);
+	while (ie_ptr < end) {
+		switch (IE_EID(ie_ptr)) {
+		case WLAN_EID_SSID:
+			f->ssid = (wlan_ie_ssid_t *) ie_ptr;
+			break;
+		case WLAN_EID_SUPP_RATES:
+			f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr;
+			break;
+		case WLAN_EID_EXT_RATES:
+			f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr;
+			break;
+		default:
+			LOG_BAD_EID(f->hdr, f->len, ie_ptr);
+			break;
+		}
+		ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr);
+	}
+}
+
+
+/* TODO: decoding of beacon and proberesp can be merged (similar structure) */
+void
+wlan_mgmt_decode_proberesp(wlan_fr_proberesp_t * f)
+{
+	u8 *ie_ptr;
+	u8 *end = (u8*)f->hdr + f->len;
+
+	f->type = WLAN_FSTYPE_PROBERESP;
+
+	/*-- Fixed Fields ----*/
+	f->ts = (u64 *) OFFSET(f->hdr, WLAN_PROBERESP_OFF_TS);
+	f->bcn_int = (u16 *) OFFSET(f->hdr, WLAN_PROBERESP_OFF_BCN_INT);
+	f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_PROBERESP_OFF_CAP_INFO);
+
+	/*-- Information elements */
+	ie_ptr = OFFSET(f->hdr, WLAN_PROBERESP_OFF_SSID);
+	while (ie_ptr < end) {
+		switch (IE_EID(ie_ptr)) {
+		case WLAN_EID_SSID:
+			f->ssid = (wlan_ie_ssid_t *) ie_ptr;
+			break;
+		case WLAN_EID_SUPP_RATES:
+			f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr;
+			break;
+		case WLAN_EID_EXT_RATES:
+			f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr;
+			break;
+		case WLAN_EID_FH_PARMS:
+			f->fh_parms = (wlan_ie_fh_parms_t *) ie_ptr;
+			break;
+		case WLAN_EID_DS_PARMS:
+			f->ds_parms = (wlan_ie_ds_parms_t *) ie_ptr;
+			break;
+		case WLAN_EID_CF_PARMS:
+			f->cf_parms = (wlan_ie_cf_parms_t *) ie_ptr;
+			break;
+		case WLAN_EID_IBSS_PARMS:
+			f->ibss_parms = (wlan_ie_ibss_parms_t *) ie_ptr;
+			break;
+		default:
+			LOG_BAD_EID(f->hdr, f->len, ie_ptr);
+			break;
+		}
+
+		ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr);
+	}
+}
+
+
+void
+wlan_mgmt_decode_authen(wlan_fr_authen_t * f)
+{
+	u8 *ie_ptr;
+	u8 *end = (u8*)f->hdr + f->len;
+
+	f->type = WLAN_FSTYPE_AUTHEN;
+
+	/*-- Fixed Fields ----*/
+	f->auth_alg = (u16 *) OFFSET(f->hdr, WLAN_AUTHEN_OFF_AUTH_ALG);
+	f->auth_seq = (u16 *) OFFSET(f->hdr, WLAN_AUTHEN_OFF_AUTH_SEQ);
+	f->status = (u16 *) OFFSET(f->hdr, WLAN_AUTHEN_OFF_STATUS);
+
+	/*-- Information elements */
+	ie_ptr = OFFSET(f->hdr, WLAN_AUTHEN_OFF_CHALLENGE);
+	if ((ie_ptr < end) && (IE_EID(ie_ptr) == WLAN_EID_CHALLENGE)) {
+		f->challenge = (wlan_ie_challenge_t *) ie_ptr;
+	}
+}
+
+
+void
+wlan_mgmt_decode_deauthen(wlan_fr_deauthen_t * f)
+{
+	f->type = WLAN_FSTYPE_DEAUTHEN;
+
+	/*-- Fixed Fields ----*/
+	f->reason = (u16 *) OFFSET(f->hdr, WLAN_DEAUTHEN_OFF_REASON);
+
+	/*-- Information elements */
+}
diff -urN oldtree/drivers/net/wireless/tiacx/wlan_compat.h newtree/drivers/net/wireless/tiacx/wlan_compat.h
--- oldtree/drivers/net/wireless/tiacx/wlan_compat.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/wlan_compat.h	2006-02-13 19:20:00.549428776 -0500
@@ -0,0 +1,331 @@
+/***********************************************************************
+** Copyright (C) 2003  ACX100 Open Source Project
+**
+** The contents of this file are subject to the Mozilla Public
+** License Version 1.1 (the "License"); you may not use this file
+** except in compliance with the License. You may obtain a copy of
+** the License at http://www.mozilla.org/MPL/
+**
+** Software distributed under the License is distributed on an "AS
+** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+** implied. See the License for the specific language governing
+** rights and limitations under the License.
+**
+** Alternatively, the contents of this file may be used under the
+** terms of the GNU Public License version 2 (the "GPL"), in which
+** case the provisions of the GPL are applicable instead of the
+** above.  If you wish to allow the use of your version of this file
+** only under the terms of the GPL and not to allow others to use
+** your version of this file under the MPL, indicate your decision
+** by deleting the provisions above and replace them with the notice
+** and other provisions required by the GPL.  If you do not delete
+** the provisions above, a recipient may use your version of this
+** file under either the MPL or the GPL.
+** ---------------------------------------------------------------------
+** Inquiries regarding the ACX100 Open Source Project can be
+** made directly to:
+**
+** acx100-users@lists.sf.net
+** http://acx100.sf.net
+** ---------------------------------------------------------------------
+*/
+
+/***********************************************************************
+** This code is based on elements which are
+** Copyright (C) 1999 AbsoluteValue Systems, Inc.  All Rights Reserved.
+** info@linux-wlan.com
+** http://www.linux-wlan.com
+*/
+
+/*=============================================================*/
+/*------ Establish Platform Identity --------------------------*/
+/*=============================================================*/
+/* Key macros: */
+/* WLAN_CPU_FAMILY */
+#define WLAN_Ix86			1
+#define WLAN_PPC			2
+#define WLAN_Ix96			3
+#define WLAN_ARM			4
+#define WLAN_ALPHA			5
+#define WLAN_MIPS			6
+#define WLAN_HPPA			7
+#define WLAN_SPARC			8
+#define WLAN_SH				9
+#define WLAN_x86_64			10
+/* WLAN_CPU_CORE */
+#define WLAN_I386CORE			1
+#define WLAN_PPCCORE			2
+#define WLAN_I296			3
+#define WLAN_ARMCORE			4
+#define WLAN_ALPHACORE			5
+#define WLAN_MIPSCORE			6
+#define WLAN_HPPACORE			7
+/* WLAN_CPU_PART */
+#define WLAN_I386PART			1
+#define WLAN_MPC860			2
+#define WLAN_MPC823			3
+#define WLAN_I296SA			4
+#define WLAN_PPCPART			5
+#define WLAN_ARMPART			6
+#define WLAN_ALPHAPART			7
+#define WLAN_MIPSPART			8
+#define WLAN_HPPAPART			9
+/* WLAN_SYSARCH */
+#define WLAN_PCAT			1
+#define WLAN_MBX			2
+#define WLAN_RPX			3
+#define WLAN_LWARCH			4
+#define WLAN_PMAC			5
+#define WLAN_SKIFF			6
+#define WLAN_BITSY			7
+#define WLAN_ALPHAARCH			7
+#define WLAN_MIPSARCH			9
+#define WLAN_HPPAARCH			10
+/* WLAN_HOSTIF (generally set on the command line, not detected) */
+#define WLAN_PCMCIA			1
+#define WLAN_ISA			2
+#define WLAN_PCI			3
+#define WLAN_USB			4
+#define WLAN_PLX			5
+
+/* Note: the PLX HOSTIF above refers to some vendors implementations for */
+/*       PCI.  It's a PLX chip that is a PCI to PCMCIA adapter, but it   */
+/*       isn't a real PCMCIA host interface adapter providing all the    */
+/*       card&socket services.                                           */
+
+#ifdef __powerpc__
+#ifndef __ppc__
+#define __ppc__
+#endif
+#endif
+
+#if (defined(CONFIG_PPC) || defined(CONFIG_8xx))
+#ifndef __ppc__
+#define __ppc__
+#endif
+#endif
+
+#if defined(__x86_64__)
+ #define WLAN_CPU_FAMILY	WLAN_x86_64
+ #define WLAN_SYSARCH		WLAN_PCAT
+#elif defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__)
+ #define WLAN_CPU_FAMILY	WLAN_Ix86
+ #define WLAN_CPU_CORE		WLAN_I386CORE
+ #define WLAN_CPU_PART		WLAN_I386PART
+ #define WLAN_SYSARCH		WLAN_PCAT
+#elif defined(__ppc__)
+ #define WLAN_CPU_FAMILY	WLAN_PPC
+ #define WLAN_CPU_CORE		WLAN_PPCCORE
+ #if defined(CONFIG_MBX)
+  #define WLAN_CPU_PART		WLAN_MPC860
+  #define WLAN_SYSARCH		WLAN_MBX
+ #elif defined(CONFIG_RPXLITE)
+  #define WLAN_CPU_PART		WLAN_MPC823
+  #define WLAN_SYSARCH		WLAN_RPX
+ #elif defined(CONFIG_RPXCLASSIC)
+  #define WLAN_CPU_PART		WLAN_MPC860
+  #define WLAN_SYSARCH		WLAN_RPX
+ #else
+  #define WLAN_CPU_PART		WLAN_PPCPART
+  #define WLAN_SYSARCH		WLAN_PMAC
+ #endif
+#elif defined(__arm__)
+ #define WLAN_CPU_FAMILY	WLAN_ARM
+ #define WLAN_CPU_CORE		WLAN_ARMCORE
+ #define WLAN_CPU_PART		WLAN_ARM_PART
+ #define WLAN_SYSARCH		WLAN_SKIFF
+#elif defined(__alpha__)
+ #define WLAN_CPU_FAMILY	WLAN_ALPHA
+ #define WLAN_CPU_CORE		WLAN_ALPHACORE
+ #define WLAN_CPU_PART		WLAN_ALPHAPART
+ #define WLAN_SYSARCH		WLAN_ALPHAARCH
+#elif defined(__mips__)
+ #define WLAN_CPU_FAMILY	WLAN_MIPS
+ #define WLAN_CPU_CORE		WLAN_MIPSCORE
+ #define WLAN_CPU_PART		WLAN_MIPSPART
+ #define WLAN_SYSARCH		WLAN_MIPSARCH
+#elif defined(__hppa__)
+ #define WLAN_CPU_FAMILY	WLAN_HPPA
+ #define WLAN_CPU_CORE		WLAN_HPPACORE
+ #define WLAN_CPU_PART		WLAN_HPPAPART
+ #define WLAN_SYSARCH		WLAN_HPPAARCH
+#elif defined(__sparc__)
+ #define WLAN_CPU_FAMILY	WLAN_SPARC
+ #define WLAN_SYSARCH		WLAN_SPARC
+#elif defined(__sh__)
+ #define WLAN_CPU_FAMILY	WLAN_SH
+ #define WLAN_SYSARCH		WLAN_SHARCH
+ #ifndef __LITTLE_ENDIAN__
+  #define __LITTLE_ENDIAN__
+ #endif
+#else
+ #error "No CPU identified!"
+#endif
+
+/*
+   Some big endian machines implicitly do all I/O in little endian mode.
+
+   In particular:
+	  Linux/PPC on PowerMacs (PCI)
+	  Arm/Intel Xscale (PCI)
+
+   This may also affect PLX boards and other BE &| PPC platforms;
+   as new ones are discovered, add them below.
+*/
+
+#if ((WLAN_SYSARCH == WLAN_SKIFF) || (WLAN_SYSARCH == WLAN_PMAC))
+#define REVERSE_ENDIAN
+#endif
+
+/*=============================================================*/
+/*------ Hardware Portability Macros --------------------------*/
+/*=============================================================*/
+#if (WLAN_CPU_FAMILY == WLAN_PPC)
+#define wlan_inw(a)                     in_be16((unsigned short *)((a)+_IO_BASE))
+#define wlan_inw_le16_to_cpu(a)         inw((a))
+#define wlan_outw(v,a)                  out_be16((unsigned short *)((a)+_IO_BASE), (v))
+#define wlan_outw_cpu_to_le16(v,a)      outw((v),(a))
+#else
+#define wlan_inw(a)                     inw((a))
+#define wlan_inw_le16_to_cpu(a)         __cpu_to_le16(inw((a)))
+#define wlan_outw(v,a)                  outw((v),(a))
+#define wlan_outw_cpu_to_le16(v,a)      outw(__cpu_to_le16((v)),(a))
+#endif
+
+/*=============================================================*/
+/*------ Bit settings -----------------------------------------*/
+/*=============================================================*/
+#define BIT0	0x00000001
+#define BIT1	0x00000002
+#define BIT2	0x00000004
+#define BIT3	0x00000008
+#define BIT4	0x00000010
+#define BIT5	0x00000020
+#define BIT6	0x00000040
+#define BIT7	0x00000080
+#define BIT8	0x00000100
+#define BIT9	0x00000200
+#define BIT10	0x00000400
+#define BIT11	0x00000800
+#define BIT12	0x00001000
+#define BIT13	0x00002000
+#define BIT14	0x00004000
+#define BIT15	0x00008000
+#define BIT16	0x00010000
+#define BIT17	0x00020000
+#define BIT18	0x00040000
+#define BIT19	0x00080000
+#define BIT20	0x00100000
+#define BIT21	0x00200000
+#define BIT22	0x00400000
+#define BIT23	0x00800000
+#define BIT24	0x01000000
+#define BIT25	0x02000000
+#define BIT26	0x04000000
+#define BIT27	0x08000000
+#define BIT28	0x10000000
+#define BIT29	0x20000000
+#define BIT30	0x40000000
+#define BIT31	0x80000000
+
+#define ieee2host16(n)	__le16_to_cpu(n)
+#define ieee2host32(n)	__le32_to_cpu(n)
+#define host2ieee16(n)	__cpu_to_le16(n)
+#define host2ieee32(n)	__cpu_to_le32(n)
+
+/* for constants */
+#ifdef __LITTLE_ENDIAN
+ #define IEEE16(a,n)     a = n, a##i = n,
+#else
+ #ifdef __BIG_ENDIAN
+  /* shifts would produce gcc warnings. Oh well... */
+  #define IEEE16(a,n)     a = n, a##i = ((n&0xff)*256 + ((n&0xff00)/256)),
+ #else
+  #error give me endianness or give me death
+ #endif
+#endif
+
+/*=============================================================*/
+/*------ Compiler Portability Macros --------------------------*/
+/*=============================================================*/
+#define __WLAN_ATTRIB_PACK__		__attribute__ ((packed))
+#define __WLAN_PRAGMA_PACK1__
+#define __WLAN_PRAGMA_PACKDFLT__
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,38))
+ typedef struct device netdevice_t;
+#elif (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4))
+ typedef struct net_device netdevice_t;
+#else
+ #undef netdevice_t
+ typedef struct net_device netdevice_t;
+#endif
+
+#ifdef WIRELESS_EXT
+#if (WIRELESS_EXT < 13)
+struct iw_request_info {
+	__u16 cmd;		/* Wireless Extension command */
+	__u16 flags;		/* More to come ;-) */
+};
+#endif
+#endif
+
+/* Interrupt handler backwards compatibility stuff */
+#ifndef IRQ_NONE
+#define IRQ_NONE
+#define IRQ_HANDLED
+typedef void irqreturn_t;
+#endif
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)  /* more or less */
+#define WLAN_MOD_INC_USE_COUNT	MOD_INC_USE_COUNT
+#define WLAN_MOD_DEC_USE_COUNT	MOD_DEC_USE_COUNT
+#else
+#define WLAN_MOD_INC_USE_COUNT
+#define WLAN_MOD_DEC_USE_COUNT
+#endif
+
+#ifndef ARPHRD_IEEE80211_PRISM
+#define ARPHRD_IEEE80211_PRISM 802
+#endif
+
+#define ETH_P_80211_RAW         (ETH_P_ECONET + 1)
+
+/*============================================================================*
+ * Constants                                                                  *
+ *============================================================================*/
+
+#define WLAN_IEEE_OUI_LEN     3
+
+#define WLAN_ETHCONV_ENCAP    1
+#define WLAN_ETHCONV_RFC1042  2
+#define WLAN_ETHCONV_8021h    3
+
+#define WLAN_MIN_ETHFRM_LEN   60
+#define WLAN_MAX_ETHFRM_LEN   1514
+#define WLAN_ETHHDR_LEN       14
+
+/*============================================================================*
+ * Types                                                                      *
+ *============================================================================*/
+
+/* local ether header type */
+typedef struct wlan_ethhdr {
+	u8	daddr[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	saddr[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u16	type __WLAN_ATTRIB_PACK__;
+} wlan_ethhdr_t;
+
+/* local llc header type */
+typedef struct wlan_llc {
+	u8	dsap __WLAN_ATTRIB_PACK__;
+	u8	ssap __WLAN_ATTRIB_PACK__;
+	u8	ctl __WLAN_ATTRIB_PACK__;
+} wlan_llc_t;
+
+/* local snap header type */
+typedef struct wlan_snap {
+	u8	oui[WLAN_IEEE_OUI_LEN] __WLAN_ATTRIB_PACK__;
+	u16	type __WLAN_ATTRIB_PACK__;
+} wlan_snap_t;
diff -urN oldtree/drivers/net/wireless/tiacx/wlan_hdr.h newtree/drivers/net/wireless/tiacx/wlan_hdr.h
--- oldtree/drivers/net/wireless/tiacx/wlan_hdr.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/wlan_hdr.h	2006-02-13 19:20:00.551428472 -0500
@@ -0,0 +1,498 @@
+/***********************************************************************
+** Copyright (C) 2003  ACX100 Open Source Project
+**
+** The contents of this file are subject to the Mozilla Public
+** License Version 1.1 (the "License"); you may not use this file
+** except in compliance with the License. You may obtain a copy of
+** the License at http://www.mozilla.org/MPL/
+**
+** Software distributed under the License is distributed on an "AS
+** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+** implied. See the License for the specific language governing
+** rights and limitations under the License.
+**
+** Alternatively, the contents of this file may be used under the
+** terms of the GNU Public License version 2 (the "GPL"), in which
+** case the provisions of the GPL are applicable instead of the
+** above.  If you wish to allow the use of your version of this file
+** only under the terms of the GPL and not to allow others to use
+** your version of this file under the MPL, indicate your decision
+** by deleting the provisions above and replace them with the notice
+** and other provisions required by the GPL.  If you do not delete
+** the provisions above, a recipient may use your version of this
+** file under either the MPL or the GPL.
+** ---------------------------------------------------------------------
+** Inquiries regarding the ACX100 Open Source Project can be
+** made directly to:
+**
+** acx100-users@lists.sf.net
+** http://acx100.sf.net
+** ---------------------------------------------------------------------
+*/
+
+/***********************************************************************
+** This code is based on elements which are
+** Copyright (C) 1999 AbsoluteValue Systems, Inc.  All Rights Reserved.
+** info@linux-wlan.com
+** http://www.linux-wlan.com
+*/
+
+/* mini-doc
+
+Here are all 11b/11g/11a rates and modulations:
+
+     11b 11g 11a
+     --- --- ---
+ 1  |B  |B  |
+ 2  |Q  |Q  |
+ 5.5|Cp |C p|
+ 6  |   |Od |O
+ 9  |   |od |o
+11  |Cp |C p|
+12  |   |Od |O
+18  |   |od |o
+22  |   |  p|
+24  |   |Od |O
+33  |   |  p|
+36  |   |od |o
+48  |   |od |o
+54  |   |od |o
+
+Mandatory:
+ B - DBPSK (Differential Binary Phase Shift Keying)
+ Q - DQPSK (Differential Quaternary Phase Shift Keying)
+ C - CCK (Complementary Code Keying, a form of DSSS
+		(Direct Sequence Spread Spectrum) modulation)
+ O - OFDM (Orthogonal Frequency Division Multiplexing)
+Optional:
+ o - OFDM
+ d - CCK-OFDM (also known as DSSS-OFDM)
+ p - PBCC (Packet Binary Convolutional Coding)
+
+The term CCK-OFDM may be used interchangeably with DSSS-OFDM
+(the IEEE 802.11g-2003 standard uses the latter terminology).
+In the CCK-OFDM, the PLCP header of the frame uses the CCK form of DSSS,
+while the PLCP payload (the MAC frame) is modulated using OFDM.
+
+Basically, you must use CCK-OFDM if you have mixed 11b/11g environment,
+or else (pure OFDM) 11b equipment may not realize that AP
+is sending a packet and start sending its own one.
+Sadly, looks like acx111 does not support CCK-OFDM, only pure OFDM.
+
+Re PBCC: avoid using it. It makes sense only if you have
+TI "11b+" hardware. You _must_ use PBCC in order to reach 22Mbps on it.
+
+Preambles:
+
+Long preamble (at 1Mbit rate, takes 144 us):
+    16 bytes	ones
+     2 bytes	0xF3A0 (lsb sent first)
+PLCP header follows (at 1Mbit also):
+     1 byte	Signal: speed, in 0.1Mbit units, except for:
+		33Mbit: 33 (instead of 330 - doesn't fit in octet)
+		all CCK-OFDM rates: 30
+     1 byte	Service
+	0,1,4:	reserved
+	2:	1=locked clock
+	3:	1=PBCC
+	5:	Length Extension (PBCC 22,33Mbit (11g only))  <-
+	6:	Length Extension (PBCC 22,33Mbit (11g only))  <- BLACK MAGIC HERE
+	7:	Length Extension                              <-
+     2 bytes	Length (time needed to tx this frame)
+		a) 5.5 Mbit/s CCK
+		   Length = octets*8/5.5, rounded up to integer
+		b) 11 Mbit/s CCK
+		   Length = octets*8/11, rounded up to integer
+		   Service bit 7:
+			0 = rounding took less than 8/11
+			1 = rounding took more than or equal to 8/11
+		c) 5.5 Mbit/s PBCC
+		   Length = (octets+1)*8/5.5, rounded up to integer
+		d) 11 Mbit/s PBCC
+		   Length = (octets+1)*8/11, rounded up to integer
+		   Service bit 7:
+			0 = rounding took less than 8/11
+			1 = rounding took more than or equal to 8/11
+		e) 22 Mbit/s PBCC
+		   Length = (octets+1)*8/22, rounded up to integer
+		   Service bits 6,7:
+			00 = rounding took less than 8/22ths
+			01 = rounding took 8/22...15/22ths
+			10 = rounding took 16/22ths or more.
+		f) 33 Mbit/s PBCC
+		   Length = (octets+1)*8/33, rounded up to integer
+		   Service bits 5,6,7:
+			000 rounding took less than 8/33
+			001 rounding took 8/33...15/33
+			010 rounding took 16/33...23/33
+			011 rounding took 24/33...31/33
+			100 rounding took 32/33 or more
+     2 bytes	CRC
+
+PSDU follows (up to 2346 bytes at selected rate)
+
+While Signal value alone is not enough to determine rate and modulation,
+Signal+Service is always sufficient.
+
+Short preamble (at 1Mbit rate, takes 72 us):
+     7 bytes	zeroes
+     2 bytes	0x05CF (lsb sent first)
+PLCP header follows *at 2Mbit/s*. Format is the same as in long preamble.
+PSDU follows (up to 2346 bytes at selected rate)
+
+OFDM preamble is completely different, uses OFDM
+modulation from the start and thus easily identifiable.
+Not shown here.
+*/
+
+
+/***********************************************************************
+** Constants
+*/
+
+/*--- Sizes -----------------------------------------------*/
+#define WLAN_CRC_LEN			4
+#define WLAN_BSS_TS_LEN			8
+#define WLAN_HDR_A3_LEN			24
+#define WLAN_HDR_A4_LEN			30
+#define WLAN_SSID_MAXLEN		32
+#define WLAN_DATA_MAXLEN		2312
+/* NB: these are sizes of unencrypted frames.
+** WEPed frames also have IV in front of encrypted data and ICV after it
+*/
+#define WLAN_A3FR_MAXLEN		(WLAN_HDR_A3_LEN + WLAN_DATA_MAXLEN + WLAN_CRC_LEN)
+#define WLAN_A4FR_MAXLEN		(WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN + WLAN_CRC_LEN)
+#define WLAN_BEACON_FR_MAXLEN		(WLAN_HDR_A3_LEN + 334)
+#define WLAN_ATIM_FR_MAXLEN		(WLAN_HDR_A3_LEN + 0)
+#define WLAN_DISASSOC_FR_MAXLEN		(WLAN_HDR_A3_LEN + 2)
+#define WLAN_ASSOCREQ_FR_MAXLEN		(WLAN_HDR_A3_LEN + 48)
+#define WLAN_ASSOCRESP_FR_MAXLEN	(WLAN_HDR_A3_LEN + 16)
+#define WLAN_REASSOCREQ_FR_MAXLEN	(WLAN_HDR_A3_LEN + 54)
+#define WLAN_REASSOCRESP_FR_MAXLEN	(WLAN_HDR_A3_LEN + 16)
+#define WLAN_PROBEREQ_FR_MAXLEN		(WLAN_HDR_A3_LEN + 44)
+#define WLAN_PROBERESP_FR_MAXLEN	(WLAN_HDR_A3_LEN + 78)
+#define WLAN_AUTHEN_FR_MAXLEN		(WLAN_HDR_A3_LEN + 261)
+#define WLAN_DEAUTHEN_FR_MAXLEN		(WLAN_HDR_A3_LEN + 2)
+#define WLAN_WEP_NKEYS			4
+#define WLAN_WEP_MAXKEYLEN		13
+#define WLAN_CHALLENGE_IE_LEN		130
+#define WLAN_CHALLENGE_LEN		128
+/* IV structure:
+** 3 bytes: Initialization Vector (24 bits)
+** 1 byte: 0..5: padding, must be 0; 6..7: key selector (0-3)
+*/
+#define WLAN_WEP_IV_LEN			4
+#define WLAN_WEP_ICV_LEN		4
+#define WLAN_A3FR_MAXLEN_WEP		(WLAN_A3FR_MAXLEN + 8)
+#define WLAN_A4FR_MAXLEN_WEP		(WLAN_A4FR_MAXLEN + 8)
+
+/*--- Frame Control Field -------------------------------------*/
+/* Frame Types */
+#define WLAN_FTYPE_MGMT			0x00
+#define WLAN_FTYPE_CTL			0x01
+#define WLAN_FTYPE_DATA			0x02
+
+/* Frame subtypes */
+/* Management */
+#define WLAN_FSTYPE_ASSOCREQ		0x00
+#define WLAN_FSTYPE_ASSOCRESP		0x01
+#define WLAN_FSTYPE_REASSOCREQ		0x02
+#define WLAN_FSTYPE_REASSOCRESP		0x03
+#define WLAN_FSTYPE_PROBEREQ		0x04
+#define WLAN_FSTYPE_PROBERESP		0x05
+#define WLAN_FSTYPE_BEACON		0x08
+#define WLAN_FSTYPE_ATIM		0x09
+#define WLAN_FSTYPE_DISASSOC		0x0a
+#define WLAN_FSTYPE_AUTHEN		0x0b
+#define WLAN_FSTYPE_DEAUTHEN		0x0c
+
+/* Control */
+#define WLAN_FSTYPE_PSPOLL		0x0a
+#define WLAN_FSTYPE_RTS			0x0b
+#define WLAN_FSTYPE_CTS			0x0c
+#define WLAN_FSTYPE_ACK			0x0d
+#define WLAN_FSTYPE_CFEND		0x0e
+#define WLAN_FSTYPE_CFENDCFACK		0x0f
+
+/* Data */
+#define WLAN_FSTYPE_DATAONLY		0x00
+#define WLAN_FSTYPE_DATA_CFACK		0x01
+#define WLAN_FSTYPE_DATA_CFPOLL		0x02
+#define WLAN_FSTYPE_DATA_CFACK_CFPOLL	0x03
+#define WLAN_FSTYPE_NULL		0x04
+#define WLAN_FSTYPE_CFACK		0x05
+#define WLAN_FSTYPE_CFPOLL		0x06
+#define WLAN_FSTYPE_CFACK_CFPOLL	0x07
+
+/*--- FC Constants v. 2.0 ------------------------------------*/
+/* Each constant is defined twice: WF_CONST is in host        */
+/* byteorder, WF_CONSTi is in ieee byteorder.                 */
+/* Usage:                                                     */
+/* printf("the frame subtype is %X", WF_FC_FTYPEi & rx.fc);   */
+/* tx.fc = WF_FTYPE_CTLi | WF_FSTYPE_RTSi;                    */
+/*------------------------------------------------------------*/
+
+enum {
+/*--- Frame Control Field -------------------------------------*/
+/* Protocol version: always 0 for current 802.11 standards */
+IEEE16(WF_FC_PVER,			0x0003)
+IEEE16(WF_FC_FTYPE,			0x000c)
+IEEE16(WF_FC_FSTYPE,			0x00f0)
+IEEE16(WF_FC_TODS,			0x0100)
+IEEE16(WF_FC_FROMDS,			0x0200)
+IEEE16(WF_FC_FROMTODS,			0x0300)
+IEEE16(WF_FC_MOREFRAG,			0x0400)
+IEEE16(WF_FC_RETRY,			0x0800)
+/* Indicates PS mode in which STA will be after successful completion
+** of current frame exchange sequence. Always 0 for AP frames */
+IEEE16(WF_FC_PWRMGT,			0x1000)
+/* What MoreData=1 means:
+** From AP to STA in PS mode: don't sleep yet, I have more frames for you
+** From Contention-Free (CF) Pollable STA in response to a CF-Poll:
+**   STA has buffered frames for transmission in response to next CF-Poll
+** Bcast/mcast frames transmitted from AP:
+**   when additional bcast/mcast frames remain to be transmitted by AP
+**   during this beacon interval
+** In all other cases MoreData=0 */
+IEEE16(WF_FC_MOREDATA,			0x2000)
+IEEE16(WF_FC_ISWEP,			0x4000)
+IEEE16(WF_FC_ORDER,			0x8000)
+
+/* Frame Types */
+IEEE16(WF_FTYPE_MGMT,			0x00)
+IEEE16(WF_FTYPE_CTL,			0x04)
+IEEE16(WF_FTYPE_DATA,			0x08)
+
+/* Frame subtypes */
+/* Management */
+IEEE16(WF_FSTYPE_ASSOCREQ,		0x00)
+IEEE16(WF_FSTYPE_ASSOCRESP,		0x10)
+IEEE16(WF_FSTYPE_REASSOCREQ,		0x20)
+IEEE16(WF_FSTYPE_REASSOCRESP,		0x30)
+IEEE16(WF_FSTYPE_PROBEREQ,		0x40)
+IEEE16(WF_FSTYPE_PROBERESP,		0x50)
+IEEE16(WF_FSTYPE_BEACON,		0x80)
+IEEE16(WF_FSTYPE_ATIM,			0x90)
+IEEE16(WF_FSTYPE_DISASSOC,		0xa0)
+IEEE16(WF_FSTYPE_AUTHEN,		0xb0)
+IEEE16(WF_FSTYPE_DEAUTHEN,		0xc0)
+
+/* Control */
+IEEE16(WF_FSTYPE_PSPOLL,		0xa0)
+IEEE16(WF_FSTYPE_RTS,			0xb0)
+IEEE16(WF_FSTYPE_CTS,			0xc0)
+IEEE16(WF_FSTYPE_ACK,			0xd0)
+IEEE16(WF_FSTYPE_CFEND,			0xe0)
+IEEE16(WF_FSTYPE_CFENDCFACK,		0xf0)
+
+/* Data */
+IEEE16(WF_FSTYPE_DATAONLY,		0x00)
+IEEE16(WF_FSTYPE_DATA_CFACK,		0x10)
+IEEE16(WF_FSTYPE_DATA_CFPOLL,		0x20)
+IEEE16(WF_FSTYPE_DATA_CFACK_CFPOLL,	0x30)
+IEEE16(WF_FSTYPE_NULL,			0x40)
+IEEE16(WF_FSTYPE_CFACK,			0x50)
+IEEE16(WF_FSTYPE_CFPOLL,		0x60)
+IEEE16(WF_FSTYPE_CFACK_CFPOLL,		0x70)
+};
+
+
+/***********************************************************************
+** Macros
+*/
+
+/*--- Duration Macros ----------------------------------------*/
+/* Macros to get/set the bitfields of the Duration Field      */
+/*  - the duration value is only valid when bit15 is zero     */
+/*  - the firmware handles these values, so I'm not going     */
+/*    these macros right now.                                 */
+/*------------------------------------------------------------*/
+
+/*--- Sequence Control  Macros -------------------------------*/
+/* Macros to get/set the bitfields of the Sequence Control    */
+/* Field.                                                     */
+/*------------------------------------------------------------*/
+#define WLAN_GET_SEQ_FRGNUM(n) (((u16)(n)) & (BIT0|BIT1|BIT2|BIT3))
+#define WLAN_GET_SEQ_SEQNUM(n) ((((u16)(n)) & (~(BIT0|BIT1|BIT2|BIT3))) >> 4)
+
+/*--- Data ptr macro -----------------------------------------*/
+/* Creates a u8* to the data portion of a frame               */
+/* Assumes you're passing in a ptr to the beginning of the hdr*/
+/*------------------------------------------------------------*/
+#define WLAN_HDR_A3_DATAP(p) (((u8*)(p)) + WLAN_HDR_A3_LEN)
+#define WLAN_HDR_A4_DATAP(p) (((u8*)(p)) + WLAN_HDR_A4_LEN)
+
+
+/***********************************************************************
+** Types
+*/
+
+/* BSS Timestamp */
+typedef u8 wlan_bss_ts_t[WLAN_BSS_TS_LEN];
+
+/* 802.11 header type
+**
+** Note the following:
+** a1 *always* is receiver's mac or bcast/mcast
+** a2 *always* is transmitter's mac, if a2 exists
+** seq: [0:3] frag#, [4:15] seq# - used for dup detection
+** (dups from retries have same seq#) */
+typedef struct wlan_hdr {
+	u16	fc __WLAN_ATTRIB_PACK__;
+	u16	dur __WLAN_ATTRIB_PACK__;
+	u8	a1[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	a2[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	a3[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u16	seq __WLAN_ATTRIB_PACK__;
+	u8	a4[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+} wlan_hdr_t;
+
+/* Separate structs for use if frame type is known */
+typedef struct wlan_hdr_a3 {
+	u16	fc __WLAN_ATTRIB_PACK__;
+	u16	dur __WLAN_ATTRIB_PACK__;
+	u8	a1[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	a2[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	a3[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u16	seq __WLAN_ATTRIB_PACK__;
+} wlan_hdr_a3_t;
+
+typedef struct wlan_hdr_mgmt {
+	u16	fc __WLAN_ATTRIB_PACK__;
+	u16	dur __WLAN_ATTRIB_PACK__;
+	u8	da[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	sa[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u16	seq __WLAN_ATTRIB_PACK__;
+} wlan_hdr_mgmt_t;
+
+#ifdef NOT_NEEDED_YET
+typedef struct { /* ad-hoc peer->peer (to/from DS = 0/0) */
+	u16	fc __WLAN_ATTRIB_PACK__;
+	u16	dur __WLAN_ATTRIB_PACK__;
+	u8	da[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	sa[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u16	seq __WLAN_ATTRIB_PACK__;
+} ibss;
+typedef struct { /* ap->sta (to/from DS = 0/1) */
+	u16	fc __WLAN_ATTRIB_PACK__;
+	u16	dur __WLAN_ATTRIB_PACK__;
+	u8	da[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	sa[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u16	seq __WLAN_ATTRIB_PACK__;
+} fromap;
+typedef struct { /* sta->ap (to/from DS = 1/0) */
+	u16	fc __WLAN_ATTRIB_PACK__;
+	u16	dur __WLAN_ATTRIB_PACK__;
+	u8	bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	sa[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	da[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u16	seq __WLAN_ATTRIB_PACK__;
+} toap;
+typedef struct { /* wds->wds (to/from DS = 1/1), the only 4addr pkt */
+	u16	fc __WLAN_ATTRIB_PACK__;
+	u16	dur __WLAN_ATTRIB_PACK__;
+	u8	ra[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	ta[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	da[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u16	seq __WLAN_ATTRIB_PACK__;
+	u8	sa[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+} wds;
+typedef struct { /* all management packets */
+	u16	fc __WLAN_ATTRIB_PACK__;
+	u16	dur __WLAN_ATTRIB_PACK__;
+	u8	da[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	sa[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u16	seq __WLAN_ATTRIB_PACK__;
+} mgmt;
+typedef struct { /* has no body, just a FCS */
+	u16	fc __WLAN_ATTRIB_PACK__;
+	u16	dur __WLAN_ATTRIB_PACK__;
+	u8	ra[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	ta[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+} rts;
+typedef struct { /* has no body, just a FCS */
+	u16	fc __WLAN_ATTRIB_PACK__;
+	u16	dur __WLAN_ATTRIB_PACK__;
+	u8	ra[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+} cts;
+typedef struct { /* has no body, just a FCS */
+	u16	fc __WLAN_ATTRIB_PACK__;
+	u16	dur __WLAN_ATTRIB_PACK__;
+	u8	ra[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+} ack;
+typedef struct { /* has no body, just a FCS */
+	u16	fc __WLAN_ATTRIB_PACK__;
+	/* NB: this one holds Assoc ID in dur field: */
+	u16	aid __WLAN_ATTRIB_PACK__;
+	u8	bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	ta[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+} pspoll;
+typedef struct { /* has no body, just a FCS */
+	u16	fc __WLAN_ATTRIB_PACK__;
+	u16	dur __WLAN_ATTRIB_PACK__;
+	u8	ra[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+} cfend;
+typedef struct { /* has no body, just a FCS */
+	u16	fc __WLAN_ATTRIB_PACK__;
+	u16	dur __WLAN_ATTRIB_PACK__;
+	u8	ra[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	u8	bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+} cfendcfack;
+#endif
+
+/* Prism header emulation (monitor mode) */
+typedef struct wlanitem_u32 {
+	u32	did __WLAN_ATTRIB_PACK__;
+	u16	status __WLAN_ATTRIB_PACK__;
+	u16	len __WLAN_ATTRIB_PACK__;
+	u32	data __WLAN_ATTRIB_PACK__;
+} wlanitem_u32_t;
+#define WLANITEM_STATUS_data_ok			0
+#define WLANITEM_STATUS_no_value		1
+#define WLANITEM_STATUS_invalid_itemname	2
+#define WLANITEM_STATUS_invalid_itemdata	3
+#define WLANITEM_STATUS_missing_itemdata	4
+#define WLANITEM_STATUS_incomplete_itemdata	5
+#define WLANITEM_STATUS_invalid_msg_did		6
+#define WLANITEM_STATUS_invalid_mib_did		7
+#define WLANITEM_STATUS_missing_conv_func	8
+#define WLANITEM_STATUS_string_too_long		9
+#define WLANITEM_STATUS_data_out_of_range	10
+#define WLANITEM_STATUS_string_too_short	11
+#define WLANITEM_STATUS_missing_valid_func	12
+#define WLANITEM_STATUS_unknown			13
+#define WLANITEM_STATUS_invalid_did		14
+#define WLANITEM_STATUS_missing_print_func	15
+
+#define WLAN_DEVNAMELEN_MAX	16
+typedef struct wlansniffrm {
+	u32		msgcode __WLAN_ATTRIB_PACK__;
+	u32		msglen __WLAN_ATTRIB_PACK__;
+	u8		devname[WLAN_DEVNAMELEN_MAX] __WLAN_ATTRIB_PACK__;
+	wlanitem_u32_t	hosttime __WLAN_ATTRIB_PACK__;
+	wlanitem_u32_t	mactime __WLAN_ATTRIB_PACK__;
+	wlanitem_u32_t	channel __WLAN_ATTRIB_PACK__;
+	wlanitem_u32_t	rssi __WLAN_ATTRIB_PACK__;
+	wlanitem_u32_t	sq __WLAN_ATTRIB_PACK__;
+	wlanitem_u32_t	signal __WLAN_ATTRIB_PACK__;
+	wlanitem_u32_t	noise __WLAN_ATTRIB_PACK__;
+	wlanitem_u32_t	rate __WLAN_ATTRIB_PACK__;
+	wlanitem_u32_t	istx __WLAN_ATTRIB_PACK__;	/* tx? 0:no 1:yes */
+	wlanitem_u32_t	frmlen __WLAN_ATTRIB_PACK__;
+} wlansniffrm_t;
+#define WLANSNIFFFRM		0x0041
+#define WLANSNIFFFRM_hosttime	0x1041
+#define WLANSNIFFFRM_mactime	0x2041
+#define WLANSNIFFFRM_channel	0x3041
+#define WLANSNIFFFRM_rssi	0x4041
+#define WLANSNIFFFRM_sq		0x5041
+#define WLANSNIFFFRM_signal	0x6041
+#define WLANSNIFFFRM_noise	0x7041
+#define WLANSNIFFFRM_rate	0x8041
+#define WLANSNIFFFRM_istx	0x9041
+#define WLANSNIFFFRM_frmlen	0xA041
diff -urN oldtree/drivers/net/wireless/tiacx/wlan_mgmt.h newtree/drivers/net/wireless/tiacx/wlan_mgmt.h
--- oldtree/drivers/net/wireless/tiacx/wlan_mgmt.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/net/wireless/tiacx/wlan_mgmt.h	2006-02-13 19:20:00.552428320 -0500
@@ -0,0 +1,580 @@
+/***********************************************************************
+** Copyright (C) 2003  ACX100 Open Source Project
+**
+** The contents of this file are subject to the Mozilla Public
+** License Version 1.1 (the "License"); you may not use this file
+** except in compliance with the License. You may obtain a copy of
+** the License at http://www.mozilla.org/MPL/
+**
+** Software distributed under the License is distributed on an "AS
+** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+** implied. See the License for the specific language governing
+** rights and limitations under the License.
+**
+** Alternatively, the contents of this file may be used under the
+** terms of the GNU Public License version 2 (the "GPL"), in which
+** case the provisions of the GPL are applicable instead of the
+** above.  If you wish to allow the use of your version of this file
+** only under the terms of the GPL and not to allow others to use
+** your version of this file under the MPL, indicate your decision
+** by deleting the provisions above and replace them with the notice
+** and other provisions required by the GPL.  If you do not delete
+** the provisions above, a recipient may use your version of this
+** file under either the MPL or the GPL.
+** ---------------------------------------------------------------------
+** Inquiries regarding the ACX100 Open Source Project can be
+** made directly to:
+**
+** acx100-users@lists.sf.net
+** http://acx100.sf.net
+** ---------------------------------------------------------------------
+*/
+
+/***********************************************************************
+** This code is based on elements which are
+** Copyright (C) 1999 AbsoluteValue Systems, Inc.  All Rights Reserved.
+** info@linux-wlan.com
+** http://www.linux-wlan.com
+*/
+
+/***********************************************************************
+** Constants
+*/
+
+/*-- Information Element IDs --------------------*/
+#define WLAN_EID_SSID		0
+#define WLAN_EID_SUPP_RATES	1
+#define WLAN_EID_FH_PARMS	2
+#define WLAN_EID_DS_PARMS	3
+#define WLAN_EID_CF_PARMS	4
+#define WLAN_EID_TIM		5
+#define WLAN_EID_IBSS_PARMS	6
+#define WLAN_EID_COUNTRY	7 /* 802.11d */
+#define WLAN_EID_FH_HOP_PARMS	8 /* 802.11d */
+#define WLAN_EID_FH_TABLE	9 /* 802.11d */
+#define WLAN_EID_REQUEST	10 /* 802.11d */
+/*-- values 11-15 reserved --*/
+#define WLAN_EID_CHALLENGE	16
+/*-- values 17-31 reserved for challenge text extension --*/
+#define WLAN_EID_ERP_INFO	42
+#define WLAN_EID_RSN		48
+#define WLAN_EID_EXT_RATES	50
+#define WLAN_EID_GENERIC	221
+
+#if 0
+#define WLAN_EID_PWR_CONSTRAINT		32	/* 11H PowerConstraint */
+#define WLAN_EID_PWR_CAP		33	/* 11H PowerCapability */
+#define WLAN_EID_TPC_REQUEST		34	/* 11H TPC Request */
+#define WLAN_EID_TPC_REPORT		35	/* 11H TPC Report */
+#define WLAN_EID_SUPP_CHANNELS		36	/* 11H Supported Channels */
+#define WLAN_EID_CHANNEL_SWITCH		37	/* 11H ChannelSwitch */
+#define WLAN_EID_MEASURE_REQUEST	38	/* 11H MeasurementRequest */
+#define WLAN_EID_MEASURE_REPORT		39	/* 11H MeasurementReport */
+#define WLAN_EID_QUIET_ID		40	/* 11H Quiet */
+#define WLAN_EID_IBSS_DFS_ID		41	/* 11H IBSS_DFS */
+#define WLAN_EID_NONERP_ID		47
+#endif
+
+/*-- Reason Codes -------------------------------*/
+#define WLAN_MGMT_REASON_RSVD			0
+#define WLAN_MGMT_REASON_UNSPEC			1
+#define WLAN_MGMT_REASON_PRIOR_AUTH_INVALID	2
+#define WLAN_MGMT_REASON_DEAUTH_LEAVING		3
+#define WLAN_MGMT_REASON_DISASSOC_INACTIVE	4
+#define WLAN_MGMT_REASON_DISASSOC_AP_BUSY	5
+#define WLAN_MGMT_REASON_CLASS2_NONAUTH		6
+#define WLAN_MGMT_REASON_CLASS3_NONASSOC	7
+#define WLAN_MGMT_REASON_DISASSOC_STA_HASLEFT	8
+#define WLAN_MGMT_REASON_CANT_ASSOC_NONAUTH	9
+
+/*-- Status Codes -------------------------------*/
+#define WLAN_MGMT_STATUS_SUCCESS		0
+#define WLAN_MGMT_STATUS_UNSPEC_FAILURE		1
+#define WLAN_MGMT_STATUS_CAPS_UNSUPPORTED	10
+#define WLAN_MGMT_STATUS_REASSOC_NO_ASSOC	11
+#define WLAN_MGMT_STATUS_ASSOC_DENIED_UNSPEC	12
+#define WLAN_MGMT_STATUS_UNSUPPORTED_AUTHALG	13
+#define WLAN_MGMT_STATUS_RX_AUTH_NOSEQ		14
+#define WLAN_MGMT_STATUS_CHALLENGE_FAIL		15
+#define WLAN_MGMT_STATUS_AUTH_TIMEOUT		16
+#define WLAN_MGMT_STATUS_ASSOC_DENIED_BUSY	17
+#define WLAN_MGMT_STATUS_ASSOC_DENIED_RATES	18
+/* p80211b additions */
+#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOSHORT	19
+#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOPBCC	20
+#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOAGILITY	21
+
+/*-- Auth Algorithm Field ---------------------------*/
+#define WLAN_AUTH_ALG_OPENSYSTEM		0
+#define WLAN_AUTH_ALG_SHAREDKEY			1
+
+/*-- Management Frame Field Offsets -------------*/
+/* Note: Not all fields are listed because of variable lengths,   */
+/*       see the code in p80211.c to see how we search for fields */
+/* Note: These offsets are from the start of the frame data       */
+
+#define WLAN_BEACON_OFF_TS			0
+#define WLAN_BEACON_OFF_BCN_INT			8
+#define WLAN_BEACON_OFF_CAPINFO			10
+#define WLAN_BEACON_OFF_SSID			12
+
+#define WLAN_DISASSOC_OFF_REASON		0
+
+#define WLAN_ASSOCREQ_OFF_CAP_INFO		0
+#define WLAN_ASSOCREQ_OFF_LISTEN_INT		2
+#define WLAN_ASSOCREQ_OFF_SSID			4
+
+#define WLAN_ASSOCRESP_OFF_CAP_INFO		0
+#define WLAN_ASSOCRESP_OFF_STATUS		2
+#define WLAN_ASSOCRESP_OFF_AID			4
+#define WLAN_ASSOCRESP_OFF_SUPP_RATES		6
+
+#define WLAN_REASSOCREQ_OFF_CAP_INFO		0
+#define WLAN_REASSOCREQ_OFF_LISTEN_INT		2
+#define WLAN_REASSOCREQ_OFF_CURR_AP		4
+#define WLAN_REASSOCREQ_OFF_SSID		10
+
+#define WLAN_REASSOCRESP_OFF_CAP_INFO		0
+#define WLAN_REASSOCRESP_OFF_STATUS		2
+#define WLAN_REASSOCRESP_OFF_AID		4
+#define WLAN_REASSOCRESP_OFF_SUPP_RATES		6
+
+#define WLAN_PROBEREQ_OFF_SSID			0
+
+#define WLAN_PROBERESP_OFF_TS			0
+#define WLAN_PROBERESP_OFF_BCN_INT		8
+#define WLAN_PROBERESP_OFF_CAP_INFO		10
+#define WLAN_PROBERESP_OFF_SSID			12
+
+#define WLAN_AUTHEN_OFF_AUTH_ALG		0
+#define WLAN_AUTHEN_OFF_AUTH_SEQ		2
+#define WLAN_AUTHEN_OFF_STATUS			4
+#define WLAN_AUTHEN_OFF_CHALLENGE		6
+
+#define WLAN_DEAUTHEN_OFF_REASON		0
+
+enum {
+IEEE16(WF_MGMT_CAP_ESS,		0x0001)
+IEEE16(WF_MGMT_CAP_IBSS,	0x0002)
+/* In (re)assoc request frames by STA:
+** Pollable=0, PollReq=0: STA is not CF-Pollable
+** 0 1: STA is CF-Pollable, not requesting to be placed on the CF-Polling list
+** 1 0: STA is CF-Pollable, requesting to be placed on the CF-Polling list
+** 1 1: STA is CF-Pollable, requesting never to be polled
+** In beacon, proberesp, (re)assoc resp frames by AP:
+** 0 0: No point coordinator at AP
+** 0 1: Point coordinator at AP for delivery only (no polling)
+** 1 0: Point coordinator at AP for delivery and polling
+** 1 1: Reserved  */
+IEEE16(WF_MGMT_CAP_CFPOLLABLE,	0x0004)
+IEEE16(WF_MGMT_CAP_CFPOLLREQ,	0x0008)
+/* 1=non-WEP data frames are disallowed */
+IEEE16(WF_MGMT_CAP_PRIVACY,	0x0010)
+/* In beacon,  proberesp, (re)assocresp by AP/AdHoc:
+** 1=use of shortpre is allowed ("I can receive shortpre") */
+IEEE16(WF_MGMT_CAP_SHORT,	0x0020)
+IEEE16(WF_MGMT_CAP_PBCC,	0x0040)
+IEEE16(WF_MGMT_CAP_AGILITY,	0x0080)
+/* In (re)assoc request frames by STA:
+** 1=short slot time implemented and enabled
+**   NB: AP shall use long slot time beginning at the next Beacon after assoc
+**   of STA with this bit set to 0
+** In beacon, proberesp, (re)assoc resp frames by AP:
+** currently used slot time value: 0/1 - long/short */
+IEEE16(WF_MGMT_CAP_SHORTSLOT,	0x0400)
+/* In (re)assoc request frames by STA: 1=CCK-OFDM is implemented and enabled
+** In beacon, proberesp, (re)assoc resp frames by AP/AdHoc:
+** 1=CCK-OFDM is allowed */
+IEEE16(WF_MGMT_CAP_CCKOFDM,	0x2000)
+};
+
+
+/***********************************************************************
+** Types
+*/
+
+/* Information Element types */
+
+/* prototype structure, all IEs start with these members */
+typedef struct wlan_ie {
+	u8 eid __WLAN_ATTRIB_PACK__;
+	u8 len __WLAN_ATTRIB_PACK__;
+} wlan_ie_t;
+
+/*-- Service Set Identity (SSID)  -----------------*/
+typedef struct wlan_ie_ssid {
+	u8 eid __WLAN_ATTRIB_PACK__;
+	u8 len __WLAN_ATTRIB_PACK__;
+	u8 ssid[1] __WLAN_ATTRIB_PACK__;	/* may be zero */
+} wlan_ie_ssid_t;
+
+/*-- Supported Rates  -----------------------------*/
+typedef struct wlan_ie_supp_rates {
+	u8 eid __WLAN_ATTRIB_PACK__;
+	u8 len __WLAN_ATTRIB_PACK__;
+	u8 rates[1] __WLAN_ATTRIB_PACK__;	/* had better be at LEAST one! */
+} wlan_ie_supp_rates_t;
+
+/*-- FH Parameter Set  ----------------------------*/
+typedef struct wlan_ie_fh_parms {
+	u8 eid __WLAN_ATTRIB_PACK__;
+	u8 len __WLAN_ATTRIB_PACK__;
+	u16 dwell __WLAN_ATTRIB_PACK__;
+	u8 hopset __WLAN_ATTRIB_PACK__;
+	u8 hoppattern __WLAN_ATTRIB_PACK__;
+	u8 hopindex __WLAN_ATTRIB_PACK__;
+} wlan_ie_fh_parms_t;
+
+/*-- DS Parameter Set  ----------------------------*/
+typedef struct wlan_ie_ds_parms {
+	u8 eid __WLAN_ATTRIB_PACK__;
+	u8 len __WLAN_ATTRIB_PACK__;
+	u8 curr_ch __WLAN_ATTRIB_PACK__;
+} wlan_ie_ds_parms_t;
+
+/*-- CF Parameter Set  ----------------------------*/
+typedef struct wlan_ie_cf_parms {
+	u8 eid __WLAN_ATTRIB_PACK__;
+	u8 len __WLAN_ATTRIB_PACK__;
+	u8 cfp_cnt __WLAN_ATTRIB_PACK__;
+	u8 cfp_period __WLAN_ATTRIB_PACK__;
+	u16 cfp_maxdur __WLAN_ATTRIB_PACK__;
+	u16 cfp_durremaining __WLAN_ATTRIB_PACK__;
+} wlan_ie_cf_parms_t;
+
+/*-- TIM ------------------------------------------*/
+typedef struct wlan_ie_tim {
+	u8 eid __WLAN_ATTRIB_PACK__;
+	u8 len __WLAN_ATTRIB_PACK__;
+	u8 dtim_cnt __WLAN_ATTRIB_PACK__;
+	u8 dtim_period __WLAN_ATTRIB_PACK__;
+	u8 bitmap_ctl __WLAN_ATTRIB_PACK__;
+	u8 virt_bm[1] __WLAN_ATTRIB_PACK__;
+} wlan_ie_tim_t;
+
+/*-- IBSS Parameter Set ---------------------------*/
+typedef struct wlan_ie_ibss_parms {
+	u8 eid __WLAN_ATTRIB_PACK__;
+	u8 len __WLAN_ATTRIB_PACK__;
+	u16 atim_win __WLAN_ATTRIB_PACK__;
+} wlan_ie_ibss_parms_t;
+
+/*-- Challenge Text  ------------------------------*/
+typedef struct wlan_ie_challenge {
+	u8 eid __WLAN_ATTRIB_PACK__;
+	u8 len __WLAN_ATTRIB_PACK__;
+	u8 challenge[1] __WLAN_ATTRIB_PACK__;
+} wlan_ie_challenge_t;
+
+/*-- ERP (42) -------------------------------------*/
+typedef struct wlan_ie_erp {
+	u8 eid __WLAN_ATTRIB_PACK__;
+	u8 len __WLAN_ATTRIB_PACK__;
+	/* bit 0:Non ERP present
+	**     1:Use Protection
+	**     2:Barker Preamble mode
+	**     3-7:reserved */
+	u8 erp __WLAN_ATTRIB_PACK__;
+} wlan_ie_erp_t;
+
+/* Types for parsing mgmt frames */
+
+/* prototype structure, all mgmt frame types will start with these members */
+typedef struct wlan_fr_mgmt {
+	u16 type;
+	u16 len;		/* DOES NOT include CRC */
+	wlan_hdr_t *hdr;
+	/* used for target specific data, skb in Linux */
+	/*-- fixed fields -----------*/
+	/*-- info elements ----------*/
+} wlan_fr_mgmt_t;
+
+/*-- Beacon ---------------------------------------*/
+typedef struct wlan_fr_beacon {
+	u16 type;
+	u16 len;
+	wlan_hdr_t *hdr;
+	/*-- fixed fields -----------*/
+	u64 *ts;
+	u16 *bcn_int;
+	u16 *cap_info;
+	/*-- info elements ----------*/
+	wlan_ie_ssid_t *ssid;
+	wlan_ie_supp_rates_t *supp_rates;
+	wlan_ie_supp_rates_t *ext_rates;
+	wlan_ie_fh_parms_t *fh_parms;
+	wlan_ie_ds_parms_t *ds_parms;
+	wlan_ie_cf_parms_t *cf_parms;
+	wlan_ie_ibss_parms_t *ibss_parms;
+	wlan_ie_tim_t *tim;	/* in beacon only, not proberesp */
+	wlan_ie_erp_t *erp;	/* in beacon only, not proberesp */
+} wlan_fr_beacon_t;
+#define wlan_fr_proberesp wlan_fr_beacon
+#define wlan_fr_proberesp_t wlan_fr_beacon_t
+
+/*-- IBSS ATIM ------------------------------------*/
+typedef struct wlan_fr_ibssatim {
+	u16 type;
+	u16 len;
+	wlan_hdr_t *hdr;
+	/*-- fixed fields -----------*/
+	/*-- info elements ----------*/
+	/* this frame type has a null body */
+} wlan_fr_ibssatim_t;
+
+/*-- Disassociation -------------------------------*/
+typedef struct wlan_fr_disassoc {
+	u16 type;
+	u16 len;
+	wlan_hdr_t *hdr;
+	/*-- fixed fields -----------*/
+	u16 *reason;
+	/*-- info elements ----------*/
+} wlan_fr_disassoc_t;
+
+/*-- Association Request --------------------------*/
+typedef struct wlan_fr_assocreq {
+	u16 type;
+	u16 len;
+	wlan_hdr_t *hdr;
+	/*-- fixed fields -----------*/
+	u16 *cap_info;
+	u16 *listen_int;
+	/*-- info elements ----------*/
+	wlan_ie_ssid_t *ssid;
+	wlan_ie_supp_rates_t *supp_rates;
+	wlan_ie_supp_rates_t *ext_rates;
+} wlan_fr_assocreq_t;
+
+/*-- Association Response -------------------------*/
+typedef struct wlan_fr_assocresp {
+	u16 type;
+	u16 len;
+	wlan_hdr_t *hdr;
+	/*-- fixed fields -----------*/
+	u16 *cap_info;
+	u16 *status;
+	u16 *aid;
+	/*-- info elements ----------*/
+	wlan_ie_supp_rates_t *supp_rates;
+	wlan_ie_supp_rates_t *ext_rates;
+} wlan_fr_assocresp_t;
+
+/*-- Reassociation Request ------------------------*/
+typedef struct wlan_fr_reassocreq {
+	u16 type;
+	u16 len;
+	wlan_hdr_t *hdr;
+	/*-- fixed fields -----------*/
+	u16 *cap_info;
+	u16 *listen_int;
+	u8 *curr_ap;
+	/*-- info elements ----------*/
+	wlan_ie_ssid_t *ssid;
+	wlan_ie_supp_rates_t *supp_rates;
+	wlan_ie_supp_rates_t *ext_rates;
+} wlan_fr_reassocreq_t;
+
+/*-- Reassociation Response -----------------------*/
+typedef struct wlan_fr_reassocresp {
+	u16 type;
+	u16 len;
+	wlan_hdr_t *hdr;
+	/*-- fixed fields -----------*/
+	u16 *cap_info;
+	u16 *status;
+	u16 *aid;
+	/*-- info elements ----------*/
+	wlan_ie_supp_rates_t *supp_rates;
+	wlan_ie_supp_rates_t *ext_rates;
+} wlan_fr_reassocresp_t;
+
+/*-- Probe Request --------------------------------*/
+typedef struct wlan_fr_probereq {
+	u16 type;
+	u16 len;
+	wlan_hdr_t *hdr;
+	/*-- fixed fields -----------*/
+	/*-- info elements ----------*/
+	wlan_ie_ssid_t *ssid;
+	wlan_ie_supp_rates_t *supp_rates;
+	wlan_ie_supp_rates_t *ext_rates;
+} wlan_fr_probereq_t;
+
+/*-- Authentication -------------------------------*/
+typedef struct wlan_fr_authen {
+	u16 type;
+	u16 len;
+	wlan_hdr_t *hdr;
+	/*-- fixed fields -----------*/
+	u16 *auth_alg;
+	u16 *auth_seq;
+	u16 *status;
+	/*-- info elements ----------*/
+	wlan_ie_challenge_t *challenge;
+} wlan_fr_authen_t;
+
+/*-- Deauthenication -----------------------------*/
+typedef struct wlan_fr_deauthen {
+	u16 type;
+	u16 len;
+	wlan_hdr_t *hdr;
+	/*-- fixed fields -----------*/
+	u16 *reason;
+	/*-- info elements ----------*/
+} wlan_fr_deauthen_t;
+
+/* Types for building mgmt frames */
+
+/* Warning. Several types used in below structs are
+** in fact variable length. Use structs with such fields with caution */
+typedef struct auth_frame_body {
+	u16	auth_alg __WLAN_ATTRIB_PACK__;
+	u16	auth_seq __WLAN_ATTRIB_PACK__;
+	u16	status __WLAN_ATTRIB_PACK__;
+	wlan_ie_challenge_t challenge __WLAN_ATTRIB_PACK__;
+} auth_frame_body_t;
+
+typedef struct assocresp_frame_body {
+	u16	cap_info __WLAN_ATTRIB_PACK__;
+	u16	status __WLAN_ATTRIB_PACK__;
+	u16	aid __WLAN_ATTRIB_PACK__;
+	wlan_ie_supp_rates_t rates __WLAN_ATTRIB_PACK__;
+} assocresp_frame_body_t;
+
+typedef struct reassocreq_frame_body {
+	u16	cap_info __WLAN_ATTRIB_PACK__;
+	u16	listen_int __WLAN_ATTRIB_PACK__;
+	u8	current_ap[ETH_ALEN] __WLAN_ATTRIB_PACK__;
+	wlan_ie_ssid_t ssid __WLAN_ATTRIB_PACK__;
+/* access to this one is disabled since ssid_t is variable length: */
+     /* wlan_ie_supp_rates_t rates __WLAN_ATTRIB_PACK__; */
+} reassocreq_frame_body_t;
+
+typedef struct reassocresp_frame_body {
+	u16	cap_info __WLAN_ATTRIB_PACK__;
+	u16	status __WLAN_ATTRIB_PACK__;
+	u16	aid __WLAN_ATTRIB_PACK__;
+	wlan_ie_supp_rates_t rates __WLAN_ATTRIB_PACK__;
+} reassocresp_frame_body_t;
+
+typedef struct deauthen_frame_body {
+	u16	reason __WLAN_ATTRIB_PACK__;
+} deauthen_frame_body_t;
+
+typedef struct disassoc_frame_body {
+	u16	reason __WLAN_ATTRIB_PACK__;
+} disassoc_frame_body_t;
+
+typedef struct probereq_frame_body {
+	wlan_ie_ssid_t ssid __WLAN_ATTRIB_PACK__;
+	wlan_ie_supp_rates_t rates __WLAN_ATTRIB_PACK__;
+} probereq_frame_body_t;
+
+typedef struct proberesp_frame_body {
+	u8	timestamp[8] __WLAN_ATTRIB_PACK__;
+	u16	beacon_int __WLAN_ATTRIB_PACK__;
+	u16	cap_info __WLAN_ATTRIB_PACK__;
+	wlan_ie_ssid_t ssid __WLAN_ATTRIB_PACK__;
+/* access to these is disabled since ssid_t is variable length: */
+     /* wlan_ie_supp_rates_t rates __WLAN_ATTRIB_PACK__; */
+     /* fhps_t	fhps __WLAN_ATTRIB_PACK__; */
+     /* dsps_t	dsps __WLAN_ATTRIB_PACK__; */
+     /* cfps_t	cfps __WLAN_ATTRIB_PACK__; */
+} proberesp_frame_body_t;
+
+
+/***********************************************************************
+** Functions
+*/
+
+/* Helpers for parsing mgmt frames */
+void wlan_mgmt_decode_ibssatim(wlan_fr_ibssatim_t *f);
+void wlan_mgmt_decode_assocreq(wlan_fr_assocreq_t *f);
+void wlan_mgmt_decode_assocresp(wlan_fr_assocresp_t *f);
+void wlan_mgmt_decode_authen(wlan_fr_authen_t *f);
+void wlan_mgmt_decode_beacon(wlan_fr_beacon_t *f);
+void wlan_mgmt_decode_deauthen(wlan_fr_deauthen_t *f);
+void wlan_mgmt_decode_disassoc(wlan_fr_disassoc_t *f);
+void wlan_mgmt_decode_probereq(wlan_fr_probereq_t *f);
+void wlan_mgmt_decode_proberesp(wlan_fr_proberesp_t *f);
+void wlan_mgmt_decode_reassocreq(wlan_fr_reassocreq_t *f);
+void wlan_mgmt_decode_reassocresp(wlan_fr_reassocresp_t *f);
+
+/* Helpers for building mgmt frames */
+static inline u8*
+wlan_fill_ie_ssid(u8 *p, int len, const char *ssid)
+{
+	struct wlan_ie_ssid *ie = (void*)p;
+	ie->eid = WLAN_EID_SSID;
+	ie->len = len;
+	memcpy(ie->ssid, ssid, len);
+	return p + len + 2;
+}
+/* This controls whether we create 802.11g 'ext supported rates' IEs
+** or just create overlong 'supported rates' IEs instead
+** (non-11g compliant) */
+#define WE_OBEY_802_11G 1
+static inline u8*
+wlan_fill_ie_rates(u8 *p, int len, const u8 *rates)
+{
+	struct wlan_ie_supp_rates *ie = (void*)p;
+#if WE_OBEY_802_11G
+	if (len > 8 ) len = 8;
+#endif
+	/* supported rates (1 to 8 octets) */
+	ie->eid = WLAN_EID_SUPP_RATES;
+	ie->len = len;
+	memcpy(ie->rates, rates, len);
+	return p + len + 2;
+}
+/* This one wouldn't create an IE at all if not needed */
+static inline u8*
+wlan_fill_ie_rates_ext(u8 *p, int len, const u8 *rates)
+{
+	struct wlan_ie_supp_rates *ie = (void*)p;
+#if !WE_OBEY_802_11G
+	return p;
+#endif
+	len -= 8;
+	if (len < 0) return p;
+	/* ext supported rates */
+	ie->eid = WLAN_EID_EXT_RATES;
+	ie->len = len;
+	memcpy(ie->rates, rates+8, len);
+	return p + len + 2;
+}
+static inline u8*
+wlan_fill_ie_ds_parms(u8 *p, int channel)
+{
+	struct wlan_ie_ds_parms *ie = (void*)p;
+	ie->eid = WLAN_EID_DS_PARMS;
+	ie->len = 1;
+	ie->curr_ch = channel;
+	return p + sizeof(*ie);
+}
+static inline u8*
+wlan_fill_ie_ibss_parms(u8 *p, int atim_win)
+{
+	struct wlan_ie_ibss_parms *ie = (void*)p;
+	ie->eid = WLAN_EID_IBSS_PARMS;
+	ie->len = 2;
+	ie->atim_win = atim_win;
+	return p + sizeof(*ie);
+}
+static inline u8*
+wlan_fill_ie_tim(u8 *p,	int rem, int period, int bcast,
+		int ofs, int len, const u8 *vbm)
+{
+	struct wlan_ie_tim *ie = (void*)p;
+	ie->eid = WLAN_EID_TIM;
+	ie->len = len + 3;
+	ie->dtim_cnt = rem;
+	ie->dtim_period = period;
+	ie->bitmap_ctl = ofs | (bcast!=0);
+	if (vbm)
+		memcpy(ie->virt_bm, vbm, len); /* min 1 byte */
+	else
+		ie->virt_bm[0] = 0;
+	return p + len + 3 + 2;
+}
diff -urN oldtree/drivers/pci/quirks.c newtree/drivers/pci/quirks.c
--- oldtree/drivers/pci/quirks.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/pci/quirks.c	2006-02-13 19:20:00.554428016 -0500
@@ -1125,6 +1125,9 @@
 	case 0x27c4:
 		ich = 7;
 		break;
+	case 0x2828:	/* ICH8M */
+		ich = 8;
+		break;
 	default:
 		/* we do not handle this PCI device */
 		return;
@@ -1144,7 +1147,7 @@
 		else
 			return;			/* not in combined mode */
 	} else {
-		WARN_ON((ich != 6) && (ich != 7));
+		WARN_ON((ich != 6) && (ich != 7) && (ich != 8));
 		tmp &= 0x3;  /* interesting bits 1:0 */
 		if (tmp & (1 << 0))
 			comb = (1 << 2);	/* PATA port 0, SATA port 1 */
diff -urN oldtree/drivers/pci/quirks.c.orig newtree/drivers/pci/quirks.c.orig
--- oldtree/drivers/pci/quirks.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/pci/quirks.c.orig	2006-02-13 19:20:00.558427408 -0500
@@ -0,0 +1,1318 @@
+/*
+ *  This file contains work-arounds for many known PCI hardware
+ *  bugs.  Devices present only on certain architectures (host
+ *  bridges et cetera) should be handled in arch-specific code.
+ *
+ *  Note: any quirks for hotpluggable devices must _NOT_ be declared __init.
+ *
+ *  Copyright (c) 1999 Martin Mares <mj@ucw.cz>
+ *
+ *  Init/reset quirks for USB host controllers should be in the
+ *  USB quirks file, where their drivers can access reuse it.
+ *
+ *  The bridge optimization stuff has been removed. If you really
+ *  have a silly BIOS which is unable to set your host bridge right,
+ *  use the PowerTweak utility (see http://powertweak.sourceforge.net).
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/acpi.h>
+#include "pci.h"
+
+/* Deal with broken BIOS'es that neglect to enable passive release,
+   which can cause problems in combination with the 82441FX/PPro MTRRs */
+static void __devinit quirk_passive_release(struct pci_dev *dev)
+{
+	struct pci_dev *d = NULL;
+	unsigned char dlc;
+
+	/* We have to make sure a particular bit is set in the PIIX3
+	   ISA bridge, so we have to go out and find it. */
+	while ((d = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, d))) {
+		pci_read_config_byte(d, 0x82, &dlc);
+		if (!(dlc & 1<<1)) {
+			printk(KERN_ERR "PCI: PIIX3: Enabling Passive Release on %s\n", pci_name(d));
+			dlc |= 1<<1;
+			pci_write_config_byte(d, 0x82, dlc);
+		}
+	}
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82441,	quirk_passive_release );
+
+/*  The VIA VP2/VP3/MVP3 seem to have some 'features'. There may be a workaround
+    but VIA don't answer queries. If you happen to have good contacts at VIA
+    ask them for me please -- Alan 
+    
+    This appears to be BIOS not version dependent. So presumably there is a 
+    chipset level fix */
+int isa_dma_bridge_buggy;		/* Exported */
+    
+static void __devinit quirk_isa_dma_hangs(struct pci_dev *dev)
+{
+	if (!isa_dma_bridge_buggy) {
+		isa_dma_bridge_buggy=1;
+		printk(KERN_INFO "Activating ISA DMA hang workarounds.\n");
+	}
+}
+	/*
+	 * Its not totally clear which chipsets are the problematic ones
+	 * We know 82C586 and 82C596 variants are affected.
+	 */
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_82C586_0,	quirk_isa_dma_hangs );
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_82C596,	quirk_isa_dma_hangs );
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82371SB_0,  quirk_isa_dma_hangs );
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL,	PCI_DEVICE_ID_AL_M1533, 	quirk_isa_dma_hangs );
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NEC,	PCI_DEVICE_ID_NEC_CBUS_1,	quirk_isa_dma_hangs );
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NEC,	PCI_DEVICE_ID_NEC_CBUS_2,	quirk_isa_dma_hangs );
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NEC,	PCI_DEVICE_ID_NEC_CBUS_3,	quirk_isa_dma_hangs );
+
+int pci_pci_problems;
+
+/*
+ *	Chipsets where PCI->PCI transfers vanish or hang
+ */
+static void __devinit quirk_nopcipci(struct pci_dev *dev)
+{
+	if ((pci_pci_problems & PCIPCI_FAIL)==0) {
+		printk(KERN_INFO "Disabling direct PCI/PCI transfers.\n");
+		pci_pci_problems |= PCIPCI_FAIL;
+	}
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_5597,		quirk_nopcipci );
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_496,		quirk_nopcipci );
+
+/*
+ *	Triton requires workarounds to be used by the drivers
+ */
+static void __devinit quirk_triton(struct pci_dev *dev)
+{
+	if ((pci_pci_problems&PCIPCI_TRITON)==0) {
+		printk(KERN_INFO "Limiting direct PCI/PCI transfers.\n");
+		pci_pci_problems |= PCIPCI_TRITON;
+	}
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 	PCI_DEVICE_ID_INTEL_82437, 	quirk_triton ); 
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 	PCI_DEVICE_ID_INTEL_82437VX, 	quirk_triton ); 
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 	PCI_DEVICE_ID_INTEL_82439, 	quirk_triton ); 
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 	PCI_DEVICE_ID_INTEL_82439TX, 	quirk_triton ); 
+
+/*
+ *	VIA Apollo KT133 needs PCI latency patch
+ *	Made according to a windows driver based patch by George E. Breese
+ *	see PCI Latency Adjust on http://www.viahardware.com/download/viatweak.shtm
+ *      Also see http://www.au-ja.org/review-kt133a-1-en.phtml for
+ *      the info on which Mr Breese based his work.
+ *
+ *	Updated based on further information from the site and also on
+ *	information provided by VIA 
+ */
+static void __devinit quirk_vialatency(struct pci_dev *dev)
+{
+	struct pci_dev *p;
+	u8 rev;
+	u8 busarb;
+	/* Ok we have a potential problem chipset here. Now see if we have
+	   a buggy southbridge */
+	   
+	p = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, NULL);
+	if (p!=NULL) {
+		pci_read_config_byte(p, PCI_CLASS_REVISION, &rev);
+		/* 0x40 - 0x4f == 686B, 0x10 - 0x2f == 686A; thanks Dan Hollis */
+		/* Check for buggy part revisions */
+		if (rev < 0x40 || rev > 0x42)
+			goto exit;
+	} else {
+		p = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8231, NULL);
+		if (p==NULL)	/* No problem parts */
+			goto exit;
+		pci_read_config_byte(p, PCI_CLASS_REVISION, &rev);
+		/* Check for buggy part revisions */
+		if (rev < 0x10 || rev > 0x12) 
+			goto exit;
+	}
+	
+	/*
+	 *	Ok we have the problem. Now set the PCI master grant to 
+	 *	occur every master grant. The apparent bug is that under high
+	 *	PCI load (quite common in Linux of course) you can get data
+	 *	loss when the CPU is held off the bus for 3 bus master requests
+	 *	This happens to include the IDE controllers....
+	 *
+	 *	VIA only apply this fix when an SB Live! is present but under
+	 *	both Linux and Windows this isnt enough, and we have seen
+	 *	corruption without SB Live! but with things like 3 UDMA IDE
+	 *	controllers. So we ignore that bit of the VIA recommendation..
+	 */
+
+	pci_read_config_byte(dev, 0x76, &busarb);
+	/* Set bit 4 and bi 5 of byte 76 to 0x01 
+	   "Master priority rotation on every PCI master grant */
+	busarb &= ~(1<<5);
+	busarb |= (1<<4);
+	pci_write_config_byte(dev, 0x76, busarb);
+	printk(KERN_INFO "Applying VIA southbridge workaround.\n");
+exit:
+	pci_dev_put(p);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8363_0,	quirk_vialatency );
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8371_1,	quirk_vialatency );
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8361,		quirk_vialatency );
+
+/*
+ *	VIA Apollo VP3 needs ETBF on BT848/878
+ */
+static void __devinit quirk_viaetbf(struct pci_dev *dev)
+{
+	if ((pci_pci_problems&PCIPCI_VIAETBF)==0) {
+		printk(KERN_INFO "Limiting direct PCI/PCI transfers.\n");
+		pci_pci_problems |= PCIPCI_VIAETBF;
+	}
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_82C597_0,	quirk_viaetbf );
+
+static void __devinit quirk_vsfx(struct pci_dev *dev)
+{
+	if ((pci_pci_problems&PCIPCI_VSFX)==0) {
+		printk(KERN_INFO "Limiting direct PCI/PCI transfers.\n");
+		pci_pci_problems |= PCIPCI_VSFX;
+	}
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_82C576,	quirk_vsfx );
+
+/*
+ *	Ali Magik requires workarounds to be used by the drivers
+ *	that DMA to AGP space. Latency must be set to 0xA and triton
+ *	workaround applied too
+ *	[Info kindly provided by ALi]
+ */	
+static void __init quirk_alimagik(struct pci_dev *dev)
+{
+	if ((pci_pci_problems&PCIPCI_ALIMAGIK)==0) {
+		printk(KERN_INFO "Limiting direct PCI/PCI transfers.\n");
+		pci_pci_problems |= PCIPCI_ALIMAGIK|PCIPCI_TRITON;
+	}
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, 	PCI_DEVICE_ID_AL_M1647, 	quirk_alimagik );
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, 	PCI_DEVICE_ID_AL_M1651, 	quirk_alimagik );
+
+/*
+ *	Natoma has some interesting boundary conditions with Zoran stuff
+ *	at least
+ */
+static void __devinit quirk_natoma(struct pci_dev *dev)
+{
+	if ((pci_pci_problems&PCIPCI_NATOMA)==0) {
+		printk(KERN_INFO "Limiting direct PCI/PCI transfers.\n");
+		pci_pci_problems |= PCIPCI_NATOMA;
+	}
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 	PCI_DEVICE_ID_INTEL_82441, 	quirk_natoma ); 
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 	PCI_DEVICE_ID_INTEL_82443LX_0, 	quirk_natoma ); 
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 	PCI_DEVICE_ID_INTEL_82443LX_1, 	quirk_natoma ); 
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 	PCI_DEVICE_ID_INTEL_82443BX_0, 	quirk_natoma ); 
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 	PCI_DEVICE_ID_INTEL_82443BX_1, 	quirk_natoma ); 
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 	PCI_DEVICE_ID_INTEL_82443BX_2, 	quirk_natoma );
+
+/*
+ *  This chip can cause PCI parity errors if config register 0xA0 is read
+ *  while DMAs are occurring.
+ */
+static void __devinit quirk_citrine(struct pci_dev *dev)
+{
+	dev->cfg_size = 0xA0;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_IBM,	PCI_DEVICE_ID_IBM_CITRINE,	quirk_citrine );
+
+/*
+ *  S3 868 and 968 chips report region size equal to 32M, but they decode 64M.
+ *  If it's needed, re-allocate the region.
+ */
+static void __devinit quirk_s3_64M(struct pci_dev *dev)
+{
+	struct resource *r = &dev->resource[0];
+
+	if ((r->start & 0x3ffffff) || r->end != r->start + 0x3ffffff) {
+		r->start = 0;
+		r->end = 0x3ffffff;
+	}
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_S3,	PCI_DEVICE_ID_S3_868,		quirk_s3_64M );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_S3,	PCI_DEVICE_ID_S3_968,		quirk_s3_64M );
+
+static void __devinit quirk_io_region(struct pci_dev *dev, unsigned region,
+	unsigned size, int nr, const char *name)
+{
+	region &= ~(size-1);
+	if (region) {
+		struct pci_bus_region bus_region;
+		struct resource *res = dev->resource + nr;
+
+		res->name = pci_name(dev);
+		res->start = region;
+		res->end = region + size - 1;
+		res->flags = IORESOURCE_IO;
+
+		/* Convert from PCI bus to resource space.  */
+		bus_region.start = res->start;
+		bus_region.end = res->end;
+		pcibios_bus_to_resource(dev, res, &bus_region);
+
+		pci_claim_resource(dev, nr);
+		printk("PCI quirk: region %04x-%04x claimed by %s\n", region, region + size - 1, name);
+	}
+}	
+
+/*
+ *	ATI Northbridge setups MCE the processor if you even
+ *	read somewhere between 0x3b0->0x3bb or read 0x3d3
+ */
+static void __devinit quirk_ati_exploding_mce(struct pci_dev *dev)
+{
+	printk(KERN_INFO "ATI Northbridge, reserving I/O ports 0x3b0 to 0x3bb.\n");
+	/* Mae rhaid i ni beidio ag edrych ar y lleoliadiau I/O hyn */
+	request_region(0x3b0, 0x0C, "RadeonIGP");
+	request_region(0x3d3, 0x01, "RadeonIGP");
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI,	PCI_DEVICE_ID_ATI_RS100,   quirk_ati_exploding_mce );
+
+/*
+ * Let's make the southbridge information explicit instead
+ * of having to worry about people probing the ACPI areas,
+ * for example.. (Yes, it happens, and if you read the wrong
+ * ACPI register it will put the machine to sleep with no
+ * way of waking it up again. Bummer).
+ *
+ * ALI M7101: Two IO regions pointed to by words at
+ *	0xE0 (64 bytes of ACPI registers)
+ *	0xE2 (32 bytes of SMB registers)
+ */
+static void __devinit quirk_ali7101_acpi(struct pci_dev *dev)
+{
+	u16 region;
+
+	pci_read_config_word(dev, 0xE0, &region);
+	quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES, "ali7101 ACPI");
+	pci_read_config_word(dev, 0xE2, &region);
+	quirk_io_region(dev, region, 32, PCI_BRIDGE_RESOURCES+1, "ali7101 SMB");
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL,	PCI_DEVICE_ID_AL_M7101,		quirk_ali7101_acpi );
+
+static void piix4_io_quirk(struct pci_dev *dev, const char *name, unsigned int port, unsigned int enable)
+{
+	u32 devres;
+	u32 mask, size, base;
+
+	pci_read_config_dword(dev, port, &devres);
+	if ((devres & enable) != enable)
+		return;
+	mask = (devres >> 16) & 15;
+	base = devres & 0xffff;
+	size = 16;
+	for (;;) {
+		unsigned bit = size >> 1;
+		if ((bit & mask) == bit)
+			break;
+		size = bit;
+	}
+	/*
+	 * For now we only print it out. Eventually we'll want to
+	 * reserve it (at least if it's in the 0x1000+ range), but
+	 * let's get enough confirmation reports first. 
+	 */
+	base &= -size;
+	printk("%s PIO at %04x-%04x\n", name, base, base + size - 1);
+}
+
+static void piix4_mem_quirk(struct pci_dev *dev, const char *name, unsigned int port, unsigned int enable)
+{
+	u32 devres;
+	u32 mask, size, base;
+
+	pci_read_config_dword(dev, port, &devres);
+	if ((devres & enable) != enable)
+		return;
+	base = devres & 0xffff0000;
+	mask = (devres & 0x3f) << 16;
+	size = 128 << 16;
+	for (;;) {
+		unsigned bit = size >> 1;
+		if ((bit & mask) == bit)
+			break;
+		size = bit;
+	}
+	/*
+	 * For now we only print it out. Eventually we'll want to
+	 * reserve it, but let's get enough confirmation reports first. 
+	 */
+	base &= -size;
+	printk("%s MMIO at %04x-%04x\n", name, base, base + size - 1);
+}
+
+/*
+ * PIIX4 ACPI: Two IO regions pointed to by longwords at
+ *	0x40 (64 bytes of ACPI registers)
+ *	0x90 (16 bytes of SMB registers)
+ * and a few strange programmable PIIX4 device resources.
+ */
+static void __devinit quirk_piix4_acpi(struct pci_dev *dev)
+{
+	u32 region, res_a;
+
+	pci_read_config_dword(dev, 0x40, &region);
+	quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES, "PIIX4 ACPI");
+	pci_read_config_dword(dev, 0x90, &region);
+	quirk_io_region(dev, region, 16, PCI_BRIDGE_RESOURCES+1, "PIIX4 SMB");
+
+	/* Device resource A has enables for some of the other ones */
+	pci_read_config_dword(dev, 0x5c, &res_a);
+
+	piix4_io_quirk(dev, "PIIX4 devres B", 0x60, 3 << 21);
+	piix4_io_quirk(dev, "PIIX4 devres C", 0x64, 3 << 21);
+
+	/* Device resource D is just bitfields for static resources */
+
+	/* Device 12 enabled? */
+	if (res_a & (1 << 29)) {
+		piix4_io_quirk(dev, "PIIX4 devres E", 0x68, 1 << 20);
+		piix4_mem_quirk(dev, "PIIX4 devres F", 0x6c, 1 << 7);
+	}
+	/* Device 13 enabled? */
+	if (res_a & (1 << 30)) {
+		piix4_io_quirk(dev, "PIIX4 devres G", 0x70, 1 << 20);
+		piix4_mem_quirk(dev, "PIIX4 devres H", 0x74, 1 << 7);
+	}
+	piix4_io_quirk(dev, "PIIX4 devres I", 0x78, 1 << 20);
+	piix4_io_quirk(dev, "PIIX4 devres J", 0x7c, 1 << 20);
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82371AB_3,	quirk_piix4_acpi );
+
+/*
+ * ICH4, ICH4-M, ICH5, ICH5-M ACPI: Three IO regions pointed to by longwords at
+ *	0x40 (128 bytes of ACPI, GPIO & TCO registers)
+ *	0x58 (64 bytes of GPIO I/O space)
+ */
+static void __devinit quirk_ich4_lpc_acpi(struct pci_dev *dev)
+{
+	u32 region;
+
+	pci_read_config_dword(dev, 0x40, &region);
+	quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, "ICH4 ACPI/GPIO/TCO");
+
+	pci_read_config_dword(dev, 0x58, &region);
+	quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES+1, "ICH4 GPIO");
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82801AA_0,		quirk_ich4_lpc_acpi );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82801AB_0,		quirk_ich4_lpc_acpi );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82801BA_0,		quirk_ich4_lpc_acpi );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82801BA_10,	quirk_ich4_lpc_acpi );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82801CA_0,		quirk_ich4_lpc_acpi );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82801CA_12,	quirk_ich4_lpc_acpi );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82801DB_0,		quirk_ich4_lpc_acpi );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82801DB_12,	quirk_ich4_lpc_acpi );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82801EB_0,		quirk_ich4_lpc_acpi );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_ESB_1,		quirk_ich4_lpc_acpi );
+
+static void __devinit quirk_ich6_lpc_acpi(struct pci_dev *dev)
+{
+	u32 region;
+
+	pci_read_config_dword(dev, 0x40, &region);
+	quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, "ICH6 ACPI/GPIO/TCO");
+
+	pci_read_config_dword(dev, 0x48, &region);
+	quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES+1, "ICH6 GPIO");
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_ICH6_1, quirk_ich6_lpc_acpi );
+
+/*
+ * VIA ACPI: One IO region pointed to by longword at
+ *	0x48 or 0x20 (256 bytes of ACPI registers)
+ */
+static void __devinit quirk_vt82c586_acpi(struct pci_dev *dev)
+{
+	u8 rev;
+	u32 region;
+
+	pci_read_config_byte(dev, PCI_CLASS_REVISION, &rev);
+	if (rev & 0x10) {
+		pci_read_config_dword(dev, 0x48, &region);
+		region &= PCI_BASE_ADDRESS_IO_MASK;
+		quirk_io_region(dev, region, 256, PCI_BRIDGE_RESOURCES, "vt82c586 ACPI");
+	}
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_82C586_3,	quirk_vt82c586_acpi );
+
+/*
+ * VIA VT82C686 ACPI: Three IO region pointed to by (long)words at
+ *	0x48 (256 bytes of ACPI registers)
+ *	0x70 (128 bytes of hardware monitoring register)
+ *	0x90 (16 bytes of SMB registers)
+ */
+static void __devinit quirk_vt82c686_acpi(struct pci_dev *dev)
+{
+	u16 hm;
+	u32 smb;
+
+	quirk_vt82c586_acpi(dev);
+
+	pci_read_config_word(dev, 0x70, &hm);
+	hm &= PCI_BASE_ADDRESS_IO_MASK;
+	quirk_io_region(dev, hm, 128, PCI_BRIDGE_RESOURCES + 1, "vt82c686 HW-mon");
+
+	pci_read_config_dword(dev, 0x90, &smb);
+	smb &= PCI_BASE_ADDRESS_IO_MASK;
+	quirk_io_region(dev, smb, 16, PCI_BRIDGE_RESOURCES + 2, "vt82c686 SMB");
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_82C686_4,	quirk_vt82c686_acpi );
+
+/*
+ * VIA VT8235 ISA Bridge: Two IO regions pointed to by words at
+ *	0x88 (128 bytes of power management registers)
+ *	0xd0 (16 bytes of SMB registers)
+ */
+static void __devinit quirk_vt8235_acpi(struct pci_dev *dev)
+{
+	u16 pm, smb;
+
+	pci_read_config_word(dev, 0x88, &pm);
+	pm &= PCI_BASE_ADDRESS_IO_MASK;
+	quirk_io_region(dev, pm, 128, PCI_BRIDGE_RESOURCES, "vt8235 PM");
+
+	pci_read_config_word(dev, 0xd0, &smb);
+	smb &= PCI_BASE_ADDRESS_IO_MASK;
+	quirk_io_region(dev, smb, 16, PCI_BRIDGE_RESOURCES + 1, "vt8235 SMB");
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8235,	quirk_vt8235_acpi);
+
+
+#ifdef CONFIG_X86_IO_APIC 
+
+#include <asm/io_apic.h>
+
+/*
+ * VIA 686A/B: If an IO-APIC is active, we need to route all on-chip
+ * devices to the external APIC.
+ *
+ * TODO: When we have device-specific interrupt routers,
+ * this code will go away from quirks.
+ */
+static void __devinit quirk_via_ioapic(struct pci_dev *dev)
+{
+	u8 tmp;
+	
+	if (nr_ioapics < 1)
+		tmp = 0;    /* nothing routed to external APIC */
+	else
+		tmp = 0x1f; /* all known bits (4-0) routed to external APIC */
+		
+	printk(KERN_INFO "PCI: %sbling Via external APIC routing\n",
+	       tmp == 0 ? "Disa" : "Ena");
+
+	/* Offset 0x58: External APIC IRQ output control */
+	pci_write_config_byte (dev, 0x58, tmp);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_82C686,	quirk_via_ioapic );
+
+/*
+ * VIA 8237: Some BIOSs don't set the 'Bypass APIC De-Assert Message' Bit.
+ * This leads to doubled level interrupt rates.
+ * Set this bit to get rid of cycle wastage.
+ * Otherwise uncritical.
+ */
+static void __devinit quirk_via_vt8237_bypass_apic_deassert(struct pci_dev *dev)
+{
+	u8 misc_control2;
+#define BYPASS_APIC_DEASSERT 8
+
+	pci_read_config_byte(dev, 0x5B, &misc_control2);
+	if (!(misc_control2 & BYPASS_APIC_DEASSERT)) {
+		printk(KERN_INFO "PCI: Bypassing VIA 8237 APIC De-Assert Message\n");
+		pci_write_config_byte(dev, 0x5B, misc_control2|BYPASS_APIC_DEASSERT);
+	}
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8237,		quirk_via_vt8237_bypass_apic_deassert);
+
+/*
+ * The AMD io apic can hang the box when an apic irq is masked.
+ * We check all revs >= B0 (yet not in the pre production!) as the bug
+ * is currently marked NoFix
+ *
+ * We have multiple reports of hangs with this chipset that went away with
+ * noapic specified. For the moment we assume its the errata. We may be wrong
+ * of course. However the advice is demonstrably good even if so..
+ */
+static void __devinit quirk_amd_ioapic(struct pci_dev *dev)
+{
+	u8 rev;
+
+	pci_read_config_byte(dev, PCI_REVISION_ID, &rev);
+	if (rev >= 0x02) {
+		printk(KERN_WARNING "I/O APIC: AMD Errata #22 may be present. In the event of instability try\n");
+		printk(KERN_WARNING "        : booting with the \"noapic\" option.\n");
+	}
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD,	PCI_DEVICE_ID_AMD_VIPER_7410,	quirk_amd_ioapic );
+
+static void __init quirk_ioapic_rmw(struct pci_dev *dev)
+{
+	if (dev->devfn == 0 && dev->bus->number == 0)
+		sis_apic_bug = 1;
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI,	PCI_ANY_ID,			quirk_ioapic_rmw );
+
+int pci_msi_quirk;
+
+#define AMD8131_revA0        0x01
+#define AMD8131_revB0        0x11
+#define AMD8131_MISC         0x40
+#define AMD8131_NIOAMODE_BIT 0
+static void __init quirk_amd_8131_ioapic(struct pci_dev *dev) 
+{ 
+        unsigned char revid, tmp;
+        
+	pci_msi_quirk = 1;
+	printk(KERN_WARNING "PCI: MSI quirk detected. pci_msi_quirk set.\n");
+
+        if (nr_ioapics == 0) 
+                return;
+
+        pci_read_config_byte(dev, PCI_REVISION_ID, &revid);
+        if (revid == AMD8131_revA0 || revid == AMD8131_revB0) {
+                printk(KERN_INFO "Fixing up AMD8131 IOAPIC mode\n"); 
+                pci_read_config_byte( dev, AMD8131_MISC, &tmp);
+                tmp &= ~(1 << AMD8131_NIOAMODE_BIT);
+                pci_write_config_byte( dev, AMD8131_MISC, tmp);
+        }
+} 
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_APIC,         quirk_amd_8131_ioapic ); 
+
+static void __init quirk_svw_msi(struct pci_dev *dev)
+{
+	pci_msi_quirk = 1;
+	printk(KERN_WARNING "PCI: MSI quirk detected. pci_msi_quirk set.\n");
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_GCNB_LE, quirk_svw_msi );
+#endif /* CONFIG_X86_IO_APIC */
+
+
+/*
+ * FIXME: it is questionable that quirk_via_acpi
+ * is needed.  It shows up as an ISA bridge, and does not
+ * support the PCI_INTERRUPT_LINE register at all.  Therefore
+ * it seems like setting the pci_dev's 'irq' to the
+ * value of the ACPI SCI interrupt is only done for convenience.
+ *	-jgarzik
+ */
+static void __devinit quirk_via_acpi(struct pci_dev *d)
+{
+	/*
+	 * VIA ACPI device: SCI IRQ line in PCI config byte 0x42
+	 */
+	u8 irq;
+	pci_read_config_byte(d, 0x42, &irq);
+	irq &= 0xf;
+	if (irq && (irq != 2))
+		d->irq = irq;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_82C586_3,	quirk_via_acpi );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_82C686_4,	quirk_via_acpi );
+
+/*
+ * Via 686A/B:  The PCI_INTERRUPT_LINE register for the on-chip
+ * devices, USB0/1, AC97, MC97, and ACPI, has an unusual feature:
+ * when written, it makes an internal connection to the PIC.
+ * For these devices, this register is defined to be 4 bits wide.
+ * Normally this is fine.  However for IO-APIC motherboards, or
+ * non-x86 architectures (yes Via exists on PPC among other places),
+ * we must mask the PCI_INTERRUPT_LINE value versus 0xf to get
+ * interrupts delivered properly.
+ */
+static void quirk_via_irq(struct pci_dev *dev)
+{
+	u8 irq, new_irq;
+
+	new_irq = dev->irq & 0xf;
+	pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
+	if (new_irq != irq) {
+		printk(KERN_INFO "PCI: Via IRQ fixup for %s, from %d to %d\n",
+			pci_name(dev), irq, new_irq);
+		udelay(15);	/* unknown if delay really needed */
+		pci_write_config_byte(dev, PCI_INTERRUPT_LINE, new_irq);
+	}
+}
+DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_ANY_ID, quirk_via_irq);
+
+/*
+ * VIA VT82C598 has its device ID settable and many BIOSes
+ * set it to the ID of VT82C597 for backward compatibility.
+ * We need to switch it off to be able to recognize the real
+ * type of the chip.
+ */
+static void __devinit quirk_vt82c598_id(struct pci_dev *dev)
+{
+	pci_write_config_byte(dev, 0xfc, 0);
+	pci_read_config_word(dev, PCI_DEVICE_ID, &dev->device);
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_82C597_0,	quirk_vt82c598_id );
+
+/*
+ * CardBus controllers have a legacy base address that enables them
+ * to respond as i82365 pcmcia controllers.  We don't want them to
+ * do this even if the Linux CardBus driver is not loaded, because
+ * the Linux i82365 driver does not (and should not) handle CardBus.
+ */
+static void __devinit quirk_cardbus_legacy(struct pci_dev *dev)
+{
+	if ((PCI_CLASS_BRIDGE_CARDBUS << 8) ^ dev->class)
+		return;
+	pci_write_config_dword(dev, PCI_CB_LEGACY_MODE_BASE, 0);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy);
+
+/*
+ * Following the PCI ordering rules is optional on the AMD762. I'm not
+ * sure what the designers were smoking but let's not inhale...
+ *
+ * To be fair to AMD, it follows the spec by default, its BIOS people
+ * who turn it off!
+ */
+static void __devinit quirk_amd_ordering(struct pci_dev *dev)
+{
+	u32 pcic;
+	pci_read_config_dword(dev, 0x4C, &pcic);
+	if ((pcic&6)!=6) {
+		pcic |= 6;
+		printk(KERN_WARNING "BIOS failed to enable PCI standards compliance, fixing this error.\n");
+		pci_write_config_dword(dev, 0x4C, pcic);
+		pci_read_config_dword(dev, 0x84, &pcic);
+		pcic |= (1<<23);	/* Required in this mode */
+		pci_write_config_dword(dev, 0x84, pcic);
+	}
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD,	PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering );
+
+/*
+ *	DreamWorks provided workaround for Dunord I-3000 problem
+ *
+ *	This card decodes and responds to addresses not apparently
+ *	assigned to it. We force a larger allocation to ensure that
+ *	nothing gets put too close to it.
+ */
+static void __devinit quirk_dunord ( struct pci_dev * dev )
+{
+	struct resource *r = &dev->resource [1];
+	r->start = 0;
+	r->end = 0xffffff;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_DUNORD,	PCI_DEVICE_ID_DUNORD_I3000,	quirk_dunord );
+
+/*
+ * i82380FB mobile docking controller: its PCI-to-PCI bridge
+ * is subtractive decoding (transparent), and does indicate this
+ * in the ProgIf. Unfortunately, the ProgIf value is wrong - 0x80
+ * instead of 0x01.
+ */
+static void __devinit quirk_transparent_bridge(struct pci_dev *dev)
+{
+	dev->transparent = 1;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82380FB,	quirk_transparent_bridge );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TOSHIBA,	0x605,	quirk_transparent_bridge );
+
+/*
+ * Common misconfiguration of the MediaGX/Geode PCI master that will
+ * reduce PCI bandwidth from 70MB/s to 25MB/s.  See the GXM/GXLV/GX1
+ * datasheets found at http://www.national.com/ds/GX for info on what
+ * these bits do.  <christer@weinigel.se>
+ */
+static void __init quirk_mediagx_master(struct pci_dev *dev)
+{
+	u8 reg;
+	pci_read_config_byte(dev, 0x41, &reg);
+	if (reg & 2) {
+		reg &= ~2;
+		printk(KERN_INFO "PCI: Fixup for MediaGX/Geode Slave Disconnect Boundary (0x41=0x%02x)\n", reg);
+                pci_write_config_byte(dev, 0x41, reg);
+	}
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CYRIX,	PCI_DEVICE_ID_CYRIX_PCI_MASTER, quirk_mediagx_master );
+
+/*
+ * As per PCI spec, ignore base address registers 0-3 of the IDE controllers
+ * running in Compatible mode (bits 0 and 2 in the ProgIf for primary and
+ * secondary channels respectively). If the device reports Compatible mode
+ * but does use BAR0-3 for address decoding, we assume that firmware has
+ * programmed these BARs with standard values (0x1f0,0x3f4 and 0x170,0x374).
+ * Exceptions (if they exist) must be handled in chip/architecture specific
+ * fixups.
+ *
+ * Note: for non x86 people. You may need an arch specific quirk to handle
+ * moving IDE devices to native mode as well. Some plug in card devices power
+ * up in compatible mode and assume the BIOS will adjust them.
+ *
+ * Q: should we load the 0x1f0,0x3f4 into the registers or zap them as
+ * we do now ? We don't want is pci_enable_device to come along
+ * and assign new resources. Both approaches work for that.
+ */ 
+static void __devinit quirk_ide_bases(struct pci_dev *dev)
+{
+       struct resource *res;
+       int first_bar = 2, last_bar = 0;
+
+       if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE)
+               return;
+
+       res = &dev->resource[0];
+
+       /* primary channel: ProgIf bit 0, BAR0, BAR1 */
+       if (!(dev->class & 1) && (res[0].flags || res[1].flags)) { 
+               res[0].start = res[0].end = res[0].flags = 0;
+               res[1].start = res[1].end = res[1].flags = 0;
+               first_bar = 0;
+               last_bar = 1;
+       }
+
+       /* secondary channel: ProgIf bit 2, BAR2, BAR3 */
+       if (!(dev->class & 4) && (res[2].flags || res[3].flags)) { 
+               res[2].start = res[2].end = res[2].flags = 0;
+               res[3].start = res[3].end = res[3].flags = 0;
+               last_bar = 3;
+       }
+
+       if (!last_bar)
+               return;
+
+       printk(KERN_INFO "PCI: Ignoring BAR%d-%d of IDE controller %s\n",
+              first_bar, last_bar, pci_name(dev));
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_ide_bases);
+
+/*
+ *	Ensure C0 rev restreaming is off. This is normally done by
+ *	the BIOS but in the odd case it is not the results are corruption
+ *	hence the presence of a Linux check
+ */
+static void __init quirk_disable_pxb(struct pci_dev *pdev)
+{
+	u16 config;
+	u8 rev;
+	
+	pci_read_config_byte(pdev, PCI_REVISION_ID, &rev);
+	if (rev != 0x04)		/* Only C0 requires this */
+		return;
+	pci_read_config_word(pdev, 0x40, &config);
+	if (config & (1<<6)) {
+		config &= ~(1<<6);
+		pci_write_config_word(pdev, 0x40, config);
+		printk(KERN_INFO "PCI: C0 revision 450NX. Disabling PCI restreaming.\n");
+	}
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82454NX,	quirk_disable_pxb );
+
+
+/*
+ *	Serverworks CSB5 IDE does not fully support native mode
+ */
+static void __devinit quirk_svwks_csb5ide(struct pci_dev *pdev)
+{
+	u8 prog;
+	pci_read_config_byte(pdev, PCI_CLASS_PROG, &prog);
+	if (prog & 5) {
+		prog &= ~5;
+		pdev->class &= ~5;
+		pci_write_config_byte(pdev, PCI_CLASS_PROG, prog);
+		/* need to re-assign BARs for compat mode */
+		quirk_ide_bases(pdev);
+	}
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, quirk_svwks_csb5ide );
+
+/*
+ *	Intel 82801CAM ICH3-M datasheet says IDE modes must be the same
+ */
+static void __init quirk_ide_samemode(struct pci_dev *pdev)
+{
+	u8 prog;
+
+	pci_read_config_byte(pdev, PCI_CLASS_PROG, &prog);
+
+	if (((prog & 1) && !(prog & 4)) || ((prog & 4) && !(prog & 1))) {
+		printk(KERN_INFO "PCI: IDE mode mismatch; forcing legacy mode\n");
+		prog &= ~5;
+		pdev->class &= ~5;
+		pci_write_config_byte(pdev, PCI_CLASS_PROG, prog);
+		/* need to re-assign BARs for compat mode */
+		quirk_ide_bases(pdev);
+	}
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_10, quirk_ide_samemode);
+
+/* This was originally an Alpha specific thing, but it really fits here.
+ * The i82375 PCI/EISA bridge appears as non-classified. Fix that.
+ */
+static void __init quirk_eisa_bridge(struct pci_dev *dev)
+{
+	dev->class = PCI_CLASS_BRIDGE_EISA << 8;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82375,	quirk_eisa_bridge );
+
+/*
+ * On ASUS P4B boards, the SMBus PCI Device within the ICH2/4 southbridge
+ * is not activated. The myth is that Asus said that they do not want the
+ * users to be irritated by just another PCI Device in the Win98 device
+ * manager. (see the file prog/hotplug/README.p4b in the lm_sensors 
+ * package 2.7.0 for details)
+ *
+ * The SMBus PCI Device can be activated by setting a bit in the ICH LPC 
+ * bridge. Unfortunately, this device has no subvendor/subdevice ID. So it 
+ * becomes necessary to do this tweak in two steps -- I've chosen the Host
+ * bridge as trigger.
+ */
+static int __initdata asus_hides_smbus = 0;
+
+static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev)
+{
+	if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_ASUSTEK)) {
+		if (dev->device == PCI_DEVICE_ID_INTEL_82845_HB)
+			switch(dev->subsystem_device) {
+			case 0x8025: /* P4B-LX */
+			case 0x8070: /* P4B */
+			case 0x8088: /* P4B533 */
+			case 0x1626: /* L3C notebook */
+				asus_hides_smbus = 1;
+			}
+		if (dev->device == PCI_DEVICE_ID_INTEL_82845G_HB)
+			switch(dev->subsystem_device) {
+			case 0x80b1: /* P4GE-V */
+			case 0x80b2: /* P4PE */
+			case 0x8093: /* P4B533-V */
+				asus_hides_smbus = 1;
+			}
+		if (dev->device == PCI_DEVICE_ID_INTEL_82850_HB)
+			switch(dev->subsystem_device) {
+			case 0x8030: /* P4T533 */
+				asus_hides_smbus = 1;
+			}
+		if (dev->device == PCI_DEVICE_ID_INTEL_7205_0)
+			switch (dev->subsystem_device) {
+			case 0x8070: /* P4G8X Deluxe */
+				asus_hides_smbus = 1;
+			}
+		if (dev->device == PCI_DEVICE_ID_INTEL_82855GM_HB)
+			switch (dev->subsystem_device) {
+			case 0x1751: /* M2N notebook */
+			case 0x1821: /* M5N notebook */
+				asus_hides_smbus = 1;
+			}
+		if (dev->device == PCI_DEVICE_ID_INTEL_82855PM_HB)
+			switch (dev->subsystem_device) {
+			case 0x184b: /* W1N notebook */
+			case 0x186a: /* M6Ne notebook */
+				asus_hides_smbus = 1;
+			}
+		if (dev->device == PCI_DEVICE_ID_INTEL_82915GM_HB) {
+			switch (dev->subsystem_device) {
+			case 0x1882: /* M6V notebook */
+				asus_hides_smbus = 1;
+			}
+		}
+	} else if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_HP)) {
+		if (dev->device ==  PCI_DEVICE_ID_INTEL_82855PM_HB)
+			switch(dev->subsystem_device) {
+			case 0x088C: /* HP Compaq nc8000 */
+			case 0x0890: /* HP Compaq nc6000 */
+				asus_hides_smbus = 1;
+			}
+		if (dev->device == PCI_DEVICE_ID_INTEL_82865_HB)
+			switch (dev->subsystem_device) {
+			case 0x12bc: /* HP D330L */
+			case 0x12bd: /* HP D530 */
+				asus_hides_smbus = 1;
+			}
+	} else if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_TOSHIBA)) {
+		if (dev->device == PCI_DEVICE_ID_INTEL_82855GM_HB)
+			switch(dev->subsystem_device) {
+			case 0x0001: /* Toshiba Satellite A40 */
+				asus_hides_smbus = 1;
+			}
+		if (dev->device == PCI_DEVICE_ID_INTEL_82855PM_HB)
+			switch(dev->subsystem_device) {
+			case 0x0001: /* Toshiba Tecra M2 */
+				asus_hides_smbus = 1;
+			}
+       } else if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG)) {
+               if (dev->device ==  PCI_DEVICE_ID_INTEL_82855PM_HB)
+                       switch(dev->subsystem_device) {
+                       case 0xC00C: /* Samsung P35 notebook */
+                               asus_hides_smbus = 1;
+                       }
+	} else if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_COMPAQ)) {
+		if (dev->device == PCI_DEVICE_ID_INTEL_82855PM_HB)
+			switch(dev->subsystem_device) {
+			case 0x0058: /* Compaq Evo N620c */
+				asus_hides_smbus = 1;
+			}
+	}
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82845_HB,	asus_hides_smbus_hostbridge );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82845G_HB,	asus_hides_smbus_hostbridge );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82850_HB,	asus_hides_smbus_hostbridge );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82865_HB,	asus_hides_smbus_hostbridge );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_7205_0,	asus_hides_smbus_hostbridge );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82855PM_HB,	asus_hides_smbus_hostbridge );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82855GM_HB,	asus_hides_smbus_hostbridge );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82915GM_HB, asus_hides_smbus_hostbridge );
+
+static void __init asus_hides_smbus_lpc(struct pci_dev *dev)
+{
+	u16 val;
+	
+	if (likely(!asus_hides_smbus))
+		return;
+
+	pci_read_config_word(dev, 0xF2, &val);
+	if (val & 0x8) {
+		pci_write_config_word(dev, 0xF2, val & (~0x8));
+		pci_read_config_word(dev, 0xF2, &val);
+		if (val & 0x8)
+			printk(KERN_INFO "PCI: i801 SMBus device continues to play 'hide and seek'! 0x%x\n", val);
+		else
+			printk(KERN_INFO "PCI: Enabled i801 SMBus device\n");
+	}
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82801DB_0,	asus_hides_smbus_lpc );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82801BA_0,	asus_hides_smbus_lpc );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82801CA_12,	asus_hides_smbus_lpc );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82801DB_12,	asus_hides_smbus_lpc );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82801EB_0,	asus_hides_smbus_lpc );
+
+static void __init asus_hides_smbus_lpc_ich6(struct pci_dev *dev)
+{
+	u32 val, rcba;
+	void __iomem *base;
+
+	if (likely(!asus_hides_smbus))
+		return;
+	pci_read_config_dword(dev, 0xF0, &rcba);
+	base = ioremap_nocache(rcba & 0xFFFFC000, 0x4000); /* use bits 31:14, 16 kB aligned */
+	if (base == NULL) return;
+	val=readl(base + 0x3418); /* read the Function Disable register, dword mode only */
+	writel(val & 0xFFFFFFF7, base + 0x3418); /* enable the SMBus device */
+	iounmap(base);
+	printk(KERN_INFO "PCI: Enabled ICH6/i801 SMBus device\n");
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_ICH6_1,	asus_hides_smbus_lpc_ich6 );
+
+/*
+ * SiS 96x south bridge: BIOS typically hides SMBus device...
+ */
+static void __init quirk_sis_96x_smbus(struct pci_dev *dev)
+{
+	u8 val = 0;
+	printk(KERN_INFO "Enabling SiS 96x SMBus.\n");
+	pci_read_config_byte(dev, 0x77, &val);
+	pci_write_config_byte(dev, 0x77, val & ~0x10);
+	pci_read_config_byte(dev, 0x77, &val);
+}
+
+/*
+ * ... This is further complicated by the fact that some SiS96x south
+ * bridges pretend to be 85C503/5513 instead.  In that case see if we
+ * spotted a compatible north bridge to make sure.
+ * (pci_find_device doesn't work yet)
+ *
+ * We can also enable the sis96x bit in the discovery register..
+ */
+static int __devinitdata sis_96x_compatible = 0;
+
+#define SIS_DETECT_REGISTER 0x40
+
+static void __init quirk_sis_503(struct pci_dev *dev)
+{
+	u8 reg;
+	u16 devid;
+
+	pci_read_config_byte(dev, SIS_DETECT_REGISTER, &reg);
+	pci_write_config_byte(dev, SIS_DETECT_REGISTER, reg | (1 << 6));
+	pci_read_config_word(dev, PCI_DEVICE_ID, &devid);
+	if (((devid & 0xfff0) != 0x0960) && (devid != 0x0018)) {
+		pci_write_config_byte(dev, SIS_DETECT_REGISTER, reg);
+		return;
+	}
+
+	/* Make people aware that we changed the config.. */
+	printk(KERN_WARNING "Uncovering SIS%x that hid as a SIS503 (compatible=%d)\n", devid, sis_96x_compatible);
+
+	/*
+	 * Ok, it now shows up as a 96x.. The 96x quirks are after
+	 * the 503 quirk in the quirk table, so they'll automatically
+	 * run and enable things like the SMBus device
+	 */
+	dev->device = devid;
+}
+
+static void __init quirk_sis_96x_compatible(struct pci_dev *dev)
+{
+	sis_96x_compatible = 1;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_645,		quirk_sis_96x_compatible );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_646,		quirk_sis_96x_compatible );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_648,		quirk_sis_96x_compatible );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_650,		quirk_sis_96x_compatible );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_651,		quirk_sis_96x_compatible );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_735,		quirk_sis_96x_compatible );
+
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_503,		quirk_sis_503 );
+
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_961,		quirk_sis_96x_smbus );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_962,		quirk_sis_96x_smbus );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_963,		quirk_sis_96x_smbus );
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_LPC,		quirk_sis_96x_smbus );
+
+#ifdef CONFIG_X86_IO_APIC
+static void __init quirk_alder_ioapic(struct pci_dev *pdev)
+{
+	int i;
+
+	if ((pdev->class >> 8) != 0xff00)
+		return;
+
+	/* the first BAR is the location of the IO APIC...we must
+	 * not touch this (and it's already covered by the fixmap), so
+	 * forcibly insert it into the resource tree */
+	if (pci_resource_start(pdev, 0) && pci_resource_len(pdev, 0))
+		insert_resource(&iomem_resource, &pdev->resource[0]);
+
+	/* The next five BARs all seem to be rubbish, so just clean
+	 * them out */
+	for (i=1; i < 6; i++) {
+		memset(&pdev->resource[i], 0, sizeof(pdev->resource[i]));
+	}
+
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_EESSC,	quirk_alder_ioapic );
+#endif
+
+#ifdef CONFIG_SCSI_SATA_INTEL_COMBINED
+static void __devinit quirk_intel_ide_combined(struct pci_dev *pdev)
+{
+	u8 prog, comb, tmp;
+	int ich = 0;
+
+	/*
+	 * Narrow down to Intel SATA PCI devices.
+	 */
+	switch (pdev->device) {
+	/* PCI ids taken from drivers/scsi/ata_piix.c */
+	case 0x24d1:
+	case 0x24df:
+	case 0x25a3:
+	case 0x25b0:
+		ich = 5;
+		break;
+	case 0x2651:
+	case 0x2652:
+	case 0x2653:
+	case 0x2680:	/* ESB2 */
+		ich = 6;
+		break;
+	case 0x27c0:
+	case 0x27c4:
+		ich = 7;
+		break;
+	default:
+		/* we do not handle this PCI device */
+		return;
+	}
+
+	/*
+	 * Read combined mode register.
+	 */
+	pci_read_config_byte(pdev, 0x90, &tmp);	/* combined mode reg */
+
+	if (ich == 5) {
+		tmp &= 0x6;  /* interesting bits 2:1, PATA primary/secondary */
+		if (tmp == 0x4)		/* bits 10x */
+			comb = (1 << 0);	/* SATA port 0, PATA port 1 */
+		else if (tmp == 0x6)	/* bits 11x */
+			comb = (1 << 2);	/* PATA port 0, SATA port 1 */
+		else
+			return;			/* not in combined mode */
+	} else {
+		WARN_ON((ich != 6) && (ich != 7));
+		tmp &= 0x3;  /* interesting bits 1:0 */
+		if (tmp & (1 << 0))
+			comb = (1 << 2);	/* PATA port 0, SATA port 1 */
+		else if (tmp & (1 << 1))
+			comb = (1 << 0);	/* SATA port 0, PATA port 1 */
+		else
+			return;			/* not in combined mode */
+	}
+
+	/*
+	 * Read programming interface register.
+	 * (Tells us if it's legacy or native mode)
+	 */
+	pci_read_config_byte(pdev, PCI_CLASS_PROG, &prog);
+
+	/* if SATA port is in native mode, we're ok. */
+	if (prog & comb)
+		return;
+
+	/* SATA port is in legacy mode.  Reserve port so that
+	 * IDE driver does not attempt to use it.  If request_region
+	 * fails, it will be obvious at boot time, so we don't bother
+	 * checking return values.
+	 */
+	if (comb == (1 << 0))
+		request_region(0x1f0, 8, "libata");	/* port 0 */
+	else
+		request_region(0x170, 8, "libata");	/* port 1 */
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,    PCI_ANY_ID,	  quirk_intel_ide_combined );
+#endif /* CONFIG_SCSI_SATA_INTEL_COMBINED */
+
+
+int pcie_mch_quirk;
+
+static void __devinit quirk_pcie_mch(struct pci_dev *pdev)
+{
+	pcie_mch_quirk = 1;
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_E7520_MCH,	quirk_pcie_mch );
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_E7320_MCH,	quirk_pcie_mch );
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_E7525_MCH,	quirk_pcie_mch );
+
+
+/*
+ * It's possible for the MSI to get corrupted if shpc and acpi
+ * are used together on certain PXH-based systems.
+ */
+static void __devinit quirk_pcie_pxh(struct pci_dev *dev)
+{
+	disable_msi_mode(dev, pci_find_capability(dev, PCI_CAP_ID_MSI),
+					PCI_CAP_ID_MSI);
+	dev->no_msi = 1;
+
+	printk(KERN_WARNING "PCI: PXH quirk detected, "
+		"disabling MSI for SHPC device\n");
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_PXHD_0,	quirk_pcie_pxh);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_PXHD_1,	quirk_pcie_pxh);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_PXH_0,	quirk_pcie_pxh);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_PXH_1,	quirk_pcie_pxh);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_PXHV,	quirk_pcie_pxh);
+
+
+static void __devinit quirk_netmos(struct pci_dev *dev)
+{
+	unsigned int num_parallel = (dev->subsystem_device & 0xf0) >> 4;
+	unsigned int num_serial = dev->subsystem_device & 0xf;
+
+	/*
+	 * These Netmos parts are multiport serial devices with optional
+	 * parallel ports.  Even when parallel ports are present, they
+	 * are identified as class SERIAL, which means the serial driver
+	 * will claim them.  To prevent this, mark them as class OTHER.
+	 * These combo devices should be claimed by parport_serial.
+	 *
+	 * The subdevice ID is of the form 0x00PS, where <P> is the number
+	 * of parallel ports and <S> is the number of serial ports.
+	 */
+	switch (dev->device) {
+	case PCI_DEVICE_ID_NETMOS_9735:
+	case PCI_DEVICE_ID_NETMOS_9745:
+	case PCI_DEVICE_ID_NETMOS_9835:
+	case PCI_DEVICE_ID_NETMOS_9845:
+	case PCI_DEVICE_ID_NETMOS_9855:
+		if ((dev->class >> 8) == PCI_CLASS_COMMUNICATION_SERIAL &&
+		    num_parallel) {
+			printk(KERN_INFO "PCI: Netmos %04x (%u parallel, "
+				"%u serial); changing class SERIAL to OTHER "
+				"(use parport_serial)\n",
+				dev->device, num_parallel, num_serial);
+			dev->class = (PCI_CLASS_COMMUNICATION_OTHER << 8) |
+			    (dev->class & 0xff);
+		}
+	}
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETMOS, PCI_ANY_ID, quirk_netmos);
+
+
+static void __devinit fixup_rev1_53c810(struct pci_dev* dev)
+{
+	/* rev 1 ncr53c810 chips don't set the class at all which means
+	 * they don't get their resources remapped. Fix that here.
+	 */
+
+	if (dev->class == PCI_CLASS_NOT_DEFINED) {
+		printk(KERN_INFO "NCR 53c810 rev 1 detected, setting PCI class.\n");
+		dev->class = PCI_CLASS_STORAGE_SCSI;
+	}
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C810, fixup_rev1_53c810);
+
+
+static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end)
+{
+	while (f < end) {
+		if ((f->vendor == dev->vendor || f->vendor == (u16) PCI_ANY_ID) &&
+ 		    (f->device == dev->device || f->device == (u16) PCI_ANY_ID)) {
+			pr_debug("PCI: Calling quirk %p for %s\n", f->hook, pci_name(dev));
+			f->hook(dev);
+		}
+		f++;
+	}
+}
+
+extern struct pci_fixup __start_pci_fixups_early[];
+extern struct pci_fixup __end_pci_fixups_early[];
+extern struct pci_fixup __start_pci_fixups_header[];
+extern struct pci_fixup __end_pci_fixups_header[];
+extern struct pci_fixup __start_pci_fixups_final[];
+extern struct pci_fixup __end_pci_fixups_final[];
+extern struct pci_fixup __start_pci_fixups_enable[];
+extern struct pci_fixup __end_pci_fixups_enable[];
+
+
+void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev)
+{
+	struct pci_fixup *start, *end;
+
+	switch(pass) {
+	case pci_fixup_early:
+		start = __start_pci_fixups_early;
+		end = __end_pci_fixups_early;
+		break;
+
+	case pci_fixup_header:
+		start = __start_pci_fixups_header;
+		end = __end_pci_fixups_header;
+		break;
+
+	case pci_fixup_final:
+		start = __start_pci_fixups_final;
+		end = __end_pci_fixups_final;
+		break;
+
+	case pci_fixup_enable:
+		start = __start_pci_fixups_enable;
+		end = __end_pci_fixups_enable;
+		break;
+
+	default:
+		/* stupid compiler warning, you would think with an enum... */
+		return;
+	}
+	pci_do_fixups(dev, start, end);
+}
+
+EXPORT_SYMBOL(pcie_mch_quirk);
+#ifdef CONFIG_HOTPLUG
+EXPORT_SYMBOL(pci_fixup_device);
+#endif
diff -urN oldtree/drivers/scsi/Kconfig newtree/drivers/scsi/Kconfig
--- oldtree/drivers/scsi/Kconfig	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/scsi/Kconfig	2006-02-13 19:20:00.559427256 -0500
@@ -209,7 +209,7 @@
 	  there should be no noticeable performance impact as long as you have
 	  logging turned off.
 
-menu "SCSI Transport Attributes"
+menu "SCSI Transports"
 	depends on SCSI
 
 config SCSI_SPI_ATTRS
@@ -242,6 +242,8 @@
 	  If you wish to export transport-specific information about
 	  each attached SAS device to sysfs, say Y.
 
+source "drivers/scsi/sas/Kconfig"
+
 endmenu
 
 menu "SCSI low-level drivers"
@@ -289,6 +291,20 @@
 	tristate "DEC SII Scsi Driver"
 	depends on MACH_DECSTATION && SCSI && 32BIT
 
+config SCSI_ARCMSR
+	tristate "ARECA ARC11X0[PCI-X]/ARC12X0[PCI-EXPRESS] SATA-RAID support"
+	depends on  PCI && SCSI
+	help
+	  This driver supports all of ARECA's SATA RAID controllers cards.
+	  This is an ARECA maintained driver by Erich Chen.
+	  If you have any problems, please mail to: < erich@areca.com.tw >
+	  Areca have suport Linux RAID config tools
+
+	  < http://www.areca.com.tw >
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called arcmsr (modprobe arcmsr) .
+
 config BLK_DEV_3W_XXXX_RAID
 	tristate "3ware 5/6/7/8xxx ATA-RAID support"
 	depends on PCI && SCSI
@@ -422,6 +438,7 @@
 	  module will be called aic7xxx_old.
 
 source "drivers/scsi/aic7xxx/Kconfig.aic79xx"
+source "drivers/scsi/aic94xx/Kconfig"
 
 # All the I2O code and drivers do not seem to be 64bit safe.
 config SCSI_DPT_I2O
diff -urN oldtree/drivers/scsi/Kconfig.orig newtree/drivers/scsi/Kconfig.orig
--- oldtree/drivers/scsi/Kconfig.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/Kconfig.orig	2006-02-13 19:20:00.564426496 -0500
@@ -0,0 +1,1835 @@
+menu "SCSI device support"
+
+config RAID_ATTRS
+	tristate "RAID Transport Class"
+	default n
+	---help---
+	  Provides RAID
+
+config SCSI
+	tristate "SCSI device support"
+	---help---
+	  If you want to use a SCSI hard disk, SCSI tape drive, SCSI CD-ROM or
+	  any other SCSI device under Linux, say Y and make sure that you know
+	  the name of your SCSI host adapter (the card inside your computer
+	  that "speaks" the SCSI protocol, also called SCSI controller),
+	  because you will be asked for it.
+
+	  You also need to say Y here if you have a device which speaks
+	  the SCSI protocol.  Examples of this include the parallel port
+	  version of the IOMEGA ZIP drive, USB storage devices, Fibre
+	  Channel, FireWire storage and the IDE-SCSI emulation driver.
+
+	  To compile this driver as a module, choose M here and read
+	  <file:Documentation/scsi/scsi.txt>.
+	  The module will be called scsi_mod.
+
+	  However, do not compile this as a module if your root file system
+	  (the one containing the directory /) is located on a SCSI device.
+
+config SCSI_PROC_FS
+	bool "legacy /proc/scsi/ support"
+	depends on SCSI && PROC_FS
+	default y
+	---help---
+	  This option enables support for the various files in
+	  /proc/scsi.  In Linux 2.6 this has been superceeded by
+	  files in sysfs but many legacy applications rely on this.
+
+	  If unusure say Y.
+
+comment "SCSI support type (disk, tape, CD-ROM)"
+	depends on SCSI
+
+config BLK_DEV_SD
+	tristate "SCSI disk support"
+	depends on SCSI
+	---help---
+	  If you want to use SCSI hard disks, Fibre Channel disks,
+	  USB storage or the SCSI or parallel port version of
+	  the IOMEGA ZIP drive, say Y and read the SCSI-HOWTO,
+	  the Disk-HOWTO and the Multi-Disk-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>. This is NOT for SCSI
+	  CD-ROMs.
+
+	  To compile this driver as a module, choose M here and read
+	  <file:Documentation/scsi/scsi.txt>.
+	  The module will be called sd_mod.
+
+	  Do not compile this driver as a module if your root file system
+	  (the one containing the directory /) is located on a SCSI disk.
+	  In this case, do not compile the driver for your SCSI host adapter
+	  (below) as a module either.
+
+config CHR_DEV_ST
+	tristate "SCSI tape support"
+	depends on SCSI
+	---help---
+	  If you want to use a SCSI tape drive under Linux, say Y and read the
+	  SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>, and
+	  <file:Documentation/scsi/st.txt> in the kernel source.  This is NOT
+	  for SCSI CD-ROMs.
+
+	  To compile this driver as a module, choose M here and read
+	  <file:Documentation/scsi/scsi.txt>. The module will be called st.
+
+config CHR_DEV_OSST
+	tristate "SCSI OnStream SC-x0 tape support"
+	depends on SCSI
+	---help---
+	  The OnStream SC-x0 SCSI tape drives can not be driven by the
+	  standard st driver, but instead need this special osst driver and
+	  use the  /dev/osstX char device nodes (major 206).  Via usb-storage
+	  and ide-scsi, you may be able to drive the USB-x0 and DI-x0 drives
+	  as well.  Note that there is also a second generation of OnStream
+	  tape drives (ADR-x0) that supports the standard SCSI-2 commands for
+	  tapes (QIC-157) and can be driven by the standard driver st.
+	  For more information, you may have a look at the SCSI-HOWTO
+	  <http://www.tldp.org/docs.html#howto>  and
+	  <file:Documentation/scsi/osst.txt>  in the kernel source.
+	  More info on the OnStream driver may be found on
+	  <http://linux1.onstream.nl/test/>
+	  Please also have a look at the standard st docu, as most of it
+	  applies to osst as well.
+
+	  To compile this driver as a module, choose M here and read
+	  <file:Documentation/scsi/scsi.txt>. The module will be called osst.
+
+config BLK_DEV_SR
+	tristate "SCSI CDROM support"
+	depends on SCSI
+	---help---
+	  If you want to use a SCSI or FireWire CD-ROM under Linux,
+	  say Y and read the SCSI-HOWTO and the CDROM-HOWTO at
+	  <http://www.tldp.org/docs.html#howto>. Also make sure to say
+	  Y or M to "ISO 9660 CD-ROM file system support" later.
+
+	  To compile this driver as a module, choose M here and read
+	  <file:Documentation/scsi/scsi.txt>.
+	  The module will be called sr_mod.
+
+config BLK_DEV_SR_VENDOR
+	bool "Enable vendor-specific extensions (for SCSI CDROM)"
+	depends on BLK_DEV_SR
+	help
+	  This enables the usage of vendor specific SCSI commands. This is
+	  required to support multisession CDs with old NEC/TOSHIBA cdrom
+	  drives (and HP Writers). If you have such a drive and get the first
+	  session only, try saying Y here; everybody else says N.
+
+config CHR_DEV_SG
+	tristate "SCSI generic support"
+	depends on SCSI
+	---help---
+	  If you want to use SCSI scanners, synthesizers or CD-writers or just
+	  about anything having "SCSI" in its name other than hard disks,
+	  CD-ROMs or tapes, say Y here. These won't be supported by the kernel
+	  directly, so you need some additional software which knows how to
+	  talk to these devices using the SCSI protocol:
+
+	  For scanners, look at SANE (<http://www.mostang.com/sane/>). For CD
+	  writer software look at Cdrtools
+	  (<http://www.fokus.gmd.de/research/cc/glone/employees/joerg.schilling/private/cdrecord.html>)
+	  and for burning a "disk at once": CDRDAO
+	  (<http://cdrdao.sourceforge.net/>). Cdparanoia is a high
+	  quality digital reader of audio CDs (<http://www.xiph.org/paranoia/>).
+	  For other devices, it's possible that you'll have to write the
+	  driver software yourself. Please read the file
+	  <file:Documentation/scsi/scsi-generic.txt> for more information.
+
+	  To compile this driver as a module, choose M here and read
+	  <file:Documentation/scsi/scsi.txt>. The module will be called sg.
+
+	  If unsure, say N.
+
+config CHR_DEV_SCH
+	tristate "SCSI media changer support"
+	depends on SCSI
+	---help---
+	  This is a driver for SCSI media changers.  Most common devices are
+	  tape libraries and MOD/CDROM jukeboxes.  *Real* jukeboxes, you
+	  don't need this for those tiny 6-slot cdrom changers.  Media
+	  changers are listed as "Type: Medium Changer" in /proc/scsi/scsi.
+	  If you have such hardware and want to use it with linux, say Y
+	  here.  Check <file:Documentation/scsi-changer.txt> for details.
+	
+	  If you want to compile this as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want),
+	  say M here and read <file:Documentation/modules.txt> and
+	  <file:Documentation/scsi.txt>. The module will be called ch.o.
+	  If unsure, say N.
+	
+
+comment "Some SCSI devices (e.g. CD jukebox) support multiple LUNs"
+	depends on SCSI
+
+config SCSI_MULTI_LUN
+	bool "Probe all LUNs on each SCSI device"
+	depends on SCSI
+	help
+	  If you have a SCSI device that supports more than one LUN (Logical
+	  Unit Number), e.g. a CD jukebox, and only one LUN is detected, you
+	  can say Y here to force the SCSI driver to probe for multiple LUNs.
+	  A SCSI device with multiple LUNs acts logically like multiple SCSI
+	  devices. The vast majority of SCSI devices have only one LUN, and
+	  so most people can say N here. The max_luns boot/module parameter 
+	  allows to override this setting.
+
+config SCSI_CONSTANTS
+	bool "Verbose SCSI error reporting (kernel size +=12K)"
+	depends on SCSI
+	help
+	  The error messages regarding your SCSI hardware will be easier to
+	  understand if you say Y here; it will enlarge your kernel by about
+	  12 KB. If in doubt, say Y.
+
+config SCSI_LOGGING
+	bool "SCSI logging facility"
+	depends on SCSI
+	---help---
+	  This turns on a logging facility that can be used to debug a number
+	  of SCSI related problems.
+
+	  If you say Y here, no logging output will appear by default, but you
+	  can enable logging by saying Y to "/proc file system support" and
+	  "Sysctl support" below and executing the command
+
+	  echo "scsi log token [level]" > /proc/scsi/scsi
+
+	  at boot time after the /proc file system has been mounted.
+
+	  There are a number of things that can be used for 'token' (you can
+	  find them in the source: <file:drivers/scsi/scsi.c>), and this
+	  allows you to select the types of information you want, and the
+	  level allows you to select the level of verbosity.
+
+	  If you say N here, it may be harder to track down some types of SCSI
+	  problems. If you say Y here your kernel will be somewhat larger, but
+	  there should be no noticeable performance impact as long as you have
+	  logging turned off.
+
+menu "SCSI Transport Attributes"
+	depends on SCSI
+
+config SCSI_SPI_ATTRS
+	tristate "Parallel SCSI (SPI) Transport Attributes"
+	depends on SCSI
+	help
+	  If you wish to export transport-specific information about
+	  each attached SCSI device to sysfs, say Y.  Otherwise, say N.
+
+config SCSI_FC_ATTRS
+	tristate "FiberChannel Transport Attributes"
+	depends on SCSI
+	help
+	  If you wish to export transport-specific information about
+	  each attached FiberChannel device to sysfs, say Y.
+	  Otherwise, say N.
+
+config SCSI_ISCSI_ATTRS
+	tristate "iSCSI Transport Attributes"
+	depends on SCSI && NET
+	help
+	  If you wish to export transport-specific information about
+	  each attached iSCSI device to sysfs, say Y.
+	  Otherwise, say N.
+
+config SCSI_SAS_ATTRS
+	tristate "SAS Transport Attributes"
+	depends on SCSI
+	help
+	  If you wish to export transport-specific information about
+	  each attached SAS device to sysfs, say Y.
+
+endmenu
+
+menu "SCSI low-level drivers"
+	depends on SCSI!=n
+
+config ISCSI_TCP
+	tristate "iSCSI Initiator over TCP/IP"
+	depends on SCSI && INET
+	select CRYPTO
+	select CRYPTO_MD5
+	select CRYPTO_CRC32C
+	select SCSI_ISCSI_ATTRS
+	help
+	 The iSCSI Driver provides a host with the ability to access storage
+	 through an IP network. The driver uses the iSCSI protocol to transport
+	 SCSI requests and responses over a TCP/IP network between the host
+	 (the "initiator") and "targets".  Architecturally, the iSCSI driver
+	 combines with the host's TCP/IP stack, network drivers, and Network
+	 Interface Card (NIC) to provide the same functions as a SCSI or a
+	 Fibre Channel (FC) adapter driver with a Host Bus Adapter (HBA).
+
+	 To compile this driver as a module, choose M here: the
+	 module will be called iscsi_tcp.
+
+	 The userspace component needed to initialize the driver, documentation,
+	 and sample configuration files can be found here:
+
+	 http://linux-iscsi.sf.net
+
+config SGIWD93_SCSI
+	tristate "SGI WD93C93 SCSI Driver"
+	depends on SGI_IP22 && SCSI
+  	help
+	  If you have a Western Digital WD93 SCSI controller on
+	  an SGI MIPS system, say Y.  Otherwise, say N.
+
+config SCSI_DECNCR
+	tristate "DEC NCR53C94 Scsi Driver"
+	depends on MACH_DECSTATION && SCSI && TC
+	help
+	  Say Y here to support the NCR53C94 SCSI controller chips on IOASIC
+	  based TURBOchannel DECstations and TURBOchannel PMAZ-A cards.
+
+config SCSI_DECSII
+	tristate "DEC SII Scsi Driver"
+	depends on MACH_DECSTATION && SCSI && 32BIT
+
+config SCSI_ARCMSR
+	tristate "ARECA ARC11X0[PCI-X]/ARC12X0[PCI-EXPRESS] SATA-RAID support"
+	depends on  PCI && SCSI
+	help
+	  This driver supports all of ARECA's SATA RAID controllers cards.
+	  This is an ARECA maintained driver by Erich Chen.
+	  If you have any problems, please mail to: < erich@areca.com.tw >
+	  Areca have suport Linux RAID config tools
+
+	  < http://www.areca.com.tw >
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called arcmsr (modprobe arcmsr) .
+
+config BLK_DEV_3W_XXXX_RAID
+	tristate "3ware 5/6/7/8xxx ATA-RAID support"
+	depends on PCI && SCSI
+	help
+	  3ware is the only hardware ATA-Raid product in Linux to date.
+	  This card is 2,4, or 8 channel master mode support only.
+	  SCSI support required!!!
+
+	  <http://www.3ware.com/>
+
+	  Please read the comments at the top of
+	  <file:drivers/scsi/3w-xxxx.c>.
+
+config SCSI_3W_9XXX
+	tristate "3ware 9xxx SATA-RAID support"
+	depends on PCI && SCSI
+	help
+	  This driver supports the 9000 series 3ware SATA-RAID cards.
+
+	  <http://www.amcc.com>
+
+	  Please read the comments at the top of
+	  <file:drivers/scsi/3w-9xxx.c>.
+
+config SCSI_7000FASST
+	tristate "7000FASST SCSI support"
+	depends on ISA && SCSI && ISA_DMA_API
+	help
+	  This driver supports the Western Digital 7000 SCSI host adapter
+	  family.  Some information is in the source:
+	  <file:drivers/scsi/wd7000.c>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called wd7000.
+
+config SCSI_ACARD
+	tristate "ACARD SCSI support"
+	depends on PCI && SCSI
+	help
+	  This driver supports the ACARD SCSI host adapter.
+	  Support Chip <ATP870 ATP876 ATP880 ATP885>
+	  To compile this driver as a module, choose M here: the
+	  module will be called atp870u.
+
+config SCSI_AHA152X
+	tristate "Adaptec AHA152X/2825 support"
+	depends on ISA && SCSI && !64BIT
+	---help---
+	  This is a driver for the AHA-1510, AHA-1520, AHA-1522, and AHA-2825
+	  SCSI host adapters. It also works for the AVA-1505, but the IRQ etc.
+	  must be manually specified in this case.
+
+	  It is explained in section 3.3 of the SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>. You might also want to
+	  read the file <file:Documentation/scsi/aha152x.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called aha152x.
+
+config SCSI_AHA1542
+	tristate "Adaptec AHA1542 support"
+	depends on ISA && SCSI && ISA_DMA_API
+	---help---
+	  This is support for a SCSI host adapter.  It is explained in section
+	  3.4 of the SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.  Note that Trantor was
+	  purchased by Adaptec, and some former Trantor products are being
+	  sold under the Adaptec name.  If it doesn't work out of the box, you
+	  may have to change some settings in <file:drivers/scsi/aha1542.h>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called aha1542.
+
+config SCSI_AHA1740
+	tristate "Adaptec AHA1740 support"
+	depends on EISA && SCSI
+	---help---
+	  This is support for a SCSI host adapter.  It is explained in section
+	  3.5 of the SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.  If it doesn't work out
+	  of the box, you may have to change some settings in
+	  <file:drivers/scsi/aha1740.h>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called aha1740.
+
+config SCSI_AACRAID
+	tristate "Adaptec AACRAID support"
+	depends on SCSI && PCI
+
+source "drivers/scsi/aic7xxx/Kconfig.aic7xxx"
+
+config SCSI_AIC7XXX_OLD
+	tristate "Adaptec AIC7xxx support (old driver)"
+	depends on (ISA || EISA || PCI ) && SCSI
+	help
+	  WARNING This driver is an older aic7xxx driver and is no longer
+	  under active development.  Adaptec, Inc. is writing a new driver to
+	  take the place of this one, and it is recommended that whenever
+	  possible, people should use the new Adaptec written driver instead
+	  of this one.  This driver will eventually be phased out entirely.
+
+	  This is support for the various aic7xxx based Adaptec SCSI
+	  controllers. These include the 274x EISA cards; 284x VLB cards;
+	  2902, 2910, 293x, 294x, 394x, 3985 and several other PCI and
+	  motherboard based SCSI controllers from Adaptec. It does not support
+	  the AAA-13x RAID controllers from Adaptec, nor will it likely ever
+	  support them. It does not support the 2920 cards from Adaptec that
+	  use the Future Domain SCSI controller chip. For those cards, you
+	  need the "Future Domain 16xx SCSI support" driver.
+
+	  In general, if the controller is based on an Adaptec SCSI controller
+	  chip from the aic777x series or the aic78xx series, this driver
+	  should work. The only exception is the 7810 which is specifically
+	  not supported (that's the RAID controller chip on the AAA-13x
+	  cards).
+
+	  Note that the AHA2920 SCSI host adapter is *not* supported by this
+	  driver; choose "Future Domain 16xx SCSI support" instead if you have
+	  one of those.
+
+	  Information on the configuration options for this controller can be
+	  found by checking the help file for each of the available
+	  configuration options. You should read
+	  <file:Documentation/scsi/aic7xxx_old.txt> at a minimum before
+	  contacting the maintainer with any questions.  The SCSI-HOWTO,
+	  available from <http://www.tldp.org/docs.html#howto>, can also
+	  be of great help.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called aic7xxx_old.
+
+source "drivers/scsi/aic7xxx/Kconfig.aic79xx"
+
+# All the I2O code and drivers do not seem to be 64bit safe.
+config SCSI_DPT_I2O
+	tristate "Adaptec I2O RAID support "
+	depends on !64BIT && SCSI && PCI
+	help
+	  This driver supports all of Adaptec's I2O based RAID controllers as 
+	  well as the DPT SmartRaid V cards.  This is an Adaptec maintained
+	  driver by Deanna Bonds.  See <file:Documentation/scsi/dpti.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called dpt_i2o.
+
+config SCSI_ADVANSYS
+	tristate "AdvanSys SCSI support"
+	depends on (ISA || EISA || PCI) && SCSI && BROKEN
+	help
+	  This is a driver for all SCSI host adapters manufactured by
+	  AdvanSys. It is documented in the kernel source in
+	  <file:drivers/scsi/advansys.c>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called advansys.
+
+config SCSI_IN2000
+	tristate "Always IN2000 SCSI support"
+	depends on ISA && SCSI
+	help
+	  This is support for an ISA bus SCSI host adapter.  You'll find more
+	  information in <file:Documentation/scsi/in2000.txt>. If it doesn't work
+	  out of the box, you may have to change the jumpers for IRQ or
+	  address selection.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called in2000.
+
+source "drivers/scsi/megaraid/Kconfig.megaraid"
+
+config SCSI_SATA
+	tristate "Serial ATA (SATA) support"
+	depends on SCSI
+	help
+	  This driver family supports Serial ATA host controllers
+	  and devices.
+
+	  If unsure, say N.
+
+config SCSI_SATA_AHCI
+	tristate "AHCI SATA support"
+	depends on SCSI_SATA && PCI
+	help
+	  This option enables support for AHCI Serial ATA.
+
+	  If unsure, say N.
+
+config SCSI_SATA_SVW
+	tristate "ServerWorks Frodo / Apple K2 SATA support"
+	depends on SCSI_SATA && PCI
+	help
+	  This option enables support for Broadcom/Serverworks/Apple K2
+	  SATA support.
+
+	  If unsure, say N.
+
+config SCSI_ATA_PIIX
+	tristate "Intel PIIX/ICH SATA support"
+	depends on SCSI_SATA && PCI
+	help
+	  This option enables support for ICH5 Serial ATA.
+	  If PATA support was enabled previously, this enables
+	  support for select Intel PIIX/ICH PATA host controllers.
+
+	  If unsure, say N.
+
+config SCSI_SATA_MV
+	tristate "Marvell SATA support (HIGHLY EXPERIMENTAL)"
+	depends on SCSI_SATA && PCI && EXPERIMENTAL
+	help
+	  This option enables support for the Marvell Serial ATA family.
+	  Currently supports 88SX[56]0[48][01] chips.
+
+	  If unsure, say N.
+
+config SCSI_SATA_NV
+	tristate "NVIDIA SATA support"
+	depends on SCSI_SATA && PCI && EXPERIMENTAL
+	help
+	  This option enables support for NVIDIA Serial ATA.
+
+	  If unsure, say N.
+
+config SCSI_PDC_ADMA
+	tristate "Pacific Digital ADMA support"
+	depends on SCSI_SATA && PCI
+	help
+	  This option enables support for Pacific Digital ADMA controllers
+
+	  If unsure, say N.
+
+config SCSI_SATA_QSTOR
+	tristate "Pacific Digital SATA QStor support"
+	depends on SCSI_SATA && PCI
+	help
+	  This option enables support for Pacific Digital Serial ATA QStor.
+
+	  If unsure, say N.
+
+config SCSI_SATA_PROMISE
+	tristate "Promise SATA TX2/TX4 support"
+	depends on SCSI_SATA && PCI
+	help
+	  This option enables support for Promise Serial ATA TX2/TX4.
+
+	  If unsure, say N.
+
+config SCSI_SATA_SX4
+	tristate "Promise SATA SX4 support"
+	depends on SCSI_SATA && PCI && EXPERIMENTAL
+	help
+	  This option enables support for Promise Serial ATA SX4.
+
+	  If unsure, say N.
+
+config SCSI_SATA_SIL
+	tristate "Silicon Image SATA support"
+	depends on SCSI_SATA && PCI && EXPERIMENTAL
+	help
+	  This option enables support for Silicon Image Serial ATA.
+
+	  If unsure, say N.
+
+config SCSI_SATA_SIL24
+	tristate "Silicon Image 3124/3132 SATA support"
+	depends on SCSI_SATA && PCI && EXPERIMENTAL
+	help
+	  This option enables support for Silicon Image 3124/3132 Serial ATA.
+
+	  If unsure, say N.
+
+config SCSI_SATA_SIS
+	tristate "SiS 964/180 SATA support"
+	depends on SCSI_SATA && PCI && EXPERIMENTAL
+	help
+	  This option enables support for SiS Serial ATA 964/180.
+
+	  If unsure, say N.
+
+config SCSI_SATA_ULI
+	tristate "ULi Electronics SATA support"
+	depends on SCSI_SATA && PCI && EXPERIMENTAL
+	help
+	  This option enables support for ULi Electronics SATA.
+
+	  If unsure, say N.
+
+config SCSI_SATA_VIA
+	tristate "VIA SATA support"
+	depends on SCSI_SATA && PCI
+	help
+	  This option enables support for VIA Serial ATA.
+
+	  If unsure, say N.
+
+config SCSI_SATA_VITESSE
+	tristate "VITESSE VSC-7174 SATA support"
+	depends on SCSI_SATA && PCI
+	help
+	  This option enables support for Vitesse VSC7174 Serial ATA.
+
+	  If unsure, say N.
+
+config SCSI_SATA_INTEL_COMBINED
+	bool
+	depends on IDE=y && !BLK_DEV_IDE_SATA && (SCSI_SATA_AHCI || SCSI_ATA_PIIX)
+	default y
+
+config SCSI_BUSLOGIC
+	tristate "BusLogic SCSI support"
+	depends on (PCI || ISA || MCA) && SCSI && ISA_DMA_API
+	---help---
+	  This is support for BusLogic MultiMaster and FlashPoint SCSI Host
+	  Adapters. Consult the SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>, and the files
+	  <file:Documentation/scsi/BusLogic.txt> and
+	  <file:Documentation/scsi/FlashPoint.txt> for more information.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called BusLogic.
+
+config SCSI_OMIT_FLASHPOINT
+	bool "Omit FlashPoint support"
+	depends on SCSI_BUSLOGIC
+	help
+	  This option allows you to omit the FlashPoint support from the
+	  BusLogic SCSI driver. The FlashPoint SCCB Manager code is
+	  substantial, so users of MultiMaster Host Adapters may wish to omit
+	  it.
+
+config SCSI_DMX3191D
+	tristate "DMX3191D SCSI support"
+	depends on PCI && SCSI
+	help
+	  This is support for Domex DMX3191D SCSI Host Adapters.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called dmx3191d.
+
+config SCSI_DTC3280
+	tristate "DTC3180/3280 SCSI support"
+	depends on ISA && SCSI
+	help
+	  This is support for DTC 3180/3280 SCSI Host Adapters.  Please read
+	  the SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>, and the file
+	  <file:Documentation/scsi/dtc3x80.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called dtc.
+
+config SCSI_EATA
+	tristate "EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) support"
+	depends on (ISA || EISA || PCI) && SCSI && ISA_DMA_API
+	---help---
+	  This driver supports all EATA/DMA-compliant SCSI host adapters.  DPT
+	  ISA and all EISA I/O addresses are probed looking for the "EATA"
+	  signature. The addresses of all the PCI SCSI controllers reported
+          by the PCI subsystem are probed as well.
+
+	  You want to read the start of <file:drivers/scsi/eata.c> and the
+	  SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called eata.
+
+config SCSI_EATA_TAGGED_QUEUE
+	bool "enable tagged command queueing"
+	depends on SCSI_EATA
+	help
+	  This is a feature of SCSI-2 which improves performance: the host
+	  adapter can send several SCSI commands to a device's queue even if
+	  previous commands haven't finished yet.
+	  This is equivalent to the "eata=tc:y" boot option.
+
+config SCSI_EATA_LINKED_COMMANDS
+	bool "enable elevator sorting"
+	depends on SCSI_EATA
+	help
+	  This option enables elevator sorting for all probed SCSI disks and
+	  CD-ROMs. It definitely reduces the average seek distance when doing
+	  random seeks, but this does not necessarily result in a noticeable
+	  performance improvement: your mileage may vary...
+	  This is equivalent to the "eata=lc:y" boot option.
+
+config SCSI_EATA_MAX_TAGS
+	int "maximum number of queued commands"
+	depends on SCSI_EATA
+	default "16"
+	help
+	  This specifies how many SCSI commands can be maximally queued for
+	  each probed SCSI device. You should reduce the default value of 16
+	  only if you have disks with buggy or limited tagged command support.
+	  Minimum is 2 and maximum is 62. This value is also the window size
+	  used by the elevator sorting option above. The effective value used
+	  by the driver for each probed SCSI device is reported at boot time.
+	  This is equivalent to the "eata=mq:8" boot option.
+
+config SCSI_EATA_PIO
+	tristate "EATA-PIO (old DPT PM2001, PM2012A) support"
+	depends on (ISA || EISA || PCI) && SCSI && BROKEN
+	---help---
+	  This driver supports all EATA-PIO protocol compliant SCSI Host
+	  Adapters like the DPT PM2001 and the PM2012A.  EATA-DMA compliant
+	  host adapters could also use this driver but are discouraged from
+	  doing so, since this driver only supports hard disks and lacks
+	  numerous features.  You might want to have a look at the SCSI-HOWTO,
+	  available from <http://www.tldp.org/docs.html#howto>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called eata_pio.
+
+config SCSI_FUTURE_DOMAIN
+	tristate "Future Domain 16xx SCSI/AHA-2920A support"
+	depends on (ISA || PCI) && SCSI
+	---help---
+	  This is support for Future Domain's 16-bit SCSI host adapters
+	  (TMC-1660/1680, TMC-1650/1670, TMC-3260, TMC-1610M/MER/MEX) and
+	  other adapters based on the Future Domain chipsets (Quantum
+	  ISA-200S, ISA-250MG; Adaptec AHA-2920A; and at least one IBM board).
+	  It is explained in section 3.7 of the SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  NOTE: Newer Adaptec AHA-2920C boards use the Adaptec AIC-7850 chip
+	  and should use the aic7xxx driver ("Adaptec AIC7xxx chipset SCSI
+	  controller support"). This Future Domain driver works with the older
+	  Adaptec AHA-2920A boards with a Future Domain chip on them.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called fdomain.
+
+config SCSI_FD_MCS
+	tristate "Future Domain MCS-600/700 SCSI support"
+	depends on MCA_LEGACY && SCSI
+	---help---
+	  This is support for Future Domain MCS 600/700 MCA SCSI adapters.
+	  Some PS/2 computers are equipped with IBM Fast SCSI Adapter/A which
+	  is identical to the MCS 700 and hence also supported by this driver.
+	  This driver also supports the Reply SB16/SCSI card (the SCSI part).
+	  It supports multiple adapters in the same system.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called fd_mcs.
+
+config SCSI_GDTH
+	tristate "Intel/ICP (former GDT SCSI Disk Array) RAID Controller support"
+	depends on (ISA || EISA || PCI) && SCSI && ISA_DMA_API
+	---help---
+	  Formerly called GDT SCSI Disk Array Controller Support.
+
+	  This is a driver for RAID/SCSI Disk Array Controllers (EISA/ISA/PCI) 
+	  manufactured by Intel Corporation/ICP vortex GmbH. It is documented
+	  in the kernel source in <file:drivers/scsi/gdth.c> and
+	  <file:drivers/scsi/gdth.h.>
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gdth.
+
+config SCSI_GENERIC_NCR5380
+	tristate "Generic NCR5380/53c400 SCSI PIO support"
+	depends on ISA && SCSI
+	---help---
+	  This is a driver for the old NCR 53c80 series of SCSI controllers
+	  on boards using PIO. Most boards such as the Trantor T130 fit this
+	  category, along with a large number of ISA 8bit controllers shipped
+	  for free with SCSI scanners. If you have a PAS16, T128 or DMX3191
+	  you should select the specific driver for that card rather than
+	  generic 5380 support.
+
+	  It is explained in section 3.8 of the SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.  If it doesn't work out
+	  of the box, you may have to change some settings in
+	  <file:drivers/scsi/g_NCR5380.h>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called g_NCR5380.
+
+config SCSI_GENERIC_NCR5380_MMIO
+	tristate "Generic NCR5380/53c400 SCSI MMIO support"
+	depends on ISA && SCSI
+	---help---
+	  This is a driver for the old NCR 53c80 series of SCSI controllers
+	  on boards using memory mapped I/O. 
+	  It is explained in section 3.8 of the SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.  If it doesn't work out
+	  of the box, you may have to change some settings in
+	  <file:drivers/scsi/g_NCR5380.h>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called g_NCR5380_mmio.
+
+config SCSI_GENERIC_NCR53C400
+	bool "Enable NCR53c400 extensions"
+	depends on SCSI_GENERIC_NCR5380
+	help
+	  This enables certain optimizations for the NCR53c400 SCSI cards.
+	  You might as well try it out.  Note that this driver will only probe
+	  for the Trantor T130B in its default configuration; you might have
+	  to pass a command line option to the kernel at boot time if it does
+	  not detect your card.  See the file
+	  <file:Documentation/scsi/g_NCR5380.txt> for details.
+
+config SCSI_IBMMCA
+	tristate "IBMMCA SCSI support"
+	depends on MCA_LEGACY && SCSI
+	---help---
+	  This is support for the IBM SCSI adapter found in many of the PS/2
+	  series computers.  These machines have an MCA bus, so you need to
+	  answer Y to "MCA support" as well and read
+	  <file:Documentation/mca.txt>.
+
+	  If the adapter isn't found during boot (a common problem for models
+	  56, 57, 76, and 77) you'll need to use the 'ibmmcascsi=<pun>' kernel
+	  option, where <pun> is the id of the SCSI subsystem (usually 7, but
+	  if that doesn't work check your reference diskette).  Owners of
+	  model 95 with a LED-matrix-display can in addition activate some
+	  activity info like under OS/2, but more informative, by setting
+	  'ibmmcascsi=display' as an additional kernel parameter.  Try "man
+	  bootparam" or see the documentation of your boot loader about how to
+	  pass options to the kernel.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ibmmca.
+
+config IBMMCA_SCSI_ORDER_STANDARD
+	bool "Standard SCSI-order"
+	depends on SCSI_IBMMCA
+	---help---
+	  In the PC-world and in most modern SCSI-BIOS-setups, SCSI-hard disks
+	  are assigned to the drive letters, starting with the lowest SCSI-id
+	  (physical number -- pun) to be drive C:, as seen from DOS and
+	  similar operating systems. When looking into papers describing the
+	  ANSI-SCSI-standard, this assignment of drives appears to be wrong.
+	  The SCSI-standard follows a hardware-hierarchy which says that id 7
+	  has the highest priority and id 0 the lowest. Therefore, the host
+	  adapters are still today everywhere placed as SCSI-id 7 by default.
+	  In the SCSI-standard, the drive letters express the priority of the
+	  disk. C: should be the hard disk, or a partition on it, with the
+	  highest priority. This must therefore be the disk with the highest
+	  SCSI-id (e.g. 6) and not the one with the lowest! IBM-BIOS kept the
+	  original definition of the SCSI-standard as also industrial- and
+	  process-control-machines, like VME-CPUs running under realtime-OSes
+	  (e.g. LynxOS, OS9) do.
+
+	  If you like to run Linux on your MCA-machine with the same
+	  assignment of hard disks as seen from e.g. DOS or OS/2 on your
+	  machine, which is in addition conformant to the SCSI-standard, you
+	  must say Y here. This is also necessary for MCA-Linux users who want
+	  to keep downward compatibility to older releases of the
+	  IBM-MCA-SCSI-driver (older than driver-release 2.00 and older than
+	  June 1997).
+
+	  If you like to have the lowest SCSI-id assigned as drive C:, as
+	  modern SCSI-BIOSes do, which does not conform to the standard, but
+	  is widespread and common in the PC-world of today, you must say N
+	  here. If unsure, say Y.
+
+config IBMMCA_SCSI_DEV_RESET
+	bool "Reset SCSI-devices at boottime"
+	depends on SCSI_IBMMCA
+	---help---
+	  By default, SCSI-devices are reset when the machine is powered on.
+	  However, some devices exist, like special-control-devices,
+	  SCSI-CNC-machines, SCSI-printer or scanners of older type, that do
+	  not reset when switched on. If you say Y here, each device connected
+	  to your SCSI-bus will be issued a reset-command after it has been
+	  probed, while the kernel is booting. This may cause problems with
+	  more modern devices, like hard disks, which do not appreciate these
+	  reset commands, and can cause your system to hang. So say Y only if
+	  you know that one of your older devices needs it; N is the safe
+	  answer.
+
+config SCSI_IPS
+	tristate "IBM ServeRAID support"
+	depends on PCI && SCSI
+	---help---
+	  This is support for the IBM ServeRAID hardware RAID controllers.
+	  See <http://www.developer.ibm.com/welcome/netfinity/serveraid.html>
+	  for more information.  If this driver does not work correctly
+	  without modification please contact the author by email at
+	  <ipslinux@adaptec.com>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ips.
+
+config SCSI_IBMVSCSI
+	tristate "IBM Virtual SCSI support"
+	depends on PPC_PSERIES || PPC_ISERIES
+	help
+	  This is the IBM POWER Virtual SCSI Client
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ibmvscsic.
+
+config SCSI_INITIO
+	tristate "Initio 9100U(W) support"
+	depends on PCI && SCSI
+	help
+	  This is support for the Initio 91XXU(W) SCSI host adapter.  Please
+	  read the SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called initio.
+
+config SCSI_INIA100
+	tristate "Initio INI-A100U2W support"
+	depends on PCI && SCSI
+	help
+	  This is support for the Initio INI-A100U2W SCSI host adapter.
+	  Please read the SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called a100u2w.
+
+config SCSI_PPA
+	tristate "IOMEGA parallel port (ppa - older drives)"
+	depends on SCSI && PARPORT
+	---help---
+	  This driver supports older versions of IOMEGA's parallel port ZIP
+	  drive (a 100 MB removable media device).
+
+	  Note that you can say N here if you have the SCSI version of the ZIP
+	  drive: it will be supported automatically if you said Y to the
+	  generic "SCSI disk support", above.
+
+	  If you have the ZIP Plus drive or a more recent parallel port ZIP
+	  drive (if the supplied cable with the drive is labeled "AutoDetect")
+	  then you should say N here and Y to "IOMEGA parallel port (imm -
+	  newer drives)", below.
+
+	  For more information about this driver and how to use it you should
+	  read the file <file:Documentation/scsi/ppa.txt>.  You should also read
+	  the SCSI-HOWTO, which is available from
+	  <http://www.tldp.org/docs.html#howto>.  If you use this driver,
+	  you will still be able to use the parallel port for other tasks,
+	  such as a printer; it is safe to compile both drivers into the
+	  kernel.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ppa.
+
+config SCSI_IMM
+	tristate "IOMEGA parallel port (imm - newer drives)"
+	depends on SCSI && PARPORT
+	---help---
+	  This driver supports newer versions of IOMEGA's parallel port ZIP
+	  drive (a 100 MB removable media device).
+
+	  Note that you can say N here if you have the SCSI version of the ZIP
+	  drive: it will be supported automatically if you said Y to the
+	  generic "SCSI disk support", above.
+
+	  If you have the ZIP Plus drive or a more recent parallel port ZIP
+	  drive (if the supplied cable with the drive is labeled "AutoDetect")
+	  then you should say Y here; if you have an older ZIP drive, say N
+	  here and Y to "IOMEGA Parallel Port (ppa - older drives)", above.
+
+	  For more information about this driver and how to use it you should
+	  read the file <file:Documentation/scsi/ppa.txt>.  You should also read
+	  the SCSI-HOWTO, which is available from
+	  <http://www.tldp.org/docs.html#howto>.  If you use this driver,
+	  you will still be able to use the parallel port for other tasks,
+	  such as a printer; it is safe to compile both drivers into the
+	  kernel.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called imm.
+
+config SCSI_IZIP_EPP16
+	bool "ppa/imm option - Use slow (but safe) EPP-16"
+	depends on PARPORT && (SCSI_PPA || SCSI_IMM)
+	---help---
+	  EPP (Enhanced Parallel Port) is a standard for parallel ports which
+	  allows them to act as expansion buses that can handle up to 64
+	  peripheral devices.
+
+	  Some parallel port chipsets are slower than their motherboard, and
+	  so we have to control the state of the chipset's FIFO queue every
+	  now and then to avoid data loss. This will be done if you say Y
+	  here.
+
+	  Generally, saying Y is the safe option and slows things down a bit.
+
+config SCSI_IZIP_SLOW_CTR
+	bool "ppa/imm option - Assume slow parport control register"
+	depends on PARPORT && (SCSI_PPA || SCSI_IMM)
+	help
+	  Some parallel ports are known to have excessive delays between
+	  changing the parallel port control register and good data being
+	  available on the parallel port data/status register. This option
+	  forces a small delay (1.0 usec to be exact) after changing the
+	  control register to let things settle out. Enabling this option may
+	  result in a big drop in performance but some very old parallel ports
+	  (found in 386 vintage machines) will not work properly.
+
+	  Generally, saying N is fine.
+
+config SCSI_NCR53C406A
+	tristate "NCR53c406a SCSI support"
+	depends on ISA && SCSI
+	help
+	  This is support for the NCR53c406a SCSI host adapter.  For user
+	  configurable parameters, check out <file:drivers/scsi/NCR53c406a.c>
+	  in the kernel source.  Also read the SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called NCR53c406.
+
+config SCSI_NCR_D700
+	tristate "NCR Dual 700 MCA SCSI support"
+	depends on MCA && SCSI
+	select SCSI_SPI_ATTRS
+	help
+	  This is a driver for the MicroChannel Dual 700 card produced by
+	  NCR and commonly used in 345x/35xx/4100 class machines.  It always
+	  tries to negotiate sync and uses tag command queueing.
+
+	  Unless you have an NCR manufactured machine, the chances are that
+	  you do not have this SCSI card, so say N.
+
+config SCSI_LASI700
+	tristate "HP Lasi SCSI support for 53c700/710"
+	depends on GSC && SCSI
+	select SCSI_SPI_ATTRS
+	help
+	  This is a driver for the SCSI controller in the Lasi chip found in
+	  many PA-RISC workstations & servers.  If you do not know whether you
+	  have a Lasi chip, it is safe to say "Y" here.
+
+config 53C700_LE_ON_BE
+	bool
+	depends on SCSI_LASI700
+	default y
+
+config SCSI_SYM53C8XX_2
+	tristate "SYM53C8XX Version 2 SCSI support"
+	depends on PCI && SCSI
+	select SCSI_SPI_ATTRS
+	---help---
+	  This driver supports the whole NCR53C8XX/SYM53C8XX family of
+	  PCI-SCSI controllers.  It also supports the subset of LSI53C10XX
+	  Ultra-160 controllers that are based on the SYM53C8XX SCRIPTS
+	  language.  It does not support LSI53C10XX Ultra-320 PCI-X SCSI
+	  controllers; you need to use the Fusion MPT driver for that.
+
+	  Please read <file:Documentation/scsi/sym53c8xx_2.txt> for more
+	  information.
+
+config SCSI_SYM53C8XX_DMA_ADDRESSING_MODE
+	int "DMA addressing mode"
+	depends on SCSI_SYM53C8XX_2
+	default "1"
+	---help---
+	  This option only applies to PCI-SCSI chips that are PCI DAC
+	  capable (875A, 895A, 896, 1010-33, 1010-66, 1000).
+
+	  When set to 0, the driver will program the chip to only perform
+	  32-bit DMA.  When set to 1, the chip will be able to perform DMA
+	  to addresses up to 1TB.  When set to 2, the driver supports the
+	  full 64-bit DMA address range, but can only address 16 segments
+	  of 4 GB each.  This limits the total addressable range to 64 GB.
+
+	  Most machines with less than 4GB of memory should use a setting
+	  of 0 for best performance.  If your machine has 4GB of memory
+	  or more, you should set this option to 1 (the default).
+
+	  The still experimental value 2 (64 bit DMA addressing with 16
+	  x 4GB segments limitation) can be used on systems that require
+	  PCI address bits past bit 39 to be set for the addressing of
+	  memory using PCI DAC cycles.
+
+config SCSI_SYM53C8XX_DEFAULT_TAGS
+	int "default tagged command queue depth"
+	depends on SCSI_SYM53C8XX_2
+	default "16"
+	help
+	  This is the default value of the command queue depth the
+	  driver will announce to the generic SCSI layer for devices
+	  that support tagged command queueing. This value can be changed
+	  from the boot command line.  This is a soft limit that cannot
+	  exceed CONFIG_SCSI_SYM53C8XX_MAX_TAGS.
+
+config SCSI_SYM53C8XX_MAX_TAGS
+	int "maximum number of queued commands"
+	depends on SCSI_SYM53C8XX_2
+	default "64"
+	help
+	  This option allows you to specify the maximum number of commands
+	  that can be queued to any device, when tagged command queuing is
+	  possible. The driver supports up to 256 queued commands per device.
+	  This value is used as a compiled-in hard limit.
+
+config SCSI_SYM53C8XX_IOMAPPED
+	bool "use port IO"
+	depends on SCSI_SYM53C8XX_2
+	help
+	  If you say Y here, the driver will use port IO to access
+	  the card.  This is significantly slower then using memory
+	  mapped IO.  Most people should answer N.
+
+config SCSI_IPR
+	tristate "IBM Power Linux RAID adapter support"
+	depends on PCI && SCSI
+	select FW_LOADER
+	---help---
+	  This driver supports the IBM Power Linux family RAID adapters.
+	  This includes IBM pSeries 5712, 5703, 5709, and 570A, as well
+	  as IBM iSeries 5702, 5703, 5709, and 570A.
+
+config SCSI_IPR_TRACE
+	bool "enable driver internal trace"
+	depends on SCSI_IPR
+	help
+	  If you say Y here, the driver will trace all commands issued
+	  to the adapter. Performance impact is minimal. Trace can be
+	  dumped using /sys/bus/class/scsi_host/hostXX/trace.
+
+config SCSI_IPR_DUMP
+	bool "enable adapter dump support"
+	depends on SCSI_IPR
+	help
+	  If you say Y here, the driver will support adapter crash dump.
+	  If you enable this support, the iprdump daemon can be used
+	  to capture adapter failure analysis information.
+
+config SCSI_ZALON
+	tristate "Zalon SCSI support"
+	depends on GSC && SCSI
+	select SCSI_SPI_ATTRS
+	help
+	  The Zalon is a GSC/HSC bus interface chip that sits between the
+	  PA-RISC processor and the NCR 53c720 SCSI controller on C100,
+	  C110, J200, J210 and some D, K & R-class machines.  It's also
+	  used on the add-in Bluefish, Barracuda & Shrike SCSI cards.
+	  Say Y here if you have one of these machines or cards.
+
+config SCSI_NCR_Q720
+	tristate "NCR Quad 720 MCA SCSI support"
+	depends on MCA && SCSI
+	select SCSI_SPI_ATTRS
+	help
+	  This is a driver for the MicroChannel Quad 720 card produced by
+	  NCR and commonly used in 345x/35xx/4100 class machines.  It always
+	  tries to negotiate sync and uses tag command queueing.
+
+	  Unless you have an NCR manufactured machine, the chances are that
+	  you do not have this SCSI card, so say N.
+
+config SCSI_NCR53C8XX_DEFAULT_TAGS
+	int "  default tagged command queue depth"
+	depends on SCSI_ZALON || SCSI_NCR_Q720
+	default "8"
+	---help---
+	  "Tagged command queuing" is a feature of SCSI-2 which improves
+	  performance: the host adapter can send several SCSI commands to a
+	  device's queue even if previous commands haven't finished yet.
+	  Because the device is intelligent, it can optimize its operations
+	  (like head positioning) based on its own request queue. Some SCSI
+	  devices don't implement this properly; if you want to disable this
+	  feature, enter 0 or 1 here (it doesn't matter which).
+
+	  The default value is 8 and should be supported by most hard disks.
+	  This value can be overridden from the boot command line using the
+	  'tags' option as follows (example):
+	  'ncr53c8xx=tags:4/t2t3q16/t0u2q10' will set default queue depth to
+	  4, set queue depth to 16 for target 2 and target 3 on controller 0
+	  and set queue depth to 10 for target 0 / lun 2 on controller 1.
+
+	  The normal answer therefore is to go with the default 8 and to use
+	  a boot command line option for devices that need to use a different
+	  command queue depth.
+
+	  There is no safe option other than using good SCSI devices.
+
+config SCSI_NCR53C8XX_MAX_TAGS
+	int "  maximum number of queued commands"
+	depends on SCSI_ZALON || SCSI_NCR_Q720
+	default "32"
+	---help---
+	  This option allows you to specify the maximum number of commands
+	  that can be queued to any device, when tagged command queuing is
+	  possible. The default value is 32. Minimum is 2, maximum is 64.
+	  Modern hard disks are able to support 64 tags and even more, but
+	  do not seem to be faster when more than 32 tags are being used.
+
+	  So, the normal answer here is to go with the default value 32 unless
+	  you are using very large hard disks with large cache (>= 1 MB) that
+	  are able to take advantage of more than 32 tagged commands.
+
+	  There is no safe option and the default answer is recommended.
+
+config SCSI_NCR53C8XX_SYNC
+	int "  synchronous transfers frequency in MHz"
+	depends on SCSI_ZALON || SCSI_NCR_Q720
+	default "20"
+	---help---
+	  The SCSI Parallel Interface-2 Standard defines 5 classes of transfer
+	  rates: FAST-5, FAST-10, FAST-20, FAST-40 and FAST-80.  The numbers
+	  are respectively the maximum data transfer rates in mega-transfers
+	  per second for each class.  For example, a FAST-20 Wide 16 device is
+	  able to transfer data at 20 million 16 bit packets per second for a
+	  total rate of 40 MB/s.
+
+	  You may specify 0 if you want to only use asynchronous data
+	  transfers. This is the safest and slowest option. Otherwise, specify
+	  a value between 5 and 80, depending on the capability of your SCSI
+	  controller.  The higher the number, the faster the data transfer.
+	  Note that 80 should normally be ok since the driver decreases the
+	  value automatically according to the controller's capabilities.
+
+	  Your answer to this question is ignored for controllers with NVRAM,
+	  since the driver will get this information from the user set-up.  It
+	  also can be overridden using a boot setup option, as follows
+	  (example): 'ncr53c8xx=sync:12' will allow the driver to negotiate
+	  for FAST-20 synchronous data transfer (20 mega-transfers per
+	  second).
+
+	  The normal answer therefore is not to go with the default but to
+	  select the maximum value 80 allowing the driver to use the maximum
+	  value supported by each controller. If this causes problems with
+	  your SCSI devices, you should come back and decrease the value.
+
+	  There is no safe option other than using good cabling, right
+	  terminations and SCSI conformant devices.
+
+config SCSI_NCR53C8XX_PROFILE
+	bool "  enable profiling"
+	depends on SCSI_ZALON || SCSI_NCR_Q720
+	help
+	  This option allows you to enable profiling information gathering.
+	  These statistics are not very accurate due to the low frequency
+	  of the kernel clock (100 Hz on i386) and have performance impact
+	  on systems that use very fast devices.
+
+	  The normal answer therefore is N.
+
+config SCSI_NCR53C8XX_NO_DISCONNECT
+	bool "  not allow targets to disconnect"
+	depends on (SCSI_ZALON || SCSI_NCR_Q720) && SCSI_NCR53C8XX_DEFAULT_TAGS=0
+	help
+	  This option is only provided for safety if you suspect some SCSI
+	  device of yours to not support properly the target-disconnect
+	  feature. In that case, you would say Y here. In general however, to
+	  not allow targets to disconnect is not reasonable if there is more
+	  than 1 device on a SCSI bus. The normal answer therefore is N.
+
+config SCSI_MCA_53C9X
+	tristate "NCR MCA 53C9x SCSI support"
+	depends on MCA_LEGACY && SCSI && BROKEN_ON_SMP
+	help
+	  Some MicroChannel machines, notably the NCR 35xx line, use a SCSI
+	  controller based on the NCR 53C94.  This driver will allow use of
+	  the controller on the 3550, and very possibly others.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mca_53c9x.
+
+config SCSI_PAS16
+	tristate "PAS16 SCSI support"
+	depends on ISA && SCSI
+	---help---
+	  This is support for a SCSI host adapter.  It is explained in section
+	  3.10 of the SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.  If it doesn't work out
+	  of the box, you may have to change some settings in
+	  <file:drivers/scsi/pas16.h>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pas16.
+
+config SCSI_PSI240I
+	tristate "PSI240i support"
+	depends on ISA && SCSI
+	help
+	  This is support for the PSI240i EIDE interface card which acts as a
+	  SCSI host adapter.  Please read the SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called psi240i.
+
+config SCSI_QLOGIC_FAS
+	tristate "Qlogic FAS SCSI support"
+	depends on ISA && SCSI
+	---help---
+	  This is a driver for the ISA, VLB, and PCMCIA versions of the Qlogic
+	  FastSCSI! cards as well as any other card based on the FASXX chip
+	  (including the Control Concepts SCSI/IDE/SIO/PIO/FDC cards).
+
+	  This driver does NOT support the PCI versions of these cards. The
+	  PCI versions are supported by the Qlogic ISP driver ("Qlogic ISP
+	  SCSI support"), below.
+
+	  Information about this driver is contained in
+	  <file:Documentation/scsi/qlogicfas.txt>.  You should also read the
+	  SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called qlogicfas.
+
+config SCSI_QLOGIC_FC
+	tristate "Qlogic ISP FC SCSI support"
+	depends on PCI && SCSI
+	help
+	  This is a driver for the QLogic ISP2100 SCSI-FCP host adapter.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called qlogicfc.
+
+config SCSI_QLOGIC_FC_FIRMWARE
+	bool "Include loadable firmware in driver"
+	depends on SCSI_QLOGIC_FC
+  	help
+	  Say Y to include ISP2X00 Fabric Initiator/Target Firmware, with
+	  expanded LUN addressing and FcTape (FCP-2) support, in the
+	  qlogicfc driver. This is required on some platforms.
+
+config SCSI_QLOGIC_1280
+	tristate "Qlogic QLA 1240/1x80/1x160 SCSI support"
+	depends on PCI && SCSI
+	help
+	  Say Y if you have a QLogic ISP1240/1x80/1x160 SCSI host adapter.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called qla1280.
+
+config SCSI_QLOGICPTI
+	tristate "PTI Qlogic, ISP Driver"
+	depends on SBUS && SCSI
+	help
+	  This driver supports SBUS SCSI controllers from PTI or QLogic. These
+	  controllers are known under Solaris as qpti and in the openprom as
+	  PTI,ptisp or QLGC,isp. Note that PCI QLogic SCSI controllers are
+	  driven by a different driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called qlogicpti.
+
+source "drivers/scsi/qla2xxx/Kconfig"
+
+config SCSI_LPFC
+	tristate "Emulex LightPulse Fibre Channel Support"
+	depends on PCI && SCSI
+	select SCSI_FC_ATTRS
+	help
+          This lpfc driver supports the Emulex LightPulse
+          Family of Fibre Channel PCI host adapters.
+
+config SCSI_SEAGATE
+	tristate "Seagate ST-02 and Future Domain TMC-8xx SCSI support"
+	depends on X86 && ISA && SCSI && BROKEN
+	---help---
+	  These are 8-bit SCSI controllers; the ST-01 is also supported by
+	  this driver.  It is explained in section 3.9 of the SCSI-HOWTO,
+	  available from <http://www.tldp.org/docs.html#howto>.  If it
+	  doesn't work out of the box, you may have to change some settings in
+	  <file:drivers/scsi/seagate.h>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called seagate.
+
+# definitely looks not 64bit safe:
+config SCSI_SIM710
+	tristate "Simple 53c710 SCSI support (Compaq, NCR machines)"
+	depends on (EISA || MCA) && SCSI
+	select SCSI_SPI_ATTRS
+	---help---
+	  This driver for NCR53c710 based SCSI host adapters.
+
+	  It currently supports Compaq EISA cards and NCR MCA cards
+
+config 53C700_IO_MAPPED
+	bool
+	depends on SCSI_SIM710
+	default y
+
+config SCSI_SYM53C416
+	tristate "Symbios 53c416 SCSI support"
+	depends on ISA && SCSI
+	---help---
+	  This is support for the sym53c416 SCSI host adapter, the SCSI
+	  adapter that comes with some HP scanners. This driver requires that
+	  the sym53c416 is configured first using some sort of PnP
+	  configuration program (e.g. isapnp) or by a PnP aware BIOS. If you
+	  are using isapnp then you need to compile this driver as a module
+	  and then load it using insmod after isapnp has run. The parameters
+	  of the configured card(s) should be passed to the driver. The format
+	  is:
+
+	  insmod sym53c416 sym53c416=<base>,<irq> [sym53c416_1=<base>,<irq>]
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sym53c416.
+
+config SCSI_DC395x
+	tristate "Tekram DC395(U/UW/F) and DC315(U) SCSI support (EXPERIMENTAL)"
+	depends on PCI && SCSI && EXPERIMENTAL
+	---help---
+	  This driver supports PCI SCSI host adapters based on the ASIC
+	  TRM-S1040 chip, e.g Tekram DC395(U/UW/F) and DC315(U) variants.
+
+	  This driver works, but is still in experimental status. So better
+	  have a bootable disk and a backup in case of emergency.
+
+	  Documentation can be found in <file:Documentation/scsi/dc395x.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called dc395x.
+
+config SCSI_DC390T
+	tristate "Tekram DC390(T) and Am53/79C974 SCSI support"
+	depends on PCI && SCSI
+	---help---
+	  This driver supports PCI SCSI host adapters based on the Am53C974A
+	  chip, e.g. Tekram DC390(T), DawiControl 2974 and some onboard
+	  PCscsi/PCnet (Am53/79C974) solutions.
+
+	  Documentation can be found in <file:Documentation/scsi/tmscsim.txt>.
+
+	  Note that this driver does NOT support Tekram DC390W/U/F, which are
+	  based on NCR/Symbios chips. Use "NCR53C8XX SCSI support" for those.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called tmscsim.
+
+config SCSI_T128
+	tristate "Trantor T128/T128F/T228 SCSI support"
+	depends on ISA && SCSI
+	---help---
+	  This is support for a SCSI host adapter. It is explained in section
+	  3.11 of the SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.  If it doesn't work out
+	  of the box, you may have to change some settings in
+	  <file:drivers/scsi/t128.h>.  Note that Trantor was purchased by
+	  Adaptec, and some former Trantor products are being sold under the
+	  Adaptec name.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called t128.
+
+config SCSI_U14_34F
+	tristate "UltraStor 14F/34F support"
+	depends on ISA && SCSI && ISA_DMA_API
+	---help---
+	  This is support for the UltraStor 14F and 34F SCSI-2 host adapters.
+	  The source at <file:drivers/scsi/u14-34f.c> contains some
+	  information about this hardware.  If the driver doesn't work out of
+	  the box, you may have to change some settings in
+	  <file: drivers/scsi/u14-34f.c>.  Read the SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.  Note that there is also
+	  another driver for the same hardware: "UltraStor SCSI support",
+	  below.  You should say Y to both only if you want 24F support as
+	  well.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called u14-34f.
+
+config SCSI_U14_34F_TAGGED_QUEUE
+	bool "enable tagged command queueing"
+	depends on SCSI_U14_34F
+	help
+	  This is a feature of SCSI-2 which improves performance: the host
+	  adapter can send several SCSI commands to a device's queue even if
+	  previous commands haven't finished yet.
+	  This is equivalent to the "u14-34f=tc:y" boot option.
+
+config SCSI_U14_34F_LINKED_COMMANDS
+	bool "enable elevator sorting"
+	depends on SCSI_U14_34F
+	help
+	  This option enables elevator sorting for all probed SCSI disks and
+	  CD-ROMs. It definitely reduces the average seek distance when doing
+	  random seeks, but this does not necessarily result in a noticeable
+	  performance improvement: your mileage may vary...
+	  This is equivalent to the "u14-34f=lc:y" boot option.
+
+config SCSI_U14_34F_MAX_TAGS
+	int "maximum number of queued commands"
+	depends on SCSI_U14_34F
+	default "8"
+	help
+	  This specifies how many SCSI commands can be maximally queued for
+	  each probed SCSI device. You should reduce the default value of 8
+	  only if you have disks with buggy or limited tagged command support.
+	  Minimum is 2 and maximum is 14. This value is also the window size
+	  used by the elevator sorting option above. The effective value used
+	  by the driver for each probed SCSI device is reported at boot time.
+	  This is equivalent to the "u14-34f=mq:8" boot option.
+
+config SCSI_ULTRASTOR
+	tristate "UltraStor SCSI support"
+	depends on X86 && ISA && SCSI
+	---help---
+	  This is support for the UltraStor 14F, 24F and 34F SCSI-2 host
+	  adapter family.  This driver is explained in section 3.12 of the
+	  SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.  If it doesn't work out
+	  of the box, you may have to change some settings in
+	  <file:drivers/scsi/ultrastor.h>.
+
+	  Note that there is also another driver for the same hardware:
+	  "UltraStor 14F/34F support", above.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ultrastor.
+
+config SCSI_NSP32
+	tristate "Workbit NinjaSCSI-32Bi/UDE support"
+	depends on PCI && SCSI && !64BIT
+	help
+	  This is support for the Workbit NinjaSCSI-32Bi/UDE PCI/Cardbus
+	  SCSI host adapter. Please read the SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called nsp32.
+
+config SCSI_DEBUG
+	tristate "SCSI debugging host simulator"
+	depends on SCSI
+	help
+	  This is a host adapter simulator that can simulate multiple hosts
+	  each with multiple dummy SCSI devices (disks). It defaults to one
+	  host adapter with one dummy SCSI disk. Each dummy disk uses kernel
+	  RAM as storage (i.e. it is a ramdisk). To save space when multiple
+	  dummy disks are simulated, they share the same kernel RAM for 
+	  their storage. See <http://www.torque.net/sg/sdebug.html> for more
+	  information. This driver is primarily of use to those testing the
+	  SCSI and block subsystems. If unsure, say N.
+
+config SCSI_MESH
+	tristate "MESH (Power Mac internal SCSI) support"
+	depends on PPC32 && PPC_PMAC && SCSI
+	help
+	  Many Power Macintoshes and clones have a MESH (Macintosh Enhanced
+	  SCSI Hardware) SCSI bus adaptor (the 7200 doesn't, but all of the
+	  other Power Macintoshes do). Say Y to include support for this SCSI
+	  adaptor.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mesh.
+
+config SCSI_MESH_SYNC_RATE
+	int "maximum synchronous transfer rate (MB/s) (0 = async)"
+	depends on SCSI_MESH
+	default "5"
+	help
+	  On Power Macintoshes (and clones) where the MESH SCSI bus adaptor
+	  drives a bus which is entirely internal to the machine (such as the
+	  7500, 7600, 8500, etc.), the MESH is capable of synchronous
+	  operation at up to 10 MB/s. On machines where the SCSI bus
+	  controlled by the MESH can have external devices connected, it is
+	  usually rated at 5 MB/s. 5 is a safe value here unless you know the
+	  MESH SCSI bus is internal only; in that case you can say 10. Say 0
+	  to disable synchronous operation.
+
+config SCSI_MESH_RESET_DELAY_MS
+	int "initial bus reset delay (ms) (0 = no reset)"
+	depends on SCSI_MESH
+	default "4000"
+
+config SCSI_MAC53C94
+	tristate "53C94 (Power Mac external SCSI) support"
+	depends on PPC32 && PPC_PMAC && SCSI
+	help
+	  On Power Macintoshes (and clones) with two SCSI buses, the external
+	  SCSI bus is usually controlled by a 53C94 SCSI bus adaptor. Older
+	  machines which only have one SCSI bus, such as the 7200, also use
+	  the 53C94. Say Y to include support for the 53C94.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mac53c94.
+
+source "drivers/scsi/arm/Kconfig"
+
+config JAZZ_ESP
+	bool "MIPS JAZZ FAS216 SCSI support"
+	depends on MACH_JAZZ && SCSI
+	help
+	  This is the driver for the onboard SCSI host adapter of MIPS Magnum
+	  4000, Acer PICA, Olivetti M700-10 and a few other identical OEM
+	  systems.
+
+config A3000_SCSI
+	tristate "A3000 WD33C93A support"
+	depends on AMIGA && SCSI
+	help
+	  If you have an Amiga 3000 and have SCSI devices connected to the
+	  built-in SCSI controller, say Y. Otherwise, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called wd33c93.
+
+config A2091_SCSI
+	tristate "A2091/A590 WD33C93A support"
+	depends on ZORRO && SCSI
+	help
+	  If you have a Commodore A2091 SCSI controller, say Y. Otherwise,
+	  say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called wd33c93.
+
+config GVP11_SCSI
+	tristate "GVP Series II WD33C93A support"
+	depends on ZORRO && SCSI
+	---help---
+	  If you have a Great Valley Products Series II SCSI controller,
+	  answer Y. Also say Y if you have a later model of GVP SCSI
+	  controller (such as the GVP A4008 or a Combo board). Otherwise,
+	  answer N. This driver does NOT work for the T-Rex series of
+	  accelerators from TekMagic and GVP-M.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gvp11.
+
+config CYBERSTORM_SCSI
+	tristate "CyberStorm SCSI support"
+	depends on ZORRO && SCSI
+	help
+	  If you have an Amiga with an original (MkI) Phase5 Cyberstorm
+	  accelerator board and the optional Cyberstorm SCSI controller,
+	  answer Y. Otherwise, say N.
+
+config CYBERSTORMII_SCSI
+	tristate "CyberStorm Mk II SCSI support"
+	depends on ZORRO && SCSI
+	help
+	  If you have an Amiga with a Phase5 Cyberstorm MkII accelerator board
+	  and the optional Cyberstorm SCSI controller, say Y. Otherwise,
+	  answer N.
+
+config BLZ2060_SCSI
+	tristate "Blizzard 2060 SCSI support"
+	depends on ZORRO && SCSI
+	help
+	  If you have an Amiga with a Phase5 Blizzard 2060 accelerator board
+	  and want to use the onboard SCSI controller, say Y. Otherwise,
+	  answer N.
+
+config BLZ1230_SCSI
+	tristate "Blizzard 1230IV/1260 SCSI support"
+	depends on ZORRO && SCSI
+	help
+	  If you have an Amiga 1200 with a Phase5 Blizzard 1230IV or Blizzard
+	  1260 accelerator, and the optional SCSI module, say Y. Otherwise,
+	  say N.
+
+config FASTLANE_SCSI
+	tristate "Fastlane SCSI support"
+	depends on ZORRO && SCSI
+	help
+	  If you have the Phase5 Fastlane Z3 SCSI controller, or plan to use
+	  one in the near future, say Y to this question. Otherwise, say N.
+
+config SCSI_AMIGA7XX
+	bool "Amiga NCR53c710 SCSI support (EXPERIMENTAL)"
+	depends on AMIGA && SCSI && EXPERIMENTAL && BROKEN
+	help
+	  Support for various NCR53c710-based SCSI controllers on the Amiga.
+	  This includes:
+	    - the builtin SCSI controller on the Amiga 4000T,
+	    - the Amiga 4091 Zorro III SCSI-2 controller,
+	    - the MacroSystem Development's WarpEngine Amiga SCSI-2 controller
+	      (info at
+	      <http://www.lysator.liu.se/amiga/ar/guide/ar310.guide?FEATURE5>),
+	    - the SCSI controller on the Phase5 Blizzard PowerUP 603e+
+	      accelerator card for the Amiga 1200,
+	    - the SCSI controller on the GVP Turbo 040/060 accelerator.
+	  Note that all of the above SCSI controllers, except for the builtin
+	  SCSI controller on the Amiga 4000T, reside on the Zorro expansion
+	  bus, so you also have to enable Zorro bus support if you want to use
+	  them.
+
+config OKTAGON_SCSI
+	tristate "BSC Oktagon SCSI support (EXPERIMENTAL)"
+	depends on ZORRO && SCSI && EXPERIMENTAL
+	help
+	  If you have the BSC Oktagon SCSI disk controller for the Amiga, say
+	  Y to this question.  If you're in doubt about whether you have one,
+	  see the picture at
+	  <http://amiga.resource.cx/exp/search.pl?product=oktagon>.
+
+config ATARI_SCSI
+	tristate "Atari native SCSI support"
+	depends on ATARI && SCSI && BROKEN
+	---help---
+	  If you have an Atari with built-in NCR5380 SCSI controller (TT,
+	  Falcon, ...) say Y to get it supported. Of course also, if you have
+	  a compatible SCSI controller (e.g. for Medusa).
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called atari_scsi.
+
+	  This driver supports both styles of NCR integration into the
+	  system: the TT style (separate DMA), and the Falcon style (via
+	  ST-DMA, replacing ACSI).  It does NOT support other schemes, like
+	  in the Hades (without DMA).
+
+config ATARI_SCSI_TOSHIBA_DELAY
+	bool "Long delays for Toshiba CD-ROMs"
+	depends on ATARI_SCSI
+	help
+	  This option increases the delay after a SCSI arbitration to
+	  accommodate some flaky Toshiba CD-ROM drives. Say Y if you intend to
+	  use a Toshiba CD-ROM drive; otherwise, the option is not needed and
+	  would impact performance a bit, so say N.
+
+config ATARI_SCSI_RESET_BOOT
+	bool "Reset SCSI-devices at boottime"
+	depends on ATARI_SCSI
+	help
+	  Reset the devices on your Atari whenever it boots.  This makes the
+	  boot process fractionally longer but may assist recovery from errors
+	  that leave the devices with SCSI operations partway completed.
+
+config TT_DMA_EMUL
+	bool "Hades SCSI DMA emulator"
+	depends on ATARI_SCSI && HADES
+	help
+	  This option enables code which emulates the TT SCSI DMA chip on the
+	  Hades. This increases the SCSI transfer rates at least ten times
+	  compared to PIO transfers.
+
+config MAC_SCSI
+	bool "Macintosh NCR5380 SCSI"
+	depends on MAC && SCSI=y
+	help
+	  This is the NCR 5380 SCSI controller included on most of the 68030
+	  based Macintoshes.  If you have one of these say Y and read the
+	  SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+config SCSI_MAC_ESP
+	tristate "Macintosh NCR53c9[46] SCSI"
+	depends on MAC && SCSI
+	help
+	  This is the NCR 53c9x SCSI controller found on most of the 68040
+	  based Macintoshes.  If you have one of these say Y and read the
+	  SCSI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mac_esp.
+
+config MVME147_SCSI
+	bool "WD33C93 SCSI driver for MVME147"
+	depends on MVME147 && SCSI=y
+	help
+	  Support for the on-board SCSI controller on the Motorola MVME147
+	  single-board computer.
+
+config MVME16x_SCSI
+	bool "NCR53C710 SCSI driver for MVME16x"
+	depends on MVME16x && SCSI && BROKEN
+	help
+	  The Motorola MVME162, 166, 167, 172 and 177 boards use the NCR53C710
+	  SCSI controller chip.  Almost everyone using one of these boards
+	  will want to say Y to this question.
+
+config BVME6000_SCSI
+	bool "NCR53C710 SCSI driver for BVME6000"
+	depends on BVME6000 && SCSI && BROKEN
+	help
+	  The BVME4000 and BVME6000 boards from BVM Ltd use the NCR53C710
+	  SCSI controller chip.  Almost everyone using one of these boards
+	  will want to say Y to this question.
+
+config SCSI_NCR53C7xx_FAST
+	bool "allow FAST-SCSI [10MHz]"
+	depends on SCSI_AMIGA7XX || MVME16x_SCSI || BVME6000_SCSI
+	help
+	  This will enable 10MHz FAST-SCSI transfers with your host
+	  adapter. Some systems have problems with that speed, so it's safest
+	  to say N here.
+
+config SUN3_SCSI
+	tristate "Sun3 NCR5380 SCSI"
+	depends on SUN3 && SCSI && BROKEN
+	help
+	  This option will enable support for the OBIO (onboard io) NCR5380
+	  SCSI controller found in the Sun 3/50 and 3/60, as well as for
+	  "Sun3" type VME scsi controllers also based on the NCR5380.
+	  General Linux information on the Sun 3 series (now discontinued)
+	  is at <http://www.angelfire.com/ca2/tech68k/sun3.html>.
+
+config SUN3X_ESP
+	bool "Sun3x ESP SCSI"
+	depends on SUN3X && SCSI=y
+	help
+	  The ESP was an on-board SCSI controller used on Sun 3/80
+	  machines.  Say Y here to compile in support for it.
+
+config SCSI_SUNESP
+	tristate "Sparc ESP Scsi Driver"
+	depends on SBUS && SCSI
+	help
+	  This is the driver for the Sun ESP SCSI host adapter. The ESP
+	  chipset is present in most SPARC SBUS-based computers.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called esp.
+
+#      bool 'Cyberstorm Mk III SCSI support (EXPERIMENTAL)' CONFIG_CYBERSTORMIII_SCSI
+
+config ZFCP
+	tristate "FCP host bus adapter driver for IBM eServer zSeries"
+	depends on ARCH_S390 && QDIO && SCSI
+	select SCSI_FC_ATTRS
+	help
+          If you want to access SCSI devices attached to your IBM eServer
+          zSeries by means of Fibre Channel interfaces say Y.
+          For details please refer to the documentation provided by IBM at
+          <http://oss.software.ibm.com/developerworks/opensource/linux390>
+
+          This driver is also available as a module. This module will be
+          called zfcp. If you want to compile it as a module, say M here
+          and read <file:Documentation/modules.txt>.
+
+endmenu
+
+source "drivers/scsi/pcmcia/Kconfig"
+
+endmenu
diff -urN oldtree/drivers/scsi/Makefile newtree/drivers/scsi/Makefile
--- oldtree/drivers/scsi/Makefile	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/scsi/Makefile	2006-02-13 19:20:00.564426496 -0500
@@ -32,6 +32,7 @@
 obj-$(CONFIG_SCSI_FC_ATTRS) 	+= scsi_transport_fc.o
 obj-$(CONFIG_SCSI_ISCSI_ATTRS)	+= scsi_transport_iscsi.o
 obj-$(CONFIG_SCSI_SAS_ATTRS)	+= scsi_transport_sas.o
+obj-$(CONFIG_SAS_CLASS)		+= sas/
 
 obj-$(CONFIG_ISCSI_TCP) 	+= iscsi_tcp.o
 obj-$(CONFIG_SCSI_AMIGA7XX)	+= amiga7xx.o	53c7xx.o
@@ -58,6 +59,7 @@
 obj-$(CONFIG_SCSI_BUSLOGIC)	+= BusLogic.o
 obj-$(CONFIG_SCSI_DPT_I2O)	+= dpt_i2o.o
 obj-$(CONFIG_SCSI_U14_34F)	+= u14-34f.o
+obj-$(CONFIG_SCSI_ARCMSR)	+= arcmsr/
 obj-$(CONFIG_SCSI_ULTRASTOR)	+= ultrastor.o
 obj-$(CONFIG_SCSI_AHA152X)	+= aha152x.o
 obj-$(CONFIG_SCSI_AHA1542)	+= aha1542.o
@@ -66,6 +68,7 @@
 obj-$(CONFIG_SCSI_AIC79XX)	+= aic7xxx/
 obj-$(CONFIG_SCSI_AACRAID)	+= aacraid/
 obj-$(CONFIG_SCSI_AIC7XXX_OLD)	+= aic7xxx_old.o
+obj-$(CONFIG_SCSI_AIC94XX)	+= aic94xx/
 obj-$(CONFIG_SCSI_IPS)		+= ips.o
 obj-$(CONFIG_SCSI_FD_MCS)	+= fd_mcs.o
 obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o
diff -urN oldtree/drivers/scsi/Makefile.orig newtree/drivers/scsi/Makefile.orig
--- oldtree/drivers/scsi/Makefile.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/Makefile.orig	2006-02-13 19:20:00.565426344 -0500
@@ -0,0 +1,189 @@
+#
+# Makefile for linux/drivers/scsi
+#
+# 30 May 2000, Christoph Hellwig <hch@infradead.org>
+# Rewritten to use lists instead of if-statements.
+#
+# 20 Sep 2000, Torben Mathiasen <tmm@image.dk>
+# Changed link order to reflect new scsi initialization.
+#
+# *!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!
+# The link order must be, SCSI Core, SCSI HBA drivers, and
+# lastly SCSI peripheral drivers (disk/tape/cdrom/etc.) to
+# satisfy certain initialization assumptions in the SCSI layer.
+# *!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!
+
+
+CFLAGS_aha152x.o =   -DAHA152X_STAT -DAUTOCONF
+CFLAGS_gdth.o    = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS
+CFLAGS_seagate.o =   -DARBITRATE -DPARITY -DSEAGATE_USE_ASM
+
+subdir-$(CONFIG_PCMCIA)		+= pcmcia
+
+obj-$(CONFIG_SCSI)		+= scsi_mod.o
+
+obj-$(CONFIG_RAID_ATTRS)	+= raid_class.o
+
+# --- NOTE ORDERING HERE ---
+# For kernel non-modular link, transport attributes need to
+# be initialised before drivers
+# --------------------------
+obj-$(CONFIG_SCSI_SPI_ATTRS)	+= scsi_transport_spi.o
+obj-$(CONFIG_SCSI_FC_ATTRS) 	+= scsi_transport_fc.o
+obj-$(CONFIG_SCSI_ISCSI_ATTRS)	+= scsi_transport_iscsi.o
+obj-$(CONFIG_SCSI_SAS_ATTRS)	+= scsi_transport_sas.o
+
+obj-$(CONFIG_ISCSI_TCP) 	+= iscsi_tcp.o
+obj-$(CONFIG_SCSI_AMIGA7XX)	+= amiga7xx.o	53c7xx.o
+obj-$(CONFIG_A3000_SCSI)	+= a3000.o	wd33c93.o
+obj-$(CONFIG_A2091_SCSI)	+= a2091.o	wd33c93.o
+obj-$(CONFIG_GVP11_SCSI)	+= gvp11.o	wd33c93.o
+obj-$(CONFIG_MVME147_SCSI)	+= mvme147.o	wd33c93.o
+obj-$(CONFIG_SGIWD93_SCSI)	+= sgiwd93.o	wd33c93.o
+obj-$(CONFIG_CYBERSTORM_SCSI)	+= NCR53C9x.o	cyberstorm.o
+obj-$(CONFIG_CYBERSTORMII_SCSI)	+= NCR53C9x.o	cyberstormII.o
+obj-$(CONFIG_BLZ2060_SCSI)	+= NCR53C9x.o	blz2060.o
+obj-$(CONFIG_BLZ1230_SCSI)	+= NCR53C9x.o	blz1230.o
+obj-$(CONFIG_FASTLANE_SCSI)	+= NCR53C9x.o	fastlane.o
+obj-$(CONFIG_OKTAGON_SCSI)	+= NCR53C9x.o	oktagon_esp.o	oktagon_io.o
+obj-$(CONFIG_ATARI_SCSI)	+= atari_scsi.o
+obj-$(CONFIG_MAC_SCSI)		+= mac_scsi.o
+obj-$(CONFIG_SCSI_MAC_ESP)	+= mac_esp.o	NCR53C9x.o
+obj-$(CONFIG_SUN3_SCSI)		+= sun3_scsi.o  sun3_scsi_vme.o
+obj-$(CONFIG_MVME16x_SCSI)	+= mvme16x.o	53c7xx.o
+obj-$(CONFIG_BVME6000_SCSI)	+= bvme6000.o	53c7xx.o
+obj-$(CONFIG_SCSI_SIM710)	+= 53c700.o	sim710.o
+obj-$(CONFIG_SCSI_ADVANSYS)	+= advansys.o
+obj-$(CONFIG_SCSI_PSI240I)	+= psi240i.o
+obj-$(CONFIG_SCSI_BUSLOGIC)	+= BusLogic.o
+obj-$(CONFIG_SCSI_DPT_I2O)	+= dpt_i2o.o
+obj-$(CONFIG_SCSI_U14_34F)	+= u14-34f.o
+obj-$(CONFIG_SCSI_ARCMSR)	+= arcmsr/
+obj-$(CONFIG_SCSI_ULTRASTOR)	+= ultrastor.o
+obj-$(CONFIG_SCSI_AHA152X)	+= aha152x.o
+obj-$(CONFIG_SCSI_AHA1542)	+= aha1542.o
+obj-$(CONFIG_SCSI_AHA1740)	+= aha1740.o
+obj-$(CONFIG_SCSI_AIC7XXX)	+= aic7xxx/
+obj-$(CONFIG_SCSI_AIC79XX)	+= aic7xxx/
+obj-$(CONFIG_SCSI_AACRAID)	+= aacraid/
+obj-$(CONFIG_SCSI_AIC7XXX_OLD)	+= aic7xxx_old.o
+obj-$(CONFIG_SCSI_IPS)		+= ips.o
+obj-$(CONFIG_SCSI_FD_MCS)	+= fd_mcs.o
+obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o
+obj-$(CONFIG_SCSI_IN2000)	+= in2000.o
+obj-$(CONFIG_SCSI_GENERIC_NCR5380) += g_NCR5380.o
+obj-$(CONFIG_SCSI_GENERIC_NCR5380_MMIO) += g_NCR5380_mmio.o
+obj-$(CONFIG_SCSI_NCR53C406A)	+= NCR53c406a.o
+obj-$(CONFIG_SCSI_NCR_D700)	+= 53c700.o NCR_D700.o
+obj-$(CONFIG_SCSI_NCR_Q720)	+= NCR_Q720_mod.o
+obj-$(CONFIG_SCSI_SYM53C416)	+= sym53c416.o
+obj-$(CONFIG_SCSI_QLOGIC_FAS)	+= qlogicfas408.o	qlogicfas.o
+obj-$(CONFIG_PCMCIA_QLOGIC)	+= qlogicfas408.o
+obj-$(CONFIG_SCSI_QLOGIC_FC)	+= qlogicfc.o 
+obj-$(CONFIG_SCSI_QLOGIC_1280)	+= qla1280.o 
+obj-$(CONFIG_SCSI_QLA2XXX)	+= qla2xxx/
+obj-$(CONFIG_SCSI_LPFC)		+= lpfc/
+obj-$(CONFIG_SCSI_PAS16)	+= pas16.o
+obj-$(CONFIG_SCSI_SEAGATE)	+= seagate.o
+obj-$(CONFIG_SCSI_FD_8xx)	+= seagate.o
+obj-$(CONFIG_SCSI_T128)		+= t128.o
+obj-$(CONFIG_SCSI_DMX3191D)	+= dmx3191d.o
+obj-$(CONFIG_SCSI_DTC3280)	+= dtc.o
+obj-$(CONFIG_SCSI_SYM53C8XX_2)	+= sym53c8xx_2/
+obj-$(CONFIG_SCSI_ZALON)	+= zalon7xx.o
+obj-$(CONFIG_SCSI_EATA_PIO)	+= eata_pio.o
+obj-$(CONFIG_SCSI_7000FASST)	+= wd7000.o
+obj-$(CONFIG_SCSI_MCA_53C9X)	+= NCR53C9x.o	mca_53c9x.o
+obj-$(CONFIG_SCSI_IBMMCA)	+= ibmmca.o
+obj-$(CONFIG_SCSI_EATA)		+= eata.o
+obj-$(CONFIG_SCSI_DC395x)	+= dc395x.o
+obj-$(CONFIG_SCSI_DC390T)	+= tmscsim.o
+obj-$(CONFIG_MEGARAID_LEGACY)	+= megaraid.o
+obj-$(CONFIG_MEGARAID_NEWGEN)	+= megaraid/
+obj-$(CONFIG_MEGARAID_SAS)	+= megaraid/
+obj-$(CONFIG_SCSI_ACARD)	+= atp870u.o
+obj-$(CONFIG_SCSI_SUNESP)	+= esp.o
+obj-$(CONFIG_SCSI_GDTH)		+= gdth.o
+obj-$(CONFIG_SCSI_INITIO)	+= initio.o
+obj-$(CONFIG_SCSI_INIA100)	+= a100u2w.o
+obj-$(CONFIG_SCSI_QLOGICPTI)	+= qlogicpti.o
+obj-$(CONFIG_BLK_DEV_IDESCSI)	+= ide-scsi.o
+obj-$(CONFIG_SCSI_MESH)		+= mesh.o
+obj-$(CONFIG_SCSI_MAC53C94)	+= mac53c94.o
+obj-$(CONFIG_SCSI_PLUTO)	+= pluto.o
+obj-$(CONFIG_SCSI_DECNCR)	+= NCR53C9x.o	dec_esp.o
+obj-$(CONFIG_BLK_DEV_3W_XXXX_RAID) += 3w-xxxx.o
+obj-$(CONFIG_SCSI_3W_9XXX)	+= 3w-9xxx.o
+obj-$(CONFIG_SCSI_PPA)		+= ppa.o
+obj-$(CONFIG_SCSI_IMM)		+= imm.o
+obj-$(CONFIG_JAZZ_ESP)		+= NCR53C9x.o	jazz_esp.o
+obj-$(CONFIG_SUN3X_ESP)		+= NCR53C9x.o	sun3x_esp.o
+obj-$(CONFIG_SCSI_DEBUG)	+= scsi_debug.o
+obj-$(CONFIG_SCSI_FCAL)		+= fcal.o
+obj-$(CONFIG_SCSI_LASI700)	+= 53c700.o lasi700.o
+obj-$(CONFIG_SCSI_NSP32)	+= nsp32.o
+obj-$(CONFIG_SCSI_IPR)		+= ipr.o
+obj-$(CONFIG_SCSI_IBMVSCSI)	+= ibmvscsi/
+obj-$(CONFIG_SCSI_SATA_AHCI)	+= libata.o ahci.o
+obj-$(CONFIG_SCSI_SATA_SVW)	+= libata.o sata_svw.o
+obj-$(CONFIG_SCSI_ATA_PIIX)	+= libata.o ata_piix.o
+obj-$(CONFIG_SCSI_SATA_PROMISE)	+= libata.o sata_promise.o
+obj-$(CONFIG_SCSI_SATA_QSTOR)	+= libata.o sata_qstor.o
+obj-$(CONFIG_SCSI_SATA_SIL)	+= libata.o sata_sil.o
+obj-$(CONFIG_SCSI_SATA_SIL24)	+= libata.o sata_sil24.o
+obj-$(CONFIG_SCSI_SATA_VIA)	+= libata.o sata_via.o
+obj-$(CONFIG_SCSI_SATA_VITESSE)	+= libata.o sata_vsc.o
+obj-$(CONFIG_SCSI_SATA_SIS)	+= libata.o sata_sis.o
+obj-$(CONFIG_SCSI_SATA_SX4)	+= libata.o sata_sx4.o
+obj-$(CONFIG_SCSI_SATA_NV)	+= libata.o sata_nv.o
+obj-$(CONFIG_SCSI_SATA_ULI)	+= libata.o sata_uli.o
+obj-$(CONFIG_SCSI_SATA_MV)	+= libata.o sata_mv.o
+obj-$(CONFIG_SCSI_PDC_ADMA)	+= libata.o pdc_adma.o
+
+obj-$(CONFIG_ARM)		+= arm/
+
+obj-$(CONFIG_CHR_DEV_ST)	+= st.o
+obj-$(CONFIG_CHR_DEV_OSST)	+= osst.o
+obj-$(CONFIG_BLK_DEV_SD)	+= sd_mod.o
+obj-$(CONFIG_BLK_DEV_SR)	+= sr_mod.o
+obj-$(CONFIG_CHR_DEV_SG)	+= sg.o
+obj-$(CONFIG_CHR_DEV_SCH)	+= ch.o
+
+scsi_mod-y			+= scsi.o hosts.o scsi_ioctl.o constants.o \
+				   scsicam.o scsi_error.o scsi_lib.o \
+				   scsi_scan.o scsi_sysfs.o \
+				   scsi_devinfo.o
+scsi_mod-$(CONFIG_SYSCTL)	+= scsi_sysctl.o
+scsi_mod-$(CONFIG_SCSI_PROC_FS)	+= scsi_proc.o
+
+sd_mod-objs	:= sd.o
+sr_mod-objs	:= sr.o sr_ioctl.o sr_vendor.o
+ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \
+		:= -DCONFIG_NCR53C8XX_PREFETCH -DSCSI_NCR_BIG_ENDIAN \
+			-DCONFIG_SCSI_NCR53C8XX_NO_WORD_TRANSFERS
+CFLAGS_ncr53c8xx.o	:= $(ncr53c8xx-flags-y) $(ncr53c8xx-flags-m)
+zalon7xx-objs	:= zalon.o ncr53c8xx.o
+NCR_Q720_mod-objs	:= NCR_Q720.o ncr53c8xx.o
+libata-objs	:= libata-core.o libata-scsi.o
+
+# Files generated that shall be removed upon make clean
+clean-files :=	53c7xx_d.h 53c700_d.h	\
+		53c7xx_u.h 53c700_u.h
+
+$(obj)/53c7xx.o:   $(obj)/53c7xx_d.h $(obj)/53c7xx_u.h
+$(obj)/53c700.o $(MODVERDIR)/$(obj)/53c700.ver: $(obj)/53c700_d.h
+
+# If you want to play with the firmware, uncomment
+# GENERATE_FIRMWARE := 1
+
+ifdef GENERATE_FIRMWARE
+
+$(obj)/53c7xx_d.h: $(src)/53c7xx.scr $(src)/script_asm.pl
+	$(CPP) -traditional -DCHIP=710 - < $< | grep -v '^#' | $(PERL) -s $(src)/script_asm.pl -ncr7x0_family $@ $(@:_d.h=_u.h)
+
+$(obj)/53c7xx_u.h: $(obj)/53c7xx_d.h
+
+$(obj)/53c700_d.h: $(src)/53c700.scr $(src)/script_asm.pl
+	$(PERL) -s $(src)/script_asm.pl -ncr7x0_family $@ $(@:_d.h=_u.h) < $<
+
+endif
diff -urN oldtree/drivers/scsi/aic94xx/Kconfig newtree/drivers/scsi/aic94xx/Kconfig
--- oldtree/drivers/scsi/aic94xx/Kconfig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/Kconfig	2006-02-13 19:20:00.565426344 -0500
@@ -0,0 +1,41 @@
+#
+# Kernel configuration file for aic94xx SAS/SATA driver.
+#
+# Copyright (c) 2005 Adaptec, Inc.  All rights reserved.
+# Copyright (c) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+#
+# This file is licensed under GPLv2.
+#
+# This file is part of the aic94xx driver.
+#
+# The aic94xx driver is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 of the
+# License.
+#
+# The aic94xx driver is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Aic94xx Driver; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#
+# $Id: //depot/aic94xx/Kconfig#9 $
+#
+
+config SCSI_AIC94XX
+	tristate "Adaptec AIC94xx SAS/SATA support"
+	depends on PCI && SAS_CLASS
+	help
+		This driver supports Adaptec's SAS/SATA 3Gb/s 64 bit PCI-X
+		AIC94xx chip based host adapters.
+
+config AIC94XX_DEBUG
+	bool "Compile in debug mode"
+	default y
+	depends on SCSI_AIC94XX
+	help
+		Compiles the aic94xx driver in debug mode.  In debug mode,
+		the driver prints some messages to the console.
diff -urN oldtree/drivers/scsi/aic94xx/Makefile newtree/drivers/scsi/aic94xx/Makefile
--- oldtree/drivers/scsi/aic94xx/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/Makefile	2006-02-13 19:20:00.567426040 -0500
@@ -0,0 +1,42 @@
+#
+# Makefile for Adaptec aic94xx SAS/SATA driver.
+#
+# Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+# Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+#
+# This file is licensed under GPLv2.
+#
+# This file is part of the the aic94xx driver.
+#
+# The aic94xx driver is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 of the
+# License.
+#
+# The aic94xx driver is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with the aic94xx driver; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#
+# $Id: //depot/aic94xx/Makefile#37 $
+#
+
+ifeq ($(CONFIG_AIC94XX_DEBUG),y)
+	EXTRA_CFLAGS += -DASD_DEBUG -DASD_ENTER_EXIT -g
+endif
+
+obj-$(CONFIG_SCSI_AIC94XX) += aic94xx.o
+aic94xx-y += aic94xx_init.o \
+	     aic94xx_hwi.o  \
+	     aic94xx_reg.o  \
+	     aic94xx_sds.o  \
+	     aic94xx_seq.o  \
+	     aic94xx_dump.o \
+	     aic94xx_scb.o  \
+	     aic94xx_dev.o  \
+	     aic94xx_tmf.o  \
+	     aic94xx_task.o
diff -urN oldtree/drivers/scsi/aic94xx/README newtree/drivers/scsi/aic94xx/README
--- oldtree/drivers/scsi/aic94xx/README	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/README	2006-02-13 19:20:00.567426040 -0500
@@ -0,0 +1,82 @@
+$Id: //depot/aic94xx/README#2 $
+
+Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+
+LICENSE AGREEMENT
+-----------------
+
+This file is licensed under GPLv2.
+
+This file is part of the aic94xx driver.
+
+The aic94xx driver is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public
+License as published by the Free Software Foundation;
+version 2 of the License.
+
+The aic94xx driver is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Aic94xx Driver; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+Introduction
+------------
+
+The aic94xx driver supports Adaptec SAS/SATA Host Adapters
+with AIC-94xx chips.  Features include 32/64 bit PCI/PCI-X
+interface supporting up to 66 MHz PCI 2.3 and up to 133 MHz
+PCI-X 2.0 operation.  It supports MSI or PCI interrupt pin.
+      * AIC-9405W is a  4 phy host adapter, 4 port maximum.
+      * AIC-9410W is an 8 phy host adapter, 8 port maximum.
+
+The driver supports Revision B0 or later of the controller.
+
+
+Hot plugging
+------------
+
+Hot plugging is supported, it works and has been tested.
+
+
+Dynamic PCI IDs
+---------------
+
+To make the driver probe and attach to a new device it
+doesn't quite recognize, you can dynamically add to the PCI
+ID table of the driver as follows:
+
+echo "vendor device" > /sys/bus/pci/drivers/aic94xx/new_id
+
+For the complete details see Documentation/pci.txt file.
+
+
+Not a HostRAID driver
+---------------------
+
+This driver is _not_ a HostRAID driver, it does _not_
+provide RAID capabilities.  Although RAID could be built on
+top of the devices exported by this driver.
+
+If you would like this driver to attach to a HostRAID
+enabled controller*, set the "attach_HostRAID" module
+parameter to "1" either at module load time or through sysfs.
+
+Kernel command line; append this:
+
+       aic94xx.attach_HostRAID=1
+
+Module loading, append this to your modprobe/insmod:
+
+       attach_HostRAID=1
+
+Or through sysfs, if the driver has already been loaded:
+
+echo "1" > /sys/module/aic94xx/parameters/attach_HostRAID
+
+* This of course could COMPLETELY destroy your HostRAID
+  array, and is NOT recommended.
diff -urN oldtree/drivers/scsi/aic94xx/aic94xx.h newtree/drivers/scsi/aic94xx/aic94xx.h
--- oldtree/drivers/scsi/aic94xx/aic94xx.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/aic94xx.h	2006-02-13 19:20:00.568425888 -0500
@@ -0,0 +1,114 @@
+/*
+ * Aic94xx SAS/SATA driver header file.
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/aic94xx/aic94xx.h#31 $
+ */
+
+#ifndef _AIC94XX_H_
+#define _AIC94XX_H_
+
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <scsi/sas/sas_discover.h>
+
+#define ASD_DRIVER_NAME		"aic94xx"
+#define ASD_DRIVER_DESCRIPTION	"Adaptec aic94xx SAS/SATA driver"
+
+#define asd_printk(fmt, ...)	printk(KERN_NOTICE ASD_DRIVER_NAME ": " fmt, ## __VA_ARGS__)
+
+#ifdef ASD_ENTER_EXIT
+#define ENTER  printk(KERN_NOTICE "%s: ENTER %s\n", ASD_DRIVER_NAME, \
+		__FUNCTION__)
+#define EXIT   printk(KERN_NOTICE "%s: --EXIT %s\n", ASD_DRIVER_NAME, \
+		__FUNCTION__)
+#else
+#define ENTER
+#define EXIT
+#endif
+
+#ifdef ASD_DEBUG
+#define ASD_DPRINTK asd_printk
+#else
+#define ASD_DPRINTK(fmt, ...)
+#endif
+
+/* 2*ITNL timeout + 1 second */
+#define AIC94XX_SCB_TIMEOUT  (5*HZ)
+
+extern kmem_cache_t *asd_dma_token_cache;
+extern kmem_cache_t *asd_ascb_cache;
+extern char sas_addr_str[2*SAS_ADDR_SIZE + 1];
+
+static inline void asd_stringify_sas_addr(char *p, const u8 *sas_addr)
+{
+	int i;
+	for (i = 0; i < SAS_ADDR_SIZE; i++, p += 2)
+		snprintf(p, 3, "%02X", sas_addr[i]);
+	*p = '\0';
+}
+
+static inline void asd_destringify_sas_addr(u8 *sas_addr, const char *p)
+{
+	int i;
+	for (i = 0; i < SAS_ADDR_SIZE; i++) {
+		u8 h, l;
+		if (!*p)
+			break;
+		h = isdigit(*p) ? *p-'0' : *p-'A'+10;
+		p++;
+		l = isdigit(*p) ? *p-'0' : *p-'A'+10;
+		p++;
+		sas_addr[i] = (h<<4) | l;
+	}
+}
+
+struct asd_ha_struct;
+struct asd_ascb;
+
+int  asd_read_ocm(struct asd_ha_struct *asd_ha);
+int  asd_read_flash(struct asd_ha_struct *asd_ha);
+
+int  asd_dev_found(struct domain_device *dev);
+void asd_dev_gone(struct domain_device *dev);
+
+void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id);
+
+int  asd_execute_task(struct sas_task *, int num, unsigned long gfp_flags);
+
+/* ---------- TMFs ---------- */
+int  asd_abort_task(struct sas_task *);
+int  asd_abort_task_set(struct domain_device *, u8 *lun);
+int  asd_clear_aca(struct domain_device *, u8 *lun);
+int  asd_clear_task_set(struct domain_device *, u8 *lun);
+int  asd_lu_reset(struct domain_device *, u8 *lun);
+int  asd_query_task(struct sas_task *);
+
+/* ---------- Adapter and Port management ---------- */
+int  asd_clear_nexus_port(struct sas_port *port);
+int  asd_clear_nexus_ha(struct sas_ha_struct *sas_ha);
+
+/* ---------- Phy Management ---------- */
+int  asd_control_phy(struct sas_phy *phy, enum phy_func func);
+
+#endif
diff -urN oldtree/drivers/scsi/aic94xx/aic94xx_dev.c newtree/drivers/scsi/aic94xx/aic94xx_dev.c
--- oldtree/drivers/scsi/aic94xx/aic94xx_dev.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/aic94xx_dev.c	2006-02-13 19:20:00.568425888 -0500
@@ -0,0 +1,350 @@
+/*
+ * Aic94xx SAS/SATA DDB management
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/aic94xx/aic94xx_dev.c#21 $
+ */
+
+#include "aic94xx.h"
+#include "aic94xx_hwi.h"
+#include "aic94xx_reg.h"
+#include "aic94xx_sas.h"
+
+#define FIND_FREE_DDB(_ha) find_first_zero_bit((_ha)->hw_prof.ddb_bitmap, \
+					       (_ha)->hw_prof.max_ddbs)
+#define SET_DDB(_ddb, _ha) set_bit(_ddb, (_ha)->hw_prof.ddb_bitmap)
+#define CLEAR_DDB(_ddb, _ha) clear_bit(_ddb, (_ha)->hw_prof.ddb_bitmap)
+
+static inline int asd_get_ddb(struct asd_ha_struct *asd_ha)
+{
+	unsigned long flags;
+	int ddb, i;
+
+	spin_lock_irqsave(&asd_ha->hw_prof.ddb_lock, flags);
+	ddb = FIND_FREE_DDB(asd_ha);
+	if (ddb >= asd_ha->hw_prof.max_ddbs) {
+		ddb = -ENOMEM;
+		spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags);
+		goto out;
+	}
+	SET_DDB(ddb, asd_ha);
+	spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags);
+
+	for (i = 0; i < sizeof(struct asd_ddb_ssp_smp_target_port); i+= 4)
+		asd_ddbsite_write_dword(asd_ha, ddb, i, 0);
+out:
+	return ddb;
+}
+
+#define INIT_CONN_TAG   offsetof(struct asd_ddb_ssp_smp_target_port, init_conn_tag)
+#define DEST_SAS_ADDR   offsetof(struct asd_ddb_ssp_smp_target_port, dest_sas_addr)
+#define SEND_QUEUE_HEAD offsetof(struct asd_ddb_ssp_smp_target_port, send_queue_head)
+#define DDB_TYPE        offsetof(struct asd_ddb_ssp_smp_target_port, ddb_type)
+#define CONN_MASK       offsetof(struct asd_ddb_ssp_smp_target_port, conn_mask)
+#define DDB_TARG_FLAGS  offsetof(struct asd_ddb_ssp_smp_target_port, flags)
+#define DDB_TARG_FLAGS2 offsetof(struct asd_ddb_stp_sata_target_port, flags2)
+#define EXEC_QUEUE_TAIL offsetof(struct asd_ddb_ssp_smp_target_port, exec_queue_tail)
+#define SEND_QUEUE_TAIL offsetof(struct asd_ddb_ssp_smp_target_port, send_queue_tail)
+#define SISTER_DDB      offsetof(struct asd_ddb_ssp_smp_target_port, sister_ddb)
+#define MAX_CCONN       offsetof(struct asd_ddb_ssp_smp_target_port, max_concurrent_conn)
+#define NUM_CTX         offsetof(struct asd_ddb_ssp_smp_target_port, num_contexts)
+#define ATA_CMD_SCBPTR  offsetof(struct asd_ddb_stp_sata_target_port, ata_cmd_scbptr)
+#define SATA_TAG_ALLOC_MASK offsetof(struct asd_ddb_stp_sata_target_port, sata_tag_alloc_mask)
+#define NUM_SATA_TAGS   offsetof(struct asd_ddb_stp_sata_target_port, num_sata_tags)
+#define SATA_STATUS     offsetof(struct asd_ddb_stp_sata_target_port, sata_status)
+#define NCQ_DATA_SCB_PTR offsetof(struct asd_ddb_stp_sata_target_port, ncq_data_scb_ptr)
+#define ITNL_TIMEOUT    offsetof(struct asd_ddb_ssp_smp_target_port, itnl_timeout)
+
+static inline void asd_free_ddb(struct asd_ha_struct *asd_ha, int ddb)
+{
+	unsigned long flags;
+
+	if (!ddb || ddb >= 0xFFFF)
+		return;
+	asd_ddbsite_write_byte(asd_ha, ddb, DDB_TYPE, DDB_TYPE_UNUSED);
+	spin_lock_irqsave(&asd_ha->hw_prof.ddb_lock, flags);
+	CLEAR_DDB(ddb, asd_ha);
+	spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags);
+}
+
+static inline void asd_set_ddb_type(struct domain_device *dev)
+{
+	struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+	int ddb = (int) (unsigned long) dev->lldd_dev;
+
+	if (dev->dev_type == SATA_PM_PORT)
+		asd_ddbsite_write_byte(asd_ha,ddb, DDB_TYPE, DDB_TYPE_PM_PORT);
+	else if (dev->tproto)
+		asd_ddbsite_write_byte(asd_ha,ddb, DDB_TYPE, DDB_TYPE_TARGET);
+	else
+		asd_ddbsite_write_byte(asd_ha,ddb,DDB_TYPE,DDB_TYPE_INITIATOR);
+}
+
+static int asd_init_sata_tag_ddb(struct domain_device *dev)
+{
+	struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+	int ddb, i;
+
+	ddb = asd_get_ddb(asd_ha);
+	if (ddb < 0)
+		return ddb;
+
+	for (i = 0; i < sizeof(struct asd_ddb_sata_tag); i += 2)
+		asd_ddbsite_write_word(asd_ha, ddb, i, 0xFFFF);
+
+	asd_ddbsite_write_word(asd_ha, (int) (unsigned long) dev->lldd_dev,
+			       SISTER_DDB, ddb);
+	return 0;
+}
+
+static inline int asd_init_sata(struct domain_device *dev)
+{
+	struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+	int ddb = (int) (unsigned long) dev->lldd_dev;
+	u32 qdepth = 0;
+	int res = 0;
+
+	asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF);
+	if ((dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM_PORT) &&
+	    dev->sata_dev.identify_device &&
+	    dev->sata_dev.identify_device[10] != 0) {
+		u16 w75 = le16_to_cpu(dev->sata_dev.identify_device[75]);
+		u16 w76 = le16_to_cpu(dev->sata_dev.identify_device[76]);
+
+		if (w76 & 0x100) /* NCQ? */
+			qdepth = (w75 & 0x1F) + 1;
+		asd_ddbsite_write_dword(asd_ha, ddb, SATA_TAG_ALLOC_MASK,
+					(1<<qdepth)-1);
+		asd_ddbsite_write_byte(asd_ha, ddb, NUM_SATA_TAGS, qdepth);
+	}
+	if (dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM ||
+	    dev->dev_type == SATA_PM_PORT) {
+		struct dev_to_host_fis *fis = (struct dev_to_host_fis *)
+			dev->frame_rcvd;
+		asd_ddbsite_write_byte(asd_ha, ddb, SATA_STATUS, fis->status);
+	}
+	asd_ddbsite_write_word(asd_ha, ddb, NCQ_DATA_SCB_PTR, 0xFFFF);
+	if (qdepth > 0)
+		res = asd_init_sata_tag_ddb(dev);
+	return res;
+}
+
+static int asd_init_target_ddb(struct domain_device *dev)
+{
+	int ddb, i;
+	struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+	u8 flags = 0;
+
+	ddb = asd_get_ddb(asd_ha);
+	if (ddb < 0)
+		return ddb;
+
+	dev->lldd_dev = (void *) (unsigned long) ddb;
+
+	asd_ddbsite_write_byte(asd_ha, ddb, 0, DDB_TP_CONN_TYPE);
+	asd_ddbsite_write_byte(asd_ha, ddb, 1, 0);
+	asd_ddbsite_write_word(asd_ha, ddb, INIT_CONN_TAG, 0xFFFF);
+	for (i = 0; i < SAS_ADDR_SIZE; i++)
+		asd_ddbsite_write_byte(asd_ha, ddb, DEST_SAS_ADDR+i,
+				       dev->sas_addr[i]);
+	asd_ddbsite_write_word(asd_ha, ddb, SEND_QUEUE_HEAD, 0xFFFF);
+	asd_set_ddb_type(dev);
+	asd_ddbsite_write_byte(asd_ha, ddb, CONN_MASK, dev->port->phy_mask);
+	if (dev->port->oob_mode != SATA_OOB_MODE) {
+		flags |= OPEN_REQUIRED;
+		if ((dev->dev_type == SATA_DEV) ||
+		    (dev->tproto & SAS_PROTO_STP)) {
+			struct smp_resp *rps_resp = &dev->sata_dev.rps_resp;
+			if (rps_resp->frame_type == SMP_RESPONSE &&
+			    rps_resp->function == SMP_REPORT_PHY_SATA &&
+			    rps_resp->result == SMP_RESP_FUNC_ACC) {
+				if (rps_resp->rps.affil_valid)
+					flags |= STP_AFFIL_POL;
+				if (rps_resp->rps.affil_supp)
+					flags |= SUPPORTS_AFFIL;
+			}
+		} else {
+			flags |= CONCURRENT_CONN_SUPP;
+			if (!dev->parent &&
+			    (dev->dev_type == EDGE_DEV ||
+			     dev->dev_type == FANOUT_DEV))
+				asd_ddbsite_write_byte(asd_ha, ddb, MAX_CCONN,
+						       4);
+			else
+				asd_ddbsite_write_byte(asd_ha, ddb, MAX_CCONN,
+						       dev->pathways);
+			asd_ddbsite_write_byte(asd_ha, ddb, NUM_CTX, 1);
+		}
+	}
+	if (dev->dev_type == SATA_PM)
+		flags |= SATA_MULTIPORT;
+	asd_ddbsite_write_byte(asd_ha, ddb, DDB_TARG_FLAGS, flags);
+
+	flags = 0;
+	if (dev->tproto & SAS_PROTO_STP)
+		flags |= STP_CL_POL_NO_TX;
+	asd_ddbsite_write_byte(asd_ha, ddb, DDB_TARG_FLAGS2, flags);
+
+	asd_ddbsite_write_word(asd_ha, ddb, EXEC_QUEUE_TAIL, 0xFFFF);
+	asd_ddbsite_write_word(asd_ha, ddb, SEND_QUEUE_TAIL, 0xFFFF);
+	asd_ddbsite_write_word(asd_ha, ddb, SISTER_DDB, 0xFFFF);
+
+	if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTO_STP)) {
+		i = asd_init_sata(dev);
+		if (i < 0) {
+			asd_free_ddb(asd_ha, ddb);
+			return i;
+		}
+	}
+
+	if (dev->dev_type == SAS_END_DEV && dev->end_dev.itnl_timeout > 0)
+		asd_ddbsite_write_word(asd_ha, ddb, ITNL_TIMEOUT,
+				       min(dev->end_dev.itnl_timeout,
+					   (u16)ITNL_TIMEOUT_CONST));
+	else
+		asd_ddbsite_write_word(asd_ha, ddb, ITNL_TIMEOUT,
+				       (u16)ITNL_TIMEOUT_CONST);
+	return 0;
+}
+
+static int asd_init_sata_pm_table_ddb(struct domain_device *dev)
+{
+	struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+	int ddb, i;
+
+	ddb = asd_get_ddb(asd_ha);
+	if (ddb < 0)
+		return ddb;
+
+	for (i = 0; i < 32; i += 2)
+		asd_ddbsite_write_word(asd_ha, ddb, i, 0xFFFF);
+
+	asd_ddbsite_write_word(asd_ha, (int) (unsigned long) dev->lldd_dev,
+			       SISTER_DDB, ddb);
+
+	return 0;
+}
+
+#define PM_PORT_FLAGS offsetof(struct asd_ddb_sata_pm_port, pm_port_flags)
+#define PARENT_DDB    offsetof(struct asd_ddb_sata_pm_port, parent_ddb)
+
+/**
+ * asd_init_sata_pm_port_ddb -- SATA Port Multiplier Port
+ * dev: pointer to domain device
+ *
+ * For SATA Port Multiplier Ports we need to allocate one SATA Port
+ * Multiplier Port DDB and depending on whether the target on it
+ * supports SATA II NCQ, one SATA Tag DDB.
+ */
+static int asd_init_sata_pm_port_ddb(struct domain_device *dev)
+{
+	int ddb, i, parent_ddb, pmtable_ddb;
+	struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+	u8  flags;
+
+	ddb = asd_get_ddb(asd_ha);
+	if (ddb < 0)
+		return ddb;
+
+	asd_set_ddb_type(dev);
+	flags = (dev->sata_dev.port_no << 4) | PM_PORT_SET;
+	asd_ddbsite_write_byte(asd_ha, ddb, PM_PORT_FLAGS, flags);
+	asd_ddbsite_write_word(asd_ha, ddb, SISTER_DDB, 0xFFFF);
+	asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF);
+	asd_init_sata(dev);
+
+	parent_ddb = (int) (unsigned long) dev->parent->lldd_dev;
+	asd_ddbsite_write_word(asd_ha, ddb, PARENT_DDB, parent_ddb);
+	pmtable_ddb = asd_ddbsite_read_word(asd_ha, parent_ddb, SISTER_DDB);
+	asd_ddbsite_write_word(asd_ha, pmtable_ddb, dev->sata_dev.port_no,ddb);
+
+	if (asd_ddbsite_read_byte(asd_ha, ddb, NUM_SATA_TAGS) > 0) {
+		i = asd_init_sata_tag_ddb(dev);
+		if (i < 0) {
+			asd_free_ddb(asd_ha, ddb);
+			return i;
+		}
+	}
+	return 0;
+}
+
+static int asd_init_initiator_ddb(struct domain_device *dev)
+{
+	return -ENODEV;
+}
+
+/**
+ * asd_init_sata_pm_ddb -- SATA Port Multiplier
+ * dev: pointer to domain device
+ *
+ * For STP and direct-attached SATA Port Multipliers we need
+ * one target port DDB entry and one SATA PM table DDB entry.
+ */
+static int asd_init_sata_pm_ddb(struct domain_device *dev)
+{
+	int res = 0;
+
+	res = asd_init_target_ddb(dev);
+	if (res)
+		goto out;
+	res = asd_init_sata_pm_table_ddb(dev);
+	if (res)
+		asd_free_ddb(dev->port->ha->lldd_ha,
+			     (int) (unsigned long) dev->lldd_dev);
+out:
+	return res;
+}
+
+int asd_dev_found(struct domain_device *dev)
+{
+	int res = 0;
+
+	switch (dev->dev_type) {
+	case SATA_PM:
+		res = asd_init_sata_pm_ddb(dev);
+		break;
+	case SATA_PM_PORT:
+		res = asd_init_sata_pm_port_ddb(dev);
+		break;
+	default:
+		if (dev->tproto)
+			res = asd_init_target_ddb(dev);
+		else
+			res = asd_init_initiator_ddb(dev);
+	}
+	return res;
+}
+
+void asd_dev_gone(struct domain_device *dev)
+{
+	int ddb, sister_ddb;
+	struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+
+	ddb = (int) (unsigned long) dev->lldd_dev;
+	sister_ddb = asd_ddbsite_read_word(asd_ha, ddb, SISTER_DDB);
+
+	if (sister_ddb != 0xFFFF)
+		asd_free_ddb(asd_ha, sister_ddb);
+	asd_free_ddb(asd_ha, ddb);
+	dev->lldd_dev = NULL;
+}
diff -urN oldtree/drivers/scsi/aic94xx/aic94xx_dump.c newtree/drivers/scsi/aic94xx/aic94xx_dump.c
--- oldtree/drivers/scsi/aic94xx/aic94xx_dump.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/aic94xx_dump.c	2006-02-13 19:20:00.571425432 -0500
@@ -0,0 +1,959 @@
+/*
+ * Aic94xx SAS/SATA driver dump interface.
+ *
+ * Copyright (C) 2004 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2004 David Chaw <david_chaw@adaptec.com>
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * 2005/07/14/LT  Complete overhaul of this file.  Update pages, register
+ * locations, names, etc.  Make use of macros.  Print more information.
+ * Print all cseq and lseq mip and mdp.
+ *
+ * $Id: //depot/aic94xx/aic94xx_dump.c#29 $
+ */
+
+#include "linux/pci.h"
+#include "aic94xx.h"
+#include "aic94xx_reg.h"
+#include "aic94xx_reg_def.h"
+#include "aic94xx_sas.h"
+
+#include "aic94xx_dump.h"
+
+#ifdef ASD_DEBUG
+
+#define MD(x)	    (1 << (x))
+#define MODE_COMMON (1 << 31)
+#define MODE_0_7    (0xFF)
+
+static const struct lseq_cio_regs {
+	char	*name;
+	u32	offs;
+	u8	width;
+	u32	mode;
+} LSEQmCIOREGS[] = {
+	{"LmMnSCBPTR",    0x20, 16, MD(0)|MD(1)|MD(2)|MD(3)|MD(4) },
+	{"LmMnDDBPTR",    0x22, 16, MD(0)|MD(1)|MD(2)|MD(3)|MD(4) },
+	{"LmREQMBX",      0x30, 32, MODE_COMMON },
+	{"LmRSPMBX",      0x34, 32, MODE_COMMON },
+	{"LmMnINT",       0x38, 32, MODE_0_7 },
+	{"LmMnINTEN",     0x3C, 32, MODE_0_7 },
+	{"LmXMTPRIMD",    0x40, 32, MODE_COMMON },
+	{"LmXMTPRIMCS",   0x44,  8, MODE_COMMON },
+	{"LmCONSTAT",     0x45,  8, MODE_COMMON },
+	{"LmMnDMAERRS",   0x46,  8, MD(0)|MD(1) },
+	{"LmMnSGDMAERRS", 0x47,  8, MD(0)|MD(1) },
+	{"LmMnEXPHDRP",   0x48,  8, MD(0) },
+	{"LmMnSASAALIGN", 0x48,  8, MD(1) },
+	{"LmMnMSKHDRP",   0x49,  8, MD(0) },
+	{"LmMnSTPALIGN",  0x49,  8, MD(1) },
+	{"LmMnRCVHDRP",   0x4A,  8, MD(0) },
+	{"LmMnXMTHDRP",   0x4A,  8, MD(1) },
+	{"LmALIGNMODE",   0x4B,  8, MD(1) },
+	{"LmMnEXPRCVCNT", 0x4C, 32, MD(0) },
+	{"LmMnXMTCNT",    0x4C, 32, MD(1) },
+	{"LmMnCURRTAG",   0x54, 16, MD(0) },
+	{"LmMnPREVTAG",   0x56, 16, MD(0) },
+	{"LmMnACKOFS",    0x58,  8, MD(1) },
+	{"LmMnXFRLVL",    0x59,  8, MD(0)|MD(1) },
+	{"LmMnSGDMACTL",  0x5A,  8, MD(0)|MD(1) },
+	{"LmMnSGDMASTAT", 0x5B,  8, MD(0)|MD(1) },
+	{"LmMnDDMACTL",   0x5C,  8, MD(0)|MD(1) },
+	{"LmMnDDMASTAT",  0x5D,  8, MD(0)|MD(1) },
+	{"LmMnDDMAMODE",  0x5E, 16, MD(0)|MD(1) },
+	{"LmMnPIPECTL",   0x61,  8, MD(0)|MD(1) },
+	{"LmMnACTSCB",    0x62, 16, MD(0)|MD(1) },
+	{"LmMnSGBHADR",   0x64,  8, MD(0)|MD(1) },
+	{"LmMnSGBADR",    0x65,  8, MD(0)|MD(1) },
+	{"LmMnSGDCNT",    0x66,  8, MD(0)|MD(1) },
+	{"LmMnSGDMADR",   0x68, 32, MD(0)|MD(1) },
+	{"LmMnSGDMADR",   0x6C, 32, MD(0)|MD(1) },
+	{"LmMnXFRCNT",    0x70, 32, MD(0)|MD(1) },
+	{"LmMnXMTCRC",    0x74, 32, MD(1) },
+	{"LmCURRTAG",     0x74, 16, MD(0) },
+	{"LmPREVTAG",     0x76, 16, MD(0) },
+	{"LmMnDPSEL",     0x7B,  8, MD(0)|MD(1) },
+	{"LmDPTHSTAT",    0x7C,  8, MODE_COMMON },
+	{"LmMnHOLDLVL",   0x7D,  8, MD(0) },
+	{"LmMnSATAFS",    0x7E,  8, MD(1) },
+	{"LmMnCMPLTSTAT", 0x7F,  8, MD(0)|MD(1) },
+	{"LmPRMSTAT0",    0x80, 32, MODE_COMMON },
+	{"LmPRMSTAT1",    0x84, 32, MODE_COMMON },
+	{"LmGPRMINT",     0x88,  8, MODE_COMMON },
+        {"LmMnCURRSCB",   0x8A, 16, MD(0) },
+	{"LmPRMICODE",    0x8C, 32, MODE_COMMON },
+	{"LmMnRCVCNT",    0x90, 16, MD(0) },
+	{"LmMnBUFSTAT",   0x92, 16, MD(0) },
+	{"LmMnXMTHDRSIZE",0x92,  8, MD(1) },
+	{"LmMnXMTSIZE",   0x93,  8, MD(1) },
+	{"LmMnTGTXFRCNT", 0x94, 32, MD(0) },
+	{"LmMnEXPROFS",   0x98, 32, MD(0) },
+	{"LmMnXMTROFS",   0x98, 32, MD(1) },
+	{"LmMnRCVROFS",   0x9C, 32, MD(0) },
+	{"LmCONCTL",      0xA0, 16, MODE_COMMON },
+	{"LmBITLTIMER",   0xA2, 16, MODE_COMMON },
+	{"LmWWNLOW",      0xA8, 32, MODE_COMMON },
+	{"LmWWNHIGH",     0xAC, 32, MODE_COMMON },
+	{"LmMnFRMERR",    0xB0, 32, MD(0) },
+	{"LmMnFRMERREN",  0xB4, 32, MD(0) },
+	{"LmAWTIMER",     0xB8, 16, MODE_COMMON },
+	{"LmAWTCTL",      0xBA,  8, MODE_COMMON },
+	{"LmMnHDRCMPS",   0xC0, 32, MD(0) },
+	{"LmMnXMTSTAT",   0xC4,  8, MD(1) },
+	{"LmHWTSTATEN",   0xC5,  8, MODE_COMMON },
+	{"LmMnRRDYRC",    0xC6,  8, MD(0) },
+        {"LmMnRRDYTC",    0xC6,  8, MD(1) },
+	{"LmHWTSTAT",     0xC7,  8, MODE_COMMON },
+	{"LmMnDATABUFADR",0xC8, 16, MD(0)|MD(1) },
+	{"LmDWSSTATUS",   0xCB,  8, MODE_COMMON },
+	{"LmMnACTSTAT",   0xCE, 16, MD(0)|MD(1) },
+	{"LmMnREQSCB",    0xD2, 16, MD(0)|MD(1) },
+	{"LmXXXPRIM",     0xD4, 32, MODE_COMMON },
+	{"LmRCVASTAT",    0xD9,  8, MODE_COMMON },
+	{"LmINTDIS1",     0xDA,  8, MODE_COMMON },
+	{"LmPSTORESEL",   0xDB,  8, MODE_COMMON },
+	{"LmPSTORE",      0xDC, 32, MODE_COMMON },
+	{"LmPRIMSTAT0EN", 0xE0, 32, MODE_COMMON },
+	{"LmPRIMSTAT1EN", 0xE4, 32, MODE_COMMON },
+	{"LmDONETCTL",    0xF2, 16, MODE_COMMON },
+	{NULL, 0, 0, 0 }
+};
+/*
+static struct lseq_cio_regs LSEQmOOBREGS[] = {
+   {"OOB_BFLTR"        ,0x100, 8, MD(5)},
+   {"OOB_INIT_MIN"     ,0x102,16, MD(5)},
+   {"OOB_INIT_MAX"     ,0x104,16, MD(5)},
+   {"OOB_INIT_NEG"     ,0x106,16, MD(5)},
+   {"OOB_SAS_MIN"      ,0x108,16, MD(5)},
+   {"OOB_SAS_MAX"      ,0x10A,16, MD(5)},
+   {"OOB_SAS_NEG"      ,0x10C,16, MD(5)},
+   {"OOB_WAKE_MIN"     ,0x10E,16, MD(5)},
+   {"OOB_WAKE_MAX"     ,0x110,16, MD(5)},
+   {"OOB_WAKE_NEG"     ,0x112,16, MD(5)},
+   {"OOB_IDLE_MAX"     ,0x114,16, MD(5)},
+   {"OOB_BURST_MAX"    ,0x116,16, MD(5)},
+   {"OOB_XMIT_BURST"   ,0x118, 8, MD(5)},
+   {"OOB_SEND_PAIRS"   ,0x119, 8, MD(5)},
+   {"OOB_INIT_IDLE"    ,0x11A, 8, MD(5)},
+   {"OOB_INIT_NEGO"    ,0x11C, 8, MD(5)},
+   {"OOB_SAS_IDLE"     ,0x11E, 8, MD(5)},
+   {"OOB_SAS_NEGO"     ,0x120, 8, MD(5)},
+   {"OOB_WAKE_IDLE"    ,0x122, 8, MD(5)},
+   {"OOB_WAKE_NEGO"    ,0x124, 8, MD(5)},
+   {"OOB_DATA_KBITS"   ,0x126, 8, MD(5)},
+   {"OOB_BURST_DATA"   ,0x128,32, MD(5)},
+   {"OOB_ALIGN_0_DATA" ,0x12C,32, MD(5)},
+   {"OOB_ALIGN_1_DATA" ,0x130,32, MD(5)},
+   {"OOB_SYNC_DATA"    ,0x134,32, MD(5)},
+   {"OOB_D10_2_DATA"   ,0x138,32, MD(5)},
+   {"OOB_PHY_RST_CNT"  ,0x13C,32, MD(5)},
+   {"OOB_SIG_GEN"      ,0x140, 8, MD(5)},
+   {"OOB_XMIT"         ,0x141, 8, MD(5)},
+   {"FUNCTION_MAKS"    ,0x142, 8, MD(5)},
+   {"OOB_MODE"         ,0x143, 8, MD(5)},
+   {"CURRENT_STATUS"   ,0x144, 8, MD(5)},
+   {"SPEED_MASK"       ,0x145, 8, MD(5)},
+   {"PRIM_COUNT"       ,0x146, 8, MD(5)},
+   {"OOB_SIGNALS"      ,0x148, 8, MD(5)},
+   {"OOB_DATA_DET"     ,0x149, 8, MD(5)},
+   {"OOB_TIME_OUT"     ,0x14C, 8, MD(5)},
+   {"OOB_TIMER_ENABLE" ,0x14D, 8, MD(5)},
+   {"OOB_STATUS"       ,0x14E, 8, MD(5)},
+   {"HOT_PLUG_DELAY"   ,0x150, 8, MD(5)},
+   {"RCD_DELAY"        ,0x151, 8, MD(5)},
+   {"COMSAS_TIMER"     ,0x152, 8, MD(5)},
+   {"SNTT_DELAY"       ,0x153, 8, MD(5)},
+   {"SPD_CHNG_DELAY"   ,0x154, 8, MD(5)},
+   {"SNLT_DELAY"       ,0x155, 8, MD(5)},
+   {"SNWT_DELAY"       ,0x156, 8, MD(5)},
+   {"ALIGN_DELAY"      ,0x157, 8, MD(5)},
+   {"INT_ENABLE_0"     ,0x158, 8, MD(5)},
+   {"INT_ENABLE_1"     ,0x159, 8, MD(5)},
+   {"INT_ENABLE_2"     ,0x15A, 8, MD(5)},
+   {"INT_ENABLE_3"     ,0x15B, 8, MD(5)},
+   {"OOB_TEST_REG"     ,0x15C, 8, MD(5)},
+   {"PHY_CONTROL_0"    ,0x160, 8, MD(5)},
+   {"PHY_CONTROL_1"    ,0x161, 8, MD(5)},
+   {"PHY_CONTROL_2"    ,0x162, 8, MD(5)},
+   {"PHY_CONTROL_3"    ,0x163, 8, MD(5)},
+   {"PHY_OOB_CAL_TX"   ,0x164, 8, MD(5)},
+   {"PHY_OOB_CAL_RX"   ,0x165, 8, MD(5)},
+   {"OOB_PHY_CAL_TX"   ,0x166, 8, MD(5)},
+   {"OOB_PHY_CAL_RX"   ,0x167, 8, MD(5)},
+   {"PHY_CONTROL_4"    ,0x168, 8, MD(5)},
+   {"PHY_TEST"         ,0x169, 8, MD(5)},
+   {"PHY_PWR_CTL"      ,0x16A, 8, MD(5)},
+   {"PHY_PWR_DELAY"    ,0x16B, 8, MD(5)},
+   {"OOB_SM_CON"       ,0x16C, 8, MD(5)},
+   {"ADDR_TRAP_1"      ,0x16D, 8, MD(5)},
+   {"ADDR_NEXT_1"      ,0x16E, 8, MD(5)},
+   {"NEXT_ST_1"        ,0x16F, 8, MD(5)},
+   {"OOB_SM_STATE"     ,0x170, 8, MD(5)},
+   {"ADDR_TRAP_2"      ,0x171, 8, MD(5)},
+   {"ADDR_NEXT_2"      ,0x172, 8, MD(5)},
+   {"NEXT_ST_2"        ,0x173, 8, MD(5)},
+   {NULL, 0, 0, 0 }
+};
+*/
+#define STR_8BIT   "   %30s[0x%04x]:0x%02x\n"
+#define STR_16BIT  "   %30s[0x%04x]:0x%04x\n"
+#define STR_32BIT  "   %30s[0x%04x]:0x%08x\n"
+#define STR_64BIT  "   %30s[0x%04x]:0x%016Lx\n"
+
+#define PRINT_REG_8bit(_ha, _n, _r) asd_printk(STR_8BIT, #_n, _n,      \
+					     asd_read_reg_byte(_ha, _r))
+#define PRINT_REG_16bit(_ha, _n, _r) asd_printk(STR_16BIT, #_n, _n,     \
+					      asd_read_reg_word(_ha, _r))
+#define PRINT_REG_32bit(_ha, _n, _r) asd_printk(STR_32BIT, #_n, _n,      \
+					      asd_read_reg_dword(_ha, _r))
+
+#define PRINT_CREG_8bit(_ha, _n) asd_printk(STR_8BIT, #_n, _n,      \
+					     asd_read_reg_byte(_ha, C##_n))
+#define PRINT_CREG_16bit(_ha, _n) asd_printk(STR_16BIT, #_n, _n,     \
+					      asd_read_reg_word(_ha, C##_n))
+#define PRINT_CREG_32bit(_ha, _n) asd_printk(STR_32BIT, #_n, _n,      \
+					      asd_read_reg_dword(_ha, C##_n))
+
+#define MSTR_8BIT   "   Mode:%02d %30s[0x%04x]:0x%02x\n"
+#define MSTR_16BIT  "   Mode:%02d %30s[0x%04x]:0x%04x\n"
+#define MSTR_32BIT  "   Mode:%02d %30s[0x%04x]:0x%08x\n"
+
+#define PRINT_MREG_8bit(_ha, _m, _n, _r) asd_printk(MSTR_8BIT, _m, #_n, _n,   \
+					     asd_read_reg_byte(_ha, _r))
+#define PRINT_MREG_16bit(_ha, _m, _n, _r) asd_printk(MSTR_16BIT, _m, #_n, _n, \
+					      asd_read_reg_word(_ha, _r))
+#define PRINT_MREG_32bit(_ha, _m, _n, _r) asd_printk(MSTR_32BIT, _m, #_n, _n, \
+					      asd_read_reg_dword(_ha, _r))
+
+/* can also be used for MD when the register is mode aware already */
+#define PRINT_MIS_byte(_ha, _n) asd_printk(STR_8BIT, #_n,CSEQ_##_n-CMAPPEDSCR,\
+                                           asd_read_reg_byte(_ha, CSEQ_##_n))
+#define PRINT_MIS_word(_ha, _n) asd_printk(STR_16BIT,#_n,CSEQ_##_n-CMAPPEDSCR,\
+                                           asd_read_reg_word(_ha, CSEQ_##_n))
+#define PRINT_MIS_dword(_ha, _n)                      \
+        asd_printk(STR_32BIT,#_n,CSEQ_##_n-CMAPPEDSCR,\
+                   asd_read_reg_dword(_ha, CSEQ_##_n))
+#define PRINT_MIS_qword(_ha, _n)                                       \
+        asd_printk(STR_64BIT, #_n,CSEQ_##_n-CMAPPEDSCR,                \
+                   (u64)(((u64)asd_read_reg_dword(_ha, CSEQ_##_n))     \
+                 | (((u64)asd_read_reg_dword(_ha, (CSEQ_##_n)+4))<<32)))
+
+#define CMDP_REG(_n, _m) (_m*(CSEQ_PAGE_SIZE*2)+CSEQ_##_n)
+#define PRINT_CMDP_word(_ha, _n) \
+asd_printk("%20s 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n", \
+	#_n, \
+	asd_read_reg_word(_ha, CMDP_REG(_n, 0)), \
+	asd_read_reg_word(_ha, CMDP_REG(_n, 1)), \
+	asd_read_reg_word(_ha, CMDP_REG(_n, 2)), \
+	asd_read_reg_word(_ha, CMDP_REG(_n, 3)), \
+	asd_read_reg_word(_ha, CMDP_REG(_n, 4)), \
+	asd_read_reg_word(_ha, CMDP_REG(_n, 5)), \
+	asd_read_reg_word(_ha, CMDP_REG(_n, 6)), \
+	asd_read_reg_word(_ha, CMDP_REG(_n, 7)))
+
+#define PRINT_CMDP_byte(_ha, _n) \
+asd_printk("%20s 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n", \
+	#_n, \
+	asd_read_reg_byte(_ha, CMDP_REG(_n, 0)), \
+	asd_read_reg_byte(_ha, CMDP_REG(_n, 1)), \
+	asd_read_reg_byte(_ha, CMDP_REG(_n, 2)), \
+	asd_read_reg_byte(_ha, CMDP_REG(_n, 3)), \
+	asd_read_reg_byte(_ha, CMDP_REG(_n, 4)), \
+	asd_read_reg_byte(_ha, CMDP_REG(_n, 5)), \
+	asd_read_reg_byte(_ha, CMDP_REG(_n, 6)), \
+	asd_read_reg_byte(_ha, CMDP_REG(_n, 7)))
+
+static void asd_dump_cseq_state(struct asd_ha_struct *asd_ha)
+{
+	int mode;
+
+	asd_printk("CSEQ STATE\n");
+
+	asd_printk("ARP2 REGISTERS\n");
+
+	PRINT_CREG_32bit(asd_ha, ARP2CTL);
+	PRINT_CREG_32bit(asd_ha, ARP2INT);
+	PRINT_CREG_32bit(asd_ha, ARP2INTEN);
+	PRINT_CREG_8bit(asd_ha, MODEPTR);
+	PRINT_CREG_8bit(asd_ha, ALTMODE);
+	PRINT_CREG_8bit(asd_ha, FLAG);
+	PRINT_CREG_8bit(asd_ha, ARP2INTCTL);
+	PRINT_CREG_16bit(asd_ha, STACK);
+	PRINT_CREG_16bit(asd_ha, PRGMCNT);
+	PRINT_CREG_16bit(asd_ha, ACCUM);
+	PRINT_CREG_16bit(asd_ha, SINDEX);
+	PRINT_CREG_16bit(asd_ha, DINDEX);
+	PRINT_CREG_8bit(asd_ha, SINDIR);
+	PRINT_CREG_8bit(asd_ha, DINDIR);
+	PRINT_CREG_8bit(asd_ha, JUMLDIR);
+	PRINT_CREG_8bit(asd_ha, ARP2HALTCODE);
+	PRINT_CREG_16bit(asd_ha, CURRADDR);
+	PRINT_CREG_16bit(asd_ha, LASTADDR);
+	PRINT_CREG_16bit(asd_ha, NXTLADDR);
+
+	asd_printk("IOP REGISTERS\n");
+
+	PRINT_REG_32bit(asd_ha, BISTCTL1, CBISTCTL);
+	PRINT_CREG_32bit(asd_ha, MAPPEDSCR);
+
+	asd_printk("CIO REGISTERS\n");
+
+	for (mode = 0; mode < 9; mode++)
+		PRINT_MREG_16bit(asd_ha, mode, MnSCBPTR, CMnSCBPTR(mode));
+	PRINT_MREG_16bit(asd_ha, 15, MnSCBPTR, CMnSCBPTR(15));
+
+	for (mode = 0; mode < 9; mode++)
+		PRINT_MREG_16bit(asd_ha, mode, MnDDBPTR, CMnDDBPTR(mode));
+	PRINT_MREG_16bit(asd_ha, 15, MnDDBPTR, CMnDDBPTR(15));
+
+	for (mode = 0; mode < 8; mode++)
+		PRINT_MREG_32bit(asd_ha, mode, MnREQMBX, CMnREQMBX(mode));
+	for (mode = 0; mode < 8; mode++)
+		PRINT_MREG_32bit(asd_ha, mode, MnRSPMBX, CMnRSPMBX(mode));
+	for (mode = 0; mode < 8; mode++)
+		PRINT_MREG_32bit(asd_ha, mode, MnINT, CMnINT(mode));
+	for (mode = 0; mode < 8; mode++)
+		PRINT_MREG_32bit(asd_ha, mode, MnINTEN, CMnINTEN(mode));
+
+	PRINT_CREG_8bit(asd_ha, SCRATCHPAGE);
+	for (mode = 0; mode < 8; mode++)
+		PRINT_MREG_8bit(asd_ha, mode, MnSCRATCHPAGE,
+				CMnSCRATCHPAGE(mode));
+
+	PRINT_REG_32bit(asd_ha, CLINKCON, CLINKCON);
+	PRINT_REG_8bit(asd_ha, CCONMSK, CCONMSK);
+	PRINT_REG_8bit(asd_ha, CCONEXIST, CCONEXIST);
+	PRINT_REG_16bit(asd_ha, CCONMODE, CCONMODE);
+	PRINT_REG_32bit(asd_ha, CTIMERCALC, CTIMERCALC);
+	PRINT_REG_8bit(asd_ha, CINTDIS, CINTDIS);
+
+	asd_printk("SCRATCH MEMORY\n");
+
+	asd_printk("MIP 4 >>>>>\n");
+	PRINT_MIS_word(asd_ha, Q_EXE_HEAD);
+	PRINT_MIS_word(asd_ha, Q_EXE_TAIL);
+	PRINT_MIS_word(asd_ha, Q_DONE_HEAD);
+	PRINT_MIS_word(asd_ha, Q_DONE_TAIL);
+	PRINT_MIS_word(asd_ha, Q_SEND_HEAD);
+	PRINT_MIS_word(asd_ha, Q_SEND_TAIL);
+	PRINT_MIS_word(asd_ha, Q_DMA2CHIM_HEAD);
+	PRINT_MIS_word(asd_ha, Q_DMA2CHIM_TAIL);
+	PRINT_MIS_word(asd_ha, Q_COPY_HEAD);
+	PRINT_MIS_word(asd_ha, Q_COPY_TAIL);
+	PRINT_MIS_word(asd_ha, REG0);
+	PRINT_MIS_word(asd_ha, REG1);
+	PRINT_MIS_dword(asd_ha, REG2);
+	PRINT_MIS_byte(asd_ha, LINK_CTL_Q_MAP);
+	PRINT_MIS_byte(asd_ha, MAX_CSEQ_MODE);
+	PRINT_MIS_byte(asd_ha, FREE_LIST_HACK_COUNT);
+
+	asd_printk("MIP 5 >>>>\n");
+	PRINT_MIS_qword(asd_ha, EST_NEXUS_REQ_QUEUE);
+	PRINT_MIS_qword(asd_ha, EST_NEXUS_REQ_COUNT);
+	PRINT_MIS_word(asd_ha, Q_EST_NEXUS_HEAD);
+	PRINT_MIS_word(asd_ha, Q_EST_NEXUS_TAIL);
+	PRINT_MIS_word(asd_ha, NEED_EST_NEXUS_SCB);
+	PRINT_MIS_byte(asd_ha, EST_NEXUS_REQ_HEAD);
+	PRINT_MIS_byte(asd_ha, EST_NEXUS_REQ_TAIL);
+	PRINT_MIS_byte(asd_ha, EST_NEXUS_SCB_OFFSET);
+
+	asd_printk("MIP 6 >>>>\n");
+	PRINT_MIS_word(asd_ha, INT_ROUT_RET_ADDR0);
+	PRINT_MIS_word(asd_ha, INT_ROUT_RET_ADDR1);
+	PRINT_MIS_word(asd_ha, INT_ROUT_SCBPTR);
+	PRINT_MIS_byte(asd_ha, INT_ROUT_MODE);
+	PRINT_MIS_byte(asd_ha, ISR_SCRATCH_FLAGS);
+	PRINT_MIS_word(asd_ha, ISR_SAVE_SINDEX);
+	PRINT_MIS_word(asd_ha, ISR_SAVE_DINDEX);
+	PRINT_MIS_word(asd_ha, SLS_SAVE_ACCUM);
+	PRINT_MIS_word(asd_ha, SLS_SAVE_SINDEX);
+	PRINT_MIS_word(asd_ha, Q_MONIRTT_HEAD);
+	PRINT_MIS_word(asd_ha, Q_MONIRTT_TAIL);
+	PRINT_MIS_byte(asd_ha, FREE_SCB_MASK);
+	PRINT_MIS_word(asd_ha, BUILTIN_FREE_SCB_HEAD);
+	PRINT_MIS_word(asd_ha, BUILTIN_FREE_SCB_TAIL);
+	PRINT_MIS_word(asd_ha, EXTENDED_FREE_SCB_HEAD);
+	PRINT_MIS_word(asd_ha, EXTENDED_FREE_SCB_TAIL);
+
+	asd_printk("MIP 7 >>>>\n");
+	PRINT_MIS_qword(asd_ha, EMPTY_REQ_QUEUE);
+	PRINT_MIS_qword(asd_ha, EMPTY_REQ_COUNT);
+	PRINT_MIS_word(asd_ha, Q_EMPTY_HEAD);
+	PRINT_MIS_word(asd_ha, Q_EMPTY_TAIL);
+	PRINT_MIS_word(asd_ha, NEED_EMPTY_SCB);
+	PRINT_MIS_byte(asd_ha, EMPTY_REQ_HEAD);
+	PRINT_MIS_byte(asd_ha, EMPTY_REQ_TAIL);
+	PRINT_MIS_byte(asd_ha, EMPTY_SCB_OFFSET);
+	PRINT_MIS_word(asd_ha, PRIMITIVE_DATA);
+	PRINT_MIS_dword(asd_ha, TIMEOUT_CONST);
+
+	asd_printk("MDP 0 >>>>\n");
+	asd_printk("%-20s %6s %6s %6s %6s %6s %6s %6s %6s\n",
+		   "Mode: ", "0", "1", "2", "3", "4", "5", "6", "7");
+	PRINT_CMDP_word(asd_ha, LRM_SAVE_SINDEX);
+	PRINT_CMDP_word(asd_ha, LRM_SAVE_SCBPTR);
+	PRINT_CMDP_word(asd_ha, Q_LINK_HEAD);
+	PRINT_CMDP_word(asd_ha, Q_LINK_TAIL);
+	PRINT_CMDP_byte(asd_ha, LRM_SAVE_SCRPAGE);
+
+	asd_printk("MDP 0 Mode 8 >>>>\n");
+	PRINT_MIS_word(asd_ha, RET_ADDR);
+	PRINT_MIS_word(asd_ha, RET_SCBPTR);
+	PRINT_MIS_word(asd_ha, SAVE_SCBPTR);
+	PRINT_MIS_word(asd_ha, EMPTY_TRANS_CTX);
+	PRINT_MIS_word(asd_ha, RESP_LEN);
+	PRINT_MIS_word(asd_ha, TMF_SCBPTR);
+	PRINT_MIS_word(asd_ha, GLOBAL_PREV_SCB);
+	PRINT_MIS_word(asd_ha, GLOBAL_HEAD);
+	PRINT_MIS_word(asd_ha, CLEAR_LU_HEAD);
+	PRINT_MIS_byte(asd_ha, TMF_OPCODE);
+	PRINT_MIS_byte(asd_ha, SCRATCH_FLAGS);
+	PRINT_MIS_word(asd_ha, HSB_SITE);
+	PRINT_MIS_word(asd_ha, FIRST_INV_SCB_SITE);
+	PRINT_MIS_word(asd_ha, FIRST_INV_DDB_SITE);
+
+	asd_printk("MDP 1 Mode 8 >>>>\n");
+	PRINT_MIS_qword(asd_ha, LUN_TO_CLEAR);
+	PRINT_MIS_qword(asd_ha, LUN_TO_CHECK);
+
+	asd_printk("MDP 2 Mode 8 >>>>\n");
+	PRINT_MIS_qword(asd_ha, HQ_NEW_POINTER);
+	PRINT_MIS_qword(asd_ha, HQ_DONE_BASE);
+	PRINT_MIS_dword(asd_ha, HQ_DONE_POINTER);
+	PRINT_MIS_byte(asd_ha, HQ_DONE_PASS);
+}
+
+#define PRINT_LREG_8bit(_h, _lseq, _n) \
+        asd_printk(STR_8BIT, #_n, _n, asd_read_reg_byte(_h, Lm##_n(_lseq)))
+#define PRINT_LREG_16bit(_h, _lseq, _n) \
+        asd_printk(STR_16BIT, #_n, _n, asd_read_reg_word(_h, Lm##_n(_lseq)))
+#define PRINT_LREG_32bit(_h, _lseq, _n) \
+        asd_printk(STR_32BIT, #_n, _n, asd_read_reg_dword(_h, Lm##_n(_lseq)))
+
+#define PRINT_LMIP_byte(_h, _lseq, _n)                              \
+	asd_printk(STR_8BIT, #_n, LmSEQ_##_n(_lseq)-LmSCRATCH(_lseq), \
+		   asd_read_reg_byte(_h, LmSEQ_##_n(_lseq)))
+#define PRINT_LMIP_word(_h, _lseq, _n)                              \
+	asd_printk(STR_16BIT, #_n, LmSEQ_##_n(_lseq)-LmSCRATCH(_lseq), \
+		   asd_read_reg_word(_h, LmSEQ_##_n(_lseq)))
+#define PRINT_LMIP_dword(_h, _lseq, _n)                             \
+	asd_printk(STR_32BIT, #_n, LmSEQ_##_n(_lseq)-LmSCRATCH(_lseq), \
+		   asd_read_reg_dword(_h, LmSEQ_##_n(_lseq)))
+#define PRINT_LMIP_qword(_h, _lseq, _n)                                \
+	asd_printk(STR_64BIT, #_n, LmSEQ_##_n(_lseq)-LmSCRATCH(_lseq), \
+		   (u64)(((u64)asd_read_reg_dword(_h, LmSEQ_##_n(_lseq)))\
+	          | (((u64)asd_read_reg_dword(_h, LmSEQ_##_n(_lseq)+4))<<32)))
+
+static void asd_print_lseq_cio_reg(struct asd_ha_struct *asd_ha,
+				   u32 lseq_cio_addr, int i)
+{
+	switch (LSEQmCIOREGS[i].width) {
+	case 8:
+		asd_printk("%20s[0x%x]: 0x%02x\n", LSEQmCIOREGS[i].name,
+			   LSEQmCIOREGS[i].offs,
+			   asd_read_reg_byte(asd_ha, lseq_cio_addr +
+					     LSEQmCIOREGS[i].offs));
+
+		break;
+	case 16:
+		asd_printk("%20s[0x%x]: 0x%04x\n", LSEQmCIOREGS[i].name,
+			   LSEQmCIOREGS[i].offs,
+			   asd_read_reg_word(asd_ha, lseq_cio_addr +
+					     LSEQmCIOREGS[i].offs));
+
+		break;
+	case 32:
+		asd_printk("%20s[0x%x]: 0x%08x\n", LSEQmCIOREGS[i].name,
+			   LSEQmCIOREGS[i].offs,
+			   asd_read_reg_dword(asd_ha, lseq_cio_addr +
+					      LSEQmCIOREGS[i].offs));
+		break;
+	}
+}
+
+static void asd_dump_lseq_state(struct asd_ha_struct *asd_ha, int lseq)
+{
+	u32 moffs;
+	int mode;
+
+	asd_printk("LSEQ %d STATE\n", lseq);
+
+	asd_printk("LSEQ%d: ARP2 REGISTERS\n", lseq);
+	PRINT_LREG_32bit(asd_ha, lseq, ARP2CTL);
+	PRINT_LREG_32bit(asd_ha, lseq, ARP2INT);
+	PRINT_LREG_32bit(asd_ha, lseq, ARP2INTEN);
+	PRINT_LREG_8bit(asd_ha, lseq, MODEPTR);
+	PRINT_LREG_8bit(asd_ha, lseq, ALTMODE);
+	PRINT_LREG_8bit(asd_ha, lseq, FLAG);
+	PRINT_LREG_8bit(asd_ha, lseq, ARP2INTCTL);
+	PRINT_LREG_16bit(asd_ha, lseq, STACK);
+	PRINT_LREG_16bit(asd_ha, lseq, PRGMCNT);
+	PRINT_LREG_16bit(asd_ha, lseq, ACCUM);
+	PRINT_LREG_16bit(asd_ha, lseq, SINDEX);
+	PRINT_LREG_16bit(asd_ha, lseq, DINDEX);
+	PRINT_LREG_8bit(asd_ha, lseq, SINDIR);
+	PRINT_LREG_8bit(asd_ha, lseq, DINDIR);
+	PRINT_LREG_8bit(asd_ha, lseq, JUMLDIR);
+	PRINT_LREG_8bit(asd_ha, lseq, ARP2HALTCODE);
+	PRINT_LREG_16bit(asd_ha, lseq, CURRADDR);
+	PRINT_LREG_16bit(asd_ha, lseq, LASTADDR);
+	PRINT_LREG_16bit(asd_ha, lseq, NXTLADDR);
+
+	asd_printk("LSEQ%d: IOP REGISTERS\n", lseq);
+
+	PRINT_LREG_32bit(asd_ha, lseq, MODECTL);
+	PRINT_LREG_32bit(asd_ha, lseq, DBGMODE);
+	PRINT_LREG_32bit(asd_ha, lseq, CONTROL);
+	PRINT_REG_32bit(asd_ha, BISTCTL0, LmBISTCTL0(lseq));
+	PRINT_REG_32bit(asd_ha, BISTCTL1, LmBISTCTL1(lseq));
+
+	asd_printk("LSEQ%d: CIO REGISTERS\n", lseq);
+	asd_printk("Mode common:\n");
+
+	for (mode = 0; mode < 8; mode++) {
+		u32 lseq_cio_addr = LmSEQ_PHY_BASE(mode, lseq);
+		int i;
+
+		for (i = 0; LSEQmCIOREGS[i].name; i++)
+			if (LSEQmCIOREGS[i].mode == MODE_COMMON)
+				asd_print_lseq_cio_reg(asd_ha,lseq_cio_addr,i);
+	}
+
+	asd_printk("Mode unique:\n");
+	for (mode = 0; mode < 8; mode++) {
+		u32 lseq_cio_addr = LmSEQ_PHY_BASE(mode, lseq);
+		int i;
+
+		asd_printk("Mode %d\n", mode);
+		for  (i = 0; LSEQmCIOREGS[i].name; i++) {
+			if (!(LSEQmCIOREGS[i].mode & (1 << mode)))
+				continue;
+			asd_print_lseq_cio_reg(asd_ha, lseq_cio_addr, i);
+		}
+	}
+
+	asd_printk("SCRATCH MEMORY\n");
+
+	asd_printk("LSEQ%d MIP 0 >>>>\n", lseq);
+	PRINT_LMIP_word(asd_ha, lseq, Q_TGTXFR_HEAD);
+	PRINT_LMIP_word(asd_ha, lseq, Q_TGTXFR_TAIL);
+	PRINT_LMIP_byte(asd_ha, lseq, LINK_NUMBER);
+	PRINT_LMIP_byte(asd_ha, lseq, SCRATCH_FLAGS);
+	PRINT_LMIP_qword(asd_ha, lseq, CONNECTION_STATE);
+	PRINT_LMIP_word(asd_ha, lseq, CONCTL);
+	PRINT_LMIP_byte(asd_ha, lseq, CONSTAT);
+	PRINT_LMIP_byte(asd_ha, lseq, CONNECTION_MODES);
+	PRINT_LMIP_word(asd_ha, lseq, REG1_ISR);
+	PRINT_LMIP_word(asd_ha, lseq, REG2_ISR);
+	PRINT_LMIP_word(asd_ha, lseq, REG3_ISR);
+	PRINT_LMIP_qword(asd_ha, lseq,REG0_ISR);
+
+	asd_printk("LSEQ%d MIP 1 >>>>\n", lseq);
+	PRINT_LMIP_word(asd_ha, lseq, EST_NEXUS_SCBPTR0);
+	PRINT_LMIP_word(asd_ha, lseq, EST_NEXUS_SCBPTR1);
+	PRINT_LMIP_word(asd_ha, lseq, EST_NEXUS_SCBPTR2);
+	PRINT_LMIP_word(asd_ha, lseq, EST_NEXUS_SCBPTR3);
+	PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_OPCODE0);
+	PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_OPCODE1);
+	PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_OPCODE2);
+	PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_OPCODE3);
+	PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_HEAD);
+	PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_TAIL);
+	PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_BUF_AVAIL);
+	PRINT_LMIP_dword(asd_ha, lseq, TIMEOUT_CONST);
+	PRINT_LMIP_word(asd_ha, lseq, ISR_SAVE_SINDEX);
+	PRINT_LMIP_word(asd_ha, lseq, ISR_SAVE_DINDEX);
+
+	asd_printk("LSEQ%d MIP 2 >>>>\n", lseq);
+	PRINT_LMIP_word(asd_ha, lseq, EMPTY_SCB_PTR0);
+	PRINT_LMIP_word(asd_ha, lseq, EMPTY_SCB_PTR1);
+	PRINT_LMIP_word(asd_ha, lseq, EMPTY_SCB_PTR2);
+	PRINT_LMIP_word(asd_ha, lseq, EMPTY_SCB_PTR3);
+	PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_OPCD0);
+	PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_OPCD1);
+	PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_OPCD2);
+	PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_OPCD3);
+	PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_HEAD);
+	PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_TAIL);
+	PRINT_LMIP_byte(asd_ha, lseq, EMPTY_BUFS_AVAIL);
+
+	asd_printk("LSEQ%d MIP 3 >>>>\n", lseq);
+	PRINT_LMIP_dword(asd_ha, lseq, DEV_PRES_TMR_TOUT_CONST);
+	PRINT_LMIP_dword(asd_ha, lseq, SATA_INTERLOCK_TIMEOUT);
+	PRINT_LMIP_dword(asd_ha, lseq, SRST_ASSERT_TIMEOUT);
+	PRINT_LMIP_dword(asd_ha, lseq, RCV_FIS_TIMEOUT);
+	PRINT_LMIP_dword(asd_ha, lseq, ONE_MILLISEC_TIMEOUT);
+	PRINT_LMIP_dword(asd_ha, lseq, TEN_MS_COMINIT_TIMEOUT);
+	PRINT_LMIP_dword(asd_ha, lseq, SMP_RCV_TIMEOUT);
+
+	for (mode = 0; mode < 3; mode++) {
+		asd_printk("LSEQ%d MDP 0 MODE %d >>>>\n", lseq, mode);
+		moffs = mode * LSEQ_MODE_SCRATCH_SIZE;
+
+		asd_printk(STR_16BIT, "RET_ADDR", 0,
+			   asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR(lseq)
+					     + moffs));
+		asd_printk(STR_16BIT, "REG0_MODE", 2,
+			   asd_read_reg_word(asd_ha, LmSEQ_REG0_MODE(lseq)
+					     + moffs));
+		asd_printk(STR_16BIT, "MODE_FLAGS", 4,
+			   asd_read_reg_word(asd_ha, LmSEQ_MODE_FLAGS(lseq)
+					     + moffs));
+		asd_printk(STR_16BIT, "RET_ADDR2", 0x6,
+			   asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR2(lseq)
+					     + moffs));
+		asd_printk(STR_16BIT, "RET_ADDR1", 0x8,
+			   asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR1(lseq)
+					     + moffs));
+		asd_printk(STR_8BIT, "OPCODE_TO_CSEQ", 0xB,
+			   asd_read_reg_byte(asd_ha, LmSEQ_OPCODE_TO_CSEQ(lseq)
+					     + moffs));
+		asd_printk(STR_16BIT, "DATA_TO_CSEQ", 0xC,
+			   asd_read_reg_word(asd_ha, LmSEQ_DATA_TO_CSEQ(lseq)
+					     + moffs));
+	}
+
+	asd_printk("LSEQ%d MDP 0 MODE 5 >>>>\n", lseq);
+	moffs = LSEQ_MODE5_PAGE0_OFFSET;
+	asd_printk(STR_16BIT, "RET_ADDR", 0,
+		   asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR(lseq) + moffs));
+	asd_printk(STR_16BIT, "REG0_MODE", 2,
+		   asd_read_reg_word(asd_ha, LmSEQ_REG0_MODE(lseq) + moffs));
+	asd_printk(STR_16BIT, "MODE_FLAGS", 4,
+		   asd_read_reg_word(asd_ha, LmSEQ_MODE_FLAGS(lseq) + moffs));
+	asd_printk(STR_16BIT, "RET_ADDR2", 0x6,
+		   asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR2(lseq) + moffs));
+	asd_printk(STR_16BIT, "RET_ADDR1", 0x8,
+		   asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR1(lseq) + moffs));
+	asd_printk(STR_8BIT, "OPCODE_TO_CSEQ", 0xB,
+	   asd_read_reg_byte(asd_ha, LmSEQ_OPCODE_TO_CSEQ(lseq) + moffs));
+	asd_printk(STR_16BIT, "DATA_TO_CSEQ", 0xC,
+	   asd_read_reg_word(asd_ha, LmSEQ_DATA_TO_CSEQ(lseq) + moffs));
+
+	asd_printk("LSEQ%d MDP 0 MODE 0 >>>>\n", lseq);
+	PRINT_LMIP_word(asd_ha, lseq, FIRST_INV_DDB_SITE);
+	PRINT_LMIP_word(asd_ha, lseq, EMPTY_TRANS_CTX);
+	PRINT_LMIP_word(asd_ha, lseq, RESP_LEN);
+	PRINT_LMIP_word(asd_ha, lseq, FIRST_INV_SCB_SITE);
+	PRINT_LMIP_dword(asd_ha, lseq, INTEN_SAVE);
+	PRINT_LMIP_byte(asd_ha, lseq, LINK_RST_FRM_LEN);
+	PRINT_LMIP_byte(asd_ha, lseq, LINK_RST_PROTOCOL);
+	PRINT_LMIP_byte(asd_ha, lseq, RESP_STATUS);
+	PRINT_LMIP_byte(asd_ha, lseq, LAST_LOADED_SGE);
+	PRINT_LMIP_byte(asd_ha, lseq, SAVE_SCBPTR);
+
+	asd_printk("LSEQ%d MDP 0 MODE 1 >>>>\n", lseq);
+	PRINT_LMIP_word(asd_ha, lseq, Q_XMIT_HEAD);
+	PRINT_LMIP_word(asd_ha, lseq, M1_EMPTY_TRANS_CTX);
+	PRINT_LMIP_word(asd_ha, lseq, INI_CONN_TAG);
+	PRINT_LMIP_byte(asd_ha, lseq, FAILED_OPEN_STATUS);
+	PRINT_LMIP_byte(asd_ha, lseq, XMIT_REQUEST_TYPE);
+	PRINT_LMIP_byte(asd_ha, lseq, M1_RESP_STATUS);
+	PRINT_LMIP_byte(asd_ha, lseq, M1_LAST_LOADED_SGE);
+	PRINT_LMIP_word(asd_ha, lseq, M1_SAVE_SCBPTR);
+
+	asd_printk("LSEQ%d MDP 0 MODE 2 >>>>\n", lseq);
+	PRINT_LMIP_word(asd_ha, lseq, PORT_COUNTER);
+	PRINT_LMIP_word(asd_ha, lseq, PM_TABLE_PTR);
+	PRINT_LMIP_word(asd_ha, lseq, SATA_INTERLOCK_TMR_SAVE);
+	PRINT_LMIP_word(asd_ha, lseq, IP_BITL);
+	PRINT_LMIP_word(asd_ha, lseq, COPY_SMP_CONN_TAG);
+	PRINT_LMIP_byte(asd_ha, lseq, P0M2_OFFS1AH);
+
+	asd_printk("LSEQ%d MDP 0 MODE 4/5 >>>>\n", lseq);
+	PRINT_LMIP_byte(asd_ha, lseq, SAVED_OOB_STATUS);
+	PRINT_LMIP_byte(asd_ha, lseq, SAVED_OOB_MODE);
+	PRINT_LMIP_word(asd_ha, lseq, Q_LINK_HEAD);
+	PRINT_LMIP_byte(asd_ha, lseq, LINK_RST_ERR);
+	PRINT_LMIP_byte(asd_ha, lseq, SAVED_OOB_SIGNALS);
+	PRINT_LMIP_byte(asd_ha, lseq, SAS_RESET_MODE);
+	PRINT_LMIP_byte(asd_ha, lseq, LINK_RESET_RETRY_COUNT);
+	PRINT_LMIP_byte(asd_ha, lseq, NUM_LINK_RESET_RETRIES);
+	PRINT_LMIP_word(asd_ha, lseq, OOB_INT_ENABLES);
+	PRINT_LMIP_word(asd_ha, lseq, NOTIFY_TIMER_TIMEOUT);
+	PRINT_LMIP_word(asd_ha, lseq, NOTIFY_TIMER_DOWN_COUNT);
+
+	asd_printk("LSEQ%d MDP 1 MODE 0 >>>>\n", lseq);
+	PRINT_LMIP_qword(asd_ha, lseq, SG_LIST_PTR_ADDR0);
+	PRINT_LMIP_qword(asd_ha, lseq, SG_LIST_PTR_ADDR1);
+
+	asd_printk("LSEQ%d MDP 1 MODE 1 >>>>\n", lseq);
+	PRINT_LMIP_qword(asd_ha, lseq, M1_SG_LIST_PTR_ADDR0);
+	PRINT_LMIP_qword(asd_ha, lseq, M1_SG_LIST_PTR_ADDR1);
+
+	asd_printk("LSEQ%d MDP 1 MODE 2 >>>>\n", lseq);
+	PRINT_LMIP_dword(asd_ha, lseq, INVALID_DWORD_COUNT);
+	PRINT_LMIP_dword(asd_ha, lseq, DISPARITY_ERROR_COUNT);
+	PRINT_LMIP_dword(asd_ha, lseq, LOSS_OF_SYNC_COUNT);
+
+	asd_printk("LSEQ%d MDP 1 MODE 4/5 >>>>\n", lseq);
+	PRINT_LMIP_dword(asd_ha, lseq, FRAME_TYPE_MASK);
+	PRINT_LMIP_dword(asd_ha, lseq, HASHED_SRC_ADDR_MASK_PRINT);
+	PRINT_LMIP_byte(asd_ha, lseq, NUM_FILL_BYTES_MASK);
+	PRINT_LMIP_word(asd_ha, lseq, TAG_MASK);
+	PRINT_LMIP_word(asd_ha, lseq, TARGET_PORT_XFER_TAG);
+	PRINT_LMIP_dword(asd_ha, lseq, DATA_OFFSET);
+
+	asd_printk("LSEQ%d MDP 2 MODE 0 >>>>\n", lseq);
+	PRINT_LMIP_dword(asd_ha, lseq, SMP_RCV_TIMER_TERM_TS);
+	PRINT_LMIP_byte(asd_ha, lseq, DEVICE_BITS);
+	PRINT_LMIP_word(asd_ha, lseq, SDB_DDB);
+	PRINT_LMIP_word(asd_ha, lseq, SDB_NUM_TAGS);
+	PRINT_LMIP_word(asd_ha, lseq, SDB_CURR_TAG);
+
+	asd_printk("LSEQ%d MDP 2 MODE 1 >>>>\n", lseq);
+	PRINT_LMIP_qword(asd_ha, lseq, TX_ID_ADDR_FRAME);
+	PRINT_LMIP_dword(asd_ha, lseq, OPEN_TIMER_TERM_TS);
+	PRINT_LMIP_dword(asd_ha, lseq, SRST_AS_TIMER_TERM_TS);
+	PRINT_LMIP_dword(asd_ha, lseq, LAST_LOADED_SG_EL);
+
+	asd_printk("LSEQ%d MDP 2 MODE 2 >>>>\n", lseq);
+	PRINT_LMIP_dword(asd_ha, lseq, CLOSE_TIMER_TERM_TS);
+	PRINT_LMIP_dword(asd_ha, lseq, BREAK_TIMER_TERM_TS);
+	PRINT_LMIP_dword(asd_ha, lseq, DWS_RESET_TIMER_TERM_TS);
+	PRINT_LMIP_dword(asd_ha, lseq, SATA_INTERLOCK_TIMER_TERM_TS);
+	PRINT_LMIP_dword(asd_ha, lseq, MCTL_TIMER_TERM_TS);
+
+	asd_printk("LSEQ%d MDP 2 MODE 4/5 >>>>\n", lseq);
+	PRINT_LMIP_dword(asd_ha, lseq, COMINIT_TIMER_TERM_TS);
+	PRINT_LMIP_dword(asd_ha, lseq, RCV_ID_TIMER_TERM_TS);
+	PRINT_LMIP_dword(asd_ha, lseq, RCV_FIS_TIMER_TERM_TS);
+	PRINT_LMIP_dword(asd_ha, lseq, DEV_PRES_TIMER_TERM_TS);
+}
+
+/**
+ * asd_dump_ddb_site -- dump a CSEQ DDB site
+ * @asd_ha: pointer to host adapter structure
+ * @site_no: site number of interest
+ */
+void asd_dump_target_ddb(struct asd_ha_struct *asd_ha, u16 site_no)
+{
+	if (site_no >= asd_ha->hw_prof.max_ddbs)
+		return;
+
+#define DDB_FIELDB(__name)                                        \
+	asd_ddbsite_read_byte(asd_ha, site_no,                    \
+			      offsetof(struct asd_ddb_ssp_smp_target_port, __name))
+#define DDB2_FIELDB(__name)                                       \
+	asd_ddbsite_read_byte(asd_ha, site_no,                    \
+			      offsetof(struct asd_ddb_stp_sata_target_port, __name))
+#define DDB_FIELDW(__name)                                        \
+	asd_ddbsite_read_word(asd_ha, site_no,                    \
+			      offsetof(struct asd_ddb_ssp_smp_target_port, __name))
+
+#define DDB_FIELDD(__name)                                         \
+	asd_ddbsite_read_dword(asd_ha, site_no,                    \
+			       offsetof(struct asd_ddb_ssp_smp_target_port, __name))
+
+	asd_printk("DDB: 0x%02x\n", site_no);
+	asd_printk("conn_type: 0x%02x\n", DDB_FIELDB(conn_type));
+	asd_printk("conn_rate: 0x%02x\n", DDB_FIELDB(conn_rate));
+	asd_printk("init_conn_tag: 0x%04x\n", be16_to_cpu(DDB_FIELDW(init_conn_tag)));
+	asd_printk("send_queue_head: 0x%04x\n", be16_to_cpu(DDB_FIELDW(send_queue_head)));
+	asd_printk("sq_suspended: 0x%02x\n", DDB_FIELDB(sq_suspended));
+	asd_printk("DDB Type: 0x%02x\n", DDB_FIELDB(ddb_type));
+	asd_printk("AWT Default: 0x%04x\n", DDB_FIELDW(awt_def));
+	asd_printk("compat_features: 0x%02x\n", DDB_FIELDB(compat_features));
+	asd_printk("Pathway Blocked Count: 0x%02x\n",
+		   DDB_FIELDB(pathway_blocked_count));
+	asd_printk("arb_wait_time: 0x%04x\n", DDB_FIELDW(arb_wait_time));
+	asd_printk("more_compat_features: 0x%08x\n",
+		   DDB_FIELDD(more_compat_features));
+	asd_printk("Conn Mask: 0x%02x\n", DDB_FIELDB(conn_mask));
+	asd_printk("flags: 0x%02x\n", DDB_FIELDB(flags));
+	asd_printk("flags2: 0x%02x\n", DDB2_FIELDB(flags2));
+	asd_printk("ExecQ Tail: 0x%04x\n",DDB_FIELDW(exec_queue_tail));
+	asd_printk("SendQ Tail: 0x%04x\n",DDB_FIELDW(send_queue_tail));
+	asd_printk("Active Task Count: 0x%04x\n",
+		   DDB_FIELDW(active_task_count));
+	asd_printk("ITNL Reason: 0x%02x\n", DDB_FIELDB(itnl_reason));
+	asd_printk("ITNL Timeout Const: 0x%04x\n", DDB_FIELDW(itnl_timeout));
+	asd_printk("ITNL timestamp: 0x%08x\n", DDB_FIELDD(itnl_timestamp));
+}
+
+void asd_dump_ddb_0(struct asd_ha_struct *asd_ha)
+{
+#define DDB0_FIELDB(__name)                                  \
+	asd_ddbsite_read_byte(asd_ha, 0,                     \
+			      offsetof(struct asd_ddb_seq_shared, __name))
+#define DDB0_FIELDW(__name)                                  \
+	asd_ddbsite_read_word(asd_ha, 0,                     \
+			      offsetof(struct asd_ddb_seq_shared, __name))
+
+#define DDB0_FIELDD(__name)                                  \
+	asd_ddbsite_read_dword(asd_ha,0 ,                    \
+			       offsetof(struct asd_ddb_seq_shared, __name))
+
+#define DDB0_FIELDA(__name, _o)                              \
+	asd_ddbsite_read_byte(asd_ha, 0,                     \
+			      offsetof(struct asd_ddb_seq_shared, __name)+_o)
+
+
+	asd_printk("DDB: 0\n");
+	asd_printk("q_free_ddb_head:%04x\n", DDB0_FIELDW(q_free_ddb_head));
+	asd_printk("q_free_ddb_tail:%04x\n", DDB0_FIELDW(q_free_ddb_tail));
+	asd_printk("q_free_ddb_cnt:%04x\n",  DDB0_FIELDW(q_free_ddb_cnt));
+	asd_printk("q_used_ddb_head:%04x\n", DDB0_FIELDW(q_used_ddb_head));
+	asd_printk("q_used_ddb_tail:%04x\n", DDB0_FIELDW(q_used_ddb_tail));
+	asd_printk("shared_mem_lock:%04x\n", DDB0_FIELDW(shared_mem_lock));
+	asd_printk("smp_conn_tag:%04x\n",    DDB0_FIELDW(smp_conn_tag));
+	asd_printk("est_nexus_buf_cnt:%04x\n", DDB0_FIELDW(est_nexus_buf_cnt));
+	asd_printk("est_nexus_buf_thresh:%04x\n",
+		   DDB0_FIELDW(est_nexus_buf_thresh));
+	asd_printk("conn_not_active:%02x\n", DDB0_FIELDB(conn_not_active));
+	asd_printk("phy_is_up:%02x\n",       DDB0_FIELDB(phy_is_up));
+	asd_printk("port_map_by_links:%02x %02x %02x %02x "
+		   "%02x %02x %02x %02x\n",
+		   DDB0_FIELDA(port_map_by_links, 0),
+		   DDB0_FIELDA(port_map_by_links, 1),
+		   DDB0_FIELDA(port_map_by_links, 2),
+		   DDB0_FIELDA(port_map_by_links, 3),
+		   DDB0_FIELDA(port_map_by_links, 4),
+		   DDB0_FIELDA(port_map_by_links, 5),
+		   DDB0_FIELDA(port_map_by_links, 6),
+		   DDB0_FIELDA(port_map_by_links, 7));
+}
+
+static void asd_dump_scb_site(struct asd_ha_struct *asd_ha, u16 site_no)
+{
+
+#define SCB_FIELDB(__name)                                                 \
+	asd_scbsite_read_byte(asd_ha, site_no, sizeof(struct scb_header)   \
+			      + offsetof(struct initiate_ssp_task, __name))
+#define SCB_FIELDW(__name)                                                 \
+	asd_scbsite_read_word(asd_ha, site_no, sizeof(struct scb_header)   \
+			      + offsetof(struct initiate_ssp_task, __name))
+#define SCB_FIELDD(__name)                                                 \
+	asd_scbsite_read_dword(asd_ha, site_no, sizeof(struct scb_header)  \
+			       + offsetof(struct initiate_ssp_task, __name))
+
+	asd_printk("Total Xfer Len: 0x%08x.\n", SCB_FIELDD(total_xfer_len));
+	asd_printk("Frame Type: 0x%02x.\n", SCB_FIELDB(ssp_frame.frame_type));
+	asd_printk("Tag: 0x%04x.\n", SCB_FIELDW(ssp_frame.tag));
+	asd_printk("Target Port Xfer Tag: 0x%04x.\n",
+		   SCB_FIELDW(ssp_frame.tptt));
+	asd_printk("Data Offset: 0x%08x.\n", SCB_FIELDW(ssp_frame.data_offs));
+	asd_printk("Retry Count: 0x%02x.\n", SCB_FIELDB(retry_count));
+}
+
+/**
+ * asd_dump_scb_sites -- dump currently used CSEQ SCB sites
+ * @asd_ha: pointer to host adapter struct
+ */
+void asd_dump_scb_sites(struct asd_ha_struct *asd_ha)
+{
+	u16	site_no;
+
+	for (site_no = 0; site_no < asd_ha->hw_prof.max_scbs; site_no++) {
+		u8 opcode;
+
+		if (!SCB_SITE_VALID(site_no))
+			continue;
+
+		/* We are only interested in SCB sites currently used.
+		 */
+		opcode = asd_scbsite_read_byte(asd_ha, site_no,
+					       offsetof(struct scb_header,
+							opcode));
+		if (opcode == 0xFF)
+			continue;
+
+		asd_printk("\nSCB: 0x%x\n", site_no);
+		asd_dump_scb_site(asd_ha, site_no);
+	}
+}
+
+/**
+ * ads_dump_seq_state -- dump CSEQ and LSEQ states
+ * @asd_ha: pointer to host adapter structure
+ * @lseq_mask: mask of LSEQs of interest
+ */
+void asd_dump_seq_state(struct asd_ha_struct *asd_ha, u8 lseq_mask)
+{
+	int lseq;
+
+	asd_dump_cseq_state(asd_ha);
+
+	if (lseq_mask != 0)
+		for_each_sequencer(lseq_mask, lseq_mask, lseq)
+			asd_dump_lseq_state(asd_ha, lseq);
+}
+
+void asd_dump_frame_rcvd(struct asd_phy *phy,
+			 struct done_list_struct *dl)
+{
+	unsigned long flags;
+	int i;
+
+	switch ((dl->status_block[1] & 0x70) >> 3) {
+	case SAS_PROTO_STP:
+		ASD_DPRINTK("STP proto device-to-host FIS:\n");
+		break;
+	default:
+	case SAS_PROTO_SSP:
+		ASD_DPRINTK("SAS proto IDENTIFY:\n");
+		break;
+	}
+	spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags);
+	for (i = 0; i < phy->sas_phy.frame_rcvd_size; i+=4)
+		ASD_DPRINTK("%02x: %02x %02x %02x %02x\n",
+			    i,
+			    phy->frame_rcvd[i],
+			    phy->frame_rcvd[i+1],
+			    phy->frame_rcvd[i+2],
+			    phy->frame_rcvd[i+3]);
+	spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);
+}
+
+static inline void asd_dump_scb(struct asd_ascb *ascb, int ind)
+{
+	asd_printk("scb%d: vaddr: 0x%p, dma_handle: 0x%08llx, next: 0x%08llx, "
+		   "index:%d, opcode:0x%02x\n",
+		   ind, ascb->dma_scb.vaddr,
+		   (u64)ascb->dma_scb.dma_handle,
+		   le64_to_cpu(ascb->scb->header.next_scb),
+		   le16_to_cpu(ascb->scb->header.index),
+		   ascb->scb->header.opcode);
+}
+
+void asd_dump_scb_list(struct asd_ascb *ascb, int num)
+{
+	int i = 0;
+
+	asd_printk("dumping %d scbs:\n", num);
+
+	asd_dump_scb(ascb, i++);
+	--num;
+
+	if (num > 0 && !list_empty(&ascb->list)) {
+		struct list_head *el;
+
+		list_for_each(el, &ascb->list) {
+			struct asd_ascb *s = list_entry(el, struct asd_ascb,
+							list);
+			asd_dump_scb(s, i++);
+			if (--num <= 0)
+				break;
+		}
+	}
+}
+
+#endif /* ASD_DEBUG */
diff -urN oldtree/drivers/scsi/aic94xx/aic94xx_dump.h newtree/drivers/scsi/aic94xx/aic94xx_dump.h
--- oldtree/drivers/scsi/aic94xx/aic94xx_dump.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/aic94xx_dump.h	2006-02-13 19:20:00.572425280 -0500
@@ -0,0 +1,53 @@
+/*
+ * Aic94xx SAS/SATA driver dump header file.
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/aic94xx/aic94xx_dump.h#10 $
+ */
+
+#ifndef _AIC94XX_DUMP_H_
+#define _AIC94XX_DUMP_H_
+
+#ifdef ASD_DEBUG
+
+void asd_dump_ddb_0(struct asd_ha_struct *asd_ha);
+void asd_dump_target_ddb(struct asd_ha_struct *asd_ha, u16 site_no);
+void asd_dump_scb_sites(struct asd_ha_struct *asd_ha);
+void asd_dump_seq_state(struct asd_ha_struct *asd_ha, u8 lseq_mask);
+void asd_dump_frame_rcvd(struct asd_phy *phy,
+			 struct done_list_struct *dl);
+void asd_dump_scb_list(struct asd_ascb *ascb, int num);
+#else /* ASD_DEBUG */
+
+static inline void asd_dump_ddb_0(struct asd_ha_struct *asd_ha) { }
+static inline void asd_dump_target_ddb(struct asd_ha_struct *asd_ha,
+				     u16 site_no) { }
+static inline void asd_dump_scb_sites(struct asd_ha_struct *asd_ha) { }
+static inline void asd_dump_seq_state(struct asd_ha_struct *asd_ha,
+				      u8 lseq_mask) { }
+static inline void asd_dump_frame_rcvd(struct asd_phy *phy,
+				       struct done_list_struct *dl) { }
+static inline void asd_dump_scb_list(struct asd_ascb *ascb, int num) { }
+#endif /* ASD_DEBUG */
+
+#endif /* _AIC94XX_DUMP_H_ */
diff -urN oldtree/drivers/scsi/aic94xx/aic94xx_hwi.c newtree/drivers/scsi/aic94xx/aic94xx_hwi.c
--- oldtree/drivers/scsi/aic94xx/aic94xx_hwi.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/aic94xx_hwi.c	2006-02-13 19:20:00.575424824 -0500
@@ -0,0 +1,1360 @@
+/*
+ * Aic94xx SAS/SATA driver hardware interface.
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/aic94xx/aic94xx_hwi.c#107 $
+ */
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+#include <scsi/sas/sas_task.h>
+
+#include "aic94xx.h"
+#include "aic94xx_reg.h"
+#include "aic94xx_hwi.h"
+#include "aic94xx_seq.h"
+#include "aic94xx_dump.h"
+
+u32 MBAR0_SWB_SIZE;
+
+/* ---------- Initialization ---------- */
+
+static void asd_get_user_sas_addr(struct asd_ha_struct *asd_ha)
+{
+	extern char sas_addr_str[];
+	/* If the user has specified a WWN it overrides other settings
+	 */
+	if (sas_addr_str[0] != '\0')
+		asd_destringify_sas_addr(asd_ha->hw_prof.sas_addr,
+					 sas_addr_str);
+	else if (asd_ha->hw_prof.sas_addr[0] != 0)
+		asd_stringify_sas_addr(sas_addr_str, asd_ha->hw_prof.sas_addr);
+}
+
+static void asd_propagate_sas_addr(struct asd_ha_struct *asd_ha)
+{
+	int i;
+
+	for (i = 0; i < ASD_MAX_PHYS; i++) {
+		if (asd_ha->hw_prof.phy_desc[i].sas_addr[0] == 0)
+			continue;
+		/* Set a phy's address only if it has none.
+		 */
+		ASD_DPRINTK("setting phy%d addr to %llx\n", i,
+			    SAS_ADDR(asd_ha->hw_prof.sas_addr));
+		memcpy(asd_ha->hw_prof.phy_desc[i].sas_addr,
+		       asd_ha->hw_prof.sas_addr, SAS_ADDR_SIZE);
+	}
+}
+
+/* ---------- PHY initialization ---------- */
+
+static void asd_init_phy_identify(struct asd_phy *phy)
+{
+	phy->identify_frame = phy->id_frm_tok->vaddr;
+
+	memset(phy->identify_frame, 0, sizeof(*phy->identify_frame));
+
+	phy->identify_frame->dev_type = SAS_END_DEV;
+	if (phy->sas_phy.role & PHY_ROLE_INITIATOR)
+		phy->identify_frame->initiator_bits = phy->sas_phy.iproto;
+	if (phy->sas_phy.role & PHY_ROLE_TARGET)
+		phy->identify_frame->target_bits = phy->sas_phy.tproto;
+	memcpy(phy->identify_frame->sas_addr, phy->phy_desc->sas_addr,
+	       SAS_ADDR_SIZE);
+	phy->identify_frame->phy_id = phy->sas_phy.id;
+}
+
+static int asd_init_phy(struct asd_phy *phy)
+{
+	struct asd_ha_struct *asd_ha = phy->sas_phy.ha->lldd_ha;
+	struct sas_phy *sas_phy = &phy->sas_phy;
+
+	sas_phy->enabled = 1;
+	sas_phy->class = SAS;
+	sas_phy->iproto = SAS_PROTO_ALL;
+	sas_phy->tproto = 0;
+	sas_phy->type = PHY_TYPE_PHYSICAL;
+	sas_phy->role = PHY_ROLE_INITIATOR;
+	sas_phy->oob_mode = OOB_NOT_CONNECTED;
+	sas_phy->linkrate = PHY_LINKRATE_NONE;
+
+	phy->id_frm_tok = asd_alloc_coherent(asd_ha,
+					     sizeof(*phy->identify_frame),
+					     GFP_KERNEL);
+	if (!phy->id_frm_tok) {
+		asd_printk("no mem for IDENTIFY for phy%d\n", sas_phy->id);
+		return -ENOMEM;
+	} else
+		asd_init_phy_identify(phy);
+
+	memset(phy->frame_rcvd, 0, sizeof(phy->frame_rcvd));
+
+	return 0;
+}
+
+static int asd_init_phys(struct asd_ha_struct *asd_ha)
+{
+	u8 i;
+	u8 phy_mask = asd_ha->hw_prof.enabled_phys;
+
+	for (i = 0; i < ASD_MAX_PHYS; i++) {
+		struct asd_phy *phy = &asd_ha->phys[i];
+
+		phy->phy_desc = &asd_ha->hw_prof.phy_desc[i];
+
+		phy->sas_phy.enabled = 0;
+		phy->sas_phy.id = i;
+		phy->sas_phy.sas_addr = &phy->phy_desc->sas_addr[0];
+		phy->sas_phy.frame_rcvd = &phy->frame_rcvd[0];
+		phy->sas_phy.ha = &asd_ha->sas_ha;
+		phy->sas_phy.lldd_phy = phy;
+	}
+
+	/* Now enable and initialize only the enabled phys. */
+	for_each_phy(phy_mask, phy_mask, i) {
+		int err = asd_init_phy(&asd_ha->phys[i]);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+/* ---------- Sliding windows ---------- */
+
+static int asd_init_sw(struct asd_ha_struct *asd_ha)
+{
+	struct pci_dev *pcidev = asd_ha->pcidev;
+	int err;
+	u32 v;
+
+	/* Unlock MBARs */
+	err = pci_read_config_dword(pcidev, PCI_CONF_MBAR_KEY, &v);
+	if (err) {
+		asd_printk("couldn't access conf. space of %s\n",
+			   pci_name(pcidev));
+		goto Err;
+	}
+	if (v)
+		err = pci_write_config_dword(pcidev, PCI_CONF_MBAR_KEY, v);
+	if (err) {
+		asd_printk("couldn't write to MBAR_KEY of %s\n",
+			   pci_name(pcidev));
+		goto Err;
+	}
+
+	/* Set sliding windows A, B and C to point to proper internal
+	 * memory regions.
+	 */
+	pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWA, REG_BASE_ADDR);
+	pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWB,
+			       REG_BASE_ADDR_CSEQCIO);
+	pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWC, REG_BASE_ADDR_EXSI);
+	asd_ha->io_handle[0].swa_base = REG_BASE_ADDR;
+	asd_ha->io_handle[0].swb_base = REG_BASE_ADDR_CSEQCIO;
+	asd_ha->io_handle[0].swc_base = REG_BASE_ADDR_EXSI;
+	MBAR0_SWB_SIZE = asd_ha->io_handle[0].len - 0x80;
+	if (!asd_ha->iospace) {
+		/* MBAR1 will point to OCM (On Chip Memory) */
+		pci_write_config_dword(pcidev, PCI_CONF_MBAR1, OCM_BASE_ADDR);
+		asd_ha->io_handle[1].swa_base = OCM_BASE_ADDR;
+	}
+	spin_lock_init(&asd_ha->iolock);
+Err:
+	return err;
+}
+
+/* ---------- SCB initialization ---------- */
+
+/**
+ * asd_init_scbs - manually allocate the first SCB.
+ * @asd_ha: pointer to host adapter structure
+ *
+ * This allocates the very first SCB which would be sent to the
+ * sequencer for execution.  Its bus address is written to
+ * CSEQ_Q_NEW_POINTER, mode page 2, mode 8.  Since the bus address of
+ * the _next_ scb to be DMA-ed to the host adapter is read from the last
+ * SCB DMA-ed to the host adapter, we have to always stay one step
+ * ahead of the sequencer and keep one SCB already allocated.
+ */
+static int asd_init_scbs(struct asd_ha_struct *asd_ha)
+{
+	struct asd_seq_data *seq = &asd_ha->seq;
+	int bitmap_bytes;
+
+	/* allocate the index array and bitmap */
+	asd_ha->seq.tc_index_bitmap_bits = asd_ha->hw_prof.max_scbs;
+	asd_ha->seq.tc_index_array = kzalloc(asd_ha->seq.tc_index_bitmap_bits*
+					     sizeof(void *), GFP_KERNEL);
+	if (!asd_ha->seq.tc_index_array)
+		return -ENOMEM;
+
+	bitmap_bytes = (asd_ha->seq.tc_index_bitmap_bits+7)/8;
+	bitmap_bytes = BITS_TO_LONGS(bitmap_bytes*8)*sizeof(unsigned long);
+	asd_ha->seq.tc_index_bitmap = kzalloc(bitmap_bytes, GFP_KERNEL);
+	if (!asd_ha->seq.tc_index_bitmap)
+		return -ENOMEM;
+
+	spin_lock_init(&seq->tc_index_lock);
+
+	seq->next_scb.size = sizeof(struct scb);
+	seq->next_scb.vaddr = dma_pool_alloc(asd_ha->scb_pool, GFP_KERNEL,
+					     &seq->next_scb.dma_handle);
+	if (!seq->next_scb.vaddr) {
+		kfree(asd_ha->seq.tc_index_bitmap);
+		kfree(asd_ha->seq.tc_index_array);
+		asd_ha->seq.tc_index_bitmap = NULL;
+		asd_ha->seq.tc_index_array = NULL;
+		return -ENOMEM;
+	}
+
+	seq->pending = 0;
+	spin_lock_init(&seq->pend_q_lock);
+	INIT_LIST_HEAD(&seq->pend_q);
+
+	return 0;
+}
+
+static inline void asd_get_max_scb_ddb(struct asd_ha_struct *asd_ha)
+{
+	asd_ha->hw_prof.max_scbs = asd_get_cmdctx_size(asd_ha)/ASD_SCB_SIZE;
+	asd_ha->hw_prof.max_ddbs = asd_get_devctx_size(asd_ha)/ASD_DDB_SIZE;
+	ASD_DPRINTK("max_scbs:%d, max_ddbs:%d\n",
+		    asd_ha->hw_prof.max_scbs,
+		    asd_ha->hw_prof.max_ddbs);
+}
+
+/* ---------- Done List initialization ---------- */
+
+static void asd_dl_tasklet_handler(unsigned long);
+
+static int asd_init_dl(struct asd_ha_struct *asd_ha)
+{
+	asd_ha->seq.actual_dl
+		= asd_alloc_coherent(asd_ha,
+			     ASD_DL_SIZE * sizeof(struct done_list_struct),
+				     GFP_KERNEL);
+	if (!asd_ha->seq.actual_dl)
+		return -ENOMEM;
+	asd_ha->seq.dl = asd_ha->seq.actual_dl->vaddr;
+	asd_ha->seq.dl_toggle = ASD_DEF_DL_TOGGLE;
+	asd_ha->seq.dl_next = 0;
+	tasklet_init(&asd_ha->seq.dl_tasklet, asd_dl_tasklet_handler,
+		     (unsigned long) asd_ha);
+
+	return 0;
+}
+
+/* ---------- EDB and ESCB init ---------- */
+
+static int asd_alloc_edbs(struct asd_ha_struct *asd_ha, unsigned int gfp_flags)
+{
+	struct asd_seq_data *seq = &asd_ha->seq;
+	int i;
+
+	seq->edb_arr = kmalloc(seq->num_edbs*sizeof(*seq->edb_arr), gfp_flags);
+	if (!seq->edb_arr)
+		return -ENOMEM;
+
+	for (i = 0; i < seq->num_edbs; i++) {
+		seq->edb_arr[i] = asd_alloc_coherent(asd_ha, ASD_EDB_SIZE,
+						     gfp_flags);
+		if (!seq->edb_arr[i])
+			goto Err_unroll;
+		memset(seq->edb_arr[i]->vaddr, 0, ASD_EDB_SIZE);
+	}
+
+	ASD_DPRINTK("num_edbs:%d\n", seq->num_edbs);
+
+	return 0;
+
+Err_unroll:
+	for (i-- ; i >= 0; i--)
+		asd_free_coherent(asd_ha, seq->edb_arr[i]);
+	kfree(seq->edb_arr);
+	seq->edb_arr = NULL;
+
+	return -ENOMEM;
+}
+
+static int asd_alloc_escbs(struct asd_ha_struct *asd_ha,
+			   unsigned int gfp_flags)
+{
+	struct asd_seq_data *seq = &asd_ha->seq;
+	struct asd_ascb *escb;
+	int i, escbs;
+
+	seq->escb_arr = kmalloc(seq->num_escbs*sizeof(*seq->escb_arr),
+				gfp_flags);
+	if (!seq->escb_arr)
+		return -ENOMEM;
+
+	escbs = seq->num_escbs;
+	escb = asd_ascb_alloc_list(asd_ha, &escbs, gfp_flags);
+	if (!escb) {
+		asd_printk("couldn't allocate list of escbs\n");
+		goto Err;
+	}
+	seq->num_escbs -= escbs;  /* subtract what was not allocated */
+	ASD_DPRINTK("num_escbs:%d\n", seq->num_escbs);
+
+	for (i = 0; i < seq->num_escbs; i++, escb = list_entry(escb->list.next,
+							       struct asd_ascb,
+							       list)) {
+		seq->escb_arr[i] = escb;
+		escb->scb->header.opcode = EMPTY_SCB;
+	}
+
+	return 0;
+Err:
+	kfree(seq->escb_arr);
+	seq->escb_arr = NULL;
+	return -ENOMEM;
+
+}
+
+static void asd_assign_edbs2escbs(struct asd_ha_struct *asd_ha)
+{
+	struct asd_seq_data *seq = &asd_ha->seq;
+	int i, k, z = 0;
+
+	for (i = 0; i < seq->num_escbs; i++) {
+		struct asd_ascb *ascb = seq->escb_arr[i];
+		struct empty_scb *escb = &ascb->scb->escb;
+
+		ascb->edb_index = z;
+
+		escb->num_valid = ASD_EDBS_PER_SCB;
+
+		for (k = 0; k < ASD_EDBS_PER_SCB; k++) {
+			struct sg_el *eb = &escb->eb[k];
+			struct asd_dma_tok *edb = seq->edb_arr[z++];
+
+			memset(eb, 0, sizeof(*eb));
+			eb->bus_addr = cpu_to_le64(((u64) edb->dma_handle));
+			eb->size = cpu_to_le32(((u32) edb->size));
+		}
+	}
+}
+
+/**
+ * asd_init_escbs -- allocate and initialize empty scbs
+ * @asd_ha: pointer to host adapter structure
+ *
+ * An empty SCB has sg_elements of ASD_EDBS_PER_SCB (7) buffers.
+ * They transport sense data, etc.
+ */
+static int asd_init_escbs(struct asd_ha_struct *asd_ha)
+{
+	struct asd_seq_data *seq = &asd_ha->seq;
+	int err = 0;
+
+	/* Allocate two empty data buffers (edb) per sequencer. */
+	int edbs = 2*(1+asd_ha->hw_prof.num_phys);
+
+	seq->num_escbs = (edbs+ASD_EDBS_PER_SCB-1)/ASD_EDBS_PER_SCB;
+	seq->num_edbs = seq->num_escbs * ASD_EDBS_PER_SCB;
+
+	err = asd_alloc_edbs(asd_ha, GFP_KERNEL);
+	if (err) {
+		asd_printk("couldn't allocate edbs\n");
+		return err;
+	}
+
+	err = asd_alloc_escbs(asd_ha, GFP_KERNEL);
+	if (err) {
+		asd_printk("couldn't allocate escbs\n");
+		return err;
+	}
+
+	asd_assign_edbs2escbs(asd_ha);
+	/* In order to insure that normal SCBs do not overfill sequencer
+	 * memory and leave no space for escbs (halting condition),
+	 * we increment pending here by the number of escbs.  However,
+	 * escbs are never pending.
+	 */
+	seq->pending   = seq->num_escbs;
+	seq->can_queue = 1 + (asd_ha->hw_prof.max_scbs - seq->pending)/2;
+
+	return 0;
+}
+
+/* ---------- HW initialization ---------- */
+
+/**
+ * asd_chip_hardrst -- hard reset the chip
+ * @asd_ha: pointer to host adapter structure
+ *
+ * This takes 16 cycles and is synchronous to CFCLK, which runs
+ * at 200 MHz, so this should take at most 80 nanoseconds.
+ */
+int asd_chip_hardrst(struct asd_ha_struct *asd_ha)
+{
+	int count = 100;
+	u32 reg;
+
+	asd_write_reg_dword(asd_ha, COMBIST, HARDRST);
+	do {
+		udelay(1);
+		reg = asd_read_reg_dword(asd_ha, CHIMINT);
+		if (reg & HARDRSTDET) {
+			asd_write_reg_dword(asd_ha, CHIMINT,
+					    HARDRSTDET|PORRSTDET);
+			return 0;
+		}
+	} while (--count > 0);
+
+	return -ENODEV;
+}
+
+/**
+ * asd_init_chip -- initialize the chip
+ * @asd_ha: pointer to host adapter structure
+ *
+ * Hard resets the chip, disables HA interrupts, downloads the sequnecer
+ * microcode and starts the sequencers.  The caller has to explicitly
+ * enable HA interrupts with asd_enable_ints(asd_ha).
+ */
+static int asd_init_chip(struct asd_ha_struct *asd_ha)
+{
+	int err;
+
+	err = asd_chip_hardrst(asd_ha);
+	if (err) {
+		asd_printk("couldn't hard reset %s\n",
+			    pci_name(asd_ha->pcidev));
+		goto out;
+	}
+
+	asd_disable_ints(asd_ha);
+
+	err = asd_init_seqs(asd_ha);
+	if (err) {
+		asd_printk("couldn't init seqs for %s\n",
+			   pci_name(asd_ha->pcidev));
+		goto out;
+	}
+
+	err = asd_start_seqs(asd_ha);
+	if (err) {
+		asd_printk("coudln't start seqs for %s\n",
+			   pci_name(asd_ha->pcidev));
+		goto out;
+	}
+out:
+	return err;
+}
+
+#define MAX_DEVS ((OCM_MAX_SIZE) / (ASD_DDB_SIZE))
+
+static int max_devs = 0;
+module_param_named(max_devs, max_devs, int, S_IRUGO);
+MODULE_PARM_DESC(max_devs, "\n"
+	"\tMaximum number of SAS devices to support (not LUs).\n"
+	"\tDefault: 2176, Maximum: 65663.\n");
+
+static int max_cmnds = 0;
+module_param_named(max_cmnds, max_cmnds, int, S_IRUGO);
+MODULE_PARM_DESC(max_cmnds, "\n"
+	"\tMaximum number of commands queuable.\n"
+	"\tDefault: 512, Maximum: 66047.\n");
+
+static void asd_extend_devctx_ocm(struct asd_ha_struct *asd_ha)
+{
+	unsigned long dma_addr = OCM_BASE_ADDR;
+	u32 d;
+
+	dma_addr -= asd_ha->hw_prof.max_ddbs * ASD_DDB_SIZE;
+	asd_write_reg_addr(asd_ha, DEVCTXBASE, (dma_addr_t) dma_addr);
+	d = asd_read_reg_dword(asd_ha, CTXDOMAIN);
+	d |= 4;
+	asd_write_reg_dword(asd_ha, CTXDOMAIN, d);
+	asd_ha->hw_prof.max_ddbs += MAX_DEVS;
+}
+
+static int asd_extend_devctx(struct asd_ha_struct *asd_ha)
+{
+	dma_addr_t dma_handle;
+	unsigned long dma_addr;
+	u32 d;
+	int size;
+
+	asd_extend_devctx_ocm(asd_ha);
+
+	asd_ha->hw_prof.ddb_ext = NULL;
+	if (max_devs <= asd_ha->hw_prof.max_ddbs || max_devs > 0xFFFF) {
+		max_devs = asd_ha->hw_prof.max_ddbs;
+		return 0;
+	}
+
+	size = (max_devs - asd_ha->hw_prof.max_ddbs + 1) * ASD_DDB_SIZE;
+
+	asd_ha->hw_prof.ddb_ext = asd_alloc_coherent(asd_ha, size, GFP_KERNEL);
+	if (!asd_ha->hw_prof.ddb_ext) {
+		asd_printk("couldn't allocate memory for %d devices\n",
+			   max_devs);
+		max_devs = asd_ha->hw_prof.max_ddbs;
+		return -ENOMEM;
+	}
+	dma_handle = asd_ha->hw_prof.ddb_ext->dma_handle;
+	dma_addr = ALIGN((unsigned long) dma_handle, ASD_DDB_SIZE);
+	dma_addr -= asd_ha->hw_prof.max_ddbs * ASD_DDB_SIZE;
+	dma_handle = (dma_addr_t) dma_addr;
+	asd_write_reg_addr(asd_ha, DEVCTXBASE, dma_handle);
+	d = asd_read_reg_dword(asd_ha, CTXDOMAIN);
+	d &= ~4;
+	asd_write_reg_dword(asd_ha, CTXDOMAIN, d);
+
+	asd_ha->hw_prof.max_ddbs = max_devs;
+
+	return 0;
+}
+
+static int asd_extend_cmdctx(struct asd_ha_struct *asd_ha)
+{
+	dma_addr_t dma_handle;
+	unsigned long dma_addr;
+	u32 d;
+	int size;
+
+	asd_ha->hw_prof.scb_ext = NULL;
+	if (max_cmnds <= asd_ha->hw_prof.max_scbs || max_cmnds > 0xFFFF) {
+		max_cmnds = asd_ha->hw_prof.max_scbs;
+		return 0;
+	}
+
+	size = (max_cmnds - asd_ha->hw_prof.max_scbs + 1) * ASD_SCB_SIZE;
+
+	asd_ha->hw_prof.scb_ext = asd_alloc_coherent(asd_ha, size, GFP_KERNEL);
+	if (!asd_ha->hw_prof.scb_ext) {
+		asd_printk("couldn't allocate memory for %d commands\n",
+			   max_cmnds);
+		max_cmnds = asd_ha->hw_prof.max_scbs;
+		return -ENOMEM;
+	}
+	dma_handle = asd_ha->hw_prof.scb_ext->dma_handle;
+	dma_addr = ALIGN((unsigned long) dma_handle, ASD_SCB_SIZE);
+	dma_addr -= asd_ha->hw_prof.max_scbs * ASD_SCB_SIZE;
+	dma_handle = (dma_addr_t) dma_addr;
+	asd_write_reg_addr(asd_ha, CMDCTXBASE, dma_handle);
+	d = asd_read_reg_dword(asd_ha, CTXDOMAIN);
+	d &= ~1;
+	asd_write_reg_dword(asd_ha, CTXDOMAIN, d);
+
+	asd_ha->hw_prof.max_scbs = max_cmnds;
+
+	return 0;
+}
+
+/**
+ * asd_init_ctxmem -- initialize context memory
+ * asd_ha: pointer to host adapter structure
+ *
+ * This function sets the maximum number of SCBs and
+ * DDBs which can be used by the sequencer.  This is normally
+ * 512 and 128 respectively.  If support for more SCBs or more DDBs
+ * is required then CMDCTXBASE, DEVCTXBASE and CTXDOMAIN are
+ * initialized here to extend context memory to point to host memory,
+ * thus allowing unlimited support for SCBs and DDBs -- only limited
+ * by host memory.
+ */
+static int asd_init_ctxmem(struct asd_ha_struct *asd_ha)
+{
+	int bitmap_bytes;
+
+	asd_get_max_scb_ddb(asd_ha);
+	asd_extend_devctx(asd_ha);
+	asd_extend_cmdctx(asd_ha);
+
+	/* The kernel wants bitmaps to be unsigned long sized. */
+	bitmap_bytes = (asd_ha->hw_prof.max_ddbs+7)/8;
+	bitmap_bytes = BITS_TO_LONGS(bitmap_bytes*8)*sizeof(unsigned long);
+	asd_ha->hw_prof.ddb_bitmap = kzalloc(bitmap_bytes, GFP_KERNEL);
+	if (!asd_ha->hw_prof.ddb_bitmap)
+		return -ENOMEM;
+	spin_lock_init(&asd_ha->hw_prof.ddb_lock);
+
+	return 0;
+}
+
+int asd_init_hw(struct asd_ha_struct *asd_ha)
+{
+	int err;
+
+	err = asd_init_sw(asd_ha);
+	if (err)
+		return err;
+
+	err = asd_read_ocm(asd_ha);
+	if (err) {
+		asd_printk("couldn't read ocm(%d)\n", err);
+		/* While suspicios, it is not an error that we
+		 * couldn't read the OCM. */
+	}
+
+	err = asd_read_flash(asd_ha);
+	if (err) {
+		asd_printk("couldn't read flash(%d)\n", err);
+		/* While suspicios, it is not an error that we
+		 * couldn't read FLASH memory.
+		 */
+	}
+
+	asd_init_ctxmem(asd_ha);
+
+	asd_get_user_sas_addr(asd_ha);
+	if (!asd_ha->hw_prof.sas_addr[0]) {
+		asd_printk("No SAS Address provided for %s\n",
+			   pci_name(asd_ha->pcidev));
+		err = -ENODEV;
+		goto Out;
+	}
+
+	asd_propagate_sas_addr(asd_ha);
+
+	err = asd_init_phys(asd_ha);
+	if (err) {
+		asd_printk("couldn't initialize phys for %s\n",
+			    pci_name(asd_ha->pcidev));
+		goto Out;
+	}
+
+	err = asd_init_scbs(asd_ha);
+	if (err) {
+		asd_printk("couldn't initialize scbs for %s\n",
+			    pci_name(asd_ha->pcidev));
+		goto Out;
+	}
+
+	err = asd_init_dl(asd_ha);
+	if (err) {
+		asd_printk("couldn't initialize the done list:%d\n",
+			    err);
+		goto Out;
+	}
+
+	err = asd_init_escbs(asd_ha);
+	if (err) {
+		asd_printk("couldn't initialize escbs\n");
+		goto Out;
+	}
+
+	err = asd_init_chip(asd_ha);
+	if (err) {
+		asd_printk("couldn't init the chip\n");
+		goto Out;
+	}
+Out:
+	return err;
+}
+
+/* ---------- Chip reset ---------- */
+
+/**
+ * asd_chip_reset -- reset the host adapter, etc
+ * @asd_ha: pointer to host adapter structure of interest
+ *
+ * Called from the ISR.  Hard reset the chip.  Let everything
+ * timeout.  This should be no different than hot-unplugging the
+ * host adapter.  Once everything times out we'll init the chip with
+ * a call to asd_init_chip() and enable interrupts with asd_enable_ints().
+ * XXX finish.
+ */
+static void asd_chip_reset(struct asd_ha_struct *asd_ha)
+{
+	struct sas_ha_struct *sas_ha = &asd_ha->sas_ha;
+
+	ASD_DPRINTK("chip reset for %s\n", pci_name(asd_ha->pcidev));
+	asd_chip_hardrst(asd_ha);
+	sas_ha->notify_ha_event(sas_ha, HAE_RESET);
+}
+
+/* ---------- Done List Routines ---------- */
+
+static void asd_dl_tasklet_handler(unsigned long data)
+{
+	struct asd_ha_struct *asd_ha = (struct asd_ha_struct *) data;
+	struct asd_seq_data *seq = &asd_ha->seq;
+	unsigned long flags;
+
+	while (1) {
+		struct done_list_struct *dl = &seq->dl[seq->dl_next];
+		struct asd_ascb *ascb;
+
+		if ((dl->toggle & DL_TOGGLE_MASK) != seq->dl_toggle)
+			break;
+
+		/* find the aSCB */
+		spin_lock_irqsave(&seq->tc_index_lock, flags);
+		ascb = asd_tc_index_find(seq, (int)le16_to_cpu(dl->index));
+		spin_unlock_irqrestore(&seq->tc_index_lock, flags);
+		if (unlikely(!ascb)) {
+			ASD_DPRINTK("BUG:sequencer:dl:no ascb?!\n");
+			goto next_1;
+		} else if (ascb->scb->header.opcode == EMPTY_SCB) {
+			goto out;
+		} else if (!ascb->uldd_timer && !del_timer(&ascb->timer)) {
+			goto next_1;
+		}
+		spin_lock_irqsave(&seq->pend_q_lock, flags);
+		list_del_init(&ascb->list);
+		seq->pending--;
+		spin_unlock_irqrestore(&seq->pend_q_lock, flags);
+	out:
+		ascb->tasklet_complete(ascb, dl);
+
+	next_1:
+		seq->dl_next = (seq->dl_next + 1) & (ASD_DL_SIZE-1);
+		if (!seq->dl_next)
+			seq->dl_toggle ^= DL_TOGGLE_MASK;
+	}
+}
+
+/* ---------- Interrupt Service Routines ---------- */
+
+/**
+ * asd_process_donelist_isr -- schedule processing of done list entries
+ * @asd_ha: pointer to host adapter structure
+ */
+static inline void asd_process_donelist_isr(struct asd_ha_struct *asd_ha)
+{
+	tasklet_schedule(&asd_ha->seq.dl_tasklet);
+}
+
+/**
+ * asd_com_sas_isr -- process device communication interrupt (COMINT)
+ * @asd_ha: pointer to host adapter structure
+ */
+static inline void asd_com_sas_isr(struct asd_ha_struct *asd_ha)
+{
+	u32 comstat = asd_read_reg_dword(asd_ha, COMSTAT);
+
+	/* clear COMSTAT int */
+	asd_write_reg_dword(asd_ha, COMSTAT, 0xFFFFFFFF);
+
+	if (comstat & CSBUFPERR) {
+		asd_printk("%s: command/status buffer dma parity error\n",
+			   pci_name(asd_ha->pcidev));
+	} else if (comstat & CSERR) {
+		int i;
+		u32 dmaerr = asd_read_reg_dword(asd_ha, DMAERR);
+		dmaerr &= 0xFF;
+		asd_printk("%s: command/status dma error, DMAERR: 0x%02x, "
+			   "CSDMAADR: 0x%04x, CSDMAADR+4: 0x%04x\n",
+			   pci_name(asd_ha->pcidev),
+			   dmaerr,
+			   asd_read_reg_dword(asd_ha, CSDMAADR),
+			   asd_read_reg_dword(asd_ha, CSDMAADR+4));
+		asd_printk("CSBUFFER:\n");
+		for (i = 0; i < 8; i++) {
+			asd_printk("%08x %08x %08x %08x\n",
+				   asd_read_reg_dword(asd_ha, CSBUFFER),
+				   asd_read_reg_dword(asd_ha, CSBUFFER+4),
+				   asd_read_reg_dword(asd_ha, CSBUFFER+8),
+				   asd_read_reg_dword(asd_ha, CSBUFFER+12));
+		}
+		asd_dump_seq_state(asd_ha, 0);
+	} else if (comstat & OVLYERR) {
+		u32 dmaerr = asd_read_reg_dword(asd_ha, DMAERR);
+		dmaerr = (dmaerr >> 8) & 0xFF;
+		asd_printk("%s: overlay dma error:0x%x\n",
+			   pci_name(asd_ha->pcidev),
+			   dmaerr);
+	}
+	asd_chip_reset(asd_ha);
+}
+
+static inline void asd_arp2_err(struct asd_ha_struct *asd_ha, u32 dchstatus)
+{
+	static const char *halt_code[256] = {
+		"UNEXPECTED_INTERRUPT0",
+		"UNEXPECTED_INTERRUPT1",
+		"UNEXPECTED_INTERRUPT2",
+		"UNEXPECTED_INTERRUPT3",
+		"UNEXPECTED_INTERRUPT4",
+		"UNEXPECTED_INTERRUPT5",
+		"UNEXPECTED_INTERRUPT6",
+		"UNEXPECTED_INTERRUPT7",
+		"UNEXPECTED_INTERRUPT8",
+		"UNEXPECTED_INTERRUPT9",
+		"UNEXPECTED_INTERRUPT10",
+		[11 ... 19] = "unknown[11,19]",
+		"NO_FREE_SCB_AVAILABLE",
+		"INVALID_SCB_OPCODE",
+		"INVALID_MBX_OPCODE",
+		"INVALID_ATA_STATE",
+		"ATA_QUEUE_FULL",
+		"ATA_TAG_TABLE_FAULT",
+		"ATA_TAG_MASK_FAULT",
+		"BAD_LINK_QUEUE_STATE",
+		"DMA2CHIM_QUEUE_ERROR",
+		"EMPTY_SCB_LIST_FULL",
+		"unknown[30]",
+		"IN_USE_SCB_ON_FREE_LIST",
+		"BAD_OPEN_WAIT_STATE",
+		"INVALID_STP_AFFILIATION",
+		"unknown[34]",
+		"EXEC_QUEUE_ERROR",
+		"TOO_MANY_EMPTIES_NEEDED",
+		"EMPTY_REQ_QUEUE_ERROR",
+		"Q_MONIRTT_MGMT_ERROR",
+		"TARGET_MODE_FLOW_ERROR",
+		"DEVICE_QUEUE_NOT_FOUND",
+		"START_IRTT_TIMER_ERROR",
+		"ABORT_TASK_ILLEGAL_REQ",
+		[43 ... 255] = "unknown[43,255]"
+	};
+
+	if (dchstatus & CSEQINT) {
+		u32 arp2int = asd_read_reg_dword(asd_ha, CARP2INT);
+
+		if (arp2int & (ARP2WAITTO|ARP2ILLOPC|ARP2PERR|ARP2CIOPERR)) {
+			asd_printk("%s: CSEQ arp2int:0x%x\n",
+				   pci_name(asd_ha->pcidev),
+				   arp2int);
+		} else if (arp2int & ARP2HALTC)
+			asd_printk("%s: CSEQ halted: %s\n",
+				   pci_name(asd_ha->pcidev),
+				   halt_code[(arp2int>>16)&0xFF]);
+		else
+			asd_printk("%s: CARP2INT:0x%x\n",
+				   pci_name(asd_ha->pcidev),
+				   arp2int);
+	}
+	if (dchstatus & LSEQINT_MASK) {
+		int lseq;
+		u8  lseq_mask = dchstatus & LSEQINT_MASK;
+
+		for_each_sequencer(lseq_mask, lseq_mask, lseq) {
+			u32 arp2int = asd_read_reg_dword(asd_ha,
+							 LmARP2INT(lseq));
+			if (arp2int & (ARP2WAITTO | ARP2ILLOPC | ARP2PERR
+				       | ARP2CIOPERR)) {
+				asd_printk("%s: LSEQ%d arp2int:0x%x\n",
+					   pci_name(asd_ha->pcidev),
+					   lseq, arp2int);
+				/* XXX we should only do lseq reset */
+			} else if (arp2int & ARP2HALTC)
+				asd_printk("%s: LSEQ%d halted: %s\n",
+					   pci_name(asd_ha->pcidev),
+					   lseq,halt_code[(arp2int>>16)&0xFF]);
+			else
+				asd_printk("%s: LSEQ%d ARP2INT:0x%x\n",
+					   pci_name(asd_ha->pcidev), lseq,
+					   arp2int);
+		}
+	}
+	asd_chip_reset(asd_ha);
+}
+
+/**
+ * asd_dch_sas_isr -- process device channel interrupt (DEVINT)
+ * @asd_ha: pointer to host adapter structure
+ */
+static inline void asd_dch_sas_isr(struct asd_ha_struct *asd_ha)
+{
+	u32 dchstatus = asd_read_reg_dword(asd_ha, DCHSTATUS);
+
+	if (dchstatus & CFIFTOERR) {
+		asd_printk("%s: CFIFTOERR\n", pci_name(asd_ha->pcidev));
+		asd_chip_reset(asd_ha);
+	} else
+		asd_arp2_err(asd_ha, dchstatus);
+}
+
+/**
+ * ads_rbi_exsi_isr -- process external system interface interrupt (INITERR)
+ * @asd_ha: pointer to host adapter structure
+ */
+static inline void asd_rbi_exsi_isr(struct asd_ha_struct *asd_ha)
+{
+	u32 stat0r = asd_read_reg_dword(asd_ha, ASISTAT0R);
+
+	if (!(stat0r & ASIERR)) {
+		asd_printk("hmm, EXSI interrupted but no error?\n");
+		return;
+	}
+
+	if (stat0r & ASIFMTERR) {
+		asd_printk("ASI SEEPROM format error for %s\n",
+			   pci_name(asd_ha->pcidev));
+	} else if (stat0r & ASISEECHKERR) {
+		u32 stat1r = asd_read_reg_dword(asd_ha, ASISTAT1R);
+		asd_printk("ASI SEEPROM checksum 0x%x error for %s\n",
+			   stat1r & CHECKSUM_MASK,
+			   pci_name(asd_ha->pcidev));
+	} else {
+		u32 statr = asd_read_reg_dword(asd_ha, ASIERRSTATR);
+
+		if (!(statr & CPI2ASIMSTERR_MASK)) {
+			ASD_DPRINTK("hmm, ASIERR?\n");
+			return;
+		} else {
+			u32 addr = asd_read_reg_dword(asd_ha, ASIERRADDR);
+			u32 data = asd_read_reg_dword(asd_ha, ASIERRDATAR);
+
+			asd_printk("%s: CPI2 xfer err: addr: 0x%x, wdata: 0x%x, "
+				   "count: 0x%x, byteen: 0x%x, targerr: 0x%x "
+				   "master id: 0x%x, master err: 0x%x\n",
+				   pci_name(asd_ha->pcidev),
+				   addr, data,
+				   (statr & CPI2ASIBYTECNT_MASK) >> 16,
+				   (statr & CPI2ASIBYTEEN_MASK) >> 12,
+				   (statr & CPI2ASITARGERR_MASK) >> 8,
+				   (statr & CPI2ASITARGMID_MASK) >> 4,
+				   (statr & CPI2ASIMSTERR_MASK));
+		}
+	}
+	asd_chip_reset(asd_ha);
+}
+
+/**
+ * asd_hst_pcix_isr -- process host interface interrupts
+ * @asd_ha: pointer to host adapter structure
+ *
+ * Asserted on PCIX errors: target abort, etc.
+ */
+static inline void asd_hst_pcix_isr(struct asd_ha_struct *asd_ha)
+{
+	u16 status;
+	u32 pcix_status;
+	u32 ecc_status;
+
+	pci_read_config_word(asd_ha->pcidev, PCI_STATUS, &status);
+	pci_read_config_dword(asd_ha->pcidev, PCIX_STATUS, &pcix_status);
+	pci_read_config_dword(asd_ha->pcidev, ECC_CTRL_STAT, &ecc_status);
+
+	if (status & PCI_STATUS_DETECTED_PARITY)
+		asd_printk("parity error for %s\n", pci_name(asd_ha->pcidev));
+	else if (status & PCI_STATUS_REC_MASTER_ABORT)
+		asd_printk("master abort for %s\n", pci_name(asd_ha->pcidev));
+	else if (status & PCI_STATUS_REC_TARGET_ABORT)
+		asd_printk("target abort for %s\n", pci_name(asd_ha->pcidev));
+	else if (status & PCI_STATUS_PARITY)
+		asd_printk("data parity for %s\n", pci_name(asd_ha->pcidev));
+	else if (pcix_status & RCV_SCE) {
+		asd_printk("received split completion error for %s\n",
+			   pci_name(asd_ha->pcidev));
+		pci_write_config_dword(asd_ha->pcidev,PCIX_STATUS,pcix_status);
+		/* XXX: Abort task? */
+		return;
+	} else if (pcix_status & UNEXP_SC) {
+		asd_printk("unexpected split completion for %s\n",
+			   pci_name(asd_ha->pcidev));
+		pci_write_config_dword(asd_ha->pcidev,PCIX_STATUS,pcix_status);
+		/* ignore */
+		return;
+	} else if (pcix_status & SC_DISCARD)
+		asd_printk("split completion discarded for %s\n",
+			   pci_name(asd_ha->pcidev));
+	else if (ecc_status & UNCOR_ECCERR)
+		asd_printk("uncorrectable ECC error for %s\n",
+			   pci_name(asd_ha->pcidev));
+	asd_chip_reset(asd_ha);
+}
+
+/**
+ * asd_hw_isr -- host adapter interrupt service routine
+ * @irq: ignored
+ * @dev_id: pointer to host adapter structure
+ * @regs: ignored
+ *
+ * The ISR processes done list entries and level 3 error handling.
+ */
+irqreturn_t asd_hw_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct asd_ha_struct *asd_ha = dev_id;
+	u32 chimint = asd_read_reg_dword(asd_ha, CHIMINT);
+
+	if (!chimint)
+		return IRQ_NONE;
+
+	asd_write_reg_dword(asd_ha, CHIMINT, chimint);
+	(void) asd_read_reg_dword(asd_ha, CHIMINT);
+
+	if (chimint & DLAVAIL)
+		asd_process_donelist_isr(asd_ha);
+	if (chimint & COMINT)
+		asd_com_sas_isr(asd_ha);
+	if (chimint & DEVINT)
+		asd_dch_sas_isr(asd_ha);
+	if (chimint & INITERR)
+		asd_rbi_exsi_isr(asd_ha);
+	if (chimint & HOSTERR)
+		asd_hst_pcix_isr(asd_ha);
+
+	return IRQ_HANDLED;
+}
+
+/* ---------- SCB handling ---------- */
+
+static inline struct asd_ascb *asd_ascb_alloc(struct asd_ha_struct *asd_ha,
+					      unsigned int gfp_flags)
+{
+	extern kmem_cache_t *asd_ascb_cache;
+	struct asd_seq_data *seq = &asd_ha->seq;
+	struct asd_ascb *ascb;
+	unsigned long flags;
+
+	ascb = kmem_cache_alloc(asd_ascb_cache, gfp_flags);
+
+	if (ascb) {
+		memset(ascb, 0, sizeof(*ascb));
+		ascb->dma_scb.size = sizeof(struct scb);
+		ascb->dma_scb.vaddr = dma_pool_alloc(asd_ha->scb_pool,
+						     gfp_flags,
+						    &ascb->dma_scb.dma_handle);
+		if (!ascb->dma_scb.vaddr) {
+			kmem_cache_free(asd_ascb_cache, ascb);
+			return NULL;
+		}
+		memset(ascb->dma_scb.vaddr, 0, sizeof(struct scb));
+		asd_init_ascb(asd_ha, ascb);
+
+		spin_lock_irqsave(&seq->tc_index_lock, flags);
+		ascb->tc_index = asd_tc_index_get(seq, ascb);
+		spin_unlock_irqrestore(&seq->tc_index_lock, flags);
+		if (ascb->tc_index == -1)
+			goto undo;
+
+		ascb->scb->header.index = cpu_to_le16((u16)ascb->tc_index);
+	}
+
+	return ascb;
+undo:
+	dma_pool_free(asd_ha->scb_pool, ascb->dma_scb.vaddr,
+		      ascb->dma_scb.dma_handle);
+	kmem_cache_free(asd_ascb_cache, ascb);
+	ASD_DPRINTK("no index for ascb\n");
+	return NULL;
+}
+
+/**
+ * asd_ascb_alloc_list -- allocate a list of aSCBs
+ * @asd_ha: pointer to host adapter structure
+ * @num: pointer to integer number of aSCBs
+ * @gfp_flags: GFP_ flags.
+ *
+ * This is the only function which is used to allocate aSCBs.
+ * It can allocate one or many. If more than one, then they form
+ * a linked list in two ways: by their list field of the ascb struct
+ * and by the next_scb field of the scb_header.
+ *
+ * Returns NULL if no memory was available, else pointer to a list
+ * of ascbs.  When this function returns, @num would be the number
+ * of SCBs which were not able to be allocated, 0 if all requested
+ * were able to be allocated.
+ */
+struct asd_ascb *asd_ascb_alloc_list(struct asd_ha_struct
+				     *asd_ha, int *num,
+				     unsigned int gfp_flags)
+{
+	struct asd_ascb *first = NULL;
+
+	for ( ; *num > 0; --*num) {
+		struct asd_ascb *ascb = asd_ascb_alloc(asd_ha, gfp_flags);
+
+		if (!ascb)
+			break;
+		else if (!first)
+			first = ascb;
+		else {
+			struct asd_ascb *last = list_entry(first->list.prev,
+							   struct asd_ascb,
+							   list);
+			list_add_tail(&ascb->list, &first->list);
+			last->scb->header.next_scb =
+				cpu_to_le64(((u64)ascb->dma_scb.dma_handle));
+		}
+	}
+
+	return first;
+}
+
+/**
+ * asd_swap_head_scb -- swap the head scb
+ * @asd_ha: pointer to host adapter structure
+ * @ascb: pointer to the head of an ascb list
+ *
+ * The sequencer knows the DMA address of the next SCB to be DMAed to
+ * the host adapter, from initialization or from the last list DMAed.
+ * seq->next_scb keeps the address of this SCB.  The sequencer will
+ * DMA to the host adapter this list of SCBs.  But the head (first
+ * element) of this list is not known to the sequencer.  Here we swap
+ * the head of the list with the known SCB (memcpy()).
+ * Only one memcpy() is required per list so it is in our interest
+ * to keep the list of SCB as long as possible so that the ratio
+ * of number of memcpy calls to the number of SCB DMA-ed is as small
+ * as possible.
+ *
+ * LOCKING: called with the pending list lock held.
+ */
+static inline void asd_swap_head_scb(struct asd_ha_struct *asd_ha,
+				     struct asd_ascb *ascb)
+{
+	struct asd_seq_data *seq = &asd_ha->seq;
+	struct asd_ascb *last = list_entry(ascb->list.prev,
+					   struct asd_ascb,
+					   list);
+	struct asd_dma_tok t = ascb->dma_scb;
+
+	memcpy(seq->next_scb.vaddr, ascb->scb, sizeof(*ascb->scb));
+	ascb->dma_scb = seq->next_scb;
+	ascb->scb = ascb->dma_scb.vaddr;
+	seq->next_scb = t;
+	last->scb->header.next_scb =
+		cpu_to_le64(((u64)seq->next_scb.dma_handle));
+}
+
+/**
+ * asd_start_timers -- (add and) start timers of SCBs
+ * @list: pointer to struct list_head of the scbs
+ * @to: timeout in jiffies
+ *
+ * If an SCB in the @list has no timer function, assign the default
+ * one,  then start the timer of the SCB.  This function is
+ * intended to be called from asd_post_ascb_list(), just prior to
+ * posting the SCBs to the sequencer.
+ */
+static inline void asd_start_scb_timers(struct list_head *list)
+{
+	struct asd_ascb *ascb;
+	list_for_each_entry(ascb, list, list) {
+		if (!ascb->uldd_timer) {
+			ascb->timer.data = (unsigned long) ascb;
+			ascb->timer.function = asd_ascb_timedout;
+			ascb->timer.expires = jiffies + AIC94XX_SCB_TIMEOUT;
+			add_timer(&ascb->timer);
+		}
+	}
+}
+
+/**
+ * asd_post_ascb_list -- post a list of 1 or more aSCBs to the host adapter
+ * @asd_ha: pointer to a host adapter structure
+ * @ascb: pointer to the first aSCB in the list
+ * @num: number of aSCBs in the list (to be posted)
+ *
+ * See queueing comment in asd_post_escb_list().
+ *
+ * Additional note on queuing: In order to minimize the ratio of memcpy()
+ * to the number of ascbs sent, we try to batch-send as many ascbs as possible
+ * in one go.
+ * Two cases are possible:
+ *    A) can_queue >= num,
+ *    B) can_queue < num.
+ * Case A: we can send the whole batch at once.  Increment "pending"
+ * in the beginning of this function, when it is checked, in order to
+ * eliminate races when this function is called by multiple processes.
+ * Case B: should never happen if the managing layer considers
+ * lldd_queue_size.
+ */
+int asd_post_ascb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb,
+		       int num)
+{
+	unsigned long flags;
+	LIST_HEAD(list);
+	int can_queue;
+
+	spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags);
+	can_queue = asd_ha->hw_prof.max_scbs - asd_ha->seq.pending;
+	if (can_queue >= num)
+		asd_ha->seq.pending += num;
+	else
+		can_queue = 0;
+
+	if (!can_queue) {
+		spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);
+		asd_printk("%s: scb queue full\n", pci_name(asd_ha->pcidev));
+		return -SAS_QUEUE_FULL;
+	}
+
+	asd_swap_head_scb(asd_ha, ascb);
+
+	__list_add(&list, ascb->list.prev, &ascb->list);
+
+	asd_start_scb_timers(&list);
+
+	asd_ha->seq.scbpro += num;
+	list_splice_init(&list, asd_ha->seq.pend_q.prev);
+	asd_write_reg_dword(asd_ha, SCBPRO, (u32)asd_ha->seq.scbpro);
+	spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);
+
+	return 0;
+}
+
+/**
+ * asd_post_escb_list -- post a list of 1 or more empty scb
+ * @asd_ha: pointer to a host adapter structure
+ * @ascb: pointer to the first empty SCB in the list
+ * @num: number of aSCBs in the list (to be posted)
+ *
+ * This is essentially the same as asd_post_ascb_list, but we do not
+ * increment pending, add those to the pending list or get indexes.
+ * See asd_init_escbs() and asd_init_post_escbs().
+ *
+ * Since sending a list of ascbs is a superset of sending a single
+ * ascb, this function exists to generalize this.  More specifically,
+ * when sending a list of those, we want to do only a _single_
+ * memcpy() at swap head, as opposed to for each ascb sent (in the
+ * case of sending them one by one).  That is, we want to minimize the
+ * ratio of memcpy() operations to the number of ascbs sent.  The same
+ * logic applies to asd_post_ascb_list().
+ */
+int asd_post_escb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb,
+		       int num)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags);
+	asd_swap_head_scb(asd_ha, ascb);
+	asd_ha->seq.scbpro += num;
+	asd_write_reg_dword(asd_ha, SCBPRO, (u32)asd_ha->seq.scbpro);
+	spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);
+
+	return 0;
+}
+
+/* ---------- LED ---------- */
+
+/**
+ * asd_turn_led -- turn on/off an LED
+ * @asd_ha: pointer to host adapter structure
+ * @phy_id: the PHY id whose LED we want to manupulate
+ * @op: 1 to turn on, 0 to turn off
+ */
+void asd_turn_led(struct asd_ha_struct *asd_ha, int phy_id, int op)
+{
+	if (phy_id < ASD_MAX_PHYS) {
+		u32 v = asd_read_reg_dword(asd_ha, LmCONTROL(phy_id));
+		if (op)
+			v |= LEDPOL;
+		else
+			v &= ~LEDPOL;
+		asd_write_reg_dword(asd_ha, LmCONTROL(phy_id), v);
+	}
+}
+
+/**
+ * asd_control_led -- enable/disable an LED on the board
+ * @asd_ha: pointer to host adapter structure
+ * @phy_id: integer, the phy id
+ * @op: integer, 1 to enable, 0 to disable the LED
+ *
+ * First we output enable the LED, then we set the source
+ * to be an external module.
+ */
+void asd_control_led(struct asd_ha_struct *asd_ha, int phy_id, int op)
+{
+	if (phy_id < ASD_MAX_PHYS) {
+		u32 v;
+
+		v = asd_read_reg_dword(asd_ha, GPIOOER);
+		if (op)
+			v |= (1 << phy_id);
+		else
+			v &= ~(1 << phy_id);
+		asd_write_reg_dword(asd_ha, GPIOOER, v);
+
+		v = asd_read_reg_dword(asd_ha, GPIOCNFGR);
+		if (op)
+			v |= (1 << phy_id);
+		else
+			v &= ~(1 << phy_id);
+		asd_write_reg_dword(asd_ha, GPIOCNFGR, v);
+	}
+}
+
+/* ---------- PHY enable ---------- */
+
+static int asd_enable_phy(struct asd_ha_struct *asd_ha, int phy_id)
+{
+	struct asd_phy *phy = &asd_ha->phys[phy_id];
+
+	asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, INT_ENABLE_2), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, HOT_PLUG_DELAY),
+			   HOTPLUG_DELAY_TIMEOUT);
+
+	/* Get defaults from manuf. sector */
+	/* XXX we need defaults for those in case MS is broken. */
+	asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_0),
+			   phy->phy_desc->phy_control_0);
+	asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_1),
+			   phy->phy_desc->phy_control_1);
+	asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_2),
+			   phy->phy_desc->phy_control_2);
+	asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_3),
+			   phy->phy_desc->phy_control_3);
+
+	asd_write_reg_dword(asd_ha, LmSEQ_TEN_MS_COMINIT_TIMEOUT(phy_id),
+			    ASD_COMINIT_TIMEOUT);
+
+	asd_write_reg_addr(asd_ha, LmSEQ_TX_ID_ADDR_FRAME(phy_id),
+			   phy->id_frm_tok->dma_handle);
+
+	asd_control_led(asd_ha, phy_id, 1);
+
+	return 0;
+}
+
+int asd_enable_phys(struct asd_ha_struct *asd_ha, const u8 phy_mask)
+{
+	u8  phy_m;
+	u8  i;
+	int num = 0, k;
+	struct asd_ascb *ascb;
+	struct asd_ascb *ascb_list;
+
+	if (!phy_mask) {
+		asd_printk("%s called with phy_mask of 0!?\n", __FUNCTION__);
+		return 0;
+	}
+
+	for_each_phy(phy_mask, phy_m, i) {
+		num++;
+		asd_enable_phy(asd_ha, i);
+	}
+
+	k = num;
+	ascb_list = asd_ascb_alloc_list(asd_ha, &k, GFP_KERNEL);
+	if (!ascb_list) {
+		asd_printk("no memory for control phy ascb list\n");
+		return -ENOMEM;
+	}
+	num -= k;
+
+	ascb = ascb_list;
+	for_each_phy(phy_mask, phy_m, i) {
+		asd_build_control_phy(ascb, i, ENABLE_PHY);
+		ascb = list_entry(ascb->list.next, struct asd_ascb, list);
+	}
+	ASD_DPRINTK("posting %d control phy scbs\n", num);
+	k = asd_post_ascb_list(asd_ha, ascb_list, num);
+	if (k)
+		asd_ascb_free_list(ascb_list);
+
+	return k;
+}
diff -urN oldtree/drivers/scsi/aic94xx/aic94xx_hwi.h newtree/drivers/scsi/aic94xx/aic94xx_hwi.h
--- oldtree/drivers/scsi/aic94xx/aic94xx_hwi.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/aic94xx_hwi.h	2006-02-13 19:20:00.576424672 -0500
@@ -0,0 +1,398 @@
+/*
+ * Aic94xx SAS/SATA driver hardware interface header file.
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/aic94xx/aic94xx_hwi.h#74 $
+ */
+
+#ifndef _AIC94XX_HWI_H_
+#define _AIC94XX_HWI_H_
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+
+#include <scsi/sas/sas_class.h>
+
+#include "aic94xx.h"
+#include "aic94xx_sas.h"
+
+/* Define ASD_MAX_PHYS to the maximum phys ever. Currently 8. */
+#define ASD_MAX_PHYS       8
+#define ASD_PCBA_SN_SIZE   12
+
+/* Those are to be further named properly, the "RAZORx" part, and
+ * subsequently included in include/linux/pci_ids.h.
+ */
+#define PCI_DEVICE_ID_ADAPTEC2_RAZOR10 0x410
+#define PCI_DEVICE_ID_ADAPTEC2_RAZOR12 0x412
+#define PCI_DEVICE_ID_ADAPTEC2_RAZOR1E 0x41E
+#define PCI_DEVICE_ID_ADAPTEC2_RAZOR30 0x430
+#define PCI_DEVICE_ID_ADAPTEC2_RAZOR32 0x432
+#define PCI_DEVICE_ID_ADAPTEC2_RAZOR3E 0x43E
+#define PCI_DEVICE_ID_ADAPTEC2_RAZOR3F 0x43F
+
+struct asd_ha_addrspace {
+	void __iomem  *addr;
+	unsigned long  start;       /* pci resource start */
+	unsigned long  len;         /* pci resource len */
+	unsigned long  flags;       /* pci resource flags */
+
+	/* addresses internal to the host adapter */
+	u32 swa_base; /* mmspace 1 (MBAR1) uses this only */
+	u32 swb_base;
+	u32 swc_base;
+};
+
+struct bios_struct {
+	int    present;
+	u8     maj;
+	u8     min;
+	u32    bld;
+};
+
+struct unit_element_struct {
+	u16    num;
+	u16    size;
+	void   *area;
+};
+
+struct flash_struct {
+	u32    bar;
+	int    present;
+	int    wide;
+	u8     manuf;
+	u8     dev_id;
+	u8     sec_prot;
+
+	u32    dir_offs;
+};
+
+struct asd_phy_desc {
+	/* From CTRL-A settings, then set to what is appropriate */
+	u8     sas_addr[SAS_ADDR_SIZE];
+	u8     max_sas_lrate;
+	u8     min_sas_lrate;
+	u8     max_sata_lrate;
+	u8     min_sata_lrate;
+	u8     flags;
+#define ASD_CRC_DIS  1
+#define ASD_SATA_SPINUP_HOLD 2
+
+	u8     phy_control_0; /* mode 5 reg 0x160 */
+	u8     phy_control_1; /* mode 5 reg 0x161 */
+	u8     phy_control_2; /* mode 5 reg 0x162 */
+	u8     phy_control_3; /* mode 5 reg 0x163 */
+};
+
+struct asd_dma_tok {
+	void *vaddr;
+	dma_addr_t dma_handle;
+	size_t size;
+};
+
+struct hw_profile {
+	struct bios_struct bios;
+	struct unit_element_struct ue;
+	struct flash_struct flash;
+
+	u8     sas_addr[SAS_ADDR_SIZE];
+	char   pcba_sn[ASD_PCBA_SN_SIZE+1];
+
+	u8     enabled_phys;	  /* mask of enabled phys */
+	struct asd_phy_desc phy_desc[ASD_MAX_PHYS];
+	u32    max_scbs;	  /* absolute sequencer scb queue size */
+	struct asd_dma_tok *scb_ext;
+	u32    max_ddbs;
+	struct asd_dma_tok *ddb_ext;
+
+	spinlock_t ddb_lock;
+	void  *ddb_bitmap;
+
+	int    num_phys;	  /* ENABLEABLE */
+	int    max_phys;	  /* REPORTED + ENABLEABLE */
+
+	unsigned addr_range;	  /* max # of addrs; max # of possible ports */
+	unsigned port_name_base;
+	unsigned dev_name_base;
+	unsigned sata_name_base;
+};
+
+struct asd_ascb {
+	struct list_head list;
+	struct asd_ha_struct *ha;
+
+	struct scb *scb;	  /* equals dma_scb->vaddr */
+	struct asd_dma_tok dma_scb;
+	struct asd_dma_tok *sg_arr;
+
+	void (*tasklet_complete)(struct asd_ascb *, struct done_list_struct *);
+	u8     uldd_timer:1;
+
+	/* internally generated command */
+	struct timer_list timer;
+	struct completion completion;
+	u8        tag_valid:1;
+	__be16    tag;		  /* error recovery only */
+
+	/* If this is an Empty SCB, index of first edb in seq->edb_arr. */
+	int    edb_index;
+
+	/* Used by the timer timeout function. */
+	int    tc_index;
+
+	void   *uldd_task;
+};
+
+#define ASD_DL_SIZE_BITS   0x8
+#define ASD_DL_SIZE        (1<<(2+ASD_DL_SIZE_BITS))
+#define ASD_DEF_DL_TOGGLE  0x01
+
+struct asd_seq_data {
+	spinlock_t pend_q_lock;
+	u16    scbpro;
+	int    pending;
+	struct list_head pend_q;
+	int    can_queue;	  /* per adapter */
+	struct asd_dma_tok next_scb; /* next scb to be delivered to CSEQ */
+
+	spinlock_t tc_index_lock;
+	void **tc_index_array;
+	void *tc_index_bitmap;
+	int   tc_index_bitmap_bits;
+
+	struct tasklet_struct dl_tasklet;
+	struct done_list_struct *dl; /* array of done list entries, equals */
+	struct asd_dma_tok *actual_dl; /* actual_dl->vaddr */
+	int    dl_toggle;
+	int    dl_next;
+
+	int    num_edbs;
+	struct asd_dma_tok **edb_arr;
+	int    num_escbs;
+	struct asd_ascb **escb_arr; /* array of pointers to escbs */
+};
+
+/* This is the Host Adapter structure.  It describes the hardware
+ * SAS adapter.
+ */
+struct asd_ha_struct {
+	struct pci_dev   *pcidev;
+	const char       *name;
+
+	struct sas_ha_struct sas_ha;
+
+	u8                revision_id;
+
+	int               iospace;
+	spinlock_t        iolock;
+	struct asd_ha_addrspace io_handle[2];
+
+	struct hw_profile hw_prof;
+
+	struct asd_phy    phys[ASD_MAX_PHYS];
+	struct sas_port   ports[ASD_MAX_PHYS];
+
+	struct dma_pool  *scb_pool;
+
+	struct asd_seq_data  seq; /* sequencer related */
+};
+
+/* ---------- Common macros ---------- */
+
+#define ASD_BUSADDR_LO(__dma_handle) ((u32)(__dma_handle))
+#define ASD_BUSADDR_HI(__dma_handle) (((sizeof(dma_addr_t))==8)     \
+                                    ? ((u32)((__dma_handle) >> 32)) \
+                                    : ((u32)0))
+
+#define dev_to_asd_ha(__dev)  pci_get_drvdata(to_pci_dev(__dev))
+#define SCB_SITE_VALID(__site_no) (((__site_no) & 0xF0FF) != 0x00FF   \
+				 && ((__site_no) & 0xF0FF) > 0x001F)
+/* For each bit set in __lseq_mask, set __lseq to equal the bit
+ * position of the set bit and execute the statement following.
+ * __mc is the temporary mask, used as a mask "counter".
+ */
+#define for_each_sequencer(__lseq_mask, __mc, __lseq)                        \
+	for ((__mc)=(__lseq_mask),(__lseq)=0;(__mc)!=0;(__lseq++),(__mc)>>=1)\
+		if (((__mc) & 1))
+#define for_each_phy(__lseq_mask, __mc, __lseq)                              \
+	for ((__mc)=(__lseq_mask),(__lseq)=0;(__mc)!=0;(__lseq++),(__mc)>>=1)\
+		if (((__mc) & 1))
+
+#define PHY_ENABLED(_HA, _I) ((_HA)->hw_prof.enabled_phys & (1<<(_I)))
+
+/* ---------- DMA allocs ---------- */
+
+static inline struct asd_dma_tok *asd_dmatok_alloc(unsigned int flags)
+{
+	return kmem_cache_alloc(asd_dma_token_cache, flags);
+}
+
+static inline void asd_dmatok_free(struct asd_dma_tok *token)
+{
+	kmem_cache_free(asd_dma_token_cache, token);
+}
+
+static inline struct asd_dma_tok *asd_alloc_coherent(struct asd_ha_struct *
+						     asd_ha, size_t size,
+						     unsigned int flags)
+{
+	struct asd_dma_tok *token = asd_dmatok_alloc(flags);
+	if (token) {
+		token->size = size;
+		token->vaddr = dma_alloc_coherent(&asd_ha->pcidev->dev,
+						  token->size,
+						  &token->dma_handle,
+						  flags);
+		if (!token->vaddr) {
+			asd_dmatok_free(token);
+			token = NULL;
+		}
+	}
+	return token;
+}
+
+static inline void asd_free_coherent(struct asd_ha_struct *asd_ha,
+				     struct asd_dma_tok *token)
+{
+	if (token) {
+		dma_free_coherent(&asd_ha->pcidev->dev, token->size,
+				  token->vaddr, token->dma_handle);
+		asd_dmatok_free(token);
+	}
+}
+
+static inline void asd_init_ascb(struct asd_ha_struct *asd_ha,
+				 struct asd_ascb *ascb)
+{
+	INIT_LIST_HEAD(&ascb->list);
+	ascb->scb = ascb->dma_scb.vaddr;
+	ascb->ha = asd_ha;
+	ascb->timer.function = NULL;
+	init_timer(&ascb->timer);
+	ascb->tc_index = -1;
+	init_completion(&ascb->completion);
+}
+
+/* Must be called with the tc_index_lock held!
+ */
+static inline void asd_tc_index_release(struct asd_seq_data *seq, int index)
+{
+	seq->tc_index_array[index] = NULL;
+	clear_bit(index, seq->tc_index_bitmap);
+}
+
+/* Must be called with the tc_index_lock held!
+ */
+static inline int asd_tc_index_get(struct asd_seq_data *seq, void *ptr)
+{
+	int index;
+
+	index = find_first_zero_bit(seq->tc_index_bitmap,
+				    seq->tc_index_bitmap_bits);
+	if (index == seq->tc_index_bitmap_bits)
+		return -1;
+
+	seq->tc_index_array[index] = ptr;
+	set_bit(index, seq->tc_index_bitmap);
+
+	return index;
+}
+
+/* Must be called with the tc_index_lock held!
+ */
+static inline void *asd_tc_index_find(struct asd_seq_data *seq, int index)
+{
+	return seq->tc_index_array[index];
+}
+
+/**
+ * asd_ascb_free -- free a single aSCB after is has completed
+ * @ascb: pointer to the aSCB of interest
+ *
+ * This frees an aSCB after it has been executed/completed by
+ * the sequencer.
+ */
+static inline void asd_ascb_free(struct asd_ascb *ascb)
+{
+	if (ascb) {
+		struct asd_ha_struct *asd_ha = ascb->ha;
+		unsigned long flags;
+
+		BUG_ON(!list_empty(&ascb->list));
+		spin_lock_irqsave(&ascb->ha->seq.tc_index_lock, flags);
+		asd_tc_index_release(&ascb->ha->seq, ascb->tc_index);
+		spin_unlock_irqrestore(&ascb->ha->seq.tc_index_lock, flags);
+		dma_pool_free(asd_ha->scb_pool, ascb->dma_scb.vaddr,
+			      ascb->dma_scb.dma_handle);
+		kmem_cache_free(asd_ascb_cache, ascb);
+	}
+}
+
+/**
+ * asd_ascb_list_free -- free a list of ascbs
+ * @ascb_list: a list of ascbs
+ *
+ * This function will free a list of ascbs allocated by asd_ascb_alloc_list.
+ * It is used when say the scb queueing function returned QUEUE_FULL,
+ * and we do not need the ascbs any more.
+ */
+static inline void asd_ascb_free_list(struct asd_ascb *ascb_list)
+{
+	LIST_HEAD(list);
+	struct list_head *n, *pos;
+
+	__list_add(&list, ascb_list->list.prev, &ascb_list->list);
+	list_for_each_safe(pos, n, &list) {
+		list_del_init(pos);
+		asd_ascb_free(list_entry(pos, struct asd_ascb, list));
+	}
+}
+
+/* ---------- Function declarations ---------- */
+
+int  asd_init_hw(struct asd_ha_struct *asd_ha);
+irqreturn_t asd_hw_isr(int irq, void *dev_id, struct pt_regs *regs);
+
+
+struct asd_ascb *asd_ascb_alloc_list(struct asd_ha_struct
+				     *asd_ha, int *num,
+				     unsigned int gfp_mask);
+
+int  asd_post_ascb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb,
+			int num);
+int  asd_post_escb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb,
+			int num);
+
+int  asd_init_post_escbs(struct asd_ha_struct *asd_ha);
+void asd_build_control_phy(struct asd_ascb *ascb, int phy_id, u8 subfunc);
+void asd_control_led(struct asd_ha_struct *asd_ha, int phy_id, int op);
+void asd_turn_led(struct asd_ha_struct *asd_ha, int phy_id, int op);
+int  asd_enable_phys(struct asd_ha_struct *asd_ha, const u8 phy_mask);
+void asd_build_initiate_link_adm_task(struct asd_ascb *ascb, int phy_id,
+				      u8 subfunc);
+
+void asd_ascb_timedout(unsigned long data);
+int  asd_chip_hardrst(struct asd_ha_struct *asd_ha);
+
+#endif
diff -urN oldtree/drivers/scsi/aic94xx/aic94xx_init.c newtree/drivers/scsi/aic94xx/aic94xx_init.c
--- oldtree/drivers/scsi/aic94xx/aic94xx_init.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/aic94xx_init.c	2006-02-13 19:20:00.577424520 -0500
@@ -0,0 +1,809 @@
+/*
+ * Aic94xx SAS/SATA driver initialization.
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/aic94xx/aic94xx_init.c#99 $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+#include <scsi/scsi_host.h>
+
+#include "aic94xx.h"
+#include "aic94xx_reg.h"
+#include "aic94xx_hwi.h"
+#include "aic94xx_seq.h"
+
+/* The format is "version.release.patchlevel" */
+#define ASD_DRIVER_VERSION "1.0.2"
+
+static int attach_HostRAID = 0;
+module_param_named(attach_HostRAID, attach_HostRAID, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(attach_HostRAID, "\n"
+	"\tEnable(1) or disable(0) attaching to HostRAID enabled host adapters.\n"
+	"\tDefault: 0");
+
+static int use_msi = 0;
+module_param_named(use_msi, use_msi, int, S_IRUGO);
+MODULE_PARM_DESC(use_msi, "\n"
+	"\tEnable(1) or disable(0) using PCI MSI.\n"
+	"\tDefault: 0");
+
+static int lldd_max_execute_num = 0;
+module_param_named(collector, lldd_max_execute_num, int, S_IRUGO);
+MODULE_PARM_DESC(collector, "\n"
+	"\tIf greater than one, tells the SAS Layer to run in Task Collector\n"
+	"\tMode.  If 1 or 0, tells the SAS Layer to run in Direct Mode.\n"
+	"\tThe aic94xx SAS LLDD supports both modes.\n"
+	"\tDefault: 0 (Direct Mode).\n");
+
+char sas_addr_str[2*SAS_ADDR_SIZE + 1] = "";
+
+static const struct scsi_host_template aic94xx_sht = {
+	.module			= THIS_MODULE,
+	/* .name is initialized */
+	.name			= "aic94xx",
+	.queuecommand		= sas_queuecommand,
+	.eh_strategy_handler	= sas_scsi_recover_host,
+	.eh_timed_out		= sas_scsi_timed_out,
+	.slave_alloc		= sas_slave_alloc,
+	.slave_configure	= sas_slave_configure,
+	.slave_destroy		= sas_slave_destroy,
+	.change_queue_depth	= sas_change_queue_depth,
+	.change_queue_type	= sas_change_queue_type,
+	.bios_param		= sas_bios_param,
+	/* .can_queue is initialized */
+	.this_id		= -1,
+	.sg_tablesize		= SG_ALL,
+	.max_sectors		= SCSI_DEFAULT_MAX_SECTORS,
+	/* .cmd_per_lun is initilized to .can_queue */
+	.use_clustering		= ENABLE_CLUSTERING,
+};
+
+static int __devinit asd_map_memio(struct asd_ha_struct *asd_ha)
+{
+	int err, i;
+	struct asd_ha_addrspace *io_handle;
+
+	asd_ha->iospace = 0;
+	for (i = 0; i < 3; i += 2) {
+		io_handle = &asd_ha->io_handle[i==0?0:1];
+		io_handle->start = pci_resource_start(asd_ha->pcidev, i);
+		io_handle->len   = pci_resource_len(asd_ha->pcidev, i);
+		io_handle->flags = pci_resource_flags(asd_ha->pcidev, i);
+		err = -ENODEV;
+		if (!io_handle->start || !io_handle->len) {
+			asd_printk("MBAR%d start or length for %s is 0.\n",
+				   i==0?0:1, pci_name(asd_ha->pcidev));
+			goto Err;
+		}
+		err = pci_request_region(asd_ha->pcidev, i, ASD_DRIVER_NAME);
+		if (err) {
+			asd_printk("couldn't reserve memory region for %s\n",
+				   pci_name(asd_ha->pcidev));
+			goto Err;
+		}
+		if (io_handle->flags & IORESOURCE_CACHEABLE)
+			io_handle->addr = ioremap(io_handle->start,
+						  io_handle->len);
+		else
+			io_handle->addr = ioremap_nocache(io_handle->start,
+							  io_handle->len);
+		if (!io_handle->addr) {
+			asd_printk("couldn't map MBAR%d of %s\n", i==0?0:1,
+				   pci_name(asd_ha->pcidev));
+			goto Err_unreq;
+		}
+	}
+
+	return 0;
+Err_unreq:
+	pci_release_region(asd_ha->pcidev, i);
+Err:
+	if (i > 0) {
+		io_handle = &asd_ha->io_handle[0];
+		iounmap(io_handle->addr);
+		pci_release_region(asd_ha->pcidev, 0);
+	}
+	return err;
+}
+
+static void __devexit asd_unmap_memio(struct asd_ha_struct *asd_ha)
+{
+	struct asd_ha_addrspace *io_handle;
+
+	io_handle = &asd_ha->io_handle[1];
+	iounmap(io_handle->addr);
+	pci_release_region(asd_ha->pcidev, 2);
+
+	io_handle = &asd_ha->io_handle[0];
+	iounmap(io_handle->addr);
+	pci_release_region(asd_ha->pcidev, 0);
+}
+
+static int __devinit asd_map_ioport(struct asd_ha_struct *asd_ha)
+{
+	int i = PCI_IOBAR_OFFSET, err;
+	struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0];
+
+	asd_ha->iospace = 1;
+	io_handle->start = pci_resource_start(asd_ha->pcidev, i);
+	io_handle->len   = pci_resource_len(asd_ha->pcidev, i);
+	io_handle->flags = pci_resource_flags(asd_ha->pcidev, i);
+	io_handle->addr  = (void __iomem *) io_handle->start;
+	if (!io_handle->start || !io_handle->len) {
+		asd_printk("couldn't get IO ports for %s\n",
+			   pci_name(asd_ha->pcidev));
+		return -ENODEV;
+	}
+	err = pci_request_region(asd_ha->pcidev, i, ASD_DRIVER_NAME);
+	if (err) {
+		asd_printk("couldn't reserve io space for %s\n",
+			   pci_name(asd_ha->pcidev));
+	}
+
+	return err;
+}
+
+static void __devexit asd_unmap_ioport(struct asd_ha_struct *asd_ha)
+{
+	pci_release_region(asd_ha->pcidev, PCI_IOBAR_OFFSET);
+}
+
+static int __devinit asd_map_ha(struct asd_ha_struct *asd_ha)
+{
+	int err;
+	u16 cmd_reg;
+
+	err = pci_read_config_word(asd_ha->pcidev, PCI_COMMAND, &cmd_reg);
+	if (err) {
+		asd_printk("couldn't read command register of %s\n",
+			   pci_name(asd_ha->pcidev));
+		goto Err;
+	}
+
+	err = -ENODEV;
+	if (cmd_reg & PCI_COMMAND_MEMORY) {
+		if ((err = asd_map_memio(asd_ha)))
+			goto Err;
+	} else if (cmd_reg & PCI_COMMAND_IO) {
+		if ((err = asd_map_ioport(asd_ha)))
+			goto Err;
+		asd_printk("%s ioport mapped -- upgrade your hardware\n",
+			   pci_name(asd_ha->pcidev));
+	} else {
+		asd_printk("no proper device access to %s\n",
+			   pci_name(asd_ha->pcidev));
+		goto Err;
+	}
+
+	return 0;
+Err:
+	return err;
+}
+
+static void __devexit asd_unmap_ha(struct asd_ha_struct *asd_ha)
+{
+	if (asd_ha->iospace)
+		asd_unmap_ioport(asd_ha);
+	else
+		asd_unmap_memio(asd_ha);
+}
+
+static const char *asd_dev_rev[30] = {
+	[0] = "A0",
+	[1] = "A1",
+	[8] = "B0",
+};
+
+static int __devinit asd_common_setup(struct asd_ha_struct *asd_ha)
+{
+	int err, i;
+
+	err = pci_read_config_byte(asd_ha->pcidev, PCI_REVISION_ID,
+				   &asd_ha->revision_id);
+	if (err) {
+		asd_printk("couldn't read REVISION ID register of %s\n",
+			   pci_name(asd_ha->pcidev));
+		goto Err;
+	}
+	err = -ENODEV;
+	if (asd_ha->revision_id < AIC9410_DEV_REV_B0) {
+		asd_printk("%s is revision %s (%X), which is not supported\n",
+			   pci_name(asd_ha->pcidev),
+			   asd_dev_rev[asd_ha->revision_id],
+			   asd_ha->revision_id);
+		goto Err;
+	}
+	/* Provide some sane default values. */
+	asd_ha->hw_prof.max_scbs = 512;
+	asd_ha->hw_prof.max_ddbs = 128;
+	asd_ha->hw_prof.num_phys = ASD_MAX_PHYS;
+	/* All phys are enabled, by default. */
+	asd_ha->hw_prof.enabled_phys = 0xFF;
+	for (i = 0; i < ASD_MAX_PHYS; i++) {
+		asd_ha->hw_prof.phy_desc[i].max_sas_lrate = PHY_LINKRATE_3;
+		asd_ha->hw_prof.phy_desc[i].min_sas_lrate = PHY_LINKRATE_1_5;
+		asd_ha->hw_prof.phy_desc[i].max_sata_lrate= PHY_LINKRATE_1_5;
+		asd_ha->hw_prof.phy_desc[i].min_sata_lrate= PHY_LINKRATE_1_5;
+	}
+
+	return 0;
+Err:
+	return err;
+}
+
+static int __devinit asd_aic9410_setup(struct asd_ha_struct *asd_ha)
+{
+	int err = asd_common_setup(asd_ha);
+
+	if (err)
+		return err;
+
+	asd_ha->hw_prof.addr_range = 8;
+	asd_ha->hw_prof.port_name_base = 0;
+	asd_ha->hw_prof.dev_name_base = 8;
+	asd_ha->hw_prof.sata_name_base = 16;
+
+	return 0;
+}
+
+static int __devinit asd_aic9405_setup(struct asd_ha_struct *asd_ha)
+{
+	int err = asd_common_setup(asd_ha);
+
+	if (err)
+		return err;
+
+	asd_ha->hw_prof.addr_range = 4;
+	asd_ha->hw_prof.port_name_base = 0;
+	asd_ha->hw_prof.dev_name_base = 4;
+	asd_ha->hw_prof.sata_name_base = 8;
+
+	return 0;
+}
+
+static ssize_t asd_show_dev_rev(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev);
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			asd_dev_rev[asd_ha->revision_id]);
+}
+static DEVICE_ATTR(revision, S_IRUGO, asd_show_dev_rev, NULL);
+
+static ssize_t asd_show_dev_bios_build(struct device *dev,
+				       struct device_attribute *attr,char *buf)
+{
+	struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev);
+	return snprintf(buf, PAGE_SIZE, "%d\n", asd_ha->hw_prof.bios.bld);
+}
+static DEVICE_ATTR(bios_build, S_IRUGO, asd_show_dev_bios_build, NULL);
+
+static ssize_t asd_show_dev_pcba_sn(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev);
+	return snprintf(buf, PAGE_SIZE, "%s\n", asd_ha->hw_prof.pcba_sn);
+}
+static DEVICE_ATTR(pcba_sn, S_IRUGO, asd_show_dev_pcba_sn, NULL);
+
+static void asd_create_dev_attrs(struct asd_ha_struct *asd_ha)
+{
+	device_create_file(&asd_ha->pcidev->dev, &dev_attr_revision);
+	device_create_file(&asd_ha->pcidev->dev, &dev_attr_bios_build);
+	device_create_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn);
+}
+
+static void asd_remove_dev_attrs(struct asd_ha_struct *asd_ha)
+{
+	device_remove_file(&asd_ha->pcidev->dev, &dev_attr_revision);
+	device_remove_file(&asd_ha->pcidev->dev, &dev_attr_bios_build);
+	device_remove_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn);
+}
+
+/* The first entry, 0, is used for dynamic ids, the rest for devices
+ * we know about.
+ */
+static struct asd_pcidev_struct {
+	const char * name;
+	int (*setup)(struct asd_ha_struct *asd_ha);
+} asd_pcidev_data[] = {
+	/* Id 0 is used for dynamic ids. */
+	{ .name  = "Adaptec AIC-94xx SAS/SATA Host Adapter",
+	  .setup = asd_aic9410_setup
+	},
+	{ .name  = "Adaptec AIC-9410W SAS/SATA Host Adapter",
+	  .setup = asd_aic9410_setup
+	},
+	{ .name  = "Adaptec AIC-9405W SAS/SATA Host Adapter",
+	  .setup = asd_aic9405_setup
+	},
+};
+
+static inline int asd_create_ha_caches(struct asd_ha_struct *asd_ha)
+{
+	asd_ha->scb_pool = dma_pool_create(ASD_DRIVER_NAME "_scb_pool",
+					   &asd_ha->pcidev->dev,
+					   sizeof(struct scb),
+					   8, 0);
+	if (!asd_ha->scb_pool) {
+		asd_printk("couldn't create scb pool\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/**
+ * asd_free_edbs -- free empty data buffers
+ * asd_ha: pointer to host adapter structure
+ */
+static inline void asd_free_edbs(struct asd_ha_struct *asd_ha)
+{
+	struct asd_seq_data *seq = &asd_ha->seq;
+	int i;
+
+	for (i = 0; i < seq->num_edbs; i++)
+		asd_free_coherent(asd_ha, seq->edb_arr[i]);
+	kfree(seq->edb_arr);
+	seq->edb_arr = NULL;
+}
+
+static inline void asd_free_escbs(struct asd_ha_struct *asd_ha)
+{
+	struct asd_seq_data *seq = &asd_ha->seq;
+	int i;
+
+	for (i = 0; i < seq->num_escbs; i++) {
+		if (!list_empty(&seq->escb_arr[i]->list))
+			list_del_init(&seq->escb_arr[i]->list);
+
+		asd_ascb_free(seq->escb_arr[i]);
+	}
+	kfree(seq->escb_arr);
+	seq->escb_arr = NULL;
+}
+
+static inline void asd_destroy_ha_caches(struct asd_ha_struct *asd_ha)
+{
+	int i;
+
+	if (asd_ha->hw_prof.ddb_ext)
+		asd_free_coherent(asd_ha, asd_ha->hw_prof.ddb_ext);
+	if (asd_ha->hw_prof.scb_ext)
+		asd_free_coherent(asd_ha, asd_ha->hw_prof.scb_ext);
+
+	if (asd_ha->hw_prof.ddb_bitmap)
+		kfree(asd_ha->hw_prof.ddb_bitmap);
+	asd_ha->hw_prof.ddb_bitmap = NULL;
+
+	for (i = 0; i < ASD_MAX_PHYS; i++) {
+		struct asd_phy *phy = &asd_ha->phys[i];
+
+		asd_free_coherent(asd_ha, phy->id_frm_tok);
+	}
+	if (asd_ha->seq.escb_arr)
+		asd_free_escbs(asd_ha);
+	if (asd_ha->seq.edb_arr)
+		asd_free_edbs(asd_ha);
+	if (asd_ha->hw_prof.ue.area) {
+		kfree(asd_ha->hw_prof.ue.area);
+		asd_ha->hw_prof.ue.area = NULL;
+	}
+	if (asd_ha->seq.tc_index_array) {
+		kfree(asd_ha->seq.tc_index_array);
+		kfree(asd_ha->seq.tc_index_bitmap);
+		asd_ha->seq.tc_index_array = NULL;
+		asd_ha->seq.tc_index_bitmap = NULL;
+	}
+	if (asd_ha->seq.actual_dl) {
+			asd_free_coherent(asd_ha, asd_ha->seq.actual_dl);
+			asd_ha->seq.actual_dl = NULL;
+			asd_ha->seq.dl = NULL;
+	}
+	if (asd_ha->seq.next_scb.vaddr) {
+		dma_pool_free(asd_ha->scb_pool, asd_ha->seq.next_scb.vaddr,
+			      asd_ha->seq.next_scb.dma_handle);
+		asd_ha->seq.next_scb.vaddr = NULL;
+	}
+	dma_pool_destroy(asd_ha->scb_pool);
+	asd_ha->scb_pool = NULL;
+}
+
+kmem_cache_t *asd_dma_token_cache;
+kmem_cache_t *asd_ascb_cache;
+
+static int asd_create_global_caches(void)
+{
+	if (!asd_dma_token_cache) {
+		asd_dma_token_cache
+			= kmem_cache_create(ASD_DRIVER_NAME "_dma_token",
+					    sizeof(struct asd_dma_tok),
+					    0,
+					    SLAB_HWCACHE_ALIGN,
+					    NULL, NULL);
+		if (!asd_dma_token_cache) {
+			asd_printk("couldn't create dma token cache\n");
+			return -ENOMEM;
+		}
+	}
+
+	if (!asd_ascb_cache) {
+		asd_ascb_cache = kmem_cache_create(ASD_DRIVER_NAME "_ascb",
+						   sizeof(struct asd_ascb),
+						   0,
+						   SLAB_HWCACHE_ALIGN,
+						   NULL, NULL);
+		if (!asd_ascb_cache) {
+			asd_printk("couldn't create ascb cache\n");
+			goto Err;
+		}
+	}
+
+	return 0;
+Err:
+	kmem_cache_destroy(asd_dma_token_cache);
+	asd_dma_token_cache = NULL;
+	return -ENOMEM;
+}
+
+static void asd_destroy_global_caches(void)
+{
+	if (asd_dma_token_cache)
+		kmem_cache_destroy(asd_dma_token_cache);
+	asd_dma_token_cache = NULL;
+
+	if (asd_ascb_cache)
+		kmem_cache_destroy(asd_ascb_cache);
+	asd_ascb_cache = NULL;
+}
+
+static int asd_register_sas_ha(struct asd_ha_struct *asd_ha)
+{
+	int i;
+	static struct sas_phy   *sas_phys[ASD_MAX_PHYS];
+	static struct sas_port  *sas_ports[ASD_MAX_PHYS];
+
+	asd_ha->sas_ha.sas_ha_name = (char *) asd_ha->name;
+	asd_ha->sas_ha.lldd_module = THIS_MODULE;
+	asd_ha->sas_ha.sas_addr = &asd_ha->hw_prof.sas_addr[0];
+
+	for (i = 0; i < ASD_MAX_PHYS; i++) {
+		sas_phys[i] = &asd_ha->phys[i].sas_phy;
+		sas_ports[i] = &asd_ha->ports[i];
+	}
+
+	asd_ha->sas_ha.sas_phy = sas_phys;
+	asd_ha->sas_ha.sas_port= sas_ports;
+	asd_ha->sas_ha.num_phys= ASD_MAX_PHYS;
+
+	asd_ha->sas_ha.lldd_port_formed = asd_update_port_links;
+
+	asd_ha->sas_ha.lldd_dev_found = asd_dev_found;
+	asd_ha->sas_ha.lldd_dev_gone = asd_dev_gone;
+
+	asd_ha->sas_ha.lldd_max_execute_num = lldd_max_execute_num;
+	asd_ha->sas_ha.lldd_queue_size = asd_ha->seq.can_queue;
+	asd_ha->sas_ha.lldd_execute_task = asd_execute_task;
+
+	asd_ha->sas_ha.lldd_abort_task     = asd_abort_task;
+	asd_ha->sas_ha.lldd_abort_task_set = asd_abort_task_set;
+	asd_ha->sas_ha.lldd_clear_aca      = asd_clear_aca;
+	asd_ha->sas_ha.lldd_clear_task_set = asd_clear_task_set;
+	asd_ha->sas_ha.lldd_I_T_nexus_reset= NULL;
+	asd_ha->sas_ha.lldd_lu_reset       = asd_lu_reset;
+	asd_ha->sas_ha.lldd_query_task     = asd_query_task;
+
+	asd_ha->sas_ha.lldd_clear_nexus_port = asd_clear_nexus_port;
+	asd_ha->sas_ha.lldd_clear_nexus_ha = asd_clear_nexus_ha;
+
+	asd_ha->sas_ha.lldd_control_phy = asd_control_phy;
+
+	return sas_register_ha(&asd_ha->sas_ha, &aic94xx_sht);
+}
+
+static int asd_unregister_sas_ha(struct asd_ha_struct *asd_ha)
+{
+	return sas_unregister_ha(&asd_ha->sas_ha);
+}
+
+static int __devinit asd_pci_probe(struct pci_dev *dev,
+				   const struct pci_device_id *id)
+{
+	struct asd_pcidev_struct *asd_dev;
+	unsigned asd_id = (unsigned) id->driver_data;
+	struct asd_ha_struct *asd_ha;
+	int err;
+
+	if (dev->class == (PCI_CLASS_STORAGE_RAID << 8) && !attach_HostRAID) {
+		asd_printk("will not attach to HostRAID enabled device %s, "
+			   "unless attach_HostRAID parameter is set\n",
+			   pci_name(dev));
+		return -ENODEV;
+	}
+
+	if (asd_id >= ARRAY_SIZE(asd_pcidev_data)) {
+		asd_printk("wrong driver_data in PCI table\n");
+		return -ENODEV;
+	}
+
+	if ((err = pci_enable_device(dev))) {
+		asd_printk("couldn't enable device %s\n", pci_name(dev));
+		return err;
+	}
+
+	pci_set_master(dev);
+
+	asd_dev = &asd_pcidev_data[asd_id];
+
+	err = -ENOMEM;
+	asd_ha = kzalloc(sizeof(*asd_ha), GFP_KERNEL);
+	if (!asd_ha) {
+		asd_printk("out of memory\n");
+		goto Err;
+	}
+	asd_ha->pcidev = dev;
+	asd_ha->sas_ha.pcidev = asd_ha->pcidev;
+	asd_ha->sas_ha.lldd_ha = asd_ha;
+
+	asd_ha->name = asd_dev->name;
+	asd_printk("found %s, device %s\n", asd_ha->name, pci_name(dev));
+	err = asd_dev->setup(asd_ha);
+	if (err)
+		goto Err_free;
+
+	err = -ENODEV;
+	if (!pci_set_dma_mask(dev, DMA_64BIT_MASK)
+	    && !pci_set_consistent_dma_mask(dev, DMA_64BIT_MASK))
+		;
+	else if (!pci_set_dma_mask(dev, DMA_32BIT_MASK)
+		 && !pci_set_consistent_dma_mask(dev, DMA_32BIT_MASK))
+		;
+	else {
+		asd_printk("no suitable DMA mask for %s\n", pci_name(dev));
+		goto Err_free;
+	}
+
+	pci_set_drvdata(dev, asd_ha);
+
+	err = asd_map_ha(asd_ha);
+	if (err)
+		goto Err_free;
+
+	err = asd_create_ha_caches(asd_ha);
+        if (err)
+		goto Err_unmap;
+
+	err = asd_init_hw(asd_ha);
+	if (err)
+		goto Err_free_cache;
+
+	asd_printk("device %s: SAS addr %llx, PCBA SN %s, %d phys, %d enabled "
+		   "phys, flash %s, BIOS %s%d\n",
+		   pci_name(dev), SAS_ADDR(asd_ha->hw_prof.sas_addr),
+		   asd_ha->hw_prof.pcba_sn, asd_ha->hw_prof.max_phys,
+		   asd_ha->hw_prof.num_phys,
+		   asd_ha->hw_prof.flash.present ? "present" : "not present",
+		   asd_ha->hw_prof.bios.present ? "build " : "not present",
+		   asd_ha->hw_prof.bios.bld);
+
+	if (use_msi)
+		pci_enable_msi(asd_ha->pcidev);
+
+	err = request_irq(asd_ha->pcidev->irq, asd_hw_isr, SA_SHIRQ,
+			  ASD_DRIVER_NAME, asd_ha);
+	if (err) {
+		asd_printk("couldn't get irq %d for %s\n",
+			   asd_ha->pcidev->irq, pci_name(asd_ha->pcidev));
+		goto Err_irq;
+	}
+	asd_enable_ints(asd_ha);
+
+	err = asd_init_post_escbs(asd_ha);
+	if (err) {
+		asd_printk("couldn't post escbs for %s\n",
+			   pci_name(asd_ha->pcidev));
+		goto Err_escbs;
+	}
+	ASD_DPRINTK("escbs posted\n");
+
+	asd_create_dev_attrs(asd_ha);
+
+	err = asd_register_sas_ha(asd_ha);
+	if (err)
+		goto Err_reg_sas;
+
+	err = asd_enable_phys(asd_ha, asd_ha->hw_prof.enabled_phys);
+	if (err) {
+		asd_printk("coudln't enable phys, err:%d\n", err);
+		goto Err_en_phys;
+	}
+	ASD_DPRINTK("enabled phys\n");
+
+	return 0;
+Err_en_phys:
+	asd_unregister_sas_ha(asd_ha);
+Err_reg_sas:
+	asd_remove_dev_attrs(asd_ha);
+Err_escbs:
+	asd_disable_ints(asd_ha);
+	free_irq(dev->irq, asd_ha);
+Err_irq:
+	if (use_msi)
+		pci_disable_msi(dev);
+	asd_chip_hardrst(asd_ha);
+Err_free_cache:
+	asd_destroy_ha_caches(asd_ha);
+Err_unmap:
+	asd_unmap_ha(asd_ha);
+Err_free:
+	kfree(asd_ha);
+Err:
+	pci_disable_device(dev);
+	return err;
+}
+
+static void asd_free_queues(struct asd_ha_struct *asd_ha)
+{
+	unsigned long flags;
+	LIST_HEAD(pending);
+	struct list_head *n, *pos;
+
+	spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags);
+	asd_ha->seq.pending = 0;
+	list_splice_init(&asd_ha->seq.pend_q, &pending);
+	spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);
+
+	if (!list_empty(&pending))
+		ASD_DPRINTK("Uh-oh! Pending is not empty!\n");
+
+	list_for_each_safe(pos, n, &pending) {
+		struct asd_ascb *ascb = list_entry(pos, struct asd_ascb, list);
+		list_del_init(pos);
+		ASD_DPRINTK("freeing from pending\n");
+		asd_ascb_free(ascb);
+	}
+}
+
+static void asd_turn_off_leds(struct asd_ha_struct *asd_ha)
+{
+	u8 phy_mask = asd_ha->hw_prof.enabled_phys;
+	u8 i;
+
+	for_each_phy(phy_mask, phy_mask, i) {
+		asd_turn_led(asd_ha, i, 0);
+		asd_control_led(asd_ha, i, 0);
+	}
+}
+
+static void __devexit asd_pci_remove(struct pci_dev *dev)
+{
+	struct asd_ha_struct *asd_ha = pci_get_drvdata(dev);
+
+	if (!asd_ha)
+		return;
+
+	asd_unregister_sas_ha(asd_ha);
+
+	asd_disable_ints(asd_ha);
+
+	asd_remove_dev_attrs(asd_ha);
+
+	/* XXX more here as needed */
+
+	free_irq(dev->irq, asd_ha);
+	if (use_msi)
+		pci_disable_msi(asd_ha->pcidev);
+	asd_turn_off_leds(asd_ha);
+	asd_chip_hardrst(asd_ha);
+	asd_free_queues(asd_ha);
+	asd_destroy_ha_caches(asd_ha);
+	asd_unmap_ha(asd_ha);
+	kfree(asd_ha);
+	pci_disable_device(dev);
+	return;
+}
+
+static ssize_t asd_version_show(struct device_driver *driver, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", ASD_DRIVER_VERSION);
+}
+static DRIVER_ATTR(version, S_IRUGO, asd_version_show, NULL);
+
+static void asd_create_driver_attrs(struct device_driver *driver)
+{
+	driver_create_file(driver, &driver_attr_version);
+}
+
+static void asd_remove_driver_attrs(struct device_driver *driver)
+{
+	driver_remove_file(driver, &driver_attr_version);
+}
+
+static const struct pci_device_id aic94xx_pci_table[] __devinitdata = {
+	{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR10),
+	 0, 0, 1},
+	{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR12),
+	 0, 0, 1},
+	{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR1E),
+	 0, 0, 1},
+	{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR30),
+	 0, 0, 2},
+	{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR32),
+	 0, 0, 2},
+	{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR3E),
+	 0, 0, 2},
+	{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR3F),
+	 0, 0, 2},
+	{}
+};
+
+MODULE_DEVICE_TABLE(pci, aic94xx_pci_table);
+
+static struct pci_driver aic94xx_pci_driver = {
+	.name		= ASD_DRIVER_NAME,
+	.id_table	= aic94xx_pci_table,
+	.probe		= asd_pci_probe,
+	.remove		= __devexit_p(asd_pci_remove),
+};
+
+static int __init aic94xx_init(void)
+{
+	int err;
+
+
+	asd_printk("%s version %s loaded\n", ASD_DRIVER_DESCRIPTION,
+		   ASD_DRIVER_VERSION);
+
+	err = asd_create_global_caches();
+	if (err)
+		return err;
+
+	err = pci_register_driver(&aic94xx_pci_driver);
+	if (!err)
+		asd_create_driver_attrs(&aic94xx_pci_driver.driver);
+
+	return err;
+}
+
+static void __exit aic94xx_exit(void)
+{
+	asd_remove_driver_attrs(&aic94xx_pci_driver.driver);
+	pci_unregister_driver(&aic94xx_pci_driver);
+	asd_destroy_global_caches();
+	asd_printk("%s version %s unloaded\n", ASD_DRIVER_DESCRIPTION,
+		   ASD_DRIVER_VERSION);
+}
+
+module_init(aic94xx_init);
+module_exit(aic94xx_exit);
+
+MODULE_AUTHOR("Luben Tuikov <luben_tuikov@adaptec.com>");
+MODULE_DESCRIPTION(ASD_DRIVER_DESCRIPTION);
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(ASD_DRIVER_VERSION);
diff -urN oldtree/drivers/scsi/aic94xx/aic94xx_reg.c newtree/drivers/scsi/aic94xx/aic94xx_reg.c
--- oldtree/drivers/scsi/aic94xx/aic94xx_reg.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/aic94xx_reg.c	2006-02-13 19:20:00.579424216 -0500
@@ -0,0 +1,333 @@
+/*
+ * Aic94xx SAS/SATA driver register access.
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/aic94xx/aic94xx_reg.c#14 $
+ */
+
+#include <linux/pci.h>
+#include "aic94xx_reg.h"
+#include "aic94xx.h"
+
+/* Writing to device address space.
+ * Offset comes before value to remind that the operation of
+ * this function is *offs = val.
+ */
+static inline void asd_write_byte(struct asd_ha_struct *asd_ha,
+				  unsigned long offs, u8 val)
+{
+	if (unlikely(asd_ha->iospace))
+		outb(val,
+		     (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
+	else
+		writeb(val, asd_ha->io_handle[0].addr + offs);
+	wmb();
+}
+
+static inline void asd_write_word(struct asd_ha_struct *asd_ha,
+				  unsigned long offs, u16 val)
+{
+	if (unlikely(asd_ha->iospace))
+		outw(val,
+		     (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
+	else
+		writew(val, asd_ha->io_handle[0].addr + offs);
+	wmb();
+}
+
+static inline void asd_write_dword(struct asd_ha_struct *asd_ha,
+				   unsigned long offs, u32 val)
+{
+	if (unlikely(asd_ha->iospace))
+		outl(val,
+		     (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
+	else
+		writel(val, asd_ha->io_handle[0].addr + offs);
+	wmb();
+}
+
+/* Reading from device address space.
+ */
+static inline u8 asd_read_byte(struct asd_ha_struct *asd_ha,
+			       unsigned long offs)
+{
+	u8 val;
+	if (unlikely(asd_ha->iospace))
+		val = inb((unsigned long) asd_ha->io_handle[0].addr
+			  + (offs & 0xFF));
+	else
+		val = readb(asd_ha->io_handle[0].addr + offs);
+	rmb();
+	return val;
+}
+
+static inline u16 asd_read_word(struct asd_ha_struct *asd_ha,
+				unsigned long offs)
+{
+	u16 val;
+	if (unlikely(asd_ha->iospace))
+		val = inw((unsigned long)asd_ha->io_handle[0].addr
+			  + (offs & 0xFF));
+	else
+		val = readw(asd_ha->io_handle[0].addr + offs);
+	rmb();
+	return val;
+}
+
+static inline u32 asd_read_dword(struct asd_ha_struct *asd_ha,
+				 unsigned long offs)
+{
+	u32 val;
+	if (unlikely(asd_ha->iospace))
+		val = inl((unsigned long) asd_ha->io_handle[0].addr
+			  + (offs & 0xFF));
+	else
+		val = readl(asd_ha->io_handle[0].addr + offs);
+	rmb();
+	return val;
+}
+
+static inline u32 asd_mem_offs_swa(void)
+{
+	return 0;
+}
+
+static inline u32 asd_mem_offs_swc(void)
+{
+	return asd_mem_offs_swa() + MBAR0_SWA_SIZE;
+}
+
+static inline u32 asd_mem_offs_swb(void)
+{
+	return asd_mem_offs_swc() + MBAR0_SWC_SIZE + 0x20;
+}
+
+/* We know that the register wanted is in the range
+ * of the sliding window.
+ */
+#define ASD_READ_SW(ww, type, ord)                                     \
+static inline type asd_read_##ww##_##ord (struct asd_ha_struct *asd_ha,\
+					  u32 reg)                     \
+{                                                                      \
+	struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0];    \
+	u32 map_offs=(reg - io_handle-> ww##_base )+asd_mem_offs_##ww ();\
+	return asd_read_##ord (asd_ha, (unsigned long) map_offs);      \
+}
+
+#define ASD_WRITE_SW(ww, type, ord)                                    \
+static inline void asd_write_##ww##_##ord (struct asd_ha_struct *asd_ha,\
+				  u32 reg, type val)                   \
+{                                                                      \
+	struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0];    \
+	u32 map_offs=(reg - io_handle-> ww##_base )+asd_mem_offs_##ww ();\
+	asd_write_##ord (asd_ha, (unsigned long) map_offs, val);       \
+}
+
+ASD_READ_SW(swa, u8,  byte);
+ASD_READ_SW(swa, u16, word);
+ASD_READ_SW(swa, u32, dword);
+
+ASD_READ_SW(swb, u8,  byte);
+ASD_READ_SW(swb, u16, word);
+ASD_READ_SW(swb, u32, dword);
+
+ASD_READ_SW(swc, u8,  byte);
+ASD_READ_SW(swc, u16, word);
+ASD_READ_SW(swc, u32, dword);
+
+ASD_WRITE_SW(swa, u8,  byte);
+ASD_WRITE_SW(swa, u16, word);
+ASD_WRITE_SW(swa, u32, dword);
+
+ASD_WRITE_SW(swb, u8,  byte);
+ASD_WRITE_SW(swb, u16, word);
+ASD_WRITE_SW(swb, u32, dword);
+
+ASD_WRITE_SW(swc, u8,  byte);
+ASD_WRITE_SW(swc, u16, word);
+ASD_WRITE_SW(swc, u32, dword);
+
+/*
+ * A word about sliding windows:
+ * MBAR0 is divided into sliding windows A, C and B, in that order.
+ * SWA starts at offset 0 of MBAR0, up to 0x57, with size 0x58 bytes.
+ * SWC starts at offset 0x58 of MBAR0, up to 0x60, with size 0x8 bytes.
+ * From 0x60 to 0x7F, we have a copy of PCI config space 0x60-0x7F.
+ * SWB starts at offset 0x80 of MBAR0 and extends to the end of MBAR0.
+ * See asd_init_sw() in aic94xx_hwi.c
+ *
+ * We map the most common registers we'd access of the internal 4GB
+ * host adapter memory space.  If a register/internal memory location
+ * is wanted which is not mapped, we slide SWB, by paging it,
+ * see asd_move_swb() in aic94xx_reg.c.
+ */
+
+/**
+ * asd_move_swb -- move sliding window B
+ * @asd_ha: pointer to host adapter structure
+ * @reg: register desired to be within range of the new window
+ */
+static inline void asd_move_swb(struct asd_ha_struct *asd_ha, u32 reg)
+{
+	u32 base = reg & ~(MBAR0_SWB_SIZE-1);
+	pci_write_config_dword(asd_ha->pcidev, PCI_CONF_MBAR0_SWB, base);
+	asd_ha->io_handle[0].swb_base = base;
+}
+
+static void __asd_write_reg_byte(struct asd_ha_struct *asd_ha, u32 reg, u8 val)
+{
+	struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0];
+	BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);
+	if (io_handle->swa_base <= reg
+	    && reg < io_handle->swa_base + MBAR0_SWA_SIZE)
+		asd_write_swa_byte (asd_ha, reg,val);
+	else if (io_handle->swb_base <= reg
+		 && reg < io_handle->swb_base + MBAR0_SWB_SIZE)
+		asd_write_swb_byte (asd_ha, reg, val);
+	else if (io_handle->swc_base <= reg
+		 && reg < io_handle->swc_base + MBAR0_SWC_SIZE)
+		asd_write_swc_byte (asd_ha, reg, val);
+	else {
+		/* Ok, we have to move SWB */
+		asd_move_swb(asd_ha, reg);
+		asd_write_swb_byte (asd_ha, reg, val);
+	}
+}
+
+#define ASD_WRITE_REG(type, ord)                                  \
+void asd_write_reg_##ord (struct asd_ha_struct *asd_ha, u32 reg, type val)\
+{                                                                 \
+	struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; \
+	unsigned long flags;                                      \
+	BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);         \
+	spin_lock_irqsave(&asd_ha->iolock, flags);                \
+	if (io_handle->swa_base <= reg                            \
+	    && reg < io_handle->swa_base + MBAR0_SWA_SIZE)        \
+		asd_write_swa_##ord (asd_ha, reg,val);            \
+	else if (io_handle->swb_base <= reg                       \
+		 && reg < io_handle->swb_base + MBAR0_SWB_SIZE)   \
+		asd_write_swb_##ord (asd_ha, reg, val);           \
+	else if (io_handle->swc_base <= reg                       \
+		 && reg < io_handle->swc_base + MBAR0_SWC_SIZE)   \
+		asd_write_swc_##ord (asd_ha, reg, val);           \
+	else {                                                    \
+		/* Ok, we have to move SWB */                     \
+		asd_move_swb(asd_ha, reg);                        \
+		asd_write_swb_##ord (asd_ha, reg, val);           \
+	}                                                         \
+	spin_unlock_irqrestore(&asd_ha->iolock, flags);           \
+}
+
+ASD_WRITE_REG(u8, byte);
+ASD_WRITE_REG(u16,word);
+ASD_WRITE_REG(u32,dword);
+
+static u8 __asd_read_reg_byte(struct asd_ha_struct *asd_ha, u32 reg)
+{
+	struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0];
+	u8 val;
+	BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);
+	if (io_handle->swa_base <= reg
+	    && reg < io_handle->swa_base + MBAR0_SWA_SIZE)
+		val = asd_read_swa_byte (asd_ha, reg);
+	else if (io_handle->swb_base <= reg
+		 && reg < io_handle->swb_base + MBAR0_SWB_SIZE)
+		val = asd_read_swb_byte (asd_ha, reg);
+	else if (io_handle->swc_base <= reg
+		 && reg < io_handle->swc_base + MBAR0_SWC_SIZE)
+		val = asd_read_swc_byte (asd_ha, reg);
+	else {
+		/* Ok, we have to move SWB */
+		asd_move_swb(asd_ha, reg);
+		val = asd_read_swb_byte (asd_ha, reg);
+	}
+	return val;
+}
+
+#define ASD_READ_REG(type, ord)                                   \
+type asd_read_reg_##ord (struct asd_ha_struct *asd_ha, u32 reg)   \
+{                                                                 \
+	struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; \
+	type val;                                                 \
+	unsigned long flags;                                      \
+	BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);         \
+	spin_lock_irqsave(&asd_ha->iolock, flags);                \
+	if (io_handle->swa_base <= reg                            \
+	    && reg < io_handle->swa_base + MBAR0_SWA_SIZE)        \
+		val = asd_read_swa_##ord (asd_ha, reg);           \
+	else if (io_handle->swb_base <= reg                       \
+		 && reg < io_handle->swb_base + MBAR0_SWB_SIZE)   \
+		val = asd_read_swb_##ord (asd_ha, reg);           \
+	else if (io_handle->swc_base <= reg                       \
+		 && reg < io_handle->swc_base + MBAR0_SWC_SIZE)   \
+		val = asd_read_swc_##ord (asd_ha, reg);           \
+	else {                                                    \
+		/* Ok, we have to move SWB */                     \
+		asd_move_swb(asd_ha, reg);                        \
+		val = asd_read_swb_##ord (asd_ha, reg);           \
+	}                                                         \
+	spin_unlock_irqrestore(&asd_ha->iolock, flags);           \
+	return val;                                               \
+}
+
+ASD_READ_REG(u8, byte);
+ASD_READ_REG(u16,word);
+ASD_READ_REG(u32,dword);
+
+/**
+ * asd_read_reg_string -- read a string of bytes from io space memory
+ * @asd_ha: pointer to host adapter structure
+ * @dst: pointer to a destination buffer where data will be written to
+ * @offs: start offset (register) to read from
+ * @count: number of bytes to read
+ */
+void asd_read_reg_string(struct asd_ha_struct *asd_ha, void *dst,
+			 u32 offs, int count)
+{
+	u8 *p = dst;
+	unsigned long flags;
+
+	spin_lock_irqsave(&asd_ha->iolock, flags);
+	for ( ; count > 0; count--, offs++, p++)
+		*p = __asd_read_reg_byte(asd_ha, offs);
+	spin_unlock_irqrestore(&asd_ha->iolock, flags);
+}
+
+/**
+ * asd_write_reg_string -- write a string of bytes to io space memory
+ * @asd_ha: pointer to host adapter structure
+ * @src: pointer to source buffer where data will be read from
+ * @offs: start offset (register) to write to
+ * @count: number of bytes to write
+ */
+void asd_write_reg_string(struct asd_ha_struct *asd_ha, void *src,
+			  u32 offs, int count)
+{
+	u8 *p = src;
+	unsigned long flags;
+
+	spin_lock_irqsave(&asd_ha->iolock, flags);
+	for ( ; count > 0; count--, offs++, p++)
+		__asd_write_reg_byte(asd_ha, offs, *p);
+	spin_unlock_irqrestore(&asd_ha->iolock, flags);
+}
diff -urN oldtree/drivers/scsi/aic94xx/aic94xx_reg.h newtree/drivers/scsi/aic94xx/aic94xx_reg.h
--- oldtree/drivers/scsi/aic94xx/aic94xx_reg.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/aic94xx_reg.h	2006-02-13 19:20:00.580424064 -0500
@@ -0,0 +1,290 @@
+/*
+ * Aic94xx SAS/SATA driver hardware registers definitions.
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/aic94xx/aic94xx_reg.h#19 $
+ */
+
+#ifndef _AIC94XX_REG_H_
+#define _AIC94XX_REG_H_
+
+#include <asm/io.h>
+#include "aic94xx_hwi.h"
+
+/* Values */
+#define AIC9410_DEV_REV_B0            0x8
+
+/* MBAR0, SWA, SWB, SWC, internal memory space addresses */
+#define REG_BASE_ADDR                 0xB8000000
+#define REG_BASE_ADDR_CSEQCIO         0xB8002000
+#define REG_BASE_ADDR_EXSI            0xB8042800
+
+#define MBAR0_SWA_SIZE                0x58
+extern  u32    MBAR0_SWB_SIZE;
+#define MBAR0_SWC_SIZE                0x8
+
+/* MBAR1, points to On Chip Memory */
+#define OCM_BASE_ADDR                 0xA0000000
+#define OCM_MAX_SIZE                  0x20000
+
+/* Smallest address possible to reference */
+#define ALL_BASE_ADDR                 OCM_BASE_ADDR
+
+/* PCI configuration space registers */
+#define PCI_IOBAR_OFFSET              4
+
+#define PCI_CONF_MBAR1                0x6C
+#define PCI_CONF_MBAR0_SWA            0x70
+#define PCI_CONF_MBAR0_SWB            0x74
+#define PCI_CONF_MBAR0_SWC            0x78
+#define PCI_CONF_MBAR_KEY             0x7C
+#define PCI_CONF_FLSH_BAR             0xB8
+
+#include "aic94xx_reg_def.h"
+
+u8  asd_read_reg_byte(struct asd_ha_struct *asd_ha, u32 reg);
+u16 asd_read_reg_word(struct asd_ha_struct *asd_ha, u32 reg);
+u32 asd_read_reg_dword(struct asd_ha_struct *asd_ha, u32 reg);
+
+void asd_write_reg_byte(struct asd_ha_struct *asd_ha, u32 reg, u8 val);
+void asd_write_reg_word(struct asd_ha_struct *asd_ha, u32 reg, u16 val);
+void asd_write_reg_dword(struct asd_ha_struct *asd_ha, u32 reg, u32 val);
+
+void asd_read_reg_string(struct asd_ha_struct *asd_ha, void *dst,
+			 u32 offs, int count);
+void asd_write_reg_string(struct asd_ha_struct *asd_ha, void *src,
+			  u32 offs, int count);
+
+#define ASD_READ_OCM(type, ord, S)                                    \
+static inline type asd_read_ocm_##ord (struct asd_ha_struct *asd_ha,  \
+					 u32 offs)                    \
+{                                                                     \
+	struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[1];   \
+	type val = read##S (io_handle->addr + (unsigned long) offs);  \
+	rmb();                                                        \
+	return val;                                                   \
+}
+
+ASD_READ_OCM(u8, byte, b);
+ASD_READ_OCM(u16,word, w);
+ASD_READ_OCM(u32,dword,l);
+
+#define ASD_DDBSITE_READ(type, ord)                                        \
+static inline type asd_ddbsite_read_##ord (struct asd_ha_struct *asd_ha,   \
+					   u16 ddb_site_no,                \
+					   u16 offs)                       \
+{                                                                          \
+	asd_write_reg_word(asd_ha, ALTCIOADR, MnDDB_SITE + offs);          \
+	asd_write_reg_word(asd_ha, ADDBPTR, ddb_site_no);                  \
+	return asd_read_reg_##ord (asd_ha, CTXACCESS);                     \
+}
+
+ASD_DDBSITE_READ(u32, dword);
+ASD_DDBSITE_READ(u16, word);
+
+static inline u8 asd_ddbsite_read_byte(struct asd_ha_struct *asd_ha,
+				       u16 ddb_site_no,
+				       u16 offs)
+{
+	if (offs & 1)
+		return asd_ddbsite_read_word(asd_ha, ddb_site_no,
+					     offs & ~1) >> 8;
+	else
+		return asd_ddbsite_read_word(asd_ha, ddb_site_no,
+					     offs) & 0xFF;
+}
+
+
+#define ASD_DDBSITE_WRITE(type, ord)                                       \
+static inline void asd_ddbsite_write_##ord (struct asd_ha_struct *asd_ha,  \
+					u16 ddb_site_no,                   \
+					u16 offs, type val)                \
+{                                                                          \
+	asd_write_reg_word(asd_ha, ALTCIOADR, MnDDB_SITE + offs);          \
+	asd_write_reg_word(asd_ha, ADDBPTR, ddb_site_no);                  \
+	asd_write_reg_##ord (asd_ha, CTXACCESS, val);                      \
+}
+
+ASD_DDBSITE_WRITE(u32, dword);
+ASD_DDBSITE_WRITE(u16, word);
+
+static inline void asd_ddbsite_write_byte(struct asd_ha_struct *asd_ha,
+					  u16 ddb_site_no,
+					  u16 offs, u8 val)
+{
+	u16 base = offs & ~1;
+	u16 rval = asd_ddbsite_read_word(asd_ha, ddb_site_no, base);
+	if (offs & 1)
+		rval = (val << 8) | (rval & 0xFF);
+	else
+		rval = (rval & 0xFF00) | val;
+	asd_ddbsite_write_word(asd_ha, ddb_site_no, base, rval);
+}
+
+
+#define ASD_SCBSITE_READ(type, ord)                                        \
+static inline type asd_scbsite_read_##ord (struct asd_ha_struct *asd_ha,   \
+					   u16 scb_site_no,                \
+					   u16 offs)                       \
+{                                                                          \
+	asd_write_reg_word(asd_ha, ALTCIOADR, MnSCB_SITE + offs);          \
+	asd_write_reg_word(asd_ha, ASCBPTR, scb_site_no);                  \
+	return asd_read_reg_##ord (asd_ha, CTXACCESS);                     \
+}
+
+ASD_SCBSITE_READ(u32, dword);
+ASD_SCBSITE_READ(u16, word);
+
+static inline u8 asd_scbsite_read_byte(struct asd_ha_struct *asd_ha,
+				       u16 scb_site_no,
+				       u16 offs)
+{
+	if (offs & 1)
+		return asd_scbsite_read_word(asd_ha, scb_site_no,
+					     offs & ~1) >> 8;
+	else
+		return asd_scbsite_read_word(asd_ha, scb_site_no,
+					     offs) & 0xFF;
+}
+
+
+#define ASD_SCBSITE_WRITE(type, ord)                                       \
+static inline void asd_scbsite_write_##ord (struct asd_ha_struct *asd_ha,  \
+					u16 scb_site_no,                   \
+					u16 offs, type val)                \
+{                                                                          \
+	asd_write_reg_word(asd_ha, ALTCIOADR, MnSCB_SITE + offs);          \
+	asd_write_reg_word(asd_ha, ASCBPTR, scb_site_no);                  \
+	asd_write_reg_##ord (asd_ha, CTXACCESS, val);                      \
+}
+
+ASD_SCBSITE_WRITE(u32, dword);
+ASD_SCBSITE_WRITE(u16, word);
+
+static inline void asd_scbsite_write_byte(struct asd_ha_struct *asd_ha,
+					  u16 scb_site_no,
+					  u16 offs, u8 val)
+{
+	u16 base = offs & ~1;
+	u16 rval = asd_scbsite_read_word(asd_ha, scb_site_no, base);
+	if (offs & 1)
+		rval = (val << 8) | (rval & 0xFF);
+	else
+		rval = (rval & 0xFF00) | val;
+	asd_scbsite_write_word(asd_ha, scb_site_no, base, rval);
+}
+
+/**
+ * asd_ddbsite_update_word -- atomically update a word in a ddb site
+ * @asd_ha: pointer to host adapter structure
+ * @ddb_site_no: the DDB site number
+ * @offs: the offset into the DDB
+ * @oldval: old value found in that offset
+ * @newval: the new value to replace it
+ *
+ * This function is used when the sequencers are running and we need to
+ * update a DDB site atomically without expensive pausing and upausing
+ * of the sequencers and accessing the DDB site through the CIO bus.
+ *
+ * Return 0 on success; -EFAULT on parity error; -EAGAIN if the old value
+ * is different than the current value at that offset.
+ */
+static inline int asd_ddbsite_update_word(struct asd_ha_struct *asd_ha,
+					  u16 ddb_site_no, u16 offs,
+					  u16 oldval, u16 newval)
+{
+	u8  done;
+	u16 oval = asd_ddbsite_read_word(asd_ha, ddb_site_no, offs);
+	if (oval != oldval)
+		return -EAGAIN;
+	asd_write_reg_word(asd_ha, AOLDDATA, oldval);
+	asd_write_reg_word(asd_ha, ANEWDATA, newval);
+	do {
+		done = asd_read_reg_byte(asd_ha, ATOMICSTATCTL);
+	} while (!(done & ATOMICDONE));
+	if (done & ATOMICERR)
+		return -EFAULT;	  /* parity error */
+	else if (done & ATOMICWIN)
+		return 0;	  /* success */
+	else
+		return -EAGAIN;	  /* oldval different than current value */
+}
+
+static inline int asd_ddbsite_update_byte(struct asd_ha_struct *asd_ha,
+					  u16 ddb_site_no, u16 offs,
+					  u8 _oldval, u8 _newval)
+{
+	u16 base = offs & ~1;
+	u16 oval;
+	u16 nval = asd_ddbsite_read_word(asd_ha, ddb_site_no, base);
+	if (offs & 1) {
+		if ((nval >> 8) != _oldval)
+			return -EAGAIN;
+		nval = (_newval << 8) | (nval & 0xFF);
+		oval = (_oldval << 8) | (nval & 0xFF);
+	} else {
+		if ((nval & 0xFF) != _oldval)
+			return -EAGAIN;
+		nval = (nval & 0xFF00) | _newval;
+		oval = (nval & 0xFF00) | _oldval;
+	}
+	return asd_ddbsite_update_word(asd_ha, ddb_site_no, base, oval, nval);
+}
+
+static inline void asd_write_reg_addr(struct asd_ha_struct *asd_ha, u32 reg,
+				      dma_addr_t dma_handle)
+{
+	asd_write_reg_dword(asd_ha, reg,   ASD_BUSADDR_LO(dma_handle));
+	asd_write_reg_dword(asd_ha, reg+4, ASD_BUSADDR_HI(dma_handle));
+}
+
+static inline u32 asd_get_cmdctx_size(struct asd_ha_struct *asd_ha)
+{
+	/* DCHREVISION returns 0, possibly broken */
+	u32 ctxmemsize = asd_read_reg_dword(asd_ha, LmMnINT(0,0)) & CTXMEMSIZE;
+	return ctxmemsize ? 65536 : 32768;
+}
+
+static inline u32 asd_get_devctx_size(struct asd_ha_struct *asd_ha)
+{
+	u32 ctxmemsize = asd_read_reg_dword(asd_ha, LmMnINT(0,0)) & CTXMEMSIZE;
+	return ctxmemsize ? 8192 : 4096;
+}
+
+static inline void asd_disable_ints(struct asd_ha_struct *asd_ha)
+{
+	asd_write_reg_dword(asd_ha, CHIMINTEN, RST_CHIMINTEN);
+}
+
+static inline void asd_enable_ints(struct asd_ha_struct *asd_ha)
+{
+	/* Enable COM SAS interrupt on errors, COMSTAT */
+	asd_write_reg_dword(asd_ha, COMSTATEN,
+			    EN_CSBUFPERR | EN_CSERR | EN_OVLYERR);
+	/* Enable DCH SAS CFIFTOERR */
+	asd_write_reg_dword(asd_ha, DCHSTATUS, EN_CFIFTOERR);
+	/* Enable Host Device interrupts */
+	asd_write_reg_dword(asd_ha, CHIMINTEN, SET_CHIMINTEN);
+}
+
+#endif
diff -urN oldtree/drivers/scsi/aic94xx/aic94xx_reg_def.h newtree/drivers/scsi/aic94xx/aic94xx_reg_def.h
--- oldtree/drivers/scsi/aic94xx/aic94xx_reg_def.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/aic94xx_reg_def.h	2006-02-13 19:20:00.585423304 -0500
@@ -0,0 +1,2386 @@
+/*
+ * Aic94xx SAS/SATA driver hardware registers defintions.
+ *
+ * Copyright (C) 2004 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2004 David Chaw <david_chaw@adaptec.com>
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * Luben Tuikov: Some register value updates to make it work with the window
+ * agnostic register r/w functions.  Some register corrections, sizes,
+ * etc.
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/aic94xx/aic94xx_reg_def.h#27 $
+ *
+ */
+
+#ifndef _ADP94XX_REG_DEF_H_
+#define _ADP94XX_REG_DEF_H_
+
+/*
+ * Common definitions.
+ */
+#define CSEQ_MODE_PAGE_SIZE	0x200		/* CSEQ mode page size */
+#define LmSEQ_MODE_PAGE_SIZE	0x200		/* LmSEQ mode page size */
+#define LmSEQ_HOST_REG_SIZE   	0x4000		/* LmSEQ Host Register size */
+
+/********************* COM_SAS registers definition *************************/
+
+/* The base is REG_BASE_ADDR, defined in aic94xx_reg.h.
+ */
+
+/*
+ * CHIM Registers, Address Range : (0x00-0xFF)
+ */
+#define COMBIST		(REG_BASE_ADDR + 0x00)
+
+/* bits 31:24 */
+#define		L7BLKRST		0x80000000
+#define		L6BLKRST		0x40000000
+#define		L5BLKRST		0x20000000
+#define		L4BLKRST		0x10000000
+#define		L3BLKRST		0x08000000
+#define		L2BLKRST		0x04000000
+#define		L1BLKRST		0x02000000
+#define		L0BLKRST		0x01000000
+#define		LmBLKRST		0xFF000000
+#define LmBLKRST_COMBIST(phyid)		(1 << (24 + phyid))
+
+#define		OCMBLKRST		0x00400000
+#define		CTXMEMBLKRST		0x00200000
+#define		CSEQBLKRST		0x00100000
+#define		EXSIBLKRST		0x00040000
+#define		DPIBLKRST		0x00020000
+#define		DFIFBLKRST		0x00010000
+#define		HARDRST			0x00000200
+#define		COMBLKRST		0x00000100
+#define		FRCDFPERR		0x00000080
+#define		FRCCIOPERR		0x00000020
+#define		FRCBISTERR		0x00000010
+#define		COMBISTEN		0x00000004
+#define		COMBISTDONE		0x00000002	/* ro */
+#define 	COMBISTFAIL		0x00000001	/* ro */
+
+#define COMSTAT		(REG_BASE_ADDR + 0x04)
+
+#define		REQMBXREAD		0x00000040
+#define 	RSPMBXAVAIL		0x00000020
+#define 	CSBUFPERR		0x00000008
+#define		OVLYERR			0x00000004
+#define 	CSERR			0x00000002
+#define		OVLYDMADONE		0x00000001
+
+#define		COMSTAT_MASK		(REQMBXREAD | RSPMBXAVAIL | \
+					 CSBUFPERR | OVLYERR | CSERR |\
+					 OVLYDMADONE)
+
+#define COMSTATEN	(REG_BASE_ADDR + 0x08)
+
+#define		EN_REQMBXREAD		0x00000040
+#define		EN_RSPMBXAVAIL		0x00000020
+#define		EN_CSBUFPERR		0x00000008
+#define		EN_OVLYERR		0x00000004
+#define		EN_CSERR		0x00000002
+#define		EN_OVLYDONE		0x00000001
+
+#define SCBPRO		(REG_BASE_ADDR + 0x0C)
+
+#define		SCBCONS_MASK		0xFFFF0000
+#define		SCBPRO_MASK		0x0000FFFF
+
+#define CHIMREQMBX	(REG_BASE_ADDR + 0x10)
+
+#define CHIMRSPMBX	(REG_BASE_ADDR + 0x14)
+
+#define CHIMINT		(REG_BASE_ADDR + 0x18)
+
+#define		EXT_INT0		0x00000800
+#define		EXT_INT1		0x00000400
+#define		PORRSTDET		0x00000200
+#define		HARDRSTDET		0x00000100
+#define		DLAVAILQ		0x00000080	/* ro */
+#define		HOSTERR			0x00000040
+#define		INITERR			0x00000020
+#define		DEVINT			0x00000010
+#define		COMINT			0x00000008
+#define		DEVTIMER2		0x00000004
+#define		DEVTIMER1		0x00000002
+#define		DLAVAIL			0x00000001
+
+#define		CHIMINT_MASK		(HOSTERR | INITERR | DEVINT | COMINT |\
+					 DEVTIMER2 | DEVTIMER1 | DLAVAIL)
+
+#define 	DEVEXCEPT_MASK		(HOSTERR | INITERR | DEVINT | COMINT)
+
+#define CHIMINTEN	(REG_BASE_ADDR + 0x1C)
+
+#define		RST_EN_EXT_INT1		0x01000000
+#define		RST_EN_EXT_INT0		0x00800000
+#define		RST_EN_HOSTERR		0x00400000
+#define		RST_EN_INITERR		0x00200000
+#define		RST_EN_DEVINT		0x00100000
+#define		RST_EN_COMINT		0x00080000
+#define		RST_EN_DEVTIMER2	0x00040000
+#define		RST_EN_DEVTIMER1	0x00020000
+#define		RST_EN_DLAVAIL		0x00010000
+#define		SET_EN_EXT_INT1		0x00000100
+#define		SET_EN_EXT_INT0		0x00000080
+#define		SET_EN_HOSTERR		0x00000040
+#define		SET_EN_INITERR		0x00000020
+#define		SET_EN_DEVINT		0x00000010
+#define		SET_EN_COMINT		0x00000008
+#define		SET_EN_DEVTIMER2	0x00000004
+#define		SET_EN_DEVTIMER1	0x00000002
+#define		SET_EN_DLAVAIL		0x00000001
+
+#define		RST_CHIMINTEN		(RST_EN_HOSTERR | RST_EN_INITERR | \
+					 RST_EN_DEVINT | RST_EN_COMINT | \
+					 RST_EN_DEVTIMER2 | RST_EN_DEVTIMER1 |\
+					 RST_EN_DLAVAIL)
+
+#define		SET_CHIMINTEN		(SET_EN_HOSTERR | SET_EN_INITERR |\
+					 SET_EN_DEVINT | SET_EN_COMINT |\
+					 SET_EN_DLAVAIL)
+
+#define OVLYDMACTL	(REG_BASE_ADDR + 0x20)
+
+#define		OVLYADR_MASK		0x07FF0000
+#define		OVLYLSEQ_MASK		0x0000FF00
+#define		OVLYCSEQ		0x00000080
+#define		OVLYHALTERR		0x00000040
+#define		PIOCMODE		0x00000020
+#define		RESETOVLYDMA		0x00000008	/* wo */
+#define		STARTOVLYDMA		0x00000004
+#define		STOPOVLYDMA		0x00000002	/* wo */
+#define		OVLYDMAACT		0x00000001	/* ro */
+
+#define OVLYDMACNT	(REG_BASE_ADDR + 0x24)
+
+#define		OVLYDOMAIN1		0x20000000	/* ro */
+#define		OVLYDOMAIN0		0x10000000
+#define		OVLYBUFADR_MASK		0x007F0000
+#define		OVLYDMACNT_MASK		0x00003FFF
+
+#define OVLYDMAADR	(REG_BASE_ADDR + 0x28)
+
+#define DMAERR		(REG_BASE_ADDR + 0x30)
+
+#define		OVLYERRSTAT_MASK	0x0000FF00	/* ro */
+#define		CSERRSTAT_MASK		0x000000FF	/* ro */
+
+#define SPIODATA	(REG_BASE_ADDR + 0x34)
+
+/* 0x38 - 0x3C are reserved  */
+
+#define T1CNTRLR	(REG_BASE_ADDR + 0x40)
+
+#define		T1DONE			0x00010000	/* ro */
+#define		TIMER64			0x00000400
+#define		T1ENABLE		0x00000200
+#define		T1RELOAD		0x00000100
+#define		T1PRESCALER_MASK	0x00000003
+
+#define	T1CMPR		(REG_BASE_ADDR + 0x44)
+
+#define T1CNTR		(REG_BASE_ADDR + 0x48)
+
+#define T2CNTRLR	(REG_BASE_ADDR + 0x4C)
+
+#define		T2DONE			0x00010000	/* ro */
+#define		T2ENABLE		0x00000200
+#define		T2RELOAD		0x00000100
+#define		T2PRESCALER_MASK	0x00000003
+
+#define	T2CMPR		(REG_BASE_ADDR + 0x50)
+
+#define T2CNTR		(REG_BASE_ADDR + 0x54)
+
+/* 0x58h - 0xFCh are reserved */
+
+/*
+ * DCH_SAS Registers, Address Range : (0x800-0xFFF)
+ */
+#define CMDCTXBASE	(REG_BASE_ADDR + 0x800)
+
+#define DEVCTXBASE	(REG_BASE_ADDR + 0x808)
+
+#define CTXDOMAIN	(REG_BASE_ADDR + 0x810)
+
+#define		DEVCTXDOMAIN1		0x00000008	/* ro */
+#define		DEVCTXDOMAIN0		0x00000004
+#define		CMDCTXDOMAIN1		0x00000002	/* ro */
+#define		CMDCTXDOMAIN0		0x00000001
+
+#define DCHCTL		(REG_BASE_ADDR + 0x814)
+
+#define		OCMBISTREPAIR		0x00080000
+#define		OCMBISTEN		0x00040000
+#define		OCMBISTDN		0x00020000	/* ro */
+#define		OCMBISTFAIL		0x00010000	/* ro */
+#define		DDBBISTEN		0x00004000
+#define		DDBBISTDN		0x00002000	/* ro */
+#define		DDBBISTFAIL		0x00001000	/* ro */
+#define		SCBBISTEN		0x00000400
+#define		SCBBISTDN		0x00000200	/* ro */
+#define		SCBBISTFAIL		0x00000100	/* ro */
+
+#define		MEMSEL_MASK		0x000000E0
+#define		MEMSEL_CCM_LSEQ		0x00000000
+#define		MEMSEL_CCM_IOP		0x00000020
+#define		MEMSEL_CCM_SASCTL	0x00000040
+#define		MEMSEL_DCM_LSEQ		0x00000060
+#define		MEMSEL_DCM_IOP		0x00000080
+#define		MEMSEL_OCM		0x000000A0
+
+#define		FRCERR			0x00000010
+#define		AUTORLS			0x00000001
+
+#define DCHREVISION	(REG_BASE_ADDR + 0x818)
+
+#define		DCHREVISION_MASK	0x000000FF
+
+#define DCHSTATUS	(REG_BASE_ADDR + 0x81C)
+
+#define		EN_CFIFTOERR		0x00020000
+#define		CFIFTOERR		0x00000200
+#define		CSEQINT			0x00000100	/* ro */
+#define		LSEQ7INT		0x00000080	/* ro */
+#define		LSEQ6INT		0x00000040	/* ro */
+#define		LSEQ5INT		0x00000020	/* ro */
+#define		LSEQ4INT		0x00000010	/* ro */
+#define		LSEQ3INT		0x00000008	/* ro */
+#define		LSEQ2INT		0x00000004	/* ro */
+#define		LSEQ1INT		0x00000002	/* ro */
+#define		LSEQ0INT		0x00000001	/* ro */
+
+#define		LSEQINT_MASK		(LSEQ7INT | LSEQ6INT | LSEQ5INT |\
+					 LSEQ4INT | LSEQ3INT | LSEQ2INT	|\
+					 LSEQ1INT | LSEQ0INT)
+
+#define DCHDFIFDEBUG	(REG_BASE_ADDR + 0x820)
+#define		ENFAIRMST		0x00FF0000
+#define		DISWRMST9		0x00000200
+#define		DISWRMST8		0x00000100
+#define		DISRDMST		0x000000FF
+
+#define ATOMICSTATCTL	(REG_BASE_ADDR + 0x824)
+/* 8 bit wide */
+#define		AUTOINC			0x80
+#define		ATOMICERR		0x04
+#define		ATOMICWIN		0x02
+#define		ATOMICDONE		0x01
+
+
+#define ALTCIOADR	(REG_BASE_ADDR + 0x828)
+/* 16 bit; bits 8:0 define CIO addr space of CSEQ */
+
+#define ASCBPTR		(REG_BASE_ADDR + 0x82C)
+/* 16 bit wide */
+
+#define ADDBPTR		(REG_BASE_ADDR + 0x82E)
+/* 16 bit wide */
+
+#define ANEWDATA	(REG_BASE_ADDR + 0x830)
+/* 16 bit */
+
+#define AOLDDATA	(REG_BASE_ADDR + 0x834)
+/* 16 bit */
+
+#define CTXACCESS	(REG_BASE_ADDR + 0x838)
+/* 32 bit */
+
+/* 0x83Ch - 0xFFCh are reserved */
+
+/*
+ * ARP2 External Processor Registers, Address Range : (0x00-0x1F)
+ */
+#define ARP2CTL		0x00
+
+#define		FRCSCRPERR		0x00040000
+#define		FRCARP2PERR		0x00020000
+#define		FRCARP2ILLOPC		0x00010000
+#define		ENWAITTO		0x00008000
+#define		PERRORDIS		0x00004000
+#define		FAILDIS			0x00002000
+#define		CIOPERRDIS		0x00001000
+#define		BREAKEN3		0x00000800
+#define		BREAKEN2		0x00000400
+#define		BREAKEN1		0x00000200
+#define		BREAKEN0		0x00000100
+#define		EPAUSE			0x00000008
+#define		PAUSED			0x00000004	/* ro */
+#define		STEP			0x00000002
+#define		ARP2RESET		0x00000001	/* wo */
+
+#define ARP2INT		0x04
+
+#define		HALTCODE_MASK		0x00FF0000	/* ro */
+#define		ARP2WAITTO		0x00000100
+#define		ARP2HALTC		0x00000080
+#define		ARP2ILLOPC		0x00000040
+#define		ARP2PERR		0x00000020
+#define		ARP2CIOPERR		0x00000010
+#define		ARP2BREAK3		0x00000008
+#define		ARP2BREAK2		0x00000004
+#define		ARP2BREAK1		0x00000002
+#define		ARP2BREAK0		0x00000001
+
+#define ARP2INTEN	0x08
+
+#define		EN_ARP2WAITTO		0x00000100
+#define		EN_ARP2HALTC		0x00000080
+#define		EN_ARP2ILLOPC		0x00000040
+#define		EN_ARP2PERR		0x00000020
+#define		EN_ARP2CIOPERR		0x00000010
+#define		EN_ARP2BREAK3		0x00000008
+#define		EN_ARP2BREAK2		0x00000004
+#define		EN_ARP2BREAK1		0x00000002
+#define		EN_ARP2BREAK0		0x00000001
+
+#define ARP2BREAKADR01	0x0C
+
+#define		BREAKADR1_MASK		0x0FFF0000
+#define		BREAKADR0_MASK		0x00000FFF
+
+#define	ARP2BREAKADR23	0x10
+
+#define		BREAKADR3_MASK		0x0FFF0000
+#define		BREAKADR2_MASK		0x00000FFF
+
+/* 0x14h - 0x1Ch are reserved */
+
+/*
+ * ARP2 Registers, Address Range : (0x00-0x1F)
+ * The definitions have the same address offset for CSEQ and LmSEQ
+ * CIO Bus Registers.
+ */
+#define MODEPTR		0x00
+
+#define		DSTMODE			0xF0
+#define		SRCMODE			0x0F
+
+#define ALTMODE		0x01
+
+#define		ALTDMODE		0xF0
+#define		ALTSMODE		0x0F
+
+#define ATOMICXCHG	0x02
+
+#define FLAG		0x04
+
+#define		INTCODE_MASK		0xF0
+#define		ALTMODEV2		0x04
+#define		CARRY_INT		0x02
+#define		CARRY			0x01
+
+#define ARP2INTCTL	0x05
+
+#define 	PAUSEDIS		0x80
+#define		RSTINTCTL		0x40
+#define		POPALTMODE		0x08
+#define		ALTMODEV		0x04
+#define		INTMASK			0x02
+#define		IRET			0x01
+
+#define STACK		0x06
+
+#define FUNCTION1	0x07
+
+#define PRGMCNT		0x08
+
+#define ACCUM		0x0A
+
+#define SINDEX		0x0C
+
+#define DINDEX		0x0E
+
+#define ALLONES		0x10
+
+#define ALLZEROS	0x11
+
+#define SINDIR		0x12
+
+#define DINDIR		0x13
+
+#define JUMLDIR		0x14
+
+#define ARP2HALTCODE	0x15
+
+#define CURRADDR	0x16
+
+#define LASTADDR	0x18
+
+#define NXTLADDR	0x1A
+
+#define DBGPORTPTR	0x1C
+
+#define DBGPORT		0x1D
+
+/*
+ * CIO Registers.
+ * The definitions have the same address offset for CSEQ and LmSEQ
+ * CIO Bus Registers.
+ */
+#define MnSCBPTR      	0x20
+
+#define MnDDBPTR      	0x22
+
+#define SCRATCHPAGE	0x24
+
+#define MnSCRATCHPAGE	0x25
+
+#define SCRATCHPAGESV	0x26
+
+#define MnSCRATCHPAGESV	0x27
+
+#define MnDMAERRS	0x46
+
+#define MnSGDMAERRS	0x47
+
+#define MnSGBUF		0x53
+
+#define MnSGDMASTAT	0x5b
+
+#define MnDDMACTL	0x5c	/* RAZOR.rspec.fm rev 1.5 is wrong */
+
+#define MnDDMASTAT	0x5d	/* RAZOR.rspec.fm rev 1.5 is wrong */
+
+#define MnDDMAMODE	0x5e	/* RAZOR.rspec.fm rev 1.5 is wrong */
+
+#define MnDMAENG	0x60
+
+#define MnPIPECTL	0x61
+
+#define MnSGBADR	0x65
+
+#define MnSCB_SITE	0x100
+
+#define MnDDB_SITE	0x180
+
+/*
+ * The common definitions below have the same address offset for both
+ * CSEQ and LmSEQ.
+ */
+#define BISTCTL0	0x4C
+
+#define BISTCTL1	0x50
+
+#define MAPPEDSCR	0x800
+
+/*
+ * CSEQ Host Register, Address Range : (0x000-0xFFC)
+ */
+#define CSEQ_HOST_REG_BASE_ADR		0xB8001000
+
+#define CARP2CTL			(CSEQ_HOST_REG_BASE_ADR	+ ARP2CTL)
+
+#define CARP2INT			(CSEQ_HOST_REG_BASE_ADR	+ ARP2INT)
+
+#define CARP2INTEN			(CSEQ_HOST_REG_BASE_ADR	+ ARP2INTEN)
+
+#define CARP2BREAKADR01			(CSEQ_HOST_REG_BASE_ADR+ARP2BREAKADR01)
+
+#define CARP2BREAKADR23			(CSEQ_HOST_REG_BASE_ADR+ARP2BREAKADR23)
+
+#define CBISTCTL			(CSEQ_HOST_REG_BASE_ADR	+ BISTCTL1)
+
+#define		CSEQRAMBISTEN		0x00000040
+#define		CSEQRAMBISTDN		0x00000020	/* ro */
+#define		CSEQRAMBISTFAIL		0x00000010	/* ro */
+#define		CSEQSCRBISTEN		0x00000004
+#define		CSEQSCRBISTDN		0x00000002	/* ro */
+#define		CSEQSCRBISTFAIL		0x00000001	/* ro */
+
+#define CMAPPEDSCR			(CSEQ_HOST_REG_BASE_ADR	+ MAPPEDSCR)
+
+/*
+ * CSEQ CIO Bus Registers, Address Range : (0x0000-0x1FFC)
+ * 16 modes, each mode is 512 bytes.
+ * Unless specified, the register should valid for all modes.
+ */
+#define CSEQ_CIO_REG_BASE_ADR		REG_BASE_ADDR_CSEQCIO
+
+#define CSEQm_CIO_REG(Mode, Reg) \
+		(CSEQ_CIO_REG_BASE_ADR  + \
+		((u32) (Mode) * CSEQ_MODE_PAGE_SIZE) + (u32) (Reg))
+
+#define CMODEPTR	(CSEQ_CIO_REG_BASE_ADR + MODEPTR)
+
+#define CALTMODE	(CSEQ_CIO_REG_BASE_ADR + ALTMODE)
+
+#define CATOMICXCHG	(CSEQ_CIO_REG_BASE_ADR + ATOMICXCHG)
+
+#define CFLAG		(CSEQ_CIO_REG_BASE_ADR + FLAG)
+
+#define CARP2INTCTL	(CSEQ_CIO_REG_BASE_ADR + ARP2INTCTL)
+
+#define CSTACK		(CSEQ_CIO_REG_BASE_ADR + STACK)
+
+#define CFUNCTION1	(CSEQ_CIO_REG_BASE_ADR + FUNCTION1)
+
+#define CPRGMCNT	(CSEQ_CIO_REG_BASE_ADR + PRGMCNT)
+
+#define CACCUM		(CSEQ_CIO_REG_BASE_ADR + ACCUM)
+
+#define CSINDEX		(CSEQ_CIO_REG_BASE_ADR + SINDEX)
+
+#define CDINDEX		(CSEQ_CIO_REG_BASE_ADR + DINDEX)
+
+#define CALLONES	(CSEQ_CIO_REG_BASE_ADR + ALLONES)
+
+#define CALLZEROS	(CSEQ_CIO_REG_BASE_ADR + ALLZEROS)
+
+#define CSINDIR		(CSEQ_CIO_REG_BASE_ADR + SINDIR)
+
+#define CDINDIR		(CSEQ_CIO_REG_BASE_ADR + DINDIR)
+
+#define CJUMLDIR	(CSEQ_CIO_REG_BASE_ADR + JUMLDIR)
+
+#define CARP2HALTCODE	(CSEQ_CIO_REG_BASE_ADR + ARP2HALTCODE)
+
+#define CCURRADDR	(CSEQ_CIO_REG_BASE_ADR + CURRADDR)
+
+#define CLASTADDR	(CSEQ_CIO_REG_BASE_ADR + LASTADDR)
+
+#define CNXTLADDR	(CSEQ_CIO_REG_BASE_ADR + NXTLADDR)
+
+#define CDBGPORTPTR	(CSEQ_CIO_REG_BASE_ADR + DBGPORTPTR)
+
+#define CDBGPORT	(CSEQ_CIO_REG_BASE_ADR + DBGPORT)
+
+#define CSCRATCHPAGE	(CSEQ_CIO_REG_BASE_ADR + SCRATCHPAGE)
+
+#define CMnSCBPTR(Mode)       CSEQm_CIO_REG(Mode, MnSCBPTR)
+
+#define CMnDDBPTR(Mode)       CSEQm_CIO_REG(Mode, MnDDBPTR)
+
+#define CMnSCRATCHPAGE(Mode)		CSEQm_CIO_REG(Mode, MnSCRATCHPAGE)
+
+#define CLINKCON	(CSEQ_CIO_REG_BASE_ADR + 0x28)
+
+#define	CCIOAACESS	(CSEQ_CIO_REG_BASE_ADR + 0x2C)
+
+/* mode 0-7 */
+#define MnREQMBX 0x30
+#define CMnREQMBX(Mode)			CSEQm_CIO_REG(Mode, 0x30)
+
+/* mode 8 */
+#define CSEQCON				CSEQm_CIO_REG(8, 0x30)
+
+/* mode 0-7 */
+#define MnRSPMBX 0x34
+#define CMnRSPMBX(Mode)			CSEQm_CIO_REG(Mode, 0x34)
+
+/* mode 8 */
+#define CSEQCOMCTL			CSEQm_CIO_REG(8, 0x34)
+
+/* mode 8 */
+#define CSEQCOMSTAT			CSEQm_CIO_REG(8, 0x35)
+
+/* mode 8 */
+#define CSEQCOMINTEN			CSEQm_CIO_REG(8, 0x36)
+
+/* mode 8 */
+#define CSEQCOMDMACTL			CSEQm_CIO_REG(8, 0x37)
+
+#define		CSHALTERR		0x10
+#define		RESETCSDMA		0x08		/* wo */
+#define		STARTCSDMA		0x04
+#define		STOPCSDMA		0x02		/* wo */
+#define		CSDMAACT		0x01		/* ro */
+
+/* mode 0-7 */
+#define MnINT 0x38
+#define CMnINT(Mode)			CSEQm_CIO_REG(Mode, 0x38)
+
+#define		CMnREQMBXE		0x02
+#define		CMnRSPMBXF		0x01
+#define		CMnINT_MASK		0x00000003
+
+/* mode 8 */
+#define CSEQREQMBX			CSEQm_CIO_REG(8, 0x38)
+
+/* mode 0-7 */
+#define MnINTEN 0x3C
+#define CMnINTEN(Mode)			CSEQm_CIO_REG(Mode, 0x3C)
+
+#define		EN_CMnRSPMBXF		0x01
+
+/* mode 8 */
+#define CSEQRSPMBX			CSEQm_CIO_REG(8, 0x3C)
+
+/* mode 8 */
+#define CSDMAADR			CSEQm_CIO_REG(8, 0x40)
+
+/* mode 8 */
+#define CSDMACNT			CSEQm_CIO_REG(8, 0x48)
+
+/* mode 8 */
+#define CSEQDLCTL			CSEQm_CIO_REG(8, 0x4D)
+
+#define		DONELISTEND		0x10
+#define 	DONELISTSIZE_MASK	0x0F
+#define		DONELISTSIZE_8ELEM	0x01
+#define		DONELISTSIZE_16ELEM	0x02
+#define		DONELISTSIZE_32ELEM	0x03
+#define		DONELISTSIZE_64ELEM	0x04
+#define		DONELISTSIZE_128ELEM	0x05
+#define		DONELISTSIZE_256ELEM	0x06
+#define		DONELISTSIZE_512ELEM	0x07
+#define		DONELISTSIZE_1024ELEM	0x08
+#define		DONELISTSIZE_2048ELEM	0x09
+#define		DONELISTSIZE_4096ELEM	0x0A
+#define		DONELISTSIZE_8192ELEM	0x0B
+#define		DONELISTSIZE_16384ELEM	0x0C
+
+/* mode 8 */
+#define CSEQDLOFFS			CSEQm_CIO_REG(8, 0x4E)
+
+/* mode 11 */
+#define CM11INTVEC0			CSEQm_CIO_REG(11, 0x50)
+
+/* mode 11 */
+#define CM11INTVEC1			CSEQm_CIO_REG(11, 0x52)
+
+/* mode 11 */
+#define CM11INTVEC2			CSEQm_CIO_REG(11, 0x54)
+
+#define	CCONMSK	  			(CSEQ_CIO_REG_BASE_ADR + 0x60)
+
+#define	CCONEXIST			(CSEQ_CIO_REG_BASE_ADR + 0x61)
+
+#define	CCONMODE			(CSEQ_CIO_REG_BASE_ADR + 0x62)
+
+#define CTIMERCALC			(CSEQ_CIO_REG_BASE_ADR + 0x64)
+
+#define CINTDIS				(CSEQ_CIO_REG_BASE_ADR + 0x68)
+
+/* mode 8, 32x32 bits, 128 bytes of mapped buffer */
+#define CSBUFFER			CSEQm_CIO_REG(8, 0x80)
+
+#define	CSCRATCH			(CSEQ_CIO_REG_BASE_ADR + 0x1C0)
+
+/* mode 0-8 */
+#define CMnSCRATCH(Mode)		CSEQm_CIO_REG(Mode, 0x1E0)
+
+/*
+ * CSEQ Mapped Instruction RAM Page, Address Range : (0x0000-0x1FFC)
+ */
+#define CSEQ_RAM_REG_BASE_ADR		0xB8004000
+
+/*
+ * The common definitions below have the same address offset for all the Link
+ * sequencers.
+ */
+#define MODECTL		0x40
+
+#define DBGMODE		0x44
+
+#define CONTROL		0x48
+#define LEDTIMER		0x00010000
+#define LEDTIMERS_10us		0x00000000
+#define LEDTIMERS_1ms		0x00000800
+#define LEDTIMERS_100ms		0x00001000
+#define LEDMODE_TXRX		0x00000000
+#define LEDMODE_CONNECTED	0x00000200
+#define LEDPOL			0x00000100
+
+#define LSEQRAM		0x1000
+
+/*
+ * LmSEQ Host Registers, Address Range : (0x0000-0x3FFC)
+ */
+#define LSEQ0_HOST_REG_BASE_ADR		0xB8020000
+#define LSEQ1_HOST_REG_BASE_ADR		0xB8024000
+#define LSEQ2_HOST_REG_BASE_ADR		0xB8028000
+#define LSEQ3_HOST_REG_BASE_ADR		0xB802C000
+#define LSEQ4_HOST_REG_BASE_ADR		0xB8030000
+#define LSEQ5_HOST_REG_BASE_ADR		0xB8034000
+#define LSEQ6_HOST_REG_BASE_ADR		0xB8038000
+#define LSEQ7_HOST_REG_BASE_ADR		0xB803C000
+
+#define LmARP2CTL(LinkNum)		(LSEQ0_HOST_REG_BASE_ADR +	  \
+					((LinkNum)*LmSEQ_HOST_REG_SIZE) + \
+					ARP2CTL)
+
+#define LmARP2INT(LinkNum)		(LSEQ0_HOST_REG_BASE_ADR +	  \
+					((LinkNum)*LmSEQ_HOST_REG_SIZE) + \
+					ARP2INT)
+
+#define LmARP2INTEN(LinkNum)		(LSEQ0_HOST_REG_BASE_ADR +	  \
+					((LinkNum)*LmSEQ_HOST_REG_SIZE) + \
+					ARP2INTEN)
+
+#define LmDBGMODE(LinkNum)		(LSEQ0_HOST_REG_BASE_ADR +	  \
+					((LinkNum)*LmSEQ_HOST_REG_SIZE) + \
+					DBGMODE)
+
+#define LmCONTROL(LinkNum)		(LSEQ0_HOST_REG_BASE_ADR +	  \
+					((LinkNum)*LmSEQ_HOST_REG_SIZE) + \
+					CONTROL)
+
+#define LmARP2BREAKADR01(LinkNum)	(LSEQ0_HOST_REG_BASE_ADR +	  \
+					((LinkNum)*LmSEQ_HOST_REG_SIZE) + \
+					ARP2BREAKADR01)
+
+#define LmARP2BREAKADR23(LinkNum)	(LSEQ0_HOST_REG_BASE_ADR +	  \
+					((LinkNum)*LmSEQ_HOST_REG_SIZE) + \
+					ARP2BREAKADR23)
+
+#define LmMODECTL(LinkNum)		(LSEQ0_HOST_REG_BASE_ADR +	  \
+					((LinkNum)*LmSEQ_HOST_REG_SIZE) + \
+					MODECTL)
+
+#define		LmAUTODISCI		0x08000000
+#define		LmDSBLBITLT		0x04000000
+#define		LmDSBLANTT		0x02000000
+#define		LmDSBLCRTT		0x01000000
+#define		LmDSBLCONT		0x00000100
+#define		LmPRIMODE		0x00000080
+#define		LmDSBLHOLD		0x00000040
+#define		LmDISACK		0x00000020
+#define		LmBLIND48		0x00000010
+#define		LmRCVMODE_MASK		0x0000000C
+#define		LmRCVMODE_PLD		0x00000000
+#define		LmRCVMODE_HPC		0x00000004
+
+#define LmDBGMODE(LinkNum)		(LSEQ0_HOST_REG_BASE_ADR +	  \
+					((LinkNum)*LmSEQ_HOST_REG_SIZE) + \
+					DBGMODE)
+
+#define		LmFRCPERR		0x80000000
+#define		LmMEMSEL_MASK		0x30000000
+#define		LmFRCRBPERR		0x00000000
+#define		LmFRCTBPERR		0x10000000
+#define		LmFRCSGBPERR		0x20000000
+#define		LmFRCARBPERR		0x30000000
+#define		LmRCVIDW		0x00080000
+#define		LmINVDWERR		0x00040000
+#define		LmRCVDISP		0x00004000
+#define		LmDISPERR		0x00002000
+#define		LmDSBLDSCR		0x00000800
+#define		LmDSBLSCR		0x00000400
+#define		LmFRCNAK		0x00000200
+#define		LmFRCROFS		0x00000100
+#define		LmFRCCRC		0x00000080
+#define		LmFRMTYPE_MASK		0x00000070
+#define		LmSG_DATA		0x00000000
+#define		LmSG_COMMAND		0x00000010
+#define		LmSG_TASK		0x00000020
+#define		LmSG_TGTXFER		0x00000030
+#define		LmSG_RESPONSE		0x00000040
+#define		LmSG_IDENADDR		0x00000050
+#define		LmSG_OPENADDR		0x00000060
+#define		LmDISCRCGEN		0x00000008
+#define		LmDISCRCCHK		0x00000004
+#define		LmSSXMTFRM		0x00000002
+#define		LmSSRCVFRM		0x00000001
+
+#define LmCONTROL(LinkNum)		(LSEQ0_HOST_REG_BASE_ADR +	  \
+					((LinkNum)*LmSEQ_HOST_REG_SIZE) + \
+					CONTROL)
+
+#define		LmSTEPXMTFRM		0x00000002
+#define		LmSTEPRCVFRM		0x00000001
+
+#define LmBISTCTL0(LinkNum)		(LSEQ0_HOST_REG_BASE_ADR +	  \
+					((LinkNum)*LmSEQ_HOST_REG_SIZE) + \
+					BISTCTL0)
+
+#define		ARBBISTEN		0x40000000
+#define		ARBBISTDN		0x20000000	/* ro */
+#define		ARBBISTFAIL		0x10000000	/* ro */
+#define		TBBISTEN		0x00000400
+#define		TBBISTDN		0x00000200	/* ro */
+#define		TBBISTFAIL		0x00000100	/* ro */
+#define		RBBISTEN		0x00000040
+#define		RBBISTDN		0x00000020	/* ro */
+#define		RBBISTFAIL		0x00000010	/* ro */
+#define		SGBISTEN		0x00000004
+#define		SGBISTDN		0x00000002	/* ro */
+#define		SGBISTFAIL		0x00000001	/* ro */
+
+#define LmBISTCTL1(LinkNum)		(LSEQ0_HOST_REG_BASE_ADR +	 \
+					((LinkNum)*LmSEQ_HOST_REG_SIZE) +\
+					BISTCTL1)
+
+#define		LmRAMPAGE1		0x00000200
+#define		LmRAMPAGE0		0x00000100
+#define		LmIMEMBISTEN		0x00000040
+#define		LmIMEMBISTDN		0x00000020	/* ro */
+#define		LmIMEMBISTFAIL		0x00000010	/* ro */
+#define		LmSCRBISTEN		0x00000004
+#define		LmSCRBISTDN		0x00000002	/* ro */
+#define		LmSCRBISTFAIL		0x00000001	/* ro */
+#define		LmRAMPAGE		(LmRAMPAGE1 + LmRAMPAGE0)
+#define		LmRAMPAGE_LSHIFT	0x8
+
+#define LmSCRATCH(LinkNum)		(LSEQ0_HOST_REG_BASE_ADR +	   \
+					((LinkNum) * LmSEQ_HOST_REG_SIZE) +\
+					MAPPEDSCR)
+
+#define LmSEQRAM(LinkNum)		(LSEQ0_HOST_REG_BASE_ADR +	   \
+					((LinkNum) * LmSEQ_HOST_REG_SIZE) +\
+					LSEQRAM)
+
+/*
+ * LmSEQ CIO Bus Register, Address Range : (0x0000-0xFFC)
+ * 8 modes, each mode is 512 bytes.
+ * Unless specified, the register should valid for all modes.
+ */
+#define LmSEQ_CIOBUS_REG_BASE		0x2000
+
+#define  LmSEQ_PHY_BASE(Mode, LinkNum) \
+		(LSEQ0_HOST_REG_BASE_ADR + \
+		(LmSEQ_HOST_REG_SIZE * (u32) (LinkNum)) + \
+		LmSEQ_CIOBUS_REG_BASE + \
+		((u32) (Mode) * LmSEQ_MODE_PAGE_SIZE))
+
+#define  LmSEQ_PHY_REG(Mode, LinkNum, Reg) \
+                 (LmSEQ_PHY_BASE(Mode, LinkNum) + (u32) (Reg))
+
+#define LmMODEPTR(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, MODEPTR)
+
+#define LmALTMODE(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, ALTMODE)
+
+#define LmATOMICXCHG(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, ATOMICXCHG)
+
+#define LmFLAG(LinkNum)			LmSEQ_PHY_REG(0, LinkNum, FLAG)
+
+#define LmARP2INTCTL(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, ARP2INTCTL)
+
+#define LmSTACK(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, STACK)
+
+#define LmFUNCTION1(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, FUNCTION1)
+
+#define LmPRGMCNT(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, PRGMCNT)
+
+#define LmACCUM(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, ACCUM)
+
+#define LmSINDEX(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, SINDEX)
+
+#define LmDINDEX(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, DINDEX)
+
+#define LmALLONES(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, ALLONES)
+
+#define LmALLZEROS(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, ALLZEROS)
+
+#define LmSINDIR(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, SINDIR)
+
+#define LmDINDIR(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, DINDIR)
+
+#define LmJUMLDIR(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, JUMLDIR)
+
+#define LmARP2HALTCODE(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, ARP2HALTCODE)
+
+#define LmCURRADDR(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, CURRADDR)
+
+#define LmLASTADDR(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, LASTADDR)
+
+#define LmNXTLADDR(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, NXTLADDR)
+
+#define LmDBGPORTPTR(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, DBGPORTPTR)
+
+#define LmDBGPORT(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, DBGPORT)
+
+#define LmSCRATCHPAGE(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, SCRATCHPAGE)
+
+#define LmMnSCRATCHPAGE(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 	\
+						      MnSCRATCHPAGE)
+
+#define LmTIMERCALC(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, 0x28)
+
+#define LmREQMBX(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, 0x30)
+
+#define LmRSPMBX(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, 0x34)
+
+#define LmMnINT(LinkNum, Mode)		LmSEQ_PHY_REG(Mode, LinkNum, 0x38)
+
+#define		CTXMEMSIZE		0x80000000	/* ro */
+#define		LmACKREQ		0x08000000
+#define		LmNAKREQ		0x04000000
+#define		LmMnXMTERR		0x02000000
+#define		LmM5OOBSVC		0x01000000
+#define		LmHWTINT		0x00800000
+#define		LmMnCTXDONE		0x00100000
+#define		LmM2REQMBXF		0x00080000
+#define		LmM2RSPMBXE		0x00040000
+#define		LmMnDMAERR		0x00020000
+#define		LmRCVPRIM		0x00010000
+#define		LmRCVERR		0x00008000
+#define		LmADDRRCV		0x00004000
+#define		LmMnHDRMISS		0x00002000
+#define		LmMnWAITSCB		0x00001000
+#define		LmMnRLSSCB		0x00000800
+#define		LmMnSAVECTX		0x00000400
+#define		LmMnFETCHSG		0x00000200
+#define		LmMnLOADCTX		0x00000100
+#define		LmMnCFGICL		0x00000080
+#define		LmMnCFGSATA		0x00000040
+#define		LmMnCFGEXPSATA		0x00000020
+#define		LmMnCFGCMPLT		0x00000010
+#define		LmMnCFGRBUF		0x00000008
+#define		LmMnSAVETTR		0x00000004
+#define		LmMnCFGRDAT		0x00000002
+#define		LmMnCFGHDR		0x00000001
+
+#define LmMnINTEN(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 0x3C)
+
+#define		EN_LmACKREQ		0x08000000
+#define		EN_LmNAKREQ		0x04000000
+#define		EN_LmMnXMTERR		0x02000000
+#define		EN_LmM5OOBSVC		0x01000000
+#define		EN_LmHWTINT		0x00800000
+#define		EN_LmMnCTXDONE		0x00100000
+#define		EN_LmM2REQMBXF		0x00080000
+#define		EN_LmM2RSPMBXE		0x00040000
+#define		EN_LmMnDMAERR		0x00020000
+#define		EN_LmRCVPRIM		0x00010000
+#define		EN_LmRCVERR		0x00008000
+#define		EN_LmADDRRCV		0x00004000
+#define		EN_LmMnHDRMISS		0x00002000
+#define		EN_LmMnWAITSCB		0x00001000
+#define		EN_LmMnRLSSCB		0x00000800
+#define		EN_LmMnSAVECTX		0x00000400
+#define		EN_LmMnFETCHSG		0x00000200
+#define		EN_LmMnLOADCTX		0x00000100
+#define		EN_LmMnCFGICL		0x00000080
+#define		EN_LmMnCFGSATA		0x00000040
+#define		EN_LmMnCFGEXPSATA	0x00000020
+#define		EN_LmMnCFGCMPLT		0x00000010
+#define		EN_LmMnCFGRBUF		0x00000008
+#define		EN_LmMnSAVETTR		0x00000004
+#define		EN_LmMnCFGRDAT		0x00000002
+#define		EN_LmMnCFGHDR		0x00000001
+
+#define		LmM0INTEN_MASK		(EN_LmMnCFGCMPLT | EN_LmMnCFGRBUF | \
+					 EN_LmMnSAVETTR | EN_LmMnCFGRDAT | \
+					 EN_LmMnCFGHDR | EN_LmRCVERR | \
+					 EN_LmADDRRCV | EN_LmMnHDRMISS | \
+					 EN_LmMnRLSSCB | EN_LmMnSAVECTX | \
+					 EN_LmMnFETCHSG | EN_LmMnLOADCTX | \
+					 EN_LmHWTINT | EN_LmMnCTXDONE | \
+					 EN_LmRCVPRIM | EN_LmMnCFGSATA | \
+					 EN_LmMnCFGEXPSATA | EN_LmMnDMAERR)
+
+#define		LmM1INTEN_MASK		(EN_LmMnCFGCMPLT | EN_LmADDRRCV | \
+					 EN_LmMnRLSSCB | EN_LmMnSAVECTX | \
+					 EN_LmMnFETCHSG | EN_LmMnLOADCTX | \
+					 EN_LmMnXMTERR | EN_LmHWTINT | \
+					 EN_LmMnCTXDONE | EN_LmRCVPRIM | \
+					 EN_LmRCVERR | EN_LmMnDMAERR)
+
+#define		LmM2INTEN_MASK		(EN_LmADDRRCV | EN_LmHWTINT | \
+					 EN_LmM2REQMBXF | EN_LmRCVPRIM | \
+					 EN_LmRCVERR)
+
+#define		LmM5INTEN_MASK		(EN_LmADDRRCV | EN_LmM5OOBSVC | \
+					 EN_LmHWTINT | EN_LmRCVPRIM | \
+					 EN_LmRCVERR)
+
+#define LmXMTPRIMD(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, 0x40)
+
+#define LmXMTPRIMCS(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, 0x44)
+
+#define LmCONSTAT(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, 0x45)
+
+#define LmMnDMAERRS(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 0x46)
+
+#define LmMnSGDMAERRS(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 0x47)
+
+#define LmM0EXPHDRP(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, 0x48)
+
+#define LmM1SASALIGN(LinkNum)		LmSEQ_PHY_REG(1, LinkNum, 0x48)
+#define SAS_ALIGN_DEFAULT		0xFF
+
+#define LmM0MSKHDRP(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, 0x49)
+
+#define LmM1STPALIGN(LinkNum)		LmSEQ_PHY_REG(1, LinkNum, 0x49)
+#define STP_ALIGN_DEFAULT		0x1F
+
+#define LmM0RCVHDRP(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, 0x4A)
+
+#define LmM1XMTHDRP(LinkNum)		LmSEQ_PHY_REG(1, LinkNum, 0x4A)
+
+#define LmM0ICLADR(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, 0x4B)
+
+#define LmM1ALIGNMODE(LinkNum)		LmSEQ_PHY_REG(1, LinkNum, 0x4B)
+
+#define		LmDISALIGN		0x20
+#define		LmROTSTPALIGN		0x10
+#define		LmSTPALIGN		0x08
+#define		LmROTNOTIFY		0x04
+#define		LmDUALALIGN		0x02
+#define		LmROTALIGN		0x01
+
+#define LmM0EXPRCVNT(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, 0x4C)
+
+#define LmM1XMTCNT(LinkNum)		LmSEQ_PHY_REG(1, LinkNum, 0x4C)
+
+#define LmMnBUFSTAT(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 0x4E)
+
+#define		LmMnBUFPERR		0x01
+
+/* mode 0-1 */
+#define LmMnXFRLVL(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 0x59)
+
+#define		LmMnXFRLVL_128		0x05
+#define		LmMnXFRLVL_256		0x04
+#define		LmMnXFRLVL_512		0x03
+#define		LmMnXFRLVL_1024		0x02
+#define		LmMnXFRLVL_1536		0x01
+#define		LmMnXFRLVL_2048		0x00
+
+ /* mode 0-1 */
+#define LmMnSGDMACTL(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 0x5A)
+
+#define 	LmMnRESETSG		0x04
+#define 	LmMnSTOPSG		0x02
+#define 	LmMnSTARTSG		0x01
+
+/* mode 0-1 */
+#define LmMnSGDMASTAT(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 0x5B)
+
+/* mode 0-1 */
+#define LmMnDDMACTL(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 0x5C)
+
+#define 	LmMnFLUSH		0x40		/* wo */
+#define 	LmMnRLSRTRY		0x20		/* wo */
+#define 	LmMnDISCARD		0x10		/* wo */
+#define 	LmMnRESETDAT		0x08		/* wo */
+#define 	LmMnSUSDAT		0x04		/* wo */
+#define 	LmMnSTOPDAT		0x02		/* wo */
+#define 	LmMnSTARTDAT		0x01		/* wo */
+
+/* mode 0-1 */
+#define LmMnDDMASTAT(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 0x5D)
+
+#define		LmMnDPEMPTY		0x80
+#define		LmMnFLUSHING		0x40
+#define		LmMnDDMAREQ		0x20
+#define		LmMnHDMAREQ		0x10
+#define		LmMnDATFREE		0x08
+#define		LmMnDATSUS		0x04
+#define		LmMnDATACT		0x02
+#define		LmMnDATEN		0x01
+
+/* mode 0-1 */
+#define LmMnDDMAMODE(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 0x5E)
+
+#define 	LmMnDMATYPE_NORMAL		0x0000
+#define 	LmMnDMATYPE_HOST_ONLY_TX	0x0001
+#define 	LmMnDMATYPE_DEVICE_ONLY_TX	0x0002
+#define 	LmMnDMATYPE_INVALID		0x0003
+#define 	LmMnDMATYPE_MASK	0x0003
+
+#define 	LmMnDMAWRAP		0x0004
+#define 	LmMnBITBUCKET		0x0008
+#define 	LmMnDISHDR		0x0010
+#define 	LmMnSTPCRC		0x0020
+#define 	LmXTEST			0x0040
+#define 	LmMnDISCRC		0x0080
+#define 	LmMnENINTLK		0x0100
+#define 	LmMnADDRFRM		0x0400
+#define 	LmMnENXMTCRC		0x0800
+
+/* mode 0-1 */
+#define LmMnXFRCNT(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 0x70)
+
+/* mode 0-1 */
+#define LmMnDPSEL(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 0x7B)
+#define 	LmMnDPSEL_MASK		0x07
+#define 	LmMnEOLPRE		0x40
+#define 	LmMnEOSPRE		0x80
+
+/* Registers used in conjunction with LmMnDPSEL and LmMnDPACC registers */
+/* Receive Mode n = 0 */
+#define LmMnHRADDR			0x00
+#define LmMnHBYTECNT			0x01
+#define LmMnHREWIND			0x02
+#define LmMnDWADDR			0x03
+#define LmMnDSPACECNT			0x04
+#define LmMnDFRMSIZE			0x05
+
+/* Registers used in conjunction with LmMnDPSEL and LmMnDPACC registers */
+/* Transmit Mode n = 1 */
+#define LmMnHWADDR			0x00
+#define LmMnHSPACECNT			0x01
+/* #define LmMnHREWIND			0x02 */
+#define LmMnDRADDR			0x03
+#define LmMnDBYTECNT			0x04
+/* #define LmMnDFRMSIZE			0x05 */
+
+/* mode 0-1 */
+#define LmMnDPACC(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 0x78)
+#define 	LmMnDPACC_MASK		0x00FFFFFF
+
+/* mode 0-1 */
+#define LmMnHOLDLVL(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 0x7D)
+
+#define LmPRMSTAT0(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, 0x80)
+#define LmPRMSTAT0BYTE0			0x80
+#define LmPRMSTAT0BYTE1			0x81
+#define LmPRMSTAT0BYTE2			0x82
+#define LmPRMSTAT0BYTE3			0x83
+
+#define		LmFRAMERCVD		0x80000000
+#define		LmXFRRDYRCVD		0x40000000
+#define		LmUNKNOWNP		0x20000000
+#define		LmBREAK			0x10000000
+#define		LmDONE			0x08000000
+#define		LmOPENACPT		0x04000000
+#define		LmOPENRJCT		0x02000000
+#define		LmOPENRTRY		0x01000000
+#define		LmCLOSERV1		0x00800000
+#define		LmCLOSERV0		0x00400000
+#define		LmCLOSENORM		0x00200000
+#define		LmCLOSECLAF		0x00100000
+#define		LmNOTIFYRV2		0x00080000
+#define		LmNOTIFYRV1		0x00040000
+#define		LmNOTIFYRV0		0x00020000
+#define		LmNOTIFYSPIN		0x00010000
+#define		LmBROADRV4		0x00008000
+#define		LmBROADRV3		0x00004000
+#define		LmBROADRV2		0x00002000
+#define		LmBROADRV1		0x00001000
+#define		LmBROADSES		0x00000800
+#define		LmBROADRVCH1		0x00000400
+#define		LmBROADRVCH0		0x00000200
+#define		LmBROADCH		0x00000100
+#define		LmAIPRVWP		0x00000080
+#define		LmAIPWP			0x00000040
+#define		LmAIPWD			0x00000020
+#define		LmAIPWC			0x00000010
+#define		LmAIPRV2		0x00000008
+#define		LmAIPRV1		0x00000004
+#define		LmAIPRV0		0x00000002
+#define		LmAIPNRML		0x00000001
+
+#define		LmBROADCAST_MASK	(LmBROADCH | LmBROADRVCH0 | \
+					 LmBROADRVCH1)
+
+#define LmPRMSTAT1(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, 0x84)
+#define LmPRMSTAT1BYTE0			0x84
+#define LmPRMSTAT1BYTE1			0x85
+#define LmPRMSTAT1BYTE2			0x86
+#define LmPRMSTAT1BYTE3			0x87
+
+#define		LmFRMRCVDSTAT		0x80000000
+#define		LmBREAK_DET		0x04000000
+#define		LmCLOSE_DET		0x02000000
+#define		LmDONE_DET		0x01000000
+#define		LmXRDY			0x00040000
+#define 	LmSYNCSRST		0x00020000
+#define 	LmSYNC			0x00010000
+#define 	LmXHOLD			0x00008000
+#define 	LmRRDY			0x00004000
+#define 	LmHOLD			0x00002000
+#define 	LmROK			0x00001000
+#define 	LmRIP			0x00000800
+#define 	LmCRBLK			0x00000400
+#define 	LmACK			0x00000200
+#define 	LmNAK			0x00000100
+#define 	LmHARDRST		0x00000080
+#define 	LmERROR			0x00000040
+#define 	LmRERR			0x00000020
+#define 	LmPMREQP		0x00000010
+#define 	LmPMREQS		0x00000008
+#define 	LmPMACK			0x00000004
+#define 	LmPMNAK			0x00000002
+#define 	LmDMAT			0x00000001
+
+/* mode 1 */
+#define	LmMnSATAFS(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 0x7E)
+#define	LmMnXMTSIZE(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 0x93)
+
+/* mode 0 */
+#define LmMnFRMERR(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 0xB0)
+
+#define		LmACRCERR		0x00000800
+#define		LmPHYOVRN		0x00000400
+#define		LmOBOVRN		0x00000200
+#define 	LmMnZERODATA		0x00000100
+#define		LmSATAINTLK		0x00000080
+#define		LmMnCRCERR		0x00000020
+#define		LmRRDYOVRN		0x00000010
+#define		LmMISSSOAF		0x00000008
+#define		LmMISSSOF		0x00000004
+#define		LmMISSEOAF		0x00000002
+#define		LmMISSEOF		0x00000001
+
+#define LmFRMERREN(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, 0xB4)
+
+#define 	EN_LmACRCERR		0x00000800
+#define 	EN_LmPHYOVRN		0x00000400
+#define 	EN_LmOBOVRN		0x00000200
+#define 	EN_LmMnZERODATA		0x00000100
+#define 	EN_LmSATAINTLK		0x00000080
+#define 	EN_LmFRMBAD		0x00000040
+#define 	EN_LmMnCRCERR		0x00000020
+#define 	EN_LmRRDYOVRN		0x00000010
+#define 	EN_LmMISSSOAF		0x00000008
+#define 	EN_LmMISSSOF		0x00000004
+#define 	EN_LmMISSEOAF		0x00000002
+#define 	EN_LmMISSEOF		0x00000001
+
+#define 	LmFRMERREN_MASK  	(EN_LmSATAINTLK | EN_LmMnCRCERR | \
+					 EN_LmRRDYOVRN | EN_LmMISSSOF | \
+					 EN_LmMISSEOAF | EN_LmMISSEOF | \
+					 EN_LmACRCERR | LmPHYOVRN | \
+					 EN_LmOBOVRN | EN_LmMnZERODATA)
+
+#define LmHWTSTATEN(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, 0xC5)
+
+#define		EN_LmDONETO		0x80
+#define		EN_LmINVDISP		0x40
+#define		EN_LmINVDW		0x20
+#define		EN_LmDWSEVENT		0x08
+#define		EN_LmCRTTTO		0x04
+#define		EN_LmANTTTO		0x02
+#define		EN_LmBITLTTO		0x01
+
+#define		LmHWTSTATEN_MASK	(EN_LmINVDISP | EN_LmINVDW | \
+					 EN_LmDWSEVENT | EN_LmCRTTTO | \
+					 EN_LmANTTTO | EN_LmDONETO | \
+					 EN_LmBITLTTO)
+
+#define LmHWTSTAT(LinkNum) 		LmSEQ_PHY_REG(0, LinkNum, 0xC7)
+
+#define		LmDONETO		0x80
+#define		LmINVDISP		0x40
+#define		LmINVDW			0x20
+#define		LmDWSEVENT		0x08
+#define		LmCRTTTO		0x04
+#define		LmANTTTO		0x02
+#define		LmBITLTTO		0x01
+
+#define LmMnDATABUFADR(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 0xC8)
+#define		LmDATABUFADR_MASK	0x0FFF
+
+#define LmMnDATABUF(LinkNum, Mode)	LmSEQ_PHY_REG(Mode, LinkNum, 0xCA)
+
+#define	LmPRIMSTAT0EN(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, 0xE0)
+
+#define 	EN_LmUNKNOWNP 		0x20000000
+#define 	EN_LmBREAK		0x10000000
+#define 	EN_LmDONE		0x08000000
+#define 	EN_LmOPENACPT		0x04000000
+#define 	EN_LmOPENRJCT		0x02000000
+#define 	EN_LmOPENRTRY		0x01000000
+#define 	EN_LmCLOSERV1		0x00800000
+#define 	EN_LmCLOSERV0		0x00400000
+#define 	EN_LmCLOSENORM		0x00200000
+#define 	EN_LmCLOSECLAF		0x00100000
+#define 	EN_LmNOTIFYRV2		0x00080000
+#define 	EN_LmNOTIFYRV1		0x00040000
+#define 	EN_LmNOTIFYRV0		0x00020000
+#define 	EN_LmNOTIFYSPIN		0x00010000
+#define 	EN_LmBROADRV4		0x00008000
+#define 	EN_LmBROADRV3		0x00004000
+#define 	EN_LmBROADRV2		0x00002000
+#define 	EN_LmBROADRV1		0x00001000
+#define 	EN_LmBROADRV0		0x00000800
+#define 	EN_LmBROADRVCH1		0x00000400
+#define 	EN_LmBROADRVCH0		0x00000200
+#define 	EN_LmBROADCH		0x00000100
+#define 	EN_LmAIPRVWP		0x00000080
+#define 	EN_LmAIPWP		0x00000040
+#define 	EN_LmAIPWD		0x00000020
+#define 	EN_LmAIPWC		0x00000010
+#define 	EN_LmAIPRV2		0x00000008
+#define 	EN_LmAIPRV1		0x00000004
+#define 	EN_LmAIPRV0		0x00000002
+#define 	EN_LmAIPNRML		0x00000001
+
+#define		LmPRIMSTAT0EN_MASK	(EN_LmBREAK | \
+					 EN_LmDONE | EN_LmOPENACPT | \
+					 EN_LmOPENRJCT | EN_LmOPENRTRY | \
+					 EN_LmCLOSERV1 | EN_LmCLOSERV0 | \
+					 EN_LmCLOSENORM | EN_LmCLOSECLAF | \
+					 EN_LmBROADRV4 | EN_LmBROADRV3 | \
+					 EN_LmBROADRV2 | EN_LmBROADRV1 | \
+					 EN_LmBROADRV0 | EN_LmBROADRVCH1 | \
+					 EN_LmBROADRVCH0 | EN_LmBROADCH | \
+					 EN_LmAIPRVWP | EN_LmAIPWP | \
+					 EN_LmAIPWD | EN_LmAIPWC | \
+					 EN_LmAIPRV2 | EN_LmAIPRV1 | \
+					 EN_LmAIPRV0 | EN_LmAIPNRML)
+
+#define LmPRIMSTAT1EN(LinkNum)		LmSEQ_PHY_REG(0, LinkNum, 0xE4)
+
+#define		EN_LmXRDY		0x00040000
+#define		EN_LmSYNCSRST		0x00020000
+#define		EN_LmSYNC		0x00010000
+#define 	EN_LmXHOLD		0x00008000
+#define 	EN_LmRRDY		0x00004000
+#define 	EN_LmHOLD		0x00002000
+#define 	EN_LmROK		0x00001000
+#define 	EN_LmRIP		0x00000800
+#define 	EN_LmCRBLK		0x00000400
+#define 	EN_LmACK		0x00000200
+#define 	EN_LmNAK		0x00000100
+#define 	EN_LmHARDRST		0x00000080
+#define 	EN_LmERROR		0x00000040
+#define 	EN_LmRERR		0x00000020
+#define 	EN_LmPMREQP		0x00000010
+#define 	EN_LmPMREQS		0x00000008
+#define 	EN_LmPMACK		0x00000004
+#define 	EN_LmPMNAK		0x00000002
+#define 	EN_LmDMAT		0x00000001
+
+#define LmPRIMSTAT1EN_MASK		(EN_LmHARDRST | \
+					 EN_LmSYNCSRST | \
+					 EN_LmPMREQP | EN_LmPMREQS | \
+					 EN_LmPMACK | EN_LmPMNAK)
+
+#define LmSMSTATE(LinkNum) 		LmSEQ_PHY_REG(0, LinkNum, 0xE8)
+
+#define LmSMSTATEBRK(LinkNum) 		LmSEQ_PHY_REG(0, LinkNum, 0xEC)
+
+#define LmSMDBGCTL(LinkNum) 		LmSEQ_PHY_REG(0, LinkNum, 0xF0)
+
+
+/*
+ * LmSEQ CIO Bus Mode 3 Register.
+ * Mode 3: Configuration and Setup, IOP Context SCB.
+ */
+#define LmM3SATATIMER(LinkNum) 		LmSEQ_PHY_REG(3, LinkNum, 0x48)
+
+#define LmM3INTVEC0(LinkNum)		LmSEQ_PHY_REG(3, LinkNum, 0x90)
+
+#define LmM3INTVEC1(LinkNum)		LmSEQ_PHY_REG(3, LinkNum, 0x92)
+
+#define LmM3INTVEC2(LinkNum)		LmSEQ_PHY_REG(3, LinkNum, 0x94)
+
+#define LmM3INTVEC3(LinkNum)		LmSEQ_PHY_REG(3, LinkNum, 0x96)
+
+#define LmM3INTVEC4(LinkNum)		LmSEQ_PHY_REG(3, LinkNum, 0x98)
+
+#define LmM3INTVEC5(LinkNum)		LmSEQ_PHY_REG(3, LinkNum, 0x9A)
+
+#define LmM3INTVEC6(LinkNum)		LmSEQ_PHY_REG(3, LinkNum, 0x9C)
+
+#define LmM3INTVEC7(LinkNum)		LmSEQ_PHY_REG(3, LinkNum, 0x9E)
+
+#define LmM3INTVEC8(LinkNum)		LmSEQ_PHY_REG(3, LinkNum, 0xA4)
+
+#define LmM3INTVEC9(LinkNum)		LmSEQ_PHY_REG(3, LinkNum, 0xA6)
+
+#define LmM3INTVEC10(LinkNum)		LmSEQ_PHY_REG(3, LinkNum, 0xB0)
+
+#define LmM3FRMGAP(LinkNum)		LmSEQ_PHY_REG(3, LinkNum, 0xB4)
+
+#define LmBITL_TIMER(LinkNum) 		LmSEQ_PHY_REG(0, LinkNum, 0xA2)
+
+#define LmWWN(LinkNum) 			LmSEQ_PHY_REG(0, LinkNum, 0xA8)
+
+
+/*
+ * LmSEQ CIO Bus Mode 5 Registers.
+ * Mode 5: Phy/OOB Control and Status.
+ */
+#define LmSEQ_OOB_REG(phy_id, reg)	LmSEQ_PHY_REG(5, (phy_id), (reg))
+
+#define OOB_BFLTR	0x100
+
+#define		BFLTR_THR_MASK		0xF0
+#define		BFLTR_TC_MASK		0x0F
+
+#define OOB_INIT_MIN	0x102
+
+#define OOB_INIT_MAX	0x104
+
+#define OOB_INIT_NEG	0x106
+
+#define	OOB_SAS_MIN	0x108
+
+#define OOB_SAS_MAX	0x10A
+
+#define OOB_SAS_NEG	0x10C
+
+#define OOB_WAKE_MIN	0x10E
+
+#define OOB_WAKE_MAX	0x110
+
+#define OOB_WAKE_NEG	0x112
+
+#define OOB_IDLE_MAX	0x114
+
+#define OOB_BURST_MAX	0x116
+
+#define OOB_DATA_KBITS	0x126
+
+#define OOB_ALIGN_0_DATA	0x12C
+
+#define OOB_ALIGN_1_DATA	0x130
+
+#define D10_2_DATA_k		0x00
+#define SYNC_DATA_k		0x02
+#define ALIGN_1_DATA_k		0x04
+#define ALIGN_0_DATA_k		0x08
+#define BURST_DATA_k		0x10
+
+#define OOB_PHY_RESET_COUNT	0x13C
+
+#define OOB_SIG_GEN	0x140
+
+#define		START_OOB		0x80
+#define		START_DWS		0x40
+#define		ALIGN_CNT3		0x30
+#define 	ALIGN_CNT2		0x20
+#define 	ALIGN_CNT1		0x10
+#define 	ALIGN_CNT4		0x00
+#define		STOP_DWS		0x08
+#define		SEND_COMSAS		0x04
+#define		SEND_COMINIT		0x02
+#define		SEND_COMWAKE		0x01
+
+#define OOB_XMIT	0x141
+
+#define		TX_ENABLE		0x80
+#define		XMIT_OOB_BURST		0x10
+#define		XMIT_D10_2		0x08
+#define		XMIT_SYNC		0x04
+#define		XMIT_ALIGN_1		0x02
+#define		XMIT_ALIGN_0		0x01
+
+#define FUNCTION_MASK	0x142
+
+#define		SAS_MODE_DIS		0x80
+#define		SATA_MODE_DIS		0x40
+#define		SPINUP_HOLD_DIS		0x20
+#define		HOT_PLUG_DIS		0x10
+#define		SATA_PS_DIS		0x08
+#define		FUNCTION_MASK_DEFAULT	(SPINUP_HOLD_DIS | SATA_PS_DIS)
+
+#define OOB_MODE	0x143
+
+#define		SAS_MODE		0x80
+#define		SATA_MODE		0x40
+#define		SLOW_CLK		0x20
+#define		FORCE_XMIT_15		0x08
+#define		PHY_SPEED_60		0x04
+#define		PHY_SPEED_30		0x02
+#define		PHY_SPEED_15		0x01
+
+#define	CURRENT_STATUS	0x144
+
+#define		CURRENT_OOB_DONE	0x80
+#define		CURRENT_LOSS_OF_SIGNAL	0x40
+#define		CURRENT_SPINUP_HOLD	0x20
+#define		CURRENT_HOT_PLUG_CNCT	0x10
+#define		CURRENT_GTO_TIMEOUT	0x08
+#define		CURRENT_OOB_TIMEOUT	0x04
+#define		CURRENT_DEVICE_PRESENT	0x02
+#define		CURRENT_OOB_ERROR	0x01
+
+#define 	CURRENT_OOB1_ERROR	(CURRENT_HOT_PLUG_CNCT | \
+					 CURRENT_GTO_TIMEOUT)
+
+#define 	CURRENT_OOB2_ERROR	(CURRENT_HOT_PLUG_CNCT | \
+					 CURRENT_OOB_ERROR)
+
+#define		DEVICE_ADDED_W_CNT	(CURRENT_OOB_DONE | \
+					 CURRENT_HOT_PLUG_CNCT | \
+					 CURRENT_DEVICE_PRESENT)
+
+#define		DEVICE_ADDED_WO_CNT	(CURRENT_OOB_DONE | \
+					 CURRENT_DEVICE_PRESENT)
+
+#define 	DEVICE_REMOVED		CURRENT_LOSS_OF_SIGNAL
+
+#define		CURRENT_PHY_MASK	(CURRENT_OOB_DONE | \
+					 CURRENT_LOSS_OF_SIGNAL | \
+					 CURRENT_SPINUP_HOLD | \
+					 CURRENT_HOT_PLUG_CNCT | \
+					 CURRENT_GTO_TIMEOUT | \
+					 CURRENT_DEVICE_PRESENT | \
+					 CURRENT_OOB_ERROR )
+
+#define		CURRENT_ERR_MASK	(CURRENT_LOSS_OF_SIGNAL | \
+					 CURRENT_GTO_TIMEOUT | \
+					 CURRENT_OOB_TIMEOUT | \
+					 CURRENT_OOB_ERROR )
+
+#define SPEED_MASK	0x145
+
+#define		SATA_SPEED_30_DIS	0x10
+#define		SATA_SPEED_15_DIS	0x08
+#define		SAS_SPEED_60_DIS	0x04
+#define		SAS_SPEED_30_DIS	0x02
+#define		SAS_SPEED_15_DIS	0x01
+#define		SAS_SPEED_MASK_DEFAULT	0x00
+
+#define OOB_TIMER_ENABLE	0x14D
+
+#define		HOT_PLUG_EN		0x80
+#define		RCD_EN			0x40
+#define 	COMTIMER_EN		0x20
+#define		SNTT_EN			0x10
+#define		SNLT_EN			0x04
+#define		SNWT_EN			0x02
+#define		ALIGN_EN		0x01
+
+#define OOB_STATUS		0x14E
+
+#define		OOB_DONE		0x80
+#define		LOSS_OF_SIGNAL		0x40		/* ro */
+#define		SPINUP_HOLD		0x20
+#define		HOT_PLUG_CNCT		0x10		/* ro */
+#define		GTO_TIMEOUT		0x08		/* ro */
+#define		OOB_TIMEOUT		0x04		/* ro */
+#define		DEVICE_PRESENT		0x02		/* ro */
+#define		OOB_ERROR		0x01		/* ro */
+
+#define		OOB_STATUS_ERROR_MASK	(LOSS_OF_SIGNAL | GTO_TIMEOUT | \
+					 OOB_TIMEOUT | OOB_ERROR)
+
+#define OOB_STATUS_CLEAR	0x14F
+
+#define		OOB_DONE_CLR		0x80
+#define		LOSS_OF_SIGNAL_CLR 	0x40
+#define		SPINUP_HOLD_CLR		0x20
+#define		HOT_PLUG_CNCT_CLR     	0x10
+#define		GTO_TIMEOUT_CLR		0x08
+#define		OOB_TIMEOUT_CLR		0x04
+#define		OOB_ERROR_CLR		0x01
+
+#define HOT_PLUG_DELAY		0x150
+/* In 5 ms units. 20 = 100 ms. */
+#define	HOTPLUG_DELAY_TIMEOUT		20
+
+
+#define INT_ENABLE_2		0x15A
+
+#define		OOB_DONE_EN		0x80
+#define		LOSS_OF_SIGNAL_EN	0x40
+#define		SPINUP_HOLD_EN		0x20
+#define		HOT_PLUG_CNCT_EN	0x10
+#define		GTO_TIMEOUT_EN		0x08
+#define		OOB_TIMEOUT_EN		0x04
+#define		DEVICE_PRESENT_EN	0x02
+#define		OOB_ERROR_EN		0x01
+
+#define PHY_CONTROL_0		0x160
+
+#define		PHY_LOWPWREN_TX		0x80
+#define		PHY_LOWPWREN_RX		0x40
+#define		SPARE_REG_160_B5	0x20
+#define		OFFSET_CANCEL_RX	0x10
+
+/* bits 3:2 */
+#define		PHY_RXCOMCENTER_60V	0x00
+#define		PHY_RXCOMCENTER_70V	0x04
+#define		PHY_RXCOMCENTER_80V	0x08
+#define		PHY_RXCOMCENTER_90V	0x0C
+#define 	PHY_RXCOMCENTER_MASK	0x0C
+
+#define		PHY_RESET		0x02
+#define		SAS_DEFAULT_SEL		0x01
+
+#define PHY_CONTROL_1		0x161
+
+/* bits 2:0 */
+#define		SATA_PHY_DETLEVEL_50mv	0x00
+#define		SATA_PHY_DETLEVEL_75mv	0x01
+#define		SATA_PHY_DETLEVEL_100mv	0x02
+#define		SATA_PHY_DETLEVEL_125mv	0x03
+#define		SATA_PHY_DETLEVEL_150mv	0x04
+#define		SATA_PHY_DETLEVEL_175mv	0x05
+#define		SATA_PHY_DETLEVEL_200mv	0x06
+#define		SATA_PHY_DETLEVEL_225mv	0x07
+#define		SATA_PHY_DETLEVEL_MASK	0x07
+
+/* bits 5:3 */
+#define		SAS_PHY_DETLEVEL_50mv	0x00
+#define		SAS_PHY_DETLEVEL_75mv	0x08
+#define		SAS_PHY_DETLEVEL_100mv	0x10
+#define		SAS_PHY_DETLEVEL_125mv	0x11
+#define		SAS_PHY_DETLEVEL_150mv	0x20
+#define		SAS_PHY_DETLEVEL_175mv	0x21
+#define		SAS_PHY_DETLEVEL_200mv	0x30
+#define		SAS_PHY_DETLEVEL_225mv	0x31
+#define		SAS_PHY_DETLEVEL_MASK	0x38
+
+#define PHY_CONTROL_2		0x162
+
+/* bits 7:5 */
+#define 	SATA_PHY_DRV_400mv	0x00
+#define 	SATA_PHY_DRV_450mv	0x20
+#define 	SATA_PHY_DRV_500mv	0x40
+#define 	SATA_PHY_DRV_550mv	0x60
+#define 	SATA_PHY_DRV_600mv	0x80
+#define 	SATA_PHY_DRV_650mv	0xA0
+#define 	SATA_PHY_DRV_725mv	0xC0
+#define 	SATA_PHY_DRV_800mv	0xE0
+#define		SATA_PHY_DRV_MASK	0xE0
+
+/* bits 4:3 */
+#define 	SATA_PREEMP_0		0x00
+#define 	SATA_PREEMP_1		0x08
+#define 	SATA_PREEMP_2		0x10
+#define 	SATA_PREEMP_3		0x18
+#define 	SATA_PREEMP_MASK	0x18
+
+#define 	SATA_CMSH1P5		0x04
+
+/* bits 1:0 */
+#define 	SATA_SLEW_0		0x00
+#define 	SATA_SLEW_1		0x01
+#define 	SATA_SLEW_2		0x02
+#define 	SATA_SLEW_3		0x03
+#define 	SATA_SLEW_MASK		0x03
+
+#define PHY_CONTROL_3		0x163
+
+/* bits 7:5 */
+#define 	SAS_PHY_DRV_400mv	0x00
+#define 	SAS_PHY_DRV_450mv	0x20
+#define 	SAS_PHY_DRV_500mv	0x40
+#define 	SAS_PHY_DRV_550mv	0x60
+#define 	SAS_PHY_DRV_600mv	0x80
+#define 	SAS_PHY_DRV_650mv	0xA0
+#define 	SAS_PHY_DRV_725mv	0xC0
+#define 	SAS_PHY_DRV_800mv	0xE0
+#define		SAS_PHY_DRV_MASK	0xE0
+
+/* bits 4:3 */
+#define 	SAS_PREEMP_0		0x00
+#define 	SAS_PREEMP_1		0x08
+#define 	SAS_PREEMP_2		0x10
+#define 	SAS_PREEMP_3		0x18
+#define 	SAS_PREEMP_MASK		0x18
+
+#define 	SAS_CMSH1P5		0x04
+
+/* bits 1:0 */
+#define 	SAS_SLEW_0		0x00
+#define 	SAS_SLEW_1		0x01
+#define 	SAS_SLEW_2		0x02
+#define 	SAS_SLEW_3		0x03
+#define 	SAS_SLEW_MASK		0x03
+
+#define PHY_CONTROL_4		0x168
+
+#define		PHY_DONE_CAL_TX		0x80
+#define		PHY_DONE_CAL_RX		0x40
+#define		RX_TERM_LOAD_DIS	0x20
+#define		TX_TERM_LOAD_DIS	0x10
+#define		AUTO_TERM_CAL_DIS	0x08
+#define		PHY_SIGDET_FLTR_EN	0x04
+#define		OSC_FREQ		0x02
+#define		PHY_START_CAL		0x01
+
+/*
+ * HST_PCIX2 Registers, Addresss Range: (0x00-0xFC)
+ */
+#define PCIX_REG_BASE_ADR		0xB8040000
+
+#define PCIC_VENDOR_ID	0x00
+
+#define PCIC_DEVICE_ID	0x02
+
+#define PCIC_COMMAND	0x04
+
+#define		INT_DIS			0x0400
+#define		FBB_EN			0x0200		/* ro */
+#define		SERR_EN			0x0100
+#define		STEP_EN			0x0080		/* ro */
+#define		PERR_EN			0x0040
+#define		VGA_EN			0x0020		/* ro */
+#define		MWI_EN			0x0010
+#define		SPC_EN			0x0008
+#define		MST_EN			0x0004
+#define		MEM_EN			0x0002
+#define		IO_EN			0x0001
+
+#define	PCIC_STATUS	0x06
+
+#define		PERR_DET		0x8000
+#define		SERR_GEN		0x4000
+#define		MABT_DET		0x2000
+#define		TABT_DET		0x1000
+#define		TABT_GEN		0x0800
+#define		DPERR_DET		0x0100
+#define		CAP_LIST		0x0010
+#define		INT_STAT		0x0008
+
+#define	PCIC_DEVREV_ID	0x08
+
+#define	PCIC_CLASS_CODE	0x09
+
+#define	PCIC_CACHELINE_SIZE	0x0C
+
+#define	PCIC_MBAR0	0x10
+
+#define 	PCIC_MBAR0_OFFSET	0
+
+#define	PCIC_MBAR1	0x18
+
+#define 	PCIC_MBAR1_OFFSET	2
+
+#define	PCIC_IOBAR	0x20
+
+#define 	PCIC_IOBAR_OFFSET	4
+
+#define	PCIC_SUBVENDOR_ID	0x2C
+
+#define PCIC_SUBSYTEM_ID	0x2E
+
+#define PCIX_STATUS		0x44
+#define 	RCV_SCE		0x20000000
+#define 	UNEXP_SC	0x00080000
+#define 	SC_DISCARD	0x00040000
+
+#define ECC_CTRL_STAT		0x48
+#define 	UNCOR_ECCERR	0x00000008
+
+#define PCIC_PM_CSR		0x5C
+
+#define		PWR_STATE_D0		0
+#define		PWR_STATE_D1		1	/* not supported */
+#define		PWR_STATE_D2		2 	/* not supported */
+#define		PWR_STATE_D3		3
+
+#define PCIC_BASE1	0x6C	/* internal use only */
+
+#define		BASE1_RSVD		0xFFFFFFF8
+
+#define PCIC_BASEA	0x70	/* internal use only */
+
+#define		BASEA_RSVD		0xFFFFFFC0
+#define 	BASEA_START		0
+
+#define PCIC_BASEB	0x74	/* internal use only */
+
+#define		BASEB_RSVD		0xFFFFFF80
+#define		BASEB_IOMAP_MASK	0x7F
+#define 	BASEB_START		0x80
+
+#define PCIC_BASEC	0x78	/* internal use only */
+
+#define		BASEC_RSVD		0xFFFFFFFC
+#define 	BASEC_MASK		0x03
+#define 	BASEC_START		0x58
+
+#define PCIC_MBAR_KEY	0x7C	/* internal use only */
+
+#define 	MBAR_KEY_MASK		0xFFFFFFFF
+
+#define PCIC_HSTPCIX_CNTRL	0xA0
+
+#define 	REWIND_DIS		0x0800
+
+#define PCIC_MBAR0_MASK	0xA8
+#define		PCIC_MBAR0_SIZE_MASK 	0x1FFFE000
+#define		PCIC_MBAR0_SIZE_SHIFT 	13
+#define		PCIC_MBAR0_SIZE(val)	\
+		    (((val) & PCIC_MBAR0_SIZE_MASK) >> PCIC_MBAR0_SIZE_SHIFT)
+
+#define PCIC_FLASH_MBAR	0xB8
+
+#define PCIC_TP_CTRL	0xFC
+
+/*
+ * EXSI Registers, Addresss Range: (0x00-0xFC)
+ */
+#define EXSI_REG_BASE_ADR		REG_BASE_ADDR_EXSI
+
+#define	EXSICNFGR	(EXSI_REG_BASE_ADR + 0x00)
+
+#define		ASIEN			0x00400000
+#define		HCMODE			0x00200000
+#define		PCIDEF			0x00100000
+#define		COMSTOCK		0x00080000
+#define		SEEPROMEND		0x00040000
+#define		MSTTIMEN		0x00020000
+#define		XREGEX			0x00000200
+#define		NVRAMW			0x00000100
+#define		NVRAMEX			0x00000080
+#define		SRAMW			0x00000040
+#define		SRAMEX			0x00000020
+#define		FLASHW			0x00000010
+#define		FLASHEX			0x00000008
+#define		SEEPROMCFG		0x00000004
+#define		SEEPROMTYP		0x00000002
+#define		SEEPROMEX		0x00000001
+
+
+#define EXSICNTRLR	(EXSI_REG_BASE_ADR + 0x04)
+
+#define		MODINT_EN		0x00000001
+
+
+#define PMSTATR		(EXSI_REG_BASE_ADR + 0x10)
+
+#define		FLASHRST		0x00000002
+#define		FLASHRDY		0x00000001
+
+
+#define FLCNFGR		(EXSI_REG_BASE_ADR + 0x14)
+
+#define		FLWEH_MASK		0x30000000
+#define		FLWESU_MASK		0x0C000000
+#define		FLWEPW_MASK		0x03F00000
+#define		FLOEH_MASK		0x000C0000
+#define 	FLOESU_MASK		0x00030000
+#define 	FLOEPW_MASK		0x0000FC00
+#define 	FLCSH_MASK		0x00000300
+#define 	FLCSSU_MASK		0x000000C0
+#define 	FLCSPW_MASK		0x0000003F
+
+#define SRCNFGR		(EXSI_REG_BASE_ADR + 0x18)
+
+#define		SRWEH_MASK		0x30000000
+#define		SRWESU_MASK		0x0C000000
+#define		SRWEPW_MASK		0x03F00000
+
+#define		SROEH_MASK		0x000C0000
+#define 	SROESU_MASK		0x00030000
+#define 	SROEPW_MASK		0x0000FC00
+#define		SRCSH_MASK		0x00000300
+#define		SRCSSU_MASK		0x000000C0
+#define		SRCSPW_MASK		0x0000003F
+
+#define NVCNFGR		(EXSI_REG_BASE_ADR + 0x1C)
+
+#define 	NVWEH_MASK		0x30000000
+#define 	NVWESU_MASK		0x0C000000
+#define 	NVWEPW_MASK		0x03F00000
+#define 	NVOEH_MASK		0x000C0000
+#define 	NVOESU_MASK		0x00030000
+#define 	NVOEPW_MASK		0x0000FC00
+#define 	NVCSH_MASK		0x00000300
+#define 	NVCSSU_MASK		0x000000C0
+#define 	NVCSPW_MASK		0x0000003F
+
+#define XRCNFGR		(EXSI_REG_BASE_ADR + 0x20)
+
+#define 	XRWEH_MASK		0x30000000
+#define 	XRWESU_MASK		0x0C000000
+#define 	XRWEPW_MASK		0x03F00000
+#define 	XROEH_MASK		0x000C0000
+#define 	XROESU_MASK		0x00030000
+#define 	XROEPW_MASK		0x0000FC00
+#define 	XRCSH_MASK		0x00000300
+#define 	XRCSSU_MASK		0x000000C0
+#define		XRCSPW_MASK		0x0000003F
+
+#define XREGADDR	(EXSI_REG_BASE_ADR + 0x24)
+
+#define 	XRADDRINCEN		0x80000000
+#define 	XREGADD_MASK		0x007FFFFF
+
+
+#define XREGDATAR	(EXSI_REG_BASE_ADR + 0x28)
+
+#define		XREGDATA_MASK 		0x0000FFFF
+
+#define GPIOOER		(EXSI_REG_BASE_ADR + 0x40)
+
+#define GPIOODENR	(EXSI_REG_BASE_ADR + 0x44)
+
+#define GPIOINVR	(EXSI_REG_BASE_ADR + 0x48)
+
+#define GPIODATAOR	(EXSI_REG_BASE_ADR + 0x4C)
+
+#define GPIODATAIR	(EXSI_REG_BASE_ADR + 0x50)
+
+#define GPIOCNFGR	(EXSI_REG_BASE_ADR + 0x54)
+
+#define		GPIO_EXTSRC		0x00000001
+
+#define SCNTRLR		(EXSI_REG_BASE_ADR + 0xA0)
+
+#define 	SXFERDONE		0x00000100
+#define 	SXFERCNT_MASK		0x000000E0
+#define 	SCMDTYP_MASK		0x0000001C
+#define 	SXFERSTART		0x00000002
+#define 	SXFEREN			0x00000001
+
+#define	SRATER		(EXSI_REG_BASE_ADR + 0xA4)
+
+#define	SADDRR		(EXSI_REG_BASE_ADR + 0xA8)
+
+#define 	SADDR_MASK		0x0000FFFF
+
+#define SDATAOR		(EXSI_REG_BASE_ADR + 0xAC)
+
+#define	SDATAOR0	(EXSI_REG_BASE_ADR + 0xAC)
+#define SDATAOR1	(EXSI_REG_BASE_ADR + 0xAD)
+#define SDATAOR2	(EXSI_REG_BASE_ADR + 0xAE)
+#define SDATAOR3	(EXSI_REG_BASE_ADR + 0xAF)
+
+#define SDATAIR		(EXSI_REG_BASE_ADR + 0xB0)
+
+#define SDATAIR0	(EXSI_REG_BASE_ADR + 0xB0)
+#define SDATAIR1	(EXSI_REG_BASE_ADR + 0xB1)
+#define SDATAIR2	(EXSI_REG_BASE_ADR + 0xB2)
+#define SDATAIR3	(EXSI_REG_BASE_ADR + 0xB3)
+
+#define ASISTAT0R	(EXSI_REG_BASE_ADR + 0xD0)
+#define 	ASIFMTERR		0x00000400
+#define 	ASISEECHKERR		0x00000200
+#define 	ASIERR			0x00000100
+
+#define ASISTAT1R	(EXSI_REG_BASE_ADR + 0xD4)
+#define 	CHECKSUM_MASK		0x0000FFFF
+
+#define ASIERRADDR	(EXSI_REG_BASE_ADR + 0xD8)
+#define ASIERRDATAR	(EXSI_REG_BASE_ADR + 0xDC)
+#define ASIERRSTATR	(EXSI_REG_BASE_ADR + 0xE0)
+#define 	CPI2ASIBYTECNT_MASK	0x00070000
+#define 	CPI2ASIBYTEEN_MASK      0x0000F000
+#define 	CPI2ASITARGERR_MASK	0x00000F00
+#define 	CPI2ASITARGMID_MASK	0x000000F0
+#define 	CPI2ASIMSTERR_MASK	0x0000000F
+
+/*
+ * XSRAM, External SRAM (DWord and any BE pattern accessible)
+ */
+#define XSRAM_REG_BASE_ADDR             0xB8100000
+#define XSRAM_SIZE                        0x100000
+
+/*
+ * Sequencers (Central and Link) Scratch RAM page definitions.
+ */
+
+/*
+ * The Central Management Sequencer (CSEQ) Scratch Memory is a 1024
+ * byte memory.  It is dword accessible and has byte parity
+ * protection. The CSEQ accesses it in 32 byte windows, either as mode
+ * dependent or mode independent memory. Each mode has 96 bytes,
+ * (three 32 byte pages 0-2, not contiguous), leaving 128 bytes of
+ * Mode Independent memory (four 32 byte pages 3-7). Note that mode
+ * dependent scratch memory, Mode 8, page 0-3 overlaps mode
+ * independent scratch memory, pages 0-3.
+ * - 896 bytes of mode dependent scratch, 96 bytes per Modes 0-7, and
+ * 128 bytes in mode 8,
+ * - 259 bytes of mode independent scratch, common to modes 0-15.
+ *
+ * Sequencer scratch RAM is 1024 bytes.  This scratch memory is
+ * divided into mode dependent and mode independent scratch with this
+ * memory further subdivided into pages of size 32 bytes. There are 5
+ * pages (160 bytes) of mode independent scratch and 3 pages of
+ * dependent scratch memory for modes 0-7 (768 bytes). Mode 8 pages
+ * 0-2 dependent scratch overlap with pages 0-2 of mode independent
+ * scratch memory.
+ *
+ * The host accesses this scratch in a different manner from the
+ * central sequencer. The sequencer has to use CSEQ registers CSCRPAGE
+ * and CMnSCRPAGE to access the scratch memory. A flat mapping of the
+ * scratch memory is avaliable for software convenience and to prevent
+ * corruption while the sequencer is running. This memory is mapped
+ * onto addresses 800h - BFFh, total of 400h bytes.
+ *
+ * These addresses are mapped as follows:
+ *
+ *        800h-83Fh   Mode Dependent Scratch Mode 0 Pages 0-1
+ *        840h-87Fh   Mode Dependent Scratch Mode 1 Pages 0-1
+ *        880h-8BFh   Mode Dependent Scratch Mode 2 Pages 0-1
+ *        8C0h-8FFh   Mode Dependent Scratch Mode 3 Pages 0-1
+ *        900h-93Fh   Mode Dependent Scratch Mode 4 Pages 0-1
+ *        940h-97Fh   Mode Dependent Scratch Mode 5 Pages 0-1
+ *        980h-9BFh   Mode Dependent Scratch Mode 6 Pages 0-1
+ *        9C0h-9FFh   Mode Dependent Scratch Mode 7 Pages 0-1
+ *        A00h-A5Fh   Mode Dependent Scratch Mode 8 Pages 0-2
+ *                    Mode Independent Scratch Pages 0-2
+ *        A60h-A7Fh   Mode Dependent Scratch Mode 8 Page 3
+ *                    Mode Independent Scratch Page 3
+ *        A80h-AFFh   Mode Independent Scratch Pages 4-7
+ *        B00h-B1Fh   Mode Dependent Scratch Mode 0 Page 2
+ *        B20h-B3Fh   Mode Dependent Scratch Mode 1 Page 2
+ *        B40h-B5Fh   Mode Dependent Scratch Mode 2 Page 2
+ *        B60h-B7Fh   Mode Dependent Scratch Mode 3 Page 2
+ *        B80h-B9Fh   Mode Dependent Scratch Mode 4 Page 2
+ *        BA0h-BBFh   Mode Dependent Scratch Mode 5 Page 2
+ *        BC0h-BDFh   Mode Dependent Scratch Mode 6 Page 2
+ *        BE0h-BFFh   Mode Dependent Scratch Mode 7 Page 2
+ */
+
+/* General macros */
+#define CSEQ_PAGE_SIZE			32  /* Scratch page size (in bytes) */
+
+/* All macros start with offsets from base + 0x800 (CMAPPEDSCR).
+ * Mode dependent scratch page 0, mode 0.
+ * For modes 1-7 you have to do arithmetic. */
+#define CSEQ_LRM_SAVE_SINDEX		(CMAPPEDSCR + 0x0000)
+#define CSEQ_LRM_SAVE_SCBPTR		(CMAPPEDSCR + 0x0002)
+#define CSEQ_Q_LINK_HEAD		(CMAPPEDSCR + 0x0004)
+#define CSEQ_Q_LINK_TAIL		(CMAPPEDSCR + 0x0006)
+#define CSEQ_LRM_SAVE_SCRPAGE		(CMAPPEDSCR + 0x0008)
+
+/* Mode dependent scratch page 0 mode 8 macros. */
+#define CSEQ_RET_ADDR			(CMAPPEDSCR + 0x0200)
+#define CSEQ_RET_SCBPTR			(CMAPPEDSCR + 0x0202)
+#define CSEQ_SAVE_SCBPTR		(CMAPPEDSCR + 0x0204)
+#define CSEQ_EMPTY_TRANS_CTX		(CMAPPEDSCR + 0x0206)
+#define CSEQ_RESP_LEN			(CMAPPEDSCR + 0x0208)
+#define CSEQ_TMF_SCBPTR			(CMAPPEDSCR + 0x020A)
+#define CSEQ_GLOBAL_PREV_SCB		(CMAPPEDSCR + 0x020C)
+#define CSEQ_GLOBAL_HEAD		(CMAPPEDSCR + 0x020E)
+#define CSEQ_CLEAR_LU_HEAD		(CMAPPEDSCR + 0x0210)
+#define CSEQ_TMF_OPCODE			(CMAPPEDSCR + 0x0212)
+#define CSEQ_SCRATCH_FLAGS		(CMAPPEDSCR + 0x0213)
+#define CSEQ_HSB_SITE                   (CMAPPEDSCR + 0x021A)
+#define CSEQ_FIRST_INV_SCB_SITE		(CMAPPEDSCR + 0x021C)
+#define CSEQ_FIRST_INV_DDB_SITE		(CMAPPEDSCR + 0x021E)
+
+/* Mode dependent scratch page 1 mode 8 macros. */
+#define CSEQ_LUN_TO_CLEAR		(CMAPPEDSCR + 0x0220)
+#define CSEQ_LUN_TO_CHECK		(CMAPPEDSCR + 0x0228)
+
+/* Mode dependent scratch page 2 mode 8 macros */
+#define CSEQ_HQ_NEW_POINTER		(CMAPPEDSCR + 0x0240)
+#define CSEQ_HQ_DONE_BASE		(CMAPPEDSCR + 0x0248)
+#define CSEQ_HQ_DONE_POINTER		(CMAPPEDSCR + 0x0250)
+#define CSEQ_HQ_DONE_PASS		(CMAPPEDSCR + 0x0254)
+
+/* Mode independent scratch page 4 macros. */
+#define CSEQ_Q_EXE_HEAD			(CMAPPEDSCR + 0x0280)
+#define CSEQ_Q_EXE_TAIL			(CMAPPEDSCR + 0x0282)
+#define CSEQ_Q_DONE_HEAD                (CMAPPEDSCR + 0x0284)
+#define CSEQ_Q_DONE_TAIL                (CMAPPEDSCR + 0x0286)
+#define CSEQ_Q_SEND_HEAD		(CMAPPEDSCR + 0x0288)
+#define CSEQ_Q_SEND_TAIL		(CMAPPEDSCR + 0x028A)
+#define CSEQ_Q_DMA2CHIM_HEAD		(CMAPPEDSCR + 0x028C)
+#define CSEQ_Q_DMA2CHIM_TAIL		(CMAPPEDSCR + 0x028E)
+#define CSEQ_Q_COPY_HEAD		(CMAPPEDSCR + 0x0290)
+#define CSEQ_Q_COPY_TAIL		(CMAPPEDSCR + 0x0292)
+#define CSEQ_REG0			(CMAPPEDSCR + 0x0294)
+#define CSEQ_REG1			(CMAPPEDSCR + 0x0296)
+#define CSEQ_REG2			(CMAPPEDSCR + 0x0298)
+#define CSEQ_LINK_CTL_Q_MAP		(CMAPPEDSCR + 0x029C)
+#define CSEQ_MAX_CSEQ_MODE		(CMAPPEDSCR + 0x029D)
+#define CSEQ_FREE_LIST_HACK_COUNT	(CMAPPEDSCR + 0x029E)
+
+/* Mode independent scratch page 5 macros. */
+#define CSEQ_EST_NEXUS_REQ_QUEUE	(CMAPPEDSCR + 0x02A0)
+#define CSEQ_EST_NEXUS_REQ_COUNT	(CMAPPEDSCR + 0x02A8)
+#define CSEQ_Q_EST_NEXUS_HEAD		(CMAPPEDSCR + 0x02B0)
+#define CSEQ_Q_EST_NEXUS_TAIL		(CMAPPEDSCR + 0x02B2)
+#define CSEQ_NEED_EST_NEXUS_SCB		(CMAPPEDSCR + 0x02B4)
+#define CSEQ_EST_NEXUS_REQ_HEAD		(CMAPPEDSCR + 0x02B6)
+#define CSEQ_EST_NEXUS_REQ_TAIL		(CMAPPEDSCR + 0x02B7)
+#define CSEQ_EST_NEXUS_SCB_OFFSET	(CMAPPEDSCR + 0x02B8)
+
+/* Mode independent scratch page 6 macros. */
+#define CSEQ_INT_ROUT_RET_ADDR0		(CMAPPEDSCR + 0x02C0)
+#define CSEQ_INT_ROUT_RET_ADDR1		(CMAPPEDSCR + 0x02C2)
+#define CSEQ_INT_ROUT_SCBPTR		(CMAPPEDSCR + 0x02C4)
+#define CSEQ_INT_ROUT_MODE		(CMAPPEDSCR + 0x02C6)
+#define CSEQ_ISR_SCRATCH_FLAGS		(CMAPPEDSCR + 0x02C7)
+#define CSEQ_ISR_SAVE_SINDEX		(CMAPPEDSCR + 0x02C8)
+#define CSEQ_ISR_SAVE_DINDEX		(CMAPPEDSCR + 0x02CA)
+#define CSEQ_SLS_SAVE_ACCUM		(CMAPPEDSCR + 0x02CC)
+#define CSEQ_SLS_SAVE_SINDEX		(CMAPPEDSCR + 0x02CE)
+#define CSEQ_Q_MONIRTT_HEAD		(CMAPPEDSCR + 0x02D0)
+#define CSEQ_Q_MONIRTT_TAIL		(CMAPPEDSCR + 0x02D2)
+#define CSEQ_FREE_SCB_MASK		(CMAPPEDSCR + 0x02D5)
+#define CSEQ_BUILTIN_FREE_SCB_HEAD	(CMAPPEDSCR + 0x02D6)
+#define CSEQ_BUILTIN_FREE_SCB_TAIL	(CMAPPEDSCR + 0x02D8)
+#define CSEQ_EXTENDED_FREE_SCB_HEAD	(CMAPPEDSCR + 0x02DA)
+#define CSEQ_EXTENDED_FREE_SCB_TAIL	(CMAPPEDSCR + 0x02DC)
+
+/* Mode independent scratch page 7 macros. */
+#define CSEQ_EMPTY_REQ_QUEUE		(CMAPPEDSCR + 0x02E0)
+#define CSEQ_EMPTY_REQ_COUNT		(CMAPPEDSCR + 0x02E8)
+#define CSEQ_Q_EMPTY_HEAD		(CMAPPEDSCR + 0x02F0)
+#define CSEQ_Q_EMPTY_TAIL		(CMAPPEDSCR + 0x02F2)
+#define CSEQ_NEED_EMPTY_SCB		(CMAPPEDSCR + 0x02F4)
+#define CSEQ_EMPTY_REQ_HEAD		(CMAPPEDSCR + 0x02F6)
+#define CSEQ_EMPTY_REQ_TAIL		(CMAPPEDSCR + 0x02F7)
+#define CSEQ_EMPTY_SCB_OFFSET		(CMAPPEDSCR + 0x02F8)
+#define CSEQ_PRIMITIVE_DATA		(CMAPPEDSCR + 0x02FA)
+#define CSEQ_TIMEOUT_CONST		(CMAPPEDSCR + 0x02FC)
+
+/***************************************************************************
+* Link m Sequencer scratch RAM is 512 bytes.
+* This scratch memory is divided into mode dependent and mode
+* independent scratch with this memory further subdivided into
+* pages of size 32 bytes. There are 4 pages (128 bytes) of
+* mode independent scratch and 4 pages of dependent scratch
+* memory for modes 0-2 (384 bytes).
+*
+* The host accesses this scratch in a different manner from the
+* link sequencer. The sequencer has to use LSEQ registers
+* LmSCRPAGE and LmMnSCRPAGE to access the scratch memory. A flat
+* mapping of the scratch memory is avaliable for software
+* convenience and to prevent corruption while the sequencer is
+* running. This memory is mapped onto addresses 800h - 9FFh.
+*
+* These addresses are mapped as follows:
+*
+*        800h-85Fh   Mode Dependent Scratch Mode 0 Pages 0-2
+*        860h-87Fh   Mode Dependent Scratch Mode 0 Page 3
+*                    Mode Dependent Scratch Mode 5 Page 0
+*        880h-8DFh   Mode Dependent Scratch Mode 1 Pages 0-2
+*        8E0h-8FFh   Mode Dependent Scratch Mode 1 Page 3
+*                    Mode Dependent Scratch Mode 5 Page 1
+*        900h-95Fh   Mode Dependent Scratch Mode 2 Pages 0-2
+*        960h-97Fh   Mode Dependent Scratch Mode 2 Page 3
+*                    Mode Dependent Scratch Mode 5 Page 2
+*        980h-9DFh   Mode Independent Scratch Pages 0-3
+*        9E0h-9FFh   Mode Independent Scratch Page 3
+*                    Mode Dependent Scratch Mode 5 Page 3
+*
+****************************************************************************/
+/* General macros */
+#define LSEQ_MODE_SCRATCH_SIZE		0x80 /* Size of scratch RAM per mode */
+#define LSEQ_PAGE_SIZE			0x20 /* Scratch page size (in bytes) */
+#define LSEQ_MODE5_PAGE0_OFFSET 	0x60
+
+/* Common mode dependent scratch page 0 macros for modes 0,1,2, and 5 */
+/* Indexed using LSEQ_MODE_SCRATCH_SIZE * mode, for modes 0,1,2. */
+#define LmSEQ_RET_ADDR(LinkNum)		(LmSCRATCH(LinkNum) + 0x0000)
+#define LmSEQ_REG0_MODE(LinkNum)	(LmSCRATCH(LinkNum) + 0x0002)
+#define LmSEQ_MODE_FLAGS(LinkNum)	(LmSCRATCH(LinkNum) + 0x0004)
+
+/* Mode flag macros (byte 0) */
+#define		SAS_SAVECTX_OCCURRED		0x80
+#define		SAS_OOBSVC_OCCURRED		0x40
+#define		SAS_OOB_DEVICE_PRESENT		0x20
+#define		SAS_CFGHDR_OCCURRED		0x10
+#define		SAS_RCV_INTS_ARE_DISABLED	0x08
+#define		SAS_OOB_HOT_PLUG_CNCT		0x04
+#define		SAS_AWAIT_OPEN_CONNECTION	0x02
+#define		SAS_CFGCMPLT_OCCURRED		0x01
+
+/* Mode flag macros (byte 1) */
+#define		SAS_RLSSCB_OCCURRED		0x80
+#define		SAS_FORCED_HEADER_MISS		0x40
+
+#define LmSEQ_RET_ADDR2(LinkNum)	(LmSCRATCH(LinkNum) + 0x0006)
+#define LmSEQ_RET_ADDR1(LinkNum)	(LmSCRATCH(LinkNum) + 0x0008)
+#define LmSEQ_OPCODE_TO_CSEQ(LinkNum)	(LmSCRATCH(LinkNum) + 0x000B)
+#define LmSEQ_DATA_TO_CSEQ(LinkNum)	(LmSCRATCH(LinkNum) + 0x000C)
+
+/* Mode dependent scratch page 0 macros for mode 0 (non-common) */
+/* Absolute offsets */
+#define LmSEQ_FIRST_INV_DDB_SITE(LinkNum)	(LmSCRATCH(LinkNum) + 0x000E)
+#define LmSEQ_EMPTY_TRANS_CTX(LinkNum)		(LmSCRATCH(LinkNum) + 0x0010)
+#define LmSEQ_RESP_LEN(LinkNum)			(LmSCRATCH(LinkNum) + 0x0012)
+#define LmSEQ_FIRST_INV_SCB_SITE(LinkNum)	(LmSCRATCH(LinkNum) + 0x0014)
+#define LmSEQ_INTEN_SAVE(LinkNum)		(LmSCRATCH(LinkNum) + 0x0016)
+#define LmSEQ_LINK_RST_FRM_LEN(LinkNum)		(LmSCRATCH(LinkNum) + 0x001A)
+#define LmSEQ_LINK_RST_PROTOCOL(LinkNum)	(LmSCRATCH(LinkNum) + 0x001B)
+#define LmSEQ_RESP_STATUS(LinkNum)		(LmSCRATCH(LinkNum) + 0x001C)
+#define LmSEQ_LAST_LOADED_SGE(LinkNum)		(LmSCRATCH(LinkNum) + 0x001D)
+#define LmSEQ_SAVE_SCBPTR(LinkNum)		(LmSCRATCH(LinkNum) + 0x001E)
+
+/* Mode dependent scratch page 0 macros for mode 1 (non-common) */
+/* Absolute offsets */
+#define LmSEQ_Q_XMIT_HEAD(LinkNum)		(LmSCRATCH(LinkNum) + 0x008E)
+#define LmSEQ_M1_EMPTY_TRANS_CTX(LinkNum)	(LmSCRATCH(LinkNum) + 0x0090)
+#define LmSEQ_INI_CONN_TAG(LinkNum)		(LmSCRATCH(LinkNum) + 0x0092)
+#define LmSEQ_FAILED_OPEN_STATUS(LinkNum)	(LmSCRATCH(LinkNum) + 0x009A)
+#define LmSEQ_XMIT_REQUEST_TYPE(LinkNum)	(LmSCRATCH(LinkNum) + 0x009B)
+#define LmSEQ_M1_RESP_STATUS(LinkNum)		(LmSCRATCH(LinkNum) + 0x009C)
+#define LmSEQ_M1_LAST_LOADED_SGE(LinkNum)	(LmSCRATCH(LinkNum) + 0x009D)
+#define LmSEQ_M1_SAVE_SCBPTR(LinkNum)		(LmSCRATCH(LinkNum) + 0x009E)
+
+/* Mode dependent scratch page 0 macros for mode 2 (non-common) */
+#define LmSEQ_PORT_COUNTER(LinkNum)		(LmSCRATCH(LinkNum) + 0x010E)
+#define LmSEQ_PM_TABLE_PTR(LinkNum)		(LmSCRATCH(LinkNum) + 0x0110)
+#define LmSEQ_SATA_INTERLOCK_TMR_SAVE(LinkNum)	(LmSCRATCH(LinkNum) + 0x0112)
+#define LmSEQ_IP_BITL(LinkNum)			(LmSCRATCH(LinkNum) + 0x0114)
+#define LmSEQ_COPY_SMP_CONN_TAG(LinkNum)	(LmSCRATCH(LinkNum) + 0x0116)
+#define LmSEQ_P0M2_OFFS1AH(LinkNum)		(LmSCRATCH(LinkNum) + 0x011A)
+
+/* Mode dependent scratch page 0 macros for modes 4/5 (non-common) */
+/* Absolute offsets */
+#define LmSEQ_SAVED_OOB_STATUS(LinkNum)		(LmSCRATCH(LinkNum) + 0x006E)
+#define LmSEQ_SAVED_OOB_MODE(LinkNum)		(LmSCRATCH(LinkNum) + 0x006F)
+#define LmSEQ_Q_LINK_HEAD(LinkNum)		(LmSCRATCH(LinkNum) + 0x0070)
+#define LmSEQ_LINK_RST_ERR(LinkNum)		(LmSCRATCH(LinkNum) + 0x0072)
+#define LmSEQ_SAVED_OOB_SIGNALS(LinkNum)	(LmSCRATCH(LinkNum) + 0x0073)
+#define LmSEQ_SAS_RESET_MODE(LinkNum)		(LmSCRATCH(LinkNum) + 0x0074)
+#define LmSEQ_LINK_RESET_RETRY_COUNT(LinkNum)	(LmSCRATCH(LinkNum) + 0x0075)
+#define LmSEQ_NUM_LINK_RESET_RETRIES(LinkNum)	(LmSCRATCH(LinkNum) + 0x0076)
+#define LmSEQ_OOB_INT_ENABLES(LinkNum)		(LmSCRATCH(LinkNum) + 0x007A)
+#define LmSEQ_NOTIFY_TIMER_TIMEOUT(LinkNum)	(LmSCRATCH(LinkNum) + 0x007C)
+#define LmSEQ_NOTIFY_TIMER_DOWN_COUNT(LinkNum)	(LmSCRATCH(LinkNum) + 0x007E)
+
+/* Mode dependent scratch page 1, mode 0 and mode 1 */
+#define LmSEQ_SG_LIST_PTR_ADDR0(LinkNum)        (LmSCRATCH(LinkNum) + 0x0020)
+#define LmSEQ_SG_LIST_PTR_ADDR1(LinkNum)        (LmSCRATCH(LinkNum) + 0x0030)
+#define LmSEQ_M1_SG_LIST_PTR_ADDR0(LinkNum)     (LmSCRATCH(LinkNum) + 0x00A0)
+#define LmSEQ_M1_SG_LIST_PTR_ADDR1(LinkNum)     (LmSCRATCH(LinkNum) + 0x00B0)
+
+/* Mode dependent scratch page 1 macros for mode 2 */
+/* Absolute offsets */
+#define LmSEQ_INVALID_DWORD_COUNT(LinkNum)	(LmSCRATCH(LinkNum) + 0x0120)
+#define LmSEQ_DISPARITY_ERROR_COUNT(LinkNum) 	(LmSCRATCH(LinkNum) + 0x0124)
+#define LmSEQ_LOSS_OF_SYNC_COUNT(LinkNum)	(LmSCRATCH(LinkNum) + 0x0128)
+
+/* Mode dependent scratch page 1 macros for mode 4/5 */
+#define LmSEQ_FRAME_TYPE_MASK(LinkNum)	      (LmSCRATCH(LinkNum) + 0x00E0)
+#define LmSEQ_HASHED_DEST_ADDR_MASK(LinkNum)  (LmSCRATCH(LinkNum) + 0x00E1)
+#define LmSEQ_HASHED_SRC_ADDR_MASK_PRINT(LinkNum) (LmSCRATCH(LinkNum) + 0x00E4)
+#define LmSEQ_HASHED_SRC_ADDR_MASK(LinkNum)   (LmSCRATCH(LinkNum) + 0x00E5)
+#define LmSEQ_NUM_FILL_BYTES_MASK(LinkNum)    (LmSCRATCH(LinkNum) + 0x00EB)
+#define LmSEQ_TAG_MASK(LinkNum)		      (LmSCRATCH(LinkNum) + 0x00F0)
+#define LmSEQ_TARGET_PORT_XFER_TAG(LinkNum)   (LmSCRATCH(LinkNum) + 0x00F2)
+#define LmSEQ_DATA_OFFSET(LinkNum)	      (LmSCRATCH(LinkNum) + 0x00F4)
+
+/* Mode dependent scratch page 2 macros for mode 0 */
+/* Absolute offsets */
+#define LmSEQ_SMP_RCV_TIMER_TERM_TS(LinkNum)	(LmSCRATCH(LinkNum) + 0x0040)
+#define LmSEQ_DEVICE_BITS(LinkNum)		(LmSCRATCH(LinkNum) + 0x005B)
+#define LmSEQ_SDB_DDB(LinkNum)			(LmSCRATCH(LinkNum) + 0x005C)
+#define LmSEQ_SDB_NUM_TAGS(LinkNum)		(LmSCRATCH(LinkNum) + 0x005E)
+#define LmSEQ_SDB_CURR_TAG(LinkNum)		(LmSCRATCH(LinkNum) + 0x005F)
+
+/* Mode dependent scratch page 2 macros for mode 1 */
+/* Absolute offsets */
+/* byte 0 bits 1-0 are domain select. */
+#define LmSEQ_TX_ID_ADDR_FRAME(LinkNum)		(LmSCRATCH(LinkNum) + 0x00C0)
+#define LmSEQ_OPEN_TIMER_TERM_TS(LinkNum)	(LmSCRATCH(LinkNum) + 0x00C8)
+#define LmSEQ_SRST_AS_TIMER_TERM_TS(LinkNum)	(LmSCRATCH(LinkNum) + 0x00CC)
+#define LmSEQ_LAST_LOADED_SG_EL(LinkNum)	(LmSCRATCH(LinkNum) + 0x00D4)
+
+/* Mode dependent scratch page 2 macros for mode 2 */
+/* Absolute offsets */
+#define LmSEQ_STP_SHUTDOWN_TIMER_TERM_TS(LinkNum) (LmSCRATCH(LinkNum) + 0x0140)
+#define LmSEQ_CLOSE_TIMER_TERM_TS(LinkNum)	(LmSCRATCH(LinkNum) + 0x0144)
+#define LmSEQ_BREAK_TIMER_TERM_TS(LinkNum)	(LmSCRATCH(LinkNum) + 0x0148)
+#define LmSEQ_DWS_RESET_TIMER_TERM_TS(LinkNum)	(LmSCRATCH(LinkNum) + 0x014C)
+#define LmSEQ_SATA_INTERLOCK_TIMER_TERM_TS(LinkNum) \
+						(LmSCRATCH(LinkNum) + 0x0150)
+#define LmSEQ_MCTL_TIMER_TERM_TS(LinkNum)	(LmSCRATCH(LinkNum) + 0x0154)
+
+/* Mode dependent scratch page 2 macros for mode 5 */
+#define LmSEQ_COMINIT_TIMER_TERM_TS(LinkNum)	(LmSCRATCH(LinkNum) + 0x0160)
+#define LmSEQ_RCV_ID_TIMER_TERM_TS(LinkNum)	(LmSCRATCH(LinkNum) + 0x0164)
+#define LmSEQ_RCV_FIS_TIMER_TERM_TS(LinkNum)	(LmSCRATCH(LinkNum) + 0x0168)
+#define LmSEQ_DEV_PRES_TIMER_TERM_TS(LinkNum)	(LmSCRATCH(LinkNum) + 0x016C)
+
+/* Mode dependent scratch page 3 macros for modes 0 and 1 */
+/* None defined */
+
+/* Mode dependent scratch page 3 macros for modes 2 and 5 */
+/* None defined */
+
+/* Mode Independent Scratch page 0 macros. */
+#define LmSEQ_Q_TGTXFR_HEAD(LinkNum)	(LmSCRATCH(LinkNum) + 0x0180)
+#define LmSEQ_Q_TGTXFR_TAIL(LinkNum)	(LmSCRATCH(LinkNum) + 0x0182)
+#define LmSEQ_LINK_NUMBER(LinkNum)	(LmSCRATCH(LinkNum) + 0x0186)
+#define LmSEQ_SCRATCH_FLAGS(LinkNum)	(LmSCRATCH(LinkNum) + 0x0187)
+/*
+ * Currently only bit 0, SAS_DWSAQD, is used.
+ */
+#define		SAS_DWSAQD			0x01  /*
+						       * DWSSTATUS: DWSAQD
+						       * bit las read in ISR.
+						       */
+#define  LmSEQ_CONNECTION_STATE(LinkNum) (LmSCRATCH(LinkNum) + 0x0188)
+/* Connection states (byte 0) */
+#define		SAS_WE_OPENED_CS		0x01
+#define		SAS_DEVICE_OPENED_CS		0x02
+#define		SAS_WE_SENT_DONE_CS		0x04
+#define		SAS_DEVICE_SENT_DONE_CS		0x08
+#define		SAS_WE_SENT_CLOSE_CS		0x10
+#define		SAS_DEVICE_SENT_CLOSE_CS	0x20
+#define		SAS_WE_SENT_BREAK_CS		0x40
+#define		SAS_DEVICE_SENT_BREAK_CS	0x80
+/* Connection states (byte 1) */
+#define		SAS_OPN_TIMEOUT_OR_OPN_RJCT_CS	0x01
+#define		SAS_AIP_RECEIVED_CS		0x02
+#define		SAS_CREDIT_TIMEOUT_OCCURRED_CS	0x04
+#define		SAS_ACKNAK_TIMEOUT_OCCURRED_CS	0x08
+#define		SAS_SMPRSP_TIMEOUT_OCCURRED_CS	0x10
+#define		SAS_DONE_TIMEOUT_OCCURRED_CS	0x20
+/* Connection states (byte 2) */
+#define		SAS_SMP_RESPONSE_RECEIVED_CS	0x01
+#define		SAS_INTLK_TIMEOUT_OCCURRED_CS	0x02
+#define		SAS_DEVICE_SENT_DMAT_CS		0x04
+#define		SAS_DEVICE_SENT_SYNCSRST_CS	0x08
+#define		SAS_CLEARING_AFFILIATION_CS	0x20
+#define		SAS_RXTASK_ACTIVE_CS		0x40
+#define		SAS_TXTASK_ACTIVE_CS		0x80
+/* Connection states (byte 3) */
+#define		SAS_PHY_LOSS_OF_SIGNAL_CS	0x01
+#define		SAS_DWS_TIMER_EXPIRED_CS	0x02
+#define		SAS_LINK_RESET_NOT_COMPLETE_CS	0x04
+#define		SAS_PHY_DISABLED_CS		0x08
+#define		SAS_LINK_CTL_TASK_ACTIVE_CS	0x10
+#define		SAS_PHY_EVENT_TASK_ACTIVE_CS	0x20
+#define		SAS_DEVICE_SENT_ID_FRAME_CS	0x40
+#define		SAS_DEVICE_SENT_REG_FIS_CS	0x40
+#define		SAS_DEVICE_SENT_HARD_RESET_CS	0x80
+#define  	SAS_PHY_IS_DOWN_FLAGS	(SAS_PHY_LOSS_OF_SIGNAL_CS|\
+					 SAS_DWS_TIMER_EXPIRED_CS |\
+					 SAS_LINK_RESET_NOT_COMPLETE_CS|\
+					 SAS_PHY_DISABLED_CS)
+
+#define		SAS_LINK_CTL_PHY_EVENT_FLAGS   (SAS_LINK_CTL_TASK_ACTIVE_CS |\
+						SAS_PHY_EVENT_TASK_ACTIVE_CS |\
+						SAS_DEVICE_SENT_ID_FRAME_CS  |\
+						SAS_DEVICE_SENT_HARD_RESET_CS)
+
+#define LmSEQ_CONCTL(LinkNum)		(LmSCRATCH(LinkNum) + 0x018C)
+#define LmSEQ_CONSTAT(LinkNum)		(LmSCRATCH(LinkNum) + 0x018E)
+#define LmSEQ_CONNECTION_MODES(LinkNum)	(LmSCRATCH(LinkNum) + 0x018F)
+#define LmSEQ_REG1_ISR(LinkNum)		(LmSCRATCH(LinkNum) + 0x0192)
+#define LmSEQ_REG2_ISR(LinkNum)		(LmSCRATCH(LinkNum) + 0x0194)
+#define LmSEQ_REG3_ISR(LinkNum)		(LmSCRATCH(LinkNum) + 0x0196)
+#define LmSEQ_REG0_ISR(LinkNum)		(LmSCRATCH(LinkNum) + 0x0198)
+
+/* Mode independent scratch page 1 macros. */
+#define LmSEQ_EST_NEXUS_SCBPTR0(LinkNum)	(LmSCRATCH(LinkNum) + 0x01A0)
+#define LmSEQ_EST_NEXUS_SCBPTR1(LinkNum)	(LmSCRATCH(LinkNum) + 0x01A2)
+#define LmSEQ_EST_NEXUS_SCBPTR2(LinkNum)	(LmSCRATCH(LinkNum) + 0x01A4)
+#define LmSEQ_EST_NEXUS_SCBPTR3(LinkNum)	(LmSCRATCH(LinkNum) + 0x01A6)
+#define LmSEQ_EST_NEXUS_SCB_OPCODE0(LinkNum)	(LmSCRATCH(LinkNum) + 0x01A8)
+#define LmSEQ_EST_NEXUS_SCB_OPCODE1(LinkNum)	(LmSCRATCH(LinkNum) + 0x01A9)
+#define LmSEQ_EST_NEXUS_SCB_OPCODE2(LinkNum)	(LmSCRATCH(LinkNum) + 0x01AA)
+#define LmSEQ_EST_NEXUS_SCB_OPCODE3(LinkNum)	(LmSCRATCH(LinkNum) + 0x01AB)
+#define LmSEQ_EST_NEXUS_SCB_HEAD(LinkNum)	(LmSCRATCH(LinkNum) + 0x01AC)
+#define LmSEQ_EST_NEXUS_SCB_TAIL(LinkNum)	(LmSCRATCH(LinkNum) + 0x01AD)
+#define LmSEQ_EST_NEXUS_BUF_AVAIL(LinkNum)	(LmSCRATCH(LinkNum) + 0x01AE)
+#define LmSEQ_TIMEOUT_CONST(LinkNum)		(LmSCRATCH(LinkNum) + 0x01B8)
+#define LmSEQ_ISR_SAVE_SINDEX(LinkNum)	        (LmSCRATCH(LinkNum) + 0x01BC)
+#define LmSEQ_ISR_SAVE_DINDEX(LinkNum)	        (LmSCRATCH(LinkNum) + 0x01BE)
+
+/* Mode independent scratch page 2 macros. */
+#define LmSEQ_EMPTY_SCB_PTR0(LinkNum)	(LmSCRATCH(LinkNum) + 0x01C0)
+#define LmSEQ_EMPTY_SCB_PTR1(LinkNum)	(LmSCRATCH(LinkNum) + 0x01C2)
+#define LmSEQ_EMPTY_SCB_PTR2(LinkNum)	(LmSCRATCH(LinkNum) + 0x01C4)
+#define LmSEQ_EMPTY_SCB_PTR3(LinkNum)	(LmSCRATCH(LinkNum) + 0x01C6)
+#define LmSEQ_EMPTY_SCB_OPCD0(LinkNum)	(LmSCRATCH(LinkNum) + 0x01C8)
+#define LmSEQ_EMPTY_SCB_OPCD1(LinkNum)	(LmSCRATCH(LinkNum) + 0x01C9)
+#define LmSEQ_EMPTY_SCB_OPCD2(LinkNum)	(LmSCRATCH(LinkNum) + 0x01CA)
+#define LmSEQ_EMPTY_SCB_OPCD3(LinkNum)	(LmSCRATCH(LinkNum) + 0x01CB)
+#define LmSEQ_EMPTY_SCB_HEAD(LinkNum)	(LmSCRATCH(LinkNum) + 0x01CC)
+#define LmSEQ_EMPTY_SCB_TAIL(LinkNum)	(LmSCRATCH(LinkNum) + 0x01CD)
+#define LmSEQ_EMPTY_BUFS_AVAIL(LinkNum)	(LmSCRATCH(LinkNum) + 0x01CE)
+#define LmSEQ_ATA_SCR_REGS(LinkNum)	(LmSCRATCH(LinkNum) + 0x01D4)
+
+/* Mode independent scratch page 3 macros. */
+#define LmSEQ_DEV_PRES_TMR_TOUT_CONST(LinkNum)	(LmSCRATCH(LinkNum) + 0x01E0)
+#define LmSEQ_SATA_INTERLOCK_TIMEOUT(LinkNum)	(LmSCRATCH(LinkNum) + 0x01E4)
+#define LmSEQ_STP_SHUTDOWN_TIMEOUT(LinkNum)	(LmSCRATCH(LinkNum) + 0x01E8)
+#define LmSEQ_SRST_ASSERT_TIMEOUT(LinkNum)	(LmSCRATCH(LinkNum) + 0x01EC)
+#define LmSEQ_RCV_FIS_TIMEOUT(LinkNum)		(LmSCRATCH(LinkNum) + 0x01F0)
+#define LmSEQ_ONE_MILLISEC_TIMEOUT(LinkNum)	(LmSCRATCH(LinkNum) + 0x01F4)
+#define LmSEQ_TEN_MS_COMINIT_TIMEOUT(LinkNum)	(LmSCRATCH(LinkNum) + 0x01F8)
+#define LmSEQ_SMP_RCV_TIMEOUT(LinkNum)		(LmSCRATCH(LinkNum) + 0x01FC)
+
+#endif
diff -urN oldtree/drivers/scsi/aic94xx/aic94xx_sas.h newtree/drivers/scsi/aic94xx/aic94xx_sas.h
--- oldtree/drivers/scsi/aic94xx/aic94xx_sas.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/aic94xx_sas.h	2006-02-13 19:20:00.586423152 -0500
@@ -0,0 +1,788 @@
+/*
+ * Aic94xx SAS/SATA driver SAS definitions and hardware interface header file.
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/aic94xx/aic94xx_sas.h#62 $
+ */
+
+#ifndef _AIC94XX_SAS_H_
+#define _AIC94XX_SAS_H_
+
+#include <scsi/sas/sas_class.h>
+#include <scsi/sas/sas_discover.h>
+
+/* ---------- DDBs ---------- */
+/* DDBs are device descriptor blocks which describe a device in the
+ * domain that this sequencer can maintain low-level connections for
+ * us.  They are be 64 bytes.
+ */
+
+struct asd_ddb_ssp_smp_target_port {
+	u8     conn_type;	  /* byte 0 */
+#define DDB_TP_CONN_TYPE 0x81	  /* Initiator port and addr frame type 0x01 */
+
+	u8     conn_rate;
+	__be16 init_conn_tag;
+	u8     dest_sas_addr[8];  /* bytes 4-11 */
+
+	__le16 send_queue_head;
+	u8     sq_suspended;
+	u8     ddb_type;	  /* DDB_TYPE_TARGET */
+#define DDB_TYPE_UNUSED    0xFF
+#define DDB_TYPE_TARGET    0xFE
+#define DDB_TYPE_INITIATOR 0xFD
+#define DDB_TYPE_PM_PORT   0xFC
+
+	__le16 _r_a;
+	__be16 awt_def;
+
+	u8     compat_features;	  /* byte 20 */
+	u8     pathway_blocked_count;
+	__be16 arb_wait_time;
+	__be32 more_compat_features; /* byte 24 */
+
+	u8     conn_mask;
+	u8     flags;	  /* concurrent conn:2,2 and open:0(1) */
+#define CONCURRENT_CONN_SUPP 0x04
+#define OPEN_REQUIRED        0x01
+
+	u16    _r_b;
+	__le16 exec_queue_tail;
+	__le16 send_queue_tail;
+	__le16 sister_ddb;
+
+	__le16 _r_c;
+
+	u8     max_concurrent_conn;
+	u8     num_concurrent_conn;
+	u8     num_contexts;
+
+	u8     _r_d;
+
+	__le16 active_task_count;
+
+	u8     _r_e[9];
+
+	u8     itnl_reason;	  /* I_T nexus loss reason */
+
+	__le16 _r_f;
+
+	__le16 itnl_timeout;
+#define ITNL_TIMEOUT_CONST 0x7D0 /* 2 seconds */
+
+	__le32 itnl_timestamp;
+} __attribute__ ((packed));
+
+struct asd_ddb_stp_sata_target_port {
+	u8     conn_type;	  /* byte 0 */
+	u8     conn_rate;
+	__be16 init_conn_tag;
+	u8     dest_sas_addr[8];  /* bytes 4-11 */
+
+	__le16 send_queue_head;
+	u8     sq_suspended;
+	u8     ddb_type;	  /* DDB_TYPE_TARGET */
+
+	__le16 _r_a;
+
+	__be16 awt_def;
+	u8     compat_features;	  /* byte 20 */
+	u8     pathway_blocked_count;
+	__be16 arb_wait_time;
+	__be32 more_compat_features; /* byte 24 */
+
+	u8     conn_mask;
+	u8     flags;	  /* concurrent conn:2,2 and open:0(1) */
+#define SATA_MULTIPORT     0x80
+#define SUPPORTS_AFFIL     0x40
+#define STP_AFFIL_POL      0x20
+
+	u8     _r_b;
+	u8     flags2;		  /* STP close policy:0 */
+#define STP_CL_POL_NO_TX    0x00
+#define STP_CL_POL_BTW_CMDS 0x01
+
+	__le16 exec_queue_tail;
+	__le16 send_queue_tail;
+	__le16 sister_ddb;
+	__le16 ata_cmd_scbptr;
+	__le32 sata_tag_alloc_mask;
+	__le16 active_task_count;
+	__le16 _r_c;
+	__le32 sata_sactive;
+	u8     num_sata_tags;
+	u8     sata_status;
+	u8     sata_ending_status;
+	u8     itnl_reason;	  /* I_T nexus loss reason */
+	__le16 ncq_data_scb_ptr;
+	__le16 itnl_timeout;
+	__le32 itnl_timestamp;
+} __attribute__ ((packed));
+
+/* This struct asd_ddb_init_port, describes the device descriptor block
+ * of an initiator port (when the sequencer is operating in target mode).
+ * Bytes [0,11] and [20,27] are from the OPEN address frame.
+ * The sequencer allocates an initiator port DDB entry.
+ */
+struct asd_ddb_init_port {
+	u8     conn_type;	  /* byte 0 */
+	u8     conn_rate;
+	__be16 init_conn_tag;     /* BE */
+	u8     dest_sas_addr[8];
+	__le16 send_queue_head;   /* LE, byte 12 */
+	u8     sq_suspended;
+	u8     ddb_type;	  /* DDB_TYPE_INITIATOR */
+	__le16 _r_a;
+	__be16 awt_def;		  /* BE */
+	u8     compat_features;
+	u8     pathway_blocked_count;
+	__be16 arb_wait_time;	  /* BE */
+	__be32 more_compat_features; /* BE */
+	u8     conn_mask;
+	u8     flags;		  /* == 5 */
+	u16    _r_b;
+	__le16 exec_queue_tail;	  /* execution queue tail */
+	__le16 send_queue_tail;
+	__le16 sister_ddb;
+	__le16 init_resp_timeout; /* initiator response timeout */
+	__le32 _r_c;
+	__le16 active_tasks;	  /* active task count */
+	__le16 init_list;	  /* initiator list link pointer */
+	__le32 _r_d;
+	u8     max_conn_to[3]; /* from Conn-Disc mode page, in us, LE */
+	u8     itnl_reason;	  /* I_T nexus loss reason */
+	__le16 bus_inact_to; /* from Conn-Disc mode page, in 100 us, LE */
+	__le16 itnl_to;		  /* from the Protocol Specific Port Ctrl MP */
+	__le32 itnl_timestamp;
+} __attribute__ ((packed));
+
+/* This struct asd_ddb_sata_tag, describes a look-up table to be used
+ * by the sequencers.  SATA II, IDENTIFY DEVICE data, word 76, bit 8:
+ * NCQ support.  This table is used by the sequencers to find the
+ * corresponding SCB, given a SATA II tag value.
+ */
+struct asd_ddb_sata_tag {
+	__le16 scb_pointer[32];
+} __attribute__ ((packed));
+
+/* This struct asd_ddb_sata_pm_table, describes a port number to
+ * connection handle look-up table.  SATA targets attached to a port
+ * multiplier require a 4-bit port number value.  There is one DDB
+ * entry of this type for each SATA port multiplier (sister DDB).
+ * Given a SATA PM port number, this table gives us the SATA PM Port
+ * DDB of the SATA port multiplier port (i.e. the SATA target
+ * discovered on the port).
+ */
+struct asd_ddb_sata_pm_table {
+	__le16 ddb_pointer[16];
+	__le16 _r_a[16];
+} __attribute__ ((packed));
+
+/* This struct asd_ddb_sata_pm_port, describes the SATA port multiplier
+ * port format DDB.
+ */
+struct asd_ddb_sata_pm_port {
+	u8     _r_a[15];
+	u8     ddb_type;
+	u8     _r_b[13];
+	u8     pm_port_flags;
+#define PM_PORT_MASK  0xF0
+#define PM_PORT_SET   0x02
+	u8     _r_c[6];
+	__le16 sister_ddb;
+	__le16 ata_cmd_scbptr;
+	__le32 sata_tag_alloc_mask;
+	__le16 active_task_count;
+	__le16 parent_ddb;
+	__le32 sata_sactive;
+	u8     num_sata_tags;
+	u8     sata_status;
+	u8     sata_ending_status;
+	u8     _r_d[9];
+} __attribute__ ((packed));
+
+/* This struct asd_ddb_seq_shared, describes a DDB shared by the
+ * central and link sequencers.  port_map_by_links is indexed phy
+ * number [0,7]; each byte is a bit mask of all the phys that are in
+ * the same port as the indexed phy.
+ */
+struct asd_ddb_seq_shared {
+	__le16 q_free_ddb_head;
+	__le16 q_free_ddb_tail;
+	__le16 q_free_ddb_cnt;
+	__le16 q_used_ddb_head;
+	__le16 q_used_ddb_tail;
+	__le16 shared_mem_lock;
+	__le16 smp_conn_tag;
+	__le16 est_nexus_buf_cnt;
+	__le16 est_nexus_buf_thresh;
+	u32    _r_a;
+	u8     settable_max_contexts;
+	u8     _r_b[23];
+	u8     conn_not_active;
+	u8     phy_is_up;
+	u8     _r_c[8];
+	u8     port_map_by_links[8];
+} __attribute__ ((packed));
+
+/* ---------- SG Element ---------- */
+
+/* This struct sg_el, describes the hardware scatter gather buffer
+ * element.  All entries are little endian.  In an SCB, there are 2 of
+ * this, plus one more, called a link element of this indicating a
+ * sublist if needed.
+ *
+ * A link element has only the bus address set and the flags (DS) bit
+ * valid.  The bus address points to the start of the sublist.
+ *
+ * If a sublist is needed, then that sublist should also include the 2
+ * sg_el embedded in the SCB, in which case next_sg_offset is 32,
+ * since sizeof(sg_el) = 16; EOS should be 1 and EOL 0 in this case.
+ */
+struct sg_el {
+	__le64 bus_addr;
+	__le32 size;
+	__le16 _r;
+	u8     next_sg_offs;
+	u8     flags;
+#define ASD_SG_EL_DS_MASK   0x30
+#define ASD_SG_EL_DS_OCM    0x10
+#define ASD_SG_EL_DS_HM     0x00
+#define ASD_SG_EL_LIST_MASK 0xC0
+#define ASD_SG_EL_LIST_EOL  0x40
+#define ASD_SG_EL_LIST_EOS  0x80
+} __attribute__ ((packed));
+
+/* ---------- SCBs ---------- */
+
+/* An SCB (sequencer control block) is comprised of a common header
+ * and a task part, for a total of 128 bytes.  All fields are in LE
+ * order, unless otherwise noted.
+ */
+
+/* This struct scb_header, defines the SCB header format.
+ */
+struct scb_header {
+	__le64 next_scb;
+	__le16 index;		  /* transaction context */
+	u8     opcode;
+} __attribute__ ((packed));
+
+/* SCB opcodes: Execution queue
+ */
+#define INITIATE_SSP_TASK       0x00
+#define INITIATE_LONG_SSP_TASK  0x01
+#define INITIATE_BIDIR_SSP_TASK 0x02
+#define ABORT_TASK              0x03
+#define INITIATE_SSP_TMF        0x04
+#define SSP_TARG_GET_DATA       0x05
+#define SSP_TARG_GET_DATA_GOOD  0x06
+#define SSP_TARG_SEND_RESP      0x07
+#define QUERY_SSP_TASK          0x08
+#define INITIATE_ATA_TASK       0x09
+#define INITIATE_ATAPI_TASK     0x0a
+#define CONTROL_ATA_DEV         0x0b
+#define INITIATE_SMP_TASK       0x0c
+#define SMP_TARG_SEND_RESP      0x0f
+
+/* SCB opcodes: Send Queue
+ */
+#define SSP_TARG_SEND_DATA      0x40
+#define SSP_TARG_SEND_DATA_GOOD 0x41
+
+/* SCB opcodes: Link Queue
+ */
+#define CONTROL_PHY             0x80
+#define SEND_PRIMITIVE          0x81
+#define INITIATE_LINK_ADM_TASK  0x82
+
+/* SCB opcodes: other
+ */
+#define EMPTY_SCB               0xc0
+#define INITIATE_SEQ_ADM_TASK   0xc1
+#define EST_ICL_TARG_WINDOW     0xc2
+#define COPY_MEM                0xc3
+#define CLEAR_NEXUS             0xc4
+#define DELIVER_FREE_I_PORT_DDB 0xc5
+#define INITIATE_DDB_ADM_TASK   0xc6
+#define ESTABLISH_NEXUS_ESCB    0xd0
+
+#define LUN_SIZE                8
+
+/* See SAS spec, task IU
+ */
+struct ssp_task_iu {
+	u8     lun[LUN_SIZE];	  /* BE */
+	u16    _r_a;
+	u8     tmf;
+	u8     _r_b;
+	__be16 tag;		  /* BE */
+	u8     _r_c[14];
+} __attribute__ ((packed));
+
+/* See SAS spec, command IU
+ */
+struct ssp_command_iu {
+	u8     lun[LUN_SIZE];
+	u8     _r_a;
+	u8     efb_prio_attr;	  /* enable first burst, task prio & attr */
+#define EFB_MASK        0x80
+#define TASK_PRIO_MASK	0x78
+#define TASK_ATTR_MASK  0x07
+
+	u8    _r_b;
+	u8     add_cdb_len;	  /* in dwords, since bit 0,1 are reserved */
+	union {
+		u8     cdb[16];
+		struct {
+			__le64 long_cdb_addr;	  /* bus address, LE */
+			__le32 long_cdb_size;	  /* LE */
+			u8     _r_c[3];
+			u8     eol_ds;		  /* eol:6,6, ds:5,4 */
+		} long_cdb;	  /* sequencer extension */
+	};
+} __attribute__ ((packed));
+
+struct xfer_rdy_iu {
+	__be32 requested_offset;  /* BE */
+	__be32 write_data_len;	  /* BE */
+	__be32 _r_a;
+} __attribute__ ((packed));
+
+/* ---------- SCB tasks ---------- */
+
+/* This is both ssp_task and long_ssp_task
+ */
+struct initiate_ssp_task {
+	u8     proto_conn_rate;	  /* proto:6,4, conn_rate:3,0 */
+	__le32 total_xfer_len;
+	struct ssp_frame_hdr  ssp_frame;
+	struct ssp_command_iu ssp_cmd;
+	__le16 sister_scb;	  /* 0xFFFF */
+	__le16 conn_handle;	  /* index to DDB for the intended target */
+	u8     data_dir;	  /* :1,0 */
+#define DATA_DIR_NONE   0x00
+#define DATA_DIR_IN     0x01
+#define DATA_DIR_OUT    0x02
+#define DATA_DIR_BYRECIPIENT 0x03
+
+	u8     _r_a;
+	u8     retry_count;
+	u8     _r_b[5];
+	struct sg_el sg_element[3]; /* 2 real and 1 link */
+} __attribute__ ((packed));
+
+/* This defines both ata_task and atapi_task.
+ * ata: C bit of FIS should be 1,
+ * atapi: C bit of FIS should be 1, and command register should be 0xA0,
+ * to indicate a packet command.
+ */
+struct initiate_ata_task {
+	u8     proto_conn_rate;
+	__le32 total_xfer_len;
+	struct host_to_dev_fis fis;
+	__le32 data_offs;
+	u8     atapi_packet[16];
+	u8     _r_a[12];
+	__le16 sister_scb;
+	__le16 conn_handle;
+	u8     ata_flags;	  /* CSMI:6,6, DTM:4,4, QT:3,3, data dir:1,0 */
+#define CSMI_TASK           0x40
+#define DATA_XFER_MODE_DMA  0x10
+#define ATA_Q_TYPE_MASK     0x08
+#define	ATA_Q_TYPE_UNTAGGED 0x00
+#define ATA_Q_TYPE_NCQ      0x08
+
+	u8     _r_b;
+	u8     retry_count;
+	u8     _r_c;
+	u8     flags;
+#define STP_AFFIL_POLICY   0x20
+#define SET_AFFIL_POLICY   0x10
+#define RET_PARTIAL_SGLIST 0x02
+
+	u8     _r_d[3];
+	struct sg_el sg_element[3];
+} __attribute__ ((packed));
+
+struct initiate_smp_task {
+	u8     proto_conn_rate;
+	u8     _r_a[40];
+	struct sg_el smp_req;
+	__le16 sister_scb;
+	__le16 conn_handle;
+	u8     _r_c[8];
+	struct sg_el smp_resp;
+	u8     _r_d[32];
+} __attribute__ ((packed));
+
+struct control_phy {
+	u8     phy_id;
+	u8     sub_func;
+#define DISABLE_PHY            0x00
+#define ENABLE_PHY             0x01
+#define RELEASE_SPINUP_HOLD    0x02
+#define ENABLE_PHY_NO_SAS_OOB  0x03
+#define ENABLE_PHY_NO_SATA_OOB 0x04
+#define PHY_NO_OP              0x05
+#define EXECUTE_HARD_RESET     0x81
+
+	u8     func_mask;
+	u8     speed_mask;
+	u8     hot_plug_delay;
+	u8     port_type;
+	u8     flags;
+#define DEV_PRES_TIMER_OVERRIDE_ENABLE 0x01
+#define DISABLE_PHY_IF_OOB_FAILS       0x02
+
+	__le32 timeout_override;
+	u8     link_reset_retries;
+	u8     _r_a[47];
+	__le16 conn_handle;
+	u8     _r_b[56];
+} __attribute__ ((packed));
+
+struct control_ata_dev {
+	u8     proto_conn_rate;
+	__le32 _r_a;
+	struct host_to_dev_fis fis;
+	u8     _r_b[32];
+	__le16 sister_scb;
+	__le16 conn_handle;
+	u8     ata_flags;	  /* 0 */
+	u8     _r_c[55];
+} __attribute__ ((packed));
+
+struct empty_scb {
+	u8     num_valid;
+	__le32 _r_a;
+#define ASD_EDBS_PER_SCB 7
+/* header+data+CRC+DMA suffix data */
+#define ASD_EDB_SIZE (24+1024+4+16)
+	struct sg_el eb[ASD_EDBS_PER_SCB];
+#define ELEMENT_NOT_VALID  0xC0
+} __attribute__ ((packed));
+
+struct initiate_link_adm {
+	u8     phy_id;
+	u8     sub_func;
+#define GET_LINK_ERROR_COUNT      0x00
+#define RESET_LINK_ERROR_COUNT    0x01
+#define ENABLE_NOTIFY_SPINUP_INTS 0x02
+
+	u8     _r_a[57];
+	__le16 conn_handle;
+	u8     _r_b[56];
+} __attribute__ ((packed));
+
+struct copy_memory {
+	u8     _r_a;
+	__le16 xfer_len;
+	__le16 _r_b;
+	__le64 src_busaddr;
+	u8     src_ds;		  /* See definition of sg_el */
+	u8     _r_c[45];
+	__le16 conn_handle;
+	__le64 _r_d;
+	__le64 dest_busaddr;
+	u8     dest_ds;		  /* See definition of sg_el */
+	u8     _r_e[39];
+} __attribute__ ((packed));
+
+struct abort_task {
+	u8     proto_conn_rate;
+	__le32 _r_a;
+	struct ssp_frame_hdr ssp_frame;
+	struct ssp_task_iu ssp_task;
+	__le16 sister_scb;
+	__le16 conn_handle;
+	u8     flags;	  /* ovrd_itnl_timer:3,3, suspend_data_trans:2,2 */
+#define SUSPEND_DATA_TRANS 0x04
+
+	u8     _r_b;
+	u8     retry_count;
+	u8     _r_c[5];
+	__le16 index;  /* Transaction context of task to be queried */
+	__le16 itnl_to;
+	u8     _r_d[44];
+} __attribute__ ((packed));
+
+struct clear_nexus {
+	u8     nexus;
+#define NEXUS_ADAPTER  0x00
+#define NEXUS_PORT     0x01
+#define NEXUS_I_T      0x02
+#define NEXUS_I_T_L    0x03
+#define NEXUS_TAG      0x04
+#define NEXUS_TRANS_CX 0x05
+#define NEXUS_SATA_TAG 0x06
+#define NEXUS_T_L      0x07
+#define NEXUS_L        0x08
+#define NEXUS_T_TAG    0x09
+
+	__le32 _r_a;
+	u8     flags;
+#define SUSPEND_TX     0x80
+#define RESUME_TX      0x40
+#define SEND_Q         0x04
+#define EXEC_Q         0x02
+#define NOTINQ         0x01
+
+	u8     _r_b[3];
+	u8     conn_mask;
+	u8     _r_c[19];
+	struct ssp_task_iu ssp_task; /* LUN and TAG */
+	__le16 _r_d;
+	__le16 conn_handle;
+	__le64 _r_e;
+	__le16 index;  /* Transaction context of task to be cleared */
+	__le16 context;		  /* Clear nexus context */
+	u8     _r_f[44];
+} __attribute__ ((packed));
+
+struct initiate_ssp_tmf {
+	u8     proto_conn_rate;
+	__le32 _r_a;
+	struct ssp_frame_hdr ssp_frame;
+	struct ssp_task_iu ssp_task;
+	__le16 sister_scb;
+	__le16 conn_handle;
+	u8     flags;	  /* itnl override and suspend data tx */
+#define OVERRIDE_ITNL_TIMER  8
+
+	u8     _r_b;
+	u8     retry_count;
+	u8     _r_c[5];
+	__le16 index;  /* Transaction context of task to be queried */
+	__le16 itnl_to;
+	u8     _r_d[44];
+} __attribute__ ((packed));
+
+/* Transmits an arbitrary primitive on the link.
+ * Used for NOTIFY and BROADCAST.
+ */
+struct send_prim {
+	u8     phy_id;
+	u8     wait_transmit; 	  /* :0,0 */
+	u8     xmit_flags;
+#define XMTPSIZE_MASK      0xF0
+#define XMTPSIZE_SINGLE    0x10
+#define XMTPSIZE_REPEATED  0x20
+#define XMTPSIZE_CONT      0x20
+#define XMTPSIZE_TRIPLE    0x30
+#define XMTPSIZE_REDUNDANT 0x60
+#define XMTPSIZE_INF       0
+
+#define XMTCONTEN          0x04
+#define XMTPFRM            0x02	  /* Transmit at the next frame boundary */
+#define XMTPIMM            0x01	  /* Transmit immediately */
+
+	__le16 _r_a;
+	u8     prim[4];		  /* K, D0, D1, D2 */
+	u8     _r_b[50];
+	__le16 conn_handle;
+	u8     _r_c[56];
+} __attribute__ ((packed));
+
+/* This describes both SSP Target Get Data and SSP Target Get Data And
+ * Send Good Response SCBs.  Used when the sequencer is operating in
+ * target mode...
+ */
+struct ssp_targ_get_data {
+	u8     proto_conn_rate;
+	__le32 total_xfer_len;
+	struct ssp_frame_hdr ssp_frame;
+	struct xfer_rdy_iu  xfer_rdy;
+	u8     lun[LUN_SIZE];
+	__le64 _r_a;
+	__le16 sister_scb;
+	__le16 conn_handle;
+	u8     data_dir;	  /* 01b */
+	u8     _r_b;
+	u8     retry_count;
+	u8     _r_c[5];
+	struct sg_el sg_element[3];
+} __attribute__ ((packed));
+
+/* ---------- The actual SCB struct ---------- */
+
+struct scb {
+	struct scb_header header;
+	union {
+		struct initiate_ssp_task ssp_task;
+		struct initiate_ata_task ata_task;
+		struct initiate_smp_task smp_task;
+		struct control_phy       control_phy;
+		struct control_ata_dev   control_ata_dev;
+		struct empty_scb         escb;
+		struct initiate_link_adm link_adm;
+		struct copy_memory       cp_mem;
+		struct abort_task        abort_task;
+		struct clear_nexus       clear_nexus;
+		struct initiate_ssp_tmf  ssp_tmf;
+	};
+} __attribute__ ((packed));
+
+/* ---------- Done List ---------- */
+/* The done list entry opcode field is defined below.
+ * The mnemonic encoding and meaning is as follows:
+ * TC - Task Complete, status was received and acknowledged
+ * TF - Task Failed, indicates an error prior to receiving acknowledgment
+ *   for the command:
+ *   - no conn,
+ *   - NACK or R_ERR received in response to this command,
+ *   - credit blocked or not available, or in the case of SMP request,
+ *   - no SMP response was received.
+ *   In these four cases it is known that the target didn't receive the
+ *   command.
+ * TI - Task Interrupted, error after the command was acknowledged.  It is
+ *   known that the command was received by the target.
+ * TU - Task Unacked, command was transmitted but neither ACK (R_OK) nor NAK
+ *   (R_ERR) was received due to loss of signal, broken connection, loss of
+ *   dword sync or other reason.  The application client should send the
+ *   appropriate task query.
+ * TA - Task Aborted, see TF.
+ * _RESP - The completion includes an empty buffer containing status.
+ * TO - Timeout.
+ */
+#define TC_NO_ERROR             0x00
+#define TC_UNDERRUN             0x01
+#define TC_OVERRUN              0x02
+#define TF_OPEN_TO              0x03
+#define TF_OPEN_REJECT          0x04
+#define TI_BREAK                0x05
+#define TI_PROTO_ERR            0x06
+#define TC_SSP_RESP             0x07
+#define TI_PHY_DOWN             0x08
+#define TF_PHY_DOWN             0x09
+#define TC_LINK_ADM_RESP        0x0a
+#define TC_CSMI                 0x0b
+#define TC_ATA_RESP             0x0c
+#define TU_PHY_DOWN             0x0d
+#define TU_BREAK                0x0e
+#define TI_SATA_TO              0x0f
+#define TI_NAK                  0x10
+#define TC_CONTROL_PHY          0x11
+#define TF_BREAK                0x12
+#define TC_RESUME               0x13
+#define TI_ACK_NAK_TO           0x14
+#define TF_SMPRSP_TO            0x15
+#define TF_SMP_XMIT_RCV_ERR     0x16
+#define TC_PARTIAL_SG_LIST      0x17
+#define TU_ACK_NAK_TO           0x18
+#define TU_SATA_TO              0x19
+#define TF_NAK_RECV             0x1a
+#define TA_I_T_NEXUS_LOSS       0x1b
+#define TC_ATA_R_ERR_RECV       0x1c
+#define TF_TMF_NO_CTX           0x1d
+#define TA_ON_REQ               0x1e
+#define TF_TMF_NO_TAG           0x1f
+#define TF_TMF_TAG_FREE         0x20
+#define TF_TMF_TASK_DONE        0x21
+#define TF_TMF_NO_CONN_HANDLE   0x22
+#define TC_TASK_CLEARED         0x23
+#define TI_SYNCS_RECV           0x24
+#define TU_SYNCS_RECV           0x25
+#define TF_IRTT_TO              0x26
+#define TF_NO_SMP_CONN          0x27
+#define TF_IU_SHORT             0x28
+#define TF_DATA_OFFS_ERR        0x29
+#define TF_INV_CONN_HANDLE      0x2a
+#define TF_REQUESTED_N_PENDING  0x2b
+
+/* 0xc1 - 0xc7: empty buffer received,
+   0xd1 - 0xd7: establish nexus empty buffer received
+*/
+/* This is the ESCB mask */
+#define ESCB_RECVD              0xC0
+
+
+/* This struct done_list_struct defines the done list entry.
+ * All fields are LE.
+ */
+struct done_list_struct {
+	__le16 index;		  /* aka transaction context */
+	u8     opcode;
+	u8     status_block[4];
+	u8     toggle;		  /* bit 0 */
+#define DL_TOGGLE_MASK     0x01
+} __attribute__ ((packed));
+
+/* ---------- PHYS ---------- */
+
+struct asd_phy {
+	struct sas_phy        sas_phy;
+	struct asd_phy_desc   *phy_desc; /* hw profile */
+
+	struct sas_identify_frame *identify_frame;
+	struct asd_dma_tok  *id_frm_tok;
+
+	u8         frame_rcvd[ASD_EDB_SIZE];
+};
+
+
+#define ASD_SCB_SIZE sizeof(struct scb)
+#define ASD_DDB_SIZE sizeof(struct asd_ddb_ssp_smp_target_port)
+
+/* Define this to 0 if you do not want NOTIFY (ENABLE SPINIP) sent.
+ * Default: 0x10 (it's a mask)
+ */
+#define ASD_NOTIFY_ENABLE_SPINUP  0x10
+
+/* If enabled, set this to the interval between transmission
+ * of NOTIFY (ENABLE SPINUP). In units of 200 us.
+ */
+#define ASD_NOTIFY_TIMEOUT        2500
+
+/* Initial delay after OOB, before we transmit NOTIFY (ENABLE SPINUP).
+ * If 0, transmit immediately. In milliseconds.
+ */
+#define ASD_NOTIFY_DOWN_COUNT     0
+
+/* Device present timer timeout constant, 10 ms. */
+#define ASD_DEV_PRESENT_TIMEOUT   0x2710
+
+#define ASD_SATA_INTERLOCK_TIMEOUT 0
+
+/* How long to wait before shutting down an STP connection, unless
+ * an STP target sent frame(s). 50 usec.
+ * IGNORED by the sequencer (i.e. value 0 always).
+ */
+#define ASD_STP_SHUTDOWN_TIMEOUT  0x0
+
+/* ATA soft reset timer timeout. 5 usec. */
+#define ASD_SRST_ASSERT_TIMEOUT   0x05
+
+/* 31 sec */
+#define ASD_RCV_FIS_TIMEOUT       0x01D905C0
+
+#define ASD_ONE_MILLISEC_TIMEOUT  0x03e8
+
+/* COMINIT timer */
+#define ASD_TEN_MILLISEC_TIMEOUT  0x2710
+#define ASD_COMINIT_TIMEOUT ASD_TEN_MILLISEC_TIMEOUT
+
+/* 1 sec */
+#define ASD_SMP_RCV_TIMEOUT       0x000F4240
+
+#endif
diff -urN oldtree/drivers/scsi/aic94xx/aic94xx_scb.c newtree/drivers/scsi/aic94xx/aic94xx_scb.c
--- oldtree/drivers/scsi/aic94xx/aic94xx_scb.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/aic94xx_scb.c	2006-02-13 19:20:00.589422696 -0500
@@ -0,0 +1,726 @@
+/*
+ * Aic94xx SAS/SATA driver SCB management.
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/aic94xx/aic94xx_scb.c#71 $
+ */
+
+#include <linux/pci.h>
+
+#include "aic94xx.h"
+#include "aic94xx_reg.h"
+#include "aic94xx_hwi.h"
+#include "aic94xx_seq.h"
+
+#include "aic94xx_dump.h"
+
+/* ---------- EMPTY SCB ---------- */
+
+#define DL_PHY_MASK      7
+#define BYTES_DMAED      0
+#define PRIMITIVE_RECVD  0x08
+#define PHY_EVENT        0x10
+#define LINK_RESET_ERROR 0x18
+#define TIMER_EVENT      0x20
+#define REQ_TASK_ABORT   0xF0
+#define REQ_DEVICE_RESET 0xF1
+#define SIGNAL_NCQ_ERROR 0xF2
+#define CLEAR_NCQ_ERROR  0xF3
+
+#define PHY_EVENTS_STATUS (CURRENT_LOSS_OF_SIGNAL | CURRENT_OOB_DONE   \
+			   | CURRENT_SPINUP_HOLD | CURRENT_GTO_TIMEOUT \
+			   | CURRENT_OOB_ERROR)
+
+static inline void get_lrate_mode(struct asd_phy *phy, u8 oob_mode)
+{
+	switch (oob_mode & 7) {
+	case PHY_SPEED_60:
+		phy->sas_phy.linkrate = PHY_LINKRATE_6;
+		break;
+	case PHY_SPEED_30:
+		phy->sas_phy.linkrate = PHY_LINKRATE_3;
+		break;
+	case PHY_SPEED_15:
+		phy->sas_phy.linkrate = PHY_LINKRATE_1_5;
+		break;
+	}
+	if (oob_mode & SAS_MODE)
+		phy->sas_phy.oob_mode = SAS_OOB_MODE;
+	else if (oob_mode & SATA_MODE)
+		phy->sas_phy.oob_mode = SATA_OOB_MODE;
+}
+
+static inline void asd_phy_event_tasklet(struct asd_ascb *ascb,
+					 struct done_list_struct *dl)
+{
+	struct asd_ha_struct *asd_ha = ascb->ha;
+	struct sas_ha_struct *sas_ha = &asd_ha->sas_ha;
+	int phy_id = dl->status_block[0] & DL_PHY_MASK;
+	struct asd_phy *phy = &asd_ha->phys[phy_id];
+
+	u8 oob_status = dl->status_block[1] & PHY_EVENTS_STATUS;
+	u8 oob_mode   = dl->status_block[2];
+
+	switch (oob_status) {
+	case CURRENT_LOSS_OF_SIGNAL:
+		/* directly attached device was removed */
+		ASD_DPRINTK("phy%d: device unplugged\n", phy_id);
+		asd_turn_led(asd_ha, phy_id, 0);
+		sas_phy_disconnected(&phy->sas_phy);
+		sas_ha->notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL);
+		break;
+	case CURRENT_OOB_DONE:
+		/* hot plugged device */
+		asd_turn_led(asd_ha, phy_id, 1);
+		get_lrate_mode(phy, oob_mode);
+		ASD_DPRINTK("phy%d device plugged: lrate:0x%x, proto:0x%x\n",
+			    phy_id, phy->sas_phy.linkrate, phy->sas_phy.iproto);
+		sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE);
+		break;
+	case CURRENT_SPINUP_HOLD:
+		/* hot plug SATA, no COMWAKE sent */
+		asd_turn_led(asd_ha, phy_id, 1);
+		sas_ha->notify_phy_event(&phy->sas_phy, PHYE_SPINUP_HOLD);
+		break;
+	case CURRENT_GTO_TIMEOUT:
+	case CURRENT_OOB_ERROR:
+		ASD_DPRINTK("phy%d error while OOB: oob status:0x%x\n", phy_id,
+			    dl->status_block[1]);
+		asd_turn_led(asd_ha, phy_id, 0);
+		sas_phy_disconnected(&phy->sas_phy);
+		sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_ERROR);
+		break;
+	}
+}
+
+/* If phys are enabled sparsely, this will do the right thing. */
+static inline unsigned ord_phy(struct asd_ha_struct *asd_ha,
+			       struct asd_phy *phy)
+{
+	u8 enabled_mask = asd_ha->hw_prof.enabled_phys;
+	int i, k = 0;
+
+	for_each_phy(enabled_mask, enabled_mask, i) {
+		if (&asd_ha->phys[i] == phy)
+			return k;
+		k++;
+	}
+}
+
+/**
+ * asd_get_attached_sas_addr -- extract/generate attached SAS address
+ * phy: pointer to asd_phy
+ * sas_addr: pointer to buffer where the SAS address is to be written
+ *
+ * This function extracts the SAS address from an IDENTIFY frame
+ * received.  If OOB is SATA, then a SAS address is generated from the
+ * HA tables.
+ *
+ * LOCKING: the frame_rcvd_lock needs to be held since this parses the frame
+ * buffer.
+ */
+static inline void asd_get_attached_sas_addr(struct asd_phy *phy, u8 *sas_addr)
+{
+	if (phy->sas_phy.frame_rcvd[0] == 0x34
+	    && phy->sas_phy.oob_mode == SATA_OOB_MODE) {
+		struct asd_ha_struct *asd_ha = phy->sas_phy.ha->lldd_ha;
+		/* FIS device-to-host */
+		u64 addr = be64_to_cpu(*(__be64 *)phy->phy_desc->sas_addr);
+
+		addr += asd_ha->hw_prof.sata_name_base + ord_phy(asd_ha, phy);
+		*(__be64 *)sas_addr = cpu_to_be64(addr);
+	} else {
+		struct sas_identify_frame *idframe =
+			(void *) phy->sas_phy.frame_rcvd;
+		memcpy(sas_addr, idframe->sas_addr, SAS_ADDR_SIZE);
+	}
+}
+
+static inline void asd_bytes_dmaed_tasklet(struct asd_ascb *ascb,
+					   struct done_list_struct *dl,
+					   int edb_id, int phy_id)
+{
+	unsigned long flags;
+	int edb_el = edb_id + ascb->edb_index;
+	struct asd_dma_tok *edb = ascb->ha->seq.edb_arr[edb_el];
+	struct asd_phy *phy = &ascb->ha->phys[phy_id];
+	struct sas_ha_struct *sas_ha = phy->sas_phy.ha;
+	u16 size = ((dl->status_block[3] & 7) << 8) | dl->status_block[2];
+
+	size = min(size, (u16) sizeof(phy->frame_rcvd));
+
+	spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags);
+	memcpy(phy->sas_phy.frame_rcvd, edb->vaddr, size);
+	phy->sas_phy.frame_rcvd_size = size;
+	asd_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr);
+	spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);
+	asd_dump_frame_rcvd(phy, dl);
+	sas_ha->notify_port_event(&phy->sas_phy, PORTE_BYTES_DMAED);
+}
+
+static inline void asd_link_reset_err_tasklet(struct asd_ascb *ascb,
+					      struct done_list_struct *dl,
+					      int phy_id)
+{
+	struct asd_ha_struct *asd_ha = ascb->ha;
+	struct sas_ha_struct *sas_ha = &asd_ha->sas_ha;
+	struct sas_phy *sas_phy = sas_ha->sas_phy[phy_id];
+	u8 lr_error = dl->status_block[1];
+	u8 retries_left = dl->status_block[2];
+
+	switch (lr_error) {
+	case 0:
+		ASD_DPRINTK("phy%d: Receive ID timer expired\n", phy_id);
+		break;
+	case 1:
+		ASD_DPRINTK("phy%d: Loss of signal\n", phy_id);
+		break;
+	case 2:
+		ASD_DPRINTK("phy%d: Loss of dword sync\n", phy_id);
+		break;
+	case 3:
+		ASD_DPRINTK("phy%d: Receive FIS timeout\n", phy_id);
+		break;
+	default:
+		ASD_DPRINTK("phy%d: unknown link reset error code: 0x%x\n",
+			    phy_id, lr_error);
+		break;
+	}
+
+	asd_turn_led(asd_ha, phy_id, 0);
+	sas_phy_disconnected(sas_phy);
+	sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+
+	if (retries_left == 0) {
+		int num = 1;
+		struct asd_ascb *cp = asd_ascb_alloc_list(ascb->ha, &num,
+							  GFP_ATOMIC);
+		if (!cp) {
+			asd_printk("%s: out of memory\n", __FUNCTION__);
+			goto out;
+		}
+		ASD_DPRINTK("phy%d: retries:0 performing link reset seq\n",
+			    phy_id);
+		asd_build_control_phy(cp, phy_id, ENABLE_PHY);
+		if (asd_post_ascb_list(ascb->ha, cp, 1) != 0)
+			asd_ascb_free(cp);
+	}
+out:
+	;
+}
+
+static inline void asd_primitive_rcvd_tasklet(struct asd_ascb *ascb,
+					      struct done_list_struct *dl,
+					      int phy_id)
+{
+	unsigned long flags;
+	struct sas_ha_struct *sas_ha = &ascb->ha->sas_ha;
+	struct sas_phy *sas_phy = sas_ha->sas_phy[phy_id];
+	u8  reg  = dl->status_block[1];
+	u32 cont = dl->status_block[2] << ((reg & 3)*8);
+
+	reg &= ~3;
+	switch (reg) {
+	case LmPRMSTAT0BYTE0:
+		switch (cont) {
+		case LmBROADCH:
+		case LmBROADRVCH0:
+		case LmBROADRVCH1:
+		case LmBROADSES:
+			ASD_DPRINTK("phy%d: BROADCAST change received:%d\n",
+				    phy_id, cont);
+			spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
+			sas_phy->sas_prim = ffs(cont);
+			spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
+			sas_ha->notify_port_event(sas_phy,PORTE_BROADCAST_RCVD);
+			break;
+
+		case LmUNKNOWNP:
+			ASD_DPRINTK("phy%d: unknown BREAK\n", phy_id);
+			break;
+
+		default:
+			ASD_DPRINTK("phy%d: primitive reg:0x%x, cont:0x%04x\n",
+				    phy_id, reg, cont);
+			break;
+		}
+		break;
+	case LmPRMSTAT1BYTE0:
+		switch (cont) {
+		case LmHARDRST:
+			ASD_DPRINTK("phy%d: HARD_RESET primitive rcvd\n",
+				    phy_id);
+			/* The sequencer disables all phys on that port.
+			 * We have to re-enable the phys ourselves. */
+			sas_ha->notify_port_event(sas_phy, PORTE_HARD_RESET);
+			break;
+
+		default:
+			ASD_DPRINTK("phy%d: primitive reg:0x%x, cont:0x%04x\n",
+				    phy_id, reg, cont);
+			break;
+		}
+		break;
+	default:
+		ASD_DPRINTK("unknown primitive register:0x%x\n",
+			    dl->status_block[1]);
+		break;
+	}
+}
+
+/**
+ * asd_invalidate_edb -- invalidate an EDB and if necessary post the ESCB
+ * @ascb: pointer to Empty SCB
+ * @edb_id: index [0,6] to the empty data buffer which is to be invalidated
+ *
+ * After an EDB has been invalidated, if all EDBs in this ESCB have been
+ * invalidated, the ESCB is posted back to the sequencer.
+ * Context is tasklet/IRQ.
+ */
+void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id)
+{
+	struct asd_seq_data *seq = &ascb->ha->seq;
+	struct empty_scb *escb = &ascb->scb->escb;
+	struct sg_el     *eb   = &escb->eb[edb_id];
+	struct asd_dma_tok *edb = seq->edb_arr[ascb->edb_index + edb_id];
+
+	memset(edb->vaddr, 0, ASD_EDB_SIZE);
+	eb->flags |= ELEMENT_NOT_VALID;
+	escb->num_valid--;
+
+	if (escb->num_valid == 0) {
+		int i;
+		/* ASD_DPRINTK("reposting escb: vaddr: 0x%p, "
+			    "dma_handle: 0x%08llx, next: 0x%08llx, "
+			    "index:%d, opcode:0x%02x\n",
+			    ascb->dma_scb.vaddr,
+			    (u64)ascb->dma_scb.dma_handle,
+			    le64_to_cpu(ascb->scb->header.next_scb),
+			    le16_to_cpu(ascb->scb->header.index),
+			    ascb->scb->header.opcode);
+		*/
+		escb->num_valid = ASD_EDBS_PER_SCB;
+		for (i = 0; i < ASD_EDBS_PER_SCB; i++)
+			escb->eb[i].flags = 0;
+		if (!list_empty(&ascb->list))
+			list_del_init(&ascb->list);
+		i = asd_post_escb_list(ascb->ha, ascb, 1);
+		if (i)
+			asd_printk("couldn't post escb, err:%d\n", i);
+	}
+}
+
+static void escb_tasklet_complete(struct asd_ascb *ascb,
+				  struct done_list_struct *dl)
+{
+	struct asd_ha_struct *asd_ha = ascb->ha;
+	struct sas_ha_struct *sas_ha = &asd_ha->sas_ha;
+	int edb = (dl->opcode & DL_PHY_MASK) - 1; /* [0xc1,0xc7] -> [0,6] */
+	u8  sb_opcode = dl->status_block[0];
+	int phy_id = sb_opcode & DL_PHY_MASK;
+	struct sas_phy *sas_phy = sas_ha->sas_phy[phy_id];
+
+	if (edb > 6 || edb < 0) {
+		ASD_DPRINTK("edb is 0x%x! dl->opcode is 0x%x\n",
+			    edb, dl->opcode);
+		ASD_DPRINTK("sb_opcode : 0x%x, phy_id: 0x%x\n",
+			    sb_opcode, phy_id);
+		ASD_DPRINTK("escb: vaddr: 0x%p, "
+			    "dma_handle: 0x%08llx, next: 0x%08llx, "
+			    "index:%d, opcode:0x%02x\n",
+			    ascb->dma_scb.vaddr,
+			    (u64)ascb->dma_scb.dma_handle,
+			    le64_to_cpu(ascb->scb->header.next_scb),
+			    le16_to_cpu(ascb->scb->header.index),
+			    ascb->scb->header.opcode);
+	}
+
+	sb_opcode &= ~DL_PHY_MASK;
+
+	switch (sb_opcode) {
+	case BYTES_DMAED:
+		ASD_DPRINTK("%s: phy%d: BYTES_DMAED\n", __FUNCTION__, phy_id);
+		asd_bytes_dmaed_tasklet(ascb, dl, edb, phy_id);
+		break;
+	case PRIMITIVE_RECVD:
+		ASD_DPRINTK("%s: phy%d: PRIMITIVE_RECVD\n", __FUNCTION__,
+			    phy_id);
+		asd_primitive_rcvd_tasklet(ascb, dl, phy_id);
+		break;
+	case PHY_EVENT:
+		ASD_DPRINTK("%s: phy%d: PHY_EVENT\n", __FUNCTION__, phy_id);
+		asd_phy_event_tasklet(ascb, dl);
+		break;
+	case LINK_RESET_ERROR:
+		ASD_DPRINTK("%s: phy%d: LINK_RESET_ERROR\n", __FUNCTION__,
+			    phy_id);
+		asd_link_reset_err_tasklet(ascb, dl, phy_id);
+		break;
+	case TIMER_EVENT:
+		ASD_DPRINTK("%s: phy%d: TIMER_EVENT, lost dw sync\n",
+			    __FUNCTION__, phy_id);
+		asd_turn_led(asd_ha, phy_id, 0);
+		/* the device is gone */
+		sas_phy_disconnected(sas_phy);
+		sas_ha->notify_port_event(sas_phy, PORTE_TIMER_EVENT);
+		break;
+	case REQ_TASK_ABORT:
+		ASD_DPRINTK("%s: phy%d: REQ_TASK_ABORT\n", __FUNCTION__,
+			    phy_id);
+		break;
+	case REQ_DEVICE_RESET:
+		ASD_DPRINTK("%s: phy%d: REQ_DEVICE_RESET\n", __FUNCTION__,
+			    phy_id);
+		break;
+	case SIGNAL_NCQ_ERROR:
+		ASD_DPRINTK("%s: phy%d: SIGNAL_NCQ_ERROR\n", __FUNCTION__,
+			    phy_id);
+		break;
+	case CLEAR_NCQ_ERROR:
+		ASD_DPRINTK("%s: phy%d: CLEAR_NCQ_ERROR\n", __FUNCTION__,
+			    phy_id);
+		break;
+	default:
+		ASD_DPRINTK("%s: phy%d: unknown event:0x%x\n", __FUNCTION__,
+			    phy_id, sb_opcode);
+		ASD_DPRINTK("edb is 0x%x! dl->opcode is 0x%x\n",
+			    edb, dl->opcode);
+		ASD_DPRINTK("sb_opcode : 0x%x, phy_id: 0x%x\n",
+			    sb_opcode, phy_id);
+		ASD_DPRINTK("escb: vaddr: 0x%p, "
+			    "dma_handle: 0x%08llx, next: 0x%08llx, "
+			    "index:%d, opcode:0x%02x\n",
+			    ascb->dma_scb.vaddr,
+			    (u64)ascb->dma_scb.dma_handle,
+			    le64_to_cpu(ascb->scb->header.next_scb),
+			    le16_to_cpu(ascb->scb->header.index),
+			    ascb->scb->header.opcode);
+
+		break;
+	}
+
+	asd_invalidate_edb(ascb, edb);
+}
+
+int asd_init_post_escbs(struct asd_ha_struct *asd_ha)
+{
+	struct asd_seq_data *seq = &asd_ha->seq;
+	int i;
+
+	for (i = 0; i < seq->num_escbs; i++)
+		seq->escb_arr[i]->tasklet_complete = escb_tasklet_complete;
+
+	ASD_DPRINTK("posting %d escbs\n", i);
+	return asd_post_escb_list(asd_ha, seq->escb_arr[0], seq->num_escbs);
+}
+
+/* ---------- CONTROL PHY ---------- */
+
+#define CONTROL_PHY_STATUS (CURRENT_DEVICE_PRESENT | CURRENT_OOB_DONE   \
+			    | CURRENT_SPINUP_HOLD | CURRENT_GTO_TIMEOUT \
+			    | CURRENT_OOB_ERROR)
+
+/**
+ * control_phy_tasklet_complete -- tasklet complete for CONTROL PHY ascb
+ * @ascb: pointer to an ascb
+ * @dl: pointer to the done list entry
+ *
+ * This function completes a CONTROL PHY scb and frees the ascb.
+ * A note on LEDs:
+ *  - an LED blinks if there is IO though it,
+ *  - if a device is connected to the LED, it is lit,
+ *  - if no device is connected to the LED, is is dimmed (off).
+ */
+static void control_phy_tasklet_complete(struct asd_ascb *ascb,
+					 struct done_list_struct *dl)
+{
+	struct asd_ha_struct *asd_ha = ascb->ha;
+	struct scb *scb = ascb->scb;
+	struct control_phy *control_phy = &scb->control_phy;
+	u8 phy_id = control_phy->phy_id;
+	struct asd_phy *phy = &ascb->ha->phys[phy_id];
+
+	u8 status     = dl->status_block[0];
+	u8 oob_status = dl->status_block[1];
+	u8 oob_mode   = dl->status_block[2];
+	/* u8 oob_signals= dl->status_block[3]; */
+
+	if (status != 0) {
+		ASD_DPRINTK("%s: phy%d status block opcode:0x%x\n",
+			    __FUNCTION__, phy_id, status);
+		goto out;
+	}
+
+	switch (control_phy->sub_func) {
+	case DISABLE_PHY:
+		asd_ha->hw_prof.enabled_phys &= ~(1 << phy_id);
+		asd_turn_led(asd_ha, phy_id, 0);
+		asd_control_led(asd_ha, phy_id, 0);
+		ASD_DPRINTK("%s: disable phy%d\n", __FUNCTION__, phy_id);
+		break;
+
+	case ENABLE_PHY:
+		asd_control_led(asd_ha, phy_id, 1);
+		if (oob_status & CURRENT_OOB_DONE) {
+			asd_ha->hw_prof.enabled_phys |= (1 << phy_id);
+			get_lrate_mode(phy, oob_mode);
+			asd_turn_led(asd_ha, phy_id, 1);
+			ASD_DPRINTK("%s: phy%d, lrate:0x%x, proto:0x%x\n",
+				    __FUNCTION__, phy_id,phy->sas_phy.linkrate,
+				    phy->sas_phy.iproto);
+		} else if (oob_status & CURRENT_SPINUP_HOLD) {
+			asd_ha->hw_prof.enabled_phys |= (1 << phy_id);
+			asd_turn_led(asd_ha, phy_id, 1);
+			ASD_DPRINTK("%s: phy%d, spinup hold\n", __FUNCTION__,
+				    phy_id);
+		} else if (oob_status & CURRENT_ERR_MASK) {
+			asd_turn_led(asd_ha, phy_id, 0);
+			ASD_DPRINTK("%s: phy%d: error: oob status:0x%02x\n",
+				    __FUNCTION__, phy_id, oob_status);
+		} else if (oob_status & (CURRENT_HOT_PLUG_CNCT
+					 | CURRENT_DEVICE_PRESENT))  {
+			asd_ha->hw_prof.enabled_phys |= (1 << phy_id);
+			asd_turn_led(asd_ha, phy_id, 1);
+			ASD_DPRINTK("%s: phy%d: hot plug or device present\n",
+				    __FUNCTION__, phy_id);
+		} else {
+			asd_ha->hw_prof.enabled_phys |= (1 << phy_id);
+			asd_turn_led(asd_ha, phy_id, 0);
+			ASD_DPRINTK("%s: phy%d: no device present: "
+				    "oob_status:0x%x\n",
+				    __FUNCTION__, phy_id, oob_status);
+		}
+		break;
+	case RELEASE_SPINUP_HOLD:
+	case PHY_NO_OP:
+	case EXECUTE_HARD_RESET:
+		ASD_DPRINTK("%s: phy%d: sub_func:0x%x\n", __FUNCTION__,
+			    phy_id, control_phy->sub_func);
+		/* XXX finish */
+		break;
+	default:
+		ASD_DPRINTK("%s: phy%d: sub_func:0x%x?\n", __FUNCTION__,
+			    phy_id, control_phy->sub_func);
+		break;
+	}
+out:
+	asd_ascb_free(ascb);
+}
+
+static inline void set_speed_mask(u8 *speed_mask, struct asd_phy_desc *pd)
+{
+	/* disable all speeds, then enable defaults */
+	*speed_mask = SAS_SPEED_60_DIS | SAS_SPEED_30_DIS | SAS_SPEED_15_DIS
+		| SATA_SPEED_30_DIS | SATA_SPEED_15_DIS;
+
+	switch (pd->max_sas_lrate) {
+	case PHY_LINKRATE_6:
+		*speed_mask &= ~SAS_SPEED_60_DIS;
+	default:
+	case PHY_LINKRATE_3:
+		*speed_mask &= ~SAS_SPEED_30_DIS;
+	case PHY_LINKRATE_1_5:
+		*speed_mask &= ~SAS_SPEED_15_DIS;
+	}
+
+	switch (pd->min_sas_lrate) {
+	case PHY_LINKRATE_6:
+		*speed_mask |= SAS_SPEED_30_DIS;
+	case PHY_LINKRATE_3:
+		*speed_mask |= SAS_SPEED_15_DIS;
+	default:
+	case PHY_LINKRATE_1_5:
+		/* nothing to do */
+		;
+	}
+
+	switch (pd->max_sata_lrate) {
+	case PHY_LINKRATE_3:
+		*speed_mask &= ~SATA_SPEED_30_DIS;
+	default:
+	case PHY_LINKRATE_1_5:
+		*speed_mask &= ~SATA_SPEED_15_DIS;
+	}
+
+	switch (pd->min_sata_lrate) {
+	case PHY_LINKRATE_3:
+		*speed_mask |= SATA_SPEED_15_DIS;
+	default:
+	case PHY_LINKRATE_1_5:
+		/* nothing to do */
+		;
+	}
+}
+
+/**
+ * asd_build_control_phy -- build a CONTROL PHY SCB
+ * @ascb: pointer to an ascb
+ * @phy_id: phy id to control, integer
+ * @subfunc: subfunction, what to actually to do the phy
+ *
+ * This function builds a CONTROL PHY scb.  No allocation of any kind
+ * is performed. @ascb is allocated with the list function.
+ * The caller can override the ascb->tasklet_complete to point
+ * to its own callback function.  It must call asd_ascb_free()
+ * at its tasklet complete function.
+ * See the default implementation.
+ */
+void asd_build_control_phy(struct asd_ascb *ascb, int phy_id, u8 subfunc)
+{
+	struct asd_phy *phy = &ascb->ha->phys[phy_id];
+	struct scb *scb = ascb->scb;
+	struct control_phy *control_phy = &scb->control_phy;
+
+	scb->header.opcode = CONTROL_PHY;
+	control_phy->phy_id = (u8) phy_id;
+	control_phy->sub_func = subfunc;
+
+	switch (subfunc) {
+	case EXECUTE_HARD_RESET:  /* 0x81 */
+	case ENABLE_PHY:          /* 0x01 */
+		/* decide hot plug delay */
+		control_phy->hot_plug_delay = HOTPLUG_DELAY_TIMEOUT;
+
+		/* decide speed mask */
+		set_speed_mask(&control_phy->speed_mask, phy->phy_desc);
+
+		/* initiator port settings are in the hi nibble */
+		if (phy->sas_phy.role == PHY_ROLE_INITIATOR)
+			control_phy->port_type = SAS_PROTO_ALL << 4;
+		else if (phy->sas_phy.role == PHY_ROLE_TARGET)
+			control_phy->port_type = SAS_PROTO_ALL;
+		else
+			control_phy->port_type =
+				(SAS_PROTO_ALL << 4) | SAS_PROTO_ALL;
+
+		/* link reset retries, this should be nominal */
+		control_phy->link_reset_retries = 10;
+
+	case RELEASE_SPINUP_HOLD: /* 0x02 */
+		/* decide the func_mask */
+		control_phy->func_mask = FUNCTION_MASK_DEFAULT;
+		if (phy->phy_desc->flags & ASD_SATA_SPINUP_HOLD)
+			control_phy->func_mask &= ~SPINUP_HOLD_DIS;
+		else
+			control_phy->func_mask |= SPINUP_HOLD_DIS;
+	}
+
+	control_phy->conn_handle = cpu_to_le16(0xFFFF);
+
+	ascb->tasklet_complete = control_phy_tasklet_complete;
+}
+
+/* ---------- INITIATE LINK ADM TASK ---------- */
+
+static void link_adm_tasklet_complete(struct asd_ascb *ascb,
+				      struct done_list_struct *dl)
+{
+	u8 opcode = dl->opcode;
+	struct initiate_link_adm *link_adm = &ascb->scb->link_adm;
+	u8 phy_id = link_adm->phy_id;
+
+	if (opcode != TC_NO_ERROR) {
+		asd_printk("phy%d: link adm task 0x%x completed with error "
+			   "0x%x\n", phy_id, link_adm->sub_func, opcode);
+	}
+	ASD_DPRINTK("phy%d: link adm task 0x%x: 0x%x\n",
+		    phy_id, link_adm->sub_func, opcode);
+
+	asd_ascb_free(ascb);
+}
+
+void asd_build_initiate_link_adm_task(struct asd_ascb *ascb, int phy_id,
+				      u8 subfunc)
+{
+	struct scb *scb = ascb->scb;
+	struct initiate_link_adm *link_adm = &scb->link_adm;
+
+	scb->header.opcode = INITIATE_LINK_ADM_TASK;
+
+	link_adm->phy_id = phy_id;
+	link_adm->sub_func = subfunc;
+	link_adm->conn_handle = cpu_to_le16(0xFFFF);
+
+	ascb->tasklet_complete = link_adm_tasklet_complete;
+}
+
+/* ---------- SCB timer ---------- */
+
+/**
+ * asd_ascb_timedout -- called when a pending SCB's timer has expired
+ * @data: unsigned long, a pointer to the ascb in question
+ *
+ * This is the default timeout function which does the most necessary.
+ * Upper layers can implement their own timeout function, say to free
+ * resources they have with this SCB, and then call this one at the
+ * end of their timeout function.  To do this, one should initialize
+ * the ascb->timer.{function, data, expires} prior to calling the post
+ * funcion.  The timer is started by the post function.
+ */
+void asd_ascb_timedout(unsigned long data)
+{
+	struct asd_ascb *ascb = (void *) data;
+	struct asd_seq_data *seq = &ascb->ha->seq;
+	unsigned long flags;
+
+	ASD_DPRINTK("scb:0x%x timed out\n", ascb->scb->header.opcode);
+
+	spin_lock_irqsave(&seq->pend_q_lock, flags);
+	seq->pending--;
+	list_del_init(&ascb->list);
+	spin_unlock_irqrestore(&seq->pend_q_lock, flags);
+
+	asd_ascb_free(ascb);
+}
+
+/* ---------- CONTROL PHY ---------- */
+
+/* Given the spec value, return a driver value. */
+static const int phy_func_table[] = {
+	[PHY_FUNC_NOP]        = PHY_NO_OP,
+	[PHY_FUNC_LINK_RESET] = ENABLE_PHY,
+	[PHY_FUNC_HARD_RESET] = EXECUTE_HARD_RESET,
+	[PHY_FUNC_DISABLE]    = DISABLE_PHY,
+	[PHY_FUNC_RELEASE_SPINUP_HOLD] = RELEASE_SPINUP_HOLD,
+};
+
+int asd_control_phy(struct sas_phy *phy, enum phy_func func)
+{
+	struct asd_ha_struct *asd_ha = phy->ha->lldd_ha;
+	struct asd_ascb *ascb;
+	int res = 1;
+
+	if (func == PHY_FUNC_CLEAR_ERROR_LOG)
+		return -ENOSYS;
+
+	ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL);
+	if (!ascb)
+		return -ENOMEM;
+
+	asd_build_control_phy(ascb, phy->id, phy_func_table[func]);
+	res = asd_post_ascb_list(asd_ha, ascb , 1);
+	if (res)
+		asd_ascb_free(ascb);
+
+	return res;
+}
diff -urN oldtree/drivers/scsi/aic94xx/aic94xx_sds.c newtree/drivers/scsi/aic94xx/aic94xx_sds.c
--- oldtree/drivers/scsi/aic94xx/aic94xx_sds.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/aic94xx_sds.c	2006-02-13 19:20:00.590422544 -0500
@@ -0,0 +1,964 @@
+/*
+ * Aic94xx SAS/SATA driver access to shared data structures and memory
+ * maps.
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/aic94xx/aic94xx_sds.c#35 $
+ */
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include "aic94xx.h"
+#include "aic94xx_reg.h"
+
+/* ---------- OCM stuff ---------- */
+
+struct asd_ocm_dir_ent {
+	u8 type;
+	u8 offs[3];
+	u8 _r1;
+	u8 size[3];
+} __attribute__ ((packed));
+
+struct asd_ocm_dir {
+	char sig[2];
+	u8   _r1[2];
+	u8   major;          /* 0 */
+	u8   minor;          /* 0 */
+	u8   _r2;
+	u8   num_de;
+	struct asd_ocm_dir_ent entry[15];
+} __attribute__ ((packed));
+
+struct asd_bios_chim_struct {
+	char sig[4];
+	u8   major;          /* 1 */
+	u8   minor;          /* 0 */
+	u8   bios_major;
+	u8   bios_minor;
+	__le32  bios_build;
+	u8   flags;
+	u8   pci_slot;
+	__le16  ue_num;
+	__le16  ue_size;
+	u8  _r[14];
+	/* The unit element array is right here.
+	 */
+} __attribute__ ((packed));
+
+/**
+ * asd_read_ocm_seg - read an on chip memory (OCM) segment
+ * @asd_ha: pointer to the host adapter structure
+ * @buffer: where to write the read data
+ * @offs: offset into OCM where to read from
+ * @size: how many bytes to read
+ *
+ * Return the number of bytes not read. Return 0 on success.
+ */
+static int asd_read_ocm_seg(struct asd_ha_struct *asd_ha, void *buffer,
+			    u32 offs, int size)
+{
+	u8 *p = buffer;
+	if (unlikely(asd_ha->iospace))
+		asd_read_reg_string(asd_ha, buffer, offs+OCM_BASE_ADDR, size);
+	else {
+		for ( ; size > 0; size--, offs++, p++)
+			*p = asd_read_ocm_byte(asd_ha, offs);
+	}
+	return size;
+}
+
+static int asd_read_ocm_dir(struct asd_ha_struct *asd_ha,
+			    struct asd_ocm_dir *dir, u32 offs)
+{
+	int err = asd_read_ocm_seg(asd_ha, (void *)dir, offs, sizeof(*dir));
+	if (err) {
+		ASD_DPRINTK("couldn't read ocm segment\n");
+		return err;
+	}
+	if (dir->sig[0] != 'M' || dir->sig[1] != 'O') {
+		ASD_DPRINTK("no valid dir signature(%c%c) at start of OCM\n",
+			    dir->sig[0], dir->sig[1]);
+		return -ENOENT;
+	}
+	if (dir->major != 0) {
+		asd_printk("unsupported major version of ocm dir:0x%x\n",
+			   dir->major);
+		return -ENOENT;
+	}
+	dir->num_de &= 0xf;
+	return 0;
+}
+
+#define THREE_TO_NUM(X) ((X)[0] | ((X)[1] << 8) | ((X)[2] << 16))
+
+static int asd_find_dir_entry(struct asd_ocm_dir *dir, u8 type,
+			      u32 *offs, u32 *size)
+{
+	int i;
+	struct asd_ocm_dir_ent *ent;
+
+	for (i = 0; i < dir->num_de; i++) {
+		if (dir->entry[i].type == type)
+			break;
+	}
+	if (i >= dir->num_de)
+		return -ENOENT;
+	ent = &dir->entry[i];
+	*offs = (u32) THREE_TO_NUM(ent->offs);
+	*size = (u32) THREE_TO_NUM(ent->size);
+	return 0;
+}
+
+#define OCM_BIOS_CHIM_DE  2
+#define BC_BIOS_PRESENT   1
+
+static int asd_get_bios_chim(struct asd_ha_struct *asd_ha,
+			     struct asd_ocm_dir *dir)
+{
+	int err;
+	struct asd_bios_chim_struct *bc_struct;
+	u32 offs, size;
+
+	err = asd_find_dir_entry(dir, OCM_BIOS_CHIM_DE, &offs, &size);
+	if (err) {
+		ASD_DPRINTK("couldn't find BIOS_CHIM dir ent\n");
+		goto out;
+	}
+	err = -ENOMEM;
+	bc_struct = kmalloc(sizeof(*bc_struct), GFP_KERNEL);
+	if (!bc_struct) {
+		asd_printk("no memory for bios_chim struct\n");
+		goto out;
+	}
+	err = asd_read_ocm_seg(asd_ha, (void *)bc_struct, offs,
+			       sizeof(*bc_struct));
+	if (err) {
+		ASD_DPRINTK("couldn't read ocm segment\n");
+		goto out2;
+	}
+	if (strncmp(bc_struct->sig, "SOIB", 4)
+	    && strncmp(bc_struct->sig, "IPSA", 4)) {
+		ASD_DPRINTK("BIOS_CHIM entry has no valid sig(%c%c%c%c)\n",
+			    bc_struct->sig[0], bc_struct->sig[1],
+			    bc_struct->sig[2], bc_struct->sig[3]);
+		err = -ENOENT;
+		goto out2;
+	}
+	if (bc_struct->major != 1) {
+		asd_printk("BIOS_CHIM unsupported major version:0x%x\n",
+			   bc_struct->major);
+		err = -ENOENT;
+		goto out2;
+	}
+	if (bc_struct->flags & BC_BIOS_PRESENT) {
+		asd_ha->hw_prof.bios.present = 1;
+		asd_ha->hw_prof.bios.maj = bc_struct->bios_major;
+		asd_ha->hw_prof.bios.min = bc_struct->bios_minor;
+		asd_ha->hw_prof.bios.bld = le32_to_cpu(bc_struct->bios_build);
+		ASD_DPRINTK("BIOS present (%d,%d), %d\n",
+			    asd_ha->hw_prof.bios.maj,
+			    asd_ha->hw_prof.bios.min,
+			    asd_ha->hw_prof.bios.bld);
+	}
+	asd_ha->hw_prof.ue.num = le16_to_cpu(bc_struct->ue_num);
+	asd_ha->hw_prof.ue.size= le16_to_cpu(bc_struct->ue_size);
+	ASD_DPRINTK("ue num:%d, ue size:%d\n", asd_ha->hw_prof.ue.num,
+		    asd_ha->hw_prof.ue.size);
+	size = asd_ha->hw_prof.ue.num * asd_ha->hw_prof.ue.size;
+	if (size > 0) {
+		err = -ENOMEM;
+		asd_ha->hw_prof.ue.area = kmalloc(size, GFP_KERNEL);
+		if (!asd_ha->hw_prof.ue.area)
+			goto out2;
+		err = asd_read_ocm_seg(asd_ha, (void *)asd_ha->hw_prof.ue.area,
+				       offs + sizeof(*bc_struct), size);
+		if (err) {
+			kfree(asd_ha->hw_prof.ue.area);
+			asd_ha->hw_prof.ue.area = NULL;
+			asd_ha->hw_prof.ue.num  = 0;
+			asd_ha->hw_prof.ue.size = 0;
+			ASD_DPRINTK("couldn't read ue entries(%d)\n", err);
+		}
+	}
+out2:
+	kfree(bc_struct);
+out:
+	return err;
+}
+
+/**
+ * asd_read_ocm - read on chip memory (OCM)
+ * @asd_ha: pointer to the host adapter structure
+ */
+int asd_read_ocm(struct asd_ha_struct *asd_ha)
+{
+	int err;
+	struct asd_ocm_dir *dir;
+
+	dir = kmalloc(sizeof(*dir), GFP_KERNEL);
+	if (!dir) {
+		asd_printk("no memory for ocm dir\n");
+		return -ENOMEM;
+	}
+
+	err = asd_read_ocm_dir(asd_ha, dir, 0);
+	if (err)
+		goto out;
+
+	err = asd_get_bios_chim(asd_ha, dir);
+out:
+	kfree(dir);
+	return err;
+}
+
+/* ---------- FLASH stuff ---------- */
+
+#define FLASH_RESET			0xF0
+#define FLASH_MANUF_AMD                 1
+
+#define FLASH_SIZE                      0x200000
+#define FLASH_DIR_COOKIE                "*** ADAPTEC FLASH DIRECTORY *** "
+#define FLASH_NEXT_ENTRY_OFFS		0x2000
+#define FLASH_MAX_DIR_ENTRIES		32
+
+#define FLASH_DE_TYPE_MASK              0x3FFFFFFF
+#define FLASH_DE_MS                     0x120
+#define FLASH_DE_CTRL_A_USER            0xE0
+
+struct asd_flash_de {
+	__le32   type;
+	__le32   offs;
+	__le32   pad_size;
+	__le32   image_size;
+	__le32   chksum;
+	u8       _r[12];
+	u8       version[32];
+} __attribute__ ((packed));
+
+struct asd_flash_dir {
+	u8    cookie[32];
+	__le32   rev;		  /* 2 */
+	__le32   chksum;
+	__le32   chksum_antidote;
+	__le32   bld;
+	u8    bld_id[32];	  /* build id data */
+	u8    ver_data[32];	  /* date and time of build */
+	__le32   ae_mask;
+	__le32   v_mask;
+	__le32   oc_mask;
+	u8    _r[20];
+	struct asd_flash_de dir_entry[FLASH_MAX_DIR_ENTRIES];
+} __attribute__ ((packed));
+
+struct asd_manuf_sec {
+	char  sig[2];		  /* 'S', 'M' */
+	u16   offs_next;
+	u8    maj;           /* 0 */
+	u8    min;           /* 0 */
+	u16   chksum;
+	u16   size;
+	u8    _r[6];
+	u8    sas_addr[SAS_ADDR_SIZE];
+	u8    pcba_sn[ASD_PCBA_SN_SIZE];
+	/* Here start the other segments */
+	u8    linked_list[0];
+} __attribute__ ((packed));
+
+struct asd_manuf_phy_desc {
+	u8    state;         /* low 4 bits */
+#define MS_PHY_STATE_ENABLEABLE 0
+#define MS_PHY_STATE_REPORTED   1
+#define MS_PHY_STATE_HIDDEN     2
+	u8    phy_id;
+	u16   _r;
+	u8    phy_control_0; /* mode 5 reg 0x160 */
+	u8    phy_control_1; /* mode 5 reg 0x161 */
+	u8    phy_control_2; /* mode 5 reg 0x162 */
+	u8    phy_control_3; /* mode 5 reg 0x163 */
+} __attribute__ ((packed));
+
+struct asd_manuf_phy_param {
+	char  sig[2];		  /* 'P', 'M' */
+	u16   next;
+	u8    maj;           /* 0 */
+	u8    min;           /* 2 */
+	u8    num_phy_desc;  /* 8 */
+	u8    phy_desc_size; /* 8 */
+	u8    _r[3];
+	u8    usage_model_id;
+	u32   _r2;
+	struct asd_manuf_phy_desc phy_desc[ASD_MAX_PHYS];
+} __attribute__ ((packed));
+
+#if 0
+static const char *asd_sb_type[] = {
+	"unknown",
+	"SGPIO",
+	[2 ... 0x7F] = "unknown",
+	[0x80] = "ADPT_I2C",
+	[0x81 ... 0xFF] = "VENDOR_UNIQUExx"
+};
+#endif
+
+struct asd_ms_sb_desc {
+	u8    type;
+	u8    node_desc_index;
+	u8    conn_desc_index;
+	u8    _recvd[0];
+} __attribute__ ((packed));
+
+#if 0
+static const char *asd_conn_type[] = {
+	[0 ... 7] = "unknown",
+	"SFF8470",
+	"SFF8482",
+	"SFF8484",
+	[0x80] = "PCIX_DAUGHTER0",
+	[0x81] = "SAS_DAUGHTER0",
+	[0x82 ... 0xFF] = "VENDOR_UNIQUExx"
+};
+
+static const char *asd_conn_location[] = {
+	"unknown",
+	"internal",
+	"external",
+	"board_to_board",
+};
+#endif
+
+struct asd_ms_conn_desc {
+	u8    type;
+	u8    location;
+	u8    num_sideband_desc;
+	u8    size_sideband_desc;
+	u32   _resvd;
+	u8    name[16];
+	struct asd_ms_sb_desc sb_desc[0];
+} __attribute__ ((packed));
+
+struct asd_nd_phy_desc {
+	u8    vp_attch_type;
+	u8    attch_specific[0];
+} __attribute__ ((packed));
+
+#if 0
+static const char *asd_node_type[] = {
+	"IOP",
+	"IO_CONTROLLER",
+	"EXPANDER",
+	"PORT_MULTIPLIER",
+	"PORT_MULTIPLEXER",
+	"MULTI_DROP_I2C_BUS",
+};
+#endif
+
+struct asd_ms_node_desc {
+	u8    type;
+	u8    num_phy_desc;
+	u8    size_phy_desc;
+	u8    _resvd;
+	u8    name[16];
+	struct asd_nd_phy_desc phy_desc[0];
+} __attribute__ ((packed));
+
+struct asd_ms_conn_map {
+	char  sig[2];		  /* 'M', 'C' */
+	__le16 next;
+	u8    maj;		  /* 0 */
+	u8    min;		  /* 0 */
+	__le16 cm_size;		  /* size of this struct */
+	u8    num_conn;
+	u8    conn_size;
+	u8    num_nodes;
+	u8    usage_model_id;
+	u32   _resvd;
+	struct asd_ms_conn_desc conn_desc[0];
+	struct asd_ms_node_desc node_desc[0];
+} __attribute__ ((packed));
+
+struct asd_ctrla_phy_entry {
+	u8    sas_addr[SAS_ADDR_SIZE];
+	u8    sas_link_rates;  /* max in hi bits, min in low bits */
+	u8    flags;
+	u8    sata_link_rates;
+	u8    _r[5];
+} __attribute__ ((packed));
+
+struct asd_ctrla_phy_settings {
+	u8    id0;		  /* P'h'y */
+	u8    _r;
+	u16   next;
+	u8    num_phys;	      /* number of PHYs in the PCI function */
+	u8    _r2[3];
+	struct asd_ctrla_phy_entry phy_ent[ASD_MAX_PHYS];
+} __attribute__ ((packed));
+
+struct asd_ll_el {
+	u8   id0;
+	u8   id1;
+	__le16  next;
+	u8   something_here[0];
+} __attribute__ ((packed));
+
+static int asd_poll_flash(struct asd_ha_struct *asd_ha)
+{
+	int c;
+	u8 d;
+
+	for (c = 5000; c > 0; c--) {
+		d  = asd_read_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar);
+		d ^= asd_read_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar);
+		if (!d)
+			return 0;
+		udelay(5);
+	}
+	return -ENOENT;
+}
+
+static int asd_reset_flash(struct asd_ha_struct *asd_ha)
+{
+	int err;
+
+	err = asd_poll_flash(asd_ha);
+	if (err)
+		return err;
+	asd_write_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar, FLASH_RESET);
+	err = asd_poll_flash(asd_ha);
+
+	return err;
+}
+
+static inline int asd_read_flash_seg(struct asd_ha_struct *asd_ha,
+				     void *buffer, u32 offs, int size)
+{
+	asd_read_reg_string(asd_ha, buffer, asd_ha->hw_prof.flash.bar+offs,
+			    size);
+	return 0;
+}
+
+/**
+ * asd_find_flash_dir - finds and reads the flash directory
+ * @asd_ha: pointer to the host adapter structure
+ * @flash_dir: pointer to flash directory structure
+ *
+ * If found, the flash directory segment will be copied to
+ * @flash_dir.  Return 1 if found, 0 if not.
+ */
+static int asd_find_flash_dir(struct asd_ha_struct *asd_ha,
+			      struct asd_flash_dir *flash_dir)
+{
+	u32 v;
+	for (v = 0; v < FLASH_SIZE; v += FLASH_NEXT_ENTRY_OFFS) {
+		asd_read_flash_seg(asd_ha, flash_dir, v,
+				   sizeof(FLASH_DIR_COOKIE)-1);
+		if (memcmp(flash_dir->cookie, FLASH_DIR_COOKIE,
+			   sizeof(FLASH_DIR_COOKIE)-1) == 0) {
+			asd_ha->hw_prof.flash.dir_offs = v;
+			asd_read_flash_seg(asd_ha, flash_dir, v,
+					   sizeof(*flash_dir));
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int asd_flash_getid(struct asd_ha_struct *asd_ha)
+{
+	int err = 0;
+	u32 reg, inc;
+
+	reg = asd_read_reg_dword(asd_ha, EXSICNFGR);
+
+	if (!(reg & FLASHEX)) {
+		ASD_DPRINTK("flash doesn't exist\n");
+		return -ENOENT;
+	}
+	if (pci_read_config_dword(asd_ha->pcidev, PCI_CONF_FLSH_BAR,
+				  &asd_ha->hw_prof.flash.bar)) {
+		asd_printk("couldn't read PCI_CONF_FLSH_BAR of %s\n",
+			   pci_name(asd_ha->pcidev));
+		return -ENOENT;
+	}
+	asd_ha->hw_prof.flash.present = 1;
+	asd_ha->hw_prof.flash.wide = reg & FLASHW ? 1 : 0;
+	err = asd_reset_flash(asd_ha);
+	if (err) {
+		ASD_DPRINTK("couldn't reset flash(%d)\n", err);
+		return err;
+	}
+	/* Get flash info. This would most likely be AMD Am29LV family flash.
+	 * First try the sequence for word mode.  It is the same as for
+	 * 008B (byte mode only), 160B (word mode) and 800D (word mode).
+	 */
+	reg = asd_ha->hw_prof.flash.bar;
+	inc = asd_ha->hw_prof.flash.wide ? 2 : 1;
+	asd_write_reg_byte(asd_ha, reg + 0x555, 0xAA);
+	asd_write_reg_byte(asd_ha, reg + 0x2AA, 0x55);
+	asd_write_reg_byte(asd_ha, reg + 0x555, 0x90);
+	asd_ha->hw_prof.flash.manuf = asd_read_reg_byte(asd_ha, reg);
+	asd_ha->hw_prof.flash.dev_id= asd_read_reg_byte(asd_ha,reg+inc);
+	asd_ha->hw_prof.flash.sec_prot = asd_read_reg_byte(asd_ha,reg+inc+inc);
+	/* Get out of autoselect mode. */
+	err = asd_reset_flash(asd_ha);
+
+	if (asd_ha->hw_prof.flash.manuf == FLASH_MANUF_AMD) {
+		ASD_DPRINTK("0Found FLASH(%d) manuf:%d, dev_id:0x%x, "
+			    "sec_prot:%d\n",
+			    asd_ha->hw_prof.flash.wide ? 16 : 8,
+			    asd_ha->hw_prof.flash.manuf,
+			    asd_ha->hw_prof.flash.dev_id,
+			    asd_ha->hw_prof.flash.sec_prot);
+		return 0;
+	}
+
+	/* Ok, try the sequence for byte mode of 160B and 800D.
+	 * We may actually never need this.
+	 */
+	asd_write_reg_byte(asd_ha, reg + 0xAAA, 0xAA);
+	asd_write_reg_byte(asd_ha, reg + 0x555, 0x55);
+	asd_write_reg_byte(asd_ha, reg + 0xAAA, 0x90);
+	asd_ha->hw_prof.flash.manuf = asd_read_reg_byte(asd_ha, reg);
+	asd_ha->hw_prof.flash.dev_id = asd_read_reg_byte(asd_ha, reg + 2);
+	asd_ha->hw_prof.flash.sec_prot = asd_read_reg_byte(asd_ha, reg + 4);
+	err = asd_reset_flash(asd_ha);
+
+	if (asd_ha->hw_prof.flash.manuf == FLASH_MANUF_AMD) {
+		ASD_DPRINTK("1Found FLASH(%d) manuf:%d, dev_id:0x%x, "
+			    "sec_prot:%d\n",
+			    asd_ha->hw_prof.flash.wide ? 16 : 8,
+			    asd_ha->hw_prof.flash.manuf,
+			    asd_ha->hw_prof.flash.dev_id,
+			    asd_ha->hw_prof.flash.sec_prot);
+		return 0;
+	}
+
+	return -ENOENT;
+}
+
+static u16 asd_calc_flash_chksum(u16 *p, int size)
+{
+	u16 chksum = 0;
+
+	while (size-- > 0)
+		chksum += *p++;
+
+	return chksum;
+}
+
+
+static int asd_find_flash_de(struct asd_flash_dir *flash_dir, u32 entry_type,
+			     u32 *offs, u32 *size)
+{
+	int i;
+	struct asd_flash_de *de;
+
+	for (i = 0; i < FLASH_MAX_DIR_ENTRIES; i++) {
+		u32 type = le32_to_cpu(flash_dir->dir_entry[i].type);
+
+		type &= FLASH_DE_TYPE_MASK;
+		if (type == entry_type)
+			break;
+	}
+	if (i >= FLASH_MAX_DIR_ENTRIES)
+		return -ENOENT;
+	de = &flash_dir->dir_entry[i];
+	*offs = le32_to_cpu(de->offs);
+	*size = le32_to_cpu(de->pad_size);
+	return 0;
+}
+
+static int asd_validate_ms(struct asd_manuf_sec *ms)
+{
+	if (ms->sig[0] != 'S' || ms->sig[1] != 'M') {
+		ASD_DPRINTK("manuf sec: no valid sig(%c%c)\n",
+			    ms->sig[0], ms->sig[1]);
+		return -ENOENT;
+	}
+	if (ms->maj != 0) {
+		asd_printk("unsupported manuf. sector. major version:%x\n",
+			   ms->maj);
+		return -ENOENT;
+	}
+	ms->offs_next = le16_to_cpu((__force __le16) ms->offs_next);
+	ms->chksum = le16_to_cpu((__force __le16) ms->chksum);
+	ms->size = le16_to_cpu((__force __le16) ms->size);
+
+	if (asd_calc_flash_chksum((u16 *)ms, ms->size/2)) {
+		ASD_DPRINTK("failed manuf sector checksum\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int asd_ms_get_sas_addr(struct asd_ha_struct *asd_ha,
+			       struct asd_manuf_sec *ms)
+{
+	memcpy(asd_ha->hw_prof.sas_addr, ms->sas_addr, SAS_ADDR_SIZE);
+	return 0;
+}
+
+static int asd_ms_get_pcba_sn(struct asd_ha_struct *asd_ha,
+			      struct asd_manuf_sec *ms)
+{
+	memcpy(asd_ha->hw_prof.pcba_sn, ms->pcba_sn, ASD_PCBA_SN_SIZE);
+	asd_ha->hw_prof.pcba_sn[ASD_PCBA_SN_SIZE] = '\0';
+	return 0;
+}
+
+/**
+ * asd_find_ll_by_id - find a linked list entry by its id
+ * @start: void pointer to the first element in the linked list
+ * @id0: the first byte of the id  (offs 0)
+ * @id1: the second byte of the id (offs 1)
+ *
+ * @start has to be the _base_ element start, since the
+ * linked list entries's offset is from this pointer.
+ * Some linked list entries use only the first id, in which case
+ * you can pass 0xFF for the second.
+ */
+static void *asd_find_ll_by_id(void * const start, const u8 id0, const u8 id1)
+{
+	struct asd_ll_el *el = start;
+
+	do {
+		switch (id1) {
+		default:
+			if (el->id1 == id1)
+		case 0xFF:
+				if (el->id0 == id0)
+					return el;
+		}
+		el = start + le16_to_cpu(el->next);
+	} while (el != start);
+
+	return NULL;
+}
+
+/**
+ * asd_ms_get_phy_params - get phy parameters from the manufacturing sector
+ * @asd_ha: pointer to the host adapter structure
+ * @manuf_sec: pointer to the manufacturing sector
+ *
+ * The manufacturing sector contans also the linked list of sub-segments,
+ * since when it was read, its size was taken from the flash directory,
+ * not from the structure size.
+ *
+ * HIDDEN phys do not count in the total count.  REPORTED phys cannot
+ * be enabled but are reported and counted towards the total.
+ * ENEBLEABLE phys are enabled by default and count towards the total.
+ * The absolute total phy number is ASD_MAX_PHYS.  hw_prof->num_phys
+ * merely specifies the number of phys the host adapter decided to
+ * report.  E.g., it is possible for phys 0, 1 and 2 to be HIDDEN,
+ * phys 3, 4 and 5 to be REPORTED and phys 6 and 7 to be ENEBLEABLE.
+ * In this case ASD_MAX_PHYS is 8, hw_prof->num_phys is 5, and only 2
+ * are actually enabled (enabled by default, max number of phys
+ * enableable in this case).
+ */
+static int asd_ms_get_phy_params(struct asd_ha_struct *asd_ha,
+				 struct asd_manuf_sec *manuf_sec)
+{
+	int i;
+	int en_phys = 0;
+	int rep_phys = 0;
+	struct asd_manuf_phy_param *phy_param;
+
+	phy_param = asd_find_ll_by_id(manuf_sec, 'P', 'M');
+	if (!phy_param) {
+		ASD_DPRINTK("ms: no phy parameters found\n");
+		return -ENOENT;
+	}
+
+	if (phy_param->maj != 0) {
+		asd_printk("unsupported manuf. phy param major version:0x%x\n",
+			   phy_param->maj);
+		return -ENOENT;
+	}
+
+	ASD_DPRINTK("ms: num_phy_desc: %d\n", phy_param->num_phy_desc);
+	asd_ha->hw_prof.enabled_phys = 0;
+	for (i = 0; i < phy_param->num_phy_desc; i++) {
+		struct asd_manuf_phy_desc *pd = &phy_param->phy_desc[i];
+		switch (pd->state & 0xF) {
+		case MS_PHY_STATE_HIDDEN:
+			ASD_DPRINTK("ms: phy%d: HIDDEN\n", i);
+			continue;
+		case MS_PHY_STATE_REPORTED:
+			ASD_DPRINTK("ms: phy%d: REPORTED\n", i);
+			asd_ha->hw_prof.enabled_phys &= ~(1 << i);
+			rep_phys++;
+			continue;
+		case MS_PHY_STATE_ENABLEABLE:
+			ASD_DPRINTK("ms: phy%d: ENEBLEABLE\n", i);
+			asd_ha->hw_prof.enabled_phys |= (1 << i);
+			en_phys++;
+			break;
+		}
+		asd_ha->hw_prof.phy_desc[i].phy_control_0 = pd->phy_control_0;
+		asd_ha->hw_prof.phy_desc[i].phy_control_1 = pd->phy_control_1;
+		asd_ha->hw_prof.phy_desc[i].phy_control_2 = pd->phy_control_2;
+		asd_ha->hw_prof.phy_desc[i].phy_control_3 = pd->phy_control_3;
+	}
+	asd_ha->hw_prof.max_phys = rep_phys + en_phys;
+	asd_ha->hw_prof.num_phys = en_phys;
+	ASD_DPRINTK("ms: max_phys:0x%x, num_phys:0x%x\n",
+		    asd_ha->hw_prof.max_phys, asd_ha->hw_prof.num_phys);
+	ASD_DPRINTK("ms: enabled_phys:0x%x\n", asd_ha->hw_prof.enabled_phys);
+	return 0;
+}
+
+static int asd_ms_get_connector_map(struct asd_ha_struct *asd_ha,
+				    struct asd_manuf_sec *manuf_sec)
+{
+	struct asd_ms_conn_map *cm;
+
+	cm = asd_find_ll_by_id(manuf_sec, 'M', 'C');
+	if (!cm) {
+		ASD_DPRINTK("ms: no connector map found\n");
+		return -ENOENT;
+	}
+
+	if (cm->maj != 0) {
+		ASD_DPRINTK("ms: unsupported: connector map major version 0x%x"
+			    "\n", cm->maj);
+		return -ENOENT;
+	}
+
+	/* XXX */
+
+	return 0;
+}
+
+
+/**
+ * asd_process_ms - find and extract information from the manufacturing sector
+ * @asd_ha: pointer to the host adapter structure
+ * @flash_dir: pointer to the flash directory
+ */
+static int asd_process_ms(struct asd_ha_struct *asd_ha,
+			  struct asd_flash_dir *flash_dir)
+{
+	int err;
+	struct asd_manuf_sec *manuf_sec;
+	u32 offs, size;
+
+	err = asd_find_flash_de(flash_dir, FLASH_DE_MS, &offs, &size);
+	if (err) {
+		ASD_DPRINTK("Couldn't find the manuf. sector\n");
+		goto out;
+	}
+
+	if (size == 0)
+		goto out;
+
+	err = -ENOMEM;
+	manuf_sec = kmalloc(size, GFP_KERNEL);
+	if (!manuf_sec) {
+		ASD_DPRINTK("no mem for manuf sector\n");
+		goto out;
+	}
+
+	err = asd_read_flash_seg(asd_ha, (void *)manuf_sec, offs, size);
+	if (err) {
+		ASD_DPRINTK("couldn't read manuf sector at 0x%x, size 0x%x\n",
+			    offs, size);
+		goto out2;
+	}
+
+	err = asd_validate_ms(manuf_sec);
+	if (err) {
+		ASD_DPRINTK("couldn't validate manuf sector\n");
+		goto out2;
+	}
+
+	err = asd_ms_get_sas_addr(asd_ha, manuf_sec);
+	if (err) {
+		ASD_DPRINTK("couldn't read the SAS_ADDR\n");
+		goto out2;
+	}
+	ASD_DPRINTK("manuf sect SAS_ADDR %llx\n",
+		    SAS_ADDR(asd_ha->hw_prof.sas_addr));
+
+	err = asd_ms_get_pcba_sn(asd_ha, manuf_sec);
+	if (err) {
+		ASD_DPRINTK("couldn't read the PCBA SN\n");
+		goto out2;
+	}
+	ASD_DPRINTK("manuf sect PCBA SN %s\n", asd_ha->hw_prof.pcba_sn);
+
+	err = asd_ms_get_phy_params(asd_ha, manuf_sec);
+	if (err) {
+		ASD_DPRINTK("ms: couldn't get phy parameters\n");
+		goto out2;
+	}
+
+	err = asd_ms_get_connector_map(asd_ha, manuf_sec);
+	if (err) {
+		ASD_DPRINTK("ms: couldn't get connector map\n");
+		goto out2;
+	}
+
+out2:
+	kfree(manuf_sec);
+out:
+	return err;
+}
+
+static int asd_process_ctrla_phy_settings(struct asd_ha_struct *asd_ha,
+					  struct asd_ctrla_phy_settings *ps)
+{
+	int i;
+	for (i = 0; i < ps->num_phys; i++) {
+		struct asd_ctrla_phy_entry *pe = &ps->phy_ent[i];
+
+		if (!PHY_ENABLED(asd_ha, i))
+			continue;
+		if (*(u64 *)pe->sas_addr == 0) {
+			asd_ha->hw_prof.enabled_phys &= ~(1 << i);
+			continue;
+		}
+		/* This is the SAS address which should be sent in IDENTIFY. */
+		memcpy(asd_ha->hw_prof.phy_desc[i].sas_addr, pe->sas_addr,
+		       SAS_ADDR_SIZE);
+		asd_ha->hw_prof.phy_desc[i].max_sas_lrate =
+			(pe->sas_link_rates & 0xF0) >> 4;
+		asd_ha->hw_prof.phy_desc[i].min_sas_lrate =
+			(pe->sas_link_rates & 0x0F);
+		asd_ha->hw_prof.phy_desc[i].max_sata_lrate =
+			(pe->sata_link_rates & 0xF0) >> 4;
+		asd_ha->hw_prof.phy_desc[i].min_sata_lrate =
+			(pe->sata_link_rates & 0x0F);
+		asd_ha->hw_prof.phy_desc[i].flags = pe->flags;
+		ASD_DPRINTK("ctrla: phy%d: sas_addr: %llx, sas rate:0x%x-0x%x,"
+			    " sata rate:0x%x-0x%x, flags:0x%x\n",
+			    i,
+			    SAS_ADDR(asd_ha->hw_prof.phy_desc[i].sas_addr),
+			    asd_ha->hw_prof.phy_desc[i].max_sas_lrate,
+			    asd_ha->hw_prof.phy_desc[i].min_sas_lrate,
+			    asd_ha->hw_prof.phy_desc[i].max_sata_lrate,
+			    asd_ha->hw_prof.phy_desc[i].min_sata_lrate,
+			    asd_ha->hw_prof.phy_desc[i].flags);
+	}
+
+	return 0;
+}
+
+/**
+ * asd_process_ctrl_a_user - process CTRL-A user settings
+ * @asd_ha: pointer to the host adapter structure
+ * @flash_dir: pointer to the flash directory
+ */
+static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha,
+				   struct asd_flash_dir *flash_dir)
+{
+	int err;
+	u32 offs, size;
+	struct asd_ll_el *el;
+	struct asd_ctrla_phy_settings *ps;
+
+	err = asd_find_flash_de(flash_dir, FLASH_DE_CTRL_A_USER, &offs, &size);
+	if (err) {
+		ASD_DPRINTK("couldn't find CTRL-A user settings section\n");
+		goto out;
+	}
+
+	if (size == 0)
+		goto out;
+
+	err = -ENOMEM;
+	el = kmalloc(size, GFP_KERNEL);
+	if (!el) {
+		ASD_DPRINTK("no mem for ctrla user settings section\n");
+		goto out;
+	}
+
+	err = asd_read_flash_seg(asd_ha, (void *)el, offs, size);
+	if (err) {
+		ASD_DPRINTK("couldn't read ctrla phy settings section\n");
+		goto out2;
+	}
+
+	err = -ENOENT;
+	ps = asd_find_ll_by_id(el, 'h', 0xFF);
+	if (!ps) {
+		ASD_DPRINTK("couldn't find ctrla phy settings struct\n");
+		goto out2;
+	}
+
+	err = asd_process_ctrla_phy_settings(asd_ha, ps);
+	if (err) {
+		ASD_DPRINTK("couldn't process ctrla phy settings\n");
+		goto out2;
+	}
+out2:
+	kfree(el);
+out:
+	return err;
+}
+
+/**
+ * asd_read_flash - read flash memory
+ * @asd_ha: pointer to the host adapter structure
+ */
+int asd_read_flash(struct asd_ha_struct *asd_ha)
+{
+	int err;
+	struct asd_flash_dir *flash_dir;
+
+	err = asd_flash_getid(asd_ha);
+	if (err)
+		return err;
+
+	flash_dir = kmalloc(sizeof(*flash_dir), GFP_KERNEL);
+	if (!flash_dir)
+		return -ENOMEM;
+
+	err = -ENOENT;
+	if (!asd_find_flash_dir(asd_ha, flash_dir)) {
+		ASD_DPRINTK("couldn't find flash directory\n");
+		goto out;
+	}
+
+	if (le32_to_cpu(flash_dir->rev) != 2) {
+		asd_printk("unsupported flash dir version:0x%x\n",
+			   le32_to_cpu(flash_dir->rev));
+		goto out;
+	}
+
+	err = asd_process_ms(asd_ha, flash_dir);
+	if (err) {
+		ASD_DPRINTK("couldn't process manuf sector settings\n");
+		goto out;
+	}
+
+	err = asd_process_ctrl_a_user(asd_ha, flash_dir);
+	if (err) {
+		ASD_DPRINTK("couldn't process CTRL-A user settings\n");
+		goto out;
+	}
+
+out:
+	kfree(flash_dir);
+	return err;
+}
diff -urN oldtree/drivers/scsi/aic94xx/aic94xx_seq.c newtree/drivers/scsi/aic94xx/aic94xx_seq.c
--- oldtree/drivers/scsi/aic94xx/aic94xx_seq.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/aic94xx_seq.c	2006-02-13 19:20:00.593422088 -0500
@@ -0,0 +1,1317 @@
+/*
+ * Aic94xx SAS/SATA driver sequencer interface.
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * Parts of this code adapted from David Chaw's adp94xx_seq.c.
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/aic94xx/aic94xx_seq.c#42 $
+ */
+
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include "aic94xx_reg.h"
+#include "aic94xx_hwi.h"
+
+#include "aic94xx_seq.h"
+#include "aic94xx_dump.h"
+
+#include "aic94xx_seq_microcode.c"
+
+/* It takes no more than 0.05 us for an instruction
+ * to complete. So waiting for 1 us should be more than
+ * plenty.
+ */
+#define PAUSE_DELAY 1
+#define PAUSE_TRIES 1000
+
+static u16 first_scb_site_no = 0xFFFF;
+static u16 last_scb_site_no;
+
+/* ---------- Pause/Unpause CSEQ/LSEQ ---------- */
+
+/**
+ * asd_pause_cseq - pause the central sequencer
+ * @asd_ha: pointer to host adapter structure
+ *
+ * Return 0 on success, negative on failure.
+ */
+int asd_pause_cseq(struct asd_ha_struct *asd_ha)
+{
+	int	count = PAUSE_TRIES;
+	u32	arp2ctl;
+
+	arp2ctl = asd_read_reg_dword(asd_ha, CARP2CTL);
+	if (arp2ctl & PAUSED)
+		return 0;
+
+	asd_write_reg_dword(asd_ha, CARP2CTL, arp2ctl | EPAUSE);
+	do {
+		arp2ctl = asd_read_reg_dword(asd_ha, CARP2CTL);
+		if (arp2ctl & PAUSED)
+			return 0;
+		udelay(PAUSE_DELAY);
+	} while (--count > 0);
+
+	ASD_DPRINTK("couldn't pause CSEQ\n");
+	return -1;
+}
+
+/**
+ * asd_unpause_cseq - unpause the central sequencer.
+ * @asd_ha: pointer to host adapter structure.
+ *
+ * Return 0 on success, negative on error.
+ */
+int asd_unpause_cseq(struct asd_ha_struct *asd_ha)
+{
+	u32	arp2ctl;
+	int	count = PAUSE_TRIES;
+
+	arp2ctl = asd_read_reg_dword(asd_ha, CARP2CTL);
+	if (!(arp2ctl & PAUSED))
+		return 0;
+
+	asd_write_reg_dword(asd_ha, CARP2CTL, arp2ctl & ~EPAUSE);
+	do {
+		arp2ctl = asd_read_reg_dword(asd_ha, CARP2CTL);
+		if (!(arp2ctl & PAUSED))
+			return 0;
+		udelay(PAUSE_DELAY);
+	} while (--count > 0);
+
+	ASD_DPRINTK("couldn't unpause the CSEQ\n");
+	return -1;
+}
+
+/**
+ * asd_seq_pause_lseq - pause a link sequencer
+ * @asd_ha: pointer to a host adapter structure
+ * @lseq: link sequencer of interest
+ *
+ * Return 0 on success, negative on error.
+ */
+static inline int asd_seq_pause_lseq(struct asd_ha_struct *asd_ha, int lseq)
+{
+	u32    arp2ctl;
+	int    count = PAUSE_TRIES;
+
+	arp2ctl = asd_read_reg_dword(asd_ha, LmARP2CTL(lseq));
+	if (arp2ctl & PAUSED)
+		return 0;
+
+	asd_write_reg_dword(asd_ha, LmARP2CTL(lseq), arp2ctl | EPAUSE);
+	do {
+		arp2ctl = asd_read_reg_dword(asd_ha, LmARP2CTL(lseq));
+		if (arp2ctl & PAUSED)
+			return 0;
+		udelay(PAUSE_DELAY);
+	} while (--count > 0);
+
+	ASD_DPRINTK("couldn't pause LSEQ %d\n", lseq);
+	return -1;
+}
+
+/**
+ * asd_pause_lseq - pause the link sequencer(s)
+ * @asd_ha: pointer to host adapter structure
+ * @lseq_mask: mask of link sequencers of interest
+ *
+ * Return 0 on success, negative on failure.
+ */
+int asd_pause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask)
+{
+	int lseq;
+	int err = 0;
+
+	for_each_sequencer(lseq_mask, lseq_mask, lseq) {
+		err = asd_seq_pause_lseq(asd_ha, lseq);
+		if (err)
+			return err;
+	}
+
+	return err;
+}
+
+/**
+ * asd_seq_unpause_lseq - unpause a link sequencer
+ * @asd_ha: pointer to host adapter structure
+ * @lseq: link sequencer of interest
+ *
+ * Return 0 on success, negative on error.
+ */
+static inline int asd_seq_unpause_lseq(struct asd_ha_struct *asd_ha, int lseq)
+{
+	u32 arp2ctl;
+	int count = PAUSE_TRIES;
+
+	arp2ctl = asd_read_reg_dword(asd_ha, LmARP2CTL(lseq));
+	if (!(arp2ctl & PAUSED))
+		return 0;
+
+	asd_write_reg_dword(asd_ha, LmARP2CTL(lseq), arp2ctl & ~EPAUSE);
+	do {
+		arp2ctl = asd_read_reg_dword(asd_ha, LmARP2CTL(lseq));
+		if (!(arp2ctl & PAUSED))
+			return 0;
+		udelay(PAUSE_DELAY);
+	} while (--count > 0);
+
+	ASD_DPRINTK("couldn't unpause LSEQ %d\n", lseq);
+	return 0;
+}
+
+
+/**
+ * asd_unpause_lseq - unpause the link sequencer(s)
+ * @asd_ha: pointer to host adapter structure
+ * @lseq_mask: mask of link sequencers of interest
+ *
+ * Return 0 on success, negative on failure.
+ */
+int asd_unpause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask)
+{
+	int lseq;
+	int err = 0;
+
+	for_each_sequencer(lseq_mask, lseq_mask, lseq) {
+		err = asd_seq_unpause_lseq(asd_ha, lseq);
+		if (err)
+			return err;
+	}
+
+	return err;
+}
+
+/* ---------- Downloading CSEQ/LSEQ microcode ---------- */
+
+static int asd_verify_cseq(struct asd_ha_struct *asd_ha, const u8 *_prog,
+			   u32 size)
+{
+	u32 addr = CSEQ_RAM_REG_BASE_ADR;
+	const u32 *prog = (u32 *) _prog;
+	u32 i;
+
+	for (i = 0; i < size; i += 4, prog++, addr += 4) {
+		u32 val = asd_read_reg_dword(asd_ha, addr);
+
+		if (le32_to_cpu(*prog) != val) {
+			asd_printk("%s: cseq verify failed at %u "
+				   "read:0x%x, wanted:0x%x\n",
+				   pci_name(asd_ha->pcidev),
+				   i, val, le32_to_cpu(*prog));
+			return -1;
+		}
+	}
+	ASD_DPRINTK("verified %d bytes, passed\n", size);
+	return 0;
+}
+
+/**
+ * asd_verify_lseq - verify the microcode of a link sequencer
+ * @asd_ha: pointer to host adapter structure
+ * @_prog: pointer to the microcode
+ * @size: size of the microcode in bytes
+ * @lseq: link sequencer of interest
+ *
+ * The link sequencer code is accessed in 4 KB pages, which are selected
+ * by setting LmRAMPAGE (bits 8 and 9) of the LmBISTCTL1 register.
+ * The 10 KB LSEQm instruction code is mapped, page at a time, at
+ * LmSEQRAM address.
+ */
+static int asd_verify_lseq(struct asd_ha_struct *asd_ha, const u8 *_prog,
+			   u32 size, int lseq)
+{
+#define LSEQ_CODEPAGE_SIZE 4096
+	int pages =  (size + LSEQ_CODEPAGE_SIZE - 1) / LSEQ_CODEPAGE_SIZE;
+	u32 page;
+	const u32 *prog = (u32 *) _prog;
+
+	for (page = 0; page < pages; page++) {
+		u32 i;
+
+		asd_write_reg_dword(asd_ha, LmBISTCTL1(lseq),
+				    page << LmRAMPAGE_LSHIFT);
+		for (i = 0; size > 0 && i < LSEQ_CODEPAGE_SIZE;
+		     i += 4, prog++, size-=4) {
+
+			u32 val = asd_read_reg_dword(asd_ha, LmSEQRAM(lseq)+i);
+
+			if (le32_to_cpu(*prog) != val) {
+				asd_printk("%s: LSEQ%d verify failed "
+					   "page:%d, offs:%d\n",
+					   pci_name(asd_ha->pcidev),
+					   lseq, page, i);
+				return -1;
+			}
+		}
+	}
+	ASD_DPRINTK("LSEQ%d verified %d bytes, passed\n", lseq,
+		    (int)((u8 *)prog-_prog));
+	return 0;
+}
+
+/**
+ * asd_verify_seq -- verify CSEQ/LSEQ microcode
+ * @asd_ha: pointer to host adapter structure
+ * @prog: pointer to microcode
+ * @size: size of the microcode
+ * @lseq_mask: if 0, verify CSEQ microcode, else mask of LSEQs of interest
+ *
+ * Return 0 if microcode is correct, negative on mismatch.
+ */
+static int asd_verify_seq(struct asd_ha_struct *asd_ha, const u8 *prog,
+			      u32 size, u8 lseq_mask)
+{
+	if (lseq_mask == 0)
+		return asd_verify_cseq(asd_ha, prog, size);
+	else {
+		int lseq, err;
+
+		for_each_sequencer(lseq_mask, lseq_mask, lseq) {
+			err = asd_verify_lseq(asd_ha, prog, size, lseq);
+			if (err)
+				return err;
+		}
+	}
+
+	return 0;
+}
+#define ASD_DMA_MODE_DOWNLOAD
+#ifdef ASD_DMA_MODE_DOWNLOAD
+/* This is the size of the CSEQ Mapped instruction page */
+#define MAX_DMA_OVLY_COUNT ((1U << 14)-1)
+static int asd_download_seq(struct asd_ha_struct *asd_ha,
+			    const u8 * const prog, u32 size, u8 lseq_mask)
+{
+	u32 comstaten;
+	u32 reg;
+	int page;
+	const int pages = (size + MAX_DMA_OVLY_COUNT - 1) / MAX_DMA_OVLY_COUNT;
+	struct asd_dma_tok *token;
+	int err = 0;
+
+	if (size % 4) {
+		asd_printk("sequencer program not multiple of 4\n");
+		return -1;
+	}
+
+	asd_pause_cseq(asd_ha);
+	asd_pause_lseq(asd_ha, 0xFF);
+
+	/* save, disable and clear interrupts */
+	comstaten = asd_read_reg_dword(asd_ha, COMSTATEN);
+	asd_write_reg_dword(asd_ha, COMSTATEN, 0);
+	asd_write_reg_dword(asd_ha, COMSTAT, COMSTAT_MASK);
+
+	asd_write_reg_dword(asd_ha, CHIMINTEN, RST_CHIMINTEN);
+	asd_write_reg_dword(asd_ha, CHIMINT, CHIMINT_MASK);
+
+	token = asd_alloc_coherent(asd_ha, MAX_DMA_OVLY_COUNT, GFP_KERNEL);
+	if (!token) {
+		asd_printk("out of memory for dma SEQ download\n");
+		err = -ENOMEM;
+		goto out;
+	}
+	ASD_DPRINTK("dma-ing %d bytes\n", size);
+
+	for (page = 0; page < pages; page++) {
+		int i;
+		u32 left = min(size-page*MAX_DMA_OVLY_COUNT,
+			       (u32)MAX_DMA_OVLY_COUNT);
+
+		memcpy(token->vaddr, prog + page*MAX_DMA_OVLY_COUNT, left);
+		asd_write_reg_addr(asd_ha, OVLYDMAADR, token->dma_handle);
+		asd_write_reg_dword(asd_ha, OVLYDMACNT, left);
+		reg = !page ? RESETOVLYDMA : 0;
+		reg |= (STARTOVLYDMA | OVLYHALTERR);
+		reg |= (lseq_mask ? (((u32)lseq_mask) << 8) : OVLYCSEQ);
+		/* Start DMA. */
+		asd_write_reg_dword(asd_ha, OVLYDMACTL, reg);
+
+		for (i = PAUSE_TRIES*100; i > 0; i--) {
+			u32 dmadone = asd_read_reg_dword(asd_ha, OVLYDMACTL);
+			if (!(dmadone & OVLYDMAACT))
+				break;
+			udelay(PAUSE_DELAY);
+		}
+	}
+
+	reg = asd_read_reg_dword(asd_ha, COMSTAT);
+	if (!(reg & OVLYDMADONE) || (reg & OVLYERR)
+	    || (asd_read_reg_dword(asd_ha, CHIMINT) & DEVEXCEPT_MASK)){
+		asd_printk("%s: error DMA-ing sequencer code\n",
+			   pci_name(asd_ha->pcidev));
+		err = -ENODEV;
+	}
+
+	asd_free_coherent(asd_ha, token);
+ out:
+	asd_write_reg_dword(asd_ha, COMSTATEN, comstaten);
+
+	return err ? : asd_verify_seq(asd_ha, prog, size, lseq_mask);
+}
+#else /* ASD_DMA_MODE_DOWNLOAD */
+static int asd_download_seq(struct asd_ha_struct *asd_ha, const u8 *_prog,
+			    u32 size, u8 lseq_mask)
+{
+	int i;
+	u32 reg = 0;
+	const u32 *prog = (u32 *) _prog;
+
+	if (size % 4) {
+		asd_printk("sequencer program not multiple of 4\n");
+		return -1;
+	}
+
+	asd_pause_cseq(asd_ha);
+	asd_pause_lseq(asd_ha, 0xFF);
+
+	reg |= (lseq_mask ? (((u32)lseq_mask) << 8) : OVLYCSEQ);
+	reg |= PIOCMODE;
+
+	asd_write_reg_dword(asd_ha, OVLYDMACNT, size);
+	asd_write_reg_dword(asd_ha, OVLYDMACTL, reg);
+
+	ASD_DPRINTK("downloading %s sequencer%s in PIO mode...\n",
+		    lseq_mask ? "LSEQ" : "CSEQ", lseq_mask ? "s" : "");
+
+	for (i = 0; i < size; i += 4, prog++)
+		asd_write_reg_dword(asd_ha, SPIODATA, *prog);
+
+	reg = (reg & ~PIOCMODE) | OVLYHALTERR;
+	asd_write_reg_dword(asd_ha, OVLYDMACTL, reg);
+
+	return asd_verify_seq(asd_ha, _prog, size, lseq_mask);
+}
+#endif /* ASD_DMA_MODE_DOWNLOAD */
+
+/**
+ * asd_seq_download_seqs - download the sequencer microcode
+ * @asd_ha: pointer to host adapter structure
+ *
+ * Download the central and link sequencer microcode.
+ */
+static int asd_seq_download_seqs(struct asd_ha_struct *asd_ha)
+{
+	int 	err;
+
+	if (!asd_ha->hw_prof.enabled_phys) {
+		asd_printk("%s: no enabled phys!\n", pci_name(asd_ha->pcidev));
+		return -ENODEV;
+	}
+
+	/* Download the CSEQ */
+	ASD_DPRINTK("downloading CSEQ...\n");
+	err = asd_download_seq(asd_ha, Cseq, sizeof(Cseq), 0);
+	if (err) {
+		asd_printk("CSEQ download failed:%d\n", err);
+		return err;
+	}
+
+	/* Download the Link Sequencers code. All of the Link Sequencers
+	 * microcode can be downloaded at the same time.
+	 */
+	ASD_DPRINTK("downloading LSEQs...\n");
+	err = asd_download_seq(asd_ha, Lseq, sizeof(Lseq),
+			       asd_ha->hw_prof.enabled_phys);
+	if (err) {
+		/* Try it one at a time */
+		u8 lseq;
+		u8 lseq_mask = asd_ha->hw_prof.enabled_phys;
+
+		for_each_sequencer(lseq_mask, lseq_mask, lseq) {
+			err = asd_download_seq(asd_ha, Lseq, sizeof(Lseq),
+					       1<<lseq);
+			if (err)
+				break;
+		}
+	}
+	if (err)
+		asd_printk("LSEQs download failed:%d\n", err);
+
+	return err;
+}
+
+/* ---------- Initializing the chip, chip memory, etc. ---------- */
+
+/**
+ * asd_init_cseq_mip - initialize CSEQ mode independent pages 4-7
+ * @asd_ha: pointer to host adapter structure
+ */
+static void asd_init_cseq_mip(struct asd_ha_struct *asd_ha)
+{
+	/* CSEQ Mode Independent, page 4 setup. */
+	asd_write_reg_word(asd_ha, CSEQ_Q_EXE_HEAD, 0xFFFF);
+	asd_write_reg_word(asd_ha, CSEQ_Q_EXE_TAIL, 0xFFFF);
+	asd_write_reg_word(asd_ha, CSEQ_Q_DONE_HEAD, 0xFFFF);
+	asd_write_reg_word(asd_ha, CSEQ_Q_DONE_TAIL, 0xFFFF);
+	asd_write_reg_word(asd_ha, CSEQ_Q_SEND_HEAD, 0xFFFF);
+	asd_write_reg_word(asd_ha, CSEQ_Q_SEND_TAIL, 0xFFFF);
+	asd_write_reg_word(asd_ha, CSEQ_Q_DMA2CHIM_HEAD, 0xFFFF);
+	asd_write_reg_word(asd_ha, CSEQ_Q_DMA2CHIM_TAIL, 0xFFFF);
+	asd_write_reg_word(asd_ha, CSEQ_Q_COPY_HEAD, 0xFFFF);
+	asd_write_reg_word(asd_ha, CSEQ_Q_COPY_TAIL, 0xFFFF);
+	asd_write_reg_word(asd_ha, CSEQ_REG0, 0);
+	asd_write_reg_word(asd_ha, CSEQ_REG1, 0);
+	asd_write_reg_dword(asd_ha, CSEQ_REG2, 0);
+	asd_write_reg_byte(asd_ha, CSEQ_LINK_CTL_Q_MAP, 0);
+	{
+		u8 con = asd_read_reg_byte(asd_ha, CCONEXIST);
+		u8 val = hweight8(con);
+		asd_write_reg_byte(asd_ha, CSEQ_MAX_CSEQ_MODE, (val<<4)|val);
+	}
+	asd_write_reg_word(asd_ha, CSEQ_FREE_LIST_HACK_COUNT, 0);
+
+	/* CSEQ Mode independent, page 5 setup. */
+	asd_write_reg_dword(asd_ha, CSEQ_EST_NEXUS_REQ_QUEUE, 0);
+	asd_write_reg_dword(asd_ha, CSEQ_EST_NEXUS_REQ_QUEUE+4, 0);
+	asd_write_reg_dword(asd_ha, CSEQ_EST_NEXUS_REQ_COUNT, 0);
+	asd_write_reg_dword(asd_ha, CSEQ_EST_NEXUS_REQ_COUNT+4, 0);
+	asd_write_reg_word(asd_ha, CSEQ_Q_EST_NEXUS_HEAD, 0xFFFF);
+	asd_write_reg_word(asd_ha, CSEQ_Q_EST_NEXUS_TAIL, 0xFFFF);
+	asd_write_reg_word(asd_ha, CSEQ_NEED_EST_NEXUS_SCB, 0);
+	asd_write_reg_byte(asd_ha, CSEQ_EST_NEXUS_REQ_HEAD, 0);
+	asd_write_reg_byte(asd_ha, CSEQ_EST_NEXUS_REQ_TAIL, 0);
+	asd_write_reg_byte(asd_ha, CSEQ_EST_NEXUS_SCB_OFFSET, 0);
+
+	/* CSEQ Mode independent, page 6 setup. */
+	asd_write_reg_word(asd_ha, CSEQ_INT_ROUT_RET_ADDR0, 0);
+	asd_write_reg_word(asd_ha, CSEQ_INT_ROUT_RET_ADDR1, 0);
+	asd_write_reg_word(asd_ha, CSEQ_INT_ROUT_SCBPTR, 0);
+	asd_write_reg_byte(asd_ha, CSEQ_INT_ROUT_MODE, 0);
+	asd_write_reg_byte(asd_ha, CSEQ_ISR_SCRATCH_FLAGS, 0);
+	asd_write_reg_word(asd_ha, CSEQ_ISR_SAVE_SINDEX, 0);
+	asd_write_reg_word(asd_ha, CSEQ_ISR_SAVE_DINDEX, 0);
+	asd_write_reg_word(asd_ha, CSEQ_Q_MONIRTT_HEAD, 0xFFFF);
+	asd_write_reg_word(asd_ha, CSEQ_Q_MONIRTT_TAIL, 0xFFFF);
+	/* Calculate the free scb mask. */
+	{
+		u16 cmdctx = asd_get_cmdctx_size(asd_ha);
+		cmdctx = (~((cmdctx/128)-1)) >> 8;
+		asd_write_reg_byte(asd_ha, CSEQ_FREE_SCB_MASK, (u8)cmdctx);
+	}
+	asd_write_reg_word(asd_ha, CSEQ_BUILTIN_FREE_SCB_HEAD,
+			   first_scb_site_no);
+	asd_write_reg_word(asd_ha, CSEQ_BUILTIN_FREE_SCB_TAIL,
+			   last_scb_site_no);
+	asd_write_reg_word(asd_ha, CSEQ_EXTENDED_FREE_SCB_HEAD, 0xFFFF);
+	asd_write_reg_word(asd_ha, CSEQ_EXTENDED_FREE_SCB_TAIL, 0xFFFF);
+
+	/* CSEQ Mode independent, page 7 setup. */
+	asd_write_reg_dword(asd_ha, CSEQ_EMPTY_REQ_QUEUE, 0);
+	asd_write_reg_dword(asd_ha, CSEQ_EMPTY_REQ_QUEUE+4, 0);
+	asd_write_reg_dword(asd_ha, CSEQ_EMPTY_REQ_COUNT, 0);
+	asd_write_reg_dword(asd_ha, CSEQ_EMPTY_REQ_COUNT+4, 0);
+	asd_write_reg_word(asd_ha, CSEQ_Q_EMPTY_HEAD, 0xFFFF);
+	asd_write_reg_word(asd_ha, CSEQ_Q_EMPTY_TAIL, 0xFFFF);
+	asd_write_reg_word(asd_ha, CSEQ_NEED_EMPTY_SCB, 0);
+	asd_write_reg_byte(asd_ha, CSEQ_EMPTY_REQ_HEAD, 0);
+	asd_write_reg_byte(asd_ha, CSEQ_EMPTY_REQ_TAIL, 0);
+	asd_write_reg_byte(asd_ha, CSEQ_EMPTY_SCB_OFFSET, 0);
+	asd_write_reg_word(asd_ha, CSEQ_PRIMITIVE_DATA, 0);
+	asd_write_reg_dword(asd_ha, CSEQ_TIMEOUT_CONST, 0);
+}
+
+/**
+ * asd_init_cseq_mdp - initialize CSEQ Mode dependent pages
+ * @asd_ha: pointer to host adapter structure
+ */
+static void asd_init_cseq_mdp(struct asd_ha_struct *asd_ha)
+{
+	int	i;
+	int	moffs;
+
+	moffs = CSEQ_PAGE_SIZE * 2;
+
+	/* CSEQ Mode dependent, modes 0-7, page 0 setup. */
+	for (i = 0; i < 8; i++) {
+		asd_write_reg_word(asd_ha, i*moffs+CSEQ_LRM_SAVE_SINDEX, 0);
+		asd_write_reg_word(asd_ha, i*moffs+CSEQ_LRM_SAVE_SCBPTR, 0);
+		asd_write_reg_word(asd_ha, i*moffs+CSEQ_Q_LINK_HEAD, 0xFFFF);
+		asd_write_reg_word(asd_ha, i*moffs+CSEQ_Q_LINK_TAIL, 0xFFFF);
+		asd_write_reg_byte(asd_ha, i*moffs+CSEQ_LRM_SAVE_SCRPAGE, 0);
+	}
+
+	/* CSEQ Mode dependent, mode 0-7, page 1 and 2 shall be ignored. */
+
+	/* CSEQ Mode dependent, mode 8, page 0 setup. */
+	asd_write_reg_word(asd_ha, CSEQ_RET_ADDR, 0xFFFF);
+	asd_write_reg_word(asd_ha, CSEQ_RET_SCBPTR, 0);
+	asd_write_reg_word(asd_ha, CSEQ_SAVE_SCBPTR, 0);
+	asd_write_reg_word(asd_ha, CSEQ_EMPTY_TRANS_CTX, 0);
+	asd_write_reg_word(asd_ha, CSEQ_RESP_LEN, 0);
+	asd_write_reg_word(asd_ha, CSEQ_TMF_SCBPTR, 0);
+	asd_write_reg_word(asd_ha, CSEQ_GLOBAL_PREV_SCB, 0);
+	asd_write_reg_word(asd_ha, CSEQ_GLOBAL_HEAD, 0);
+	asd_write_reg_word(asd_ha, CSEQ_CLEAR_LU_HEAD, 0);
+	asd_write_reg_byte(asd_ha, CSEQ_TMF_OPCODE, 0);
+	asd_write_reg_byte(asd_ha, CSEQ_SCRATCH_FLAGS, 0);
+	asd_write_reg_word(asd_ha, CSEQ_HSB_SITE, 0);
+	asd_write_reg_word(asd_ha, CSEQ_FIRST_INV_SCB_SITE,
+			   (u16)last_scb_site_no+1);
+	asd_write_reg_word(asd_ha, CSEQ_FIRST_INV_DDB_SITE,
+			   (u16)asd_ha->hw_prof.max_ddbs);
+
+	/* CSEQ Mode dependent, mode 8, page 1 setup. */
+	asd_write_reg_dword(asd_ha, CSEQ_LUN_TO_CLEAR, 0);
+	asd_write_reg_dword(asd_ha, CSEQ_LUN_TO_CLEAR + 4, 0);
+	asd_write_reg_dword(asd_ha, CSEQ_LUN_TO_CHECK, 0);
+	asd_write_reg_dword(asd_ha, CSEQ_LUN_TO_CHECK + 4, 0);
+
+	/* CSEQ Mode dependent, mode 8, page 2 setup. */
+	/* Tell the sequencer the bus address of the first SCB. */
+	asd_write_reg_addr(asd_ha, CSEQ_HQ_NEW_POINTER,
+			   asd_ha->seq.next_scb.dma_handle);
+	ASD_DPRINTK("First SCB dma_handle: 0x%08llx\n",
+		    (u64)asd_ha->seq.next_scb.dma_handle);
+
+	/* Tell the sequencer the first Done List entry address. */
+	asd_write_reg_addr(asd_ha, CSEQ_HQ_DONE_BASE,
+			   asd_ha->seq.actual_dl->dma_handle);
+
+	/* Initialize the Q_DONE_POINTER with the least significant
+	 * 4 bytes of the first Done List address. */
+	asd_write_reg_dword(asd_ha, CSEQ_HQ_DONE_POINTER,
+			    ASD_BUSADDR_LO(asd_ha->seq.actual_dl->dma_handle));
+
+	asd_write_reg_byte(asd_ha, CSEQ_HQ_DONE_PASS, ASD_DEF_DL_TOGGLE);
+
+	/* CSEQ Mode dependent, mode 8, page 3 shall be ignored. */
+}
+
+/**
+ * asd_init_cseq_scratch -- setup and init CSEQ
+ * @asd_ha: pointer to host adapter structure
+ *
+ * Setup and initialize Central sequencers. Initialiaze the mode
+ * independent and dependent scratch page to the default settings.
+ */
+static void asd_init_cseq_scratch(struct asd_ha_struct *asd_ha)
+{
+	asd_init_cseq_mip(asd_ha);
+	asd_init_cseq_mdp(asd_ha);
+}
+
+/**
+ * asd_init_lseq_mip -- initialize LSEQ Mode independent pages 0-3
+ * @asd_ha: pointer to host adapter structure
+ */
+static void asd_init_lseq_mip(struct asd_ha_struct *asd_ha, u8 lseq)
+{
+	int i;
+
+	/* LSEQ Mode independent page 0 setup. */
+	asd_write_reg_word(asd_ha, LmSEQ_Q_TGTXFR_HEAD(lseq), 0xFFFF);
+	asd_write_reg_word(asd_ha, LmSEQ_Q_TGTXFR_TAIL(lseq), 0xFFFF);
+	asd_write_reg_byte(asd_ha, LmSEQ_LINK_NUMBER(lseq), lseq);
+	asd_write_reg_byte(asd_ha, LmSEQ_SCRATCH_FLAGS(lseq),
+			   ASD_NOTIFY_ENABLE_SPINUP);
+	asd_write_reg_dword(asd_ha, LmSEQ_CONNECTION_STATE(lseq),0x08000000);
+	asd_write_reg_word(asd_ha, LmSEQ_CONCTL(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_CONSTAT(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_CONNECTION_MODES(lseq), 0);
+	asd_write_reg_word(asd_ha, LmSEQ_REG1_ISR(lseq), 0);
+	asd_write_reg_word(asd_ha, LmSEQ_REG2_ISR(lseq), 0);
+	asd_write_reg_word(asd_ha, LmSEQ_REG3_ISR(lseq), 0);
+	asd_write_reg_dword(asd_ha, LmSEQ_REG0_ISR(lseq), 0);
+	asd_write_reg_dword(asd_ha, LmSEQ_REG0_ISR(lseq)+4, 0);
+
+	/* LSEQ Mode independent page 1 setup. */
+	asd_write_reg_word(asd_ha, LmSEQ_EST_NEXUS_SCBPTR0(lseq), 0xFFFF);
+	asd_write_reg_word(asd_ha, LmSEQ_EST_NEXUS_SCBPTR1(lseq), 0xFFFF);
+	asd_write_reg_word(asd_ha, LmSEQ_EST_NEXUS_SCBPTR2(lseq), 0xFFFF);
+	asd_write_reg_word(asd_ha, LmSEQ_EST_NEXUS_SCBPTR3(lseq), 0xFFFF);
+	asd_write_reg_byte(asd_ha, LmSEQ_EST_NEXUS_SCB_OPCODE0(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_EST_NEXUS_SCB_OPCODE1(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_EST_NEXUS_SCB_OPCODE2(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_EST_NEXUS_SCB_OPCODE3(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_EST_NEXUS_SCB_HEAD(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_EST_NEXUS_SCB_TAIL(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_EST_NEXUS_BUF_AVAIL(lseq), 0);
+	asd_write_reg_dword(asd_ha, LmSEQ_TIMEOUT_CONST(lseq), 0);
+	asd_write_reg_word(asd_ha, LmSEQ_ISR_SAVE_SINDEX(lseq), 0);
+	asd_write_reg_word(asd_ha, LmSEQ_ISR_SAVE_DINDEX(lseq), 0);
+
+	/* LSEQ Mode Independent page 2 setup. */
+	asd_write_reg_word(asd_ha, LmSEQ_EMPTY_SCB_PTR0(lseq), 0xFFFF);
+	asd_write_reg_word(asd_ha, LmSEQ_EMPTY_SCB_PTR1(lseq), 0xFFFF);
+	asd_write_reg_word(asd_ha, LmSEQ_EMPTY_SCB_PTR2(lseq), 0xFFFF);
+	asd_write_reg_word(asd_ha, LmSEQ_EMPTY_SCB_PTR3(lseq), 0xFFFF);
+	asd_write_reg_byte(asd_ha, LmSEQ_EMPTY_SCB_OPCD0(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_EMPTY_SCB_OPCD1(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_EMPTY_SCB_OPCD2(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_EMPTY_SCB_OPCD3(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_EMPTY_SCB_HEAD(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_EMPTY_SCB_TAIL(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_EMPTY_BUFS_AVAIL(lseq), 0);
+	for (i = 0; i < 12; i += 4)
+		asd_write_reg_dword(asd_ha, LmSEQ_ATA_SCR_REGS(lseq) + i, 0);
+
+	/* LSEQ Mode Independent page 3 setup. */
+
+	/* Device present timer timeout */
+	asd_write_reg_dword(asd_ha, LmSEQ_DEV_PRES_TMR_TOUT_CONST(lseq),
+			    ASD_DEV_PRESENT_TIMEOUT);
+
+	/* SATA interlock timer disabled */
+	asd_write_reg_dword(asd_ha, LmSEQ_SATA_INTERLOCK_TIMEOUT(lseq),
+			    ASD_SATA_INTERLOCK_TIMEOUT);
+
+	/* STP shutdown timer timeout constant, IGNORED by the sequencer,
+	 * always 0. */
+	asd_write_reg_dword(asd_ha, LmSEQ_STP_SHUTDOWN_TIMEOUT(lseq),
+			    ASD_STP_SHUTDOWN_TIMEOUT);
+
+	asd_write_reg_dword(asd_ha, LmSEQ_SRST_ASSERT_TIMEOUT(lseq),
+			    ASD_SRST_ASSERT_TIMEOUT);
+
+	asd_write_reg_dword(asd_ha, LmSEQ_RCV_FIS_TIMEOUT(lseq),
+			    ASD_RCV_FIS_TIMEOUT);
+
+	asd_write_reg_dword(asd_ha, LmSEQ_ONE_MILLISEC_TIMEOUT(lseq),
+			    ASD_ONE_MILLISEC_TIMEOUT);
+
+	/* COM_INIT timer */
+	asd_write_reg_dword(asd_ha, LmSEQ_TEN_MS_COMINIT_TIMEOUT(lseq),
+			    ASD_TEN_MILLISEC_TIMEOUT);
+
+	asd_write_reg_dword(asd_ha, LmSEQ_SMP_RCV_TIMEOUT(lseq),
+			    ASD_SMP_RCV_TIMEOUT);
+}
+
+/**
+ * asd_init_lseq_mdp -- initialize LSEQ mode dependent pages.
+ * @asd_ha: pointer to host adapter structure
+ */
+static void asd_init_lseq_mdp(struct asd_ha_struct *asd_ha,  int lseq)
+{
+	int    i;
+	u32    moffs;
+	static const u16 ret_addr[] = {
+		0xFFFF,		  /* mode 0 */
+		0xFFFF,		  /* mode 1 */
+		MODE2_TASK,	  /* mode 2 */
+		0,
+		0xFFFF,		  /* mode 4/5 */
+		0xFFFF,		  /* mode 4/5 */
+	};
+
+	/*
+	 * Mode 0,1,2 and 4/5 have common field on page 0 for the first
+	 * 14 bytes.
+	 */
+	for (i = 0; i < 3; i++) {
+		moffs = i * LSEQ_MODE_SCRATCH_SIZE;
+		asd_write_reg_word(asd_ha, LmSEQ_RET_ADDR(lseq)+moffs,
+				   ret_addr[i]);
+		asd_write_reg_word(asd_ha, LmSEQ_REG0_MODE(lseq)+moffs, 0);
+		asd_write_reg_word(asd_ha, LmSEQ_MODE_FLAGS(lseq)+moffs, 0);
+		asd_write_reg_word(asd_ha, LmSEQ_RET_ADDR2(lseq)+moffs,0xFFFF);
+		asd_write_reg_word(asd_ha, LmSEQ_RET_ADDR1(lseq)+moffs,0xFFFF);
+		asd_write_reg_byte(asd_ha, LmSEQ_OPCODE_TO_CSEQ(lseq)+moffs,0);
+		asd_write_reg_word(asd_ha, LmSEQ_DATA_TO_CSEQ(lseq)+moffs,0);
+	}
+	/*
+	 *  Mode 5 page 0 overlaps the same scratch page with Mode 0 page 3.
+	 */
+	asd_write_reg_word(asd_ha,
+			 LmSEQ_RET_ADDR(lseq)+LSEQ_MODE5_PAGE0_OFFSET,
+			   ret_addr[5]);
+	asd_write_reg_word(asd_ha,
+			 LmSEQ_REG0_MODE(lseq)+LSEQ_MODE5_PAGE0_OFFSET,0);
+	asd_write_reg_word(asd_ha,
+			 LmSEQ_MODE_FLAGS(lseq)+LSEQ_MODE5_PAGE0_OFFSET, 0);
+	asd_write_reg_word(asd_ha,
+			 LmSEQ_RET_ADDR2(lseq)+LSEQ_MODE5_PAGE0_OFFSET,0xFFFF);
+	asd_write_reg_word(asd_ha,
+			 LmSEQ_RET_ADDR1(lseq)+LSEQ_MODE5_PAGE0_OFFSET,0xFFFF);
+	asd_write_reg_byte(asd_ha,
+		         LmSEQ_OPCODE_TO_CSEQ(lseq)+LSEQ_MODE5_PAGE0_OFFSET,0);
+	asd_write_reg_word(asd_ha,
+		         LmSEQ_DATA_TO_CSEQ(lseq)+LSEQ_MODE5_PAGE0_OFFSET, 0);
+
+	/* LSEQ Mode dependent 0, page 0 setup. */
+	asd_write_reg_word(asd_ha, LmSEQ_FIRST_INV_DDB_SITE(lseq),
+			   (u16)asd_ha->hw_prof.max_ddbs);
+	asd_write_reg_word(asd_ha, LmSEQ_EMPTY_TRANS_CTX(lseq), 0);
+	asd_write_reg_word(asd_ha, LmSEQ_RESP_LEN(lseq), 0);
+	asd_write_reg_word(asd_ha, LmSEQ_FIRST_INV_SCB_SITE(lseq),
+			   (u16)last_scb_site_no+1);
+	asd_write_reg_dword(asd_ha, LmSEQ_INTEN_SAVE(lseq),
+			    (u32) LmM0INTEN_MASK);
+	asd_write_reg_byte(asd_ha, LmSEQ_LINK_RST_FRM_LEN(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_LINK_RST_PROTOCOL(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_RESP_STATUS(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_LAST_LOADED_SGE(lseq), 0);
+	asd_write_reg_word(asd_ha, LmSEQ_SAVE_SCBPTR(lseq), 0);
+
+	/* LSEQ mode dependent, mode 1, page 0 setup. */
+	asd_write_reg_word(asd_ha, LmSEQ_Q_XMIT_HEAD(lseq), 0xFFFF);
+	asd_write_reg_word(asd_ha, LmSEQ_M1_EMPTY_TRANS_CTX(lseq), 0);
+	asd_write_reg_word(asd_ha, LmSEQ_INI_CONN_TAG(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_FAILED_OPEN_STATUS(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_XMIT_REQUEST_TYPE(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_M1_RESP_STATUS(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_M1_LAST_LOADED_SGE(lseq), 0);
+	asd_write_reg_word(asd_ha, LmSEQ_M1_SAVE_SCBPTR(lseq), 0);
+
+	/* LSEQ Mode dependent mode 2, page 0 setup */
+	asd_write_reg_word(asd_ha, LmSEQ_PORT_COUNTER(lseq), 0);
+	asd_write_reg_word(asd_ha, LmSEQ_PM_TABLE_PTR(lseq), 0);
+	asd_write_reg_word(asd_ha, LmSEQ_SATA_INTERLOCK_TMR_SAVE(lseq), 0);
+	asd_write_reg_word(asd_ha, LmSEQ_IP_BITL(lseq), 0);
+	asd_write_reg_word(asd_ha, LmSEQ_COPY_SMP_CONN_TAG(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_P0M2_OFFS1AH(lseq), 0);
+
+	/* LSEQ Mode dependent, mode 4/5, page 0 setup. */
+	asd_write_reg_byte(asd_ha, LmSEQ_SAVED_OOB_STATUS(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_SAVED_OOB_MODE(lseq), 0);
+	asd_write_reg_word(asd_ha, LmSEQ_Q_LINK_HEAD(lseq), 0xFFFF);
+	asd_write_reg_byte(asd_ha, LmSEQ_LINK_RST_ERR(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_SAVED_OOB_SIGNALS(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_SAS_RESET_MODE(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_LINK_RESET_RETRY_COUNT(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_NUM_LINK_RESET_RETRIES(lseq), 0);
+	asd_write_reg_word(asd_ha, LmSEQ_OOB_INT_ENABLES(lseq), 0);
+	/*
+	 * Set the desired interval between transmissions of the NOTIFY
+	 * (ENABLE SPINUP) primitive.  Must be initilized to val - 1.
+	 */
+	asd_write_reg_word(asd_ha, LmSEQ_NOTIFY_TIMER_TIMEOUT(lseq),
+			   ASD_NOTIFY_TIMEOUT - 1);
+	/* No delay for the first NOTIFY to be sent to the attached target. */
+	asd_write_reg_word(asd_ha, LmSEQ_NOTIFY_TIMER_DOWN_COUNT(lseq),
+			   ASD_NOTIFY_DOWN_COUNT);
+
+	/* LSEQ Mode dependent, mode 0 and 1, page 1 setup. */
+	for (i = 0; i < 2; i++)	{
+		int j;
+		/* Start from Page 1 of Mode 0 and 1. */
+		moffs = LSEQ_PAGE_SIZE + i*LSEQ_MODE_SCRATCH_SIZE;
+		/* All the fields of page 1 can be intialized to 0. */
+		for (j = 0; j < LSEQ_PAGE_SIZE; j += 4)
+			asd_write_reg_dword(asd_ha, LmSCRATCH(lseq)+moffs+j,0);
+	}
+
+	/* LSEQ Mode dependent, mode 2, page 1 setup. */
+	asd_write_reg_dword(asd_ha, LmSEQ_INVALID_DWORD_COUNT(lseq), 0);
+	asd_write_reg_dword(asd_ha, LmSEQ_DISPARITY_ERROR_COUNT(lseq), 0);
+	asd_write_reg_dword(asd_ha, LmSEQ_LOSS_OF_SYNC_COUNT(lseq), 0);
+
+	/* LSEQ Mode dependent, mode 4/5, page 1. */
+	for (i = 0; i < LSEQ_PAGE_SIZE; i+=4)
+		asd_write_reg_dword(asd_ha, LmSEQ_FRAME_TYPE_MASK(lseq)+i, 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_FRAME_TYPE_MASK(lseq), 0xFF);
+	asd_write_reg_byte(asd_ha, LmSEQ_HASHED_DEST_ADDR_MASK(lseq), 0xFF);
+	asd_write_reg_byte(asd_ha, LmSEQ_HASHED_DEST_ADDR_MASK(lseq)+1,0xFF);
+	asd_write_reg_byte(asd_ha, LmSEQ_HASHED_DEST_ADDR_MASK(lseq)+2,0xFF);
+	asd_write_reg_byte(asd_ha, LmSEQ_HASHED_SRC_ADDR_MASK(lseq), 0xFF);
+	asd_write_reg_byte(asd_ha, LmSEQ_HASHED_SRC_ADDR_MASK(lseq)+1, 0xFF);
+	asd_write_reg_byte(asd_ha, LmSEQ_HASHED_SRC_ADDR_MASK(lseq)+2, 0xFF);
+	asd_write_reg_dword(asd_ha, LmSEQ_DATA_OFFSET(lseq), 0xFFFFFFFF);
+
+	/* LSEQ Mode dependent, mode 0, page 2 setup. */
+	asd_write_reg_dword(asd_ha, LmSEQ_SMP_RCV_TIMER_TERM_TS(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_DEVICE_BITS(lseq), 0);
+	asd_write_reg_word(asd_ha, LmSEQ_SDB_DDB(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_SDB_NUM_TAGS(lseq), 0);
+	asd_write_reg_byte(asd_ha, LmSEQ_SDB_CURR_TAG(lseq), 0);
+
+	/* LSEQ Mode Dependent 1, page 2 setup. */
+	asd_write_reg_dword(asd_ha, LmSEQ_TX_ID_ADDR_FRAME(lseq), 0);
+	asd_write_reg_dword(asd_ha, LmSEQ_TX_ID_ADDR_FRAME(lseq)+4, 0);
+	asd_write_reg_dword(asd_ha, LmSEQ_OPEN_TIMER_TERM_TS(lseq), 0);
+	asd_write_reg_dword(asd_ha, LmSEQ_SRST_AS_TIMER_TERM_TS(lseq), 0);
+	asd_write_reg_dword(asd_ha, LmSEQ_LAST_LOADED_SG_EL(lseq), 0);
+
+	/* LSEQ Mode Dependent 2, page 2 setup. */
+	/* The LmSEQ_STP_SHUTDOWN_TIMER_TERM_TS is IGNORED by the sequencer,
+	 * i.e. always 0. */
+	asd_write_reg_dword(asd_ha, LmSEQ_STP_SHUTDOWN_TIMER_TERM_TS(lseq),0);
+	asd_write_reg_dword(asd_ha, LmSEQ_CLOSE_TIMER_TERM_TS(lseq), 0);
+	asd_write_reg_dword(asd_ha, LmSEQ_BREAK_TIMER_TERM_TS(lseq), 0);
+	asd_write_reg_dword(asd_ha, LmSEQ_DWS_RESET_TIMER_TERM_TS(lseq), 0);
+	asd_write_reg_dword(asd_ha,LmSEQ_SATA_INTERLOCK_TIMER_TERM_TS(lseq),0);
+	asd_write_reg_dword(asd_ha, LmSEQ_MCTL_TIMER_TERM_TS(lseq), 0);
+
+	/* LSEQ Mode Dependent 4/5, page 2 setup. */
+	asd_write_reg_dword(asd_ha, LmSEQ_COMINIT_TIMER_TERM_TS(lseq), 0);
+	asd_write_reg_dword(asd_ha, LmSEQ_RCV_ID_TIMER_TERM_TS(lseq), 0);
+	asd_write_reg_dword(asd_ha, LmSEQ_RCV_FIS_TIMER_TERM_TS(lseq), 0);
+	asd_write_reg_dword(asd_ha, LmSEQ_DEV_PRES_TIMER_TERM_TS(lseq),	0);
+}
+
+/**
+ * asd_init_lseq_scratch -- setup and init link sequencers
+ * @asd_ha: pointer to host adapter struct
+ */
+static void asd_init_lseq_scratch(struct asd_ha_struct *asd_ha)
+{
+	u8 lseq;
+	u8 lseq_mask;
+
+	lseq_mask = asd_ha->hw_prof.enabled_phys;
+	for_each_sequencer(lseq_mask, lseq_mask, lseq) {
+		asd_init_lseq_mip(asd_ha, lseq);
+		asd_init_lseq_mdp(asd_ha, lseq);
+	}
+}
+
+/**
+ * asd_init_scb_sites -- initialize sequencer SCB sites (memory).
+ * @asd_ha: pointer to host adapter structure
+ *
+ * This should be done before initializing common CSEQ and LSEQ
+ * scratch since those areas depend on some computed values here,
+ * last_scb_site_no, etc.
+ */
+static void asd_init_scb_sites(struct asd_ha_struct *asd_ha)
+{
+	u16	site_no;
+	u16     max_scbs = 0;
+
+	for (site_no = asd_ha->hw_prof.max_scbs-1;
+	     site_no != (u16) -1;
+	     site_no--) {
+		u16	i;
+
+		/* Initialize all fields in the SCB site to 0. */
+		for (i = 0; i < ASD_SCB_SIZE; i += 4)
+			asd_scbsite_write_dword(asd_ha, site_no, i, 0);
+
+		/* Workaround needed by SEQ to fix a SATA issue is to exclude
+		 * certain SCB sites from the free list. */
+		if (!SCB_SITE_VALID(site_no))
+			continue;
+
+		if (last_scb_site_no == 0)
+			last_scb_site_no = site_no;
+
+		/* For every SCB site, we need to initialize the
+		 * following fields: Q_NEXT, SCB_OPCODE, SCB_FLAGS,
+		 * and SG Element Flag. */
+
+		/* Q_NEXT field of the last SCB is invalidated. */
+		asd_scbsite_write_word(asd_ha, site_no, 0, first_scb_site_no);
+
+		/* Initialize SCB Site Opcode field to invalid. */
+		asd_scbsite_write_byte(asd_ha, site_no,
+				       offsetof(struct scb_header, opcode),
+				       0xFF);
+
+		/* Initialize SCB Site Flags field to mean a response
+		 * frame has been received.  This means inadvertent
+		 * frames received to be dropped. */
+		asd_scbsite_write_byte(asd_ha, site_no, 0x49, 0x01);
+
+		first_scb_site_no = site_no;
+		max_scbs++;
+	}
+	asd_ha->hw_prof.max_scbs = max_scbs;
+	ASD_DPRINTK("max_scbs:%d\n", asd_ha->hw_prof.max_scbs);
+	ASD_DPRINTK("first_scb_site_no:0x%x\n", first_scb_site_no);
+	ASD_DPRINTK("last_scb_site_no:0x%x\n", last_scb_site_no);
+}
+
+/**
+ * asd_init_cseq_cio - initialize CSEQ CIO registers
+ * @asd_ha: pointer to host adapter structure
+ */
+static void asd_init_cseq_cio(struct asd_ha_struct *asd_ha)
+{
+	int i;
+
+	asd_write_reg_byte(asd_ha, CSEQCOMINTEN, 0);
+	asd_write_reg_byte(asd_ha, CSEQDLCTL, ASD_DL_SIZE_BITS);
+	asd_write_reg_byte(asd_ha, CSEQDLOFFS, 0);
+	asd_write_reg_byte(asd_ha, CSEQDLOFFS+1, 0);
+	asd_ha->seq.scbpro = 0;
+	asd_write_reg_dword(asd_ha, SCBPRO, 0);
+	asd_write_reg_dword(asd_ha, CSEQCON, 0);
+
+	/* Intialize CSEQ Mode 11 Interrupt Vectors.
+	 * The addresses are 16 bit wide and in dword units.
+	 * The values of their macros are in byte units.
+	 * Thus we have to divide by 4. */
+	asd_write_reg_word(asd_ha, CM11INTVEC0, CSEQ_INT_VEC0);
+	asd_write_reg_word(asd_ha, CM11INTVEC1, CSEQ_INT_VEC1);
+	asd_write_reg_word(asd_ha, CM11INTVEC2, CSEQ_INT_VEC2);
+
+	/* Enable ARP2HALTC (ARP2 Halted from Halt Code Write). */
+	asd_write_reg_byte(asd_ha, CARP2INTEN, EN_ARP2HALTC);
+
+	/* Initialize CSEQ Scratch Page to 0x04. */
+	asd_write_reg_byte(asd_ha, CSCRATCHPAGE, 0x04);
+
+	/* Initialize CSEQ Mode[0-8] Dependent registers. */
+	/* Initialize Scratch Page to 0. */
+	for (i = 0; i < 9; i++)
+		asd_write_reg_byte(asd_ha, CMnSCRATCHPAGE(i), 0);
+
+	/* Reset the ARP2 Program Count. */
+	asd_write_reg_word(asd_ha, CPRGMCNT, CSEQ_IDLE_LOOP_ENTRY);
+
+	for (i = 0; i < 8; i++) {
+		/* Intialize Mode n Link m Interrupt Enable. */
+		asd_write_reg_dword(asd_ha, CMnINTEN(i), EN_CMnRSPMBXF);
+		/* Initialize Mode n Request Mailbox. */
+		asd_write_reg_dword(asd_ha, CMnREQMBX(i), 0);
+	}
+}
+
+/**
+ * asd_init_lseq_cio -- initialize LmSEQ CIO registers
+ * @asd_ha: pointer to host adapter structure
+ */
+static void asd_init_lseq_cio(struct asd_ha_struct *asd_ha, int lseq)
+{
+	u8  *sas_addr;
+	int  i;
+
+	/* Enable ARP2HALTC (ARP2 Halted from Halt Code Write). */
+	asd_write_reg_dword(asd_ha, LmARP2INTEN(lseq), EN_ARP2HALTC);
+
+	asd_write_reg_byte(asd_ha, LmSCRATCHPAGE(lseq), 0);
+
+	/* Initialize Mode 0,1, and 2 SCRATCHPAGE to 0. */
+	for (i = 0; i < 3; i++)
+		asd_write_reg_byte(asd_ha, LmMnSCRATCHPAGE(lseq, i), 0);
+
+	/* Initialize Mode 5 SCRATCHPAGE to 0. */
+	asd_write_reg_byte(asd_ha, LmMnSCRATCHPAGE(lseq, 5), 0);
+
+	asd_write_reg_dword(asd_ha, LmRSPMBX(lseq), 0);
+	/* Initialize Mode 0,1,2 and 5 Interrupt Enable and
+	 * Interrupt registers. */
+	asd_write_reg_dword(asd_ha, LmMnINTEN(lseq, 0), LmM0INTEN_MASK);
+	asd_write_reg_dword(asd_ha, LmMnINT(lseq, 0), 0xFFFFFFFF);
+	/* Mode 1 */
+	asd_write_reg_dword(asd_ha, LmMnINTEN(lseq, 1), LmM1INTEN_MASK);
+	asd_write_reg_dword(asd_ha, LmMnINT(lseq, 1), 0xFFFFFFFF);
+	/* Mode 2 */
+	asd_write_reg_dword(asd_ha, LmMnINTEN(lseq, 2), LmM2INTEN_MASK);
+	asd_write_reg_dword(asd_ha, LmMnINT(lseq, 2), 0xFFFFFFFF);
+	/* Mode 5 */
+	asd_write_reg_dword(asd_ha, LmMnINTEN(lseq, 5), LmM5INTEN_MASK);
+	asd_write_reg_dword(asd_ha, LmMnINT(lseq, 5), 0xFFFFFFFF);
+
+	/* Enable HW Timer status. */
+	asd_write_reg_byte(asd_ha, LmHWTSTATEN(lseq), LmHWTSTATEN_MASK);
+
+	/* Enable Primitive Status 0 and 1. */
+	asd_write_reg_dword(asd_ha, LmPRIMSTAT0EN(lseq), LmPRIMSTAT0EN_MASK);
+	asd_write_reg_dword(asd_ha, LmPRIMSTAT1EN(lseq), LmPRIMSTAT1EN_MASK);
+
+	/* Enable Frame Error. */
+	asd_write_reg_dword(asd_ha, LmFRMERREN(lseq), LmFRMERREN_MASK);
+	asd_write_reg_byte(asd_ha, LmMnHOLDLVL(lseq, 0), 0x50);
+
+	/* Initialize Mode 0 Transfer Level to 512. */
+	asd_write_reg_byte(asd_ha,  LmMnXFRLVL(lseq, 0), LmMnXFRLVL_512);
+	/* Initialize Mode 1 Transfer Level to 256. */
+	asd_write_reg_byte(asd_ha, LmMnXFRLVL(lseq, 1), LmMnXFRLVL_256);
+
+	/* Initialize Program Count. */
+	asd_write_reg_word(asd_ha, LmPRGMCNT(lseq), LSEQ_IDLE_LOOP_ENTRY);
+
+	/* Enable Blind SG Move. */
+	asd_write_reg_dword(asd_ha, LmMODECTL(lseq), LmBLIND48);
+	asd_write_reg_word(asd_ha, LmM3SATATIMER(lseq),
+			   ASD_SATA_INTERLOCK_TIMEOUT);
+
+	(void) asd_read_reg_dword(asd_ha, LmREQMBX(lseq));
+
+	/* Clear Primitive Status 0 and 1. */
+	asd_write_reg_dword(asd_ha, LmPRMSTAT0(lseq), 0xFFFFFFFF);
+	asd_write_reg_dword(asd_ha, LmPRMSTAT1(lseq), 0xFFFFFFFF);
+
+	/* Clear HW Timer status. */
+	asd_write_reg_byte(asd_ha, LmHWTSTAT(lseq), 0xFF);
+
+	/* Clear DMA Errors for Mode 0 and 1. */
+	asd_write_reg_byte(asd_ha, LmMnDMAERRS(lseq, 0), 0xFF);
+	asd_write_reg_byte(asd_ha, LmMnDMAERRS(lseq, 1), 0xFF);
+
+	/* Clear SG DMA Errors for Mode 0 and 1. */
+	asd_write_reg_byte(asd_ha, LmMnSGDMAERRS(lseq, 0), 0xFF);
+	asd_write_reg_byte(asd_ha, LmMnSGDMAERRS(lseq, 1), 0xFF);
+
+	/* Clear Mode 0 Buffer Parity Error. */
+	asd_write_reg_byte(asd_ha, LmMnBUFSTAT(lseq, 0), LmMnBUFPERR);
+
+	/* Clear Mode 0 Frame Error register. */
+	asd_write_reg_dword(asd_ha, LmMnFRMERR(lseq, 0), 0xFFFFFFFF);
+
+	/* Reset LSEQ external interrupt arbiter. */
+	asd_write_reg_byte(asd_ha, LmARP2INTCTL(lseq), RSTINTCTL);
+
+	/* Set the Phy SAS for the LmSEQ WWN. */
+	sas_addr = asd_ha->phys[lseq].phy_desc->sas_addr;
+	for (i = 0; i < SAS_ADDR_SIZE; i++)
+		asd_write_reg_byte(asd_ha, LmWWN(lseq) + i, sas_addr[i]);
+
+	/* Set the Transmit Size to 1024 bytes, 0 = 256 Dwords. */
+	asd_write_reg_byte(asd_ha, LmMnXMTSIZE(lseq, 1), 0);
+
+	/* Set the Bus Inactivity Time Limit Timer. */
+	asd_write_reg_word(asd_ha, LmBITL_TIMER(lseq), 9);
+
+	/* Enable SATA Port Multiplier. */
+	asd_write_reg_byte(asd_ha, LmMnSATAFS(lseq, 1), 0x80);
+
+	/* Initialize Interrupt Vector[0-10] address in Mode 3.
+	 * See the comment on CSEQ_INT_* */
+	asd_write_reg_word(asd_ha, LmM3INTVEC0(lseq), LSEQ_INT_VEC0);
+	asd_write_reg_word(asd_ha, LmM3INTVEC1(lseq), LSEQ_INT_VEC1);
+	asd_write_reg_word(asd_ha, LmM3INTVEC2(lseq), LSEQ_INT_VEC2);
+	asd_write_reg_word(asd_ha, LmM3INTVEC3(lseq), LSEQ_INT_VEC3);
+	asd_write_reg_word(asd_ha, LmM3INTVEC4(lseq), LSEQ_INT_VEC4);
+	asd_write_reg_word(asd_ha, LmM3INTVEC5(lseq), LSEQ_INT_VEC5);
+	asd_write_reg_word(asd_ha, LmM3INTVEC6(lseq), LSEQ_INT_VEC6);
+	asd_write_reg_word(asd_ha, LmM3INTVEC7(lseq), LSEQ_INT_VEC7);
+	asd_write_reg_word(asd_ha, LmM3INTVEC8(lseq), LSEQ_INT_VEC8);
+	asd_write_reg_word(asd_ha, LmM3INTVEC9(lseq), LSEQ_INT_VEC9);
+	asd_write_reg_word(asd_ha, LmM3INTVEC10(lseq), LSEQ_INT_VEC10);
+	/*
+	 * Program the Link LED control, applicable only for
+	 * Chip Rev. B or later.
+	 */
+	asd_write_reg_dword(asd_ha, LmCONTROL(lseq),
+			    (LEDTIMER | LEDMODE_TXRX | LEDTIMERS_100ms));
+
+	/* Set the Align Rate for SAS and STP mode. */
+	asd_write_reg_byte(asd_ha, LmM1SASALIGN(lseq), SAS_ALIGN_DEFAULT);
+	asd_write_reg_byte(asd_ha, LmM1STPALIGN(lseq), STP_ALIGN_DEFAULT);
+}
+
+
+/**
+ * asd_post_init_cseq -- clear CSEQ Mode n Int. status and Response mailbox
+ * @asd_ha: pointer to host adapter struct
+ */
+static void asd_post_init_cseq(struct asd_ha_struct *asd_ha)
+{
+	int i;
+
+	for (i = 0; i < 8; i++)
+		asd_write_reg_dword(asd_ha, CMnINT(i), 0xFFFFFFFF);
+	for (i = 0; i < 8; i++)
+		asd_read_reg_dword(asd_ha, CMnRSPMBX(i));
+	/* Reset the external interrupt arbiter. */
+	asd_write_reg_byte(asd_ha, CARP2INTCTL, RSTINTCTL);
+}
+
+/**
+ * asd_init_ddb_0 -- initialize DDB 0
+ * @asd_ha: pointer to host adapter structure
+ *
+ * Initialize DDB site 0 which is used internally by the sequencer.
+ */
+static void asd_init_ddb_0(struct asd_ha_struct *asd_ha)
+{
+	int	i;
+
+	/* Zero out the DDB explicitly */
+	for (i = 0; i < sizeof(struct asd_ddb_seq_shared); i+=4)
+		asd_ddbsite_write_dword(asd_ha, 0, i, 0);
+
+	asd_ddbsite_write_word(asd_ha, 0,
+		 offsetof(struct asd_ddb_seq_shared, q_free_ddb_head), 0);
+	asd_ddbsite_write_word(asd_ha, 0,
+		 offsetof(struct asd_ddb_seq_shared, q_free_ddb_tail),
+			       asd_ha->hw_prof.max_ddbs-1);
+	asd_ddbsite_write_word(asd_ha, 0,
+		 offsetof(struct asd_ddb_seq_shared, q_free_ddb_cnt), 0);
+	asd_ddbsite_write_word(asd_ha, 0,
+		 offsetof(struct asd_ddb_seq_shared, q_used_ddb_head), 0xFFFF);
+	asd_ddbsite_write_word(asd_ha, 0,
+		 offsetof(struct asd_ddb_seq_shared, q_used_ddb_tail), 0xFFFF);
+	asd_ddbsite_write_word(asd_ha, 0,
+		 offsetof(struct asd_ddb_seq_shared, shared_mem_lock), 0);
+	asd_ddbsite_write_word(asd_ha, 0,
+		 offsetof(struct asd_ddb_seq_shared, smp_conn_tag), 0);
+	asd_ddbsite_write_word(asd_ha, 0,
+		 offsetof(struct asd_ddb_seq_shared, est_nexus_buf_cnt), 0);
+	asd_ddbsite_write_word(asd_ha, 0,
+		 offsetof(struct asd_ddb_seq_shared, est_nexus_buf_thresh),
+			       asd_ha->hw_prof.num_phys * 2);
+	asd_ddbsite_write_byte(asd_ha, 0,
+		 offsetof(struct asd_ddb_seq_shared, settable_max_contexts),0);
+	asd_ddbsite_write_byte(asd_ha, 0,
+	       offsetof(struct asd_ddb_seq_shared, conn_not_active), 0xFF);
+	asd_ddbsite_write_byte(asd_ha, 0,
+	       offsetof(struct asd_ddb_seq_shared, phy_is_up), 0x00);
+	/* DDB 0 is reserved */
+	set_bit(0, asd_ha->hw_prof.ddb_bitmap);
+}
+
+/**
+ * asd_seq_setup_seqs -- setup and initialize central and link sequencers
+ * @asd_ha: pointer to host adapter structure
+ */
+static void asd_seq_setup_seqs(struct asd_ha_struct *asd_ha)
+{
+	int 		lseq;
+	u8		lseq_mask;
+
+	/* Initialize SCB sites. Done first to compute some values which
+	 * the rest of the init code depends on. */
+	asd_init_scb_sites(asd_ha);
+
+	/* Initialize CSEQ Scratch RAM registers. */
+	asd_init_cseq_scratch(asd_ha);
+
+	/* Initialize LmSEQ Scratch RAM registers. */
+	asd_init_lseq_scratch(asd_ha);
+
+	/* Initialize CSEQ CIO registers. */
+	asd_init_cseq_cio(asd_ha);
+
+	asd_init_ddb_0(asd_ha);
+
+	/* Initialize LmSEQ CIO registers. */
+	lseq_mask = asd_ha->hw_prof.enabled_phys;
+	for_each_sequencer(lseq_mask, lseq_mask, lseq)
+		asd_init_lseq_cio(asd_ha, lseq);
+	asd_post_init_cseq(asd_ha);
+}
+
+
+/**
+ * asd_seq_start_cseq -- start the central sequencer, CSEQ
+ * @asd_ha: pointer to host adapter structure
+ */
+static int asd_seq_start_cseq(struct asd_ha_struct *asd_ha)
+{
+	/* Reset the ARP2 instruction to location zero. */
+	asd_write_reg_word(asd_ha, CPRGMCNT, CSEQ_IDLE_LOOP_ENTRY);
+
+	/* Unpause the CSEQ  */
+	return asd_unpause_cseq(asd_ha);
+}
+
+/**
+ * asd_seq_start_lseq -- start a link sequencer
+ * @asd_ha: pointer to host adapter structure
+ * @lseq: the link sequencer of interest
+ */
+static int asd_seq_start_lseq(struct asd_ha_struct *asd_ha, int lseq)
+{
+	/* Reset the ARP2 instruction to location zero. */
+	asd_write_reg_word(asd_ha, LmPRGMCNT(lseq), LSEQ_IDLE_LOOP_ENTRY);
+
+	/* Unpause the LmSEQ  */
+	return asd_seq_unpause_lseq(asd_ha, lseq);
+}
+
+int asd_init_seqs(struct asd_ha_struct *asd_ha)
+{
+	int err;
+
+	asd_printk("using sequencer %s\n", SAS_RAZOR_SEQUENCER_VERSION);
+	err = asd_seq_download_seqs(asd_ha);
+	if (err) {
+		asd_printk("couldn't download sequencers for %s\n",
+			   pci_name(asd_ha->pcidev));
+		return err;
+	}
+
+	asd_seq_setup_seqs(asd_ha);
+
+	return 0;
+}
+
+int asd_start_seqs(struct asd_ha_struct *asd_ha)
+{
+	int err;
+	u8  lseq_mask;
+	int lseq;
+
+	err = asd_seq_start_cseq(asd_ha);
+	if (err) {
+		asd_printk("couldn't start CSEQ for %s\n",
+			   pci_name(asd_ha->pcidev));
+		return err;
+	}
+
+	lseq_mask = asd_ha->hw_prof.enabled_phys;
+	for_each_sequencer(lseq_mask, lseq_mask, lseq) {
+		err = asd_seq_start_lseq(asd_ha, lseq);
+		if (err) {
+			asd_printk("coudln't start LSEQ %d for %s\n", lseq,
+				   pci_name(asd_ha->pcidev));
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * asd_update_port_links -- update port_map_by_links and phy_is_up
+ * @sas_phy: pointer to the phy which has been added to a port
+ *
+ * 1) When a link reset has completed and we got BYTES DMAED with a
+ * valid frame we call this function for that phy, to indicate that
+ * the phy is up, i.e. we update the phy_is_up in DDB 0.  The
+ * sequencer checks phy_is_up when pending SCBs are to be sent, and
+ * when an open address frame has been received.
+ *
+ * 2) When we know of ports, we call this function to update the map
+ * of phys participaing in that port, i.e. we update the
+ * port_map_by_links in DDB 0.  When a HARD_RESET primitive has been
+ * received, the sequencer disables all phys in that port.
+ * port_map_by_links is also used as the conn_mask byte in the
+ * initiator/target port DDB.
+ */
+void asd_update_port_links(struct sas_phy *sas_phy)
+{
+	struct asd_ha_struct *asd_ha = sas_phy->ha->lldd_ha;
+	const u8 phy_mask = (u8) sas_phy->port->phy_mask;
+	u8  phy_is_up;
+	u8  mask;
+	int i, err;
+
+	for_each_phy(phy_mask, mask, i)
+		asd_ddbsite_write_byte(asd_ha, 0,
+				       offsetof(struct asd_ddb_seq_shared,
+						port_map_by_links)+i,phy_mask);
+
+	for (i = 0; i < 12; i++) {
+		phy_is_up = asd_ddbsite_read_byte(asd_ha, 0,
+			  offsetof(struct asd_ddb_seq_shared, phy_is_up));
+		err = asd_ddbsite_update_byte(asd_ha, 0,
+				offsetof(struct asd_ddb_seq_shared, phy_is_up),
+				phy_is_up,
+				phy_is_up | phy_mask);
+		if (!err)
+			break;
+		else if (err == -EFAULT) {
+			asd_printk("phy_is_up: parity error in DDB 0\n");
+			break;
+		}
+	}
+
+	if (err)
+		asd_printk("couldn't update DDB 0:error:%d\n", err);
+}
diff -urN oldtree/drivers/scsi/aic94xx/aic94xx_seq.h newtree/drivers/scsi/aic94xx/aic94xx_seq.h
--- oldtree/drivers/scsi/aic94xx/aic94xx_seq.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/aic94xx_seq.h	2006-02-13 19:20:00.594421936 -0500
@@ -0,0 +1,40 @@
+/*
+ * Aic94xx SAS/SATA driver sequencer interface header file.
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/aic94xx/aic94xx_seq.h#7 $
+ */
+
+#ifndef _AIC94XX_SEQ_H_
+#define _AIC94XX_SEQ_H_
+
+int asd_pause_cseq(struct asd_ha_struct *asd_ha);
+int asd_unpause_cseq(struct asd_ha_struct *asd_ha);
+int asd_pause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask);
+int asd_unpause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask);
+int asd_init_seqs(struct asd_ha_struct *asd_ha);
+int asd_start_seqs(struct asd_ha_struct *asd_ha);
+
+void asd_update_port_links(struct sas_phy *phy);
+
+#endif
diff -urN oldtree/drivers/scsi/aic94xx/aic94xx_seq_microcode.c newtree/drivers/scsi/aic94xx/aic94xx_seq_microcode.c
--- oldtree/drivers/scsi/aic94xx/aic94xx_seq_microcode.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/aic94xx_seq_microcode.c	2006-02-13 19:20:00.599421176 -0500
@@ -0,0 +1,1469 @@
+/*
+ * Aic94xx SAS/SATA driver central and link sequencer code for AIC-94xx
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/aic94xx/aic94xx_seq_microcode.c#24 $
+ *
+ * Compile options: RAZOR BYPASS_OOB SATA_II_NCQ TARGET_MODE CONCURR_CONNECTION
+ */
+
+/*
+ * Those are offsets in byte units.  Addresses in sequencer are in
+ * dword units, so those values will have to be divided by 4 before
+ * being written out to the sequencer.
+ */
+#define CSEQ_IDLE_LOOP_ENTRY	(0x0000/4)
+#define CSEQ_INT_VEC0		(0x0858/4)
+#define CSEQ_INT_VEC1		(0x00E8/4)
+#define CSEQ_INT_VEC2		(0x00EC/4)
+
+#define LSEQ_IDLE_LOOP_ENTRY	(0x0000/4)
+#define LSEQ_INT_VEC0		(0x012C/4)
+#define LSEQ_INT_VEC1		(0x163C/4)
+#define LSEQ_INT_VEC2		(0x0350/4)
+#define LSEQ_INT_VEC3		(0x0380/4)
+#define LSEQ_INT_VEC4		(0x1624/4)
+#define LSEQ_INT_VEC5		(0x27C8/4)
+#define LSEQ_INT_VEC6		(0x28CC/4)
+#define LSEQ_INT_VEC7		(0x011C/4)
+#define LSEQ_INT_VEC8		(0x1594/4)
+#define LSEQ_INT_VEC9		(0x1880/4)
+#define LSEQ_INT_VEC10		(0x004C/4)
+
+#define MODE2_TASK              (0x1000/4)
+
+#define SAS_RAZOR_SEQUENCER_VERSION "Razor_10a1"
+
+static const u8 Cseq[] = {
+0x04,0x10,0x88,0xB3,0x88,0x11,0x00,0x80,0x06,0x11,0x48,0x80,0x01,0xC7,0x5F,0x68,
+0xFD,0x05,0x0A,0x88,0x07,0x11,0x48,0x00,0x00,0x00,0x14,0x38,0x02,0x05,0x0A,0x00,
+0x8A,0xD4,0x17,0x28,0xFF,0xD1,0x17,0x70,0x00,0x0C,0xDC,0x5A,0xFD,0x05,0x0A,0x88,
+0x05,0x11,0x48,0x80,0x00,0x00,0x14,0x38,0x02,0x05,0x0A,0x00,0x8A,0xD4,0x25,0xA8,
+0xFF,0xD1,0x25,0xF0,0x00,0x0C,0xDC,0x5A,0xFD,0x05,0x0A,0x88,0x04,0x11,0x48,0x00,
+0xFF,0xC1,0x2D,0xF0,0x00,0x0C,0x2C,0xDB,0xFF,0xC9,0x31,0xF0,0x00,0x0C,0x34,0xDB,
+0x06,0x11,0x48,0x80,0xFF,0xD1,0x37,0xF0,0x00,0x0C,0x94,0xDE,0x04,0x11,0x48,0x00,
+0xFF,0xDC,0x3D,0xF8,0x00,0x0C,0xF2,0xDB,0x02,0x05,0x0A,0x00,0x80,0xE1,0x45,0x68,
+0x02,0xE2,0x41,0x30,0x02,0xE0,0x0D,0xB4,0x01,0x35,0xB0,0xE9,0xFF,0xCD,0xFD,0xE0,
+0xFF,0xC5,0x7B,0xE0,0xFF,0xD1,0x69,0x61,0x00,0x0C,0x02,0xC0,0x06,0x11,0x48,0x80,
+0x01,0x00,0x8C,0xB3,0x02,0x20,0x88,0xB3,0x04,0x06,0x80,0xB3,0x01,0xC7,0x8F,0x03,
+0x04,0x11,0x48,0x00,0x88,0x11,0x00,0x80,0x00,0x0C,0x3E,0xC0,0xFE,0xC7,0x8F,0x8B,
+0x01,0xC6,0x01,0xB0,0x02,0xC4,0x41,0xB0,0x02,0xC2,0x0D,0x30,0x02,0xC0,0x0D,0xB0,
+0x07,0x11,0x48,0x84,0x02,0x20,0xC4,0x33,0x02,0x06,0xC0,0x33,0x02,0xE0,0x0D,0xB4,
+0x80,0xE1,0xC3,0x03,0x00,0x0C,0x02,0xC0,0x01,0x11,0x2A,0x80,0x02,0x11,0x2A,0x80,
+0x01,0x05,0x0A,0x84,0x80,0x00,0x1C,0x38,0x02,0xC4,0x41,0xB0,0x02,0x08,0x27,0xB0,
+0x01,0x0A,0x15,0xB0,0xC0,0x0A,0x86,0xF0,0xD0,0x0A,0xA6,0x60,0x00,0x04,0x27,0x00,
+0x44,0x04,0x19,0xA8,0x01,0x11,0x1A,0x80,0x01,0x12,0x08,0x32,0x01,0x0C,0x18,0x18,
+0x04,0x12,0x26,0xB0,0x01,0x0C,0x19,0x1A,0x01,0x0C,0x15,0xB0,0x00,0x0B,0xA1,0xE0,
+0x5A,0x00,0x0C,0x38,0xD0,0x0A,0x9F,0xF0,0x07,0x11,0x94,0xC4,0x05,0x11,0x94,0x44,
+0xFF,0x04,0xBF,0x68,0x02,0x00,0x41,0x30,0x00,0x0C,0xBE,0x40,0x01,0x4D,0x15,0xB0,
+0x01,0x0A,0x26,0xB0,0x04,0x3C,0xB1,0x33,0xFF,0x0A,0xB2,0x68,0x02,0x30,0xB0,0xB3,
+0x00,0x00,0xB4,0x3B,0x04,0xD8,0x27,0x30,0x02,0x00,0x0D,0xB0,0x02,0x0E,0x0C,0xB0,
+0xB1,0x00,0xAC,0xA8,0x02,0x06,0x1C,0xB0,0x02,0x06,0x40,0xB0,0x02,0x11,0x4A,0x80,
+0x01,0xF4,0x27,0xB0,0x00,0x11,0x4A,0x88,0x01,0x4E,0x22,0x30,0xFF,0x21,0xCC,0x70,
+0xFF,0x0E,0xCC,0x78,0x10,0x4D,0x7E,0x78,0x02,0x20,0x88,0xB3,0xFF,0x21,0xD2,0xE0,
+0xFF,0xFF,0x8C,0xBB,0x02,0x11,0x4A,0x80,0x04,0xF0,0x81,0x30,0x04,0xEC,0x89,0x30,
+0x03,0xE8,0x15,0x88,0x44,0x0A,0x14,0xA8,0x00,0x11,0x4A,0x88,0x80,0x0E,0x90,0x98,
+0xFF,0x0A,0x96,0x08,0x1C,0x11,0x6E,0x80,0x00,0x0C,0x6A,0x58,0x40,0x35,0x02,0xF8,
+0x40,0x11,0x6A,0x00,0x04,0x11,0x68,0x80,0x02,0x11,0x4A,0x80,0x04,0x40,0xE0,0x33,
+0x10,0x4D,0xF8,0xF8,0x04,0xE8,0xE1,0x33,0xFC,0xF0,0xE1,0x0B,0x01,0xF4,0xE9,0x93,
+0x00,0x11,0x4A,0x88,0x00,0x0C,0x70,0xC0,0x07,0x11,0x48,0x00,0xFF,0xD1,0x03,0x70,
+0xB1,0x00,0x80,0x28,0xFF,0x0E,0xFE,0x68,0x04,0x11,0x48,0x00,0x02,0x20,0xC8,0x33,
+0x80,0x00,0x1C,0x38,0x02,0xCC,0x41,0x30,0x17,0x4D,0x17,0xF1,0x0C,0x4D,0x1B,0xF1,
+0x0B,0x4D,0x1B,0x71,0x0A,0x4D,0x2D,0xF1,0x1C,0x11,0x2A,0x80,0x30,0x50,0x27,0xB0,
+0x00,0x0C,0x2E,0xC1,0x04,0x0C,0x27,0x30,0x00,0x00,0x26,0xB8,0x14,0x00,0x26,0xB8,
+0x08,0x11,0x26,0xB0,0x14,0x10,0x27,0x30,0x0C,0x28,0x27,0xB0,0x02,0x46,0x45,0x30,
+0x04,0xB0,0x27,0xB0,0x00,0x0C,0x2E,0xC1,0x10,0x10,0x27,0xB0,0x02,0xE4,0x41,0x30,
+0x44,0x0C,0x18,0xA8,0x01,0x11,0x1A,0x80,0x01,0x0C,0xD2,0x33,0x80,0xFF,0x14,0xB8,
+0x83,0x0E,0x14,0xA8,0xB1,0x00,0x52,0xA9,0x00,0x11,0xD0,0x8B,0x80,0xE8,0xD1,0x2B,
+0x08,0x12,0x80,0xB0,0x01,0x0A,0x90,0x30,0x07,0x0C,0x18,0x18,0x30,0x12,0x14,0x08,
+0xFF,0x0A,0x96,0x08,0x1C,0x11,0x6E,0x80,0x02,0x08,0xCD,0x33,0x00,0x0C,0x8A,0x5C,
+0x02,0xCC,0x41,0x30,0x02,0xE6,0x79,0xB2,0x02,0xE8,0x7D,0xB2,0x00,0x0C,0x6A,0x58,
+0x40,0x35,0x02,0xF8,0x40,0x11,0x6A,0x00,0xFF,0xFF,0xB0,0xBB,0x01,0x11,0x1A,0x80,
+0xCC,0x11,0x54,0x5F,0x00,0x0C,0x58,0x5F,0xFF,0xCD,0xFD,0xE0,0x00,0x0C,0x70,0xC0,
+0x02,0xD0,0x41,0xB0,0x02,0x0C,0x19,0xB0,0x80,0x00,0x14,0xB8,0xB1,0x00,0xDA,0x2E,
+0x80,0x0F,0x76,0xE9,0x80,0x00,0xFC,0x3A,0x00,0x0C,0x78,0xC1,0x02,0x0C,0xFD,0x32,
+0x08,0x10,0x81,0xB0,0x08,0x18,0x97,0x80,0x01,0x7E,0x91,0xB0,0x1C,0x11,0x6E,0x80,
+0x00,0x0C,0x6A,0x58,0x40,0x35,0x02,0xF8,0x40,0x11,0x6A,0x00,0x08,0x40,0x20,0xB2,
+0x08,0x50,0x81,0x30,0xFF,0x58,0x97,0x08,0x01,0x7E,0x91,0xB0,0x1C,0x11,0x6E,0x80,
+0x00,0x0C,0x6A,0x58,0x40,0x35,0x02,0xF8,0x40,0x11,0x6A,0x00,0x08,0x40,0xA0,0x32,
+0x02,0x7E,0x15,0xB0,0x82,0x10,0x14,0x28,0x01,0x10,0x22,0x98,0x84,0x11,0x14,0xA8,
+0x83,0x0C,0x19,0x2A,0x02,0x0C,0x15,0xB0,0x89,0x10,0x6A,0x29,0xFF,0xFF,0xB0,0xBB,
+0x01,0x11,0x1A,0x80,0xD0,0x11,0x54,0xDF,0x00,0x11,0x5A,0xDF,0x00,0x0C,0x70,0xC0,
+0x00,0x0C,0xD8,0xD9,0x01,0x11,0x6A,0x00,0x02,0x11,0x4A,0x80,0xFC,0xE0,0x81,0x88,
+0x07,0xE1,0x83,0xB0,0x03,0xE0,0x15,0x08,0x44,0x0A,0x14,0xA8,0x00,0x11,0x4A,0x88,
+0x80,0x11,0x90,0x00,0x08,0x0A,0x96,0x00,0x1C,0x11,0x6E,0x80,0x01,0x00,0x14,0xB8,
+0x83,0x30,0x60,0x28,0x00,0x0C,0x6A,0x58,0x40,0x35,0x02,0xF8,0x40,0x11,0x6A,0x00,
+0x80,0x80,0x00,0x32,0x00,0x0C,0xEC,0x59,0x88,0x11,0x00,0x80,0x00,0x0C,0x70,0xC0,
+0x06,0x11,0x48,0x80,0xD6,0x01,0x18,0x38,0xFF,0xD7,0xE3,0xE1,0xDA,0x01,0x18,0x38,
+0xFF,0xDB,0xEB,0xF1,0x02,0x0C,0x1C,0xB0,0x02,0x12,0x40,0xB0,0x02,0x00,0x27,0x30,
+0x04,0x11,0x48,0x9C,0x14,0x11,0x2A,0x00,0x02,0x11,0x4A,0x80,0x08,0x00,0xC1,0xB3,
+0x00,0x11,0x4A,0x88,0xC0,0x0A,0x15,0x88,0xFF,0x0A,0x0A,0x7A,0x40,0x0A,0x46,0xF2,
+0x80,0x0A,0x6E,0xF2,0x01,0x0A,0x15,0xB0,0xC0,0x0A,0x8E,0xF2,0xC3,0x0A,0xC4,0x72,
+0xC4,0x0A,0x08,0xF2,0xC6,0x0A,0xC6,0xF2,0xD0,0x0A,0x90,0x72,0x15,0x11,0x2A,0x80,
+0xA1,0x00,0x20,0xAB,0x10,0x0B,0x2D,0x7A,0x16,0x10,0x2D,0x62,0x08,0x48,0x1B,0xFA,
+0x02,0x46,0x45,0x30,0x08,0x9F,0x3F,0x03,0x02,0x52,0x21,0xB3,0x80,0xBF,0x1B,0x7A,
+0x00,0x0C,0x10,0x5F,0x03,0x0A,0x1F,0xE2,0xA1,0x00,0xE2,0xAA,0x08,0x0A,0x2D,0x72,
+0x04,0x48,0x2D,0xFA,0xB1,0x00,0xF8,0x2E,0x00,0x0C,0x5A,0x4F,0x02,0x20,0x0C,0xB0,
+0x00,0x0C,0xCC,0xDF,0x02,0x06,0x40,0xB0,0xB1,0x00,0xBA,0xA9,0xB1,0x00,0xC0,0xA8,
+0x00,0x0C,0x58,0xCF,0x02,0x46,0x45,0x30,0x01,0x00,0x14,0xB8,0x83,0xAC,0x59,0x2B,
+0x0F,0x0A,0xBB,0x76,0x02,0xA0,0xAD,0xB3,0x02,0x20,0x40,0x33,0xFF,0xFF,0x04,0x3A,
+0xFF,0xD7,0x45,0x72,0xA1,0x00,0xA4,0x2F,0xC1,0x11,0x52,0xC7,0xB1,0x00,0xBA,0xA9,
+0xB1,0x00,0xC0,0xA8,0x80,0x0B,0x59,0x7A,0x00,0x11,0x18,0x08,0xB0,0x00,0x60,0xAF,
+0x04,0x0C,0x81,0xB2,0x02,0x46,0x45,0x30,0x01,0x00,0x14,0xB8,0x83,0xAC,0x59,0x2B,
+0x00,0x04,0x18,0xB8,0xB1,0x00,0x72,0x29,0xFF,0xFF,0x08,0x3A,0x02,0x46,0x45,0x30,
+0x02,0xA2,0xAD,0x33,0x02,0x20,0x44,0xB3,0xFF,0xD7,0x69,0xF2,0xA1,0x00,0xAC,0xAF,
+0xFF,0x8E,0x6D,0x6A,0xC9,0x11,0x52,0x47,0x02,0x20,0x18,0x37,0x02,0x20,0x0C,0xB0,
+0x44,0x0B,0x15,0xA8,0x00,0x0B,0x01,0x80,0x02,0x06,0x40,0xB0,0xFF,0xFF,0x00,0xBA,
+0xFF,0xE5,0x83,0x62,0xFF,0xE7,0x85,0xE2,0x02,0x20,0xC8,0x33,0x02,0x20,0xCC,0xB3,
+0xA1,0x00,0xA6,0x28,0xFF,0xE7,0x8D,0xF2,0x02,0xE6,0xA9,0xB3,0x02,0x20,0xCC,0xB3,
+0x02,0xD4,0x41,0x30,0x02,0xE6,0x01,0x36,0x1B,0x11,0x2A,0x00,0x07,0x11,0x9A,0x42,
+0x00,0x00,0x44,0x38,0x00,0x11,0x16,0x88,0x01,0x0B,0x15,0x30,0x83,0x8E,0x1D,0x2B,
+0x05,0x11,0x9A,0xC2,0x01,0x0C,0x48,0x30,0x00,0x11,0x08,0x0A,0x00,0x11,0x0A,0x8A,
+0x00,0x11,0x18,0x8A,0xFF,0xFF,0x04,0x3A,0xFF,0xFF,0x00,0xBA,0xFF,0xD1,0xB1,0x62,
+0x02,0x20,0xA0,0xB3,0x02,0x20,0xA4,0x33,0x01,0x11,0xB0,0x83,0x00,0x0C,0xB8,0xC2,
+0x02,0x20,0x14,0xB0,0x02,0xD2,0x41,0x30,0x02,0x0A,0x04,0x32,0x02,0x0A,0xA4,0xB3,
+0x06,0x11,0x48,0x80,0x01,0xC7,0x15,0x30,0x04,0x11,0x48,0x00,0x01,0x0A,0x1E,0xED,
+0x01,0x0C,0x48,0x30,0x00,0x0C,0x1E,0x43,0xD1,0x11,0x52,0x47,0x02,0x46,0x45,0x30,
+0xB1,0x00,0xF8,0x2E,0x00,0x0C,0x5A,0x4F,0xFD,0x0B,0xD9,0xE2,0xFD,0x8F,0xD9,0xE2,
+0x04,0x9F,0xDB,0x6A,0x04,0x9F,0x3F,0x03,0x02,0x20,0x60,0xB3,0xA1,0x00,0x08,0x2F,
+0x2A,0x11,0x5A,0x47,0x2B,0x11,0x5A,0xC7,0xC0,0x01,0x18,0xB8,0x01,0xD6,0x15,0x30,
+0x00,0x0C,0x18,0x98,0x01,0x12,0x00,0x30,0xC8,0x01,0x18,0x38,0x0F,0x00,0x14,0x08,
+0x00,0x0C,0x18,0x98,0x02,0x0C,0x1C,0xB0,0xFF,0x12,0x2A,0x7B,0xFF,0x0C,0x18,0x98,
+0x02,0x0C,0x0C,0x30,0x02,0x0E,0x0C,0xB0,0xB1,0x00,0x80,0x28,0xFF,0x0E,0xFE,0x7A,
+0xFF,0xD1,0xF5,0x62,0x04,0x06,0x22,0x30,0x00,0x0C,0x26,0xC3,0x01,0x0C,0xC0,0x33,
+0x02,0x06,0x1C,0xB0,0x02,0x06,0x18,0x30,0xFF,0xFF,0x14,0x38,0x83,0xD4,0xA9,0x2B,
+0xFF,0x12,0x26,0x18,0x01,0xD6,0xAD,0x1B,0x07,0xD6,0xAD,0x8B,0xFF,0x0C,0x18,0x98,
+0xFF,0x12,0x1C,0x7B,0xC0,0x01,0x1C,0x38,0x01,0xD7,0x15,0xB0,0x00,0x0E,0x1C,0x98,
+0x01,0x00,0x26,0xB0,0x07,0x0E,0xAE,0x8B,0x00,0xE0,0x57,0xDF,0x02,0x05,0x0A,0x00,
+0x00,0x00,0x14,0x38,0x8A,0xD4,0x27,0x2B,0xFF,0xD1,0xDD,0x62,0x04,0x11,0x48,0x00,
+0x88,0x11,0x00,0x04,0x25,0x11,0x2A,0x80,0xC0,0x11,0x3C,0x5B,0x00,0x0C,0x5E,0xCF,
+0x00,0x11,0x56,0xDF,0x88,0x11,0x00,0x04,0xC8,0x11,0x3C,0xDB,0x00,0x0C,0x5E,0xCF,
+0x01,0x11,0x56,0x5F,0x88,0x11,0x00,0x04,0x01,0x11,0x1A,0x80,0x02,0x05,0x0A,0x00,
+0xFF,0xFF,0xB0,0xBB,0x02,0x12,0x40,0xB0,0xFE,0x0C,0x18,0x18,0x02,0x0C,0x0C,0x30,
+0x02,0x46,0x45,0x30,0x01,0x9D,0x5B,0x7B,0x20,0x0B,0x5D,0x7B,0x02,0x9E,0x5D,0x7B,
+0x04,0x4C,0x5D,0xEB,0x04,0x49,0x5D,0xEB,0x20,0x9D,0xDD,0x6B,0xB1,0x00,0xCE,0x2E,
+0x00,0x0C,0xDC,0x4B,0x40,0x9E,0xDD,0x6B,0x01,0x9C,0x15,0xB0,0x02,0x22,0x0C,0x30,
+0x00,0x00,0x44,0x38,0x00,0xAF,0x15,0x88,0x02,0x06,0x44,0x30,0xFF,0x0A,0x70,0xEB,
+0x80,0xBF,0xDD,0x6B,0x09,0x11,0x6E,0x03,0x00,0x0C,0x10,0x5F,0x00,0x0C,0xE0,0xC3,
+0x80,0xBF,0x7F,0x7B,0x09,0xB7,0x77,0xE3,0x00,0x11,0x7E,0x0B,0x02,0x0A,0x0C,0x30,
+0x00,0x0C,0x38,0x5F,0x02,0x06,0x14,0x30,0x00,0x0C,0xEA,0x4B,0x02,0x22,0x0C,0x30,
+0x00,0x00,0x44,0x38,0x00,0xAE,0xC1,0x08,0x02,0x06,0x44,0x30,0x02,0x62,0x14,0xB0,
+0x01,0x0B,0xE0,0xEB,0xFF,0x0A,0xE0,0xF3,0x04,0x9D,0xA3,0xFB,0x02,0x0A,0x0C,0x30,
+0x00,0x11,0x12,0xDC,0x04,0x11,0x14,0x30,0x01,0xA8,0x19,0x30,0x01,0xA9,0x15,0xB0,
+0xB1,0x00,0xDA,0x2E,0x02,0x06,0x14,0x30,0x80,0x0F,0xA2,0xFB,0x01,0x11,0x12,0x5C,
+0x00,0x0C,0xE0,0xC3,0x02,0x06,0x18,0x30,0x02,0x20,0x0C,0xB0,0x02,0x22,0x0C,0x30,
+0x01,0x0A,0x00,0x30,0x02,0x06,0x44,0x30,0x02,0x06,0x40,0xB0,0x00,0x0C,0x54,0x5F,
+0x04,0x4C,0xC1,0x7B,0xFF,0x03,0xC1,0xF3,0x02,0x20,0x0C,0xB0,0x02,0x02,0x41,0xB0,
+0x01,0x0C,0x18,0x18,0x00,0x0C,0x52,0x5F,0x02,0x06,0x40,0xB0,0xFF,0xFF,0x04,0x3A,
+0x04,0x9D,0xDB,0xFB,0xC0,0x0C,0xDA,0xF3,0xFF,0xAA,0xDB,0xFB,0x02,0x20,0x0C,0xB0,
+0x01,0x11,0x14,0x00,0x00,0xAA,0xD1,0xE3,0xB1,0x00,0x60,0x2F,0x00,0x0C,0xD8,0x43,
+0xB1,0x00,0x5A,0x2F,0xFF,0x21,0xD8,0x73,0x01,0x0A,0x14,0x18,0x00,0x0C,0xCA,0x43,
+0x02,0x06,0x40,0xB0,0x01,0x11,0x22,0x9C,0x00,0x0C,0x38,0x5F,0x00,0x0C,0xEA,0x4B,
+0x02,0x06,0x18,0x30,0xFF,0x01,0xF1,0x73,0x02,0x20,0xB0,0x33,0x02,0x00,0x41,0x30,
+0x00,0x0C,0x46,0xC3,0x02,0x06,0x18,0x30,0xB1,0x00,0xD6,0xA9,0xFF,0x21,0x46,0x63,
+0x01,0x10,0x22,0x1C,0x00,0x11,0x00,0x08,0x02,0x05,0x0A,0x00,0xFF,0xE5,0x09,0x74,
+0x02,0xE4,0x41,0x30,0xFF,0xFF,0xC8,0xBB,0x01,0x00,0x0E,0xB0,0xFF,0x07,0x14,0x90,
+0x00,0xDC,0xB9,0x0B,0x0A,0x11,0x56,0xDF,0x02,0x05,0x0A,0x00,0xFF,0xDC,0x0F,0xFC,
+0x11,0x00,0x00,0x98,0x01,0x00,0x14,0x30,0x00,0xDD,0xF7,0x63,0xFD,0x05,0x0A,0x88,
+0x88,0x11,0x00,0x04,0x02,0xA8,0x15,0x30,0x01,0x0A,0x04,0xB0,0x01,0x0B,0x06,0x98,
+0xFF,0x0C,0x1C,0xFC,0xFF,0x0B,0x06,0x18,0xE0,0xA8,0x21,0x2C,0xFF,0x11,0x22,0x8C,
+0x02,0xA8,0x51,0x33,0x00,0x0C,0x12,0xC4,0x04,0x9D,0xCB,0xFF,0x00,0x0C,0x5C,0xDF,
+0xFF,0x21,0xCA,0xF7,0x01,0x10,0x22,0x1C,0x3C,0x00,0x0C,0x38,0x02,0x34,0xA8,0x33,
+0x02,0x36,0x40,0xB0,0xF8,0xD4,0x15,0x08,0xC0,0x0A,0x72,0xF4,0xD0,0x0A,0x72,0x74,
+0x01,0xD4,0x15,0xB0,0x00,0x11,0x16,0x88,0x20,0x02,0x18,0x38,0x83,0x0C,0x0C,0xAC,
+0x00,0x0C,0x7E,0x45,0x00,0x0C,0x86,0xC5,0x00,0x0C,0x92,0xC5,0x00,0x0C,0x9E,0xC5,
+0x00,0x0C,0xA8,0x44,0x00,0x0C,0x24,0x45,0x00,0x0C,0x9E,0x44,0x00,0x0C,0x20,0xC5,
+0x00,0x0C,0xB8,0x45,0x00,0x0C,0xCC,0x45,0x00,0x0C,0xE2,0x45,0x00,0x0C,0xD8,0xC4,
+0x00,0x0C,0x54,0xC5,0x00,0x0C,0x8C,0xC5,0x00,0x0C,0x50,0x45,0x00,0x0C,0xA6,0x45,
+0x00,0x0C,0x46,0xC5,0x00,0x0C,0xD4,0x45,0x00,0x0C,0xDE,0x45,0x00,0x0C,0xE6,0xC5,
+0x00,0x0C,0x58,0x47,0x00,0x0C,0xC8,0xC6,0x00,0x0C,0x5C,0x46,0x00,0x0C,0xE8,0x45,
+0x00,0x0C,0xB6,0xC6,0x07,0xD4,0x8B,0xFC,0x01,0x05,0xAD,0xB3,0x07,0xD4,0x0B,0x8A,
+0x44,0x05,0x1D,0xA8,0x01,0x11,0x1E,0x00,0x00,0x11,0x26,0x88,0xFF,0x04,0x87,0xFC,
+0x44,0xD6,0x1D,0x28,0x01,0x11,0x1E,0x00,0x01,0x05,0x27,0xB4,0x01,0x05,0x09,0xB2,
+0xC5,0x11,0x52,0x47,0x01,0x0C,0x19,0x1A,0x01,0x0C,0x15,0xB0,0x00,0x0B,0x1F,0xE5,
+0x07,0x11,0x94,0xDC,0xA1,0x00,0xAC,0x28,0x01,0x0C,0x48,0x30,0x02,0xD0,0x15,0x30,
+0x88,0x20,0x9C,0xAC,0xB1,0x00,0x9C,0xA8,0x04,0x11,0x48,0x84,0x80,0x0B,0xA3,0x7C,
+0x00,0x0C,0x24,0x5E,0xFF,0xFF,0x04,0x3A,0x00,0x0C,0x46,0xDD,0xA1,0x00,0xA0,0xAF,
+0x80,0x0B,0xAD,0xFC,0x00,0x0C,0x24,0x5E,0x00,0x0C,0x46,0xDD,0x02,0x20,0x0C,0xB0,
+0x02,0x46,0x45,0x30,0x02,0x02,0x0D,0x30,0xFF,0xFF,0x04,0x3A,0x02,0x06,0x40,0xB0,
+0xFF,0xFF,0x40,0xBB,0xFF,0x21,0xD6,0x74,0x02,0x02,0xB1,0xB3,0x40,0x49,0xC5,0x7C,
+0x00,0x0C,0xAA,0x5D,0x00,0x0C,0xD2,0xC4,0x02,0xA0,0xAD,0xB3,0x02,0x20,0x40,0x33,
+0xFF,0xFF,0x04,0x3A,0xFF,0xD7,0xD1,0xF4,0xB1,0x00,0xA4,0xAF,0x00,0x0C,0xD2,0xC4,
+0xC1,0x11,0x52,0xDF,0x02,0xD8,0x41,0x30,0xFF,0x21,0xBC,0xE4,0xA1,0x00,0x9E,0x2F,
+0x02,0x46,0x45,0x30,0x02,0x06,0xB1,0x33,0xFF,0xFF,0x0C,0xBA,0x40,0x49,0x19,0xED,
+0x04,0x4C,0x0F,0xFD,0xB1,0x00,0xA0,0x2F,0xFF,0xA1,0xEB,0xE4,0xC1,0x11,0x52,0xDF,
+0x00,0x0C,0x1A,0xC5,0x02,0x20,0xA8,0x33,0xC0,0x11,0x5C,0xDF,0xFF,0x21,0xF2,0xE4,
+0x23,0x11,0x2A,0x80,0x02,0x20,0x14,0xB0,0xFF,0xD7,0xFD,0x74,0x02,0xD6,0x41,0xB0,
+0x02,0xD4,0x01,0x32,0x00,0x0C,0xFE,0x44,0x02,0xD4,0x81,0x33,0x02,0x0A,0x40,0xB0,
+0x02,0x00,0xAD,0xB3,0x02,0xD4,0x41,0x30,0x02,0x0A,0x04,0x32,0x02,0xD6,0x01,0xB2,
+0xFF,0xD7,0x1B,0xE5,0x02,0x20,0x84,0xB3,0x00,0x0C,0x1A,0xC5,0x80,0x49,0x15,0xED,
+0x00,0x0C,0x58,0x5A,0x00,0x0C,0x1A,0xC5,0xB1,0x00,0xC8,0xA9,0x00,0x0C,0x1A,0xC5,
+0x00,0x0C,0xAA,0x5D,0x02,0xD8,0x41,0x30,0xFF,0x21,0xD8,0x64,0xFF,0x11,0x22,0x8C,
+0x00,0x0C,0x36,0x5D,0xA1,0x00,0xA0,0xAF,0x02,0x04,0x0D,0x30,0x00,0x0C,0x36,0x5D,
+0x02,0x06,0x08,0x32,0x02,0x20,0x0C,0xB0,0x02,0x46,0x45,0x30,0x02,0x04,0x0D,0x30,
+0xFF,0xFF,0x08,0x3A,0x02,0x06,0x40,0xB0,0xA1,0x00,0x78,0xAF,0x80,0x49,0x3F,0x6D,
+0xFF,0x4D,0x43,0xED,0x04,0x49,0x43,0x7D,0x00,0x0C,0x5C,0xC2,0x80,0x0B,0x47,0x6D,
+0xA1,0x00,0xC8,0x29,0x80,0x0B,0x47,0xFD,0x00,0x0C,0x3C,0x5E,0xB1,0x00,0xC8,0xA9,
+0x40,0x49,0xB3,0xED,0x80,0x49,0x1F,0xED,0x01,0x49,0x1F,0x7D,0xA1,0x00,0x00,0x28,
+0xB1,0x00,0x9A,0x29,0x00,0x0C,0x46,0xC5,0x07,0x11,0x48,0x00,0x02,0x20,0xB4,0xB3,
+0x84,0x80,0x14,0xB8,0x88,0xDA,0x63,0x2D,0x04,0x11,0x48,0x00,0xB1,0x00,0x3C,0x2F,
+0x07,0x11,0x48,0x00,0xFF,0xD1,0x69,0xE5,0x00,0x0C,0x4E,0x58,0xFF,0xD1,0x65,0x75,
+0xB1,0x00,0x80,0x28,0xFF,0x0E,0x62,0x6D,0x44,0x0C,0x1C,0x28,0x02,0x0E,0x1C,0x18,
+0x01,0x11,0x1E,0xA0,0x0F,0x00,0x14,0x08,0x08,0x0A,0x26,0x80,0x02,0xDA,0x27,0xB0,
+0x04,0x11,0x48,0x00,0x01,0x0C,0xA8,0xB3,0x00,0x0C,0x74,0xC4,0x08,0x4C,0xC3,0x6D,
+0x02,0x46,0x45,0x30,0x02,0x20,0x0C,0xB0,0x00,0x0C,0xB8,0xC4,0x02,0x46,0x45,0x30,
+0x02,0x20,0x0C,0xB0,0xA1,0x00,0x78,0xAF,0xB1,0x00,0xD2,0x2A,0x00,0x0C,0x1E,0xCD,
+0xA1,0x00,0x00,0x28,0x02,0x20,0x0C,0xB0,0x02,0x46,0x45,0x30,0x80,0x0B,0x9B,0x7D,
+0x04,0x0C,0x79,0x32,0x00,0x4D,0xAB,0xDD,0x00,0x0C,0xB2,0xC4,0x02,0x20,0x0C,0xB0,
+0x02,0x46,0x45,0x30,0x00,0x4D,0xAB,0xDD,0x00,0x0C,0x2E,0x45,0x02,0x46,0x45,0x30,
+0x01,0x4D,0x19,0xB0,0xB1,0x00,0xC8,0xA9,0x40,0x49,0xB3,0xED,0x80,0x49,0x1F,0xED,
+0xA1,0x00,0x40,0x2A,0xFF,0x45,0xB7,0x65,0x1B,0x11,0xB0,0xC5,0x23,0x11,0xB0,0x45,
+0x02,0x20,0x0C,0xB0,0x00,0x0C,0xCE,0xDE,0x00,0x0C,0x96,0xCD,0x08,0x4C,0xB9,0xFC,
+0x02,0x06,0x22,0x30,0xF7,0x11,0x18,0x00,0xB1,0x00,0xAC,0x29,0x04,0x11,0x18,0x00,
+0xB1,0x00,0x7E,0x29,0x00,0x0C,0xD8,0xC4,0x02,0x20,0x0C,0xB0,0x00,0x0C,0xCE,0xDE,
+0x00,0x0C,0xA2,0x4D,0xA1,0x00,0x78,0xAF,0x02,0x00,0xC9,0x33,0x00,0x0C,0xDE,0x5D,
+0xFF,0xE5,0xDD,0x65,0xFF,0xFF,0xCC,0xBF,0xA1,0x00,0xA6,0x28,0x0A,0x4D,0x59,0x67,
+0xCD,0x11,0x52,0xC7,0x02,0x20,0xC8,0x33,0xA1,0x00,0xA6,0x28,0x07,0x11,0xEA,0x45,
+0x05,0x11,0xEA,0xC5,0x01,0x0C,0x48,0x30,0x02,0x38,0xF8,0x7D,0xFF,0xD1,0xF9,0x75,
+0xB1,0x00,0x80,0x28,0xFF,0x0E,0xEE,0xED,0x01,0x0C,0x60,0x30,0x02,0x20,0x64,0xB4,
+0x01,0x00,0x14,0xB8,0x83,0xD4,0xA9,0x2B,0xC8,0x01,0x18,0x38,0x0F,0x00,0x14,0x08,
+0x00,0x0C,0x18,0x98,0x02,0x0C,0x1C,0xB0,0x01,0x12,0x14,0x30,0xFF,0x0A,0x14,0x6E,
+0x01,0x11,0x26,0x80,0xC0,0x01,0x1C,0x38,0x01,0xD7,0x15,0xB0,0x00,0x0E,0x1C,0x98,
+0x01,0x00,0x26,0xB0,0x07,0x0E,0xAE,0x0F,0x01,0x0A,0x14,0x18,0x00,0x0C,0x1A,0x4E,
+0x01,0x0A,0x26,0x34,0x24,0x11,0x2A,0x00,0x00,0x40,0x18,0xB8,0xA1,0x00,0x1E,0xAF,
+0xFF,0xBF,0x18,0xB8,0xA1,0x00,0x2E,0xAF,0xC0,0x49,0x1F,0x6D,0x04,0x4E,0x2D,0xEE,
+0x03,0x4E,0x39,0x6E,0x27,0x11,0x2A,0x00,0xFB,0x4E,0x9D,0x8A,0x00,0x0C,0x5C,0x5E,
+0x00,0x0C,0x1E,0xD5,0x06,0x11,0x48,0x80,0x00,0x0C,0x64,0xDE,0x04,0x11,0x48,0x84,
+0xFC,0x4E,0x9D,0x0A,0xA1,0x00,0xD2,0xAA,0xC0,0x49,0x1F,0x6D,0xFF,0x4D,0x4F,0xEE,
+0x00,0x0C,0x44,0x46,0xC0,0x49,0x1F,0x6D,0x20,0x48,0x4B,0xEE,0x01,0x48,0x1F,0xFD,
+0x00,0x0C,0x5C,0x46,0x20,0x49,0x4F,0xEE,0x01,0x4E,0x51,0x6E,0xA1,0x00,0xD2,0xAA,
+0x00,0x11,0x66,0x8A,0x01,0x4E,0x9D,0x8A,0xFC,0x49,0x93,0x0A,0xB1,0x00,0xBA,0xA9,
+0x02,0x46,0x45,0x30,0x00,0x0C,0x3A,0xC2,0x02,0x46,0x45,0x30,0xA6,0x01,0x18,0xB8,
+0x30,0x01,0x1C,0x38,0x00,0x0C,0x18,0xC7,0xFF,0xFF,0x0C,0xBA,0xFF,0xD1,0x6D,0x66,
+0x02,0x20,0xA0,0xB3,0x02,0x20,0xA4,0xB7,0x02,0x20,0x14,0xB0,0x02,0xD2,0x41,0x30,
+0x02,0x0A,0x0C,0xB2,0x02,0x0A,0xA4,0xB3,0x02,0x0A,0x40,0x34,0xFF,0xD9,0x83,0xE6,
+0x06,0x11,0x48,0x80,0x02,0x06,0xA1,0xB3,0xFF,0x07,0x81,0x66,0xFF,0xFF,0xA4,0xBB,
+0x04,0x11,0x48,0x84,0x02,0x20,0xA8,0x33,0x02,0x06,0xAD,0xB3,0x02,0xD8,0x41,0x30,
+0x02,0xD6,0x0D,0xB2,0x02,0xD4,0x41,0x30,0xFF,0x07,0x1F,0xE5,0x06,0x11,0x48,0x80,
+0x02,0xD8,0xA5,0x33,0x04,0x11,0x48,0x84,0x02,0x05,0x0A,0x00,0x02,0xD0,0x41,0xB0,
+0x04,0x11,0x48,0x00,0xFF,0xFF,0xB0,0xBB,0x01,0x49,0xAD,0xEE,0x80,0x33,0xAD,0xFE,
+0x02,0x46,0x45,0x30,0xA6,0x01,0x1C,0x38,0x33,0x01,0x18,0xB8,0x00,0x0C,0x3C,0xDF,
+0x00,0x0C,0xAE,0x56,0x26,0x11,0x5A,0x5F,0x00,0x0C,0x76,0xDE,0xFF,0x07,0x5F,0x77,
+0x02,0x20,0xB0,0x33,0x02,0x06,0x41,0x30,0x00,0x0C,0x9C,0x46,0x00,0x0C,0x42,0x5E,
+0x00,0x0C,0x46,0xC5,0x44,0x43,0x15,0xA8,0x00,0x43,0x15,0x80,0x02,0x20,0x0C,0xB0,
+0x01,0x0A,0x00,0x30,0x02,0x06,0x40,0xB0,0x15,0x11,0x56,0x5F,0x02,0x05,0x0A,0x84,
+0x02,0x46,0x45,0x30,0xB1,0x00,0xC8,0xA9,0x00,0x4D,0x5B,0x47,0x02,0x46,0x45,0x30,
+0x03,0xD5,0x09,0x77,0x09,0xD5,0x09,0x77,0x12,0xD5,0x09,0x77,0xF0,0xD5,0x15,0x08,
+0x80,0x0A,0xEA,0x76,0x08,0xD5,0xE1,0x6E,0x44,0xD5,0x0F,0x28,0x06,0x07,0xE2,0x7E,
+0x00,0x11,0x7E,0x0B,0x0F,0xD5,0x7D,0x0A,0x02,0x11,0x7A,0x02,0x04,0x11,0x60,0x5F,
+0x01,0x10,0x22,0x1C,0x44,0xD5,0x15,0xA8,0x80,0x0A,0xF6,0xEE,0x01,0x0A,0x0E,0xB0,
+0x0C,0x07,0xF6,0xEE,0xC2,0x07,0xFE,0xEE,0x31,0x07,0xFA,0x6E,0x00,0x11,0x7E,0x0B,
+0x01,0x11,0x22,0x9C,0x80,0xBF,0xFF,0x6E,0x00,0x0C,0x10,0x5F,0x04,0x11,0x6E,0x83,
+0x0F,0x9F,0x3F,0x0B,0x44,0xD5,0x15,0xA8,0x00,0x9F,0x3F,0x83,0x01,0x11,0x22,0x9C,
+0x80,0xBF,0x0D,0x6F,0x00,0x0C,0x10,0x5F,0x01,0xD5,0x6F,0xB3,0x01,0x11,0x22,0x9C,
+0xBA,0x01,0x18,0x38,0xBC,0x01,0x1C,0xB8,0x08,0x9F,0x19,0x7F,0x90,0x01,0x18,0xB8,
+0x02,0x12,0x14,0x30,0x8B,0x10,0xCA,0xAF,0x00,0x0C,0x22,0xDF,0x04,0x64,0x26,0x30,
+0x01,0x10,0x22,0x1C,0x07,0x11,0x48,0x00,0x04,0x11,0xB8,0x33,0x02,0x0A,0xBA,0xB3,
+0x00,0x0C,0x30,0xDF,0x00,0x0C,0x30,0xDF,0x04,0xDC,0xC9,0xB0,0x04,0x11,0x48,0x84,
+0x02,0xDC,0x15,0x30,0x83,0xDC,0xB9,0x2B,0x02,0xDE,0x15,0xB0,0x84,0xDE,0xBD,0x2F,
+0xBA,0x01,0x1C,0xB8,0xBF,0x01,0x18,0x38,0x11,0x12,0x22,0xA8,0x00,0x0C,0x1E,0xD5,
+0x02,0x0C,0x0C,0x30,0x02,0x0E,0x18,0xB0,0x02,0x12,0x14,0x30,0x00,0x0C,0x22,0xDF,
+0x02,0x06,0x18,0x30,0xFC,0x0C,0x18,0x98,0x04,0x12,0xC8,0xB0,0xFF,0x11,0x22,0x20,
+0x11,0x67,0x22,0xAC,0xA1,0x00,0x32,0xA8,0xA1,0x00,0x4C,0xA8,0xA1,0x00,0x66,0x28,
+0xA1,0x00,0x72,0xAA,0xA1,0x00,0x6C,0xAA,0xA1,0x00,0x9E,0x2A,0xFD,0x05,0x0A,0x0C,
+0xA1,0x00,0x8C,0x29,0x00,0x0C,0x1C,0xDE,0x02,0x86,0x45,0x30,0xFF,0x23,0x20,0x76,
+0xB1,0x00,0xEC,0x2D,0x00,0x0C,0x6E,0xD7,0xB1,0x00,0x12,0xAE,0x02,0xAE,0x45,0x30,
+0x00,0x0C,0x66,0xC7,0x00,0x00,0x40,0xB8,0x00,0x0C,0xAA,0xDF,0x00,0x0C,0x84,0x57,
+0x02,0x20,0x0C,0xB0,0x02,0xEA,0x41,0xB0,0x02,0x34,0x15,0x30,0x02,0x06,0x40,0xB0,
+0x88,0x20,0x85,0x2F,0x01,0x10,0x22,0x1C,0xB1,0x00,0xB0,0x2E,0x00,0x0C,0x74,0x57,
+0x1F,0x11,0x18,0x9C,0x00,0x00,0x40,0xB8,0x00,0x0C,0xAA,0xDF,0x00,0x0C,0xA4,0xD7,
+0x02,0x20,0x0C,0xB0,0x02,0xEA,0x41,0xB0,0x09,0x0B,0x9F,0xE7,0x02,0x34,0x15,0x30,
+0x02,0x06,0x40,0xB0,0x88,0x20,0xA5,0xAF,0x00,0x0C,0xA0,0xC7,0x02,0x06,0x40,0xB0,
+0x23,0x11,0x18,0x00,0xB1,0x00,0x2C,0xAC,0xB1,0x00,0xB0,0x2E,0x00,0x0C,0x8C,0xD7,
+0xFF,0x11,0x22,0x8C,0xB1,0x00,0xEE,0xAE,0x00,0x0C,0x1E,0xD5,0x01,0x11,0x4A,0x80,
+0x28,0x01,0x18,0xB8,0x80,0x0B,0xB7,0x7F,0x34,0x01,0x18,0x38,0x08,0x12,0xD0,0xB3,
+0xE0,0x01,0x18,0x38,0x01,0x12,0x14,0x30,0x07,0x0C,0x18,0x18,0x00,0x12,0xC8,0xE7,
+0xF8,0x0C,0x18,0x18,0xE8,0x0C,0xBA,0xE7,0x00,0x11,0x4A,0x88,0x01,0x10,0x22,0x1C,
+0x00,0x11,0x4A,0x88,0x01,0x11,0x22,0x9C,0x01,0x8E,0x1D,0x1B,0x01,0x8E,0x1F,0xE5,
+0xC8,0x11,0x5C,0x5F,0xFF,0x21,0xD8,0xF7,0x02,0xD6,0xB1,0xB3,0x00,0x0C,0x54,0x5F,
+0x02,0x20,0x18,0x37,0xFF,0x8E,0x1D,0x9B,0xFF,0x8E,0x1F,0xED,0xFF,0x8D,0x1F,0xF5,
+0x02,0x8C,0x41,0xB0,0xFF,0xFF,0x18,0x3B,0xC9,0x11,0x52,0x47,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0xFF,0x4B,0x39,0x6D,0xFF,0x4D,0x25,0xE8,0x03,0x48,0x25,0x78,0x20,0x48,0x1D,0xE8,
+0x80,0x49,0x0F,0xF8,0xFF,0xFE,0x18,0xB8,0x00,0x0C,0xA0,0xC1,0x02,0x4C,0x19,0x78,
+0x00,0x00,0x14,0x38,0x88,0x0C,0x19,0x28,0x88,0x0E,0x19,0xA8,0x17,0x11,0x2E,0x40,
+0x04,0x0C,0x79,0x32,0x01,0x11,0x22,0xC0,0x20,0x49,0x25,0x78,0x04,0x11,0x78,0xB2,
+0x02,0x11,0x22,0xC0,0x00,0x0C,0x8C,0x59,0x80,0x4C,0x73,0xFA,0x21,0x1F,0x2D,0xE8,
+0x40,0x48,0x73,0x7A,0x0B,0x11,0x2E,0xC0,0x0C,0x11,0x2E,0x40,0x00,0x0C,0x8C,0x59,
+0xCD,0x11,0x32,0x40,0x01,0x11,0x1A,0x80,0xFF,0xFF,0x00,0xBA,0xFF,0x12,0x40,0x60,
+0x01,0x11,0x1E,0x00,0xFE,0x0C,0x1C,0x98,0x02,0x20,0x26,0x30,0x02,0x20,0x26,0xB4,
+0x02,0x0C,0x1C,0xB0,0x02,0x20,0x14,0xB0,0x02,0x12,0x40,0xB0,0x02,0x0A,0x00,0xB2,
+0x02,0x0A,0x26,0xB0,0x02,0x0A,0x40,0x34,0x02,0x0C,0x1C,0xB0,0xFF,0xD9,0x57,0xE0,
+0x02,0x00,0x27,0x30,0xFF,0x01,0x39,0x65,0xFF,0xFF,0x26,0x3C,0x02,0x20,0xA8,0x33,
+0x02,0x00,0xAD,0xB3,0x02,0xD8,0x41,0x30,0x02,0xD6,0x01,0xB2,0x02,0xD4,0x41,0x30,
+0xFF,0x01,0x39,0x65,0x02,0x0C,0x1C,0x98,0x02,0xD8,0x27,0xB4,0x02,0x0C,0xC0,0x33,
+0x02,0x20,0xC4,0x33,0x01,0x24,0xD0,0xB3,0x04,0x11,0x48,0x00,0xFD,0x05,0x0A,0x88,
+0x02,0x05,0x0A,0x00,0x02,0x38,0x78,0x68,0xFD,0x05,0x0A,0x88,0x00,0x0C,0x70,0xC0,
+0x01,0xE0,0x61,0x30,0x02,0xE2,0x65,0x30,0x01,0xE8,0x49,0xB0,0xFD,0x05,0x0A,0x0C,
+0x02,0xD0,0x41,0xB0,0x44,0xD8,0x19,0x28,0x0F,0x0C,0x18,0x98,0x01,0x11,0x1A,0x20,
+0xC0,0x12,0x1C,0x88,0x05,0x24,0x8E,0x70,0xC0,0xD8,0x99,0x40,0xFF,0x0E,0x96,0xE8,
+0x00,0x00,0x44,0x38,0xFF,0xFF,0x14,0x38,0x83,0x8E,0x1D,0x2B,0xD0,0xD8,0x99,0xC0,
+0x01,0xD8,0xB1,0x1B,0x07,0xD8,0x39,0xED,0x02,0x02,0xA1,0x33,0xFF,0xFF,0x04,0x3A,
+0xFF,0xD1,0xA5,0xE0,0xFF,0xFF,0xA4,0xBB,0x01,0x11,0xB0,0x07,0x01,0x00,0x0E,0xB0,
+0x01,0x07,0x14,0xB0,0x00,0xDC,0xB9,0x07,0xFF,0x11,0x14,0x02,0xFF,0xFF,0x10,0x3A,
+0xFF,0xFF,0x8C,0x3A,0x06,0x11,0x48,0x80,0x01,0xD5,0x15,0x30,0x00,0x21,0xBA,0xE8,
+0xD7,0x11,0xBC,0xC0,0xDB,0x11,0xBC,0xC0,0x00,0x0C,0x32,0xD8,0x04,0x11,0x48,0x84,
+0xC0,0x0A,0x15,0x88,0x40,0x0A,0xFE,0x70,0x01,0x0A,0x15,0xB0,0x00,0x11,0x16,0x88,
+0x66,0x04,0x18,0xB8,0x83,0x0C,0x0C,0xAC,0x00,0x0C,0xF8,0xC0,0x00,0x0C,0xF8,0xC0,
+0x00,0x0C,0xF8,0xC0,0x00,0x0C,0xEC,0xC0,0x00,0x0C,0xF8,0xC0,0x00,0x0C,0xFE,0xC0,
+0x00,0x0C,0xFE,0xC0,0x00,0x0C,0xFE,0xC0,0x00,0x0C,0xEC,0xC0,0x00,0x0C,0x04,0x41,
+0x00,0x0C,0x40,0x41,0x00,0x0C,0x40,0x41,0x00,0x0C,0x40,0x41,0xA0,0x00,0x06,0x2A,
+0xA0,0x00,0x06,0x2A,0x00,0x0C,0x40,0x41,0xFF,0xFF,0x14,0x38,0x88,0x34,0xF5,0x28,
+0x00,0x0C,0x92,0xDE,0x00,0x0C,0x38,0x4D,0x00,0x0C,0x62,0xDE,0x00,0x0C,0x38,0x4D,
+0x01,0x21,0x40,0x32,0x01,0x20,0x42,0x32,0x01,0x11,0x22,0x9C,0x01,0x21,0x44,0xB2,
+0x01,0x20,0x46,0xB2,0x01,0x11,0x22,0x9C,0x08,0x48,0x41,0xF9,0x02,0x46,0x45,0x30,
+0x04,0x11,0xA8,0xB3,0x01,0xB4,0xAD,0xB3,0xA8,0x01,0x18,0x38,0x01,0x12,0xB0,0xB3,
+0xFF,0xD8,0x17,0xE9,0x08,0xD4,0xA9,0x1B,0x00,0x0C,0x1E,0xC1,0x44,0xD4,0x0F,0xA8,
+0x01,0x07,0x14,0xB0,0x00,0xD8,0x23,0x69,0x01,0xD4,0xA9,0x1B,0x07,0xD4,0x17,0x69,
+0x00,0x0C,0x0E,0x41,0x02,0x0C,0x1C,0xB0,0xFF,0x0E,0x1C,0x98,0x00,0xD8,0x27,0x10,
+0x01,0xD4,0x15,0xB0,0x00,0x0A,0x14,0x98,0x80,0x01,0x1C,0xB8,0x00,0x0E,0x1C,0x98,
+0x02,0xA4,0x45,0x30,0x02,0x20,0x26,0x30,0x33,0xD4,0x15,0x28,0x00,0x1C,0x39,0x82,
+0x01,0x11,0x22,0x9C,0x00,0x00,0x14,0x38,0x88,0x12,0xB8,0xAE,0x88,0x12,0xB8,0xAE,
+0x01,0x11,0x22,0x9C,0x02,0x22,0x0C,0x30,0x00,0x00,0x44,0x38,0xB8,0x01,0x18,0xB8,
+0x0F,0x00,0x14,0x08,0x00,0x0C,0x18,0x98,0x01,0x12,0x14,0x30,0x02,0x06,0x44,0x30,
+0xFF,0x11,0x22,0x8C,0x02,0x0C,0x0C,0x30,0x08,0x0C,0x18,0x18,0x02,0x12,0x1C,0xB0,
+0x02,0x0A,0x0C,0x30,0x82,0x10,0x14,0x28,0x01,0x10,0x22,0x98,0x84,0x11,0x14,0xA8,
+0x83,0x0E,0x1C,0x28,0x02,0x06,0x14,0x30,0x80,0x0F,0x6A,0xF9,0xFE,0x0C,0x18,0x18,
+0x02,0x12,0x14,0x30,0x02,0x06,0x18,0x30,0xFF,0x11,0x22,0x8C,0x01,0x0C,0x1A,0xB0,
+0x00,0x11,0x72,0xC1,0x02,0x48,0x15,0xB0,0x80,0x0C,0x04,0xA8,0xE1,0x48,0x7B,0x29,
+0xFF,0x11,0x22,0x8C,0x02,0x48,0x91,0x32,0x00,0x0C,0x72,0xC1,0x00,0x11,0x1A,0x88,
+0x02,0x4C,0x15,0x30,0x80,0x0C,0x04,0xA8,0xE1,0x4C,0x89,0x29,0xFF,0x11,0x22,0x8C,
+0x02,0x4C,0x99,0x32,0x00,0x0C,0x80,0x41,0x02,0x4C,0x15,0x30,0x01,0x0A,0x04,0x80,
+0x01,0x0C,0x06,0x30,0xE1,0x4C,0x97,0x29,0xFF,0x11,0x22,0x8C,0x02,0x4C,0x99,0x32,
+0x00,0x0C,0x8C,0x41,0x7F,0x11,0x9C,0x41,0x01,0x0C,0x1A,0xB0,0xFF,0x11,0xA0,0xC1,
+0x02,0x48,0x15,0xB0,0x81,0x0C,0x04,0x28,0xE1,0x48,0xA9,0x29,0xFF,0x11,0x22,0x8C,
+0x02,0x48,0x91,0x32,0x00,0x0C,0xA0,0xC1,0xFF,0x11,0x1A,0x00,0x02,0x4C,0x15,0x30,
+0x81,0x0C,0x04,0x28,0xE1,0x4C,0xB7,0xA9,0xFF,0x11,0x22,0x8C,0x02,0x4C,0x99,0x32,
+0x00,0x0C,0xAE,0x41,0x02,0x4A,0x15,0x30,0x01,0x0A,0x04,0xB0,0x01,0x0B,0x06,0x98,
+0xE1,0x4A,0xC5,0xA9,0xFF,0x11,0x22,0x8C,0x02,0x4A,0x95,0x32,0x00,0x0C,0xBA,0x41,
+0x02,0x4A,0x15,0x30,0x01,0x0A,0x04,0xB0,0xFF,0x0B,0x06,0x18,0xE1,0x4A,0xD3,0x29,
+0xFF,0x11,0x22,0x8C,0x02,0x4A,0x95,0x32,0x00,0x0C,0xC8,0x41,0x02,0x0C,0x0C,0x30,
+0x02,0xD8,0x0D,0xB0,0x00,0x0C,0x4C,0xD8,0x1B,0x11,0x14,0x80,0x00,0x0C,0x1A,0x5A,
+0xC0,0x0C,0xE4,0xF1,0xC0,0x11,0xF0,0xC1,0xFF,0x8E,0xEF,0xF9,0x02,0x8C,0x41,0xB0,
+0xFF,0x21,0xFC,0xF1,0xFF,0xFF,0x18,0x3B,0xC8,0x11,0xF8,0xC1,0xC8,0x11,0xF0,0x41,
+0x00,0x0C,0x9E,0x5A,0xFF,0x21,0xFC,0xF1,0x02,0xD6,0xB1,0xB3,0x00,0x0C,0x4C,0xD8,
+0x1B,0x11,0x14,0x80,0x00,0x0C,0x1A,0x5A,0x00,0x00,0x40,0xB8,0xFF,0xFF,0xD4,0x3B,
+0x00,0x0C,0xEE,0x5E,0x00,0x0C,0x06,0x52,0x1B,0x11,0x2C,0x5C,0x00,0x0C,0xB0,0xDE,
+0x00,0x0C,0x00,0x52,0x00,0x0C,0x16,0xDE,0x02,0x06,0xB0,0xB3,0x02,0x06,0x18,0x30,
+0xFF,0xD9,0x17,0x72,0x02,0xD8,0x41,0x30,0x02,0x00,0x41,0xB4,0x02,0x12,0x40,0xB0,
+0xFE,0x0C,0x18,0x9C,0x02,0x0C,0x0C,0x30,0x00,0x0C,0x36,0xDA,0x02,0x0A,0x0C,0x30,
+0x00,0x0A,0x40,0x5A,0x02,0x06,0x14,0x30,0x02,0xD4,0x19,0xB0,0x02,0x12,0x0C,0x30,
+0x02,0xD4,0x1D,0x30,0xFF,0xFF,0x26,0xB8,0x02,0x06,0x40,0xB0,0xFF,0x21,0x1E,0x62,
+0x02,0x06,0x18,0x30,0x02,0xDA,0x1D,0xB0,0xFF,0xFF,0x26,0x3C,0xC0,0x0C,0x3C,0x62,
+0x02,0x01,0xA8,0xBB,0xA0,0x01,0xB4,0x3F,0x04,0x01,0xA8,0xBB,0xA2,0x01,0xB4,0xBF,
+0x1B,0x0C,0x52,0xE2,0x01,0xB7,0x79,0x32,0x04,0xB7,0x4D,0xE2,0x01,0x11,0x7A,0x02,
+0xF0,0x9F,0x7D,0x8A,0x00,0x0C,0x6C,0xC2,0x03,0xB7,0x6D,0xE2,0x01,0x9E,0x7B,0x8A,
+0x00,0x0C,0x6C,0xC2,0x23,0x0C,0x56,0x72,0x13,0x0C,0x6C,0xE2,0x02,0x20,0x78,0x32,
+0x80,0x0B,0x5F,0x7A,0x01,0x21,0x79,0x32,0x01,0x20,0x7B,0x32,0x88,0x11,0x02,0x00,
+0x02,0x20,0x0C,0xB0,0x02,0xEA,0x41,0xB0,0x02,0x52,0x15,0x30,0x02,0x06,0x40,0xB0,
+0x01,0x01,0x22,0xB0,0x02,0x0A,0x7C,0x32,0x00,0x0C,0xD2,0xDA,0x00,0x0C,0x38,0x4D,
+0x00,0x0C,0x8C,0x59,0x00,0x0C,0xF6,0x5D,0x00,0x0C,0x9C,0x52,0x02,0x46,0x45,0x30,
+0xFF,0xFF,0x14,0x38,0x83,0xAC,0x59,0x2B,0x04,0x9F,0x81,0xFA,0x00,0x0C,0x08,0x5F,
+0x40,0x49,0x9B,0xFA,0xFF,0x45,0x9B,0xF2,0x02,0x20,0x0C,0xB0,0x02,0x44,0x0D,0xB0,
+0x00,0x0C,0xB2,0xDA,0xC5,0x11,0x32,0xD8,0x88,0x11,0x02,0x00,0x02,0x06,0x40,0xB0,
+0xFF,0xFF,0x14,0x38,0x83,0x4A,0x95,0x2A,0x00,0x0C,0x60,0xDC,0x01,0x01,0x22,0xB0,
+0x00,0x0C,0x36,0x45,0x00,0x0C,0xB2,0xDA,0xC5,0x11,0x32,0xC0,0xFF,0xFF,0xAC,0x3B,
+0x01,0x11,0x1A,0x80,0x02,0x12,0x40,0xB0,0xFE,0x0C,0x18,0x18,0xFF,0x21,0x38,0xF5,
+0x00,0x0C,0xF2,0xDE,0x00,0x0C,0x38,0x4D,0x02,0x20,0xAC,0xB3,0x02,0x00,0x41,0x30,
+0x00,0x0C,0xA6,0xC2,0x09,0x0A,0x39,0xE5,0x08,0x48,0x39,0x7D,0x3D,0x1C,0x17,0xA8,
+0x02,0x22,0x0C,0x30,0x02,0x46,0x45,0x30,0xA8,0x01,0x18,0x38,0x3D,0x0B,0x14,0xA8,
+0x00,0x0C,0x18,0x98,0x02,0x0C,0x1C,0xB0,0x44,0x0B,0x0E,0xA8,0x01,0x07,0x14,0xB0,
+0x00,0x12,0xD0,0x6A,0xFF,0x0C,0x18,0x98,0x00,0x12,0x26,0x00,0x00,0x0C,0x4E,0xC1,
+0x1A,0x11,0x2A,0x80,0x02,0x48,0x15,0xB0,0x01,0x0B,0xB8,0x6E,0x01,0x0A,0x04,0xB0,
+0x01,0x0B,0x06,0x80,0xE1,0x48,0xDF,0xAA,0x01,0x11,0x22,0x9C,0x02,0x48,0x91,0x32,
+0x00,0x0C,0xD2,0xC2,0x02,0x20,0xD4,0xB3,0x01,0x0A,0xE5,0xB3,0x10,0x0B,0xF1,0xFA,
+0xFF,0xFF,0x14,0x38,0x8A,0x34,0xF1,0xAA,0x00,0x0C,0x3E,0xDD,0x00,0x0C,0x72,0x4A,
+0x02,0x46,0x45,0x30,0xFD,0x8F,0x1F,0xF3,0xC0,0x11,0x4C,0xDD,0x00,0x0C,0xFE,0xD2,
+0x1E,0x11,0x6C,0xDA,0x02,0xEA,0x41,0xB0,0x00,0x11,0x6C,0xC2,0xFF,0x8E,0x09,0xFB,
+0x02,0x8C,0x41,0xB0,0xFF,0x21,0x0C,0x73,0x00,0x0C,0xB2,0x5D,0x00,0x0C,0x0A,0x43,
+0xC8,0x11,0x4C,0x5D,0x00,0x0C,0x14,0xCB,0x02,0xEA,0x41,0xB0,0x00,0x0C,0x92,0xDE,
+0x00,0x0C,0x72,0x4A,0x02,0x0A,0x40,0xB0,0x10,0x0B,0x1D,0xFB,0x80,0x11,0x6E,0xD9,
+0x02,0xEA,0x41,0xB0,0xA0,0x00,0x2C,0xAA,0x1D,0x11,0x6C,0xC2,0x2A,0x11,0x2A,0x80,
+0x02,0x20,0xD4,0xB3,0x01,0x0A,0xE5,0xB3,0xFF,0x0B,0x3B,0xFB,0x01,0x0B,0x3D,0xF3,
+0x02,0x0B,0x53,0x73,0x03,0x0B,0x83,0x73,0x04,0x0B,0xA9,0x73,0x05,0x0B,0xB3,0x73,
+0x06,0x0B,0xAF,0xF3,0x07,0x0B,0xF9,0x73,0x08,0x0B,0xF7,0xF3,0x09,0x0B,0x15,0xF4,
+0xA0,0x00,0x06,0x2A,0xFF,0x11,0x28,0x02,0xC0,0x11,0x88,0xDC,0xC8,0x11,0x88,0x5C,
+0x00,0x00,0x40,0xB8,0x00,0x0C,0xF2,0xDD,0x00,0x0C,0x4C,0x53,0x00,0x0C,0xE2,0x5D,
+0x00,0x0C,0x4C,0x53,0x23,0x11,0x2C,0xDC,0x00,0x0C,0xB0,0xDE,0x00,0x0C,0x42,0xD3,
+0x00,0x0C,0x5C,0xC4,0x00,0x0C,0xF8,0xDE,0x00,0x0C,0x6C,0x4A,0x02,0x10,0x5D,0x7B,
+0xC0,0x11,0xB2,0xDC,0x02,0xEA,0x41,0xB0,0x04,0x10,0x63,0xFB,0x00,0x0C,0xA4,0x5C,
+0x02,0xEA,0x41,0xB0,0x01,0x10,0x73,0x7B,0x00,0x00,0x40,0xB8,0x00,0x0C,0xEE,0x5E,
+0x00,0x0C,0x6C,0xD3,0x23,0x11,0x2C,0xDC,0x00,0x0C,0xB0,0xDE,0x00,0x0C,0x66,0xD3,
+0x02,0xEA,0x41,0xB0,0xFD,0x8F,0x81,0x73,0x80,0x10,0x7B,0xFB,0xB0,0x00,0xCC,0xAF,
+0x02,0xEA,0x41,0xB0,0x40,0x10,0x81,0xFB,0xB0,0x00,0xDA,0x2F,0x02,0xEA,0x41,0xB0,
+0x00,0x0C,0x5C,0xC4,0x02,0x46,0x45,0x30,0x00,0x0C,0x24,0x5D,0x02,0x10,0x8D,0xFB,
+0xC0,0x11,0xC8,0x5C,0x02,0xEA,0x41,0xB0,0x04,0x10,0x93,0xFB,0x00,0x0C,0xBE,0xDC,
+0x02,0xEA,0x41,0xB0,0x01,0x10,0x99,0xFB,0xB0,0x00,0x8A,0x2F,0x02,0xEA,0x41,0xB0,
+0xFD,0x8F,0xA7,0xF3,0x80,0x10,0xA1,0x7B,0xB0,0x00,0xCC,0xAF,0x02,0xEA,0x41,0xB0,
+0x40,0x10,0xA7,0x7B,0xB0,0x00,0xDA,0x2F,0x02,0xEA,0x41,0xB0,0x00,0x0C,0x5C,0xC4,
+0x00,0x0C,0x3E,0xDD,0x00,0x0C,0x72,0x4A,0x00,0x0C,0xB2,0x43,0x00,0x0C,0x3A,0x5D,
+0x00,0x0C,0x72,0x4A,0x02,0x46,0x45,0x30,0xC0,0x11,0x4C,0xDD,0x00,0x0C,0xEC,0x4B,
+0xFF,0x8E,0xC3,0xFB,0x02,0x8C,0x41,0xB0,0xFF,0x21,0xC6,0x73,0x00,0x0C,0xB2,0x5D,
+0x00,0x0C,0xC4,0xC3,0xC8,0x11,0x4C,0x5D,0x00,0x0C,0xCE,0x4B,0x02,0xEA,0x41,0xB0,
+0x00,0x0C,0x92,0xDE,0x00,0x0C,0x72,0x4A,0x02,0x0A,0x40,0xB0,0x02,0x20,0x0C,0xB0,
+0x02,0xEA,0x41,0xB0,0x01,0x10,0x15,0x30,0x02,0x06,0x40,0xB0,0x40,0x0A,0xF2,0xFB,
+0xFF,0x4D,0xE7,0x6B,0x00,0x0C,0x9A,0xD9,0x40,0x01,0x18,0x38,0x00,0x0C,0x3A,0xD9,
+0x00,0x0C,0xE8,0xD3,0xB0,0x00,0x46,0x2A,0x00,0x0C,0xE8,0x43,0x00,0x4D,0x6D,0x5A,
+0x02,0xEA,0x41,0xB0,0x13,0x11,0x40,0xC2,0x23,0x11,0x40,0xDA,0x02,0xEA,0x41,0xB0,
+0x00,0x11,0x6C,0xC2,0x23,0x11,0x2C,0xDC,0x00,0x0C,0x5C,0xC4,0xFF,0x11,0x28,0x02,
+0x00,0x0C,0x24,0x5D,0xB0,0x00,0x1C,0xAE,0x02,0x86,0x45,0x30,0xFF,0x23,0x10,0xF4,
+0x00,0x0C,0xEC,0xDD,0x00,0x0C,0x0C,0x54,0xC0,0x11,0xC8,0x5C,0xC8,0x11,0xC8,0xDC,
+0xB0,0x00,0x8A,0x2F,0x02,0xEA,0x41,0xB0,0x02,0xAE,0x45,0x30,0x00,0x0C,0xFE,0xC3,
+0xB0,0x00,0x20,0xAE,0x00,0x0C,0x5C,0xC4,0x00,0x0C,0xF8,0xDE,0x00,0x0C,0x6C,0x4A,
+0xFD,0x8F,0x1D,0xF4,0x22,0x11,0x6C,0xC2,0x00,0x0C,0x24,0x5D,0xB0,0x00,0x72,0xAF,
+0x02,0xEA,0x41,0xB0,0x00,0x0C,0x6C,0x52,0xC0,0x11,0xC8,0x5C,0xC8,0x11,0xC8,0xDC,
+0xB0,0x00,0x8A,0x2F,0x00,0x0C,0x5C,0xC4,0x65,0x07,0x0C,0xB8,0x00,0x0C,0xBA,0xDE,
+0x02,0x0C,0x0C,0x30,0x80,0x11,0x6E,0xD9,0x02,0x06,0x18,0x30,0xFF,0x4B,0x41,0x7A,
+0x40,0x49,0x4D,0x7C,0xFF,0x45,0x4D,0x74,0x02,0x20,0x0C,0xB0,0x02,0x44,0x41,0x30,
+0xFF,0xFF,0x14,0x38,0x83,0x4A,0x95,0x2A,0x02,0x4A,0x15,0x30,0x89,0x10,0x4A,0xAC,
+0x00,0x11,0x6C,0xDA,0x02,0x06,0x40,0xB0,0x02,0xEA,0x89,0xB2,0x40,0x11,0x6E,0xD9,
+0xFF,0xEB,0x39,0x75,0x02,0x20,0xA8,0x33,0x02,0xEA,0x41,0xB0,0x01,0x00,0x14,0xB8,
+0x83,0x4A,0x95,0x2A,0x02,0xD4,0x41,0xB4,0x02,0xEA,0x41,0xB0,0x40,0x10,0x83,0x74,
+0x02,0x4A,0x15,0x30,0x89,0x10,0x38,0x2D,0xFF,0x0B,0x7B,0xFC,0x01,0x0B,0x7B,0xF4,
+0x02,0x0B,0x7F,0x74,0x03,0x0B,0x7F,0xF4,0x04,0x0B,0x7F,0x74,0x05,0x0B,0x7F,0xF4,
+0x06,0x0B,0x7F,0xF4,0x07,0x0B,0x85,0x74,0x08,0x0B,0x85,0x74,0x09,0x0B,0x7F,0xF4,
+0xA0,0x00,0x06,0x2A,0x00,0x0C,0xFE,0xDD,0x00,0x11,0x6C,0xC2,0x00,0x0C,0x12,0x5E,
+0x00,0x11,0x6C,0xC2,0x13,0x11,0x40,0xC2,0xB0,0x00,0x62,0x2F,0x00,0x11,0x6C,0xC2,
+0x01,0x11,0x1A,0x80,0xFF,0xFF,0xB0,0xBB,0x02,0x12,0x40,0xB0,0xFE,0x0C,0x18,0x18,
+0xFF,0x21,0x38,0xF5,0x00,0x0C,0xE2,0x5D,0x00,0x0C,0xA0,0x54,0x00,0x0C,0x4C,0xD8,
+0x23,0x11,0x14,0x00,0x00,0x0C,0x1A,0x5A,0xFF,0xD9,0x8D,0x74,0x02,0xD8,0x41,0x30,
+0x02,0x00,0x41,0x30,0x00,0x0C,0x90,0xC4,0xFF,0x8E,0xB1,0x7C,0x02,0x8C,0x41,0xB0,
+0xFF,0x21,0x38,0xF5,0xFF,0xFF,0x18,0x3B,0xFF,0xFF,0x44,0x3B,0xC8,0x11,0xBA,0xC4,
+0xC8,0x11,0xB2,0x44,0x00,0x0C,0x9E,0x5A,0xFF,0x21,0x38,0xF5,0x02,0xD6,0xB1,0xB3,
+0x00,0x0C,0x4C,0xD8,0x23,0x11,0x14,0x00,0x00,0x0C,0x1A,0x42,0xFF,0x8E,0xC7,0xFC,
+0x02,0x8C,0x41,0xB0,0xFF,0x21,0x38,0xF5,0xC8,0x11,0xD0,0xC4,0xC8,0x11,0xC8,0xC4,
+0x00,0x0C,0x9E,0x5A,0xFF,0x21,0x38,0xF5,0x02,0xD6,0xB1,0xB3,0x00,0x0C,0x4C,0xD8,
+0x02,0x0C,0x0C,0x30,0x02,0x20,0xE0,0x33,0x00,0x0C,0x36,0xDA,0xFF,0xFF,0xB0,0xBB,
+0xB0,0x00,0xAE,0x2F,0x00,0x0C,0x0C,0xD5,0x02,0x20,0x0C,0xB0,0x02,0xEA,0x41,0xB0,
+0x09,0x0B,0xEB,0xE4,0x02,0x34,0x15,0x30,0x02,0x06,0x40,0xB0,0x88,0x20,0x0D,0xAD,
+0x00,0x0C,0xEC,0x44,0x02,0x06,0x40,0xB0,0xFF,0xD9,0xF7,0x64,0x02,0xD4,0x19,0xB0,
+0x02,0x12,0xE0,0xB3,0xFF,0xF1,0xFB,0xF4,0x00,0x0C,0xFE,0x44,0x00,0x0C,0x2A,0xDD,
+0xFF,0xD7,0xFF,0x64,0x02,0xDA,0x1D,0xB0,0x02,0xD8,0x27,0x30,0x02,0xD4,0x19,0xB0,
+0x02,0x12,0x0C,0x30,0x02,0xD4,0x1D,0x30,0xFF,0xFF,0x26,0xB8,0x23,0x11,0x40,0xDA,
+0x02,0x06,0x40,0xB0,0x00,0x0C,0x12,0x45,0x02,0x20,0xB0,0x33,0x02,0xD4,0x19,0xB0,
+0x02,0x12,0x40,0xB0,0xFF,0x21,0xD8,0x64,0x02,0x06,0x18,0x30,0xC8,0x0C,0x1A,0xE5,
+0xFF,0x8E,0x23,0xED,0xFF,0xF1,0x39,0xF5,0x02,0xF0,0x41,0x30,0x01,0x0C,0x18,0x18,
+0x00,0x0C,0x32,0xC0,0x02,0xF0,0x19,0x37,0x01,0x11,0x4A,0x80,0x08,0x28,0xC1,0xB3,
+0x00,0x11,0x4A,0x0C,0x02,0x20,0x0C,0xB0,0x02,0xD4,0x19,0xB0,0x02,0x12,0xAC,0x33,
+0x02,0xD8,0x41,0x30,0x02,0xD4,0x1D,0x30,0x02,0xD6,0x27,0xB0,0x02,0x06,0x40,0xB0,
+0xFF,0x11,0x22,0x8C,0x00,0x0C,0x4E,0x5E,0x00,0x0C,0x40,0xC5,0x00,0x0C,0x62,0xDE,
+0x00,0x0C,0x38,0x4D,0x00,0x0C,0x8C,0xDE,0x02,0x08,0x15,0x30,0x02,0xEA,0x41,0xB0,
+0x02,0x0A,0xA0,0xB2,0x01,0x11,0x22,0x9C,0x00,0x0C,0x9E,0x5A,0x02,0x0C,0xDC,0xB3,
+0x02,0xD6,0xD9,0x33,0x00,0x0C,0x36,0xDA,0xFF,0xFF,0xB0,0xBB,0xFF,0x21,0x40,0x71,
+0x02,0x20,0x0C,0xB0,0x02,0xEA,0x41,0xB0,0x02,0x50,0x15,0xB0,0x02,0x06,0x40,0xB0,
+0x8A,0x08,0x6B,0x2D,0x02,0x20,0xB0,0x33,0x02,0xD4,0x19,0xB0,0x02,0x12,0x40,0xB0,
+0x00,0x0C,0x56,0x45,0xC4,0xF2,0x6F,0x75,0x04,0x4C,0x41,0xE9,0xFF,0xD9,0x7B,0xF5,
+0x00,0x0C,0x2A,0xDD,0xFF,0xD7,0xAD,0x65,0x02,0xDA,0x1D,0xB0,0x02,0xD8,0x27,0x30,
+0x00,0x0C,0xAC,0x45,0x02,0xD4,0x19,0xB0,0x02,0x12,0x14,0x30,0xFF,0x0B,0xA4,0x75,
+0xFF,0xED,0x8D,0x75,0x02,0x20,0x0C,0xB0,0x02,0xEC,0x41,0xB0,0x02,0x0A,0x00,0xB2,
+0x02,0x06,0x40,0xB0,0x00,0x0C,0x90,0x45,0x02,0xEE,0x1D,0x30,0x02,0x0A,0x26,0xB0,
+0xFF,0x01,0x99,0x65,0x02,0xEE,0x1D,0x30,0x02,0x0E,0x1C,0x18,0x02,0x0A,0x26,0xB0,
+0x02,0x20,0x0C,0xB0,0x02,0x00,0xAD,0xB3,0x02,0x0A,0x40,0xB0,0x02,0xD6,0x01,0xB2,
+0x02,0x06,0x40,0xB0,0x00,0x0C,0xAC,0x45,0x02,0xDA,0x1D,0xB0,0xFF,0xFF,0x26,0xB8,
+0x02,0xEC,0xB1,0xB3,0x00,0xEE,0x4D,0x58,0xC8,0xEE,0xB1,0x65,0x00,0x0C,0xC8,0x59,
+0x01,0x10,0x22,0x1C,0xFF,0xFF,0xB0,0xBB,0xFF,0x21,0x40,0x71,0x02,0x20,0x0C,0xB0,
+0x02,0xEA,0x41,0xB0,0x02,0x50,0x15,0xB0,0x02,0x06,0x40,0xB0,0x8A,0x08,0xC7,0x2D,
+0x02,0x20,0xB0,0x33,0x02,0x04,0x41,0xB0,0x00,0x0C,0xB4,0x45,0xFF,0xD9,0xD9,0x75,
+0x02,0x20,0x0C,0xB0,0x02,0x04,0xAD,0x33,0x02,0xD8,0x41,0x30,0x02,0xD6,0x09,0x32,
+0x02,0x06,0x40,0xB0,0xFF,0xD7,0xDF,0x65,0x02,0xD8,0x45,0xB3,0x00,0x0C,0xDE,0x45,
+0x02,0x04,0x19,0x33,0xFF,0x05,0xDF,0x65,0xFF,0xFF,0x44,0x3B,0x00,0x0C,0xC8,0x59,
+0x01,0x10,0x22,0x1C,0x02,0x46,0x45,0x30,0x02,0x20,0x0C,0xB0,0x02,0xEA,0x41,0xB0,
+0x00,0x0C,0xEC,0xDD,0x00,0x0C,0x36,0x45,0x01,0x14,0x15,0xB0,0x00,0x9C,0x41,0x79,
+0x01,0x10,0x22,0x1C,0x01,0x49,0x41,0xE9,0xFF,0x0A,0x41,0xF1,0xC0,0x0A,0x15,0x88,
+0x80,0x0A,0x40,0xF1,0xC0,0x0A,0x40,0x71,0x01,0x10,0x22,0x1C,0x01,0x00,0x44,0xB8,
+0xFE,0x8F,0x09,0xE6,0x00,0x0C,0xEC,0xDD,0x00,0x0C,0x08,0x56,0x00,0x0C,0x16,0xDE,
+0x01,0x00,0x14,0xB8,0x83,0x22,0x44,0x28,0x02,0xFE,0x15,0x30,0x88,0x22,0x00,0xAE,
+0xFF,0x11,0x22,0x8C,0x02,0xAC,0x15,0xB0,0x89,0x10,0x38,0x2D,0x00,0x00,0x3C,0x3B,
+0x04,0x10,0x40,0x33,0x00,0x11,0x7E,0x0B,0x04,0x9D,0x39,0x6D,0xFF,0xFF,0x4C,0xBB,
+0x04,0x11,0x60,0x33,0x00,0x11,0x6A,0x0B,0x00,0x11,0x6C,0x0B,0xFF,0xB4,0x39,0xFD,
+0x04,0x11,0x50,0x33,0x3D,0xB4,0x15,0xA8,0xA8,0x01,0x1C,0xB8,0x00,0x11,0x16,0x88,
+0xFF,0x0A,0x3A,0xFE,0xFF,0x11,0x26,0x00,0x08,0x0B,0x16,0x18,0xFF,0x0A,0x14,0x98,
+0x00,0x0C,0x30,0x46,0xFF,0x0B,0x14,0x90,0x01,0x0A,0x14,0x18,0x00,0xB4,0x15,0x18,
+0xFF,0x0A,0x46,0x7E,0x44,0x0A,0x0E,0x28,0xFF,0x07,0x26,0x98,0x02,0x22,0x14,0x30,
+0x01,0xA4,0x45,0x30,0x40,0x10,0x00,0xB3,0x02,0x0A,0x44,0xB4,0x02,0x20,0x0C,0xB0,
+0x02,0x46,0x45,0x30,0x02,0xA4,0x45,0x30,0x01,0x35,0x15,0xB0,0x00,0x0A,0x14,0x98,
+0x80,0x01,0x18,0x38,0x00,0x0C,0x18,0x98,0x02,0x12,0x40,0xB0,0xFF,0x21,0x80,0xF6,
+0x00,0x0C,0x72,0x46,0x02,0x20,0x0C,0xB0,0x00,0x0C,0x8C,0xDE,0x02,0x20,0x18,0xB0,
+0x02,0xFC,0x15,0xB0,0x00,0x0C,0xDA,0xDE,0x80,0x0F,0x80,0xFE,0xFF,0x20,0x72,0x66,
+0xF0,0x21,0x80,0x7E,0xFF,0x0A,0x83,0xF6,0x01,0x49,0x85,0xEE,0x02,0x20,0x14,0xB0,
+0x02,0x06,0x40,0xB0,0x01,0x0A,0x6A,0xB2,0x01,0x0B,0x68,0xB2,0x01,0x11,0x22,0x9C,
+0x1F,0x11,0x86,0x46,0x20,0x11,0x86,0x46,0x21,0x11,0x86,0xC6,0x02,0x06,0x40,0xB0,
+0x00,0x0C,0x8C,0x59,0x01,0x10,0x22,0x1C,0x01,0x35,0x15,0xB0,0x01,0x34,0x17,0xB0,
+0x02,0x0A,0x40,0x34,0x02,0x50,0xA9,0x33,0x02,0x20,0x0C,0xB0,0x00,0x00,0x40,0xB8,
+0x02,0x08,0x15,0x30,0x8A,0xD4,0xA7,0xAE,0x00,0x0C,0xB0,0xDE,0x00,0x0C,0x98,0x56,
+0x02,0x06,0x40,0xB0,0x1D,0x11,0x8C,0x59,0x01,0x10,0x22,0x1C,0x02,0x20,0x14,0xB0,
+0x02,0x06,0x40,0xB0,0x01,0x0B,0x68,0xB2,0x01,0x0A,0x6A,0xB2,0x01,0x11,0x22,0x9C,
+0x01,0x00,0x14,0xB8,0x83,0x20,0x40,0x28,0x02,0xFC,0x15,0xB0,0x88,0x20,0x40,0x29,
+0x01,0x10,0x22,0x1C,0x02,0x48,0x15,0xB0,0x02,0x0B,0xBA,0xEE,0x01,0x0A,0x04,0xB0,
+0x02,0x0B,0x06,0x80,0xE1,0x48,0xC7,0x2E,0xFF,0x11,0x22,0x8C,0x02,0x48,0x91,0x32,
+0x00,0x0C,0xBA,0xC6,0xFF,0xFD,0x18,0xB8,0x00,0x0C,0xA0,0xC1,0x01,0x9E,0x1D,0xB0,
+0x08,0x0E,0xD6,0x7E,0xB0,0x01,0x18,0x38,0x00,0x0C,0x3A,0xC1,0xFF,0xA7,0xB9,0xE6,
+0x01,0x11,0x22,0x9C,0x82,0x10,0x14,0x28,0x01,0x10,0x22,0x98,0x84,0x11,0x14,0xA8,
+0x02,0x0A,0x0C,0x30,0xFF,0xFF,0x14,0x38,0x84,0x11,0x1C,0x28,0x02,0x06,0x14,0x30,
+0x83,0x0C,0x18,0x28,0x00,0x00,0x14,0x38,0x84,0x0E,0x1C,0x2C,0x00,0x0C,0xF2,0xDD,
+0x00,0x0C,0x38,0x55,0x02,0x46,0x15,0x30,0x88,0x22,0x40,0xA9,0x01,0x10,0x22,0x1C,
+0x02,0x46,0x45,0x30,0x00,0x00,0x14,0x38,0x8A,0x22,0x06,0xAF,0x02,0x22,0x18,0x30,
+0x02,0xFE,0x15,0x30,0xB1,0x00,0xDA,0x2E,0x80,0x0F,0x40,0xE9,0x22,0x11,0xB8,0x46,
+0x02,0xAC,0x15,0xB0,0x89,0x10,0x38,0x2D,0x02,0x20,0x0C,0xB0,0x02,0xB0,0x41,0xB0,
+0x02,0x12,0x75,0xB3,0x02,0x14,0x4D,0x33,0x02,0x16,0x71,0xB3,0x03,0x18,0x69,0xB3,
+0xFB,0x9F,0x3F,0x8B,0x00,0x11,0x6C,0xDA,0x00,0x0C,0x36,0x45,0x00,0x00,0x44,0x38,
+0x02,0x8A,0x15,0x30,0x89,0x0C,0x1E,0xAF,0x80,0x0C,0x04,0xA8,0xE1,0x8A,0x2B,0xAF,
+0xFF,0x11,0x22,0x8C,0x02,0x8A,0x15,0x33,0x00,0x0C,0x1E,0xC7,0x00,0x00,0x44,0x38,
+0x02,0x8A,0x15,0x30,0x81,0x0C,0x04,0x28,0xE1,0x8A,0x39,0xAF,0xFF,0x11,0x22,0x8C,
+0x02,0x8A,0x15,0x33,0x00,0x0C,0x2E,0xC7,0x00,0x0C,0x42,0xD9,0x01,0x0A,0xB0,0xB3,
+0x0F,0x00,0x18,0x08,0x00,0x11,0x1A,0x88,0x00,0x11,0x02,0x88,0x01,0xD8,0x15,0xB0,
+0x01,0x00,0x0E,0xB0,0x00,0x07,0x52,0x7F,0x02,0x38,0x4C,0xFF,0x02,0x00,0x60,0xB8,
+0x02,0x0C,0x64,0xB0,0x11,0x00,0x00,0x98,0x01,0x00,0x14,0x30,0x00,0xDD,0x47,0x67,
+0x01,0x01,0x22,0x34,0xC0,0x0C,0x5E,0xE7,0x02,0x02,0x41,0x34,0x02,0x04,0x41,0x34,
+0xC0,0x0C,0x6C,0x67,0x02,0x02,0x0D,0x30,0xFF,0xFF,0x04,0x3A,0x02,0x06,0x40,0xB0,
+0xFF,0x21,0x38,0xF5,0xC1,0x11,0x32,0x40,0x02,0x04,0x0D,0x30,0xFF,0xFF,0x08,0x3A,
+0x02,0x06,0x40,0xB0,0xFF,0x21,0x38,0xF5,0xC9,0x11,0x32,0xC0,0xA0,0x00,0x24,0x2C,
+0x02,0x20,0x0C,0xB0,0xC8,0x11,0x76,0xDF,0x02,0x06,0x40,0xB0,0x00,0x0C,0x82,0x4F,
+0xFF,0xFF,0x44,0x3B,0xFF,0x21,0x9E,0x77,0x02,0x04,0xB1,0xB3,0x40,0x49,0x8D,0x7F,
+0xB0,0x00,0xAA,0x2D,0x00,0x0C,0x9A,0xC7,0x02,0xA2,0xAD,0x33,0x02,0x20,0x44,0xB3,
+0xFF,0xFF,0x08,0x3A,0xFF,0xD7,0x99,0xF7,0x00,0x0C,0xAC,0xDF,0x00,0x0C,0x9A,0xC7,
+0xC9,0x11,0x32,0xD8,0x02,0xD8,0x41,0x30,0xFF,0x21,0x84,0x67,0x02,0x06,0x40,0xB0,
+0x10,0x49,0x39,0xFD,0xEF,0x11,0x9C,0x41,0x02,0x20,0xA8,0x33,0x02,0xD6,0x41,0xB0,
+0x02,0xD4,0x05,0xB2,0x02,0xD4,0x41,0xB4,0x02,0x20,0xA8,0x33,0x02,0xD6,0x41,0xB0,
+0x02,0xD4,0x09,0xB2,0x02,0xD4,0x41,0xB4,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+};
+
+static const u8 Lseq[] = {
+0x33,0x11,0x00,0x80,0x00,0x00,0x44,0x38,0x00,0x11,0x00,0x08,0xFD,0x05,0x0A,0x88,
+0x44,0x00,0x12,0x70,0x11,0x00,0x00,0x98,0x33,0x00,0x14,0xE0,0x44,0x11,0x00,0x80,
+0x00,0x0C,0x14,0x40,0x00,0x11,0x00,0x08,0x80,0xE1,0x09,0xE8,0x00,0x0C,0x1E,0x40,
+0x02,0x06,0xC0,0x33,0x02,0x06,0xD0,0xB3,0x02,0x06,0xCC,0x33,0x04,0xE6,0x0D,0x30,
+0x02,0xE0,0x0D,0xB4,0x80,0xE1,0xC3,0x03,0x00,0x0C,0x06,0x40,0x4A,0x00,0x0C,0xB8,
+0x02,0x3C,0x14,0x30,0x81,0x38,0xB0,0xAB,0x02,0x3E,0x14,0xB0,0x81,0x3A,0xB4,0xAB,
+0x22,0x00,0x34,0x60,0x08,0xDA,0x83,0xE8,0x01,0xDA,0x59,0xF8,0x10,0x83,0x14,0x08,
+0x00,0xE3,0x14,0x88,0xFF,0x0A,0x4A,0x78,0x40,0xD9,0x7F,0x78,0x40,0x11,0x72,0x00,
+0xB1,0x00,0x78,0x2A,0x40,0xC8,0x47,0xF8,0x80,0xC8,0x91,0x03,0x10,0x11,0x06,0x81,
+0x01,0x11,0x74,0x84,0xFF,0x80,0x14,0x88,0x00,0xE0,0x14,0x88,0xFF,0x0A,0x7E,0xF8,
+0x40,0xD9,0x7F,0x78,0xC0,0xD9,0x14,0x88,0xC0,0x0A,0x7E,0x70,0x00,0x0C,0xFA,0x45,
+0x55,0x11,0x02,0x00,0x01,0x3B,0x14,0xB0,0x00,0x3F,0x14,0x08,0x01,0x0A,0x86,0x68,
+0x01,0x01,0x22,0xB0,0x40,0xD9,0xFB,0xED,0x80,0xD9,0x67,0x6A,0xFF,0x00,0x6E,0x78,
+0x11,0x00,0x74,0x70,0x22,0x00,0x7A,0xF0,0x00,0x0C,0x8E,0x40,0x20,0xD9,0x1F,0x69,
+0x02,0xDA,0x81,0x68,0xFF,0x11,0x22,0x8C,0x02,0xDB,0x8D,0xE8,0x02,0xDA,0x81,0x68,
+0xFF,0x11,0x22,0x8C,0x80,0xDA,0x85,0xE8,0xFF,0x11,0x22,0x8C,0xA1,0x00,0xE0,0x2C,
+0xA3,0x00,0xAE,0xAA,0xA1,0x00,0x48,0x2E,0xA1,0x00,0x3C,0xAF,0x01,0x01,0x22,0xB0,
+0x55,0x11,0x00,0x80,0xA2,0x00,0x00,0x28,0x02,0x11,0x76,0x04,0xF0,0x04,0x14,0x88,
+0x4C,0x0A,0x14,0x28,0xFF,0x0A,0x2A,0x18,0x01,0x05,0x0A,0x84,0x4A,0x00,0x0C,0xB8,
+0x10,0x45,0xCE,0xE8,0x20,0xCE,0xDB,0x68,0x02,0x54,0x40,0x30,0xB1,0x00,0x8C,0xAC,
+0x00,0x0C,0x18,0x49,0x00,0x00,0x90,0x38,0x18,0x10,0xA1,0xB0,0x55,0x11,0x02,0x00,
+0x01,0x25,0x14,0xB0,0x01,0x11,0x4A,0x80,0x05,0x11,0x00,0x80,0x18,0xE0,0xA3,0x30,
+0x55,0x11,0x00,0x80,0x0A,0x11,0x4A,0x00,0x01,0x01,0x22,0xB0,0x00,0x11,0x94,0x88,
+0x01,0x52,0x14,0xB0,0x01,0x0A,0xC4,0x70,0x00,0x11,0x92,0x88,0x00,0x11,0xA2,0x88,
+0x07,0x0A,0xC4,0xE0,0xFD,0x11,0xC6,0xC0,0xFF,0x11,0xC6,0x40,0x0A,0x11,0x92,0x00,
+0x01,0x0C,0xA2,0xB0,0x02,0x8A,0xC4,0x30,0x80,0x11,0x90,0x84,0x01,0xCA,0xDB,0x78,
+0x00,0x00,0x90,0x38,0x34,0x00,0xA0,0xB8,0xFF,0x00,0xA2,0xB8,0xB2,0x00,0x5E,0x2D,
+0x80,0x11,0x90,0x84,0xB2,0x00,0x6C,0xAD,0x00,0x0C,0x18,0x49,0x00,0x11,0x94,0x88,
+0xA1,0x52,0x1A,0x71,0xB1,0x00,0x9C,0x2C,0x00,0x0C,0x18,0x49,0xC0,0xC8,0x19,0xE9,
+0x40,0x9E,0x19,0x69,0xB2,0x00,0x4E,0xAD,0x03,0x3A,0x1D,0x61,0x00,0x11,0x94,0x88,
+0x01,0x52,0x14,0xB0,0x46,0x0A,0xFA,0xF0,0x39,0x0A,0x08,0x71,0x34,0x0A,0x12,0x71,
+0x80,0x11,0x90,0x84,0x1B,0x48,0x15,0x88,0x13,0x0A,0x1C,0x61,0x02,0x11,0x82,0x5F,
+0xB2,0x00,0x08,0x2D,0x04,0x0C,0x81,0xB2,0xB2,0x00,0x1C,0x2E,0x00,0x0C,0x14,0xC1,
+0x1B,0x48,0x15,0x88,0x13,0x0A,0x1C,0x61,0x01,0x11,0x82,0x5F,0xB2,0x00,0x0E,0x2D,
+0x00,0x0C,0x14,0xC1,0xB2,0x00,0xDE,0x2C,0xB2,0x00,0x4E,0xAD,0x80,0x11,0x90,0x84,
+0x02,0xE4,0xC9,0x03,0xB2,0x00,0x44,0x2F,0x80,0x11,0x90,0x84,0x20,0x11,0x72,0x00,
+0x02,0xE4,0x3F,0x69,0xFF,0xC0,0x3E,0x69,0xFF,0xC1,0x3E,0xE9,0x0F,0xC2,0x3E,0xE9,
+0x00,0x11,0x94,0x88,0x01,0x52,0x14,0xB0,0x01,0x0A,0x34,0xF1,0x05,0x0A,0x3C,0x61,
+0xB1,0x00,0x28,0x2C,0x00,0x0C,0x3C,0xC1,0xA0,0x00,0x0C,0x38,0x20,0x49,0x3B,0x69,
+0xA1,0x00,0x46,0xAB,0xA1,0x00,0x32,0xAB,0x80,0x11,0x7E,0x5F,0x18,0x11,0xB8,0x80,
+0xFD,0xE4,0xC9,0x8B,0xFF,0xFF,0xC4,0x3C,0x80,0x0B,0x49,0xF9,0xB3,0x00,0x66,0xA9,
+0x00,0x0C,0x18,0x58,0x80,0xE4,0x89,0xE9,0x08,0xC8,0x87,0xE9,0x80,0xE5,0x87,0xE9,
+0x0F,0xCB,0x6F,0x69,0x80,0x48,0x93,0x69,0x20,0xE4,0x93,0x69,0x40,0x49,0x87,0xE9,
+0x80,0xC8,0x7D,0xE9,0x80,0xCE,0x61,0xF9,0x20,0xE5,0x91,0x69,0x00,0x0C,0x06,0x40,
+0x20,0xC8,0x87,0xE9,0x02,0xCA,0x7F,0xE9,0x02,0x86,0x80,0x69,0x40,0x4C,0x07,0x78,
+0x02,0x93,0x06,0x78,0x02,0x7F,0x06,0x68,0x00,0x0C,0x86,0x41,0xC0,0x06,0x14,0x38,
+0x8B,0xB0,0x76,0x29,0xC0,0x06,0x60,0xB9,0x80,0x11,0x72,0x00,0x08,0x11,0xCE,0x5D,
+0x08,0x11,0xB8,0x00,0x00,0x0C,0x9E,0x41,0x05,0x11,0x84,0xC1,0x0F,0x11,0x84,0xC1,
+0x40,0xE5,0xCB,0x03,0x24,0x11,0x84,0xC1,0x00,0x0C,0xCE,0xDD,0x00,0x0C,0xA0,0xDF,
+0x80,0x4C,0x9F,0x79,0xB1,0x00,0x98,0xA9,0x00,0x0C,0x52,0xDD,0x00,0x0C,0x5C,0xC2,
+0x00,0x0C,0xA0,0xDF,0x06,0x11,0x18,0x80,0x80,0x0B,0x9D,0xF9,0x28,0x11,0x18,0x80,
+0x20,0xE5,0x9D,0x69,0x29,0x11,0x18,0x00,0x00,0x0C,0xCE,0xDD,0xB1,0x00,0x98,0xA9,
+0x00,0x0C,0x5C,0xC2,0x06,0x11,0xF8,0x03,0x00,0x0C,0xDE,0x5D,0x00,0x0C,0x5C,0xC2,
+0x4A,0x00,0x0C,0xB8,0x02,0x48,0xB9,0x79,0xB1,0x00,0x18,0x2C,0x80,0x48,0xBB,0x69,
+0x12,0x11,0x94,0x00,0x02,0x52,0x44,0x32,0xB1,0x00,0x2A,0xAC,0x00,0x0C,0xBC,0x41,
+0x80,0x11,0x7E,0x5F,0xB1,0x00,0x28,0x2C,0x18,0x11,0xB8,0x80,0xFF,0xFF,0xC4,0x3C,
+0x4A,0x00,0x0C,0xB8,0xF3,0x00,0x0C,0x38,0x00,0x11,0x94,0x88,0x01,0x52,0x14,0xB0,
+0x40,0xCE,0xD7,0xE9,0x80,0xCC,0xD1,0xF9,0x07,0x0A,0xEA,0xF1,0x00,0x0C,0xE4,0xC1,
+0x06,0x0A,0xD4,0xF1,0x16,0x0A,0xE4,0xE1,0xA3,0x00,0xBC,0x28,0x80,0xCC,0xDD,0xF9,
+0x41,0x0A,0xE0,0xE1,0xA1,0x00,0x20,0xAE,0x40,0x0A,0xE0,0x61,0xA3,0x00,0xA2,0x28,
+0x20,0xE4,0xC9,0x03,0xB1,0x00,0x74,0x2A,0x18,0x11,0xB8,0x04,0x40,0xCE,0x9F,0xEF,
+0xFF,0xFF,0xC4,0x3C,0x02,0xE4,0x3F,0x69,0xFF,0xC0,0xE4,0xE9,0xFF,0xC1,0xE4,0x69,
+0x0F,0xC2,0xE4,0x69,0x80,0x7F,0xF6,0xE9,0x07,0x11,0xFA,0x41,0x05,0x01,0xC0,0xBB,
+0x00,0x0C,0x02,0x42,0x01,0x0C,0xF8,0xB3,0xB3,0x00,0x74,0x2B,0x00,0x0C,0xE4,0x49,
+0x08,0x01,0xC0,0x3B,0xB1,0x00,0x64,0xAC,0x40,0xCA,0x95,0x03,0x02,0x06,0x22,0x30,
+0xFF,0x11,0x22,0x8C,0x00,0x0C,0x52,0xDD,0x18,0x11,0xB8,0x80,0x00,0x0C,0x5A,0xC2,
+0xB1,0x00,0xA2,0xAF,0x00,0x0C,0x56,0x4A,0x04,0x0C,0x49,0x31,0x03,0x0A,0x1B,0x62,
+0x02,0x34,0x15,0x30,0x02,0x0A,0x48,0xB1,0xFC,0xFF,0x14,0x38,0x83,0x90,0x48,0xA9,
+0x08,0x11,0x48,0xB1,0x30,0xCE,0x2D,0xFA,0x02,0x11,0x48,0x00,0x0C,0xD4,0x4D,0x31,
+0x04,0xB0,0x4D,0x31,0x00,0x11,0x48,0x08,0x02,0x20,0xFC,0xB3,0xB1,0x00,0xAE,0xA9,
+0x44,0x0A,0xE6,0x2B,0x0C,0x00,0x14,0x38,0x30,0xCE,0x39,0xFA,0x14,0x0A,0x14,0x98,
+0x83,0x90,0x14,0x28,0xB1,0x00,0xEA,0xA9,0x00,0x11,0xE4,0x0B,0x80,0xF2,0xE5,0x2B,
+0x20,0x11,0xB8,0x00,0xB1,0x00,0x06,0x2A,0x02,0x08,0xE1,0xB3,0xC0,0x11,0x8C,0x5F,
+0x02,0xFE,0x41,0xB0,0x00,0xFC,0x89,0x5F,0x02,0xF0,0x79,0x32,0x02,0xF2,0x7D,0x32,
+0xB1,0x00,0x58,0xAC,0x0E,0x11,0x8C,0xDF,0x00,0x0C,0x5A,0xC2,0x18,0x11,0xB8,0x80,
+0xB3,0x00,0x92,0xAB,0xFF,0xFF,0xC4,0xB8,0xB1,0x00,0x6E,0xAC,0xBF,0xCA,0x95,0x8B,
+0x01,0xE4,0x39,0x7D,0xB3,0x00,0x32,0xAB,0x00,0x0C,0x38,0xC5,0x00,0x11,0x00,0x08,
+0x02,0xB4,0x14,0x30,0x81,0xB0,0x14,0x28,0x80,0x0A,0x84,0x6A,0x40,0x0A,0xA8,0xEA,
+0x20,0x0A,0xB6,0xEA,0x10,0x0A,0xBA,0xEA,0x04,0x0A,0xBC,0xEA,0x02,0x0A,0xBE,0x6A,
+0x01,0x0A,0xC0,0x6A,0x08,0x0B,0xC2,0x6A,0x04,0x0B,0xC4,0x6A,0x02,0x0B,0xC6,0xEA,
+0x01,0x0B,0xCA,0xEA,0x08,0xF0,0x60,0xBD,0x23,0x11,0x02,0x80,0x02,0x48,0xE4,0x33,
+0x33,0x11,0x00,0x80,0x00,0x00,0x90,0x38,0x22,0x11,0x00,0x80,0x03,0x11,0x48,0x80,
+0xC4,0x01,0x18,0x38,0xB3,0x00,0x46,0x2A,0x00,0x11,0x48,0x08,0x00,0x0C,0x9C,0x4A,
+0xB2,0x00,0x30,0xAE,0x00,0x0C,0xA4,0x42,0x18,0x11,0x18,0x80,0xB1,0x00,0x8A,0xAA,
+0xC4,0x01,0x1C,0xB8,0xF0,0x11,0xB6,0x5F,0x01,0x01,0x22,0xB0,0x80,0x11,0x60,0x05,
+0x40,0xCE,0xB1,0xEA,0xB1,0x00,0x4A,0x2B,0x00,0x0C,0xB2,0x4A,0x40,0x11,0x60,0x05,
+0xB1,0x00,0x74,0x2A,0x20,0xE4,0xC9,0x03,0x40,0x11,0x60,0x05,0x00,0x0C,0xE8,0xDA,
+0x20,0x11,0x60,0x05,0x10,0x11,0x60,0x05,0x04,0x11,0x60,0x05,0x02,0x11,0x60,0x05,
+0x01,0x11,0x60,0x05,0x08,0x11,0x62,0x85,0x04,0x11,0x62,0x85,0x00,0x0C,0xE8,0xDA,
+0x02,0x11,0x62,0x85,0x02,0x90,0x14,0x30,0x8B,0x10,0xDA,0x2A,0x03,0x0A,0xE0,0x39,
+0xFF,0x11,0x22,0x20,0x10,0x00,0xD0,0xB9,0x00,0x00,0xD4,0xB9,0x01,0x11,0x62,0x01,
+0x00,0x00,0xE0,0xBD,0x02,0x54,0x40,0x30,0xB1,0x00,0x8C,0xAC,0x00,0x0C,0xE6,0xCA,
+0xC0,0x11,0xAE,0x5F,0x20,0xE5,0xCB,0x03,0xB1,0x00,0x4C,0x2B,0x01,0x11,0x62,0x85,
+0x80,0xCE,0xF9,0xEA,0x40,0xCE,0xF3,0xEA,0xB1,0x00,0x48,0xAB,0x00,0x0C,0x9E,0xD7,
+0x20,0xE4,0xC9,0x87,0x18,0x11,0xB8,0x80,0x20,0xE4,0xC9,0x03,0xA1,0x00,0x74,0xAA,
+0x10,0x11,0xB8,0x84,0x95,0x01,0x0C,0xB8,0x08,0x11,0xB8,0x00,0x00,0x00,0x90,0xB9,
+0x80,0x0B,0x09,0xFB,0x3F,0x4E,0x15,0x88,0x01,0x0A,0x08,0xE3,0xA3,0x00,0xC2,0xA9,
+0x01,0x0A,0x15,0xB0,0x00,0x11,0x16,0x88,0x88,0x01,0x18,0xB8,0x83,0x0C,0x0C,0xAC,
+0x00,0x0C,0x38,0xC3,0x00,0x0C,0x40,0xC3,0x00,0x0C,0x36,0x43,0x00,0x0C,0x50,0x43,
+0x00,0x0C,0x50,0x43,0x00,0x0C,0x5C,0x43,0x00,0x0C,0x5C,0x43,0xA3,0x00,0xB0,0xA9,
+0x00,0x0C,0x50,0x43,0xA2,0x00,0xAC,0x2B,0xA2,0x00,0x94,0xAB,0xA2,0x00,0xAC,0x2B,
+0x00,0x0C,0x56,0x43,0x02,0x05,0x0A,0x00,0x01,0x4C,0x31,0xEB,0x00,0x11,0x88,0xDF,
+0xB3,0x00,0xB2,0x2B,0x20,0x13,0x08,0x39,0x05,0x11,0xB8,0x04,0x15,0x11,0x2A,0x80,
+0x06,0x11,0x94,0x81,0x33,0x11,0x95,0x31,0x34,0x11,0x84,0x5F,0x10,0x01,0xBC,0x3C,
+0x06,0x11,0x94,0x81,0x23,0x11,0x95,0xB1,0x24,0x00,0x14,0x38,0x83,0x3C,0x99,0x28,
+0x04,0x11,0x86,0xDF,0x00,0x11,0x86,0x5F,0x10,0x34,0xC1,0x30,0x10,0x01,0xBC,0x3C,
+0x34,0x10,0x95,0x31,0x34,0x11,0x84,0x5F,0x10,0x01,0xBC,0x3C,0x10,0x00,0xBC,0x38,
+0x04,0x3C,0x99,0x30,0x10,0x34,0xC1,0xB4,0x05,0x11,0x94,0x81,0x13,0x11,0x95,0xB1,
+0x04,0x11,0x94,0x31,0x04,0x24,0x95,0xB1,0x08,0x2C,0x95,0x31,0x24,0x11,0x84,0xDF,
+0x10,0x01,0xBC,0x3C,0x00,0x0C,0xFA,0xDA,0x02,0xE4,0x11,0x6F,0x04,0x5D,0x80,0x7B,
+0x30,0xCE,0x7F,0xFB,0x00,0x0C,0x18,0x58,0xB2,0x00,0x32,0x2E,0x00,0x0C,0x88,0xCB,
+0x00,0x0C,0xC6,0xDF,0xB1,0x00,0xCE,0xAC,0x00,0x0C,0x80,0xC3,0xB1,0x00,0xC0,0x2C,
+0x00,0x0C,0x18,0x58,0x40,0xCE,0x07,0x6C,0x80,0xCE,0x89,0x7B,0x08,0x5D,0xA4,0xEB,
+0x0F,0xCB,0x19,0xEC,0x80,0xC8,0x1B,0xEC,0x30,0xCE,0xB1,0xEB,0x04,0xC9,0x17,0x6C,
+0x08,0xC9,0x1D,0x6C,0x01,0x85,0x34,0x6C,0x04,0x85,0x06,0xF8,0xFF,0xC6,0x06,0x68,
+0x02,0x11,0xB6,0x5D,0x00,0x0C,0xC2,0xDD,0x02,0x85,0xA4,0xEB,0x04,0x11,0x0A,0x81,
+0x04,0xC9,0x93,0x03,0x00,0x0C,0x16,0x44,0x04,0x11,0x80,0x32,0x10,0x11,0x7E,0x5F,
+0x08,0xC9,0xAF,0xEB,0x02,0x11,0x0A,0x81,0x00,0x0C,0xE0,0xC3,0x04,0x11,0x4E,0x44,
+0x20,0xC8,0xC7,0xEB,0x02,0xCA,0x21,0x6C,0x02,0x86,0x22,0xEC,0x04,0xC4,0xC6,0x6B,
+0x08,0x5D,0x06,0xF8,0x20,0x84,0x34,0xEC,0x10,0x85,0x06,0xF8,0x10,0x11,0x0A,0x81,
+0x0B,0x0A,0xE1,0x63,0x01,0x3A,0xE1,0x63,0xA2,0x00,0xD4,0x2D,0x04,0x5D,0x14,0x08,
+0x00,0xE4,0xC9,0x83,0x02,0x11,0xB6,0x5D,0x00,0x0C,0xC2,0xDD,0x10,0x85,0xDC,0xEB,
+0x20,0x84,0x34,0xEC,0x08,0x11,0xB6,0x5D,0x04,0xE4,0xDB,0xEB,0x08,0x4C,0xDB,0x6B,
+0xB2,0x00,0x42,0xAE,0x00,0x11,0x56,0xC4,0x10,0x11,0x0A,0x81,0x00,0x0C,0x10,0x44,
+0x80,0xCC,0xE5,0x6B,0xC0,0xC9,0x11,0xEC,0xFF,0x03,0x11,0xF4,0x08,0x48,0xC7,0x0B,
+0x79,0x0B,0xC5,0x0B,0x02,0x02,0x41,0xB0,0x08,0x48,0x17,0x88,0x79,0x0B,0x15,0x88,
+0x02,0xEE,0x41,0x30,0x00,0xE2,0x11,0x64,0x30,0xCE,0xFF,0x7B,0x08,0xCE,0x11,0x7C,
+0x01,0x0B,0x14,0xB0,0x00,0xE3,0x11,0xE4,0x20,0xC8,0x11,0xEC,0x02,0x02,0xDD,0xB3,
+0x06,0x11,0x44,0x5D,0x02,0xEE,0x41,0x30,0x00,0x0C,0x6A,0x43,0x08,0x5D,0x06,0xF8,
+0x00,0x11,0x02,0x88,0xDC,0x01,0x1C,0xB8,0xE0,0x11,0xB6,0xDF,0x01,0x01,0x22,0xB0,
+0x08,0x4C,0x15,0x6C,0x04,0x11,0x56,0x44,0x06,0x11,0x56,0xC4,0x00,0x11,0x4E,0xC4,
+0x0D,0x11,0x26,0xC4,0x0E,0x11,0x26,0xC4,0x80,0xE5,0xA5,0x6B,0x18,0x11,0x26,0x44,
+0x19,0x11,0x26,0xC4,0x40,0xE5,0xCB,0x03,0x25,0x11,0x26,0xC4,0x80,0x0B,0x4B,0xEC,
+0x80,0xCE,0x2D,0x7C,0x16,0x10,0x4B,0xF4,0x00,0x0C,0xCE,0xDD,0x30,0xCE,0x33,0xFC,
+0xB2,0x00,0x42,0xAE,0x02,0x11,0x4E,0x44,0x20,0x01,0x08,0x39,0x1A,0x11,0x38,0xC4,
+0x30,0xCE,0x3F,0xFC,0xB2,0x00,0x42,0xAE,0x00,0x0C,0x46,0x44,0x80,0x0B,0x4B,0x7C,
+0x3F,0x4E,0x15,0x88,0x01,0x0A,0x46,0xF4,0x02,0x0A,0x4A,0x64,0xB3,0x00,0xA2,0xAB,
+0xFF,0x4A,0x6B,0x6B,0x00,0x0C,0x88,0xDF,0x02,0x11,0x4E,0x44,0x01,0x0C,0xD6,0xB3,
+0x08,0x11,0xB6,0x5D,0x01,0xEB,0x19,0xB0,0x20,0xE4,0xC9,0x03,0x00,0x0C,0x44,0x5D,
+0xDF,0x5F,0xBE,0x88,0x04,0x11,0x88,0x81,0x00,0x0C,0x1C,0xC5,0x02,0x20,0x14,0x31,
+0x02,0x20,0xA4,0xB1,0x00,0x0C,0x18,0x58,0x10,0x39,0x06,0xE8,0x00,0x0C,0xC6,0xDF,
+0x80,0x48,0x85,0xEC,0xC0,0x49,0x85,0xEC,0x00,0x11,0x94,0x88,0x10,0x0B,0x77,0x7C,
+0x18,0x10,0xA5,0x30,0x18,0x11,0x24,0x01,0x00,0x0C,0x7A,0x44,0x52,0x11,0xBC,0xDF,
+0x04,0x11,0x24,0x81,0xB1,0x00,0x58,0xAC,0x08,0x11,0xB8,0x00,0x01,0xC0,0x23,0xB0,
+0xFF,0x11,0x22,0x20,0xB1,0x00,0x6E,0x2B,0x02,0xE4,0x11,0x6F,0xB1,0x00,0xC0,0x2C,
+0x00,0x0C,0x18,0x58,0x02,0x05,0x0A,0x00,0x0F,0xCB,0xFD,0xEC,0x80,0xC8,0xFF,0xEC,
+0x80,0x48,0xD3,0xEC,0xC0,0x49,0x11,0xED,0x30,0xCE,0xBD,0x6C,0xFF,0x58,0x9A,0x6C,
+0xC0,0xC9,0xF9,0xEC,0x04,0xC9,0xF9,0x6C,0x08,0xC9,0x01,0x6D,0x01,0x85,0x0A,0x6D,
+0x04,0x85,0xB6,0xFC,0xFF,0xC6,0xB6,0x6C,0x80,0xE4,0xD5,0xEC,0x02,0x11,0xB6,0x5D,
+0x04,0x7F,0xB0,0x6C,0x04,0x11,0x0A,0x81,0x04,0xC9,0x93,0x03,0x00,0x0C,0xF8,0x44,
+0x01,0x11,0xB8,0x00,0x00,0x0C,0x18,0x58,0x00,0x0C,0xC4,0x44,0xFF,0x58,0x06,0xE8,
+0x02,0x11,0x0A,0x81,0x00,0x0C,0xC4,0x44,0xB2,0x00,0xFC,0xAD,0x20,0xC8,0xCB,0x6C,
+0x02,0xCA,0x05,0xED,0x02,0x86,0x06,0x6D,0x80,0xE5,0xCF,0xEC,0x80,0xE4,0xD5,0xEC,
+0x00,0x0C,0x06,0x40,0x00,0x0C,0x8E,0xDF,0x00,0x0C,0xF6,0xC4,0x00,0x0C,0xA0,0xDF,
+0x00,0x0C,0xD4,0xC4,0x06,0x11,0xCE,0xDD,0x30,0xCE,0xDF,0x7C,0xB2,0x00,0xFC,0xAD,
+0x08,0xCE,0xF7,0xFC,0xFF,0x05,0xF7,0x74,0x00,0x0C,0xEA,0x44,0xFF,0x05,0xF7,0x74,
+0x79,0x0B,0x15,0x88,0x02,0x04,0x41,0xB0,0x79,0x0B,0xC5,0x0B,0x02,0xEE,0x41,0x30,
+0x00,0xE2,0xF7,0xE4,0x02,0x04,0xDD,0xB3,0x07,0x11,0x44,0xDD,0x00,0x00,0xC8,0xBB,
+0x02,0xEE,0x41,0x30,0x00,0x0C,0x5E,0x44,0x20,0xE4,0xC9,0x03,0x05,0x11,0x14,0x45,
+0x00,0x0C,0x8E,0xDF,0x01,0x11,0x14,0xC5,0x08,0x11,0x0E,0x45,0x05,0x11,0x0E,0xC5,
+0x80,0xE5,0xF5,0xEC,0x14,0x11,0x0E,0xC5,0x0F,0x11,0x0E,0xC5,0x40,0xE5,0xCB,0x03,
+0x24,0x11,0x0E,0xC5,0x03,0x11,0x0A,0x01,0x10,0x11,0x0E,0x45,0x00,0x0C,0xCE,0xDD,
+0x00,0x0C,0x8E,0xDF,0x03,0x11,0x14,0x45,0x30,0xCE,0x1B,0x7D,0x40,0x9E,0x1B,0xFD,
+0xB2,0x00,0x42,0xAE,0x00,0x0C,0x44,0x5D,0xB1,0x00,0x58,0xAC,0xFF,0xFF,0xDC,0xBB,
+0x20,0xE4,0x2D,0xED,0xFF,0x8E,0x2D,0x6D,0xFF,0xC1,0x2D,0xF5,0x00,0x0C,0x5E,0xDD,
+0xFF,0xEF,0x2D,0xF5,0x02,0xE0,0x0D,0xB4,0x02,0x05,0x0A,0x00,0x7F,0xCA,0x95,0x8B,
+0x01,0xE4,0x35,0x7D,0xB3,0x00,0x32,0xAB,0x02,0x86,0x38,0x7D,0x00,0x11,0xB0,0x88,
+0x40,0xE5,0x3F,0xFD,0x02,0x11,0x0C,0x81,0x02,0xE6,0xCC,0x01,0x00,0x00,0xC8,0xBB,
+0xFF,0xFF,0x40,0xB8,0x00,0x0C,0x22,0x40,0x02,0x0C,0x0C,0x30,0x10,0x11,0x7C,0xDF,
+0x02,0x06,0x18,0x30,0x00,0x0C,0x8C,0x5F,0x00,0x0C,0x18,0x58,0x10,0x49,0x07,0xE8,
+0xFF,0xFF,0xC4,0x3C,0xB3,0x00,0x74,0x2B,0x00,0x0C,0x9E,0xCF,0xB1,0x00,0xA2,0xAF,
+0x00,0x0C,0x5C,0xCD,0x0E,0x11,0x8C,0xC7,0xA3,0x00,0x92,0x2B,0x11,0x11,0x02,0x00,
+0x02,0x05,0x0A,0x00,0x02,0xC0,0x41,0x30,0x02,0x20,0xDC,0x33,0x04,0x4C,0x95,0x6D,
+0x80,0x49,0x7B,0xFD,0xB3,0x00,0x92,0xAB,0x02,0xEE,0x15,0xB0,0x88,0x20,0x72,0x2D,
+0xFF,0xFF,0xDC,0xBB,0x02,0x06,0x41,0x30,0xFF,0x21,0x8A,0xF5,0xFF,0xEF,0x65,0xF5,
+0x00,0x0C,0x68,0xC5,0x02,0x06,0x09,0xB2,0xFF,0xFF,0x0C,0xBA,0x04,0x11,0x7C,0xDF,
+0x02,0x04,0x41,0xB0,0xFF,0x21,0x8A,0xF5,0x04,0x4C,0x69,0xFD,0x02,0x20,0x80,0x33,
+0x00,0x0C,0x8C,0xC5,0x00,0x0C,0xAE,0xDD,0xFF,0xEF,0xA9,0xF5,0x01,0x11,0xF6,0x03,
+0x2F,0x02,0xC0,0x3B,0x00,0x0C,0xA0,0x45,0x00,0x11,0xF6,0x0B,0xB5,0x01,0xC0,0x3B,
+0x02,0x06,0x81,0x33,0xFF,0xFF,0x0C,0xBA,0xFF,0xC1,0xA1,0xE5,0xFF,0xFF,0x84,0x3B,
+0x02,0xEE,0x41,0x30,0x02,0x46,0x45,0x30,0x00,0x00,0xC8,0xBB,0x80,0xCA,0x95,0x03,
+0x01,0x01,0x22,0xB0,0xFD,0x05,0x0A,0x0C,0x02,0xC0,0x41,0x30,0x04,0x10,0x80,0xB7,
+0xFF,0xC1,0x9F,0x77,0x00,0x0C,0xAC,0x5D,0x0B,0x11,0x8C,0xC7,0x01,0x0C,0xD8,0x33,
+0x01,0xEC,0xB9,0x30,0xB0,0x00,0x18,0x28,0x01,0xEC,0xB9,0x30,0x20,0x5D,0x06,0x68,
+0xFF,0x11,0x22,0x8C,0x00,0x0C,0x18,0x58,0x05,0x11,0xF6,0x80,0x02,0x78,0x14,0x30,
+0x00,0x7A,0x14,0x00,0x89,0x10,0x06,0xA8,0xFF,0x11,0x22,0x8C,0x80,0xCE,0xD3,0x7D,
+0x80,0x49,0x9F,0xEF,0x00,0x0C,0x88,0xDF,0x80,0x0B,0x9F,0xEF,0x02,0x08,0xE1,0xB3,
+0x01,0x4D,0xF9,0x33,0x80,0x11,0x7C,0xDF,0x80,0xCE,0xE3,0xED,0x40,0x9E,0x9F,0xEF,
+0x40,0x9E,0x3D,0x03,0x02,0x20,0xFC,0xB3,0xB1,0x00,0xAE,0xA9,0x02,0x0C,0x1C,0x98,
+0x80,0xCE,0xF1,0x7D,0xF0,0x11,0x26,0x00,0x02,0xF0,0x27,0x30,0x00,0x0C,0xF4,0xC5,
+0xF1,0x11,0x26,0x80,0x02,0x22,0x26,0xB0,0x01,0xFC,0x27,0x30,0x00,0x0A,0x8C,0x5F,
+0x02,0xFE,0x41,0x34,0xC0,0xD9,0x14,0x88,0x40,0x0A,0x0A,0xF6,0xC0,0x0A,0x0A,0x76,
+0xFF,0x0A,0x04,0x7E,0x40,0x11,0x72,0x84,0x30,0xCB,0x03,0x7E,0x40,0xCB,0x97,0x03,
+0xBF,0x3D,0x7A,0x0C,0x01,0x03,0x0C,0xB8,0x01,0xCD,0x9F,0xEF,0x30,0xCB,0x9F,0x6F,
+0x20,0xD9,0xFE,0xEE,0x00,0x11,0xB2,0x89,0xF0,0xD8,0x14,0x08,0x10,0x0A,0x28,0xF6,
+0x20,0x0A,0x2C,0x76,0x90,0x0A,0x20,0xF6,0x80,0x0A,0x24,0xF6,0x00,0x0C,0xFC,0x46,
+0x08,0xCF,0x2F,0x6E,0x00,0x0C,0xFC,0x46,0x02,0xCF,0x2F,0x6E,0x00,0x0C,0xFC,0x46,
+0x80,0xCF,0x2F,0x6E,0x00,0x0C,0xFC,0x46,0x40,0xCF,0xFD,0xFE,0x0F,0xD8,0x14,0x08,
+0x08,0x0A,0x38,0x76,0x09,0x0A,0x00,0x67,0x55,0x11,0x00,0x80,0x01,0x43,0x01,0x6F,
+0x00,0x11,0x00,0x08,0x44,0xC6,0x0F,0xA8,0x01,0x07,0x14,0xB0,0x00,0x00,0x44,0x38,
+0x00,0xAF,0xFB,0x7E,0x02,0xC8,0x91,0x03,0x01,0xC8,0x85,0x7E,0x11,0x11,0x00,0x80,
+0x08,0xE4,0x63,0xEE,0x01,0x96,0x17,0x30,0x01,0x97,0x15,0x30,0x16,0x11,0xB2,0x81,
+0x01,0xD8,0x1A,0xB0,0x01,0xD8,0x18,0x30,0x8A,0x0C,0x5C,0x2E,0xB3,0x00,0x6E,0x2A,
+0x80,0x0F,0x0E,0xEF,0x00,0x0C,0x62,0xC6,0xA8,0x00,0x18,0xB8,0xB3,0x00,0xB6,0xAB,
+0xFF,0x0A,0x0E,0xFF,0x00,0x0C,0xC2,0x5F,0x84,0x01,0x18,0xB8,0xB3,0x00,0xB6,0xAB,
+0x01,0x0A,0x84,0x66,0x00,0x11,0xB2,0x89,0xF0,0xD8,0xB0,0x8B,0x80,0xD8,0xA5,0x8B,
+0x70,0xD8,0xB1,0x8B,0x70,0x0B,0x15,0x88,0x00,0xD8,0x85,0x66,0x80,0x80,0x7D,0xFE,
+0xFF,0xD2,0x85,0xEE,0x00,0x0C,0x7E,0x46,0x80,0xD2,0x85,0xE6,0x20,0xD8,0x85,0x76,
+0x11,0x11,0x00,0x80,0xB1,0x00,0xBE,0x2C,0x00,0x11,0xB2,0x89,0xF0,0xD8,0x14,0x08,
+0x80,0x0A,0xB0,0x0B,0x30,0x0A,0x14,0x08,0x80,0xD8,0x91,0x6E,0x80,0x0A,0x14,0x00,
+0x01,0xD8,0x98,0x6E,0x55,0x11,0x00,0x80,0x01,0x43,0x99,0x6E,0x08,0x0A,0x14,0x00,
+0x00,0x11,0x00,0x08,0x01,0x0A,0xA4,0xB3,0x80,0x0A,0xA0,0xEE,0xA3,0x00,0x00,0xA8,
+0x01,0xD8,0x46,0xB0,0x01,0xD8,0x44,0x30,0x00,0x00,0x14,0x38,0x8A,0x22,0xFC,0x2E,
+0x02,0x22,0x18,0x30,0x02,0xEE,0x15,0xB0,0xB3,0x00,0x6E,0x2A,0x80,0x0F,0xFC,0x7E,
+0xFE,0x8F,0xFD,0x66,0x84,0x01,0x18,0xB8,0xB3,0x00,0xB6,0xAB,0x01,0x0A,0xFC,0x66,
+0x20,0xD2,0xC5,0x7E,0x20,0x9E,0xFB,0xEE,0x40,0x9D,0xC1,0x7E,0x02,0x9E,0xFD,0x7E,
+0xB2,0x00,0x20,0xAF,0x00,0x0C,0xD8,0x46,0x02,0xC9,0xD9,0x6E,0x04,0x9D,0xD9,0x7E,
+0xB3,0x00,0xDA,0xAB,0x04,0x11,0x14,0x30,0x01,0xA8,0x19,0x30,0x01,0xA9,0x15,0xB0,
+0xB3,0x00,0x6E,0x2A,0x80,0x0F,0xD8,0x7E,0xB3,0x00,0xEA,0xAB,0x00,0x0C,0xFA,0x46,
+0x00,0x11,0x7E,0x0B,0x08,0xD2,0x41,0x09,0x01,0xD2,0x15,0xB0,0xB1,0x00,0xE6,0x28,
+0x34,0x11,0xBA,0xDF,0xB1,0x00,0xBA,0xAA,0xB1,0x00,0x08,0xA9,0xB0,0xCC,0x15,0x08,
+0x10,0x0A,0xEC,0xE6,0xB3,0x00,0x72,0xA9,0xFE,0xC8,0x91,0x8B,0xB3,0x00,0x32,0xAB,
+0x80,0xCA,0xF5,0xEE,0xA3,0x00,0x64,0xAA,0xB1,0x00,0x64,0xAC,0xB6,0x03,0xC0,0xBB,
+0x40,0xCA,0x95,0x87,0x4F,0x11,0x02,0xC7,0x44,0x11,0x02,0x47,0x51,0x11,0x02,0xC7,
+0x41,0x11,0x02,0x47,0xFD,0xC9,0x93,0x8B,0x00,0x0C,0xBA,0x5F,0x80,0xCA,0x0F,0xFF,
+0xBF,0x3D,0x7A,0x88,0xBA,0x03,0xC0,0xBB,0x40,0xCA,0x95,0x87,0xFD,0xC8,0x91,0x0F,
+0x00,0x0C,0x18,0x58,0x02,0x02,0x14,0x38,0x81,0xC8,0x15,0xA8,0x02,0x0B,0x26,0x6F,
+0x02,0x0A,0x2C,0xEF,0x10,0xE4,0x55,0x6F,0x0F,0xCB,0x33,0x6F,0x80,0xC8,0x35,0xEF,
+0xEB,0x11,0x8A,0x5F,0x00,0x0C,0x06,0xD0,0x03,0x11,0x36,0xC7,0xFD,0xE4,0xC9,0x8B,
+0xFF,0xFB,0x6F,0x7B,0x00,0x0C,0x86,0x44,0xB3,0x00,0xEA,0xAB,0xFB,0x9E,0x3D,0x8B,
+0x00,0xFB,0x4F,0xC4,0x09,0x11,0x36,0xC7,0x12,0x11,0x36,0xC7,0x01,0x0C,0xF4,0xB3,
+0x00,0x0C,0xC2,0x5F,0xB1,0x00,0x74,0x2A,0x22,0x11,0x02,0x00,0xE8,0x11,0xB8,0xDF,
+0x01,0x01,0x22,0xB0,0xB1,0x00,0xBA,0xAA,0x00,0x0C,0x54,0xCF,0x00,0x0C,0x18,0x58,
+0x80,0xC8,0x55,0xEF,0x22,0x11,0x02,0x00,0xEB,0x11,0x8A,0x5F,0x01,0x01,0x22,0xB0,
+0x00,0x0C,0x06,0xD0,0x01,0x9E,0x3D,0x03,0xB3,0x00,0xEA,0xAB,0xB2,0x00,0x6E,0xAF,
+0xFD,0x05,0x0A,0x88,0x08,0xFB,0x4F,0x44,0x10,0xC7,0x9F,0x7F,0x10,0x45,0x9E,0x6F,
+0x20,0xCE,0x9F,0xEF,0x02,0xFE,0x15,0x30,0x83,0x10,0xFC,0xAB,0x89,0x10,0x9E,0xAF,
+0x02,0xFC,0xFD,0xB3,0x30,0x11,0xBA,0x47,0x00,0x0C,0x18,0x58,0x80,0xCA,0x07,0x68,
+0xB3,0x00,0x64,0x2A,0x00,0x0C,0x5C,0xC2,0x00,0x0C,0x18,0x58,0x80,0xCA,0x07,0x68,
+0xB2,0x00,0x6E,0xAF,0x00,0x0C,0x5C,0xC2,0xA3,0x00,0xDE,0x2A,0xA3,0x00,0xE2,0x2A,
+0xA3,0x00,0xF2,0xAA,0xA3,0x00,0x04,0x2B,0xA3,0x00,0x54,0xAA,0xA3,0x00,0x4E,0x2A,
+0xA3,0x00,0xBE,0x2A,0xA1,0x00,0x2C,0x2A,0xA3,0x00,0x34,0xAA,0x20,0xE4,0xC9,0x03,
+0x80,0xE4,0x9F,0x6F,0x08,0x5D,0x9E,0x6F,0x02,0x11,0xB6,0x5D,0x05,0x11,0xB8,0x80,
+0x80,0x11,0xB8,0x00,0x00,0x0C,0x18,0x58,0x80,0xE4,0x07,0xF8,0xFF,0x11,0x22,0x8C,
+0x80,0xE4,0x9F,0x6F,0x08,0x5D,0x9E,0x6F,0xA0,0xE5,0xA9,0x6F,0xC0,0x11,0xAE,0x5F,
+0xB0,0x00,0x18,0x28,0x80,0xE4,0x07,0xF8,0xFF,0x11,0x22,0x8C,0xFF,0x00,0xB4,0xEF,
+0x00,0x11,0x86,0x09,0x40,0x11,0x90,0x00,0x01,0x0C,0xB8,0xB4,0xA1,0x00,0x18,0xAA,
+0xA1,0x00,0x16,0x2A,0xA1,0x00,0x7E,0xAA,0xA2,0x00,0xDA,0xAB,0xFE,0xBA,0x74,0x89,
+0x02,0x92,0x71,0x31,0xFE,0xBA,0x74,0x89,0x02,0xB8,0x2C,0x37,0x02,0x20,0xC4,0xB4,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x02,0x05,0x0A,0x00,0x01,0xCB,0x2A,0xE8,0xEF,0x11,0x2C,0xDA,0x00,0x0C,0x2A,0x50,
+0xEF,0x11,0x52,0xDA,0x02,0xCB,0x97,0x03,0xB3,0x00,0x32,0xAB,0x55,0x11,0x02,0x00,
+0x01,0x48,0x15,0xB0,0x01,0x01,0x22,0xB0,0xFF,0x0A,0x2A,0xE8,0x30,0xCB,0x2B,0xE8,
+0x44,0x11,0x02,0x00,0x9A,0x07,0xC0,0xBB,0x01,0x01,0x22,0xB0,0xFD,0x05,0x0A,0x88,
+0x00,0x0C,0xAE,0x59,0x02,0x0C,0x1C,0x98,0x20,0xC6,0x27,0x00,0x00,0x11,0x26,0x88,
+0x00,0x0A,0xAC,0xD9,0xFD,0x05,0x0A,0x88,0x00,0x04,0xC0,0xBB,0x02,0x05,0x0A,0x00,
+0x71,0x04,0x0C,0xB8,0x01,0xCD,0xE1,0xF8,0x80,0xCA,0x61,0xEC,0x0F,0xCB,0x93,0xE8,
+0x10,0x45,0xD2,0x68,0x40,0xC8,0x81,0x68,0x80,0xC8,0x75,0xE8,0x10,0xC8,0x6D,0xE8,
+0x40,0xCE,0x9B,0xE8,0x20,0xCE,0xA7,0xE8,0x04,0xC8,0x5B,0xE8,0xFF,0x8E,0x55,0x68,
+0xFF,0xC1,0xDB,0x60,0x40,0x83,0x60,0xEC,0x01,0xC8,0x55,0x68,0x08,0xC8,0x55,0x68,
+0x80,0xCC,0x55,0x78,0x40,0xC9,0x61,0x7C,0x5A,0x11,0x7C,0x5A,0xB1,0x00,0x9E,0xAF,
+0x01,0x11,0xE4,0x81,0x08,0xC8,0x61,0xE8,0x20,0xC9,0x61,0x7C,0x00,0x0C,0x78,0x40,
+0x02,0x11,0xE4,0x81,0xB1,0x00,0x9E,0xAF,0x00,0x0C,0x12,0xD9,0x00,0x0C,0x5E,0x5A,
+0x20,0xC8,0x95,0x68,0xE4,0x11,0x16,0x42,0x20,0xC8,0x95,0x68,0xE7,0x11,0x2C,0x5A,
+0x00,0x0C,0x60,0x54,0x00,0x0C,0x78,0x40,0x40,0x39,0x78,0x78,0x40,0x11,0x72,0x00,
+0x00,0x0C,0x12,0xD9,0x00,0x0C,0x74,0xDA,0x80,0xC8,0x89,0xE8,0xE8,0x11,0x16,0x42,
+0x80,0xC8,0x89,0xE8,0xEB,0x11,0x2C,0x5A,0x00,0x0C,0x60,0x54,0x01,0x9E,0x3D,0x03,
+0x03,0xC8,0x91,0x0B,0xF0,0x08,0x04,0xB9,0x03,0x11,0x0E,0x81,0x3F,0xC9,0x93,0x0B,
+0x00,0x0C,0x94,0xC0,0x00,0x0C,0x12,0xD9,0x40,0xCA,0x61,0xEC,0xB0,0x00,0xB0,0xAD,
+0x00,0x0C,0x3E,0x41,0x80,0xCC,0xA1,0x68,0x10,0xC9,0x61,0x7C,0x00,0x0C,0x64,0xC0,
+0x40,0xCA,0x61,0xEC,0x10,0xC9,0x79,0x68,0x00,0x0C,0x64,0xC0,0x80,0x83,0xCC,0x68,
+0x10,0xCA,0xCD,0x68,0x02,0xCA,0xCF,0xE8,0x20,0xC8,0xB7,0x68,0xFF,0xC1,0xDB,0x60,
+0x01,0x9F,0xBD,0x78,0xB2,0x00,0x02,0x2E,0x00,0x0C,0x60,0x4C,0x02,0xA1,0x42,0x01,
+0x04,0xA1,0xB8,0xF8,0x00,0x0C,0x64,0xC0,0x02,0xA1,0x42,0x01,0x80,0x83,0xCC,0x68,
+0x13,0x00,0xE0,0xB9,0xFF,0x11,0x22,0x20,0xFF,0xE8,0xCC,0x68,0x04,0x86,0xCC,0x68,
+0x04,0xA1,0xBE,0xF8,0x00,0x0C,0x64,0xC0,0xFD,0xA1,0x42,0x0D,0xB2,0x00,0xE4,0xAD,
+0x00,0x0C,0x78,0x40,0xB2,0x00,0x02,0x2E,0x00,0x0C,0x92,0x50,0x02,0xCA,0xDD,0xE8,
+0xFF,0xC1,0x61,0xF4,0xA0,0x00,0x5E,0x2D,0xB2,0x00,0xE4,0xAD,0x00,0x0C,0x92,0xC0,
+0xA0,0x00,0xB0,0x2D,0x00,0x04,0xC0,0xBB,0xA0,0x00,0x06,0xA8,0x30,0x0A,0x18,0x08,
+0xFF,0x0C,0xF6,0xF8,0x20,0x0C,0xF8,0x70,0x30,0x0C,0xFE,0xF0,0x20,0x11,0x18,0x00,
+0x80,0x0A,0x02,0xE9,0x01,0xC8,0x03,0x79,0xA3,0x00,0x18,0x29,0x10,0x11,0x02,0xC1,
+0x08,0x11,0x0C,0x59,0x20,0x9E,0x3D,0x03,0x00,0x0C,0x00,0xC1,0x00,0x11,0x0C,0xD9,
+0x40,0x11,0x02,0xC1,0xB2,0x00,0x68,0xAF,0x01,0x0A,0x40,0x31,0x01,0x0A,0x60,0xFC,
+0x02,0xA0,0x98,0xB3,0x01,0x45,0x9C,0x37,0x11,0x11,0x02,0x00,0x01,0x0C,0x96,0x30,
+0x01,0x01,0x22,0x34,0x01,0xA1,0x60,0x7C,0xF5,0x45,0x8A,0x08,0x10,0x45,0x60,0xEC,
+0x20,0x45,0x26,0x69,0x80,0xA0,0x22,0x69,0xF7,0x11,0x52,0xDA,0x80,0x45,0x22,0x79,
+0x02,0xF4,0x45,0xB1,0x00,0x0C,0x2A,0x59,0x01,0x11,0x0C,0x41,0x0A,0x11,0x0C,0xD9,
+0x1F,0x11,0x32,0xD9,0x08,0xA0,0x14,0x88,0x02,0x0A,0x14,0x00,0x00,0x11,0x16,0x88,
+0x02,0x0A,0x40,0xB5,0x11,0x11,0x02,0x00,0x01,0x0C,0x90,0x30,0x01,0x01,0x22,0x34,
+0xFF,0x11,0x32,0x59,0xF7,0xA0,0x40,0x89,0x01,0x11,0x0C,0x41,0x02,0x05,0x0A,0x00,
+0x04,0x9E,0x45,0xF9,0xF9,0x9E,0x3D,0x0B,0xDF,0x9E,0x3D,0x8B,0xB3,0x00,0xEA,0xAB,
+0x03,0x11,0x90,0xB3,0x10,0x45,0x52,0x69,0x00,0x00,0x98,0xBB,0x00,0x11,0x9C,0x0B,
+0x00,0x0C,0x60,0xC1,0x00,0x11,0x02,0x88,0xF7,0x5E,0xBC,0x88,0x40,0x11,0x90,0x00,
+0x00,0x11,0x86,0x09,0x01,0x01,0x22,0xB0,0xFE,0xCD,0x9B,0x8B,0xF5,0xCE,0x9D,0x0B,
+0x40,0x3D,0x7A,0x00,0xFF,0xE0,0xC0,0x81,0xB3,0x00,0x94,0x2A,0xB3,0x00,0x32,0xAB,
+0xFD,0x05,0x0A,0x0C,0xFD,0x4D,0x9B,0x8A,0xB0,0x00,0x5C,0xAF,0xB3,0x00,0x98,0x29,
+0x30,0x45,0x74,0xF9,0x00,0x0C,0x7E,0xD9,0x55,0x11,0x02,0x00,0x1D,0x11,0xAC,0x02,
+0x02,0x4D,0x9B,0x02,0x02,0x5B,0xB7,0x02,0x01,0x01,0x22,0x34,0x22,0x11,0x02,0x00,
+0x02,0x11,0x4A,0x80,0xF3,0x11,0x4C,0x5A,0x00,0x0C,0x94,0xD1,0xC4,0x01,0x1C,0xB8,
+0xF3,0x11,0x2E,0xDA,0x00,0x0C,0x90,0x51,0xB2,0x00,0x30,0xAE,0x00,0x0C,0x92,0x41,
+0x08,0x44,0x94,0xE9,0xF3,0x11,0x52,0x5A,0x00,0x11,0x4A,0x88,0x01,0x01,0x22,0x34,
+0xB0,0x00,0x18,0x28,0x80,0x5D,0xE4,0xF8,0xFF,0x4D,0xA5,0xE9,0x80,0x0B,0xA3,0x79,
+0x18,0x11,0xAC,0xC1,0x10,0x11,0xAC,0x41,0x0F,0x11,0xAC,0xD9,0x30,0xCE,0x61,0x7C,
+0x40,0x9E,0x61,0xFC,0xA2,0x00,0x42,0x2E,0xA3,0x00,0x34,0xAA,0x13,0x11,0xD6,0x83,
+0x02,0x11,0xB6,0xC1,0x17,0x11,0xD6,0x03,0x01,0x11,0xB6,0xC1,0x01,0x0C,0xD8,0x33,
+0xB0,0x00,0x18,0x28,0x02,0x05,0x0A,0x00,0x04,0x3A,0xE4,0x78,0x01,0xEB,0x69,0x30,
+0x03,0x11,0x6A,0xB0,0xFD,0x05,0x0A,0x88,0xB0,0x00,0x18,0x28,0x01,0xEC,0x49,0x30,
+0xFF,0xCE,0xCF,0x69,0x00,0x11,0x48,0x08,0x00,0x0C,0xE4,0x40,0xC8,0xCC,0x19,0x98,
+0x01,0x11,0x1A,0x80,0x01,0x12,0x16,0xB0,0x11,0xCC,0x15,0x28,0x03,0x0C,0x98,0x8B,
+0xFF,0xCE,0x9D,0x9B,0xC0,0x0A,0x18,0x98,0x02,0x0C,0x1C,0xB0,0x02,0x12,0x40,0xB0,
+0xFF,0xFF,0x26,0xB8,0x00,0x11,0x48,0x08,0x01,0x0B,0x14,0xB0,0x44,0x0A,0x18,0xA8,
+0x02,0x0C,0x1C,0x34,0x02,0x0C,0x0C,0x30,0x08,0x0C,0x18,0x18,0x02,0x12,0x1C,0xB0,
+0x02,0x0A,0x0C,0x30,0x82,0x10,0x14,0x28,0x01,0x10,0x22,0x98,0x84,0x11,0x14,0xA8,
+0x83,0x0E,0x1C,0x28,0x02,0x06,0x14,0x30,0x80,0x0F,0x02,0x7A,0xFE,0x0C,0x18,0x18,
+0x02,0x12,0x14,0x30,0x02,0x06,0x18,0x30,0xFF,0x11,0x22,0x8C,0x08,0x0C,0x1C,0x98,
+0x01,0x0D,0x1E,0xB0,0x02,0x0A,0x26,0xB0,0x00,0x00,0x26,0xB8,0x10,0x12,0xC0,0x30,
+0xF0,0x0C,0x18,0x98,0x20,0x00,0xBC,0x38,0x49,0x11,0xB8,0x84,0xD4,0x01,0x1C,0x38,
+0x01,0x0E,0x1A,0x30,0x01,0x0C,0x1C,0xB0,0x01,0x0D,0x18,0xB0,0x03,0x11,0x48,0x80,
+0x00,0x0C,0x5A,0xDA,0xB3,0x00,0xCE,0x2A,0x04,0x12,0x50,0x30,0x04,0x28,0x26,0xB0,
+0x00,0x00,0x48,0x38,0xA3,0x00,0xD8,0x2A,0xD4,0x01,0x1C,0x38,0x03,0x11,0x48,0x80,
+0x00,0x0C,0x5A,0xDA,0x11,0x12,0x22,0xA8,0x00,0x0C,0x4A,0xD2,0x02,0x0C,0x0C,0x30,
+0x02,0x0E,0x18,0xB0,0x02,0x05,0x0A,0x00,0x04,0x12,0x50,0x30,0x02,0x06,0x18,0x30,
+0xFC,0x0C,0x18,0x98,0x04,0x12,0x50,0x30,0xFF,0x11,0x22,0x20,0x11,0x2B,0x22,0xA8,
+0xFD,0x05,0x0A,0x88,0x00,0x00,0x48,0xBC,0x00,0x0C,0x5A,0xDA,0x11,0x12,0x22,0xA8,
+0x00,0x11,0x4A,0x0C,0x00,0x0C,0x5A,0xDA,0x02,0x0C,0x1C,0xB0,0x00,0x11,0x26,0x88,
+0x00,0x11,0x4A,0x0C,0x02,0x11,0x4A,0x80,0x01,0x11,0x1A,0x04,0x10,0x30,0x14,0xB8,
+0x00,0x0C,0x7C,0x5C,0x20,0xCE,0x6F,0x7A,0x20,0x9D,0x6F,0x7A,0xB2,0x00,0x02,0x2E,
+0x00,0x0C,0x6E,0xCA,0x04,0x9E,0x3D,0x03,0x28,0x11,0x70,0x42,0x29,0x11,0x70,0xC2,
+0x00,0x0C,0x82,0xDA,0x00,0x0C,0x38,0x41,0x40,0xC0,0x14,0xB8,0x00,0x0C,0x7C,0x5C,
+0x1C,0x11,0x86,0xDA,0x00,0x0C,0x38,0x41,0x04,0xC8,0x91,0x03,0x01,0x0C,0x14,0x30,
+0x10,0x11,0x92,0xC2,0x01,0x0C,0x14,0x30,0x30,0x11,0x92,0x42,0x01,0x0C,0x14,0x30,
+0x60,0x11,0x92,0x42,0x01,0x0C,0x14,0x30,0x20,0x11,0x92,0xC2,0x01,0x0C,0x14,0x30,
+0x24,0x11,0x92,0x42,0x01,0x05,0x16,0xB0,0x02,0x05,0x0A,0x00,0x02,0xCA,0x9D,0xEA,
+0xF0,0x44,0x9E,0x6A,0x08,0x44,0xA2,0x7A,0x08,0x11,0x88,0x00,0x00,0x0C,0xBA,0x5A,
+0x00,0x0C,0xB8,0x4A,0xFF,0x21,0xAC,0xF2,0x81,0x0A,0xAD,0xE2,0x04,0x10,0x81,0xB0,
+0x01,0x0D,0x89,0x30,0x00,0x0C,0xB8,0xC2,0x01,0x0A,0xB6,0x31,0x04,0xDC,0x80,0x30,
+0x30,0xCE,0xB5,0x7A,0x02,0x0C,0xB6,0xC2,0x01,0x0C,0x18,0x00,0x01,0x0C,0x88,0x30,
+0x01,0x0B,0x0A,0x34,0x08,0x44,0xB0,0x7C,0x0B,0xCB,0xC7,0x6A,0x55,0x11,0x02,0x00,
+0x40,0x4E,0x1B,0x88,0x01,0x01,0x22,0xB0,0xFF,0x0D,0xBA,0x7A,0x08,0x11,0x88,0x00,
+0x01,0x10,0x22,0x1C,0x4A,0x00,0x0C,0xB8,0x01,0x39,0xF8,0xEA,0x02,0x39,0x60,0xFC,
+0x04,0x67,0x14,0x08,0x80,0x5B,0xDA,0x6A,0x08,0x68,0xB0,0x33,0x30,0x67,0xA4,0x0B,
+0x00,0x0C,0xE4,0xC2,0x08,0x53,0xB0,0xB3,0x07,0x53,0x22,0x30,0x30,0x53,0xA4,0x8B,
+0x08,0xD8,0xD1,0x30,0x00,0xD2,0xCF,0x80,0xFF,0x67,0xCE,0x08,0x80,0x11,0xCC,0x00,
+0x05,0x11,0xB4,0x80,0x01,0x11,0x4A,0x80,0x22,0x0A,0x1C,0x28,0xE0,0x0E,0x1C,0x18,
+0x01,0x11,0x1E,0xA0,0x08,0xD8,0x27,0x30,0x07,0x11,0x26,0xB0,0x01,0xD2,0x27,0xB4,
+0xFF,0x65,0x02,0x7B,0xBF,0xE4,0xC9,0x8B,0x0F,0x53,0xC0,0xB0,0x01,0x53,0xFA,0x33,
+0x00,0x0C,0x0A,0x43,0x80,0x5B,0x0E,0x7B,0x40,0xE4,0x0F,0xEB,0x40,0xE4,0xC9,0x03,
+0x0F,0x11,0xC0,0xB0,0x01,0xFD,0xC1,0x30,0x01,0x11,0x72,0x84,0x10,0x53,0xC0,0x30,
+0x01,0x11,0x72,0x84,0x4A,0x00,0x0C,0xB8,0x20,0x11,0xBC,0xDC,0x00,0x0C,0xAC,0x5B,
+0xFF,0x00,0x60,0xEC,0x30,0xCE,0x61,0x7C,0x40,0x11,0x90,0x84,0x4A,0x00,0x0C,0xB8,
+0x01,0x48,0x47,0x7B,0x88,0x49,0x33,0xEB,0x20,0x48,0x31,0x6B,0xB3,0x00,0x74,0x2B,
+0x00,0x0C,0x32,0x4B,0x00,0x0C,0x6E,0xDB,0xA2,0x00,0xC0,0xBB,0x40,0xCA,0x95,0x87,
+0x20,0x11,0xBA,0xDC,0x00,0x0C,0x6A,0x5B,0x1B,0x48,0x15,0x88,0x01,0x0A,0x60,0xE4,
+0x04,0x11,0x80,0x32,0xB2,0x00,0x16,0x2D,0x80,0x4C,0x61,0xFC,0xB3,0x00,0x74,0x2B,
+0x00,0x0C,0x60,0x4C,0xC5,0x00,0xC0,0x3B,0x40,0xCA,0x95,0x87,0x80,0x11,0xBC,0xDC,
+0x00,0x0C,0x6A,0x5B,0x80,0xCE,0x51,0x7B,0x80,0x49,0xB1,0xEC,0x00,0x0C,0x52,0xC3,
+0x40,0x9E,0xB1,0xEC,0x00,0x0C,0x64,0x5C,0x80,0xE1,0x69,0x7B,0xFF,0x21,0x64,0xF3,
+0xB3,0x00,0x74,0x2B,0x00,0x0C,0x60,0xD3,0x00,0x0C,0x6E,0x5C,0x01,0x11,0x22,0x9C,
+0x00,0x0C,0x2C,0xDB,0x01,0x10,0x22,0x1C,0xD1,0x00,0xC0,0x3B,0x40,0xCA,0x95,0x03,
+0x01,0x10,0x22,0x1C,0x00,0x0C,0xB4,0xDC,0x18,0x11,0xB8,0x04,0x04,0x40,0x99,0xB0,
+0x04,0x0C,0xE1,0x30,0x04,0x24,0x31,0x31,0x20,0x10,0xBC,0xB8,0x11,0x00,0x80,0xF3,
+0x20,0x0B,0x85,0x7B,0xF0,0x04,0x14,0x88,0x60,0x0A,0x84,0x73,0x20,0x11,0x86,0x43,
+0x01,0xCD,0x85,0xEB,0x05,0x11,0x86,0xC3,0x00,0x11,0x86,0xC3,0x01,0x05,0x14,0x30,
+0x82,0x05,0x0A,0x80,0x30,0x50,0xC1,0x30,0x01,0x0C,0xB8,0x30,0x01,0x0A,0x0A,0x30,
+0x40,0xE4,0xC9,0x03,0x20,0x0C,0x96,0x63,0x02,0xC3,0x86,0x01,0x40,0x61,0x60,0xEC,
+0x5F,0x01,0x18,0xB8,0x80,0x5F,0x9F,0xEB,0x6F,0x01,0x18,0xB8,0x04,0x12,0x1C,0x08,
+0x22,0x0E,0x1C,0xA8,0xE0,0x0E,0x1C,0x18,0x01,0x11,0x1E,0xA0,0x01,0x11,0x4A,0x80,
+0x10,0x12,0x26,0xB0,0x00,0x11,0x4A,0x0C,0x40,0x61,0xB2,0xFB,0x30,0x60,0xA0,0x32,
+0x00,0x0C,0xC4,0xC3,0x20,0x60,0xA0,0xB2,0x80,0x6F,0xDF,0x02,0x04,0x6F,0x19,0x08,
+0x22,0x0C,0x18,0xA8,0xE0,0x0C,0x18,0x18,0x01,0x11,0x1A,0x20,0x01,0x11,0x4A,0x80,
+0x10,0x12,0xE0,0x32,0x00,0x11,0x4A,0x88,0x11,0x00,0xDE,0x73,0x30,0xCE,0xDF,0xFB,
+0x0C,0x3F,0xD5,0x7B,0x01,0x7F,0xD4,0xFB,0x0C,0x3F,0xB1,0x0B,0x2E,0xD8,0xB1,0xAB,
+0x03,0x11,0xB2,0xB3,0x04,0xD8,0xE1,0x30,0x03,0x3F,0xDF,0x7B,0x01,0x93,0xDE,0x7B,
+0x03,0x3F,0xB1,0x0B,0x03,0x11,0xB2,0xB3,0x04,0xD8,0x99,0x30,0x04,0x70,0x18,0x32,
+0x04,0x98,0x48,0x32,0x04,0x4C,0x80,0xB2,0x11,0x00,0x02,0x74,0x01,0x7F,0xEC,0x7B,
+0x20,0x11,0xBA,0xDC,0x00,0x0C,0xB4,0xDC,0xFF,0xFF,0xC4,0xB8,0x00,0x0C,0x62,0x5C,
+0x01,0x93,0x14,0x30,0x48,0x11,0xB8,0x80,0x30,0xCE,0x17,0xFC,0x02,0x0A,0x14,0x6C,
+0x20,0x49,0x15,0x6C,0x01,0x0A,0x16,0x7C,0x80,0x11,0xBC,0xDC,0x00,0x0C,0xB4,0xDC,
+0x00,0x0C,0x14,0xC4,0x01,0x7F,0x14,0xB0,0x08,0x11,0xB8,0x00,0x04,0x0A,0x0E,0xEC,
+0x20,0x48,0x17,0xFC,0x80,0x11,0xBC,0xDC,0x00,0x0C,0x16,0x44,0xFF,0xFB,0x18,0xB8,
+0xB3,0x00,0xF6,0xAA,0x30,0xCE,0x17,0xFC,0xB2,0x00,0x16,0x2D,0x80,0xE4,0xC9,0x87,
+0x40,0x01,0x18,0x38,0xB3,0x00,0x46,0x2A,0x00,0x0C,0x26,0xCC,0x94,0x00,0x18,0xB8,
+0xB3,0x00,0x46,0x2A,0x00,0x0C,0x26,0xD4,0x04,0x94,0x80,0x36,0x80,0x11,0xBC,0xC4,
+0x04,0x10,0x80,0xB2,0xB3,0x00,0x74,0x2B,0x00,0x0C,0x60,0x4C,0xFF,0xFF,0x0C,0xBA,
+0xFF,0xC1,0x37,0x64,0x02,0x20,0x80,0x33,0x02,0x20,0x84,0x37,0x02,0x20,0x14,0xB0,
+0x02,0xC2,0x41,0xB0,0x02,0x0A,0x0C,0xB2,0x02,0x0A,0x84,0x33,0x02,0x0A,0x40,0x34,
+0x4A,0x00,0x0C,0xB8,0x08,0x39,0x4E,0x6C,0x04,0x39,0x48,0x6C,0x10,0x3A,0x60,0xFC,
+0x00,0x0C,0xAC,0x5B,0x04,0x11,0x72,0x00,0x10,0x11,0x74,0x84,0xFF,0x63,0x56,0xF4,
+0xC0,0x11,0x18,0x80,0xB0,0x00,0xAE,0x2F,0x80,0xE5,0xCB,0x03,0x08,0x11,0x72,0x84,
+0xB0,0x00,0x18,0x28,0x0F,0x00,0x14,0x08,0x01,0x0A,0x14,0x18,0x00,0x7C,0xE4,0x78,
+0xFF,0x11,0x22,0x8C,0x80,0xE1,0x61,0xEC,0x08,0xE4,0x61,0xEC,0x04,0x3C,0xEC,0xB3,
+0x00,0x11,0x78,0x08,0x5F,0x3D,0x7A,0x08,0x08,0xE4,0xC9,0x87,0x02,0x05,0x0A,0x00,
+0x08,0xE4,0x61,0x7C,0x40,0x3D,0x14,0x08,0xBF,0xF7,0xEF,0x8B,0x00,0xF7,0xEF,0x83,
+0x04,0xF6,0x79,0xB0,0xF7,0xE4,0xC9,0x0F,0x02,0x05,0x0A,0x00,0x00,0xC8,0x91,0x83,
+0x01,0x0B,0x14,0xB0,0x00,0xC8,0x17,0x88,0x00,0x0B,0x8A,0x64,0xBF,0x3D,0x7A,0x88,
+0x00,0x11,0xC0,0x89,0xA3,0x00,0xB2,0xAB,0x00,0x0C,0x9C,0xDC,0x00,0x0C,0x60,0x4C,
+0xC0,0xC8,0x69,0xEB,0x10,0x48,0x69,0x7B,0x80,0xCC,0x15,0x08,0x80,0x0B,0x17,0x08,
+0x00,0x0B,0x68,0x73,0x01,0x11,0x22,0x9C,0x02,0x20,0x18,0xB0,0x02,0xF4,0x15,0x30,
+0xB3,0x00,0x6E,0x2A,0x80,0x0F,0x68,0xFB,0xFF,0x0A,0x69,0x73,0xC0,0x0A,0x15,0x88,
+0x80,0x0A,0x68,0x73,0xC0,0x0A,0x68,0xF3,0x01,0x49,0x69,0x6B,0x40,0x49,0x69,0x6B,
+0x01,0x11,0x22,0x9C,0xA3,0x00,0xBE,0x2A,0x08,0x5E,0xBC,0x00,0xFF,0x21,0x60,0xF4,
+0x08,0x11,0xBA,0xC4,0xA3,0x00,0xDE,0x2A,0xA3,0x00,0xE2,0x2A,0x02,0xC9,0x93,0x03,
+0x04,0x5D,0x60,0x7C,0xFF,0xFB,0xDD,0x6C,0x30,0xCE,0xCB,0xEC,0xB0,0x00,0xC6,0xAF,
+0x00,0x0C,0xDA,0x44,0xB2,0x00,0x32,0x2E,0x00,0x0C,0x60,0x4C,0x0B,0x0A,0xD7,0xE4,
+0x04,0x1F,0xDB,0x7C,0x04,0x11,0x60,0x33,0x00,0x0C,0xDA,0x44,0x08,0x48,0xDB,0xFC,
+0xB2,0x00,0x26,0xAF,0x49,0x11,0xB8,0x84,0x01,0x11,0xB8,0x84,0xA3,0x00,0x54,0xAA,
+0x7F,0x06,0x0C,0xB8,0x02,0xE0,0x14,0xB0,0x81,0x80,0xB0,0xAB,0x02,0xE2,0x14,0x30,
+0x81,0x82,0xB4,0xAB,0x02,0xE4,0x14,0x30,0x81,0x84,0xB8,0xAB,0x02,0xE6,0x14,0xB0,
+0x81,0x86,0xBC,0xAB,0x01,0x88,0x00,0xED,0x10,0x88,0xA8,0x6D,0x08,0x88,0xB6,0x6D,
+0x04,0x88,0xD0,0x6D,0x02,0x88,0xDC,0x6D,0x02,0x06,0x22,0x30,0x01,0x11,0x74,0x84,
+0x04,0xDB,0x0F,0xED,0x08,0xDB,0x8B,0xED,0xF0,0xDA,0x91,0x6D,0xFF,0xD8,0x7F,0xED,
+0x03,0xDB,0x4B,0x6D,0x10,0xDB,0x9F,0xED,0x00,0x0C,0xF4,0x44,0xA4,0x06,0x0C,0xB8,
+0x01,0xCD,0x61,0x6C,0x11,0x11,0x00,0x80,0x01,0xC8,0x19,0xED,0x00,0x11,0x8C,0x8D,
+0x04,0x9E,0x1F,0x7D,0xFB,0x9E,0x3D,0x8B,0x02,0x9E,0x3D,0x03,0x70,0x0B,0x15,0x88,
+0x80,0x80,0xB1,0x0B,0x00,0xD8,0x15,0x00,0x08,0xA0,0xB0,0x0B,0x00,0xD8,0x15,0x00,
+0x01,0x0A,0x14,0x00,0x00,0x0C,0xE6,0xD8,0x00,0x0C,0xBE,0xDC,0xB0,0x00,0xBE,0xAF,
+0x00,0x11,0x2A,0x8B,0x00,0x11,0x7E,0x0B,0xB3,0x00,0x5C,0xAA,0x40,0xCE,0x45,0x7D,
+0x01,0x11,0x00,0x00,0x02,0x20,0x40,0x30,0x00,0x11,0x00,0x08,0xE3,0x11,0x52,0xDA,
+0xF7,0x06,0xC0,0xBB,0x40,0xCA,0x95,0x87,0x80,0xCC,0x61,0xEC,0xA3,0x00,0x72,0x29,
+0x04,0x11,0x06,0x05,0x01,0xCD,0x7D,0x6D,0xF7,0xA0,0x40,0x89,0x11,0x11,0x00,0x80,
+0xFE,0xC8,0x91,0x8B,0x10,0xE4,0xC9,0x03,0x01,0x83,0x5E,0xED,0x0F,0x8C,0x14,0x88,
+0x90,0x0A,0xF4,0x03,0xB0,0x00,0xBE,0xAF,0x00,0x0C,0x78,0x45,0x4C,0x8C,0x14,0xA8,
+0x80,0x0A,0xF4,0x83,0x80,0x8C,0x6E,0xED,0xF0,0x8C,0x0E,0x08,0x0C,0x07,0x6E,0x6D,
+0xB0,0x00,0xC2,0x2F,0xC2,0x07,0x72,0x6D,0x31,0x07,0x78,0x6D,0xB0,0x00,0xBE,0xAF,
+0x00,0x0C,0x78,0x45,0x01,0x95,0x2B,0x1B,0x00,0x0C,0x78,0xD5,0xFF,0x11,0x2A,0x03,
+0xBF,0x3D,0x7A,0x88,0x00,0x11,0xC0,0x89,0x03,0x11,0x06,0x85,0x01,0xCD,0x89,0xED,
+0x11,0x11,0x00,0x80,0x02,0xC8,0x87,0x6D,0x08,0xE4,0xC9,0x03,0xE8,0x11,0x16,0x5A,
+0xFF,0x11,0x00,0x85,0x80,0xCE,0x8F,0x7D,0x08,0xC8,0x91,0x03,0x08,0x11,0x06,0x05,
+0x60,0xCE,0x99,0xED,0x80,0xCE,0x9D,0x7D,0x0C,0xC8,0xB1,0x8B,0x0C,0xD8,0x9D,0x65,
+0x20,0x30,0x14,0xB8,0x00,0x0C,0x7C,0x5C,0xF0,0x11,0x04,0x05,0x10,0x45,0xA6,0x6D,
+0x03,0xC8,0xA7,0xFD,0x80,0xC0,0x14,0xB8,0x00,0x0C,0x7C,0x5C,0x10,0x11,0x06,0x05,
+0x1E,0xDC,0xF7,0x7C,0x18,0xDC,0xB5,0xFD,0x87,0x11,0x8E,0xDA,0x22,0x11,0x00,0x80,
+0x80,0xE4,0xC9,0x03,0xE7,0xE4,0xC8,0x09,0x1E,0x11,0x08,0x05,0x80,0xDC,0xBD,0x6D,
+0x02,0xDE,0xC7,0x6D,0x00,0x0C,0xF8,0x44,0x01,0xCD,0xC5,0x6D,0x30,0xCB,0xC5,0x7D,
+0x80,0xCB,0xC5,0x6D,0x80,0xCB,0x97,0x03,0x80,0x11,0x08,0x85,0x01,0xA1,0xCE,0x7D,
+0xFD,0xE6,0xCC,0x89,0x00,0x11,0x00,0x08,0x00,0x0C,0x52,0xC3,0x02,0x11,0x0C,0x05,
+0xFF,0xD9,0xFB,0x7C,0x81,0x11,0xB0,0x03,0xFF,0x81,0xB2,0x0B,0xFF,0x11,0x02,0x81,
+0x01,0xA1,0x60,0xEC,0x00,0x0C,0xE8,0x45,0x0E,0xDA,0xE3,0xED,0x01,0xDA,0xFD,0xFC,
+0xFE,0xE2,0xC4,0x89,0x82,0x11,0xB0,0x03,0x0F,0x82,0xB2,0x0B,0x0F,0x11,0x04,0x81,
+0x04,0x3A,0xE8,0x7D,0x0C,0x00,0x68,0xB8,0x02,0xD8,0x6D,0x34,0xB0,0x00,0x18,0x28,
+0x02,0x05,0x0A,0x00,0x10,0xE4,0x15,0x6E,0x0F,0xCB,0x09,0xEE,0x20,0xE4,0x0D,0x6E,
+0x80,0xC8,0x0B,0xEE,0x20,0xC8,0x0D,0xEE,0xFD,0x05,0x0A,0x88,0xDC,0x01,0x1C,0xB8,
+0xE3,0x11,0x2E,0x5A,0x00,0x0C,0xE4,0xD0,0x10,0xC9,0x93,0x03,0x15,0x11,0x0E,0x46,
+0x08,0x11,0x0E,0x46,0x05,0x11,0x0E,0xC6,0x16,0x11,0x0E,0x46,0xFD,0x05,0x0A,0x88,
+0x00,0x0C,0xB2,0xDC,0x14,0x11,0x1C,0xC6,0xFD,0x05,0x0A,0x88,0x00,0x0C,0x58,0x5C,
+0xB3,0x00,0x92,0xAB,0x0D,0x11,0x1C,0x46,0x00,0x0C,0xAC,0xD9,0xA0,0x00,0x5C,0x2A,
+0x10,0xE4,0x47,0xEE,0xB3,0x00,0x82,0x2B,0x02,0x58,0x19,0x30,0xFC,0xFF,0x14,0x38,
+0x83,0x90,0x14,0x28,0x8A,0x0C,0x3E,0xAE,0xB3,0x00,0x6E,0x2A,0x80,0x0F,0x3A,0xEE,
+0x04,0x0C,0x78,0xB2,0x01,0x11,0xB2,0x5C,0xFC,0xFF,0x14,0x38,0x83,0x90,0xB0,0x2A,
+0x00,0x0C,0x3E,0xC6,0x04,0x11,0x78,0xB2,0x02,0x11,0xB2,0x5C,0x10,0x50,0xC1,0xB0,
+0x20,0x00,0xBC,0x38,0x69,0x11,0xB8,0x80,0x10,0xE4,0xC9,0x87,0x18,0x11,0xB8,0x04,
+0x01,0x30,0x14,0x30,0xFF,0x0A,0x5E,0x7E,0x01,0x0A,0x5E,0x76,0x15,0x0A,0x5C,0xF6,
+0x0A,0x0A,0x1C,0x77,0x02,0x0A,0x2C,0xF7,0xF8,0x0A,0x14,0x88,0xC0,0x0A,0x02,0x77,
+0xD0,0x0A,0x04,0xF7,0x16,0x11,0x2A,0x80,0xA3,0x00,0xD2,0x29,0x04,0x30,0xB0,0xB3,
+0x11,0x11,0x00,0x80,0x02,0x22,0x0C,0x30,0x02,0x20,0x0C,0xB0,0x02,0xDA,0x41,0xB0,
+0x02,0x46,0x45,0x30,0x03,0xC8,0xA3,0xEE,0xC0,0xCA,0xA3,0x6E,0x3F,0xCB,0xA3,0xEE,
+0x04,0x06,0x22,0x30,0x01,0xD8,0xF7,0xB3,0x02,0x20,0xDC,0x33,0x20,0x0B,0xAD,0x7E,
+0x40,0x9E,0xA9,0x6E,0x08,0x48,0x15,0x08,0xF7,0x9E,0x3D,0x8B,0x00,0x9E,0x3D,0x83,
+0xB2,0x00,0x20,0xAF,0x01,0x9D,0x9D,0xEE,0x01,0x02,0x90,0x3B,0x80,0x11,0x94,0x83,
+0x01,0xCD,0x9B,0x03,0x01,0x45,0x9C,0xB3,0xB3,0x00,0x32,0xAB,0xB3,0x00,0x5C,0xAA,
+0x01,0xFB,0x9B,0xF6,0xB0,0x00,0xFA,0xAA,0x10,0xCE,0x99,0xFE,0x00,0x0C,0xC4,0x5C,
+0xB6,0x01,0xC0,0xBF,0x2F,0x02,0xC0,0xBF,0x02,0x9E,0xAD,0xEE,0x04,0x9E,0x3D,0x03,
+0x00,0x0C,0xAC,0x46,0xB3,0x00,0xEA,0xAB,0x02,0x06,0x40,0xB0,0x02,0x06,0x44,0x30,
+0x04,0x3A,0xA8,0xFE,0x04,0xD8,0x69,0xB4,0x70,0x0B,0x15,0x88,0xFF,0x0A,0xB6,0x7E,
+0x80,0x80,0xBD,0x6E,0x02,0x82,0xE5,0xB3,0x00,0x0C,0xC0,0x46,0x01,0x7C,0xB6,0x7E,
+0xFF,0xFF,0xE4,0x3B,0x00,0x0C,0xC0,0x46,0x01,0x23,0xE4,0xB3,0x01,0x22,0xE6,0xB3,
+0x08,0x11,0xB8,0x00,0x00,0x00,0x90,0xB9,0x8F,0x80,0x17,0x08,0x00,0x0B,0x94,0x01,
+0x09,0x0B,0x15,0x08,0x55,0x11,0x00,0x80,0x01,0x43,0xD1,0xFE,0x08,0x11,0x14,0x00,
+0x11,0x11,0x00,0x80,0xF6,0x81,0x17,0x08,0x00,0x0B,0x94,0x01,0x02,0xF2,0x95,0x31,
+0x08,0x84,0x95,0xB1,0x08,0xA8,0x94,0xB1,0x08,0x94,0x95,0x31,0x09,0x0A,0xE8,0x76,
+0x55,0x11,0x00,0x80,0x01,0x43,0xE7,0x6E,0x08,0x11,0x40,0x01,0x11,0x11,0x00,0x80,
+0x10,0x04,0xBC,0xB8,0x1C,0x11,0xDE,0x5C,0x49,0x11,0xB8,0x00,0x01,0xC8,0x91,0x03,
+0x80,0xCA,0x95,0x03,0xB3,0x00,0x32,0xAB,0x02,0x96,0x71,0xB1,0x01,0xBA,0x74,0x01,
+0xFE,0x9E,0x3D,0x8B,0xE8,0x11,0x16,0x5A,0x02,0x7C,0xFC,0xFE,0x02,0xE4,0xC9,0x03,
+0x00,0x0C,0x90,0x46,0x02,0x11,0x06,0x47,0x01,0x11,0x06,0x47,0x01,0x0C,0x48,0x30,
+0x04,0xCE,0x1B,0xF7,0xC8,0xCD,0x1D,0x98,0x01,0x11,0x1E,0x00,0x01,0x30,0x26,0xB0,
+0x11,0xCD,0x15,0xA8,0x03,0x0E,0x9A,0x8B,0x01,0xCE,0x9D,0x1B,0xC0,0x0A,0x1C,0x18,
+0x02,0x32,0x26,0xB4,0x1D,0x11,0x2A,0x00,0x04,0x30,0xB0,0xB3,0x30,0xCB,0xA9,0xEE,
+0x44,0x11,0x00,0x80,0x02,0xDA,0xE1,0xB3,0x10,0xCB,0x97,0x03,0x80,0xE1,0x61,0x7C,
+0xB3,0x00,0x32,0xAB,0x2F,0x08,0xC0,0xBF,0x04,0x30,0x22,0x30,0x44,0x11,0x00,0x80,
+0xB2,0x00,0xC8,0x28,0xA3,0x00,0x32,0x2B,0xB2,0x00,0xB6,0xAA,0xEB,0x00,0xF4,0xBB,
+0xB2,0x00,0x4E,0x2A,0xA0,0x00,0x22,0xA8,0x01,0xC7,0x14,0xB0,0x00,0xC5,0x14,0x08,
+0x80,0x0A,0x50,0x6F,0x40,0x0A,0x54,0xEF,0x20,0x0A,0x58,0xEF,0x08,0x0A,0x70,0xEF,
+0x04,0x0A,0x86,0xEF,0x02,0x0A,0x90,0x6F,0x01,0x0A,0x9A,0x6F,0xFF,0x11,0x22,0x8C,
+0x80,0x11,0x8E,0x81,0x20,0xC9,0x93,0x87,0x40,0x11,0x8E,0x81,0xE4,0x11,0x5C,0xC7,
+0x20,0x11,0x8E,0x81,0xE0,0x11,0x5C,0x47,0x01,0x11,0x4A,0x80,0x01,0x11,0x1A,0x80,
+0x02,0x0C,0x1C,0xB0,0x04,0x12,0xB0,0xB3,0x01,0x00,0x14,0xB8,0x83,0xD8,0xB1,0x2B,
+0x00,0x00,0x14,0x38,0x84,0xDA,0xB5,0xAB,0x00,0x0C,0x60,0x4C,0x04,0xD8,0x27,0xB4,
+0x01,0xC7,0x19,0x88,0x01,0xCB,0x14,0x08,0x00,0x0C,0x7E,0xF7,0xFE,0xC7,0x8F,0x8B,
+0x00,0xC7,0x8F,0x83,0x01,0x0A,0x80,0x7F,0xEF,0x11,0x52,0xDA,0x08,0x11,0x8E,0x05,
+0x08,0x11,0x8E,0x81,0xEC,0x11,0x16,0xDA,0xE8,0x11,0x5C,0xC7,0x04,0x11,0x8E,0x81,
+0x04,0xC9,0x93,0x03,0x59,0x11,0x7C,0x5A,0xB1,0x00,0x9E,0xAF,0x09,0x11,0xE4,0x85,
+0x02,0x11,0x8E,0x81,0x08,0xC9,0x93,0x03,0x58,0x11,0x7C,0xDA,0xB1,0x00,0x9E,0xAF,
+0x05,0x11,0xE4,0x85,0x01,0x11,0x8E,0x81,0x40,0xC9,0x93,0x87,0x80,0x11,0x8E,0x81,
+0xDF,0xC9,0x93,0x0F,0x02,0x48,0x15,0xB0,0x01,0x0B,0x68,0xEB,0x02,0x05,0x0A,0x00,
+0x01,0x0A,0x04,0xB0,0x01,0x0B,0x06,0x80,0xE1,0x48,0xB3,0xAF,0xFD,0x05,0x0A,0x88,
+0x01,0x11,0x22,0x9C,0xFD,0x05,0x0A,0x88,0x02,0x48,0x91,0x32,0x00,0x0C,0xA2,0x47,
+0x02,0x06,0x44,0x30,0xFF,0x11,0x22,0x8C,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x01,0x4E,0x15,0xB0,0x01,0x4C,0x17,0xB0,0x81,0x5A,0x15,0x28,0x02,0x0B,0x0C,0xF8,
+0xB1,0x00,0x6A,0x29,0x00,0x0C,0x5C,0x40,0x20,0x0A,0x14,0x78,0x30,0xE4,0xC9,0x83,
+0x22,0x11,0x9E,0x82,0xDD,0x5A,0xB5,0x0A,0x02,0x0A,0x1A,0xF8,0x20,0xE4,0xC9,0x03,
+0x02,0x11,0x9E,0x02,0x10,0x0A,0x20,0xF8,0x40,0xE4,0xC9,0x03,0x10,0x11,0x9E,0x02,
+0x40,0x0A,0x26,0xF8,0xFD,0x4D,0x9B,0x8A,0xFD,0x5B,0xB7,0x8A,0x10,0xCB,0x3D,0xE8,
+0xEB,0x0A,0x14,0x08,0xFF,0x0A,0x5A,0xF8,0x20,0xCB,0x5B,0xE8,0x20,0xCB,0x97,0x03,
+0x97,0xC5,0x8A,0x89,0x82,0x08,0xC0,0xBB,0x00,0x0C,0xB6,0x5A,0x40,0x0A,0x44,0x78,
+0x01,0xCB,0x97,0x03,0x00,0x0C,0x44,0x40,0x40,0x4E,0x9F,0x0A,0x20,0x0A,0x44,0xE8,
+0xA9,0x44,0x15,0x88,0xFF,0x0A,0x5A,0xF8,0xA9,0x0A,0x48,0xF8,0x80,0xE4,0xC9,0x03,
+0x01,0x0A,0xDC,0xB3,0x00,0x0C,0x0C,0x5B,0x40,0xEF,0x51,0x78,0x01,0xCA,0x95,0x03,
+0x20,0xE4,0x55,0x78,0x02,0xEE,0xDD,0x03,0x40,0xE4,0x59,0x78,0x10,0xEE,0xDD,0x03,
+0xFE,0x3F,0x7E,0x88,0xFF,0x11,0x9E,0x82,0x01,0x11,0x76,0x04,0x02,0xF0,0x41,0x30,
+0x80,0x0A,0x79,0x70,0x81,0x0A,0x4F,0xF3,0x00,0x0C,0x5E,0xC3,0xB2,0x00,0x66,0x2F,
+0x00,0x0C,0x24,0x5B,0xFF,0xF1,0x5F,0x60,0x02,0x05,0x0A,0x00,0x0F,0xCB,0x97,0x0B,
+0x40,0x3D,0x7A,0x00,0x01,0x3F,0x7E,0x00,0xB3,0x00,0x32,0xAB,0xA0,0x00,0x22,0xA8,
+0xF4,0xCB,0x97,0x8B,0x01,0x0C,0x15,0xB0,0x01,0x0A,0x8A,0x70,0x81,0x0A,0x8E,0x70,
+0x02,0x0A,0x92,0x70,0xFF,0x0A,0xC6,0xF8,0x03,0x0A,0x8A,0xF0,0x04,0x0A,0x8A,0x70,
+0x00,0x0C,0xEC,0xC0,0x01,0x11,0xE8,0x03,0x00,0x0C,0xA8,0xC0,0x02,0x11,0xE8,0x03,
+0x00,0x0C,0xA8,0xC0,0x54,0x11,0x02,0x80,0x02,0x05,0x0A,0x00,0x01,0x0D,0x85,0xB2,
+0x55,0x11,0x00,0x80,0x20,0x11,0x9E,0x02,0x20,0x11,0x88,0x82,0x20,0x11,0x9C,0x82,
+0x22,0x5A,0xB5,0x82,0xFD,0x05,0x0A,0x88,0x01,0x01,0x22,0xB0,0x00,0x0C,0xAA,0x40,
+0x00,0x0C,0x2A,0x5A,0x00,0x0C,0xCA,0xDA,0x01,0x11,0xB5,0xF8,0x03,0x11,0x48,0x80,
+0x04,0xD8,0x81,0x33,0x00,0x11,0x48,0x08,0x00,0x0C,0xBE,0xC8,0x34,0x08,0x0C,0x38,
+0x20,0xEE,0x4F,0xEF,0x02,0x11,0x4F,0x7F,0x00,0x0C,0xC8,0xC0,0x00,0x0C,0x24,0x5B,
+0xFF,0xFF,0x40,0xB8,0x40,0xEF,0xA5,0xE9,0x00,0x0C,0x2A,0x41,0x7F,0x08,0x0C,0x38,
+0x55,0x11,0x02,0x00,0x02,0x05,0x0A,0x00,0x00,0x00,0xB0,0x3A,0x00,0x00,0xB4,0xBA,
+0xFE,0x3F,0x7E,0x88,0x08,0x11,0x80,0x02,0x00,0x11,0x82,0x8A,0x00,0x11,0x80,0x0A,
+0xC2,0x60,0xC1,0x02,0xFF,0xFF,0x94,0x3A,0xFF,0x11,0x9E,0x82,0x01,0x01,0x22,0xB0,
+0xB3,0x00,0x82,0xAA,0x97,0xC5,0x8A,0x89,0xB2,0x00,0x12,0xAB,0x03,0x11,0x90,0xB3,
+0x08,0x11,0x96,0x03,0xA3,0x00,0xB2,0xAB,0x10,0x0D,0xF5,0xF8,0x55,0x11,0x00,0x80,
+0xEF,0x5A,0xB5,0x8A,0x44,0x11,0x00,0x80,0x00,0x0C,0x6A,0xDA,0x10,0x0D,0xFF,0x68,
+0x55,0x11,0x00,0x80,0x10,0x5A,0xB5,0x02,0x44,0x11,0x00,0x80,0x00,0x11,0x78,0x8A,
+0x03,0x11,0x7A,0xB2,0x11,0x11,0x66,0x40,0x40,0xEE,0x11,0xF9,0xB1,0x00,0xAE,0xA9,
+0x00,0x0C,0x1A,0xDB,0x00,0x0A,0x54,0x5F,0xBF,0xEE,0xDD,0x8B,0xA9,0xEE,0x6B,0x78,
+0x14,0xCB,0x97,0x83,0xF4,0xCB,0x97,0x8B,0x01,0x11,0xE8,0x03,0xFF,0xFF,0x40,0xB8,
+0x00,0x0C,0xCA,0xDA,0xB1,0x00,0xAE,0xA9,0x00,0x0C,0x1A,0xDB,0x00,0x0A,0x54,0x5F,
+0x08,0xEE,0x27,0xF9,0x00,0x0C,0x4E,0xDA,0x00,0x0C,0x6A,0x40,0x80,0xEE,0x6B,0xF8,
+0x40,0xEF,0xA5,0xE9,0x00,0x11,0x68,0x5F,0x91,0x00,0x40,0xB9,0x02,0x00,0x40,0xB9,
+0x01,0x11,0x56,0x5F,0xB0,0x00,0x5C,0xAF,0xB0,0x00,0x18,0x28,0xC0,0xCA,0x61,0x6F,
+0x02,0xF4,0x6F,0xF1,0x00,0x0C,0x2C,0xDB,0xE4,0x11,0x50,0xDF,0x11,0x11,0x4A,0xDB,
+0xB1,0x00,0x58,0xAC,0x44,0x11,0x4A,0xDB,0xB0,0x00,0x18,0x28,0x40,0xCB,0x57,0x69,
+0x80,0xCB,0x67,0x69,0x00,0x0C,0x18,0xDA,0x00,0x0C,0xF6,0x49,0x02,0xCB,0xF9,0xE9,
+0xE7,0x11,0x4C,0x5F,0x00,0x0C,0xFA,0x49,0x00,0x0C,0x60,0xC7,0x00,0x11,0x4A,0xDB,
+0xB1,0x00,0x58,0xAC,0x00,0x00,0x90,0xB9,0x00,0x11,0xB2,0x89,0x1C,0xD8,0x94,0x31,
+0x40,0x11,0x72,0x00,0x1C,0xF0,0xF4,0x3B,0x00,0x0C,0xD4,0xC1,0x84,0x80,0x40,0x38,
+0x0C,0x11,0x54,0x5F,0xFF,0xFF,0x40,0xB8,0x00,0x0C,0x6A,0x40,0x00,0x0C,0xB6,0x5A,
+0x55,0x11,0x00,0x80,0x10,0x11,0x80,0x02,0x44,0x11,0x00,0x80,0x2D,0x11,0x58,0x5F,
+0x02,0x05,0x0A,0x00,0x0F,0xCB,0x97,0x0B,0xB3,0x00,0x32,0xAB,0xFD,0x05,0x0A,0x88,
+0xB0,0x00,0x18,0x28,0x00,0x0C,0x18,0xDA,0x00,0x0C,0x90,0x49,0x08,0xC7,0x8C,0xF9,
+0x08,0x11,0x8E,0x81,0x01,0xCB,0x90,0x79,0xFF,0xF1,0x6B,0xE0,0x00,0x0C,0x60,0xC7,
+0x40,0x11,0x72,0x00,0xB0,0x00,0x18,0x28,0x55,0x11,0x00,0x80,0x40,0x48,0x9F,0x69,
+0x44,0x11,0x00,0x80,0xFF,0xF1,0x6B,0xE0,0x00,0x0C,0x60,0xC7,0x90,0x11,0x80,0x82,
+0x44,0x11,0x00,0x80,0x00,0x0C,0x6A,0x40,0x02,0xCC,0x0D,0xB0,0xB1,0x11,0x14,0x80,
+0xB1,0x00,0xE6,0x28,0x02,0x06,0x14,0x30,0x01,0x0B,0xB2,0x79,0xB0,0x00,0x18,0x28,
+0x01,0xCD,0x61,0x6F,0xFE,0xCD,0x9B,0x8B,0xB0,0x00,0x18,0x28,0x40,0xCA,0x61,0xEF,
+0xD0,0x01,0x1C,0xB8,0xE8,0x11,0x5A,0xDF,0xB0,0x00,0x18,0x28,0x40,0xCB,0xCF,0xE9,
+0x00,0x0C,0x18,0xDA,0x00,0x0C,0xF6,0x49,0x02,0xCB,0xF9,0xE9,0xD0,0x01,0x1C,0xB8,
+0xEB,0x11,0x4A,0x5F,0x00,0x0C,0xFC,0x49,0x00,0x0C,0x60,0xC7,0x00,0x11,0x4A,0xDB,
+0x20,0x11,0xB8,0x00,0x14,0x20,0xF4,0x3B,0xB1,0x00,0xAE,0xA9,0x01,0x0A,0xC4,0xB3,
+0x01,0xFA,0x15,0xB0,0x00,0x11,0x16,0x88,0xB1,0x00,0xEA,0xA9,0xB1,0x00,0x06,0x2A,
+0x02,0x0C,0x1C,0x98,0xFF,0xC6,0x27,0x08,0x01,0xFB,0x27,0xB0,0x02,0x0A,0x26,0xB0,
+0xB1,0x00,0x58,0xAC,0x00,0xE2,0x55,0xDF,0xFF,0xFF,0x40,0xB8,0x44,0x11,0x4A,0xDB,
+0xFB,0xCB,0x97,0x8B,0x01,0xF6,0xEB,0x33,0x00,0x0C,0x6A,0x40,0x01,0x11,0xFE,0xC1,
+0x02,0x11,0xFE,0xC1,0x00,0x11,0xFE,0x41,0x03,0x11,0xFE,0x41,0x01,0x0C,0xE4,0x33,
+0xB1,0x00,0xAE,0xA9,0x03,0xF2,0x07,0x62,0x00,0x11,0xEA,0x8B,0x02,0x0C,0x1C,0x98,
+0x18,0xC6,0x27,0x80,0x01,0xF2,0x27,0xB0,0x01,0xF5,0x27,0x30,0x00,0x0A,0x54,0x5F,
+0x35,0x08,0x0C,0xB8,0xFF,0xF5,0xC9,0xF8,0xFF,0xF5,0xEB,0x9B,0x00,0x0C,0x4E,0xC2,
+0x55,0x11,0x02,0x00,0x40,0x4E,0x27,0xFA,0x00,0x11,0x82,0x8A,0xC0,0x11,0x9E,0x82,
+0x01,0x11,0x76,0x80,0x01,0x01,0x22,0xB0,0x01,0x10,0x22,0x1C,0x01,0x01,0x22,0xB0,
+0x01,0x11,0x22,0x9C,0x00,0x0C,0xB6,0x5A,0x00,0x0C,0x6A,0xDA,0x01,0x10,0x9F,0xB3,
+0x01,0x16,0xED,0xB3,0x01,0x16,0xEB,0xB3,0x01,0x11,0x3D,0x7A,0x03,0x11,0x48,0x80,
+0x04,0x12,0x81,0x33,0x00,0x11,0x48,0x08,0x55,0x11,0x00,0x80,0x03,0x0A,0x7C,0x72,
+0x04,0x0A,0x7C,0xF2,0xFD,0x11,0x9E,0x02,0xEB,0x00,0xF4,0xBB,0x10,0x42,0x4B,0x6A,
+0x10,0xFA,0xF5,0x03,0xFD,0x60,0xC1,0x8A,0x44,0x11,0x00,0x80,0xE3,0x11,0x5E,0xDF,
+0x00,0x0C,0x5A,0x52,0xB0,0x00,0x18,0x28,0xD8,0x01,0x1C,0x38,0xE3,0x11,0x4A,0xDF,
+0x00,0x0C,0x60,0x57,0x55,0x11,0x02,0x00,0x00,0x11,0x82,0x8A,0x10,0x11,0x80,0x02,
+0x90,0x11,0x80,0x82,0x02,0xFA,0xB5,0x32,0x01,0x01,0x22,0xB0,0xD8,0x01,0x1C,0x38,
+0xE0,0x11,0x5A,0x47,0x54,0x11,0x02,0x80,0x01,0x0F,0xA1,0x32,0x01,0x0D,0x85,0xB2,
+0x01,0x0E,0x8B,0x32,0x06,0x11,0xE2,0x02,0x00,0x11,0xE4,0x8A,0x29,0x11,0xE6,0x02,
+0x01,0x11,0xD8,0x82,0x01,0x01,0x22,0x34,0x01,0xA2,0x18,0x38,0x03,0x02,0x1C,0x38,
+0x03,0x0A,0x88,0xF2,0x08,0xA1,0x18,0x38,0x11,0x10,0x1C,0x38,0x01,0xCA,0x95,0x03,
+0x00,0x11,0x8C,0x0A,0x3D,0x60,0xC1,0x8A,0x01,0x0C,0x82,0xB2,0x50,0x11,0x80,0x82,
+0x08,0x11,0xC4,0x83,0xFF,0xE2,0xC5,0x9B,0xFF,0xE2,0x93,0xEA,0xA0,0x41,0x83,0x82,
+0x01,0x0E,0x14,0xB0,0x00,0x49,0xC5,0x8B,0xFF,0xE2,0x9B,0xFA,0x01,0x0D,0x82,0x32,
+0x01,0x0F,0x14,0x30,0x00,0xE2,0xA7,0x6A,0x00,0x49,0xA5,0xFA,0x80,0x11,0x9C,0x82,
+0xE0,0x11,0x82,0x82,0x03,0x11,0x8C,0x82,0xA0,0xE4,0xC9,0x83,0x82,0x11,0xDC,0x03,
+0x00,0x0C,0x0C,0x5B,0xFF,0x11,0x9E,0x82,0x44,0x11,0x00,0x04,0x04,0xCB,0x97,0x03,
+0x22,0x11,0x02,0x00,0x97,0xC5,0x8A,0x89,0xEF,0x11,0x5C,0x5F,0xFE,0xC7,0x8F,0x8B,
+0x01,0x01,0x22,0xB0,0x08,0x11,0x88,0x00,0x02,0x00,0x40,0xB9,0x00,0x11,0x8A,0x88,
+0x00,0x11,0x56,0xC7,0x01,0x3F,0x7E,0x00,0xC0,0x01,0x1C,0x38,0xEC,0x11,0x5A,0x5F,
+0xB0,0x00,0x18,0x28,0x80,0xE4,0xE3,0x7A,0x80,0xEE,0xDF,0x7A,0x02,0xF4,0xDF,0x72,
+0xB2,0x00,0x12,0xAB,0x68,0xC5,0x8A,0x01,0xB1,0x00,0x74,0x29,0xB3,0x00,0x8C,0x2A,
+0x00,0x0C,0xF6,0xC2,0x20,0xE4,0x61,0xEF,0xC0,0x01,0x1C,0x38,0xEF,0x11,0x4A,0xDF,
+0x00,0x0C,0x60,0x57,0x55,0x11,0x02,0x00,0xFE,0x3F,0x7E,0x88,0x01,0x44,0xDD,0x33,
+0x00,0x0C,0x0C,0x5B,0xFF,0x11,0x9E,0x82,0x01,0x01,0x22,0xB0,0x1F,0xE4,0xC9,0x8B,
+0xFF,0x21,0x04,0xF3,0x11,0x11,0x66,0xDF,0x00,0x11,0x78,0x8A,0x01,0xEE,0x7B,0xB2,
+0x01,0xEF,0x7D,0x32,0x01,0xF3,0x7F,0x32,0x01,0xCB,0x14,0x08,0xFE,0xC7,0x8F,0x8B,
+0x00,0xC7,0x8F,0x83,0x11,0xEE,0x23,0xAC,0x01,0x43,0xDF,0x33,0x80,0x48,0xE7,0x8B,
+0xFF,0x11,0x94,0x06,0x06,0x11,0x96,0x01,0x68,0xC7,0x4E,0x7F,0x68,0x11,0x8E,0x81,
+0x80,0x11,0x74,0x84,0x02,0x0C,0x1C,0x98,0x10,0xC6,0x27,0x00,0x01,0xEE,0x27,0x30,
+0x01,0xEF,0x27,0xB0,0x01,0xF3,0x27,0xB4,0x02,0x00,0xE1,0x33,0xFF,0xF1,0x2B,0x63,
+0x11,0x11,0x54,0x47,0x12,0x11,0x54,0x47,0x11,0x11,0x02,0x00,0x08,0x11,0xB8,0x00,
+0x01,0xC0,0x23,0xB0,0x02,0x11,0x4A,0x80,0x03,0xE0,0x15,0x08,0x1C,0x00,0x98,0x38,
+0xFC,0xE0,0xC1,0x08,0x07,0xE1,0xC1,0xB0,0x1C,0x11,0xC0,0x00,0x06,0x11,0xC0,0xB0,
+0x44,0x0A,0xC0,0xA8,0x00,0x11,0x4A,0x88,0x10,0x04,0xBC,0xB8,0x49,0x11,0xB8,0x00,
+0x01,0x01,0x22,0x34,0x80,0xE1,0xC3,0x03,0x01,0x0C,0x00,0xB4,0xB1,0x00,0x92,0xAA,
+0x00,0x0C,0x5C,0xCB,0xF0,0x0D,0x5B,0xFB,0x01,0x0C,0x5B,0xFB,0xB1,0x00,0xBA,0xAA,
+0x00,0x0C,0x5C,0xCB,0x00,0x11,0x66,0x40,0x09,0x11,0x66,0x40,0x01,0x0C,0x15,0xB0,
+0xFF,0x0A,0x66,0xFB,0x01,0x0A,0x7C,0x73,0x00,0x0C,0x8E,0x43,0x02,0x05,0x0A,0x00,
+0x22,0x11,0x02,0x00,0x01,0x11,0x4A,0x80,0x42,0x11,0x00,0x80,0x0C,0xE0,0x21,0xB2,
+0x22,0x11,0x00,0x80,0x00,0x11,0x4A,0x88,0x45,0x11,0x00,0x00,0x04,0x3C,0x39,0xB2,
+0x01,0x01,0x22,0xB0,0x0A,0x11,0x66,0x40,0x02,0x05,0x0A,0x00,0x22,0x11,0x02,0x00,
+0x01,0x11,0x4A,0x80,0x0C,0x11,0xC0,0xB3,0x00,0x11,0x4A,0x88,0x55,0x11,0x00,0x80,
+0x04,0x11,0x78,0xB2,0x01,0x01,0x22,0xB0,0x00,0x11,0x66,0x40,0x01,0x11,0x04,0x01,
+0x01,0xE2,0xC4,0x01,0x00,0x11,0x66,0x40,0x04,0x4C,0xAD,0x7B,0xFB,0xFF,0x18,0xB8,
+0xB3,0x00,0x24,0x2B,0x08,0x11,0x64,0xDF,0x00,0x0C,0xBE,0xDC,0xCA,0x11,0xDA,0xDB,
+0x0C,0x28,0x95,0x31,0x10,0x3F,0xA7,0xFB,0x04,0x34,0x95,0x31,0x10,0x00,0xBC,0x38,
+0x00,0xC8,0x52,0xDF,0x00,0x0C,0xD4,0x43,0x80,0x9E,0xB1,0xFB,0xA0,0x00,0xDA,0x2B,
+0x10,0x4C,0xB9,0x7B,0xDF,0x9D,0x3B,0x8B,0x20,0x4C,0x15,0x88,0x00,0x9D,0x3B,0x83,
+0x00,0x0C,0x18,0x5E,0x00,0x0C,0xB6,0x5C,0x14,0x10,0x95,0xB1,0x00,0xC8,0x52,0xDF,
+0x08,0x48,0xD1,0xEB,0x02,0x20,0x4C,0x33,0x10,0x00,0xBC,0x38,0x11,0x48,0x15,0x88,
+0x11,0x0A,0xD2,0xE3,0x04,0x0C,0x81,0xB2,0x00,0x0C,0x1C,0xDE,0x00,0x0C,0xD4,0x43,
+0x10,0x20,0xBC,0xB8,0x04,0x11,0x80,0x32,0x00,0x11,0x94,0x88,0x04,0x11,0xA4,0xB0,
+0x04,0x11,0x24,0x05,0x01,0x0C,0x1C,0xB0,0x00,0x11,0x1E,0x08,0x46,0x11,0x26,0x80,
+0x0F,0x11,0x27,0x08,0x00,0x00,0x26,0x3C,0x4A,0x00,0x0C,0xB8,0x01,0xCA,0xED,0x7B,
+0xFE,0xCA,0x95,0x8B,0x40,0xCB,0x97,0x87,0xB0,0x00,0xC6,0xAF,0x02,0x38,0x0D,0xB4,
+0xB1,0x00,0x46,0x2B,0x00,0x0C,0xFA,0x43,0x18,0x11,0xB8,0x80,0x40,0x11,0x90,0x00,
+0xFD,0xE4,0xC9,0x8B,0xFF,0xFF,0xC4,0xB8,0xFF,0x11,0x22,0x8C,0x04,0x11,0x64,0xDF,
+0x00,0x0C,0x9E,0xDD,0x0C,0x94,0x06,0x74,0x10,0x3F,0x7F,0x02,0xB1,0x00,0x2A,0xAC,
+0x00,0x0C,0xF4,0xC3,0x00,0x0C,0xAE,0xDD,0x80,0xB5,0xF5,0x6B,0xFD,0x09,0x0C,0xB8,
+0x08,0x48,0x25,0x7C,0x21,0xB5,0x1B,0x6C,0x18,0x11,0xB8,0x80,0x1B,0x48,0x15,0x88,
+0x00,0x0C,0xD0,0x44,0x02,0x20,0x0C,0xB0,0xF8,0x1C,0x17,0x08,0x00,0x0C,0xF4,0xDE,
+0x02,0x06,0x40,0xB0,0x00,0x0C,0x2E,0xC4,0xFF,0xFF,0x4C,0xBB,0x21,0xB5,0x2F,0xEC,
+0x0B,0x0A,0x2F,0xF4,0x40,0x48,0x31,0xEC,0xA0,0x00,0xF6,0x29,0x0C,0x11,0x32,0x44,
+0x0B,0x11,0x32,0xC4,0xA0,0x00,0xFA,0x29,0x00,0x0C,0x42,0x5D,0x01,0x48,0x3F,0x6C,
+0x00,0x0C,0x9E,0xDD,0x04,0xE4,0xC9,0x03,0x00,0x0C,0xBA,0xC5,0x00,0x0C,0x9E,0xDD,
+0x00,0x0C,0xCA,0x45,0x40,0x4C,0x55,0x7C,0x01,0xB6,0x6B,0x33,0x1B,0x48,0x15,0x88,
+0x01,0x0A,0x54,0x64,0x80,0x11,0x64,0xDF,0xFF,0xFF,0x4C,0xBB,0x02,0x11,0x48,0x00,
+0x0C,0xD4,0x51,0xB2,0x00,0x11,0x48,0x08,0xB1,0x00,0x22,0xAB,0x08,0x49,0xFB,0xEB,
+0x00,0x0C,0xFC,0x43,0xFA,0x09,0x0C,0x38,0xB1,0x00,0x2A,0xAC,0x08,0x48,0x73,0x6E,
+0x04,0x0C,0xB1,0x33,0x00,0x0C,0x22,0x5E,0x00,0x0C,0x0E,0xC5,0x4A,0x00,0x0C,0xB8,
+0x02,0xE4,0xF5,0xEB,0xB0,0x00,0xC6,0xAF,0x00,0x11,0x94,0x88,0x01,0x52,0x14,0xB0,
+0xA1,0x0A,0xA2,0xF4,0x34,0x0A,0x0A,0x74,0x46,0x0A,0x96,0x74,0x39,0x0A,0xA0,0xF4,
+0x41,0x0A,0x62,0xF6,0x5F,0x0A,0x80,0x74,0x27,0x0A,0xF0,0x73,0x00,0x0C,0xF4,0xC3,
+0x18,0x48,0x15,0x88,0xFF,0x0A,0xF0,0x6B,0x01,0x11,0x94,0x80,0x20,0x52,0x14,0x08,
+0x5B,0x0A,0x14,0x28,0x01,0x0A,0x14,0x18,0x03,0x0A,0x14,0x10,0x00,0x48,0xF1,0x7B,
+0x01,0x0A,0x36,0xFC,0x46,0x3A,0x37,0x64,0x00,0x0C,0xF0,0x43,0x00,0x11,0x86,0x09,
+0x46,0x3A,0xF1,0xE3,0x01,0x48,0xF1,0xFB,0xB1,0x00,0x30,0xAB,0x00,0x0C,0xFA,0x43,
+0x00,0x0C,0xF0,0x43,0x02,0x11,0x94,0x80,0x77,0x52,0x14,0x88,0x88,0xB5,0x6B,0x0B,
+0x00,0xB5,0x6B,0x83,0x4A,0x0B,0xC0,0x3B,0x50,0xCA,0x95,0x83,0xB1,0x00,0x64,0xAC,
+0x02,0x11,0x4A,0x80,0x02,0x22,0xF8,0xB3,0x00,0x0C,0xFA,0x43,0x09,0x0A,0xBF,0x74,
+0x0A,0x0A,0xD9,0x74,0x04,0x1F,0xDF,0xFC,0x01,0x00,0x74,0xBE,0x0B,0x48,0xDF,0x7C,
+0x1B,0x48,0x15,0x88,0x08,0x0A,0xDE,0x6C,0x01,0x0A,0xEA,0xF4,0x02,0x0A,0xE4,0x74,
+0x03,0x0A,0xF0,0xF4,0x11,0x0A,0x08,0xF5,0x12,0x0A,0x0E,0xF5,0x13,0x0A,0x14,0xF5,
+0x19,0x0A,0xFC,0x74,0x1A,0x0A,0xF6,0x74,0x1B,0x0A,0x02,0xF5,0x17,0x11,0x2A,0x00,
+0x02,0x00,0x74,0x3A,0xFF,0x20,0x9C,0x3A,0xFF,0x09,0x70,0xBE,0x34,0x00,0x74,0x3A,
+0xFF,0x00,0x9C,0xBA,0x05,0x0A,0x70,0xBE,0x5F,0x00,0x74,0xBA,0xFF,0x20,0x9C,0x3A,
+0x1C,0x0A,0x70,0x3E,0x5F,0x20,0x74,0x3A,0xFF,0x20,0x9C,0x3A,0x1F,0x0A,0x70,0x3E,
+0x5F,0x00,0x74,0xBA,0xFF,0x00,0x9C,0xBA,0x1A,0x0A,0x70,0x3E,0x41,0x00,0x74,0xBA,
+0xFF,0x20,0x9C,0x3A,0x2A,0x0B,0x70,0xBE,0x41,0x20,0x74,0x3A,0xFF,0x20,0x9C,0x3A,
+0x2E,0x0B,0x70,0x3E,0x41,0x00,0x74,0xBA,0xFF,0x00,0x9C,0xBA,0x28,0x0B,0x70,0x3E,
+0x46,0x00,0x74,0x3A,0xFF,0x00,0x9C,0xBA,0x21,0x0A,0x70,0xBE,0x39,0x00,0x74,0xBA,
+0xFF,0x00,0x9C,0xBA,0x2D,0x0A,0x70,0xBE,0x03,0x00,0x74,0x3E,0x20,0x48,0x37,0x6D,
+0x40,0x4C,0x37,0xED,0x1B,0x48,0x15,0x88,0x08,0x0A,0x30,0x7D,0x40,0x01,0x18,0x38,
+0x01,0x0A,0x24,0xED,0x28,0x01,0x18,0xB8,0xB3,0x00,0x46,0x2A,0x00,0x0C,0x4E,0x4F,
+0x7F,0x9E,0x3D,0x8B,0x1B,0x48,0x15,0x88,0x01,0x0A,0xC4,0xEC,0xFF,0x11,0x22,0x8C,
+0x10,0x0A,0x4E,0x6F,0x01,0xB6,0x6B,0x33,0x00,0x0C,0xC4,0x44,0x1B,0x48,0x15,0x88,
+0x01,0x0A,0x3C,0x65,0x0A,0x0A,0x15,0x65,0x08,0x0A,0xDE,0xFC,0x7F,0x9E,0x3D,0x8B,
+0x00,0x0C,0x14,0x45,0x01,0x11,0x94,0x80,0x20,0x52,0x18,0x08,0x5B,0x0C,0x18,0x28,
+0x01,0x0C,0x18,0x18,0xB0,0x00,0x82,0xAF,0x00,0x0C,0xBE,0xC4,0x00,0x00,0x90,0x38,
+0x02,0x3A,0x5B,0x75,0x01,0x3A,0x45,0xF7,0x03,0x3A,0x45,0x77,0x02,0x3A,0xA1,0xB0,
+0x00,0x0C,0x5C,0x45,0x5F,0x00,0xA0,0x38,0x02,0x4E,0xA3,0x30,0x16,0x11,0xA0,0x30,
+0x16,0x11,0xA2,0xB0,0x00,0x11,0x90,0x08,0x41,0x50,0x4E,0x67,0x10,0x10,0x90,0x38,
+0x04,0x24,0xA1,0xB0,0x04,0x10,0xA2,0xB4,0x00,0x11,0x94,0x88,0x01,0x52,0x14,0xB0,
+0xA1,0x0A,0x90,0xF5,0x08,0xCE,0x95,0xFD,0x34,0x0A,0x94,0x75,0x41,0x0A,0x8C,0x65,
+0x02,0xA4,0x45,0x30,0x1F,0x54,0x14,0x08,0x00,0x0A,0x14,0x98,0x80,0x01,0x18,0x38,
+0x00,0x0C,0x18,0x98,0x02,0x12,0x40,0xB0,0xFF,0x21,0x9C,0x75,0x02,0x46,0x45,0x30,
+0x02,0x20,0x70,0x33,0x00,0x0C,0x98,0xC5,0x02,0xB8,0x41,0x30,0x00,0x0C,0x96,0x45,
+0xFF,0xFF,0x40,0xB8,0x00,0x0C,0x9A,0x45,0x02,0xA6,0x41,0x30,0xFF,0x21,0x9C,0x75,
+0x02,0x20,0x14,0x31,0x01,0x11,0x22,0x9C,0x01,0x10,0x22,0x1C,0x0F,0x11,0x94,0x00,
+0x01,0x52,0x6C,0xB3,0x04,0x4C,0xAF,0x6D,0x01,0x48,0xAF,0x7D,0x88,0xB6,0xAF,0xED,
+0x40,0x11,0x64,0xDF,0x00,0x11,0x94,0x88,0x14,0x52,0x20,0x32,0x02,0x11,0x94,0x80,
+0x01,0x52,0x6A,0xB3,0xFB,0xE4,0xC9,0x8B,0x01,0x11,0x94,0x80,0x80,0x52,0x4E,0x7F,
+0x04,0xE4,0xC9,0x87,0xB1,0x00,0x18,0x2C,0x80,0x48,0xC3,0x7D,0xB1,0x00,0x28,0x2C,
+0x00,0x0C,0xF4,0xC3,0xFA,0x09,0x0C,0x38,0x04,0xE4,0x0F,0x7D,0x08,0x48,0x5D,0xEC,
+0xA1,0x00,0x2A,0x2C,0xB1,0x00,0x18,0x2C,0x80,0x48,0xF5,0xEB,0x00,0x0C,0x1C,0xDE,
+0x00,0x0C,0x08,0xDD,0x00,0x0C,0xF4,0xC3,0xCC,0x01,0x1C,0x38,0xEC,0x11,0x5A,0x5F,
+0xB0,0x00,0x18,0x28,0xCC,0x01,0x1C,0x38,0xEF,0x11,0x4A,0xDF,0x00,0x0C,0x60,0x57,
+0xFB,0x1F,0x3F,0x8A,0xA0,0x00,0x6A,0xAB,0x11,0x11,0x02,0x00,0x10,0x02,0xE0,0x39,
+0xFF,0x11,0x22,0x20,0x04,0x11,0xD0,0x31,0x0F,0x02,0xE0,0xB9,0xFF,0x11,0x22,0x20,
+0x04,0x11,0xD0,0x31,0x00,0x00,0xE0,0x39,0x00,0x11,0xB0,0x88,0x32,0x11,0x00,0x00,
+0x02,0xF2,0x91,0x30,0x01,0x01,0x22,0x34,0x20,0x84,0x4E,0xFF,0x20,0x11,0x08,0x01,
+0x1C,0x11,0x66,0x47,0x01,0x9E,0x1D,0xB0,0x08,0x0E,0x0A,0xFE,0xB0,0x01,0x18,0x38,
+0xA3,0x00,0x46,0xAA,0xFF,0xA7,0x25,0x62,0x01,0x11,0x22,0x9C,0x03,0x0C,0x14,0x08,
+0xFF,0x0A,0x14,0x10,0x01,0x0A,0x14,0x18,0x04,0x0A,0x14,0x18,0x03,0x0A,0x14,0x8C,
+0x00,0x0C,0x0F,0x5E,0x22,0x0A,0x7E,0xAE,0x00,0x40,0x0F,0xDE,0xFC,0x3F,0x7F,0x0A,
+0x00,0x3F,0x7F,0x06,0xFF,0xFF,0x14,0x38,0x89,0xDA,0x2D,0xAE,0x00,0xE0,0x14,0xB8,
+0x89,0xD8,0x2D,0x2E,0x04,0xD8,0x81,0x36,0x00,0x20,0x80,0xBA,0x00,0x00,0x84,0x3E,
+0x02,0xCA,0x95,0x87,0x08,0x4C,0x29,0x6A,0x0B,0x0A,0x39,0x66,0x04,0x1F,0x29,0xFA,
+0x88,0xB5,0x3D,0xFE,0x01,0x10,0x22,0x1C,0x80,0xB5,0x6B,0x03,0x02,0x20,0x4C,0x33,
+0x01,0x11,0x22,0x9C,0x00,0x0C,0x46,0xDE,0x77,0xB5,0x6B,0x8F,0x08,0x48,0x4F,0xFE,
+0x02,0x0C,0x0C,0x30,0xB2,0x00,0x2E,0x2F,0x02,0x06,0x18,0x30,0xFF,0xFF,0x4C,0x3F,
+0x00,0x0C,0x42,0x5D,0x01,0x48,0x5D,0x6E,0x80,0x9E,0x3D,0x03,0x00,0x0C,0xB2,0x5D,
+0x04,0x94,0x50,0x32,0x00,0x0C,0xBA,0xC5,0x80,0x9E,0x3D,0x03,0x00,0x0C,0xB2,0x5D,
+0x00,0x0C,0xCA,0x45,0xFF,0xC0,0xF0,0x6B,0xFF,0xC1,0xF0,0xEB,0xF0,0xC2,0xF0,0xEB,
+0x02,0x48,0xF1,0xFB,0x01,0x11,0x94,0x80,0x20,0x52,0xF0,0x6B,0xB1,0x00,0x28,0x2C,
+0x00,0x0C,0xF0,0x43,0x04,0x28,0xB1,0x33,0xB2,0x00,0x22,0xAE,0xFF,0xFF,0x14,0x38,
+0x82,0x40,0xB1,0x2B,0x82,0x42,0xB5,0x2B,0x01,0x00,0x14,0xB8,0x83,0xD8,0xB1,0x2B,
+0x00,0x00,0x14,0x38,0x84,0xDA,0xB5,0xAB,0x02,0xD8,0x15,0xB0,0x83,0x28,0x51,0x2A,
+0x02,0xDA,0x15,0x30,0x84,0x2A,0x55,0xAA,0x28,0x01,0x18,0xB8,0xB3,0x00,0x46,0x2A,
+0x00,0x0C,0xF6,0x54,0x00,0x0C,0x0E,0xC5,0x02,0x11,0x4A,0x80,0x02,0xFC,0x45,0xB0,
+0x10,0x9E,0xD5,0xEE,0x00,0x11,0xFE,0x8B,0xFF,0xB4,0x15,0x90,0x01,0x0A,0xFC,0x1B,
+0x04,0x11,0x94,0x80,0x01,0x52,0xF6,0xB3,0xFF,0xFB,0xBF,0xFE,0x44,0xFF,0x0F,0xA8,
+0x01,0x07,0x14,0xB0,0x00,0xFB,0xC5,0x7E,0xFF,0x0A,0x14,0x10,0x00,0xFB,0xF7,0x0B,
+0x33,0xFF,0x17,0xA8,0x00,0x11,0x4A,0x88,0x00,0x0C,0xF4,0xDE,0x00,0x0C,0xBA,0x4E,
+0xB0,0x00,0x52,0xAD,0x02,0x11,0x4A,0x80,0xFF,0xFB,0xC5,0xEE,0xF8,0xFF,0xFF,0x8B,
+0x08,0xFF,0xFF,0x1B,0x00,0x0C,0xC6,0x46,0x01,0xFF,0xFF,0x1B,0x01,0xFE,0x15,0x30,
+0x00,0xFF,0x15,0x18,0x80,0x0A,0xD0,0xFE,0x07,0xFF,0xA7,0x6E,0x00,0x0C,0xA2,0xC6,
+0x00,0x11,0x4A,0x88,0x01,0xB5,0xEF,0x7E,0x00,0x11,0x4A,0x88,0xB1,0x00,0xAE,0xA9,
+0x02,0x0C,0x1C,0x98,0x10,0x9E,0xE9,0xEE,0xF2,0x11,0x26,0x80,0x10,0x9E,0x3D,0x03,
+0x04,0x11,0x60,0x33,0xF5,0x45,0x8A,0x08,0xF5,0xCE,0x9D,0x0B,0x00,0x0C,0xEA,0xC6,
+0xF3,0x11,0x26,0x00,0x02,0x22,0x26,0xB0,0x00,0x0A,0x54,0x5F,0x18,0x11,0xB8,0x80,
+0xEF,0xCA,0x95,0x8B,0xA0,0x00,0x5C,0x2A,0x00,0x0C,0x38,0x5F,0x00,0x12,0x24,0x7A,
+0xFF,0x0C,0x18,0x98,0x00,0x12,0x26,0x90,0x02,0x22,0x0C,0x30,0x02,0xA4,0x45,0x30,
+0x2E,0x0B,0x14,0x28,0x80,0x01,0x18,0x38,0x00,0x0C,0x18,0x98,0x02,0x0C,0x1C,0xB0,
+0x02,0x12,0x40,0xB0,0xFF,0xFF,0x26,0xB8,0x02,0x06,0x44,0x30,0x02,0x0A,0x0C,0x30,
+0xB1,0x00,0x9C,0x2C,0x02,0x06,0x14,0x30,0x00,0x0C,0x4E,0x4F,0xF8,0x1C,0x15,0x88,
+0x00,0x0B,0x1E,0xE7,0x02,0x46,0x15,0x30,0x8A,0x22,0x28,0xAA,0x19,0x11,0x2A,0x80,
+0x08,0x9E,0x25,0xFF,0x02,0x45,0x8A,0x84,0xF5,0x45,0x8A,0x8C,0xF8,0x1C,0x17,0x08,
+0xB2,0x00,0x38,0xAF,0x00,0x12,0x26,0x00,0x00,0x0C,0x36,0xC7,0xF8,0x1C,0x17,0x08,
+0xB2,0x00,0x38,0xAF,0xFF,0x0A,0x14,0x10,0x00,0x12,0x26,0x88,0xFF,0x11,0x22,0x8C,
+0x6A,0x0B,0x14,0x28,0xB0,0x01,0x18,0x38,0x00,0x0C,0x18,0x98,0x02,0x0C,0x1C,0xB0,
+0x11,0x0B,0x0E,0xA8,0x01,0x07,0x14,0x34,0x00,0x00,0x90,0x38,0x18,0x11,0xA0,0xB0,
+0x18,0x10,0xA2,0x34,0xA1,0x00,0x2E,0xAA,0xA1,0x00,0x2C,0x2A,0xFF,0x11,0x22,0x8C,
+0xA1,0x00,0x16,0x2A,0xA3,0x00,0x54,0xAA,0xA3,0x00,0x34,0xAA,0xA1,0x00,0x0C,0xA9,
+0xA1,0x00,0x86,0x2A,0xA1,0x00,0x18,0xAA,0xA1,0x00,0x52,0x2A,0xA1,0x00,0x4C,0x2A,
+0xA0,0x00,0x06,0xA8,0xA3,0x00,0xDE,0x2A,0xA3,0x00,0x14,0xAB,0xA3,0x00,0xBE,0x2A,
+0x00,0x11,0x02,0x88,0x01,0x0C,0x8C,0x31,0x01,0x01,0x22,0x34,0x11,0x11,0x02,0x00,
+0xF5,0x45,0x8A,0x08,0xF7,0xA0,0x40,0x89,0xFB,0x9E,0x3D,0x8B,0x00,0x00,0x90,0x3B,
+0xC0,0xCA,0x95,0x0B,0x02,0x05,0x0A,0x00,0x40,0x3D,0x7A,0x00,0xFF,0xE0,0xC0,0x81,
+0xB3,0x00,0x88,0xAA,0x01,0x01,0x22,0xB0,0x01,0xE4,0xC9,0x87,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+0x00,0x0C,0x26,0x59,0x00,0x0C,0x06,0xD0,0xA0,0x00,0xFA,0xAE,0x00,0x0C,0x40,0x59,
+0x02,0x86,0xA9,0xB3,0xFF,0xD5,0x1F,0x70,0x02,0xD4,0x45,0xB0,0x84,0x01,0x18,0xB8,
+0x00,0x0C,0xB6,0xDB,0x01,0x0A,0x1A,0xE0,0x44,0xC6,0x0F,0xA8,0x01,0x07,0x14,0xB0,
+0x00,0x9C,0x69,0x68,0x02,0xAE,0xA9,0xB3,0x00,0x0C,0x0A,0x40,0x00,0x0C,0x3C,0xD9,
+0x02,0x84,0x15,0xB0,0x8B,0x10,0x72,0x28,0x83,0x10,0x08,0x2B,0x02,0x80,0xA9,0xB3,
+0x02,0xD4,0x45,0xB0,0x02,0xAE,0x15,0x30,0xFF,0xFF,0x5C,0x3B,0x00,0x00,0x44,0x38,
+0x02,0x0A,0x00,0x33,0xFF,0x0B,0x36,0x60,0x02,0x0A,0x04,0xB3,0x00,0x0C,0x52,0x59,
+0xFF,0x87,0x3F,0xE0,0x02,0xD4,0x0D,0xB3,0x00,0x0C,0x44,0x40,0x02,0x88,0x45,0xB0,
+0x02,0xD4,0x5D,0xB3,0x00,0x00,0x44,0x38,0x02,0xD4,0x11,0x33,0x02,0xD4,0x45,0xB0,
+0x00,0x11,0xB2,0x89,0x0F,0xD8,0x00,0x0B,0xF0,0xD8,0x02,0x8B,0x0C,0x11,0xB2,0x01,
+0x08,0xD8,0x08,0xB3,0x08,0xD8,0x28,0x33,0x00,0x0C,0x20,0x5A,0x01,0x0A,0x38,0xB3,
+0x01,0x0B,0x54,0x33,0x00,0x0C,0x78,0x58,0x04,0x9D,0x3B,0x03,0x01,0x11,0x14,0x00,
+0x00,0x9C,0x65,0xF8,0x01,0xA8,0x51,0x1B,0x11,0x0A,0x14,0xA8,0xFF,0x0A,0x60,0x68,
+0x02,0x11,0xB2,0x81,0x02,0xD8,0x04,0xB3,0x00,0x0C,0x56,0xD9,0x02,0xD4,0x45,0xB0,
+0xA0,0x00,0xC4,0x2E,0x00,0x0C,0x56,0xD9,0x00,0x0C,0x52,0x59,0xA0,0x00,0xFC,0xAE,
+0x00,0x00,0x44,0x38,0x02,0x86,0x15,0x30,0xFF,0x0B,0xA0,0xF0,0x8A,0xD4,0x9B,0xA8,
+0x02,0x0A,0x0C,0x30,0x02,0x0A,0x44,0x30,0x01,0x9C,0x15,0xB0,0x02,0xD4,0x45,0xB0,
+0x00,0x9C,0x99,0xF8,0x02,0x06,0x44,0x30,0x02,0xA6,0xAD,0xB3,0x08,0xB4,0xB1,0x33,
+0x02,0xD4,0x45,0xB0,0x02,0xD6,0x4D,0xB3,0x08,0xD8,0x69,0x33,0x00,0x11,0x6E,0x0F,
+0x02,0x06,0x14,0x30,0x02,0x0A,0x44,0x30,0x02,0xAE,0x15,0x30,0x00,0x0C,0x7C,0xC0,
+0x02,0xD4,0x45,0x34,0x02,0x22,0x0C,0x30,0x00,0x00,0x44,0x38,0x01,0x00,0x18,0xB8,
+0x02,0x8C,0x15,0x30,0x83,0x0C,0x04,0xA8,0xE3,0x8C,0xB1,0xA8,0x00,0x0C,0xB4,0x40,
+0x02,0x8C,0x19,0x33,0x00,0x0C,0xA8,0xC0,0x02,0x06,0x44,0x30,0x22,0x11,0x02,0x00,
+0x02,0x0A,0xEC,0xB3,0x01,0x01,0x22,0xB0,0xB1,0x00,0x64,0xAC,0x61,0x0C,0xC0,0xBB,
+0x40,0xCA,0x95,0x87,0x08,0x84,0x49,0x31,0x02,0x22,0x48,0xB1,0x40,0xCE,0xCF,0xF8,
+0x22,0x11,0x02,0x00,0x02,0xF6,0x15,0xB0,0x01,0x01,0x22,0xB0,0x02,0x0A,0x48,0xB1,
+0xFC,0xFF,0x14,0x38,0x83,0x90,0x48,0xA9,0x02,0x11,0x48,0xB1,0xB1,0x00,0xB2,0x29,
+0x01,0x0A,0xC4,0xB3,0x02,0x0C,0x0C,0x30,0x00,0x0C,0x0A,0xD9,0x02,0x06,0x18,0x30,
+0x0C,0x00,0x14,0x38,0x83,0x90,0x14,0x28,0xB1,0x00,0xEA,0xA9,0x20,0x11,0xB8,0x00,
+0xB1,0x00,0x06,0x2A,0x02,0x0A,0x0C,0x30,0x02,0x0C,0x1C,0x98,0x28,0xC6,0x27,0x80,
+0x55,0x11,0x02,0x00,0x01,0x43,0xF9,0x68,0x08,0xCC,0xF9,0xE8,0x09,0x11,0xFA,0x40,
+0x08,0x11,0xFA,0xC0,0x01,0x01,0x22,0xB0,0x30,0xCC,0x15,0x88,0x80,0x0A,0x14,0x00,
+0x00,0x0C,0x26,0x00,0x02,0x06,0x26,0xB0,0xB1,0x00,0x58,0xAC,0x00,0xE2,0x35,0xDA,
+0xA0,0x00,0x5C,0x2A,0x00,0x0C,0x26,0x59,0x00,0x0C,0xDC,0xD2,0x80,0x45,0xDC,0xFA,
+0x01,0xC9,0xDD,0x6A,0x00,0x11,0x8C,0x09,0x53,0x11,0x3A,0xD9,0x01,0xC9,0x93,0x87,
+0x02,0x0A,0x0C,0x30,0x00,0x0C,0x26,0x59,0x00,0x0C,0x22,0x51,0x00,0x0C,0x14,0xD9,
+0x00,0x11,0x18,0x08,0x02,0x06,0x14,0x30,0xA1,0x00,0x02,0x29,0x02,0x22,0x0C,0x30,
+0x00,0x00,0x44,0x38,0x02,0x90,0x15,0xB0,0x82,0x10,0x14,0x28,0x01,0x10,0x22,0x98,
+0x84,0x11,0x14,0xA8,0x83,0x8E,0x15,0xA8,0x02,0x06,0x44,0x30,0x80,0x0B,0x80,0xEB,
+0x01,0x11,0x22,0x9C,0xA1,0x00,0x7E,0xAA,0x00,0x80,0x18,0xB8,0x00,0x0C,0x42,0xC1,
+0x00,0x40,0x18,0xB8,0x00,0x00,0x44,0x38,0x02,0x8A,0x15,0x30,0x89,0x0C,0x44,0xA9,
+0x80,0x0C,0x04,0xA8,0xE3,0x8A,0x4F,0xA9,0xFF,0x11,0x22,0x8C,0x02,0x8A,0x15,0x33,
+0x00,0x0C,0x44,0xC1,0xFF,0x7F,0x18,0xB8,0x00,0x0C,0x58,0x41,0xFF,0xBF,0x18,0xB8,
+0x00,0x00,0x44,0x38,0x02,0x8A,0x15,0x30,0x81,0x0C,0x04,0x28,0xE3,0x8A,0x63,0x29,
+0xFF,0x11,0x22,0x8C,0x02,0x8A,0x15,0x33,0x00,0x0C,0x5A,0xC1,0x80,0xCC,0x6B,0xE9,
+0x10,0x4E,0x6D,0xE9,0x29,0x11,0x2A,0x80,0x02,0xA6,0x15,0xB0,0x8B,0x10,0xDC,0x2A,
+0x16,0x11,0x34,0xC2,0x04,0x9F,0x7B,0xF9,0x02,0xAC,0x15,0xB0,0x89,0x10,0x7A,0xA9,
+0x04,0x9F,0x79,0xE9,0x03,0xB4,0xB1,0xB3,0x00,0x11,0xB6,0x8B,0x02,0xB8,0xA5,0x33,
+0x22,0x11,0x02,0x00,0x02,0xA2,0xE8,0xB3,0x02,0xD2,0x45,0x31,0xFF,0xD8,0x91,0x69,
+0xFF,0xD9,0x91,0xE9,0xFF,0xDA,0x91,0xE9,0xF7,0x11,0x30,0x5A,0x01,0x01,0x22,0x34,
+0x04,0xD8,0x51,0xB0,0x02,0x11,0x4A,0x80,0x04,0x28,0xE8,0x33,0x00,0x0C,0xAC,0xC1,
+0x22,0x11,0x02,0x00,0x02,0x11,0x4A,0x80,0x80,0xF7,0xAD,0xF9,0x03,0xB4,0x51,0x30,
+0x00,0x11,0x56,0x08,0x04,0xF4,0x51,0x30,0xFF,0x11,0x22,0x20,0x80,0x2B,0xAC,0xF9,
+0x00,0x11,0xEE,0x0B,0x80,0xC9,0x93,0x03,0x00,0x11,0x4A,0x88,0x01,0x01,0x22,0x34,
+0xC0,0x5F,0x15,0x88,0xC0,0x0A,0xCA,0xF1,0x18,0x10,0x95,0xB1,0x18,0x00,0x14,0x38,
+0x83,0x58,0x99,0xA8,0x04,0x11,0x4E,0x5A,0x00,0x11,0x4E,0xDA,0x10,0x50,0xC1,0xB0,
+0x10,0x01,0xBC,0x3C,0x07,0x11,0x94,0x01,0x13,0x11,0x95,0xB1,0x04,0x11,0x94,0x31,
+0x00,0x0C,0xCC,0xC1,0x18,0x10,0x95,0xB1,0x18,0x11,0x94,0xB1,0x30,0x11,0x54,0x5A,
+0x10,0x01,0xBC,0x3C,0x04,0x30,0xB0,0xB3,0x02,0xDA,0x41,0xB0,0x0F,0xCB,0xFF,0x69,
+0x01,0xCD,0xFD,0xF9,0x80,0xCC,0xFD,0xE9,0x40,0xCE,0xFD,0xF9,0x02,0x44,0x15,0xB0,
+0x88,0xF6,0xFD,0xA9,0x11,0x11,0x00,0x80,0x02,0xDA,0x41,0xB0,0x02,0x46,0x45,0x30,
+0x00,0x00,0x90,0xB9,0x10,0x00,0xBC,0x38,0x04,0x58,0x99,0xB0,0x10,0x50,0xC1,0xB0,
+0x00,0x11,0xBE,0xDA,0x20,0x13,0x08,0x39,0x49,0x11,0xB8,0x00,0x00,0x0C,0x5C,0xDA,
+0x80,0xCA,0x95,0x03,0x02,0x0D,0xC0,0xBF,0x27,0x11,0x00,0xC2,0x09,0x11,0x00,0xC2,
+0x00,0x0C,0xBE,0xDA,0xA1,0x00,0xA8,0xAE,0xB0,0x00,0x18,0x28,0x02,0x05,0x0A,0x00,
+0x0F,0xCB,0x13,0xEA,0x80,0xC8,0x19,0x6A,0x08,0x5D,0x32,0xFA,0x10,0xC9,0x93,0x03,
+0x00,0x0C,0x1C,0x42,0x08,0x11,0x18,0x00,0xB0,0x00,0xB6,0xAD,0x09,0x11,0x1A,0x42,
+0x05,0x11,0x1A,0x42,0x00,0x0C,0xBE,0xDA,0x15,0x11,0x34,0xDA,0xA0,0x00,0x2C,0x2D,
+0x02,0x22,0x0C,0x30,0x00,0x00,0x44,0x38,0xB8,0x01,0x18,0xB8,0x01,0xC6,0x15,0xB0,
+0x00,0x0C,0x18,0x98,0x01,0x12,0x14,0x30,0x01,0x96,0x17,0x30,0xA1,0x00,0xB8,0xAF,
+0xA1,0x00,0x52,0x2A,0xA0,0x00,0x06,0xA8,0x01,0x0C,0xD6,0xB3,0x02,0x20,0xD8,0xB3,
+0xB0,0x00,0x18,0x28,0x02,0x05,0x0A,0x00,0x04,0x3A,0x32,0x7A,0x01,0xEB,0x69,0x30,
+0x01,0xFA,0x6B,0xB0,0x02,0xEC,0x6D,0x30,0xFD,0x05,0x0A,0x0C,0x00,0x00,0x14,0x38,
+0x88,0x12,0x80,0x2B,0x88,0x12,0x80,0x2B,0x01,0x11,0x22,0x9C,0x01,0x0C,0xF6,0x30,
+0x01,0x0A,0xF0,0x30,0x02,0x11,0xF2,0x34,0x01,0x0C,0x98,0xB0,0x03,0x11,0x9A,0xB0,
+0x01,0x0C,0x14,0x30,0x04,0x11,0x4E,0x42,0x01,0x11,0x02,0x80,0x02,0x22,0x44,0x30,
+0x21,0x11,0x00,0x80,0x00,0x0C,0x6A,0xC2,0x10,0x11,0x02,0x80,0x02,0x22,0x44,0x30,
+0x20,0x11,0x00,0x00,0x02,0x22,0x44,0x30,0x01,0x01,0x22,0x34,0x82,0x10,0x14,0x28,
+0x01,0x10,0x22,0x98,0x84,0x11,0x14,0xA8,0x02,0x0A,0x0C,0x30,0xFF,0xFF,0x14,0x38,
+0x84,0x11,0x1C,0x28,0x02,0x06,0x14,0x30,0x83,0x0C,0x18,0x28,0x00,0x00,0x14,0x38,
+0x84,0x0E,0x1C,0x2C,0x09,0x10,0x00,0xB1,0x01,0x11,0x74,0x00,0x00,0x0C,0xA4,0x42,
+0x00,0x0C,0x8C,0x5A,0x00,0x0C,0x96,0xC2,0x00,0x0C,0x9A,0xDA,0x10,0x11,0x06,0x81,
+0x04,0x11,0x0E,0x01,0x01,0x11,0x10,0x85,0x00,0x0C,0x9A,0xDA,0x80,0x11,0x08,0x01,
+0x08,0x11,0x10,0x85,0xFF,0x11,0x00,0x01,0xF0,0xEF,0x04,0xB9,0x7F,0xFF,0x08,0xB9,
+0xFF,0xFB,0x0C,0x39,0x10,0x11,0x10,0x01,0x00,0x11,0x02,0x88,0x00,0x0C,0xB0,0x5A,
+0x11,0x11,0x00,0x80,0x00,0x0C,0xB8,0xDA,0x01,0x01,0x22,0x34,0x11,0x00,0xB8,0xF2,
+0xFF,0x11,0x8C,0x00,0xFF,0x11,0x8E,0x80,0x7F,0x03,0x24,0x39,0x02,0x11,0x74,0x84,
+0xFF,0x11,0x8C,0x00,0xFF,0x11,0x8E,0x80,0x02,0x11,0x74,0x84,0x00,0x0C,0xCE,0x5A,
+0x02,0x4C,0x15,0x30,0x01,0x0A,0x04,0x80,0x01,0x0C,0x06,0x30,0xE3,0x4C,0xCB,0xAA,
+0x00,0x0C,0xD8,0xC2,0x02,0x4C,0x99,0x32,0x00,0x0C,0xC0,0xC2,0x02,0x06,0x14,0x30,
+0x01,0x05,0x0C,0x30,0x00,0x11,0x0C,0x08,0x02,0x05,0x0A,0x00,0x02,0x0A,0x0C,0xB4,
+0x01,0x06,0x0A,0x30,0x01,0x06,0x22,0x30,0xFF,0x11,0x22,0x8C,0x01,0x0C,0x1A,0xB0,
+0x00,0x11,0xE4,0xC2,0x00,0x11,0x1A,0x88,0x00,0x0C,0xCE,0x5A,0x02,0x48,0x15,0xB0,
+0x80,0x0C,0x04,0xA8,0xE3,0x48,0xEF,0x2A,0x00,0x0C,0xD8,0xC2,0x02,0x48,0x91,0x32,
+0x00,0x0C,0xE6,0x42,0x01,0x0C,0x1A,0xB0,0xFF,0x11,0xF6,0xC2,0x00,0x0C,0xCE,0x5A,
+0x02,0x48,0x15,0xB0,0x81,0x0C,0x04,0x28,0xE3,0x48,0x01,0xAB,0x00,0x0C,0xD8,0xC2,
+0x02,0x48,0x91,0x32,0x00,0x0C,0xF8,0x42,0x00,0x11,0x1A,0x88,0x00,0x0C,0xCE,0x5A,
+0x02,0x48,0x15,0xB0,0x82,0x0C,0x04,0x28,0xE3,0x48,0x11,0x2B,0x00,0x0C,0xD8,0xC2,
+0x02,0x48,0x91,0x32,0x00,0x0C,0x08,0xC3,0x00,0x11,0x1A,0x88,0x00,0x0C,0xCE,0x5A,
+0x02,0x4C,0x15,0x30,0x80,0x0C,0x04,0xA8,0xE3,0x4C,0x21,0xAB,0x00,0x0C,0xD8,0xC2,
+0x02,0x4C,0x99,0x32,0x00,0x0C,0x18,0x43,0x00,0x0C,0xCE,0x5A,0x02,0x4C,0x15,0x30,
+0x81,0x0C,0x04,0x28,0xE3,0x4C,0x2F,0x2B,0x00,0x0C,0xD8,0xC2,0x02,0x4C,0x99,0x32,
+0x00,0x0C,0x26,0xC3,0x00,0x0C,0xCE,0x5A,0x02,0x22,0x0C,0x30,0x00,0x00,0x44,0x38,
+0x44,0xC6,0x0F,0xA8,0x01,0x07,0x1C,0x30,0x02,0xAE,0x19,0x30,0xFF,0x0E,0x14,0x90,
+0x00,0x0C,0xB0,0x8B,0x01,0x0D,0xB2,0xB3,0x0F,0xCB,0x49,0xFB,0x00,0xD9,0xB3,0x0B,
+0x01,0x0E,0x14,0xB0,0x03,0xC8,0x53,0xEB,0xC0,0xCA,0x53,0x6B,0x30,0xCB,0x53,0xEB,
+0x00,0xD8,0xB1,0x83,0x02,0x0C,0x14,0x30,0x02,0xD8,0x05,0x30,0xE3,0xAE,0x5D,0x2B,
+0x02,0x06,0x44,0x30,0x00,0x0C,0xD8,0xC2,0x02,0xAE,0x5D,0x33,0x00,0x0C,0x3C,0x43,
+0x02,0x48,0x15,0xB0,0x02,0x0B,0x60,0x6B,0x01,0x0A,0x04,0xB0,0x02,0x0B,0x06,0x80,
+0xE3,0x48,0x6D,0xAB,0xFF,0x11,0x22,0x8C,0x02,0x48,0x91,0x32,0x00,0x0C,0x60,0x43,
+0xFF,0xFD,0x18,0xB8,0x00,0x0C,0xF6,0xC2,0x00,0x0C,0x60,0x5B,0x40,0x49,0x7F,0xEB,
+0x00,0x0C,0x82,0x5B,0x00,0x0C,0x70,0xDB,0x01,0x11,0x22,0x9C,0x00,0x0C,0x70,0xDB,
+0x01,0x10,0x22,0x1C,0x02,0x05,0x0A,0x00,0x02,0x4A,0x15,0x30,0x01,0x0A,0x04,0xB0,
+0x01,0x0B,0x06,0x98,0xE3,0x4A,0x8F,0x2B,0x00,0x0C,0xB2,0x43,0x02,0x4A,0x95,0x32,
+0x00,0x0C,0x84,0x43,0x02,0x05,0x0A,0x00,0x02,0x4A,0x15,0x30,0x01,0x0A,0x04,0xB0,
+0xFF,0x0B,0x06,0x18,0xE3,0x4A,0x9F,0xAB,0x00,0x0C,0xB2,0x43,0x02,0x4A,0x95,0x32,
+0x00,0x0C,0x94,0xC3,0x02,0x05,0x0A,0x00,0x02,0x4A,0x15,0x30,0xFF,0x0A,0x04,0x18,
+0x01,0x0B,0x06,0xB0,0xE3,0x4A,0xAF,0xAB,0x00,0x0C,0xB2,0x43,0x02,0x4A,0x95,0x32,
+0x00,0x0C,0xA4,0xC3,0xF0,0x04,0xDC,0xEA,0xFD,0x05,0x0A,0x0C,0x0C,0x11,0xB2,0x01,
+0x01,0x12,0x14,0x30,0x00,0xD8,0xC2,0x63,0x1F,0xD9,0x14,0x08,0x14,0x0A,0xB8,0xE3,
+0x01,0x11,0x14,0x84,0xFF,0xD9,0xB2,0x99,0xFF,0x0C,0x18,0x98,0xFF,0x12,0x14,0x10,
+0xFF,0x11,0x16,0x00,0x01,0x10,0x22,0x98,0x84,0x11,0x14,0xA8,0x01,0xD8,0xB0,0xB3,
+0x00,0x11,0xB2,0x0B,0x83,0xD8,0x15,0xA8,0x80,0x0B,0xD8,0x6B,0x02,0x11,0x14,0x84,
+0x00,0x11,0x14,0x8C,0xB3,0x00,0xCE,0x2A,0x02,0xA8,0x15,0x30,0x01,0x0A,0x04,0xB0,
+0x01,0x0B,0x06,0x98,0xE3,0xA8,0xE7,0xAB,0xA3,0x00,0xD8,0x2A,0x02,0xA8,0x51,0x33,
+0x00,0x0C,0xDC,0xC3,0x04,0x9D,0xDD,0x7A,0xB3,0x00,0xCE,0x2A,0x02,0xA8,0x15,0x30,
+0x01,0x0A,0x04,0xB0,0xFF,0x0B,0x06,0x18,0xE3,0xA8,0xF9,0xAB,0xA3,0x00,0xD8,0x2A,
+0x02,0xA8,0x51,0x33,0x00,0x0C,0xEE,0x43,0x00,0x00,0x01,0xB8,0x00,0x00,0x01,0xB8,
+};
diff -urN oldtree/drivers/scsi/aic94xx/aic94xx_task.c newtree/drivers/scsi/aic94xx/aic94xx_task.c
--- oldtree/drivers/scsi/aic94xx/aic94xx_task.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/aic94xx_task.c	2006-02-13 19:20:00.601420872 -0500
@@ -0,0 +1,645 @@
+/*
+ * Aic94xx SAS/SATA Tasks
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/aic94xx/aic94xx_task.c#51 $
+ */
+
+#include <linux/spinlock.h>
+#include <scsi/sas/sas_task.h>
+#include <scsi/sas/sas_frames.h>
+#include "aic94xx.h"
+#include "aic94xx_sas.h"
+#include "aic94xx_hwi.h"
+
+static void asd_unbuild_ata_ascb(struct asd_ascb *a);
+static void asd_unbuild_smp_ascb(struct asd_ascb *a);
+static void asd_unbuild_ssp_ascb(struct asd_ascb *a);
+
+static inline void asd_can_dequeue(struct asd_ha_struct *asd_ha, int num)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags);
+	asd_ha->seq.can_queue += num;
+	spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);
+}
+
+/* PCI_DMA_... to our direction translation.
+ */
+static const u8 data_dir_flags[] = {
+	[PCI_DMA_BIDIRECTIONAL] = DATA_DIR_BYRECIPIENT,	/* UNSPECIFIED */
+	[PCI_DMA_TODEVICE]      = DATA_DIR_OUT, /* OUTBOUND */
+	[PCI_DMA_FROMDEVICE]    = DATA_DIR_IN, /* INBOUND */
+	[PCI_DMA_NONE]          = DATA_DIR_NONE, /* NO TRANSFER */
+};
+
+static inline int asd_map_scatterlist(struct sas_task *task,
+				      struct sg_el *sg_arr,
+				      unsigned long gfp_flags)
+{
+	struct asd_ascb *ascb = task->lldd_task;
+	struct asd_ha_struct *asd_ha = ascb->ha;
+	struct scatterlist *sc;
+	int num_sg, res;
+
+	if (task->data_dir == PCI_DMA_NONE)
+		return 0;
+
+	if (task->num_scatter == 0) {
+		void *p = task->scatter;
+		dma_addr_t dma = pci_map_single(asd_ha->pcidev, p,
+						task->total_xfer_len,
+						task->data_dir);
+		sg_arr[0].bus_addr = cpu_to_le64((u64)dma);
+		sg_arr[0].size = cpu_to_le32(task->total_xfer_len);
+		sg_arr[0].flags |= ASD_SG_EL_LIST_EOL;
+		return 0;
+	}
+
+	num_sg = pci_map_sg(asd_ha->pcidev, task->scatter, task->num_scatter,
+			    task->data_dir);
+	if (num_sg == 0)
+		return -ENOMEM;
+
+	if (num_sg > 3) {
+		int i;
+
+		ascb->sg_arr = asd_alloc_coherent(asd_ha,
+						  num_sg*sizeof(struct sg_el),
+						  gfp_flags);
+		if (!ascb->sg_arr) {
+			res = -ENOMEM;
+			goto err_unmap;
+		}
+		for (sc = task->scatter, i = 0; i < num_sg; i++, sc++) {
+			struct sg_el *sg =
+				&((struct sg_el *)ascb->sg_arr->vaddr)[i];
+			sg->bus_addr = cpu_to_le64((u64)sg_dma_address(sc));
+			sg->size = cpu_to_le32((u32)sg_dma_len(sc));
+			if (i == num_sg-1)
+				sg->flags |= ASD_SG_EL_LIST_EOL;
+		}
+
+		for (sc = task->scatter, i = 0; i < 2; i++, sc++) {
+			sg_arr[i].bus_addr =
+				cpu_to_le64((u64)sg_dma_address(sc));
+			sg_arr[i].size = cpu_to_le32((u32)sg_dma_len(sc));
+		}
+		sg_arr[1].next_sg_offs = 2 * sizeof(*sg_arr);
+		sg_arr[1].flags |= ASD_SG_EL_LIST_EOS;
+
+		memset(&sg_arr[2], 0, sizeof(*sg_arr));
+		sg_arr[2].bus_addr=cpu_to_le64((u64)ascb->sg_arr->dma_handle);
+	} else {
+		int i;
+		for (sc = task->scatter, i = 0; i < num_sg; i++, sc++) {
+			sg_arr[i].bus_addr =
+				cpu_to_le64((u64)sg_dma_address(sc));
+			sg_arr[i].size = cpu_to_le32((u32)sg_dma_len(sc));
+		}
+		sg_arr[i-1].flags |= ASD_SG_EL_LIST_EOL;
+	}
+
+	return 0;
+err_unmap:
+	pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter,
+		     task->data_dir);
+	return res;
+}
+
+static inline void asd_unmap_scatterlist(struct asd_ascb *ascb)
+{
+	struct asd_ha_struct *asd_ha = ascb->ha;
+	struct sas_task *task = ascb->uldd_task;
+
+	if (task->data_dir == PCI_DMA_NONE)
+		return;
+
+	if (task->num_scatter == 0) {
+		dma_addr_t dma = (dma_addr_t)
+		       le64_to_cpu(ascb->scb->ssp_task.sg_element[0].bus_addr);
+		pci_unmap_single(ascb->ha->pcidev, dma, task->total_xfer_len,
+				 task->data_dir);
+		return;
+	}
+
+	asd_free_coherent(asd_ha, ascb->sg_arr);
+	pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter,
+		     task->data_dir);
+}
+
+/* ---------- Task complete tasklet ---------- */
+
+static void asd_get_response_tasklet(struct asd_ascb *ascb,
+				     struct done_list_struct *dl)
+{
+	struct asd_ha_struct *asd_ha = ascb->ha;
+	struct sas_task *task = ascb->uldd_task;
+	struct task_status_struct *ts = &task->task_status;
+	unsigned long flags;
+	struct tc_resp_sb_struct {
+		__le16 index_escb;
+		u8     len_lsb;
+		u8     flags;
+	} __attribute__ ((packed)) *resp_sb = (void *) dl->status_block;
+
+/* 	int  size   = ((resp_sb->flags & 7) << 8) | resp_sb->len_lsb; */
+	int  edb_id = ((resp_sb->flags & 0x70) >> 4)-1;
+	struct asd_ascb *escb;
+	struct asd_dma_tok *edb;
+	void *r;
+
+	spin_lock_irqsave(&asd_ha->seq.tc_index_lock, flags);
+	escb = asd_tc_index_find(&asd_ha->seq,
+				 (int)le16_to_cpu(resp_sb->index_escb));
+	spin_unlock_irqrestore(&asd_ha->seq.tc_index_lock, flags);
+
+	if (!escb) {
+		ASD_DPRINTK("Uh-oh! No escb for this dl?!\n");
+		return;
+	}
+
+	ts->buf_valid_size = 0;
+	edb = asd_ha->seq.edb_arr[edb_id + escb->edb_index];
+	r = edb->vaddr;
+	if (task->task_proto == SAS_PROTO_SSP) {
+		struct ssp_response_iu *iu =
+			r + 16 + sizeof(struct ssp_frame_hdr);
+
+		ts->residual = le32_to_cpu(*(__le32 *)r);
+		ts->resp = SAS_TASK_COMPLETE;
+		if (iu->datapres == 0)
+			ts->stat = iu->status;
+		else if (iu->datapres == 1)
+			ts->stat = iu->resp_data[3];
+		else if (iu->datapres == 2) {
+			ts->stat = SAM_CHECK_COND;
+			ts->buf_valid_size = min((u32) SAS_STATUS_BUF_SIZE,
+					 be32_to_cpu(iu->sense_data_len));
+			memcpy(ts->buf, iu->sense_data, ts->buf_valid_size);
+			if (iu->status != SAM_CHECK_COND) {
+				ASD_DPRINTK("device %llx sent sense data, but "
+					    "stat(0x%x) is not CHECK_CONDITION"
+					    "\n",
+					    SAS_ADDR(task->dev->sas_addr),
+					    ts->stat);
+			}
+		}
+	}  else {
+		struct ata_task_resp *resp = (void *) &ts->buf[0];
+
+		ts->residual = le32_to_cpu(*(__le32 *)r);
+
+		if (SAS_STATUS_BUF_SIZE >= sizeof(*resp)) {
+			resp->frame_len = le16_to_cpu(*(__le16 *)(r+6));
+			memcpy(&resp->ending_fis[0], r+16, 24);
+			ts->buf_valid_size = sizeof(*resp);
+		}
+	}
+
+	asd_invalidate_edb(escb, edb_id);
+}
+
+static void asd_task_tasklet_complete(struct asd_ascb *ascb,
+				      struct done_list_struct *dl)
+{
+	struct sas_task *task = ascb->uldd_task;
+	struct task_status_struct *ts = &task->task_status;
+	unsigned long flags;
+	u8 opcode = dl->opcode;
+
+	asd_can_dequeue(ascb->ha, 1);
+
+Again:
+	switch (opcode) {
+	case TC_NO_ERROR:
+		ts->resp = SAS_TASK_COMPLETE;
+		ts->stat = SAM_GOOD;
+		break;
+	case TC_UNDERRUN:
+		ts->resp = SAS_TASK_COMPLETE;
+		ts->stat = SAS_DATA_UNDERRUN;
+		ts->residual = le32_to_cpu(*(__le32 *)dl->status_block);
+		break;
+	case TC_OVERRUN:
+		ts->resp = SAS_TASK_COMPLETE;
+		ts->stat = SAS_DATA_OVERRUN;
+		ts->residual = 0;
+		break;
+	case TC_SSP_RESP:
+	case TC_ATA_RESP:
+		ts->resp = SAS_TASK_COMPLETE;
+		ts->stat = SAS_PROTO_RESPONSE;
+		asd_get_response_tasklet(ascb, dl);
+		break;
+	case TF_OPEN_REJECT:
+		ts->resp = SAS_TASK_UNDELIVERED;
+		ts->stat = SAS_OPEN_REJECT;
+		if (dl->status_block[1] & 2)
+			ts->open_rej_reason = 1 + dl->status_block[2];
+		else if (dl->status_block[1] & 1)
+			ts->open_rej_reason = (dl->status_block[2] >> 4)+10;
+		else
+			ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+		break;
+	case TF_OPEN_TO:
+		ts->resp = SAS_TASK_UNDELIVERED;
+		ts->stat = SAS_OPEN_TO;
+		break;
+	case TF_PHY_DOWN:
+	case TU_PHY_DOWN:
+		ts->resp = SAS_TASK_UNDELIVERED;
+		ts->stat = SAS_PHY_DOWN;
+		break;
+	case TI_PHY_DOWN:
+		ts->resp = SAS_TASK_COMPLETE;
+		ts->stat = SAS_PHY_DOWN;
+		break;
+	case TI_BREAK:
+	case TI_PROTO_ERR:
+	case TI_NAK:
+	case TI_ACK_NAK_TO:
+	case TF_SMP_XMIT_RCV_ERR:
+	case TC_ATA_R_ERR_RECV:
+		ts->resp = SAS_TASK_COMPLETE;
+		ts->stat = SAS_INTERRUPTED;
+		break;
+	case TF_BREAK:
+	case TU_BREAK:
+	case TU_ACK_NAK_TO:
+	case TF_SMPRSP_TO:
+		ts->resp = SAS_TASK_UNDELIVERED;
+		ts->stat = SAS_DEV_NO_RESPONSE;
+		break;
+	case TF_NAK_RECV:
+		ts->resp = SAS_TASK_COMPLETE;
+		ts->stat = SAS_NAK_R_ERR;
+		break;
+	case TA_I_T_NEXUS_LOSS:
+		opcode = dl->status_block[0];
+		goto Again;
+		break;
+	case TF_INV_CONN_HANDLE:
+		ts->resp = SAS_TASK_UNDELIVERED;
+		ts->stat = SAS_DEVICE_UNKNOWN;
+		break;
+	case TF_REQUESTED_N_PENDING:
+		ts->resp = SAS_TASK_UNDELIVERED;
+		ts->stat = SAS_PENDING;
+		break;
+	case TC_TASK_CLEARED:
+	case TA_ON_REQ:
+		ts->resp = SAS_TASK_COMPLETE;
+		ts->stat = SAS_ABORTED_TASK;
+		break;
+
+	case TF_NO_SMP_CONN:
+	case TF_TMF_NO_CTX:
+	case TF_TMF_NO_TAG:
+	case TF_TMF_TAG_FREE:
+	case TF_TMF_TASK_DONE:
+	case TF_TMF_NO_CONN_HANDLE:
+	case TF_IRTT_TO:
+	case TF_IU_SHORT:
+	case TF_DATA_OFFS_ERR:
+		ts->resp = SAS_TASK_UNDELIVERED;
+		ts->stat = SAS_DEV_NO_RESPONSE;
+		break;
+
+	case TC_LINK_ADM_RESP:
+	case TC_CONTROL_PHY:
+	case TC_RESUME:
+	case TC_PARTIAL_SG_LIST:
+	default:
+		ASD_DPRINTK("%s: dl opcode: 0x%x?\n", __FUNCTION__, opcode);
+		break;
+	}
+
+	switch (task->task_proto) {
+	case SATA_PROTO:
+	case SAS_PROTO_STP:
+		asd_unbuild_ata_ascb(ascb);
+		break;
+	case SAS_PROTO_SMP:
+		asd_unbuild_smp_ascb(ascb);
+		break;
+	case SAS_PROTO_SSP:
+		asd_unbuild_ssp_ascb(ascb);
+	default:
+		break;
+	}
+
+	spin_lock_irqsave(&task->task_state_lock, flags);
+	task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+	task->task_state_flags |= SAS_TASK_STATE_DONE;
+	if (unlikely((task->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+		spin_unlock_irqrestore(&task->task_state_lock, flags);
+		ASD_DPRINTK("task 0x%p done with opcode 0x%x resp 0x%x "
+			    "stat 0x%x but aborted by upper layer!\n",
+			    task, opcode, ts->resp, ts->stat);
+		complete(&ascb->completion);
+	} else {
+		spin_unlock_irqrestore(&task->task_state_lock, flags);
+		task->lldd_task = NULL;
+		asd_ascb_free(ascb);
+		mb();
+		task->task_done(task);
+	}
+}
+
+/* ---------- ATA ---------- */
+
+static int asd_build_ata_ascb(struct asd_ascb *ascb, struct sas_task *task,
+			      unsigned long gfp_flags)
+{
+	struct domain_device *dev = task->dev;
+	struct scb *scb;
+	u8     flags;
+	int    res = 0;
+
+	scb = ascb->scb;
+
+	if (unlikely(task->ata_task.device_control_reg_update))
+		scb->header.opcode = CONTROL_ATA_DEV;
+	else if (dev->sata_dev.command_set == ATA_COMMAND_SET)
+		scb->header.opcode = INITIATE_ATA_TASK;
+	else
+		scb->header.opcode = INITIATE_ATAPI_TASK;
+
+	scb->ata_task.proto_conn_rate = (1 << 5); /* STP */
+	if (dev->port->oob_mode == SAS_OOB_MODE)
+		scb->ata_task.proto_conn_rate |= dev->linkrate;
+
+	scb->ata_task.total_xfer_len = cpu_to_le32(task->total_xfer_len);
+	scb->ata_task.fis = task->ata_task.fis;
+	scb->ata_task.fis.fis_type = 0x27;
+	if (likely(!task->ata_task.device_control_reg_update))
+		scb->ata_task.fis.flags |= 0x80; /* C=1: update ATA cmd reg */
+	scb->ata_task.fis.flags &= 0xF0; /* PM_PORT field shall be 0 */
+	if (dev->sata_dev.command_set == ATAPI_COMMAND_SET)
+		memcpy(scb->ata_task.atapi_packet, task->ata_task.atapi_packet,
+		       16);
+	scb->ata_task.sister_scb = cpu_to_le16(0xFFFF);
+	scb->ata_task.conn_handle = cpu_to_le16(
+		(u16)(unsigned long)dev->lldd_dev);
+
+	if (likely(!task->ata_task.device_control_reg_update)) {
+		flags = 0;
+		if (task->ata_task.dma_xfer)
+			flags |= DATA_XFER_MODE_DMA;
+		if (task->ata_task.use_ncq &&
+		    dev->sata_dev.command_set != ATAPI_COMMAND_SET)
+			flags |= ATA_Q_TYPE_NCQ;
+		flags |= data_dir_flags[task->data_dir];
+		scb->ata_task.ata_flags = flags;
+
+		scb->ata_task.retry_count = task->ata_task.retry_count;
+
+		flags = 0;
+		if (task->ata_task.set_affil_pol)
+			flags |= SET_AFFIL_POLICY;
+		if (task->ata_task.stp_affil_pol)
+			flags |= STP_AFFIL_POLICY;
+		scb->ata_task.flags = flags;
+	}
+	ascb->tasklet_complete = asd_task_tasklet_complete;
+
+	if (likely(!task->ata_task.device_control_reg_update))
+		res = asd_map_scatterlist(task, scb->ata_task.sg_element,
+					  gfp_flags);
+
+	return res;
+}
+
+static void asd_unbuild_ata_ascb(struct asd_ascb *a)
+{
+	asd_unmap_scatterlist(a);
+}
+
+/* ---------- SMP ---------- */
+
+static int asd_build_smp_ascb(struct asd_ascb *ascb, struct sas_task *task,
+			      unsigned long gfp_flags)
+{
+	struct asd_ha_struct *asd_ha = ascb->ha;
+	struct domain_device *dev = task->dev;
+	struct scb *scb;
+
+	pci_map_sg(asd_ha->pcidev, &task->smp_task.smp_req, 1,
+		   PCI_DMA_FROMDEVICE);
+	pci_map_sg(asd_ha->pcidev, &task->smp_task.smp_resp, 1,
+		   PCI_DMA_FROMDEVICE);
+
+	scb = ascb->scb;
+
+	scb->header.opcode = INITIATE_SMP_TASK;
+
+	scb->smp_task.proto_conn_rate = dev->linkrate;
+
+	scb->smp_task.smp_req.bus_addr =
+		cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_req));
+	scb->smp_task.smp_req.size =
+		cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_req)-4);
+
+	scb->smp_task.smp_resp.bus_addr =
+		cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_resp));
+	scb->smp_task.smp_resp.size =
+		cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_resp)-4);
+
+	scb->smp_task.sister_scb = cpu_to_le16(0xFFFF);
+	scb->smp_task.conn_handle = cpu_to_le16((u16)
+						(unsigned long)dev->lldd_dev);
+
+	ascb->tasklet_complete = asd_task_tasklet_complete;
+
+	return 0;
+}
+
+static void asd_unbuild_smp_ascb(struct asd_ascb *a)
+{
+	struct sas_task *task = a->uldd_task;
+
+	BUG_ON(!task);
+	pci_unmap_sg(a->ha->pcidev, &task->smp_task.smp_req, 1,
+		     PCI_DMA_FROMDEVICE);
+	pci_unmap_sg(a->ha->pcidev, &task->smp_task.smp_resp, 1,
+		     PCI_DMA_FROMDEVICE);
+}
+
+/* ---------- SSP ---------- */
+
+static int asd_build_ssp_ascb(struct asd_ascb *ascb, struct sas_task *task,
+			      unsigned long gfp_flags)
+{
+	struct domain_device *dev = task->dev;
+	struct scb *scb;
+	int    res = 0;
+
+	scb = ascb->scb;
+
+	scb->header.opcode = INITIATE_SSP_TASK;
+
+	scb->ssp_task.proto_conn_rate  = (1 << 4); /* SSP */
+	scb->ssp_task.proto_conn_rate |= dev->linkrate;
+	scb->ssp_task.total_xfer_len = cpu_to_le32(task->total_xfer_len);
+	scb->ssp_task.ssp_frame.frame_type = SSP_DATA;
+	memcpy(scb->ssp_task.ssp_frame.hashed_dest_addr, dev->hashed_sas_addr,
+	       HASHED_SAS_ADDR_SIZE);
+	memcpy(scb->ssp_task.ssp_frame.hashed_src_addr,
+	       dev->port->ha->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
+	scb->ssp_task.ssp_frame.tptt = cpu_to_be16(0xFFFF);
+
+	memcpy(scb->ssp_task.ssp_cmd.lun, task->ssp_task.LUN, 8);
+	if (task->ssp_task.enable_first_burst)
+		scb->ssp_task.ssp_cmd.efb_prio_attr |= EFB_MASK;
+	scb->ssp_task.ssp_cmd.efb_prio_attr |= (task->ssp_task.task_prio << 3);
+	scb->ssp_task.ssp_cmd.efb_prio_attr |= (task->ssp_task.task_attr & 7);
+	memcpy(scb->ssp_task.ssp_cmd.cdb, task->ssp_task.cdb, 16);
+
+	scb->ssp_task.sister_scb = cpu_to_le16(0xFFFF);
+	scb->ssp_task.conn_handle = cpu_to_le16(
+		(u16)(unsigned long)dev->lldd_dev);
+	scb->ssp_task.data_dir = data_dir_flags[task->data_dir];
+	scb->ssp_task.retry_count = scb->ssp_task.retry_count;
+
+	ascb->tasklet_complete = asd_task_tasklet_complete;
+
+	res = asd_map_scatterlist(task, scb->ssp_task.sg_element, gfp_flags);
+
+	return res;
+}
+
+static void asd_unbuild_ssp_ascb(struct asd_ascb *a)
+{
+	asd_unmap_scatterlist(a);
+}
+
+/* ---------- Execute Task ---------- */
+
+static inline int asd_can_queue(struct asd_ha_struct *asd_ha, int num)
+{
+	int res = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags);
+	if ((asd_ha->seq.can_queue - num) < 0)
+		res = -SAS_QUEUE_FULL;
+	else
+		asd_ha->seq.can_queue -= num;
+	spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);
+
+	return res;
+}
+
+int asd_execute_task(struct sas_task *task, const int num,
+		     unsigned long gfp_flags)
+{
+	int res = 0;
+	LIST_HEAD(alist);
+	struct sas_task *t = task;
+	struct asd_ascb *ascb = NULL, *a;
+	struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha;
+
+	res = asd_can_queue(asd_ha, num);
+	if (res)
+		return res;
+
+	res = num;
+	ascb = asd_ascb_alloc_list(asd_ha, &res, gfp_flags);
+	if (res) {
+		res = -ENOMEM;
+		goto out_err;
+	}
+
+	__list_add(&alist, ascb->list.prev, &ascb->list);
+	list_for_each_entry(a, &alist, list) {
+		a->uldd_task = t;
+		t->lldd_task = a;
+		t = list_entry(t->list.next, struct sas_task, list);
+	}
+	list_for_each_entry(a, &alist, list) {
+		t = a->uldd_task;
+		a->uldd_timer = 1;
+		if (t->task_proto & SAS_PROTO_STP)
+			t->task_proto = SAS_PROTO_STP;
+		switch (t->task_proto) {
+		case SATA_PROTO:
+		case SAS_PROTO_STP:
+			res = asd_build_ata_ascb(a, t, gfp_flags);
+			break;
+		case SAS_PROTO_SMP:
+			res = asd_build_smp_ascb(a, t, gfp_flags);
+			break;
+		case SAS_PROTO_SSP:
+			res = asd_build_ssp_ascb(a, t, gfp_flags);
+			break;
+		default:
+			asd_printk("unknown sas_task proto: 0x%x\n",
+				   t->task_proto);
+			res = -ENOMEM;
+			break;
+		}
+		if (res)
+			goto out_err_unmap;
+	}
+	list_del_init(&alist);
+
+	res = asd_post_ascb_list(asd_ha, ascb, num);
+	if (unlikely(res)) {
+		a = NULL;
+		__list_add(&alist, ascb->list.prev, &ascb->list);
+		goto out_err_unmap;
+	}
+
+	return 0;
+out_err_unmap:
+	{
+		struct asd_ascb *b = a;
+		list_for_each_entry(a, &alist, list) {
+			if (a == b)
+				break;
+			t = a->uldd_task;
+			switch (t->task_proto) {
+			case SATA_PROTO:
+			case SAS_PROTO_STP:
+				asd_unbuild_ata_ascb(a);
+				break;
+			case SAS_PROTO_SMP:
+				asd_unbuild_smp_ascb(a);
+				break;
+			case SAS_PROTO_SSP:
+				asd_unbuild_ssp_ascb(a);
+			default:
+				break;
+			}
+			t->lldd_task = NULL;
+		}
+	}
+	list_del_init(&alist);
+out_err:
+	if (ascb)
+		asd_ascb_free_list(ascb);
+	asd_can_dequeue(asd_ha, num);
+	return res;
+}
diff -urN oldtree/drivers/scsi/aic94xx/aic94xx_tmf.c newtree/drivers/scsi/aic94xx/aic94xx_tmf.c
--- oldtree/drivers/scsi/aic94xx/aic94xx_tmf.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/aic94xx/aic94xx_tmf.c	2006-02-13 19:20:00.602420720 -0500
@@ -0,0 +1,634 @@
+/*
+ * Aic94xx Task Management Functions
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/aic94xx/aic94xx_tmf.c#35 $
+ */
+
+#include <linux/spinlock.h>
+#include <scsi/sas/sas_task.h>
+#include "aic94xx.h"
+#include "aic94xx_sas.h"
+#include "aic94xx_hwi.h"
+
+/* ---------- Internal enqueue ---------- */
+
+static int asd_enqueue_internal(struct asd_ascb *ascb,
+		void (*tasklet_complete)(struct asd_ascb *,
+					 struct done_list_struct *),
+				void (*timed_out)(unsigned long))
+{
+	int res;
+
+	ascb->tasklet_complete = tasklet_complete;
+	ascb->uldd_timer = 1;
+
+	ascb->timer.data = (unsigned long) ascb;
+	ascb->timer.function = timed_out;
+	ascb->timer.expires = jiffies + AIC94XX_SCB_TIMEOUT;
+
+	add_timer(&ascb->timer);
+
+	res = asd_post_ascb_list(ascb->ha, ascb, 1);
+	if (unlikely(res))
+		del_timer(&ascb->timer);
+	return res;
+}
+
+static inline void asd_timedout_common(unsigned long data)
+{
+	struct asd_ascb *ascb = (void *) data;
+	struct asd_seq_data *seq = &ascb->ha->seq;
+        unsigned long flags;
+
+	spin_lock_irqsave(&seq->pend_q_lock, flags);
+        seq->pending--;
+        list_del_init(&ascb->list);
+        spin_unlock_irqrestore(&seq->pend_q_lock, flags);
+}
+
+/* ---------- CLEAR NEXUS ---------- */
+
+static void asd_clear_nexus_tasklet_complete(struct asd_ascb *ascb,
+					     struct done_list_struct *dl)
+{
+	ASD_DPRINTK("%s: here\n", __FUNCTION__);
+	if (!del_timer(&ascb->timer)) {
+		ASD_DPRINTK("%s: couldn't delete timer\n", __FUNCTION__);
+		return;
+	}
+	ASD_DPRINTK("%s: opcode: 0x%x\n", __FUNCTION__, dl->opcode);
+	ascb->uldd_task = (void *) (unsigned long) dl->opcode;
+	complete(&ascb->completion);
+}
+
+static void asd_clear_nexus_timedout(unsigned long data)
+{
+	struct asd_ascb *ascb = (void *) data;
+
+	ASD_DPRINTK("%s: here\n", __FUNCTION__);
+	asd_timedout_common(data);
+	ascb->uldd_task = (void *) TMF_RESP_FUNC_FAILED;
+	complete(&ascb->completion);
+}
+
+#define CLEAR_NEXUS_PRE         \
+	ASD_DPRINTK("%s: PRE\n", __FUNCTION__); \
+        res = 1;                \
+	ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); \
+	if (!ascb)              \
+		return -ENOMEM; \
+                                \
+	scb = ascb->scb;        \
+	scb->header.opcode = CLEAR_NEXUS
+
+#define CLEAR_NEXUS_POST        \
+	ASD_DPRINTK("%s: POST\n", __FUNCTION__); \
+	res = asd_enqueue_internal(ascb, asd_clear_nexus_tasklet_complete, \
+				   asd_clear_nexus_timedout);              \
+	if (res)                \
+		goto out_err;   \
+	ASD_DPRINTK("%s: clear nexus posted, waiting...\n", __FUNCTION__); \
+	wait_for_completion(&ascb->completion); \
+	res = (int) (unsigned long) ascb->uldd_task; \
+	if (res == TC_NO_ERROR) \
+		res = TMF_RESP_FUNC_COMPLETE;   \
+out_err:                        \
+	asd_ascb_free(ascb);    \
+	return res
+
+int asd_clear_nexus_ha(struct sas_ha_struct *sas_ha)
+{
+	struct asd_ha_struct *asd_ha = sas_ha->lldd_ha;
+	struct asd_ascb *ascb;
+	struct scb *scb;
+	int res;
+
+	CLEAR_NEXUS_PRE;
+	scb->clear_nexus.nexus = NEXUS_ADAPTER;
+	CLEAR_NEXUS_POST;
+}
+
+int asd_clear_nexus_port(struct sas_port *port)
+{
+	struct asd_ha_struct *asd_ha = port->ha->lldd_ha;
+	struct asd_ascb *ascb;
+	struct scb *scb;
+	int res;
+
+	CLEAR_NEXUS_PRE;
+	scb->clear_nexus.nexus = NEXUS_PORT;
+	scb->clear_nexus.conn_mask = port->phy_mask;
+	CLEAR_NEXUS_POST;
+}
+
+#if 0
+static int asd_clear_nexus_I_T(struct domain_device *dev)
+{
+	struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+	struct asd_ascb *ascb;
+	struct scb *scb;
+	int res;
+
+	CLEAR_NEXUS_PRE;
+	scb->clear_nexus.nexus = NEXUS_I_T;
+	scb->clear_nexus.flags = SEND_Q | EXEC_Q | NOTINQ;
+	if (dev->tproto)
+		scb->clear_nexus.flags |= SUSPEND_TX;
+	scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
+						   dev->lldd_dev);
+	CLEAR_NEXUS_POST;
+}
+#endif
+
+static int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun)
+{
+	struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+	struct asd_ascb *ascb;
+	struct scb *scb;
+	int res;
+
+	CLEAR_NEXUS_PRE;
+	scb->clear_nexus.nexus = NEXUS_I_T_L;
+	scb->clear_nexus.flags = SEND_Q | EXEC_Q | NOTINQ;
+	if (dev->tproto)
+		scb->clear_nexus.flags |= SUSPEND_TX;
+	memcpy(scb->clear_nexus.ssp_task.lun, lun, 8);
+	scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
+						   dev->lldd_dev);
+	CLEAR_NEXUS_POST;
+}
+
+static int asd_clear_nexus_tag(struct sas_task *task)
+{
+	struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha;
+	struct asd_ascb *tascb = task->lldd_task;
+	struct asd_ascb *ascb;
+	struct scb *scb;
+	int res;
+
+	CLEAR_NEXUS_PRE;
+	scb->clear_nexus.nexus = NEXUS_TAG;
+	memcpy(scb->clear_nexus.ssp_task.lun, task->ssp_task.LUN, 8);
+	scb->clear_nexus.ssp_task.tag = tascb->tag;
+	if (task->dev->tproto)
+		scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
+							  task->dev->lldd_dev);
+	CLEAR_NEXUS_POST;
+}
+
+static int asd_clear_nexus_index(struct sas_task *task)
+{
+	struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha;
+	struct asd_ascb *tascb = task->lldd_task;
+	struct asd_ascb *ascb;
+	struct scb *scb;
+	int res;
+
+	CLEAR_NEXUS_PRE;
+	scb->clear_nexus.nexus = NEXUS_TRANS_CX;
+	if (task->dev->tproto)
+		scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
+							  task->dev->lldd_dev);
+	scb->clear_nexus.index = cpu_to_le16(tascb->tc_index);
+	CLEAR_NEXUS_POST;
+}
+
+/* ---------- TMFs ---------- */
+
+static void asd_tmf_timedout(unsigned long data)
+{
+	struct asd_ascb *ascb = (void *) data;
+
+	ASD_DPRINTK("tmf timed out\n");
+	asd_timedout_common(data);
+	ascb->uldd_task = (void *) TMF_RESP_FUNC_FAILED;
+	complete(&ascb->completion);
+}
+
+static int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb,
+				    struct done_list_struct *dl)
+{
+	struct asd_ha_struct *asd_ha = ascb->ha;
+	unsigned long flags;
+	struct tc_resp_sb_struct {
+		__le16 index_escb;
+		u8     len_lsb;
+		u8     flags;
+	} __attribute__ ((packed)) *resp_sb = (void *) dl->status_block;
+
+	int  edb_id = ((resp_sb->flags & 0x70) >> 4)-1;
+	struct asd_ascb *escb;
+	struct asd_dma_tok *edb;
+	struct ssp_frame_hdr *fh;
+	struct ssp_response_iu   *ru;
+	int res = TMF_RESP_FUNC_FAILED;
+
+	ASD_DPRINTK("tmf resp tasklet\n");
+
+	spin_lock_irqsave(&asd_ha->seq.tc_index_lock, flags);
+	escb = asd_tc_index_find(&asd_ha->seq,
+				 (int)le16_to_cpu(resp_sb->index_escb));
+	spin_unlock_irqrestore(&asd_ha->seq.tc_index_lock, flags);
+
+	if (!escb) {
+		ASD_DPRINTK("Uh-oh! No escb for this dl?!\n");
+		return res;
+	}
+
+	edb = asd_ha->seq.edb_arr[edb_id + escb->edb_index];
+	ascb->tag = *(__be16 *)(edb->vaddr+4);
+	fh = edb->vaddr + 16;
+	ru = edb->vaddr + 16 + sizeof(*fh);
+	res = ru->status;
+	if (ru->datapres == 1)	  /* Response data present */
+		res = ru->resp_data[3];
+#if 0
+	ascb->tag = fh->tag;
+#endif
+	ascb->tag_valid = 1;
+
+	asd_invalidate_edb(escb, edb_id);
+	return res;
+}
+
+static void asd_tmf_tasklet_complete(struct asd_ascb *ascb,
+				     struct done_list_struct *dl)
+{
+	if (!del_timer(&ascb->timer))
+		return;
+
+	ASD_DPRINTK("tmf tasklet complete\n");
+
+	if (dl->opcode == TC_SSP_RESP)
+		ascb->uldd_task = (void *) (unsigned long)
+			asd_get_tmf_resp_tasklet(ascb, dl);
+	else
+		ascb->uldd_task = (void *) 0xFF00 + (unsigned long) dl->opcode;
+
+	complete(&ascb->completion);
+}
+
+static inline int asd_clear_nexus(struct sas_task *task)
+{
+	int res = TMF_RESP_FUNC_FAILED;
+	struct asd_ascb *tascb = task->lldd_task;
+	unsigned long flags;
+
+	ASD_DPRINTK("task not done, clearing nexus\n");
+	if (tascb->tag_valid)
+		res = asd_clear_nexus_tag(task);
+	else
+		res = asd_clear_nexus_index(task);
+	wait_for_completion_timeout(&tascb->completion,
+				    AIC94XX_SCB_TIMEOUT);
+	ASD_DPRINTK("came back from clear nexus\n");
+	spin_lock_irqsave(&task->task_state_lock, flags);
+	if (task->task_state_flags & SAS_TASK_STATE_DONE)
+		res = TMF_RESP_FUNC_COMPLETE;
+	spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+	return res;
+}
+
+/**
+ * asd_abort_task -- ABORT TASK TMF
+ * @task: the task to be aborted
+ *
+ * Before calling ABORT TASK the task state flags should be ORed with
+ * SAS_TASK_STATE_ABORTED (unless SAS_TASK_STATE_DONE is set) under
+ * the task_state_lock IRQ spinlock, then ABORT TASK *must* be called.
+ *
+ * Implements the ABORT TASK TMF, I_T_L_Q nexus.
+ * Returns: SAS TMF responses (see sas_task.h),
+ *          -ENOMEM,
+ *          -SAS_QUEUE_FULL.
+ *
+ * When ABORT TASK returns, the caller of ABORT TASK checks first the
+ * task->task_state_flags, and then the return value of ABORT TASK.
+ *
+ * If the task has task state bit SAS_TASK_STATE_DONE set, then the
+ * task was completed successfully prior to it being aborted.  The
+ * caller of ABORT TASK has responsibility to call task->task_done()
+ * xor free the task, depending on their framework.  The return code
+ * is TMF_RESP_FUNC_FAILED in this case.
+ *
+ * Else the SAS_TASK_STATE_DONE bit is not set,
+ * 	If the return code is TMF_RESP_FUNC_COMPLETE, then
+ * 		the task was aborted successfully.  The caller of
+ * 		ABORT TASK has responsibility to call task->task_done()
+ *              to finish the task, xor free the task depending on their
+ *		framework.
+ *	else
+ * 		the ABORT TASK returned some kind of error. The task
+ *              was _not_ cancelled.  Nothing can be assumed.
+ *		The caller of ABORT TASK may wish to retry.
+ */
+int asd_abort_task(struct sas_task *task)
+{
+	struct asd_ascb *tascb = task->lldd_task;
+	struct asd_ha_struct *asd_ha = tascb->ha;
+	int res = 1;
+	unsigned long flags;
+	struct asd_ascb *ascb = NULL;
+	struct scb *scb;
+
+	spin_lock_irqsave(&task->task_state_lock, flags);
+	if (task->task_state_flags & SAS_TASK_STATE_DONE) {
+		spin_unlock_irqrestore(&task->task_state_lock, flags);
+		res = TMF_RESP_FUNC_COMPLETE;
+		ASD_DPRINTK("%s: task 0x%p done\n", __FUNCTION__, task);
+		goto out_done;
+	}
+	spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+	ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL);
+	if (!ascb)
+		return -ENOMEM;
+	scb = ascb->scb;
+
+	scb->header.opcode = ABORT_TASK;
+
+	switch (task->task_proto) {
+	case SATA_PROTO:
+	case SAS_PROTO_STP:
+		scb->abort_task.proto_conn_rate = (1 << 5); /* STP */
+		break;
+	case SAS_PROTO_SSP:
+		scb->abort_task.proto_conn_rate  = (1 << 4); /* SSP */
+		scb->abort_task.proto_conn_rate |= task->dev->linkrate;
+		break;
+	case SAS_PROTO_SMP:
+		break;
+	default:
+		break;
+	}
+
+	if (task->task_proto == SAS_PROTO_SSP) {
+		scb->abort_task.ssp_frame.frame_type = SSP_TASK;
+		memcpy(scb->abort_task.ssp_frame.hashed_dest_addr,
+		       task->dev->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
+		memcpy(scb->abort_task.ssp_frame.hashed_src_addr,
+		       task->dev->port->ha->hashed_sas_addr,
+		       HASHED_SAS_ADDR_SIZE);
+		scb->abort_task.ssp_frame.tptt = cpu_to_be16(0xFFFF);
+
+		memcpy(scb->abort_task.ssp_task.lun, task->ssp_task.LUN, 8);
+		scb->abort_task.ssp_task.tmf = TMF_ABORT_TASK;
+		scb->abort_task.ssp_task.tag = cpu_to_be16(0xFFFF);
+	}
+
+	scb->abort_task.sister_scb = cpu_to_le16(0xFFFF);
+	scb->abort_task.conn_handle = cpu_to_le16(
+		(u16)(unsigned long)task->dev->lldd_dev);
+	scb->abort_task.retry_count = 1;
+	scb->abort_task.index = cpu_to_le16((u16)tascb->tc_index);
+	scb->abort_task.itnl_to = cpu_to_le16(ITNL_TIMEOUT_CONST);
+
+	res = asd_enqueue_internal(ascb, asd_tmf_tasklet_complete,
+				   asd_tmf_timedout);
+	if (res)
+		goto out;
+	wait_for_completion(&ascb->completion);
+	ASD_DPRINTK("tmf came back\n");
+
+	res = (int) (unsigned long) ascb->uldd_task;
+	tascb->tag = ascb->tag;
+	tascb->tag_valid = ascb->tag_valid;
+
+	spin_lock_irqsave(&task->task_state_lock, flags);
+	if (task->task_state_flags & SAS_TASK_STATE_DONE) {
+		spin_unlock_irqrestore(&task->task_state_lock, flags);
+		res = TMF_RESP_FUNC_COMPLETE;
+		ASD_DPRINTK("%s: task 0x%p done\n", __FUNCTION__, task);
+		goto out_done;
+	}
+	spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+	switch (res) {
+	/* The task to be aborted has been sent to the device.
+	 * We got a Response IU for the ABORT TASK TMF. */
+	case TC_NO_ERROR + 0xFF00:
+	case TMF_RESP_FUNC_COMPLETE:
+	case TMF_RESP_FUNC_FAILED:
+		res = asd_clear_nexus(task);
+		break;
+	case TMF_RESP_INVALID_FRAME:
+	case TMF_RESP_OVERLAPPED_TAG:
+	case TMF_RESP_FUNC_ESUPP:
+	case TMF_RESP_NO_LUN:
+		goto out_done; break;
+	}
+	/* In the following we assume that the managing layer
+	 * will _never_ make a mistake, when issuing ABORT TASK.
+	 */
+	switch (res) {
+	default:
+		res = asd_clear_nexus(task);
+		/* fallthrough */
+	case TC_NO_ERROR + 0xFF00:
+	case TMF_RESP_FUNC_COMPLETE:
+		break;
+	/* The task hasn't been sent to the device xor we never got
+	 * a (sane) Response IU for the ABORT TASK TMF.
+	 */
+	case TF_NAK_RECV + 0xFF00:
+		res = TMF_RESP_INVALID_FRAME;
+		break;
+	case TF_TMF_TASK_DONE + 0xFF00:	/* done but not reported yet */
+		res = TMF_RESP_FUNC_FAILED;
+		wait_for_completion_timeout(&tascb->completion,
+					    AIC94XX_SCB_TIMEOUT);
+		spin_lock_irqsave(&task->task_state_lock, flags);
+		if (task->task_state_flags & SAS_TASK_STATE_DONE)
+			res = TMF_RESP_FUNC_COMPLETE;
+		spin_unlock_irqrestore(&task->task_state_lock, flags);
+		goto out_done; break;
+	case TF_TMF_NO_TAG + 0xFF00:
+	case TF_TMF_TAG_FREE + 0xFF00: /* the tag is in the free list */
+	case TF_TMF_NO_CTX + 0xFF00: /* not in seq, or proto != SSP */
+	case TF_TMF_NO_CONN_HANDLE + 0xFF00: /* no such device */
+		res = TMF_RESP_FUNC_COMPLETE;
+		goto out_done; break;
+	}
+out_done:
+	if (res == TMF_RESP_FUNC_COMPLETE) {
+		task->lldd_task = NULL;
+		mb();
+		asd_ascb_free(tascb);
+	}
+out:
+	asd_ascb_free(ascb);
+	ASD_DPRINTK("task 0x%p aborted, res: 0x%x\n", task, res);
+	return res;
+}
+
+/**
+ * asd_initiate_ssp_tmf -- send a TMF to an I_T_L or I_T_L_Q nexus
+ * @dev: pointer to struct domain_device of interest
+ * @lun: pointer to u8[8] which is the LUN
+ * @tmf: the TMF to be performed (see sas_task.h or the SAS spec)
+ * @index: the transaction context of the task to be queried if QT TMF
+ *
+ * This function is used to send ABORT TASK SET, CLEAR ACA,
+ * CLEAR TASK SET, LU RESET and QUERY TASK TMFs.
+ *
+ * No SCBs should be queued to the I_T_L nexus when this SCB is
+ * pending.
+ *
+ * Returns: TMF response code (see sas_task.h or the SAS spec)
+ */
+static int asd_initiate_ssp_tmf(struct domain_device *dev, u8 *lun,
+				int tmf, int index)
+{
+	struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+	struct asd_ascb *ascb;
+	int res = 1;
+	struct scb *scb;
+
+	if (!(dev->tproto & SAS_PROTO_SSP))
+		return TMF_RESP_FUNC_ESUPP;
+
+	ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL);
+	if (!ascb)
+		return -ENOMEM;
+	scb = ascb->scb;
+
+	if (tmf == TMF_QUERY_TASK)
+		scb->header.opcode = QUERY_SSP_TASK;
+	else
+		scb->header.opcode = INITIATE_SSP_TMF;
+
+	scb->ssp_tmf.proto_conn_rate  = (1 << 4); /* SSP */
+	scb->ssp_tmf.proto_conn_rate |= dev->linkrate;
+	/* SSP frame header */
+	scb->ssp_tmf.ssp_frame.frame_type = SSP_TASK;
+	memcpy(scb->ssp_tmf.ssp_frame.hashed_dest_addr,
+	       dev->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
+	memcpy(scb->ssp_tmf.ssp_frame.hashed_src_addr,
+	       dev->port->ha->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
+	scb->ssp_tmf.ssp_frame.tptt = cpu_to_be16(0xFFFF);
+	/* SSP Task IU */
+	memcpy(scb->ssp_tmf.ssp_task.lun, lun, 8);
+	scb->ssp_tmf.ssp_task.tmf = tmf;
+
+	scb->ssp_tmf.sister_scb = cpu_to_le16(0xFFFF);
+	scb->ssp_tmf.conn_handle= cpu_to_le16((u16)(unsigned long)
+					      dev->lldd_dev);
+	scb->ssp_tmf.retry_count = 1;
+	scb->ssp_tmf.itnl_to = cpu_to_le16(ITNL_TIMEOUT_CONST);
+	if (tmf == TMF_QUERY_TASK)
+		scb->ssp_tmf.index = cpu_to_le16(index);
+
+	res = asd_enqueue_internal(ascb, asd_tmf_tasklet_complete,
+				   asd_tmf_timedout);
+	if (res)
+		goto out_err;
+	wait_for_completion(&ascb->completion);
+	res = (int) (unsigned long) ascb->uldd_task;
+
+	switch (res) {
+	case TC_NO_ERROR + 0xFF00:
+		res = TMF_RESP_FUNC_COMPLETE;
+		break;
+	case TF_NAK_RECV + 0xFF00:
+		res = TMF_RESP_INVALID_FRAME;
+		break;
+	case TF_TMF_TASK_DONE + 0xFF00:
+		res = TMF_RESP_FUNC_FAILED;
+		break;
+	case TF_TMF_NO_TAG + 0xFF00:
+	case TF_TMF_TAG_FREE + 0xFF00: /* the tag is in the free list */
+	case TF_TMF_NO_CTX + 0xFF00: /* not in seq, or proto != SSP */
+	case TF_TMF_NO_CONN_HANDLE + 0xFF00: /* no such device */
+		res = TMF_RESP_FUNC_COMPLETE;
+		break;
+	default:
+		ASD_DPRINTK("%s: converting result 0x%x to TMF_RESP_FUNC_FAILED\n",
+			    __FUNCTION__, res);
+		res = TMF_RESP_FUNC_FAILED;
+		break;
+	}
+out_err:
+	asd_ascb_free(ascb);
+	return res;
+}
+
+int asd_abort_task_set(struct domain_device *dev, u8 *lun)
+{
+	int res = asd_initiate_ssp_tmf(dev, lun, TMF_ABORT_TASK_SET, 0);
+
+	if (res == TMF_RESP_FUNC_COMPLETE)
+		asd_clear_nexus_I_T_L(dev, lun);
+	return res;
+}
+
+int asd_clear_aca(struct domain_device *dev, u8 *lun)
+{
+	int res = asd_initiate_ssp_tmf(dev, lun, TMF_CLEAR_ACA, 0);
+
+	if (res == TMF_RESP_FUNC_COMPLETE)
+		asd_clear_nexus_I_T_L(dev, lun);
+	return res;
+}
+
+int asd_clear_task_set(struct domain_device *dev, u8 *lun)
+{
+	int res = asd_initiate_ssp_tmf(dev, lun, TMF_CLEAR_TASK_SET, 0);
+
+	if (res == TMF_RESP_FUNC_COMPLETE)
+		asd_clear_nexus_I_T_L(dev, lun);
+	return res;
+}
+
+int asd_lu_reset(struct domain_device *dev, u8 *lun)
+{
+	int res = asd_initiate_ssp_tmf(dev, lun, TMF_LU_RESET, 0);
+
+	if (res == TMF_RESP_FUNC_COMPLETE)
+		asd_clear_nexus_I_T_L(dev, lun);
+	return res;
+}
+
+/**
+ * asd_query_task -- send a QUERY TASK TMF to an I_T_L_Q nexus
+ * task: pointer to sas_task struct of interest
+ *
+ * Returns: TMF_RESP_FUNC_COMPLETE if the task is not in the task set,
+ * or TMF_RESP_FUNC_SUCC if the task is in the task set.
+ *
+ * Normally the management layer sets the task to aborted state,
+ * and then calls query task and then abort task.
+ */
+int asd_query_task(struct sas_task *task)
+{
+	struct asd_ascb *ascb = task->lldd_task;
+	int index;
+
+	if (ascb) {
+		index = ascb->tc_index;
+		return asd_initiate_ssp_tmf(task->dev, task->ssp_task.LUN,
+					    TMF_QUERY_TASK, index);
+	}
+	return TMF_RESP_FUNC_COMPLETE;
+}
diff -urN oldtree/drivers/scsi/arcmsr/Makefile newtree/drivers/scsi/arcmsr/Makefile
--- oldtree/drivers/scsi/arcmsr/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/arcmsr/Makefile	2006-02-13 19:20:00.603420568 -0500
@@ -0,0 +1,8 @@
+# File: drivers/arcmsr/Makefile
+# Makefile for the ARECA PCI-X PCI-EXPRESS SATA RAID controllers SCSI driver.
+
+obj-$(CONFIG_SCSI_ARCMSR) := arcmsr.o
+
+EXTRA_CFLAGS += -I.
+
+
diff -urN oldtree/drivers/scsi/arcmsr/arcmsr.c newtree/drivers/scsi/arcmsr/arcmsr.c
--- oldtree/drivers/scsi/arcmsr/arcmsr.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/arcmsr/arcmsr.c	2006-02-13 19:20:00.609419656 -0500
@@ -0,0 +1,2708 @@
+/*
+******************************************************************************************
+**        O.S   : Linux
+**   FILE NAME  : arcmsr.c
+**        BY    : Erich Chen
+**   Description: SCSI RAID Device Driver for
+**                ARECA RAID Host adapter
+************************************************************************
+** Copyright (C) 2002 - 2005, Areca Technology Corporation All rights reserved.
+**
+**     Web site: www.areca.com.tw
+**       E-mail: erich@areca.com.tw
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License version 2 as
+** published by the Free Software Foundation.
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+************************************************************************
+** Redistribution and use in source and binary forms,with or without
+** modification,are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+**    notice,this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+**    notice,this list of conditions and the following disclaimer in the
+**    documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+**    derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES,INCLUDING,BUT NOT LIMITED TO,THE IMPLIED WARRANTIES
+** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,INDIRECT,
+** INCIDENTAL,SPECIAL,EXEMPLARY,OR CONSEQUENTIAL DAMAGES(INCLUDING,BUT
+** NOT LIMITED TO,PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA,OR PROFITS; OR BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY,WHETHER IN CONTRACT,STRICT LIABILITY,OR TORT
+**(INCLUDING NEGLIGENCE OR OTHERWISE)ARISING IN ANY WAY OUT OF THE USE OF
+** THIS SOFTWARE,EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**************************************************************************
+** History
+**
+**        REV#         DATE	            NAME	         DESCRIPTION
+**     1.00.00.00    3/31/2004	       Erich Chen	     First release
+**     1.10.00.04    7/28/2004         Erich Chen        modify for ioctl
+**     1.10.00.06    8/28/2004         Erich Chen        modify for 2.6.x
+**     1.10.00.08    9/28/2004         Erich Chen        modify for x86_64
+**     1.10.00.10   10/10/2004         Erich Chen        bug fix for SMP & ioctl
+**     1.20.00.00   11/29/2004         Erich Chen        bug fix with arcmsr_bus_reset when PHY error
+**     1.20.00.02   12/09/2004         Erich Chen        bug fix with over 2T bytes RAID Volume
+**     1.20.00.04    1/09/2005         Erich Chen        fits for Debian linux kernel version 2.2.xx
+**     1.20.00.05    2/20/2005         Erich Chen        cleanly as look like a Linux driver at 2.6.x
+**     													 thanks for peoples kindness comment
+**     															Kornel Wieliczek
+**     															Christoph Hellwig
+**     															Adrian Bunk
+**     															Andrew Morton
+**     															Christoph Hellwig
+**     															James Bottomley
+**     															Arjan van de Ven
+**     1.20.00.06    3/12/2005         Erich Chen        fix with arcmsr_pci_unmap_dma "unsigned long" cast,
+**                                                       modify PCCB POOL allocated by "dma_alloc_coherent"
+**                                                       (Kornel Wieliczek's comment)
+**     1.20.00.07    3/23/2005         Erich Chen        bug fix with arcmsr_scsi_host_template_init ocur segmentation fault,
+**                                                       if RAID adapter does not on PCI slot and modprobe/rmmod this driver twice.
+**                                                       bug fix enormous stack usage (Adrian Bunk's comment)
+**     1.20.00.08    6/23/2005         Erich Chen        bug fix with abort command,in case of heavy loading when sata cable
+**                                                       working on low quality connection
+**     1.20.00.09    9/12/2005         Erich Chen        bug fix with abort command handling,firmware version check
+**                                                       and firmware update notify for hardware bug fix
+**     1.20.00.10    9/23/2005         Erich Chen        enhance sysfs function for change driver's max tag Q number.
+**                                                       add DMA_64BIT_MASK for backward compatible with all 2.6.x
+**                                                       add some useful message for abort command
+**                                                       add ioctl code 'ARCMSR_IOCTL_FLUSH_ADAPTER_CACHE'
+**                                                       customer can send this command for sync raid volume data
+**     1.20.00.11    9/29/2005         Erich Chen        by comment of Arjan van de Ven fix incorrect msleep redefine
+**                                                       cast off sizeof(dma_addr_t) condition for 64bit pci_set_dma_mask
+**     1.20.00.12    9/30/2005         Erich Chen        bug fix with 64bit platform's ccbs using if over 4G system memory
+**                                                       change 64bit pci_set_consistent_dma_mask into 32bit
+**                                                       increcct adapter count if adapter initialize fail.
+**                                                       miss edit at arcmsr_build_ccb....
+**                                                       psge += sizeof(struct _SG64ENTRY *) =>  psge += sizeof(struct _SG64ENTRY)
+**                                                       64 bits sg entry would be incorrectly calculated
+**                                                       thanks Kornel Wieliczek give me kindly notify and detail description
+******************************************************************************************
+*/
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/pci.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_device.h>
+#include "arcmsr.h"
+
+MODULE_AUTHOR("Erich Chen <erich@areca.com.tw>");
+MODULE_DESCRIPTION("ARECA (ARC11xx/12xx) SATA RAID HOST Adapter");
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+**********************************************************************************
+**********************************************************************************
+*/
+static u_int8_t arcmsr_adapterCnt = 0;
+static struct _HCBARC arcmsr_host_control_block;
+/*
+**********************************************************************************
+**********************************************************************************
+*/
+static int arcmsr_fops_ioctl(struct inode *inode, struct file *filep,
+			     unsigned int ioctl_cmd, unsigned long arg);
+static int arcmsr_fops_close(struct inode *inode, struct file *filep);
+static int arcmsr_fops_open(struct inode *inode, struct file *filep);
+static int arcmsr_halt_notify(struct notifier_block *nb, unsigned long event,
+			      void *buf);
+static int arcmsr_initialize(struct _ACB *pACB, struct pci_dev *pPCI_DEV);
+static int arcmsr_iop_ioctlcmd(struct _ACB *pACB, int ioctl_cmd, void *arg);
+static int arcmsr_proc_info(struct Scsi_Host *host, char *buffer, char **start,
+			    off_t offset, int length, int inout);
+static int arcmsr_bios_param(struct scsi_device *sdev,
+			     struct block_device *bdev, sector_t capacity,
+			     int *info);
+static int arcmsr_release(struct Scsi_Host *);
+static int arcmsr_queue_command(struct scsi_cmnd *cmd,
+				void (*done) (struct scsi_cmnd *));
+static int arcmsr_cmd_abort(struct scsi_cmnd *);
+static int arcmsr_bus_reset(struct scsi_cmnd *);
+static int arcmsr_ioctl(struct scsi_device *dev, int ioctl_cmd, void *arg);
+static int __devinit arcmsr_device_probe(struct pci_dev *pPCI_DEV,
+					 const struct pci_device_id *id);
+static void arcmsr_device_remove(struct pci_dev *pPCI_DEV);
+static void arcmsr_pcidev_disattach(struct _ACB *pACB);
+static void arcmsr_iop_init(struct _ACB *pACB);
+static void arcmsr_free_ccb_pool(struct _ACB *pACB);
+static irqreturn_t arcmsr_interrupt(struct _ACB *pACB);
+static u_int8_t arcmsr_wait_msgint_ready(struct _ACB *pACB);
+static const char *arcmsr_info(struct Scsi_Host *);
+/*
+**********************************************************************************
+**
+**********************************************************************************
+*/
+static ssize_t arcmsr_show_firmware_info(struct class_device *dev, char *buf)
+{
+	struct Scsi_Host *host = class_to_shost(dev);
+	struct _ACB *pACB = (struct _ACB *)host->hostdata;
+	unsigned long flags = 0;
+	ssize_t len;
+
+	spin_lock_irqsave(pACB->host->host_lock, flags);
+	len = snprintf(buf, PAGE_SIZE,
+		       "=================================\n"
+		       "Firmware Version:  %s\n"
+		       "%s"
+		       "Adapter Model:           %s\n"
+		       "Reguest Lenth:               %4d\n"
+		       "Numbers of Queue:            %4d\n"
+		       "SDRAM Size:                  %4d\n"
+		       "IDE Channels:                %4d\n"
+		       "=================================\n",
+		       pACB->firm_version,
+		       (strncmp(pACB->firm_version, "V1.37", 5) <
+			0) ?
+		       "                   PLEASE UPDATE RAID FIRMWARE VERSION EQUAL OR MORE THAN 'V1.37'\n"
+		       : "", pACB->firm_model, pACB->firm_request_len,
+		       pACB->firm_numbers_queue, pACB->firm_sdram_size,
+		       pACB->firm_ide_channels);
+	spin_unlock_irqrestore(pACB->host->host_lock, flags);
+	return len;
+}
+static ssize_t arcmsr_show_driver_state(struct class_device *dev, char *buf)
+{
+	struct Scsi_Host *host = class_to_shost(dev);
+	struct _ACB *pACB = (struct _ACB *)host->hostdata;
+	unsigned long flags = 0;
+	ssize_t len;
+
+	spin_lock_irqsave(pACB->host->host_lock, flags);
+	len = snprintf(buf, PAGE_SIZE,
+		       "=================================\n"
+		       "ARCMSR: %s\n"
+		       "Current commands posted:     %4d\n"
+		       "Max commands posted:         %4d\n"
+		       "Current pending commands:    %4d\n"
+		       "Max pending commands:        %4d\n"
+		       "Max sgl length:              %4d\n"
+		       "Max sector count:            %4d\n"
+		       "SCSI Host Resets:            %4d\n"
+		       "SCSI Aborts/Timeouts:        %4d\n"
+		       "=================================\n",
+		       ARCMSR_DRIVER_VERSION,
+		       atomic_read(&pACB->ccboutstandingcount),
+		       ARCMSR_MAX_OUTSTANDING_CMD,
+		       atomic_read(&pACB->ccbwait2gocount),
+		       ARCMSR_MAX_FREECCB_NUM - ARCMSR_MAX_OUTSTANDING_CMD,
+		       ARCMSR_MAX_SG_ENTRIES,
+		       ARCMSR_MAX_XFER_SECTORS,
+		       pACB->num_resets, pACB->num_aborts);
+	spin_unlock_irqrestore(pACB->host->host_lock, flags);
+	return len;
+}
+static struct class_device_attribute arcmsr_firmware_info_attr = {
+	.attr = {
+		 .name = "firmware_info",
+		 .mode = S_IRUGO,
+		 },
+	.show = arcmsr_show_firmware_info,
+};
+static struct class_device_attribute arcmsr_driver_state_attr = {
+	.attr = {
+		 .name = "driver_state",
+		 .mode = S_IRUGO,
+		 },
+	.show = arcmsr_show_driver_state
+};
+static struct class_device_attribute *arcmsr_scsi_host_attr[] = {
+	&arcmsr_firmware_info_attr,
+	&arcmsr_driver_state_attr,
+	NULL
+};
+static int arcmsr_adjust_disk_queue_depth(struct scsi_device *sdev,
+					  int queue_depth)
+{
+	if (queue_depth > ARCMSR_MAX_CMD_PERLUN) {
+		queue_depth = ARCMSR_MAX_CMD_PERLUN;
+	}
+	scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
+	return queue_depth;
+}
+static struct scsi_host_template arcmsr_scsi_host_template = {
+	.module = THIS_MODULE,
+	.proc_name = "arcmsr",
+	.proc_info = arcmsr_proc_info,
+	.name = "ARCMSR ARECA SATA RAID HOST Adapter" ARCMSR_DRIVER_VERSION,	/* *name */
+	.release = arcmsr_release,
+	.info = arcmsr_info,
+	.ioctl = arcmsr_ioctl,
+	.queuecommand = arcmsr_queue_command,
+	.eh_strategy_handler = NULL,
+	.eh_abort_handler = arcmsr_cmd_abort,
+	.eh_device_reset_handler = NULL,
+	.eh_bus_reset_handler = arcmsr_bus_reset,
+	.eh_host_reset_handler = NULL,
+	.bios_param = arcmsr_bios_param,
+	.change_queue_depth = arcmsr_adjust_disk_queue_depth,
+	.can_queue = ARCMSR_MAX_OUTSTANDING_CMD,
+	.this_id = ARCMSR_SCSI_INITIATOR_ID,
+	.sg_tablesize = ARCMSR_MAX_SG_ENTRIES,
+	.max_sectors = ARCMSR_MAX_XFER_SECTORS,
+	.cmd_per_lun = ARCMSR_MAX_CMD_PERLUN,
+	.unchecked_isa_dma = 0,
+	.use_clustering = ENABLE_CLUSTERING,
+	.shost_attrs = arcmsr_scsi_host_attr,
+};
+
+/*
+**********************************************************************************
+** notifier block to get a notify on system shutdown/halt/reboot
+**********************************************************************************
+*/
+static struct notifier_block arcmsr_event_notifier =
+    { arcmsr_halt_notify, NULL, 0 };
+static struct file_operations arcmsr_file_operations = {
+	.owner = THIS_MODULE,
+	.ioctl = arcmsr_fops_ioctl,
+	.open = arcmsr_fops_open,
+	.release = arcmsr_fops_close,
+};
+
+/* We do our own ID filtering.  So, grab all SCSI storage class devices. */
+static struct pci_device_id arcmsr_device_id_table[] __devinitdata = {
+	{.vendor = PCIVendorIDARECA,.device = PCIDeviceIDARC1110,.subvendor =
+	 PCI_ANY_ID,.subdevice = PCI_ANY_ID,},
+	{.vendor = PCIVendorIDARECA,.device = PCIDeviceIDARC1120,.subvendor =
+	 PCI_ANY_ID,.subdevice = PCI_ANY_ID,},
+	{.vendor = PCIVendorIDARECA,.device = PCIDeviceIDARC1130,.subvendor =
+	 PCI_ANY_ID,.subdevice = PCI_ANY_ID,},
+	{.vendor = PCIVendorIDARECA,.device = PCIDeviceIDARC1160,.subvendor =
+	 PCI_ANY_ID,.subdevice = PCI_ANY_ID,},
+	{.vendor = PCIVendorIDARECA,.device = PCIDeviceIDARC1170,.subvendor =
+	 PCI_ANY_ID,.subdevice = PCI_ANY_ID,},
+	{.vendor = PCIVendorIDARECA,.device = PCIDeviceIDARC1210,.subvendor =
+	 PCI_ANY_ID,.subdevice = PCI_ANY_ID,},
+	{.vendor = PCIVendorIDARECA,.device = PCIDeviceIDARC1220,.subvendor =
+	 PCI_ANY_ID,.subdevice = PCI_ANY_ID,},
+	{.vendor = PCIVendorIDARECA,.device = PCIDeviceIDARC1230,.subvendor =
+	 PCI_ANY_ID,.subdevice = PCI_ANY_ID,},
+	{.vendor = PCIVendorIDARECA,.device = PCIDeviceIDARC1260,.subvendor =
+	 PCI_ANY_ID,.subdevice = PCI_ANY_ID,},
+	{.vendor = PCIVendorIDARECA,.device = PCIDeviceIDARC1270,.subvendor =
+	 PCI_ANY_ID,.subdevice = PCI_ANY_ID,},
+	{0, 0},			/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(pci, arcmsr_device_id_table);
+static struct pci_driver arcmsr_pci_driver = {
+	.name = "arcmsr",
+	.id_table = arcmsr_device_id_table,
+	.probe = arcmsr_device_probe,
+	.remove = arcmsr_device_remove,
+};
+
+/*
+*********************************************************************
+*********************************************************************
+*/
+static irqreturn_t arcmsr_do_interrupt(int irq, void *dev_id,
+				       struct pt_regs *regs)
+{
+	irqreturn_t handle_state;
+	struct _HCBARC *pHCBARC = &arcmsr_host_control_block;
+	struct _ACB *pACB;
+	struct _ACB *pACBtmp;
+	int i = 0;
+
+	pACB = (struct _ACB *)dev_id;
+	pACBtmp = pHCBARC->pACB[i];
+	while ((pACB != pACBtmp) && pACBtmp && (i < ARCMSR_MAX_ADAPTER)) {
+		i++;
+		pACBtmp = pHCBARC->pACB[i];
+	}
+	if (!pACBtmp) {
+		return IRQ_NONE;
+	}
+	spin_lock_irq(&pACB->isr_lockunlock);
+	handle_state = arcmsr_interrupt(pACB);
+	spin_unlock_irq(&pACB->isr_lockunlock);
+	return (handle_state);
+}
+
+/*
+*********************************************************************
+*********************************************************************
+*/
+static int arcmsr_bios_param(struct scsi_device *sdev,
+			     struct block_device *bdev, sector_t capacity,
+			     int *geom)
+{
+	int heads, sectors, cylinders, total_capacity;
+
+	total_capacity = capacity;
+	heads = 64;
+	sectors = 32;
+	cylinders = total_capacity / (heads * sectors);
+	if (cylinders > 1024) {
+		heads = 255;
+		sectors = 63;
+		cylinders = total_capacity / (heads * sectors);
+	}
+	geom[0] = heads;
+	geom[1] = sectors;
+	geom[2] = cylinders;
+	return (0);
+}
+
+/*
+************************************************************************
+************************************************************************
+*/
+static int __devinit arcmsr_device_probe(struct pci_dev *pPCI_DEV,
+					 const struct pci_device_id *id)
+{
+	struct Scsi_Host *host;
+	struct _HCBARC *pHCBARC = &arcmsr_host_control_block;
+	struct _ACB *pACB;
+	uint8_t bus, dev_fun;
+
+	if (pci_enable_device(pPCI_DEV)) {
+		printk("arcmsr%d adapter probe: pci_enable_device error \n",
+		       arcmsr_adapterCnt);
+		return -ENODEV;
+	}
+	/* allocate scsi host information (includes out adapter) scsi_host_alloc==scsi_register */
+	if ((host =
+	     scsi_host_alloc(&arcmsr_scsi_host_template,
+			     sizeof(struct _ACB))) == 0) {
+		printk("arcmsr%d adapter probe: scsi_host_alloc error \n",
+		       arcmsr_adapterCnt);
+		return -ENODEV;
+	}
+	if (!pci_set_dma_mask(pPCI_DEV, DMA_64BIT_MASK)) {
+		printk
+		    ("ARECA RAID ADAPTER%d: 64BITS PCI BUS DMA ADDRESSING SUPPORTED\n",
+		     arcmsr_adapterCnt);
+	} else if (!pci_set_dma_mask(pPCI_DEV, DMA_32BIT_MASK)) {
+		printk
+		    ("ARECA RAID ADAPTER%d: 32BITS PCI BUS DMA ADDRESSING SUPPORTED\n",
+		     arcmsr_adapterCnt);
+	} else {
+		printk("ARECA RAID ADAPTER%d: No suitable DMA available.\n",
+		       arcmsr_adapterCnt);
+		return -ENOMEM;
+	}
+	if (pci_set_consistent_dma_mask(pPCI_DEV, DMA_32BIT_MASK)) {
+		printk
+		    ("ARECA RAID ADAPTER%d: No 32BIT coherent DMA adressing available.\n",
+		     arcmsr_adapterCnt);
+		return -ENOMEM;
+	}
+	bus = pPCI_DEV->bus->number;
+	dev_fun = pPCI_DEV->devfn;
+	pACB = (struct _ACB *)host->hostdata;
+	memset(pACB, 0, sizeof(struct _ACB));
+	spin_lock_init(&pACB->isr_lockunlock);
+	spin_lock_init(&pACB->wait2go_lockunlock);
+	spin_lock_init(&pACB->qbuffer_lockunlock);
+	spin_lock_init(&pACB->ccb_doneindex_lockunlock);
+	spin_lock_init(&pACB->ccb_startindex_lockunlock);
+	pACB->pPCI_DEV = pPCI_DEV;
+	pACB->host = host;
+	host->max_sectors = ARCMSR_MAX_XFER_SECTORS;
+	host->max_lun = ARCMSR_MAX_TARGETLUN;
+	host->max_id = ARCMSR_MAX_TARGETID;	/*16:8 */
+	host->max_cmd_len = 16;	/*this is issue of 64bit LBA ,over 2T byte */
+	host->sg_tablesize = ARCMSR_MAX_SG_ENTRIES;
+	host->can_queue = ARCMSR_MAX_OUTSTANDING_CMD;	/* max simultaneous cmds */
+	host->cmd_per_lun = ARCMSR_MAX_CMD_PERLUN;
+	host->this_id = ARCMSR_SCSI_INITIATOR_ID;
+	host->unique_id = (bus << 8) | dev_fun;
+	host->io_port = 0;
+	host->n_io_port = 0;
+	host->irq = pPCI_DEV->irq;
+	pci_set_master(pPCI_DEV);
+	if (arcmsr_initialize(pACB, pPCI_DEV)) {
+		printk("arcmsr%d initialize got error \n", arcmsr_adapterCnt);
+		pHCBARC->adapterCnt = arcmsr_adapterCnt;
+		pHCBARC->pACB[arcmsr_adapterCnt] = NULL;
+		scsi_host_put(host);
+		return -ENODEV;
+	}
+	if (pci_request_regions(pPCI_DEV, "arcmsr")) {
+		printk("arcmsr%d adapter probe: pci_request_regions failed \n",
+		       arcmsr_adapterCnt--);
+		pHCBARC->adapterCnt = arcmsr_adapterCnt;
+		arcmsr_pcidev_disattach(pACB);
+		scsi_host_put(host);
+		return -ENODEV;
+	}
+	if (request_irq
+	    (pPCI_DEV->irq, arcmsr_do_interrupt, SA_INTERRUPT | SA_SHIRQ,
+	     "arcmsr", pACB)) {
+		printk("arcmsr%d request IRQ=%d failed !\n",
+		       arcmsr_adapterCnt--, pPCI_DEV->irq);
+		pHCBARC->adapterCnt = arcmsr_adapterCnt;
+		arcmsr_pcidev_disattach(pACB);
+		scsi_host_put(host);
+		return -ENODEV;
+	}
+	arcmsr_iop_init(pACB);
+	if (scsi_add_host(host, &pPCI_DEV->dev)) {
+		printk("arcmsr%d scsi_add_host got error \n",
+		       arcmsr_adapterCnt--);
+		pHCBARC->adapterCnt = arcmsr_adapterCnt;
+		arcmsr_pcidev_disattach(pACB);
+		scsi_host_put(host);
+		return -ENODEV;
+	}
+	pHCBARC->adapterCnt = arcmsr_adapterCnt;
+	pci_set_drvdata(pPCI_DEV, host);
+	scsi_scan_host(host);
+	return 0;
+}
+
+/*
+************************************************************************
+************************************************************************
+*/
+static void arcmsr_device_remove(struct pci_dev *pPCI_DEV)
+{
+	struct Scsi_Host *host = pci_get_drvdata(pPCI_DEV);
+	struct _HCBARC *pHCBARC = &arcmsr_host_control_block;
+	struct _ACB *pACB = (struct _ACB *)host->hostdata;
+	int i;
+
+	/* Flush cache to disk */
+	/* Free irq,otherwise extra interrupt is generated       */
+	/* Issue a blocking(interrupts disabled) command to the card */
+	arcmsr_pcidev_disattach(pACB);
+	scsi_remove_host(host);
+	scsi_host_put(host);
+	pci_set_drvdata(pPCI_DEV, NULL);
+	/*if this is last pACB */
+	for (i = 0; i < ARCMSR_MAX_ADAPTER; i++) {
+		if (pHCBARC->pACB[i] != NULL) {
+			return;	/* this is not last adapter's release */
+		}
+	}
+	unregister_chrdev(pHCBARC->arcmsr_major_number, "arcmsr");
+	unregister_reboot_notifier(&arcmsr_event_notifier);
+	return;
+}
+
+/*
+************************************************************************
+************************************************************************
+*/
+static int arcmsr_scsi_host_template_init(struct scsi_host_template
+					  *host_template)
+{
+	int error;
+	struct _HCBARC *pHCBARC = &arcmsr_host_control_block;
+
+	/*
+	 ** register as a PCI hot-plug driver module
+	 */
+	memset(pHCBARC, 0, sizeof(struct _HCBARC));
+	error = pci_module_init(&arcmsr_pci_driver);
+	if (pHCBARC->pACB[0] != NULL) {
+		host_template->proc_name = "arcmsr";
+		register_reboot_notifier(&arcmsr_event_notifier);
+		pHCBARC->arcmsr_major_number =
+		    register_chrdev(0, "arcmsr", &arcmsr_file_operations);
+		printk("arcmsr device major number %d \n",
+		       pHCBARC->arcmsr_major_number);
+	}
+	return (error);
+}
+
+/*
+************************************************************************
+************************************************************************
+*/
+static int arcmsr_module_init(void)
+{
+	return (arcmsr_scsi_host_template_init(&arcmsr_scsi_host_template));
+}
+
+/*
+************************************************************************
+************************************************************************
+*/
+static void arcmsr_module_exit(void)
+{
+	pci_unregister_driver(&arcmsr_pci_driver);
+	return;
+}
+
+module_init(arcmsr_module_init);
+module_exit(arcmsr_module_exit);
+/*
+**********************************************************************
+**********************************************************************
+*/
+static void arcmsr_pci_unmap_dma(struct _CCB *pCCB)
+{
+	struct _ACB *pACB = pCCB->pACB;
+	struct scsi_cmnd *pcmd = pCCB->pcmd;
+
+	if (pcmd->use_sg != 0) {
+		struct scatterlist *sl;
+
+		sl = (struct scatterlist *)pcmd->request_buffer;
+		pci_unmap_sg(pACB->pPCI_DEV, sl, pcmd->use_sg,
+			     pcmd->sc_data_direction);
+	} else if (pcmd->request_bufflen != 0) {
+		pci_unmap_single(pACB->pPCI_DEV,
+				 (dma_addr_t) (unsigned long)pcmd->SCp.ptr,
+				 pcmd->request_bufflen,
+				 pcmd->sc_data_direction);
+	}
+	return;
+}
+
+/*
+**********************************************************************************
+**********************************************************************************
+*/
+static int arcmsr_fops_open(struct inode *inode, struct file *filep)
+{
+	int i, minor;
+	struct _HCBARC *pHCBARC = &arcmsr_host_control_block;
+	struct _ACB *pACB;
+
+	minor = MINOR(inode->i_rdev);
+	if (minor >= pHCBARC->adapterCnt) {
+		return -ENXIO;
+	}
+	for (i = 0; i < ARCMSR_MAX_ADAPTER; i++) {
+		if ((pACB = pHCBARC->pACB[i]) != NULL) {
+			if (pACB->adapter_index == minor) {
+				break;
+			}
+		}
+	}
+	if (i >= ARCMSR_MAX_ADAPTER) {
+		return -ENXIO;
+	}
+	return 0;		/* success */
+}
+
+/*
+**********************************************************************************
+**********************************************************************************
+*/
+static int arcmsr_fops_close(struct inode *inode, struct file *filep)
+{
+	int i, minor;
+	struct _HCBARC *pHCBARC = &arcmsr_host_control_block;
+	struct _ACB *pACB;
+
+	minor = MINOR(inode->i_rdev);
+	if (minor >= pHCBARC->adapterCnt) {
+		return -ENXIO;
+	}
+	for (i = 0; i < ARCMSR_MAX_ADAPTER; i++) {
+		if ((pACB = pHCBARC->pACB[i]) != NULL) {
+			if (pACB->adapter_index == minor) {
+				break;
+			}
+		}
+	}
+	if (i >= ARCMSR_MAX_ADAPTER) {
+		return -ENXIO;
+	}
+	return 0;
+}
+
+/*
+**********************************************************************************
+**********************************************************************************
+*/
+static int arcmsr_fops_ioctl(struct inode *inode, struct file *filep,
+			     unsigned int ioctl_cmd, unsigned long arg)
+{
+	int i, minor;
+	struct _HCBARC *pHCBARC = &arcmsr_host_control_block;
+	struct _ACB *pACB;
+
+	minor = MINOR(inode->i_rdev);
+	if (minor >= pHCBARC->adapterCnt) {
+		return -ENXIO;
+	}
+	for (i = 0; i < ARCMSR_MAX_ADAPTER; i++) {
+		if ((pACB = pHCBARC->pACB[i]) != NULL) {
+			if (pACB->adapter_index == minor) {
+				break;
+			}
+		}
+	}
+	if (i >= ARCMSR_MAX_ADAPTER) {
+		return -ENXIO;
+	}
+	/*
+	 ************************************************************
+	 ** We do not allow muti ioctls to the driver at the same duration.
+	 ************************************************************
+	 */
+	return arcmsr_iop_ioctlcmd(pACB, ioctl_cmd, (void *)arg);
+}
+
+/*
+************************************************************************
+************************************************************************
+*/
+static void arcmsr_flush_adapter_cache(struct _ACB *pACB)
+{
+	writel(ARCMSR_INBOUND_MESG0_FLUSH_CACHE, &pACB->pmu->inbound_msgaddr0);
+	return;
+}
+
+/*
+**********************************************************************
+**  Q back this CCB into ACB ArrayCCB
+**********************************************************************
+*/
+static void arcmsr_ccb_complete(struct _CCB *pCCB)
+{
+	unsigned long flag;
+	struct _ACB *pACB = pCCB->pACB;
+	struct scsi_cmnd *pcmd = pCCB->pcmd;
+
+	arcmsr_pci_unmap_dma(pCCB);
+	spin_lock_irqsave(&pACB->ccb_doneindex_lockunlock, flag);
+	atomic_dec(&pACB->ccboutstandingcount);
+	pCCB->startdone = ARCMSR_CCB_DONE;
+	pCCB->ccb_flags = 0;
+	pACB->pccbringQ[pACB->ccb_doneindex] = pCCB;
+	pACB->ccb_doneindex++;
+	pACB->ccb_doneindex %= ARCMSR_MAX_FREECCB_NUM;
+	spin_unlock_irqrestore(&pACB->ccb_doneindex_lockunlock, flag);
+	pcmd->scsi_done(pcmd);
+	return;
+}
+
+/*
+**********************************************************************
+**       if scsi error do auto request sense
+**********************************************************************
+*/
+static void arcmsr_report_sense_info(struct _CCB *pCCB)
+{
+	struct scsi_cmnd *pcmd = pCCB->pcmd;
+	struct _SENSE_DATA *psenseBuffer =
+	    (struct _SENSE_DATA *)pcmd->sense_buffer;
+
+	pcmd->result = DID_OK << 16;
+	if (psenseBuffer) {
+		int sense_data_length =
+		    sizeof(struct _SENSE_DATA) <
+		    sizeof(pcmd->
+			   sense_buffer) ? sizeof(struct _SENSE_DATA) :
+		    sizeof(pcmd->sense_buffer);
+		memset(psenseBuffer, 0, sizeof(pcmd->sense_buffer));
+		memcpy(psenseBuffer, pCCB->arcmsr_cdb.SenseData,
+		       sense_data_length);
+		psenseBuffer->ErrorCode = 0x70;
+		psenseBuffer->Valid = 1;
+	}
+	return;
+}
+
+/*
+*********************************************************************
+** to insert pCCB into tail of pACB wait exec ccbQ
+*********************************************************************
+*/
+static void arcmsr_queue_wait2go_ccb(struct _ACB *pACB, struct _CCB *pCCB)
+{
+	unsigned long flag;
+	int i = 0;
+
+	spin_lock_irqsave(&pACB->wait2go_lockunlock, flag);
+	while (1) {
+		if (pACB->pccbwait2go[i] == NULL) {
+			pACB->pccbwait2go[i] = pCCB;
+			atomic_inc(&pACB->ccbwait2gocount);
+			spin_unlock_irqrestore(&pACB->wait2go_lockunlock, flag);
+			return;
+		}
+		i++;
+		i %= ARCMSR_MAX_OUTSTANDING_CMD;
+	}
+	return;
+}
+
+/*
+*********************************************************************
+*********************************************************************
+*/
+static void arcmsr_abort_allcmd(struct _ACB *pACB)
+{
+	printk
+	    ("arcmsr_abort_allcmd: try to abort all outstanding command............\n");
+	writel(ARCMSR_INBOUND_MESG0_ABORT_CMD, &pACB->pmu->inbound_msgaddr0);
+	return;
+}
+
+/*
+**********************************************************************
+**********************************************************************
+*/
+static u_int8_t arcmsr_wait_msgint_ready(struct _ACB *pACB)
+{
+	uint32_t Index;
+	uint8_t Retries = 0x00;
+	do {
+		for (Index = 0; Index < 100; Index++) {
+			if (readl(&pACB->pmu->outbound_intstatus) &
+			    ARCMSR_MU_OUTBOUND_MESSAGE0_INT) {
+				writel(ARCMSR_MU_OUTBOUND_MESSAGE0_INT, &pACB->pmu->outbound_intstatus);	/*clear interrupt */
+				return 0x00;
+			}
+			msleep_interruptible(10);
+		}		/*max 1 seconds */
+	} while (Retries++ < 20);	/*max 20 sec */
+	return 0xff;
+}
+
+/*
+****************************************************************************
+** Routine Description: Reset 80331 iop.
+**           Arguments:
+**        Return Value: Nothing.
+****************************************************************************
+*/
+static void arcmsr_iop_reset(struct _ACB *pACB)
+{
+	struct _CCB *pCCB;
+	uint32_t intmask_org, mask;
+	int i = 0;
+
+	if (atomic_read(&pACB->ccboutstandingcount) != 0) {
+		/* disable all outbound interrupt */
+		intmask_org = readl(&pACB->pmu->outbound_intmask);
+		writel(intmask_org | ARCMSR_MU_OUTBOUND_ALL_INTMASKENABLE,
+		       &pACB->pmu->outbound_intmask);
+		/* talk to iop 331 outstanding command aborted */
+		arcmsr_abort_allcmd(pACB);
+		if (arcmsr_wait_msgint_ready(pACB)) {
+			printk
+			    ("arcmsr_iop_reset: wait 'abort all outstanding command' timeout................. \n");
+		}
+		/*clear all outbound posted Q */
+		for (i = 0; i < ARCMSR_MAX_OUTSTANDING_CMD; i++) {
+			readl(&pACB->pmu->outbound_queueport);
+		}
+		for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) {
+			pCCB = pACB->pccb_pool[i];
+			if (pCCB->startdone == ARCMSR_CCB_START) {
+				pCCB->startdone = ARCMSR_CCB_ABORTED;
+				pCCB->pcmd->result = DID_ABORT << 16;
+				arcmsr_ccb_complete(pCCB);
+			}
+		}
+		/* enable all outbound interrupt */
+		mask =
+		    ~(ARCMSR_MU_OUTBOUND_POSTQUEUE_INTMASKENABLE |
+		      ARCMSR_MU_OUTBOUND_DOORBELL_INTMASKENABLE |
+		      ARCMSR_MU_OUTBOUND_MESSAGE0_INTMASKENABLE);
+		writel(intmask_org & mask, &pACB->pmu->outbound_intmask);
+		/* post abort all outstanding command message to RAID controller */
+	}
+	i = 0;
+	while (atomic_read(&pACB->ccbwait2gocount) != 0) {
+		pCCB = pACB->pccbwait2go[i];
+		if (pCCB != NULL) {
+			pACB->pccbwait2go[i] = NULL;
+			pCCB->startdone = ARCMSR_CCB_ABORTED;
+			pCCB->pcmd->result = DID_ABORT << 16;
+			arcmsr_ccb_complete(pCCB);
+			atomic_dec(&pACB->ccbwait2gocount);
+		}
+		i++;
+		i %= ARCMSR_MAX_OUTSTANDING_CMD;
+	}
+	atomic_set(&pACB->ccboutstandingcount, 0);
+	return;
+}
+
+/*
+**********************************************************************
+**********************************************************************
+*/
+static void arcmsr_build_ccb(struct _ACB *pACB, struct _CCB *pCCB,
+			     struct scsi_cmnd *pcmd)
+{
+	struct _ARCMSR_CDB *pARCMSR_CDB =
+	    (struct _ARCMSR_CDB *)&pCCB->arcmsr_cdb;
+	int8_t *psge = (int8_t *) & pARCMSR_CDB->u;
+	uint32_t address_lo, address_hi;
+	int arccdbsize = 0x30;
+
+	pCCB->pcmd = pcmd;
+	memset(pARCMSR_CDB, 0, sizeof(struct _ARCMSR_CDB));
+	pARCMSR_CDB->Bus = 0;
+	pARCMSR_CDB->TargetID = pcmd->device->id;
+	pARCMSR_CDB->LUN = pcmd->device->lun;
+	pARCMSR_CDB->Function = 1;
+	pARCMSR_CDB->CdbLength = (uint8_t) pcmd->cmd_len;
+	pARCMSR_CDB->Context = (unsigned long)pARCMSR_CDB;
+	memcpy(pARCMSR_CDB->Cdb, pcmd->cmnd, pcmd->cmd_len);
+	if (pcmd->use_sg) {
+		int length, sgcount, i, cdb_sgcount = 0;
+		struct scatterlist *sl;
+
+		/* Get Scatter Gather List from scsiport. */
+		sl = (struct scatterlist *)pcmd->request_buffer;
+		sgcount =
+		    pci_map_sg(pACB->pPCI_DEV, sl, pcmd->use_sg,
+			       pcmd->sc_data_direction);
+		/* map stor port SG list to our iop SG List. */
+		for (i = 0; i < sgcount; i++) {
+			/* Get the physical address of the current data pointer */
+			length = cpu_to_le32(sg_dma_len(sl));
+			address_lo =
+			    cpu_to_le32(dma_addr_lo32(sg_dma_address(sl)));
+			address_hi =
+			    cpu_to_le32(dma_addr_hi32(sg_dma_address(sl)));
+			if (address_hi == 0) {
+				struct _SG32ENTRY *pdma_sg =
+				    (struct _SG32ENTRY *)psge;
+
+				pdma_sg->address = address_lo;
+				pdma_sg->length = length;
+				psge += sizeof(struct _SG32ENTRY);
+				arccdbsize += sizeof(struct _SG32ENTRY);
+			} else {
+				struct _SG64ENTRY *pdma_sg =
+				    (struct _SG64ENTRY *)psge;
+
+				pdma_sg->addresshigh = address_hi;
+				pdma_sg->address = address_lo;
+				pdma_sg->length = length | IS_SG64_ADDR;
+				psge += sizeof(struct _SG64ENTRY);
+				arccdbsize += sizeof(struct _SG64ENTRY);
+			}
+			sl++;
+			cdb_sgcount++;
+		}
+		pARCMSR_CDB->sgcount = (uint8_t) cdb_sgcount;
+		pARCMSR_CDB->DataLength = pcmd->request_bufflen;
+		if (arccdbsize > 256) {
+			pARCMSR_CDB->Flags |= ARCMSR_CDB_FLAG_SGL_BSIZE;
+		}
+	} else if (pcmd->request_bufflen) {
+		dma_addr_t dma_addr;
+		dma_addr =
+		    pci_map_single(pACB->pPCI_DEV, pcmd->request_buffer,
+				   pcmd->request_bufflen,
+				   pcmd->sc_data_direction);
+		pcmd->SCp.ptr = (char *)(unsigned long)dma_addr;
+		address_lo = cpu_to_le32(dma_addr_lo32(dma_addr));
+		address_hi = cpu_to_le32(dma_addr_hi32(dma_addr));
+		if (address_hi == 0) {
+			struct _SG32ENTRY *pdma_sg = (struct _SG32ENTRY *)psge;
+			pdma_sg->address = address_lo;
+			pdma_sg->length = pcmd->request_bufflen;
+		} else {
+			struct _SG64ENTRY *pdma_sg = (struct _SG64ENTRY *)psge;
+			pdma_sg->addresshigh = address_hi;
+			pdma_sg->address = address_lo;
+			pdma_sg->length = pcmd->request_bufflen | IS_SG64_ADDR;
+		}
+		pARCMSR_CDB->sgcount = 1;
+		pARCMSR_CDB->DataLength = pcmd->request_bufflen;
+	}
+	if (pcmd->cmnd[0] | WRITE_6 || pcmd->cmnd[0] | WRITE_10) {
+		pARCMSR_CDB->Flags |= ARCMSR_CDB_FLAG_WRITE;
+		pCCB->ccb_flags |= CCB_FLAG_WRITE;
+	}
+	return;
+}
+
+/*
+**************************************************************************
+**	arcmsr_post_ccb - Send a protocol specific ARC send postcard to a AIOC .
+**	handle: Handle of registered ARC protocol driver
+**	adapter_id: AIOC unique identifier(integer)
+**	pPOSTCARD_SEND: Pointer to ARC send postcard
+**
+**	This routine posts a ARC send postcard to the request post FIFO of a
+**	specific ARC adapter.
+**************************************************************************
+*/
+static void arcmsr_post_ccb(struct _ACB *pACB, struct _CCB *pCCB)
+{
+	uint32_t cdb_shifted_phyaddr = pCCB->cdb_shifted_phyaddr;
+	struct _ARCMSR_CDB *pARCMSR_CDB =
+	    (struct _ARCMSR_CDB *)&pCCB->arcmsr_cdb;
+
+	atomic_inc(&pACB->ccboutstandingcount);
+	pCCB->startdone = ARCMSR_CCB_START;
+	if (pARCMSR_CDB->Flags & ARCMSR_CDB_FLAG_SGL_BSIZE) {
+		writel(cdb_shifted_phyaddr | ARCMSR_CCBPOST_FLAG_SGL_BSIZE,
+		       &pACB->pmu->inbound_queueport);
+	} else {
+		writel(cdb_shifted_phyaddr, &pACB->pmu->inbound_queueport);
+	}
+	return;
+}
+
+/*
+**************************************************************************
+**************************************************************************
+*/
+static void arcmsr_post_wait2go_ccb(struct _ACB *pACB)
+{
+	unsigned long flag;
+	struct _CCB *pCCB;
+	int i = 0;
+
+	spin_lock_irqsave(&pACB->wait2go_lockunlock, flag);
+	while ((atomic_read(&pACB->ccbwait2gocount) > 0)
+	       && (atomic_read(&pACB->ccboutstandingcount) <
+		   ARCMSR_MAX_OUTSTANDING_CMD)) {
+		pCCB = pACB->pccbwait2go[i];
+		if (pCCB != NULL) {
+			pACB->pccbwait2go[i] = NULL;
+			arcmsr_post_ccb(pACB, pCCB);
+			atomic_dec(&pACB->ccbwait2gocount);
+		}
+		i++;
+		i %= ARCMSR_MAX_OUTSTANDING_CMD;
+	}
+	spin_unlock_irqrestore(&pACB->wait2go_lockunlock, flag);
+	return;
+}
+
+/*
+**********************************************************************
+**   Function: arcmsr_post_Qbuffer
+**     Output:
+**********************************************************************
+*/
+static void arcmsr_post_Qbuffer(struct _ACB *pACB)
+{
+	uint8_t *pQbuffer;
+	PQBUFFER pwbuffer = (PQBUFFER) & pACB->pmu->ioctl_wbuffer;
+	uint8_t *iop_data = (uint8_t *) pwbuffer->data;
+	int32_t allxfer_len = 0;
+
+	while ((pACB->wqbuf_firstindex != pACB->wqbuf_lastindex)
+	       && (allxfer_len < 124)) {
+		pQbuffer = &pACB->wqbuffer[pACB->wqbuf_firstindex];
+		memcpy(iop_data, pQbuffer, 1);
+		pACB->wqbuf_firstindex++;
+		pACB->wqbuf_firstindex %= ARCMSR_MAX_QBUFFER;	/*if last index number set it to 0 */
+		iop_data++;
+		allxfer_len++;
+	}
+	pwbuffer->data_len = allxfer_len;
+	/*
+	 ** push inbound doorbell and wait reply at hwinterrupt routine for next Qbuffer post
+	 */
+	writel(ARCMSR_INBOUND_DRIVER_DATA_WRITE_OK,
+	       &pACB->pmu->inbound_doorbell);
+	return;
+}
+
+/*
+************************************************************************
+************************************************************************
+*/
+static void arcmsr_stop_adapter_bgrb(struct _ACB *pACB)
+{
+	pACB->acb_flags |= ACB_F_MSG_STOP_BGRB;
+	pACB->acb_flags &= ~ACB_F_MSG_START_BGRB;
+	writel(ARCMSR_INBOUND_MESG0_STOP_BGRB, &pACB->pmu->inbound_msgaddr0);
+	return;
+}
+
+/*
+************************************************************************
+************************************************************************
+*/
+static void arcmsr_free_ccb_pool(struct _ACB *pACB)
+{
+	dma_free_coherent(&pACB->pPCI_DEV->dev,
+			  ARCMSR_MAX_FREECCB_NUM * sizeof(struct _CCB) + 0x20,
+			  pACB->dma_coherent, pACB->dma_coherent_handle);
+	return;
+}
+
+/*
+**********************************************************************
+**   Function:  arcmsr_interrupt
+**     Output:  void
+** DID_OK          0x00	// NO error
+** DID_NO_CONNECT  0x01	// Couldn't connect before timeout period
+** DID_BUS_BUSY    0x02	// BUS stayed busy through time out period
+** DID_TIME_OUT    0x03	// TIMED OUT for other reason
+** DID_BAD_TARGET  0x04	// BAD target.
+** DID_ABORT       0x05	// Told to abort for some other reason
+** DID_PARITY      0x06	// Parity error
+** DID_ERROR       0x07	// Internal error
+** DID_RESET       0x08	// Reset by somebody.
+** DID_BAD_INTR    0x09	// Got an interrupt we weren't expecting.
+** DID_PASSTHROUGH 0x0a	// Force command past mid-layer
+** DID_SOFT_ERROR  0x0b	// The low level driver just wish a retry
+** DRIVER_OK       0x00	// Driver status
+**********************************************************************
+*/
+static irqreturn_t arcmsr_interrupt(struct _ACB *pACB)
+{
+	struct _CCB *pCCB;
+	uint32_t flag_ccb, outbound_intstatus, outbound_doorbell;
+
+	/*
+	 *********************************************
+	 **   check outbound intstatus ÀË¹î¦³µL¶l®t«öªù¹a
+	 *********************************************
+	 */
+	outbound_intstatus =
+	    readl(&pACB->pmu->outbound_intstatus) & pACB->outbound_int_enable;
+	writel(outbound_intstatus, &pACB->pmu->outbound_intstatus);	/*clear interrupt */
+	if (outbound_intstatus & ARCMSR_MU_OUTBOUND_DOORBELL_INT) {
+		/*
+		 *********************************************
+		 **  DOORBELL ¥m¾´! ¬O§_¦³¶l¥ó­nÃ±¦¬
+		 *********************************************
+		 */
+		outbound_doorbell = readl(&pACB->pmu->outbound_doorbell);
+		writel(outbound_doorbell, &pACB->pmu->outbound_doorbell);	/*clear interrupt */
+		if (outbound_doorbell & ARCMSR_OUTBOUND_IOP331_DATA_WRITE_OK) {
+			PQBUFFER prbuffer =
+			    (PQBUFFER) & pACB->pmu->ioctl_rbuffer;
+			uint8_t *iop_data = (uint8_t *) prbuffer->data;
+			uint8_t *pQbuffer;
+			int32_t my_empty_len, iop_len, rqbuf_firstindex,
+			    rqbuf_lastindex;
+
+			/*check this iop data if overflow my rqbuffer */
+			rqbuf_lastindex = pACB->rqbuf_lastindex;
+			rqbuf_firstindex = pACB->rqbuf_firstindex;
+			iop_len = prbuffer->data_len;
+			my_empty_len =
+			    (rqbuf_firstindex - rqbuf_lastindex -
+			     1) & (ARCMSR_MAX_QBUFFER - 1);
+			if (my_empty_len >= iop_len) {
+				while (iop_len > 0) {
+					pQbuffer =
+					    &pACB->rqbuffer[pACB->
+							    rqbuf_lastindex];
+					memcpy(pQbuffer, iop_data, 1);
+					pACB->rqbuf_lastindex++;
+					pACB->rqbuf_lastindex %= ARCMSR_MAX_QBUFFER;	/*if last index number set it to 0 */
+					iop_data++;
+					iop_len--;
+				}
+				writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK, &pACB->pmu->inbound_doorbell);	/*signature, let IOP331 know data has been readed */
+			} else {
+				pACB->acb_flags |= ACB_F_IOPDATA_OVERFLOW;
+			}
+		}
+		if (outbound_doorbell & ARCMSR_OUTBOUND_IOP331_DATA_READ_OK) {
+			/*
+			 *********************************************
+			 **           ¬Ý¬Ý¬O§_ÁÙ¦³¶l¥ó­n¶¶¹D±H¥X
+			 *********************************************
+			 */
+			if (pACB->wqbuf_firstindex != pACB->wqbuf_lastindex) {
+				uint8_t *pQbuffer;
+				PQBUFFER pwbuffer =
+				    (PQBUFFER) & pACB->pmu->ioctl_wbuffer;
+				uint8_t *iop_data = (uint8_t *) pwbuffer->data;
+				int32_t allxfer_len = 0;
+
+				while ((pACB->wqbuf_firstindex !=
+					pACB->wqbuf_lastindex)
+				       && (allxfer_len < 124)) {
+					pQbuffer =
+					    &pACB->wqbuffer[pACB->
+							    wqbuf_firstindex];
+					memcpy(iop_data, pQbuffer, 1);
+					pACB->wqbuf_firstindex++;
+					pACB->wqbuf_firstindex %= ARCMSR_MAX_QBUFFER;	/*if last index number set it to 0 */
+					iop_data++;
+					allxfer_len++;
+				}
+				pwbuffer->data_len = allxfer_len;
+				/*
+				 ** push inbound doorbell tell iop driver data write ok and wait reply on next hwinterrupt for next Qbuffer post
+				 */
+				writel(ARCMSR_INBOUND_DRIVER_DATA_WRITE_OK,
+				       &pACB->pmu->inbound_doorbell);
+			} else {
+				pACB->acb_flags |= ACB_F_IOCTL_WQBUFFER_CLEARED;
+			}
+		}
+	}
+	if (outbound_intstatus & ARCMSR_MU_OUTBOUND_POSTQUEUE_INT) {
+		int id, lun;
+		/*
+		 *****************************************************************************
+		 **               areca cdb command done
+		 *****************************************************************************
+		 */
+		while (1) {
+			if ((flag_ccb =
+			     readl(&pACB->pmu->outbound_queueport)) ==
+			    0xFFFFFFFF) {
+				break;	/*chip FIFO no ccb for completion already */
+			}
+			/* check if command done with no error */
+			pCCB = (struct _CCB *)(pACB->vir2phy_offset + (flag_ccb << 5));	/*frame must be 32 bytes aligned */
+			if ((pCCB->pACB != pACB)
+			    || (pCCB->startdone != ARCMSR_CCB_START)) {
+				if (pCCB->startdone == ARCMSR_CCB_ABORTED) {
+					printk
+					    ("arcmsr%d scsi id=%d lun=%d ccb='0x%p' isr command abort successfully \n",
+					     pACB->adapter_index,
+					     pCCB->pcmd->device->id,
+					     pCCB->pcmd->device->lun, pCCB);
+					pCCB->pcmd->result = DID_ABORT << 16;
+					arcmsr_ccb_complete(pCCB);
+					continue;
+				}
+				printk
+				    ("arcmsr%d isr and getting an illegal ccb command done acb='0x%p' ccb='0x%p' ccbacb='0x%p' startdone=0x%x ccboutstandingcount=%d \n",
+				     pACB->adapter_index, pACB, pCCB,
+				     pCCB->pACB, pCCB->startdone,
+				     atomic_read(&pACB->ccboutstandingcount));
+				continue;
+			}
+			id = pCCB->pcmd->device->id;
+			lun = pCCB->pcmd->device->lun;
+			if ((flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR) == 0) {
+				if (pACB->devstate[id][lun] == ARECA_RAID_GONE) {
+					pACB->devstate[id][lun] =
+					    ARECA_RAID_GOOD;
+				}
+				pCCB->pcmd->result = DID_OK << 16;
+				arcmsr_ccb_complete(pCCB);
+			} else {
+				switch (pCCB->arcmsr_cdb.DeviceStatus) {
+				case ARCMSR_DEV_SELECT_TIMEOUT:
+					{
+						pACB->devstate[id][lun] =
+						    ARECA_RAID_GONE;
+						pCCB->pcmd->result =
+						    DID_TIME_OUT << 16;
+						arcmsr_ccb_complete(pCCB);
+					}
+					break;
+				case ARCMSR_DEV_ABORTED:
+				case ARCMSR_DEV_INIT_FAIL:
+					{
+						pACB->devstate[id][lun] =
+						    ARECA_RAID_GONE;
+						pCCB->pcmd->result =
+						    DID_BAD_TARGET << 16;
+						arcmsr_ccb_complete(pCCB);
+					}
+					break;
+				case SCSISTAT_CHECK_CONDITION:
+					{
+						pACB->devstate[id][lun] =
+						    ARECA_RAID_GOOD;
+						arcmsr_report_sense_info(pCCB);
+						arcmsr_ccb_complete(pCCB);
+					}
+					break;
+				default:
+					/* error occur Q all error ccb to errorccbpending Q */
+					printk
+					    ("arcmsr%d scsi id=%d lun=%d isr get command error done, but got unknow DeviceStatus=0x%x \n",
+					     pACB->adapter_index, id, lun,
+					     pCCB->arcmsr_cdb.DeviceStatus);
+					pACB->devstate[id][lun] =
+					    ARECA_RAID_GONE;
+					pCCB->pcmd->result = DID_NO_CONNECT << 16;	/*unknow error or crc error just for retry */
+					arcmsr_ccb_complete(pCCB);
+					break;
+				}
+			}
+		}		/*drain reply FIFO */
+	}
+	if (!(outbound_intstatus & ARCMSR_MU_OUTBOUND_HANDLE_INT)) {
+		/*it must be share irq */
+		return IRQ_NONE;
+	}
+	if (atomic_read(&pACB->ccbwait2gocount) != 0) {
+		arcmsr_post_wait2go_ccb(pACB);	/*try to post all pending ccb */
+	}
+	return IRQ_HANDLED;
+}
+
+/*
+*******************************************************************************
+*******************************************************************************
+*/
+static void arcmsr_iop_parking(struct _ACB *pACB)
+{
+	if (pACB != NULL) {
+		/* stop adapter background rebuild */
+		if (pACB->acb_flags & ACB_F_MSG_START_BGRB) {
+			pACB->acb_flags &= ~ACB_F_MSG_START_BGRB;
+			arcmsr_stop_adapter_bgrb(pACB);
+			if (arcmsr_wait_msgint_ready(pACB)) {
+				printk
+				    ("arcmsr%d wait 'stop adapter rebulid' timeout \n",
+				     pACB->adapter_index);
+			}
+			arcmsr_flush_adapter_cache(pACB);
+			if (arcmsr_wait_msgint_ready(pACB)) {
+				printk
+				    ("arcmsr%d wait 'stop adapter rebulid' timeout \n",
+				     pACB->adapter_index);
+			}
+		}
+	}
+}
+
+/*
+***********************************************************************
+************************************************************************
+*/
+static int arcmsr_iop_ioctlcmd(struct _ACB *pACB, int ioctl_cmd, void *arg)
+{
+	struct _CMD_IOCTL_FIELD *pcmdioctlfld;
+	dma_addr_t cmd_handle;
+	int retvalue = 0;
+	/* Only let one of these through at a time */
+
+	pcmdioctlfld =
+	    pci_alloc_consistent(pACB->pPCI_DEV,
+				 sizeof(struct _CMD_IOCTL_FIELD), &cmd_handle);
+	if (pcmdioctlfld == NULL) {
+		return -ENOMEM;
+	}
+	if (copy_from_user(pcmdioctlfld, arg, sizeof(struct _CMD_IOCTL_FIELD))
+	    != 0) {
+		retvalue = -EFAULT;
+		goto ioctl_out;
+	}
+	if (memcmp(pcmdioctlfld->cmdioctl.Signature, "ARCMSR", 6) != 0) {
+		retvalue = -EINVAL;
+		goto ioctl_out;
+	}
+	switch (ioctl_cmd) {
+	case ARCMSR_IOCTL_READ_RQBUFFER:
+		{
+			unsigned long flag;
+			unsigned long *ver_addr;
+			dma_addr_t buf_handle;
+			uint8_t *pQbuffer, *ptmpQbuffer;
+			int32_t allxfer_len = 0;
+
+			ver_addr =
+			    pci_alloc_consistent(pACB->pPCI_DEV, 1032,
+						 &buf_handle);
+			if (ver_addr == NULL) {
+				retvalue = -ENOMEM;
+				goto ioctl_out;
+			}
+			ptmpQbuffer = (uint8_t *) ver_addr;
+			spin_lock_irqsave(&pACB->qbuffer_lockunlock, flag);
+			while ((pACB->rqbuf_firstindex != pACB->rqbuf_lastindex)
+			       && (allxfer_len < 1031)) {
+				/*copy READ QBUFFER to srb */
+				pQbuffer =
+				    &pACB->rqbuffer[pACB->rqbuf_firstindex];
+				memcpy(ptmpQbuffer, pQbuffer, 1);
+				pACB->rqbuf_firstindex++;
+				pACB->rqbuf_firstindex %= ARCMSR_MAX_QBUFFER;	/*if last index number set it to 0 */
+				ptmpQbuffer++;
+				allxfer_len++;
+			}
+			if (pACB->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
+				PQBUFFER prbuffer =
+				    (PQBUFFER) & pACB->pmu->ioctl_rbuffer;
+				uint8_t *pQbuffer;
+				uint8_t *iop_data = (uint8_t *) prbuffer->data;
+				int32_t iop_len;
+
+				pACB->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
+				iop_len = (int32_t) prbuffer->data_len;
+				/*this iop data does no chance to make me overflow again here, so just do it */
+				while (iop_len > 0) {
+					pQbuffer =
+					    &pACB->rqbuffer[pACB->
+							    rqbuf_lastindex];
+					memcpy(pQbuffer, iop_data, 1);
+					pACB->rqbuf_lastindex++;
+					pACB->rqbuf_lastindex %= ARCMSR_MAX_QBUFFER;	/*if last index number set it to 0 */
+					iop_data++;
+					iop_len--;
+				}
+				writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK, &pACB->pmu->inbound_doorbell);	/*signature, let IOP331 know data has been readed */
+			}
+			spin_unlock_irqrestore(&pACB->qbuffer_lockunlock, flag);
+			memcpy(pcmdioctlfld->ioctldatabuffer,
+			       (uint8_t *) ver_addr, allxfer_len);
+			pcmdioctlfld->cmdioctl.Length = allxfer_len;
+			pcmdioctlfld->cmdioctl.ReturnCode =
+			    ARCMSR_IOCTL_RETURNCODE_OK;
+			if (copy_to_user
+			    (arg, pcmdioctlfld,
+			     sizeof(struct _CMD_IOCTL_FIELD)) != 0) {
+				retvalue = -EFAULT;
+			}
+			pci_free_consistent(pACB->pPCI_DEV, 1032, ver_addr,
+					    buf_handle);
+		}
+		break;
+	case ARCMSR_IOCTL_WRITE_WQBUFFER:
+		{
+			unsigned long flag;
+			unsigned long *ver_addr;
+			dma_addr_t buf_handle;
+			int32_t my_empty_len, user_len, wqbuf_firstindex,
+			    wqbuf_lastindex;
+			uint8_t *pQbuffer, *ptmpuserbuffer;
+
+			ver_addr =
+			    pci_alloc_consistent(pACB->pPCI_DEV, 1032,
+						 &buf_handle);
+			if (ver_addr == NULL) {
+				retvalue = -ENOMEM;
+				goto ioctl_out;
+			}
+			ptmpuserbuffer = (uint8_t *) ver_addr;
+			user_len = pcmdioctlfld->cmdioctl.Length;
+			memcpy(ptmpuserbuffer, pcmdioctlfld->ioctldatabuffer,
+			       user_len);
+			/*check if data xfer length of this request will overflow my array qbuffer */
+			spin_lock_irqsave(&pACB->qbuffer_lockunlock, flag);
+			wqbuf_lastindex = pACB->wqbuf_lastindex;
+			wqbuf_firstindex = pACB->wqbuf_firstindex;
+			my_empty_len =
+			    (wqbuf_firstindex - wqbuf_lastindex -
+			     1) & (ARCMSR_MAX_QBUFFER - 1);
+			if (my_empty_len >= user_len) {
+				while (user_len > 0) {
+					/*copy srb data to wqbuffer */
+					pQbuffer =
+					    &pACB->wqbuffer[pACB->
+							    wqbuf_lastindex];
+					memcpy(pQbuffer, ptmpuserbuffer, 1);
+					pACB->wqbuf_lastindex++;
+					pACB->wqbuf_lastindex %= ARCMSR_MAX_QBUFFER;	/*if last index number set it to 0 */
+					ptmpuserbuffer++;
+					user_len--;
+				}
+				/*post fist Qbuffer */
+				if (pACB->
+				    acb_flags & ACB_F_IOCTL_WQBUFFER_CLEARED) {
+					pACB->acb_flags &=
+					    ~ACB_F_IOCTL_WQBUFFER_CLEARED;
+					arcmsr_post_Qbuffer(pACB);
+				}
+				pcmdioctlfld->cmdioctl.ReturnCode =
+				    ARCMSR_IOCTL_RETURNCODE_OK;
+			} else {
+				pcmdioctlfld->cmdioctl.ReturnCode =
+				    ARCMSR_IOCTL_RETURNCODE_ERROR;
+			}
+			spin_unlock_irqrestore(&pACB->qbuffer_lockunlock, flag);
+			if (copy_to_user
+			    (arg, pcmdioctlfld,
+			     sizeof(struct _CMD_IOCTL_FIELD)) != 0) {
+				retvalue = -EFAULT;
+			}
+			pci_free_consistent(pACB->pPCI_DEV, 1032, ver_addr,
+					    buf_handle);
+		}
+		break;
+	case ARCMSR_IOCTL_CLEAR_RQBUFFER:
+		{
+			unsigned long flag;
+			uint8_t *pQbuffer = pACB->rqbuffer;
+
+			if (pACB->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
+				pACB->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
+				writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK, &pACB->pmu->inbound_doorbell);	/*signature, let IOP331 know data has been readed */
+			}
+			pACB->acb_flags |= ACB_F_IOCTL_RQBUFFER_CLEARED;
+			spin_lock_irqsave(&pACB->qbuffer_lockunlock, flag);
+			pACB->rqbuf_firstindex = 0;
+			pACB->rqbuf_lastindex = 0;
+			memset(pQbuffer, 0, ARCMSR_MAX_QBUFFER);
+			spin_unlock_irqrestore(&pACB->qbuffer_lockunlock, flag);
+			/*report success */
+			pcmdioctlfld->cmdioctl.ReturnCode =
+			    ARCMSR_IOCTL_RETURNCODE_OK;
+			if (copy_to_user
+			    (arg, pcmdioctlfld,
+			     sizeof(struct _CMD_IOCTL_FIELD)) != 0) {
+				retvalue = -EFAULT;
+			}
+		}
+		break;
+	case ARCMSR_IOCTL_CLEAR_WQBUFFER:
+		{
+			unsigned long flag;
+			uint8_t *pQbuffer = pACB->wqbuffer;
+
+			if (pACB->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
+				pACB->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
+				writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK, &pACB->pmu->inbound_doorbell);	/*signature, let IOP331 know data has been readed */
+			}
+			pACB->acb_flags |= ACB_F_IOCTL_WQBUFFER_CLEARED;
+			spin_lock_irqsave(&pACB->qbuffer_lockunlock, flag);
+			pACB->wqbuf_firstindex = 0;
+			pACB->wqbuf_lastindex = 0;
+			memset(pQbuffer, 0, ARCMSR_MAX_QBUFFER);
+			spin_unlock_irqrestore(&pACB->qbuffer_lockunlock, flag);
+			/*report success */
+			pcmdioctlfld->cmdioctl.ReturnCode =
+			    ARCMSR_IOCTL_RETURNCODE_OK;
+			if (copy_to_user
+			    (arg, pcmdioctlfld,
+			     sizeof(struct _CMD_IOCTL_FIELD)) != 0) {
+				retvalue = -EFAULT;
+			}
+		}
+		break;
+	case ARCMSR_IOCTL_CLEAR_ALLQBUFFER:
+		{
+			unsigned long flag;
+			uint8_t *pQbuffer;
+
+			if (pACB->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
+				pACB->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
+				writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK, &pACB->pmu->inbound_doorbell);	/*signature, let IOP331 know data has been readed */
+			}
+			pACB->acb_flags |=
+			    (ACB_F_IOCTL_WQBUFFER_CLEARED |
+			     ACB_F_IOCTL_RQBUFFER_CLEARED);
+			spin_lock_irqsave(&pACB->qbuffer_lockunlock, flag);
+			pACB->rqbuf_firstindex = 0;
+			pACB->rqbuf_lastindex = 0;
+			pACB->wqbuf_firstindex = 0;
+			pACB->wqbuf_lastindex = 0;
+			pQbuffer = pACB->rqbuffer;
+			memset(pQbuffer, 0, sizeof(struct _QBUFFER));
+			pQbuffer = pACB->wqbuffer;
+			memset(pQbuffer, 0, sizeof(struct _QBUFFER));
+			spin_unlock_irqrestore(&pACB->qbuffer_lockunlock, flag);
+			/*report success */
+			pcmdioctlfld->cmdioctl.ReturnCode =
+			    ARCMSR_IOCTL_RETURNCODE_OK;
+			if (copy_to_user
+			    (arg, pcmdioctlfld,
+			     sizeof(struct _CMD_IOCTL_FIELD)) != 0) {
+				retvalue = -EFAULT;
+			}
+		}
+		break;
+	case ARCMSR_IOCTL_RETURN_CODE_3F:
+		{
+			pcmdioctlfld->cmdioctl.ReturnCode =
+			    ARCMSR_IOCTL_RETURNCODE_3F;
+			if (copy_to_user
+			    (arg, pcmdioctlfld,
+			     sizeof(struct _CMD_IOCTL_FIELD)) != 0) {
+				retvalue = -EFAULT;
+			}
+		}
+		break;
+	case ARCMSR_IOCTL_SAY_HELLO:
+		{
+			int8_t *hello_string = "Hello! I am ARCMSR";
+
+			memcpy(pcmdioctlfld->ioctldatabuffer, hello_string,
+			       (int16_t) strlen(hello_string));
+			pcmdioctlfld->cmdioctl.ReturnCode =
+			    ARCMSR_IOCTL_RETURNCODE_OK;
+			if (copy_to_user
+			    (arg, pcmdioctlfld,
+			     sizeof(struct _CMD_IOCTL_FIELD)) != 0) {
+				retvalue = -EFAULT;
+			}
+		}
+		break;
+	case ARCMSR_IOCTL_SAY_GOODBYE:
+		{
+			arcmsr_iop_parking(pACB);
+		}
+		break;
+	case ARCMSR_IOCTL_FLUSH_ADAPTER_CACHE:
+		{
+			arcmsr_flush_adapter_cache(pACB);
+			if (arcmsr_wait_msgint_ready(pACB)) {
+				printk
+				    ("arcmsr%d ioctl flush cache wait 'flush adapter cache' timeout \n",
+				     pACB->adapter_index);
+			}
+		}
+		break;
+	default:
+		retvalue = -EFAULT;
+	}
+      ioctl_out:
+	pci_free_consistent(pACB->pPCI_DEV, sizeof(struct _CMD_IOCTL_FIELD),
+			    pcmdioctlfld, cmd_handle);
+	return retvalue;
+}
+
+/*
+************************************************************************
+**  arcmsr_ioctl
+** Performs ioctl requests not satified by the upper levels.
+** copy_from_user(to,from,n)
+** copy_to_user(to,from,n)
+**
+**  The scsi_device struct contains what we know about each given scsi
+**  device.
+**
+** FIXME(eric) - one of the great regrets that I have is that I failed to define
+** these structure elements as something like sdev_foo instead of foo.  This would
+** make it so much easier to grep through sources and so forth.  I propose that
+** all new elements that get added to these structures follow this convention.
+** As time goes on and as people have the stomach for it, it should be possible to
+** go back and retrofit at least some of the elements here with with the prefix.
+**
+**
+**struct scsi_device {
+**  %% private: %%
+**	                                        %%
+**	                                        %% This information is private to the scsi mid-layer.  Wrapping it in a
+**	                                        %% struct private is a way of marking it in a sort of C++ type of way.
+**	                                        %%
+**
+**	struct scsi_device      *next;	                    %% Used for linked list %%
+**	struct scsi_device      *prev;	                    %% Used for linked list %%
+**	wait_queue_head_t       scpnt_wait;	                %% Used to wait if device is busy %%
+**	struct Scsi_Host        *host;
+**	request_queue_t         request_queue;
+**  atomic_t                device_active;              %% commands checked out for device %%
+**	volatile unsigned short device_busy;	            %% commands actually active on low-level %%
+**	int (*scsi_init_io_fn)  (struct scsi_cmnd *);	            %% Used to initialize  new request %%
+**	struct scsi_cmnd               *device_queue;	            %% queue of SCSI Command structures %%
+**
+**  %% public: %%
+**
+**	unsigned int            id, lun, channel;
+**	unsigned int            manufacturer;	            %% Manufacturer of device, for using vendor-specific cmd's %%
+**	unsigned                sector_size;	            %% size in bytes %%
+**	int                     attached;		            %% # of high level drivers attached to this %%
+**	int                     access_count;	            %% Count of open channels/mounts %%
+**	void                    *hostdata;		            %% available to low-level driver %%
+**	devfs_handle_t          de;                         %% directory for the device %%
+**	char                    type;
+**	char                    scsi_level;
+**	char                    vendor[8], model[16], rev[4];
+**	unsigned char           current_tag;             	%% current tag %%
+**	unsigned char           sync_min_period;	        %% Not less than this period %%
+**	unsigned char           sync_max_offset;	        %% Not greater than this offset %%
+**	unsigned char           queue_depth;	            %% How deep a queue to use %%
+**	unsigned                online:1;
+**	unsigned                writeable:1;
+**	unsigned                removable:1;
+**	unsigned                random:1;
+**	unsigned                has_cmdblocks:1;
+**	unsigned                changed:1;	                %% Data invalid due to media change %%
+**	unsigned                busy:1;	                    %% Used to prevent races %%
+**	unsigned                lockable:1;	                %% Able to prevent media removal %%
+**	unsigned                borken:1;	                %% Tell the Seagate driver to be painfully slow on this device %%
+**	unsigned                tagged_supported:1;	        %% Supports SCSI-II tagged queuing %%
+**	unsigned                tagged_queue:1;	            %% SCSI-II tagged queuing enabled %%
+**	unsigned                disconnect:1;	            %% can disconnect %%
+**	unsigned                soft_reset:1;	            %% Uses soft reset option %%
+**	unsigned                sync:1;	                    %% Negotiate for sync transfers %%
+**	unsigned                wide:1;	                    %% Negotiate for WIDE transfers %%
+**	unsigned                single_lun:1;	            %% Indicates we should only allow I/O to one of the luns for the device at a time. %%
+**	unsigned                was_reset:1;	            %% There was a bus reset on the bus for this device %%
+**	unsigned                expecting_cc_ua:1;	        %% Expecting a CHECK_CONDITION/UNIT_ATTN because we did a bus reset. %%
+**	unsigned                device_blocked:1;	        %% Device returned QUEUE_FULL. %%
+**	unsigned                ten:1;		                %% support ten byte read / write %%
+**	unsigned                remap:1;	                %% support remapping  %%
+**	unsigned                starved:1;	                %% unable to process commands because  host busy %%
+**	int                     allow_revalidate;           %% Flag to allow revalidate to succeed in sd_open
+**};
+**
+************************************************************************
+*/
+static int arcmsr_ioctl(struct scsi_device *dev, int ioctl_cmd, void *arg)
+{
+	struct _ACB *pACB;
+	struct _HCBARC *pHCBARC = &arcmsr_host_control_block;
+	int32_t match = 0x55AA, i;
+
+	for (i = 0; i < ARCMSR_MAX_ADAPTER; i++) {
+		if ((pACB = pHCBARC->pACB[i]) != NULL) {
+			if (pACB->host == dev->host) {
+				match = i;
+				break;
+			}
+		}
+	}
+	if (match == 0x55AA) {
+		return -ENXIO;
+	}
+	if (!arg) {
+		return -EINVAL;
+	}
+	return (arcmsr_iop_ioctlcmd(pACB, ioctl_cmd, arg));
+}
+
+/*
+**************************************************************************
+**************************************************************************
+*/
+static struct _CCB *arcmsr_get_freeccb(struct _ACB *pACB)
+{
+	struct _CCB *pCCB;
+	unsigned long flag;
+	int ccb_startindex, ccb_doneindex;
+
+	spin_lock_irqsave(&pACB->ccb_startindex_lockunlock, flag);
+	ccb_doneindex = pACB->ccb_doneindex;
+	ccb_startindex = pACB->ccb_startindex;
+	pCCB = pACB->pccbringQ[ccb_startindex];
+	ccb_startindex++;
+	ccb_startindex %= ARCMSR_MAX_FREECCB_NUM;
+	if (ccb_doneindex != ccb_startindex) {
+		pACB->ccb_startindex = ccb_startindex;
+	} else {
+		pCCB = NULL;
+	}
+	spin_unlock_irqrestore(&pACB->ccb_startindex_lockunlock, flag);
+	return (pCCB);
+}
+
+/*
+***********************************************************************
+**
+** struct scsi_cmnd {
+**	int          sc_magic;
+**  // private: //
+**	           //
+**	           // This information is private to the scsi mid-layer. Wrapping it in a
+**	           // struct private is a way of marking it in a sort of C++ type of way.
+**	           //
+**	struct Scsi_Host   *host;
+**	unsigned short    state;
+**	unsigned short    owner;
+**	struct scsi_device      *device;
+**	Scsi_Request     *sc_request;
+**	struct scsi_cmnd   *next;
+**	struct scsi_cmnd   *reset_chain;
+**
+**	int eh_state;		 // Used for state tracking in error handlr
+**	void (*done) (struct scsi_cmnd *);
+**            // Mid-level done function
+**	           //
+**	           // A SCSI Command is assigned a nonzero serial_number when internal_cmnd
+**	           // passes it to the driver's queue command function. The serial_number
+**	           // is cleared when scsi_done is entered indicating that the command has
+**	           // been completed. If a timeout occurs,the serial number at the moment
+**	           // of timeout is copied into serial_number_at_timeout. By subsequently
+**	           // comparing the serial_number and serial_number_at_timeout fields
+**	           // during abort or reset processing,we can detect whether the command
+**	           // has already completed. This also detects cases where the command has
+**	           // completed and the SCSI Command structure has already being reused
+**	           // for another command,so that we can avoid incorrectly aborting or
+**	           // resetting the new command.
+**	           //
+**
+**	unsigned long     serial_number;
+**	unsigned long     serial_number_at_timeout;
+**
+**	int          retries;
+**	int          allowed;
+**	int          timeout_per_command;
+**	int          timeout_total;
+**	int          timeout;
+**
+**	           //
+**	           // We handle the timeout differently if it happens when a reset,
+**	           // abort,etc are in process.
+**	           //
+**	unsigned volatile char internal_timeout;
+**	struct scsi_cmnd   *bh_next;
+**            // To enumerate the commands waiting to be processed.
+**
+**  // public: //
+**
+**	unsigned int     target;
+**	unsigned int     lun;
+**	unsigned int     channel;
+**	unsigned char     cmd_len;
+**	unsigned char     old_cmd_len;
+**	unsigned char     sc_data_direction;
+**	unsigned char     sc_old_data_direction;
+**	           // These elements define the operation we are about to perform
+**	unsigned char     cmnd[MAX_COMMAND_SIZE];
+**	unsigned       request_bufflen;
+**            // Actual request size
+**
+**	struct timer_list eh_timeout;
+**            // Used to time out the command.
+**	void         *request_buffer;
+**            // Actual requested buffer
+**	           // These elements define the operation we ultimately want to perform
+**	unsigned char data_cmnd[MAX_COMMAND_SIZE];
+**	unsigned short old_use_sg;
+**            // We save use_sg here when requesting sense info
+**	unsigned short use_sg;
+**            // Number of pieces of scatter-gather
+**	unsigned short sglist_len;
+**            // size of malloc'd scatter-gather list
+**	unsigned short abort_reason;
+**            // If the mid-level code requests an abort,this is the reason.
+**	unsigned bufflen;
+**            // Size of data buffer
+**	void *buffer;
+**            // Data buffer
+**	unsigned underflow;
+**            // Return error if less than this amount is transferred
+**	unsigned old_underflow;
+**            // save underflow here when reusing the command for error handling
+**
+**	unsigned transfersize;
+**           	 // How much we are guaranteed to transfer with each SCSI transfer
+**            // (ie,between disconnect/reconnects.  Probably==sector size
+**	int resid;
+**            // Number of bytes requested to be transferred
+**            // less actual number transferred (0 if not supported)
+**	struct request request;
+**            // A copy of the command we are working on
+**	unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE];
+**            // obtained by REQUEST SENSE when CHECK CONDITION is received on original command (auto-sense)
+**	unsigned flags;
+**	           // Used to indicate that a command which has timed out also
+**	           // completed normally. Typically the completion function will
+**	           // do nothing but set this flag in this instance because the
+**	           // timeout handler is already running.
+**	unsigned done_late:1;
+**	           // Low-level done function - can be used by low-level driver to point
+**	           // to completion function. Not used by mid/upper level code.
+**	void (*scsi_done) (struct scsi_cmnd *);
+**	           // The following fields can be written to by the host specific code.
+**	           // Everything else should be left alone.
+**	Scsi_Pointer SCp;
+**            // Scratchpad used by some host adapters
+**	unsigned char *host_scribble;
+**            // The host adapter is allowed to
+**					   // call scsi_malloc and get some memory
+**					   // and hang it here.   The host adapter
+**					   // is also expected to call scsi_free
+**					   // to release this memory. (The memory
+**					   // obtained by scsi_malloc is guaranteed
+**					   // to be at an address < 16Mb).
+**	int result;
+**            // Status code from lower level driver
+**	unsigned char tag;
+**            // SCSI-II queued command tag
+**	unsigned long pid;
+**            // Process ID,starts at 0
+** };
+**
+** The scsi_cmnd structure is used by scsi.c internally,
+** and for communication
+** with low level drivers that support multiple outstanding commands.
+**
+**typedef struct scsi_pointer
+**{
+**  char       * ptr;        // data pointer
+**  int        this_residual;   // left in this buffer
+**  struct scatterlist *buffer;      // which buffer
+**  int        buffers_residual; // how many buffers left
+**
+**  volatile int    Status;
+**  volatile int    Message;
+**  volatile int    have_data_in;
+**  volatile int    sent_command;
+**  volatile int    phase;
+**} Scsi_Pointer;
+***********************************************************************
+*/
+static int arcmsr_queue_command(struct scsi_cmnd *cmd,
+				void (*done) (struct scsi_cmnd *))
+{
+	struct Scsi_Host *host = cmd->device->host;
+	struct _ACB *pACB = (struct _ACB *)host->hostdata;
+	struct _CCB *pCCB;
+	int target = cmd->device->id;
+	int lun = cmd->device->lun;
+
+	cmd->scsi_done = done;
+	cmd->host_scribble = NULL;
+	cmd->result = 0;
+	if (cmd->cmnd[0] == SYNCHRONIZE_CACHE) {	/* 0x35 avoid synchronizing disk cache cmd after .remove : arcmsr_device_remove (linux bug) */
+		if (pACB->devstate[target][lun] == ARECA_RAID_GONE) {
+			cmd->result = (DID_NO_CONNECT << 16);
+		}
+		cmd->scsi_done(cmd);
+		return (0);
+	}
+	if (pACB->acb_flags & ACB_F_BUS_RESET) {
+		printk("arcmsr%d bus reset and return busy \n",
+		       pACB->adapter_index);
+		cmd->result = (DID_BUS_BUSY << 16);
+		cmd->scsi_done(cmd);
+		return (0);
+	}
+	if (pACB->devstate[target][lun] == ARECA_RAID_GONE) {
+		uint8_t block_cmd;
+
+		block_cmd = cmd->cmnd[0] & 0x0f;
+		if (block_cmd == 0x08 || block_cmd == 0x0a) {
+			printk
+			    ("arcmsr%d block 'read/write' command with gone raid volume Cmd=%2x,TargetId=%d,Lun=%d \n",
+			     pACB->adapter_index, cmd->cmnd[0], target, lun);
+			cmd->result = (DID_NO_CONNECT << 16);
+			cmd->scsi_done(cmd);
+			return (0);
+		}
+	}
+	if ((pCCB = arcmsr_get_freeccb(pACB)) != NULL) {
+		arcmsr_build_ccb(pACB, pCCB, cmd);
+		if (atomic_read(&pACB->ccboutstandingcount) <
+		    ARCMSR_MAX_OUTSTANDING_CMD) {
+			/*
+			 ******************************************************************
+			 ** and we can make sure there were no pending ccb in this duration
+			 ******************************************************************
+			 */
+			arcmsr_post_ccb(pACB, pCCB);
+		} else {
+			/*
+			 ******************************************************************
+			 ** Q of ccbwaitexec will be post out when any outstanding command complete
+			 ******************************************************************
+			 */
+			arcmsr_queue_wait2go_ccb(pACB, pCCB);
+		}
+	} else {
+		printk
+		    ("arcmsr%d 'out of ccbs resource' ccb outstanding=%d pending=%d \n",
+		     pACB->adapter_index,
+		     atomic_read(&pACB->ccboutstandingcount),
+		     atomic_read(&pACB->ccbwait2gocount));
+		cmd->result = (DID_BUS_BUSY << 16);
+		cmd->scsi_done(cmd);
+	}
+	return (0);
+}
+
+/*
+**********************************************************************
+**  get firmware miscellaneous data
+**********************************************************************
+*/
+static void arcmsr_get_firmware_spec(struct _ACB *pACB)
+{
+	char *acb_firm_model = pACB->firm_model;
+	char *acb_firm_version = pACB->firm_version;
+	char *iop_firm_model = (char *)(&pACB->pmu->message_rbuffer[15]);	/*firm_model,15,60-67 */
+	char *iop_firm_version = (char *)(&pACB->pmu->message_rbuffer[17]);	/*firm_version,17,68-83 */
+	int count;
+
+	writel(ARCMSR_INBOUND_MESG0_GET_CONFIG, &pACB->pmu->inbound_msgaddr0);
+	if (arcmsr_wait_msgint_ready(pACB)) {
+		printk
+		    ("arcmsr%d wait 'get adapter firmware miscellaneous data' timeout \n",
+		     pACB->adapter_index);
+	}
+	count = 8;
+	while (count) {
+		*acb_firm_model = readb(iop_firm_model);
+		acb_firm_model++;
+		iop_firm_model++;
+		count--;
+	}
+	count = 16;
+	while (count) {
+		*acb_firm_version = readb(iop_firm_version);
+		acb_firm_version++;
+		iop_firm_version++;
+		count--;
+	}
+	printk("ARECA RAID ADAPTER%d: FIRMWARE VERSION %s \n",
+	       pACB->adapter_index, pACB->firm_version);
+	if (strncmp(pACB->firm_version, "V1.37", 5) < 0) {
+		printk
+		    ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
+		printk
+		    ("!!!!!!   PLEASE UPDATE RAID FIRMWARE VERSION EQUAL OR MORE THAN 'V1.37'   !!!!!!\n");
+		printk
+		    ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
+	}
+	pACB->firm_request_len = readl(&pACB->pmu->message_rbuffer[1]);	/*firm_request_len,1,04-07 */
+	pACB->firm_numbers_queue = readl(&pACB->pmu->message_rbuffer[2]);	/*firm_numbers_queue,2,08-11 */
+	pACB->firm_sdram_size = readl(&pACB->pmu->message_rbuffer[3]);	/*firm_sdram_size,3,12-15 */
+	pACB->firm_ide_channels = readl(&pACB->pmu->message_rbuffer[4]);	/*firm_ide_channels,4,16-19 */
+	return;
+}
+
+/*
+**********************************************************************
+**  start background rebulid
+**********************************************************************
+*/
+static void arcmsr_start_adapter_bgrb(struct _ACB *pACB)
+{
+	pACB->acb_flags |= ACB_F_MSG_START_BGRB;
+	pACB->acb_flags &= ~ACB_F_MSG_STOP_BGRB;
+	writel(ARCMSR_INBOUND_MESG0_START_BGRB, &pACB->pmu->inbound_msgaddr0);
+	return;
+}
+
+/*
+**********************************************************************
+**********************************************************************
+*/
+static void arcmsr_polling_ccbdone(struct _ACB *pACB, struct _CCB *poll_ccb)
+{
+	struct _CCB *pCCB;
+	uint32_t flag_ccb, outbound_intstatus, poll_ccb_done = 0, poll_count =
+	    0;
+	int id, lun;
+
+      polling_ccb_retry:
+	poll_count++;
+	outbound_intstatus =
+	    readl(&pACB->pmu->outbound_intstatus) & pACB->outbound_int_enable;
+	writel(outbound_intstatus, &pACB->pmu->outbound_intstatus);	/*clear interrupt */
+	while (1) {
+		if ((flag_ccb =
+		     readl(&pACB->pmu->outbound_queueport)) == 0xFFFFFFFF) {
+			if (poll_ccb_done) {
+				break;	/*chip FIFO no ccb for completion already */
+			} else {
+				msleep(25);
+				if (poll_count > 100) {
+					break;
+				}
+				goto polling_ccb_retry;
+			}
+		}
+		/* check ifcommand done with no error */
+		pCCB = (struct _CCB *)(pACB->vir2phy_offset + (flag_ccb << 5));	/*frame must be 32 bytes aligned */
+		if ((pCCB->pACB != pACB)
+		    || (pCCB->startdone != ARCMSR_CCB_START)) {
+			if ((pCCB->startdone == ARCMSR_CCB_ABORTED)
+			    && (pCCB == poll_ccb)) {
+				printk
+				    ("arcmsr%d scsi id=%d lun=%d ccb='0x%p' poll command abort successfully \n",
+				     pACB->adapter_index,
+				     pCCB->pcmd->device->id,
+				     pCCB->pcmd->device->lun, pCCB);
+				pCCB->pcmd->result = DID_ABORT << 16;
+				arcmsr_ccb_complete(pCCB);
+				poll_ccb_done = 1;
+				continue;
+			}
+			printk
+			    ("arcmsr%d polling and getting an illegal ccb command done ccb='0x%p' ccboutstandingcount=%d \n",
+			     pACB->adapter_index, pCCB,
+			     atomic_read(&pACB->ccboutstandingcount));
+			continue;
+		}
+		id = pCCB->pcmd->device->id;
+		lun = pCCB->pcmd->device->lun;
+		if ((flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR) == 0) {
+			if (pACB->devstate[id][lun] == ARECA_RAID_GONE) {
+				pACB->devstate[id][lun] = ARECA_RAID_GOOD;
+			}
+			pCCB->pcmd->result = DID_OK << 16;
+			arcmsr_ccb_complete(pCCB);
+		} else {
+			switch (pCCB->arcmsr_cdb.DeviceStatus) {
+			case ARCMSR_DEV_SELECT_TIMEOUT:
+				{
+					pACB->devstate[id][lun] =
+					    ARECA_RAID_GONE;
+					pCCB->pcmd->result = DID_TIME_OUT << 16;
+					arcmsr_ccb_complete(pCCB);
+				}
+				break;
+			case ARCMSR_DEV_ABORTED:
+			case ARCMSR_DEV_INIT_FAIL:
+				{
+					pACB->devstate[id][lun] =
+					    ARECA_RAID_GONE;
+					pCCB->pcmd->result =
+					    DID_BAD_TARGET << 16;
+					arcmsr_ccb_complete(pCCB);
+				}
+				break;
+			case SCSISTAT_CHECK_CONDITION:
+				{
+					pACB->devstate[id][lun] =
+					    ARECA_RAID_GOOD;
+					arcmsr_report_sense_info(pCCB);
+					arcmsr_ccb_complete(pCCB);
+				}
+				break;
+			default:
+				/* error occur Q all error ccb to errorccbpending Q */
+				printk
+				    ("arcmsr%d scsi id=%d lun=%d polling and getting command error done, but got unknow DeviceStatus=0x%x \n",
+				     pACB->adapter_index, id, lun,
+				     pCCB->arcmsr_cdb.DeviceStatus);
+				pACB->devstate[id][lun] = ARECA_RAID_GONE;
+				pCCB->pcmd->result = DID_BAD_TARGET << 16;	/*unknow error or crc error just for retry */
+				arcmsr_ccb_complete(pCCB);
+				break;
+			}
+		}
+	}			/*drain reply FIFO */
+	return;
+}
+
+/*
+**********************************************************************
+**  start background rebulid
+**********************************************************************
+*/
+static void arcmsr_iop_init(struct _ACB *pACB)
+{
+	uint32_t intmask_org, mask, outbound_doorbell, firmware_state = 0;
+
+	do {
+		firmware_state = readl(&pACB->pmu->outbound_msgaddr1);
+	} while ((firmware_state & ARCMSR_OUTBOUND_MESG1_FIRMWARE_OK) == 0);
+	intmask_org = readl(&pACB->pmu->outbound_intmask);	/*change "disable iop interrupt" to arcmsr_initialize */
+	arcmsr_get_firmware_spec(pACB);
+	/*start background rebuild */
+	arcmsr_start_adapter_bgrb(pACB);
+	if (arcmsr_wait_msgint_ready(pACB)) {
+		printk
+		    ("arcmsr%d wait 'start adapter background rebulid' timeout \n",
+		     pACB->adapter_index);
+	}
+	/* clear Qbuffer if door bell ringed */
+	outbound_doorbell = readl(&pACB->pmu->outbound_doorbell);
+	if (outbound_doorbell & ARCMSR_OUTBOUND_IOP331_DATA_WRITE_OK) {
+		writel(outbound_doorbell, &pACB->pmu->outbound_doorbell);	/*clear interrupt */
+		writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK,
+		       &pACB->pmu->inbound_doorbell);
+	}
+	/* enable outbound Post Queue,outbound message0,outbell doorbell Interrupt */
+	mask =
+	    ~(ARCMSR_MU_OUTBOUND_POSTQUEUE_INTMASKENABLE |
+	      ARCMSR_MU_OUTBOUND_DOORBELL_INTMASKENABLE |
+	      ARCMSR_MU_OUTBOUND_MESSAGE0_INTMASKENABLE);
+	writel(intmask_org & mask, &pACB->pmu->outbound_intmask);
+	pACB->outbound_int_enable = ~(intmask_org & mask) & 0x000000ff;
+	pACB->acb_flags |= ACB_F_IOP_INITED;
+	return;
+}
+
+/*
+****************************************************************************
+****************************************************************************
+*/
+static int arcmsr_bus_reset(struct scsi_cmnd *cmd)
+{
+	struct _ACB *pACB;
+	int retry = 0;
+
+	pACB = (struct _ACB *)cmd->device->host->hostdata;
+	printk("arcmsr%d bus reset ..... \n", pACB->adapter_index);
+	pACB->num_resets++;
+	pACB->acb_flags |= ACB_F_BUS_RESET;
+	while (atomic_read(&pACB->ccboutstandingcount) != 0 && retry < 400) {
+		printk
+		    ("arcmsr%d bus reset wait ccb outstanding command count equal to zero \n",
+		     pACB->adapter_index);
+		msleep_interruptible(25);
+		retry++;
+	}
+	arcmsr_iop_reset(pACB);
+	pACB->acb_flags &= ~ACB_F_BUS_RESET;
+	return SUCCESS;
+}
+
+/*
+*****************************************************************************************
+*****************************************************************************************
+*/
+static int arcmsr_seek_cmd2abort(struct scsi_cmnd *pabortcmd)
+{
+	struct _ACB *pACB = (struct _ACB *)pabortcmd->device->host->hostdata;
+	struct _CCB *pCCB;
+	uint32_t intmask_org, mask;
+	int i = 0;
+
+	pACB->num_aborts++;
+	/*
+	 *****************************************************************************
+	 ** It is the upper layer do abort command this lock just prior to calling us.
+	 ** First determine if we currently own this command.
+	 ** Start by searching the device queue. If not found
+	 ** at all,and the system wanted us to just abort the
+	 ** command return success.
+	 *****************************************************************************
+	 */
+	if (atomic_read(&pACB->ccboutstandingcount) != 0) {
+		for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) {
+			pCCB = pACB->pccb_pool[i];
+			if (pCCB->startdone == ARCMSR_CCB_START) {
+				if (pCCB->pcmd == pabortcmd) {
+					pCCB->startdone = ARCMSR_CCB_ABORTED;
+					printk
+					    ("arcmsr%d scsi id=%d lun=%d abort ccb '0x%p' outstanding command \n",
+					     pACB->adapter_index,
+					     pabortcmd->device->id,
+					     pabortcmd->device->lun, pCCB);
+					goto abort_outstanding_cmd;
+				}
+			}
+		}
+	}
+	/*
+	 **********************************************************
+	 ** seek this command at our command list
+	 ** if command found then remove,abort it and free this CCB
+	 **********************************************************
+	 */
+	if (atomic_read(&pACB->ccbwait2gocount) != 0) {
+		for (i = 0; i < ARCMSR_MAX_OUTSTANDING_CMD; i++) {
+			pCCB = pACB->pccbwait2go[i];
+			if (pCCB != NULL) {
+				if (pCCB->pcmd == pabortcmd) {
+					printk
+					    ("arcmsr%d scsi id=%d lun=%d abort ccb '0x%p' pending command \n",
+					     pACB->adapter_index,
+					     pabortcmd->device->id,
+					     pabortcmd->device->lun, pCCB);
+					pACB->pccbwait2go[i] = NULL;
+					pCCB->startdone = ARCMSR_CCB_ABORTED;
+					pCCB->pcmd->result = DID_ABORT << 16;
+					arcmsr_ccb_complete(pCCB);
+					atomic_dec(&pACB->ccbwait2gocount);
+					return (SUCCESS);
+				}
+			}
+		}
+	}
+	return (SUCCESS);
+      abort_outstanding_cmd:
+	/* disable all outbound interrupt */
+	intmask_org = readl(&pACB->pmu->outbound_intmask);
+	writel(intmask_org | ARCMSR_MU_OUTBOUND_ALL_INTMASKENABLE,
+	       &pACB->pmu->outbound_intmask);
+	/* do not talk to iop 331 abort command */
+	arcmsr_polling_ccbdone(pACB, pCCB);
+	/* enable all outbound interrupt */
+	mask =
+	    ~(ARCMSR_MU_OUTBOUND_POSTQUEUE_INTMASKENABLE |
+	      ARCMSR_MU_OUTBOUND_DOORBELL_INTMASKENABLE |
+	      ARCMSR_MU_OUTBOUND_MESSAGE0_INTMASKENABLE);
+	writel(intmask_org & mask, &pACB->pmu->outbound_intmask);
+	atomic_set(&pACB->ccboutstandingcount, 0);
+	return (SUCCESS);
+}
+
+/*
+*****************************************************************************************
+*****************************************************************************************
+*/
+static int arcmsr_cmd_abort(struct scsi_cmnd *cmd)
+{
+	struct _ACB *pACB = (struct _ACB *)cmd->device->host->hostdata;
+	int error;
+
+	printk("arcmsr%d abort device command of scsi id=%d lun=%d \n",
+	       pACB->adapter_index, cmd->device->id, cmd->device->lun);
+	/*
+	 ************************************************
+	 ** the all interrupt service routine is locked
+	 ** we need to handle it as soon as possible and exit
+	 ************************************************
+	 */
+	error = arcmsr_seek_cmd2abort(cmd);
+	if (error != SUCCESS) {
+		printk("arcmsr%d abort command failed scsi id=%d lun=%d \n",
+		       pACB->adapter_index, cmd->device->id, cmd->device->lun);
+	}
+	return (error);
+}
+
+/*
+*********************************************************************
+** arcmsr_info()
+**struct pci_dev {
+**	struct list_head      global_list;		## node in list of all PCI devices ##
+**	struct list_head      bus_list;	    	## node in per-bus list ##
+**	struct pci_bus	      *bus;		    	## bus this device is on ##
+**	struct pci_bus	      *subordinate;		## bus this device bridges to ##
+**	void		          *sysdata;	    	## hook for sys-specific extension ##
+**	struct proc_dir_entry *procent;	        ## device entry in /proc/bus/pci ##
+**	unsigned int	      devfn;		    ## encoded device & function index ##
+**	unsigned short	      vendor;
+**	unsigned short	      device;
+**	unsigned short	      subsystem_vendor;
+**	unsigned short	      subsystem_device;
+**	unsigned int	      class;		    ## 3 bytes: (base,sub,prog-if) ##
+**	u8		              hdr_type;	        ## PCI header type (`multi' flag masked out) ##
+**	u8		              rom_base_reg;	    ## which config register controls the ROM ##
+**
+**	struct pci_driver    *driver;	        ## which driver has allocated this device ##
+**	void		         *driver_data;	    ## data private to the driver ##
+**	u64		              dma_mask;	        ## Mask of the bits of bus address this device implements.  Normally this is
+**					                        ## 0xffffffff.  You only need to change this if your device has broken DMA
+**					                        ## or supports 64-bit transfers.
+**	u32                   current_state;    ## Current operating state. In ACPI-speak, this is D0-D3, D0 being fully functional, and D3 being off. ##
+**	unsigned short vendor_compatible[DEVICE_COUNT_COMPATIBLE]; ## device is compatible with these IDs ##
+**	unsigned short device_compatible[DEVICE_COUNT_COMPATIBLE];
+**	 										##
+**	 										##Instead of touching interrupt line and base address registers
+**	 										##directly, use the values stored here. They might be different!
+**	 										##
+**	unsigned int	      irq;
+**	struct resource       resource[DEVICE_COUNT_RESOURCE]; ## I/O and memory regions + expansion ROMs ##
+**	struct resource       dma_resource[DEVICE_COUNT_DMA];
+**	struct resource       irq_resource[DEVICE_COUNT_IRQ];
+**	char		          name[90];	                       ## device name ##
+**	char		          slot_name[8];	                   ## slot name ##
+**	u32		              saved_state[16];                 ## for saving the config space before suspend ##
+**	int		              active;		                   ## ISAPnP: device is active ##
+**	int		              ro;		                       ## ISAPnP: read only ##
+**	unsigned short	      regs;		                       ## ISAPnP: supported registers ##
+**	                                                       ## These fields are used by common fixups ##
+**	unsigned short	      transparent:1;	               ## Transparent PCI bridge ##
+**	int (*prepare)(struct pci_dev *dev);	               ## ISAPnP hooks ##
+**	int (*activate)(struct pci_dev *dev);
+**	int (*deactivate)(struct pci_dev *dev);
+**};
+**
+*********************************************************************
+*/
+static const char *arcmsr_info(struct Scsi_Host *host)
+{
+	static char buf[256];
+	struct _ACB *pACB;
+	uint16_t device_id;
+
+	pACB = (struct _ACB *)host->hostdata;
+	device_id = pACB->pPCI_DEV->device;
+	switch (device_id) {
+	case PCIDeviceIDARC1110:
+		{
+			sprintf(buf,
+				"ARECA ARC1110 PCI-X 4 PORTS SATA RAID CONTROLLER\n        %s",
+				ARCMSR_DRIVER_VERSION);
+			break;
+		}
+	case PCIDeviceIDARC1120:
+		{
+			sprintf(buf,
+				"ARECA ARC1120 PCI-X 8 PORTS SATA RAID CONTROLLER (RAID6-ENGINE Inside)\n        %s",
+				ARCMSR_DRIVER_VERSION);
+			break;
+		}
+	case PCIDeviceIDARC1130:
+		{
+			sprintf(buf,
+				"ARECA ARC1130 PCI-X 12 PORTS SATA RAID CONTROLLER (RAID6-ENGINE Inside)\n        %s",
+				ARCMSR_DRIVER_VERSION);
+			break;
+		}
+	case PCIDeviceIDARC1160:
+		{
+			sprintf(buf,
+				"ARECA ARC1160 PCI-X 16 PORTS SATA RAID CONTROLLER (RAID6-ENGINE Inside)\n        %s",
+				ARCMSR_DRIVER_VERSION);
+			break;
+		}
+	case PCIDeviceIDARC1170:
+		{
+			sprintf(buf,
+				"ARECA ARC1170 PCI-X 24 PORTS SATA RAID CONTROLLER (RAID6-ENGINE Inside)\n        %s",
+				ARCMSR_DRIVER_VERSION);
+			break;
+		}
+	case PCIDeviceIDARC1210:
+		{
+			sprintf(buf,
+				"ARECA ARC1210 PCI-EXPRESS 4 PORTS SATA RAID CONTROLLER\n        %s",
+				ARCMSR_DRIVER_VERSION);
+			break;
+		}
+	case PCIDeviceIDARC1220:
+		{
+			sprintf(buf,
+				"ARECA ARC1220 PCI-EXPRESS 8 PORTS SATA RAID CONTROLLER (RAID6-ENGINE Inside)\n        %s",
+				ARCMSR_DRIVER_VERSION);
+			break;
+		}
+	case PCIDeviceIDARC1230:
+		{
+			sprintf(buf,
+				"ARECA ARC1230 PCI-EXPRESS 12 PORTS SATA RAID CONTROLLER (RAID6-ENGINE Inside)\n        %s",
+				ARCMSR_DRIVER_VERSION);
+			break;
+		}
+	case PCIDeviceIDARC1260:
+		{
+			sprintf(buf,
+				"ARECA ARC1260 PCI-EXPRESS 16 PORTS SATA RAID CONTROLLER (RAID6-ENGINE Inside)\n        %s",
+				ARCMSR_DRIVER_VERSION);
+			break;
+		}
+	case PCIDeviceIDARC1270:
+		{
+			sprintf(buf,
+				"ARECA ARC1270 PCI-EXPRESS 24 PORTS SATA RAID CONTROLLER (RAID6-ENGINE Inside)\n        %s",
+				ARCMSR_DRIVER_VERSION);
+			break;
+		}
+	}
+	return buf;
+}
+
+/*
+************************************************************************
+************************************************************************
+*/
+static int arcmsr_initialize(struct _ACB *pACB, struct pci_dev *pPCI_DEV)
+{
+	uint32_t intmask_org, page_base, page_offset, mem_base_start;
+	dma_addr_t dma_coherent_handle, dma_addr;
+	struct _HCBARC *pHCBARC = &arcmsr_host_control_block;
+	uint8_t pcicmd;
+	void *dma_coherent;
+	void *page_remapped;
+	int i, j;
+	struct _CCB *pccb_tmp;
+
+	/* Enable Busmaster/Mem */
+	pci_read_config_byte(pPCI_DEV, PCI_COMMAND, &pcicmd);
+	pci_write_config_byte(pPCI_DEV, PCI_COMMAND,
+			      pcicmd | PCI_COMMAND_INVALIDATE |
+			      PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
+	mem_base_start = (uint32_t) pci_resource_start(pPCI_DEV, 0);
+	page_base = mem_base_start & PAGE_MASK;
+	page_offset = mem_base_start - page_base;
+	page_remapped = ioremap(page_base, page_offset + 0x1FFF);
+	if (page_remapped == NULL) {
+		printk("arcmsr%d memory mapping region fail \n",
+		       arcmsr_adapterCnt);
+		return (ENXIO);
+	}
+	pACB->pmu = (struct _MU *)(page_remapped + page_offset);
+	pACB->acb_flags |=
+	    (ACB_F_IOCTL_WQBUFFER_CLEARED | ACB_F_IOCTL_RQBUFFER_CLEARED);
+	pACB->acb_flags &= ~ACB_F_SCSISTOPADAPTER;
+	pACB->irq = pPCI_DEV->irq;
+	/*
+	 *******************************************************************************
+	 **                 Allocate the PCCB memory
+	 ******************************************************
+	 **   Using large dma-coherent buffers
+	 **   ------------------------------------------
+	 **   void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, int flag)
+	 **   void *pci_alloc_consistent(struct pci_dev *dev, size_t size, dma_addr_t *dma_handle)
+	 **   Consistent memory is memory for which a write by either the device or
+	 **   the processor can immediately be read by the processor or device
+	 **   without having to worry about caching effects.
+	 **   This routine allocates a region of <size> bytes of consistent memory.
+	 **   it also returns a <dma_handle> which may be cast to an unsigned
+	 **   integer the same width as the bus and used as the physical address
+	 **   base of the region.
+	 **   Returns: a pointer to the allocated region (in the processor's virtual
+	 **   address space) or NULL if the allocation failed.
+	 **   Note: consistent memory can be expensive on some platforms, and the
+	 **   minimum allocation length may be as big as a page, so you should
+	 **   consolidate your requests for consistent memory as much as possible.
+	 **   The simplest way to do that is to use the dma_pool calls (see below).
+	 **   The flag parameter (dma_alloc_coherent only) allows the caller to
+	 **   specify the GFP_ flags (see kmalloc) for the allocation (the
+	 **   implementation may chose to ignore flags that affect the location of
+	 **   the returned memory, like GFP_DMA).  For pci_alloc_consistent, you
+	 **   must assume GFP_ATOMIC behaviour.
+	 **   void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr dma_addr_t dma_handle)
+	 **   void pci_free_consistent(struct pci_dev *dev, size_t size, void *cpu_addr dma_addr_t dma_handle)
+	 **   Free the region of consistent memory you previously allocated.  dev,
+	 **   size and dma_handle must all be the same as those passed into the
+	 **   consistent allocate.  cpu_addr must be the virtual address returned by
+	 **   the consistent allocate
+	 ******************************************************************************
+	 */
+	/* Attempt to claim larger area for request queue pCCB). */
+	dma_coherent =
+	    dma_alloc_coherent(&pPCI_DEV->dev,
+			       ARCMSR_MAX_FREECCB_NUM * sizeof(struct _CCB) +
+			       0x20, &dma_coherent_handle, GFP_KERNEL);
+	if (dma_coherent == NULL) {
+		printk("arcmsr%d dma_alloc_coherent got error \n",
+		       arcmsr_adapterCnt);
+		return -ENOMEM;
+	}
+	pACB->dma_coherent = dma_coherent;
+	pACB->dma_coherent_handle = dma_coherent_handle;
+	memset(dma_coherent, 0,
+	       ARCMSR_MAX_FREECCB_NUM * sizeof(struct _CCB) + 0x20);
+	if (((unsigned long)dma_coherent & 0x1F) != 0) {	/*ccb address must 32 (0x20) boundary */
+		dma_coherent =
+		    dma_coherent + (0x20 -
+				    ((unsigned long)dma_coherent & 0x1F));
+		dma_coherent_handle =
+		    dma_coherent_handle + (0x20 -
+					   ((unsigned long)dma_coherent_handle &
+					    0x1F));
+	}
+	dma_addr = dma_coherent_handle;
+	pccb_tmp = (struct _CCB *)dma_coherent;
+	for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) {
+		pccb_tmp->cdb_shifted_phyaddr = dma_addr >> 5;
+		pccb_tmp->pACB = pACB;
+		pACB->pccbringQ[i] = pACB->pccb_pool[i] = pccb_tmp;
+		dma_addr = dma_addr + sizeof(struct _CCB);
+		pccb_tmp++;
+	}
+	pACB->vir2phy_offset =
+	    (unsigned long)pccb_tmp - (unsigned long)dma_addr;
+	/*
+	 ********************************************************************
+	 ** init raid volume state
+	 ********************************************************************
+	 */
+	for (i = 0; i < ARCMSR_MAX_TARGETID; i++) {
+		for (j = 0; j < ARCMSR_MAX_TARGETLUN; j++) {
+			pACB->devstate[i][j] = ARECA_RAID_GOOD;
+		}
+	}
+	/*
+	 ********************************************************************
+	 ** here we need to tell iop 331 our pccb_tmp.HighPart
+	 ** if pccb_tmp.HighPart is not zero
+	 ********************************************************************
+	 */
+	pACB->adapter_index = arcmsr_adapterCnt;
+	pHCBARC->pACB[arcmsr_adapterCnt] = pACB;
+	/* disable iop all outbound interrupt */
+	intmask_org = readl(&pACB->pmu->outbound_intmask);
+	writel(intmask_org | ARCMSR_MU_OUTBOUND_ALL_INTMASKENABLE,
+	       &pACB->pmu->outbound_intmask);
+	arcmsr_adapterCnt++;
+	return (0);
+}
+
+/*
+*********************************************************************
+*********************************************************************
+*/
+static int arcmsr_set_info(char *buffer, int length)
+{
+	return (0);
+}
+
+/*
+*********************************************************************
+*********************************************************************
+*/
+static void arcmsr_pcidev_disattach(struct _ACB *pACB)
+{
+	struct _CCB *pCCB;
+	struct _HCBARC *pHCBARC = &arcmsr_host_control_block;
+	uint32_t intmask_org, mask;
+	int i = 0;
+
+	/* disable all outbound interrupt */
+	intmask_org = readl(&pACB->pmu->outbound_intmask);
+	writel(intmask_org | ARCMSR_MU_OUTBOUND_ALL_INTMASKENABLE,
+	       &pACB->pmu->outbound_intmask);
+	/* stop adapter background rebuild */
+	arcmsr_stop_adapter_bgrb(pACB);
+	if (arcmsr_wait_msgint_ready(pACB)) {
+		printk
+		    ("arcmsr%d pcidev disattach wait 'stop adapter rebulid' timeout \n",
+		     pACB->adapter_index);
+	}
+	arcmsr_flush_adapter_cache(pACB);
+	if (arcmsr_wait_msgint_ready(pACB)) {
+		printk
+		    ("arcmsr%d pcidev disattach wait 'flush adapter cache' timeout \n",
+		     pACB->adapter_index);
+	}
+	/* abort all outstanding command */
+	pACB->acb_flags |= ACB_F_SCSISTOPADAPTER;
+	pACB->acb_flags &= ~ACB_F_IOP_INITED;
+	if (atomic_read(&pACB->ccboutstandingcount) != 0) {
+		/* disable all outbound interrupt */
+		intmask_org = readl(&pACB->pmu->outbound_intmask);
+		writel(intmask_org | ARCMSR_MU_OUTBOUND_ALL_INTMASKENABLE,
+		       &pACB->pmu->outbound_intmask);
+		/* talk to iop 331 outstanding command aborted */
+		arcmsr_abort_allcmd(pACB);
+		if (arcmsr_wait_msgint_ready(pACB)) {
+			printk
+			    ("arcmsr%d pcidev disattach wait 'abort all outstanding command' timeout \n",
+			     pACB->adapter_index);
+		}
+		/*clear all outbound posted Q */
+		for (i = 0; i < ARCMSR_MAX_OUTSTANDING_CMD; i++) {
+			readl(&pACB->pmu->outbound_queueport);
+		}
+		for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) {
+			pCCB = pACB->pccb_pool[i];
+			if (pCCB->startdone == ARCMSR_CCB_START) {
+				pCCB->startdone = ARCMSR_CCB_ABORTED;
+			}
+		}
+		/* enable all outbound interrupt */
+		mask =
+		    ~(ARCMSR_MU_OUTBOUND_POSTQUEUE_INTMASKENABLE |
+		      ARCMSR_MU_OUTBOUND_DOORBELL_INTMASKENABLE |
+		      ARCMSR_MU_OUTBOUND_MESSAGE0_INTMASKENABLE);
+		writel(intmask_org & mask, &pACB->pmu->outbound_intmask);
+		atomic_set(&pACB->ccboutstandingcount, 0);
+	}
+	if (atomic_read(&pACB->ccbwait2gocount) != 0) {	/*remove first wait2go ccb and abort it */
+		for (i = 0; i < ARCMSR_MAX_OUTSTANDING_CMD; i++) {
+			pCCB = pACB->pccbwait2go[i];
+			if (pCCB != NULL) {
+				pACB->pccbwait2go[i] = NULL;
+				pCCB->startdone = ARCMSR_CCB_ABORTED;
+				pCCB->pcmd->result = DID_ABORT << 16;
+				arcmsr_ccb_complete(pCCB);
+				atomic_dec(&pACB->ccbwait2gocount);
+			}
+		}
+	}
+	free_irq(pACB->pPCI_DEV->irq, pACB);
+	pci_release_regions(pACB->pPCI_DEV);
+	iounmap(pACB->pmu);
+	arcmsr_free_ccb_pool(pACB);
+	pHCBARC->pACB[pACB->adapter_index] = 0;	/* clear record */
+	arcmsr_adapterCnt--;
+	return;
+}
+
+/*
+***************************************************************
+***************************************************************
+*/
+static int arcmsr_halt_notify(struct notifier_block *nb, unsigned long event,
+			      void *buf)
+{
+	struct _ACB *pACB;
+	struct _HCBARC *pHCBARC = &arcmsr_host_control_block;
+	struct Scsi_Host *host;
+	int i;
+
+	if ((event != SYS_RESTART) && (event != SYS_HALT)
+	    && (event != SYS_POWER_OFF)) {
+		return NOTIFY_DONE;
+	}
+	for (i = 0; i < ARCMSR_MAX_ADAPTER; i++) {
+		pACB = pHCBARC->pACB[i];
+		if (pACB == NULL) {
+			continue;
+		}
+		/* Flush cache to disk */
+		/* Free irq,otherwise extra interrupt is generated       */
+		/* Issue a blocking(interrupts disabled) command to the card */
+		host = pACB->host;
+		arcmsr_pcidev_disattach(pACB);
+		scsi_remove_host(host);
+		scsi_host_put(host);
+	}
+	unregister_chrdev(pHCBARC->arcmsr_major_number, "arcmsr");
+	unregister_reboot_notifier(&arcmsr_event_notifier);
+	return NOTIFY_OK;
+}
+
+/*
+*********************************************************************
+*********************************************************************
+*/
+#undef SPRINTF
+#define SPRINTF(args...) pos +=sprintf(pos,## args)
+#define YESNO(YN)\
+if(YN) SPRINTF(" Yes ");\
+else SPRINTF(" No ")
+
+static int arcmsr_proc_info(struct Scsi_Host *host, char *buffer, char **start,
+			    off_t offset, int length, int inout)
+{
+	uint8_t i;
+	struct _HCBARC *pHCBARC = &arcmsr_host_control_block;
+	char *pos = buffer;
+	struct _ACB *pACB;
+
+	if (inout) {
+		return (arcmsr_set_info(buffer, length));
+	}
+	for (i = 0; i < ARCMSR_MAX_ADAPTER; i++) {
+		pACB = pHCBARC->pACB[i];
+		if (pACB == NULL)
+			continue;
+		SPRINTF("ARECA SATA RAID Mass Storage Host Adadpter \n");
+		SPRINTF("Driver Version %s ", ARCMSR_DRIVER_VERSION);
+		SPRINTF("IRQ%d \n", pACB->pPCI_DEV->irq);
+		SPRINTF("===========================\n");
+	}
+	*start = buffer + offset;
+	if (pos - buffer < offset) {
+		return 0;
+	} else if (pos - buffer - offset < length) {
+		return (pos - buffer - offset);
+	} else {
+		return length;
+	}
+}
+
+/*
+************************************************************************
+************************************************************************
+*/
+static int arcmsr_release(struct Scsi_Host *host)
+{
+	struct _ACB *pACB;
+	struct _HCBARC *pHCBARC = &arcmsr_host_control_block;
+	uint8_t match = 0xff, i;
+
+	if (host == NULL) {
+		return -ENXIO;
+	}
+	pACB = (struct _ACB *)host->hostdata;
+	if (pACB == NULL) {
+		return -ENXIO;
+	}
+	for (i = 0; i < ARCMSR_MAX_ADAPTER; i++) {
+		if (pHCBARC->pACB[i] == pACB) {
+			match = i;
+		}
+	}
+	if (match == 0xff) {
+		return -ENXIO;
+	}
+	/* Flush cache to disk */
+	/* Free irq,otherwise extra interrupt is generated       */
+	/* Issue a blocking(interrupts disabled) command to the card */
+	arcmsr_pcidev_disattach(pACB);
+	scsi_unregister(host);
+	/*if this is last pACB */
+	for (i = 0; i < ARCMSR_MAX_ADAPTER; i++) {
+		if (pHCBARC->pACB[i] != NULL) {
+			return (0);	/* this is not last adapter's release */
+		}
+	}
+	unregister_chrdev(pHCBARC->arcmsr_major_number, "arcmsr");
+	unregister_reboot_notifier(&arcmsr_event_notifier);
+	return (0);
+}
diff -urN oldtree/drivers/scsi/arcmsr/arcmsr.h newtree/drivers/scsi/arcmsr/arcmsr.h
--- oldtree/drivers/scsi/arcmsr/arcmsr.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/arcmsr/arcmsr.h	2006-02-13 19:20:00.623417528 -0500
@@ -0,0 +1,4658 @@
+/*
+***********************************************************************************************
+**        O.S   : Linux
+**   FILE NAME  : arcmsr.h
+**        BY    : Erich Chen
+**   Description: SCSI RAID Device Driver for
+**                ARECA RAID Host adapter
+***********************************************************************************************
+** Copyright (C) 2002 - 2005, Areca Technology Corporation All rights reserved.
+**
+**     Web site: www.areca.com.tw
+**       E-mail: erich@areca.com.tw
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License version 2 as
+** published by the Free Software Foundation.
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+************************************************************************
+** Redistribution and use in source and binary forms,with or without
+** modification,are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+**    notice,this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+**    notice,this list of conditions and the following disclaimer in the
+**    documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+**    derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES,INCLUDING,BUT NOT LIMITED TO,THE IMPLIED WARRANTIES
+** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,INDIRECT,
+** INCIDENTAL,SPECIAL,EXEMPLARY,OR CONSEQUENTIAL DAMAGES(INCLUDING,BUT
+** NOT LIMITED TO,PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA,OR PROFITS; OR BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY,WHETHER IN CONTRACT,STRICT LIABILITY,OR TORT
+**(INCLUDING NEGLIGENCE OR OTHERWISE)ARISING IN ANY WAY OUT OF THE USE OF
+** THIS SOFTWARE,EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**************************************************************************
+*/
+#include <linux/config.h>
+#include <linux/version.h>
+/*
+**********************************************************************************
+**
+**********************************************************************************
+*/
+#define ARCMSR_MAX_OUTSTANDING_CMD                                            256
+#define ARCMSR_MAX_FREECCB_NUM                                                320
+#define ARCMSR_DRIVER_VERSION                         "Driver Version 1.20.00.12"
+#define ARCMSR_SCSI_INITIATOR_ID                                               16
+#define ARCMSR_DEV_SECTOR_SIZE                                                512
+#define ARCMSR_MAX_XFER_SECTORS                                               256
+#define ARCMSR_MAX_XFER_LEN      ARCMSR_MAX_XFER_SECTORS * ARCMSR_DEV_SECTOR_SIZE	/*128k */
+#define ARCMSR_MAX_TARGETID                                                    16	/*16 max target id + 1 */
+#define ARCMSR_MAX_TARGETLUN                                                    8	/*8 */
+#define ARCMSR_MAX_CHIPTYPE_NUM                                                 4
+#define ARCMSR_MAX_CMD_PERLUN                          ARCMSR_MAX_OUTSTANDING_CMD	/* if eq. 256 will get kernel panic at 2.2.x */
+#define ARCMSR_MAX_DPC                                                         16	/* defer procedure call */
+#define ARCMSR_MAX_QBUFFER                                                   4096	/* ioctl QBUFFER */
+#define ARCMSR_MAX_SG_ENTRIES                                                  38	/* max 38 */
+#define ARCMSR_MAX_ADAPTER                                                      4
+/*
+**********************************************************************************
+**
+**********************************************************************************
+*/
+#define PCIVendorIDARECA                                             0x17D3	/* Vendor ID    */
+#define PCIDeviceIDARC1110                                           0x1110	/* Device ID    */
+#define PCIDeviceIDARC1120                                           0x1120	/* Device ID        */
+#define PCIDeviceIDARC1130                                           0x1130	/* Device ID        */
+#define PCIDeviceIDARC1160                                           0x1160	/* Device ID        */
+#define PCIDeviceIDARC1170                                           0x1170	/* Device ID        */
+#define PCIDeviceIDARC1210                                           0x1210	/* Device ID    */
+#define PCIDeviceIDARC1220                                           0x1220	/* Device ID        */
+#define PCIDeviceIDARC1230                                           0x1230	/* Device ID        */
+#define PCIDeviceIDARC1260                                           0x1260	/* Device ID        */
+#define PCIDeviceIDARC1270                                           0x1270	/* Device ID        */
+/*
+**********************************************************************************
+**((unsigned long)addr & 0xffff)
+**********************************************************************************
+*/
+#define dma_addr_hi32(addr)               (uint32_t) ((addr>>16)>>16)
+#define dma_addr_lo32(addr)               (uint32_t) (addr & 0xffffffff)
+
+#ifndef DMA_64BIT_MASK
+#define DMA_64BIT_MASK	              0xffffffffffffffffULL
+#define DMA_32BIT_MASK	              0x00000000ffffffffULL
+#endif
+/*
+************************************************************************
+**        IOCTL CONTROL CODE
+************************************************************************
+*/
+typedef struct _CMD_IO_CONTROL {
+	uint32_t HeaderLength;
+	uint8_t Signature[8];
+	uint32_t Timeout;
+	uint32_t ControlCode;
+	uint32_t ReturnCode;
+	uint32_t Length;
+} CMD_IO_CONTROL, *PCMD_IO_CONTROL;
+/*
+************************************************************************************************************
+**
+************************************************************************************************************
+*/
+typedef struct _CMD_IOCTL_FIELD {
+	CMD_IO_CONTROL cmdioctl;	/*ioctl header */
+	uint8_t ioctldatabuffer[1032];	/*areca gui program does not accept more than 1031 byte */
+} CMD_IOCTL_FIELD, *PCMD_IOCTL_FIELD;
+/*error code for StorPortLogError,ScsiPortLogError*/
+#define ARCMSR_IOP_ERROR_ILLEGALPCI            	0x0001
+#define ARCMSR_IOP_ERROR_VENDORID              	0x0002
+#define ARCMSR_IOP_ERROR_DEVICEID              	0x0002
+#define ARCMSR_IOP_ERROR_ILLEGALCDB           	0x0003
+#define ARCMSR_IOP_ERROR_UNKNOW_CDBERR        	0x0004
+#define ARCMSR_SYS_ERROR_MEMORY_ALLOCATE    	0x0005
+#define ARCMSR_SYS_ERROR_MEMORY_CROSS4G     	0x0006
+#define ARCMSR_SYS_ERROR_MEMORY_LACK        	0x0007
+#define ARCMSR_SYS_ERROR_MEMORY_RANGE           0x0008
+#define ARCMSR_SYS_ERROR_DEVICE_BASE            0x0009
+#define ARCMSR_SYS_ERROR_PORT_VALIDATE          0x000A
+/*DeviceType*/
+#define ARECA_SATA_RAID                      	0x90000000
+/*FunctionCode*/
+#define FUNCTION_READ_RQBUFFER               	0x0801
+#define FUNCTION_WRITE_WQBUFFER              	0x0802
+#define FUNCTION_CLEAR_RQBUFFER              	0x0803
+#define FUNCTION_CLEAR_WQBUFFER              	0x0804
+#define FUNCTION_CLEAR_ALLQBUFFER            	0x0805
+#define FUNCTION_RETURN_CODE_3F              	0x0806
+#define FUNCTION_SAY_HELLO                   	0x0807
+#define FUNCTION_SAY_GOODBYE                    0x0808
+#define FUNCTION_FLUSH_ADAPTER_CACHE           	0x0809
+/* ARECA IO CONTROL CODE*/
+#define ARCMSR_IOCTL_READ_RQBUFFER           	ARECA_SATA_RAID | FUNCTION_READ_RQBUFFER
+#define ARCMSR_IOCTL_WRITE_WQBUFFER          	ARECA_SATA_RAID | FUNCTION_WRITE_WQBUFFER
+#define ARCMSR_IOCTL_CLEAR_RQBUFFER          	ARECA_SATA_RAID | FUNCTION_CLEAR_RQBUFFER
+#define ARCMSR_IOCTL_CLEAR_WQBUFFER          	ARECA_SATA_RAID | FUNCTION_CLEAR_WQBUFFER
+#define ARCMSR_IOCTL_CLEAR_ALLQBUFFER        	ARECA_SATA_RAID | FUNCTION_CLEAR_ALLQBUFFER
+#define ARCMSR_IOCTL_RETURN_CODE_3F          	ARECA_SATA_RAID | FUNCTION_RETURN_CODE_3F
+#define ARCMSR_IOCTL_SAY_HELLO               	ARECA_SATA_RAID | FUNCTION_SAY_HELLO
+#define ARCMSR_IOCTL_SAY_GOODBYE                ARECA_SATA_RAID | FUNCTION_SAY_GOODBYE
+#define ARCMSR_IOCTL_FLUSH_ADAPTER_CACHE        ARECA_SATA_RAID | FUNCTION_FLUSH_ADAPTER_CACHE
+/* ARECA IOCTL ReturnCode */
+#define ARCMSR_IOCTL_RETURNCODE_OK              0x00000001
+#define ARCMSR_IOCTL_RETURNCODE_ERROR           0x00000006
+#define ARCMSR_IOCTL_RETURNCODE_3F              0x0000003F
+/*
+*************************************************************
+**   structure for holding DMA address data
+*************************************************************
+*/
+#define IS_SG64_ADDR                0x01000000	/* bit24 */
+typedef struct _SG32ENTRY {	/* size 8 bytes *//* length bit 24 == 0                      */
+	uint32_t length;	/* high 8 bit == flag,low 24 bit == length */
+	uint32_t address;
+} SG32ENTRY, *PSG32ENTRY;
+typedef struct _SG64ENTRY {	/* size 12 bytes *//* length bit 24 == 1                      */
+	uint32_t length;	/* high 8 bit == flag,low 24 bit == length */
+	uint32_t address;
+	uint32_t addresshigh;
+} SG64ENTRY, *PSG64ENTRY;
+typedef struct _SGENTRY_UNION {
+	union {
+		SG32ENTRY sg32entry;	/* 30h   Scatter gather address  */
+		SG64ENTRY sg64entry;	/* 30h                           */
+	} u;
+} SGENTRY_UNION, PSGENTRY_UNION;
+/*
+**********************************
+**
+**********************************
+*/
+typedef struct _QBUFFER {
+	uint32_t data_len;
+	uint8_t data[124];
+} QBUFFER, *PQBUFFER;
+/*
+************************************************************************************************
+**      FIRMWARE INFO
+************************************************************************************************
+*/
+typedef struct _FIRMWARE_INFO {
+	uint32_t signature;	/*0,00-03 */
+	uint32_t request_len;	/*1,04-07 */
+	uint32_t numbers_queue;	/*2,08-11 */
+	uint32_t sdram_size;	/*3,12-15 */
+	uint32_t ide_channels;	/*4,16-19 */
+	char vendor[40];	/*5,20-59 */
+	char model[8];		/*15,60-67 */
+	char firmware_ver[16];	/*17,68-83 */
+	char device_map[16];	/*21,84-99 */
+} FIRMWARE_INFO, *PFIRMWARE_INFO;
+/*
+************************************************************************************************
+**                            ARECA FIRMWARE SPEC
+************************************************************************************************
+**		Usage of IOP331 adapter
+**		(All In/Out is in IOP331's view)
+**		1. Message 0 --> InitThread message and retrun code
+**		2. Doorbell is used for RS-232 emulation
+**				inDoorBell :    bit0 -- data in ready            (DRIVER DATA WRITE OK)
+**								bit1 -- data out has been read   (DRIVER DATA READ OK)
+**				outDooeBell:    bit0 -- data out ready           (IOP331 DATA WRITE OK)
+**								bit1 -- data in has been read    (IOP331 DATA READ OK)
+**		3. Index Memory Usage
+**			offset 0xf00 : for RS232 out (request buffer)
+**			offset 0xe00 : for RS232 in  (scratch buffer)
+**			offset 0x800 : for inbound message code message_wbuffer (driver send to IOP331)
+**			offset 0xa00 : for outbound message code message_rbuffer (IOP331 send to driver)
+**		4. RS-232 emulation
+**			Currently 128 byte buffer is used
+**			          1st uint32_t : Data length (1--124)
+**			        Byte 4--127 : Max 124 bytes of data
+**		5. PostQ
+**		All SCSI Command must be sent through postQ:
+**		(inbound queue port)	Request frame must be 32 bytes aligned
+**            #   bit27--bit31 => flag for post ccb
+**			  #   bit0--bit26 => real address (bit27--bit31) of post arcmsr_cdb
+**													bit31 : 0 : 256 bytes frame
+**															1 : 512 bytes frame
+**													bit30 : 0 : normal request
+**															1 : BIOS request
+**                                                  bit29 : reserved
+**                                                  bit28 : reserved
+**                                                  bit27 : reserved
+**  -------------------------------------------------------------------------------
+**		(outbount queue port)	Request reply
+**            #   bit27--bit31 => flag for reply
+**			  #   bit0--bit26 => real address (bit27--bit31) of reply arcmsr_cdb
+**													bit31 : must be 0 (for this type of reply)
+**													bit30 : reserved for BIOS handshake
+**													bit29 : reserved
+**													bit28 : 0 : no error, ignore AdapStatus/DevStatus/SenseData
+**															1 : Error, error code in AdapStatus/DevStatus/SenseData
+**													bit27 : reserved
+**		6. BIOS request
+**			All BIOS request is the same with request from PostQ
+**			Except :
+**				Request frame is sent from configuration space
+**								offset: 0x78 : Request Frame (bit30 == 1)
+**								offset: 0x18 : writeonly to generate IRQ to IOP331
+**				Completion of request:
+**				                      (bit30 == 0, bit28==err flag)
+**		7. Definition of SGL entry (structure)
+**		8. Message1 Out - Diag Status Code (????)
+**		9. Message0 message code :
+**			0x00 : NOP
+**			0x01 : Get Config ->offset 0xa00 :for outbound message code message_rbuffer (IOP331 send to driver)
+**												Signature             0x87974060(4)
+**												Request len           0x00000200(4)
+**												numbers of queue      0x00000100(4)
+**												SDRAM Size            0x00000100(4)-->256 MB
+**												IDE Channels          0x00000008(4)
+**												vendor                40 bytes char
+**												model                  8 bytes char
+**												FirmVer               16 bytes char
+**                          					Device Map            16 bytes char
+**
+**                          					FirmwareVersion DWORD <== Added for checking of new firmware capability
+**			0x02 : Set Config ->offset 0x800 : for inbound message code message_wbuffer (driver send to IOP331)
+**												Signature             0x87974063(4)
+**												UPPER32 of Request Frame  (4)-->Driver Only
+**			0x03 : Reset (Abort all queued Command)
+**			0x04 : Stop Background Activity
+**			0x05 : Flush Cache
+**			0x06 : Start Background Activity (re-start if background is halted)
+**			0x07 : Check If Host Command Pending (Novell May Need This Function)
+**			0x08 : Set controller time ->offset 0xa00 : for inbound message code message_wbuffer (driver to IOP331)
+**			        							byte 0 : 0xaa <-- signature
+**                          					byte 1 : 0x55 <-- signature
+**			        							byte 2 : year (04)
+**			        							byte 3 : month (1..12)
+**			        							byte 4 : date (1..31)
+**			        							byte 5 : hour (0..23)
+**			        							byte 6 : minute (0..59)
+**			        							byte 7 : second (0..59)
+************************************************************************************************
+*/
+/* message code of inbound message register */
+#define ARCMSR_INBOUND_MESG0_NOP                      0x00000000
+#define ARCMSR_INBOUND_MESG0_GET_CONFIG               0x00000001
+#define ARCMSR_INBOUND_MESG0_SET_CONFIG               0x00000002
+#define ARCMSR_INBOUND_MESG0_ABORT_CMD                0x00000003
+#define ARCMSR_INBOUND_MESG0_STOP_BGRB                0x00000004
+#define ARCMSR_INBOUND_MESG0_FLUSH_CACHE              0x00000005
+#define ARCMSR_INBOUND_MESG0_START_BGRB               0x00000006
+#define ARCMSR_INBOUND_MESG0_CHK331PENDING            0x00000007
+#define ARCMSR_INBOUND_MESG0_SYNC_TIMER               0x00000008
+/* doorbell interrupt generator */
+#define ARCMSR_INBOUND_DRIVER_DATA_WRITE_OK           0x00000001
+#define ARCMSR_INBOUND_DRIVER_DATA_READ_OK            0x00000002
+#define ARCMSR_OUTBOUND_IOP331_DATA_WRITE_OK          0x00000001
+#define ARCMSR_OUTBOUND_IOP331_DATA_READ_OK           0x00000002
+/* ccb areca cdb flag */
+#define ARCMSR_CCBPOST_FLAG_SGL_BSIZE                 0x80000000
+#define ARCMSR_CCBPOST_FLAG_IAM_BIOS                  0x40000000
+#define ARCMSR_CCBREPLY_FLAG_IAM_BIOS                 0x40000000
+#define ARCMSR_CCBREPLY_FLAG_ERROR                    0x10000000
+/* outbound firmware ok */
+#define ARCMSR_OUTBOUND_MESG1_FIRMWARE_OK             0x80000000
+/*
+************************************************************************************************
+**    size 0x1F8 (504)
+************************************************************************************************
+*/
+typedef struct _ARCMSR_CDB {
+	uint8_t Bus;		/* 00h   should be 0            */
+	uint8_t TargetID;	/* 01h   should be 0--15        */
+	uint8_t LUN;		/* 02h   should be 0--7         */
+	uint8_t Function;	/* 03h   should be 1            */
+
+	uint8_t CdbLength;	/* 04h   not used now           */
+	uint8_t sgcount;	/* 05h                          */
+	uint8_t Flags;		/* 06h                          */
+#define ARCMSR_CDB_FLAG_SGL_BSIZE          0x01	/* bit 0: 0(256) / 1(512) bytes         */
+#define ARCMSR_CDB_FLAG_BIOS               0x02	/* bit 1: 0(from driver) / 1(from BIOS) */
+#define ARCMSR_CDB_FLAG_WRITE              0x04	/* bit 2: 0(Data in) / 1(Data out)      */
+#define ARCMSR_CDB_FLAG_SIMPLEQ            0x00	/* bit 4/3 ,00 : simple Q,01 : head of Q,10 : ordered Q */
+#define ARCMSR_CDB_FLAG_HEADQ              0x08
+#define ARCMSR_CDB_FLAG_ORDEREDQ           0x10
+	uint8_t Reserved1;	/* 07h                             */
+
+	uint32_t Context;	/* 08h   Address of this request */
+	uint32_t DataLength;	/* 0ch   not used now            */
+
+	uint8_t Cdb[16];	/* 10h   SCSI CDB                */
+	/*
+	 ********************************************************
+	 **Device Status : the same from SCSI bus if error occur
+	 ** SCSI bus status codes.
+	 ********************************************************
+	 */
+	uint8_t DeviceStatus;	/* 20h   if error                */
+#define SCSISTAT_GOOD                  		0x00
+#define SCSISTAT_CHECK_CONDITION       		0x02
+#define SCSISTAT_CONDITION_MET         		0x04
+#define SCSISTAT_BUSY                  		0x08
+#define SCSISTAT_INTERMEDIATE          		0x10
+#define SCSISTAT_INTERMEDIATE_COND_MET 		0x14
+#define SCSISTAT_RESERVATION_CONFLICT  		0x18
+#define SCSISTAT_COMMAND_TERMINATED    		0x22
+#define SCSISTAT_QUEUE_FULL            		0x28
+#define ARCMSR_DEV_SELECT_TIMEOUT           0xF0
+#define ARCMSR_DEV_ABORTED                  0xF1
+#define ARCMSR_DEV_INIT_FAIL                0xF2
+
+	uint8_t SenseData[15];	/* 21h   output                  */
+
+	union {
+		SG32ENTRY sg32entry[ARCMSR_MAX_SG_ENTRIES];	/* 30h   Scatter gather address  */
+		SG64ENTRY sg64entry[ARCMSR_MAX_SG_ENTRIES];	/* 30h                           */
+	} u;
+} ARCMSR_CDB, *PARCMSR_CDB;
+/*
+******************************************************************************************************
+**                 Messaging Unit (MU) of the Intel R 80331 I/O processor (80331)
+**  ==================================================================================================
+**	The Messaging Unit (MU) transfers data between the PCI system and the 80331
+**  notifies the respective system when new data arrives.
+**	The PCI window for messaging transactions is always the first 4 Kbytes of the inbound translation.
+**	window defined by:
+**                    1.Inbound ATU Base Address Register 0 (IABAR0)
+**                    2.Inbound ATU Limit Register 0 (IALR0)
+**	All of the Messaging Unit errors are reported in the same manner as ATU errors.
+**  Error conditions and status can be found in :
+**                                               1.ATUSR
+**                                               2.ATUISR
+**====================================================================================================
+**     Mechanism        Quantity               Assert PCI Interrupt Signals      Generate I/O Processor Interrupt
+**----------------------------------------------------------------------------------------------------
+**  Message Registers      2 Inbound                   Optional                              Optional
+**                         2 Outbound
+**----------------------------------------------------------------------------------------------------
+**  Doorbell Registers     1 Inbound                   Optional                              Optional
+**                         1 Outbound
+**----------------------------------------------------------------------------------------------------
+**  Circular Queues        4 Circular Queues           Under certain conditions              Under certain conditions
+**----------------------------------------------------------------------------------------------------
+**  Index Registers     1004 32-bit Memory Locations   No                                    Optional
+**====================================================================================================
+**     PCI Memory Map: First 4 Kbytes of the ATU Inbound PCI Address Space
+**====================================================================================================
+**  0000H           Reserved
+**  0004H           Reserved
+**  0008H           Reserved
+**  000CH           Reserved
+**------------------------------------------------------------------------
+**  0010H 			Inbound Message Register 0              ]
+**  0014H 			Inbound Message Register 1              ]
+**  0018H 			Outbound Message Register 0             ]
+**  001CH 			Outbound Message Register 1             ]   4 Message Registers
+**------------------------------------------------------------------------
+**  0020H 			Inbound Doorbell Register               ]
+**  0024H 			Inbound Interrupt Status Register       ]
+**  0028H 			Inbound Interrupt Mask Register         ]
+**  002CH 			Outbound Doorbell Register              ]
+**  0030H 			Outbound Interrupt Status Register      ]
+**  0034H 			Outbound Interrupt Mask Register        ]   2 Doorbell Registers and 4 Interrupt Registers
+**------------------------------------------------------------------------
+**  0038H 			Reserved
+**  003CH 			Reserved
+**------------------------------------------------------------------------
+**  0040H 			Inbound Queue Port                      ]
+**  0044H 			Outbound Queue Port                     ]   2 Queue Ports
+**------------------------------------------------------------------------
+**  0048H 			Reserved
+**  004CH 			Reserved
+**------------------------------------------------------------------------
+**  0050H                                                   ]
+**    :                                                     ]
+**    :      Intel Xscale Microarchitecture Local Memory    ]
+**    :                                                     ]
+**  0FFCH                                                   ]   1004 Index Registers
+*******************************************************************************
+*/
+typedef struct _MU {
+	uint32_t resrved0[4];	/*0000 000F */
+	uint32_t inbound_msgaddr0;	/*0010 0013 */
+	uint32_t inbound_msgaddr1;	/*0014 0017 */
+	uint32_t outbound_msgaddr0;	/*0018 001B */
+	uint32_t outbound_msgaddr1;	/*001C 001F */
+	uint32_t inbound_doorbell;	/*0020 0023 */
+	uint32_t inbound_intstatus;	/*0024 0027 */
+	uint32_t inbound_intmask;	/*0028 002B */
+	uint32_t outbound_doorbell;	/*002C 002F */
+	uint32_t outbound_intstatus;	/*0030 0033 */
+	uint32_t outbound_intmask;	/*0034 0037 */
+	uint32_t reserved1[2];	/*0038 003F */
+	uint32_t inbound_queueport;	/*0040 0043 */
+	uint32_t outbound_queueport;	/*0044 0047 */
+	uint32_t reserved2[2];	/*0048 004F */
+	uint32_t reserved3[492];	/*0050 07FF ......local_buffer 492 */
+	uint32_t message_wbuffer[128];	/*0800 09FF                    128 */
+	uint32_t message_rbuffer[256];	/*0a00 0DFF                    256 */
+	uint32_t ioctl_wbuffer[32];	/*0E00 0E7F                     32 */
+	uint32_t reserved4[32];	/*0E80 0EFF                     32 */
+	uint32_t ioctl_rbuffer[32];	/*0F00 0F7F                     32 */
+	uint32_t reserved5[32];	/*0F80 0FFF                     32 */
+} MU, *PMU;
+/*
+*********************************************************************
+**                 Adapter Control Block
+**
+*********************************************************************
+*/
+typedef struct _ACB {
+	struct pci_dev *pPCI_DEV;
+	struct Scsi_Host *host;
+	unsigned long vir2phy_offset;	/* Offset is used in making arc cdb physical to virtual calculations */
+	uint32_t outbound_int_enable;
+
+	struct _MU *pmu;	/* message unit ATU inbound base address0 */
+
+	uint8_t adapter_index;	/*  */
+	uint8_t irq;
+	uint16_t acb_flags;	/*  */
+#define ACB_F_SCSISTOPADAPTER         0x0001
+#define ACB_F_MSG_STOP_BGRB           0x0002	/* stop RAID background rebuild */
+#define ACB_F_MSG_START_BGRB          0x0004	/* stop RAID background rebuild */
+#define ACB_F_IOPDATA_OVERFLOW        0x0008	/* iop ioctl data rqbuffer overflow */
+#define ACB_F_IOCTL_WQBUFFER_CLEARED  0x0010	/* ioctl clear wqbuffer */
+#define ACB_F_IOCTL_RQBUFFER_CLEARED  0x0020	/* ioctl clear rqbuffer */
+#define ACB_F_BUS_RESET               0x0040
+#define ACB_F_IOP_INITED              0x0080	/* iop init */
+
+	struct _CCB *pccbwait2go[ARCMSR_MAX_OUTSTANDING_CMD];
+	atomic_t ccbwait2gocount;
+	atomic_t ccboutstandingcount;
+
+	void *dma_coherent;	/* dma_coherent used for memory free */
+	dma_addr_t dma_coherent_handle;	/* dma_coherent_handle used for memory free */
+	struct _CCB *pccb_pool[ARCMSR_MAX_FREECCB_NUM];	/* serial ccb pointer array */
+	struct _CCB *pccbringQ[ARCMSR_MAX_FREECCB_NUM];	/* working ccb pointer array */
+	int32_t ccb_doneindex;	/* done ccb array index */
+	int32_t ccb_startindex;	/* start ccb array index  */
+
+	uint8_t rqbuffer[ARCMSR_MAX_QBUFFER];	/* data collection buffer for read from 80331 */
+	int32_t rqbuf_firstindex;	/* first of read buffer  */
+	int32_t rqbuf_lastindex;	/* last of read buffer   */
+
+	uint8_t wqbuffer[ARCMSR_MAX_QBUFFER];	/* data collection buffer for write to 80331  */
+	int32_t wqbuf_firstindex;	/* first of write buffer */
+	int32_t wqbuf_lastindex;	/* last of write buffer  */
+
+	spinlock_t isr_lockunlock;
+	spinlock_t wait2go_lockunlock;
+	spinlock_t qbuffer_lockunlock;
+	spinlock_t ccb_doneindex_lockunlock;
+	spinlock_t ccb_startindex_lockunlock;
+	uint8_t devstate[ARCMSR_MAX_TARGETID][ARCMSR_MAX_TARGETLUN];	/* id0 ..... id15,lun0...lun7 */
+#define ARECA_RAID_GONE               0x55
+#define ARECA_RAID_GOOD               0xaa
+	uint32_t num_resets;
+	uint32_t num_aborts;
+	uint32_t firm_request_len;	/*1,04-07 */
+	uint32_t firm_numbers_queue;	/*2,08-11 */
+	uint32_t firm_sdram_size;	/*3,12-15 */
+	uint32_t firm_ide_channels;	/*4,16-19 */
+	char firm_model[12];	/*15,60-67 */
+	char firm_version[20];	/*17,68-83 */
+} ACB, *PACB;			/* HW_DEVICE_EXTENSION */
+/*
+*********************************************************************
+**                   Command Control Block (SrbExtension)
+** CCB must be not cross page boundary,and the order from offset 0
+**         structure describing an ATA disk request
+**             this CCB length must be 32 bytes boundary
+*********************************************************************
+*/
+typedef struct _CCB {
+	struct _ARCMSR_CDB arcmsr_cdb;	/* 0-503 (size of CDB=504): arcmsr messenger scsi command descriptor size 504 bytes */
+	uint32_t cdb_shifted_phyaddr;	/* 504-507 */
+	uint32_t reserved1;	/* 508-511 */
+	/*  ======================512+32 bytes========================  */
+#if BITS_PER_LONG == 64
+	struct scsi_cmnd *pcmd;	/* 512-515 516-519 pointer of linux scsi command */
+	struct _ACB *pACB;	/* 520-523 524-527 */
+
+	uint16_t ccb_flags;	/* 528-529 */
+#define		CCB_FLAG_READ		        0x0000
+#define		CCB_FLAG_WRITE		        0x0001
+#define		CCB_FLAG_ERROR		        0x0002
+#define		CCB_FLAG_FLUSHCACHE	        0x0004
+#define		CCB_FLAG_MASTER_ABORTED     0x0008
+	uint16_t startdone;	/* 530-531 */
+#define		ARCMSR_CCB_DONE   	        0x0000
+#define		ARCMSR_CCB_START   	        0x55AA
+#define		ARCMSR_CCB_ABORTED	        0xAA55
+#define		ARCMSR_CCB_ILLEGAL	        0xFFFF
+	uint32_t reserved2[3];	/* 532-535 536-539 540-543 */
+#else
+	struct scsi_cmnd *pcmd;	/* 512-515 pointer of linux scsi command */
+	struct _ACB *pACB;	/* 516-519 */
+
+	uint16_t ccb_flags;	/* 520-521 */
+#define		CCB_FLAG_READ		        0x0000
+#define		CCB_FLAG_WRITE		        0x0001
+#define		CCB_FLAG_ERROR		        0x0002
+#define		CCB_FLAG_FLUSHCACHE	        0x0004
+#define		CCB_FLAG_MASTER_ABORTED     0x0008
+	uint16_t startdone;	/* 522-523 */
+#define		ARCMSR_CCB_DONE   	        0x0000
+#define		ARCMSR_CCB_START   	        0x55AA
+#define		ARCMSR_CCB_ABORTED	        0xAA55
+#define		ARCMSR_CCB_ILLEGAL	        0xFFFF
+	uint32_t reserved2[5];	/* 524-527 528-531 532-535 536-539 540-543 */
+#endif
+	/*  ==========================================================  */
+} CCB, *PCCB;
+/*
+*********************************************************************
+**
+*********************************************************************
+*/
+typedef struct _HCBARC {
+	struct _ACB *pACB[ARCMSR_MAX_ADAPTER];
+
+	int32_t arcmsr_major_number;
+
+	uint8_t adapterCnt;
+	uint8_t reserved[3];
+} HCBARC, *PHCBARC;
+/*
+*************************************************************
+*************************************************************
+*/
+typedef struct _SENSE_DATA {
+	uint8_t ErrorCode:7;
+	uint8_t Valid:1;
+	uint8_t SegmentNumber;
+	uint8_t SenseKey:4;
+	uint8_t Reserved:1;
+	uint8_t IncorrectLength:1;
+	uint8_t EndOfMedia:1;
+	uint8_t FileMark:1;
+	uint8_t Information[4];
+	uint8_t AdditionalSenseLength;
+	uint8_t CommandSpecificInformation[4];
+	uint8_t AdditionalSenseCode;
+	uint8_t AdditionalSenseCodeQualifier;
+	uint8_t FieldReplaceableUnitCode;
+	uint8_t SenseKeySpecific[3];
+} SENSE_DATA, *PSENSE_DATA;
+/*
+**********************************
+**  Peripheral Device Type definitions
+**********************************
+*/
+#define SCSI_DASD		      0x00	/* Direct-access Device         */
+#define SCSI_SEQACESS		  0x01	/* Sequential-access device */
+#define SCSI_PRINTER		  0x02	/* Printer device               */
+#define SCSI_PROCESSOR		  0x03	/* Processor device             */
+#define SCSI_WRITEONCE		  0x04	/* Write-once device            */
+#define SCSI_CDROM		      0x05	/* CD-ROM device                    */
+#define SCSI_SCANNER		  0x06	/* Scanner device               */
+#define SCSI_OPTICAL		  0x07	/* Optical memory device        */
+#define SCSI_MEDCHGR		  0x08	/* Medium changer device        */
+#define SCSI_COMM		      0x09	/* Communications device        */
+#define SCSI_NODEV		      0x1F	/* Unknown or no device type */
+/*
+************************************************************************************************************
+**				         @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+**				                          80331 PCI-to-PCI Bridge
+**				                          PCI Configuration Space
+**
+**				         @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+**				                            Programming Interface
+**				                          ========================
+**				            Configuration Register Address Space Groupings and Ranges
+**				         =============================================================
+**				                 Register Group                      Configuration  Offset
+**				         -------------------------------------------------------------
+**				            Standard PCI Configuration                      00-3Fh
+**				         -------------------------------------------------------------
+**				             Device Specific Registers                      40-A7h
+**				         -------------------------------------------------------------
+**				                   Reserved                                 A8-CBh
+**				         -------------------------------------------------------------
+**				              Enhanced Capability List                      CC-FFh
+** ==========================================================================================================
+**                         Standard PCI [Type 1] Configuration Space Address Map
+** **********************************************************************************************************
+** |    Byte 3              |         Byte 2         |        Byte 1          |       Byte 0              |   Configu-ration Byte Offset
+** ----------------------------------------------------------------------------------------------------------
+** |                    Device ID                    |                     Vendor ID                      | 00h
+** ----------------------------------------------------------------------------------------------------------
+** |                 Primary Status                  |                  Primary Command                   | 04h
+** ----------------------------------------------------------------------------------------------------------
+** |                   Class Code                                             |        RevID              | 08h
+** ----------------------------------------------------------------------------------------------------------
+** |        reserved        |      Header Type       |      Primary MLT       |      Primary CLS          | 0Ch
+** ----------------------------------------------------------------------------------------------------------
+** |                                             Reserved                                                 | 10h
+** ----------------------------------------------------------------------------------------------------------
+** |                                             Reserved                                                 | 14h
+** ----------------------------------------------------------------------------------------------------------
+** |     Secondary MLT      | Subordinate Bus Number |  Secondary Bus Number  |     Primary Bus Number    | 18h
+** ----------------------------------------------------------------------------------------------------------
+** |                 Secondary Status                |       I/O Limit        |        I/O Base           | 1Ch
+** ----------------------------------------------------------------------------------------------------------
+** |      Non-prefetchable Memory Limit Address      |       Non-prefetchable Memory Base Address         | 20h
+** ----------------------------------------------------------------------------------------------------------
+** |        Prefetchable Memory Limit Address        |           Prefetchable Memory Base Address         | 24h
+** ----------------------------------------------------------------------------------------------------------
+** |                          Prefetchable Memory Base Address Upper 32 Bits                              | 28h
+** ----------------------------------------------------------------------------------------------------------
+** |                          Prefetchable Memory Limit Address Upper 32 Bits                             | 2Ch
+** ----------------------------------------------------------------------------------------------------------
+** |             I/O Limit Upper 16 Bits             |                 I/O Base Upper 16                  | 30h
+** ----------------------------------------------------------------------------------------------------------
+** |                                Reserved                                  |   Capabilities Pointer    | 34h
+** ----------------------------------------------------------------------------------------------------------
+** |                                             Reserved                                                 | 38h
+** ----------------------------------------------------------------------------------------------------------
+** |                   Bridge Control                |  Primary Interrupt Pin | Primary Interrupt Line    | 3Ch
+**=============================================================================================================
+*/
+/*
+**=============================================================================================================
+**  0x03-0x00 :
+** Bit       Default             Description
+**31:16       0335h            Device ID (DID): Indicates the unique device ID that is assigned to bridge by the PCI SIG.
+**                             ID is unique per product speed as indicated.
+**15:00       8086h            Vendor ID (VID): 16-bit field which indicates that Intel is the vendor.
+**=============================================================================================================
+*/
+#define     ARCMSR_PCI2PCI_VENDORID_REG		         0x00	/*word */
+#define     ARCMSR_PCI2PCI_DEVICEID_REG		         0x02	/*word */
+/*
+**==============================================================================
+**  0x05-0x04 : command register
+** Bit       Default 		               Description
+**15:11        00h		   		             Reserved
+** 10          0		   		           Interrupt Disable: Disables/Enables the generation of Interrupts on the primary bus.
+**                		   		                              The bridge does not support interrupts.
+** 09          0		   		                 FB2B Enable: Enables/Disables the generation of fast back to back transactions on the primary bus.
+**                		   		                              The bridge does not generate fast back to back transactions on the primary bus.
+** 08          0		   		          SERR# Enable (SEE): Enables primary bus SERR# assertions.
+**                		   		                              0=The bridge does not assert P_SERR#.
+**                		   		                              1=The bridge may assert P_SERR#, subject to other programmable criteria.
+** 07          0		   		    Wait Cycle Control (WCC): Always returns 0bzero indicating that bridge does not perform address or data stepping,
+** 06          0		   		 Parity Error Response (PER): Controls bridge response to a detected primary bus parity error.
+**                		   		                              0=When a data parity error is detected bridge does not assert S_PERR#.
+**                		   		                                  Also bridge does not assert P_SERR# in response to a detected address or attribute parity error.
+**                		   		                              1=When a data parity error is detected bridge asserts S_PERR#.
+**                		   		                                  The bridge also asserts P_SERR# (when enabled globally via bit(8) of this register) in response to a detected address or attribute parity error.
+** 05          0		  VGA Palette Snoop Enable (VGA_PSE): Controls bridge response to VGA-compatible palette write transactions.
+**                		                                      VGA palette write transactions are I/O transactions whose address bits are: P_AD[9:0] equal to 3C6h, 3C8h or 3C9h
+**                		                                      P_AD[15:10] are not decoded (i.e. aliases are claimed), or are fully decoding (i.e., must be all 0's depending upon the VGA aliasing bit in the Bridge Control Register, offset 3Eh.
+**                		                                      P_AD[31:16] equal to 0000h
+**                		                                      0=The bridge ignores VGA palette write transactions, unless decoded by the standard I/O address range window.
+**                		                                      1=The bridge responds to VGA palette write transactions with medium DEVSEL# timing and forwards them to the secondary bus.
+** 04          0   Memory Write and Invalidate Enable (MWIE): The bridge does not promote MW transactions to MWI transactions.
+**                                                            MWI transactions targeting resources on the opposite side of the bridge, however, are forwarded as MWI transactions.
+** 03          0                  Special Cycle Enable (SCE): The bridge ignores special cycle transactions.
+**                                                            This bit is read only and always returns 0 when read
+** 02          0                     Bus Master Enable (BME): Enables bridge to initiate memory and I/O transactions on the primary interface.
+**                                                            Initiation of configuration transactions is not affected by the state of this bit.
+**                                                            0=The bridge does not initiate memory or I/O transactions on the primary interface.
+**                                                            1=The bridge is enabled to function as an initiator on the primary interface.
+** 01          0                   Memory Space Enable (MSE): Controls target response to memory transactions on the primary interface.
+**                                                            0=The bridge target response to memory transactions on the primary interface is disabled.
+**                                                            1=The bridge target response to memory transactions on the primary interface is enabled.
+** 00          0                     I/O Space Enable (IOSE): Controls target response to I/O transactions on the primary interface.
+**                                                            0=The bridge target response to I/O transactions on the primary interface is disabled.
+**                                                            1=The bridge target response to I/O transactions on the primary interface is enabled.
+**==============================================================================
+*/
+#define     ARCMSR_PCI2PCI_PRIMARY_COMMAND_REG		 0x04	/*word */
+#define     PCI_DISABLE_INTERRUPT          0x0400
+/*
+**==============================================================================
+**  0x07-0x06 : status register
+** Bit       Default                       Description
+** 15          0                       Detected Parity Error: The bridge sets this bit to a 1b whenever it detects an address, attribute or data parity error.
+**                                                            This bit is set regardless of the state of the PER bit in the command register.
+** 14          0                       Signaled System Error: The bridge sets this bit to a 1b whenever it asserts SERR# on the primary bus.
+** 13          0                       Received Master Abort: The bridge sets this bit to a 1b when, acting as the initiator on the primary bus, its transaction (with the exception of special cycles) has been terminated with a Master Abort.
+** 12          0                       Received Target Abort: The bridge sets this bit to a 1b when, acting as the initiator on the primary bus, its transaction has been terminated with a Target Abort.
+** 11          0                       Signaled Target Abort: The bridge sets this bit to a 1b when it, as the target of a transaction, terminates it with a Target Abort.
+**                                                            In PCI-X mode this bit is also set when it forwards a SCM with a target abort error code.
+** 10:09       01                             DEVSEL# Timing: Indicates slowest response to a non-configuration command on the primary interface.
+**                                                            Returns ¡§01b¡¨ when read, indicating that bridge responds no slower than with medium timing.
+** 08          0                    Master Data Parity Error: The bridge sets this bit to a 1b when all of the following conditions are true: The bridge is the current master on the primary bus
+**                                                            S_PERR# is detected asserted or is asserted by bridge
+**                                                            The Parity Error Response bit is set in the Command register
+** 07          1                   Fast Back to Back Capable: Returns a 1b when read indicating that bridge is able to respond to fast back to back transactions on its primary interface.
+** 06          0                             Reserved
+** 05          1                   66 MHz Capable Indication: Returns a 1b when read indicating that bridge primary interface is 66 MHz capable.
+**                                                            1 =
+** 04          1                    Capabilities List Enable: Returns 1b when read indicating that bridge supports PCI standard enhanced capabilities.
+**                                                            Offset 34h (Capability Pointer register) provides the offset for the first entry in the linked list of enhanced capabilities.
+** 03          0                            Interrupt Status: Reflects the state of the interrupt in the device/function.
+**                                                            The bridge does not support interrupts.
+** 02:00       000                           Reserved
+**==============================================================================
+*/
+#define     ARCMSR_PCI2PCI_PRIMARY_STATUS_REG	     0x06	/*word: 06,07 */
+#define          ARCMSR_ADAP_66MHZ         0x20
+/*
+**==============================================================================
+**  0x08 : revision ID
+** Bit       Default                       Description
+** 07:00       00000000                  Revision ID (RID): '00h' indicating bridge A-0 stepping.
+**==============================================================================
+*/
+#define     ARCMSR_PCI2PCI_REVISIONID_REG		     0x08	/*byte */
+/*
+**==============================================================================
+**  0x0b-0x09 : 0180_00 (class code 1,native pci mode )
+** Bit       Default                       Description
+** 23:16       06h                     Base Class Code (BCC): Indicates that this is a bridge device.
+** 15:08       04h                      Sub Class Code (SCC): Indicates this is of type PCI-to-PCI bridge.
+** 07:00       00h               Programming Interface (PIF): Indicates that this is standard (non-subtractive) PCI-PCI bridge.
+**==============================================================================
+*/
+#define     ARCMSR_PCI2PCI_CLASSCODE_REG	         0x09	/*3bytes */
+/*
+**==============================================================================
+**  0x0c : cache line size
+** Bit       Default                       Description
+** 07:00       00h                     Cache Line Size (CLS): Designates the cache line size in 32-bit dword units.
+**                                                            The contents of this register are factored into internal policy decisions associated with memory read prefetching, and the promotion of Memory Write transactions to MWI transactions.
+**                                                            Valid cache line sizes are 8 and 16 dwords.
+**                                                            When the cache line size is set to an invalid value, bridge behaves as though the cache line size was set to 00h.
+**==============================================================================
+*/
+#define     ARCMSR_PCI2PCI_PRIMARY_CACHELINESIZE_REG 0x0C	/*byte */
+/*
+**==============================================================================
+**  0x0d : latency timer (number of pci clock 00-ff )
+** Bit       Default                       Description
+**                                   Primary Latency Timer (PTV):
+** 07:00      00h (Conventional PCI)   Conventional PCI Mode: Primary bus Master latency timer. Indicates the number of PCI clock cycles,
+**                                                            referenced from the assertion of FRAME# to the expiration of the timer,
+**                                                            when bridge may continue as master of the current transaction. All bits are writable,
+**                                                            resulting in a granularity of 1 PCI clock cycle.
+**                                                            When the timer expires (i.e., equals 00h) bridge relinquishes the bus after the first data transfer when its PCI bus grant has been deasserted.
+**         or 40h (PCI-X)                         PCI-X Mode: Primary bus Master latency timer.
+**                                                            Indicates the number of PCI clock cycles,
+**                                                            referenced from the assertion of FRAME# to the expiration of the timer,
+**                                                            when bridge may continue as master of the current transaction.
+**                                                            All bits are writable, resulting in a granularity of 1 PCI clock cycle.
+**                                                            When the timer expires (i.e., equals 00h) bridge relinquishes the bus at the next ADB.
+**                                                            (Except in the case where MLT expires within 3 data phases of an ADB.In this case bridge continues on until it reaches the next ADB before relinquishing the bus.)
+**==============================================================================
+*/
+#define     ARCMSR_PCI2PCI_PRIMARY_LATENCYTIMER_REG	 0x0D	/*byte */
+/*
+**==============================================================================
+**  0x0e : (header type,single function )
+** Bit       Default                       Description
+** 07           0                Multi-function device (MVD): 80331 is a single-function device.
+** 06:00       01h                       Header Type (HTYPE): Defines the layout of addresses 10h through 3Fh in configuration space.
+**                                                            Returns ¡§01h¡¨ when read indicating that the register layout conforms to the standard PCI-to-PCI bridge layout.
+**==============================================================================
+*/
+#define     ARCMSR_PCI2PCI_HEADERTYPE_REG	         0x0E	/*byte */
+/*
+**==============================================================================
+**     0x0f   :
+**==============================================================================
+*/
+/*
+**==============================================================================
+**  0x13-0x10 :
+**  PCI CFG Base Address #0 (0x10)
+**==============================================================================
+*/
+/*
+**==============================================================================
+**  0x17-0x14 :
+**  PCI CFG Base Address #1 (0x14)
+**==============================================================================
+*/
+/*
+**==============================================================================
+**  0x1b-0x18 :
+**  PCI CFG Base Address #2 (0x18)
+**-----------------0x1A,0x19,0x18--Bus Number Register - BNR
+** Bit       Default                       Description
+** 23:16       00h             Subordinate Bus Number (SBBN): Indicates the highest PCI bus number below this bridge.
+**                                                            Any Type 1 configuration cycle on the primary bus whose bus number is greater than the secondary bus number,
+**                                                            and less than or equal to the subordinate bus number is forwarded unaltered as a Type 1 configuration cycle on the secondary PCI bus.
+** 15:08       00h               Secondary Bus Number (SCBN): Indicates the bus number of PCI to which the secondary interface is connected.
+**                                                            Any Type 1 configuration cycle matching this bus number is translated to a Type 0 configuration cycle (or a Special Cycle) before being executed on bridge's secondary PCI bus.
+** 07:00       00h                  Primary Bus Number (PBN): Indicates bridge primary bus number.
+**                                                            Any Type 1 configuration cycle on the primary interface with a bus number that is less than the contents of this register field does not be claimed by bridge.
+**-----------------0x1B--Secondary Latency Timer Register - SLTR
+** Bit       Default                       Description
+**                             Secondary Latency Timer (STV):
+** 07:00       00h (Conventional PCI)  Conventional PCI Mode: Secondary bus Master latency timer.
+**                                                            Indicates the number of PCI clock cycles,referenced from the assertion of FRAME# to the expiration of the timer,
+**                                                            when bridge may continue as master of the current transaction. All bits are writable,
+**                                                            resulting in a granularity of 1 PCI clock cycle.
+**                                                            When the timer expires (i.e., equals 00h) bridge relinquishes the bus after the first data transfer when its PCI bus grant has been deasserted.
+**          or 40h (PCI-X)                        PCI-X Mode: Secondary bus Master latency timer.
+**                                                            Indicates the number of PCI clock cycles,referenced from the assertion of FRAME# to the expiration of the timer,
+**                                                            when bridge may continue as master of the current transaction. All bits are writable,
+**                                                            resulting in a granularity of 1 PCI clock cycle.
+**                                                            When the timer expires (i.e., equals 00h) bridge relinquishes the bus at the next ADB.
+**                                                            (Except in the case where MLT expires within 3 data phases of an ADB. In this case bridge continues on until it reaches the next ADB before relinquishing the bus)
+**==============================================================================
+*/
+#define     ARCMSR_PCI2PCI_PRIMARY_BUSNUMBER_REG	         0x18	/*3byte 0x1A,0x19,0x18 */
+#define     ARCMSR_PCI2PCI_SECONDARY_BUSNUMBER_REG	         0x19	/*byte */
+#define     ARCMSR_PCI2PCI_SUBORDINATE_BUSNUMBER_REG         0x1A	/*byte */
+#define     ARCMSR_PCI2PCI_SECONDARY_LATENCYTIMER_REG	     0x1B	/*byte */
+/*
+**==============================================================================
+**  0x1f-0x1c :
+**  PCI CFG Base Address #3 (0x1C)
+**-----------------0x1D,0x1C--I/O Base and Limit Register - IOBL
+** Bit       Default                       Description
+** 15:12        0h            I/O Limit Address Bits [15:12]: Defines the top address of an address range to determine when to forward I/O transactions from one interface to the other.
+**                                                            These bits correspond to address lines 15:12 for 4KB alignment.
+**                                                            Bits 11:0 are assumed to be FFFh.
+** 11:08        1h           I/O Limit Addressing Capability: This field is hard-wired to 1h, indicating support 32-bit I/O addressing.
+** 07:04        0h             I/O Base Address Bits [15:12]: Defines the bottom address of an address range to determine when to forward I/O transactions from one interface to the other.
+**                                                            These bits correspond to address lines 15:12 for 4KB alignment. Bits 11:0 are assumed to be 000h.
+** 03:00        1h            I/O Base Addressing Capability: This is hard-wired to 1h, indicating support for 32-bit I/O addressing.
+**-----------------0x1F,0x1E--Secondary Status Register - SSR
+** Bit       Default                       Description
+** 15           0b                     Detected Parity Error: The bridge sets this bit to a 1b whenever it detects an address, attribute or data parity error on its secondary interface.
+** 14           0b                     Received System Error: The bridge sets this bit when it samples SERR# asserted on its secondary bus interface.
+** 13           0b                     Received Master Abort: The bridge sets this bit to a 1b when, acting as the initiator on the secondary bus, it's transaction (with the exception of special cycles) has been terminated with a Master Abort.
+** 12           0b                     Received Target Abort: The bridge sets this bit to a 1b when, acting as the initiator on the secondary bus, it's transaction has been terminated with a Target Abort.
+** 11           0b                     Signaled Target Abort: The bridge sets this bit to a 1b when it, as the target of a transaction, terminates it with a Target Abort.
+**                                                            In PCI-X mode this bit is also set when it forwards a SCM with a target abort error code.
+** 10:09       01b                            DEVSEL# Timing: Indicates slowest response to a non-configuration command on the secondary interface.
+**                                                            Returns ¡§01b¡¨ when read, indicating that bridge responds no slower than with medium timing.
+** 08           0b                  Master Data Parity Error: The bridge sets this bit to a 1b when all of the following conditions are true:
+**                                                            The bridge is the current master on the secondary bus
+**                                                            S_PERR# is detected asserted or is asserted by bridge
+**                                                            The Parity Error Response bit is set in the Command register
+** 07           1b           Fast Back-to-Back Capable (FBC): Indicates that the secondary interface of bridge can receive fast back-to-back cycles.
+** 06           0b                           Reserved
+** 05           1b                      66 MHz Capable (C66): Indicates the secondary interface of the bridge is 66 MHz capable.
+**                                                            1 =
+** 04:00       00h                           Reserved
+**==============================================================================
+*/
+#define     ARCMSR_PCI2PCI_IO_BASE_REG	                     0x1C	/*byte */
+#define     ARCMSR_PCI2PCI_IO_LIMIT_REG	                     0x1D	/*byte */
+#define     ARCMSR_PCI2PCI_SECONDARY_STATUS_REG	             0x1E	/*word: 0x1F,0x1E */
+/*
+**==============================================================================
+**  0x23-0x20 :
+**  PCI CFG Base Address #4 (0x20)
+**-----------------0x23,0x22,0x21,0x20--Memory Base and Limit Register - MBL
+** Bit       Default                       Description
+** 31:20      000h                              Memory Limit: These 12 bits are compared with P_AD[31:20] of the incoming address to determine
+**                                                            the upper 1MB aligned value (exclusive) of the range.
+**                                                            The incoming address must be less than or equal to this value.
+**                                                            For the purposes of address decoding the lower 20 address bits (P_AD[19:0] are assumed to be F FFFFh.
+** 19:16        0h                            Reserved.
+** 15:04      000h                               Memory Base: These 12 bits are compared with bits P_AD[31:20] of the incoming address to determine the lower 1MB aligned value (inclusive) of the range.
+**                                                            The incoming address must be greater than or equal to this value.
+**                                                            For the purposes of address decoding the lower 20 address bits (P_AD[19:0]) are assumed to be 0 0000h.
+** 03:00        0h                            Reserved.
+**==============================================================================
+*/
+#define     ARCMSR_PCI2PCI_NONPREFETCHABLE_MEMORY_BASE_REG   0x20	/*word: 0x21,0x20 */
+#define     ARCMSR_PCI2PCI_NONPREFETCHABLE_MEMORY_LIMIT_REG  0x22	/*word: 0x23,0x22 */
+/*
+**==============================================================================
+**  0x27-0x24 :
+**  PCI CFG Base Address #5 (0x24)
+**-----------------0x27,0x26,0x25,0x24--Prefetchable Memory Base and Limit Register - PMBL
+** Bit       Default                       Description
+** 31:20      000h                 Prefetchable Memory Limit: These 12 bits are compared with P_AD[31:20] of the incoming address to determine
+**                                                            the upper 1MB aligned value (exclusive) of the range.
+**                                                            The incoming address must be less than or equal to this value.
+**                                                            For the purposes of address decoding the lower 20 address bits (P_AD[19:0] are assumed to be F FFFFh.
+** 19:16        1h                          64-bit Indicator: Indicates that 64-bit addressing is supported.
+** 15:04      000h                  Prefetchable Memory Base: These 12 bits are compared with bits P_AD[31:20] of the incoming address to determine the lower 1MB aligned value (inclusive) of the range.
+**                                                            The incoming address must be greater than or equal to this value.
+**                                                            For the purposes of address decoding the lower 20 address bits (P_AD[19:0]) are assumed to be 0 0000h.
+** 03:00        1h                          64-bit Indicator: Indicates that 64-bit addressing is supported.
+**==============================================================================
+*/
+#define     ARCMSR_PCI2PCI_PREFETCHABLE_MEMORY_BASE_REG      0x24	/*word: 0x25,0x24 */
+#define     ARCMSR_PCI2PCI_PREFETCHABLE_MEMORY_LIMIT_REG     0x26	/*word: 0x27,0x26 */
+/*
+**==============================================================================
+**  0x2b-0x28 :
+** Bit       Default                       Description
+** 31:00    00000000h Prefetchable Memory Base Upper Portion: All bits are read/writable
+**                                                            bridge supports full 64-bit addressing.
+**==============================================================================
+*/
+#define     ARCMSR_PCI2PCI_PREFETCHABLE_MEMORY_BASE_UPPER32_REG     0x28	/*dword: 0x2b,0x2a,0x29,0x28 */
+/*
+**==============================================================================
+**  0x2f-0x2c :
+** Bit       Default                       Description
+** 31:00    00000000h Prefetchable Memory Limit Upper Portion: All bits are read/writable
+**                                                             bridge supports full 64-bit addressing.
+**==============================================================================
+*/
+#define     ARCMSR_PCI2PCI_PREFETCHABLE_MEMORY_LIMIT_UPPER32_REG    0x2C	/*dword: 0x2f,0x2e,0x2d,0x2c */
+/*
+**==============================================================================
+**  0x33-0x30 :
+** Bit       Default                       Description
+** 07:00       DCh                      Capabilities Pointer: Pointer to the first CAP ID entry in the capabilities list is at DCh in PCI configuration
+**                                                            space. (Power Management Capability Registers)
+**==============================================================================
+*/
+#define     ARCMSR_PCI2PCI_CAPABILITIES_POINTER_REG	                 0x34	/*byte */
+/*
+**==============================================================================
+**  0x3b-0x35 : reserved
+**==============================================================================
+*/
+/*
+**==============================================================================
+**  0x3d-0x3c :
+**
+** Bit       Default                       Description
+** 15:08       00h                       Interrupt Pin (PIN): Bridges do not support the generation of interrupts.
+** 07:00       00h                     Interrupt Line (LINE): The bridge does not generate interrupts, so this is reserved as '00h'.
+**==============================================================================
+*/
+#define     ARCMSR_PCI2PCI_PRIMARY_INTERRUPT_LINE_REG                0x3C	/*byte */
+#define     ARCMSR_PCI2PCI_PRIMARY_INTERRUPT_PIN_REG                 0x3D	/*byte */
+/*
+**==============================================================================
+**  0x3f-0x3e :
+** Bit       Default                       Description
+** 15:12        0h                          Reserved
+** 11           0b                Discard Timer SERR# Enable: Controls the generation of SERR# on the primary interface (P_SERR#) in response
+**                                                            to a timer discard on either the primary or secondary interface.
+**                                                            0b=SERR# is not asserted.
+**                                                            1b=SERR# is asserted.
+** 10           0b                Discard Timer Status (DTS): This bit is set to a '1b' when either the primary or secondary discard timer expires.
+**                                                            The delayed completion is then discarded.
+** 09           0b             Secondary Discard Timer (SDT): Sets the maximum number of PCI clock cycles that bridge waits for an initiator on the secondary bus to repeat a delayed transaction request.
+**                                                            The counter starts when the delayed transaction completion is ready to be returned to the initiator.
+**                                                            When the initiator has not repeated the transaction at least once before the counter expires,bridge discards the delayed transaction from its queues.
+**                                                            0b=The secondary master time-out counter is 2 15 PCI clock cycles.
+**                                                            1b=The secondary master time-out counter is 2 10 PCI clock cycles.
+** 08           0b               Primary Discard Timer (PDT): Sets the maximum number of PCI clock cycles that bridge waits for an initiator on the primary bus to repeat a delayed transaction request.
+**                                                            The counter starts when the delayed transaction completion is ready to be returned to the initiator.
+**                                                            When the initiator has not repeated the transaction at least once before the counter expires, bridge discards the delayed transaction from its queues.
+**                                                            0b=The primary master time-out counter is 2 15 PCI clock cycles.
+**                                                            1b=The primary master time-out counter is 2 10 PCI clock cycles.
+** 07           0b            Fast Back-to-Back Enable (FBE): The bridge does not initiate back to back transactions.
+** 06           0b                 Secondary Bus Reset (SBR):
+**                                                            When cleared to 0b: The bridge deasserts S_RST#, when it had been asserted by writing this bit to a 1b.
+**                                                                When set to 1b: The bridge asserts S_RST#.
+** 05           0b                   Master Abort Mode (MAM): Dictates bridge behavior on the initiator bus when a master abort termination occurs in response to a delayed transaction initiated by bridge on the target bus.
+**                                                            0b=The bridge asserts TRDY# in response to a non-locked delayed transaction,and returns FFFF FFFFh when a read.
+**                                                            1b=When the transaction had not yet been completed on the initiator bus (e.g.,delayed reads, or non-posted writes),
+**                                                                 then bridge returns a Target Abort in response to the original requester
+**                                                                 when it returns looking for its delayed completion on the initiator bus.
+**                                                                 When the transaction had completed on the initiator bus (e.g., a PMW), then bridge asserts P_SERR# (when enabled).
+**                                   For PCI-X transactions this bit is an enable for the assertion of P_SERR# due to a master abort while attempting to deliver a posted memory write on the destination bus.
+** 04           0b                   VGA Alias Filter Enable: This bit dictates bridge behavior in conjunction with the VGA enable bit (also of this register),
+**                                                            and the VGA Palette Snoop Enable bit (Command Register).
+**                                                            When the VGA enable, or VGA Palette Snoop enable bits are on (i.e., 1b) the VGA Aliasing bit for the corresponding enabled functionality,:
+**                                                            0b=Ignores address bits AD[15:10] when decoding VGA I/O addresses.
+**                                                            1b=Ensures that address bits AD[15:10] equal 000000b when decoding VGA I/O addresses.
+**                                   When all VGA cycle forwarding is disabled, (i.e., VGA Enable bit =0b and VGA Palette Snoop bit =0b), then this bit has no impact on bridge behavior.
+** 03           0b                                VGA Enable: Setting this bit enables address decoding and transaction forwarding of the following VGA transactions from the primary bus to the secondary bus:
+**                                                            frame buffer memory addresses 000A0000h:000BFFFFh, VGA I/O addresses 3B0:3BBh and 3C0h:3DFh, where AD[31:16]=¡§0000h¡¨ and AD[15:10] are either not decoded (i.e., don't cares), or must be ¡§000000b¡¨
+**                                                            depending upon the state of the VGA Alias Filter Enable bit. (bit(4) of this register)
+**                                                            I/O and Memory Enable bits must be set in the Command register to enable forwarding of VGA cycles.
+** 02           0b                                ISA Enable: Setting this bit enables special handling for the forwarding of ISA I/O transactions that fall within the address range specified by the I/O Base and Limit registers, and are within the lowest 64Kbyte of the I/O address map (i.e., 0000 0000h - 0000 FFFFh).
+**                                                            0b=All I/O transactions that fall within the I/O Base and Limit registers' specified range are forwarded from primary to secondary unfiltered.
+**                                                            1b=Blocks the forwarding from primary to secondary of the top 768 bytes of each 1Kbyte alias. On the secondary the top 768 bytes of each 1K alias are inversely decoded and forwarded from secondary to primary.
+** 01           0b                      SERR# Forward Enable: 0b=The bridge does not assert P_SERR# as a result of an S_SERR# assertion.
+**                                                            1b=The bridge asserts P_SERR# whenever S_SERR# is detected asserted provided the SERR# Enable bit is set (PCI Command Register bit(8)=1b).
+** 00           0b                     Parity Error Response: This bit controls bridge response to a parity error that is detected on its secondary interface.
+**                                                            0b=When a data parity error is detected bridge does not assert S_PERR#.
+**                                                            Also bridge does not assert P_SERR# in response to a detected address or attribute parity error.
+**                                                            1b=When a data parity error is detected bridge asserts S_PERR#. The bridge also asserts P_SERR# (when enabled globally via bit(8) of the Command register)
+**                                                            in response to a detected address or attribute parity error.
+**==============================================================================
+*/
+#define     ARCMSR_PCI2PCI_BRIDGE_CONTROL_REG	                     0x3E	/*word */
+/*
+**************************************************************************
+**                  Device Specific Registers 40-A7h
+**************************************************************************
+** ----------------------------------------------------------------------------------------------------------
+** |    Byte 3              |         Byte 2         |        Byte 1          |       Byte 0              | Configu-ration Byte Offset
+** ----------------------------------------------------------------------------------------------------------
+** |    Bridge Control 0    |             Arbiter Control/Status              |      Reserved             | 40h
+** ----------------------------------------------------------------------------------------------------------
+** |                 Bridge Control 2                |                 Bridge Control 1                   | 44h
+** ----------------------------------------------------------------------------------------------------------
+** |                    Reserved                     |                 Bridge Status                      | 48h
+** ----------------------------------------------------------------------------------------------------------
+** |                                             Reserved                                                 | 4Ch
+** ----------------------------------------------------------------------------------------------------------
+** |                 Prefetch Policy                 |               Multi-Transaction Timer              | 50h
+** ----------------------------------------------------------------------------------------------------------
+** |       Reserved         |      Pre-boot Status   |             P_SERR# Assertion Control              | 54h
+** ----------------------------------------------------------------------------------------------------------
+** |       Reserved         |        Reserved        |             Secondary Decode Enable                | 58h
+** ----------------------------------------------------------------------------------------------------------
+** |                    Reserved                     |                 Secondary IDSEL                    | 5Ch
+** ----------------------------------------------------------------------------------------------------------
+** |                                              Reserved                                                | 5Ch
+** ----------------------------------------------------------------------------------------------------------
+** |                                              Reserved                                                | 68h:CBh
+** ----------------------------------------------------------------------------------------------------------
+**************************************************************************
+**==============================================================================
+**  0x42-0x41: Secondary Arbiter Control/Status Register - SACSR
+** Bit       Default                       Description
+** 15:12      1111b                  Grant Time-out Violator: This field indicates the agent that violated the Grant Time-out rule (PCI=16 clocks,PCI-X=6 clocks).
+**                                   Note that this field is only meaningful when:
+**                                                              # Bit[11] of this register is set to 1b, indicating that a Grant Time-out violation had occurred.
+**                                                              # bridge internal arbiter is enabled.
+**                                           Bits[15:12] Violating Agent (REQ#/GNT# pair number)
+**                                                 0000b REQ#/GNT#[0]
+**                                                 0001b REQ#/GNT#[1]
+**                                                 0010b REQ#/GNT#[2]
+**                                                 0011b REQ#/GNT#[3]
+**                                                 1111b Default Value (no violation detected)
+**                                   When bit[11] is cleared by software, this field reverts back to its default value.
+**                                   All other values are Reserved
+** 11            0b                  Grant Time-out Occurred: When set to 1b,
+**                                   this indicates that a Grant Time-out error had occurred involving one of the secondary bus agents.
+**                                   Software clears this bit by writing a 1b to it.
+** 10            0b                      Bus Parking Control: 0=During bus idle, bridge parks the bus on the last master to use the bus.
+**                                                            1=During bus idle, bridge parks the bus on itself. The bus grant is removed from the last master and internally asserted to bridge.
+** 09:08        00b                          Reserved
+** 07:00      0000 0000b  Secondary Bus Arbiter Priority Configuration: The bridge secondary arbiter provides two rings of arbitration priority.
+**                                                                      Each bit of this field assigns its corresponding secondary bus master to either the high priority arbiter ring (1b) or to the low priority arbiter ring (0b).
+**                                                                      Bits [3:0] correspond to request inputs S_REQ#[3:0], respectively.
+**                                                                      Bit [6] corresponds to the bridge internal secondary bus request while Bit [7] corresponds to the SATU secondary bus request.
+**                                                                      Bits [5:4] are unused.
+**                                                                      0b=Indicates that the master belongs to the low priority group.
+**                                                                      1b=Indicates that the master belongs to the high priority group
+**=================================================================================
+**  0x43: Bridge Control Register 0 - BCR0
+** Bit       Default                       Description
+** 07           0b                  Fully Dynamic Queue Mode: 0=The number of Posted write transactions is limited to eight and the Posted Write data is limited to 4KB.
+**                                                            1=Operation in fully dynamic queue mode. The bridge enqueues up to 14 Posted Memory Write transactions and 8KB of posted write data.
+** 06:03        0H                          Reserved.
+** 02           0b                 Upstream Prefetch Disable: This bit disables bridge ability to perform upstream prefetch operations for Memory Read requests received on its secondary interface.
+**                                 This bit also controls the bridge's ability to generate advanced read commands when forwarding a Memory Read Block transaction request upstream from a PCI-X bus to a Conventional PCI bus.
+**                                 0b=bridge treats all upstream Memory Read requests as though they target prefetchable memory. The use of Memory Read Line and Memory Read
+**                                      Multiple is enabled when forwarding a PCI-X Memory Read Block request to an upstream bus operating in Conventional PCI mode.
+**                                 1b=bridge treats upstream PCI Memory Read requests as though they target non-prefetchable memory and forwards upstream PCI-X Memory Read Block commands as Memory Read when the primary bus is operating in Conventional PCI mode.
+**                                 NOTE: This bit does not affect bridge ability to perform read prefetching when the received command is Memory Read Line or Memory Read Multiple.
+**=================================================================================
+**  0x45-0x44: Bridge Control Register 1 - BCR1 (Sheet 2 of 2)
+** Bit       Default                       Description
+** 15:08    0000000b                         Reserved
+** 07:06         00b                   Alias Command Mapping: This two bit field determines how bridge handles PCI-X ¡§Alias¡¨ commands, specifically the Alias to Memory Read Block and Alias to Memory Write Block commands.
+**                                                            The three options for handling these alias commands are to either pass it as is, re-map to the actual block memory read/write command encoding, or ignore
+**                                                            the transaction forcing a Master Abort to occur on the Origination Bus.
+**                                                   Bit (7:6) Handling of command
+**                                                        0 0 Re-map to Memory Read/Write Block before forwarding
+**                                                        0 1 Enqueue and forward the alias command code unaltered
+**                                                        1 0 Ignore the transaction, forcing Master Abort
+**                                                        1 1 Reserved
+** 05            1b                  Watchdog Timers Disable: Disables or enables all 2 24 Watchdog Timers in both directions.
+**                                                            The watchdog timers are used to detect prohibitively long latencies in the system.
+**                                                            The watchdog timer expires when any Posted Memory Write (PMW), Delayed Request,
+**                                                            or Split Requests (PCI-X mode) is not completed within 2 24 events
+**                                                            (¡§events¡¨ are defined as PCI Clocks when operating in PCI-X mode, and as the number of times being retried when operating in Conventional PCI mode)
+**                                                            0b=All 2 24 watchdog timers are enabled.
+**                                                            1b=All 2 24 watchdog timers are disabled and there is no limits to the number of attempts bridge makes when initiating a PMW,
+**                                                                 transacting a Delayed Transaction, or how long it waits for a split completion corresponding to one of its requests.
+** 04            0b                  GRANT# time-out disable: This bit enables/disables the GNT# time-out mechanism.
+**                                                            Grant time-out is 16 clocks for conventional PCI, and 6 clocks for PCI-X.
+**                                                            0b=The Secondary bus arbiter times out an agent that does not assert FRAME# within 16/6 clocks of receiving its grant, once the bus has gone idle.
+**                                                                 The time-out counter begins as soon as the bus goes idle with the new GNT# asserted.
+**                                                                 An infringing agent does not receive a subsequent GNT# until it de-asserts its REQ# for at least one clock cycle.
+**                                                            1b=GNT# time-out mechanism is disabled.
+** 03           00b                           Reserved.
+** 02            0b          Secondary Discard Timer Disable: This bit enables/disables bridge secondary delayed transaction discard mechanism.
+**                                                            The time out mechanism is used to ensure that initiators of delayed transactions return for their delayed completion data/status within a reasonable amount of time after it is available from bridge.
+**                                                            0b=The secondary master time-out counter is enabled and uses the value specified by the Secondary Discard Timer bit (see Bridge Control Register).
+**                                                            1b=The secondary master time-out counter is disabled. The bridge waits indefinitely for a secondary bus master to repeat a delayed transaction.
+** 01            0b            Primary Discard Timer Disable: This bit enables/disables bridge primary delayed transaction discard mechanism. The time out mechanism is used to ensure that initiators of delayed transactions return for their delayed completion data/status within a reasonable amount of time after it is available from bridge.
+**                                                            0b=The primary master time-out counter is enabled and uses the value specified by the Primary Discard Timer bit (see Bridge Control Register).
+**                                                            1b=The secondary master time-out counter is disabled. The bridge waits indefinitely for a secondary bus master to repeat a delayed transaction.
+** 00            0b                           Reserved
+**=================================================================================
+**  0x47-0x46: Bridge Control Register 2 - BCR2
+** Bit       Default                       Description
+** 15:07      0000b                          Reserved.
+** 06            0b Global Clock Out Disable (External Secondary Bus Clock Source Enable): This bit disables all of the secondary PCI clock outputs including the feedback clock S_CLKOUT.
+**                                                            This means that the user is required to provide an S_CLKIN input source.
+** 05:04        11 (66 MHz)                  Preserved.
+**              01 (100 MHz)
+**              00 (133 MHz)
+** 03:00        Fh (100 MHz & 66 MHz)
+**              7h (133 MHz)
+**                                        This 4 bit field provides individual enable/disable mask bits for each of bridge
+**                                        secondary PCI clock outputs. Some, or all secondary clock outputs (S_CLKO[3:0])
+**                                        default to being enabled following the rising edge of P_RST#, depending on the
+**                                        frequency of the secondary bus clock:
+**                                               ¡E Designs with 100 MHz (or lower) Secondary PCI clock power up with all four S_CLKOs enabled by default. (SCLKO[3:0])¡P
+**                                               ¡E Designs with 133 MHz Secondary PCI clock power up with the lower order 3 S_CLKOs enabled by default. (S_CLKO[2:0]) Only those SCLKs that power up enabled by can be connected to downstream device clock inputs.
+**=================================================================================
+**  0x49-0x48: Bridge Status Register - BSR
+** Bit       Default                       Description
+** 15           0b  Upstream Delayed Transaction Discard Timer Expired: This bit is set to a 1b and P_SERR# is conditionally asserted when the secondary discard timer expires.
+** 14           0b  Upstream Delayed/Split Read Watchdog Timer Expired:
+**                                                     Conventional PCI Mode: This bit is set to a 1b and P_SERR# is conditionally asserted when bridge discards an upstream delayed read transaction request after 2 24 retries following the initial retry.
+**                                                                PCI-X Mode: This bit is set to a 1b and P_SERR# is conditionally asserted when bridge discards an upstream split read request after waiting in excess of 2 24 clocks for the corresponding Split Completion to arrive.
+** 13           0b Upstream Delayed/Split Write Watchdog Timer Expired:
+**                                                     Conventional PCI Mode: This bit is set to a 1b and P_SERR# is conditionally asserted when bridge discards an upstream delayed write transaction request after 2 24 retries following the initial retry.
+**                                                                PCI-X Mode: This bit is set to a 1b and P_SERR# is conditionally asserted when bridge discards an upstream split write request after waiting in excess of 2 24 clocks for the corresponding Split Completion to arrive.
+** 12           0b           Master Abort during Upstream Posted Write: This bit is set to a 1b and P_SERR# is conditionally asserted when a Master Abort occurs as a result of an attempt, by bridge, to retire a PMW upstream.
+** 11           0b           Target Abort during Upstream Posted Write: This bit is set to a 1b and P_SERR# is conditionally asserted when a Target Abort occurs as a result of an attempt, by bridge, to retire a PMW upstream.
+** 10           0b                Upstream Posted Write Data Discarded: This bit is set to a 1b and P_SERR# is conditionally asserted when bridge discards an upstream PMW transaction after receiving 2 24 target retries from the primary bus target
+** 09           0b             Upstream Posted Write Data Parity Error: This bit is set to a 1b and P_SERR# is conditionally asserted when a data parity error is detected by bridge while attempting to retire a PMW upstream
+** 08           0b                  Secondary Bus Address Parity Error: This bit is set to a 1b and P_SERR# is conditionally asserted when bridge detects an address parity error on the secondary bus.
+** 07           0b Downstream Delayed Transaction Discard Timer Expired: This bit is set to a 1b and P_SERR# is conditionally asserted when the primary bus discard timer expires.
+** 06           0b Downstream Delayed/Split Read Watchdog Timer Expired:
+**                                                     Conventional PCI Mode: This bit is set to a 1b and P_SERR# is conditionally asserted when bridge discards a downstream delayed read transaction request after receiving 2 24 target retries from the secondary bus target.
+**                                                                PCI-X Mode: This bit is set to a 1b and P_SERR# is conditionally asserted when bridge discards a downstream split read request after waiting in excess of 2 24 clocks for the corresponding Split Completion to arrive.
+** 05           0b Downstream Delayed Write/Split Watchdog Timer Expired:
+**                                                     Conventional PCI Mode: This bit is set to a 1b and P_SERR# is conditionally asserted when bridge discards a downstream delayed write transaction request after receiving 2 24 target retries from the secondary bus target.
+**                                                                PCI-X Mode: This bit is set to a 1b and P_SERR# is conditionally asserted when bridge discards a downstream split write request after waiting in excess of 2 24 clocks for the corresponding Split Completion to arrive.
+** 04           0b          Master Abort during Downstream Posted Write: This bit is set to a 1b and P_SERR# is conditionally asserted when a Master Abort occurs as a result of an attempt, by bridge, to retire a PMW downstream.
+** 03           0b          Target Abort during Downstream Posted Write: This bit is set to a 1b and P_SERR# is conditionally asserted when a Target Abort occurs as a result of an attempt, by bridge, to retire a PMW downstream.
+** 02           0b               Downstream Posted Write Data Discarded: This bit is set to a 1b and P_SERR# is conditionally asserted when bridge discards a downstream PMW transaction after receiving 2 24 target retries from the secondary bus target
+** 01           0b            Downstream Posted Write Data Parity Error: This bit is set to a 1b and P_SERR# is conditionally asserted when a data parity error is detected by bridge while attempting to retire a PMW downstream.
+** 00           0b                     Primary Bus Address Parity Error: This bit is set to a 1b and P_SERR# is conditionally asserted when bridge detects an address parity error on the primary bus.
+**==================================================================================
+**  0x51-0x50: Bridge Multi-Transaction Timer Register - BMTTR
+** Bit       Default                       Description
+** 15:13       000b                          Reserved
+** 12:10       000b                          GRANT# Duration: This field specifies the count (PCI clocks) that a secondary bus master has its grant maintained in order to enable multiple transactions to execute within the same arbitration cycle.
+**                                                    Bit[02:00] GNT# Extended Duration
+**                                                               000 MTT Disabled (Default=no GNT# extension)
+**                                                               001 16 clocks
+**                                                               010 32 clocks
+**                                                               011 64 clocks
+**                                                               100 128 clocks
+**                                                               101 256 clocks
+**                                                               110 Invalid (treated as 000)
+**                                                               111 Invalid (treated as 000)
+** 09:08        00b                          Reserved
+** 07:00        FFh                                 MTT Mask: This field enables/disables MTT usage for each REQ#/GNT# pair supported by bridge secondary arbiter.
+**                                                            Bit(7) corresponds to SATU internal REQ#/GNT# pair,
+**                                                            bit(6) corresponds to bridge internal REQ#/GNT# pair,
+**                                                            bit(5) corresponds to REQ#/GNT#(5) pair, etc.
+**                                                  When a given bit is set to 1b, its corresponding REQ#/GNT# pair is enabled for MTT functionality as determined by bits(12:10) of this register.
+**                                                  When a given bit is cleared to 0b, its corresponding REQ#/GNT# pair is disabled from using the MTT.
+**==================================================================================
+**  0x53-0x52: Read Prefetch Policy Register - RPPR
+** Bit       Default                       Description
+** 15:13       000b                    ReRead_Primary Bus: 3-bit field indicating the multiplication factor to be used in calculating the number of bytes to prefetch from the secondary bus interface on subsequent PreFetch operations given that the read demands were not satisfied using the FirstRead parameter.
+**                                           The default value of 000b correlates to: Command Type Hardwired pre-fetch amount Memory Read 4 DWORDs Memory Read Line 1 cache lines Memory Read Multiple 2 cache lines
+** 12:10       000b                 FirstRead_Primary Bus: 3-bit field indicating the multiplication factor to be used in calculating the number of bytes to prefetch from the secondary bus interface on the initial PreFetch operation.
+**                                           The default value of 000b correlates to: Command Type Hardwired pre-fetch amount Memory Read 4 DWORDs Memory Read Line 1 cache line Memory Read Multiple 2 cache lines
+** 09:07       010b                  ReRead_Secondary Bus: 3-bit field indicating the multiplication factor to be used in calculating the number of bytes to prefetch from the primary bus interface on subsequent PreFetch operations given that the read demands were not satisfied using the FirstRead parameter.
+**                                           The default value of 010b correlates to: Command Type Hardwired pre-fetch amount Memory Read 3 cache lines Memory Read Line 3 cache lines Memory Read Multiple 6 cache lines
+** 06:04       000b               FirstRead_Secondary Bus: 3-bit field indicating the multiplication factor to be used in calculating the number of bytes to prefetch from the primary bus interface on the initial PreFetch operation.
+**                                           The default value of 000b correlates to: Command Type Hardwired pre-fetch amount Memory Read 4 DWORDs Memory Read Line 1 cache line Memory Read Multiple 2 cache lines
+** 03:00      1111b                Staged Prefetch Enable: This field enables/disables the FirstRead/ReRead pre-fetch algorithm for the secondary and the primary bus interfaces.
+**                                                         Bit(3) is a ganged enable bit for REQ#/GNT#[7:3], and bits(2:0) provide individual
+**                                                                            enable bits for REQ#/GNT#[2:0]. (bit(2) is the enable bit for REQ#/GNT#[2], etc...)
+**                                                                            1b: enables the staged pre-fetch feature
+**                                                                            0b: disables staged pre-fetch,
+**                                                         and hardwires read pre-fetch policy to the following for
+**                                                         Memory Read,
+**                                                         Memory Read Line,
+**                                                     and Memory Read Multiple commands:
+**                                                     Command Type Hardwired Pre-Fetch Amount...
+**                                                                                      Memory Read 4 DWORDs
+**                                                                                      Memory Read Line 1 cache line
+**                                                                                      Memory Read Multiple 2 cache lines
+** NOTE: When the starting address is not cache line aligned, bridge pre-fetches Memory Read line commands only to the next higher cache line boundary.For non-cache line aligned Memory Read Multiple commands bridge pre-fetches only to the second cache line boundary encountered.
+**==================================================================================
+**  0x55-0x54: P_SERR# Assertion Control - SERR_CTL
+** Bit       Default                       Description
+**  15          0b   Upstream Delayed Transaction Discard Timer Expired: Dictates the bridge behavior in response to its discarding of a delayed transaction that was initiated from the primary bus.
+**                                                                       0b=bridge asserts P_SERR#.
+**                                                                       1b=bridge does not assert P_SERR#
+**  14          0b   Upstream Delayed/Split Read Watchdog Timer Expired: Dictates bridge behavior following expiration of the subject watchdog timer.
+**                                                                       0b=bridge asserts P_SERR#.
+**                                                                       1b=bridge does not assert P_SERR#
+**  13          0b   Upstream Delayed/Split Write Watchdog Timer Expired: Dictates bridge behavior following expiration of the subject watchdog timer.
+**                                                                       0b=bridge asserts P_SERR#.
+**                                                                       1b=bridge does not assert P_SERR#
+**  12          0b             Master Abort during Upstream Posted Write: Dictates bridge behavior following its having detected a Master Abort while attempting to retire one of its PMWs upstream.
+**                                                                       0b=bridge asserts P_SERR#.
+**                                                                       1b=bridge does not assert P_SERR#
+**  11          0b             Target Abort during Upstream Posted Write: Dictates bridge behavior following its having been terminated with Target Abort while attempting to retire one of its PMWs upstream.
+**                                                                       0b=bridge asserts P_SERR#.
+**                                                                       1b=bridge does not assert P_SERR#
+**  10          0b                  Upstream Posted Write Data Discarded: Dictates bridge behavior in the event that it discards an upstream posted write transaction.
+**                                                                       0b=bridge asserts P_SERR#.
+**                                                                       1b=bridge does not assert P_SERR#
+**  09          0b               Upstream Posted Write Data Parity Error: Dictates bridge behavior when a data parity error is detected while attempting to retire on of its PMWs upstream.
+**                                                                       0b=bridge asserts P_SERR#.
+**                                                                       1b=bridge does not assert P_SERR#
+**  08          0b                    Secondary Bus Address Parity Error: This bit dictates bridge behavior when it detects an address parity error on the secondary bus.
+**                                                                       0b=bridge asserts P_SERR#.
+**                                                                       1b=bridge does not assert P_SERR#
+**  07          0b  Downstream Delayed Transaction Discard Timer Expired: Dictates bridge behavior in response to its discarding of a delayed transaction that was initiated on the secondary bus.
+**                                                                       0b=bridge asserts P_SERR#.
+**                                                                       1b=bridge does not assert P_SERR#
+**  06          0b  Downstream Delayed/Split Read Watchdog Timer Expired: Dictates bridge behavior following expiration of the subject watchdog timer.
+**                                                                       0b=bridge asserts P_SERR#.
+**                                                                       1b=bridge does not assert P_SERR#
+**  05          0b Downstream Delayed/Split Write Watchdog Timer Expired: Dictates bridge behavior following expiration of the subject watchdog timer.
+**                                                                       0b=bridge asserts P_SERR#.
+**                                                                       1b=bridge does not assert P_SERR#
+**  04          0b           Master Abort during Downstream Posted Write: Dictates bridge behavior following its having detected a Master Abort while attempting to retire one of its PMWs downstream.
+**                                                                       0b=bridge asserts P_SERR#.
+**                                                                       1b=bridge does not assert P_SERR#
+**  03          0b           Target Abort during Downstream Posted Write: Dictates bridge behavior following its having been terminated with Target Abort while attempting to retire one of its PMWs downstream.
+**                                                                       0b=bridge asserts P_SERR#.
+**                                                                       1b=bridge does not assert P_SERR#
+**  02          0b                Downstream Posted Write Data Discarded: Dictates bridge behavior in the event that it discards a downstream posted write transaction.
+**                                                                       0b=bridge asserts P_SERR#.
+**                                                                       1b=bridge does not assert P_SERR#
+**  01          0b             Downstream Posted Write Data Parity Error: Dictates bridge behavior when a data parity error is detected while attempting to retire on of its PMWs downstream.
+**                                                                       0b=bridge asserts P_SERR#.
+**                                                                       1b=bridge does not assert P_SERR#
+**  00          0b                      Primary Bus Address Parity Error: This bit dictates bridge behavior when it detects an address parity error on the primary bus.
+**                                                                       0b=bridge asserts P_SERR#.
+**                                                                       1b=bridge does not assert P_SERR#
+**===============================================================================
+**  0x56: Pre-Boot Status Register - PBSR
+** Bit       Default                       							Description
+** 07           1                          							 Reserved
+** 06           -                          							 Reserved - value indeterminate
+** 05:02        0                          							 Reserved
+** 01      Varies with External State of S_133EN at PCI Bus Reset    Secondary Bus Max Frequency Setting: This bit reflect captured S_133EN strap, indicating the maximum secondary bus clock frequency when in PCI-X mode.
+**                                                                   Max Allowable Secondary Bus Frequency
+**																									S_133EN PCI-X Mode
+**																									0 100 MHz
+**																									1 133 MH
+** 00          0b                                                    Reserved
+**===============================================================================
+**  0x59-0x58: Secondary Decode Enable Register - SDER
+** Bit       Default                       							Description
+** 15:03      FFF1h                        							 Preserved.
+** 02     Varies with External State of PRIVMEM at PCI Bus Reset   Private Memory Space Enable - when set, bridge overrides its secondary inverse decode logic and not
+**                                                                 forward upstream any secondary bus initiated DAC Memory transactions with AD(63)=1b.
+**                                                                 This creates a private memory space on the Secondary PCI bus that allows peer-to-peer transactions.
+** 01:00      10 2                                                   Preserved.
+**===============================================================================
+**  0x5D-0x5C: Secondary IDSEL Select Register - SISR
+** Bit       Default                       							Description
+** 15:10     000000 2                      							 Reserved.
+** 09    Varies with External State of PRIVDEV at PCI Bus Reset     AD25- IDSEL Disable - When this bit is set, AD25 is deasserted for any possible Type 1 to Type 0 conversion.
+**                                                                                        When this bit is clear, AD25 is asserted when Primary addresses AD[15:11]=01001 2 during a Type 1 to Type 0 conversion.
+** 08    Varies with External State of PRIVDEV at PCI Bus Reset     AD24- IDSEL Disable - When this bit is set, AD24 is deasserted for any possible Type 1 to Type 0 conversion.
+**                                                                                        When this bit is clear, AD24 is asserted when Primary addresses AD[15:11]=01000 2 during a Type 1 to Type 0 conversion.
+** 07    Varies with External State of PRIVDEV at PCI Bus Reset     AD23- IDSEL Disable - When this bit is set, AD23 is deasserted for any possible Type 1 to Type 0 conversion.
+**                                                                                        When this bit is clear, AD23 is asserted when Primary addresses AD[15:11]=00111 2 during a Type 1 to Type 0 conversion.
+** 06    Varies with External State of PRIVDEV at PCI Bus Reset     AD22- IDSEL Disable - When this bit is set, AD22 is deasserted for any possible Type 1 to Type 0 conversion.
+**                                                                                        When this bit is clear, AD22 is asserted when Primary addresses AD[15:11]=00110 2 during a Type 1 to Type 0 conversion.
+** 05    Varies with External State of PRIVDEV at PCI Bus Reset     AD21- IDSEL Disable - When this bit is set, AD21 is deasserted for any possible Type 1 to Type 0 conversion.
+**                                                                                        When this bit is clear, AD21 is asserted when Primary addresses AD[15:11]=00101 2 during a Type 1 to Type 0 conversion.
+** 04    Varies with External State of PRIVDEV at PCI Bus Reset     AD20- IDSEL Disable - When this bit is set, AD20 is deasserted for any possible Type 1 to Type 0 conversion.
+**                                                                                        When this bit is clear, AD20 is asserted when Primary addresses AD[15:11]=00100 2 during a Type 1 to Type 0 conversion.
+** 03    Varies with External State of PRIVDEV at PCI Bus Reset     AD19- IDSEL Disable - When this bit is set, AD19 is deasserted for any possible Type 1 to Type 0 conversion.
+**                                                                                        When this bit is clear, AD19 is asserted when Primary addresses AD[15:11]=00011 2 during a Type 1 to Type 0 conversion.
+** 02    Varies with External State of PRIVDEV at PCI Bus Reset     AD18- IDSEL Disable - When this bit is set, AD18 is deasserted for any possible Type 1 to Type 0 conversion.
+**                                                                                        When this bit is clear, AD18 is asserted when Primary addresses AD[15:11]=00010 2 during a Type 1 to Type 0 conversion.
+** 01    Varies with External State of PRIVDEV at PCI Bus Reset     AD17- IDSEL Disable - When this bit is set, AD17 is deasserted for any possible Type 1 to Type 0 conversion.
+**                                                                                        When this bit is clear, AD17 is asserted when Primary addresses AD[15:11]=00001 2 during a Type 1 to Type 0 conversion.
+** 00    Varies with External State of PRIVDEV at PCI Bus Reset     AD16- IDSEL Disable - When this bit is set, AD16 is deasserted for any possible Type 1 to Type 0 conversion.
+**                                                                                        When this bit is clear, AD16 is asserted when Primary addresses AD[15:11]=00000 2 during a Type 1 to Type 0 conversion.
+**************************************************************************
+*/
+/*
+**************************************************************************
+**                 Reserved      A8-CBh
+**************************************************************************
+*/
+/*
+**************************************************************************
+**                  PCI Extended Enhanced Capabilities List CC-FFh
+**************************************************************************
+** ----------------------------------------------------------------------------------------------------------
+** |    Byte 3              |         Byte 2         |        Byte 1          |       Byte 0              | Configu-ration Byte Offset
+** ----------------------------------------------------------------------------------------------------------
+** |           Power Management Capabilities         |        Next Item Ptr   |     Capability ID         | DCh
+** ----------------------------------------------------------------------------------------------------------
+** |        PM Data         |       PPB Support      |            Extensions Power Management CSR         | E0h
+** ----------------------------------------------------------------------------------------------------------
+** |                    Reserved                     |        Reserved        |        Reserved           | E4h
+** ----------------------------------------------------------------------------------------------------------
+** |                                              Reserved                                                | E8h
+** ----------------------------------------------------------------------------------------------------------
+** |       Reserved         |        Reserved        |        Reserved        |         Reserved          | ECh
+** ----------------------------------------------------------------------------------------------------------
+** |              PCI-X Secondary Status             |       Next Item Ptr    |       Capability ID       | F0h
+** ----------------------------------------------------------------------------------------------------------
+** |                                         PCI-X Bridge Status                                          | F4h
+** ----------------------------------------------------------------------------------------------------------
+** |                                PCI-X Upstream Split Transaction Control                              | F8h
+** ----------------------------------------------------------------------------------------------------------
+** |                               PCI-X Downstream Split Transaction Control                             | FCh
+** ----------------------------------------------------------------------------------------------------------
+**===============================================================================
+**  0xDC: Power Management Capabilities Identifier - PM_CAPID
+** Bit       Default                       Description
+** 07:00       01h                        Identifier (ID): PCI SIG assigned ID for PCI-PM register block
+**===============================================================================
+**  0xDD: Next Item Pointer - PM_NXTP
+** Bit       Default                       Description
+** 07:00       F0H                Next Capabilities Pointer (PTR): The register defaults to F0H pointing to the PCI-X Extended Capability Header.
+**===============================================================================
+**  0xDF-0xDE: Power Management Capabilities Register - PMCR
+** Bit       Default                       Description
+** 15:11       00h                     PME Supported (PME): PME# cannot be asserted by bridge.
+** 10           0h                 State D2 Supported (D2): Indicates no support for state D2. No power management action in this state.
+** 09           1h                 State D1 Supported (D1): Indicates support for state D1. No power management action in this state.
+** 08:06        0h                Auxiliary Current (AUXC): This 3 bit field reports the 3.3Vaux auxiliary current requirements for the PCI function.
+**                                                          This returns 000b as PME# wake-up for bridge is not implemented.
+** 05           0   Special Initialization Required (SINT): Special initialization is not required for bridge.
+** 04:03       00                            Reserved
+** 02:00       010                            Version (VS): Indicates that this supports PCI Bus Power Management Interface Specification, Revision 1.1.
+**===============================================================================
+**  0xE1-0xE0: Power Management Control / Status - Register - PMCSR
+** Bit       Default                       Description
+** 15:09       00h                          Reserved
+** 08          0b                          PME_Enable: This bit, when set to 1b enables bridge to assert PME#. Note that bridge never has occasion to assert PME# and implements this dummy R/W bit only for the purpose of working around an OS PCI-PM bug.
+** 07:02       00h                          Reserved
+** 01:00       00                Power State (PSTATE): This 2-bit field is used both to determine the current power state of a function and to set the Function into a new power state.
+**  													00 - D0 state
+**  													01 - D1 state
+**  													10 - D2 state
+**  													11 - D3 hot state
+**===============================================================================
+**  0xE2: Power Management Control / Status PCI to PCI Bridge Support - PMCSR_BSE
+** Bit       Default                       Description
+** 07          0         Bus Power/Clock Control Enable (BPCC_En): Indicates that the bus power/clock control policies have been disabled.
+** 06          0                B2/B3 support for D3 Hot (B2_B3#): The state of this bit determines the action that is to occur as a direct result of programming the function to D3 hot.
+**                                                                 This bit is only meaningful when bit 7 (BPCC_En) is a ¡§1¡¨.
+** 05:00     00h                            Reserved
+**===============================================================================
+**  0xE3: Power Management Data Register - PMDR
+** Bit       Default                       Description
+** 07:00       00h                          Reserved
+**===============================================================================
+**  0xF0: PCI-X Capabilities Identifier - PX_CAPID
+** Bit       Default                       Description
+** 07:00       07h                       Identifier (ID): Indicates this is a PCI-X capabilities list.
+**===============================================================================
+**  0xF1: Next Item Pointer - PX_NXTP
+** Bit       Default                       Description
+** 07:00       00h                     Next Item Pointer: Points to the next capability in the linked list The power on default value of this
+**                                                        register is 00h indicating that this is the last entry in the linked list of capabilities.
+**===============================================================================
+**  0xF3-0xF2: PCI-X Secondary Status - PX_SSTS
+** Bit       Default                       Description
+** 15:09       00h                          Reserved
+** 08:06       Xxx                Secondary Clock Frequency (SCF): This field is set with the frequency of the secondary bus.
+**                                                                 The values are:
+** 																					BitsMax FrequencyClock Period
+** 																					000PCI ModeN/A
+** 																					00166 15
+** 																					01010010
+** 																					0111337.5
+** 																					1xxreservedreserved
+** 																					The default value for this register is the operating frequency of the secondary bus
+** 05           0b                   Split Request Delayed. (SRD):  This bit is supposed to be set by a bridge when it cannot forward a transaction on the
+** 																	secondary bus to the primary bus because there is not enough room within the limit
+** 																	specified in the Split Transaction Commitment Limit field in the Downstream Split
+** 																	Transaction Control register. The bridge does not set this bit.
+** 04           0b                 Split Completion Overrun (SCO): This bit is supposed to be set when a bridge terminates a Split Completion on the secondary bus with retry or Disconnect at next ADB because its buffers are full. The bridge does not set this bit.
+** 03           0b              Unexpected Split Completion (USC): This bit is set when an unexpected split completion with a requester ID equal to bridge secondary bus number, device number 00h, and function number 0 is received on the secondary interface. This bit is cleared by software writing a '1'.
+** 02           0b               Split Completion Discarded (SCD): This bit is set when bridge discards a split completion moving toward the secondary bus because the requester would not accept it. This bit cleared by software writing a '1'.
+** 01           1b                                133 MHz Capable: Indicates that bridge is capable of running its secondary bus at 133 MHz
+** 00           1b                            64-bit Device (D64): Indicates the width of the secondary bus as 64-bits.
+**===============================================================================
+**  0xF7-0xF6-0xf5-0xF4: PCI-X Bridge Status - PX_BSTS
+** Bit       Default      								                 Description
+** 31:22        0         								                  Reserved
+** 21           0         								        Split Request Delayed (SRD): This bit does not be set by bridge.
+** 20           0         								     Split Completion Overrun (SCO): This bit does not be set by bridge because bridge throttles traffic on the completion side.
+** 19           0         								  Unexpected Split Completion (USC): The bridge sets this bit to 1b when it encounters a corrupted Split Completion, possibly with an inconsistent remaining byte count.Software clears this bit by writing a 1b to it.
+** 18           0         								   Split Completion Discarded (SCD): The bridge sets this bit to 1b when it has discarded a Split Completion.Software clears this bit by writing a 1b to it.
+** 17           1         								                    133 MHz Capable: This bit indicates that the bridge primary interface is capable of 133 MHz operation in PCI-X mode.
+**                        								                                     0=The maximum operating frequency is 66 MHz.
+**                        								                                     1=The maximum operating frequency is 133 MHz.
+** 16 Varies with the external state of P_32BITPCI# at PCI Bus Reset    64-bit Device (D64): Indicates bus width of the Primary PCI bus interface.
+**																							 0=Primary Interface is connected as a 32-bit PCI bus.
+**																							 1=Primary Interface is connected as a 64-bit PCI bus.
+** 15:08       00h                                                        Bus Number (BNUM): This field is simply an alias to the PBN field of the BNUM register at offset 18h.
+**                                                                                           Apparently it was deemed necessary reflect it here for diagnostic purposes.
+** 07:03       1fh                                                     Device Number (DNUM): Indicates which IDSEL bridge consumes. May be updated whenever a PCI-X
+** 																							 configuration write cycle that targets bridge scores a hit.
+** 02:00        0h                                                   Function Number (FNUM): The bridge Function #
+**===============================================================================
+**  0xFB-0xFA-0xF9-0xF8: PCI-X Upstream Split Transaction Control - PX_USTC
+** Bit       Default                       Description
+** 31:16      003Eh                 Split Transaction Limit (STL): This register indicates the size of the commitment limit in units of ADQs.
+**                                                                 Software is permitted to program this register to any value greater than or equal to
+**                                                                 the contents of the Split Transaction Capacity register. A value less than the contents
+**                                                                 of the Split Transaction Capacity register causes unspecified results.
+**                                                                 A value of 003Eh or greater enables the bridge to forward all Split Requests of any
+**                                                                 size regardless of the amount of buffer space available.
+** 15:00      003Eh              Split Transaction Capacity (STC): This read-only field indicates the size of the buffer (number of ADQs) for storing
+** 																   split completions. This register controls behavior of the bridge buffers for forwarding
+** 																   Split Transactions from a primary bus requester to a secondary bus completer.
+** 																   The default value of 003Eh indicates there is available buffer space for 62 ADQs (7936 bytes).
+**===============================================================================
+**  0xFF-0xFE-0xFD-0xFC: PCI-X Downstream Split Transaction Control - PX_DSTC
+** Bit       Default                       Description
+** 31:16      003Eh                 Split Transaction Limit (STL):  This register indicates the size of the commitment limit in units of ADQs.
+**																	Software is permitted to program this register to any value greater than or equal to
+**																	the contents of the Split Transaction Capacity register. A value less than the contents
+**																	of the Split Transaction Capacity register causes unspecified results.
+**																	A value of 003Eh or greater enables the bridge to forward all Split Requests of any
+**																	size regardless of the amount of buffer space available.
+** 15:00      003Eh              Split Transaction Capacity (STC): This read-only field indicates the size of the buffer (number of ADQs) for storing
+**                                                                 split completions. This register controls behavior of the bridge buffers for forwarding
+**                                                                 Split Transactions from a primary bus requester to a secondary bus completer.
+**                                                                 The default value of 003Eh indicates there is available buffer space for 62 ADQs (7936 bytes).
+**************************************************************************
+*/
+
+/*
+*************************************************************************************************************************************
+**                       80331 Address Translation Unit Register Definitions
+**                               ATU Interface Configuration Header Format
+**               The ATU is programmed via a [Type 0] configuration command on the PCI interface.
+*************************************************************************************************************************************
+** |    Byte 3              |         Byte 2         |        Byte 1          |       Byte 0              | Configuration Byte Offset
+**===================================================================================================================================
+** |                ATU Device ID                    |                     Vendor ID                      | 00h
+** ----------------------------------------------------------------------------------------------------------
+** |                     Status                      |                     Command                        | 04H
+** ----------------------------------------------------------------------------------------------------------
+** |                              ATU Class Code                              |       Revision ID         | 08H
+** ----------------------------------------------------------------------------------------------------------
+** |         ATUBISTR       |     Header Type        |      Latency Timer     |      Cacheline Size       | 0CH
+** ----------------------------------------------------------------------------------------------------------
+** |                                     Inbound ATU Base Address 0                                       | 10H
+** ----------------------------------------------------------------------------------------------------------
+** |                               Inbound ATU Upper Base Address 0                                       | 14H
+** ----------------------------------------------------------------------------------------------------------
+** |                                     Inbound ATU Base Address 1                                       | 18H
+** ----------------------------------------------------------------------------------------------------------
+** |                               Inbound ATU Upper Base Address 1                                       | 1CH
+** ----------------------------------------------------------------------------------------------------------
+** |                                     Inbound ATU Base Address 2                                       | 20H
+** ----------------------------------------------------------------------------------------------------------
+** |                               Inbound ATU Upper Base Address 2                                       | 24H
+** ----------------------------------------------------------------------------------------------------------
+** |                                             Reserved                                                 | 28H
+** ----------------------------------------------------------------------------------------------------------
+** |                ATU Subsystem ID                 |                ATU Subsystem Vendor ID             | 2CH
+** ----------------------------------------------------------------------------------------------------------
+** |                                       Expansion ROM Base Address                                     | 30H
+** ----------------------------------------------------------------------------------------------------------
+** |                                    Reserved Capabilities Pointer                                     | 34H
+** ----------------------------------------------------------------------------------------------------------
+** |                                             Reserved                                                 | 38H
+** ----------------------------------------------------------------------------------------------------------
+** |     Maximum Latency    |     Minimum Grant      |       Interrupt Pin    |      Interrupt Line       | 3CH
+** ----------------------------------------------------------------------------------------------------------
+*********************************************************************************************************************
+*/
+/*
+***********************************************************************************
+**  ATU Vendor ID Register - ATUVID
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  15:00      8086H (0x17D3)               ATU Vendor ID - This is a 16-bit value assigned to Intel. This register, combined with the DID, uniquely identify the PCI device.
+**                                                          Access type is Read/Write to allow the 80331 to configure the register as a different vendor ID to simulate the interface of a standard mechanism currently used by existing application software.
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_VENDOR_ID_REG		         0x00	/*word */
+/*
+***********************************************************************************
+**  ATU Device ID Register - ATUDID
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  15:00      0336H (0x1110)               ATU Device ID - This is a 16-bit value assigned to the ATU. This ID, combined with the VID, uniquely identify any PCI device.
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_DEVICE_ID_REG		         0x02	/*word */
+/*
+***********************************************************************************
+**  ATU Command Register - ATUCMD
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  15:11      000000 2                     Reserved
+**  10           0                          Interrupt Disable - This bit disables 80331 from asserting the ATU interrupt signal.
+**                                                              0=enables the assertion of interrupt signal.
+**                                                              1=disables the assertion of its interrupt signal.
+**  09          0 2                         Fast Back to Back Enable - When cleared, the ATU interface is not allowed to generate fast back-to-back cycles on its bus. Ignored when operating in the PCI-X mode.
+**  08          0 2                         SERR# Enable - When cleared, the ATU interface is not allowed to assert SERR# on the PCI interface.
+**  07          1 2                         Address/Data Stepping Control - Address stepping is implemented for configuration transactions. The
+**                                          ATU inserts 2 clock cycles of address stepping for Conventional Mode and 4 clock cycles of address stepping for PCI-X mode.
+**  06          0 2                         Parity Error Response - When set, the ATU takes normal action when a parity error is detected. When cleared, parity checking is disabled.
+**  05          0 2                         VGA Palette Snoop Enable - The ATU interface does not support I/O writes and therefore, does not perform VGA palette snooping.
+**  04          0 2                         Memory Write and Invalidate Enable - When set, ATU may generate MWI commands. When clear, ATU use Memory Write commands instead of MWI. Ignored when operating in the PCI-X mode.
+**  03          0 2                         Special Cycle Enable - The ATU interface does not respond to special cycle commands in any way. Not implemented and a reserved bit field.
+**  02          0 2                         Bus Master Enable - The ATU interface can act as a master on the PCI bus. When cleared, disables the device from generating PCI accesses. When set, allows the device to behave as a PCI bus master.
+**                                          When operating in the PCI-X mode, ATU initiates a split completion transaction regardless of the state of this bit.
+**  01          0 2                         Memory Enable - Controls the ATU interface¡¦s response to PCI memory addresses. When cleared, the ATU interface does not respond to any memory access on the PCI bus.
+**  00          0 2                         I/O Space Enable - Controls the ATU interface response to I/O transactions. Not implemented and a reserved bit field.
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_COMMAND_REG		         0x04	/*word */
+/*
+***********************************************************************************
+**  ATU Status Register - ATUSR (Sheet 1 of 2)
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  15          0 2                         Detected Parity Error - set when a parity error is detected in data received by the ATU on the PCI bus even
+**  										when the ATUCMD register¡¦s Parity Error Response bit is cleared. Set under the following conditions:
+**  										¡E Write Data Parity Error when the ATU is a target (inbound write).
+**  										¡E Read Data Parity Error when the ATU is a requester (outbound read).
+**  										¡E Any Address or Attribute (PCI-X Only) Parity Error on the Bus (including one generated by the ATU).
+**  14          0 2                         SERR# Asserted - set when SERR# is asserted on the PCI bus by the ATU.
+**  13          0 2                         Master Abort - set when a transaction initiated by the ATU PCI master interface, ends in a Master-Abort
+**                                          or when the ATU receives a Master Abort Split Completion Error Message in PCI-X mode.
+**  12          0 2                         Target Abort (master) - set when a transaction initiated by the ATU PCI master interface, ends in a target
+**                                          abort or when the ATU receives a Target Abort Split Completion Error Message in PCI-X mode.
+**  11          0 2                         Target Abort (target) - set when the ATU interface, acting as a target, terminates the transaction on the PCI bus with a target abort.
+**  10:09       01 2                        DEVSEL# Timing - These bits are read-only and define the slowest DEVSEL# timing for a target device in Conventional PCI Mode regardless of the operating mode (except configuration accesses).
+**  										00 2=Fast
+**  										01 2=Medium
+**  										10 2=Slow
+**  										11 2=Reserved
+**                                          The ATU interface uses Medium timing.
+**  08           0 2                        Master Parity Error - The ATU interface sets this bit under the following conditions:
+**  										¡E The ATU asserted PERR# itself or the ATU observed PERR# asserted.
+**  										¡E And the ATU acted as the requester for the operation in which the error occurred.
+**  										¡E And the ATUCMD register¡¦s Parity Error Response bit is set
+**  										¡E Or (PCI-X Mode Only) the ATU received a Write Data Parity Error Message
+**  										¡E And the ATUCMD register¡¦s Parity Error Response bit is set
+**  07           1 2  (Conventional mode)
+**               0 2  (PCI-X mode)
+**  										Fast Back-to-Back - The ATU/Messaging Unit interface is capable of accepting fast back-to-back
+**  										transactions in Conventional PCI mode when the transactions are not to the same target. Since fast
+**  										back-to-back transactions do not exist in PCI-X mode, this bit is forced to 0 in the PCI-X mode.
+**  06           0 2                        UDF Supported - User Definable Features are not supported
+**  05           1 2                        66 MHz. Capable - 66 MHz operation is supported.
+**  04           1 2                        Capabilities - When set, this function implements extended capabilities.
+**  03             0                        Interrupt Status - reflects the state of the ATU interrupt when the Interrupt Disable bit in the command register is a 0.
+**  										0=ATU interrupt signal deasserted.
+**  										1=ATU interrupt signal asserted.
+**  										NOTE: Setting the Interrupt Disable bit to a 1 has no effect on the state of this bit. Refer to
+**  										Section 3.10.23, ¡§ATU Interrupt Pin Register - ATUIPR¡¨ on page 236 for details on the ATU
+**  										interrupt signal.
+**  02:00      00000 2                      Reserved.
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_STATUS_REG		         0x06	/*word */
+/*
+***********************************************************************************
+**  ATU Revision ID Register - ATURID
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  07:00        00H                        ATU Revision - identifies the 80331 revision number.
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_REVISION_REG		         0x08	/*byte */
+/*
+***********************************************************************************
+**  ATU Class Code Register - ATUCCR
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  23:16        05H                        Base Class - Memory Controller
+**  15:08        80H                        Sub Class - Other Memory Controller
+**  07:00        00H                        Programming Interface - None defined
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_CLASS_CODE_REG		         0x09	/*3bytes 0x0B,0x0A,0x09 */
+/*
+***********************************************************************************
+**  ATU Cacheline Size Register - ATUCLSR
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  07:00        00H                        ATU Cacheline Size - specifies the system cacheline size in DWORDs. Cacheline size is restricted to either 0, 8 or 16 DWORDs.
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_CACHELINE_SIZE_REG		         0x0C	/*byte */
+/*
+***********************************************************************************
+**  ATU Latency Timer Register - ATULT
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  07:03     00000 2 (for Conventional mode)
+**            01000 2 (for PCI-X mode)
+**  										Programmable Latency Timer - This field varies the latency timer for the interface from 0 to 248 clocks.
+**  										The default value is 0 clocks for Conventional PCI mode, and 64 clocks for PCI-X mode.
+**  02:00       000 2                       Latency Timer Granularity - These Bits are read only giving a programmable granularity of 8 clocks for the latency timer.
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_LATENCY_TIMER_REG		         0x0D	/*byte */
+/*
+***********************************************************************************
+**  ATU Header Type Register - ATUHTR
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  07           0 2                        Single Function/Multi-Function Device - Identifies the 80331 as a single-function PCI device.
+**  06:00   000000 2                        PCI Header Type - This bit field indicates the type of PCI header implemented. The ATU interface
+**                                          header conforms to PCI Local Bus Specification, Revision 2.3.
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_HEADER_TYPE_REG		         0x0E	/*byte */
+/*
+***********************************************************************************
+**  ATU BIST Register - ATUBISTR
+**
+**  The ATU BIST Register controls the functions the Intel XScale core performs when BIST is
+**  initiated. This register is the interface between the host processor requesting BIST functions and
+**  the 80331 replying with the results from the software implementation of the BIST functionality.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  07           0 2                        BIST Capable - This bit value is always equal to the ATUCR ATU BIST Interrupt Enable bit.
+**  06           0 2                        Start BIST - When the ATUCR BIST Interrupt Enable bit is set:
+**  													 Setting this bit generates an interrupt to the Intel XScale core to perform a software BIST function.
+**  													 The Intel XScale core clears this bit when the BIST software has completed with the BIST results
+**  													 found in ATUBISTR register bits [3:0].
+**  													 When the ATUCR BIST Interrupt Enable bit is clear:
+**  													 Setting this bit does not generate an interrupt to the Intel XScale core and no BIST functions is performed.
+**                                                       The Intel XScale core does not clear this bit.
+**  05:04       00 2                        Reserved
+**  03:00     0000 2                        BIST Completion Code - when the ATUCR BIST Interrupt Enable bit is set and the ATUBISTR Start BIST bit is set (bit 6):
+**                                          The Intel XScale  core places the results of the software BIST in these bits. A nonzero value indicates a device-specific error.
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_BIST_REG		         0x0F	/*byte */
+
+/*
+***************************************************************************************
+**            ATU Base Registers and Associated Limit Registers
+***************************************************************************************
+**           Base Address                         Register Limit                          Register Description
+**  Inbound ATU Base Address Register 0           Inbound ATU Limit Register 0            Defines the inbound translation window 0 from the PCI bus.
+**  Inbound ATU Upper Base Address Register 0     N/A                                     Together with ATU Base Address Register 0 defines the inbound translation window 0 from the PCI bus for DACs.
+**  Inbound ATU Base Address Register 1           Inbound ATU Limit Register 1            Defines inbound window 1 from the PCI bus.
+**  Inbound ATU Upper Base Address Register 1     N/A                                     Together with ATU Base Address Register 1 defines inbound window 1 from the PCI bus for DACs.
+**  Inbound ATU Base Address Register 2           Inbound ATU Limit Register 2            Defines the inbound translation window 2 from the PCI bus.
+**  Inbound ATU Upper Base Address Register 2     N/A                                     Together with ATU Base Address Register 2 defines the inbound translation window 2 from the PCI bus for DACs.
+**  Inbound ATU Base Address Register 3           Inbound ATU Limit Register 3            Defines the inbound translation window 3 from the PCI bus.
+**  Inbound ATU Upper Base Address Register 3     N/A                                     Together with ATU Base Address Register 3 defines the inbound translation window 3 from the PCI bus for DACs.
+**                                                                                        NOTE: This is a private BAR that resides outside of the standard PCI configuration header space (offsets 00H-3FH).
+**  Expansion ROM Base Address Register           Expansion ROM Limit Register            Defines the window of addresses used by a bus master for reading from an Expansion ROM.
+**--------------------------------------------------------------------------------------
+**  ATU Inbound Window 1 is not a translate window.
+**  The ATU does not claim any PCI accesses that fall within this range.
+**  This window is used to allocate host memory for use by Private Devices.
+**  When enabled, the ATU interrupts the Intel  XScale core when either the IABAR1 register or the IAUBAR1 register is written from the PCI bus.
+***********************************************************************************
+*/
+
+/*
+***********************************************************************************
+**  Inbound ATU Base Address Register 0 - IABAR0
+**
+**  . The Inbound ATU Base Address Register 0 (IABAR0) together with the Inbound ATU Upper Base Address Register 0 (IAUBAR0) defines the block of memory addresses where the inbound translation window 0 begins.
+**  . The inbound ATU decodes and forwards the bus request to the 80331 internal bus with a translated address to map into 80331 local memory.
+**  . The IABAR0 and IAUBAR0 define the base address and describes the required memory block size.
+**  . Bits 31 through 12 of the IABAR0 is either read/write bits or read only with a value of 0
+**    depending on the value located within the IALR0.
+**    This configuration allows the IABAR0 to be programmed per PCI Local Bus Specification.
+**    The first 4 Kbytes of memory defined by the IABAR0, IAUBAR0 and the IALR0 is reserved for the Messaging Unit.
+**    The programmed value within the base address register must comply with the PCI programming requirements for address alignment.
+**  Warning:
+**    When IALR0 is cleared prior to host configuration:
+**                          the user should also clear the Prefetchable Indicator and the Type Indicator.
+**    Assuming IALR0 is not cleared:
+**                          a. Since non prefetchable memory windows can never be placed above the 4 Gbyte address boundary,
+**                             when the Prefetchable Indicator is cleared prior to host configuration,
+**                             the user should also set the Type Indicator for 32 bit addressability.
+**                          b. For compliance to the PCI-X Addendum to the PCI Local Bus Specification,
+**                             when the Prefetchable Indicator is set prior to host configuration, the user
+**                             should also set the Type Indicator for 64 bit addressability.
+**                             This is the default for IABAR0.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:12     00000H                        Translation Base Address 0 - These bits define the actual location the translation function is to respond to when addressed from the PCI bus.
+**  11:04        00H                        Reserved.
+**  03           1 2                        Prefetchable Indicator - When set, defines the memory space as prefetchable.
+**  02:01       10 2                        Type Indicator - Defines the width of the addressability for this memory window:
+**  												00 - Memory Window is locatable anywhere in 32 bit address space
+**  												10 - Memory Window is locatable anywhere in 64 bit address space
+**  00           0 2                        Memory Space Indicator - This bit field describes memory or I/O space base address.
+**                                                                   The ATU does not occupy I/O space,
+**                                                                   thus this bit must be zero.
+***********************************************************************************
+*/
+#define     ARCMSR_INBOUND_ATU_BASE_ADDRESS0_REG		         0x10	/*dword 0x13,0x12,0x11,0x10 */
+#define     ARCMSR_INBOUND_ATU_MEMORY_PREFETCHABLE	                 0x08
+#define     ARCMSR_INBOUND_ATU_MEMORY_WINDOW64		                 0x04
+/*
+***********************************************************************************
+**  Inbound ATU Upper Base Address Register 0 - IAUBAR0
+**
+**  This register contains the upper base address when decoding PCI addresses beyond 4 GBytes.
+**  Together with the Translation Base Address this register defines the actual location the translation
+**  function is to respond to when addressed from the PCI bus for addresses > 4GBytes (for DACs).
+**  The programmed value within the base address register must comply with the PCI programming requirements for address alignment.
+**  Note:
+**      When the Type indicator of IABAR0 is set to indicate 32 bit addressability,
+**      the IAUBAR0 register attributes are read-only.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:0      00000H                        Translation Upper Base Address 0 - Together with the Translation Base Address 0 these bits define the
+**                                                                             actual location the translation function is to respond to when addressed from the PCI bus for addresses > 4GBytes.
+***********************************************************************************
+*/
+#define     ARCMSR_INBOUND_ATU_UPPER_BASE_ADDRESS0_REG		     0x14	/*dword 0x17,0x16,0x15,0x14 */
+/*
+***********************************************************************************
+**  Inbound ATU Base Address Register 1 - IABAR1
+**
+**  . The Inbound ATU Base Address Register (IABAR1) together with the Inbound ATU Upper Base Address Register 1 (IAUBAR1) defines the block of memory addresses where the inbound translation window 1 begins.
+**  . This window is used merely to allocate memory on the PCI bus and, the ATU does not process any PCI bus transactions to this memory range.
+**  . The programmed value within the base address register must comply with the PCI programming requirements for address alignment.
+**  . When enabled, the ATU interrupts the Intel XScale core when the IABAR1 register is written from the PCI bus.
+**  Warning:
+**    When a non-zero value is not written to IALR1 prior to host configuration,
+**                          the user should not set either the Prefetchable Indicator or the Type Indicator for 64 bit addressability.
+**                          This is the default for IABAR1.
+**    Assuming a non-zero value is written to IALR1,
+**               			the user may set the Prefetchable Indicator
+**               			              or the Type         Indicator:
+**  						a. Since non prefetchable memory windows can never be placed above the 4 Gbyte address
+**  						   boundary, when the Prefetchable Indicator is not set prior to host configuration,
+**                             the user should also leave the Type Indicator set for 32 bit addressability.
+**                             This is the default for IABAR1.
+**  						b. when the Prefetchable Indicator is set prior to host configuration,
+**                             the user should also set the Type Indicator for 64 bit addressability.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:12     00000H                        Translation Base Address 1 - These bits define the actual location of window 1 on the PCI bus.
+**  11:04        00H                        Reserved.
+**  03           0 2                        Prefetchable Indicator - When set, defines the memory space as prefetchable.
+**  02:01       00 2                        Type Indicator - Defines the width of the addressability for this memory window:
+**  												00 - Memory Window is locatable anywhere in 32 bit address space
+**  												10 - Memory Window is locatable anywhere in 64 bit address space
+**  00           0 2                        Memory Space Indicator - This bit field describes memory or I/O space base address.
+**                                                                   The ATU does not occupy I/O space,
+**                                                                   thus this bit must be zero.
+***********************************************************************************
+*/
+#define     ARCMSR_INBOUND_ATU_BASE_ADDRESS1_REG		         0x18	/*dword 0x1B,0x1A,0x19,0x18 */
+/*
+***********************************************************************************
+**  Inbound ATU Upper Base Address Register 1 - IAUBAR1
+**
+**  This register contains the upper base address when locating this window for PCI addresses beyond 4 GBytes.
+**  Together with the IABAR1 this register defines the actual location for this memory window for addresses > 4GBytes (for DACs).
+**  This window is used merely to allocate memory on the PCI bus and, the ATU does not process any PCI bus transactions to this memory range.
+**  The programmed value within the base address register must comply with the PCI programming
+**  requirements for address alignment.
+**  When enabled, the ATU interrupts the Intel XScale core when the IAUBAR1 register is written
+**  from the PCI bus.
+**  Note:
+**      When the Type indicator of IABAR1 is set to indicate 32 bit addressability,
+**      the IAUBAR1 register attributes are read-only.
+**      This is the default for IABAR1.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:0      00000H                        Translation Upper Base Address 1 - Together with the Translation Base Address 1 these bits define the actual location for this memory window on the PCI bus for addresses > 4GBytes.
+***********************************************************************************
+*/
+#define     ARCMSR_INBOUND_ATU_UPPER_BASE_ADDRESS1_REG		         0x1C	/*dword 0x1F,0x1E,0x1D,0x1C */
+/*
+***********************************************************************************
+**  Inbound ATU Base Address Register 2 - IABAR2
+**
+**  . The Inbound ATU Base Address Register 2 (IABAR2) together with the Inbound ATU Upper Base Address Register 2 (IAUBAR2) defines the block of memory addresses where the inbound translation window 2 begins.
+**  . The inbound ATU decodes and forwards the bus request to the 80331 internal bus with a translated address to map into 80331 local memory.
+**  . The IABAR2 and IAUBAR2 define the base address and describes the required memory block size
+**  . Bits 31 through 12 of the IABAR2 is either read/write bits or read only with a value of 0 depending on the value located within the IALR2.
+**    The programmed value within the base address register must comply with the PCI programming requirements for address alignment.
+**  Warning:
+**    When a non-zero value is not written to IALR2 prior to host configuration,
+**                          the user should not set either the Prefetchable Indicator
+**                                                      or the Type         Indicator for 64 bit addressability.
+**                          This is the default for IABAR2.
+**  Assuming a non-zero value is written to IALR2,
+**                          the user may set the Prefetchable Indicator
+**                                        or the Type         Indicator:
+**  						a. Since non prefetchable memory windows can never be placed above the 4 Gbyte address boundary,
+**                             when the Prefetchable Indicator is not set prior to host configuration,
+**                             the user should also leave the Type Indicator set for 32 bit addressability.
+**                             This is the default for IABAR2.
+**  						b. when the Prefetchable Indicator is set prior to host configuration,
+**                             the user should also set the Type Indicator for 64 bit addressability.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:12     00000H                        Translation Base Address 2 - These bits define the actual location the translation function is to respond to when addressed from the PCI bus.
+**  11:04        00H                        Reserved.
+**  03           0 2                        Prefetchable Indicator - When set, defines the memory space as prefetchable.
+**  02:01       00 2                        Type Indicator - Defines the width of the addressability for this memory window:
+**  												00 - Memory Window is locatable anywhere in 32 bit address space
+**  												10 - Memory Window is locatable anywhere in 64 bit address space
+**  00           0 2                        Memory Space Indicator - This bit field describes memory or I/O space base address.
+**                                                                   The ATU does not occupy I/O space,
+**                                                                   thus this bit must be zero.
+***********************************************************************************
+*/
+#define     ARCMSR_INBOUND_ATU_BASE_ADDRESS2_REG		         0x20	/*dword 0x23,0x22,0x21,0x20 */
+/*
+***********************************************************************************
+**  Inbound ATU Upper Base Address Register 2 - IAUBAR2
+**
+**  This register contains the upper base address when decoding PCI addresses beyond 4 GBytes.
+**  Together with the Translation Base Address this register defines the actual location the translation function is to respond to when addressed from the PCI bus for addresses > 4GBytes (for DACs).
+**  The programmed value within the base address register must comply with the PCI programming
+**  requirements for address alignment.
+**  Note:
+**      When the Type indicator of IABAR2 is set to indicate 32 bit addressability,
+**      the IAUBAR2 register attributes are read-only.
+**      This is the default for IABAR2.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:0      00000H                        Translation Upper Base Address 2 - Together with the Translation Base Address 2 these bits define the actual location the translation function is to respond to when addressed from the PCI bus for addresses > 4GBytes.
+***********************************************************************************
+*/
+#define     ARCMSR_INBOUND_ATU_UPPER_BASE_ADDRESS2_REG		         0x24	/*dword 0x27,0x26,0x25,0x24 */
+/*
+***********************************************************************************
+**  ATU Subsystem Vendor ID Register - ASVIR
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  15:0      0000H                         Subsystem Vendor ID - This register uniquely identifies the add-in board or subsystem vendor.
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_SUBSYSTEM_VENDOR_ID_REG		         0x2C	/*word 0x2D,0x2C */
+/*
+***********************************************************************************
+**  ATU Subsystem ID Register - ASIR
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  15:0      0000H                         Subsystem ID - uniquely identifies the add-in board or subsystem.
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_SUBSYSTEM_ID_REG		         0x2E	/*word 0x2F,0x2E */
+/*
+***********************************************************************************
+**  Expansion ROM Base Address Register -ERBAR
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:12     00000H                        Expansion ROM Base Address - These bits define the actual location where the Expansion ROM address window resides when addressed from the PCI bus on any 4 Kbyte boundary.
+**  11:01     000H                          Reserved
+**  00        0 2                           Address Decode Enable - This bit field shows the ROM address decoder is enabled or disabled. When cleared, indicates the address decoder is disabled.
+***********************************************************************************
+*/
+#define     ARCMSR_EXPANSION_ROM_BASE_ADDRESS_REG		         0x30	/*dword 0x33,0x32,0v31,0x30 */
+#define     ARCMSR_EXPANSION_ROM_ADDRESS_DECODE_ENABLE   		     0x01
+/*
+***********************************************************************************
+**  ATU Capabilities Pointer Register - ATU_CAP_PTR
+**  -----------------------------------------------------------------
+**  Bit Default Description
+**  07:00     C0H                           Capability List Pointer - This provides an offset in this function¡¦s configuration space that points to the 80331 PCl Bus Power Management extended capability.
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_CAPABILITY_PTR_REG		     0x34	/*byte */
+/*
+***********************************************************************************
+**  Determining Block Sizes for Base Address Registers
+**  The required address size and type can be determined by writing ones to a base address register and
+**  reading from the registers. By scanning the returned value from the least-significant bit of the base
+**  address registers upwards, the programmer can determine the required address space size. The
+**  binary-weighted value of the first non-zero bit found indicates the required amount of space.
+**  Table 105 describes the relationship between the values read back and the byte sizes the base
+**  address register requires.
+**  As an example, assume that FFFF.FFFFH is written to the ATU Inbound Base Address Register 0
+**  (IABAR0) and the value read back is FFF0.0008H. Bit zero is a zero, so the device requires
+**  memory address space. Bit three is one, so the memory does supports prefetching. Scanning
+**  upwards starting at bit four, bit twenty is the first one bit found. The binary-weighted value of this
+**  bit is 1,048,576, indicated that the device requires 1 Mbyte of memory space.
+**  The ATU Base Address Registers and the Expansion ROM Base Address Register use their
+**  associated limit registers to enable which bits within the base address register are read/write and
+**  which bits are read only (0). This allows the programming of these registers in a manner similar to
+**  other PCI devices even though the limit is variable.
+**  Table 105. Memory Block Size Read Response
+**  Response After Writing all 1s
+**  to the Base Address Register
+**  Size
+**  (Bytes)
+**  Response After Writing all 1s
+**  to the Base Address Register
+**  Size
+**  (Bytes)
+**  FFFFFFF0H 16 FFF00000H 1 M
+**  FFFFFFE0H 32 FFE00000H 2 M
+**  FFFFFFC0H 64 FFC00000H 4 M
+**  FFFFFF80H 128 FF800000H 8 M
+**  FFFFFF00H 256 FF000000H 16 M
+**  FFFFFE00H 512 FE000000H 32 M
+**  FFFFFC00H 1K FC000000H 64 M
+**  FFFFF800H 2K F8000000H 128 M
+**  FFFFF000H 4K F0000000H 256 M
+**  FFFFE000H 8K E0000000H 512 M
+**  FFFFC000H 16K C0000000H 1 G
+**  FFFF8000H 32K 80000000H 2 G
+**  FFFF0000H 64K
+**  00000000H
+**  Register not
+**  imple-mented,
+**  no
+**  address
+**  space
+**  required.
+**  FFFE0000H 128K
+**  FFFC0000H 256K
+**  FFF80000H 512K
+**
+***************************************************************************************
+*/
+
+/*
+***********************************************************************************
+**  ATU Interrupt Line Register - ATUILR
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  07:00       FFH                         Interrupt Assigned - system-assigned value identifies which system interrupt controller¡¦s interrupt
+**                                                               request line connects to the device's PCI interrupt request lines (as specified in the interrupt pin register).
+**                                                               A value of FFH signifies ¡§no connection¡¨ or ¡§unknown¡¨.
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_INTERRUPT_LINE_REG		     0x3C	/*byte */
+/*
+***********************************************************************************
+**  ATU Interrupt Pin Register - ATUIPR
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  07:00       01H                         Interrupt Used - A value of 01H signifies that the ATU interface unit uses INTA# as the interrupt pin.
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_INTERRUPT_PIN_REG		     0x3D	/*byte */
+/*
+***********************************************************************************
+**  ATU Minimum Grant Register - ATUMGNT
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  07:00       80H                         This register specifies how long a burst period the device needs in increments of 8 PCI clocks.
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_MINIMUM_GRANT_REG		     0x3E	/*byte */
+/*
+***********************************************************************************
+**  ATU Maximum Latency Register - ATUMLAT
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  07:00       00H                         Specifies frequency (how often) the device needs to access the PCI bus in increments of 8 PCI clocks. A zero value indicates the device has no stringent requirement.
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_MAXIMUM_LATENCY_REG		     0x3F	/*byte */
+/*
+***********************************************************************************
+**  Inbound Address Translation
+**
+**  The ATU allows external PCI bus initiators to directly access the internal bus.
+**  These PCI bus initiators can read or write 80331 memory-mapped registers or 80331 local memory space.
+**  The process of inbound address translation involves two steps:
+**  1. Address Detection.
+**             ¡E Determine when the 32-bit PCI address (64-bit PCI address during DACs) is
+**                within the address windows defined for the inbound ATU.
+**             ¡E Claim the PCI transaction with medium DEVSEL# timing in the conventional PCI
+**                mode and with Decode A DEVSEL# timing in the PCI-X mode.
+**  2. Address Translation.
+**             ¡E Translate the 32-bit PCI address (lower 32-bit PCI address during DACs) to a 32-bit 80331 internal bus address.
+**  				The ATU uses the following registers in inbound address window 0 translation:
+**  				¡E Inbound ATU Base Address Register 0
+**  				¡E Inbound ATU Limit Register 0
+**  				¡E Inbound ATU Translate Value Register 0
+**  				The ATU uses the following registers in inbound address window 2 translation:
+**  				¡E Inbound ATU Base Address Register 2
+**  				¡E Inbound ATU Limit Register 2
+**  				¡E Inbound ATU Translate Value Register 2
+**  				The ATU uses the following registers in inbound address window 3 translation:
+**  				¡E Inbound ATU Base Address Register 3
+**  				¡E Inbound ATU Limit Register 3
+**  				¡E Inbound ATU Translate Value Register 3
+**    Note: Inbound Address window 1 is not a translate window.
+**          Instead, window 1 may be used to allocate host memory for Private Devices.
+**          Inbound Address window 3 does not reside in the standard section of the configuration header (offsets 00H - 3CH),
+**          thus the host BIOS does not configure window 3.
+**          Window 3 is intended to be used as a special window into local memory for private PCI
+**          agents controlled by the 80331 in conjunction with the Private Memory Space of the bridge.
+**          PCI-to-PCI Bridge in 80331 or
+**          Inbound address detection is determined from the 32-bit PCI address,
+**          (64-bit PCI address during DACs) the base address register and the limit register.
+**          In the case of DACs none of the upper 32-bits of the address is masked during address comparison.
+**
+**  The algorithm for detection is:
+**
+**  Equation 1. Inbound Address Detection
+**              When (PCI_Address [31:0] & Limit_Register[31:0]) == (Base_Register[31:0] & PCI_Address [63:32]) == Base_Register[63:32] (for DACs only)
+**              the PCI Address is claimed by the Inbound ATU.
+**
+**  			The incoming 32-bit PCI address (lower 32-bits of the address in case of DACs) is bitwise ANDed
+**  			with the associated inbound limit register.
+**              When the result matches the base register (and upper base address matches upper PCI address in case of DACs),
+**              the inbound PCI address is detected as being within the inbound translation window and is claimed by the ATU.
+**
+**  			Note:   The first 4 Kbytes of the ATU inbound address translation window 0 are reserved for the Messaging Unit.
+**  					Once the transaction is claimed, the address must be translated from a PCI address to a 32-bit
+**  					internal bus address. In case of DACs upper 32-bits of the address is simply discarded and only the
+**  					lower 32-bits are used during address translation.
+**              		The algorithm is:
+**
+**
+**  Equation 2. Inbound Translation
+**              Intel I/O processor Internal Bus Address=(PCI_Address[31:0] & ~Limit_Register[31:0]) | ATU_Translate_Value_Register[31:0].
+**
+**  			The incoming 32-bit PCI address (lower 32-bits in case of DACs) is first bitwise ANDed with the
+**  			bitwise inverse of the limit register. This result is bitwise ORed with the ATU Translate Value and
+**  			the result is the internal bus address. This translation mechanism is used for all inbound memory
+**  			read and write commands excluding inbound configuration read and writes.
+**  			In the PCI mode for inbound memory transactions, the only burst order supported is Linear
+**  			Incrementing. For any other burst order, the ATU signals a Disconnect after the first data phase.
+**  			The PCI-X supports linear incrementing only, and hence above situation is not encountered in the PCI-X mode.
+**  example:
+**  	    Register Values
+**  		         Base_Register=3A00 0000H
+**  		        Limit_Register=FF80 0000H (8 Mbyte limit value)
+**  		        Value_Register=B100 0000H
+**  		        Inbound Translation Window ranges from 3A00 0000H to 3A7F FFFFH (8 Mbytes)
+**
+**  		Address Detection (32-bit address)
+**
+**  						PCI_Address & Limit_Register == Base_Register
+**  						3A45 012CH  &   FF80 0000H   ==  3A00 0000H
+**
+**  					ANS: PCI_Address is in the Inbound Translation Window
+**  		Address Translation (to get internal bus address)
+**
+**  						IB_Address=(PCI_Address & ~Limit_Register) | Value_Reg
+**  						IB_Address=(3A45 012CH & 007F FFFFH) | B100 0000H
+**
+**  					ANS:IB_Address=B145 012CH
+***********************************************************************************
+*/
+
+/*
+***********************************************************************************
+**  Inbound ATU Limit Register 0 - IALR0
+**
+**  Inbound address translation for memory window 0 occurs for data transfers occurring from the PCI
+**  bus (originated from the PCI bus) to the 80331 internal bus. The address translation block converts
+**  PCI addresses to internal bus addresses.
+**  The 80331 translate value register¡¦s programmed value must be naturally aligned with the base
+**  address register¡¦s programmed value. The limit register is used as a mask; thus, the lower address
+**  bits programmed into the 80331 translate value register are invalid. Refer to the PCI Local Bus
+**  Specification, Revision 2.3 for additional information on programming base address registers.
+**  Bits 31 to 12 within the IALR0 have a direct effect on the IABAR0 register, bits 31 to 12, with a
+**  one to one correspondence. A value of 0 in a bit within the IALR0 makes the corresponding bit
+**  within the IABAR0 a read only bit which always returns 0. A value of 1 in a bit within the IALR0
+**  makes the corresponding bit within the IABAR0 read/write from PCI. Note that a consequence of
+**  this programming scheme is that unless a valid value exists within the IALR0, all writes to the
+**  IABAR0 has no effect since a value of all zeros within the IALR0 makes the IABAR0 a read only  register.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:12     FF000H                        Inbound Translation Limit 0 - This readback value determines the memory block size required for
+**                                                                        inbound memory window 0 of the address translation unit. This defaults to an inbound window of 16MB.
+**  11:00       000H                        Reserved
+***********************************************************************************
+*/
+#define     ARCMSR_INBOUND_ATU_LIMIT0_REG		     0x40	/*dword 0x43,0x42,0x41,0x40 */
+/*
+***********************************************************************************
+**  Inbound ATU Translate Value Register 0 - IATVR0
+**
+**  The Inbound ATU Translate Value Register 0 (IATVR0) contains the internal bus address used to
+**  convert PCI bus addresses. The converted address is driven on the internal bus as a result of the
+**  inbound ATU address translation.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:12     FF000H                        Inbound ATU Translation Value 0 - This value is used to convert the PCI address to internal bus addresses.
+**                                          This value must be 64-bit aligned on the internal bus. The default address allows the ATU to access the internal 80331 memory-mapped registers.
+**  11:00       000H                        Reserved
+***********************************************************************************
+*/
+#define     ARCMSR_INBOUND_ATU_TRANSLATE_VALUE0_REG		     0x44	/*dword 0x47,0x46,0x45,0x44 */
+/*
+***********************************************************************************
+**  Expansion ROM Limit Register - ERLR
+**
+**  The Expansion ROM Limit Register (ERLR) defines the block size of addresses the ATU defines
+**  as Expansion ROM address space. The block size is programmed by writing a value into the ERLR.
+**  Bits 31 to 12 within the ERLR have a direct effect on the ERBAR register, bits 31 to 12, with a one
+**  to one correspondence. A value of 0 in a bit within the ERLR makes the corresponding bit within
+**  the ERBAR a read only bit which always returns 0. A value of 1 in a bit within the ERLR makes
+**  the corresponding bit within the ERBAR read/write from PCI.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:12     000000H                       Expansion ROM Limit - Block size of memory required for the Expansion ROM translation unit. Default
+**                                                                value is 0, which indicates no Expansion ROM address space and all bits within the ERBAR are read only with a value of 0.
+**  11:00        000H                       Reserved.
+***********************************************************************************
+*/
+#define     ARCMSR_EXPANSION_ROM_LIMIT_REG		          0x48	/*dword 0x4B,0x4A,0x49,0x48 */
+/*
+***********************************************************************************
+**  Expansion ROM Translate Value Register - ERTVR
+**
+**  The Expansion ROM Translate Value Register contains the 80331 internal bus address which the
+**  ATU converts the PCI bus access. This address is driven on the internal bus as a result of the
+**  Expansion ROM address translation.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:12     00000H                        Expansion ROM Translation Value - Used to convert PCI addresses to 80331 internal bus addresses
+**                                                                            for Expansion ROM accesses. The Expansion ROM address translation value must be word aligned on the internal bus.
+**  11:00       000H                        Reserved
+***********************************************************************************
+*/
+#define     ARCMSR_EXPANSION_ROM_TRANSLATE_VALUE_REG		          0x4C	/*dword 0x4F,0x4E,0x4D,0x4C */
+/*
+***********************************************************************************
+**  Inbound ATU Limit Register 1 - IALR1
+**
+**  Bits 31 to 12 within the IALR1 have a direct effect on the IABAR1 register, bits 31 to 12, with a
+**  one to one correspondence. A value of 0 in a bit within the IALR1 makes the corresponding bit
+**  within the IABAR1 a read only bit which always returns 0. A value of 1 in a bit within the IALR1
+**  makes the corresponding bit within the IABAR1 read/write from PCI. Note that a consequence of
+**  this programming scheme is that unless a valid value exists within the IALR1, all writes to the
+**  IABAR1 has no effect since a value of all zeros within the IALR1 makes the IABAR1 a read only
+**  register.
+**  The inbound memory window 1 is used merely to allocate memory on the PCI bus. The ATU does
+**  not process any PCI bus transactions to this memory range.
+**  Warning: The ATU does not claim any PCI accesses that fall within the range defined by IABAR1,
+**  IAUBAR1, and IALR1.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:12     00000H                        Inbound Translation Limit 1 - This readback value determines the memory block size required for the ATUs memory window 1.
+**  11:00 000H Reserved
+***********************************************************************************
+*/
+#define     ARCMSR_INBOUND_ATU_LIMIT1_REG		          0x50	/*dword 0x53,0x52,0x51,0x50 */
+/*
+***********************************************************************************
+**  Inbound ATU Limit Register 2 - IALR2
+**
+**  Inbound address translation for memory window 2 occurs for data transfers occurring from the PCI
+**  bus (originated from the PCI bus) to the 80331 internal bus. The address translation block converts
+**  PCI addresses to internal bus addresses.
+**  The inbound translation base address for inbound window 2 is specified in Section 3.10.15. When
+**  determining block size requirements ¡X as described in Section 3.10.21 ¡X the translation limit
+**  register provides the block size requirements for the base address register. The remaining registers
+**  used for performing address translation are discussed in Section 3.2.1.1.
+**  The 80331 translate value register¡¦s programmed value must be naturally aligned with the base
+**  address register¡¦s programmed value. The limit register is used as a mask; thus, the lower address
+**  bits programmed into the 80331 translate value register are invalid. Refer to the PCI Local Bus
+**  Specification, Revision 2.3 for additional information on programming base address registers.
+**  Bits 31 to 12 within the IALR2 have a direct effect on the IABAR2 register, bits 31 to 12, with a
+**  one to one correspondence. A value of 0 in a bit within the IALR2 makes the corresponding bit
+**  within the IABAR2 a read only bit which always returns 0. A value of 1 in a bit within the IALR2
+**  makes the corresponding bit within the IABAR2 read/write from PCI. Note that a consequence of
+**  this programming scheme is that unless a valid value exists within the IALR2, all writes to the
+**  IABAR2 has no effect since a value of all zeros within the IALR2 makes the IABAR2 a read only
+**  register.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:12     00000H                        Inbound Translation Limit 2 - This readback value determines the memory block size required for the ATUs memory window 2.
+**  11:00       000H                        Reserved
+***********************************************************************************
+*/
+#define     ARCMSR_INBOUND_ATU_LIMIT2_REG		          0x54	/*dword 0x57,0x56,0x55,0x54 */
+/*
+***********************************************************************************
+**  Inbound ATU Translate Value Register 2 - IATVR2
+**
+**  The Inbound ATU Translate Value Register 2 (IATVR2) contains the internal bus address used to
+**  convert PCI bus addresses. The converted address is driven on the internal bus as a result of the
+**  inbound ATU address translation.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:12     00000H                        Inbound ATU Translation Value 2 - This value is used to convert the PCI address to internal bus addresses.
+**                                                                            This value must be 64-bit aligned on the internal bus. The default address allows the ATU to access the internal 80331 memory-mapped registers.
+**  11:00       000H                        Reserved
+***********************************************************************************
+*/
+#define     ARCMSR_INBOUND_ATU_TRANSLATE_VALUE2_REG		          0x58	/*dword 0x5B,0x5A,0x59,0x58 */
+/*
+***********************************************************************************
+**  Outbound I/O Window Translate Value Register - OIOWTVR
+**
+**  The Outbound I/O Window Translate Value Register (OIOWTVR) contains the PCI I/O address
+**  used to convert the internal bus access to a PCI address. This address is driven on the PCI bus as a
+**  result of the outbound ATU address translation.
+**  The I/O window is from 80331 internal bus address 9000 000H to 9000 FFFFH with the fixed
+**  length of 64 Kbytes.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:16     0000H                         Outbound I/O Window Translate Value - Used to convert internal bus addresses to PCI addresses.
+**  15:00     0000H                         Reserved
+***********************************************************************************
+*/
+#define     ARCMSR_OUTBOUND_IO_WINDOW_TRANSLATE_VALUE_REG		          0x5C	/*dword 0x5F,0x5E,0x5D,0x5C */
+/*
+***********************************************************************************
+**  Outbound Memory Window Translate Value Register 0 -OMWTVR0
+**
+**  The Outbound Memory Window Translate Value Register 0 (OMWTVR0) contains the PCI
+**  address used to convert 80331 internal bus addresses for outbound transactions. This address is
+**  driven on the PCI bus as a result of the outbound ATU address translation.
+**  The memory window is from internal bus address 8000 000H to 83FF FFFFH with the fixed length
+**  of 64 Mbytes.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:26       00H                         Outbound MW Translate Value - Used to convert 80331 internal bus addresses to PCI addresses.
+**  25:02     00 0000H                      Reserved
+**  01:00      00 2                         Burst Order - This bit field shows the address sequence during a memory burst. Only linear incrementing mode is supported.
+***********************************************************************************
+*/
+#define     ARCMSR_OUTBOUND_MEMORY_WINDOW_TRANSLATE_VALUE0_REG		          0x60	/*dword 0x63,0x62,0x61,0x60 */
+/*
+***********************************************************************************
+**  Outbound Upper 32-bit Memory Window Translate Value Register 0 - OUMWTVR0
+**
+**  The Outbound Upper 32-bit Memory Window Translate Value Register 0 (OUMWTVR0) defines
+**  the upper 32-bits of address used during a dual address cycle. This enables the outbound ATU to
+**  directly address anywhere within the 64-bit host address space. When this register is all-zero, then
+**  a SAC is generated on the PCI bus.
+**  The memory window is from internal bus address 8000 000H to 83FF FFFFH with the fixed
+**  length of 64 Mbytes.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:00     0000 0000H                    These bits define the upper 32-bits of address driven during the dual address cycle (DAC).
+***********************************************************************************
+*/
+#define     ARCMSR_OUTBOUND_UPPER32_MEMORY_WINDOW_TRANSLATE_VALUE0_REG		          0x64	/*dword 0x67,0x66,0x65,0x64 */
+/*
+***********************************************************************************
+**  Outbound Memory Window Translate Value Register 1 -OMWTVR1
+**
+**  The Outbound Memory Window Translate Value Register 1 (OMWTVR1) contains the PCI
+**  address used to convert 80331 internal bus addresses for outbound transactions. This address is
+**  driven on the PCI bus as a result of the outbound ATU address translation.
+**  The memory window is from internal bus address 8400 000H to 87FF FFFFH with the fixed length
+**  of 64 Mbytes.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:26       00H                         Outbound MW Translate Value - Used to convert 80331 internal bus addresses to PCI addresses.
+**  25:02     00 0000H                      Reserved
+**  01:00       00 2                        Burst Order - This bit field shows the address sequence during a memory burst. Only linear incrementing mode is supported.
+***********************************************************************************
+*/
+#define     ARCMSR_OUTBOUND_MEMORY_WINDOW_TRANSLATE_VALUE1_REG		          0x68	/*dword 0x6B,0x6A,0x69,0x68 */
+/*
+***********************************************************************************
+**  Outbound Upper 32-bit Memory Window Translate Value Register 1 - OUMWTVR1
+**
+**  The Outbound Upper 32-bit Memory Window Translate Value Register 1 (OUMWTVR1) defines
+**  the upper 32-bits of address used during a dual address cycle. This enables the outbound ATU to
+**  directly address anywhere within the 64-bit host address space. When this register is all-zero, then
+**  a SAC is generated on the PCI bus.
+**  The memory window is from internal bus address 8400 000H to 87FF FFFFH with the fixed length
+**  of 64 Mbytes.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:00    0000 0000H                     These bits define the upper 32-bits of address driven during the dual address cycle (DAC).
+***********************************************************************************
+*/
+#define     ARCMSR_OUTBOUND_UPPER32_MEMORY_WINDOW_TRANSLATE_VALUE1_REG		          0x6C	/*dword 0x6F,0x6E,0x6D,0x6C */
+/*
+***********************************************************************************
+**  Outbound Upper 32-bit Direct Window Translate Value Register - OUDWTVR
+**
+**  The Outbound Upper 32-bit Direct Window Translate Value Register (OUDWTVR) defines the
+**  upper 32-bits of address used during a dual address cycle for the transactions via Direct Addressing
+**  Window. This enables the outbound ATU to directly address anywhere within the 64-bit host
+**  address space. When this register is all-zero, then a SAC is generated on the PCI bus.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:00    0000 0000H                     These bits define the upper 32-bits of address driven during the dual address cycle (DAC).
+***********************************************************************************
+*/
+#define     ARCMSR_OUTBOUND_UPPER32_DIRECT_WINDOW_TRANSLATE_VALUE_REG		          0x78	/*dword 0x7B,0x7A,0x79,0x78 */
+/*
+***********************************************************************************
+**  ATU Configuration Register - ATUCR
+**
+**  The ATU Configuration Register controls the outbound address translation for address translation
+**  unit. It also contains bits for Conventional PCI Delayed Read Command (DRC) aliasing, discard
+**  timer status, SERR# manual assertion, SERR# detection interrupt masking, and ATU BIST
+**  interrupt enabling.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:20       00H                         Reserved
+**  19          0 2                         ATU DRC Alias - when set, the ATU does not distinguish read commands when attempting to match a
+**  														current PCI read transaction with read data enqueued within the DRC buffer. When clear, a current read
+**  														transaction must have the exact same read command as the DRR for the ATU to deliver DRC data. Not
+**  														applicable in the PCI-X mode.
+**  18          0 2                         Direct Addressing Upper 2Gbytes Translation Enable - When set, with Direct Addressing enabled (bit 7 of the ATUCR set), the ATU forwards internal bus cycles with an address between 0000.0040H and
+**                                                          7FFF.FFFFH to the PCI bus with bit 31 of the address set (8000.0000H - FFFF.FFFFH). When clear, no translation occurs.
+**  17          0 2                         Reserved
+**  16          0 2                         SERR# Manual Assertion - when set, the ATU asserts SERR# for one clock on the PCI interface. Until
+**                                                          cleared, SERR# may not be manually asserted again. Once cleared, operation proceeds as specified.
+**  15          0 2                         ATU Discard Timer Status - when set, one of the 4 discard timers within the ATU has expired and
+**                                                          discarded the delayed completion transaction within the queue. When clear, no timer has expired.
+**  14:10    00000 2                        Reserved
+**  09          0 2                         SERR# Detected Interrupt Enable - When set, the Intel XScale core is signalled an HPI# interrupt
+**                                                          when the ATU detects that SERR# was asserted. When clear, the Intel XScale core is not interrupted when SERR# is detected.
+**  08          0 2                         Direct Addressing Enable - Setting this bit enables direct outbound addressing through the ATU.
+**  														Internal bus cycles with an address between 0000.0040H and 7FFF.FFFFH automatically forwards to
+**  														the PCI bus with or without translation of address bit 31 based on the setting of bit 18 of the ATUCR.
+**  07:04    0000 2                         Reserved
+**  03          0 2                         ATU BIST Interrupt Enable - When set, enables an interrupt to the Intel XScale core when the start
+**                                                          BIST bit is set in the ATUBISTR register. This bit is also reflected as the BIST Capable bit 7 in the ATUBISTR register.
+**  02          0 2                         Reserved
+**  01          0 2                         Outbound ATU Enable - When set, enables the outbound address translation unit. When cleared, disables the outbound ATU.
+**  00          0 2                         Reserved
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_CONFIGURATION_REG		          0x80	/*dword 0x83,0x82,0x81,0x80 */
+/*
+***********************************************************************************
+**  PCI Configuration and Status Register - PCSR
+**
+**  The PCI Configuration and Status Register has additional bits for controlling and monitoring
+**  various features of the PCI bus interface.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:19      0000H                        Reserved
+**  18          0 2                         Detected Address or Attribute Parity Error - set when a parity error is detected during either the address
+**  														or attribute phase of a transaction on the PCI bus even when the ATUCMD register Parity Error
+**  														Response bit is cleared. Set under the following conditions:
+**  														¡E Any Address or Attribute (PCI-X Only) Parity Error on the Bus (including one generated by the ATU).
+**  17:16  Varies with
+**  										external state
+**  										of DEVSEL#,
+**  										STOP#, and
+**  										TRDY#,
+**  										during
+**  										P_RST#
+**  										PCI-X capability - These two bits define the mode of the PCI bus (conventional or PCI-X) as well as the
+**  										operating frequency in the case of PCI-X mode.
+**  										00 - Conventional PCI mode
+**  										01 - PCI-X 66
+**  										10 - PCI-X 100
+**  										11 - PCI-X 133
+**  										As defined by the PCI-X Addendum to the PCI Local Bus Specification, Revision 1.0a, the operating
+**  										mode is determined by an initialization pattern on the PCI bus during P_RST# assertion:
+**  										DEVSEL# STOP# TRDY# Mode
+**  										Deasserted Deasserted Deasserted Conventional
+**  										Deasserted Deasserted Asserted PCI-X 66
+**  										Deasserted Asserted Deasserted PCI-X 100
+**  										Deasserted Asserted Asserted PCI-X 133
+**  										All other patterns are reserved.
+**  15          0 2
+**  										Outbound Transaction Queue Busy:
+**  										    0=Outbound Transaction Queue Empty
+**  										    1=Outbound Transaction Queue Busy
+**  14          0 2
+**  										Inbound Transaction Queue Busy:
+**  										    0=Inbound Transaction Queue Empty
+**  										    1=Inbound Transaction Queue Busy
+**  13          0 2                         Reserved.
+**  12          0 2
+**  										Discard Timer Value - This bit controls the time-out value for the four discard timers attached to the queues holding read data.
+**                                                         A value of 0 indicates the time-out value is 2 15 clocks.
+**                                                         A value of 1 indicates the time-out value is 2 10 clocks.
+**  11          0 2                         Reserved.
+**  10      Varies with
+**  										external state
+**  										of M66EN
+**  										during
+**  										P_RST#
+**  										Bus Operating at 66 MHz - When set, the interface has been initialized to function at 66 MHz in
+**  										Conventional PCI mode by the assertion of M66EN during bus initialization. When clear, the interface
+**  										has been initialized as a 33 MHz bus.
+**  										NOTE: When PCSR bits 17:16 are not equal to zero, then this bit is meaningless since the 80331 is
+**  										operating in PCI-X mode.
+**  09          0 2                         Reserved
+**  08      Varies with
+**  										external state
+**  										of REQ64#
+**  										during
+**  										P_RST#
+**  										PCI Bus 64-Bit Capable - When clear, the PCI bus interface has been configured as 64-bit capable by
+**  										the assertion of REQ64# on the rising edge of P_RST#. When set, the PCI interface is configured as
+**  										32-bit only.
+**  07:06      00 2                         Reserved.
+**  05         0 2   						Reset Internal Bus - This bit controls the reset of the Intel XScale core and all units on the internal
+**  													bus. In addition to the internal bus initialization, this bit triggers the assertion of the M_RST# pin for
+**  													initialization of registered DIMMs. When set:
+**  													When operating in the conventional PCI mode:
+**  													¡E All current PCI transactions being mastered by the ATU completes, and the ATU master interfaces
+**  													proceeds to an idle state. No additional transactions is mastered by these units until the internal bus
+**  													reset is complete.
+**  													¡E All current transactions being slaved by the ATU on either the PCI bus or the internal bus
+**  													completes, and the ATU target interfaces proceeds to an idle state. All future slave transactions
+**  													master aborts, with the exception of the completion cycle for the transaction that set the Reset
+**  													Internal Bus bit in the PCSR.
+**  													¡E When the value of the Core Processor Reset bit in the PCSR (upon P_RST# assertion) is set, the
+**  													Intel XScale core is held in reset when the internal bus reset is complete.
+**  													¡E The ATU ignores configuration cycles, and they appears as master aborts for: 32 Internal Bus clocks.
+**  													¡E The 80331 hardware clears this bit after the reset operation completes.
+**  													When operating in the PCI-X mode:
+**  													The ATU hardware responds the same as in Conventional PCI-X mode. However, this may create a
+**  													problem in PCI-X mode for split requests in that there may still be an outstanding split completion that the
+**  													ATU is either waiting to receive (Outbound Request) or initiate (Inbound Read Request). For a cleaner
+**  													internal bus reset, host software can take the following steps prior to asserting Reset Internal bus:
+**  													1. Clear the Bus Master (bit 2 of the ATUCMD) and the Memory Enable (bit 1 of the ATUCMD) bits in
+**  													the ATUCMD. This ensures that no new transactions, either outbound or inbound are enqueued.
+**  													2. Wait for both the Outbound (bit 15 of the PCSR) and Inbound Read (bit 14 of the PCSR) Transaction
+**  													queue busy bits to be clear.
+**  													3. Set the Reset Internal Bus bit
+**  													As a result, the ATU hardware resets the internal bus using the same logic as in conventional mode,
+**  													however the user is now assured that the ATU no longer has any pending inbound or outbound split
+**  													completion transactions.
+**  													NOTE: Since the Reset Internal Bus bit is set using an inbound configuration cycle, the user is
+**  													guaranteed that any prior configuration cycles have properly completed since there is only a one
+**  													deep transaction queue for configuration transaction requests. The ATU sends the appropriate
+**    												    Split Write Completion Message to the Requester prior to the onset of Internal Bus Reset.
+**  04      0 2						        Bus Master Indicator Enable: Provides software control for the Bus Master Indicator signal P_BMI used
+**  													for external RAIDIOS logic control of private devices. Only valid when operating with the bridge and
+**  													central resource/arbiter disabled (BRG_EN =low, ARB_EN=low).
+**  03		Varies with
+**  													external state
+**  													of PRIVDEV
+**  													during
+**  													P_RST#
+**  													Private Device Enable - This bit indicates the state of the reset strap which enables the private device
+**  													control mechanism within the PCI-to-PCI Bridge SISR configuration register.
+**  													0=Private Device control Disabled - SISR register bits default to zero
+**  													1=Private Device control Enabled - SISR register bits default to one
+**	02		Varies with
+**  													external state
+**  													of RETRY
+**  													during
+**  													P_RST#
+**  													Configuration Cycle Retry - When this bit is set, the PCI interface of the 80331 responds to all
+**  													configuration cycles with a Retry condition. When clear, the 80331 responds to the appropriate
+**  													configuration cycles.
+**  													The default condition for this bit is based on the external state of the RETRY pin at the rising edge of
+**  													P_RST#. When the external state of the pin is high, the bit is set. When the external state of the pin is
+**  													low, the bit is cleared.
+**  01		Varies with
+**  													external state
+**  													of
+**  													CORE_RST#
+**  													during
+**  													P_RST#
+**  													Core Processor Reset - This bit is set to its default value by the hardware when either P_RST# is
+**  													asserted or the Reset Internal Bus bit in PCSR is set. When this bit is set, the Intel XScale core is
+**  													being held in reset. Software cannot set this bit. Software is required to clear this bit to deassert Intel
+**  													XScale  core reset.
+**  													The default condition for this bit is based on the external state of the CORE_RST# pin at the rising edge
+**  													of P_RST#. When the external state of the pin is low, the bit is set. When the external state of the pin is
+**  													high, the bit is clear.
+**  00		Varies with
+**  													external state
+**  													of PRIVMEM
+**  													during
+**  													P_RST#
+**  													Private Memory Enable - This bit indicates the state of the reset strap which enables the private device
+**  													control mechanism within the PCI-to-PCI Bridge SDER configuration register.
+**  													0=Private Memory control Disabled - SDER register bit 2 default to zero
+**  													1=Private Memory control Enabled - SDER register bits 2 default to one
+***********************************************************************************
+*/
+#define     ARCMSR_PCI_CONFIGURATION_STATUS_REG		          0x84	/*dword 0x87,0x86,0x85,0x84 */
+/*
+***********************************************************************************
+**  ATU Interrupt Status Register - ATUISR
+**
+**  The ATU Interrupt Status Register is used to notify the core processor of the source of an ATU
+**  interrupt. In addition, this register is written to clear the source of the interrupt to the interrupt unit
+**  of the 80331. All bits in this register are Read/Clear.
+**  Bits 4:0 are a direct reflection of bits 14:11 and bit 8 (respectively) of the ATU Status Register
+**  (these bits are set at the same time by hardware but need to be cleared independently). Bit 7 is set
+**  by an error associated with the internal bus of the 80331. Bit 8 is for software BIST. The
+**  conditions that result in an ATU interrupt are cleared by writing a 1 to the appropriate bits in this
+**  register.
+**  Note: Bits 4:0, and bits 15 and 13:7 can result in an interrupt being driven to the Intel XScale core.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:18      0000H                        Reserved
+**  17          0 2                         VPD Address Register Updated - This bit is set when a PCI bus configuration write occurs to the VPDAR
+**  														register. Configuration register writes to the VPDAR does NOT result in bit 15 also being set. When set,
+**  														this bit results in the assertion of the ATU Configure Register Write Interrupt.
+**  16          0 2                         Reserved
+**  15          0 2                         ATU Configuration Write - This bit is set when a PCI bus configuration write occurs to any ATU register.
+**                                                          When set, this bit results in the assertion of the ATU Configure Register Write Interrupt.
+**  14          0 2                         ATU Inbound Memory Window 1 Base Updated - This bit is set when a PCI bus configuration write
+**  														occurs to either the IABAR1 register or the IAUBAR1 register. Configuration register writes to these
+**  														registers deos NOT result in bit 15 also being set. When set, this bit results in the assertion of the ATU
+**  														Configure Register Write Interrupt.
+**  13          0 2                         Initiated Split Completion Error Message - This bit is set when the device initiates a Split Completion
+**                                                          Message on the PCI Bus with the Split Completion Error attribute bit set.
+**  12          0 2                         Received Split Completion Error Message - This bit is set when the device receives a Split Completion
+**                                                          Message from the PCI Bus with the Split Completion Error attribute bit set.
+**  11          0 2                         Power State Transition - When the Power State Field of the ATU Power Management Control/Status
+**  														Register is written to transition the ATU function Power State from D0 to D3, D0 to D1, or D3 to D0 and
+**  														the ATU Power State Transition Interrupt mask bit is cleared, this bit is set.
+**  10          0 2                         P_SERR# Asserted - set when P_SERR# is asserted on the PCI bus by the ATU.
+**  09          0 2                         Detected Parity Error - set when a parity error is detected on the PCI bus even when the ATUCMD
+**  														register¡¦s Parity Error Response bit is cleared. Set under the following conditions:
+**  														¡E Write Data Parity Error when the ATU is a target (inbound write).
+**  														¡E Read Data Parity Error when the ATU is an initiator (outbound read).
+**  														¡E Any Address or Attribute (PCI-X Only) Parity Error on the Bus.
+**  08          0 2                         ATU BIST Interrupt - When set, generates the ATU BIST Start Interrupt and indicates the host processor
+**  														has set the Start BIST bit (ATUBISTR register bit 6), when the ATU BIST interrupt is enabled (ATUCR
+**  														register bit 3). The Intel XScale core can initiate the software BIST and store the result in ATUBISTR
+**  														register bits 3:0.
+**  														Configuration register writes to the ATUBISTR does NOT result in bit 15 also being set or the assertion
+**  														of the ATU Configure Register Write Interrupt.
+**  07          0 2                         Internal Bus Master Abort - set when a transaction initiated by the ATU internal bus initiator interface ends in a Master-abort.
+**  06:05      00 2                         Reserved.
+**  04          0 2                         P_SERR# Detected - set when P_SERR# is detected on the PCI bus by the ATU.
+**  03          0 2                         PCI Master Abort - set when a transaction initiated by the ATU PCI initiator interface ends in a Master-abort.
+**  02          0 2                         PCI Target Abort (master) - set when a transaction initiated by the ATU PCI master interface ends in a Target-abort.
+**  01          0 2                         PCI Target Abort (target) - set when the ATU interface, acting as a target, terminates the transaction on the PCI bus with a target abort.
+**  00          0 2                         PCI Master Parity Error - Master Parity Error - The ATU interface sets this bit under the following
+**  														conditions:
+**  														¡E The ATU asserted PERR# itself or the ATU observed PERR# asserted.
+**  														¡E And the ATU acted as the requester for the operation in which the error occurred.
+**  														¡E And the ATUCMD register¡¦s Parity Error Response bit is set
+**  														¡E Or (PCI-X Mode Only) the ATU received a Write Data Parity Error Message
+**  														¡E And the ATUCMD register¡¦s Parity Error Response bit is set
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_INTERRUPT_STATUS_REG		          0x88	/*dword 0x8B,0x8A,0x89,0x88 */
+/*
+***********************************************************************************
+**  ATU Interrupt Mask Register - ATUIMR
+**
+**  The ATU Interrupt Mask Register contains the control bit to enable and disable interrupts
+**  generated by the ATU.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:15     0 0000H                       Reserved
+**  14        0 2                           VPD Address Register Updated Mask - Controls the setting of bit 17 of the ATUISR and generation of the
+**  										ATU Configuration Register Write interrupt when a PCI bus write occurs to the VPDAR register.
+**  										0=Not Masked
+**  										1=Masked
+**  13        0 2                           Reserved
+**  12        0 2                           Configuration Register Write Mask - Controls the setting of bit 15 of the ATUISR and generation of the
+**  										ATU Configuration Register Write interrupt when a PCI bus write occurs to any ATU configuration register
+**  										except those covered by mask bit 11 and bit 14 of this register, and ATU BIST enable bit 3 of the ATUCR.
+**  										0=Not Masked
+**  										1=Masked
+**  11        1 2                           ATU Inbound Memory Window 1 Base Updated Mask - Controls the setting of bit 14 of the ATUISR and
+**  														generation of the ATU Configuration Register Write interrupt when a PCI bus write occurs to either the
+**  														IABAR1 register or the IAUBAR1 register.
+**  														0=Not Masked
+**  														1=Masked
+**  10        0 2                           Initiated Split Completion Error Message Interrupt Mask - Controls the setting of bit 13 of the ATUISR and
+**  														generation of the ATU Error interrupt when the ATU initiates a Split Completion Error Message.
+**  														0=Not Masked
+**  														1=Masked
+**  09        0 2                           Received Split Completion Error Message Interrupt Mask- Controls the setting of bit 12 of the ATUISR
+**  														and generation of the ATU Error interrupt when a Split Completion Error Message results in bit 29 of the
+**  														PCIXSR being set.
+**  														0=Not Masked
+**  														1=Masked
+**  08        1 2                           Power State Transition Interrupt Mask - Controls the setting of bit 12 of the ATUISR and generation of the
+**  														ATU Error interrupt when ATU Power Management Control/Status Register is written to transition the
+**  														ATU Function Power State from D0 to D3, D0 to D1, D1 to D3 or D3 to D0.
+**  														0=Not Masked
+**  														1=Masked
+**  07        0 2                           ATU Detected Parity Error Interrupt Mask - Controls the setting of bit 9 of the ATUISR and generation of
+**  														the ATU Error interrupt when a parity error detected on the PCI bus that sets bit 15 of the ATUSR.
+**  														0=Not Masked
+**  														1=Masked
+**  06        0 2                           ATU SERR# Asserted Interrupt Mask - Controls the setting of bit 10 of the ATUISR and generation of the
+**  														ATU Error interrupt when SERR# is asserted on the PCI interface resulting in bit 14 of the ATUSR being set.
+**  														0=Not Masked
+**  														1=Masked
+**  														NOTE: This bit is specific to the ATU asserting SERR# and not detecting SERR# from another master.
+**  05        0 2                           ATU PCI Master Abort Interrupt Mask - Controls the setting of bit 3 of the ATUISR and generation of the
+**  														ATU Error interrupt when a master abort error resulting in bit 13 of the ATUSR being set.
+**  														0=Not Masked
+**  														1=Masked
+**  04        0 2                           ATU PCI Target Abort (Master) Interrupt Mask- Controls the setting of bit 12 of the ATUISR and ATU Error
+**  														generation of the interrupt when a target abort error resulting in bit 12 of the ATUSR being set
+**  														0=Not Masked
+**  														1=Masked
+**  03        0 2                           ATU PCI Target Abort (Target) Interrupt Mask- Controls the setting of bit 1 of the ATUISR and generation
+**  														of the ATU Error interrupt when a target abort error resulting in bit 11 of the ATUSR being set.
+**  														0=Not Masked
+**  														1=Masked
+**  02        0 2                           ATU PCI Master Parity Error Interrupt Mask - Controls the setting of bit 0 of the ATUISR and generation
+**  														of the ATU Error interrupt when a parity error resulting in bit 8 of the ATUSR being set.
+**  														0=Not Masked
+**  														1=Masked
+**  01        0 2                           ATU Inbound Error SERR# Enable - Controls when the ATU asserts (when enabled through the
+**  														ATUCMD) SERR# on the PCI interface in response to a master abort on the internal bus during an
+**  														inbound write transaction.
+**  														0=SERR# Not Asserted due to error
+**  														1=SERR# Asserted due to error
+**  00        0 2                           ATU ECC Target Abort Enable - Controls the ATU response on the PCI interface to a target abort (ECC
+**  														error) from the memory controller on the internal bus. In conventional mode, this action only occurs
+**  														during an inbound read transaction where the data phase that was target aborted on the internal bus is
+**  														actually requested from the inbound read queue.
+**  														0=Disconnect with data (the data being up to 64 bits of 1¡¦s)
+**  														1=Target Abort
+**  														NOTE: In PCI-X Mode, The ATU initiates a Split Completion Error Message (with message class=2h -
+**  														completer error and message index=81h - 80331 internal bus target abort) on the PCI bus,
+**  														independent of the setting of this bit.
+***********************************************************************************
+*/
+#define     ARCMSR_ATU_INTERRUPT_MASK_REG		          0x8C	/*dword 0x8F,0x8E,0x8D,0x8C */
+/*
+***********************************************************************************
+**  Inbound ATU Base Address Register 3 - IABAR3
+**
+**  . The Inbound ATU Base Address Register 3 (IABAR3) together with the Inbound ATU Upper Base Address Register 3 (IAUBAR3) defines the block of memory addresses where the inbound translation window 3 begins.
+**  . The inbound ATU decodes and forwards the bus request to the 80331 internal bus with a translated address to map into 80331 local memory.
+**  . The IABAR3 and IAUBAR3 define the base address and describes the required memory block size.
+**  . Bits 31 through 12 of the IABAR3 is either read/write bits or read only with a value of 0 depending on the value located within the IALR3.
+**    The programmed value within the base address register must comply with the PCI programming requirements for address alignment.
+**  Note:
+**      Since IABAR3 does not appear in the standard PCI configuration header space (offsets 00H - 3CH),
+**      IABAR3 is not configured by the host during normal system initialization.
+**  Warning:
+**    When a non-zero value is not written to IALR3,
+**                          the user should not set either the Prefetchable Indicator
+**                                                      or the Type         Indicator for 64 bit addressability.
+**                          This is the default for IABAR3.
+**  Assuming a non-zero value is written to IALR3,
+**                          the user may set the Prefetchable Indicator
+**                                        or the Type         Indicator:
+**  						a. Since non prefetchable memory windows can never be placed above the 4 Gbyte address boundary,
+**                             when the Prefetchable Indicator is not set,
+**                             the user should also leave the Type Indicator set for 32 bit addressability.
+**                             This is the default for IABAR3.
+**  						b. when the Prefetchable Indicator is set,
+**                             the user should also set the Type Indicator for 64 bit addressability.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:12     00000H                        Translation Base Address 3 - These bits define the actual location the translation function is to respond to when addressed from the PCI bus.
+**  11:04        00H                        Reserved.
+**  03           0 2                        Prefetchable Indicator - When set, defines the memory space as prefetchable.
+**  02:01       00 2                        Type Indicator - Defines the width of the addressability for this memory window:
+**  												00 - Memory Window is locatable anywhere in 32 bit address space
+**  												10 - Memory Window is locatable anywhere in 64 bit address space
+**  00           0 2                        Memory Space Indicator - This bit field describes memory or I/O space base address.
+**                                                                   The ATU does not occupy I/O space,
+**                                                                   thus this bit must be zero.
+***********************************************************************************
+*/
+#define     ARCMSR_INBOUND_ATU_BASE_ADDRESS3_REG		          0x90	/*dword 0x93,0x92,0x91,0x90 */
+/*
+***********************************************************************************
+**  Inbound ATU Upper Base Address Register 3 - IAUBAR3
+**
+**  This register contains the upper base address when decoding PCI addresses beyond 4 GBytes.
+**  Together with the Translation Base Address this register defines the actual location the translation function is to respond to when addressed from the PCI bus for addresses > 4GBytes (for DACs).
+**  The programmed value within the base address register must comply with the PCI programming
+**  requirements for address alignment.
+**  Note:
+**      When the Type indicator of IABAR3 is set to indicate 32 bit addressability,
+**      the IAUBAR3 register attributes are read-only.
+**      This is the default for IABAR3.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:0      00000H                        Translation Upper Base Address 3 - Together with the Translation Base Address 3 these bits define the actual location the translation function is to respond to when addressed from the PCI bus for addresses > 4GBytes.
+***********************************************************************************
+*/
+#define     ARCMSR_INBOUND_ATU_UPPER_BASE_ADDRESS3_REG		          0x94	/*dword 0x97,0x96,0x95,0x94 */
+/*
+***********************************************************************************
+**  Inbound ATU Limit Register 3 - IALR3
+**
+**  Inbound address translation for memory window 3 occurs for data transfers occurring from the PCI
+**  bus (originated from the PCI bus) to the 80331 internal bus. The address translation block converts
+**  PCI addresses to internal bus addresses.
+**  The inbound translation base address for inbound window 3 is specified in Section 3.10.15. When
+**  determining block size requirements ¡X as described in Section 3.10.21 ¡X the translation limit
+**  register provides the block size requirements for the base address register. The remaining registers
+**  used for performing address translation are discussed in Section 3.2.1.1.
+**  The 80331 translate value register¡¦s programmed value must be naturally aligned with the base
+**  address register¡¦s programmed value. The limit register is used as a mask; thus, the lower address
+**  bits programmed into the 80331 translate value register are invalid. Refer to the PCI Local Bus
+**  Specification, Revision 2.3 for additional information on programming base address registers.
+**  Bits 31 to 12 within the IALR3 have a direct effect on the IABAR3 register, bits 31 to 12, with a
+**  one to one correspondence. A value of 0 in a bit within the IALR3 makes the corresponding bit
+**  within the IABAR3 a read only bit which always returns 0. A value of 1 in a bit within the IALR3
+**  makes the corresponding bit within the IABAR3 read/write from PCI. Note that a consequence of
+**  this programming scheme is that unless a valid value exists within the IALR3, all writes to the
+**  IABAR3 has no effect since a value of all zeros within the IALR3 makes the IABAR3 a read only
+**  register.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:12     00000H                        Inbound Translation Limit 3 - This readback value determines the memory block size required for the ATUs memory window 3.
+**  11:00       000H                        Reserved
+***********************************************************************************
+*/
+#define     ARCMSR_INBOUND_ATU_LIMIT3_REG		          0x98	/*dword 0x9B,0x9A,0x99,0x98 */
+/*
+***********************************************************************************
+**  Inbound ATU Translate Value Register 3 - IATVR3
+**
+**  The Inbound ATU Translate Value Register 3 (IATVR3) contains the internal bus address used to
+**  convert PCI bus addresses. The converted address is driven on the internal bus as a result of the
+**  inbound ATU address translation.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:12     00000H                        Inbound ATU Translation Value 3 - This value is used to convert the PCI address to internal bus addresses.
+**                                                          This value must be 64-bit aligned on the internal bus. The default address allows the ATU to
+**                                                          access the internal 80331 memory-mapped registers.
+**  11:00       000H                        Reserved
+***********************************************************************************
+*/
+#define     ARCMSR_INBOUND_ATU_TRANSLATE_VALUE3_REG		          0x9C	/*dword 0x9F,0x9E,0x9D,0x9C */
+/*
+***********************************************************************************
+**  Outbound Configuration Cycle Address Register - OCCAR
+**
+**  The Outbound Configuration Cycle Address Register is used to hold the 32-bit PCI configuration
+**  cycle address. The Intel XScale core writes the PCI configuration cycles address which then
+**  enables the outbound configuration read or write. The Intel XScale core then performs a read or
+**  write to the Outbound Configuration Cycle Data Register to initiate the configuration cycle on the
+**  PCI bus.
+**  Note: Bits 15:11 of the configuration cycle address for Type 0 configuration cycles are defined differently
+**  for Conventional versus PCI-X modes. When 80331 software programs the OCCAR to initiate a
+**  Type 0 configuration cycle, the OCCAR should always be loaded based on the PCI-X definition for
+**  the Type 0 configuration cycle address. When operating in Conventional mode, the 80331 clears
+**  bits 15:11 of the OCCAR prior to initiating an outbound Type 0 configuration cycle. See the PCI-X
+**  Addendum to the PCI Local Bus Specification, Revision 1.0a for details on the two formats.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:00    0000 0000H                     Configuration Cycle Address - These bits define the 32-bit PCI address used during an outbound configuration read or write cycle.
+***********************************************************************************
+*/
+#define     ARCMSR_OUTBOUND_CONFIGURATION_CYCLE_ADDRESS_REG		          0xA4	/*dword 0xA7,0xA6,0xA5,0xA4 */
+/*
+***********************************************************************************
+**  Outbound Configuration Cycle Data Register - OCCDR
+**
+**  The Outbound Configuration Cycle Data Register is used to initiate a configuration read or write
+**  on the PCI bus. The register is logical rather than physical meaning that it is an address not a
+**  register. The Intel XScale core reads or writes the data registers memory-mapped address to
+**  initiate the configuration cycle on the PCI bus with the address found in the OCCAR. For a
+**  configuration write, the data is latched from the internal bus and forwarded directly to the OWQ.
+**  For a read, the data is returned directly from the ORQ to the Intel XScale core and is never
+**  actually entered into the data register (which does not physically exist).
+**  The OCCDR is only visible from 80331 internal bus address space and appears as a reserved value
+**  within the ATU configuration space.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:00    0000 0000H                     Configuration Cycle Data - These bits define the data used during an outbound configuration read or write cycle.
+***********************************************************************************
+*/
+#define     ARCMSR_OUTBOUND_CONFIGURATION_CYCLE_DATA_REG		          0xAC	/*dword 0xAF,0xAE,0xAD,0xAC */
+/*
+***********************************************************************************
+**  VPD Capability Identifier Register - VPD_CAPID
+**
+**  The Capability Identifier Register bits adhere to the definitions in the PCI Local Bus Specification,
+**  Revision 2.3. This register in the PCI Extended Capability header identifies the type of Extended
+**  Capability contained in that header. In the case of the 80331, this is the VPD extended capability
+**  with an ID of 03H as defined by the PCI Local Bus Specification, Revision 2.3.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  07:00       03H                         Cap_Id - This field with its¡¦ 03H value identifies this item in the linked list of Extended Capability Headers as being the VPD capability registers.
+***********************************************************************************
+*/
+#define     ARCMSR_VPD_CAPABILITY_IDENTIFIER_REG		      0xB8	/*byte */
+/*
+***********************************************************************************
+**  VPD Next Item Pointer Register - VPD_NXTP
+**
+**  The Next Item Pointer Register bits adhere to the definitions in the PCI Local Bus Specification,
+**  Revision 2.3. This register describes the location of the next item in the function¡¦s capability list.
+**  For the 80331, this the final capability list, and hence, this register is set to 00H.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  07:00       00H                         Next_ Item_ Pointer - This field provides an offset into the function¡¦s configuration space pointing to the
+**                                                          next item in the function¡¦s capability list. Since the VPD capabilities are the last in the linked list of
+**                                                          extended capabilities in the 80331, the register is set to 00H.
+***********************************************************************************
+*/
+#define     ARCMSR_VPD_NEXT_ITEM_PTR_REG		          0xB9	/*byte */
+/*
+***********************************************************************************
+**  VPD Address Register - VPD_AR
+**
+**  The VPD Address register (VPDAR) contains the DWORD-aligned byte address of the VPD to be
+**  accessed. The register is read/write and the initial value at power-up is indeterminate.
+**  A PCI Configuration Write to the VPDAR interrupts the Intel XScale core. Software can use
+**  the Flag setting to determine whether the configuration write was intended to initiate a read or
+**  write of the VPD through the VPD Data Register.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  15          0 2                         Flag - A flag is used to indicate when a transfer of data between the VPD Data Register and the storage
+**                                                          component has completed. Please see Section 3.9, ¡§Vital Product Data¡¨ on page 201 for more details on
+**                                                          how the 80331 handles the data transfer.
+**  14:0       0000H                        VPD Address - This register is written to set the DWORD-aligned byte address used to read or write
+**                                                          Vital Product Data from the VPD storage component.
+***********************************************************************************
+*/
+#define     ARCMSR_VPD_ADDRESS_REG		          0xBA	/*word 0xBB,0xBA */
+/*
+***********************************************************************************
+**  VPD Data Register - VPD_DR
+**
+**  This register is used to transfer data between the 80331 and the VPD storage component.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:00      0000H                        VPD Data - Four bytes are always read or written through this register to/from the VPD storage component.
+***********************************************************************************
+*/
+#define     ARCMSR_VPD_DATA_REG		          0xBC	/*dword 0xBF,0xBE,0xBD,0xBC */
+/*
+***********************************************************************************
+**  Power Management Capability Identifier Register -PM_CAPID
+**
+**  The Capability Identifier Register bits adhere to the definitions in the PCI Local Bus Specification,
+**  Revision 2.3. This register in the PCI Extended Capability header identifies the type of Extended
+**  Capability contained in that header. In the case of the 80331, this is the PCI Bus Power
+**  Management extended capability with an ID of 01H as defined by the PCI Bus Power Management
+**  Interface Specification, Revision 1.1.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  07:00       01H                         Cap_Id - This field with its¡¦ 01H value identifies this item in the linked list of Extended Capability Headers as being the PCI Power Management Registers.
+***********************************************************************************
+*/
+#define     ARCMSR_POWER_MANAGEMENT_CAPABILITY_IDENTIFIER_REG		          0xC0	/*byte */
+/*
+***********************************************************************************
+**  Power Management Next Item Pointer Register - PM_NXTP
+**
+**  The Next Item Pointer Register bits adhere to the definitions in the PCI Local Bus Specification,
+**  Revision 2.3. This register describes the location of the next item in the function¡¦s capability list.
+**  For the 80331, the next capability (MSI capability list) is located at off-set D0H.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  07:00       D0H                         Next_ Item_ Pointer - This field provides an offset into the function¡¦s configuration space pointing to the
+**                                                          next item in the function¡¦s capability list which in the 80331 is the MSI extended capabilities header.
+***********************************************************************************
+*/
+#define     ARCMSR_POWER_NEXT_ITEM_PTR_REG		          0xC1	/*byte */
+/*
+***********************************************************************************
+**  Power Management Capabilities Register - PM_CAP
+**
+**  Power Management Capabilities bits adhere to the definitions in the PCI Bus Power Management
+**  Interface Specification, Revision 1.1. This register is a 16-bit read-only register which provides
+**  information on the capabilities of the ATU function related to power management.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  15:11   00000 2                         PME_Support - This function is not capable of asserting the PME# signal in any state, since PME# is not supported by the 80331.
+**  10          0 2                         D2_Support - This bit is set to 0 2 indicating that the 80331 does not support the D2 Power Management State
+**  9           1 2                         D1_Support - This bit is set to 1 2 indicating that the 80331 supports the D1 Power Management State
+**  8:6       000 2                         Aux_Current - This field is set to 000 2 indicating that the 80331 has no current requirements for the
+**                                                          3.3Vaux signal as defined in the PCI Bus Power Management Interface Specification, Revision 1.1
+**  5           0 2                         DSI - This field is set to 0 2 meaning that this function requires a device specific initialization sequence
+**                                                          following the transition to the D0 uninitialized state.
+**  4           0 2                         Reserved.
+**  3           0 2                         PME Clock - Since the 80331 does not support PME# signal generation this bit is cleared to 0 2 .
+**  2:0       010 2                         Version - Setting these bits to 010 2 means that this function complies with PCI Bus Power Management Interface Specification, Revision 1.1
+***********************************************************************************
+*/
+#define     ARCMSR_POWER_MANAGEMENT_CAPABILITY_REG		          0xC2	/*word 0xC3,0xC2 */
+/*
+***********************************************************************************
+**  Power Management Control/Status Register - PM_CSR
+**
+**  Power Management Control/Status bits adhere to the definitions in the PCI Bus Power
+**  Management Interface Specification, Revision 1.1. This 16-bit register is the control and status
+**  interface for the power management extended capability.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  15          0 2                         PME_Status - This function is not capable of asserting the PME# signal in any state, since PME## is not supported by the 80331.
+**  14:9        00H                         Reserved
+**  8           0 2                         PME_En - This bit is hardwired to read-only 0 2 since this function does not support PME# generation from any power state.
+**  7:2    000000 2                         Reserved
+**  1:0        00 2                         Power State - This 2-bit field is used both to determine the current power state of a function and to set the function into a new power state. The definition of the values is:
+**  														00 2 - D0
+**  														01 2 - D1
+**  														10 2 - D2 (Unsupported)
+**  														11 2 - D3 hot
+**  														The 80331 supports only the D0 and D3 hot states.
+**
+***********************************************************************************
+*/
+#define     ARCMSR_POWER_MANAGEMENT_CONTROL_STATUS_REG		          0xC4	/*word 0xC5,0xC4 */
+/*
+***********************************************************************************
+**  PCI-X Capability Identifier Register - PX_CAPID
+**
+**  The Capability Identifier Register bits adhere to the definitions in the PCI Local Bus Specification,
+**  Revision 2.3. This register in the PCI Extended Capability header identifies the type of Extended
+**  Capability contained in that header. In the case of the 80331, this is the PCI-X extended capability with
+**  an ID of 07H as defined by the PCI-X Addendum to the PCI Local Bus Specification, Revision 1.0a.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  07:00       07H                         Cap_Id - This field with its¡¦ 07H value identifies this item in the linked list of Extended Capability Headers as being the PCI-X capability registers.
+***********************************************************************************
+*/
+#define     ARCMSR_PCIX_CAPABILITY_IDENTIFIER_REG		          0xE0	/*byte */
+/*
+***********************************************************************************
+**  PCI-X Next Item Pointer Register - PX_NXTP
+**
+**  The Next Item Pointer Register bits adhere to the definitions in the PCI Local Bus Specification,
+**  Revision 2.3. This register describes the location of the next item in the function¡¦s capability list.
+**  By default, the PCI-X capability is the last capabilities list for the 80331, thus this register defaults
+**  to 00H.
+**  However, this register may be written to B8H prior to host configuration to include the VPD
+**  capability located at off-set B8H.
+**  Warning: Writing this register to any value other than 00H (default) or B8H is not supported and may
+**  produce unpredictable system behavior.
+**  In order to guarantee that this register is written prior to host configuration, the 80331 must be
+**  initialized at P_RST# assertion to Retry Type 0 configuration cycles (bit 2 of PCSR). Typically,
+**  the Intel XScale core would be enabled to boot immediately following P_RST# assertion in
+**  this case (bit 1 of PCSR), as well. Please see Table 125, ¡§PCI Configuration and Status Register -
+**  PCSR¡¨ on page 253 for more details on the 80331 initialization modes.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  07:00       00H                         Next_ Item_ Pointer - This field provides an offset into the function¡¦s configuration space pointing to the
+**  														next item in the function¡¦s capability list. Since the PCI-X capabilities are the last in the linked list of
+**  														extended capabilities in the 80331, the register is set to 00H.
+**  														However, this field may be written prior to host configuration with B8H to extend the list to include the
+**  														VPD extended capabilities header.
+***********************************************************************************
+*/
+#define     ARCMSR_PCIX_NEXT_ITEM_PTR_REG		          0xE1	/*byte */
+/*
+***********************************************************************************
+**  PCI-X Command Register - PX_CMD
+**
+**  This register controls various modes and features of ATU and Message Unit when operating in the
+**  PCI-X mode.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  15:7     000000000 2                    Reserved.
+**  6:4        011 2                        Maximum Outstanding Split Transactions - This register sets the maximum number of Split Transactions
+**  														the device is permitted to have outstanding at one time.
+**  														Register Maximum Outstanding
+**  														0 1
+**  														1 2
+**  														2 3
+**  														3 4
+**  														4 8
+**  														5 12
+**  														6 16
+**  														7 32
+**  3:2        00 2                         Maximum Memory Read Byte Count - This register sets the maximum byte count the device uses when
+**                                                          initiating a Sequence with one of the burst memory read commands.
+**  														Register Maximum Byte Count
+**  														0 512
+**  														1 1024
+**  														2 2048
+**  														3 4096
+**  														1 0 2
+**  														Enable Relaxed Ordering - The 80331 does not set the relaxed ordering bit in the Requester Attributes
+**  														of Transactions.
+**  0          0 2                          Data Parity Error Recovery Enable - The device driver sets this bit to enable the device to attempt to
+**                                                          recover from data parity errors. When this bit is 0 and the device is in PCI-X mode, the device asserts
+**                                                          SERR# (when enabled) whenever the Master Data Parity Error bit (Status register, bit 8) is set.
+***********************************************************************************
+*/
+#define     ARCMSR_PCIX_COMMAND_REG		          0xE2	/*word 0xE3,0xE2 */
+/*
+***********************************************************************************
+**  PCI-X Status Register - PX_SR
+**
+**  This register identifies the capabilities and current operating mode of ATU, DMAs and Message
+**  Unit when operating in the PCI-X mode.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:30       00 2                        Reserved
+**  29           0 2                        Received Split Completion Error Message - This bit is set when the device receives a Split Completion
+**  														Message with the Split Completion Error attribute bit set. Once set, this bit remains set until software
+**  														writes a 1 to this location.
+**  														0=no Split Completion error message received.
+**  														1=a Split Completion error message has been received.
+**  28:26      001 2                        Designed Maximum Cumulative Read Size (DMCRS) - The value of this register depends on the setting
+**  														of the Maximum Memory Read Byte Count field of the PCIXCMD register:
+**  														DMCRS Max ADQs Maximum Memory Read Byte Count Register Setting
+**  														1 16 512 (Default)
+**  														2 32 1024
+**  														2 32 2048
+**  														2 32 4096
+**  25:23      011 2                        Designed Maximum Outstanding Split Transactions - The 80331 can have up to four outstanding split transactions.
+**  22:21       01 2                        Designed Maximum Memory Read Byte Count - The 80331 can generate memory reads with byte counts up to 1024 bytes.
+**  20           1 2                        80331 is a complex device.
+**  19           0 2                        Unexpected Split Completion - This bit is set when an unexpected Split Completion with this device¡¦s
+**  														Requester ID is received. Once set, this bit remains set until software writes a 1 to this location.
+**  														0=no unexpected Split Completion has been received.
+**  														1=an unexpected Split Completion has been received.
+**  18           0 2                        Split Completion Discarded - This bit is set when the device discards a Split Completion because the
+**  														requester would not accept it. See Section 5.4.4 of the PCI-X Addendum to the PCI Local Bus
+**  														Specification, Revision 1.0a for details. Once set, this bit remains set until software writes a 1 to this
+**  														location.
+**  														0=no Split Completion has been discarded.
+**  														1=a Split Completion has been discarded.
+**  														NOTE: The 80331 does not set this bit since there is no Inbound address responding to Inbound Read
+**  														Requests with Split Responses (Memory or Register) that has ¡§read side effects.¡¨
+**  17           1 2                        80331 is a 133 MHz capable device.
+**  16           1 2 or P_32BITPCI#			80331 with bridge enabled (BRG_EN=1) implements the ATU with a 64-bit interface on the secondary PCI bus, therefore this bit is always set.
+**                              			80331 with no bridge and central resource disabled (BRG_EN=0, ARB_EN=0), use this bit to identify the add-in card to the system as 64-bit or 32-bit wide via a user-configurable strap (P_32BITPCI#).
+**                              			This strap, by default, identifies the add in card based on 80331 with bridge disabled as 64-bit unless the user attaches the appropriate pull-down resistor to the strap.
+**  														0=The bus is 32 bits wide.
+**  														1=The bus is 64 bits wide.
+**  15:8         FFH                        Bus Number - This register is read for diagnostic purposes only. It indicates the number of the bus
+**  														segment for the device containing this function. The function uses this number as part of its Requester
+**  														ID and Completer ID. For all devices other than the source bridge, each time the function is addressed
+**  														by a Configuration Write transaction, the function must update this register with the contents of AD[7::0]
+**  														of the attribute phase of the Configuration Write, regardless of which register in the function is
+**  														addressed by the transaction. The function is addressed by a Configuration Write transaction when all of
+**  														the following are true:
+**  														1. The transaction uses a Configuration Write command.
+**  														2. IDSEL is asserted during the address phase.
+**  														3. AD[1::0] are 00b (Type 0 configuration transaction).
+**  														4. AD[10::08] of the configuration address contain the appropriate function number.
+**  7:3          1FH                        Device Number - This register is read for diagnostic purposes only. It indicates the number of the device
+**  														containing this function, i.e., the number in the Device Number field (AD[15::11]) of the address of a
+**  														Type 0 configuration transaction that is assigned to the device containing this function by the connection
+**  														of the system hardware. The system must assign a device number other than 00h (00h is reserved for
+**  														the source bridge). The function uses this number as part of its Requester ID and Completer ID. Each
+**  														time the function is addressed by a Configuration Write transaction, the device must update this register
+**  														with the contents of AD[15::11] of the address phase of the Configuration Write, regardless of which
+**  														register in the function is addressed by the transaction. The function is addressed by a Configuration
+**  														Write transaction when all of the following are true:
+**  														1. The transaction uses a Configuration Write command.
+**  														2. IDSEL is asserted during the address phase.
+**  														3. AD[1::0] are 00b (Type 0 configuration transaction).
+**  														4. AD[10::08] of the configuration address contain the appropriate function number.
+**  2:0        000 2                        Function Number - This register is read for diagnostic purposes only. It indicates the number of this
+**  														function; i.e., the number in the Function Number field (AD[10::08]) of the address of a Type 0
+**  														configuration transaction to which this function responds. The function uses this number as part of its
+**  														Requester ID and Completer ID.
+**
+**************************************************************************
+*/
+#define     ARCMSR_PCIX_STATUS_REG		          0xE4	/*dword 0xE7,0xE6,0xE5,0xE4 */
+
+/*
+**************************************************************************
+**                 Inbound Read Transaction
+**  ========================================================================
+**	An inbound read transaction is initiated by a PCI initiator and is targeted at either 80331 local
+**	memory or a 80331 memory-mapped register space. The read transaction is propagated through
+**	the inbound transaction queue (ITQ) and read data is returned through the inbound read queue
+**	(IRQ).
+**	When operating in the conventional PCI mode, all inbound read transactions are processed as
+**	delayed read transactions. When operating in the PCI-X mode, all inbound read transactions are
+**	processed as split transactions. The ATUs PCI interface claims the read transaction and forwards
+**	the read request through to the internal bus and returns the read data to the PCI bus. Data flow for
+**	an inbound read transaction on the PCI bus is summarized in the following statements:
+**	¡E The ATU claims the PCI read transaction when the PCI address is within the inbound
+**	translation window defined by ATU Inbound Base Address Register (and Inbound Upper Base
+**	Address Register during DACs) and Inbound Limit Register.
+**	¡E When operating in the conventional PCI mode, when the ITQ is currently holding transaction
+**	information from a previous delayed read, the current transaction information is compared to
+**	the previous transaction information (based on the setting of the DRC Alias bit in
+**	Section 3.10.39, ¡§ATU Configuration Register - ATUCR¡¨ on page 252). When there is a
+**	match and the data is in the IRQ, return the data to the master on the PCI bus. When there is a
+**	match and the data is not available, a Retry is signaled with no other action taken. When there
+**	is not a match and when the ITQ has less than eight entries, capture the transaction
+**	information, signal a Retry and initiate a delayed transaction. When there is not a match and
+**	when the ITQ is full, then signal a Retry with no other action taken.
+**	¡X When an address parity error is detected, the address parity response defined in
+**	Section 3.7 is used.
+**	¡E When operating in the conventional PCI mode, once read data is driven onto the PCI bus from
+**	the IRQ, it continues until one of the following is true:
+**	¡X The initiator completes the PCI transaction. When there is data left unread in the IRQ, the
+**	data is flushed.
+**	¡X An internal bus Target Abort was detected. In this case, the QWORD associated with the
+**	Target Abort is never entered into the IRQ, and therefore is never returned.
+**	¡X Target Abort or a Disconnect with Data is returned in response to the Internal Bus Error.
+**	¡X The IRQ becomes empty. In this case, the PCI interface signals a Disconnect with data to
+**	the initiator on the last data word available.
+**	¡E When operating in the PCI-X mode, when ITQ is not full, the PCI address, attribute and
+**	command are latched into the available ITQ and a Split Response Termination is signalled to
+**	the initiator.
+**	¡E When operating in the PCI-X mode, when the transaction does not cross a 1024 byte aligned
+**	boundary, then the ATU waits until it receives the full byte count from the internal bus target
+**	before returning read data by generating the split completion transaction on the PCI-X bus.
+**	When the read requested crosses at least one 1024 byte boundary, then ATU completes the
+**	transfer by returning data in 1024 byte aligned chunks.
+**	¡E When operating in the PCI-X mode, once a split completion transaction has started, it
+**	continues until one of the following is true:
+**	¡X The requester (now the target) generates a Retry Termination, or a Disconnection at Next
+**	ADB (when the requester is a bridge)
+**	¡X The byte count is satisfied.
+**	¡X An internal bus Target Abort was detected. The ATU generates a Split Completion
+**	Message (message class=2h - completer error, and message index=81h - target abort) to
+**	inform the requester about the abnormal condition. The ITQ for this transaction is flushed.
+**	Refer to Section 3.7.1.
+**	¡X An internal bus Master Abort was detected. The ATU generates a Split Completion
+**	Message (message class=2h - completer error, and message index=80h - Master abort) to
+**	inform the requester about the abnormal condition. The ITQ for this transaction is flushed.
+**	Refer to Section 3.7.1
+**	¡E When operating in the conventional PCI mode, when the master inserts wait states on the PCI
+**	bus, the ATU PCI slave interface waits with no premature disconnects.
+**	¡E When a data parity error occurs signified by PERR# asserted from the initiator, no action is
+**	taken by the target interface. Refer to Section 3.7.2.5.
+**	¡E When operating in the conventional PCI mode, when the read on the internal bus is
+**	target-aborted, either a target-abort or a disconnect with data is signaled to the initiator. This is
+**	based on the ATU ECC Target Abort Enable bit (bit 0 of the ATUIMR for ATU). When set, a
+**	target abort is used, when clear, a disconnect is used.
+**	¡E When operating in the PCI-X mode (with the exception of the MU queue ports at offsets 40h
+**	and 44h), when the transaction on the internal bus resulted in a target abort, the ATU generates
+**	a Split Completion Message (message class=2h - completer error, and message index=81h -
+**	internal bus target abort) to inform the requester about the abnormal condition. For the MU
+**	queue ports, the ATU returns either a target abort or a single data phase disconnect depending
+**	on the ATU ECC Target Abort Enable bit (bit 0 of the ATUIMR for ATU). The ITQ for this
+**	transaction is flushed. Refer to Section 3.7.1.
+**	¡E When operating in the conventional PCI mode, when the transaction on the internal bus
+**	resulted in a master abort, the ATU returns a target abort to inform the requester about the
+**	abnormal condition. The ITQ for this transaction is flushed. Refer to Section 3.7.1
+**	¡E When operating in the PCI-X mode, when the transaction on the internal bus resulted in a
+**	master abort, the ATU generates a Split Completion Message (message class=2h - completer
+**	error, and message index=80h - internal bus master abort) to inform the requester about the
+**	abnormal condition. The ITQ for this transaction is flushed. Refer to Section 3.7.1.
+**	¡E When operating in the PCI-X mode, when the Split Completion transaction completes with
+**	either Master-Abort or Target-Abort, the requester is indicating a failure condition that
+**	prevents it from accepting the completion it requested. In this case, since the Split Request
+**	addresses a location that has no read side effects, the completer must discard the Split
+**	Completion and take no further action.
+**	The data flow for an inbound read transaction on the internal bus is summarized in the following
+**	statements:
+**	¡E The ATU internal bus master interface requests the internal bus when a PCI address appears in
+**		an ITQ and transaction ordering has been satisfied. When operating in the PCI-X mode the
+**		ATU does not use the information provided by the Relax Ordering Attribute bit. That is, ATU
+**		always uses conventional PCI ordering rules.
+**	¡E Once the internal bus is granted, the internal bus master interface drives the translated address
+**		onto the bus and wait for IB_DEVSEL#. When a Retry is signaled, the request is repeated.
+**		When a master abort occurs, the transaction is considered complete and a target abort is loaded
+**		into the associated IRQ for return to the PCI initiator (transaction is flushed once the PCI
+**		master has been delivered the target abort).
+**	¡E Once the translated address is on the bus and the transaction has been accepted, the internal
+**		bus target starts returning data with the assertion of IB_TRDY#. Read data is continuously
+**		received by the IRQ until one of the following is true:
+**	¡X The full byte count requested by the ATU read request is received. The ATU internal bus
+**	    initiator interface performs a initiator completion in this case.
+**	¡X When operating in the conventional PCI mode, a Target Abort is received on the internal
+**		bus from the internal bus target. In this case, the transaction is aborted and the PCI side is
+**		informed.
+**	¡X When operating in the PCI-X mode, a Target Abort is received on the internal bus from
+**		the internal bus target. In this case, the transaction is aborted. The ATU generates a Split
+**		Completion Message (message class=2h - completer error, and message index=81h -
+**		target abort) on the PCI bus to inform the requester about the abnormal condition. The
+**		ITQ for this transaction is flushed.
+**	¡X When operating in the conventional PCI mode, a single data phase disconnection is
+**		received from the internal bus target. When the data has not been received up to the next
+**		QWORD boundary, the ATU internal bus master interface attempts to reacquire the bus.
+**		When not, the bus returns to idle.
+**	¡X When operating in the PCI-X mode, a single data phase disconnection is received from
+**		the internal bus target. The ATU IB initiator interface attempts to reacquire the bus to
+**		obtain remaining data.
+**	¡X When operating in the conventional PCI mode, a disconnection at Next ADB is received
+**	    from the internal bus target. The bus returns to idle.
+**	¡X When operating in the PCI-X mode, a disconnection at Next ADB is received from the
+**		internal bus target. The ATU IB initiator interface attempts to reacquire the bus to obtain
+**		remaining data.
+**		To support PCI Local Bus Specification, Revision 2.0 devices, the ATU can be programmed to
+**		ignore the memory read command (Memory Read, Memory Read Line, and Memory Read
+**		Multiple) when trying to match the current inbound read transaction with data in a DRC queue
+**		which was read previously (DRC on target bus). When the Read Command Alias Bit in the
+**		ATUCR register is set, the ATU does not distinguish the read commands on transactions. For
+**		example, the ATU enqueues a DRR with a Memory Read Multiple command and performs the read
+**		on the internal bus. Some time later, a PCI master attempts a Memory Read with the same address
+**		as the previous Memory Read Multiple. When the Read Command Bit is set, the ATU would return
+**		the read data from the DRC queue and consider the Delayed Read transaction complete. When the
+**		Read Command bit in the ATUCR was clear, the ATU would not return data since the PCI read
+**		commands did not match, only the address.
+**************************************************************************
+*/
+/*
+**************************************************************************
+**                    Inbound Write Transaction
+**========================================================================
+**	  An inbound write transaction is initiated by a PCI master and is targeted at either 80331 local
+**	  memory or a 80331 memory-mapped register.
+**	Data flow for an inbound write transaction on the PCI bus is summarized as:
+**	¡E The ATU claims the PCI write transaction when the PCI address is within the inbound
+**	  translation window defined by the ATU Inbound Base Address Register (and Inbound Upper
+**	  Base Address Register during DACs) and Inbound Limit Register.
+**	¡E When the IWADQ has at least one address entry available and the IWQ has at least one buffer
+**	  available, the address is captured and the first data phase is accepted.
+**	¡E The PCI interface continues to accept write data until one of the following is true:
+**	  ¡X The initiator performs a disconnect.
+**	  ¡X The transaction crosses a buffer boundary.
+**	¡E When an address parity error is detected during the address phase of the transaction, the
+**	  address parity error mechanisms are used. Refer to Section 3.7.1 for details of the address
+**	  parity error response.
+**	¡E When operating in the PCI-X mode when an attribute parity error is detected, the attribute
+**	  parity error mechanism described in Section 3.7.1 is used.
+**	¡E When a data parity error is detected while accepting data, the slave interface sets the
+**	  appropriate bits based on PCI specifications. No other action is taken. Refer to Section 3.7.2.6
+**	  for details of the inbound write data parity error response.
+**	  Once the PCI interface places a PCI address in the IWADQ, when IWQ has received data sufficient
+**	  to cross a buffer boundary or the master disconnects on the PCI bus, the ATUs internal bus
+**	  interface becomes aware of the inbound write. When there are additional write transactions ahead
+**	  in the IWQ/IWADQ, the current transaction remains posted until ordering and priority have been
+**	  satisfied (Refer to Section 3.5.3) and the transaction is attempted on the internal bus by the ATU
+**	  internal master interface. The ATU does not insert target wait states nor do data merging on the PCI
+**	  interface, when operating in the PCI mode.
+**	  In the PCI-X mode memory writes are always executed as immediate transactions, while
+**	  configuration write transactions are processed as split transactions. The ATU generates a Split
+**	  Completion Message, (with Message class=0h - Write Completion Class and Message index =
+**	  00h - Write Completion Message) once a configuration write is successfully executed.
+**	  Also, when operating in the PCI-X mode a write sequence may contain multiple write transactions.
+**	  The ATU handles such transactions as independent transactions.
+**	  Data flow for the inbound write transaction on the internal bus is summarized as:
+**	¡E The ATU internal bus master requests the internal bus when IWADQ has at least one entry
+**	  with associated data in the IWQ.
+**	¡E When the internal bus is granted, the internal bus master interface initiates the write
+**	  transaction by driving the translated address onto the internal bus. For details on inbound
+**	  address translation.
+**	¡E When IB_DEVSEL# is not returned, a master abort condition is signaled on the internal bus.
+**	  The current transaction is flushed from the queue and SERR# may be asserted on the PCI
+**	  interface.
+**	¡E The ATU initiator interface asserts IB_REQ64# to attempt a 64-bit transfer. When
+**	  IB_ACK64# is not returned, a 32-bit transfer is used. Transfers of less than 64-bits use the
+**	  IB_C/BE[7:0]# to mask the bytes not written in the 64-bit data phase. Write data is transferred
+**	  from the IWQ to the internal bus when data is available and the internal bus interface retains
+**	  internal bus ownership.
+**	¡E The internal bus interface stops transferring data from the current transaction to the internal
+**	  bus when one of the following conditions becomes true:
+**	¡X The internal bus initiator interface loses bus ownership. The ATU internal initiator
+**	  terminates the transfer (initiator disconnection) at the next ADB (for the internal bus ADB
+**	  is defined as a naturally aligned 128-byte boundary) and attempt to reacquire the bus to
+**	  complete the delivery of remaining data using the same sequence ID but with the
+**	  modified starting address and byte count.
+**	¡X A Disconnect at Next ADB is signaled on the internal bus from the internal target. When
+**	  the transaction in the IWQ completes at that ADB, the initiator returns to idle. When the
+**	  transaction in the IWQ is not complete, the initiator attempts to reacquire the bus to
+**	  complete the delivery of remaining data using the same sequence ID but with the
+**	  modified starting address and byte count.
+**	¡X A Single Data Phase Disconnect is signaled on the internal bus from the internal target.
+**	  When the transaction in the IWQ needs only a single data phase, the master returns to idle.
+**	  When the transaction in the IWQ is not complete, the initiator attempts to reacquire the
+**	  bus to complete the delivery of remaining data using the same sequence ID but with the
+**	  modified starting address and byte count.
+**	¡X The data from the current transaction has completed (satisfaction of byte count). An
+**	  initiator termination is performed and the bus returns to idle.
+**	¡X A Master Abort is signaled on the internal bus. SERR# may be asserted on the PCI bus.
+**	  Data is flushed from the IWQ.
+*****************************************************************
+*/
+
+/*
+**************************************************************************
+**               Inbound Read Completions Data Parity Errors
+**========================================================================
+**	As an initiator, the ATU may encounter this error condition when operating in the PCI-X mode.
+**	When as the completer of a Split Read Request the ATU observes PERR# assertion during the split
+**	completion transaction, the ATU attempts to complete the transaction normally and no further
+**	action is taken.
+**************************************************************************
+*/
+
+/*
+**************************************************************************
+**               Inbound Configuration Write Completion Message Data Parity Errors
+**========================================================================
+**  As an initiator, the ATU may encounter this error condition when operating in the PCI-X mode.
+**  When as the completer of a Configuration (Split) Write Request the ATU observes PERR#
+**  assertion during the split completion transaction, the ATU attempts to complete the transaction
+**  normally and no further action is taken.
+**************************************************************************
+*/
+
+/*
+**************************************************************************
+**              Inbound Read Request Data Parity Errors
+**===================== Immediate Data Transfer ==========================
+**  As a target, the ATU may encounter this error when operating in the Conventional PCI or PCI-X modes.
+**  Inbound read data parity errors occur when read data delivered from the IRQ is detected as having
+**  bad parity by the initiator of the transaction who is receiving the data. The initiator may optionally
+**  report the error to the system by asserting PERR#. As a target device in this scenario, no action is
+**  required and no error bits are set.
+**=====================Split Response Termination=========================
+**  As a target, the ATU may encounter this error when operating in the PCI-X mode.
+**  Inbound read data parity errors occur during the Split Response Termination. The initiator may
+**  optionally report the error to the system by asserting PERR#. As a target device in this scenario, no
+**  action is required and no error bits are set.
+**************************************************************************
+*/
+
+/*
+**************************************************************************
+**              Inbound Write Request Data Parity Errors
+**========================================================================
+**	As a target, the ATU may encounter this error when operating in the Conventional or PCI-X modes.
+**	Data parity errors occurring during write operations received by the ATU may assert PERR# on
+**	the PCI Bus. When an error occurs, the ATU continues accepting data until the initiator of the write
+**	transaction completes or a queue fill condition is reached. Specifically, the following actions with
+**	the given constraints are taken by the ATU:
+**	¡E PERR# is asserted two clocks cycles (three clock cycles when operating in the PCI-X mode)
+**	following the data phase in which the data parity error is detected on the bus. This is only
+**	done when the Parity Error Response bit in the ATUCMD is set.
+**	¡E The Detected Parity Error bit in the ATUSR is set. When the ATU sets this bit, additional
+**	actions is taken:
+**	¡X When the ATU Detected Parity Error Interrupt Mask bit in the ATUIMR is clear, set the
+**	Detected Parity Error bit in the ATUISR. When set, no action.
+***************************************************************************
+*/
+
+/*
+***************************************************************************
+**                 Inbound Configuration Write Request
+**  =====================================================================
+**  As a target, the ATU may encounter this error when operating in the Conventional or PCI-X modes.
+**  ===============================================
+**              Conventional PCI Mode
+**  ===============================================
+**  To allow for correct data parity calculations for delayed write transactions, the ATU delays the
+**  assertion of STOP# (signalling a Retry) until PAR is driven by the master. A parity error during a
+**  delayed write transaction (inbound configuration write cycle) can occur in any of the following
+**  parts of the transactions:
+**  ¡E During the initial Delayed Write Request cycle on the PCI bus when the ATU latches the
+**  address/command and data for delayed delivery to the internal configuration register.
+**  ¡E During the Delayed Write Completion cycle on the PCI bus when the ATU delivers the status
+**  of the operation back to the original master.
+**  The 80331 ATU PCI interface has the following responses to a delayed write parity error for
+**  inbound transactions during Delayed Write Request cycles with the given constraints:
+**  ¡E When the Parity Error Response bit in the ATUCMD is set, the ATU asserts TRDY#
+**  (disconnects with data) and two clock cycles later asserts PERR# notifying the initiator of the
+**  parity error. The delayed write cycle is not enqueued and forwarded to the internal bus.
+**  When the Parity Error Response bit in the ATUCMD is cleared, the ATU retries the
+**  transaction by asserting STOP# and enqueues the Delayed Write Request cycle to be
+**  forwarded to the internal bus. PERR# is not asserted.
+**  ¡E The Detected Parity Error bit in the ATUSR is set. When the ATU sets this bit, additional
+**  actions is taken:
+**  ¡X When the ATU Detected Parity Error Interrupt Mask bit in the ATUIMR is clear, set the
+**  Detected Parity Error bit in the ATUISR. When set, no action.
+**  For the original write transaction to be completed, the initiator retries the transaction on the PCI
+**  bus and the ATU returns the status from the internal bus, completing the transaction.
+**  For the Delayed Write Completion transaction on the PCI bus where a data parity error occurs and
+**  therefore does not agree with the status being returned from the internal bus (i.e. status being
+**  returned is normal completion) the ATU performs the following actions with the given constraints:
+**  ¡E When the Parity Error Response Bit is set in the ATUCMD, the ATU asserts TRDY#
+**  (disconnects with data) and two clocks later asserts PERR#. The Delayed Completion cycle in
+**  the IDWQ remains since the data of retried command did not match the data within the queue.
+**  ¡E The Detected Parity Error bit in the ATUSR is set. When the ATU sets this bit, additional
+**  actions is taken:
+**  ¡X When the ATU Detected Parity Error Interrupt Mask bit in the ATUIMR is clear, set the
+**  Detected Parity Error bit in the ATUISR. When set, no action.
+**  ===================================================
+**                       PCI-X Mode
+**  ===================================================
+**  Data parity errors occurring during configuration write operations received by the ATU may cause
+**  PERR# assertion and delivery of a Split Completion Error Message on the PCI Bus. When an error
+**  occurs, the ATU accepts the write data and complete with a Split Response Termination.
+**  Specifically, the following actions with the given constraints are then taken by the ATU:
+**  ¡E When the Parity Error Response bit in the ATUCMD is set, PERR# is asserted three clocks
+**  cycles following the Split Response Termination in which the data parity error is detected on
+**  the bus. When the ATU asserts PERR#, additional actions is taken:
+**  ¡X A Split Write Data Parity Error message (with message class=2h - completer error and
+**  message index=01h - Split Write Data Parity Error) is initiated by the ATU on the PCI bus
+**  that addresses the requester of the configuration write.
+**  ¡X When the Initiated Split Completion Error Message Interrupt Mask in the ATUIMR is
+**  clear, set the Initiated Split Completion Error Message bit in the ATUISR. When set, no
+**  action.
+**  ¡X The Split Write Request is not enqueued and forwarded to the internal bus.
+**  ¡E The Detected Parity Error bit in the ATUSR is set. When the ATU sets this bit, additional
+**  actions is taken:
+**  ¡X When the ATU Detected Parity Error Interrupt Mask bit in the ATUIMR is clear, set the
+**  Detected Parity Error bit in the ATUISR. When set, no action.
+**
+***************************************************************************
+*/
+
+/*
+***************************************************************************
+**                       Split Completion Messages
+**  =======================================================================
+**  As a target, the ATU may encounter this error when operating in the PCI-X mode.
+**  Data parity errors occurring during Split Completion Messages claimed by the ATU may assert
+**  PERR# (when enabled) or SERR# (when enabled) on the PCI Bus. When an error occurs, the
+**  ATU accepts the data and complete normally. Specifically, the following actions with the given
+**  constraints are taken by the ATU:
+**  ¡E PERR# is asserted three clocks cycles following the data phase in which the data parity error
+**  is detected on the bus. This is only done when the Parity Error Response bit in the ATUCMD
+**  is set. When the ATU asserts PERR#, additional actions is taken:
+**  ¡X The Master Parity Error bit in the ATUSR is set.
+**  ¡X When the ATU PCI Master Parity Error Interrupt Mask Bit in the ATUIMR is clear, set the
+**  PCI Master Parity Error bit in the ATUISR. When set, no action.
+**  ¡X When the SERR# Enable bit in the ATUCMD is set, and the Data Parity Error Recover
+**  Enable bit in the PCIXCMD register is clear, assert SERR#; otherwise no action is taken.
+**  When the ATU asserts SERR#, additional actions is taken:
+**  Set the SERR# Asserted bit in the ATUSR.
+**  When the ATU SERR# Asserted Interrupt Mask Bit in the ATUIMR is clear, set the
+**  SERR# Asserted bit in the ATUISR. When set, no action.
+**  When the ATU SERR# Detected Interrupt Enable Bit in the ATUCR is set, set the
+**  SERR# Detected bit in the ATUISR. When clear, no action.
+**  ¡E When the SCE bit (Split Completion Error -- bit 30 of the Completer Attributes) is set during
+**  the Attribute phase, the Received Split Completion Error Message bit in the PCIXSR is set.
+**  When the ATU sets this bit, additional actions is taken:
+**  ¡X When the ATU Received Split Completion Error Message Interrupt Mask bit in the
+**  ATUIMR is clear, set the Received Split Completion Error Message bit in the ATUISR.
+**  When set, no action.
+**  ¡E The Detected Parity Error bit in the ATUSR is set. When the ATU sets this bit, additional
+**  actions is taken:
+**  ¡X When the ATU Detected Parity Error Interrupt Mask bit in the ATUIMR is clear, set the
+**  Detected Parity Error bit in the ATUISR. When set, no action.
+**  ¡E The transaction associated with the Split Completion Message is discarded.
+**  ¡E When the discarded transaction was a read, a completion error message (with message
+**  class=2h - completer error and message index=82h - PCI bus read parity error) is generated on
+**  the internal bus of the 80331.
+*****************************************************************************
+*/
+
+/*
+*****************************************************************************
+**                      Theory of MU Operation
+*****************************************************************************
+**--------------------
+**   inbound_msgaddr0:
+**   inbound_msgaddr1:
+**  outbound_msgaddr0:
+**  outbound_msgaddr1:
+**  .  The MU has four independent messaging mechanisms.
+**     There are four Message Registers that are similar to a combination of mailbox and doorbell registers.
+**     Each holds a 32-bit value and generates an interrupt when written.
+**--------------------
+**   inbound_doorbell:
+**  outbound_doorbell:
+**  .  The two Doorbell Registers support software interrupts.
+**     When a bit is set in a Doorbell Register, an interrupt is generated.
+**--------------------
+**  inbound_queueport:
+** outbound_queueport:
+**
+**
+**  .  The Circular Queues support a message passing scheme that uses 4 circular queues.
+**     The 4 circular queues are implemented in 80331 local memory.
+**     Two queues are used for inbound messages and two are used for outbound messages.
+**     Interrupts may be generated when the queue is written.
+**--------------------
+** local_buffer 0x0050 ....0x0FFF
+**  .  The Index Registers use a portion of the 80331 local memory to implement a large set of message registers.
+**     When one of the Index Registers is written, an interrupt is generated and the address of the register written is captured.
+**     Interrupt status for all interrupts is recorded in the Inbound Interrupt Status Register and the Outbound Interrupt Status Register.
+**     Each interrupt generated by the Messaging Unit can be masked.
+**--------------------
+**  .  Multi-DWORD PCI burst accesses are not supported by the Messaging Unit,
+**     with the exception of Multi-DWORD reads to the index registers.
+**     In Conventional mode: the MU terminates   Multi-DWORD PCI transactions (other than index register reads) with a disconnect at the next Qword boundary, with the exception of queue ports.
+**     In PCI-X mode       : the MU terminates a Multi-DWORD PCI read transaction with a Split Response and the data is returned through split completion transaction(s).
+**     however, when the burst request crosses into or through the range of  offsets 40h to 4Ch (e.g., this includes the queue ports) the transaction is signaled target-abort immediately on the PCI bus.
+**	   In PCI-X mode, Multi-DWORD PCI writes is signaled a Single-Data-Phase Disconnect which means that no data beyond the first Qword (Dword when the MU does not assert P_ACK64#) is written.
+**--------------------
+**  .  All registers needed to configure and control the Messaging Unit are memory-mapped registers.
+**     The MU uses the first 4 Kbytes of the inbound translation window in the Address Translation Unit (ATU).
+**     This PCI address window is used for PCI transactions that access the 80331 local memory.
+**     The  PCI address of the inbound translation window is contained in the Inbound ATU Base Address Register.
+**--------------------
+**  .  From the PCI perspective, the Messaging Unit is part of the Address Translation Unit.
+**     The Messaging Unit uses the PCI configuration registers of the ATU for control and status information.
+**     The Messaging Unit must observe all PCI control bits in the ATU Command Register and ATU Configuration Register.
+**     The Messaging Unit reports all PCI errors in the ATU Status Register.
+**--------------------
+**  .  Parts of the Messaging Unit can be accessed as a 64-bit PCI device.
+**     The register interface, message registers, doorbell registers, and index registers returns a P_ACK64# in response to a P_REQ64# on the PCI interface.
+**     Up to 1 Qword of data can be read or written per transaction (except Index Register reads).
+**     The Inbound and Outbound Queue Ports are always 32-bit addresses and the MU does not assert P_ACK64# to offsets 40H and 44H.
+**************************************************************************
+*/
+/*
+**************************************************************************
+**  Message Registers
+**  ==============================
+**  . Messages can be sent and received by the 80331 through the use of the Message Registers.
+**  . When written, the message registers may cause an interrupt to be generated to either the Intel XScale core or the host processor.
+**  . Inbound messages are sent by the host processor and received by the 80331.
+**    Outbound messages are sent by the 80331 and received by the host processor.
+**  . The interrupt status for outbound messages is recorded in the Outbound Interrupt Status Register.
+**    Interrupt status for inbound messages is recorded in the Inbound Interrupt Status Register.
+**
+**  Inbound Messages:
+**  -----------------
+**  . When an inbound message register is written by an external PCI agent, an interrupt may be generated to the Intel XScale core.
+**  . The interrupt may be masked by the mask bits in the Inbound Interrupt Mask Register.
+**  . The Intel XScale core interrupt is recorded in the Inbound Interrupt Status Register.
+**    The interrupt causes the Inbound Message Interrupt bit to be set in the Inbound Interrupt Status Register.
+**    This is a Read/Clear bit that is set by the MU hardware and cleared by software.
+**    The interrupt is cleared when the Intel XScale core writes a value of 1 to the Inbound Message Interrupt bit in the Inbound Interrupt Status Register.
+**  ------------------------------------------------------------------------
+**  Inbound Message Register - IMRx
+**
+**  . There are two Inbound Message Registers: IMR0 and IMR1.
+**  . When the IMR register is written, an interrupt to the Intel XScale core may be generated.
+**    The interrupt is recorded in the Inbound Interrupt Status Register and may be masked by the Inbound Message Interrupt Mask bit in the Inbound Interrupt Mask Register.
+**  -----------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:00    0000 0000H                     Inbound Message - This is a 32-bit message written by an external PCI agent.
+**                                                            When written, an interrupt to the Intel XScale core may be generated.
+**************************************************************************
+*/
+#define     ARCMSR_MU_INBOUND_MESSAGE_REG0		          0x10	/*dword 0x13,0x12,0x11,0x10 */
+#define     ARCMSR_MU_INBOUND_MESSAGE_REG1		          0x14	/*dword 0x17,0x16,0x15,0x14 */
+/*
+**************************************************************************
+**  Outbound Message Register - OMRx
+**  --------------------------------
+**  There are two Outbound Message Registers: OMR0 and OMR1. When the OMR register is
+**  written, a PCI interrupt may be generated. The interrupt is recorded in the Outbound Interrupt
+**  Status Register and may be masked by the Outbound Message Interrupt Mask bit in the Outbound
+**  Interrupt Mask Register.
+**
+**  Bit       Default                       Description
+**  31:00    00000000H                      Outbound Message - This is 32-bit message written by the Intel  XScale  core. When written, an
+**                                                             interrupt may be generated on the PCI Interrupt pin determined by the ATU Interrupt Pin Register.
+**************************************************************************
+*/
+#define     ARCMSR_MU_OUTBOUND_MESSAGE_REG0		          0x18	/*dword 0x1B,0x1A,0x19,0x18 */
+#define     ARCMSR_MU_OUTBOUND_MESSAGE_REG1		          0x1C	/*dword 0x1F,0x1E,0x1D,0x1C */
+/*
+**************************************************************************
+**        Doorbell Registers
+**  ==============================
+**  There are two Doorbell Registers:
+**                                  Inbound Doorbell Register
+**                                  Outbound Doorbell Register
+**  The Inbound Doorbell Register allows external PCI agents to generate interrupts to the Intel R XScale core.
+**  The Outbound Doorbell Register allows the Intel R XScale core to generate a PCI interrupt.
+**  Both Doorbell Registers may generate interrupts whenever a bit in the register is set.
+**
+**  Inbound Doorbells:
+**  ------------------
+**  . When the Inbound Doorbell Register is written by an external PCI agent, an interrupt may be generated to the Intel R XScale  core.
+**    An interrupt is generated when any of the bits in the doorbell register is written to a value of 1.
+**    Writing a value of 0 to any bit does not change the value of that bit and does not cause an interrupt to be generated.
+**  . Once a bit is set in the Inbound Doorbell Register, it cannot be cleared by any external PCI agent.
+**    The interrupt is recorded in the Inbound Interrupt Status Register.
+**  . The interrupt may be masked by the Inbound Doorbell Interrupt mask bit in the Inbound Interrupt Mask Register.
+**    When the mask bit is set for a particular bit, no interrupt is generated for that bit.
+**    The Inbound Interrupt Mask Register affects only the generation of the normal messaging unit interrupt and not the values written to the Inbound Doorbell Register.
+**    One bit in the Inbound Doorbell Register is reserved for an Error Doorbell interrupt.
+**  . The interrupt is cleared when the Intel R XScale core writes a value of 1 to the bits in the Inbound Doorbell Register that are set.
+**    Writing a value of 0 to any bit does not change the value of that bit and does not clear the interrupt.
+**  ------------------------------------------------------------------------
+**  Inbound Doorbell Register - IDR
+**
+**  . The Inbound Doorbell Register (IDR) is used to generate interrupts to the Intel XScale core.
+**  . Bit 31 is reserved for generating an Error Doorbell interrupt.
+**    When bit 31 is set, an Error interrupt may be generated to the Intel XScale core.
+**    All other bits, when set, cause the Normal Messaging Unit interrupt line of the Intel XScale core to be asserted,
+**    when the interrupt is not masked by the Inbound Doorbell Interrupt Mask bit in the Inbound Interrupt Mask Register.
+**    The bits in the IDR register can only be set by an external PCI agent and can only be cleared by the Intel XScale  core.
+**  ------------------------------------------------------------------------
+**  Bit       Default                       Description
+**  31          0 2                         Error Interrupt - Generate an Error Interrupt to the Intel XScale core.
+**  30:00    00000000H                      Normal Interrupt - When any bit is set, generate a Normal interrupt to the Intel XScale core.
+**                                                             When all bits are clear, do not generate a Normal Interrupt.
+**************************************************************************
+*/
+#define     ARCMSR_MU_INBOUND_DOORBELL_REG		          0x20	/*dword 0x23,0x22,0x21,0x20 */
+/*
+**************************************************************************
+**  Inbound Interrupt Status Register - IISR
+**
+**  . The Inbound Interrupt Status Register (IISR) contains hardware interrupt status.
+**    It records the status of Intel XScale core interrupts generated by the Message Registers, Doorbell Registers, and the Circular Queues.
+**    All interrupts are routed to the Normal Messaging Unit interrupt input of the Intel XScale core,
+**    except for the Error Doorbell Interrupt and the Outbound Free Queue Full interrupt;
+**    these two are routed to the Messaging Unit Error interrupt input.
+**    The generation of interrupts recorded in the Inbound Interrupt Status Register may be masked by setting the corresponding bit in the Inbound Interrupt Mask Register.
+**    Some of the bits in this register are Read Only.
+**    For those bits, the interrupt must be cleared through another register.
+**
+**  Bit       Default                       Description
+**  31:07    0000000H 0 2                   Reserved
+**  06          0 2                         Index Register Interrupt - This bit is set by the MU hardware when an Index Register has been written after a PCI transaction.
+**  05          0 2                         Outbound Free Queue Full Interrupt - This bit is set when the Outbound Free Head Pointer becomes equal to the Tail Pointer and the queue is full.
+**                                                                               An Error interrupt is generated for this condition.
+**  04          0 2                         Inbound Post Queue Interrupt - This bit is set by the MU hardware when the Inbound Post Queue has been written.
+**                                                                         Once cleared, an interrupt does NOT be generated when the head and tail pointers remain unequal (i.e. queue status is Not Empty).
+**                                                                         Therefore, when software leaves any unprocessed messages in the post queue when the interrupt is cleared,
+**                                                                         software must retain the information that the Inbound Post queue status is not empty.
+**                                                                         NOTE:
+**                                                                         This interrupt is provided with dedicated support in the 80331 Interrupt Controller.
+**  03          0 2                         Error Doorbell Interrupt - This bit is set when the Error Interrupt of the Inbound Doorbell Register is set.
+**                                                                     To clear this bit (and the interrupt), the Error Interrupt bit of the Inbound Doorbell Register must be clear.
+**  02          0 2                         Inbound Doorbell Interrupt - This bit is set when at least one Normal Interrupt bit in the Inbound Doorbell Register is set.
+**                                                                       To clear this bit (and the interrupt), the Normal Interrupt bits in the Inbound Doorbell Register must all be clear.
+**  01          0 2                         Inbound Message 1 Interrupt - This bit is set by the MU hardware when the Inbound Message 1 Register has been written.
+**  00          0 2                         Inbound Message 0 Interrupt - This bit is set by the MU hardware when the Inbound Message 0 Register has been written.
+**************************************************************************
+*/
+#define     ARCMSR_MU_INBOUND_INTERRUPT_STATUS_REG	      0x24	/*dword 0x27,0x26,0x25,0x24 */
+#define     ARCMSR_MU_INBOUND_INDEX_INT                      0x40
+#define     ARCMSR_MU_INBOUND_QUEUEFULL_INT                  0x20
+#define     ARCMSR_MU_INBOUND_POSTQUEUE_INT                  0x10
+#define     ARCMSR_MU_INBOUND_ERROR_DOORBELL_INT             0x08
+#define     ARCMSR_MU_INBOUND_DOORBELL_INT                   0x04
+#define     ARCMSR_MU_INBOUND_MESSAGE1_INT                   0x02
+#define     ARCMSR_MU_INBOUND_MESSAGE0_INT                   0x01
+/*
+**************************************************************************
+**  Inbound Interrupt Mask Register - IIMR
+**
+**  . The Inbound Interrupt Mask Register (IIMR) provides the ability to mask Intel XScale core interrupts generated by the Messaging Unit.
+**    Each bit in the Mask register corresponds to an interrupt bit in the Inbound Interrupt Status Register.
+**    Setting or clearing bits in this register does not affect the Inbound Interrupt Status Register.
+**    They only affect the generation of the Intel XScale core interrupt.
+**  ------------------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:07     000000H 0 2                   Reserved
+**  06        0 2                           Index Register Interrupt Mask - When set, this bit masks the interrupt generated by the MU hardware when an Index Register has been written after a PCI transaction.
+**  05        0 2                           Outbound Free Queue Full Interrupt Mask - When set, this bit masks the Error interrupt generated when the Outbound Free Head Pointer becomes equal to the Tail Pointer and the queue is full.
+**  04        0 2                           Inbound Post Queue Interrupt Mask - When set, this bit masks the interrupt generated by the MU hardware when the Inbound Post Queue has been written.
+**  03        0 2                           Error Doorbell Interrupt Mask - When set, this bit masks the Error Interrupt when the Error Interrupt bit of the Inbound Doorbell Register is set.
+**  02        0 2                           Inbound Doorbell Interrupt Mask - When set, this bit masks the interrupt generated when at least one Normal Interrupt bit in the Inbound Doorbell Register is set.
+**  01        0 2                           Inbound Message 1 Interrupt Mask - When set, this bit masks the Inbound Message 1 Interrupt generated by a write to the Inbound Message 1 Register.
+**  00        0 2                           Inbound Message 0 Interrupt Mask - When set, this bit masks the Inbound Message 0 Interrupt generated by a write to the Inbound Message 0 Register.
+**************************************************************************
+*/
+#define     ARCMSR_MU_INBOUND_INTERRUPT_MASK_REG	      0x28	/*dword 0x2B,0x2A,0x29,0x28 */
+#define     ARCMSR_MU_INBOUND_INDEX_INTMASKENABLE               0x40
+#define     ARCMSR_MU_INBOUND_QUEUEFULL_INTMASKENABLE           0x20
+#define     ARCMSR_MU_INBOUND_POSTQUEUE_INTMASKENABLE           0x10
+#define     ARCMSR_MU_INBOUND_DOORBELL_ERROR_INTMASKENABLE      0x08
+#define     ARCMSR_MU_INBOUND_DOORBELL_INTMASKENABLE            0x04
+#define     ARCMSR_MU_INBOUND_MESSAGE1_INTMASKENABLE            0x02
+#define     ARCMSR_MU_INBOUND_MESSAGE0_INTMASKENABLE            0x01
+/*
+**************************************************************************
+**  Outbound Doorbell Register - ODR
+**
+**  The Outbound Doorbell Register (ODR) allows software interrupt generation. It allows the Intel
+**  XScale  core to generate PCI interrupts to the host processor by writing to this register. The
+**  generation of PCI interrupts through the Outbound Doorbell Register may be masked by setting the
+**  Outbound Doorbell Interrupt Mask bit in the Outbound Interrupt Mask Register.
+**  The Software Interrupt bits in this register can only be set by the Intel  XScale  core and can only
+**  be cleared by an external PCI agent.
+**  ----------------------------------------------------------------------
+**  Bit       Default                       Description
+**  31          0 2                          Reserved
+**  30          0 2                          Reserved.
+**  29          0 2                          Reserved
+**  28       0000 0000H                      PCI Interrupt - When set, this bit causes the P_INTC# interrupt output (P_INTA# with BRG_EN and ARB_EN straps low)
+**                                                           signal to be asserted or a Message-signaled Interrupt is generated (when enabled).
+**                                                           When this bit is cleared, the P_INTC# interrupt output (P_INTA# with BRG_EN and ARB_EN straps low)
+**                                                           signal is deasserted.
+**  27:00     000 0000H                      Software Interrupts - When any bit is set the P_INTC# interrupt output (P_INTA# with BRG_EN and ARB_EN straps low)
+**                                                           signal is asserted or a Message-signaled Interrupt is generated (when enabled).
+**                                                           When all bits are cleared, the P_INTC# interrupt output (P_INTA# with BRG_EN and ARB_EN straps low)
+**                                                           signal is deasserted.
+**************************************************************************
+*/
+#define     ARCMSR_MU_OUTBOUND_DOORBELL_REG		          0x2C	//dword 0x2F,0x2E,0x2D,0x2C//
+/*
+**************************************************************************
+**  Outbound Interrupt Status Register - OISR
+**
+**  The Outbound Interrupt Status Register (OISR) contains hardware interrupt status. It records the
+**  status of PCI interrupts generated by the Message Registers, Doorbell Registers, and the Circular
+**  Queues. The generation of PCI interrupts recorded in the Outbound Interrupt Status Register may
+**  be masked by setting the corresponding bit in the Outbound Interrupt Mask Register. Some of the
+**  bits in this register are Read Only. For those bits, the interrupt must be cleared through another
+**  register.
+**  ----------------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:05     000000H 000 2                 Reserved
+**  04        0 2                           PCI Interrupt - This bit is set when the PCI Interrupt bit (bit 28) is set in the Outbound Doorbell Register.
+**                                                          To clear this bit (and the interrupt), the PCI Interrupt bit must be cleared.
+**  03        0 2                           Outbound Post Queue Interrupt - This bit is set when data in the prefetch buffer is valid. This bit is
+**                                                          cleared when any prefetch data has been read from the Outbound Queue Port.
+**  02        0 2                           Outbound Doorbell Interrupt - This bit is set when at least one Software Interrupt bit in the Outbound
+**                                                          Doorbell Register is set. To clear this bit (and the interrupt), the Software Interrupt bits in the Outbound
+**                                                          Doorbell Register must all be clear.
+**  01        0 2                           Outbound Message 1 Interrupt - This bit is set by the MU when the Outbound Message 1 Register is
+**                                                          written. Clearing this bit clears the interrupt.
+**  00        0 2                           Outbound Message 0 Interrupt - This bit is set by the MU when the Outbound Message 0 Register is
+**                                                          written. Clearing this bit clears the interrupt.
+**************************************************************************
+*/
+#define     ARCMSR_MU_OUTBOUND_INTERRUPT_STATUS_REG	      0x30	//dword 0x33,0x32,0x31,0x30//
+#define     ARCMSR_MU_OUTBOUND_PCI_INT       	              0x10
+#define     ARCMSR_MU_OUTBOUND_POSTQUEUE_INT    	          0x08
+#define     ARCMSR_MU_OUTBOUND_DOORBELL_INT 		          0x04
+#define     ARCMSR_MU_OUTBOUND_MESSAGE1_INT 		          0x02
+#define     ARCMSR_MU_OUTBOUND_MESSAGE0_INT 		          0x01
+#define     ARCMSR_MU_OUTBOUND_HANDLE_INT                     (ARCMSR_MU_OUTBOUND_MESSAGE0_INT|ARCMSR_MU_OUTBOUND_MESSAGE1_INT|ARCMSR_MU_OUTBOUND_DOORBELL_INT|ARCMSR_MU_OUTBOUND_POSTQUEUE_INT|ARCMSR_MU_OUTBOUND_PCI_INT)
+/*
+**************************************************************************
+**  Outbound Interrupt Mask Register - OIMR
+**  The Outbound Interrupt Mask Register (OIMR) provides the ability to mask outbound PCI
+**  interrupts generated by the Messaging Unit. Each bit in the mask register corresponds to a
+**  hardware interrupt bit in the Outbound Interrupt Status Register. When the bit is set, the PCI
+**  interrupt is not generated. When the bit is clear, the interrupt is allowed to be generated.
+**  Setting or clearing bits in this register does not affect the Outbound Interrupt Status Register. They
+**  only affect the generation of the PCI interrupt.
+**  ----------------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:05     000000H                       Reserved
+**  04          0 2                         PCI Interrupt Mask - When set, this bit masks the interrupt generation when the PCI Interrupt bit (bit 28)
+**                                                               in the Outbound Doorbell Register is set.
+**  03          0 2                         Outbound Post Queue Interrupt Mask - When set, this bit masks the interrupt generated when data in
+**                                                               the prefetch buffer is valid.
+**  02          0 2                         Outbound Doorbell Interrupt Mask - When set, this bit masks the interrupt generated by the Outbound
+**                                                               Doorbell Register.
+**  01          0 2                         Outbound Message 1 Interrupt Mask - When set, this bit masks the Outbound Message 1 Interrupt
+**                                                               generated by a write to the Outbound Message 1 Register.
+**  00          0 2                         Outbound Message 0 Interrupt Mask- When set, this bit masks the Outbound Message 0 Interrupt
+**                                                               generated by a write to the Outbound Message 0 Register.
+**************************************************************************
+*/
+#define     ARCMSR_MU_OUTBOUND_INTERRUPT_MASK_REG		  0x34	//dword 0x37,0x36,0x35,0x34//
+#define     ARCMSR_MU_OUTBOUND_PCI_INTMASKENABLE   	          0x10
+#define     ARCMSR_MU_OUTBOUND_POSTQUEUE_INTMASKENABLE	      0x08
+#define     ARCMSR_MU_OUTBOUND_DOORBELL_INTMASKENABLE		  0x04
+#define     ARCMSR_MU_OUTBOUND_MESSAGE1_INTMASKENABLE		  0x02
+#define     ARCMSR_MU_OUTBOUND_MESSAGE0_INTMASKENABLE		  0x01
+#define     ARCMSR_MU_OUTBOUND_ALL_INTMASKENABLE		      0x1F
+/*
+**************************************************************************
+**
+**************************************************************************
+*/
+#define     ARCMSR_MU_INBOUND_QUEUE_PORT_REG        	  0x40	//dword 0x43,0x42,0x41,0x40//
+#define     ARCMSR_MU_OUTBOUND_QUEUE_PORT_REG  	          0x44	//dword 0x47,0x46,0x45,0x44//
+/*
+**************************************************************************
+**                          Circular Queues
+**  ======================================================================
+**  The MU implements four circular queues. There are 2 inbound queues and 2 outbound queues. In
+**  this case, inbound and outbound refer to the direction of the flow of posted messages.
+**  Inbound messages are either:
+**  						¡E posted messages by other processors for the Intel XScale core to process or
+**  						¡E free (or empty) messages that can be reused by other processors.
+**  Outbound messages are either:
+** 							¡E posted messages by the Intel XScale core for other processors to process or
+** 							¡E free (or empty) messages that can be reused by the Intel XScale core.
+**  Therefore, free inbound messages flow away from the 80331 and free outbound messages flow toward the 80331.
+**  The four Circular Queues are used to pass messages in the following manner.
+**  	. The two inbound queues are used to handle inbound messages
+**  	  and the two outbound queues are used to handle  outbound messages.
+**  	. One of the inbound queues is designated the Free queue and it contains inbound free messages.
+**  	  The other inbound queue is designated the Post queue and it contains inbound posted messages.
+**  	  Similarly, one of the outbound queues is designated the Free queue and the other outbound queue is designated the Post queue.
+**
+**  =============================================================================================================
+**  Circular Queue Summary
+**   _____________________________________________________________________________________________________________
+**  |    Queue Name        |                     Purpose                                |  Action on PCI Interface|
+**  |______________________|____________________________________________________________|_________________________|
+**  |Inbound Post  Queue   |    Queue for inbound messages from other processors        |          Written        |
+**  |                      |     waiting to be processed by the 80331                   |                         |
+**  |Inbound Free  Queue   |    Queue for empty inbound messages from the 80331         |          Read           |
+**  |                      |    available for use by other processors                   |                         |
+**  |Outbound Post Queue   |    Queue for outbound messages from the 80331              |          Read           |
+**  |                      |    that are being posted to the other processors           |                         |
+**  |Outbound Free Queue   |    Queue for empty outbound messages from other processors |          Written        |
+**  |                      |    available for use by the 80331                          |                         |
+**  |______________________|____________________________________________________________|_________________________|
+**
+**  . The two inbound queues allow the host processor to post inbound messages for the 80331 in one
+**    queue and to receive free messages returning from the 80331.
+**    The host processor posts inbound messages,
+**    the Intel XScale core receives the posted message and when it is finished with the message,
+**    places it back on the inbound free queue for reuse by the host processor.
+**
+**  The circular queues are accessed by external PCI agents through two port locations in the PCI
+**  address space:
+**              Inbound Queue Port
+**          and Outbound Queue Port.
+**  The Inbound Queue Port is used by external PCI agents to read the Inbound Free Queue and write the Inbound Post Queue.
+**  The Outbound Queue Port is used by external PCI agents to read the Outbound Post Queue and write the Outbound Free Queue.
+**  Note that a PCI transaction to the inbound or outbound queue ports with null byte enables (P_C/BE[3:0]#=1111 2 )
+**  does not cause the MU hardware to increment the queue pointers.
+**  This is treated as when the PCI transaction did not occur.
+**  The Inbound and Outbound Queue Ports never respond with P_ACK64# on the PCI interface.
+**  ======================================================================================
+**  Overview of Circular Queue Operation
+**  ======================================================================================
+**  . The data storage for the circular queues must be provided by the 80331 local memory.
+**  . The base address of the circular queues is contained in the Queue Base Address Register.
+**    Each entry in the queue is a 32-bit data value.
+**  . Each read from or write to the queue may access only one queue entry.
+**  . Multi-DWORD accesses to the circular queues are not allowed.
+**    Sub-DWORD accesses are promoted to DWORD accesses.
+**  . Each circular queue has a head pointer and a tail pointer.
+**    The pointers are offsets from the Queue Base Address.
+**  . Writes to a queue occur at the head of the queue and reads occur from the tail.
+**    The head and tail pointers are incremented by either the Intel XScale core or the Messaging Unit hardware.
+**    Which unit maintains the pointer is determined by the writer of the queue.
+**    More details about the pointers are given in the queue descriptions below.
+**    The pointers are incremented after the queue access.
+**    Both pointers wrap around to the first address of the circular queue when they reach the circular queue size.
+**
+**  Messaging Unit...
+**
+**  The Messaging Unit generates an interrupt to the Intel XScale core or generate a PCI interrupt under certain conditions.
+**  . In general, when a Post queue is written, an interrupt is generated to notify the receiver that a message was posted.
+**    The size of each circular queue can range from 4K entries (16 Kbytes) to 64K entries (256 Kbytes).
+**  . All four queues must be the same size and may be contiguous.
+**    Therefore, the total amount of local memory needed by the circular queues ranges from 64 Kbytes to 1 Mbytes.
+**    The Queue size is determined by the Queue Size field in the MU Configuration Register.
+**  . There is one base address for all four queues.
+**    It is stored in the Queue Base Address Register (QBAR).
+**    The starting addresses of each queue is based on the Queue Base Address and the Queue Size field.
+**    here shows an example of how the circular queues should be set up based on the
+**    Intelligent I/O (I 2 O) Architecture Specification.
+**    Other ordering of the circular queues is possible.
+**
+**  				Queue                           Starting Address
+**  				Inbound Free Queue              QBAR
+**  				Inbound Post Queue              QBAR + Queue Size
+**  				Outbound Post Queue             QBAR + 2 * Queue Size
+**  				Outbound Free Queue             QBAR + 3 * Queue Size
+**  ===================================================================================
+**  Inbound Post Queue
+**  ------------------
+**  The Inbound Post Queue holds posted messages placed there by other processors for the Intel XScale core to process.
+**  This queue is read from the queue tail by the Intel XScale core. It is written to the queue head by external PCI agents.
+**  The tail pointer is maintained by the Intel XScale core. The head pointer is maintained by the MU hardware.
+**  For a PCI write transaction that accesses the Inbound Queue Port, the MU writes the data to the local memory location address in the Inbound Post Head Pointer Register.
+**  When the data written to the Inbound Queue Port is written to local memory, the MU hardware increments the Inbound Post Head Pointer Register.
+**  An Intel XScale core interrupt may be generated when the Inbound Post Queue is written.
+**  The Inbound Post Queue Interrupt bit in the Inbound Interrupt Status Register indicates the interrupt status.
+**  The interrupt is cleared when the Inbound Post Queue Interrupt bit is cleared.
+**  The interrupt can be masked by the Inbound Interrupt Mask Register.
+**  Software must be aware of the state of the Inbound Post Queue Interrupt Mask bit to guarantee that the full condition is recognized by the core processor.
+**  In addition, to guarantee that the queue does not get overwritten, software must process messages from the tail of the queue before incrementing the tail pointer and clearing this interrupt.
+**  Once cleared, an interrupt is NOT generated when the head and tail pointers remain unequal (i.e. queue status is Not Empty).
+**  Only a new message posting the in the inbound queue generates a new interrupt.
+**  Therefore, when software leaves any unprocessed messages in the post queue when the interrupt is cleared, software must retain the information that the Inbound Post queue status.
+**  From the time that the PCI write transaction is received until the data is written in local memory and the Inbound Post Head Pointer Register is incremented, any PCI transaction that attempts to access the Inbound Post Queue Port is signalled a Retry.
+**  The Intel XScale core may read messages from the Inbound Post Queue by reading the data from the local memory location pointed to by the Inbound Post Tail Pointer Register.
+**  The Intel XScale core must then increment the Inbound Post Tail Pointer Register.
+**  When the Inbound Post Queue is full (head and tail pointers are equal and the head pointer was last updated by hardware), the hardware retries any PCI writes until a slot in the queue becomes available.
+**  A slot in the post queue becomes available by the Intel XScale core incrementing the tail pointer.
+**  ===================================================================================
+**  Inbound Free Queue
+**  ------------------
+**  The Inbound Free Queue holds free inbound messages placed there by the Intel XScale core for other processors to use.
+**  This queue is read from the queue tail by external PCI agents.
+**  It is written to the queue head by the Intel XScale core.
+**  The tail pointer is maintained by the MU hardware.
+**  The head pointer is maintained by the Intel XScale core.
+**  For a PCI read transaction that accesses the Inbound Queue Port,
+**  the MU attempts to read the data at the local memory address in the Inbound Free Tail Pointer.
+**  When the queue is not empty (head and tail pointers are not equal) or full (head and tail pointers are equal but the head pointer was last written by software), the data is returned.
+**  When the queue is empty (head and tail pointers are equal and the head pointer was last updated by hardware), the value of -1 (FFFF.FFFFH) is  returned.
+**  When the queue was not empty and the MU succeeded in returning the data at the tail,
+**  the MU hardware must increment the value in the Inbound Free Tail Pointer Register.
+**  To reduce latency for the PCI read access, the MU implements a prefetch mechanism to anticipate accesses to the Inbound Free Queue.
+**  The MU hardware prefetches the data at the tail of the Inbound Free Queue and load it into an internal prefetch register.
+**  When the PCI read access occurs, the data is read directly from the prefetch register.
+**  The prefetch mechanism loads a value of -1 (FFFF.FFFFH) into the prefetch register
+**  when the head and tail pointers are equal and the queue is empty.
+**  In order to update the prefetch register when messages are added to the queue and it becomes non-empty,
+**  the prefetch mechanism automatically starts a prefetch when the prefetch register contains FFFF.FFFFH and the Inbound Free Head Pointer Register is written.
+**  The Intel XScale core needs to update the Inbound Free Head Pointer Register when it adds messages to the queue.
+**  A prefetch must appear atomic from the perspective of the external PCI agent.
+**  When a prefetch is started, any PCI transaction that attempts to access the Inbound Free Queue is signalled a Retry until the prefetch is completed.
+**  The Intel XScale core may place messages in the Inbound Free Queue by writing the data to the
+**  local memory location pointed to by the Inbound Free Head Pointer Register.
+**  The processor must then increment the Inbound Free Head Pointer Register.
+**  ==================================================================================
+**  Outbound Post Queue
+**  -------------------
+**  The Outbound Post Queue holds outbound posted messages placed there by the Intel XScale
+**  core for other processors to process. This queue is read from the queue tail by external PCI agents.
+**  It is written to the queue head by the Intel XScale  core. The tail pointer is maintained by the
+**  MU hardware. The head pointer is maintained by the Intel XScale  core.
+**  For a PCI read transaction that accesses the Outbound Queue Port, the MU attempts to read the
+**  data at the local memory address in the Outbound Post Tail Pointer Register. When the queue is not
+**  empty (head and tail pointers are not equal) or full (head and tail pointers are equal but the head
+**  pointer was last written by software), the data is returned. When the queue is empty (head and tail
+**  pointers are equal and the head pointer was last updated by hardware), the value of -1
+**  (FFFF.FFFFH) is returned. When the queue was not empty and the MU succeeded in returning the
+**  data at the tail, the MU hardware must increment the value in the Outbound Post Tail Pointer
+**  Register.
+**  To reduce latency for the PCI read access, the MU implements a prefetch mechanism to anticipate
+**  accesses to the Outbound Post Queue. The MU hardware prefetches the data at the tail of the
+**  Outbound Post Queue and load it into an internal prefetch register. When the PCI read access
+**  occurs, the data is read directly from the prefetch register.
+**  The prefetch mechanism loads a value of -1 (FFFF.FFFFH) into the prefetch register when the head
+**  and tail pointers are equal and the queue is empty. In order to update the prefetch register when
+**  messages are added to the queue and it becomes non-empty, the prefetch mechanism automatically
+**  starts a prefetch when the prefetch register contains FFFF.FFFFH and the Outbound Post Head
+**  Pointer Register is written. The Intel XScale  core needs to update the Outbound Post Head
+**  Pointer Register when it adds messages to the queue.
+**  A prefetch must appear atomic from the perspective of the external PCI agent. When a prefetch is
+**  started, any PCI transaction that attempts to access the Outbound Post Queue is signalled a Retry
+**  until the prefetch is completed.
+**  A PCI interrupt may be generated when data in the prefetch buffer is valid. When the prefetch
+**  queue is clear, no interrupt is generated. The Outbound Post Queue Interrupt bit in the Outbound
+**  Interrupt Status Register shall indicate the status of the prefetch buffer data and therefore the
+**  interrupt status. The interrupt is cleared when any prefetched data has been read from the Outbound
+**  Queue Port. The interrupt can be masked by the Outbound Interrupt Mask Register.
+**  The Intel XScale  core may place messages in the Outbound Post Queue by writing the data to
+**  the local memory address in the Outbound Post Head Pointer Register. The processor must then
+**  increment the Outbound Post Head Pointer Register.
+**  ==================================================
+**  Outbound Free Queue
+**  -----------------------
+**  The Outbound Free Queue holds free messages placed there by other processors for the Intel
+**  XScale  core to use. This queue is read from the queue tail by the Intel XScale  core. It is
+**  written to the queue head by external PCI agents. The tail pointer is maintained by the Intel
+**  XScale  core. The head pointer is maintained by the MU hardware.
+**  For a PCI write transaction that accesses the Outbound Queue Port, the MU writes the data to the
+**  local memory address in the Outbound Free Head Pointer Register. When the data written to the
+**  Outbound Queue Port is written to local memory, the MU hardware increments the Outbound Free
+**  Head Pointer Register.
+**  When the head pointer and the tail pointer become equal and the queue is full, the MU may signal
+**  an interrupt to the Intel XScale  core to register the queue full condition. This interrupt is
+**  recorded in the Inbound Interrupt Status Register. The interrupt is cleared when the Outbound Free
+**  Queue Full Interrupt bit is cleared and not by writing to the head or tail pointers. The interrupt can
+**  be masked by the Inbound Interrupt Mask Register. Software must be aware of the state of the
+**  Outbound Free Queue Interrupt Mask bit to guarantee that the full condition is recognized by the
+**  core processor.
+**  From the time that a PCI write transaction is received until the data is written in local memory and
+**  the Outbound Free Head Pointer Register is incremented, any PCI transaction that attempts to
+**  access the Outbound Free Queue Port is signalled a retry.
+**  The Intel XScale  core may read messages from the Outbound Free Queue by reading the data
+**  from the local memory address in the Outbound Free Tail Pointer Register. The processor must
+**  then increment the Outbound Free Tail Pointer Register. When the Outbound Free Queue is full,
+**  the hardware must retry any PCI writes until a slot in the queue becomes available.
+**
+**  ==================================================================================
+**  Circular Queue Summary
+**  ----------------------
+**  ________________________________________________________________________________________________________________________________________________
+** | Queue Name  |  PCI Port     |Generate PCI Interrupt |Generate Intel Xscale Core Interrupt|Head Pointer maintained by|Tail Pointer maintained by|
+** |_____________|_______________|_______________________|____________________________________|__________________________|__________________________|
+** |Inbound Post | Inbound Queue |                       |                                    |                          |                          |
+** |    Queue    |     Port      |          NO           |      Yes, when queue is written    |         MU hardware      |     Intel XScale         |
+** |_____________|_______________|_______________________|____________________________________|__________________________|__________________________|
+** |Inbound Free | Inbound Queue |                       |                                    |                          |                          |
+** |    Queue    |     Port      |          NO           |      NO                            |        Intel XScale      |      MU hardware         |
+** |_____________|_______________|_______________________|____________________________________|__________________________|__________________________|
+** ==================================================================================
+**  Circular Queue Status Summary
+**  ----------------------
+**  ____________________________________________________________________________________________________
+** |     Queue Name      |  Queue Status  | Head & Tail Pointer |         Last Pointer Update           |
+** |_____________________|________________|_____________________|_______________________________________|
+** | Inbound Post Queue  |      Empty     |       Equal         | Tail pointer last updated by software |
+** |_____________________|________________|_____________________|_______________________________________|
+** | Inbound Free Queue  |      Empty     |       Equal         | Head pointer last updated by hardware |
+** |_____________________|________________|_____________________|_______________________________________|
+**************************************************************************
+*/
+
+/*
+**************************************************************************
+**       Index Registers
+**  ========================
+**  . The Index Registers are a set of 1004 registers that when written by an external PCI agent can generate an interrupt to the Intel XScale core.
+**    These registers are for inbound messages only.
+**    The interrupt is recorded in the Inbound Interrupt Status Register.
+**    The storage for the Index Registers is allocated from the 80331 local memory.
+**    PCI write accesses to the Index Registers write the data to local memory.
+**    PCI read accesses to the Index Registers read the data from local memory.
+**  . The local memory used for the Index Registers ranges from Inbound ATU Translate Value Register + 050H
+**                                                           to Inbound ATU Translate Value Register + FFFH.
+**  . The address of the first write access is stored in the Index Address Register.
+**    This register is written during the earliest write access and provides a means to determine which Index Register was written.
+**    Once updated by the MU, the Index Address Register is not updated until the Index Register Interrupt bit in the Inbound Interrupt Status Register is cleared.
+**  . When the interrupt is cleared, the Index Address Register is re-enabled and stores the address of the next Index Register write access.
+**    Writes by the Intel XScale core to the local memory used by the Index Registers does not cause an interrupt and does not update the Index Address Register.
+**  . The index registers can be accessed with Multi-DWORD reads and single QWORD aligned writes.
+**************************************************************************
+*/
+/*
+**************************************************************************
+**    Messaging Unit Internal Bus Memory Map
+**  =======================================
+**  Internal Bus Address___Register Description (Name)____________________|_PCI Configuration Space Register Number_
+**  FFFF E300H             reserved                                       |
+**    ..                     ..                                           |
+**  FFFF E30CH             reserved                                       |
+**  FFFF E310H             Inbound Message Register 0                     | Available through
+**  FFFF E314H             Inbound Message Register 1                     | ATU Inbound Translation Window
+**  FFFF E318H             Outbound Message Register 0                    |
+**  FFFF E31CH             Outbound Message Register 1                    | or
+**  FFFF E320H             Inbound Doorbell Register                      |
+**  FFFF E324H             Inbound Interrupt Status Register              | must translate PCI address to
+**  FFFF E328H             Inbound Interrupt Mask Register                | the Intel Xscale Core
+**  FFFF E32CH             Outbound Doorbell Register                     | Memory-Mapped Address
+**  FFFF E330H             Outbound Interrupt Status Register             |
+**  FFFF E334H             Outbound Interrupt Mask Register               |
+**  ______________________________________________________________________|________________________________________
+**  FFFF E338H             reserved                                       |
+**  FFFF E33CH             reserved                                       |
+**  FFFF E340H             reserved                                       |
+**  FFFF E344H             reserved                                       |
+**  FFFF E348H             reserved                                       |
+**  FFFF E34CH             reserved                                       |
+**  FFFF E350H             MU Configuration Register                      |
+**  FFFF E354H             Queue Base Address Register                    |
+**  FFFF E358H             reserved                                       |
+**  FFFF E35CH             reserved                                       | must translate PCI address to
+**  FFFF E360H             Inbound Free Head Pointer Register             | the Intel Xscale Core
+**  FFFF E364H             Inbound Free Tail Pointer Register             | Memory-Mapped Address
+**  FFFF E368H             Inbound Post Head pointer Register             |
+**  FFFF E36CH             Inbound Post Tail Pointer Register             |
+**  FFFF E370H             Outbound Free Head Pointer Register            |
+**  FFFF E374H             Outbound Free Tail Pointer Register            |
+**  FFFF E378H             Outbound Post Head pointer Register            |
+**  FFFF E37CH             Outbound Post Tail Pointer Register            |
+**  FFFF E380H             Index Address Register                         |
+**  FFFF E384H             reserved                                       |
+**   ..                       ..                                          |
+**  FFFF E3FCH             reserved                                       |
+**  ______________________________________________________________________|_______________________________________
+**************************************************************************
+*/
+/*
+**************************************************************************
+**  MU Configuration Register - MUCR  FFFF.E350H
+**
+**  . The MU Configuration Register (MUCR) contains the Circular Queue Enable bit and the size of one Circular Queue.
+**  . The Circular Queue Enable bit enables or disables the Circular Queues.
+**    The Circular Queues are disabled at reset to allow the software to initialize the head and tail pointer registers before any PCI accesses to the Queue Ports.
+**  . Each Circular Queue may range from 4 K entries (16 Kbytes) to 64 K entries (256 Kbytes) and there are four Circular Queues.
+**  ------------------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:06     000000H 00 2                  Reserved
+**  05:01     00001 2                       Circular Queue Size - This field determines the size of each Circular Queue.
+**                                                                All four queues are the same size.
+**  																¡E 00001 2 - 4K Entries (16 Kbytes)
+**  																¡E 00010 2 - 8K Entries (32 Kbytes)
+**  																¡E 00100 2 - 16K Entries (64 Kbytes)
+**  																¡E 01000 2 - 32K Entries (128 Kbytes)
+**  																¡E 10000 2 - 64K Entries (256 Kbytes)
+**  00        0 2                           Circular Queue Enable - This bit enables or disables the Circular Queues. When clear the Circular
+**  																Queues are disabled, however the MU accepts PCI accesses to the Circular Queue Ports but ignores
+**  																the data for Writes and return FFFF.FFFFH for Reads. Interrupts are not generated to the core when
+**  																disabled. When set, the Circular Queues are fully enabled.
+**************************************************************************
+*/
+#define     ARCMSR_MU_CONFIGURATION_REG  	          0xFFFFE350
+#define     ARCMSR_MU_CIRCULAR_QUEUE_SIZE64K  	          0x0020
+#define     ARCMSR_MU_CIRCULAR_QUEUE_SIZE32K  	          0x0010
+#define     ARCMSR_MU_CIRCULAR_QUEUE_SIZE16K  	          0x0008
+#define     ARCMSR_MU_CIRCULAR_QUEUE_SIZE8K  	          0x0004
+#define     ARCMSR_MU_CIRCULAR_QUEUE_SIZE4K  	          0x0002
+#define     ARCMSR_MU_CIRCULAR_QUEUE_ENABLE  	          0x0001	/*0:disable 1:enable */
+/*
+**************************************************************************
+**  Queue Base Address Register - QBAR
+**
+**  . The Queue Base Address Register (QBAR) contains the local memory address of the Circular Queues.
+**    The base address is required to be located on a 1 Mbyte address boundary.
+**  . All Circular Queue head and tail pointers are based on the QBAR.
+**    When the head and tail pointer registers are read, the Queue Base Address is returned in the upper 12 bits.
+**    Writing to the upper 12 bits of the head and tail pointer registers does not affect the Queue Base Address or Queue Base Address Register.
+**  Warning:
+**         The QBAR must designate a range allocated to the 80331 DDR SDRAM interface
+**  ------------------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:20     000H                          Queue Base Address - Local memory address of the circular queues.
+**  19:00     00000H                        Reserved
+**************************************************************************
+*/
+#define     ARCMSR_MU_QUEUE_BASE_ADDRESS_REG  	      0xFFFFE354
+/*
+**************************************************************************
+**  Inbound Free Head Pointer Register - IFHPR
+**
+**  . The Inbound Free Head Pointer Register (IFHPR) contains the local memory offset from the Queue Base Address of the head pointer for the Inbound Free Queue.
+**    The Head Pointer must be aligned on a DWORD address boundary.
+**    When read, the Queue Base Address is provided in the upper 12 bits of the register.
+**    Writes to the upper 12 bits of the register are ignored.
+**    This register is maintained by software.
+**  ------------------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:20     000H                          Queue Base Address - Local memory address of the circular queues.
+**  19:02     0000H 00 2                    Inbound Free Head Pointer - Local memory offset of the head pointer for the Inbound Free Queue.
+**  01:00     00 2                          Reserved
+**************************************************************************
+*/
+#define     ARCMSR_MU_INBOUND_FREE_HEAD_PTR_REG       0xFFFFE360
+/*
+**************************************************************************
+**  Inbound Free Tail Pointer Register - IFTPR
+**
+**  . The Inbound Free Tail Pointer Register (IFTPR) contains the local memory offset from the Queue
+**    Base Address of the tail pointer for the Inbound Free Queue. The Tail Pointer must be aligned on a
+**    DWORD address boundary. When read, the Queue Base Address is provided in the upper 12 bits
+**    of the register. Writes to the upper 12 bits of the register are ignored.
+**  ------------------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:20     000H                          Queue Base Address - Local memory address of the circular queues.
+**  19:02     0000H 00 2                    Inbound Free Tail Pointer - Local memory offset of the tail pointer for the Inbound Free Queue.
+**  01:00     00 2                          Reserved
+**************************************************************************
+*/
+#define     ARCMSR_MU_INBOUND_FREE_TAIL_PTR_REG       0xFFFFE364
+/*
+**************************************************************************
+**  Inbound Post Head Pointer Register - IPHPR
+**
+**  . The Inbound Post Head Pointer Register (IPHPR) contains the local memory offset from the Queue
+**    Base Address of the head pointer for the Inbound Post Queue. The Head Pointer must be aligned on
+**    a DWORD address boundary. When read, the Queue Base Address is provided in the upper 12 bits
+**    of the register. Writes to the upper 12 bits of the register are ignored.
+**  ------------------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:20     000H                          Queue Base Address - Local memory address of the circular queues.
+**  19:02     0000H 00 2                    Inbound Post Head Pointer - Local memory offset of the head pointer for the Inbound Post Queue.
+**  01:00     00 2                          Reserved
+**************************************************************************
+*/
+#define     ARCMSR_MU_INBOUND_POST_HEAD_PTR_REG       0xFFFFE368
+/*
+**************************************************************************
+**  Inbound Post Tail Pointer Register - IPTPR
+**
+**  . The Inbound Post Tail Pointer Register (IPTPR) contains the local memory offset from the Queue
+**    Base Address of the tail pointer for the Inbound Post Queue. The Tail Pointer must be aligned on a
+**    DWORD address boundary. When read, the Queue Base Address is provided in the upper 12 bits
+**    of the register. Writes to the upper 12 bits of the register are ignored.
+**  ------------------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:20     000H                          Queue Base Address - Local memory address of the circular queues.
+**  19:02     0000H 00 2                    Inbound Post Tail Pointer - Local memory offset of the tail pointer for the Inbound Post Queue.
+**  01:00     00 2                          Reserved
+**************************************************************************
+*/
+#define     ARCMSR_MU_INBOUND_POST_TAIL_PTR_REG       0xFFFFE36C
+/*
+**************************************************************************
+**  Index Address Register - IAR
+**
+**  . The Index Address Register (IAR) contains the offset of the least recently accessed Index Register.
+**    It is written by the MU when the Index Registers are written by a PCI agent.
+**    The register is not updated until the Index Interrupt bit in the Inbound Interrupt Status Register is cleared.
+**  . The local memory address of the Index Register least recently accessed is computed by adding the Index Address Register to the Inbound ATU Translate Value Register.
+**  ------------------------------------------------------------------------
+**  Bit       Default                       Description
+**  31:12     000000H                       Reserved
+**  11:02     00H 00 2                      Index Address - is the local memory offset of the Index Register written (050H to FFCH)
+**  01:00     00 2                          Reserved
+**************************************************************************
+*/
+#define     ARCMSR_MU_LOCAL_MEMORY_INDEX_REG  	      0xFFFFE380	/*1004 dwords 0x0050....0x0FFC, 4016 bytes 0x0050...0x0FFF */
+/*
+**********************************************************************************************************
+**                                RS-232 Interface for Areca Raid Controller
+**                    The low level command interface is exclusive with VT100 terminal
+**  --------------------------------------------------------------------
+**    1. Sequence of command execution
+**  --------------------------------------------------------------------
+**    	(A) Header : 3 bytes sequence (0x5E, 0x01, 0x61)
+**    	(B) Command block : variable length of data including length, command code, data and checksum byte
+**    	(C) Return data : variable length of data
+**  --------------------------------------------------------------------
+**    2. Command block
+**  --------------------------------------------------------------------
+**    	(A) 1st byte : command block length (low byte)
+**    	(B) 2nd byte : command block length (high byte)
+**                note ..command block length shouldn't > 2040 bytes, length excludes these two bytes
+**    	(C) 3rd byte : command code
+**    	(D) 4th and following bytes : variable length data bytes depends on command code
+**    	(E) last byte : checksum byte (sum of 1st byte until last data byte)
+**  --------------------------------------------------------------------
+**    3. Command code and associated data
+**  --------------------------------------------------------------------
+**    	The following are command code defined in raid controller Command code 0x10--0x1? are used for system level management, no password checking is needed and should be implemented in separate well controlled utility and not for end user access.
+**    	Command code 0x20--0x?? always check the password, password must be entered to enable these command.
+**    	enum
+**    	{
+**    		GUI_SET_SERIAL=0x10,
+**    		GUI_SET_VENDOR,
+**    		GUI_SET_MODEL,
+**    		GUI_IDENTIFY,
+**    		GUI_CHECK_PASSWORD,
+**    		GUI_LOGOUT,
+**    		GUI_HTTP,
+**    		GUI_SET_ETHERNET_ADDR,
+**    		GUI_SET_LOGO,
+**    		GUI_POLL_EVENT,
+**    		GUI_GET_EVENT,
+**    		GUI_GET_HW_MONITOR,
+**
+**    		//    GUI_QUICK_CREATE=0x20, (function removed)
+**    		GUI_GET_INFO_R=0x20,
+**    		GUI_GET_INFO_V,
+**    		GUI_GET_INFO_P,
+**    		GUI_GET_INFO_S,
+**    		GUI_CLEAR_EVENT,
+**
+**    		GUI_MUTE_BEEPER=0x30,
+**    		GUI_BEEPER_SETTING,
+**    		GUI_SET_PASSWORD,
+**    		GUI_HOST_INTERFACE_MODE,
+**    		GUI_REBUILD_PRIORITY,
+**    		GUI_MAX_ATA_MODE,
+**    		GUI_RESET_CONTROLLER,
+**    		GUI_COM_PORT_SETTING,
+**    		GUI_NO_OPERATION,
+**    		GUI_DHCP_IP,
+**
+**    		GUI_CREATE_PASS_THROUGH=0x40,
+**    		GUI_MODIFY_PASS_THROUGH,
+**    		GUI_DELETE_PASS_THROUGH,
+**    		GUI_IDENTIFY_DEVICE,
+**
+**    		GUI_CREATE_RAIDSET=0x50,
+**    		GUI_DELETE_RAIDSET,
+**    		GUI_EXPAND_RAIDSET,
+**    		GUI_ACTIVATE_RAIDSET,
+**    		GUI_CREATE_HOT_SPARE,
+**    		GUI_DELETE_HOT_SPARE,
+**
+**    		GUI_CREATE_VOLUME=0x60,
+**    		GUI_MODIFY_VOLUME,
+**    		GUI_DELETE_VOLUME,
+**    		GUI_START_CHECK_VOLUME,
+**    		GUI_STOP_CHECK_VOLUME
+**    	};
+**
+**    Command description :
+**
+**    	GUI_SET_SERIAL : Set the controller serial#
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x10
+**    		byte 3          : password length (should be 0x0f)
+**    		byte 4-0x13     : should be "ArEcATecHnoLogY"
+**    		byte 0x14--0x23 : Serial number string (must be 16 bytes)
+**      GUI_SET_VENDOR : Set vendor string for the controller
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x11
+**    		byte 3          : password length (should be 0x08)
+**    		byte 4-0x13     : should be "ArEcAvAr"
+**    		byte 0x14--0x3B : vendor string (must be 40 bytes)
+**      GUI_SET_MODEL : Set the model name of the controller
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x12
+**    		byte 3          : password length (should be 0x08)
+**    		byte 4-0x13     : should be "ArEcAvAr"
+**    		byte 0x14--0x1B : model string (must be 8 bytes)
+**      GUI_IDENTIFY : Identify device
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x13
+**    		                  return "Areca RAID Subsystem "
+**      GUI_CHECK_PASSWORD : Verify password
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x14
+**    		byte 3          : password length
+**    		byte 4-0x??     : user password to be checked
+**      GUI_LOGOUT : Logout GUI (force password checking on next command)
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x15
+**      GUI_HTTP : HTTP interface (reserved for Http proxy service)(0x16)
+**
+**      GUI_SET_ETHERNET_ADDR : Set the ethernet MAC address
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x17
+**    		byte 3          : password length (should be 0x08)
+**    		byte 4-0x13     : should be "ArEcAvAr"
+**    		byte 0x14--0x19 : Ethernet MAC address (must be 6 bytes)
+**      GUI_SET_LOGO : Set logo in HTTP
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x18
+**    		byte 3          : Page# (0/1/2/3) (0xff --> clear OEM logo)
+**    		byte 4/5/6/7    : 0x55/0xaa/0xa5/0x5a
+**    		byte 8          : TITLE.JPG data (each page must be 2000 bytes)
+**    		                  note .... page0 1st 2 byte must be actual length of the JPG file
+**      GUI_POLL_EVENT : Poll If Event Log Changed
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x19
+**      GUI_GET_EVENT : Read Event
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x1a
+**    		byte 3          : Event Page (0:1st page/1/2/3:last page)
+**      GUI_GET_HW_MONITOR : Get HW monitor data
+**    		byte 0,1        : length
+**    		byte 2 			: command code 0x1b
+**    		byte 3 			: # of FANs(example 2)
+**    		byte 4 			: # of Voltage sensor(example 3)
+**    		byte 5 			: # of temperature sensor(example 2)
+**    		byte 6 			: # of power
+**    		byte 7/8        : Fan#0 (RPM)
+**    		byte 9/10       : Fan#1
+**    		byte 11/12 		: Voltage#0 original value in *1000
+**    		byte 13/14 		: Voltage#0 value
+**    		byte 15/16 		: Voltage#1 org
+**    		byte 17/18 		: Voltage#1
+**    		byte 19/20 		: Voltage#2 org
+**    		byte 21/22 		: Voltage#2
+**    		byte 23 		: Temp#0
+**    		byte 24 		: Temp#1
+**    		byte 25 		: Power indicator (bit0 : power#0, bit1 : power#1)
+**    		byte 26 		: UPS indicator
+**      GUI_QUICK_CREATE : Quick create raid/volume set
+**    	    byte 0,1        : length
+**    	    byte 2          : command code 0x20
+**    	    byte 3/4/5/6    : raw capacity
+**    	    byte 7 			: raid level
+**    	    byte 8 			: stripe size
+**    	    byte 9 			: spare
+**    	    byte 10/11/12/13: device mask (the devices to create raid/volume)
+**    		                  This function is removed, application like to implement quick create function
+**    		                  need to use GUI_CREATE_RAIDSET and GUI_CREATE_VOLUMESET function.
+**      GUI_GET_INFO_R : Get Raid Set Information
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x20
+**    		byte 3          : raidset#
+**
+**    	typedef struct sGUI_RAIDSET
+**    	{
+**    		BYTE grsRaidSetName[16];
+**    		DWORD grsCapacity;
+**    		DWORD grsCapacityX;
+**    		DWORD grsFailMask;
+**    		BYTE grsDevArray[32];
+**    		BYTE grsMemberDevices;
+**    		BYTE grsNewMemberDevices;
+**    		BYTE grsRaidState;
+**    		BYTE grsVolumes;
+**    		BYTE grsVolumeList[16];
+**    		BYTE grsRes1;
+**    		BYTE grsRes2;
+**    		BYTE grsRes3;
+**    		BYTE grsFreeSegments;
+**    		DWORD grsRawStripes[8];
+**    		DWORD grsRes4;
+**    		DWORD grsRes5; //     Total to 128 bytes
+**    		DWORD grsRes6; //     Total to 128 bytes
+**    	} sGUI_RAIDSET, *pGUI_RAIDSET;
+**      GUI_GET_INFO_V : Get Volume Set Information
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x21
+**    		byte 3          : volumeset#
+**
+**    	typedef struct sGUI_VOLUMESET
+**    	{
+**    		BYTE gvsVolumeName[16]; //     16
+**    		DWORD gvsCapacity;
+**    		DWORD gvsCapacityX;
+**    		DWORD gvsFailMask;
+**    		DWORD gvsStripeSize;
+**    		DWORD gvsNewFailMask;
+**    		DWORD gvsNewStripeSize;
+**    		DWORD gvsVolumeStatus;
+**    		DWORD gvsProgress; //     32
+**    		sSCSI_ATTR gvsScsi;
+**    		BYTE gvsMemberDisks;
+**    		BYTE gvsRaidLevel; //     8
+**
+**    		BYTE gvsNewMemberDisks;
+**    		BYTE gvsNewRaidLevel;
+**    		BYTE gvsRaidSetNumber;
+**    		BYTE gvsRes0; //     4
+**    		BYTE gvsRes1[4]; //     64 bytes
+**    	} sGUI_VOLUMESET, *pGUI_VOLUMESET;
+**
+**      GUI_GET_INFO_P : Get Physical Drive Information
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x22
+**    		byte 3          : drive # (from 0 to max-channels - 1)
+**
+**    	typedef struct sGUI_PHY_DRV
+**    	{
+**    		BYTE gpdModelName[40];
+**    		BYTE gpdSerialNumber[20];
+**    		BYTE gpdFirmRev[8];
+**    		DWORD gpdCapacity;
+**    		DWORD gpdCapacityX; //     Reserved for expansion
+**    		BYTE gpdDeviceState;
+**    		BYTE gpdPioMode;
+**    		BYTE gpdCurrentUdmaMode;
+**    		BYTE gpdUdmaMode;
+**    		BYTE gpdDriveSelect;
+**    		BYTE gpdRaidNumber; //     0xff if not belongs to a raid set
+**    		sSCSI_ATTR gpdScsi;
+**    		BYTE gpdReserved[40]; //     Total to 128 bytes
+**    	} sGUI_PHY_DRV, *pGUI_PHY_DRV;
+**
+**    	GUI_GET_INFO_S : Get System Information
+**      	byte 0,1        : length
+**      	byte 2          : command code 0x23
+**
+**    	typedef struct sCOM_ATTR
+**    	{
+**    		BYTE comBaudRate;
+**    		BYTE comDataBits;
+**    		BYTE comStopBits;
+**    		BYTE comParity;
+**    		BYTE comFlowControl;
+**    	} sCOM_ATTR, *pCOM_ATTR;
+**
+**    	typedef struct sSYSTEM_INFO
+**    	{
+**    		BYTE gsiVendorName[40];
+**    		BYTE gsiSerialNumber[16];
+**    		BYTE gsiFirmVersion[16];
+**    		BYTE gsiBootVersion[16];
+**    		BYTE gsiMbVersion[16];
+**    		BYTE gsiModelName[8];
+**    		BYTE gsiLocalIp[4];
+**    		BYTE gsiCurrentIp[4];
+**    		DWORD gsiTimeTick;
+**    		DWORD gsiCpuSpeed;
+**    		DWORD gsiICache;
+**    		DWORD gsiDCache;
+**    		DWORD gsiScache;
+**    		DWORD gsiMemorySize;
+**    		DWORD gsiMemorySpeed;
+**    		DWORD gsiEvents;
+**    		BYTE gsiMacAddress[6];
+**    		BYTE gsiDhcp;
+**    		BYTE gsiBeeper;
+**    		BYTE gsiChannelUsage;
+**    		BYTE gsiMaxAtaMode;
+**    		BYTE gsiSdramEcc; //     1:if ECC enabled
+**    		BYTE gsiRebuildPriority;
+**    		sCOM_ATTR gsiComA; //     5 bytes
+**    		sCOM_ATTR gsiComB; //     5 bytes
+**    		BYTE gsiIdeChannels;
+**    		BYTE gsiScsiHostChannels;
+**    		BYTE gsiIdeHostChannels;
+**    		BYTE gsiMaxVolumeSet;
+**    		BYTE gsiMaxRaidSet;
+**    		BYTE gsiEtherPort; //     1:if ether net port supported
+**    		BYTE gsiRaid6Engine; //     1:Raid6 engine supported
+**    		BYTE gsiRes[75];
+**    	} sSYSTEM_INFO, *pSYSTEM_INFO;
+**
+**    	GUI_CLEAR_EVENT : Clear System Event
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x24
+**
+**      GUI_MUTE_BEEPER : Mute current beeper
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x30
+**
+**      GUI_BEEPER_SETTING : Disable beeper
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x31
+**    		byte 3          : 0->disable, 1->enable
+**
+**      GUI_SET_PASSWORD : Change password
+**    		byte 0,1        : length
+**    		byte 2 			: command code 0x32
+**    		byte 3 			: pass word length ( must <= 15 )
+**    		byte 4 			: password (must be alpha-numerical)
+**
+**    	GUI_HOST_INTERFACE_MODE : Set host interface mode
+**    		byte 0,1        : length
+**    		byte 2 			: command code 0x33
+**    		byte 3 			: 0->Independent, 1->cluster
+**
+**      GUI_REBUILD_PRIORITY : Set rebuild priority
+**    		byte 0,1        : length
+**    		byte 2 			: command code 0x34
+**    		byte 3 			: 0/1/2/3 (low->high)
+**
+**      GUI_MAX_ATA_MODE : Set maximum ATA mode to be used
+**    		byte 0,1        : length
+**    		byte 2 			: command code 0x35
+**    		byte 3 			: 0/1/2/3 (133/100/66/33)
+**
+**      GUI_RESET_CONTROLLER : Reset Controller
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x36
+**                            *Response with VT100 screen (discard it)
+**
+**      GUI_COM_PORT_SETTING : COM port setting
+**    		byte 0,1        : length
+**    		byte 2 			: command code 0x37
+**    		byte 3 			: 0->COMA (term port), 1->COMB (debug port)
+**    		byte 4 			: 0/1/2/3/4/5/6/7 (1200/2400/4800/9600/19200/38400/57600/115200)
+**    		byte 5 			: data bit (0:7 bit, 1:8 bit : must be 8 bit)
+**    		byte 6 			: stop bit (0:1, 1:2 stop bits)
+**    		byte 7 			: parity (0:none, 1:off, 2:even)
+**    		byte 8 			: flow control (0:none, 1:xon/xoff, 2:hardware => must use none)
+**
+**      GUI_NO_OPERATION : No operation
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x38
+**
+**      GUI_DHCP_IP : Set DHCP option and local IP address
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x39
+**    		byte 3          : 0:dhcp disabled, 1:dhcp enabled
+**    		byte 4/5/6/7    : IP address
+**
+**      GUI_CREATE_PASS_THROUGH : Create pass through disk
+**    		byte 0,1        : length
+**    		byte 2 			: command code 0x40
+**    		byte 3 			: device #
+**    		byte 4 			: scsi channel (0/1)
+**    		byte 5 			: scsi id (0-->15)
+**    		byte 6 			: scsi lun (0-->7)
+**    		byte 7 			: tagged queue (1 : enabled)
+**    		byte 8 			: cache mode (1 : enabled)
+**    		byte 9 			: max speed (0/1/2/3/4, async/20/40/80/160 for scsi)
+**    								    (0/1/2/3/4, 33/66/100/133/150 for ide  )
+**
+**      GUI_MODIFY_PASS_THROUGH : Modify pass through disk
+**    		byte 0,1        : length
+**    		byte 2 			: command code 0x41
+**    		byte 3 			: device #
+**    		byte 4 			: scsi channel (0/1)
+**    		byte 5 			: scsi id (0-->15)
+**    		byte 6 			: scsi lun (0-->7)
+**    		byte 7 			: tagged queue (1 : enabled)
+**    		byte 8 			: cache mode (1 : enabled)
+**    		byte 9 			: max speed (0/1/2/3/4, async/20/40/80/160 for scsi)
+**    							        (0/1/2/3/4, 33/66/100/133/150 for ide  )
+**
+**      GUI_DELETE_PASS_THROUGH : Delete pass through disk
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x42
+**    		byte 3          : device# to be deleted
+**
+**      GUI_IDENTIFY_DEVICE : Identify Device
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x43
+**    		byte 3          : Flash Method(0:flash selected, 1:flash not selected)
+**    		byte 4/5/6/7    : IDE device mask to be flashed
+**                           note .... no response data available
+**
+**    	GUI_CREATE_RAIDSET : Create Raid Set
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x50
+**    		byte 3/4/5/6    : device mask
+**    		byte 7-22       : raidset name (if byte 7 == 0:use default)
+**
+**      GUI_DELETE_RAIDSET : Delete Raid Set
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x51
+**    		byte 3          : raidset#
+**
+**    	GUI_EXPAND_RAIDSET : Expand Raid Set
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x52
+**    		byte 3          : raidset#
+**    		byte 4/5/6/7    : device mask for expansion
+**    		byte 8/9/10     : (8:0 no change, 1 change, 0xff:terminate, 9:new raid level,10:new stripe size 0/1/2/3/4/5->4/8/16/32/64/128K )
+**    		byte 11/12/13   : repeat for each volume in the raidset ....
+**
+**      GUI_ACTIVATE_RAIDSET : Activate incomplete raid set
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x53
+**    		byte 3          : raidset#
+**
+**      GUI_CREATE_HOT_SPARE : Create hot spare disk
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x54
+**    		byte 3/4/5/6    : device mask for hot spare creation
+**
+**    	GUI_DELETE_HOT_SPARE : Delete hot spare disk
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x55
+**    		byte 3/4/5/6    : device mask for hot spare deletion
+**
+**    	GUI_CREATE_VOLUME : Create volume set
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x60
+**    		byte 3          : raidset#
+**    		byte 4-19       : volume set name (if byte4 == 0, use default)
+**    		byte 20-27      : volume capacity (blocks)
+**    		byte 28 		: raid level
+**    		byte 29 		: stripe size (0/1/2/3/4/5->4/8/16/32/64/128K)
+**    		byte 30 		: channel
+**    		byte 31 		: ID
+**    		byte 32 		: LUN
+**    		byte 33 		: 1 enable tag
+**    		byte 34 		: 1 enable cache
+**    		byte 35 		: speed (0/1/2/3/4->async/20/40/80/160 for scsi)
+**    								(0/1/2/3/4->33/66/100/133/150 for IDE  )
+**    		byte 36 		: 1 to select quick init
+**
+**    	GUI_MODIFY_VOLUME : Modify volume Set
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x61
+**    		byte 3          : volumeset#
+**    		byte 4-19       : new volume set name (if byte4 == 0, not change)
+**    		byte 20-27      : new volume capacity (reserved)
+**    		byte 28 		: new raid level
+**    		byte 29 		: new stripe size (0/1/2/3/4/5->4/8/16/32/64/128K)
+**    		byte 30 		: new channel
+**    		byte 31 		: new ID
+**    		byte 32 		: new LUN
+**    		byte 33 		: 1 enable tag
+**    		byte 34 		: 1 enable cache
+**    		byte 35 		: speed (0/1/2/3/4->async/20/40/80/160 for scsi)
+**    								(0/1/2/3/4->33/66/100/133/150 for IDE  )
+**
+**    	GUI_DELETE_VOLUME : Delete volume set
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x62
+**    		byte 3          : volumeset#
+**
+**    	GUI_START_CHECK_VOLUME : Start volume consistency check
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x63
+**    		byte 3          : volumeset#
+**
+**    	GUI_STOP_CHECK_VOLUME : Stop volume consistency check
+**    		byte 0,1        : length
+**    		byte 2          : command code 0x64
+** ---------------------------------------------------------------------
+**    4. Returned data
+** ---------------------------------------------------------------------
+**    	(A) Header          : 3 bytes sequence (0x5E, 0x01, 0x61)
+**    	(B) Length          : 2 bytes (low byte 1st, excludes length and checksum byte)
+**    	(C) status or data  :
+**           <1> If length == 1 ==> 1 byte status code
+**    								#define GUI_OK                    0x41
+**    								#define GUI_RAIDSET_NOT_NORMAL    0x42
+**    								#define GUI_VOLUMESET_NOT_NORMAL  0x43
+**    								#define GUI_NO_RAIDSET            0x44
+**    								#define GUI_NO_VOLUMESET          0x45
+**    								#define GUI_NO_PHYSICAL_DRIVE     0x46
+**    								#define GUI_PARAMETER_ERROR       0x47
+**    								#define GUI_UNSUPPORTED_COMMAND   0x48
+**    								#define GUI_DISK_CONFIG_CHANGED   0x49
+**    								#define GUI_INVALID_PASSWORD      0x4a
+**    								#define GUI_NO_DISK_SPACE         0x4b
+**    								#define GUI_CHECKSUM_ERROR        0x4c
+**    								#define GUI_PASSWORD_REQUIRED     0x4d
+**           <2> If length > 1 ==> data block returned from controller and the contents depends on the command code
+**        (E) Checksum : checksum of length and status or data byte
+**************************************************************************
+*/
diff -urN oldtree/drivers/scsi/arcmsr/arcmsr.txt newtree/drivers/scsi/arcmsr/arcmsr.txt
--- oldtree/drivers/scsi/arcmsr/arcmsr.txt	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/arcmsr/arcmsr.txt	2006-02-13 19:20:00.624417376 -0500
@@ -0,0 +1,134 @@
+README file for the arcmsr RAID controller SCSI driver
+======================================================
+History
+
+        REV#         DATE	       NAME	         DESCRIPTION
+     1.00.00.00    3/31/2004         Erich Chen        First release
+     1.10.00.04    7/28/2004         Erich Chen        modify for ioctl
+     1.10.00.06    8/28/2004         Erich Chen        modify for 2.6.x
+     1.10.00.08    9/28/2004         Erich Chen        modify for x86_64
+     1.10.00.10   10/10/2004         Erich Chen        bug fix for SMP & ioctl
+     1.20.00.00   11/29/2004         Erich Chen        bug fix with arcmsr_bus_reset when PHY error
+     1.20.00.02   12/09/2004         Erich Chen        bug fix with over 2T bytes RAID Volume
+     1.20.00.04    1/09/2005         Erich Chen        fits for Debian linux kernel version 2.2.xx
+     1.20.00.05    2/20/2005         Erich Chen        cleanly as look like a Linux driver at 2.6.x
+                                                       thanks for peoples kindness comment
+                                                                Kornel Wieliczek
+                                                                Christoph Hellwig
+                                                                Adrian Bunk
+                                                                Andrew Morton
+                                                                Christoph Hellwig
+                                                                James Bottomley
+                                                                Arjan van de Ven
+     1.20.00.06    3/12/2005         Erich Chen        fix with arcmsr_pci_unmap_dma "unsigned long" cast,
+                                                       modify PCCB POOL allocated by "dma_alloc_coherent"
+                                                       (Kornel Wieliczek's comment)
+
+     1.20.00.07    3/23/2005         Erich Chen        bug fix with arcmsr_scsi_host_template_init ocur segmentation fault,
+                                                       if RAID adapter does not on PCI slot and modprobe/rmmod this driver twice.
+                                                       bug fix enormous stack usage (Adrian Bunk's comment)
+     1.20.00.08    6/23/2005         Erich Chen        bug fix with abort command,in case of heavy loading when sata cable
+                                                       working on low quality connection
+     1.20.00.09    9/12/2005         Erich Chen        bug fix with abort command handling,firmware version check
+                                                       and firmware update notify for hardware bug fix
+     1.20.00.10    9/23/2005         Erich Chen        enhance sysfs function for change driver's max tag Q number.
+                                                       add DMA_64BIT_MASK for backward compatible with all 2.6.x
+                                                       add some useful message for abort command
+                                                       add ioctl code 'ARCMSR_IOCTL_FLUSH_ADAPTER_CACHE'
+                                                       customer can send this command for sync raid volume data
+     1.20.00.11    9/29/2005         Erich Chen        by comment of Arjan van de Ven fix incorrect msleep redefine
+                                                       cast off sizeof(dma_addr_t) condition for 64bit pci_set_dma_mask
+     1.20.00.12    9/30/2005         Erich Chen        bug fix with 64bit platform's ccbs using if over 4G system memory
+                                                       change 64bit pci_set_consistent_dma_mask into 32bit
+                                                       increcct adapter count if adapter initialize fail.
+                                                       miss edit at arcmsr_build_ccb....
+                                                       psge += sizeof(struct _SG64ENTRY *) =>  psge += sizeof(struct _SG64ENTRY)
+                                                       64 bits sg entry would be incorrectly calculated
+                                                       thanks Kornel Wieliczek give me kindly notify and detail description
+=====================================================================================================
+Support:
+
+        Linux SCSI RAID driver technical support
+
+        mail address: erich@areca.com.tw
+                 Tel: 886-2-8797-4060 Ext.203
+                 Fax: 886-2-8797-5970
+            Web site: http//:www.areca.com.tw (We support Linux RAID config tools)
+======================================================================================================
+         @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+     /usr/src/linux/drivers/scsi/Makefile
+         @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+	  ...
+	  ....
+
+	  obj-$(CONFIG_SCSI_ARCMSR)	+= arcmsr/
+
+	  ....
+	  ...
+         @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+     /usr/src/linux/drivers/scsi/Kconfig
+         @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+	  ...
+	  ....
+config SCSI_ARCMSR
+	tristate "ARECA ARC11X0[PCI-X]/ARC12X0[PCI-EXPRESS] SATA-RAID support"
+	depends on  PCI && SCSI
+	help
+	  This driver supports all of ARECA's SATA RAID controllers cards.
+	  This is an ARECA maintained driver by Erich Chen.
+	  If you have any problems, please mail to: < erich@areca.com.tw >
+	  Areca have suport Linux RAID config tools
+
+	  < http://www.areca.com.tw >
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called arcmsr (modprobe arcmsr) .
+
+	  ....
+	  ...
+	 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+###############################################################################
+
+Make new Linux kernel......
+
+
+Copyright
+---------
+************************************************************************
+** Copyright (C) 2002 - 2005, Areca Technology Corporation All rights reserved.
+**
+**     Web site: www.areca.com.tw
+**       E-mail: erich@areca.com.tw
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License version 2 as
+** published by the Free Software Foundation.
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+************************************************************************
+** Redistribution and use in source and binary forms,with or without
+** modification,are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+**    notice,this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+**    notice,this list of conditions and the following disclaimer in the
+**    documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+**    derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES,INCLUDING,BUT NOT LIMITED TO,THE IMPLIED WARRANTIES
+** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,INDIRECT,
+** INCIDENTAL,SPECIAL,EXEMPLARY,OR CONSEQUENTIAL DAMAGES(INCLUDING,BUT
+** NOT LIMITED TO,PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA,OR PROFITS; OR BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY,WHETHER IN CONTRACT,STRICT LIABILITY,OR TORT
+**(INCLUDING NEGLIGENCE OR OTHERWISE)ARISING IN ANY WAY OUT OF THE USE OF
+** THIS SOFTWARE,EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**************************************************************************
diff -urN oldtree/drivers/scsi/libata-core.c newtree/drivers/scsi/libata-core.c
--- oldtree/drivers/scsi/libata-core.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/scsi/libata-core.c	2006-02-13 19:20:00.627416920 -0500
@@ -1032,18 +1032,22 @@
 {
 	u16 modes;
 
-	/* Usual case. Word 53 indicates word 88 is valid */
-	if (adev->id[ATA_ID_FIELD_VALID] & (1 << 2)) {
+	/* Usual case. Word 53 indicates word 64 is valid */
+	if (adev->id[ATA_ID_FIELD_VALID] & (1 << 1)) {
 		modes = adev->id[ATA_ID_PIO_MODES] & 0x03;
 		modes <<= 3;
 		modes |= 0x7;
 		return modes;
 	}
 
-	/* If word 88 isn't valid then Word 51 holds the PIO timing number
-	   for the maximum. Turn it into a mask and return it */
-	modes = (2 << (adev->id[ATA_ID_OLD_PIO_MODES] & 0xFF)) - 1 ;
+	/* If word 64 isn't valid then Word 51 high byte holds the PIO timing
+	   number for the maximum. Turn it into a mask and return it */
+	modes = (2 << ((adev->id[ATA_ID_OLD_PIO_MODES] >> 8) & 0xFF)) - 1 ;
 	return modes;
+	/* But wait.. there's more. Design your standards by committee and
+	   you too can get a free iordy field to process. However its the
+	   speeds not the modes that are supported... Note drivers using the
+	   timing API will get this right anyway */
 }
 
 static int ata_qc_wait_err(struct ata_queued_cmd *qc,
diff -urN oldtree/drivers/scsi/libata-core.c.orig newtree/drivers/scsi/libata-core.c.orig
--- oldtree/drivers/scsi/libata-core.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/libata-core.c.orig	2006-02-13 19:20:00.634415856 -0500
@@ -0,0 +1,4966 @@
+/*
+ *  libata-core.c - helper library for ATA
+ *
+ *  Maintained by:  Jeff Garzik <jgarzik@pobox.com>
+ *    		    Please ALWAYS copy linux-ide@vger.kernel.org
+ *		    on emails.
+ *
+ *  Copyright 2003-2004 Red Hat, Inc.  All rights reserved.
+ *  Copyright 2003-2004 Jeff Garzik
+ *
+ *
+ *  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
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *  libata documentation is available via 'make {ps|pdf}docs',
+ *  as Documentation/DocBook/libata.*
+ *
+ *  Hardware documentation available from http://www.t13.org/ and
+ *  http://www.sata-io.org/
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/spinlock.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/suspend.h>
+#include <linux/workqueue.h>
+#include <linux/jiffies.h>
+#include <linux/scatterlist.h>
+#include <scsi/scsi.h>
+#include "scsi_priv.h"
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+#include <asm/byteorder.h>
+
+#include "libata.h"
+
+static unsigned int ata_busy_sleep (struct ata_port *ap,
+				    unsigned long tmout_pat,
+			    	    unsigned long tmout);
+static void ata_dev_reread_id(struct ata_port *ap, struct ata_device *dev);
+static void ata_dev_init_params(struct ata_port *ap, struct ata_device *dev);
+static void ata_set_mode(struct ata_port *ap);
+static void ata_dev_set_xfermode(struct ata_port *ap, struct ata_device *dev);
+static unsigned int ata_get_mode_mask(const struct ata_port *ap, int shift);
+static int fgb(u32 bitmap);
+static int ata_choose_xfer_mode(const struct ata_port *ap,
+				u8 *xfer_mode_out,
+				unsigned int *xfer_shift_out);
+static void __ata_qc_complete(struct ata_queued_cmd *qc);
+
+static unsigned int ata_unique_id = 1;
+static struct workqueue_struct *ata_wq;
+
+int atapi_enabled = 0;
+module_param(atapi_enabled, int, 0444);
+MODULE_PARM_DESC(atapi_enabled, "Enable discovery of ATAPI devices (0=off, 1=on)");
+
+MODULE_AUTHOR("Jeff Garzik");
+MODULE_DESCRIPTION("Library module for ATA devices");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+/**
+ *	ata_tf_load_pio - send taskfile registers to host controller
+ *	@ap: Port to which output is sent
+ *	@tf: ATA taskfile register set
+ *
+ *	Outputs ATA taskfile to standard ATA host controller.
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+
+static void ata_tf_load_pio(struct ata_port *ap, const struct ata_taskfile *tf)
+{
+	struct ata_ioports *ioaddr = &ap->ioaddr;
+	unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
+
+	if (tf->ctl != ap->last_ctl) {
+		outb(tf->ctl, ioaddr->ctl_addr);
+		ap->last_ctl = tf->ctl;
+		ata_wait_idle(ap);
+	}
+
+	if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {
+		outb(tf->hob_feature, ioaddr->feature_addr);
+		outb(tf->hob_nsect, ioaddr->nsect_addr);
+		outb(tf->hob_lbal, ioaddr->lbal_addr);
+		outb(tf->hob_lbam, ioaddr->lbam_addr);
+		outb(tf->hob_lbah, ioaddr->lbah_addr);
+		VPRINTK("hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X\n",
+			tf->hob_feature,
+			tf->hob_nsect,
+			tf->hob_lbal,
+			tf->hob_lbam,
+			tf->hob_lbah);
+	}
+
+	if (is_addr) {
+		outb(tf->feature, ioaddr->feature_addr);
+		outb(tf->nsect, ioaddr->nsect_addr);
+		outb(tf->lbal, ioaddr->lbal_addr);
+		outb(tf->lbam, ioaddr->lbam_addr);
+		outb(tf->lbah, ioaddr->lbah_addr);
+		VPRINTK("feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n",
+			tf->feature,
+			tf->nsect,
+			tf->lbal,
+			tf->lbam,
+			tf->lbah);
+	}
+
+	if (tf->flags & ATA_TFLAG_DEVICE) {
+		outb(tf->device, ioaddr->device_addr);
+		VPRINTK("device 0x%X\n", tf->device);
+	}
+
+	ata_wait_idle(ap);
+}
+
+/**
+ *	ata_tf_load_mmio - send taskfile registers to host controller
+ *	@ap: Port to which output is sent
+ *	@tf: ATA taskfile register set
+ *
+ *	Outputs ATA taskfile to standard ATA host controller using MMIO.
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+
+static void ata_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf)
+{
+	struct ata_ioports *ioaddr = &ap->ioaddr;
+	unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
+
+	if (tf->ctl != ap->last_ctl) {
+		writeb(tf->ctl, (void __iomem *) ap->ioaddr.ctl_addr);
+		ap->last_ctl = tf->ctl;
+		ata_wait_idle(ap);
+	}
+
+	if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {
+		writeb(tf->hob_feature, (void __iomem *) ioaddr->feature_addr);
+		writeb(tf->hob_nsect, (void __iomem *) ioaddr->nsect_addr);
+		writeb(tf->hob_lbal, (void __iomem *) ioaddr->lbal_addr);
+		writeb(tf->hob_lbam, (void __iomem *) ioaddr->lbam_addr);
+		writeb(tf->hob_lbah, (void __iomem *) ioaddr->lbah_addr);
+		VPRINTK("hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X\n",
+			tf->hob_feature,
+			tf->hob_nsect,
+			tf->hob_lbal,
+			tf->hob_lbam,
+			tf->hob_lbah);
+	}
+
+	if (is_addr) {
+		writeb(tf->feature, (void __iomem *) ioaddr->feature_addr);
+		writeb(tf->nsect, (void __iomem *) ioaddr->nsect_addr);
+		writeb(tf->lbal, (void __iomem *) ioaddr->lbal_addr);
+		writeb(tf->lbam, (void __iomem *) ioaddr->lbam_addr);
+		writeb(tf->lbah, (void __iomem *) ioaddr->lbah_addr);
+		VPRINTK("feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n",
+			tf->feature,
+			tf->nsect,
+			tf->lbal,
+			tf->lbam,
+			tf->lbah);
+	}
+
+	if (tf->flags & ATA_TFLAG_DEVICE) {
+		writeb(tf->device, (void __iomem *) ioaddr->device_addr);
+		VPRINTK("device 0x%X\n", tf->device);
+	}
+
+	ata_wait_idle(ap);
+}
+
+
+/**
+ *	ata_tf_load - send taskfile registers to host controller
+ *	@ap: Port to which output is sent
+ *	@tf: ATA taskfile register set
+ *
+ *	Outputs ATA taskfile to standard ATA host controller using MMIO
+ *	or PIO as indicated by the ATA_FLAG_MMIO flag.
+ *	Writes the control, feature, nsect, lbal, lbam, and lbah registers.
+ *	Optionally (ATA_TFLAG_LBA48) writes hob_feature, hob_nsect,
+ *	hob_lbal, hob_lbam, and hob_lbah.
+ *
+ *	This function waits for idle (!BUSY and !DRQ) after writing
+ *	registers.  If the control register has a new value, this
+ *	function also waits for idle after writing control and before
+ *	writing the remaining registers.
+ *
+ *	May be used as the tf_load() entry in ata_port_operations.
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+void ata_tf_load(struct ata_port *ap, const struct ata_taskfile *tf)
+{
+	if (ap->flags & ATA_FLAG_MMIO)
+		ata_tf_load_mmio(ap, tf);
+	else
+		ata_tf_load_pio(ap, tf);
+}
+
+/**
+ *	ata_exec_command_pio - issue ATA command to host controller
+ *	@ap: port to which command is being issued
+ *	@tf: ATA taskfile register set
+ *
+ *	Issues PIO write to ATA command register, with proper
+ *	synchronization with interrupt handler / other threads.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+
+static void ata_exec_command_pio(struct ata_port *ap, const struct ata_taskfile *tf)
+{
+	DPRINTK("ata%u: cmd 0x%X\n", ap->id, tf->command);
+
+       	outb(tf->command, ap->ioaddr.command_addr);
+	ata_pause(ap);
+}
+
+
+/**
+ *	ata_exec_command_mmio - issue ATA command to host controller
+ *	@ap: port to which command is being issued
+ *	@tf: ATA taskfile register set
+ *
+ *	Issues MMIO write to ATA command register, with proper
+ *	synchronization with interrupt handler / other threads.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+
+static void ata_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf)
+{
+	DPRINTK("ata%u: cmd 0x%X\n", ap->id, tf->command);
+
+       	writeb(tf->command, (void __iomem *) ap->ioaddr.command_addr);
+	ata_pause(ap);
+}
+
+
+/**
+ *	ata_exec_command - issue ATA command to host controller
+ *	@ap: port to which command is being issued
+ *	@tf: ATA taskfile register set
+ *
+ *	Issues PIO/MMIO write to ATA command register, with proper
+ *	synchronization with interrupt handler / other threads.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+void ata_exec_command(struct ata_port *ap, const struct ata_taskfile *tf)
+{
+	if (ap->flags & ATA_FLAG_MMIO)
+		ata_exec_command_mmio(ap, tf);
+	else
+		ata_exec_command_pio(ap, tf);
+}
+
+/**
+ *	ata_tf_to_host - issue ATA taskfile to host controller
+ *	@ap: port to which command is being issued
+ *	@tf: ATA taskfile register set
+ *
+ *	Issues ATA taskfile register set to ATA host controller,
+ *	with proper synchronization with interrupt handler and
+ *	other threads.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+
+static inline void ata_tf_to_host(struct ata_port *ap,
+				  const struct ata_taskfile *tf)
+{
+	ap->ops->tf_load(ap, tf);
+	ap->ops->exec_command(ap, tf);
+}
+
+/**
+ *	ata_tf_read_pio - input device's ATA taskfile shadow registers
+ *	@ap: Port from which input is read
+ *	@tf: ATA taskfile register set for storing input
+ *
+ *	Reads ATA taskfile registers for currently-selected device
+ *	into @tf.
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+
+static void ata_tf_read_pio(struct ata_port *ap, struct ata_taskfile *tf)
+{
+	struct ata_ioports *ioaddr = &ap->ioaddr;
+
+	tf->command = ata_check_status(ap);
+	tf->feature = inb(ioaddr->error_addr);
+	tf->nsect = inb(ioaddr->nsect_addr);
+	tf->lbal = inb(ioaddr->lbal_addr);
+	tf->lbam = inb(ioaddr->lbam_addr);
+	tf->lbah = inb(ioaddr->lbah_addr);
+	tf->device = inb(ioaddr->device_addr);
+
+	if (tf->flags & ATA_TFLAG_LBA48) {
+		outb(tf->ctl | ATA_HOB, ioaddr->ctl_addr);
+		tf->hob_feature = inb(ioaddr->error_addr);
+		tf->hob_nsect = inb(ioaddr->nsect_addr);
+		tf->hob_lbal = inb(ioaddr->lbal_addr);
+		tf->hob_lbam = inb(ioaddr->lbam_addr);
+		tf->hob_lbah = inb(ioaddr->lbah_addr);
+	}
+}
+
+/**
+ *	ata_tf_read_mmio - input device's ATA taskfile shadow registers
+ *	@ap: Port from which input is read
+ *	@tf: ATA taskfile register set for storing input
+ *
+ *	Reads ATA taskfile registers for currently-selected device
+ *	into @tf via MMIO.
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+
+static void ata_tf_read_mmio(struct ata_port *ap, struct ata_taskfile *tf)
+{
+	struct ata_ioports *ioaddr = &ap->ioaddr;
+
+	tf->command = ata_check_status(ap);
+	tf->feature = readb((void __iomem *)ioaddr->error_addr);
+	tf->nsect = readb((void __iomem *)ioaddr->nsect_addr);
+	tf->lbal = readb((void __iomem *)ioaddr->lbal_addr);
+	tf->lbam = readb((void __iomem *)ioaddr->lbam_addr);
+	tf->lbah = readb((void __iomem *)ioaddr->lbah_addr);
+	tf->device = readb((void __iomem *)ioaddr->device_addr);
+
+	if (tf->flags & ATA_TFLAG_LBA48) {
+		writeb(tf->ctl | ATA_HOB, (void __iomem *) ap->ioaddr.ctl_addr);
+		tf->hob_feature = readb((void __iomem *)ioaddr->error_addr);
+		tf->hob_nsect = readb((void __iomem *)ioaddr->nsect_addr);
+		tf->hob_lbal = readb((void __iomem *)ioaddr->lbal_addr);
+		tf->hob_lbam = readb((void __iomem *)ioaddr->lbam_addr);
+		tf->hob_lbah = readb((void __iomem *)ioaddr->lbah_addr);
+	}
+}
+
+
+/**
+ *	ata_tf_read - input device's ATA taskfile shadow registers
+ *	@ap: Port from which input is read
+ *	@tf: ATA taskfile register set for storing input
+ *
+ *	Reads ATA taskfile registers for currently-selected device
+ *	into @tf.
+ *
+ *	Reads nsect, lbal, lbam, lbah, and device.  If ATA_TFLAG_LBA48
+ *	is set, also reads the hob registers.
+ *
+ *	May be used as the tf_read() entry in ata_port_operations.
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+void ata_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
+{
+	if (ap->flags & ATA_FLAG_MMIO)
+		ata_tf_read_mmio(ap, tf);
+	else
+		ata_tf_read_pio(ap, tf);
+}
+
+/**
+ *	ata_check_status_pio - Read device status reg & clear interrupt
+ *	@ap: port where the device is
+ *
+ *	Reads ATA taskfile status register for currently-selected device
+ *	and return its value. This also clears pending interrupts
+ *      from this device
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+static u8 ata_check_status_pio(struct ata_port *ap)
+{
+	return inb(ap->ioaddr.status_addr);
+}
+
+/**
+ *	ata_check_status_mmio - Read device status reg & clear interrupt
+ *	@ap: port where the device is
+ *
+ *	Reads ATA taskfile status register for currently-selected device
+ *	via MMIO and return its value. This also clears pending interrupts
+ *      from this device
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+static u8 ata_check_status_mmio(struct ata_port *ap)
+{
+       	return readb((void __iomem *) ap->ioaddr.status_addr);
+}
+
+
+/**
+ *	ata_check_status - Read device status reg & clear interrupt
+ *	@ap: port where the device is
+ *
+ *	Reads ATA taskfile status register for currently-selected device
+ *	and return its value. This also clears pending interrupts
+ *      from this device
+ *
+ *	May be used as the check_status() entry in ata_port_operations.
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+u8 ata_check_status(struct ata_port *ap)
+{
+	if (ap->flags & ATA_FLAG_MMIO)
+		return ata_check_status_mmio(ap);
+	return ata_check_status_pio(ap);
+}
+
+
+/**
+ *	ata_altstatus - Read device alternate status reg
+ *	@ap: port where the device is
+ *
+ *	Reads ATA taskfile alternate status register for
+ *	currently-selected device and return its value.
+ *
+ *	Note: may NOT be used as the check_altstatus() entry in
+ *	ata_port_operations.
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+u8 ata_altstatus(struct ata_port *ap)
+{
+	if (ap->ops->check_altstatus)
+		return ap->ops->check_altstatus(ap);
+
+	if (ap->flags & ATA_FLAG_MMIO)
+		return readb((void __iomem *)ap->ioaddr.altstatus_addr);
+	return inb(ap->ioaddr.altstatus_addr);
+}
+
+
+/**
+ *	ata_tf_to_fis - Convert ATA taskfile to SATA FIS structure
+ *	@tf: Taskfile to convert
+ *	@fis: Buffer into which data will output
+ *	@pmp: Port multiplier port
+ *
+ *	Converts a standard ATA taskfile to a Serial ATA
+ *	FIS structure (Register - Host to Device).
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+
+void ata_tf_to_fis(const struct ata_taskfile *tf, u8 *fis, u8 pmp)
+{
+	fis[0] = 0x27;	/* Register - Host to Device FIS */
+	fis[1] = (pmp & 0xf) | (1 << 7); /* Port multiplier number,
+					    bit 7 indicates Command FIS */
+	fis[2] = tf->command;
+	fis[3] = tf->feature;
+
+	fis[4] = tf->lbal;
+	fis[5] = tf->lbam;
+	fis[6] = tf->lbah;
+	fis[7] = tf->device;
+
+	fis[8] = tf->hob_lbal;
+	fis[9] = tf->hob_lbam;
+	fis[10] = tf->hob_lbah;
+	fis[11] = tf->hob_feature;
+
+	fis[12] = tf->nsect;
+	fis[13] = tf->hob_nsect;
+	fis[14] = 0;
+	fis[15] = tf->ctl;
+
+	fis[16] = 0;
+	fis[17] = 0;
+	fis[18] = 0;
+	fis[19] = 0;
+}
+
+/**
+ *	ata_tf_from_fis - Convert SATA FIS to ATA taskfile
+ *	@fis: Buffer from which data will be input
+ *	@tf: Taskfile to output
+ *
+ *	Converts a serial ATA FIS structure to a standard ATA taskfile.
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+
+void ata_tf_from_fis(const u8 *fis, struct ata_taskfile *tf)
+{
+	tf->command	= fis[2];	/* status */
+	tf->feature	= fis[3];	/* error */
+
+	tf->lbal	= fis[4];
+	tf->lbam	= fis[5];
+	tf->lbah	= fis[6];
+	tf->device	= fis[7];
+
+	tf->hob_lbal	= fis[8];
+	tf->hob_lbam	= fis[9];
+	tf->hob_lbah	= fis[10];
+
+	tf->nsect	= fis[12];
+	tf->hob_nsect	= fis[13];
+}
+
+static const u8 ata_rw_cmds[] = {
+	/* pio multi */
+	ATA_CMD_READ_MULTI,
+	ATA_CMD_WRITE_MULTI,
+	ATA_CMD_READ_MULTI_EXT,
+	ATA_CMD_WRITE_MULTI_EXT,
+	/* pio */
+	ATA_CMD_PIO_READ,
+	ATA_CMD_PIO_WRITE,
+	ATA_CMD_PIO_READ_EXT,
+	ATA_CMD_PIO_WRITE_EXT,
+	/* dma */
+	ATA_CMD_READ,
+	ATA_CMD_WRITE,
+	ATA_CMD_READ_EXT,
+	ATA_CMD_WRITE_EXT
+};
+
+/**
+ *	ata_rwcmd_protocol - set taskfile r/w commands and protocol
+ *	@qc: command to examine and configure
+ *
+ *	Examine the device configuration and tf->flags to calculate 
+ *	the proper read/write commands and protocol to use.
+ *
+ *	LOCKING:
+ *	caller.
+ */
+void ata_rwcmd_protocol(struct ata_queued_cmd *qc)
+{
+	struct ata_taskfile *tf = &qc->tf;
+	struct ata_device *dev = qc->dev;
+
+	int index, lba48, write;
+ 
+	lba48 = (tf->flags & ATA_TFLAG_LBA48) ? 2 : 0;
+	write = (tf->flags & ATA_TFLAG_WRITE) ? 1 : 0;
+
+	if (dev->flags & ATA_DFLAG_PIO) {
+		tf->protocol = ATA_PROT_PIO;
+		index = dev->multi_count ? 0 : 4;
+	} else {
+		tf->protocol = ATA_PROT_DMA;
+		index = 8;
+	}
+
+	tf->command = ata_rw_cmds[index + lba48 + write];
+}
+
+static const char * xfer_mode_str[] = {
+	"UDMA/16",
+	"UDMA/25",
+	"UDMA/33",
+	"UDMA/44",
+	"UDMA/66",
+	"UDMA/100",
+	"UDMA/133",
+	"UDMA7",
+	"MWDMA0",
+	"MWDMA1",
+	"MWDMA2",
+	"PIO0",
+	"PIO1",
+	"PIO2",
+	"PIO3",
+	"PIO4",
+};
+
+/**
+ *	ata_udma_string - convert UDMA bit offset to string
+ *	@mask: mask of bits supported; only highest bit counts.
+ *
+ *	Determine string which represents the highest speed
+ *	(highest bit in @udma_mask).
+ *
+ *	LOCKING:
+ *	None.
+ *
+ *	RETURNS:
+ *	Constant C string representing highest speed listed in
+ *	@udma_mask, or the constant C string "<n/a>".
+ */
+
+static const char *ata_mode_string(unsigned int mask)
+{
+	int i;
+
+	for (i = 7; i >= 0; i--)
+		if (mask & (1 << i))
+			goto out;
+	for (i = ATA_SHIFT_MWDMA + 2; i >= ATA_SHIFT_MWDMA; i--)
+		if (mask & (1 << i))
+			goto out;
+	for (i = ATA_SHIFT_PIO + 4; i >= ATA_SHIFT_PIO; i--)
+		if (mask & (1 << i))
+			goto out;
+
+	return "<n/a>";
+
+out:
+	return xfer_mode_str[i];
+}
+
+/**
+ *	ata_pio_devchk - PATA device presence detection
+ *	@ap: ATA channel to examine
+ *	@device: Device to examine (starting at zero)
+ *
+ *	This technique was originally described in
+ *	Hale Landis's ATADRVR (www.ata-atapi.com), and
+ *	later found its way into the ATA/ATAPI spec.
+ *
+ *	Write a pattern to the ATA shadow registers,
+ *	and if a device is present, it will respond by
+ *	correctly storing and echoing back the
+ *	ATA shadow register contents.
+ *
+ *	LOCKING:
+ *	caller.
+ */
+
+static unsigned int ata_pio_devchk(struct ata_port *ap,
+				   unsigned int device)
+{
+	struct ata_ioports *ioaddr = &ap->ioaddr;
+	u8 nsect, lbal;
+
+	ap->ops->dev_select(ap, device);
+
+	outb(0x55, ioaddr->nsect_addr);
+	outb(0xaa, ioaddr->lbal_addr);
+
+	outb(0xaa, ioaddr->nsect_addr);
+	outb(0x55, ioaddr->lbal_addr);
+
+	outb(0x55, ioaddr->nsect_addr);
+	outb(0xaa, ioaddr->lbal_addr);
+
+	nsect = inb(ioaddr->nsect_addr);
+	lbal = inb(ioaddr->lbal_addr);
+
+	if ((nsect == 0x55) && (lbal == 0xaa))
+		return 1;	/* we found a device */
+
+	return 0;		/* nothing found */
+}
+
+/**
+ *	ata_mmio_devchk - PATA device presence detection
+ *	@ap: ATA channel to examine
+ *	@device: Device to examine (starting at zero)
+ *
+ *	This technique was originally described in
+ *	Hale Landis's ATADRVR (www.ata-atapi.com), and
+ *	later found its way into the ATA/ATAPI spec.
+ *
+ *	Write a pattern to the ATA shadow registers,
+ *	and if a device is present, it will respond by
+ *	correctly storing and echoing back the
+ *	ATA shadow register contents.
+ *
+ *	LOCKING:
+ *	caller.
+ */
+
+static unsigned int ata_mmio_devchk(struct ata_port *ap,
+				    unsigned int device)
+{
+	struct ata_ioports *ioaddr = &ap->ioaddr;
+	u8 nsect, lbal;
+
+	ap->ops->dev_select(ap, device);
+
+	writeb(0x55, (void __iomem *) ioaddr->nsect_addr);
+	writeb(0xaa, (void __iomem *) ioaddr->lbal_addr);
+
+	writeb(0xaa, (void __iomem *) ioaddr->nsect_addr);
+	writeb(0x55, (void __iomem *) ioaddr->lbal_addr);
+
+	writeb(0x55, (void __iomem *) ioaddr->nsect_addr);
+	writeb(0xaa, (void __iomem *) ioaddr->lbal_addr);
+
+	nsect = readb((void __iomem *) ioaddr->nsect_addr);
+	lbal = readb((void __iomem *) ioaddr->lbal_addr);
+
+	if ((nsect == 0x55) && (lbal == 0xaa))
+		return 1;	/* we found a device */
+
+	return 0;		/* nothing found */
+}
+
+/**
+ *	ata_devchk - PATA device presence detection
+ *	@ap: ATA channel to examine
+ *	@device: Device to examine (starting at zero)
+ *
+ *	Dispatch ATA device presence detection, depending
+ *	on whether we are using PIO or MMIO to talk to the
+ *	ATA shadow registers.
+ *
+ *	LOCKING:
+ *	caller.
+ */
+
+static unsigned int ata_devchk(struct ata_port *ap,
+				    unsigned int device)
+{
+	if (ap->flags & ATA_FLAG_MMIO)
+		return ata_mmio_devchk(ap, device);
+	return ata_pio_devchk(ap, device);
+}
+
+/**
+ *	ata_dev_classify - determine device type based on ATA-spec signature
+ *	@tf: ATA taskfile register set for device to be identified
+ *
+ *	Determine from taskfile register contents whether a device is
+ *	ATA or ATAPI, as per "Signature and persistence" section
+ *	of ATA/PI spec (volume 1, sect 5.14).
+ *
+ *	LOCKING:
+ *	None.
+ *
+ *	RETURNS:
+ *	Device type, %ATA_DEV_ATA, %ATA_DEV_ATAPI, or %ATA_DEV_UNKNOWN
+ *	the event of failure.
+ */
+
+unsigned int ata_dev_classify(const struct ata_taskfile *tf)
+{
+	/* Apple's open source Darwin code hints that some devices only
+	 * put a proper signature into the LBA mid/high registers,
+	 * So, we only check those.  It's sufficient for uniqueness.
+	 */
+
+	if (((tf->lbam == 0) && (tf->lbah == 0)) ||
+	    ((tf->lbam == 0x3c) && (tf->lbah == 0xc3))) {
+		DPRINTK("found ATA device by sig\n");
+		return ATA_DEV_ATA;
+	}
+
+	if (((tf->lbam == 0x14) && (tf->lbah == 0xeb)) ||
+	    ((tf->lbam == 0x69) && (tf->lbah == 0x96))) {
+		DPRINTK("found ATAPI device by sig\n");
+		return ATA_DEV_ATAPI;
+	}
+
+	DPRINTK("unknown device\n");
+	return ATA_DEV_UNKNOWN;
+}
+
+/**
+ *	ata_dev_try_classify - Parse returned ATA device signature
+ *	@ap: ATA channel to examine
+ *	@device: Device to examine (starting at zero)
+ *
+ *	After an event -- SRST, E.D.D., or SATA COMRESET -- occurs,
+ *	an ATA/ATAPI-defined set of values is placed in the ATA
+ *	shadow registers, indicating the results of device detection
+ *	and diagnostics.
+ *
+ *	Select the ATA device, and read the values from the ATA shadow
+ *	registers.  Then parse according to the Error register value,
+ *	and the spec-defined values examined by ata_dev_classify().
+ *
+ *	LOCKING:
+ *	caller.
+ */
+
+static u8 ata_dev_try_classify(struct ata_port *ap, unsigned int device)
+{
+	struct ata_device *dev = &ap->device[device];
+	struct ata_taskfile tf;
+	unsigned int class;
+	u8 err;
+
+	ap->ops->dev_select(ap, device);
+
+	memset(&tf, 0, sizeof(tf));
+
+	ap->ops->tf_read(ap, &tf);
+	err = tf.feature;
+
+	dev->class = ATA_DEV_NONE;
+
+	/* see if device passed diags */
+	if (err == 1)
+		/* do nothing */ ;
+	else if ((device == 0) && (err == 0x81))
+		/* do nothing */ ;
+	else
+		return err;
+
+	/* determine if device if ATA or ATAPI */
+	class = ata_dev_classify(&tf);
+	if (class == ATA_DEV_UNKNOWN)
+		return err;
+	if ((class == ATA_DEV_ATA) && (ata_chk_status(ap) == 0))
+		return err;
+
+	dev->class = class;
+
+	return err;
+}
+
+/**
+ *	ata_dev_id_string - Convert IDENTIFY DEVICE page into string
+ *	@id: IDENTIFY DEVICE results we will examine
+ *	@s: string into which data is output
+ *	@ofs: offset into identify device page
+ *	@len: length of string to return. must be an even number.
+ *
+ *	The strings in the IDENTIFY DEVICE page are broken up into
+ *	16-bit chunks.  Run through the string, and output each
+ *	8-bit chunk linearly, regardless of platform.
+ *
+ *	LOCKING:
+ *	caller.
+ */
+
+void ata_dev_id_string(const u16 *id, unsigned char *s,
+		       unsigned int ofs, unsigned int len)
+{
+	unsigned int c;
+
+	while (len > 0) {
+		c = id[ofs] >> 8;
+		*s = c;
+		s++;
+
+		c = id[ofs] & 0xff;
+		*s = c;
+		s++;
+
+		ofs++;
+		len -= 2;
+	}
+}
+
+
+/**
+ *	ata_noop_dev_select - Select device 0/1 on ATA bus
+ *	@ap: ATA channel to manipulate
+ *	@device: ATA device (numbered from zero) to select
+ *
+ *	This function performs no actual function.
+ *
+ *	May be used as the dev_select() entry in ata_port_operations.
+ *
+ *	LOCKING:
+ *	caller.
+ */
+void ata_noop_dev_select (struct ata_port *ap, unsigned int device)
+{
+}
+
+
+/**
+ *	ata_std_dev_select - Select device 0/1 on ATA bus
+ *	@ap: ATA channel to manipulate
+ *	@device: ATA device (numbered from zero) to select
+ *
+ *	Use the method defined in the ATA specification to
+ *	make either device 0, or device 1, active on the
+ *	ATA channel.  Works with both PIO and MMIO.
+ *
+ *	May be used as the dev_select() entry in ata_port_operations.
+ *
+ *	LOCKING:
+ *	caller.
+ */
+
+void ata_std_dev_select (struct ata_port *ap, unsigned int device)
+{
+	u8 tmp;
+
+	if (device == 0)
+		tmp = ATA_DEVICE_OBS;
+	else
+		tmp = ATA_DEVICE_OBS | ATA_DEV1;
+
+	if (ap->flags & ATA_FLAG_MMIO) {
+		writeb(tmp, (void __iomem *) ap->ioaddr.device_addr);
+	} else {
+		outb(tmp, ap->ioaddr.device_addr);
+	}
+	ata_pause(ap);		/* needed; also flushes, for mmio */
+}
+
+/**
+ *	ata_dev_select - Select device 0/1 on ATA bus
+ *	@ap: ATA channel to manipulate
+ *	@device: ATA device (numbered from zero) to select
+ *	@wait: non-zero to wait for Status register BSY bit to clear
+ *	@can_sleep: non-zero if context allows sleeping
+ *
+ *	Use the method defined in the ATA specification to
+ *	make either device 0, or device 1, active on the
+ *	ATA channel.
+ *
+ *	This is a high-level version of ata_std_dev_select(),
+ *	which additionally provides the services of inserting
+ *	the proper pauses and status polling, where needed.
+ *
+ *	LOCKING:
+ *	caller.
+ */
+
+void ata_dev_select(struct ata_port *ap, unsigned int device,
+			   unsigned int wait, unsigned int can_sleep)
+{
+	VPRINTK("ENTER, ata%u: device %u, wait %u\n",
+		ap->id, device, wait);
+
+	if (wait)
+		ata_wait_idle(ap);
+
+	ap->ops->dev_select(ap, device);
+
+	if (wait) {
+		if (can_sleep && ap->device[device].class == ATA_DEV_ATAPI)
+			msleep(150);
+		ata_wait_idle(ap);
+	}
+}
+
+/**
+ *	ata_dump_id - IDENTIFY DEVICE info debugging output
+ *	@dev: Device whose IDENTIFY DEVICE page we will dump
+ *
+ *	Dump selected 16-bit words from a detected device's
+ *	IDENTIFY PAGE page.
+ *
+ *	LOCKING:
+ *	caller.
+ */
+
+static inline void ata_dump_id(const struct ata_device *dev)
+{
+	DPRINTK("49==0x%04x  "
+		"53==0x%04x  "
+		"63==0x%04x  "
+		"64==0x%04x  "
+		"75==0x%04x  \n",
+		dev->id[49],
+		dev->id[53],
+		dev->id[63],
+		dev->id[64],
+		dev->id[75]);
+	DPRINTK("80==0x%04x  "
+		"81==0x%04x  "
+		"82==0x%04x  "
+		"83==0x%04x  "
+		"84==0x%04x  \n",
+		dev->id[80],
+		dev->id[81],
+		dev->id[82],
+		dev->id[83],
+		dev->id[84]);
+	DPRINTK("88==0x%04x  "
+		"93==0x%04x\n",
+		dev->id[88],
+		dev->id[93]);
+}
+
+/*
+ *	Compute the PIO modes available for this device. This is not as
+ *	trivial as it seems if we must consider early devices correctly.
+ *
+ *	FIXME: pre IDE drive timing (do we care ?). 
+ */
+
+static unsigned int ata_pio_modes(const struct ata_device *adev)
+{
+	u16 modes;
+
+	/* Usual case. Word 53 indicates word 88 is valid */
+	if (adev->id[ATA_ID_FIELD_VALID] & (1 << 2)) {
+		modes = adev->id[ATA_ID_PIO_MODES] & 0x03;
+		modes <<= 3;
+		modes |= 0x7;
+		return modes;
+	}
+
+	/* If word 88 isn't valid then Word 51 holds the PIO timing number
+	   for the maximum. Turn it into a mask and return it */
+	modes = (2 << (adev->id[ATA_ID_OLD_PIO_MODES] & 0xFF)) - 1 ;
+	return modes;
+}
+
+static int ata_qc_wait_err(struct ata_queued_cmd *qc,
+			   struct completion *wait)
+{
+	int rc = 0;
+
+	if (wait_for_completion_timeout(wait, 30 * HZ) < 1) {
+		/* timeout handling */
+		unsigned int err_mask = ac_err_mask(ata_chk_status(qc->ap));
+
+		if (!err_mask) {
+			printk(KERN_WARNING "ata%u: slow completion (cmd %x)\n",
+			       qc->ap->id, qc->tf.command);
+		} else {
+			printk(KERN_WARNING "ata%u: qc timeout (cmd %x)\n",
+			       qc->ap->id, qc->tf.command);
+			rc = -EIO;
+		}
+
+		ata_qc_complete(qc, err_mask);
+	}
+
+	return rc;
+}
+
+/**
+ *	ata_dev_identify - obtain IDENTIFY x DEVICE page
+ *	@ap: port on which device we wish to probe resides
+ *	@device: device bus address, starting at zero
+ *
+ *	Following bus reset, we issue the IDENTIFY [PACKET] DEVICE
+ *	command, and read back the 512-byte device information page.
+ *	The device information page is fed to us via the standard
+ *	PIO-IN protocol, but we hand-code it here. (TODO: investigate
+ *	using standard PIO-IN paths)
+ *
+ *	After reading the device information page, we use several
+ *	bits of information from it to initialize data structures
+ *	that will be used during the lifetime of the ata_device.
+ *	Other data from the info page is used to disqualify certain
+ *	older ATA devices we do not wish to support.
+ *
+ *	LOCKING:
+ *	Inherited from caller.  Some functions called by this function
+ *	obtain the host_set lock.
+ */
+
+static void ata_dev_identify(struct ata_port *ap, unsigned int device)
+{
+	struct ata_device *dev = &ap->device[device];
+	unsigned int major_version;
+	u16 tmp;
+	unsigned long xfer_modes;
+	unsigned int using_edd;
+	DECLARE_COMPLETION(wait);
+	struct ata_queued_cmd *qc;
+	unsigned long flags;
+	int rc;
+
+	if (!ata_dev_present(dev)) {
+		DPRINTK("ENTER/EXIT (host %u, dev %u) -- nodev\n",
+			ap->id, device);
+		return;
+	}
+
+	if (ap->flags & (ATA_FLAG_SRST | ATA_FLAG_SATA_RESET))
+		using_edd = 0;
+	else
+		using_edd = 1;
+
+	DPRINTK("ENTER, host %u, dev %u\n", ap->id, device);
+
+	assert (dev->class == ATA_DEV_ATA || dev->class == ATA_DEV_ATAPI ||
+		dev->class == ATA_DEV_NONE);
+
+	ata_dev_select(ap, device, 1, 1); /* select device 0/1 */
+
+	qc = ata_qc_new_init(ap, dev);
+	BUG_ON(qc == NULL);
+
+	ata_sg_init_one(qc, dev->id, sizeof(dev->id));
+	qc->dma_dir = DMA_FROM_DEVICE;
+	qc->tf.protocol = ATA_PROT_PIO;
+	qc->nsect = 1;
+
+retry:
+	if (dev->class == ATA_DEV_ATA) {
+		qc->tf.command = ATA_CMD_ID_ATA;
+		DPRINTK("do ATA identify\n");
+	} else {
+		qc->tf.command = ATA_CMD_ID_ATAPI;
+		DPRINTK("do ATAPI identify\n");
+	}
+
+	qc->waiting = &wait;
+	qc->complete_fn = ata_qc_complete_noop;
+
+	spin_lock_irqsave(&ap->host_set->lock, flags);
+	rc = ata_qc_issue(qc);
+	spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
+	if (rc)
+		goto err_out;
+	else
+		ata_qc_wait_err(qc, &wait);
+
+	spin_lock_irqsave(&ap->host_set->lock, flags);
+	ap->ops->tf_read(ap, &qc->tf);
+	spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
+	if (qc->tf.command & ATA_ERR) {
+		/*
+		 * arg!  EDD works for all test cases, but seems to return
+		 * the ATA signature for some ATAPI devices.  Until the
+		 * reason for this is found and fixed, we fix up the mess
+		 * here.  If IDENTIFY DEVICE returns command aborted
+		 * (as ATAPI devices do), then we issue an
+		 * IDENTIFY PACKET DEVICE.
+		 *
+		 * ATA software reset (SRST, the default) does not appear
+		 * to have this problem.
+		 */
+		if ((using_edd) && (dev->class == ATA_DEV_ATA)) {
+			u8 err = qc->tf.feature;
+			if (err & ATA_ABORTED) {
+				dev->class = ATA_DEV_ATAPI;
+				qc->cursg = 0;
+				qc->cursg_ofs = 0;
+				qc->cursect = 0;
+				qc->nsect = 1;
+				goto retry;
+			}
+		}
+		goto err_out;
+	}
+
+	swap_buf_le16(dev->id, ATA_ID_WORDS);
+
+	/* print device capabilities */
+	printk(KERN_DEBUG "ata%u: dev %u cfg "
+	       "49:%04x 82:%04x 83:%04x 84:%04x 85:%04x 86:%04x 87:%04x 88:%04x\n",
+	       ap->id, device, dev->id[49],
+	       dev->id[82], dev->id[83], dev->id[84],
+	       dev->id[85], dev->id[86], dev->id[87],
+	       dev->id[88]);
+
+	/*
+	 * common ATA, ATAPI feature tests
+	 */
+
+	/* we require DMA support (bits 8 of word 49) */
+	if (!ata_id_has_dma(dev->id)) {
+		printk(KERN_DEBUG "ata%u: no dma\n", ap->id);
+		goto err_out_nosup;
+	}
+
+	/* quick-n-dirty find max transfer mode; for printk only */
+	xfer_modes = dev->id[ATA_ID_UDMA_MODES];
+	if (!xfer_modes)
+		xfer_modes = (dev->id[ATA_ID_MWDMA_MODES]) << ATA_SHIFT_MWDMA;
+	if (!xfer_modes)
+		xfer_modes = ata_pio_modes(dev);
+
+	ata_dump_id(dev);
+
+	/* ATA-specific feature tests */
+	if (dev->class == ATA_DEV_ATA) {
+		if (!ata_id_is_ata(dev->id))	/* sanity check */
+			goto err_out_nosup;
+
+		/* get major version */
+		tmp = dev->id[ATA_ID_MAJOR_VER];
+		for (major_version = 14; major_version >= 1; major_version--)
+			if (tmp & (1 << major_version))
+				break;
+
+		/*
+		 * The exact sequence expected by certain pre-ATA4 drives is:
+		 * SRST RESET
+		 * IDENTIFY
+		 * INITIALIZE DEVICE PARAMETERS
+		 * anything else..
+		 * Some drives were very specific about that exact sequence.
+		 */
+		if (major_version < 4 || (!ata_id_has_lba(dev->id))) {
+			ata_dev_init_params(ap, dev);
+
+			/* current CHS translation info (id[53-58]) might be
+			 * changed. reread the identify device info.
+			 */
+			ata_dev_reread_id(ap, dev);
+		}
+
+		if (ata_id_has_lba(dev->id)) {
+			dev->flags |= ATA_DFLAG_LBA;
+
+			if (ata_id_has_lba48(dev->id)) {
+				dev->flags |= ATA_DFLAG_LBA48;
+				dev->n_sectors = ata_id_u64(dev->id, 100);
+			} else {
+				dev->n_sectors = ata_id_u32(dev->id, 60);
+			}
+
+			/* print device info to dmesg */
+			printk(KERN_INFO "ata%u: dev %u ATA-%d, max %s, %Lu sectors:%s\n",
+			       ap->id, device,
+			       major_version,
+			       ata_mode_string(xfer_modes),
+			       (unsigned long long)dev->n_sectors,
+			       dev->flags & ATA_DFLAG_LBA48 ? " LBA48" : " LBA");
+		} else { 
+			/* CHS */
+
+			/* Default translation */
+			dev->cylinders	= dev->id[1];
+			dev->heads	= dev->id[3];
+			dev->sectors	= dev->id[6];
+			dev->n_sectors	= dev->cylinders * dev->heads * dev->sectors;
+
+			if (ata_id_current_chs_valid(dev->id)) {
+				/* Current CHS translation is valid. */
+				dev->cylinders = dev->id[54];
+				dev->heads     = dev->id[55];
+				dev->sectors   = dev->id[56];
+				
+				dev->n_sectors = ata_id_u32(dev->id, 57);
+			}
+
+			/* print device info to dmesg */
+			printk(KERN_INFO "ata%u: dev %u ATA-%d, max %s, %Lu sectors: CHS %d/%d/%d\n",
+			       ap->id, device,
+			       major_version,
+			       ata_mode_string(xfer_modes),
+			       (unsigned long long)dev->n_sectors,
+			       (int)dev->cylinders, (int)dev->heads, (int)dev->sectors);
+
+		}
+
+		ap->host->max_cmd_len = 16;
+	}
+
+	/* ATAPI-specific feature tests */
+	else if (dev->class == ATA_DEV_ATAPI) {
+		if (ata_id_is_ata(dev->id))		/* sanity check */
+			goto err_out_nosup;
+
+		rc = atapi_cdb_len(dev->id);
+		if ((rc < 12) || (rc > ATAPI_CDB_LEN)) {
+			printk(KERN_WARNING "ata%u: unsupported CDB len\n", ap->id);
+			goto err_out_nosup;
+		}
+		ap->cdb_len = (unsigned int) rc;
+		ap->host->max_cmd_len = (unsigned char) ap->cdb_len;
+
+		/* print device info to dmesg */
+		printk(KERN_INFO "ata%u: dev %u ATAPI, max %s\n",
+		       ap->id, device,
+		       ata_mode_string(xfer_modes));
+	}
+
+	DPRINTK("EXIT, drv_stat = 0x%x\n", ata_chk_status(ap));
+	return;
+
+err_out_nosup:
+	printk(KERN_WARNING "ata%u: dev %u not supported, ignoring\n",
+	       ap->id, device);
+err_out:
+	dev->class++;	/* converts ATA_DEV_xxx into ATA_DEV_xxx_UNSUP */
+	DPRINTK("EXIT, err\n");
+}
+
+
+static inline u8 ata_dev_knobble(const struct ata_port *ap)
+{
+	return ((ap->cbl == ATA_CBL_SATA) && (!ata_id_is_sata(ap->device->id)));
+}
+
+/**
+ * 	ata_dev_config - Run device specific handlers and check for
+ * 			 SATA->PATA bridges
+ * 	@ap: Bus
+ * 	@i:  Device
+ *
+ * 	LOCKING:
+ */
+
+void ata_dev_config(struct ata_port *ap, unsigned int i)
+{
+	/* limit bridge transfers to udma5, 200 sectors */
+	if (ata_dev_knobble(ap)) {
+		printk(KERN_INFO "ata%u(%u): applying bridge limits\n",
+			ap->id, ap->device->devno);
+		ap->udma_mask &= ATA_UDMA5;
+		ap->host->max_sectors = ATA_MAX_SECTORS;
+		ap->host->hostt->max_sectors = ATA_MAX_SECTORS;
+		ap->device->flags |= ATA_DFLAG_LOCK_SECTORS;
+	}
+
+	if (ap->ops->dev_config)
+		ap->ops->dev_config(ap, &ap->device[i]);
+}
+
+/**
+ *	ata_bus_probe - Reset and probe ATA bus
+ *	@ap: Bus to probe
+ *
+ *	Master ATA bus probing function.  Initiates a hardware-dependent
+ *	bus reset, then attempts to identify any devices found on
+ *	the bus.
+ *
+ *	LOCKING:
+ *	PCI/etc. bus probe sem.
+ *
+ *	RETURNS:
+ *	Zero on success, non-zero on error.
+ */
+
+static int ata_bus_probe(struct ata_port *ap)
+{
+	unsigned int i, found = 0;
+
+	ap->ops->phy_reset(ap);
+	if (ap->flags & ATA_FLAG_PORT_DISABLED)
+		goto err_out;
+
+	for (i = 0; i < ATA_MAX_DEVICES; i++) {
+		ata_dev_identify(ap, i);
+		if (ata_dev_present(&ap->device[i])) {
+			found = 1;
+			ata_dev_config(ap,i);
+		}
+	}
+
+	if ((!found) || (ap->flags & ATA_FLAG_PORT_DISABLED))
+		goto err_out_disable;
+
+	ata_set_mode(ap);
+	if (ap->flags & ATA_FLAG_PORT_DISABLED)
+		goto err_out_disable;
+
+	return 0;
+
+err_out_disable:
+	ap->ops->port_disable(ap);
+err_out:
+	return -1;
+}
+
+/**
+ *	ata_port_probe - Mark port as enabled
+ *	@ap: Port for which we indicate enablement
+ *
+ *	Modify @ap data structure such that the system
+ *	thinks that the entire port is enabled.
+ *
+ *	LOCKING: host_set lock, or some other form of
+ *	serialization.
+ */
+
+void ata_port_probe(struct ata_port *ap)
+{
+	ap->flags &= ~ATA_FLAG_PORT_DISABLED;
+}
+
+/**
+ *	__sata_phy_reset - Wake/reset a low-level SATA PHY
+ *	@ap: SATA port associated with target SATA PHY.
+ *
+ *	This function issues commands to standard SATA Sxxx
+ *	PHY registers, to wake up the phy (and device), and
+ *	clear any reset condition.
+ *
+ *	LOCKING:
+ *	PCI/etc. bus probe sem.
+ *
+ */
+void __sata_phy_reset(struct ata_port *ap)
+{
+	u32 sstatus;
+	unsigned long timeout = jiffies + (HZ * 5);
+
+	if (ap->flags & ATA_FLAG_SATA_RESET) {
+		/* issue phy wake/reset */
+		scr_write_flush(ap, SCR_CONTROL, 0x301);
+		/* Couldn't find anything in SATA I/II specs, but
+		 * AHCI-1.1 10.4.2 says at least 1 ms. */
+		mdelay(1);
+	}
+	scr_write_flush(ap, SCR_CONTROL, 0x300); /* phy wake/clear reset */
+
+	/* wait for phy to become ready, if necessary */
+	do {
+		msleep(200);
+		sstatus = scr_read(ap, SCR_STATUS);
+		if ((sstatus & 0xf) != 1)
+			break;
+	} while (time_before(jiffies, timeout));
+
+	/* TODO: phy layer with polling, timeouts, etc. */
+	if (sata_dev_present(ap))
+		ata_port_probe(ap);
+	else {
+		sstatus = scr_read(ap, SCR_STATUS);
+		printk(KERN_INFO "ata%u: no device found (phy stat %08x)\n",
+		       ap->id, sstatus);
+		ata_port_disable(ap);
+	}
+
+	if (ap->flags & ATA_FLAG_PORT_DISABLED)
+		return;
+
+	if (ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT)) {
+		ata_port_disable(ap);
+		return;
+	}
+
+	ap->cbl = ATA_CBL_SATA;
+}
+
+/**
+ *	sata_phy_reset - Reset SATA bus.
+ *	@ap: SATA port associated with target SATA PHY.
+ *
+ *	This function resets the SATA bus, and then probes
+ *	the bus for devices.
+ *
+ *	LOCKING:
+ *	PCI/etc. bus probe sem.
+ *
+ */
+void sata_phy_reset(struct ata_port *ap)
+{
+	__sata_phy_reset(ap);
+	if (ap->flags & ATA_FLAG_PORT_DISABLED)
+		return;
+	ata_bus_reset(ap);
+}
+
+/**
+ *	ata_port_disable - Disable port.
+ *	@ap: Port to be disabled.
+ *
+ *	Modify @ap data structure such that the system
+ *	thinks that the entire port is disabled, and should
+ *	never attempt to probe or communicate with devices
+ *	on this port.
+ *
+ *	LOCKING: host_set lock, or some other form of
+ *	serialization.
+ */
+
+void ata_port_disable(struct ata_port *ap)
+{
+	ap->device[0].class = ATA_DEV_NONE;
+	ap->device[1].class = ATA_DEV_NONE;
+	ap->flags |= ATA_FLAG_PORT_DISABLED;
+}
+
+/*
+ * This mode timing computation functionality is ported over from
+ * drivers/ide/ide-timing.h and was originally written by Vojtech Pavlik
+ */
+/*
+ * PIO 0-5, MWDMA 0-2 and UDMA 0-6 timings (in nanoseconds).
+ * These were taken from ATA/ATAPI-6 standard, rev 0a, except
+ * for PIO 5, which is a nonstandard extension and UDMA6, which
+ * is currently supported only by Maxtor drives. 
+ */
+
+static const struct ata_timing ata_timing[] = {
+
+	{ XFER_UDMA_6,     0,   0,   0,   0,   0,   0,   0,  15 },
+	{ XFER_UDMA_5,     0,   0,   0,   0,   0,   0,   0,  20 },
+	{ XFER_UDMA_4,     0,   0,   0,   0,   0,   0,   0,  30 },
+	{ XFER_UDMA_3,     0,   0,   0,   0,   0,   0,   0,  45 },
+
+	{ XFER_UDMA_2,     0,   0,   0,   0,   0,   0,   0,  60 },
+	{ XFER_UDMA_1,     0,   0,   0,   0,   0,   0,   0,  80 },
+	{ XFER_UDMA_0,     0,   0,   0,   0,   0,   0,   0, 120 },
+
+/*	{ XFER_UDMA_SLOW,  0,   0,   0,   0,   0,   0,   0, 150 }, */
+                                          
+	{ XFER_MW_DMA_2,  25,   0,   0,   0,  70,  25, 120,   0 },
+	{ XFER_MW_DMA_1,  45,   0,   0,   0,  80,  50, 150,   0 },
+	{ XFER_MW_DMA_0,  60,   0,   0,   0, 215, 215, 480,   0 },
+                                          
+	{ XFER_SW_DMA_2,  60,   0,   0,   0, 120, 120, 240,   0 },
+	{ XFER_SW_DMA_1,  90,   0,   0,   0, 240, 240, 480,   0 },
+	{ XFER_SW_DMA_0, 120,   0,   0,   0, 480, 480, 960,   0 },
+
+/*	{ XFER_PIO_5,     20,  50,  30, 100,  50,  30, 100,   0 }, */
+	{ XFER_PIO_4,     25,  70,  25, 120,  70,  25, 120,   0 },
+	{ XFER_PIO_3,     30,  80,  70, 180,  80,  70, 180,   0 },
+
+	{ XFER_PIO_2,     30, 290,  40, 330, 100,  90, 240,   0 },
+	{ XFER_PIO_1,     50, 290,  93, 383, 125, 100, 383,   0 },
+	{ XFER_PIO_0,     70, 290, 240, 600, 165, 150, 600,   0 },
+
+/*	{ XFER_PIO_SLOW, 120, 290, 240, 960, 290, 240, 960,   0 }, */
+
+	{ 0xFF }
+};
+
+#define ENOUGH(v,unit)		(((v)-1)/(unit)+1)
+#define EZ(v,unit)		((v)?ENOUGH(v,unit):0)
+
+static void ata_timing_quantize(const struct ata_timing *t, struct ata_timing *q, int T, int UT)
+{
+	q->setup   = EZ(t->setup   * 1000,  T);
+	q->act8b   = EZ(t->act8b   * 1000,  T);
+	q->rec8b   = EZ(t->rec8b   * 1000,  T);
+	q->cyc8b   = EZ(t->cyc8b   * 1000,  T);
+	q->active  = EZ(t->active  * 1000,  T);
+	q->recover = EZ(t->recover * 1000,  T);
+	q->cycle   = EZ(t->cycle   * 1000,  T);
+	q->udma    = EZ(t->udma    * 1000, UT);
+}
+
+void ata_timing_merge(const struct ata_timing *a, const struct ata_timing *b,
+		      struct ata_timing *m, unsigned int what)
+{
+	if (what & ATA_TIMING_SETUP  ) m->setup   = max(a->setup,   b->setup);
+	if (what & ATA_TIMING_ACT8B  ) m->act8b   = max(a->act8b,   b->act8b);
+	if (what & ATA_TIMING_REC8B  ) m->rec8b   = max(a->rec8b,   b->rec8b);
+	if (what & ATA_TIMING_CYC8B  ) m->cyc8b   = max(a->cyc8b,   b->cyc8b);
+	if (what & ATA_TIMING_ACTIVE ) m->active  = max(a->active,  b->active);
+	if (what & ATA_TIMING_RECOVER) m->recover = max(a->recover, b->recover);
+	if (what & ATA_TIMING_CYCLE  ) m->cycle   = max(a->cycle,   b->cycle);
+	if (what & ATA_TIMING_UDMA   ) m->udma    = max(a->udma,    b->udma);
+}
+
+static const struct ata_timing* ata_timing_find_mode(unsigned short speed)
+{
+	const struct ata_timing *t;
+
+	for (t = ata_timing; t->mode != speed; t++)
+		if (t->mode == 0xFF)
+			return NULL;
+	return t; 
+}
+
+int ata_timing_compute(struct ata_device *adev, unsigned short speed,
+		       struct ata_timing *t, int T, int UT)
+{
+	const struct ata_timing *s;
+	struct ata_timing p;
+
+	/*
+	 * Find the mode. 
+	 */
+
+	if (!(s = ata_timing_find_mode(speed)))
+		return -EINVAL;
+
+	memcpy(t, s, sizeof(*s));
+
+	/*
+	 * If the drive is an EIDE drive, it can tell us it needs extended
+	 * PIO/MW_DMA cycle timing.
+	 */
+
+	if (adev->id[ATA_ID_FIELD_VALID] & 2) {	/* EIDE drive */
+		memset(&p, 0, sizeof(p));
+		if(speed >= XFER_PIO_0 && speed <= XFER_SW_DMA_0) {
+			if (speed <= XFER_PIO_2) p.cycle = p.cyc8b = adev->id[ATA_ID_EIDE_PIO];
+					    else p.cycle = p.cyc8b = adev->id[ATA_ID_EIDE_PIO_IORDY];
+		} else if(speed >= XFER_MW_DMA_0 && speed <= XFER_MW_DMA_2) {
+			p.cycle = adev->id[ATA_ID_EIDE_DMA_MIN];
+		}
+		ata_timing_merge(&p, t, t, ATA_TIMING_CYCLE | ATA_TIMING_CYC8B);
+	}
+
+	/*
+	 * Convert the timing to bus clock counts.
+	 */
+
+	ata_timing_quantize(t, t, T, UT);
+
+	/*
+	 * Even in DMA/UDMA modes we still use PIO access for IDENTIFY, S.M.A.R.T
+	 * and some other commands. We have to ensure that the DMA cycle timing is
+	 * slower/equal than the fastest PIO timing.
+	 */
+
+	if (speed > XFER_PIO_4) {
+		ata_timing_compute(adev, adev->pio_mode, &p, T, UT);
+		ata_timing_merge(&p, t, t, ATA_TIMING_ALL);
+	}
+
+	/*
+	 * Lenghten active & recovery time so that cycle time is correct.
+	 */
+
+	if (t->act8b + t->rec8b < t->cyc8b) {
+		t->act8b += (t->cyc8b - (t->act8b + t->rec8b)) / 2;
+		t->rec8b = t->cyc8b - t->act8b;
+	}
+
+	if (t->active + t->recover < t->cycle) {
+		t->active += (t->cycle - (t->active + t->recover)) / 2;
+		t->recover = t->cycle - t->active;
+	}
+
+	return 0;
+}
+
+static const struct {
+	unsigned int shift;
+	u8 base;
+} xfer_mode_classes[] = {
+	{ ATA_SHIFT_UDMA,	XFER_UDMA_0 },
+	{ ATA_SHIFT_MWDMA,	XFER_MW_DMA_0 },
+	{ ATA_SHIFT_PIO,	XFER_PIO_0 },
+};
+
+static inline u8 base_from_shift(unsigned int shift)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(xfer_mode_classes); i++)
+		if (xfer_mode_classes[i].shift == shift)
+			return xfer_mode_classes[i].base;
+
+	return 0xff;
+}
+
+static void ata_dev_set_mode(struct ata_port *ap, struct ata_device *dev)
+{
+	int ofs, idx;
+	u8 base;
+
+	if (!ata_dev_present(dev) || (ap->flags & ATA_FLAG_PORT_DISABLED))
+		return;
+
+	if (dev->xfer_shift == ATA_SHIFT_PIO)
+		dev->flags |= ATA_DFLAG_PIO;
+
+	ata_dev_set_xfermode(ap, dev);
+
+	base = base_from_shift(dev->xfer_shift);
+	ofs = dev->xfer_mode - base;
+	idx = ofs + dev->xfer_shift;
+	WARN_ON(idx >= ARRAY_SIZE(xfer_mode_str));
+
+	DPRINTK("idx=%d xfer_shift=%u, xfer_mode=0x%x, base=0x%x, offset=%d\n",
+		idx, dev->xfer_shift, (int)dev->xfer_mode, (int)base, ofs);
+
+	printk(KERN_INFO "ata%u: dev %u configured for %s\n",
+		ap->id, dev->devno, xfer_mode_str[idx]);
+}
+
+static int ata_host_set_pio(struct ata_port *ap)
+{
+	unsigned int mask;
+	int x, i;
+	u8 base, xfer_mode;
+
+	mask = ata_get_mode_mask(ap, ATA_SHIFT_PIO);
+	x = fgb(mask);
+	if (x < 0) {
+		printk(KERN_WARNING "ata%u: no PIO support\n", ap->id);
+		return -1;
+	}
+
+	base = base_from_shift(ATA_SHIFT_PIO);
+	xfer_mode = base + x;
+
+	DPRINTK("base 0x%x xfer_mode 0x%x mask 0x%x x %d\n",
+		(int)base, (int)xfer_mode, mask, x);
+
+	for (i = 0; i < ATA_MAX_DEVICES; i++) {
+		struct ata_device *dev = &ap->device[i];
+		if (ata_dev_present(dev)) {
+			dev->pio_mode = xfer_mode;
+			dev->xfer_mode = xfer_mode;
+			dev->xfer_shift = ATA_SHIFT_PIO;
+			if (ap->ops->set_piomode)
+				ap->ops->set_piomode(ap, dev);
+		}
+	}
+
+	return 0;
+}
+
+static void ata_host_set_dma(struct ata_port *ap, u8 xfer_mode,
+			    unsigned int xfer_shift)
+{
+	int i;
+
+	for (i = 0; i < ATA_MAX_DEVICES; i++) {
+		struct ata_device *dev = &ap->device[i];
+		if (ata_dev_present(dev)) {
+			dev->dma_mode = xfer_mode;
+			dev->xfer_mode = xfer_mode;
+			dev->xfer_shift = xfer_shift;
+			if (ap->ops->set_dmamode)
+				ap->ops->set_dmamode(ap, dev);
+		}
+	}
+}
+
+/**
+ *	ata_set_mode - Program timings and issue SET FEATURES - XFER
+ *	@ap: port on which timings will be programmed
+ *
+ *	Set ATA device disk transfer mode (PIO3, UDMA6, etc.).
+ *
+ *	LOCKING:
+ *	PCI/etc. bus probe sem.
+ *
+ */
+static void ata_set_mode(struct ata_port *ap)
+{
+	unsigned int xfer_shift;
+	u8 xfer_mode;
+	int rc;
+
+	/* step 1: always set host PIO timings */
+	rc = ata_host_set_pio(ap);
+	if (rc)
+		goto err_out;
+
+	/* step 2: choose the best data xfer mode */
+	xfer_mode = xfer_shift = 0;
+	rc = ata_choose_xfer_mode(ap, &xfer_mode, &xfer_shift);
+	if (rc)
+		goto err_out;
+
+	/* step 3: if that xfer mode isn't PIO, set host DMA timings */
+	if (xfer_shift != ATA_SHIFT_PIO)
+		ata_host_set_dma(ap, xfer_mode, xfer_shift);
+
+	/* step 4: update devices' xfer mode */
+	ata_dev_set_mode(ap, &ap->device[0]);
+	ata_dev_set_mode(ap, &ap->device[1]);
+
+	if (ap->flags & ATA_FLAG_PORT_DISABLED)
+		return;
+
+	if (ap->ops->post_set_mode)
+		ap->ops->post_set_mode(ap);
+
+	return;
+
+err_out:
+	ata_port_disable(ap);
+}
+
+/**
+ *	ata_busy_sleep - sleep until BSY clears, or timeout
+ *	@ap: port containing status register to be polled
+ *	@tmout_pat: impatience timeout
+ *	@tmout: overall timeout
+ *
+ *	Sleep until ATA Status register bit BSY clears,
+ *	or a timeout occurs.
+ *
+ *	LOCKING: None.
+ *
+ */
+
+static unsigned int ata_busy_sleep (struct ata_port *ap,
+				    unsigned long tmout_pat,
+			    	    unsigned long tmout)
+{
+	unsigned long timer_start, timeout;
+	u8 status;
+
+	status = ata_busy_wait(ap, ATA_BUSY, 300);
+	timer_start = jiffies;
+	timeout = timer_start + tmout_pat;
+	while ((status & ATA_BUSY) && (time_before(jiffies, timeout))) {
+		msleep(50);
+		status = ata_busy_wait(ap, ATA_BUSY, 3);
+	}
+
+	if (status & ATA_BUSY)
+		printk(KERN_WARNING "ata%u is slow to respond, "
+		       "please be patient\n", ap->id);
+
+	timeout = timer_start + tmout;
+	while ((status & ATA_BUSY) && (time_before(jiffies, timeout))) {
+		msleep(50);
+		status = ata_chk_status(ap);
+	}
+
+	if (status & ATA_BUSY) {
+		printk(KERN_ERR "ata%u failed to respond (%lu secs)\n",
+		       ap->id, tmout / HZ);
+		return 1;
+	}
+
+	return 0;
+}
+
+static void ata_bus_post_reset(struct ata_port *ap, unsigned int devmask)
+{
+	struct ata_ioports *ioaddr = &ap->ioaddr;
+	unsigned int dev0 = devmask & (1 << 0);
+	unsigned int dev1 = devmask & (1 << 1);
+	unsigned long timeout;
+
+	/* if device 0 was found in ata_devchk, wait for its
+	 * BSY bit to clear
+	 */
+	if (dev0)
+		ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT);
+
+	/* if device 1 was found in ata_devchk, wait for
+	 * register access, then wait for BSY to clear
+	 */
+	timeout = jiffies + ATA_TMOUT_BOOT;
+	while (dev1) {
+		u8 nsect, lbal;
+
+		ap->ops->dev_select(ap, 1);
+		if (ap->flags & ATA_FLAG_MMIO) {
+			nsect = readb((void __iomem *) ioaddr->nsect_addr);
+			lbal = readb((void __iomem *) ioaddr->lbal_addr);
+		} else {
+			nsect = inb(ioaddr->nsect_addr);
+			lbal = inb(ioaddr->lbal_addr);
+		}
+		if ((nsect == 1) && (lbal == 1))
+			break;
+		if (time_after(jiffies, timeout)) {
+			dev1 = 0;
+			break;
+		}
+		msleep(50);	/* give drive a breather */
+	}
+	if (dev1)
+		ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT);
+
+	/* is all this really necessary? */
+	ap->ops->dev_select(ap, 0);
+	if (dev1)
+		ap->ops->dev_select(ap, 1);
+	if (dev0)
+		ap->ops->dev_select(ap, 0);
+}
+
+/**
+ *	ata_bus_edd - Issue EXECUTE DEVICE DIAGNOSTIC command.
+ *	@ap: Port to reset and probe
+ *
+ *	Use the EXECUTE DEVICE DIAGNOSTIC command to reset and
+ *	probe the bus.  Not often used these days.
+ *
+ *	LOCKING:
+ *	PCI/etc. bus probe sem.
+ *	Obtains host_set lock.
+ *
+ */
+
+static unsigned int ata_bus_edd(struct ata_port *ap)
+{
+	struct ata_taskfile tf;
+	unsigned long flags;
+
+	/* set up execute-device-diag (bus reset) taskfile */
+	/* also, take interrupts to a known state (disabled) */
+	DPRINTK("execute-device-diag\n");
+	ata_tf_init(ap, &tf, 0);
+	tf.ctl |= ATA_NIEN;
+	tf.command = ATA_CMD_EDD;
+	tf.protocol = ATA_PROT_NODATA;
+
+	/* do bus reset */
+	spin_lock_irqsave(&ap->host_set->lock, flags);
+	ata_tf_to_host(ap, &tf);
+	spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
+	/* spec says at least 2ms.  but who knows with those
+	 * crazy ATAPI devices...
+	 */
+	msleep(150);
+
+	return ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT);
+}
+
+static unsigned int ata_bus_softreset(struct ata_port *ap,
+				      unsigned int devmask)
+{
+	struct ata_ioports *ioaddr = &ap->ioaddr;
+
+	DPRINTK("ata%u: bus reset via SRST\n", ap->id);
+
+	/* software reset.  causes dev0 to be selected */
+	if (ap->flags & ATA_FLAG_MMIO) {
+		writeb(ap->ctl, (void __iomem *) ioaddr->ctl_addr);
+		udelay(20);	/* FIXME: flush */
+		writeb(ap->ctl | ATA_SRST, (void __iomem *) ioaddr->ctl_addr);
+		udelay(20);	/* FIXME: flush */
+		writeb(ap->ctl, (void __iomem *) ioaddr->ctl_addr);
+	} else {
+		outb(ap->ctl, ioaddr->ctl_addr);
+		udelay(10);
+		outb(ap->ctl | ATA_SRST, ioaddr->ctl_addr);
+		udelay(10);
+		outb(ap->ctl, ioaddr->ctl_addr);
+	}
+
+	/* spec mandates ">= 2ms" before checking status.
+	 * We wait 150ms, because that was the magic delay used for
+	 * ATAPI devices in Hale Landis's ATADRVR, for the period of time
+	 * between when the ATA command register is written, and then
+	 * status is checked.  Because waiting for "a while" before
+	 * checking status is fine, post SRST, we perform this magic
+	 * delay here as well.
+	 */
+	msleep(150);
+
+	ata_bus_post_reset(ap, devmask);
+
+	return 0;
+}
+
+/**
+ *	ata_bus_reset - reset host port and associated ATA channel
+ *	@ap: port to reset
+ *
+ *	This is typically the first time we actually start issuing
+ *	commands to the ATA channel.  We wait for BSY to clear, then
+ *	issue EXECUTE DEVICE DIAGNOSTIC command, polling for its
+ *	result.  Determine what devices, if any, are on the channel
+ *	by looking at the device 0/1 error register.  Look at the signature
+ *	stored in each device's taskfile registers, to determine if
+ *	the device is ATA or ATAPI.
+ *
+ *	LOCKING:
+ *	PCI/etc. bus probe sem.
+ *	Obtains host_set lock.
+ *
+ *	SIDE EFFECTS:
+ *	Sets ATA_FLAG_PORT_DISABLED if bus reset fails.
+ */
+
+void ata_bus_reset(struct ata_port *ap)
+{
+	struct ata_ioports *ioaddr = &ap->ioaddr;
+	unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS;
+	u8 err;
+	unsigned int dev0, dev1 = 0, rc = 0, devmask = 0;
+
+	DPRINTK("ENTER, host %u, port %u\n", ap->id, ap->port_no);
+
+	/* determine if device 0/1 are present */
+	if (ap->flags & ATA_FLAG_SATA_RESET)
+		dev0 = 1;
+	else {
+		dev0 = ata_devchk(ap, 0);
+		if (slave_possible)
+			dev1 = ata_devchk(ap, 1);
+	}
+
+	if (dev0)
+		devmask |= (1 << 0);
+	if (dev1)
+		devmask |= (1 << 1);
+
+	/* select device 0 again */
+	ap->ops->dev_select(ap, 0);
+
+	/* issue bus reset */
+	if (ap->flags & ATA_FLAG_SRST)
+		rc = ata_bus_softreset(ap, devmask);
+	else if ((ap->flags & ATA_FLAG_SATA_RESET) == 0) {
+		/* set up device control */
+		if (ap->flags & ATA_FLAG_MMIO)
+			writeb(ap->ctl, (void __iomem *) ioaddr->ctl_addr);
+		else
+			outb(ap->ctl, ioaddr->ctl_addr);
+		rc = ata_bus_edd(ap);
+	}
+
+	if (rc)
+		goto err_out;
+
+	/*
+	 * determine by signature whether we have ATA or ATAPI devices
+	 */
+	err = ata_dev_try_classify(ap, 0);
+	if ((slave_possible) && (err != 0x81))
+		ata_dev_try_classify(ap, 1);
+
+	/* re-enable interrupts */
+	if (ap->ioaddr.ctl_addr)	/* FIXME: hack. create a hook instead */
+		ata_irq_on(ap);
+
+	/* is double-select really necessary? */
+	if (ap->device[1].class != ATA_DEV_NONE)
+		ap->ops->dev_select(ap, 1);
+	if (ap->device[0].class != ATA_DEV_NONE)
+		ap->ops->dev_select(ap, 0);
+
+	/* if no devices were detected, disable this port */
+	if ((ap->device[0].class == ATA_DEV_NONE) &&
+	    (ap->device[1].class == ATA_DEV_NONE))
+		goto err_out;
+
+	if (ap->flags & (ATA_FLAG_SATA_RESET | ATA_FLAG_SRST)) {
+		/* set up device control for ATA_FLAG_SATA_RESET */
+		if (ap->flags & ATA_FLAG_MMIO)
+			writeb(ap->ctl, (void __iomem *) ioaddr->ctl_addr);
+		else
+			outb(ap->ctl, ioaddr->ctl_addr);
+	}
+
+	DPRINTK("EXIT\n");
+	return;
+
+err_out:
+	printk(KERN_ERR "ata%u: disabling port\n", ap->id);
+	ap->ops->port_disable(ap);
+
+	DPRINTK("EXIT\n");
+}
+
+static void ata_pr_blacklisted(const struct ata_port *ap,
+			       const struct ata_device *dev)
+{
+	printk(KERN_WARNING "ata%u: dev %u is on DMA blacklist, disabling DMA\n",
+		ap->id, dev->devno);
+}
+
+static const char * ata_dma_blacklist [] = {
+	"WDC AC11000H",
+	"WDC AC22100H",
+	"WDC AC32500H",
+	"WDC AC33100H",
+	"WDC AC31600H",
+	"WDC AC32100H",
+	"WDC AC23200L",
+	"Compaq CRD-8241B",
+	"CRD-8400B",
+	"CRD-8480B",
+	"CRD-8482B",
+ 	"CRD-84",
+	"SanDisk SDP3B",
+	"SanDisk SDP3B-64",
+	"SANYO CD-ROM CRD",
+	"HITACHI CDR-8",
+	"HITACHI CDR-8335",
+	"HITACHI CDR-8435",
+	"Toshiba CD-ROM XM-6202B",
+	"TOSHIBA CD-ROM XM-1702BC",
+	"CD-532E-A",
+	"E-IDE CD-ROM CR-840",
+	"CD-ROM Drive/F5A",
+	"WPI CDD-820",
+	"SAMSUNG CD-ROM SC-148C",
+	"SAMSUNG CD-ROM SC",
+	"SanDisk SDP3B-64",
+	"ATAPI CD-ROM DRIVE 40X MAXIMUM",
+	"_NEC DV5800A",
+};
+
+static int ata_dma_blacklisted(const struct ata_device *dev)
+{
+	unsigned char model_num[40];
+	char *s;
+	unsigned int len;
+	int i;
+
+	ata_dev_id_string(dev->id, model_num, ATA_ID_PROD_OFS,
+			  sizeof(model_num));
+	s = &model_num[0];
+	len = strnlen(s, sizeof(model_num));
+
+	/* ATAPI specifies that empty space is blank-filled; remove blanks */
+	while ((len > 0) && (s[len - 1] == ' ')) {
+		len--;
+		s[len] = 0;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(ata_dma_blacklist); i++)
+		if (!strncmp(ata_dma_blacklist[i], s, len))
+			return 1;
+
+	return 0;
+}
+
+static unsigned int ata_get_mode_mask(const struct ata_port *ap, int shift)
+{
+	const struct ata_device *master, *slave;
+	unsigned int mask;
+
+	master = &ap->device[0];
+	slave = &ap->device[1];
+
+	assert (ata_dev_present(master) || ata_dev_present(slave));
+
+	if (shift == ATA_SHIFT_UDMA) {
+		mask = ap->udma_mask;
+		if (ata_dev_present(master)) {
+			mask &= (master->id[ATA_ID_UDMA_MODES] & 0xff);
+			if (ata_dma_blacklisted(master)) {
+				mask = 0;
+				ata_pr_blacklisted(ap, master);
+			}
+		}
+		if (ata_dev_present(slave)) {
+			mask &= (slave->id[ATA_ID_UDMA_MODES] & 0xff);
+			if (ata_dma_blacklisted(slave)) {
+				mask = 0;
+				ata_pr_blacklisted(ap, slave);
+			}
+		}
+	}
+	else if (shift == ATA_SHIFT_MWDMA) {
+		mask = ap->mwdma_mask;
+		if (ata_dev_present(master)) {
+			mask &= (master->id[ATA_ID_MWDMA_MODES] & 0x07);
+			if (ata_dma_blacklisted(master)) {
+				mask = 0;
+				ata_pr_blacklisted(ap, master);
+			}
+		}
+		if (ata_dev_present(slave)) {
+			mask &= (slave->id[ATA_ID_MWDMA_MODES] & 0x07);
+			if (ata_dma_blacklisted(slave)) {
+				mask = 0;
+				ata_pr_blacklisted(ap, slave);
+			}
+		}
+	}
+	else if (shift == ATA_SHIFT_PIO) {
+		mask = ap->pio_mask;
+		if (ata_dev_present(master)) {
+			/* spec doesn't return explicit support for
+			 * PIO0-2, so we fake it
+			 */
+			u16 tmp_mode = master->id[ATA_ID_PIO_MODES] & 0x03;
+			tmp_mode <<= 3;
+			tmp_mode |= 0x7;
+			mask &= tmp_mode;
+		}
+		if (ata_dev_present(slave)) {
+			/* spec doesn't return explicit support for
+			 * PIO0-2, so we fake it
+			 */
+			u16 tmp_mode = slave->id[ATA_ID_PIO_MODES] & 0x03;
+			tmp_mode <<= 3;
+			tmp_mode |= 0x7;
+			mask &= tmp_mode;
+		}
+	}
+	else {
+		mask = 0xffffffff; /* shut up compiler warning */
+		BUG();
+	}
+
+	return mask;
+}
+
+/* find greatest bit */
+static int fgb(u32 bitmap)
+{
+	unsigned int i;
+	int x = -1;
+
+	for (i = 0; i < 32; i++)
+		if (bitmap & (1 << i))
+			x = i;
+
+	return x;
+}
+
+/**
+ *	ata_choose_xfer_mode - attempt to find best transfer mode
+ *	@ap: Port for which an xfer mode will be selected
+ *	@xfer_mode_out: (output) SET FEATURES - XFER MODE code
+ *	@xfer_shift_out: (output) bit shift that selects this mode
+ *
+ *	Based on host and device capabilities, determine the
+ *	maximum transfer mode that is amenable to all.
+ *
+ *	LOCKING:
+ *	PCI/etc. bus probe sem.
+ *
+ *	RETURNS:
+ *	Zero on success, negative on error.
+ */
+
+static int ata_choose_xfer_mode(const struct ata_port *ap,
+				u8 *xfer_mode_out,
+				unsigned int *xfer_shift_out)
+{
+	unsigned int mask, shift;
+	int x, i;
+
+	for (i = 0; i < ARRAY_SIZE(xfer_mode_classes); i++) {
+		shift = xfer_mode_classes[i].shift;
+		mask = ata_get_mode_mask(ap, shift);
+
+		x = fgb(mask);
+		if (x >= 0) {
+			*xfer_mode_out = xfer_mode_classes[i].base + x;
+			*xfer_shift_out = shift;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+/**
+ *	ata_dev_set_xfermode - Issue SET FEATURES - XFER MODE command
+ *	@ap: Port associated with device @dev
+ *	@dev: Device to which command will be sent
+ *
+ *	Issue SET FEATURES - XFER MODE command to device @dev
+ *	on port @ap.
+ *
+ *	LOCKING:
+ *	PCI/etc. bus probe sem.
+ */
+
+static void ata_dev_set_xfermode(struct ata_port *ap, struct ata_device *dev)
+{
+	DECLARE_COMPLETION(wait);
+	struct ata_queued_cmd *qc;
+	int rc;
+	unsigned long flags;
+
+	/* set up set-features taskfile */
+	DPRINTK("set features - xfer mode\n");
+
+	qc = ata_qc_new_init(ap, dev);
+	BUG_ON(qc == NULL);
+
+	qc->tf.command = ATA_CMD_SET_FEATURES;
+	qc->tf.feature = SETFEATURES_XFER;
+	qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+	qc->tf.protocol = ATA_PROT_NODATA;
+	qc->tf.nsect = dev->xfer_mode;
+
+	qc->waiting = &wait;
+	qc->complete_fn = ata_qc_complete_noop;
+
+	spin_lock_irqsave(&ap->host_set->lock, flags);
+	rc = ata_qc_issue(qc);
+	spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
+	if (rc)
+		ata_port_disable(ap);
+	else
+		ata_qc_wait_err(qc, &wait);
+
+	DPRINTK("EXIT\n");
+}
+
+/**
+ *	ata_dev_reread_id - Reread the device identify device info
+ *	@ap: port where the device is
+ *	@dev: device to reread the identify device info
+ *
+ *	LOCKING:
+ */
+
+static void ata_dev_reread_id(struct ata_port *ap, struct ata_device *dev)
+{
+	DECLARE_COMPLETION(wait);
+	struct ata_queued_cmd *qc;
+	unsigned long flags;
+	int rc;
+
+	qc = ata_qc_new_init(ap, dev);
+	BUG_ON(qc == NULL);
+
+	ata_sg_init_one(qc, dev->id, sizeof(dev->id));
+	qc->dma_dir = DMA_FROM_DEVICE;
+
+	if (dev->class == ATA_DEV_ATA) {
+		qc->tf.command = ATA_CMD_ID_ATA;
+		DPRINTK("do ATA identify\n");
+	} else {
+		qc->tf.command = ATA_CMD_ID_ATAPI;
+		DPRINTK("do ATAPI identify\n");
+	}
+
+	qc->tf.flags |= ATA_TFLAG_DEVICE;
+	qc->tf.protocol = ATA_PROT_PIO;
+	qc->nsect = 1;
+
+	qc->waiting = &wait;
+	qc->complete_fn = ata_qc_complete_noop;
+
+	spin_lock_irqsave(&ap->host_set->lock, flags);
+	rc = ata_qc_issue(qc);
+	spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
+	if (rc)
+		goto err_out;
+
+	ata_qc_wait_err(qc, &wait);
+
+	swap_buf_le16(dev->id, ATA_ID_WORDS);
+
+	ata_dump_id(dev);
+
+	DPRINTK("EXIT\n");
+
+	return;
+err_out:
+	ata_port_disable(ap);
+}
+
+/**
+ *	ata_dev_init_params - Issue INIT DEV PARAMS command
+ *	@ap: Port associated with device @dev
+ *	@dev: Device to which command will be sent
+ *
+ *	LOCKING:
+ */
+
+static void ata_dev_init_params(struct ata_port *ap, struct ata_device *dev)
+{
+	DECLARE_COMPLETION(wait);
+	struct ata_queued_cmd *qc;
+	int rc;
+	unsigned long flags;
+	u16 sectors = dev->id[6];
+	u16 heads   = dev->id[3];
+
+	/* Number of sectors per track 1-255. Number of heads 1-16 */
+	if (sectors < 1 || sectors > 255 || heads < 1 || heads > 16)
+		return;
+
+	/* set up init dev params taskfile */
+	DPRINTK("init dev params \n");
+
+	qc = ata_qc_new_init(ap, dev);
+	BUG_ON(qc == NULL);
+
+	qc->tf.command = ATA_CMD_INIT_DEV_PARAMS;
+	qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+	qc->tf.protocol = ATA_PROT_NODATA;
+	qc->tf.nsect = sectors;
+	qc->tf.device |= (heads - 1) & 0x0f; /* max head = num. of heads - 1 */
+
+	qc->waiting = &wait;
+	qc->complete_fn = ata_qc_complete_noop;
+
+	spin_lock_irqsave(&ap->host_set->lock, flags);
+	rc = ata_qc_issue(qc);
+	spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
+	if (rc)
+		ata_port_disable(ap);
+	else
+		ata_qc_wait_err(qc, &wait);
+
+	DPRINTK("EXIT\n");
+}
+
+/**
+ *	ata_sg_clean - Unmap DMA memory associated with command
+ *	@qc: Command containing DMA memory to be released
+ *
+ *	Unmap all mapped DMA memory associated with this command.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+
+static void ata_sg_clean(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct scatterlist *sg = qc->__sg;
+	int dir = qc->dma_dir;
+	void *pad_buf = NULL;
+
+	assert(qc->flags & ATA_QCFLAG_DMAMAP);
+	assert(sg != NULL);
+
+	if (qc->flags & ATA_QCFLAG_SINGLE)
+		assert(qc->n_elem == 1);
+
+	VPRINTK("unmapping %u sg elements\n", qc->n_elem);
+
+	/* if we padded the buffer out to 32-bit bound, and data
+	 * xfer direction is from-device, we must copy from the
+	 * pad buffer back into the supplied buffer
+	 */
+	if (qc->pad_len && !(qc->tf.flags & ATA_TFLAG_WRITE))
+		pad_buf = ap->pad + (qc->tag * ATA_DMA_PAD_SZ);
+
+	if (qc->flags & ATA_QCFLAG_SG) {
+		if (qc->n_elem)
+			dma_unmap_sg(ap->host_set->dev, sg, qc->n_elem, dir);
+		/* restore last sg */
+		sg[qc->orig_n_elem - 1].length += qc->pad_len;
+		if (pad_buf) {
+			struct scatterlist *psg = &qc->pad_sgent;
+			void *addr = kmap_atomic(psg->page, KM_IRQ0);
+			memcpy(addr + psg->offset, pad_buf, qc->pad_len);
+			kunmap_atomic(addr, KM_IRQ0);
+		}
+	} else {
+		if (sg_dma_len(&sg[0]) > 0)
+			dma_unmap_single(ap->host_set->dev,
+				sg_dma_address(&sg[0]), sg_dma_len(&sg[0]),
+				dir);
+		/* restore sg */
+		sg->length += qc->pad_len;
+		if (pad_buf)
+			memcpy(qc->buf_virt + sg->length - qc->pad_len,
+			       pad_buf, qc->pad_len);
+	}
+
+	qc->flags &= ~ATA_QCFLAG_DMAMAP;
+	qc->__sg = NULL;
+}
+
+/**
+ *	ata_fill_sg - Fill PCI IDE PRD table
+ *	@qc: Metadata associated with taskfile to be transferred
+ *
+ *	Fill PCI IDE PRD (scatter-gather) table with segments
+ *	associated with the current disk command.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ *
+ */
+static void ata_fill_sg(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct scatterlist *sg;
+	unsigned int idx;
+
+	assert(qc->__sg != NULL);
+	assert(qc->n_elem > 0);
+
+	idx = 0;
+	ata_for_each_sg(sg, qc) {
+		u32 addr, offset;
+		u32 sg_len, len;
+
+		/* determine if physical DMA addr spans 64K boundary.
+		 * Note h/w doesn't support 64-bit, so we unconditionally
+		 * truncate dma_addr_t to u32.
+		 */
+		addr = (u32) sg_dma_address(sg);
+		sg_len = sg_dma_len(sg);
+
+		while (sg_len) {
+			offset = addr & 0xffff;
+			len = sg_len;
+			if ((offset + sg_len) > 0x10000)
+				len = 0x10000 - offset;
+
+			ap->prd[idx].addr = cpu_to_le32(addr);
+			ap->prd[idx].flags_len = cpu_to_le32(len & 0xffff);
+			VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr, len);
+
+			idx++;
+			sg_len -= len;
+			addr += len;
+		}
+	}
+
+	if (idx)
+		ap->prd[idx - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT);
+}
+/**
+ *	ata_check_atapi_dma - Check whether ATAPI DMA can be supported
+ *	@qc: Metadata associated with taskfile to check
+ *
+ *	Allow low-level driver to filter ATA PACKET commands, returning
+ *	a status indicating whether or not it is OK to use DMA for the
+ *	supplied PACKET command.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ *
+ *	RETURNS: 0 when ATAPI DMA can be used
+ *               nonzero otherwise
+ */
+int ata_check_atapi_dma(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	int rc = 0; /* Assume ATAPI DMA is OK by default */
+
+	if (ap->ops->check_atapi_dma)
+		rc = ap->ops->check_atapi_dma(qc);
+
+	return rc;
+}
+/**
+ *	ata_qc_prep - Prepare taskfile for submission
+ *	@qc: Metadata associated with taskfile to be prepared
+ *
+ *	Prepare ATA taskfile for submission.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+void ata_qc_prep(struct ata_queued_cmd *qc)
+{
+	if (!(qc->flags & ATA_QCFLAG_DMAMAP))
+		return;
+
+	ata_fill_sg(qc);
+}
+
+/**
+ *	ata_sg_init_one - Associate command with memory buffer
+ *	@qc: Command to be associated
+ *	@buf: Memory buffer
+ *	@buflen: Length of memory buffer, in bytes.
+ *
+ *	Initialize the data-related elements of queued_cmd @qc
+ *	to point to a single memory buffer, @buf of byte length @buflen.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+
+void ata_sg_init_one(struct ata_queued_cmd *qc, void *buf, unsigned int buflen)
+{
+	struct scatterlist *sg;
+
+	qc->flags |= ATA_QCFLAG_SINGLE;
+
+	memset(&qc->sgent, 0, sizeof(qc->sgent));
+	qc->__sg = &qc->sgent;
+	qc->n_elem = 1;
+	qc->orig_n_elem = 1;
+	qc->buf_virt = buf;
+
+	sg = qc->__sg;
+	sg_init_one(sg, buf, buflen);
+}
+
+/**
+ *	ata_sg_init - Associate command with scatter-gather table.
+ *	@qc: Command to be associated
+ *	@sg: Scatter-gather table.
+ *	@n_elem: Number of elements in s/g table.
+ *
+ *	Initialize the data-related elements of queued_cmd @qc
+ *	to point to a scatter-gather table @sg, containing @n_elem
+ *	elements.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+
+void ata_sg_init(struct ata_queued_cmd *qc, struct scatterlist *sg,
+		 unsigned int n_elem)
+{
+	qc->flags |= ATA_QCFLAG_SG;
+	qc->__sg = sg;
+	qc->n_elem = n_elem;
+	qc->orig_n_elem = n_elem;
+}
+
+/**
+ *	ata_sg_setup_one - DMA-map the memory buffer associated with a command.
+ *	@qc: Command with memory buffer to be mapped.
+ *
+ *	DMA-map the memory buffer associated with queued_cmd @qc.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ *
+ *	RETURNS:
+ *	Zero on success, negative on error.
+ */
+
+static int ata_sg_setup_one(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	int dir = qc->dma_dir;
+	struct scatterlist *sg = qc->__sg;
+	dma_addr_t dma_address;
+
+	/* we must lengthen transfers to end on a 32-bit boundary */
+	qc->pad_len = sg->length & 3;
+	if (qc->pad_len) {
+		void *pad_buf = ap->pad + (qc->tag * ATA_DMA_PAD_SZ);
+		struct scatterlist *psg = &qc->pad_sgent;
+
+		assert(qc->dev->class == ATA_DEV_ATAPI);
+
+		memset(pad_buf, 0, ATA_DMA_PAD_SZ);
+
+		if (qc->tf.flags & ATA_TFLAG_WRITE)
+			memcpy(pad_buf, qc->buf_virt + sg->length - qc->pad_len,
+			       qc->pad_len);
+
+		sg_dma_address(psg) = ap->pad_dma + (qc->tag * ATA_DMA_PAD_SZ);
+		sg_dma_len(psg) = ATA_DMA_PAD_SZ;
+		/* trim sg */
+		sg->length -= qc->pad_len;
+
+		DPRINTK("padding done, sg->length=%u pad_len=%u\n",
+			sg->length, qc->pad_len);
+	}
+
+	if (!sg->length) {
+		sg_dma_address(sg) = 0;
+		goto skip_map;
+	}
+
+	dma_address = dma_map_single(ap->host_set->dev, qc->buf_virt,
+				     sg->length, dir);
+	if (dma_mapping_error(dma_address)) {
+		/* restore sg */
+		sg->length += qc->pad_len;
+		return -1;
+	}
+
+	sg_dma_address(sg) = dma_address;
+skip_map:
+	sg_dma_len(sg) = sg->length;
+
+	DPRINTK("mapped buffer of %d bytes for %s\n", sg_dma_len(sg),
+		qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read");
+
+	return 0;
+}
+
+/**
+ *	ata_sg_setup - DMA-map the scatter-gather table associated with a command.
+ *	@qc: Command with scatter-gather table to be mapped.
+ *
+ *	DMA-map the scatter-gather table associated with queued_cmd @qc.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ *
+ *	RETURNS:
+ *	Zero on success, negative on error.
+ *
+ */
+
+static int ata_sg_setup(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct scatterlist *sg = qc->__sg;
+	struct scatterlist *lsg = &sg[qc->n_elem - 1];
+	int n_elem, pre_n_elem, dir, trim_sg = 0;
+
+	VPRINTK("ENTER, ata%u\n", ap->id);
+	assert(qc->flags & ATA_QCFLAG_SG);
+
+	/* we must lengthen transfers to end on a 32-bit boundary */
+	qc->pad_len = lsg->length & 3;
+	if (qc->pad_len) {
+		void *pad_buf = ap->pad + (qc->tag * ATA_DMA_PAD_SZ);
+		struct scatterlist *psg = &qc->pad_sgent;
+		unsigned int offset;
+
+		assert(qc->dev->class == ATA_DEV_ATAPI);
+
+		memset(pad_buf, 0, ATA_DMA_PAD_SZ);
+
+		/*
+		 * psg->page/offset are used to copy to-be-written
+		 * data in this function or read data in ata_sg_clean.
+		 */
+		offset = lsg->offset + lsg->length - qc->pad_len;
+		psg->page = nth_page(lsg->page, offset >> PAGE_SHIFT);
+		psg->offset = offset_in_page(offset);
+
+		if (qc->tf.flags & ATA_TFLAG_WRITE) {
+			void *addr = kmap_atomic(psg->page, KM_IRQ0);
+			memcpy(pad_buf, addr + psg->offset, qc->pad_len);
+			kunmap_atomic(addr, KM_IRQ0);
+		}
+
+		sg_dma_address(psg) = ap->pad_dma + (qc->tag * ATA_DMA_PAD_SZ);
+		sg_dma_len(psg) = ATA_DMA_PAD_SZ;
+		/* trim last sg */
+		lsg->length -= qc->pad_len;
+		if (lsg->length == 0)
+			trim_sg = 1;
+
+		DPRINTK("padding done, sg[%d].length=%u pad_len=%u\n",
+			qc->n_elem - 1, lsg->length, qc->pad_len);
+	}
+
+	pre_n_elem = qc->n_elem;
+	if (trim_sg && pre_n_elem)
+		pre_n_elem--;
+
+	if (!pre_n_elem) {
+		n_elem = 0;
+		goto skip_map;
+	}
+
+	dir = qc->dma_dir;
+	n_elem = dma_map_sg(ap->host_set->dev, sg, pre_n_elem, dir);
+	if (n_elem < 1) {
+		/* restore last sg */
+		lsg->length += qc->pad_len;
+		return -1;
+	}
+
+	DPRINTK("%d sg elements mapped\n", n_elem);
+
+skip_map:
+	qc->n_elem = n_elem;
+
+	return 0;
+}
+
+/**
+ *	ata_poll_qc_complete - turn irq back on and finish qc
+ *	@qc: Command to complete
+ *	@err_mask: ATA status register content
+ *
+ *	LOCKING:
+ *	None.  (grabs host lock)
+ */
+
+void ata_poll_qc_complete(struct ata_queued_cmd *qc, unsigned int err_mask)
+{
+	struct ata_port *ap = qc->ap;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ap->host_set->lock, flags);
+	ap->flags &= ~ATA_FLAG_NOINTR;
+	ata_irq_on(ap);
+	ata_qc_complete(qc, err_mask);
+	spin_unlock_irqrestore(&ap->host_set->lock, flags);
+}
+
+/**
+ *	ata_pio_poll -
+ *	@ap: the target ata_port
+ *
+ *	LOCKING:
+ *	None.  (executing in kernel thread context)
+ *
+ *	RETURNS:
+ *	timeout value to use
+ */
+
+static unsigned long ata_pio_poll(struct ata_port *ap)
+{
+	u8 status;
+	unsigned int poll_state = HSM_ST_UNKNOWN;
+	unsigned int reg_state = HSM_ST_UNKNOWN;
+
+	switch (ap->hsm_task_state) {
+	case HSM_ST:
+	case HSM_ST_POLL:
+		poll_state = HSM_ST_POLL;
+		reg_state = HSM_ST;
+		break;
+	case HSM_ST_LAST:
+	case HSM_ST_LAST_POLL:
+		poll_state = HSM_ST_LAST_POLL;
+		reg_state = HSM_ST_LAST;
+		break;
+	default:
+		BUG();
+		break;
+	}
+
+	status = ata_chk_status(ap);
+	if (status & ATA_BUSY) {
+		if (time_after(jiffies, ap->pio_task_timeout)) {
+			ap->hsm_task_state = HSM_ST_TMOUT;
+			return 0;
+		}
+		ap->hsm_task_state = poll_state;
+		return ATA_SHORT_PAUSE;
+	}
+
+	ap->hsm_task_state = reg_state;
+	return 0;
+}
+
+/**
+ *	ata_pio_complete - check if drive is busy or idle
+ *	@ap: the target ata_port
+ *
+ *	LOCKING:
+ *	None.  (executing in kernel thread context)
+ *
+ *	RETURNS:
+ *	Non-zero if qc completed, zero otherwise.
+ */
+
+static int ata_pio_complete (struct ata_port *ap)
+{
+	struct ata_queued_cmd *qc;
+	u8 drv_stat;
+
+	/*
+	 * This is purely heuristic.  This is a fast path.  Sometimes when
+	 * we enter, BSY will be cleared in a chk-status or two.  If not,
+	 * the drive is probably seeking or something.  Snooze for a couple
+	 * msecs, then chk-status again.  If still busy, fall back to
+	 * HSM_ST_POLL state.
+	 */
+	drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 10);
+	if (drv_stat & (ATA_BUSY | ATA_DRQ)) {
+		msleep(2);
+		drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 10);
+		if (drv_stat & (ATA_BUSY | ATA_DRQ)) {
+			ap->hsm_task_state = HSM_ST_LAST_POLL;
+			ap->pio_task_timeout = jiffies + ATA_TMOUT_PIO;
+			return 0;
+		}
+	}
+
+	drv_stat = ata_wait_idle(ap);
+	if (!ata_ok(drv_stat)) {
+		ap->hsm_task_state = HSM_ST_ERR;
+		return 0;
+	}
+
+	qc = ata_qc_from_tag(ap, ap->active_tag);
+	assert(qc != NULL);
+
+	ap->hsm_task_state = HSM_ST_IDLE;
+
+	ata_poll_qc_complete(qc, 0);
+
+	/* another command may start at this point */
+
+	return 1;
+}
+
+
+/**
+ *	swap_buf_le16 - swap halves of 16-words in place
+ *	@buf:  Buffer to swap
+ *	@buf_words:  Number of 16-bit words in buffer.
+ *
+ *	Swap halves of 16-bit words if needed to convert from
+ *	little-endian byte order to native cpu byte order, or
+ *	vice-versa.
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+void swap_buf_le16(u16 *buf, unsigned int buf_words)
+{
+#ifdef __BIG_ENDIAN
+	unsigned int i;
+
+	for (i = 0; i < buf_words; i++)
+		buf[i] = le16_to_cpu(buf[i]);
+#endif /* __BIG_ENDIAN */
+}
+
+/**
+ *	ata_mmio_data_xfer - Transfer data by MMIO
+ *	@ap: port to read/write
+ *	@buf: data buffer
+ *	@buflen: buffer length
+ *	@write_data: read/write
+ *
+ *	Transfer data from/to the device data register by MMIO.
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+
+static void ata_mmio_data_xfer(struct ata_port *ap, unsigned char *buf,
+			       unsigned int buflen, int write_data)
+{
+	unsigned int i;
+	unsigned int words = buflen >> 1;
+	u16 *buf16 = (u16 *) buf;
+	void __iomem *mmio = (void __iomem *)ap->ioaddr.data_addr;
+
+	/* Transfer multiple of 2 bytes */
+	if (write_data) {
+		for (i = 0; i < words; i++)
+			writew(le16_to_cpu(buf16[i]), mmio);
+	} else {
+		for (i = 0; i < words; i++)
+			buf16[i] = cpu_to_le16(readw(mmio));
+	}
+
+	/* Transfer trailing 1 byte, if any. */
+	if (unlikely(buflen & 0x01)) {
+		u16 align_buf[1] = { 0 };
+		unsigned char *trailing_buf = buf + buflen - 1;
+
+		if (write_data) {
+			memcpy(align_buf, trailing_buf, 1);
+			writew(le16_to_cpu(align_buf[0]), mmio);
+		} else {
+			align_buf[0] = cpu_to_le16(readw(mmio));
+			memcpy(trailing_buf, align_buf, 1);
+		}
+	}
+}
+
+/**
+ *	ata_pio_data_xfer - Transfer data by PIO
+ *	@ap: port to read/write
+ *	@buf: data buffer
+ *	@buflen: buffer length
+ *	@write_data: read/write
+ *
+ *	Transfer data from/to the device data register by PIO.
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+
+static void ata_pio_data_xfer(struct ata_port *ap, unsigned char *buf,
+			      unsigned int buflen, int write_data)
+{
+	unsigned int words = buflen >> 1;
+
+	/* Transfer multiple of 2 bytes */
+	if (write_data)
+		outsw(ap->ioaddr.data_addr, buf, words);
+	else
+		insw(ap->ioaddr.data_addr, buf, words);
+
+	/* Transfer trailing 1 byte, if any. */
+	if (unlikely(buflen & 0x01)) {
+		u16 align_buf[1] = { 0 };
+		unsigned char *trailing_buf = buf + buflen - 1;
+
+		if (write_data) {
+			memcpy(align_buf, trailing_buf, 1);
+			outw(le16_to_cpu(align_buf[0]), ap->ioaddr.data_addr);
+		} else {
+			align_buf[0] = cpu_to_le16(inw(ap->ioaddr.data_addr));
+			memcpy(trailing_buf, align_buf, 1);
+		}
+	}
+}
+
+/**
+ *	ata_data_xfer - Transfer data from/to the data register.
+ *	@ap: port to read/write
+ *	@buf: data buffer
+ *	@buflen: buffer length
+ *	@do_write: read/write
+ *
+ *	Transfer data from/to the device data register.
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+
+static void ata_data_xfer(struct ata_port *ap, unsigned char *buf,
+			  unsigned int buflen, int do_write)
+{
+	if (ap->flags & ATA_FLAG_MMIO)
+		ata_mmio_data_xfer(ap, buf, buflen, do_write);
+	else
+		ata_pio_data_xfer(ap, buf, buflen, do_write);
+}
+
+/**
+ *	ata_pio_sector - Transfer ATA_SECT_SIZE (512 bytes) of data.
+ *	@qc: Command on going
+ *
+ *	Transfer ATA_SECT_SIZE of data from/to the ATA device.
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+
+static void ata_pio_sector(struct ata_queued_cmd *qc)
+{
+	int do_write = (qc->tf.flags & ATA_TFLAG_WRITE);
+	struct scatterlist *sg = qc->__sg;
+	struct ata_port *ap = qc->ap;
+	struct page *page;
+	unsigned int offset;
+	unsigned char *buf;
+
+	if (qc->cursect == (qc->nsect - 1))
+		ap->hsm_task_state = HSM_ST_LAST;
+
+	page = sg[qc->cursg].page;
+	offset = sg[qc->cursg].offset + qc->cursg_ofs * ATA_SECT_SIZE;
+
+	/* get the current page and offset */
+	page = nth_page(page, (offset >> PAGE_SHIFT));
+	offset %= PAGE_SIZE;
+
+	buf = kmap(page) + offset;
+
+	qc->cursect++;
+	qc->cursg_ofs++;
+
+	if ((qc->cursg_ofs * ATA_SECT_SIZE) == (&sg[qc->cursg])->length) {
+		qc->cursg++;
+		qc->cursg_ofs = 0;
+	}
+
+	DPRINTK("data %s\n", qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read");
+
+	/* do the actual data transfer */
+	do_write = (qc->tf.flags & ATA_TFLAG_WRITE);
+	ata_data_xfer(ap, buf, ATA_SECT_SIZE, do_write);
+
+	kunmap(page);
+}
+
+/**
+ *	__atapi_pio_bytes - Transfer data from/to the ATAPI device.
+ *	@qc: Command on going
+ *	@bytes: number of bytes
+ *
+ *	Transfer Transfer data from/to the ATAPI device.
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ *
+ */
+
+static void __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes)
+{
+	int do_write = (qc->tf.flags & ATA_TFLAG_WRITE);
+	struct scatterlist *sg = qc->__sg;
+	struct ata_port *ap = qc->ap;
+	struct page *page;
+	unsigned char *buf;
+	unsigned int offset, count;
+
+	if (qc->curbytes + bytes >= qc->nbytes)
+		ap->hsm_task_state = HSM_ST_LAST;
+
+next_sg:
+	if (unlikely(qc->cursg >= qc->n_elem)) {
+		/*
+		 * The end of qc->sg is reached and the device expects
+		 * more data to transfer. In order not to overrun qc->sg
+		 * and fulfill length specified in the byte count register,
+		 *    - for read case, discard trailing data from the device
+		 *    - for write case, padding zero data to the device
+		 */
+		u16 pad_buf[1] = { 0 };
+		unsigned int words = bytes >> 1;
+		unsigned int i;
+
+		if (words) /* warning if bytes > 1 */
+			printk(KERN_WARNING "ata%u: %u bytes trailing data\n",
+			       ap->id, bytes);
+
+		for (i = 0; i < words; i++)
+			ata_data_xfer(ap, (unsigned char*)pad_buf, 2, do_write);
+
+		ap->hsm_task_state = HSM_ST_LAST;
+		return;
+	}
+
+	sg = &qc->__sg[qc->cursg];
+
+	page = sg->page;
+	offset = sg->offset + qc->cursg_ofs;
+
+	/* get the current page and offset */
+	page = nth_page(page, (offset >> PAGE_SHIFT));
+	offset %= PAGE_SIZE;
+
+	/* don't overrun current sg */
+	count = min(sg->length - qc->cursg_ofs, bytes);
+
+	/* don't cross page boundaries */
+	count = min(count, (unsigned int)PAGE_SIZE - offset);
+
+	buf = kmap(page) + offset;
+
+	bytes -= count;
+	qc->curbytes += count;
+	qc->cursg_ofs += count;
+
+	if (qc->cursg_ofs == sg->length) {
+		qc->cursg++;
+		qc->cursg_ofs = 0;
+	}
+
+	DPRINTK("data %s\n", qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read");
+
+	/* do the actual data transfer */
+	ata_data_xfer(ap, buf, count, do_write);
+
+	kunmap(page);
+
+	if (bytes)
+		goto next_sg;
+}
+
+/**
+ *	atapi_pio_bytes - Transfer data from/to the ATAPI device.
+ *	@qc: Command on going
+ *
+ *	Transfer Transfer data from/to the ATAPI device.
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+
+static void atapi_pio_bytes(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct ata_device *dev = qc->dev;
+	unsigned int ireason, bc_lo, bc_hi, bytes;
+	int i_write, do_write = (qc->tf.flags & ATA_TFLAG_WRITE) ? 1 : 0;
+
+	ap->ops->tf_read(ap, &qc->tf);
+	ireason = qc->tf.nsect;
+	bc_lo = qc->tf.lbam;
+	bc_hi = qc->tf.lbah;
+	bytes = (bc_hi << 8) | bc_lo;
+
+	/* shall be cleared to zero, indicating xfer of data */
+	if (ireason & (1 << 0))
+		goto err_out;
+
+	/* make sure transfer direction matches expected */
+	i_write = ((ireason & (1 << 1)) == 0) ? 1 : 0;
+	if (do_write != i_write)
+		goto err_out;
+
+	__atapi_pio_bytes(qc, bytes);
+
+	return;
+
+err_out:
+	printk(KERN_INFO "ata%u: dev %u: ATAPI check failed\n",
+	      ap->id, dev->devno);
+	ap->hsm_task_state = HSM_ST_ERR;
+}
+
+/**
+ *	ata_pio_block - start PIO on a block
+ *	@ap: the target ata_port
+ *
+ *	LOCKING:
+ *	None.  (executing in kernel thread context)
+ */
+
+static void ata_pio_block(struct ata_port *ap)
+{
+	struct ata_queued_cmd *qc;
+	u8 status;
+
+	/*
+	 * This is purely heuristic.  This is a fast path.
+	 * Sometimes when we enter, BSY will be cleared in
+	 * a chk-status or two.  If not, the drive is probably seeking
+	 * or something.  Snooze for a couple msecs, then
+	 * chk-status again.  If still busy, fall back to
+	 * HSM_ST_POLL state.
+	 */
+	status = ata_busy_wait(ap, ATA_BUSY, 5);
+	if (status & ATA_BUSY) {
+		msleep(2);
+		status = ata_busy_wait(ap, ATA_BUSY, 10);
+		if (status & ATA_BUSY) {
+			ap->hsm_task_state = HSM_ST_POLL;
+			ap->pio_task_timeout = jiffies + ATA_TMOUT_PIO;
+			return;
+		}
+	}
+
+	qc = ata_qc_from_tag(ap, ap->active_tag);
+	assert(qc != NULL);
+
+	if (is_atapi_taskfile(&qc->tf)) {
+		/* no more data to transfer or unsupported ATAPI command */
+		if ((status & ATA_DRQ) == 0) {
+			ap->hsm_task_state = HSM_ST_LAST;
+			return;
+		}
+
+		atapi_pio_bytes(qc);
+	} else {
+		/* handle BSY=0, DRQ=0 as error */
+		if ((status & ATA_DRQ) == 0) {
+			ap->hsm_task_state = HSM_ST_ERR;
+			return;
+		}
+
+		ata_pio_sector(qc);
+	}
+}
+
+static void ata_pio_error(struct ata_port *ap)
+{
+	struct ata_queued_cmd *qc;
+
+	printk(KERN_WARNING "ata%u: PIO error\n", ap->id);
+
+	qc = ata_qc_from_tag(ap, ap->active_tag);
+	assert(qc != NULL);
+
+	ap->hsm_task_state = HSM_ST_IDLE;
+
+	ata_poll_qc_complete(qc, AC_ERR_ATA_BUS);
+}
+
+static void ata_pio_task(void *_data)
+{
+	struct ata_port *ap = _data;
+	unsigned long timeout;
+	int qc_completed;
+
+fsm_start:
+	timeout = 0;
+	qc_completed = 0;
+
+	switch (ap->hsm_task_state) {
+	case HSM_ST_IDLE:
+		return;
+
+	case HSM_ST:
+		ata_pio_block(ap);
+		break;
+
+	case HSM_ST_LAST:
+		qc_completed = ata_pio_complete(ap);
+		break;
+
+	case HSM_ST_POLL:
+	case HSM_ST_LAST_POLL:
+		timeout = ata_pio_poll(ap);
+		break;
+
+	case HSM_ST_TMOUT:
+	case HSM_ST_ERR:
+		ata_pio_error(ap);
+		return;
+	}
+
+	if (timeout)
+		queue_delayed_work(ata_wq, &ap->pio_task, timeout);
+	else if (!qc_completed)
+		goto fsm_start;
+}
+
+/**
+ *	ata_qc_timeout - Handle timeout of queued command
+ *	@qc: Command that timed out
+ *
+ *	Some part of the kernel (currently, only the SCSI layer)
+ *	has noticed that the active command on port @ap has not
+ *	completed after a specified length of time.  Handle this
+ *	condition by disabling DMA (if necessary) and completing
+ *	transactions, with error if necessary.
+ *
+ *	This also handles the case of the "lost interrupt", where
+ *	for some reason (possibly hardware bug, possibly driver bug)
+ *	an interrupt was not delivered to the driver, even though the
+ *	transaction completed successfully.
+ *
+ *	LOCKING:
+ *	Inherited from SCSI layer (none, can sleep)
+ */
+
+static void ata_qc_timeout(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct ata_host_set *host_set = ap->host_set;
+	u8 host_stat = 0, drv_stat;
+	unsigned long flags;
+
+	DPRINTK("ENTER\n");
+
+	spin_lock_irqsave(&host_set->lock, flags);
+
+	/* hack alert!  We cannot use the supplied completion
+	 * function from inside the ->eh_strategy_handler() thread.
+	 * libata is the only user of ->eh_strategy_handler() in
+	 * any kernel, so the default scsi_done() assumes it is
+	 * not being called from the SCSI EH.
+	 */
+	qc->scsidone = scsi_finish_command;
+
+	switch (qc->tf.protocol) {
+
+	case ATA_PROT_DMA:
+	case ATA_PROT_ATAPI_DMA:
+		host_stat = ap->ops->bmdma_status(ap);
+
+		/* before we do anything else, clear DMA-Start bit */
+		ap->ops->bmdma_stop(qc);
+
+		/* fall through */
+
+	default:
+		ata_altstatus(ap);
+		drv_stat = ata_chk_status(ap);
+
+		/* ack bmdma irq events */
+		ap->ops->irq_clear(ap);
+
+		printk(KERN_ERR "ata%u: command 0x%x timeout, stat 0x%x host_stat 0x%x\n",
+		       ap->id, qc->tf.command, drv_stat, host_stat);
+
+		/* complete taskfile transaction */
+		ata_qc_complete(qc, ac_err_mask(drv_stat));
+		break;
+	}
+
+	spin_unlock_irqrestore(&host_set->lock, flags);
+
+	DPRINTK("EXIT\n");
+}
+
+/**
+ *	ata_eng_timeout - Handle timeout of queued command
+ *	@ap: Port on which timed-out command is active
+ *
+ *	Some part of the kernel (currently, only the SCSI layer)
+ *	has noticed that the active command on port @ap has not
+ *	completed after a specified length of time.  Handle this
+ *	condition by disabling DMA (if necessary) and completing
+ *	transactions, with error if necessary.
+ *
+ *	This also handles the case of the "lost interrupt", where
+ *	for some reason (possibly hardware bug, possibly driver bug)
+ *	an interrupt was not delivered to the driver, even though the
+ *	transaction completed successfully.
+ *
+ *	LOCKING:
+ *	Inherited from SCSI layer (none, can sleep)
+ */
+
+void ata_eng_timeout(struct ata_port *ap)
+{
+	struct ata_queued_cmd *qc;
+
+	DPRINTK("ENTER\n");
+
+	qc = ata_qc_from_tag(ap, ap->active_tag);
+	if (qc)
+		ata_qc_timeout(qc);
+	else {
+		printk(KERN_ERR "ata%u: BUG: timeout without command\n",
+		       ap->id);
+		goto out;
+	}
+
+out:
+	DPRINTK("EXIT\n");
+}
+
+/**
+ *	ata_qc_new - Request an available ATA command, for queueing
+ *	@ap: Port associated with device @dev
+ *	@dev: Device from whom we request an available command structure
+ *
+ *	LOCKING:
+ *	None.
+ */
+
+static struct ata_queued_cmd *ata_qc_new(struct ata_port *ap)
+{
+	struct ata_queued_cmd *qc = NULL;
+	unsigned int i;
+
+	for (i = 0; i < ATA_MAX_QUEUE; i++)
+		if (!test_and_set_bit(i, &ap->qactive)) {
+			qc = ata_qc_from_tag(ap, i);
+			break;
+		}
+
+	if (qc)
+		qc->tag = i;
+
+	return qc;
+}
+
+/**
+ *	ata_qc_new_init - Request an available ATA command, and initialize it
+ *	@ap: Port associated with device @dev
+ *	@dev: Device from whom we request an available command structure
+ *
+ *	LOCKING:
+ *	None.
+ */
+
+struct ata_queued_cmd *ata_qc_new_init(struct ata_port *ap,
+				      struct ata_device *dev)
+{
+	struct ata_queued_cmd *qc;
+
+	qc = ata_qc_new(ap);
+	if (qc) {
+		qc->scsicmd = NULL;
+		qc->ap = ap;
+		qc->dev = dev;
+
+		ata_qc_reinit(qc);
+	}
+
+	return qc;
+}
+
+int ata_qc_complete_noop(struct ata_queued_cmd *qc, unsigned int err_mask)
+{
+	return 0;
+}
+
+static void __ata_qc_complete(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	unsigned int tag, do_clear = 0;
+
+	qc->flags = 0;
+	tag = qc->tag;
+	if (likely(ata_tag_valid(tag))) {
+		if (tag == ap->active_tag)
+			ap->active_tag = ATA_TAG_POISON;
+		qc->tag = ATA_TAG_POISON;
+		do_clear = 1;
+	}
+
+	if (qc->waiting) {
+		struct completion *waiting = qc->waiting;
+		qc->waiting = NULL;
+		complete(waiting);
+	}
+
+	if (likely(do_clear))
+		clear_bit(tag, &ap->qactive);
+}
+
+/**
+ *	ata_qc_free - free unused ata_queued_cmd
+ *	@qc: Command to complete
+ *
+ *	Designed to free unused ata_queued_cmd object
+ *	in case something prevents using it.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+void ata_qc_free(struct ata_queued_cmd *qc)
+{
+	assert(qc != NULL);	/* ata_qc_from_tag _might_ return NULL */
+	assert(qc->waiting == NULL);	/* nothing should be waiting */
+
+	__ata_qc_complete(qc);
+}
+
+/**
+ *	ata_qc_complete - Complete an active ATA command
+ *	@qc: Command to complete
+ *	@err_mask: ATA Status register contents
+ *
+ *	Indicate to the mid and upper layers that an ATA
+ *	command has completed, with either an ok or not-ok status.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+
+void ata_qc_complete(struct ata_queued_cmd *qc, unsigned int err_mask)
+{
+	int rc;
+
+	assert(qc != NULL);	/* ata_qc_from_tag _might_ return NULL */
+	assert(qc->flags & ATA_QCFLAG_ACTIVE);
+
+	if (likely(qc->flags & ATA_QCFLAG_DMAMAP))
+		ata_sg_clean(qc);
+
+	/* atapi: mark qc as inactive to prevent the interrupt handler
+	 * from completing the command twice later, before the error handler
+	 * is called. (when rc != 0 and atapi request sense is needed)
+	 */
+	qc->flags &= ~ATA_QCFLAG_ACTIVE;
+
+	/* call completion callback */
+	rc = qc->complete_fn(qc, err_mask);
+
+	/* if callback indicates not to complete command (non-zero),
+	 * return immediately
+	 */
+	if (rc != 0)
+		return;
+
+	__ata_qc_complete(qc);
+
+	VPRINTK("EXIT\n");
+}
+
+static inline int ata_should_dma_map(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+
+	switch (qc->tf.protocol) {
+	case ATA_PROT_DMA:
+	case ATA_PROT_ATAPI_DMA:
+		return 1;
+
+	case ATA_PROT_ATAPI:
+	case ATA_PROT_PIO:
+	case ATA_PROT_PIO_MULT:
+		if (ap->flags & ATA_FLAG_PIO_DMA)
+			return 1;
+
+		/* fall through */
+
+	default:
+		return 0;
+	}
+
+	/* never reached */
+}
+
+/**
+ *	ata_qc_issue - issue taskfile to device
+ *	@qc: command to issue to device
+ *
+ *	Prepare an ATA command to submission to device.
+ *	This includes mapping the data into a DMA-able
+ *	area, filling in the S/G table, and finally
+ *	writing the taskfile to hardware, starting the command.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ *
+ *	RETURNS:
+ *	Zero on success, negative on error.
+ */
+
+int ata_qc_issue(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+
+	if (ata_should_dma_map(qc)) {
+		if (qc->flags & ATA_QCFLAG_SG) {
+			if (ata_sg_setup(qc))
+				goto err_out;
+		} else if (qc->flags & ATA_QCFLAG_SINGLE) {
+			if (ata_sg_setup_one(qc))
+				goto err_out;
+		}
+	} else {
+		qc->flags &= ~ATA_QCFLAG_DMAMAP;
+	}
+
+	ap->ops->qc_prep(qc);
+
+	qc->ap->active_tag = qc->tag;
+	qc->flags |= ATA_QCFLAG_ACTIVE;
+
+	return ap->ops->qc_issue(qc);
+
+err_out:
+	return -1;
+}
+
+
+/**
+ *	ata_qc_issue_prot - issue taskfile to device in proto-dependent manner
+ *	@qc: command to issue to device
+ *
+ *	Using various libata functions and hooks, this function
+ *	starts an ATA command.  ATA commands are grouped into
+ *	classes called "protocols", and issuing each type of protocol
+ *	is slightly different.
+ *
+ *	May be used as the qc_issue() entry in ata_port_operations.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ *
+ *	RETURNS:
+ *	Zero on success, negative on error.
+ */
+
+int ata_qc_issue_prot(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+
+	ata_dev_select(ap, qc->dev->devno, 1, 0);
+
+	switch (qc->tf.protocol) {
+	case ATA_PROT_NODATA:
+		ata_tf_to_host(ap, &qc->tf);
+		break;
+
+	case ATA_PROT_DMA:
+		ap->ops->tf_load(ap, &qc->tf);	 /* load tf registers */
+		ap->ops->bmdma_setup(qc);	    /* set up bmdma */
+		ap->ops->bmdma_start(qc);	    /* initiate bmdma */
+		break;
+
+	case ATA_PROT_PIO: /* load tf registers, initiate polling pio */
+		ata_qc_set_polling(qc);
+		ata_tf_to_host(ap, &qc->tf);
+		ap->hsm_task_state = HSM_ST;
+		queue_work(ata_wq, &ap->pio_task);
+		break;
+
+	case ATA_PROT_ATAPI:
+		ata_qc_set_polling(qc);
+		ata_tf_to_host(ap, &qc->tf);
+		queue_work(ata_wq, &ap->packet_task);
+		break;
+
+	case ATA_PROT_ATAPI_NODATA:
+		ap->flags |= ATA_FLAG_NOINTR;
+		ata_tf_to_host(ap, &qc->tf);
+		queue_work(ata_wq, &ap->packet_task);
+		break;
+
+	case ATA_PROT_ATAPI_DMA:
+		ap->flags |= ATA_FLAG_NOINTR;
+		ap->ops->tf_load(ap, &qc->tf);	 /* load tf registers */
+		ap->ops->bmdma_setup(qc);	    /* set up bmdma */
+		queue_work(ata_wq, &ap->packet_task);
+		break;
+
+	default:
+		WARN_ON(1);
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ *	ata_bmdma_setup_mmio - Set up PCI IDE BMDMA transaction
+ *	@qc: Info associated with this ATA transaction.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+
+static void ata_bmdma_setup_mmio (struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE);
+	u8 dmactl;
+	void __iomem *mmio = (void __iomem *) ap->ioaddr.bmdma_addr;
+
+	/* load PRD table addr. */
+	mb();	/* make sure PRD table writes are visible to controller */
+	writel(ap->prd_dma, mmio + ATA_DMA_TABLE_OFS);
+
+	/* specify data direction, triple-check start bit is clear */
+	dmactl = readb(mmio + ATA_DMA_CMD);
+	dmactl &= ~(ATA_DMA_WR | ATA_DMA_START);
+	if (!rw)
+		dmactl |= ATA_DMA_WR;
+	writeb(dmactl, mmio + ATA_DMA_CMD);
+
+	/* issue r/w command */
+	ap->ops->exec_command(ap, &qc->tf);
+}
+
+/**
+ *	ata_bmdma_start_mmio - Start a PCI IDE BMDMA transaction
+ *	@qc: Info associated with this ATA transaction.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+
+static void ata_bmdma_start_mmio (struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	void __iomem *mmio = (void __iomem *) ap->ioaddr.bmdma_addr;
+	u8 dmactl;
+
+	/* start host DMA transaction */
+	dmactl = readb(mmio + ATA_DMA_CMD);
+	writeb(dmactl | ATA_DMA_START, mmio + ATA_DMA_CMD);
+
+	/* Strictly, one may wish to issue a readb() here, to
+	 * flush the mmio write.  However, control also passes
+	 * to the hardware at this point, and it will interrupt
+	 * us when we are to resume control.  So, in effect,
+	 * we don't care when the mmio write flushes.
+	 * Further, a read of the DMA status register _immediately_
+	 * following the write may not be what certain flaky hardware
+	 * is expected, so I think it is best to not add a readb()
+	 * without first all the MMIO ATA cards/mobos.
+	 * Or maybe I'm just being paranoid.
+	 */
+}
+
+/**
+ *	ata_bmdma_setup_pio - Set up PCI IDE BMDMA transaction (PIO)
+ *	@qc: Info associated with this ATA transaction.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+
+static void ata_bmdma_setup_pio (struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE);
+	u8 dmactl;
+
+	/* load PRD table addr. */
+	outl(ap->prd_dma, ap->ioaddr.bmdma_addr + ATA_DMA_TABLE_OFS);
+
+	/* specify data direction, triple-check start bit is clear */
+	dmactl = inb(ap->ioaddr.bmdma_addr + ATA_DMA_CMD);
+	dmactl &= ~(ATA_DMA_WR | ATA_DMA_START);
+	if (!rw)
+		dmactl |= ATA_DMA_WR;
+	outb(dmactl, ap->ioaddr.bmdma_addr + ATA_DMA_CMD);
+
+	/* issue r/w command */
+	ap->ops->exec_command(ap, &qc->tf);
+}
+
+/**
+ *	ata_bmdma_start_pio - Start a PCI IDE BMDMA transaction (PIO)
+ *	@qc: Info associated with this ATA transaction.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+
+static void ata_bmdma_start_pio (struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	u8 dmactl;
+
+	/* start host DMA transaction */
+	dmactl = inb(ap->ioaddr.bmdma_addr + ATA_DMA_CMD);
+	outb(dmactl | ATA_DMA_START,
+	     ap->ioaddr.bmdma_addr + ATA_DMA_CMD);
+}
+
+
+/**
+ *	ata_bmdma_start - Start a PCI IDE BMDMA transaction
+ *	@qc: Info associated with this ATA transaction.
+ *
+ *	Writes the ATA_DMA_START flag to the DMA command register.
+ *
+ *	May be used as the bmdma_start() entry in ata_port_operations.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+void ata_bmdma_start(struct ata_queued_cmd *qc)
+{
+	if (qc->ap->flags & ATA_FLAG_MMIO)
+		ata_bmdma_start_mmio(qc);
+	else
+		ata_bmdma_start_pio(qc);
+}
+
+
+/**
+ *	ata_bmdma_setup - Set up PCI IDE BMDMA transaction
+ *	@qc: Info associated with this ATA transaction.
+ *
+ *	Writes address of PRD table to device's PRD Table Address
+ *	register, sets the DMA control register, and calls
+ *	ops->exec_command() to start the transfer.
+ *
+ *	May be used as the bmdma_setup() entry in ata_port_operations.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+void ata_bmdma_setup(struct ata_queued_cmd *qc)
+{
+	if (qc->ap->flags & ATA_FLAG_MMIO)
+		ata_bmdma_setup_mmio(qc);
+	else
+		ata_bmdma_setup_pio(qc);
+}
+
+
+/**
+ *	ata_bmdma_irq_clear - Clear PCI IDE BMDMA interrupt.
+ *	@ap: Port associated with this ATA transaction.
+ *
+ *	Clear interrupt and error flags in DMA status register.
+ *
+ *	May be used as the irq_clear() entry in ata_port_operations.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+
+void ata_bmdma_irq_clear(struct ata_port *ap)
+{
+    if (ap->flags & ATA_FLAG_MMIO) {
+        void __iomem *mmio = ((void __iomem *) ap->ioaddr.bmdma_addr) + ATA_DMA_STATUS;
+        writeb(readb(mmio), mmio);
+    } else {
+        unsigned long addr = ap->ioaddr.bmdma_addr + ATA_DMA_STATUS;
+        outb(inb(addr), addr);
+    }
+
+}
+
+
+/**
+ *	ata_bmdma_status - Read PCI IDE BMDMA status
+ *	@ap: Port associated with this ATA transaction.
+ *
+ *	Read and return BMDMA status register.
+ *
+ *	May be used as the bmdma_status() entry in ata_port_operations.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+
+u8 ata_bmdma_status(struct ata_port *ap)
+{
+	u8 host_stat;
+	if (ap->flags & ATA_FLAG_MMIO) {
+		void __iomem *mmio = (void __iomem *) ap->ioaddr.bmdma_addr;
+		host_stat = readb(mmio + ATA_DMA_STATUS);
+	} else
+		host_stat = inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS);
+	return host_stat;
+}
+
+
+/**
+ *	ata_bmdma_stop - Stop PCI IDE BMDMA transfer
+ *	@qc: Command we are ending DMA for
+ *
+ *	Clears the ATA_DMA_START flag in the dma control register
+ *
+ *	May be used as the bmdma_stop() entry in ata_port_operations.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+
+void ata_bmdma_stop(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	if (ap->flags & ATA_FLAG_MMIO) {
+		void __iomem *mmio = (void __iomem *) ap->ioaddr.bmdma_addr;
+
+		/* clear start/stop bit */
+		writeb(readb(mmio + ATA_DMA_CMD) & ~ATA_DMA_START,
+			mmio + ATA_DMA_CMD);
+	} else {
+		/* clear start/stop bit */
+		outb(inb(ap->ioaddr.bmdma_addr + ATA_DMA_CMD) & ~ATA_DMA_START,
+			ap->ioaddr.bmdma_addr + ATA_DMA_CMD);
+	}
+
+	/* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */
+	ata_altstatus(ap);        /* dummy read */
+}
+
+/**
+ *	ata_host_intr - Handle host interrupt for given (port, task)
+ *	@ap: Port on which interrupt arrived (possibly...)
+ *	@qc: Taskfile currently active in engine
+ *
+ *	Handle host interrupt for given queued command.  Currently,
+ *	only DMA interrupts are handled.  All other commands are
+ *	handled via polling with interrupts disabled (nIEN bit).
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ *
+ *	RETURNS:
+ *	One if interrupt was handled, zero if not (shared irq).
+ */
+
+inline unsigned int ata_host_intr (struct ata_port *ap,
+				   struct ata_queued_cmd *qc)
+{
+	u8 status, host_stat;
+
+	switch (qc->tf.protocol) {
+
+	case ATA_PROT_DMA:
+	case ATA_PROT_ATAPI_DMA:
+	case ATA_PROT_ATAPI:
+		/* check status of DMA engine */
+		host_stat = ap->ops->bmdma_status(ap);
+		VPRINTK("ata%u: host_stat 0x%X\n", ap->id, host_stat);
+
+		/* if it's not our irq... */
+		if (!(host_stat & ATA_DMA_INTR))
+			goto idle_irq;
+
+		/* before we do anything else, clear DMA-Start bit */
+		ap->ops->bmdma_stop(qc);
+
+		/* fall through */
+
+	case ATA_PROT_ATAPI_NODATA:
+	case ATA_PROT_NODATA:
+		/* check altstatus */
+		status = ata_altstatus(ap);
+		if (status & ATA_BUSY)
+			goto idle_irq;
+
+		/* check main status, clearing INTRQ */
+		status = ata_chk_status(ap);
+		if (unlikely(status & ATA_BUSY))
+			goto idle_irq;
+		DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n",
+			ap->id, qc->tf.protocol, status);
+
+		/* ack bmdma irq events */
+		ap->ops->irq_clear(ap);
+
+		/* complete taskfile transaction */
+		ata_qc_complete(qc, ac_err_mask(status));
+		break;
+
+	default:
+		goto idle_irq;
+	}
+
+	return 1;	/* irq handled */
+
+idle_irq:
+	ap->stats.idle_irq++;
+
+#ifdef ATA_IRQ_TRAP
+	if ((ap->stats.idle_irq % 1000) == 0) {
+		handled = 1;
+		ata_irq_ack(ap, 0); /* debug trap */
+		printk(KERN_WARNING "ata%d: irq trap\n", ap->id);
+	}
+#endif
+	return 0;	/* irq not handled */
+}
+
+/**
+ *	ata_interrupt - Default ATA host interrupt handler
+ *	@irq: irq line (unused)
+ *	@dev_instance: pointer to our ata_host_set information structure
+ *	@regs: unused
+ *
+ *	Default interrupt handler for PCI IDE devices.  Calls
+ *	ata_host_intr() for each port that is not disabled.
+ *
+ *	LOCKING:
+ *	Obtains host_set lock during operation.
+ *
+ *	RETURNS:
+ *	IRQ_NONE or IRQ_HANDLED.
+ */
+
+irqreturn_t ata_interrupt (int irq, void *dev_instance, struct pt_regs *regs)
+{
+	struct ata_host_set *host_set = dev_instance;
+	unsigned int i;
+	unsigned int handled = 0;
+	unsigned long flags;
+
+	/* TODO: make _irqsave conditional on x86 PCI IDE legacy mode */
+	spin_lock_irqsave(&host_set->lock, flags);
+
+	for (i = 0; i < host_set->n_ports; i++) {
+		struct ata_port *ap;
+
+		ap = host_set->ports[i];
+		if (ap &&
+		    !(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))) {
+			struct ata_queued_cmd *qc;
+
+			qc = ata_qc_from_tag(ap, ap->active_tag);
+			if (qc && (!(qc->tf.ctl & ATA_NIEN)) &&
+			    (qc->flags & ATA_QCFLAG_ACTIVE))
+				handled |= ata_host_intr(ap, qc);
+		}
+	}
+
+	spin_unlock_irqrestore(&host_set->lock, flags);
+
+	return IRQ_RETVAL(handled);
+}
+
+/**
+ *	atapi_packet_task - Write CDB bytes to hardware
+ *	@_data: Port to which ATAPI device is attached.
+ *
+ *	When device has indicated its readiness to accept
+ *	a CDB, this function is called.  Send the CDB.
+ *	If DMA is to be performed, exit immediately.
+ *	Otherwise, we are in polling mode, so poll
+ *	status under operation succeeds or fails.
+ *
+ *	LOCKING:
+ *	Kernel thread context (may sleep)
+ */
+
+static void atapi_packet_task(void *_data)
+{
+	struct ata_port *ap = _data;
+	struct ata_queued_cmd *qc;
+	u8 status;
+
+	qc = ata_qc_from_tag(ap, ap->active_tag);
+	assert(qc != NULL);
+	assert(qc->flags & ATA_QCFLAG_ACTIVE);
+
+	/* sleep-wait for BSY to clear */
+	DPRINTK("busy wait\n");
+	if (ata_busy_sleep(ap, ATA_TMOUT_CDB_QUICK, ATA_TMOUT_CDB))
+		goto err_out_status;
+
+	/* make sure DRQ is set */
+	status = ata_chk_status(ap);
+	if ((status & (ATA_BUSY | ATA_DRQ)) != ATA_DRQ)
+		goto err_out;
+
+	/* send SCSI cdb */
+	DPRINTK("send cdb\n");
+	assert(ap->cdb_len >= 12);
+
+	if (qc->tf.protocol == ATA_PROT_ATAPI_DMA ||
+	    qc->tf.protocol == ATA_PROT_ATAPI_NODATA) {
+		unsigned long flags;
+
+		/* Once we're done issuing command and kicking bmdma,
+		 * irq handler takes over.  To not lose irq, we need
+		 * to clear NOINTR flag before sending cdb, but
+		 * interrupt handler shouldn't be invoked before we're
+		 * finished.  Hence, the following locking.
+		 */
+		spin_lock_irqsave(&ap->host_set->lock, flags);
+		ap->flags &= ~ATA_FLAG_NOINTR;
+		ata_data_xfer(ap, qc->cdb, ap->cdb_len, 1);
+		if (qc->tf.protocol == ATA_PROT_ATAPI_DMA)
+			ap->ops->bmdma_start(qc);	/* initiate bmdma */
+		spin_unlock_irqrestore(&ap->host_set->lock, flags);
+	} else {
+		ata_data_xfer(ap, qc->cdb, ap->cdb_len, 1);
+
+		/* PIO commands are handled by polling */
+		ap->hsm_task_state = HSM_ST;
+		queue_work(ata_wq, &ap->pio_task);
+	}
+
+	return;
+
+err_out_status:
+	status = ata_chk_status(ap);
+err_out:
+	ata_poll_qc_complete(qc, __ac_err_mask(status));
+}
+
+
+/**
+ *	ata_port_start - Set port up for dma.
+ *	@ap: Port to initialize
+ *
+ *	Called just after data structures for each port are
+ *	initialized.  Allocates space for PRD table.
+ *
+ *	May be used as the port_start() entry in ata_port_operations.
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+
+int ata_port_start (struct ata_port *ap)
+{
+	struct device *dev = ap->host_set->dev;
+	int rc;
+
+	ap->prd = dma_alloc_coherent(dev, ATA_PRD_TBL_SZ, &ap->prd_dma, GFP_KERNEL);
+	if (!ap->prd)
+		return -ENOMEM;
+
+	rc = ata_pad_alloc(ap, dev);
+	if (rc) {
+		dma_free_coherent(dev, ATA_PRD_TBL_SZ, ap->prd, ap->prd_dma);
+		return rc;
+	}
+
+	DPRINTK("prd alloc, virt %p, dma %llx\n", ap->prd, (unsigned long long) ap->prd_dma);
+
+	return 0;
+}
+
+
+/**
+ *	ata_port_stop - Undo ata_port_start()
+ *	@ap: Port to shut down
+ *
+ *	Frees the PRD table.
+ *
+ *	May be used as the port_stop() entry in ata_port_operations.
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+
+void ata_port_stop (struct ata_port *ap)
+{
+	struct device *dev = ap->host_set->dev;
+
+	dma_free_coherent(dev, ATA_PRD_TBL_SZ, ap->prd, ap->prd_dma);
+	ata_pad_free(ap, dev);
+}
+
+void ata_host_stop (struct ata_host_set *host_set)
+{
+	if (host_set->mmio_base)
+		iounmap(host_set->mmio_base);
+}
+
+
+/**
+ *	ata_host_remove - Unregister SCSI host structure with upper layers
+ *	@ap: Port to unregister
+ *	@do_unregister: 1 if we fully unregister, 0 to just stop the port
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+
+static void ata_host_remove(struct ata_port *ap, unsigned int do_unregister)
+{
+	struct Scsi_Host *sh = ap->host;
+
+	DPRINTK("ENTER\n");
+
+	if (do_unregister)
+		scsi_remove_host(sh);
+
+	ap->ops->port_stop(ap);
+}
+
+/**
+ *	ata_host_init - Initialize an ata_port structure
+ *	@ap: Structure to initialize
+ *	@host: associated SCSI mid-layer structure
+ *	@host_set: Collection of hosts to which @ap belongs
+ *	@ent: Probe information provided by low-level driver
+ *	@port_no: Port number associated with this ata_port
+ *
+ *	Initialize a new ata_port structure, and its associated
+ *	scsi_host.
+ *
+ *	LOCKING:
+ *	Inherited from caller.
+ */
+
+static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host,
+			  struct ata_host_set *host_set,
+			  const struct ata_probe_ent *ent, unsigned int port_no)
+{
+	unsigned int i;
+
+	host->max_id = 16;
+	host->max_lun = 1;
+	host->max_channel = 1;
+	host->unique_id = ata_unique_id++;
+	host->max_cmd_len = 12;
+
+	ap->flags = ATA_FLAG_PORT_DISABLED;
+	ap->id = host->unique_id;
+	ap->host = host;
+	ap->ctl = ATA_DEVCTL_OBS;
+	ap->host_set = host_set;
+	ap->port_no = port_no;
+	ap->hard_port_no =
+		ent->legacy_mode ? ent->hard_port_no : port_no;
+	ap->pio_mask = ent->pio_mask;
+	ap->mwdma_mask = ent->mwdma_mask;
+	ap->udma_mask = ent->udma_mask;
+	ap->flags |= ent->host_flags;
+	ap->ops = ent->port_ops;
+	ap->cbl = ATA_CBL_NONE;
+	ap->active_tag = ATA_TAG_POISON;
+	ap->last_ctl = 0xFF;
+
+	INIT_WORK(&ap->packet_task, atapi_packet_task, ap);
+	INIT_WORK(&ap->pio_task, ata_pio_task, ap);
+
+	for (i = 0; i < ATA_MAX_DEVICES; i++)
+		ap->device[i].devno = i;
+
+#ifdef ATA_IRQ_TRAP
+	ap->stats.unhandled_irq = 1;
+	ap->stats.idle_irq = 1;
+#endif
+
+	memcpy(&ap->ioaddr, &ent->port[port_no], sizeof(struct ata_ioports));
+}
+
+/**
+ *	ata_host_add - Attach low-level ATA driver to system
+ *	@ent: Information provided by low-level driver
+ *	@host_set: Collections of ports to which we add
+ *	@port_no: Port number associated with this host
+ *
+ *	Attach low-level ATA driver to system.
+ *
+ *	LOCKING:
+ *	PCI/etc. bus probe sem.
+ *
+ *	RETURNS:
+ *	New ata_port on success, for NULL on error.
+ */
+
+static struct ata_port * ata_host_add(const struct ata_probe_ent *ent,
+				      struct ata_host_set *host_set,
+				      unsigned int port_no)
+{
+	struct Scsi_Host *host;
+	struct ata_port *ap;
+	int rc;
+
+	DPRINTK("ENTER\n");
+	host = scsi_host_alloc(ent->sht, sizeof(struct ata_port));
+	if (!host)
+		return NULL;
+
+	ap = (struct ata_port *) &host->hostdata[0];
+
+	ata_host_init(ap, host, host_set, ent, port_no);
+
+	rc = ap->ops->port_start(ap);
+	if (rc)
+		goto err_out;
+
+	return ap;
+
+err_out:
+	scsi_host_put(host);
+	return NULL;
+}
+
+/**
+ *	ata_device_add - Register hardware device with ATA and SCSI layers
+ *	@ent: Probe information describing hardware device to be registered
+ *
+ *	This function processes the information provided in the probe
+ *	information struct @ent, allocates the necessary ATA and SCSI
+ *	host information structures, initializes them, and registers
+ *	everything with requisite kernel subsystems.
+ *
+ *	This function requests irqs, probes the ATA bus, and probes
+ *	the SCSI bus.
+ *
+ *	LOCKING:
+ *	PCI/etc. bus probe sem.
+ *
+ *	RETURNS:
+ *	Number of ports registered.  Zero on error (no ports registered).
+ */
+
+int ata_device_add(const struct ata_probe_ent *ent)
+{
+	unsigned int count = 0, i;
+	struct device *dev = ent->dev;
+	struct ata_host_set *host_set;
+
+	DPRINTK("ENTER\n");
+	/* alloc a container for our list of ATA ports (buses) */
+	host_set = kzalloc(sizeof(struct ata_host_set) +
+			   (ent->n_ports * sizeof(void *)), GFP_KERNEL);
+	if (!host_set)
+		return 0;
+	spin_lock_init(&host_set->lock);
+
+	host_set->dev = dev;
+	host_set->n_ports = ent->n_ports;
+	host_set->irq = ent->irq;
+	host_set->mmio_base = ent->mmio_base;
+	host_set->private_data = ent->private_data;
+	host_set->ops = ent->port_ops;
+
+	/* register each port bound to this device */
+	for (i = 0; i < ent->n_ports; i++) {
+		struct ata_port *ap;
+		unsigned long xfer_mode_mask;
+
+		ap = ata_host_add(ent, host_set, i);
+		if (!ap)
+			goto err_out;
+
+		host_set->ports[i] = ap;
+		xfer_mode_mask =(ap->udma_mask << ATA_SHIFT_UDMA) |
+				(ap->mwdma_mask << ATA_SHIFT_MWDMA) |
+				(ap->pio_mask << ATA_SHIFT_PIO);
+
+		/* print per-port info to dmesg */
+		printk(KERN_INFO "ata%u: %cATA max %s cmd 0x%lX ctl 0x%lX "
+				 "bmdma 0x%lX irq %lu\n",
+			ap->id,
+			ap->flags & ATA_FLAG_SATA ? 'S' : 'P',
+			ata_mode_string(xfer_mode_mask),
+	       		ap->ioaddr.cmd_addr,
+	       		ap->ioaddr.ctl_addr,
+	       		ap->ioaddr.bmdma_addr,
+	       		ent->irq);
+
+		ata_chk_status(ap);
+		host_set->ops->irq_clear(ap);
+		count++;
+	}
+
+	if (!count)
+		goto err_free_ret;
+
+	/* obtain irq, that is shared between channels */
+	if (request_irq(ent->irq, ent->port_ops->irq_handler, ent->irq_flags,
+			DRV_NAME, host_set))
+		goto err_out;
+
+	/* perform each probe synchronously */
+	DPRINTK("probe begin\n");
+	for (i = 0; i < count; i++) {
+		struct ata_port *ap;
+		int rc;
+
+		ap = host_set->ports[i];
+
+		DPRINTK("ata%u: probe begin\n", ap->id);
+		rc = ata_bus_probe(ap);
+		DPRINTK("ata%u: probe end\n", ap->id);
+
+		if (rc) {
+			/* FIXME: do something useful here?
+			 * Current libata behavior will
+			 * tear down everything when
+			 * the module is removed
+			 * or the h/w is unplugged.
+			 */
+		}
+
+		rc = scsi_add_host(ap->host, dev);
+		if (rc) {
+			printk(KERN_ERR "ata%u: scsi_add_host failed\n",
+			       ap->id);
+			/* FIXME: do something useful here */
+			/* FIXME: handle unconditional calls to
+			 * scsi_scan_host and ata_host_remove, below,
+			 * at the very least
+			 */
+		}
+	}
+
+	/* probes are done, now scan each port's disk(s) */
+	DPRINTK("probe begin\n");
+	for (i = 0; i < count; i++) {
+		struct ata_port *ap = host_set->ports[i];
+
+		ata_scsi_scan_host(ap);
+	}
+
+	dev_set_drvdata(dev, host_set);
+
+	VPRINTK("EXIT, returning %u\n", ent->n_ports);
+	return ent->n_ports; /* success */
+
+err_out:
+	for (i = 0; i < count; i++) {
+		ata_host_remove(host_set->ports[i], 1);
+		scsi_host_put(host_set->ports[i]->host);
+	}
+err_free_ret:
+	kfree(host_set);
+	VPRINTK("EXIT, returning 0\n");
+	return 0;
+}
+
+/**
+ *	ata_host_set_remove - PCI layer callback for device removal
+ *	@host_set: ATA host set that was removed
+ *
+ *	Unregister all objects associated with this host set. Free those 
+ *	objects.
+ *
+ *	LOCKING:
+ *	Inherited from calling layer (may sleep).
+ */
+
+void ata_host_set_remove(struct ata_host_set *host_set)
+{
+	struct ata_port *ap;
+	unsigned int i;
+
+	for (i = 0; i < host_set->n_ports; i++) {
+		ap = host_set->ports[i];
+		scsi_remove_host(ap->host);
+	}
+
+	free_irq(host_set->irq, host_set);
+
+	for (i = 0; i < host_set->n_ports; i++) {
+		ap = host_set->ports[i];
+
+		ata_scsi_release(ap->host);
+
+		if ((ap->flags & ATA_FLAG_NO_LEGACY) == 0) {
+			struct ata_ioports *ioaddr = &ap->ioaddr;
+
+			if (ioaddr->cmd_addr == 0x1f0)
+				release_region(0x1f0, 8);
+			else if (ioaddr->cmd_addr == 0x170)
+				release_region(0x170, 8);
+		}
+
+		scsi_host_put(ap->host);
+	}
+
+	if (host_set->ops->host_stop)
+		host_set->ops->host_stop(host_set);
+
+	kfree(host_set);
+}
+
+/**
+ *	ata_scsi_release - SCSI layer callback hook for host unload
+ *	@host: libata host to be unloaded
+ *
+ *	Performs all duties necessary to shut down a libata port...
+ *	Kill port kthread, disable port, and release resources.
+ *
+ *	LOCKING:
+ *	Inherited from SCSI layer.
+ *
+ *	RETURNS:
+ *	One.
+ */
+
+int ata_scsi_release(struct Scsi_Host *host)
+{
+	struct ata_port *ap = (struct ata_port *) &host->hostdata[0];
+
+	DPRINTK("ENTER\n");
+
+	ap->ops->port_disable(ap);
+	ata_host_remove(ap, 0);
+
+	DPRINTK("EXIT\n");
+	return 1;
+}
+
+/**
+ *	ata_std_ports - initialize ioaddr with standard port offsets.
+ *	@ioaddr: IO address structure to be initialized
+ *
+ *	Utility function which initializes data_addr, error_addr,
+ *	feature_addr, nsect_addr, lbal_addr, lbam_addr, lbah_addr,
+ *	device_addr, status_addr, and command_addr to standard offsets
+ *	relative to cmd_addr.
+ *
+ *	Does not set ctl_addr, altstatus_addr, bmdma_addr, or scr_addr.
+ */
+
+void ata_std_ports(struct ata_ioports *ioaddr)
+{
+	ioaddr->data_addr = ioaddr->cmd_addr + ATA_REG_DATA;
+	ioaddr->error_addr = ioaddr->cmd_addr + ATA_REG_ERR;
+	ioaddr->feature_addr = ioaddr->cmd_addr + ATA_REG_FEATURE;
+	ioaddr->nsect_addr = ioaddr->cmd_addr + ATA_REG_NSECT;
+	ioaddr->lbal_addr = ioaddr->cmd_addr + ATA_REG_LBAL;
+	ioaddr->lbam_addr = ioaddr->cmd_addr + ATA_REG_LBAM;
+	ioaddr->lbah_addr = ioaddr->cmd_addr + ATA_REG_LBAH;
+	ioaddr->device_addr = ioaddr->cmd_addr + ATA_REG_DEVICE;
+	ioaddr->status_addr = ioaddr->cmd_addr + ATA_REG_STATUS;
+	ioaddr->command_addr = ioaddr->cmd_addr + ATA_REG_CMD;
+}
+
+static struct ata_probe_ent *
+ata_probe_ent_alloc(struct device *dev, const struct ata_port_info *port)
+{
+	struct ata_probe_ent *probe_ent;
+
+	probe_ent = kzalloc(sizeof(*probe_ent), GFP_KERNEL);
+	if (!probe_ent) {
+		printk(KERN_ERR DRV_NAME "(%s): out of memory\n",
+		       kobject_name(&(dev->kobj)));
+		return NULL;
+	}
+
+	INIT_LIST_HEAD(&probe_ent->node);
+	probe_ent->dev = dev;
+
+	probe_ent->sht = port->sht;
+	probe_ent->host_flags = port->host_flags;
+	probe_ent->pio_mask = port->pio_mask;
+	probe_ent->mwdma_mask = port->mwdma_mask;
+	probe_ent->udma_mask = port->udma_mask;
+	probe_ent->port_ops = port->port_ops;
+
+	return probe_ent;
+}
+
+
+
+#ifdef CONFIG_PCI
+
+void ata_pci_host_stop (struct ata_host_set *host_set)
+{
+	struct pci_dev *pdev = to_pci_dev(host_set->dev);
+
+	pci_iounmap(pdev, host_set->mmio_base);
+}
+
+/**
+ *	ata_pci_init_native_mode - Initialize native-mode driver
+ *	@pdev:  pci device to be initialized
+ *	@port:  array[2] of pointers to port info structures.
+ *	@ports: bitmap of ports present
+ *
+ *	Utility function which allocates and initializes an
+ *	ata_probe_ent structure for a standard dual-port
+ *	PIO-based IDE controller.  The returned ata_probe_ent
+ *	structure can be passed to ata_device_add().  The returned
+ *	ata_probe_ent structure should then be freed with kfree().
+ *
+ *	The caller need only pass the address of the primary port, the
+ *	secondary will be deduced automatically. If the device has non
+ *	standard secondary port mappings this function can be called twice,
+ *	once for each interface.
+ */
+
+struct ata_probe_ent *
+ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int ports)
+{
+	struct ata_probe_ent *probe_ent =
+		ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]);
+	int p = 0;
+
+	if (!probe_ent)
+		return NULL;
+
+	probe_ent->irq = pdev->irq;
+	probe_ent->irq_flags = SA_SHIRQ;
+	probe_ent->private_data = port[0]->private_data;
+
+	if (ports & ATA_PORT_PRIMARY) {
+		probe_ent->port[p].cmd_addr = pci_resource_start(pdev, 0);
+		probe_ent->port[p].altstatus_addr =
+		probe_ent->port[p].ctl_addr =
+			pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS;
+		probe_ent->port[p].bmdma_addr = pci_resource_start(pdev, 4);
+		ata_std_ports(&probe_ent->port[p]);
+		p++;
+	}
+
+	if (ports & ATA_PORT_SECONDARY) {
+		probe_ent->port[p].cmd_addr = pci_resource_start(pdev, 2);
+		probe_ent->port[p].altstatus_addr =
+		probe_ent->port[p].ctl_addr =
+			pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS;
+		probe_ent->port[p].bmdma_addr = pci_resource_start(pdev, 4) + 8;
+		ata_std_ports(&probe_ent->port[p]);
+		p++;
+	}
+
+	probe_ent->n_ports = p;
+	return probe_ent;
+}
+
+static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev, struct ata_port_info *port, int port_num)
+{
+	struct ata_probe_ent *probe_ent;
+
+	probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port);
+	if (!probe_ent)
+		return NULL;
+
+	probe_ent->legacy_mode = 1;
+	probe_ent->n_ports = 1;
+	probe_ent->hard_port_no = port_num;
+	probe_ent->private_data = port->private_data;
+
+	switch(port_num)
+	{
+		case 0:
+			probe_ent->irq = 14;
+			probe_ent->port[0].cmd_addr = 0x1f0;
+			probe_ent->port[0].altstatus_addr =
+			probe_ent->port[0].ctl_addr = 0x3f6;
+			break;
+		case 1:
+			probe_ent->irq = 15;
+			probe_ent->port[0].cmd_addr = 0x170;
+			probe_ent->port[0].altstatus_addr =
+			probe_ent->port[0].ctl_addr = 0x376;
+			break;
+	}
+	probe_ent->port[0].bmdma_addr = pci_resource_start(pdev, 4) + 8 * port_num;
+	ata_std_ports(&probe_ent->port[0]);
+	return probe_ent;
+}
+
+/**
+ *	ata_pci_init_one - Initialize/register PCI IDE host controller
+ *	@pdev: Controller to be initialized
+ *	@port_info: Information from low-level host driver
+ *	@n_ports: Number of ports attached to host controller
+ *
+ *	This is a helper function which can be called from a driver's
+ *	xxx_init_one() probe function if the hardware uses traditional
+ *	IDE taskfile registers.
+ *
+ *	This function calls pci_enable_device(), reserves its register
+ *	regions, sets the dma mask, enables bus master mode, and calls
+ *	ata_device_add()
+ *
+ *	LOCKING:
+ *	Inherited from PCI layer (may sleep).
+ *
+ *	RETURNS:
+ *	Zero on success, negative on errno-based value on error.
+ */
+
+int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
+		      unsigned int n_ports)
+{
+	struct ata_probe_ent *probe_ent = NULL, *probe_ent2 = NULL;
+	struct ata_port_info *port[2];
+	u8 tmp8, mask;
+	unsigned int legacy_mode = 0;
+	int disable_dev_on_err = 1;
+	int rc;
+
+	DPRINTK("ENTER\n");
+
+	port[0] = port_info[0];
+	if (n_ports > 1)
+		port[1] = port_info[1];
+	else
+		port[1] = port[0];
+
+	if ((port[0]->host_flags & ATA_FLAG_NO_LEGACY) == 0
+	    && (pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
+		/* TODO: What if one channel is in native mode ... */
+		pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8);
+		mask = (1 << 2) | (1 << 0);
+		if ((tmp8 & mask) != mask)
+			legacy_mode = (1 << 3);
+	}
+
+	/* FIXME... */
+	if ((!legacy_mode) && (n_ports > 2)) {
+		printk(KERN_ERR "ata: BUG: native mode, n_ports > 2\n");
+		n_ports = 2;
+		/* For now */
+	}
+
+	/* FIXME: Really for ATA it isn't safe because the device may be
+	   multi-purpose and we want to leave it alone if it was already
+	   enabled. Secondly for shared use as Arjan says we want refcounting
+	   
+	   Checking dev->is_enabled is insufficient as this is not set at
+	   boot for the primary video which is BIOS enabled
+         */
+         
+	rc = pci_enable_device(pdev);
+	if (rc)
+		return rc;
+
+	rc = pci_request_regions(pdev, DRV_NAME);
+	if (rc) {
+		disable_dev_on_err = 0;
+		goto err_out;
+	}
+
+	/* FIXME: Should use platform specific mappers for legacy port ranges */
+	if (legacy_mode) {
+		if (!request_region(0x1f0, 8, "libata")) {
+			struct resource *conflict, res;
+			res.start = 0x1f0;
+			res.end = 0x1f0 + 8 - 1;
+			conflict = ____request_resource(&ioport_resource, &res);
+			if (!strcmp(conflict->name, "libata"))
+				legacy_mode |= (1 << 0);
+			else {
+				disable_dev_on_err = 0;
+				printk(KERN_WARNING "ata: 0x1f0 IDE port busy\n");
+			}
+		} else
+			legacy_mode |= (1 << 0);
+
+		if (!request_region(0x170, 8, "libata")) {
+			struct resource *conflict, res;
+			res.start = 0x170;
+			res.end = 0x170 + 8 - 1;
+			conflict = ____request_resource(&ioport_resource, &res);
+			if (!strcmp(conflict->name, "libata"))
+				legacy_mode |= (1 << 1);
+			else {
+				disable_dev_on_err = 0;
+				printk(KERN_WARNING "ata: 0x170 IDE port busy\n");
+			}
+		} else
+			legacy_mode |= (1 << 1);
+	}
+
+	/* we have legacy mode, but all ports are unavailable */
+	if (legacy_mode == (1 << 3)) {
+		rc = -EBUSY;
+		goto err_out_regions;
+	}
+
+	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+	if (rc)
+		goto err_out_regions;
+	rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
+	if (rc)
+		goto err_out_regions;
+
+	if (legacy_mode) {
+		if (legacy_mode & (1 << 0))
+			probe_ent = ata_pci_init_legacy_port(pdev, port[0], 0);
+		if (legacy_mode & (1 << 1))
+			probe_ent2 = ata_pci_init_legacy_port(pdev, port[1], 1);
+	} else {
+		if (n_ports == 2)
+			probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
+		else
+			probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY);
+	}
+	if (!probe_ent && !probe_ent2) {
+		rc = -ENOMEM;
+		goto err_out_regions;
+	}
+
+	pci_set_master(pdev);
+
+	/* FIXME: check ata_device_add return */
+	if (legacy_mode) {
+		if (legacy_mode & (1 << 0))
+			ata_device_add(probe_ent);
+		if (legacy_mode & (1 << 1))
+			ata_device_add(probe_ent2);
+	} else
+		ata_device_add(probe_ent);
+
+	kfree(probe_ent);
+	kfree(probe_ent2);
+
+	return 0;
+
+err_out_regions:
+	if (legacy_mode & (1 << 0))
+		release_region(0x1f0, 8);
+	if (legacy_mode & (1 << 1))
+		release_region(0x170, 8);
+	pci_release_regions(pdev);
+err_out:
+	if (disable_dev_on_err)
+		pci_disable_device(pdev);
+	return rc;
+}
+
+/**
+ *	ata_pci_remove_one - PCI layer callback for device removal
+ *	@pdev: PCI device that was removed
+ *
+ *	PCI layer indicates to libata via this hook that
+ *	hot-unplug or module unload event has occurred.
+ *	Handle this by unregistering all objects associated
+ *	with this PCI device.  Free those objects.  Then finally
+ *	release PCI resources and disable device.
+ *
+ *	LOCKING:
+ *	Inherited from PCI layer (may sleep).
+ */
+
+void ata_pci_remove_one (struct pci_dev *pdev)
+{
+	struct device *dev = pci_dev_to_dev(pdev);
+	struct ata_host_set *host_set = dev_get_drvdata(dev);
+
+	ata_host_set_remove(host_set);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	dev_set_drvdata(dev, NULL);
+}
+
+/* move to PCI subsystem */
+int pci_test_config_bits(struct pci_dev *pdev, const struct pci_bits *bits)
+{
+	unsigned long tmp = 0;
+
+	switch (bits->width) {
+	case 1: {
+		u8 tmp8 = 0;
+		pci_read_config_byte(pdev, bits->reg, &tmp8);
+		tmp = tmp8;
+		break;
+	}
+	case 2: {
+		u16 tmp16 = 0;
+		pci_read_config_word(pdev, bits->reg, &tmp16);
+		tmp = tmp16;
+		break;
+	}
+	case 4: {
+		u32 tmp32 = 0;
+		pci_read_config_dword(pdev, bits->reg, &tmp32);
+		tmp = tmp32;
+		break;
+	}
+
+	default:
+		return -EINVAL;
+	}
+
+	tmp &= bits->mask;
+
+	return (tmp == bits->val) ? 1 : 0;
+}
+#endif /* CONFIG_PCI */
+
+
+static int __init ata_init(void)
+{
+	ata_wq = create_workqueue("ata");
+	if (!ata_wq)
+		return -ENOMEM;
+
+	printk(KERN_DEBUG "libata version " DRV_VERSION " loaded.\n");
+	return 0;
+}
+
+static void __exit ata_exit(void)
+{
+	destroy_workqueue(ata_wq);
+}
+
+module_init(ata_init);
+module_exit(ata_exit);
+
+static unsigned long ratelimit_time;
+static spinlock_t ata_ratelimit_lock = SPIN_LOCK_UNLOCKED;
+
+int ata_ratelimit(void)
+{
+	int rc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ata_ratelimit_lock, flags);
+
+	if (time_after(jiffies, ratelimit_time)) {
+		rc = 1;
+		ratelimit_time = jiffies + (HZ/5);
+	} else
+		rc = 0;
+
+	spin_unlock_irqrestore(&ata_ratelimit_lock, flags);
+
+	return rc;
+}
+
+/*
+ * libata is essentially a library of internal helper functions for
+ * low-level ATA host controller drivers.  As such, the API/ABI is
+ * likely to change as new drivers are added and updated.
+ * Do not depend on ABI/API stability.
+ */
+
+EXPORT_SYMBOL_GPL(ata_std_bios_param);
+EXPORT_SYMBOL_GPL(ata_std_ports);
+EXPORT_SYMBOL_GPL(ata_device_add);
+EXPORT_SYMBOL_GPL(ata_host_set_remove);
+EXPORT_SYMBOL_GPL(ata_sg_init);
+EXPORT_SYMBOL_GPL(ata_sg_init_one);
+EXPORT_SYMBOL_GPL(ata_qc_complete);
+EXPORT_SYMBOL_GPL(ata_qc_issue_prot);
+EXPORT_SYMBOL_GPL(ata_eng_timeout);
+EXPORT_SYMBOL_GPL(ata_tf_load);
+EXPORT_SYMBOL_GPL(ata_tf_read);
+EXPORT_SYMBOL_GPL(ata_noop_dev_select);
+EXPORT_SYMBOL_GPL(ata_std_dev_select);
+EXPORT_SYMBOL_GPL(ata_tf_to_fis);
+EXPORT_SYMBOL_GPL(ata_tf_from_fis);
+EXPORT_SYMBOL_GPL(ata_check_status);
+EXPORT_SYMBOL_GPL(ata_altstatus);
+EXPORT_SYMBOL_GPL(ata_exec_command);
+EXPORT_SYMBOL_GPL(ata_port_start);
+EXPORT_SYMBOL_GPL(ata_port_stop);
+EXPORT_SYMBOL_GPL(ata_host_stop);
+EXPORT_SYMBOL_GPL(ata_interrupt);
+EXPORT_SYMBOL_GPL(ata_qc_prep);
+EXPORT_SYMBOL_GPL(ata_bmdma_setup);
+EXPORT_SYMBOL_GPL(ata_bmdma_start);
+EXPORT_SYMBOL_GPL(ata_bmdma_irq_clear);
+EXPORT_SYMBOL_GPL(ata_bmdma_status);
+EXPORT_SYMBOL_GPL(ata_bmdma_stop);
+EXPORT_SYMBOL_GPL(ata_port_probe);
+EXPORT_SYMBOL_GPL(sata_phy_reset);
+EXPORT_SYMBOL_GPL(__sata_phy_reset);
+EXPORT_SYMBOL_GPL(ata_bus_reset);
+EXPORT_SYMBOL_GPL(ata_port_disable);
+EXPORT_SYMBOL_GPL(ata_ratelimit);
+EXPORT_SYMBOL_GPL(ata_scsi_ioctl);
+EXPORT_SYMBOL_GPL(ata_scsi_queuecmd);
+EXPORT_SYMBOL_GPL(ata_scsi_error);
+EXPORT_SYMBOL_GPL(ata_scsi_slave_config);
+EXPORT_SYMBOL_GPL(ata_scsi_release);
+EXPORT_SYMBOL_GPL(ata_host_intr);
+EXPORT_SYMBOL_GPL(ata_dev_classify);
+EXPORT_SYMBOL_GPL(ata_dev_id_string);
+EXPORT_SYMBOL_GPL(ata_dev_config);
+EXPORT_SYMBOL_GPL(ata_scsi_simulate);
+
+EXPORT_SYMBOL_GPL(ata_timing_compute);
+EXPORT_SYMBOL_GPL(ata_timing_merge);
+
+#ifdef CONFIG_PCI
+EXPORT_SYMBOL_GPL(pci_test_config_bits);
+EXPORT_SYMBOL_GPL(ata_pci_host_stop);
+EXPORT_SYMBOL_GPL(ata_pci_init_native_mode);
+EXPORT_SYMBOL_GPL(ata_pci_init_one);
+EXPORT_SYMBOL_GPL(ata_pci_remove_one);
+#endif /* CONFIG_PCI */
diff -urN oldtree/drivers/scsi/qla2xxx/qla_os.c newtree/drivers/scsi/qla2xxx/qla_os.c
--- oldtree/drivers/scsi/qla2xxx/qla_os.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/scsi/qla2xxx/qla_os.c	2006-02-13 19:20:00.636415552 -0500
@@ -266,7 +266,7 @@
 	return str;
 }
 
-char *
+static char *
 qla2x00_fw_version_str(struct scsi_qla_host *ha, char *str)
 {
 	char un_str[10];
@@ -304,7 +304,7 @@
 	return (str);
 }
 
-char *
+static char *
 qla24xx_fw_version_str(struct scsi_qla_host *ha, char *str)
 {
 	sprintf(str, "%d.%02d.%02d ", ha->fw_major_version,
@@ -580,7 +580,7 @@
 *
 * Note:
 **************************************************************************/
-int
+static int
 qla2xxx_eh_abort(struct scsi_cmnd *cmd)
 {
 	scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
@@ -714,7 +714,7 @@
 *    SUCCESS/FAILURE (defined as macro in scsi.h).
 *
 **************************************************************************/
-int
+static int
 qla2xxx_eh_device_reset(struct scsi_cmnd *cmd)
 {
 	scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
@@ -845,7 +845,7 @@
 *    SUCCESS/FAILURE (defined as macro in scsi.h).
 *
 **************************************************************************/
-int
+static int
 qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
 {
 	scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
@@ -906,7 +906,7 @@
 *
 * Note:
 **************************************************************************/
-int
+static int
 qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
 {
 	scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
diff -urN oldtree/drivers/scsi/qla2xxx/qla_os.c.orig newtree/drivers/scsi/qla2xxx/qla_os.c.orig
--- oldtree/drivers/scsi/qla2xxx/qla_os.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/qla2xxx/qla_os.c.orig	2006-02-13 19:20:00.640414944 -0500
@@ -0,0 +1,2593 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c)  2003-2005 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#include "qla_def.h"
+
+#include <linux/moduleparam.h>
+#include <linux/vmalloc.h>
+#include <linux/smp_lock.h>
+#include <linux/delay.h>
+
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsicam.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_fc.h>
+
+/*
+ * Driver version
+ */
+char qla2x00_version_str[40];
+
+/*
+ * SRB allocation cache
+ */
+static kmem_cache_t *srb_cachep;
+
+/*
+ * Ioctl related information.
+ */
+static int num_hosts;
+
+int ql2xlogintimeout = 20;
+module_param(ql2xlogintimeout, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ql2xlogintimeout,
+		"Login timeout value in seconds.");
+
+int qlport_down_retry = 30;
+module_param(qlport_down_retry, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(qlport_down_retry,
+		"Maximum number of command retries to a port that returns"
+		"a PORT-DOWN status.");
+
+int ql2xplogiabsentdevice;
+module_param(ql2xplogiabsentdevice, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ql2xplogiabsentdevice,
+		"Option to enable PLOGI to devices that are not present after "
+		"a Fabric scan.  This is needed for several broken switches."
+		"Default is 0 - no PLOGI. 1 - perfom PLOGI.");
+
+int ql2xloginretrycount = 0;
+module_param(ql2xloginretrycount, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ql2xloginretrycount,
+		"Specify an alternate value for the NVRAM login retry count.");
+
+int ql2xfwloadbin=1;
+module_param(ql2xfwloadbin, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ql2xfwloadbin,
+		"Load ISP2xxx firmware image via hotplug.");
+
+static void qla2x00_free_device(scsi_qla_host_t *);
+
+static void qla2x00_config_dma_addressing(scsi_qla_host_t *ha);
+
+int ql2xfdmienable;
+module_param(ql2xfdmienable, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ql2xfdmienable,
+		"Enables FDMI registratons "
+		"Default is 0 - no FDMI. 1 - perfom FDMI.");
+
+/*
+ * SCSI host template entry points
+ */
+static int qla2xxx_slave_configure(struct scsi_device * device);
+static int qla2xxx_slave_alloc(struct scsi_device *);
+static void qla2xxx_slave_destroy(struct scsi_device *);
+static int qla2x00_queuecommand(struct scsi_cmnd *cmd,
+		void (*fn)(struct scsi_cmnd *));
+static int qla24xx_queuecommand(struct scsi_cmnd *cmd,
+		void (*fn)(struct scsi_cmnd *));
+static int qla2xxx_eh_abort(struct scsi_cmnd *);
+static int qla2xxx_eh_device_reset(struct scsi_cmnd *);
+static int qla2xxx_eh_bus_reset(struct scsi_cmnd *);
+static int qla2xxx_eh_host_reset(struct scsi_cmnd *);
+static int qla2x00_loop_reset(scsi_qla_host_t *ha);
+static int qla2x00_device_reset(scsi_qla_host_t *, fc_port_t *);
+
+static int qla2x00_change_queue_depth(struct scsi_device *, int);
+static int qla2x00_change_queue_type(struct scsi_device *, int);
+
+static struct scsi_host_template qla2x00_driver_template = {
+	.module			= THIS_MODULE,
+	.name			= "qla2xxx",
+	.queuecommand		= qla2x00_queuecommand,
+
+	.eh_abort_handler	= qla2xxx_eh_abort,
+	.eh_device_reset_handler = qla2xxx_eh_device_reset,
+	.eh_bus_reset_handler	= qla2xxx_eh_bus_reset,
+	.eh_host_reset_handler	= qla2xxx_eh_host_reset,
+
+	.slave_configure	= qla2xxx_slave_configure,
+
+	.slave_alloc		= qla2xxx_slave_alloc,
+	.slave_destroy		= qla2xxx_slave_destroy,
+	.change_queue_depth	= qla2x00_change_queue_depth,
+	.change_queue_type	= qla2x00_change_queue_type,
+	.this_id		= -1,
+	.cmd_per_lun		= 3,
+	.use_clustering		= ENABLE_CLUSTERING,
+	.sg_tablesize		= SG_ALL,
+
+	/*
+	 * The RISC allows for each command to transfer (2^32-1) bytes of data,
+	 * which equates to 0x800000 sectors.
+	 */
+	.max_sectors		= 0xFFFF,
+	.shost_attrs		= qla2x00_host_attrs,
+};
+
+static struct scsi_host_template qla24xx_driver_template = {
+	.module			= THIS_MODULE,
+	.name			= "qla2xxx",
+	.queuecommand		= qla24xx_queuecommand,
+
+	.eh_abort_handler	= qla2xxx_eh_abort,
+	.eh_device_reset_handler = qla2xxx_eh_device_reset,
+	.eh_bus_reset_handler	= qla2xxx_eh_bus_reset,
+	.eh_host_reset_handler	= qla2xxx_eh_host_reset,
+
+	.slave_configure	= qla2xxx_slave_configure,
+
+	.slave_alloc		= qla2xxx_slave_alloc,
+	.slave_destroy		= qla2xxx_slave_destroy,
+	.change_queue_depth	= qla2x00_change_queue_depth,
+	.change_queue_type	= qla2x00_change_queue_type,
+	.this_id		= -1,
+	.cmd_per_lun		= 3,
+	.use_clustering		= ENABLE_CLUSTERING,
+	.sg_tablesize		= SG_ALL,
+
+	.max_sectors		= 0xFFFF,
+	.shost_attrs		= qla2x00_host_attrs,
+};
+
+static struct scsi_transport_template *qla2xxx_transport_template = NULL;
+
+/* TODO Convert to inlines
+ *
+ * Timer routines
+ */
+#define	WATCH_INTERVAL		1       /* number of seconds */
+
+static void qla2x00_timer(scsi_qla_host_t *);
+
+static __inline__ void qla2x00_start_timer(scsi_qla_host_t *,
+    void *, unsigned long);
+static __inline__ void qla2x00_restart_timer(scsi_qla_host_t *, unsigned long);
+static __inline__ void qla2x00_stop_timer(scsi_qla_host_t *);
+
+static inline void
+qla2x00_start_timer(scsi_qla_host_t *ha, void *func, unsigned long interval)
+{
+	init_timer(&ha->timer);
+	ha->timer.expires = jiffies + interval * HZ;
+	ha->timer.data = (unsigned long)ha;
+	ha->timer.function = (void (*)(unsigned long))func;
+	add_timer(&ha->timer);
+	ha->timer_active = 1;
+}
+
+static inline void
+qla2x00_restart_timer(scsi_qla_host_t *ha, unsigned long interval)
+{
+	mod_timer(&ha->timer, jiffies + interval * HZ);
+}
+
+static __inline__ void
+qla2x00_stop_timer(scsi_qla_host_t *ha)
+{
+	del_timer_sync(&ha->timer);
+	ha->timer_active = 0;
+}
+
+static int qla2x00_do_dpc(void *data);
+
+static void qla2x00_rst_aen(scsi_qla_host_t *);
+
+static uint8_t qla2x00_mem_alloc(scsi_qla_host_t *);
+static void qla2x00_mem_free(scsi_qla_host_t *ha);
+static int qla2x00_allocate_sp_pool( scsi_qla_host_t *ha);
+static void qla2x00_free_sp_pool(scsi_qla_host_t *ha);
+static void qla2x00_sp_free_dma(scsi_qla_host_t *, srb_t *);
+void qla2x00_sp_compl(scsi_qla_host_t *ha, srb_t *);
+
+/* -------------------------------------------------------------------------- */
+
+static char *
+qla2x00_pci_info_str(struct scsi_qla_host *ha, char *str)
+{
+	static char *pci_bus_modes[] = {
+		"33", "66", "100", "133",
+	};
+	uint16_t pci_bus;
+
+	strcpy(str, "PCI");
+	pci_bus = (ha->pci_attr & (BIT_9 | BIT_10)) >> 9;
+	if (pci_bus) {
+		strcat(str, "-X (");
+		strcat(str, pci_bus_modes[pci_bus]);
+	} else {
+		pci_bus = (ha->pci_attr & BIT_8) >> 8;
+		strcat(str, " (");
+		strcat(str, pci_bus_modes[pci_bus]);
+	}
+	strcat(str, " MHz)");
+
+	return (str);
+}
+
+static char *
+qla24xx_pci_info_str(struct scsi_qla_host *ha, char *str)
+{
+	static char *pci_bus_modes[] = { "33", "66", "100", "133", };
+	uint32_t pci_bus;
+	int pcie_reg;
+
+	pcie_reg = pci_find_capability(ha->pdev, PCI_CAP_ID_EXP);
+	if (pcie_reg) {
+		char lwstr[6];
+		uint16_t pcie_lstat, lspeed, lwidth;
+
+		pcie_reg += 0x12;
+		pci_read_config_word(ha->pdev, pcie_reg, &pcie_lstat);
+		lspeed = pcie_lstat & (BIT_0 | BIT_1 | BIT_2 | BIT_3);
+		lwidth = (pcie_lstat &
+		    (BIT_4 | BIT_5 | BIT_6 | BIT_7 | BIT_8 | BIT_9)) >> 4;
+
+		strcpy(str, "PCIe (");
+		if (lspeed == 1)
+			strcat(str, "2.5Gb/s ");
+		else
+			strcat(str, "<unknown> ");
+		snprintf(lwstr, sizeof(lwstr), "x%d)", lwidth);
+		strcat(str, lwstr);
+
+		return str;
+	}
+
+	strcpy(str, "PCI");
+	pci_bus = (ha->pci_attr & CSRX_PCIX_BUS_MODE_MASK) >> 8;
+	if (pci_bus == 0 || pci_bus == 8) {
+		strcat(str, " (");
+		strcat(str, pci_bus_modes[pci_bus >> 3]);
+	} else {
+		strcat(str, "-X ");
+		if (pci_bus & BIT_2)
+			strcat(str, "Mode 2");
+		else
+			strcat(str, "Mode 1");
+		strcat(str, " (");
+		strcat(str, pci_bus_modes[pci_bus & ~BIT_2]);
+	}
+	strcat(str, " MHz)");
+
+	return str;
+}
+
+char *
+qla2x00_fw_version_str(struct scsi_qla_host *ha, char *str)
+{
+	char un_str[10];
+
+	sprintf(str, "%d.%02d.%02d ", ha->fw_major_version,
+	    ha->fw_minor_version,
+	    ha->fw_subminor_version);
+
+	if (ha->fw_attributes & BIT_9) {
+		strcat(str, "FLX");
+		return (str);
+	}
+
+	switch (ha->fw_attributes & 0xFF) {
+	case 0x7:
+		strcat(str, "EF");
+		break;
+	case 0x17:
+		strcat(str, "TP");
+		break;
+	case 0x37:
+		strcat(str, "IP");
+		break;
+	case 0x77:
+		strcat(str, "VI");
+		break;
+	default:
+		sprintf(un_str, "(%x)", ha->fw_attributes);
+		strcat(str, un_str);
+		break;
+	}
+	if (ha->fw_attributes & 0x100)
+		strcat(str, "X");
+
+	return (str);
+}
+
+char *
+qla24xx_fw_version_str(struct scsi_qla_host *ha, char *str)
+{
+	sprintf(str, "%d.%02d.%02d ", ha->fw_major_version,
+	    ha->fw_minor_version,
+	    ha->fw_subminor_version);
+
+	if (ha->fw_attributes & BIT_0)
+		strcat(str, "[Class 2] ");
+	if (ha->fw_attributes & BIT_1)
+		strcat(str, "[IP] ");
+	if (ha->fw_attributes & BIT_2)
+		strcat(str, "[Multi-ID] ");
+	if (ha->fw_attributes & BIT_13)
+		strcat(str, "[Experimental]");
+	return str;
+}
+
+static inline srb_t *
+qla2x00_get_new_sp(scsi_qla_host_t *ha, fc_port_t *fcport,
+    struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+{
+	srb_t *sp;
+
+	sp = mempool_alloc(ha->srb_mempool, GFP_ATOMIC);
+	if (!sp)
+		return sp;
+
+	atomic_set(&sp->ref_count, 1);
+	sp->ha = ha;
+	sp->fcport = fcport;
+	sp->cmd = cmd;
+	sp->flags = 0;
+	CMD_SP(cmd) = (void *)sp;
+	cmd->scsi_done = done;
+
+	return sp;
+}
+
+static int
+qla2x00_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+{
+	scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
+	fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
+	struct fc_rport *rport = starget_to_rport(scsi_target(cmd->device));
+	srb_t *sp;
+	int rval;
+
+	rval = fc_remote_port_chkready(rport);
+	if (rval) {
+		cmd->result = rval;
+		goto qc_fail_command;
+	}
+
+	if (atomic_read(&fcport->state) != FCS_ONLINE) {
+		if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD ||
+		    atomic_read(&ha->loop_state) == LOOP_DEAD) {
+			cmd->result = DID_NO_CONNECT << 16;
+			goto qc_fail_command;
+		}
+		goto qc_host_busy;
+	}
+
+	spin_unlock_irq(ha->host->host_lock);
+
+	sp = qla2x00_get_new_sp(ha, fcport, cmd, done);
+	if (!sp)
+		goto qc_host_busy_lock;
+
+	rval = qla2x00_start_scsi(sp);
+	if (rval != QLA_SUCCESS)
+		goto qc_host_busy_free_sp;
+
+	spin_lock_irq(ha->host->host_lock);
+
+	return 0;
+
+qc_host_busy_free_sp:
+	qla2x00_sp_free_dma(ha, sp);
+	mempool_free(sp, ha->srb_mempool);
+
+qc_host_busy_lock:
+	spin_lock_irq(ha->host->host_lock);
+
+qc_host_busy:
+	return SCSI_MLQUEUE_HOST_BUSY;
+
+qc_fail_command:
+	done(cmd);
+
+	return 0;
+}
+
+
+static int
+qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+{
+	scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
+	fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
+	struct fc_rport *rport = starget_to_rport(scsi_target(cmd->device));
+	srb_t *sp;
+	int rval;
+
+	rval = fc_remote_port_chkready(rport);
+	if (rval) {
+		cmd->result = rval;
+		goto qc24_fail_command;
+	}
+
+	if (atomic_read(&fcport->state) != FCS_ONLINE) {
+		if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD ||
+		    atomic_read(&ha->loop_state) == LOOP_DEAD) {
+			cmd->result = DID_NO_CONNECT << 16;
+			goto qc24_fail_command;
+		}
+		goto qc24_host_busy;
+	}
+
+	spin_unlock_irq(ha->host->host_lock);
+
+	sp = qla2x00_get_new_sp(ha, fcport, cmd, done);
+	if (!sp)
+		goto qc24_host_busy_lock;
+
+	rval = qla24xx_start_scsi(sp);
+	if (rval != QLA_SUCCESS)
+		goto qc24_host_busy_free_sp;
+
+	spin_lock_irq(ha->host->host_lock);
+
+	return 0;
+
+qc24_host_busy_free_sp:
+	qla2x00_sp_free_dma(ha, sp);
+	mempool_free(sp, ha->srb_mempool);
+
+qc24_host_busy_lock:
+	spin_lock_irq(ha->host->host_lock);
+
+qc24_host_busy:
+	return SCSI_MLQUEUE_HOST_BUSY;
+
+qc24_fail_command:
+	done(cmd);
+
+	return 0;
+}
+
+
+/*
+ * qla2x00_eh_wait_on_command
+ *    Waits for the command to be returned by the Firmware for some
+ *    max time.
+ *
+ * Input:
+ *    ha = actual ha whose done queue will contain the command
+ *	      returned by firmware.
+ *    cmd = Scsi Command to wait on.
+ *    flag = Abort/Reset(Bus or Device Reset)
+ *
+ * Return:
+ *    Not Found : 0
+ *    Found : 1
+ */
+static int
+qla2x00_eh_wait_on_command(scsi_qla_host_t *ha, struct scsi_cmnd *cmd)
+{
+#define ABORT_POLLING_PERIOD	1000
+#define ABORT_WAIT_ITER		((10 * 1000) / (ABORT_POLLING_PERIOD))
+	unsigned long wait_iter = ABORT_WAIT_ITER;
+	int ret = QLA_SUCCESS;
+
+	while (CMD_SP(cmd)) {
+		msleep(ABORT_POLLING_PERIOD);
+
+		if (--wait_iter)
+			break;
+	}
+	if (CMD_SP(cmd))
+		ret = QLA_FUNCTION_FAILED;
+
+	return ret;
+}
+
+/*
+ * qla2x00_wait_for_hba_online
+ *    Wait till the HBA is online after going through
+ *    <= MAX_RETRIES_OF_ISP_ABORT  or
+ *    finally HBA is disabled ie marked offline
+ *
+ * Input:
+ *     ha - pointer to host adapter structure
+ *
+ * Note:
+ *    Does context switching-Release SPIN_LOCK
+ *    (if any) before calling this routine.
+ *
+ * Return:
+ *    Success (Adapter is online) : 0
+ *    Failed  (Adapter is offline/disabled) : 1
+ */
+static int
+qla2x00_wait_for_hba_online(scsi_qla_host_t *ha)
+{
+	int		return_status;
+	unsigned long	wait_online;
+
+	wait_online = jiffies + (MAX_LOOP_TIMEOUT * HZ);
+	while (((test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) ||
+	    test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags) ||
+	    test_bit(ISP_ABORT_RETRY, &ha->dpc_flags) ||
+	    ha->dpc_active) && time_before(jiffies, wait_online)) {
+
+		msleep(1000);
+	}
+	if (ha->flags.online)
+		return_status = QLA_SUCCESS;
+	else
+		return_status = QLA_FUNCTION_FAILED;
+
+	DEBUG2(printk("%s return_status=%d\n",__func__,return_status));
+
+	return (return_status);
+}
+
+/*
+ * qla2x00_wait_for_loop_ready
+ *    Wait for MAX_LOOP_TIMEOUT(5 min) value for loop
+ *    to be in LOOP_READY state.
+ * Input:
+ *     ha - pointer to host adapter structure
+ *
+ * Note:
+ *    Does context switching-Release SPIN_LOCK
+ *    (if any) before calling this routine.
+ *
+ *
+ * Return:
+ *    Success (LOOP_READY) : 0
+ *    Failed  (LOOP_NOT_READY) : 1
+ */
+static inline int
+qla2x00_wait_for_loop_ready(scsi_qla_host_t *ha)
+{
+	int 	 return_status = QLA_SUCCESS;
+	unsigned long loop_timeout ;
+
+	/* wait for 5 min at the max for loop to be ready */
+	loop_timeout = jiffies + (MAX_LOOP_TIMEOUT * HZ);
+
+	while ((!atomic_read(&ha->loop_down_timer) &&
+	    atomic_read(&ha->loop_state) == LOOP_DOWN) ||
+	    atomic_read(&ha->loop_state) != LOOP_READY) {
+		msleep(1000);
+		if (time_after_eq(jiffies, loop_timeout)) {
+			return_status = QLA_FUNCTION_FAILED;
+			break;
+		}
+	}
+	return (return_status);
+}
+
+/**************************************************************************
+* qla2xxx_eh_abort
+*
+* Description:
+*    The abort function will abort the specified command.
+*
+* Input:
+*    cmd = Linux SCSI command packet to be aborted.
+*
+* Returns:
+*    Either SUCCESS or FAILED.
+*
+* Note:
+**************************************************************************/
+int
+qla2xxx_eh_abort(struct scsi_cmnd *cmd)
+{
+	scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
+	srb_t *sp;
+	int ret, i;
+	unsigned int id, lun;
+	unsigned long serial;
+	unsigned long flags;
+
+	if (!CMD_SP(cmd))
+		return FAILED;
+
+	ret = FAILED;
+
+	id = cmd->device->id;
+	lun = cmd->device->lun;
+	serial = cmd->serial_number;
+
+	/* Check active list for command command. */
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	for (i = 1; i < MAX_OUTSTANDING_COMMANDS; i++) {
+		sp = ha->outstanding_cmds[i];
+
+		if (sp == NULL)
+			continue;
+
+		if (sp->cmd != cmd)
+			continue;
+
+		DEBUG2(printk("%s(%ld): aborting sp %p from RISC. pid=%ld "
+		    "sp->state=%x\n", __func__, ha->host_no, sp, serial,
+		    sp->state));
+		DEBUG3(qla2x00_print_scsi_cmd(cmd);)
+
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
+		if (ha->isp_ops.abort_command(ha, sp)) {
+			DEBUG2(printk("%s(%ld): abort_command "
+			    "mbx failed.\n", __func__, ha->host_no));
+		} else {
+			DEBUG3(printk("%s(%ld): abort_command "
+			    "mbx success.\n", __func__, ha->host_no));
+			ret = SUCCESS;
+		}
+		spin_lock_irqsave(&ha->hardware_lock, flags);
+
+		break;
+	}
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	/* Wait for the command to be returned. */
+	if (ret == SUCCESS) {
+		if (qla2x00_eh_wait_on_command(ha, cmd) != QLA_SUCCESS) {
+			qla_printk(KERN_ERR, ha,
+			    "scsi(%ld:%d:%d): Abort handler timed out -- %lx "
+			    "%x.\n", ha->host_no, id, lun, serial, ret);
+		}
+	}
+
+	qla_printk(KERN_INFO, ha,
+	    "scsi(%ld:%d:%d): Abort command issued -- %lx %x.\n", ha->host_no,
+	    id, lun, serial, ret);
+
+	return ret;
+}
+
+/**************************************************************************
+* qla2x00_eh_wait_for_pending_target_commands
+*
+* Description:
+*    Waits for all the commands to come back from the specified target.
+*
+* Input:
+*    ha - pointer to scsi_qla_host structure.
+*    t  - target
+* Returns:
+*    Either SUCCESS or FAILED.
+*
+* Note:
+**************************************************************************/
+static int
+qla2x00_eh_wait_for_pending_target_commands(scsi_qla_host_t *ha, unsigned int t)
+{
+	int	cnt;
+	int	status;
+	srb_t		*sp;
+	struct scsi_cmnd *cmd;
+	unsigned long flags;
+
+	status = 0;
+
+	/*
+	 * Waiting for all commands for the designated target in the active
+	 * array
+	 */
+	for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+		spin_lock_irqsave(&ha->hardware_lock, flags);
+		sp = ha->outstanding_cmds[cnt];
+		if (sp) {
+			cmd = sp->cmd;
+			spin_unlock_irqrestore(&ha->hardware_lock, flags);
+			if (cmd->device->id == t) {
+				if (!qla2x00_eh_wait_on_command(ha, cmd)) {
+					status = 1;
+					break;
+				}
+			}
+		} else {
+			spin_unlock_irqrestore(&ha->hardware_lock, flags);
+		}
+	}
+	return (status);
+}
+
+
+/**************************************************************************
+* qla2xxx_eh_device_reset
+*
+* Description:
+*    The device reset function will reset the target and abort any
+*    executing commands.
+*
+*    NOTE: The use of SP is undefined within this context.  Do *NOT*
+*          attempt to use this value, even if you determine it is
+*          non-null.
+*
+* Input:
+*    cmd = Linux SCSI command packet of the command that cause the
+*          bus device reset.
+*
+* Returns:
+*    SUCCESS/FAILURE (defined as macro in scsi.h).
+*
+**************************************************************************/
+int
+qla2xxx_eh_device_reset(struct scsi_cmnd *cmd)
+{
+	scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
+	fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
+	srb_t *sp;
+	int ret;
+	unsigned int id, lun;
+	unsigned long serial;
+
+	ret = FAILED;
+
+	id = cmd->device->id;
+	lun = cmd->device->lun;
+	serial = cmd->serial_number;
+
+	sp = (srb_t *) CMD_SP(cmd);
+	if (!sp || !fcport)
+		return ret;
+
+	qla_printk(KERN_INFO, ha,
+	    "scsi(%ld:%d:%d): DEVICE RESET ISSUED.\n", ha->host_no, id, lun);
+
+	if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS)
+		goto eh_dev_reset_done;
+
+	if (qla2x00_wait_for_loop_ready(ha) == QLA_SUCCESS) {
+		if (qla2x00_device_reset(ha, fcport) == 0)
+			ret = SUCCESS;
+
+#if defined(LOGOUT_AFTER_DEVICE_RESET)
+		if (ret == SUCCESS) {
+			if (fcport->flags & FC_FABRIC_DEVICE) {
+				ha->isp_ops.fabric_logout(ha, fcport->loop_id);
+				qla2x00_mark_device_lost(ha, fcport);
+			}
+		}
+#endif
+	} else {
+		DEBUG2(printk(KERN_INFO
+		    "%s failed: loop not ready\n",__func__);)
+	}
+
+	if (ret == FAILED) {
+		DEBUG3(printk("%s(%ld): device reset failed\n",
+		    __func__, ha->host_no));
+		qla_printk(KERN_INFO, ha, "%s: device reset failed\n",
+		    __func__);
+
+		goto eh_dev_reset_done;
+	}
+
+	/* Flush outstanding commands. */
+	if (qla2x00_eh_wait_for_pending_target_commands(ha, id))
+		ret = FAILED;
+	if (ret == FAILED) {
+		DEBUG3(printk("%s(%ld): failed while waiting for commands\n",
+		    __func__, ha->host_no));
+		qla_printk(KERN_INFO, ha,
+		    "%s: failed while waiting for commands\n", __func__);
+	} else
+		qla_printk(KERN_INFO, ha,
+		    "scsi(%ld:%d:%d): DEVICE RESET SUCCEEDED.\n", ha->host_no,
+		    id, lun);
+ eh_dev_reset_done:
+	return ret;
+}
+
+/**************************************************************************
+* qla2x00_eh_wait_for_pending_commands
+*
+* Description:
+*    Waits for all the commands to come back from the specified host.
+*
+* Input:
+*    ha - pointer to scsi_qla_host structure.
+*
+* Returns:
+*    1 : SUCCESS
+*    0 : FAILED
+*
+* Note:
+**************************************************************************/
+static int
+qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *ha)
+{
+	int	cnt;
+	int	status;
+	srb_t		*sp;
+	struct scsi_cmnd *cmd;
+	unsigned long flags;
+
+	status = 1;
+
+	/*
+	 * Waiting for all commands for the designated target in the active
+	 * array
+	 */
+	for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+		spin_lock_irqsave(&ha->hardware_lock, flags);
+		sp = ha->outstanding_cmds[cnt];
+		if (sp) {
+			cmd = sp->cmd;
+			spin_unlock_irqrestore(&ha->hardware_lock, flags);
+			status = qla2x00_eh_wait_on_command(ha, cmd);
+			if (status == 0)
+				break;
+		}
+		else {
+			spin_unlock_irqrestore(&ha->hardware_lock, flags);
+		}
+	}
+	return (status);
+}
+
+
+/**************************************************************************
+* qla2xxx_eh_bus_reset
+*
+* Description:
+*    The bus reset function will reset the bus and abort any executing
+*    commands.
+*
+* Input:
+*    cmd = Linux SCSI command packet of the command that cause the
+*          bus reset.
+*
+* Returns:
+*    SUCCESS/FAILURE (defined as macro in scsi.h).
+*
+**************************************************************************/
+int
+qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
+{
+	scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
+	fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
+	srb_t *sp;
+	int ret;
+	unsigned int id, lun;
+	unsigned long serial;
+
+	ret = FAILED;
+
+	id = cmd->device->id;
+	lun = cmd->device->lun;
+	serial = cmd->serial_number;
+
+	sp = (srb_t *) CMD_SP(cmd);
+	if (!sp || !fcport)
+		return ret;
+
+	qla_printk(KERN_INFO, ha,
+	    "scsi(%ld:%d:%d): LOOP RESET ISSUED.\n", ha->host_no, id, lun);
+
+	if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS) {
+		DEBUG2(printk("%s failed:board disabled\n",__func__));
+		goto eh_bus_reset_done;
+	}
+
+	if (qla2x00_wait_for_loop_ready(ha) == QLA_SUCCESS) {
+		if (qla2x00_loop_reset(ha) == QLA_SUCCESS)
+			ret = SUCCESS;
+	}
+	if (ret == FAILED)
+		goto eh_bus_reset_done;
+
+	/* Flush outstanding commands. */
+	if (!qla2x00_eh_wait_for_pending_commands(ha))
+		ret = FAILED;
+
+eh_bus_reset_done:
+	qla_printk(KERN_INFO, ha, "%s: reset %s\n", __func__,
+	    (ret == FAILED) ? "failed" : "succeded");
+
+	return ret;
+}
+
+/**************************************************************************
+* qla2xxx_eh_host_reset
+*
+* Description:
+*    The reset function will reset the Adapter.
+*
+* Input:
+*      cmd = Linux SCSI command packet of the command that cause the
+*            adapter reset.
+*
+* Returns:
+*      Either SUCCESS or FAILED.
+*
+* Note:
+**************************************************************************/
+int
+qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
+{
+	scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
+	fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
+	srb_t *sp;
+	int ret;
+	unsigned int id, lun;
+	unsigned long serial;
+
+	ret = FAILED;
+
+	id = cmd->device->id;
+	lun = cmd->device->lun;
+	serial = cmd->serial_number;
+
+	sp = (srb_t *) CMD_SP(cmd);
+	if (!sp || !fcport)
+		return ret;
+
+	qla_printk(KERN_INFO, ha,
+	    "scsi(%ld:%d:%d): ADAPTER RESET ISSUED.\n", ha->host_no, id, lun);
+
+	if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS)
+		goto eh_host_reset_lock;
+
+	/*
+	 * Fixme-may be dpc thread is active and processing
+	 * loop_resync,so wait a while for it to
+	 * be completed and then issue big hammer.Otherwise
+	 * it may cause I/O failure as big hammer marks the
+	 * devices as lost kicking of the port_down_timer
+	 * while dpc is stuck for the mailbox to complete.
+	 */
+	qla2x00_wait_for_loop_ready(ha);
+	set_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+	if (qla2x00_abort_isp(ha)) {
+		clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+		/* failed. schedule dpc to try */
+		set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+
+		if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS)
+			goto eh_host_reset_lock;
+	}
+	clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+
+	/* Waiting for our command in done_queue to be returned to OS.*/
+	if (qla2x00_eh_wait_for_pending_commands(ha))
+		ret = SUCCESS;
+
+eh_host_reset_lock:
+	qla_printk(KERN_INFO, ha, "%s: reset %s\n", __func__,
+	    (ret == FAILED) ? "failed" : "succeded");
+
+	return ret;
+}
+
+/*
+* qla2x00_loop_reset
+*      Issue loop reset.
+*
+* Input:
+*      ha = adapter block pointer.
+*
+* Returns:
+*      0 = success
+*/
+static int
+qla2x00_loop_reset(scsi_qla_host_t *ha)
+{
+	int status = QLA_SUCCESS;
+	struct fc_port *fcport;
+
+	if (ha->flags.enable_lip_reset) {
+		status = qla2x00_lip_reset(ha);
+	}
+
+	if (status == QLA_SUCCESS && ha->flags.enable_target_reset) {
+		list_for_each_entry(fcport, &ha->fcports, list) {
+			if (fcport->port_type != FCT_TARGET)
+				continue;
+
+			status = qla2x00_device_reset(ha, fcport);
+			if (status != QLA_SUCCESS)
+				break;
+		}
+	}
+
+	if (status == QLA_SUCCESS &&
+		((!ha->flags.enable_target_reset &&
+		  !ha->flags.enable_lip_reset) ||
+		ha->flags.enable_lip_full_login)) {
+
+		status = qla2x00_full_login_lip(ha);
+	}
+
+	/* Issue marker command only when we are going to start the I/O */
+	ha->marker_needed = 1;
+
+	if (status) {
+		/* Empty */
+		DEBUG2_3(printk("%s(%ld): **** FAILED ****\n",
+				__func__,
+				ha->host_no);)
+	} else {
+		/* Empty */
+		DEBUG3(printk("%s(%ld): exiting normally.\n",
+				__func__,
+				ha->host_no);)
+	}
+
+	return(status);
+}
+
+/*
+ * qla2x00_device_reset
+ *	Issue bus device reset message to the target.
+ *
+ * Input:
+ *	ha = adapter block pointer.
+ *	t = SCSI ID.
+ *	TARGET_QUEUE_LOCK must be released.
+ *	ADAPTER_STATE_LOCK must be released.
+ *
+ * Context:
+ *	Kernel context.
+ */
+static int
+qla2x00_device_reset(scsi_qla_host_t *ha, fc_port_t *reset_fcport)
+{
+	/* Abort Target command will clear Reservation */
+	return ha->isp_ops.abort_target(reset_fcport);
+}
+
+static int
+qla2xxx_slave_alloc(struct scsi_device *sdev)
+{
+	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+
+	if (!rport || fc_remote_port_chkready(rport))
+		return -ENXIO;
+
+	sdev->hostdata = *(fc_port_t **)rport->dd_data;
+
+	return 0;
+}
+
+static int
+qla2xxx_slave_configure(struct scsi_device *sdev)
+{
+	scsi_qla_host_t *ha = to_qla_host(sdev->host);
+	struct fc_rport *rport = starget_to_rport(sdev->sdev_target);
+
+	if (sdev->tagged_supported)
+		scsi_activate_tcq(sdev, 32);
+	else
+		scsi_deactivate_tcq(sdev, 32);
+
+	rport->dev_loss_tmo = ha->port_down_retry_count + 5;
+
+	return 0;
+}
+
+static void
+qla2xxx_slave_destroy(struct scsi_device *sdev)
+{
+	sdev->hostdata = NULL;
+}
+
+static int
+qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth)
+{
+	scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+	return sdev->queue_depth;
+}
+
+static int
+qla2x00_change_queue_type(struct scsi_device *sdev, int tag_type)
+{
+	if (sdev->tagged_supported) {
+		scsi_set_tag_type(sdev, tag_type);
+		if (tag_type)
+			scsi_activate_tcq(sdev, sdev->queue_depth);
+		else
+			scsi_deactivate_tcq(sdev, sdev->queue_depth);
+	} else
+		tag_type = 0;
+
+	return tag_type;
+}
+
+/**
+ * qla2x00_config_dma_addressing() - Configure OS DMA addressing method.
+ * @ha: HA context
+ *
+ * At exit, the @ha's flags.enable_64bit_addressing set to indicated
+ * supported addressing method.
+ */
+static void
+qla2x00_config_dma_addressing(scsi_qla_host_t *ha)
+{
+	/* Assume a 32bit DMA mask. */
+	ha->flags.enable_64bit_addressing = 0;
+
+	if (!dma_set_mask(&ha->pdev->dev, DMA_64BIT_MASK)) {
+		/* Any upper-dword bits set? */
+		if (MSD(dma_get_required_mask(&ha->pdev->dev)) &&
+		    !pci_set_consistent_dma_mask(ha->pdev, DMA_64BIT_MASK)) {
+			/* Ok, a 64bit DMA mask is applicable. */
+			ha->flags.enable_64bit_addressing = 1;
+			ha->isp_ops.calc_req_entries = qla2x00_calc_iocbs_64;
+			ha->isp_ops.build_iocbs = qla2x00_build_scsi_iocbs_64;
+			return;
+		}
+	}
+
+	dma_set_mask(&ha->pdev->dev, DMA_32BIT_MASK);
+	pci_set_consistent_dma_mask(ha->pdev, DMA_32BIT_MASK);
+}
+
+static int
+qla2x00_iospace_config(scsi_qla_host_t *ha)
+{
+	unsigned long	pio, pio_len, pio_flags;
+	unsigned long	mmio, mmio_len, mmio_flags;
+
+	/* We only need PIO for Flash operations on ISP2312 v2 chips. */
+	pio = pci_resource_start(ha->pdev, 0);
+	pio_len = pci_resource_len(ha->pdev, 0);
+	pio_flags = pci_resource_flags(ha->pdev, 0);
+	if (pio_flags & IORESOURCE_IO) {
+		if (pio_len < MIN_IOBASE_LEN) {
+			qla_printk(KERN_WARNING, ha,
+			    "Invalid PCI I/O region size (%s)...\n",
+				pci_name(ha->pdev));
+			pio = 0;
+		}
+	} else {
+		qla_printk(KERN_WARNING, ha,
+		    "region #0 not a PIO resource (%s)...\n",
+		    pci_name(ha->pdev));
+		pio = 0;
+	}
+
+	/* Use MMIO operations for all accesses. */
+	mmio = pci_resource_start(ha->pdev, 1);
+	mmio_len = pci_resource_len(ha->pdev, 1);
+	mmio_flags = pci_resource_flags(ha->pdev, 1);
+
+	if (!(mmio_flags & IORESOURCE_MEM)) {
+		qla_printk(KERN_ERR, ha,
+		    "region #0 not an MMIO resource (%s), aborting\n",
+		    pci_name(ha->pdev));
+		goto iospace_error_exit;
+	}
+	if (mmio_len < MIN_IOBASE_LEN) {
+		qla_printk(KERN_ERR, ha,
+		    "Invalid PCI mem region size (%s), aborting\n",
+			pci_name(ha->pdev));
+		goto iospace_error_exit;
+	}
+
+	if (pci_request_regions(ha->pdev, ha->brd_info->drv_name)) {
+		qla_printk(KERN_WARNING, ha,
+		    "Failed to reserve PIO/MMIO regions (%s)\n",
+		    pci_name(ha->pdev));
+
+		goto iospace_error_exit;
+	}
+
+	ha->pio_address = pio;
+	ha->pio_length = pio_len;
+	ha->iobase = ioremap(mmio, MIN_IOBASE_LEN);
+	if (!ha->iobase) {
+		qla_printk(KERN_ERR, ha,
+		    "cannot remap MMIO (%s), aborting\n", pci_name(ha->pdev));
+
+		goto iospace_error_exit;
+	}
+
+	return (0);
+
+iospace_error_exit:
+	return (-ENOMEM);
+}
+
+static void
+qla2x00_enable_intrs(scsi_qla_host_t *ha)
+{
+	unsigned long flags = 0;
+	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	ha->interrupts_on = 1;
+	/* enable risc and host interrupts */
+	WRT_REG_WORD(&reg->ictrl, ICR_EN_INT | ICR_EN_RISC);
+	RD_REG_WORD(&reg->ictrl);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+}
+
+static void
+qla2x00_disable_intrs(scsi_qla_host_t *ha)
+{
+	unsigned long flags = 0;
+	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	ha->interrupts_on = 0;
+	/* disable risc and host interrupts */
+	WRT_REG_WORD(&reg->ictrl, 0);
+	RD_REG_WORD(&reg->ictrl);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+static void
+qla24xx_enable_intrs(scsi_qla_host_t *ha)
+{
+	unsigned long flags = 0;
+	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	ha->interrupts_on = 1;
+	WRT_REG_DWORD(&reg->ictrl, ICRX_EN_RISC_INT);
+	RD_REG_DWORD(&reg->ictrl);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+static void
+qla24xx_disable_intrs(scsi_qla_host_t *ha)
+{
+	unsigned long flags = 0;
+	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	ha->interrupts_on = 0;
+	WRT_REG_DWORD(&reg->ictrl, 0);
+	RD_REG_DWORD(&reg->ictrl);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+/*
+ * PCI driver interface
+ */
+int qla2x00_probe_one(struct pci_dev *pdev, struct qla_board_info *brd_info)
+{
+	int	ret = -ENODEV;
+	device_reg_t __iomem *reg;
+	struct Scsi_Host *host;
+	scsi_qla_host_t *ha;
+	unsigned long	flags = 0;
+	unsigned long	wait_switch = 0;
+	char pci_info[20];
+	char fw_str[30];
+	fc_port_t *fcport;
+
+	if (pci_enable_device(pdev))
+		goto probe_out;
+
+	host = scsi_host_alloc(brd_info->sht ? brd_info->sht:
+	    &qla2x00_driver_template, sizeof(scsi_qla_host_t));
+	if (host == NULL) {
+		printk(KERN_WARNING
+		    "qla2xxx: Couldn't allocate host from scsi layer!\n");
+		goto probe_disable_device;
+	}
+
+	/* Clear our data area */
+	ha = (scsi_qla_host_t *)host->hostdata;
+	memset(ha, 0, sizeof(scsi_qla_host_t));
+
+	ha->pdev = pdev;
+	ha->host = host;
+	ha->host_no = host->host_no;
+	ha->brd_info = brd_info;
+	sprintf(ha->host_str, "%s_%ld", ha->brd_info->drv_name, ha->host_no);
+
+	ha->dpc_pid = -1;
+
+	/* Configure PCI I/O space */
+	ret = qla2x00_iospace_config(ha);
+	if (ret)
+		goto probe_failed;
+
+	qla_printk(KERN_INFO, ha,
+	    "Found an %s, irq %d, iobase 0x%p\n", ha->brd_info->isp_name,
+	    pdev->irq, ha->iobase);
+
+	spin_lock_init(&ha->hardware_lock);
+
+	ha->prev_topology = 0;
+	ha->ports = MAX_BUSES;
+	ha->init_cb_size = sizeof(init_cb_t);
+	ha->mgmt_svr_loop_id = MANAGEMENT_SERVER;
+
+	/* Assign ISP specific operations. */
+	ha->isp_ops.pci_config		= qla2100_pci_config;
+	ha->isp_ops.reset_chip		= qla2x00_reset_chip;
+	ha->isp_ops.chip_diag		= qla2x00_chip_diag;
+	ha->isp_ops.config_rings	= qla2x00_config_rings;
+	ha->isp_ops.reset_adapter	= qla2x00_reset_adapter;
+	ha->isp_ops.nvram_config	= qla2x00_nvram_config;
+	ha->isp_ops.update_fw_options	= qla2x00_update_fw_options;
+	ha->isp_ops.load_risc		= qla2x00_load_risc;
+	ha->isp_ops.pci_info_str	= qla2x00_pci_info_str;
+	ha->isp_ops.fw_version_str	= qla2x00_fw_version_str;
+	ha->isp_ops.intr_handler	= qla2100_intr_handler;
+	ha->isp_ops.enable_intrs	= qla2x00_enable_intrs;
+	ha->isp_ops.disable_intrs	= qla2x00_disable_intrs;
+	ha->isp_ops.abort_command	= qla2x00_abort_command;
+	ha->isp_ops.abort_target	= qla2x00_abort_target;
+	ha->isp_ops.fabric_login	= qla2x00_login_fabric;
+	ha->isp_ops.fabric_logout	= qla2x00_fabric_logout;
+	ha->isp_ops.calc_req_entries	= qla2x00_calc_iocbs_32;
+	ha->isp_ops.build_iocbs		= qla2x00_build_scsi_iocbs_32;
+	ha->isp_ops.prep_ms_iocb	= qla2x00_prep_ms_iocb;
+	ha->isp_ops.prep_ms_fdmi_iocb	= qla2x00_prep_ms_fdmi_iocb;
+	ha->isp_ops.read_nvram		= qla2x00_read_nvram_data;
+	ha->isp_ops.write_nvram		= qla2x00_write_nvram_data;
+	ha->isp_ops.fw_dump		= qla2100_fw_dump;
+	ha->isp_ops.ascii_fw_dump	= qla2100_ascii_fw_dump;
+	if (IS_QLA2100(ha)) {
+		host->max_id = MAX_TARGETS_2100;
+		ha->mbx_count = MAILBOX_REGISTER_COUNT_2100;
+		ha->request_q_length = REQUEST_ENTRY_CNT_2100;
+		ha->response_q_length = RESPONSE_ENTRY_CNT_2100;
+		ha->last_loop_id = SNS_LAST_LOOP_ID_2100;
+		host->sg_tablesize = 32;
+		ha->gid_list_info_size = 4;
+	} else if (IS_QLA2200(ha)) {
+		host->max_id = MAX_TARGETS_2200;
+		ha->mbx_count = MAILBOX_REGISTER_COUNT;
+		ha->request_q_length = REQUEST_ENTRY_CNT_2200;
+		ha->response_q_length = RESPONSE_ENTRY_CNT_2100;
+		ha->last_loop_id = SNS_LAST_LOOP_ID_2100;
+		ha->gid_list_info_size = 4;
+	} else if (IS_QLA23XX(ha)) {
+		host->max_id = MAX_TARGETS_2200;
+		ha->mbx_count = MAILBOX_REGISTER_COUNT;
+		ha->request_q_length = REQUEST_ENTRY_CNT_2200;
+		ha->response_q_length = RESPONSE_ENTRY_CNT_2300;
+		ha->last_loop_id = SNS_LAST_LOOP_ID_2300;
+		ha->isp_ops.pci_config = qla2300_pci_config;
+		ha->isp_ops.intr_handler = qla2300_intr_handler;
+		ha->isp_ops.fw_dump = qla2300_fw_dump;
+		ha->isp_ops.ascii_fw_dump = qla2300_ascii_fw_dump;
+		ha->gid_list_info_size = 6;
+	} else if (IS_QLA24XX(ha) || IS_QLA25XX(ha)) {
+		host->max_id = MAX_TARGETS_2200;
+		ha->mbx_count = MAILBOX_REGISTER_COUNT;
+		ha->request_q_length = REQUEST_ENTRY_CNT_24XX;
+		ha->response_q_length = RESPONSE_ENTRY_CNT_2300;
+		ha->last_loop_id = SNS_LAST_LOOP_ID_2300;
+		ha->init_cb_size = sizeof(struct init_cb_24xx);
+		ha->mgmt_svr_loop_id = 10;
+		ha->isp_ops.pci_config = qla24xx_pci_config;
+		ha->isp_ops.reset_chip = qla24xx_reset_chip;
+		ha->isp_ops.chip_diag = qla24xx_chip_diag;
+		ha->isp_ops.config_rings = qla24xx_config_rings;
+		ha->isp_ops.reset_adapter = qla24xx_reset_adapter;
+		ha->isp_ops.nvram_config = qla24xx_nvram_config;
+		ha->isp_ops.update_fw_options = qla24xx_update_fw_options;
+		ha->isp_ops.load_risc = qla24xx_load_risc_flash;
+		if (ql2xfwloadbin)
+			ha->isp_ops.load_risc = qla24xx_load_risc_hotplug;
+		ha->isp_ops.pci_info_str = qla24xx_pci_info_str;
+		ha->isp_ops.fw_version_str = qla24xx_fw_version_str;
+		ha->isp_ops.intr_handler = qla24xx_intr_handler;
+		ha->isp_ops.enable_intrs = qla24xx_enable_intrs;
+		ha->isp_ops.disable_intrs = qla24xx_disable_intrs;
+		ha->isp_ops.abort_command = qla24xx_abort_command;
+		ha->isp_ops.abort_target = qla24xx_abort_target;
+		ha->isp_ops.fabric_login = qla24xx_login_fabric;
+		ha->isp_ops.fabric_logout = qla24xx_fabric_logout;
+		ha->isp_ops.prep_ms_iocb = qla24xx_prep_ms_iocb;
+		ha->isp_ops.prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb;
+		ha->isp_ops.read_nvram = qla24xx_read_nvram_data;
+		ha->isp_ops.write_nvram = qla24xx_write_nvram_data;
+		ha->isp_ops.fw_dump = qla24xx_fw_dump;
+		ha->isp_ops.ascii_fw_dump = qla24xx_ascii_fw_dump;
+		ha->gid_list_info_size = 8;
+	}
+	host->can_queue = ha->request_q_length + 128;
+
+	/* load the F/W, read paramaters, and init the H/W */
+	ha->instance = num_hosts;
+
+	init_MUTEX(&ha->mbx_cmd_sem);
+	init_MUTEX_LOCKED(&ha->mbx_intr_sem);
+
+	INIT_LIST_HEAD(&ha->list);
+	INIT_LIST_HEAD(&ha->fcports);
+	INIT_LIST_HEAD(&ha->rscn_fcports);
+
+	/*
+	 * These locks are used to prevent more than one CPU
+	 * from modifying the queue at the same time. The
+	 * higher level "host_lock" will reduce most
+	 * contention for these locks.
+	 */
+	spin_lock_init(&ha->mbx_reg_lock);
+
+	init_completion(&ha->dpc_inited);
+	init_completion(&ha->dpc_exited);
+
+	qla2x00_config_dma_addressing(ha);
+	if (qla2x00_mem_alloc(ha)) {
+		qla_printk(KERN_WARNING, ha,
+		    "[ERROR] Failed to allocate memory for adapter\n");
+
+		ret = -ENOMEM;
+		goto probe_failed;
+	}
+
+	if (qla2x00_initialize_adapter(ha) &&
+	    !(ha->device_flags & DFLG_NO_CABLE)) {
+
+		qla_printk(KERN_WARNING, ha,
+		    "Failed to initialize adapter\n");
+
+		DEBUG2(printk("scsi(%ld): Failed to initialize adapter - "
+		    "Adapter flags %x.\n",
+		    ha->host_no, ha->device_flags));
+
+		ret = -ENODEV;
+		goto probe_failed;
+	}
+
+	/*
+	 * Startup the kernel thread for this host adapter
+	 */
+	ha->dpc_should_die = 0;
+	ha->dpc_pid = kernel_thread(qla2x00_do_dpc, ha, 0);
+	if (ha->dpc_pid < 0) {
+		qla_printk(KERN_WARNING, ha,
+		    "Unable to start DPC thread!\n");
+
+		ret = -ENODEV;
+		goto probe_failed;
+	}
+	wait_for_completion(&ha->dpc_inited);
+
+	host->this_id = 255;
+	host->cmd_per_lun = 3;
+	host->unique_id = ha->instance;
+	host->max_cmd_len = MAX_CMDSZ;
+	host->max_channel = ha->ports - 1;
+	host->max_lun = MAX_LUNS;
+	host->transportt = qla2xxx_transport_template;
+
+	ret = request_irq(pdev->irq, ha->isp_ops.intr_handler,
+	    SA_INTERRUPT|SA_SHIRQ, ha->brd_info->drv_name, ha);
+	if (ret) {
+		qla_printk(KERN_WARNING, ha,
+		    "Failed to reserve interrupt %d already in use.\n",
+		    pdev->irq);
+		goto probe_failed;
+	}
+	host->irq = pdev->irq;
+
+	/* Initialized the timer */
+	qla2x00_start_timer(ha, qla2x00_timer, WATCH_INTERVAL);
+
+	DEBUG2(printk("DEBUG: detect hba %ld at address = %p\n",
+	    ha->host_no, ha));
+
+	ha->isp_ops.disable_intrs(ha);
+
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	reg = ha->iobase;
+	if (IS_QLA24XX(ha) || IS_QLA25XX(ha)) {
+		WRT_REG_DWORD(&reg->isp24.hccr, HCCRX_CLR_HOST_INT);
+		WRT_REG_DWORD(&reg->isp24.hccr, HCCRX_CLR_RISC_INT);
+	} else {
+		WRT_REG_WORD(&reg->isp.semaphore, 0);
+		WRT_REG_WORD(&reg->isp.hccr, HCCR_CLR_RISC_INT);
+		WRT_REG_WORD(&reg->isp.hccr, HCCR_CLR_HOST_INT);
+
+		/* Enable proper parity */
+		if (!IS_QLA2100(ha) && !IS_QLA2200(ha)) {
+			if (IS_QLA2300(ha))
+				/* SRAM parity */
+				WRT_REG_WORD(&reg->isp.hccr,
+				    (HCCR_ENABLE_PARITY + 0x1));
+			else
+				/* SRAM, Instruction RAM and GP RAM parity */
+				WRT_REG_WORD(&reg->isp.hccr,
+				    (HCCR_ENABLE_PARITY + 0x7));
+		}
+	}
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	ha->isp_ops.enable_intrs(ha);
+
+	/* v2.19.5b6 */
+	/*
+	 * Wait around max loop_reset_delay secs for the devices to come
+	 * on-line. We don't want Linux scanning before we are ready.
+	 *
+	 */
+	for (wait_switch = jiffies + (ha->loop_reset_delay * HZ);
+	    time_before(jiffies,wait_switch) &&
+	     !(ha->device_flags & (DFLG_NO_CABLE | DFLG_FABRIC_DEVICES))
+	     && (ha->device_flags & SWITCH_FOUND) ;) {
+
+		qla2x00_check_fabric_devices(ha);
+
+		msleep(10);
+	}
+
+	pci_set_drvdata(pdev, ha);
+	ha->flags.init_done = 1;
+	num_hosts++;
+
+	ret = scsi_add_host(host, &pdev->dev);
+	if (ret)
+		goto probe_failed;
+
+	qla2x00_alloc_sysfs_attr(ha);
+
+	qla2x00_init_host_attr(ha);
+
+	qla_printk(KERN_INFO, ha, "\n"
+	    " QLogic Fibre Channel HBA Driver: %s\n"
+	    "  QLogic %s - %s\n"
+	    "  %s: %s @ %s hdma%c, host#=%ld, fw=%s\n", qla2x00_version_str,
+	    ha->model_number, ha->model_desc ? ha->model_desc: "",
+	    ha->brd_info->isp_name, ha->isp_ops.pci_info_str(ha, pci_info),
+	    pci_name(pdev), ha->flags.enable_64bit_addressing ? '+': '-',
+	    ha->host_no, ha->isp_ops.fw_version_str(ha, fw_str));
+
+	/* Go with fc_rport registration. */
+	list_for_each_entry(fcport, &ha->fcports, list)
+		qla2x00_reg_remote_port(ha, fcport);
+
+	return 0;
+
+probe_failed:
+	qla2x00_free_device(ha);
+
+	scsi_host_put(host);
+
+probe_disable_device:
+	pci_disable_device(pdev);
+
+probe_out:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(qla2x00_probe_one);
+
+void qla2x00_remove_one(struct pci_dev *pdev)
+{
+	scsi_qla_host_t *ha;
+
+	ha = pci_get_drvdata(pdev);
+
+	qla2x00_free_sysfs_attr(ha);
+
+	fc_remove_host(ha->host);
+
+	scsi_remove_host(ha->host);
+
+	qla2x00_free_device(ha);
+
+	scsi_host_put(ha->host);
+
+	pci_set_drvdata(pdev, NULL);
+}
+EXPORT_SYMBOL_GPL(qla2x00_remove_one);
+
+static void
+qla2x00_free_device(scsi_qla_host_t *ha)
+{
+	int ret;
+
+	/* Abort any outstanding IO descriptors. */
+	if (!IS_QLA2100(ha) && !IS_QLA2200(ha))
+		qla2x00_cancel_io_descriptors(ha);
+
+	/* Disable timer */
+	if (ha->timer_active)
+		qla2x00_stop_timer(ha);
+
+	/* Kill the kernel thread for this host */
+	if (ha->dpc_pid >= 0) {
+		ha->dpc_should_die = 1;
+		wmb();
+		ret = kill_proc(ha->dpc_pid, SIGHUP, 1);
+		if (ret) {
+			qla_printk(KERN_ERR, ha,
+			    "Unable to signal DPC thread -- (%d)\n", ret);
+
+			/* TODO: SOMETHING MORE??? */
+		} else {
+			wait_for_completion(&ha->dpc_exited);
+		}
+	}
+
+	/* Stop currently executing firmware. */
+	qla2x00_stop_firmware(ha);
+
+	/* turn-off interrupts on the card */
+	if (ha->interrupts_on)
+		ha->isp_ops.disable_intrs(ha);
+
+	qla2x00_mem_free(ha);
+
+	ha->flags.online = 0;
+
+	/* Detach interrupts */
+	if (ha->pdev->irq)
+		free_irq(ha->pdev->irq, ha);
+
+	/* release io space registers  */
+	if (ha->iobase)
+		iounmap(ha->iobase);
+	pci_release_regions(ha->pdev);
+
+	pci_disable_device(ha->pdev);
+}
+
+/*
+ * qla2x00_mark_device_lost Updates fcport state when device goes offline.
+ *
+ * Input: ha = adapter block pointer.  fcport = port structure pointer.
+ *
+ * Return: None.
+ *
+ * Context:
+ */
+void qla2x00_mark_device_lost(scsi_qla_host_t *ha, fc_port_t *fcport,
+    int do_login)
+{
+	if (atomic_read(&fcport->state) == FCS_ONLINE && fcport->rport)
+		schedule_work(&fcport->rport_del_work);
+
+	/*
+	 * We may need to retry the login, so don't change the state of the
+	 * port but do the retries.
+	 */
+	if (atomic_read(&fcport->state) != FCS_DEVICE_DEAD)
+		atomic_set(&fcport->state, FCS_DEVICE_LOST);
+
+	if (!do_login)
+		return;
+
+	if (fcport->login_retry == 0) {
+		fcport->login_retry = ha->login_retry_count;
+		set_bit(RELOGIN_NEEDED, &ha->dpc_flags);
+
+		DEBUG(printk("scsi(%ld): Port login retry: "
+		    "%02x%02x%02x%02x%02x%02x%02x%02x, "
+		    "id = 0x%04x retry cnt=%d\n",
+		    ha->host_no,
+		    fcport->port_name[0],
+		    fcport->port_name[1],
+		    fcport->port_name[2],
+		    fcport->port_name[3],
+		    fcport->port_name[4],
+		    fcport->port_name[5],
+		    fcport->port_name[6],
+		    fcport->port_name[7],
+		    fcport->loop_id,
+		    fcport->login_retry));
+	}
+}
+
+/*
+ * qla2x00_mark_all_devices_lost
+ *	Updates fcport state when device goes offline.
+ *
+ * Input:
+ *	ha = adapter block pointer.
+ *	fcport = port structure pointer.
+ *
+ * Return:
+ *	None.
+ *
+ * Context:
+ */
+void
+qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha)
+{
+	fc_port_t *fcport;
+
+	list_for_each_entry(fcport, &ha->fcports, list) {
+		if (fcport->port_type != FCT_TARGET)
+			continue;
+
+		/*
+		 * No point in marking the device as lost, if the device is
+		 * already DEAD.
+		 */
+		if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD)
+			continue;
+		if (atomic_read(&fcport->state) == FCS_ONLINE && fcport->rport)
+			schedule_work(&fcport->rport_del_work);
+		atomic_set(&fcport->state, FCS_DEVICE_LOST);
+	}
+}
+
+/*
+* qla2x00_mem_alloc
+*      Allocates adapter memory.
+*
+* Returns:
+*      0  = success.
+*      1  = failure.
+*/
+static uint8_t
+qla2x00_mem_alloc(scsi_qla_host_t *ha)
+{
+	char	name[16];
+	uint8_t   status = 1;
+	int	retry= 10;
+
+	do {
+		/*
+		 * This will loop only once if everything goes well, else some
+		 * number of retries will be performed to get around a kernel
+		 * bug where available mem is not allocated until after a
+		 * little delay and a retry.
+		 */
+		ha->request_ring = dma_alloc_coherent(&ha->pdev->dev,
+		    (ha->request_q_length + 1) * sizeof(request_t),
+		    &ha->request_dma, GFP_KERNEL);
+		if (ha->request_ring == NULL) {
+			qla_printk(KERN_WARNING, ha,
+			    "Memory Allocation failed - request_ring\n");
+
+			qla2x00_mem_free(ha);
+			msleep(100);
+
+			continue;
+		}
+
+		ha->response_ring = dma_alloc_coherent(&ha->pdev->dev,
+		    (ha->response_q_length + 1) * sizeof(response_t),
+		    &ha->response_dma, GFP_KERNEL);
+		if (ha->response_ring == NULL) {
+			qla_printk(KERN_WARNING, ha,
+			    "Memory Allocation failed - response_ring\n");
+
+			qla2x00_mem_free(ha);
+			msleep(100);
+
+			continue;
+		}
+
+		ha->gid_list = dma_alloc_coherent(&ha->pdev->dev, GID_LIST_SIZE,
+		    &ha->gid_list_dma, GFP_KERNEL);
+		if (ha->gid_list == NULL) {
+			qla_printk(KERN_WARNING, ha,
+			    "Memory Allocation failed - gid_list\n");
+
+			qla2x00_mem_free(ha);
+			msleep(100);
+
+			continue;
+		}
+
+		ha->rlc_rsp = dma_alloc_coherent(&ha->pdev->dev,
+		    sizeof(rpt_lun_cmd_rsp_t), &ha->rlc_rsp_dma, GFP_KERNEL);
+		if (ha->rlc_rsp == NULL) {
+			qla_printk(KERN_WARNING, ha,
+				"Memory Allocation failed - rlc");
+
+			qla2x00_mem_free(ha);
+			msleep(100);
+
+			continue;
+		}
+
+		snprintf(name, sizeof(name), "qla2xxx_%ld", ha->host_no);
+		ha->s_dma_pool = dma_pool_create(name, &ha->pdev->dev,
+		    DMA_POOL_SIZE, 8, 0);
+		if (ha->s_dma_pool == NULL) {
+			qla_printk(KERN_WARNING, ha,
+			    "Memory Allocation failed - s_dma_pool\n");
+
+			qla2x00_mem_free(ha);
+			msleep(100);
+
+			continue;
+		}
+
+		/* get consistent memory allocated for init control block */
+		ha->init_cb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
+		    &ha->init_cb_dma);
+		if (ha->init_cb == NULL) {
+			qla_printk(KERN_WARNING, ha,
+			    "Memory Allocation failed - init_cb\n");
+
+			qla2x00_mem_free(ha);
+			msleep(100);
+
+			continue;
+		}
+		memset(ha->init_cb, 0, ha->init_cb_size);
+
+		/* Get consistent memory allocated for Get Port Database cmd */
+		ha->iodesc_pd = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
+		    &ha->iodesc_pd_dma);
+		if (ha->iodesc_pd == NULL) {
+			/* error */
+			qla_printk(KERN_WARNING, ha,
+			    "Memory Allocation failed - iodesc_pd\n");
+
+			qla2x00_mem_free(ha);
+			msleep(100);
+
+			continue;
+		}
+		memset(ha->iodesc_pd, 0, PORT_DATABASE_SIZE);
+
+		/* Allocate ioctl related memory. */
+		if (qla2x00_alloc_ioctl_mem(ha)) {
+			qla_printk(KERN_WARNING, ha,
+			    "Memory Allocation failed - ioctl_mem\n");
+
+			qla2x00_mem_free(ha);
+			msleep(100);
+
+			continue;
+		}
+
+		if (qla2x00_allocate_sp_pool(ha)) {
+			qla_printk(KERN_WARNING, ha,
+			    "Memory Allocation failed - "
+			    "qla2x00_allocate_sp_pool()\n");
+
+			qla2x00_mem_free(ha);
+			msleep(100);
+
+			continue;
+		}
+
+		/* Allocate memory for SNS commands */
+		if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+			/* Get consistent memory allocated for SNS commands */
+			ha->sns_cmd = dma_alloc_coherent(&ha->pdev->dev,
+			    sizeof(struct sns_cmd_pkt), &ha->sns_cmd_dma,
+			    GFP_KERNEL);
+			if (ha->sns_cmd == NULL) {
+				/* error */
+				qla_printk(KERN_WARNING, ha,
+				    "Memory Allocation failed - sns_cmd\n");
+
+				qla2x00_mem_free(ha);
+				msleep(100);
+
+				continue;
+			}
+			memset(ha->sns_cmd, 0, sizeof(struct sns_cmd_pkt));
+		} else {
+			/* Get consistent memory allocated for MS IOCB */
+			ha->ms_iocb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
+			    &ha->ms_iocb_dma);
+			if (ha->ms_iocb == NULL) {
+				/* error */
+				qla_printk(KERN_WARNING, ha,
+				    "Memory Allocation failed - ms_iocb\n");
+
+				qla2x00_mem_free(ha);
+				msleep(100);
+
+				continue;
+			}
+			memset(ha->ms_iocb, 0, sizeof(ms_iocb_entry_t));
+
+			/*
+			 * Get consistent memory allocated for CT SNS
+			 * commands
+			 */
+			ha->ct_sns = dma_alloc_coherent(&ha->pdev->dev,
+			    sizeof(struct ct_sns_pkt), &ha->ct_sns_dma,
+			    GFP_KERNEL);
+			if (ha->ct_sns == NULL) {
+				/* error */
+				qla_printk(KERN_WARNING, ha,
+				    "Memory Allocation failed - ct_sns\n");
+
+				qla2x00_mem_free(ha);
+				msleep(100);
+
+				continue;
+			}
+			memset(ha->ct_sns, 0, sizeof(struct ct_sns_pkt));
+		}
+
+		/* Done all allocations without any error. */
+		status = 0;
+
+	} while (retry-- && status != 0);
+
+	if (status) {
+		printk(KERN_WARNING
+			"%s(): **** FAILED ****\n", __func__);
+	}
+
+	return(status);
+}
+
+/*
+* qla2x00_mem_free
+*      Frees all adapter allocated memory.
+*
+* Input:
+*      ha = adapter block pointer.
+*/
+static void
+qla2x00_mem_free(scsi_qla_host_t *ha)
+{
+	struct list_head	*fcpl, *fcptemp;
+	fc_port_t	*fcport;
+	unsigned int	wtime;/* max wait time if mbx cmd is busy. */
+
+	if (ha == NULL) {
+		/* error */
+		DEBUG2(printk("%s(): ERROR invalid ha pointer.\n", __func__));
+		return;
+	}
+
+	/* Make sure all other threads are stopped. */
+	wtime = 60 * 1000;
+	while (ha->dpc_wait && wtime)
+		wtime = msleep_interruptible(wtime);
+
+	/* free ioctl memory */
+	qla2x00_free_ioctl_mem(ha);
+
+	/* free sp pool */
+	qla2x00_free_sp_pool(ha);
+
+	if (ha->sns_cmd)
+		dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt),
+		    ha->sns_cmd, ha->sns_cmd_dma);
+
+	if (ha->ct_sns)
+		dma_free_coherent(&ha->pdev->dev, sizeof(struct ct_sns_pkt),
+		    ha->ct_sns, ha->ct_sns_dma);
+
+	if (ha->ms_iocb)
+		dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
+
+	if (ha->iodesc_pd)
+		dma_pool_free(ha->s_dma_pool, ha->iodesc_pd, ha->iodesc_pd_dma);
+
+	if (ha->init_cb)
+		dma_pool_free(ha->s_dma_pool, ha->init_cb, ha->init_cb_dma);
+
+	if (ha->s_dma_pool)
+		dma_pool_destroy(ha->s_dma_pool);
+
+	if (ha->rlc_rsp)
+		dma_free_coherent(&ha->pdev->dev,
+		    sizeof(rpt_lun_cmd_rsp_t), ha->rlc_rsp,
+		    ha->rlc_rsp_dma);
+
+	if (ha->gid_list)
+		dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list,
+		    ha->gid_list_dma);
+
+	if (ha->response_ring)
+		dma_free_coherent(&ha->pdev->dev,
+		    (ha->response_q_length + 1) * sizeof(response_t),
+		    ha->response_ring, ha->response_dma);
+
+	if (ha->request_ring)
+		dma_free_coherent(&ha->pdev->dev,
+		    (ha->request_q_length + 1) * sizeof(request_t),
+		    ha->request_ring, ha->request_dma);
+
+	ha->sns_cmd = NULL;
+	ha->sns_cmd_dma = 0;
+	ha->ct_sns = NULL;
+	ha->ct_sns_dma = 0;
+	ha->ms_iocb = NULL;
+	ha->ms_iocb_dma = 0;
+	ha->iodesc_pd = NULL;
+	ha->iodesc_pd_dma = 0;
+	ha->init_cb = NULL;
+	ha->init_cb_dma = 0;
+
+	ha->s_dma_pool = NULL;
+
+	ha->rlc_rsp = NULL;
+	ha->rlc_rsp_dma = 0;
+	ha->gid_list = NULL;
+	ha->gid_list_dma = 0;
+
+	ha->response_ring = NULL;
+	ha->response_dma = 0;
+	ha->request_ring = NULL;
+	ha->request_dma = 0;
+
+	list_for_each_safe(fcpl, fcptemp, &ha->fcports) {
+		fcport = list_entry(fcpl, fc_port_t, list);
+
+		/* fc ports */
+		list_del_init(&fcport->list);
+		kfree(fcport);
+	}
+	INIT_LIST_HEAD(&ha->fcports);
+
+	if (ha->fw_dump)
+		free_pages((unsigned long)ha->fw_dump, ha->fw_dump_order);
+
+	vfree(ha->fw_dump24);
+
+	vfree(ha->fw_dump_buffer);
+
+	ha->fw_dump = NULL;
+	ha->fw_dump24 = NULL;
+	ha->fw_dumped = 0;
+	ha->fw_dump_reading = 0;
+	ha->fw_dump_buffer = NULL;
+}
+
+/*
+ * qla2x00_allocate_sp_pool
+ * 	 This routine is called during initialization to allocate
+ *  	 memory for local srb_t.
+ *
+ * Input:
+ *	 ha   = adapter block pointer.
+ *
+ * Context:
+ *      Kernel context.
+ *
+ * Note: Sets the ref_count for non Null sp to one.
+ */
+static int
+qla2x00_allocate_sp_pool(scsi_qla_host_t *ha)
+{
+	int      rval;
+
+	rval = QLA_SUCCESS;
+	ha->srb_mempool = mempool_create(SRB_MIN_REQ, mempool_alloc_slab,
+	    mempool_free_slab, srb_cachep);
+	if (ha->srb_mempool == NULL) {
+		qla_printk(KERN_INFO, ha, "Unable to allocate SRB mempool.\n");
+		rval = QLA_FUNCTION_FAILED;
+	}
+	return (rval);
+}
+
+/*
+ *  This routine frees all adapter allocated memory.
+ *
+ */
+static void
+qla2x00_free_sp_pool( scsi_qla_host_t *ha)
+{
+	if (ha->srb_mempool) {
+		mempool_destroy(ha->srb_mempool);
+		ha->srb_mempool = NULL;
+	}
+}
+
+/**************************************************************************
+* qla2x00_do_dpc
+*   This kernel thread is a task that is schedule by the interrupt handler
+*   to perform the background processing for interrupts.
+*
+* Notes:
+* This task always run in the context of a kernel thread.  It
+* is kick-off by the driver's detect code and starts up
+* up one per adapter. It immediately goes to sleep and waits for
+* some fibre event.  When either the interrupt handler or
+* the timer routine detects a event it will one of the task
+* bits then wake us up.
+**************************************************************************/
+static int
+qla2x00_do_dpc(void *data)
+{
+	DECLARE_MUTEX_LOCKED(sem);
+	scsi_qla_host_t *ha;
+	fc_port_t	*fcport;
+	uint8_t		status;
+	uint16_t	next_loopid;
+
+	ha = (scsi_qla_host_t *)data;
+
+	lock_kernel();
+
+	daemonize("%s_dpc", ha->host_str);
+	allow_signal(SIGHUP);
+
+	ha->dpc_wait = &sem;
+
+	set_user_nice(current, -20);
+
+	unlock_kernel();
+
+	complete(&ha->dpc_inited);
+
+	while (1) {
+		DEBUG3(printk("qla2x00: DPC handler sleeping\n"));
+
+		if (down_interruptible(&sem))
+			break;
+
+		if (ha->dpc_should_die)
+			break;
+
+		DEBUG3(printk("qla2x00: DPC handler waking up\n"));
+
+		/* Initialization not yet finished. Don't do anything yet. */
+		if (!ha->flags.init_done || ha->dpc_active)
+			continue;
+
+		DEBUG3(printk("scsi(%ld): DPC handler\n", ha->host_no));
+
+		ha->dpc_active = 1;
+
+		if (ha->flags.mbox_busy) {
+			ha->dpc_active = 0;
+			continue;
+		}
+
+		if (test_and_clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) {
+
+			DEBUG(printk("scsi(%ld): dpc: sched "
+			    "qla2x00_abort_isp ha = %p\n",
+			    ha->host_no, ha));
+			if (!(test_and_set_bit(ABORT_ISP_ACTIVE,
+			    &ha->dpc_flags))) {
+
+				if (qla2x00_abort_isp(ha)) {
+					/* failed. retry later */
+					set_bit(ISP_ABORT_NEEDED,
+					    &ha->dpc_flags);
+				}
+				clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+			}
+			DEBUG(printk("scsi(%ld): dpc: qla2x00_abort_isp end\n",
+			    ha->host_no));
+		}
+
+		if (test_and_clear_bit(LOOP_RESET_NEEDED, &ha->dpc_flags)) {
+			DEBUG(printk("scsi(%ld): dpc: sched loop_reset()\n",
+			    ha->host_no));
+			qla2x00_loop_reset(ha);
+		}
+
+		if (test_and_clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) &&
+		    (!(test_and_set_bit(RESET_ACTIVE, &ha->dpc_flags)))) {
+
+			DEBUG(printk("scsi(%ld): qla2x00_reset_marker()\n",
+			    ha->host_no));
+
+			qla2x00_rst_aen(ha);
+			clear_bit(RESET_ACTIVE, &ha->dpc_flags);
+		}
+
+		/* Retry each device up to login retry count */
+		if ((test_and_clear_bit(RELOGIN_NEEDED, &ha->dpc_flags)) &&
+		    !test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) &&
+		    atomic_read(&ha->loop_state) != LOOP_DOWN) {
+
+			DEBUG(printk("scsi(%ld): qla2x00_port_login()\n",
+			    ha->host_no));
+
+			next_loopid = 0;
+			list_for_each_entry(fcport, &ha->fcports, list) {
+				if (fcport->port_type != FCT_TARGET)
+					continue;
+
+				/*
+				 * If the port is not ONLINE then try to login
+				 * to it if we haven't run out of retries.
+				 */
+				if (atomic_read(&fcport->state) != FCS_ONLINE &&
+				    fcport->login_retry) {
+
+					fcport->login_retry--;
+					if (fcport->flags & FCF_FABRIC_DEVICE) {
+						if (fcport->flags &
+						    FCF_TAPE_PRESENT)
+							ha->isp_ops.fabric_logout(
+							    ha, fcport->loop_id,
+							    fcport->d_id.b.domain,
+							    fcport->d_id.b.area,
+							    fcport->d_id.b.al_pa);
+						status = qla2x00_fabric_login(
+						    ha, fcport, &next_loopid);
+					} else
+						status =
+						    qla2x00_local_device_login(
+							ha, fcport->loop_id);
+
+					if (status == QLA_SUCCESS) {
+						fcport->old_loop_id = fcport->loop_id;
+
+						DEBUG(printk("scsi(%ld): port login OK: logged in ID 0x%x\n",
+						    ha->host_no, fcport->loop_id));
+
+						fcport->port_login_retry_count =
+						    ha->port_down_retry_count * PORT_RETRY_TIME;
+						atomic_set(&fcport->state, FCS_ONLINE);
+						atomic_set(&fcport->port_down_timer,
+						    ha->port_down_retry_count * PORT_RETRY_TIME);
+
+						fcport->login_retry = 0;
+					} else if (status == 1) {
+						set_bit(RELOGIN_NEEDED, &ha->dpc_flags);
+						/* retry the login again */
+						DEBUG(printk("scsi(%ld): Retrying %d login again loop_id 0x%x\n",
+						    ha->host_no,
+						    fcport->login_retry, fcport->loop_id));
+					} else {
+						fcport->login_retry = 0;
+					}
+				}
+				if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
+					break;
+			}
+			DEBUG(printk("scsi(%ld): qla2x00_port_login - end\n",
+			    ha->host_no));
+		}
+
+		if ((test_bit(LOGIN_RETRY_NEEDED, &ha->dpc_flags)) &&
+		    atomic_read(&ha->loop_state) != LOOP_DOWN) {
+
+			clear_bit(LOGIN_RETRY_NEEDED, &ha->dpc_flags);
+			DEBUG(printk("scsi(%ld): qla2x00_login_retry()\n",
+			    ha->host_no));
+
+			set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
+
+			DEBUG(printk("scsi(%ld): qla2x00_login_retry - end\n",
+			    ha->host_no));
+		}
+
+		if (test_and_clear_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) {
+
+			DEBUG(printk("scsi(%ld): qla2x00_loop_resync()\n",
+			    ha->host_no));
+
+			if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE,
+			    &ha->dpc_flags))) {
+
+				qla2x00_loop_resync(ha);
+
+				clear_bit(LOOP_RESYNC_ACTIVE, &ha->dpc_flags);
+			}
+
+			DEBUG(printk("scsi(%ld): qla2x00_loop_resync - end\n",
+			    ha->host_no));
+		}
+
+		if (test_and_clear_bit(FCPORT_RESCAN_NEEDED, &ha->dpc_flags)) {
+
+			DEBUG(printk("scsi(%ld): Rescan flagged fcports...\n",
+			    ha->host_no));
+
+			qla2x00_rescan_fcports(ha);
+
+			DEBUG(printk("scsi(%ld): Rescan flagged fcports..."
+			    "end.\n",
+			    ha->host_no));
+		}
+
+		if (!ha->interrupts_on)
+			ha->isp_ops.enable_intrs(ha);
+
+		ha->dpc_active = 0;
+	} /* End of while(1) */
+
+	DEBUG(printk("scsi(%ld): DPC handler exiting\n", ha->host_no));
+
+	/*
+	 * Make sure that nobody tries to wake us up again.
+	 */
+	ha->dpc_wait = NULL;
+	ha->dpc_active = 0;
+
+	complete_and_exit(&ha->dpc_exited, 0);
+}
+
+/*
+*  qla2x00_rst_aen
+*      Processes asynchronous reset.
+*
+* Input:
+*      ha  = adapter block pointer.
+*/
+static void
+qla2x00_rst_aen(scsi_qla_host_t *ha)
+{
+	if (ha->flags.online && !ha->flags.reset_active &&
+	    !atomic_read(&ha->loop_down_timer) &&
+	    !(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags))) {
+		do {
+			clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
+
+			/*
+			 * Issue marker command only when we are going to start
+			 * the I/O.
+			 */
+			ha->marker_needed = 1;
+		} while (!atomic_read(&ha->loop_down_timer) &&
+		    (test_bit(RESET_MARKER_NEEDED, &ha->dpc_flags)));
+	}
+}
+
+static void
+qla2x00_sp_free_dma(scsi_qla_host_t *ha, srb_t *sp)
+{
+	struct scsi_cmnd *cmd = sp->cmd;
+
+	if (sp->flags & SRB_DMA_VALID) {
+		if (cmd->use_sg) {
+			dma_unmap_sg(&ha->pdev->dev, cmd->request_buffer,
+			    cmd->use_sg, cmd->sc_data_direction);
+		} else if (cmd->request_bufflen) {
+			dma_unmap_single(&ha->pdev->dev, sp->dma_handle,
+			    cmd->request_bufflen, cmd->sc_data_direction);
+		}
+		sp->flags &= ~SRB_DMA_VALID;
+	}
+	CMD_SP(cmd) = NULL;
+}
+
+void
+qla2x00_sp_compl(scsi_qla_host_t *ha, srb_t *sp)
+{
+	struct scsi_cmnd *cmd = sp->cmd;
+
+	qla2x00_sp_free_dma(ha, sp);
+
+	mempool_free(sp, ha->srb_mempool);
+
+	cmd->scsi_done(cmd);
+}
+
+/**************************************************************************
+*   qla2x00_timer
+*
+* Description:
+*   One second timer
+*
+* Context: Interrupt
+***************************************************************************/
+static void
+qla2x00_timer(scsi_qla_host_t *ha)
+{
+	unsigned long	cpu_flags = 0;
+	fc_port_t	*fcport;
+	int		start_dpc = 0;
+	int		index;
+	srb_t		*sp;
+	int		t;
+
+	/*
+	 * Ports - Port down timer.
+	 *
+	 * Whenever, a port is in the LOST state we start decrementing its port
+	 * down timer every second until it reaches zero. Once  it reaches zero
+	 * the port it marked DEAD.
+	 */
+	t = 0;
+	list_for_each_entry(fcport, &ha->fcports, list) {
+		if (fcport->port_type != FCT_TARGET)
+			continue;
+
+		if (atomic_read(&fcport->state) == FCS_DEVICE_LOST) {
+
+			if (atomic_read(&fcport->port_down_timer) == 0)
+				continue;
+
+			if (atomic_dec_and_test(&fcport->port_down_timer) != 0)
+				atomic_set(&fcport->state, FCS_DEVICE_DEAD);
+
+			DEBUG(printk("scsi(%ld): fcport-%d - port retry count: "
+			    "%d remaining\n",
+			    ha->host_no,
+			    t, atomic_read(&fcport->port_down_timer)));
+		}
+		t++;
+	} /* End of for fcport  */
+
+
+	/* Loop down handler. */
+	if (atomic_read(&ha->loop_down_timer) > 0 &&
+	    !(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags)) && ha->flags.online) {
+
+		if (atomic_read(&ha->loop_down_timer) ==
+		    ha->loop_down_abort_time) {
+
+			DEBUG(printk("scsi(%ld): Loop Down - aborting the "
+			    "queues before time expire\n",
+			    ha->host_no));
+
+			if (!IS_QLA2100(ha) && ha->link_down_timeout)
+				atomic_set(&ha->loop_state, LOOP_DEAD);
+
+			/* Schedule an ISP abort to return any tape commands. */
+			spin_lock_irqsave(&ha->hardware_lock, cpu_flags);
+			for (index = 1; index < MAX_OUTSTANDING_COMMANDS;
+			    index++) {
+				fc_port_t *sfcp;
+
+				sp = ha->outstanding_cmds[index];
+				if (!sp)
+					continue;
+				sfcp = sp->fcport;
+				if (!(sfcp->flags & FCF_TAPE_PRESENT))
+					continue;
+
+				set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+				break;
+			}
+			spin_unlock_irqrestore(&ha->hardware_lock, cpu_flags);
+
+			set_bit(ABORT_QUEUES_NEEDED, &ha->dpc_flags);
+			start_dpc++;
+		}
+
+		/* if the loop has been down for 4 minutes, reinit adapter */
+		if (atomic_dec_and_test(&ha->loop_down_timer) != 0) {
+			DEBUG(printk("scsi(%ld): Loop down exceed 4 mins - "
+			    "restarting queues.\n",
+			    ha->host_no));
+
+			set_bit(RESTART_QUEUES_NEEDED, &ha->dpc_flags);
+			start_dpc++;
+
+			if (!(ha->device_flags & DFLG_NO_CABLE)) {
+				DEBUG(printk("scsi(%ld): Loop down - "
+				    "aborting ISP.\n",
+				    ha->host_no));
+				qla_printk(KERN_WARNING, ha,
+				    "Loop down - aborting ISP.\n");
+
+				set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+			}
+		}
+		DEBUG3(printk("scsi(%ld): Loop Down - seconds remaining %d\n",
+		    ha->host_no,
+		    atomic_read(&ha->loop_down_timer)));
+	}
+
+	/* Schedule the DPC routine if needed */
+	if ((test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) ||
+	    test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) ||
+	    test_bit(LOOP_RESET_NEEDED, &ha->dpc_flags) ||
+	    start_dpc ||
+	    test_bit(LOGIN_RETRY_NEEDED, &ha->dpc_flags) ||
+	    test_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) ||
+	    test_bit(RELOGIN_NEEDED, &ha->dpc_flags)) &&
+	    ha->dpc_wait && !ha->dpc_active) {
+
+		up(ha->dpc_wait);
+	}
+
+	qla2x00_restart_timer(ha, WATCH_INTERVAL);
+}
+
+/* XXX(hch): crude hack to emulate a down_timeout() */
+int
+qla2x00_down_timeout(struct semaphore *sema, unsigned long timeout)
+{
+	const unsigned int step = 100; /* msecs */
+	unsigned int iterations = jiffies_to_msecs(timeout)/100;
+
+	do {
+		if (!down_trylock(sema))
+			return 0;
+		if (msleep_interruptible(step))
+			break;
+	} while (--iterations >= 0);
+
+	return -ETIMEDOUT;
+}
+
+static struct qla_board_info qla_board_tbl[] = {
+	{
+		.drv_name	= "qla2400",
+		.isp_name	= "ISP2422",
+		.fw_fname	= "ql2400_fw.bin",
+		.sht		= &qla24xx_driver_template,
+	},
+	{
+		.drv_name	= "qla2400",
+		.isp_name	= "ISP2432",
+		.fw_fname	= "ql2400_fw.bin",
+		.sht		= &qla24xx_driver_template,
+	},
+};
+
+static struct pci_device_id qla2xxx_pci_tbl[] = {
+	{
+		.vendor		= PCI_VENDOR_ID_QLOGIC,
+		.device		= PCI_DEVICE_ID_QLOGIC_ISP2422,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= (unsigned long)&qla_board_tbl[0],
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_QLOGIC,
+		.device		= PCI_DEVICE_ID_QLOGIC_ISP2432,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= (unsigned long)&qla_board_tbl[1],
+	},
+	{0, 0},
+};
+MODULE_DEVICE_TABLE(pci, qla2xxx_pci_tbl);
+
+static int __devinit
+qla2xxx_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	return qla2x00_probe_one(pdev,
+	    (struct qla_board_info *)id->driver_data);
+}
+
+static void __devexit
+qla2xxx_remove_one(struct pci_dev *pdev)
+{
+	qla2x00_remove_one(pdev);
+}
+
+static struct pci_driver qla2xxx_pci_driver = {
+	.name		= "qla2xxx",
+	.id_table	= qla2xxx_pci_tbl,
+	.probe		= qla2xxx_probe_one,
+	.remove		= __devexit_p(qla2xxx_remove_one),
+};
+
+/**
+ * qla2x00_module_init - Module initialization.
+ **/
+static int __init
+qla2x00_module_init(void)
+{
+	int ret = 0;
+
+	/* Allocate cache for SRBs. */
+	srb_cachep = kmem_cache_create("qla2xxx_srbs", sizeof(srb_t), 0,
+	    SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (srb_cachep == NULL) {
+		printk(KERN_ERR
+		    "qla2xxx: Unable to allocate SRB cache...Failing load!\n");
+		return -ENOMEM;
+	}
+
+	/* Derive version string. */
+	strcpy(qla2x00_version_str, QLA2XXX_VERSION);
+#if DEBUG_QLA2100
+	strcat(qla2x00_version_str, "-debug");
+#endif
+	qla2xxx_transport_template =
+	    fc_attach_transport(&qla2xxx_transport_functions);
+	if (!qla2xxx_transport_template)
+		return -ENODEV;
+
+	printk(KERN_INFO "QLogic Fibre Channel HBA Driver\n");
+	ret = pci_module_init(&qla2xxx_pci_driver);
+	if (ret) {
+		kmem_cache_destroy(srb_cachep);
+		fc_release_transport(qla2xxx_transport_template);
+	}
+	return ret;
+}
+
+/**
+ * qla2x00_module_exit - Module cleanup.
+ **/
+static void __exit
+qla2x00_module_exit(void)
+{
+	pci_unregister_driver(&qla2xxx_pci_driver);
+	kmem_cache_destroy(srb_cachep);
+	fc_release_transport(qla2xxx_transport_template);
+}
+
+module_init(qla2x00_module_init);
+module_exit(qla2x00_module_exit);
+
+MODULE_AUTHOR("QLogic Corporation");
+MODULE_DESCRIPTION("QLogic Fibre Channel HBA Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(QLA2XXX_VERSION);
diff -urN oldtree/drivers/scsi/qla2xxx/qla_sup.c newtree/drivers/scsi/qla2xxx/qla_sup.c
--- oldtree/drivers/scsi/qla2xxx/qla_sup.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/scsi/qla2xxx/qla_sup.c	2006-02-13 19:20:00.641414792 -0500
@@ -428,7 +428,7 @@
 	return FARX_ACCESS_NVRAM_DATA | naddr;
 }
 
-uint32_t
+static uint32_t
 qla24xx_read_flash_dword(scsi_qla_host_t *ha, uint32_t addr)
 {
 	int rval;
@@ -469,7 +469,7 @@
 	return dwptr;
 }
 
-int
+static int
 qla24xx_write_flash_dword(scsi_qla_host_t *ha, uint32_t addr, uint32_t data)
 {
 	int rval;
@@ -491,6 +491,8 @@
 	return rval;
 }
 
+#if 0
+
 void
 qla24xx_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id,
     uint8_t *flash_id)
@@ -581,6 +583,8 @@
 	return ret;
 }
 
+#endif  /*  0  */
+
 uint8_t *
 qla2x00_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
     uint32_t bytes)
diff -urN oldtree/drivers/scsi/sas/Kconfig newtree/drivers/scsi/sas/Kconfig
--- oldtree/drivers/scsi/sas/Kconfig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/sas/Kconfig	2006-02-13 19:20:00.641414792 -0500
@@ -0,0 +1,45 @@
+#
+# Kernel configuration file for the SAS Class
+#
+# Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+# Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+#
+# This file is licensed under GPLv2.
+#
+# 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 the Free Software Foundation; version 2 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+#
+# $Id: //depot/sas-class/Kconfig#6 $
+#
+
+config SAS_CLASS
+	tristate "SAS Layer and Discovery"
+	depends on SCSI
+	help
+		If you wish to use a SAS Low Level Device Driver (LLDD)
+		say Y or M here.  Otherwise, say N.
+
+		SAS LLDDs which can make use of the SAS Transport Layer
+		are LLDDs who do not hide the transport in firmware.
+		Such LLDDs expose the transport to a management layer.
+		E.g. aic94xx SAS LLDD is such a LLDD.
+
+config SAS_DEBUG
+	bool "Compile the SAS Layer in debug mode"
+	default y
+	depends on SAS_CLASS
+	help
+		Compiles the SAS Layer in debug mode.  In debug mode, the
+		SAS Layer prints diagnostic and debug messages.
diff -urN oldtree/drivers/scsi/sas/Makefile newtree/drivers/scsi/sas/Makefile
--- oldtree/drivers/scsi/sas/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/sas/Makefile	2006-02-13 19:20:00.642414640 -0500
@@ -0,0 +1,42 @@
+#
+# Kernel Makefile for the SAS Class
+#
+# Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+# Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+#
+# This file is licensed under GPLv2.
+#
+# 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 the Free Software Foundation; version 2 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+#
+# $Id: //depot/sas-class/Makefile#20 $
+#
+
+ifeq ($(CONFIG_SAS_DEBUG),y)
+	EXTRA_CFLAGS += -DSAS_DEBUG -g
+endif
+
+clean-files += expander_conf
+
+obj-$(CONFIG_SAS_CLASS) += sas_class.o
+sas_class-y +=  sas_init.o     \
+		sas_common.o   \
+		sas_phy.o      \
+		sas_port.o     \
+		sas_event.o    \
+		sas_dump.o     \
+		sas_discover.o \
+		sas_expander.o \
+		sas_scsi_host.o
diff -urN oldtree/drivers/scsi/sas/README newtree/drivers/scsi/sas/README
--- oldtree/drivers/scsi/sas/README	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/sas/README	2006-02-13 19:20:00.644414336 -0500
@@ -0,0 +1,484 @@
+SAS Layer
+---------
+
+The SAS Layer is a management infrastructure which manages
+SAS LLDDs.  It sits between SCSI Core and SAS LLDDs.  The
+layout is as follows: while SCSI Core is concerned with
+SAM/SPC issues, and a SAS LLDD+sequencer is concerned with
+phy/OOB/link management, the SAS layer is concerned with:
+
+      * SAS Phy/Port/HA event management (LLDD generates,
+        SAS Layer processes),
+      * SAS Port management (creation/destruction),
+      * SAS Domain discovery and revalidation,
+      * SAS Domain device management,
+      * SCSI Host registration/unregistration,
+      * Device registration with SCSI Core (SAS) or libata
+        (SATA), and
+      * Expander management and exporting expander control
+        to user space.
+
+A SAS LLDD is a PCI device driver.  It is concerned with
+phy/OOB management, and vendor specific tasks and generates
+events to the SAS layer.
+
+The SAS Layer does most SAS tasks as outlined in the SAS 1.1
+spec.
+
+The sas_ha_struct describes the SAS LLDD to the SAS layer.
+Most of it is used by the SAS Layer but a few fields need to
+be initialized by the LLDDs.
+
+After initializing your hardware, from the probe() function
+you call sas_register_ha(). It will register your LLDD with
+the SCSI subsystem, creating a SCSI host and it will
+register your SAS driver with the sysfs SAS tree it creates.
+It will then return.  Then you enable your phys to actually
+start OOB (at which point your driver will start calling the
+notify_* event callbacks).
+
+Structure descriptions:
+
+struct sas_phy --------------------
+Normally this is statically embedded to your driver's
+phy structure:
+	struct my_phy {
+	       blah;
+	       struct sas_phy sas_phy;
+	       bleh;
+	};
+And then all the phys are an array of my_phy in your HA
+struct (shown below).
+
+Then as you go along and initialize your phys you also
+initialize the sas_phy struct, along with your own
+phy structure.
+
+In general, the phys are managed by the LLDD and the ports
+are managed by the SAS layer.  So the phys are initialized
+and updated by the LLDD and the ports are initialized and
+updated by the SAS layer.
+
+There is a scheme where the LLDD can RW certain fields,
+and the SAS layer can only read such ones, and vice versa.
+The idea is to avoid unnecessary locking.
+
+enabled -- must be set (0/1)
+id -- must be set [0,MAX_PHYS)
+class, proto, type, role, oob_mode, linkrate -- must be set
+oob_mode --  you set this when OOB has finished and then notify
+the SAS Layer.
+
+sas_addr -- this normally points to an array holding the sas
+address of the phy, possibly somewhere in your my_phy
+struct.
+
+attached_sas_addr -- set this when you (LLDD) receive an
+IDENTIFY frame or a FIS frame, _before_ notifying the SAS
+layer.  The idea is that sometimes the LLDD may want to fake
+or provide a different SAS address on that phy/port and this
+allows it to do this.  At best you should copy the sas
+address from the IDENTIFY frame or maybe generate a SAS
+address for SATA directly attached devices.  The Discover
+process may later change this.
+
+frame_rcvd -- this is where you copy the IDENTIFY/FIS frame
+when you get it; you lock, copy, set frame_rcvd_size and
+unlock the lock, and then call the event.  It is a pointer
+since there's no way to know your hw frame size _exactly_,
+so you define the actual array in your phy struct and let
+this pointer point to it.  You copy the frame from your
+DMAable memory to that area holding the lock.
+
+sas_prim -- this is where primitives go when they're
+received.  See sas.h. Grab the lock, set the primitive,
+release the lock, notify.
+
+port -- this points to the sas_port if the phy belongs
+to a port -- the LLDD only reads this. It points to the
+sas_port this phy is part of.  Set by the SAS Layer.
+
+ha -- may be set; the SAS layer sets it anyway.
+
+lldd_phy -- you should set this to point to your phy so you
+can find your way around faster when the SAS layer calls one
+of your callbacks and passes you a phy.  If the sas_phy is
+embedded you can also use container_of -- whatever you
+prefer.
+
+
+struct sas_port --------------------
+The LLDD doesn't set any fields of this struct -- it only
+reads them.  They should be self explanatory.
+
+phy_mask is 32 bit, this should be enough for now, as I
+haven't heard of a HA having more than 8 phys.
+
+lldd_port -- I haven't found use for that -- maybe other
+LLDD who wish to have internal port representation can make
+use of this.
+
+
+struct sas_ha_struct --------------------
+It normally is statically declared in your own LLDD
+structure describing your adapter:
+struct my_sas_ha {
+       blah;
+       struct sas_ha_struct sas_ha;
+       struct my_phy phys[MAX_PHYS];
+       struct sas_port sas_ports[MAX_PHYS]; /* (1) */
+       bleh;
+};
+
+(1) If your LLDD doesn't have its own port representation.
+
+What needs to be initialized (sample function given below).
+
+pcidev
+sas_addr -- since the SAS layer doesn't want to mess with
+	 memory allocation, etc, this points to statically
+	 allocated array somewhere (say in your host adapter
+	 structure) and holds the SAS address of the host
+	 adapter as given by you or the manufacturer, etc.
+sas_port
+sas_phy -- an array of pointers to structures. (see
+	note above on sas_addr).
+	These must be set.  See more notes below.
+num_phys -- the number of phys present in the sas_phy array,
+	 and the number of ports present in the sas_port
+	 array.  There can be a maximum num_phys ports (one per
+	 port) so we drop the num_ports, and only use
+	 num_phys.
+
+The event interface:
+
+	/* LLDD calls these to notify the class of an event. */
+	void (*notify_ha_event)(struct sas_ha_struct *, enum ha_event);
+	void (*notify_port_event)(struct sas_phy *, enum port_event);
+	void (*notify_phy_event)(struct sas_phy *, enum phy_event);
+
+When sas_register_ha() returns, those are set and can be
+called by the LLDD to notify the SAS layer of such events
+the SAS layer.
+
+The port notification:
+
+	/* The class calls these to notify the LLDD of an event. */
+	void (*lldd_port_formed)(struct sas_phy *);
+	void (*lldd_port_deformed)(struct sas_phy *);
+
+If the LLDD wants notification when a port has been formed
+or deformed it sets those to a function satisfying the type.
+
+A SAS LLDD should also implement at least one of the Task
+Management Functions (TMFs) described in SAM:
+
+	/* Task Management Functions. Must be called from process context. */
+	int (*lldd_abort_task)(struct sas_task *);
+	int (*lldd_abort_task_set)(struct domain_device *, u8 *lun);
+	int (*lldd_clear_aca)(struct domain_device *, u8 *lun);
+	int (*lldd_clear_task_set)(struct domain_device *, u8 *lun);
+	int (*lldd_I_T_nexus_reset)(struct domain_device *);
+	int (*lldd_lu_reset)(struct domain_device *, u8 *lun);
+	int (*lldd_query_task)(struct sas_task *);
+
+For more information please read SAM from T10.org.
+
+Port and Adapter management:
+
+	/* Port and Adapter management */
+	int (*lldd_clear_nexus_port)(struct sas_port *);
+	int (*lldd_clear_nexus_ha)(struct sas_ha_struct *);
+
+A SAS LLDD should implement at least one of those.
+
+Phy management:
+
+	/* Phy management */
+	int (*lldd_control_phy)(struct sas_phy *, enum phy_func);
+
+lldd_ha -- set this to point to your HA struct. You can also
+use container_of if you embedded it as shown above.
+
+A sample initialization and registration function
+can look like this (called last thing from probe())
+*but* before you enable the phys to do OOB:
+
+static int register_sas_ha(struct my_sas_ha *my_ha)
+{
+	int i;
+	static struct sas_phy   *sas_phys[MAX_PHYS];
+	static struct sas_port  *sas_ports[MAX_PHYS];
+
+	my_ha->sas_ha.sas_addr = &my_ha->sas_addr[0];
+
+	for (i = 0; i < MAX_PHYS; i++) {
+		sas_phys[i] = &my_ha->phys[i].sas_phy;
+		sas_ports[i] = &my_ha->sas_ports[i];
+	}
+
+	my_ha->sas_ha.sas_phy  = sas_phys;
+	my_ha->sas_ha.sas_port = sas_ports;
+	my_ha->sas_ha.num_phys = MAX_PHYS;
+
+	my_ha->sas_ha.lldd_port_formed = my_port_formed;
+
+	my_ha->sas_ha.lldd_dev_found = my_dev_found;
+	my_ha->sas_ha.lldd_dev_gone = my_dev_gone;
+
+	my_ha->sas_ha.lldd_max_execute_num = lldd_max_execute_num; (1)
+
+	my_ha->sas_ha.lldd_queue_size = ha_can_queue;
+	my_ha->sas_ha.lldd_execute_task = my_execute_task;
+
+	my_ha->sas_ha.lldd_abort_task     = my_abort_task;
+	my_ha->sas_ha.lldd_abort_task_set = my_abort_task_set;
+	my_ha->sas_ha.lldd_clear_aca      = my_clear_aca;
+	my_ha->sas_ha.lldd_clear_task_set = my_clear_task_set;
+	my_ha->sas_ha.lldd_I_T_nexus_reset= NULL; (2)
+	my_ha->sas_ha.lldd_lu_reset       = my_lu_reset;
+	my_ha->sas_ha.lldd_query_task     = my_query_task;
+
+	my_ha->sas_ha.lldd_clear_nexus_port = my_clear_nexus_port;
+	my_ha->sas_ha.lldd_clear_nexus_ha = my_clear_nexus_ha;
+
+	my_ha->sas_ha.lldd_control_phy = my_control_phy;
+
+	return sas_register_ha(&my_ha->sas_ha);
+}
+
+(1) This is normally a LLDD parameter, something of the
+lines of a task collector.  What it tells the SAS Layer is
+whether the SAS layer should run in Direct Mode (default:
+value 0 or 1) or Task Collector Mode (value greater than 1).
+
+In Direct Mode, the SAS Layer calls Execute Task as soon as
+it has a command to send to the SDS, _and_ this is a single
+command, i.e. not linked.
+
+Some hardware (e.g. aic94xx) has the capability to DMA more
+than one task at a time (interrupt) from host memory.  Task
+Collector Mode is an optional feature for HAs which support
+this in their hardware.  (Again, it is completely optional
+even if your hardware supports it.)
+
+In Task Collector Mode, the SAS Layer would do _natural_
+coalescing of tasks and at the appropriate moment it would
+call your driver to DMA more than one task in a single HA
+interrupt. DMBS may want to use this by insmod/modprobe
+setting the lldd_max_execute_num to something greater than
+1.
+
+(2) SAS 1.1 does not define I_T Nexus Reset TMF.
+
+Events
+------
+
+Events are _the only way_ a SAS LLDD notifies the SAS layer
+of anything.  There is no other method or way a LLDD to tell
+the SAS layer of anything happening internally or in the SAS
+domain.
+
+Phy events:
+	PHYE_LOSS_OF_SIGNAL, (C)
+	PHYE_OOB_DONE,
+	PHYE_OOB_ERROR,      (C)
+	PHYE_SPINUP_HOLD.
+
+Port events, passed on a _phy_:
+	PORTE_BYTES_DMAED,      (M)
+	PORTE_BROADCAST_RCVD,   (E)
+	PORTE_LINK_RESET_ERR,   (C)
+	PORTE_TIMER_EVENT,      (C)
+	PORTE_HARD_RESET.
+
+Host Adapter event:
+	HAE_RESET
+
+A SAS LLDD should be able to generate
+	- at least one event from group C (choice),
+	- events marked M (mandatory) are mandatory (only one),
+	- events marked E (expander) if it wants the SAS layer
+	  to handle domain revalidation (only one such).
+	- Unmarked events are optional.
+
+Meaning:
+
+HAE_RESET -- when your HA got internal error and was reset.
+
+PORTE_BYTES_DMAED -- on receiving an IDENTIFY/FIS frame
+PORTE_BROADCAST_RCVD -- on receiving a primitive
+PORTE_LINK_RESET_ERR -- timer expired, loss of signal, loss
+of DWS, etc. (*)
+PORTE_TIMER_EVENT -- DWS reset timeout timer expired (*)
+PORTE_HARD_RESET -- Hard Reset primitive received.
+
+PHYE_LOSS_OF_SIGNAL -- the device is gone (*)
+PHYE_OOB_DONE -- OOB went fine and oob_mode is valid
+PHYE_OOB_ERROR -- Error while doing OOB, the device probably
+got disconnected. (*)
+PHYE_SPINUP_HOLD -- SATA is present, COMWAKE not sent.
+
+(*) should set/clear the appropriate fields in the phy,
+    or alternatively call the inlined sas_phy_disconnected()
+    which is just a helper, from their tasklet.
+
+The Execute Command SCSI RPC:
+
+	int (*lldd_execute_task)(struct sas_task *, int num,
+				 unsigned long gfp_flags);
+
+Used to queue a task to the SAS LLDD.  @task is the tasks to
+be executed.  @num should be the number of tasks being
+queued at this function call (they are linked listed via
+task::list), @gfp_mask should be the gfp_mask defining the
+context of the caller.
+
+This function should implement the Execute Command SCSI RPC,
+or if you're sending a SCSI Task as linked commands, you
+should also use this function.
+
+That is, when lldd_execute_task() is called, the command(s)
+go out on the transport *immediately*.  There is *no*
+queuing of any sort and at any level in a SAS LLDD.
+
+The use of task::list is two-fold, one for linked commands,
+the other discussed below.
+
+It is possible to queue up more than one task at a time, by
+initializing the list element of struct sas_task, and
+passing the number of tasks enlisted in this manner in num.
+
+Returns: -SAS_QUEUE_FULL, -ENOMEM, nothing was queued;
+	 0, the task(s) were queued.
+
+If you want to pass num > 1, then either
+A) you're the only caller of this function and keep track
+   of what you've queued to the LLDD, or
+B) you know what you're doing and have a strategy of
+   retrying.
+
+As opposed to queuing one task at a time (function call),
+batch queuing of tasks, by having num > 1, greatly
+simplifies LLDD code, sequencer code, and _hardware design_,
+and has some performance advantages in certain situations
+(DBMS).
+
+The LLDD advertises if it can take more than one command at
+a time at lldd_execute_task(), by setting the
+lldd_max_execute_num parameter (controlled by "collector"
+module parameter in aic94xx SAS LLDD).
+
+You should leave this to the default 1, unless you know what
+you're doing.
+
+This is a function of the LLDD, to which the SAS layer can
+cater to.
+
+int lldd_queue_size
+	The host adapter's queue size.  This is the maximum
+number of commands the lldd can have pending to domain
+devices on behalf of all upper layers submitting through
+lldd_execute_task().
+
+You really want to set this to something (much) larger than
+1.
+
+This _really_ has absolutely nothing to do with queuing.
+There is no queuing in SAS LLDDs.
+
+struct sas_task {
+	dev -- the device this task is destined to
+	list -- must be initialized (INIT_LIST_HEAD)
+	task_proto -- _one_ of enum sas_proto
+	scatter -- pointer to scatter gather list array
+	num_scatter -- number of elements in scatter
+	total_xfer_len -- total number of bytes expected to be transfered
+	data_dir -- PCI_DMA_...
+	task_done -- callback when the task has finished execution
+};
+
+When an external entity, entity other than the LLDD or the
+SAS Layer, wants to work with a struct domain_device, it
+_must_ call kobject_get() when getting a handle on the
+device and kobject_put() when it is done with the device.
+
+This does two things:
+     A) implements proper kfree() for the device;
+     B) increments/decrements the kref for all players:
+     domain_device
+	all domain_device's ... (if past an expander)
+	    port
+		host adapter
+		     pci device
+			 and up the ladder, etc.
+
+DISCOVERY
+---------
+
+The sysfs tree has the following purposes:
+    a) It shows you the physical layout of the SAS domain at
+       the current time, i.e. how the domain looks in the
+       physical world right now.
+    b) Shows some device parameters _at_discovery_time_.
+
+This is a link to the tree(1) program, very useful in
+viewing the SAS domain:
+ftp://mama.indstate.edu/linux/tree/
+I expect user space applications to actually create a
+graphical interface of this.
+
+That is, the sysfs domain tree doesn't show or keep state if
+you e.g., change the meaning of the READY LED MEANING
+setting, but it does show you the current connection status
+of the domain device.
+
+Keeping internal device state changes is responsibility of
+upper layers (Command set drivers) and user space.
+
+When a device or devices are unplugged from the domain, this
+is reflected in the sysfs tree immediately, and the device(s)
+removed from the system.
+
+The structure domain_device describes any device in the SAS
+domain.  It is completely managed by the SAS layer.  A task
+points to a domain device, this is how the SAS LLDD knows
+where to send the task(s) to.  A SAS LLDD only reads the
+contents of the domain_device structure, but it never creates
+or destroys one.
+
+Expander management from User Space
+-----------------------------------
+
+In each expander directory in sysfs, there is a file called
+"smp_portal".  It is a binary sysfs attribute file, which
+implements an SMP portal (Note: this is *NOT* an SMP port),
+to which user space applications can send SMP requests and
+receive SMP responses.
+
+Functionality is deceptively simple:
+
+1. Build the SMP frame you want to send. The format and layout
+   is described in the SAS spec.  Leave the CRC field equal 0.
+open(2)
+2. Open the expander's SMP portal sysfs file in RW mode.
+write(2)
+3. Write the frame you built in 1.
+read(2)
+4. Read the amount of data you expect to receive for the frame you built.
+   If you receive different amount of data you expected to receive,
+   then there was some kind of error.
+close(2)
+All this process is shown in detail in the function do_smp_func()
+and its callers, in the file "expander_conf.c".
+
+The kernel functionality is implemented in the file
+"sas_expander.c".
+
+The program "expander_conf.c" implements this. It takes one
+argument, the sysfs file name of the SMP portal to the
+expander, and gives expander information, including routing
+tables.
+
+The SMP portal gives you complete control of the expander,
+so please be careful.
diff -urN oldtree/drivers/scsi/sas/expander_conf.c newtree/drivers/scsi/sas/expander_conf.c
--- oldtree/drivers/scsi/sas/expander_conf.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/sas/expander_conf.c	2006-02-13 19:20:00.645414184 -0500
@@ -0,0 +1,463 @@
+/*
+ * Serial Attached SCSI (SAS) Expander communication user space program
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/sas-class/expander_conf.c#7 $
+ */
+
+/* This is a simple program to show how to communicate with
+ * expander devices in a SAS domain.
+ *
+ * The process is simple:
+ * 1. Build the SMP frame you want to send. The format and layout
+ *    is described in the SAS spec.  Leave the CRC field equal 0.
+ * 2. Open the expander's SMP portal sysfs file in RW mode.
+ * 3. Write the frame you built in 1.
+ * 4. Read the amount of data you expect to receive for the frame you built.
+ *    If you receive different amount of data you expected to receive,
+ *    then there was some kind of error.
+ * All this process is shown in detail in the function do_smp_func()
+ * and its callers, below.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <string.h>
+#include <errno.h>
+#include <endian.h>
+#include <byteswap.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#define LEFT_FIELD_SIZE 25
+
+#ifdef __LITTLE_ENDIAN
+#define be64_to_cpu(_x)  __bswap_64(*(uint64_t *)(&(_x)))
+#define be32_to_cpu(_x)  __bswap_32(*(uint32_t *)(&(_x)))
+#define be16_to_cpu(_x)  __bswap_16(*(uint16_t *)(&(_x)))
+#define cpu_to_be64(_x)  __bswap_64(*(uint64_t *)(&(_x)))
+#define cpu_to_be32(_x)  __bswap_32(*(uint32_t *)(&(_x)))
+#define cpu_to_be16(_x)  __bswap_16(*(uint16_t *)(&(_x)))
+#else
+#define be64_to_cpu(_x)  (_x)
+#define be32_to_cpu(_x)  (_x)
+#define be16_to_cpu(_x)  (_x)
+#define cpu_to_be64(_x)  (_x)
+#define cpu_to_be32(_x)  (_x)
+#define cpu_to_be16(_x)  (_x)
+#endif
+
+#define SAS_ADDR(_x) ((unsigned long long) be64_to_cpu(*(uint64_t *)(_x)))
+#define SAS_ADDR_SIZE 8
+
+const char *prog;
+
+struct route_table_entry {
+	int      disabled;
+	uint8_t  routed_sas_addr[SAS_ADDR_SIZE];
+};
+
+struct expander {
+	int     num_phys;
+	uint8_t *phy_attr;
+	int     route_indexes;
+};
+
+static int do_smp_func(char *smp_portal_name, void *smp_req, int smp_req_size,
+		       void *smp_resp, int smp_resp_size)
+{
+	int fd;
+	ssize_t res;
+
+	fd = open(smp_portal_name, O_RDWR);
+	if (fd == -1) {
+		printf("%s: opening %s: %s(%d)\n", prog, smp_portal_name,
+		       strerror(errno), errno);
+		return fd;
+	}
+
+	res = write(fd, smp_req, smp_req_size);
+	if (!res) {
+		printf("%s: nothing could be written to %s\n", prog,
+		       smp_portal_name);
+		goto out_err;
+	} else if (res == -1) {
+		printf("%s: writing to %s: %s(%d)\n", prog, smp_portal_name,
+		       strerror(errno), errno);
+		goto out_err;
+	}
+
+	res = read(fd, smp_resp, smp_resp_size);
+	if (!res) {
+		printf("%s: nothing could be read from %s\n", prog,
+		       smp_portal_name);
+		goto out_err;
+	} else if (res == -1) {
+		printf("%s: reading from %s: %s(%d)\n", prog, smp_portal_name,
+		       strerror(errno), errno);
+		goto out_err;
+	}
+	close(fd);
+	return res;
+ out_err:
+	close(fd);
+	return -1;
+}
+
+#define MI_REQ_SIZE   8
+#define MI_RESP_SIZE 64
+
+static unsigned char mi_req[MI_REQ_SIZE] = { 0x40, 1, 0, };
+static unsigned char mi_resp[MI_RESP_SIZE];
+
+#define MI_FIELD_SIZE 20
+#define MI_PRINTS(a, b) printf("%*s %*s\n",LEFT_FIELD_SIZE,a,MI_FIELD_SIZE,b)
+#define MI_PRINTD(a, b) printf("%*s %*u\n",LEFT_FIELD_SIZE,a,MI_FIELD_SIZE,b)
+#define MI_PRINTA(a, b) printf("%*s %0*llx\n",LEFT_FIELD_SIZE,a,MI_FIELD_SIZE,b)
+
+static int mi_expander(char *smp_portal_name, struct expander *ex)
+{
+	int res;
+
+	res = do_smp_func(smp_portal_name, mi_req, MI_REQ_SIZE,
+			  mi_resp, MI_RESP_SIZE);
+	if (res == MI_RESP_SIZE && mi_resp[2] == 0) {
+		char buf[20];
+
+		memcpy(buf, mi_resp+12, 8);
+		buf[8] = 0;
+		MI_PRINTS("Vendor:", buf);
+
+		memcpy(buf, mi_resp+20, 16);
+		buf[16] = 0;
+		MI_PRINTS("Product:", buf);
+
+		memcpy(buf, mi_resp+36, 4);
+		buf[4] = 0;
+		MI_PRINTS("Revision:", buf);
+
+		if (!(mi_resp[8] & 1))
+			return 0;
+
+		memcpy(buf, mi_resp+40, 8);
+		buf[8] = 0;
+		MI_PRINTS("Component:", buf);
+
+		MI_PRINTD("Component ID:", be16_to_cpu(mi_resp[48]));
+		MI_PRINTD("Component revision:", mi_resp[50]);
+	}
+	return 0;
+}
+
+#define RG_REQ_SIZE   8
+#define RG_RESP_SIZE 32
+
+static unsigned char rg_req[RG_REQ_SIZE] = { 0x40, 0, };
+static unsigned char rg_resp[RG_RESP_SIZE];
+
+static int rg_expander(char *smp_portal_name, struct expander *ex)
+{
+	int res;
+
+	res = do_smp_func(smp_portal_name, rg_req, RG_REQ_SIZE, rg_resp,
+			  RG_RESP_SIZE);
+
+	if (res == RG_RESP_SIZE && rg_resp[2] == 0) {
+		MI_PRINTD("Expander Change Count:", be16_to_cpu(rg_resp[4]));
+		MI_PRINTD("Expander Route Indexes:", be16_to_cpu(rg_resp[6]));
+		ex->route_indexes = be16_to_cpu(rg_resp[6]);
+		MI_PRINTD("Number of phys:", rg_resp[9]);
+		ex->num_phys = rg_resp[9];
+		MI_PRINTS("Configuring:", (rg_resp[10] & 2) ? "Yes" : "No");
+		MI_PRINTS("Configurable route table:",
+			  (rg_resp[10] & 1) ? "Yes" : "No");
+		MI_PRINTA("Enclosure Logical Identifier:",
+			  SAS_ADDR(rg_resp+12));
+		ex->phy_attr = malloc(ex->num_phys * sizeof(*ex->phy_attr));
+	}
+	return 0;
+}
+
+#define DISCOVER_REQ_SIZE  16
+#define DISCOVER_RESP_SIZE 56
+
+static unsigned char disc_req[DISCOVER_REQ_SIZE] = {0x40, 0x10, 0, };
+static unsigned char disc_resp[DISCOVER_RESP_SIZE];
+
+#define PHY_EEXIST 0x10
+#define PHY_VACANT 0x16
+
+static const char *attached_dev_type[8] = {
+	[0] = "none",
+	[1] = "end device",
+	[2] = "edge expander",
+	[3] = "fanout expander",
+	[4 ... 7] = "unknown",
+};
+
+static const char *phy_link_rate[16] = {
+	[0] = "unknown",
+	[1] = "disabled",
+	[2] = "phy reset problem",
+	[3] = "spinup hold",
+	[4] = "port selector",
+	[5 ... 7] = "unknown",
+	[8] = "G1 (1,5 Gb/s)",
+	[9] = "G2 (3 GB/s)",
+	[10 ... 15] = "Unknown",
+};
+
+static const char *proto_table[8] = {
+	"",
+	"SMP", "STP", "STP|SMP", "SSP",
+	"SSP|SMP", "SSP|STP", "SSP|STP|SMP",
+};
+
+#define DIRECT_ROUTING      0
+#define SUBTRACTIVE_ROUTING 1
+#define TABLE_ROUTING       2
+
+static const char *routing_attr[8] = {
+	[DIRECT_ROUTING] = "D",
+	[SUBTRACTIVE_ROUTING] = "S",
+	[TABLE_ROUTING] = "T",
+	[3 ... 7] = "x",
+};
+
+static const char *conn_type[0x80] = {
+	[0] = "No information",
+	[1] = "SAS external receptacle (i.e., SFF-8470)(see SAS-1.1)",
+	[2] = "SAS external compact receptacle (i.e., SFF-8088)(see SAS-1.1)",
+	[3 ... 0x0f ] = "External connector",
+	[0x10] = "SAS internal wide plug (i.e., SFF-8484)(see SAS-1.1)",
+	[0x11] = "SAS internal compact wide plug (i.e., SFF-8087)(see SAS-1.1)",
+	[0x12 ... 0x1f] = "Internal wide connector",
+	[0x20] = "SAS backplane receptacle (i.e., SFF-8482)(see SAS-1.1)",
+	[0x21] = "SATA-style host plug (i.e., ATA/ATAPI-7 V3)(see SAS-1.1)",
+	[0x22] = "SAS plug (i.e., SFF-8482)(see SAS-1.1)",
+	[0x23] = "SATA device plug (i.e., ATA/ATAPI-7 V3)(see SAS-1.1)",
+	[0x24 ... 0x2f] = "Internal connector to end device",
+	[0x30 ... 0x6f] = "Unknown\n",
+	[0x70 ... 0x7f] = "Vendor specific",
+};
+
+static int discover_phy(char *smp_portal_name, int phy_id, struct expander *ex)
+{
+	int res;
+	const char *dev_str;
+
+	disc_req[9] = phy_id;
+
+	res = do_smp_func(smp_portal_name, disc_req, DISCOVER_REQ_SIZE,
+			  disc_resp, DISCOVER_RESP_SIZE);
+
+	if (res != DISCOVER_RESP_SIZE) {
+		printf("%s: error disovering phy %d\n", prog, phy_id);
+		goto out;
+	}
+	switch (disc_resp[2]) {
+	case PHY_VACANT:
+		printf("phy%02d: vacant\n", phy_id);
+		goto out; break;
+	case PHY_EEXIST:
+		printf("phy%02d doesn't exist\n", phy_id);
+		goto out; break;
+	case 0:
+		break;
+	default:
+		printf("phy%02d SMP function result: 0x%x\n",
+		       phy_id, disc_resp[2]);
+		goto out;
+	}
+
+	printf("Phy%02d:%s    attached: %016llx:%02d    chg count:%02d\n",
+	       phy_id, routing_attr[disc_resp[44] & 0x0F],
+	       SAS_ADDR(disc_resp+24), disc_resp[32], disc_resp[42]);
+
+	if (ex->phy_attr)
+		ex->phy_attr[phy_id] = disc_resp[44] & 0x0F;
+
+	if (disc_resp[14] & 1)
+		dev_str = "SATA Host";
+	else if (disc_resp[15] & 0x80)
+		dev_str = "SATA Port Selector";
+	else if (disc_resp[15] & 1)
+		dev_str = "SATA device";
+	else
+		dev_str = attached_dev_type[(disc_resp[12] & 0x70) >> 4];
+	printf(" Attached device: %15s    Link rate: %15s\n",
+	       dev_str, phy_link_rate[disc_resp[13] & 0xf]);
+
+	printf(" Tproto: %15s    Iproto: %15s\n",
+	       proto_table[(disc_resp[15] & 0xe) >> 1],
+	       proto_table[(disc_resp[14] & 0xe) >> 1]);
+
+	printf(" Programmed MIN-MAX linkrate: %s - %s\n",
+	       phy_link_rate[(disc_resp[40] & 0xF0)>>4],
+	       phy_link_rate[(disc_resp[41] & 0xF0)>>4]);
+
+	printf(" Hardware   MIN-MAX linkrate: %s - %s\n",
+	       phy_link_rate[(disc_resp[40] & 0x0F)],
+	       phy_link_rate[(disc_resp[41] & 0x0F)]);
+
+	printf(" %s phy\n", (disc_resp[43] & 0x80) ? "Virtual" : "Physical");
+	printf(" Partial pathway timeout value: %d microseconds\n",
+	       disc_resp[43] & 0x0F);
+
+	printf(" Connector type: %s\n", conn_type[disc_resp[45] & 0x7F]);
+	printf(" Connector element index: %d\n", disc_resp[46]);
+	printf(" Connector physical link: %d\n", disc_resp[47]);
+out:
+	return 0;
+}
+
+#define RPEL_REQ_SIZE  16
+#define RPEL_RESP_SIZE 32
+
+static unsigned char rpel_req[RPEL_REQ_SIZE] = { 0x40, 0x11, 0, };
+static unsigned char rpel_resp[RPEL_RESP_SIZE];
+
+static int report_phy_error_log(char *smp_portal_name, int phy_id)
+{
+	int res;
+
+	rpel_req[9] = phy_id;
+
+	res = do_smp_func(smp_portal_name, rpel_req, RPEL_REQ_SIZE,
+			  rpel_resp, RPEL_RESP_SIZE);
+	if (res != RPEL_RESP_SIZE) {
+		printf("%s: error reading error log for phy %d (%d)\n",
+		       prog, phy_id, res);
+		goto out;
+	}
+	MI_PRINTD(" Invalid DW count:", be32_to_cpu(rpel_resp[12]));
+	MI_PRINTD(" RD error count:", be32_to_cpu(rpel_resp[16]));
+	MI_PRINTD(" DW sync loss count:", be32_to_cpu(rpel_resp[20]));
+	MI_PRINTD(" Reset problem count:", be32_to_cpu(rpel_resp[24]));
+out:
+	return 0;
+}
+
+#define RRI_REQ_SIZE  16
+#define RRI_RESP_SIZE 44
+
+static unsigned char rri_req[RRI_REQ_SIZE] = { 0x40, 0x13, 0, };
+static unsigned char rri_resp[RRI_RESP_SIZE];
+
+static int show_routing_table(char *smp_portal_name, int phy_id,
+			      struct expander *ex)
+{
+	struct route_table_entry *rt;
+	int res, i, last_non_zero = -1;
+
+	if (!ex->phy_attr || ex->phy_attr[phy_id] != TABLE_ROUTING)
+		return 0;
+
+	rt = malloc(sizeof(struct route_table_entry)*ex->route_indexes);
+	if (!rt)
+		return 0;
+
+	rri_req[9] = phy_id;
+
+	for (i = 0; i < ex->route_indexes; i++) {
+		*(uint16_t *)(rri_req+6) = cpu_to_be16(i);
+		res = do_smp_func(smp_portal_name, rri_req, RRI_REQ_SIZE,
+				  rri_resp, RRI_RESP_SIZE);
+		if (res != RRI_RESP_SIZE) {
+			printf("Error getting phy %d route index %d (%d)\n",
+			       phy_id, i, res);
+			goto out;
+		}
+		if (rri_resp[2] != 0)
+			break;
+		if (be16_to_cpu(rri_resp[6]) != i) {
+			printf("Expander FW error for phy %d index %d\n",
+			       phy_id, i);
+			goto out;
+		}
+		rt[i].disabled = rri_resp[12] & 0x80 ? 1 : 0;
+		memcpy(rt[i].routed_sas_addr, rri_resp+16, SAS_ADDR_SIZE);
+		if (rt[i].routed_sas_addr[0])
+			last_non_zero = i;
+	}
+	printf(" Routing Table\n");
+	if (last_non_zero == -1)
+		printf("  Empty (all zero)\n");
+	else {
+		for (i = 0; i <= last_non_zero; i++)
+			printf("  %02d %8s %016llx\n",
+			       i, rt[i].disabled ? "disabled" : "enabled",
+			       SAS_ADDR(rt[i].routed_sas_addr));
+	}
+out:
+	free(rt);
+	return 0;
+}
+
+static int discover_expander(char *smp_portal_name, struct expander *ex)
+{
+	int i;
+
+	for (i = 0; i < ex->num_phys; i++) {
+		printf("\n");
+		discover_phy(smp_portal_name, i, ex);
+		report_phy_error_log(smp_portal_name, i);
+		show_routing_table(smp_portal_name, i, ex);
+	}
+
+	return 0;
+}
+
+static void print_info(void)
+{
+	printf("%s <smp portal file>\n", prog);
+	printf("\tWhere <smp portal file> is the binary attribute file of the"
+	       "\n\texpander device in sysfs.\n");
+}
+
+int main(int argc, char *argv[])
+{
+	prog = strrchr(argv[0], '/');
+	if (prog)
+		prog++;
+	else
+		prog = argv[0];
+
+	if (argc < 2) {
+		print_info();
+		return 0;
+	} else {
+		struct expander ex;
+
+		memset(&ex, 0, sizeof(ex));
+
+		mi_expander(argv[1], &ex);
+		rg_expander(argv[1], &ex);
+		discover_expander(argv[1], &ex);
+		if (ex.phy_attr)
+			free(ex.phy_attr);
+	}
+	return 0;
+}
diff -urN oldtree/drivers/scsi/sas/sas_common.c newtree/drivers/scsi/sas/sas_common.c
--- oldtree/drivers/scsi/sas/sas_common.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/sas/sas_common.c	2006-02-13 19:20:00.645414184 -0500
@@ -0,0 +1,115 @@
+/*
+ * Serial Attached SCSI (SAS) class common functions
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * $Id: //depot/sas-class/sas_common.c#9 $
+ */
+
+#include <scsi/sas/sas_class.h>
+#include "sas_internal.h"
+
+int sas_show_class(enum sas_class class, char *buf)
+{
+	static const char *class_str[] = {
+		[SAS] = "SAS",
+		[EXPANDER] = "EXPANDER",
+	};
+	return sprintf(buf, "%s\n", class_str[class]);
+}
+
+int sas_show_proto(enum sas_proto proto, char *page)
+{
+	static const char *proto_str[] = {
+		[SATA_PROTO] = "SATA",
+		[SAS_PROTO_SMP] = "SMP",
+		[SAS_PROTO_STP] = "STP",
+		[SAS_PROTO_SSP] = "SSP",
+	};
+	int  v;
+	char *buf = page;
+
+	for (v = 1; proto != 0 && v <= SAS_PROTO_SSP; v <<= 1) {
+		if (v & proto) {
+			buf += sprintf(buf, "%s", proto_str[v]);
+
+			if (proto & ~((v<<1)-1))
+				buf += sprintf(buf, "|");
+			else
+				buf += sprintf(buf, "\n");
+		}
+	}
+	return buf-page;
+}
+
+int sas_show_linkrate(enum sas_phy_linkrate linkrate, char *page)
+{
+	static const char *phy_linkrate_str[] = {
+		[PHY_LINKRATE_NONE] = "",
+		[PHY_DISABLED] = "disabled",
+		[PHY_RESET_PROBLEM] = "phy reset problem",
+		[PHY_SPINUP_HOLD] = "phy spinup hold",
+		[PHY_PORT_SELECTOR] = "phy port selector",
+		[PHY_LINKRATE_1_5] = "1,5 GB/s",
+		[PHY_LINKRATE_3]  = "3,0 GB/s",
+		[PHY_LINKRATE_6] = "6,0 GB/s",
+	};
+	return sprintf(page, "%s\n", phy_linkrate_str[linkrate]);
+}
+
+int sas_show_oob_mode(enum sas_oob_mode oob_mode, char *buf)
+{
+	switch (oob_mode) {
+	case OOB_NOT_CONNECTED:
+		return sprintf(buf, "%s", "");
+		break;
+	case SATA_OOB_MODE:
+		return sprintf(buf, "%s\n", "SATA");
+		break;
+	case SAS_OOB_MODE:
+		return sprintf(buf, "%s\n", "SAS");
+		break;
+	}
+	return 0;
+}
+
+void sas_hash_addr(u8 *hashed, const u8 *sas_addr)
+{
+	const u32 poly = 0x00DB2777;
+	u32 	r = 0;
+	int 	i;
+
+	for (i = 0; i < 8; i++) {
+		int b;
+		for (b = 7; b >= 0; b--) {
+			r <<= 1;
+			if ((1 << b) & sas_addr[i]) {
+				if (!(r & 0x01000000))
+					r ^= poly;
+			} else if (r & 0x01000000)
+				r ^= poly;
+		}
+	}
+
+	hashed[0] = (r >> 16) & 0xFF;
+	hashed[1] = (r >> 8) & 0xFF ;
+	hashed[2] = r & 0xFF;
+}
diff -urN oldtree/drivers/scsi/sas/sas_discover.c newtree/drivers/scsi/sas/sas_discover.c
--- oldtree/drivers/scsi/sas/sas_discover.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/sas/sas_discover.c	2006-02-13 19:20:00.647413880 -0500
@@ -0,0 +1,1599 @@
+/*
+ * Serial Attached SCSI (SAS) Discover process
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/sas-class/sas_discover.c#140 $
+ */
+
+#include <linux/pci.h>
+#include <linux/scatterlist.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_eh.h>
+#include "sas_internal.h"
+#include <scsi/sas/sas_task.h>
+#include <scsi/sas/sas_discover.h>
+
+/* ---------- Domain device attributes ---------- */
+
+ssize_t dev_show_type(struct domain_device *dev, char *page)
+{
+	static const char *dev_type[] = {
+		"no device",
+		"end device",
+		"edge expander",
+		"fanout expander",
+		"host adapter",
+		"sata device",
+		"sata port multiplier",
+		"sata port multiplier port",
+	};
+	return sprintf(page, "%s\n", dev_type[dev->dev_type]);
+}
+
+ssize_t dev_show_iproto(struct domain_device *dev, char *page)
+{
+	return sas_show_proto(dev->iproto, page);
+}
+
+ssize_t dev_show_tproto(struct domain_device *dev, char *page)
+{
+	return sas_show_proto(dev->tproto, page);
+}
+
+ssize_t dev_show_sas_addr(struct domain_device *dev, char *page)
+{
+	return sprintf(page, "%llx\n", SAS_ADDR(dev->sas_addr));
+}
+
+ssize_t dev_show_linkrate(struct domain_device *dev, char *page)
+{
+	return sas_show_linkrate(dev->linkrate, page);
+}
+
+ssize_t dev_show_min_linkrate(struct domain_device *dev, char *page)
+{
+	return sas_show_linkrate(dev->min_linkrate, page);
+}
+
+ssize_t dev_show_max_linkrate(struct domain_device *dev, char *page)
+{
+	return sas_show_linkrate(dev->max_linkrate, page);
+}
+
+ssize_t dev_show_pathways(struct domain_device *dev, char *page)
+{
+	return sprintf(page, "%d\n", dev->pathways);
+}
+
+/* ---------- SATA specific sysfs ---------- */
+
+static ssize_t sata_show_command_set(struct domain_device *dev, char *page)
+{
+	static const char *cs[] = {
+		"ATA",
+		"ATAPI",
+	};
+	return sprintf(page, "%s\n", cs[dev->sata_dev.command_set]);
+}
+
+static ssize_t sata_show_rps_resp(struct domain_device *dev, char *page)
+{
+	char *buf = page;
+	if ((dev->tproto & SAS_PROTO_STP) &&
+	    dev->sata_dev.rps_resp.frame_type == SMP_RESPONSE &&
+	    dev->sata_dev.rps_resp.function == SMP_REPORT_PHY_SATA &&
+	    dev->sata_dev.rps_resp.result == SMP_RESP_FUNC_ACC) {
+		int i = 0;
+		u8 *p = (u8 *) &dev->sata_dev.rps_resp;
+		for (i = 0; i < sizeof(struct smp_resp); i+=4, p+=4) {
+			buf += sprintf(buf, "%02x %02x %02x %02x\n",
+				       *(p+0), *(p+1), *(p+2), *(p+3));
+		}
+	}
+	return buf-page;
+}
+
+static inline int show_chars(__le16 *p, int start, int words, char *page)
+{
+	int i;
+	char *buf = page;
+
+	for (i = start; i < start+words; i++) {
+		u16  s = le16_to_cpu(p[i]);
+		char a = (s&0xFF00)>>8;
+		char b = s&0x00FF;
+
+		if (a == 0)
+			break;
+		buf += sprintf(buf, "%c", a);
+		if (b == 0)
+			break;
+		buf += sprintf(buf, "%c", b);
+	}
+	return buf-page;
+}
+
+static ssize_t sata_show_serial_number(struct domain_device *dev, char *page)
+{
+	char *buf = page;
+	__le16 *identify_x = NULL;
+
+	if (dev->sata_dev.command_set == ATA_COMMAND_SET)
+		identify_x = dev->sata_dev.identify_device;
+	else
+		identify_x = dev->sata_dev.identify_packet_device;
+
+	if (identify_x &&
+	    (dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM_PORT)) {
+		buf += show_chars(identify_x, 10, 10, buf);
+		buf += sprintf(buf, "\n");
+	}
+	return buf-page;
+}
+
+static ssize_t sata_show_firmware_rev(struct domain_device *dev, char *page)
+{
+	char *buf = page;
+	__le16 *identify_x = NULL;
+
+	if (dev->sata_dev.command_set == ATA_COMMAND_SET)
+		identify_x = dev->sata_dev.identify_device;
+	else
+		identify_x = dev->sata_dev.identify_packet_device;
+
+	if (identify_x &&
+	    (dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM_PORT)) {
+		buf += show_chars(identify_x, 23, 4, buf);
+		buf += sprintf(buf, "\n");
+	}
+	return buf-page;
+}
+
+static ssize_t sata_show_model_number(struct domain_device *dev, char *page)
+{
+	char *buf = page;
+	__le16 *identify_x = NULL;
+
+	if (dev->sata_dev.command_set == ATA_COMMAND_SET)
+		identify_x = dev->sata_dev.identify_device;
+	else
+		identify_x = dev->sata_dev.identify_packet_device;
+
+	if (identify_x &&
+	    (dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM_PORT)) {
+		buf += show_chars(identify_x, 27, 20, buf);
+		buf += sprintf(buf, "\n");
+	}
+	return buf-page;
+}
+
+static ssize_t sata_show_identify_device(struct domain_device *dev, char *page)
+{
+	char *buf = page;
+
+	if (dev->sata_dev.identify_device &&
+	    (dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM_PORT)) {
+		__le16 *p = dev->sata_dev.identify_device;
+		int i;
+
+		for (i = 0; i < 16; i++) {
+			int k;
+			for (k = 0; k < 16; k++)
+				buf += sprintf(buf, "%04x%s",
+					      le16_to_cpu(p[i*16+k]),
+					      k==15 ? "\n" : " ");
+		}
+	}
+	return buf-page;
+}
+
+static ssize_t sata_show_identify_packet(struct domain_device *dev, char *page)
+{
+	char *buf = page;
+
+	if (dev->sata_dev.identify_packet_device &&
+	    (dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM_PORT)) {
+		__le16 *p = dev->sata_dev.identify_packet_device;
+		int i;
+
+		for (i = 0; i < 16; i++) {
+			int k;
+			for (k = 0; k < 16; k++)
+				buf += sprintf(buf, "%04x%s",
+					      le16_to_cpu(p[i*16+k]),
+					      k==15 ? "\n" : " ");
+		}
+	}
+
+	return buf-page;
+}
+
+static ssize_t sata_show_port_no(struct domain_device *dev, char *page)
+{
+	int res = 0;
+
+	if (dev->dev_type == SATA_PM || dev->dev_type == SATA_PM_PORT)
+		res = sprintf(page, "%02Xh\n", dev->sata_dev.port_no);
+	return res;
+}
+
+/* ---------- SAS end device specific ---------- */
+
+static ssize_t sas_show_rled_meaning(struct domain_device *dev, char *page)
+{
+	return sprintf(page, "%d\n", dev->end_dev.ready_led_meaning);
+}
+
+static ssize_t sas_show_itnl_timeout(struct domain_device *dev, char *page)
+{
+	return sprintf(page, "0x%04x\n", dev->end_dev.itnl_timeout);
+}
+
+static ssize_t sas_show_iresp_timeout(struct domain_device *dev, char *page)
+{
+	return sprintf(page, "0x%04x\n", dev->end_dev.iresp_timeout);
+}
+
+static ssize_t sas_show_rl_wlun(struct domain_device *dev, char *page)
+{
+	return sprintf(page, "%d\n", dev->end_dev.rl_wlun);
+}
+
+/* ---------- LU specific ---------- */
+
+static ssize_t lu_show_lun(struct LU *lu, char *page)
+{
+	return sprintf(page, "%016llx\n", SAS_ADDR(lu->LUN));
+}
+
+static ssize_t lu_show_inq(struct LU *lu, char *_page)
+{
+	int i;
+	char *buf = _page;
+	if (lu->inquiry_valid_data_len <= 0)
+		return 0;
+	for (i = 0; i < lu->inquiry_valid_data_len; i += 4) {
+		buf += sprintf(buf, "%02x %02x %02x %02x\n",
+			       lu->inquiry_data[i+0], lu->inquiry_data[i+1],
+			       lu->inquiry_data[i+2], lu->inquiry_data[i+3]);
+	}
+	return buf-_page;
+}
+
+static ssize_t lu_show_tm_type(struct LU *lu, char *page)
+{
+	static const char *tm_type[] = {
+		"none",
+		"full",
+		"basic",
+	};
+	return sprintf(page, "%s\n", tm_type[lu->tm_type]);
+}
+
+static ssize_t lu_show_channel(struct LU *lu, char *page)
+{
+	if (lu->uldd_dev)
+		return sprintf(page, "%d\n", lu->map.channel);
+	return 0;
+}
+
+static ssize_t lu_show_id(struct LU *lu, char *page)
+{
+	if (lu->uldd_dev)
+		return sprintf(page, "%d\n", lu->map.id);
+	return 0;
+}
+
+/* ---------- Sysfs attribute implementation ---------- */
+
+struct lu_dev_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct LU *lu, char *);
+	ssize_t (*store)(struct LU *lu, const char *, size_t);
+};
+
+static struct lu_dev_attribute lu_attrs[] = {
+	__ATTR(lun, 0444, lu_show_lun, NULL),
+	__ATTR(inquiry_data, 0444, lu_show_inq, NULL),
+	__ATTR(channel, 0444, lu_show_channel, NULL),
+	__ATTR(id, 0444, lu_show_id, NULL),
+	__ATTR(task_management, 0444, lu_show_tm_type, NULL),
+	__ATTR_NULL,
+};
+
+static struct domain_dev_attribute dev_attrs[] = {
+	__ATTR(dev_type, 0444, dev_show_type, NULL),
+	__ATTR(iproto, 0444, dev_show_iproto, NULL),
+	__ATTR(tproto, 0444, dev_show_tproto, NULL),
+	__ATTR(sas_addr, 0444, dev_show_sas_addr, NULL),
+	__ATTR(ready_led_meaning, 0444, sas_show_rled_meaning, NULL),
+	__ATTR(itnl_timeout, 0444, sas_show_itnl_timeout, NULL),
+	__ATTR(iresp_timeout, 0444, sas_show_iresp_timeout, NULL),
+	__ATTR(rl_wlun, 0444, sas_show_rl_wlun, NULL),
+	__ATTR(linkrate, 0444, dev_show_linkrate, NULL),
+	__ATTR(min_linkrate, 0444, dev_show_min_linkrate, NULL),
+	__ATTR(max_linkrate, 0444, dev_show_max_linkrate, NULL),
+	__ATTR(pathways, 0444, dev_show_pathways, NULL),
+	__ATTR_NULL,
+};
+
+static struct domain_dev_attribute sata_attrs[] = {
+	__ATTR(dev_type, 0444, dev_show_type, NULL),
+	__ATTR(iproto, 0444, dev_show_iproto, NULL),
+	__ATTR(tproto, 0444, dev_show_tproto, NULL),
+	__ATTR(sas_addr, 0444, dev_show_sas_addr, NULL),
+	__ATTR(linkrate, 0444, dev_show_linkrate, NULL),
+	__ATTR(min_linkrate, 0444, dev_show_min_linkrate, NULL),
+	__ATTR(max_linkrate, 0444, dev_show_max_linkrate, NULL),
+	__ATTR(command_set, 0444, sata_show_command_set, NULL),
+	__ATTR(report_phy_sata_resp, 0444, sata_show_rps_resp, NULL),
+	__ATTR(serial_number, 0444, sata_show_serial_number, NULL),
+	__ATTR(firmware_rev, 0444, sata_show_firmware_rev, NULL),
+	__ATTR(model_number, 0444, sata_show_model_number, NULL),
+	__ATTR(identify_device, 0444, sata_show_identify_device, NULL),
+	__ATTR(identify_packet_device, 0444, sata_show_identify_packet, NULL),
+	__ATTR(port_no, 0444, sata_show_port_no, NULL),
+	__ATTR_NULL,
+};
+
+static void end_dev_release(struct kobject *obj)
+{
+	struct domain_device *dev = to_dom_device(obj);
+	BUG_ON(!list_empty(&dev->end_dev.LU_list));
+	SAS_DPRINTK("freeing dev %llx\n", SAS_ADDR(dev->sas_addr));
+	kfree(dev);
+}
+
+static void sata_dev_release(struct kobject *obj)
+{
+	struct domain_device *dev = to_dom_device(obj);
+
+	SAS_DPRINTK("freeing SATA dev %llx\n", SAS_ADDR(dev->sas_addr));
+	/* XXX Hint: unregister this SATA device with SATL.
+	if (dev->sata_dev->lu)
+		sas_satl_unregister_dev(dev);
+	*/
+	if (dev->sata_dev.identify_device) {
+		void *p = dev->sata_dev.identify_device;
+		mb();
+		dev->sata_dev.identify_device = NULL;
+		kfree(p);
+	}
+	if (dev->sata_dev.identify_packet_device) {
+		void *p = dev->sata_dev.identify_packet_device;
+		mb();
+		dev->sata_dev.identify_packet_device = NULL;
+		kfree(p);
+	}
+	kfree(dev);
+}
+
+static void sas_lu_release(struct kobject *obj)
+{
+	struct LU *lu = to_lu_device(obj);
+	SAS_DPRINTK("freeing LUN %016llx\n", SAS_ADDR(lu->LUN));
+	sas_release_scsi_id(lu->parent->port, lu->map.id);
+	kfree(lu);
+}
+
+static ssize_t dev_show_attr(struct kobject *kobj, struct attribute *attr,
+			     char *page)
+{
+	ssize_t ret = 0;
+	struct domain_device *dev = to_dom_device(kobj);
+	struct domain_dev_attribute *dev_attr = to_dev_attr(attr);
+
+	if (dev_attr->show)
+		ret = dev_attr->show(dev, page);
+	return ret;
+}
+
+static ssize_t lu_show_attr(struct kobject *obj, struct attribute *attr,
+			    char *page)
+{
+	ssize_t ret = 0;
+	struct LU *lu = to_lu_device(obj);
+	struct lu_dev_attribute *lu_attr = to_lu_attr(attr);
+
+	if (lu_attr->show)
+		ret = lu_attr->show(lu, page);
+	return ret;
+}
+
+struct sysfs_ops dev_sysfs_ops = {
+	.show = dev_show_attr,
+};
+static struct sysfs_ops lu_sysfs_ops = {
+	.show = lu_show_attr,
+};
+
+static struct attribute *end_dev_attrs[ARRAY_SIZE(dev_attrs)];
+static struct attribute *sata_dev_attrs[ARRAY_SIZE(sata_attrs)];
+static struct attribute *lu_dev_attrs[ARRAY_SIZE(lu_attrs)];
+
+static struct kobj_type end_dev_ktype = {
+	.release = end_dev_release,
+	.sysfs_ops = &dev_sysfs_ops,
+	.default_attrs = end_dev_attrs,
+};
+static struct kobj_type sata_dev_ktype = {
+	.release = sata_dev_release,
+	.sysfs_ops = &dev_sysfs_ops,
+	.default_attrs = sata_dev_attrs,
+};
+static struct kobj_type lu_dev_ktype = {
+	.release = sas_lu_release,
+	.sysfs_ops = &lu_sysfs_ops,
+	.default_attrs = lu_dev_attrs,
+};
+
+struct kobj_type *dev_ktype[] = {
+	NULL,
+	&end_dev_ktype,
+	&ex_dev_ktype,
+	&ex_dev_ktype,
+	NULL,
+	&sata_dev_ktype,
+	NULL,
+	NULL,
+};
+
+/* ---------- Basic task processing for discovery purposes ---------- */
+
+static void sas_task_timedout(unsigned long _task)
+{
+	struct sas_task *task = (void *) _task;
+	unsigned long flags;
+
+	spin_lock_irqsave(&task->task_state_lock, flags);
+	if (!(task->task_state_flags & SAS_TASK_STATE_DONE))
+		task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+	spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+	complete(&task->completion);
+}
+
+static void sas_disc_task_done(struct sas_task *task)
+{
+	if (!del_timer(&task->timer))
+		return;
+	complete(&task->completion);
+}
+
+#define SAS_DEV_TIMEOUT 10
+
+/**
+ * sas_execute_task -- Basic task processing for discovery
+ * @task: the task to be executed
+ * @buffer: pointer to buffer to do I/O
+ * @size: size of @buffer
+ * @pci_dma_dir: PCI_DMA_...
+ */
+static int sas_execute_task(struct sas_task *task, void *buffer, int size,
+			    int pci_dma_dir)
+{
+	int res = 0;
+	struct scatterlist *scatter = NULL;
+	struct task_status_struct *ts = &task->task_status;
+	int num_scatter = 0;
+	int retries = 0;
+
+	if (pci_dma_dir != PCI_DMA_NONE) {
+		scatter = kzalloc(sizeof(*scatter), GFP_KERNEL);
+		if (!scatter)
+			goto out;
+
+		sg_init_one(scatter, buffer, size);
+		num_scatter = 1;
+	}
+
+	task->task_proto = task->dev->tproto;
+	task->scatter = scatter;
+	task->num_scatter = num_scatter;
+	task->total_xfer_len = size;
+	task->data_dir = pci_dma_dir;
+	task->task_done = sas_disc_task_done;
+
+	for (retries = 0; retries < 5; retries++) {
+		task->task_state_flags = SAS_TASK_STATE_PENDING;
+		init_completion(&task->completion);
+
+		task->timer.data = (unsigned long) task;
+		task->timer.function = sas_task_timedout;
+		task->timer.expires = jiffies + SAS_DEV_TIMEOUT*HZ;
+		add_timer(&task->timer);
+
+		res = task->dev->port->ha->lldd_execute_task(task, 1,
+							     GFP_KERNEL);
+		if (res) {
+			del_timer(&task->timer);
+			SAS_DPRINTK("executing SAS discovery task failed:%d\n",
+				    res);
+			goto ex_err;
+		}
+		wait_for_completion(&task->completion);
+		res = -ETASK;
+		if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
+			int res2;
+			SAS_DPRINTK("task aborted, flags:0x%x\n",
+				    task->task_state_flags);
+			res2 = task->dev->port->ha->lldd_abort_task(task);
+			SAS_DPRINTK("came back from abort task\n");
+			if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
+				if (res2 == TMF_RESP_FUNC_COMPLETE)
+					continue; /* Retry the task */
+				else
+					goto ex_err;
+			}
+		}
+		if (task->task_status.stat == SAM_BUSY ||
+			   task->task_status.stat == SAM_TASK_SET_FULL ||
+			   task->task_status.stat == SAS_QUEUE_FULL) {
+			SAS_DPRINTK("task: q busy, sleeping...\n");
+			schedule_timeout_interruptible(HZ);
+		} else if (task->task_status.stat == SAM_CHECK_COND) {
+			struct scsi_sense_hdr shdr;
+
+			if (!scsi_normalize_sense(ts->buf, ts->buf_valid_size,
+						  &shdr)) {
+				SAS_DPRINTK("couldn't normalize sense\n");
+				continue;
+			}
+			if ((shdr.sense_key == 6 && shdr.asc == 0x29) ||
+			    (shdr.sense_key == 2 && shdr.asc == 4 &&
+			     shdr.ascq == 1)) {
+				SAS_DPRINTK("device %016llx LUN: %016llx "
+					    "powering up or not ready yet, "
+					    "sleeping...\n",
+					    SAS_ADDR(task->dev->sas_addr),
+					    SAS_ADDR(task->ssp_task.LUN));
+
+				schedule_timeout_interruptible(5*HZ);
+			} else if (shdr.sense_key == 1) {
+				res = 0;
+				break;
+			} else if (shdr.sense_key == 5) {
+				break;
+			} else {
+				SAS_DPRINTK("dev %016llx LUN: %016llx "
+					    "sense key:0x%x ASC:0x%x ASCQ:0x%x"
+					    "\n",
+					    SAS_ADDR(task->dev->sas_addr),
+					    SAS_ADDR(task->ssp_task.LUN),
+					    shdr.sense_key,
+					    shdr.asc, shdr.ascq);
+			}
+		} else if (task->task_status.resp != SAS_TASK_COMPLETE ||
+			   task->task_status.stat != SAM_GOOD) {
+			SAS_DPRINTK("task finished with resp:0x%x, "
+				    "stat:0x%x\n",
+				    task->task_status.resp,
+				    task->task_status.stat);
+			goto ex_err;
+		} else {
+			res = 0;
+			break;
+		}
+	}
+ex_err:
+	if (pci_dma_dir != PCI_DMA_NONE)
+		kfree(scatter);
+out:
+	return res;
+}
+
+/* ---------- Domain device discovery ---------- */
+
+/**
+ * sas_get_port_device -- Discover devices which caused port creation
+ * @port: pointer to struct sas_port of interest
+ *
+ * Devices directly attached to a HA port, have no parent.  This is
+ * how we know they are (domain) "root" devices.  All other devices
+ * do, and should have their "parent" pointer set appropriately as
+ * soon as a child device is discovered.
+ */
+static int sas_get_port_device(struct sas_port *port)
+{
+	unsigned long flags;
+	struct sas_phy *phy;
+	struct domain_device *dev;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&port->phy_list_lock, flags);
+	if (list_empty(&port->phy_list)) {
+		spin_unlock_irqrestore(&port->phy_list_lock, flags);
+		kfree(dev);
+		return -ENODEV;
+	}
+	phy = container_of(port->phy_list.next, struct sas_phy, port_phy_el);
+	spin_lock(&phy->frame_rcvd_lock);
+	memcpy(dev->frame_rcvd, phy->frame_rcvd, min(sizeof(dev->frame_rcvd),
+					     (size_t)phy->frame_rcvd_size));
+	spin_unlock(&phy->frame_rcvd_lock);
+	spin_unlock_irqrestore(&port->phy_list_lock, flags);
+
+	if (dev->frame_rcvd[0] == 0x34 && port->oob_mode == SATA_OOB_MODE) {
+		struct dev_to_host_fis *fis =
+			(struct dev_to_host_fis *) dev->frame_rcvd;
+		if (fis->interrupt_reason == 1 && fis->lbal == 1 &&
+		    fis->byte_count_low==0x69 && fis->byte_count_high == 0x96
+		    && (fis->device & ~0x10) == 0)
+			dev->dev_type = SATA_PM;
+		else
+			dev->dev_type = SATA_DEV;
+		dev->tproto = SATA_PROTO;
+	} else {
+		struct sas_identify_frame *id =
+			(struct sas_identify_frame *) dev->frame_rcvd;
+		dev->dev_type = id->dev_type;
+		dev->iproto = id->initiator_bits;
+		dev->tproto = id->target_bits;
+	}
+
+	sas_init_dev(dev);
+
+	memcpy(dev->sas_addr, port->attached_sas_addr, SAS_ADDR_SIZE);
+	sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr);
+	port->port_dev = dev;
+	dev->port = port;
+	dev->linkrate = port->linkrate;
+	dev->min_linkrate = port->linkrate;
+	dev->max_linkrate = port->linkrate;
+	dev->pathways = port->num_phys;
+	memset(port->disc.fanout_sas_addr, 0, SAS_ADDR_SIZE);
+	memset(port->disc.eeds_a, 0, SAS_ADDR_SIZE);
+	memset(port->disc.eeds_b, 0, SAS_ADDR_SIZE);
+	port->disc.max_level = 0;
+
+	return 0;
+}
+
+/* ---------- Discover and Revalidate ---------- */
+
+/* ---------- SATA ---------- */
+
+static inline void sas_get_ata_command_set(struct domain_device *dev)
+{
+	struct dev_to_host_fis *fis =
+		(struct dev_to_host_fis *) dev->frame_rcvd;
+
+	if ((fis->sector_count == 1 && /* ATA */
+	     fis->lbal         == 1 &&
+	     fis->lbam         == 0 &&
+	     fis->lbah         == 0 &&
+	     fis->device       == 0)
+	    ||
+	    (fis->sector_count == 0 && /* CE-ATA (mATA) */
+	     fis->lbal         == 0 &&
+	     fis->lbam         == 0xCE &&
+	     fis->lbah         == 0xAA &&
+	     (fis->device & ~0x10) == 0))
+
+		dev->sata_dev.command_set = ATA_COMMAND_SET;
+
+	else if ((fis->interrupt_reason == 1 &&	/* ATAPI */
+		  fis->lbal             == 1 &&
+		  fis->byte_count_low   == 0x14 &&
+		  fis->byte_count_high  == 0xEB &&
+		  (fis->device & ~0x10) == 0))
+
+		dev->sata_dev.command_set = ATAPI_COMMAND_SET;
+
+	else if ((fis->sector_count == 1 && /* SEMB */
+		  fis->lbal         == 1 &&
+		  fis->lbam         == 0x3C &&
+		  fis->lbah         == 0xC3 &&
+		  fis->device       == 0)
+		||
+		 (fis->interrupt_reason == 1 &&	/* SATA PM */
+		  fis->lbal             == 1 &&
+		  fis->byte_count_low   == 0x69 &&
+		  fis->byte_count_high  == 0x96 &&
+		  (fis->device & ~0x10) == 0))
+
+		/* Treat it as a superset? */
+		dev->sata_dev.command_set = ATAPI_COMMAND_SET;
+}
+
+/**
+ * sas_issue_ata_cmd -- Basic SATA command processing for discovery
+ * @dev: the device to send the command to
+ * @command: the command register
+ * @features: the features register
+ * @buffer: pointer to buffer to do I/O
+ * @size: size of @buffer
+ * @pci_dma_dir: PCI_DMA_...
+ */
+static int sas_issue_ata_cmd(struct domain_device *dev, u8 command,
+			     u8 features, void *buffer, int size,
+			     int pci_dma_dir)
+{
+	int res = 0;
+	struct sas_task *task;
+	struct dev_to_host_fis *d2h_fis = (struct dev_to_host_fis *)
+		&dev->frame_rcvd[0];
+
+	res = -ENOMEM;
+	task = sas_alloc_task(GFP_KERNEL);
+	if (!task)
+		goto out;
+
+	task->dev = dev;
+
+	task->ata_task.fis.command = command;
+	task->ata_task.fis.features = features;
+	task->ata_task.fis.device = d2h_fis->device;
+	task->ata_task.retry_count = 1;
+
+	res = sas_execute_task(task, buffer, size, pci_dma_dir);
+
+	sas_free_task(task);
+out:
+	return res;
+}
+
+static void sas_sata_propagate_sas_addr(struct domain_device *dev)
+{
+	unsigned long flags;
+	struct sas_port *port = dev->port;
+	struct sas_phy  *phy;
+
+	BUG_ON(dev->parent);
+
+	memcpy(port->attached_sas_addr, dev->sas_addr, SAS_ADDR_SIZE);
+	spin_lock_irqsave(&port->phy_list_lock, flags);
+	list_for_each_entry(phy, &port->phy_list, port_phy_el)
+		memcpy(phy->attached_sas_addr, dev->sas_addr, SAS_ADDR_SIZE);
+	spin_unlock_irqrestore(&port->phy_list_lock, flags);
+}
+
+#define ATA_IDENTIFY_DEV         0xEC
+#define ATA_IDENTIFY_PACKET_DEV  0xA1
+#define ATA_SET_FEATURES         0xEF
+#define ATA_FEATURE_PUP_STBY_SPIN_UP 0x07
+
+/**
+ * sas_discover_sata_dev -- discover a STP/SATA device (SATA_DEV)
+ * @dev: STP/SATA device of interest (ATA/ATAPI)
+ *
+ * The LLDD has already been notified of this device, so that we can
+ * send FISes to it.  Here we try to get IDENTIFY DEVICE or IDENTIFY
+ * PACKET DEVICE, if ATAPI device, so that the LLDD can fine-tune its
+ * performance for this device.
+ */
+static int sas_discover_sata_dev(struct domain_device *dev)
+{
+	int     res;
+	__le16  *identify_x;
+	u8      command;
+
+	identify_x = kzalloc(512, GFP_KERNEL);
+	if (!identify_x)
+		return -ENOMEM;
+
+	if (dev->sata_dev.command_set == ATA_COMMAND_SET) {
+		dev->sata_dev.identify_device = identify_x;
+		command = ATA_IDENTIFY_DEV;
+	} else {
+		dev->sata_dev.identify_packet_device = identify_x;
+		command = ATA_IDENTIFY_PACKET_DEV;
+	}
+
+	res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512,
+				PCI_DMA_FROMDEVICE);
+	if (res)
+		goto out_err;
+
+	/* lives on the media? */
+	if (le16_to_cpu(identify_x[0]) & 4) {
+		/* incomplete response */
+		SAS_DPRINTK("sending SET FEATURE/PUP_STBY_SPIN_UP to "
+			    "dev %llx\n", SAS_ADDR(dev->sas_addr));
+		if (!le16_to_cpu(identify_x[83] & (1<<6)))
+			goto cont1;
+		res = sas_issue_ata_cmd(dev, ATA_SET_FEATURES,
+					ATA_FEATURE_PUP_STBY_SPIN_UP,
+					NULL, 0, PCI_DMA_NONE);
+		if (res)
+			goto cont1;
+
+		schedule_timeout_interruptible(5*HZ); /* More time? */
+		res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512,
+					PCI_DMA_FROMDEVICE);
+		if (res)
+			goto out_err;
+	}
+cont1:
+	/* Get WWN */
+	if (dev->port->oob_mode != SATA_OOB_MODE) {
+		memcpy(dev->sas_addr, dev->sata_dev.rps_resp.rps.stp_sas_addr,
+		       SAS_ADDR_SIZE);
+	} else if (dev->sata_dev.command_set == ATA_COMMAND_SET &&
+		   (le16_to_cpu(dev->sata_dev.identify_device[108]) & 0xF000)
+		   == 0x5000) {
+		int i;
+
+		for (i = 0; i < 4; i++) {
+			dev->sas_addr[2*i] =
+	     (le16_to_cpu(dev->sata_dev.identify_device[108+i]) & 0xFF00) >> 8;
+			dev->sas_addr[2*i+1] =
+	      le16_to_cpu(dev->sata_dev.identify_device[108+i]) & 0x00FF;
+		}
+	}
+	sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr);
+	if (!dev->parent)
+		sas_sata_propagate_sas_addr(dev);
+
+	/* XXX Hint: register this SATA device with SATL.
+	   When this returns, dev->sata_dev->lu is alive and
+	   present.
+	sas_satl_register_dev(dev);
+	*/
+	return 0;
+out_err:
+	dev->sata_dev.identify_packet_device = NULL;
+	dev->sata_dev.identify_device = NULL;
+	kfree(identify_x);
+	return res;
+}
+
+static int sas_discover_sata_pm(struct domain_device *dev)
+{
+	return -ENODEV;
+}
+
+/* ---------- SAS end devices ---------- */
+
+static int sas_get_itnl_timeout(struct domain_device *dev)
+{
+	static const u8 mode_sense_6[16]  = { 0x1a, };
+	static const u8 mode_sense_10[16] = { 0x5a, };
+
+	int res = -ENOMEM;
+	struct sas_task *task;
+	u8 *buffer, *__buf;
+	const int buffer_size = 12;
+
+	task = sas_alloc_task(GFP_KERNEL);
+	if (!task)
+		return -ENOMEM;
+	buffer = kzalloc(buffer_size, GFP_KERNEL);
+	if (!buffer)
+		goto out;
+	__buf = buffer;
+
+	task->dev = dev;
+
+	task->ssp_task.retry_count = 1;
+	memcpy(task->ssp_task.cdb, mode_sense_6, 16);
+	task->ssp_task.cdb[1] |= (1 << 3);
+	task->ssp_task.cdb[2] = 0x19;
+	task->ssp_task.cdb[4] = buffer_size;
+
+	res = sas_execute_task(task, buffer, buffer_size, PCI_DMA_FROMDEVICE);
+	if (res) {
+		SAS_DPRINTK("task to device %llx returned stat 0x%x for "
+			    "MODE SENSE 6\n",
+			    SAS_ADDR(dev->sas_addr), task->task_status.stat);
+		memcpy(task->ssp_task.cdb, mode_sense_10, 16);
+		task->ssp_task.cdb[1] |= (1 << 3);
+		task->ssp_task.cdb[2] = 0x19;
+		task->ssp_task.cdb[8] = buffer_size;
+
+		res = sas_execute_task(task, buffer, buffer_size,
+				       PCI_DMA_FROMDEVICE);
+		if (res) {
+			SAS_DPRINTK("task to device %llx returned stat 0x%x "
+				    "for MODE SENSE 10\n",
+				    SAS_ADDR(dev->sas_addr),
+				    task->task_status.stat);
+			goto out_buf;
+		}
+		dev->end_dev.ms_10 = 1;
+		buffer += 4;
+	}
+
+	buffer += 4;		  /* skip mode parameter header */
+
+	dev->end_dev.ready_led_meaning = (buffer[2] & (1<<4)) ? 1 : 0;
+	dev->end_dev.itnl_timeout = be16_to_cpu(*(__be16 *)(buffer+4));
+	dev->end_dev.iresp_timeout= be16_to_cpu(*(__be16 *)(buffer+6));
+
+	res = 0;
+
+out_buf:
+	kfree(__buf);
+out:
+	sas_free_task(task);
+	return res;
+}
+
+static int sas_get_report_luns(struct domain_device *dev, u8 **buffer,
+			       int *size)
+{
+	static const u8 report_luns[16] = { 0xA0, };
+	static const u8 RL_WLUN[8] = { 0xC1, 0x01, };
+
+	int res = -ENOMEM;
+	struct sas_task *task;
+	u8 *buf;
+	int buffer_size = 16;
+	u32 len;
+
+	*buffer = kzalloc(buffer_size, GFP_KERNEL);
+	if (!*buffer)
+		return -ENOMEM;
+	buf = *buffer;
+
+	task = sas_alloc_task(GFP_KERNEL);
+	if (!task)
+		goto out_err;
+
+	task->dev = dev;
+	task->ssp_task.retry_count = 1;
+	memcpy(task->ssp_task.cdb, report_luns, 16);
+	*(__be32 *)(&task->ssp_task.cdb[6]) = cpu_to_be32(buffer_size);
+
+	res = sas_execute_task(task, buf, buffer_size, PCI_DMA_FROMDEVICE);
+	if (res) {
+		SAS_DPRINTK("REPORT LUNS to LUN0 failed for device %llx "
+			    "with status:0x%x\n",
+			    SAS_ADDR(dev->sas_addr), task->task_status.stat);
+		memcpy(task->ssp_task.LUN, RL_WLUN, 8);
+		res = sas_execute_task(task, buf, buffer_size,
+				       PCI_DMA_FROMDEVICE);
+		if (res) {
+			SAS_DPRINTK("REPORT LUNS to REPORT LUNS W-LUN failed "
+				    "for device %llx with status:0x%x\n",
+				    SAS_ADDR(dev->sas_addr),
+				    task->task_status.stat);
+			goto out_err_task;
+		}
+		dev->end_dev.rl_wlun = 1;
+	}
+
+	len = be32_to_cpu(*(__be32 *)buf);
+	if (len + 8 > buffer_size) {
+		SAS_DPRINTK("need bigger buffer for REPORT LUNS\n");
+		buffer_size = len + 8;
+		res = -ENOMEM;
+		buf = kzalloc(buffer_size, GFP_KERNEL);
+		if (!buf)
+			goto out_err_task;
+		kfree(*buffer);
+		*buffer = buf;
+		if (dev->end_dev.rl_wlun)
+			memcpy(task->ssp_task.LUN, RL_WLUN, 8);
+		else
+			memset(task->ssp_task.LUN, 0, 8);
+		res = sas_execute_task(task, buf, buffer_size,
+				       PCI_DMA_FROMDEVICE);
+		if (res) {
+			SAS_DPRINTK("2nd REPORT LUNS to %s failed "
+				    "for device %llx with status:0x%x\n",
+				    dev->end_dev.rl_wlun ? "REPORT LUNS W-LUN"
+				    : "LUN0",
+				    SAS_ADDR(dev->sas_addr),
+				    task->task_status.stat);
+			goto out_err_task;
+		}
+	}
+
+	*size = len+8;
+	sas_free_task(task);
+	return 0;
+
+out_err_task:
+	sas_free_task(task);
+out_err:
+	kfree(*buffer);
+	*buffer = NULL;
+	size = 0;
+	return res;
+}
+
+#if 0
+static int sas_get_inquiry(struct LU *lu)
+{
+	static const u8 inquiry_cmd[16] = { 0x12, };
+	struct sas_task *task;
+	int res;
+
+	task = sas_alloc_task(GFP_KERNEL);
+	if (!task)
+		return -ENOMEM;
+
+	task->dev = lu->parent;
+	task->ssp_task.retry_count = 1;
+	memcpy(task->ssp_task.LUN, lu->LUN, 8);
+	memcpy(task->ssp_task.cdb, inquiry_cmd, 16);
+	*(__be16 *)(task->ssp_task.cdb+3) = cpu_to_be16(SAS_INQUIRY_DATA_LEN);
+
+	res = sas_execute_task(task, lu->inquiry_data, SAS_INQUIRY_DATA_LEN,
+			       PCI_DMA_FROMDEVICE);
+	if (!res)
+		lu->inquiry_valid_data_len = min(SAS_INQUIRY_DATA_LEN,
+						 lu->inquiry_data[4]+5);
+	sas_free_task(task);
+	return res;
+}
+#endif
+
+static struct LU *sas_alloc_lu(void)
+{
+	struct LU *lu = kzalloc(sizeof(*lu), GFP_KERNEL);
+	if (lu)
+		INIT_LIST_HEAD(&lu->list);
+	return lu;
+}
+
+static int sas_register_lu(struct domain_device *dev, u8 *buf, int size)
+{
+#if 0
+	int res;
+
+	for (buf = buf+8, size -= 8; size > 0; size -= 8, buf += 8) {
+		struct LU *lu = sas_alloc_lu();
+
+		SAS_DPRINTK("%016llx probing LUN:%016llx\n",
+			    SAS_ADDR(dev->sas_addr),
+			    be64_to_cpu(*(__be64 *)buf));
+		if (lu) {
+			lu->parent = dev;
+			memcpy(lu->LUN, buf, 8);
+			res = sas_get_inquiry(lu);
+			if (res) {
+				SAS_DPRINTK("dev %llx LUN %016llx didn't reply"
+					    " to INQUIRY, forgotten\n",
+					    SAS_ADDR(dev->sas_addr),
+					    SAS_ADDR(lu->LUN));
+				kfree(lu);
+				continue;
+			}
+			lu->lu_obj.kset = &dev->end_dev.LU_kset;
+			kobject_set_name(&lu->lu_obj, "%016llx",
+					 SAS_ADDR(lu->LUN));
+			lu->lu_obj.ktype = dev->end_dev.LU_kset.ktype;
+			list_add_tail(&lu->list, &dev->end_dev.LU_list);
+		}
+	}
+#else
+	struct LU *lu = sas_alloc_lu();
+	if (!lu)
+		goto out;
+	
+	lu->parent = dev;
+	memset(lu->LUN, 0, 8);
+
+	lu->lu_obj.kset = &dev->end_dev.LU_kset;
+	kobject_set_name(&lu->lu_obj, "%016llx",
+			 SAS_ADDR(lu->LUN));
+	lu->lu_obj.ktype = dev->end_dev.LU_kset.ktype;
+	list_add_tail(&lu->list, &dev->end_dev.LU_list);
+out:
+#endif
+
+	return list_empty(&dev->end_dev.LU_list) ? -ENODEV : 0;
+}
+
+/**
+ * sas_do_lu_discovery -- Discover LUs of a SCSI device
+ * @dev: pointer to a domain device of interest
+ *
+ * Discover logical units present in the SCSI device.  I'd like this
+ * to be moved to SCSI Core, but SCSI Core has no concept of a "SCSI
+ * device with a SCSI Target port".  A SCSI device with a SCSI Target
+ * port is a device which the _transport_ found, but other than that,
+ * the transport has little or _no_ knowledge about the device.
+ * Ideally, a LLDD would register a "SCSI device with a SCSI Target
+ * port" with SCSI Core and then SCSI Core would do LU discovery of
+ * that device.
+ *
+ * REPORT LUNS is mandatory.  If a device doesn't support it,
+ * it is broken and you should return it.  Nevertheless, we
+ * assume (optimistically) that the link hasn't been severed and
+ * that maybe we can get to the device anyhow.
+ */
+static int sas_do_lu_discovery(struct domain_device *dev)
+{
+	int  res;
+	u8  *buffer;
+	int  size;
+
+	res = sas_get_report_luns(dev, &buffer, &size);
+	if (res) {
+		SAS_DPRINTK("dev %llx didn't reply to REPORT LUNS, trying "
+			    "LUN 0 anyway\n",
+			    SAS_ADDR(dev->sas_addr));
+		size = 16;
+		buffer = kzalloc(size, GFP_KERNEL);
+	}
+
+	res = sas_register_lu(dev, buffer, size);
+	if (res) {
+		SAS_DPRINTK("dev %llx didn't report any LUs\n",
+			    SAS_ADDR(dev->sas_addr));
+		res = 0;
+	}
+
+	kfree(buffer);
+	return res;
+}
+
+/* ---------- Common/dispatchers ---------- */
+
+void sas_kobj_set(struct domain_device *dev)
+{
+	if (!dev->parent) {
+		/* device directly attached to the host adapter */
+		dev->dev_obj.kset = &dev->port->dev_kset;
+	} else {
+		/* parent is an expander */
+		dev->dev_obj.parent = &dev->parent->dev_obj;
+		dev->port = dev->parent->port;
+	}
+
+	list_add_tail(&dev->dev_list_node, &dev->port->dev_list);
+	kobject_set_name(&dev->dev_obj, "%016llx", SAS_ADDR(dev->sas_addr));
+	dev->dev_obj.ktype = dev_ktype[dev->dev_type];
+}
+
+/**
+ * sas_discover_sata -- discover an STP/SATA domain device
+ * @dev: pointer to struct domain_device of interest
+ *
+ * First we notify the LLDD of this device, so we can send frames to
+ * it.  Then depending on the type of device we call the appropriate
+ * discover functions.  Once device discover is done, we notify the
+ * LLDD so that it can fine-tune its parameters for the device, by
+ * removing it and then adding it.  That is, the second time around,
+ * the driver would have certain fields, that it is looking at, set.
+ * Finally we initialize the kobj so that the device can be added to
+ * the system at registration time.  Devices directly attached to a HA
+ * port, have no parents.  All other devices do, and should have their
+ * "parent" pointer set appropriately before calling this function.
+ */
+int sas_discover_sata(struct domain_device *dev)
+{
+	int res;
+
+	sas_get_ata_command_set(dev);
+
+	res = sas_notify_lldd_dev_found(dev);
+	if (res)
+		return res;
+
+	switch (dev->dev_type) {
+	case SATA_DEV:
+		res = sas_discover_sata_dev(dev);
+		break;
+	case SATA_PM:
+		res = sas_discover_sata_pm(dev);
+		break;
+	default:
+		break;
+	}
+
+	sas_notify_lldd_dev_gone(dev);
+	if (!res) {
+		sas_notify_lldd_dev_found(dev);
+		sas_kobj_set(dev);
+	}
+	return res;
+}
+
+/**
+ * sas_discover_end_dev -- discover an end device (SSP, etc)
+ * @end: pointer to domain device of interest
+ *
+ * See comment in sas_discover_sata().
+ */
+int sas_discover_end_dev(struct domain_device *dev)
+{
+	int res;
+
+	res = sas_notify_lldd_dev_found(dev);
+	if (res)
+		return res;
+
+	res = sas_get_itnl_timeout(dev);
+	if (!res) {
+		sas_notify_lldd_dev_gone(dev);
+		sas_notify_lldd_dev_found(dev);
+	}
+
+	dev->end_dev.LU_kset.kobj.parent = &dev->dev_obj;
+	dev->end_dev.LU_kset.ktype  = &lu_dev_ktype;
+
+	res = sas_do_lu_discovery(dev);
+	if (res)
+		goto out_err;
+
+	kobject_set_name(&dev->end_dev.LU_kset.kobj, "%s", "LUNS");
+
+	sas_kobj_set(dev);
+	return 0;
+
+out_err:
+	sas_notify_lldd_dev_gone(dev);
+	return res;
+}
+
+/* ---------- Device registration and unregistration ---------- */
+
+static inline void sas_unregister_common_dev(struct domain_device *dev)
+{
+	sas_notify_lldd_dev_gone(dev);
+	if (!dev->parent)
+		dev->port->port_dev = NULL;
+	else
+		list_del_init(&dev->siblings);
+	list_del_init(&dev->dev_list_node);
+	kobject_unregister(&dev->dev_obj);
+}
+
+static int sas_register_end_dev(struct domain_device *dev)
+{
+	struct LU *lu;
+
+	kobject_register(&dev->dev_obj);
+	kset_register(&dev->end_dev.LU_kset);
+
+	list_for_each_entry(lu, &dev->end_dev.LU_list, list) {
+		sas_register_with_scsi(lu);
+	}
+
+	return 0;
+}
+
+static void sas_unregister_end_dev(struct domain_device *dev)
+{
+	struct LU *lu, *n;
+
+	list_for_each_entry_safe(lu, n, &dev->end_dev.LU_list, list) {
+		sas_unregister_with_scsi(lu);
+		list_del_init(&lu->list);
+	}
+	kset_unregister(&dev->end_dev.LU_kset);
+	sas_unregister_common_dev(dev);
+}
+
+static int sas_register_sata(struct domain_device *dev)
+{
+	/* XXX Hint: Register the SATL supported LU with SCSI.
+	if (dev->sata_dev->lu)
+		sas_register_with_scsi(dev->sata_dev->lu)
+	*/
+	kobject_register(&dev->dev_obj);
+	return 0;
+}
+
+static void sas_unregister_sata(struct domain_device *dev)
+{
+	/* XXX Hint: See hint above.
+	if (dev->sata_dev->lu)
+		sas_unregister_with_scsi(dev->sata_dev->lu);
+	*/
+	sas_unregister_common_dev(dev);
+}
+
+/**
+ * sas_register_ex_dev -- Register this expander
+ * @ex: pointer to domain device
+ *
+ * It is imperative that this is done breadth-first.  Other parts of
+ * the code rely on that.
+ */
+static int sas_register_ex_dev(struct domain_device *dev)
+{
+	kobject_register(&dev->dev_obj);
+	sysfs_create_bin_file(&dev->dev_obj, &dev->ex_dev.smp_bin_attr);
+	return 0;
+}
+
+static void sas_unregister_ex_dev(struct domain_device *dev)
+{
+	BUG_ON(!list_empty(&dev->ex_dev.children));
+	sas_unregister_common_dev(dev);
+}
+
+/**
+ * sas_register_domain_devs -- register the domain devices with sysfs
+ * @port: the port to the domain
+ *
+ * This function registers the domain devices with sysfs and with
+ * the SCSI subsystem.
+ */
+static int sas_register_domain_devs(struct sas_port *port)
+{
+	struct domain_device *dev;
+
+	list_for_each_entry(dev, &port->dev_list, dev_list_node) {
+		if (dev->dev_obj.dentry)
+			continue;
+		switch (dev->dev_type) {
+		case SAS_END_DEV:
+			sas_register_end_dev(dev);
+			break;
+		case EDGE_DEV:
+		case FANOUT_DEV:
+			sas_register_ex_dev(dev);
+			break;
+		case SATA_DEV:
+		case SATA_PM:
+			sas_register_sata(dev);
+			break;
+		default:
+			SAS_DPRINTK("%s: unknown device type %d\n",
+				    __FUNCTION__, dev->dev_type);
+			break;
+		}
+	}
+
+	return 0;
+}
+
+void sas_unregister_dev(struct domain_device *dev)
+{
+	switch (dev->dev_type) {
+	case SAS_END_DEV:
+		sas_unregister_end_dev(dev);
+		break;
+	case EDGE_DEV:
+	case FANOUT_DEV:
+		sas_unregister_ex_dev(dev);
+		break;
+	case SATA_DEV:
+	case SATA_PM:
+		sas_unregister_sata(dev);
+		break;
+	default:
+		SAS_DPRINTK("%s: unknown device type %d\n",
+			    __FUNCTION__, dev->dev_type);
+		BUG_ON(dev);
+		break;
+	}
+}
+
+static void sas_unregister_domain_devices(struct sas_port *port)
+{
+	struct domain_device *dev, *n;
+
+	list_for_each_entry_reverse_safe(dev,n,&port->dev_list,dev_list_node)
+		sas_unregister_dev(dev);
+}
+
+/* ---------- Discovery and Revalidation ---------- */
+
+/**
+ * sas_discover_domain -- discover the domain
+ * @port: port to the domain of interest
+ *
+ * NOTE: this process _must_ quit (return) as soon as any connection
+ * errors are encountered.  Connection recovery is done elsewhere.
+ * Discover process only interrogates devices in order to discover the
+ * domain.
+ */
+static int sas_discover_domain(struct sas_port *port)
+{
+	int error = 0;
+
+	if (port->port_dev)
+		return 0;
+	else {
+		error = sas_get_port_device(port);
+		if (error)
+			return error;
+	}
+
+	SAS_DPRINTK("DOING DISCOVERY on port %d, pid:%d\n", port->id,
+		    current->pid);
+
+	switch (port->port_dev->dev_type) {
+	case SAS_END_DEV:
+		error = sas_discover_end_dev(port->port_dev);
+		break;
+	case EDGE_DEV:
+	case FANOUT_DEV:
+		error = sas_discover_root_expander(port->port_dev);
+		break;
+	case SATA_DEV:
+	case SATA_PM:
+		error = sas_discover_sata(port->port_dev);
+		break;
+	default:
+		SAS_DPRINTK("unhandled device %d\n", port->port_dev->dev_type);
+		break;
+	}
+
+	if (error) {
+		kfree(port->port_dev); /* not kobject_register-ed yet */
+		port->port_dev = NULL;
+	} else
+		sas_register_domain_devs(port);
+
+	SAS_DPRINTK("DONE DISCOVERY on port %d, pid:%d, result:%d\n", port->id,
+		    current->pid, error);
+
+	return error;
+}
+
+static int sas_revalidate_domain(struct sas_port *port)
+{
+	int res = 0;
+
+	SAS_DPRINTK("REVALIDATING DOMAIN on port %d, pid:%d\n", port->id,
+		    current->pid);
+	if (port->port_dev) {
+		res = sas_ex_revalidate_domain(port->port_dev);
+		if (!res)
+			sas_register_domain_devs(port);
+	}
+	SAS_DPRINTK("done REVALIDATING DOMAIN on port %d, pid:%d, res 0x%x\n",
+		    port->id, current->pid, res);
+	return res;
+}
+
+/* ---------- Threads and events ---------- */
+
+static DECLARE_COMPLETION(disc_comp_start);
+
+static int sas_discover_thread(void *_sas_port)
+{
+	struct sas_port *port = _sas_port;
+	struct sas_ha_struct *sas_ha = port->ha;
+	struct sas_discovery *disc = &port->disc;
+
+	daemonize("sas_disc_h%dp%d", sas_ha->core.shost->host_no, port->id);
+
+	spin_lock(&disc->disc_event_lock);
+	disc->disc_thread = current;
+	complete(&disc_comp_start);
+	while (!disc->disc_thread_quit && !list_empty(&disc->disc_event_list)){
+		struct list_head *head = disc->disc_event_list.next;
+		enum discover_event disc_ev = container_of(head,
+							   struct sas_event,
+							   el)->event;
+		list_del_init(head);
+		spin_unlock(&disc->disc_event_lock);
+
+		switch (disc_ev) {
+		case DISCE_DISCOVER_DOMAIN:
+			sas_discover_domain(port);
+			break;
+		case DISCE_REVALIDATE_DOMAIN:
+			sas_revalidate_domain(port);
+			break;
+		case DISCE_PORT_GONE:
+			sas_unregister_domain_devices(port);
+			complete(&port->port_gone_completion);
+			break;
+		}
+		spin_lock(&disc->disc_event_lock);
+	}
+	INIT_LIST_HEAD(&disc->disc_event_list);
+	disc->disc_thread = NULL;
+	spin_unlock(&disc->disc_event_lock);
+	up(&disc->disc_sema);
+
+	return 0;
+}
+
+static int sas_create_discover_thread(struct sas_port *port)
+{
+	int i;
+
+	init_completion(&disc_comp_start);
+	i = kernel_thread(sas_discover_thread, port, 0);
+	if (i >= 0)
+		wait_for_completion(&disc_comp_start);
+
+	return i < 0 ? i : 0;
+}
+
+int sas_discover_event(struct sas_port *port, enum discover_event ev)
+{
+	int res;
+	struct sas_discovery *disc = &port->disc;
+
+	spin_lock(&disc->disc_event_lock);
+	list_move_tail(&disc->disc_events[ev].el,
+		       &disc->disc_event_list);
+	if (disc->disc_thread) {
+		spin_unlock(&disc->disc_event_lock);
+		return 0;
+	}
+	down_interruptible(&disc->disc_sema);
+	disc->disc_thread_quit = 0;
+	spin_unlock(&disc->disc_event_lock);
+
+	/* The event thread (caller) is single threaded so this is safe. */
+	res = sas_create_discover_thread(port);
+	if (res) {
+		SAS_DPRINTK("sas port%d: couldn't create discovery thread!\n",
+			    port->id);
+		up(&disc->disc_sema);
+	}
+	return res;
+}
+
+void sas_kill_disc_thread(struct sas_port *port)
+{
+	struct sas_discovery *disc = &port->disc;
+
+	spin_lock(&disc->disc_event_lock);
+	disc->disc_thread_quit = 1;
+	if (disc->disc_thread) {
+		wake_up_process(disc->disc_thread);
+		spin_unlock(&disc->disc_event_lock);
+		down_interruptible(&disc->disc_sema);
+		return;
+	}
+	spin_unlock(&disc->disc_event_lock);
+}
+
+/**
+ * sas_init_disc -- initialize the discovery struct in the port
+ * @port: pointer to struct port
+ *
+ * Called when the ports are being initialized.
+ */
+void sas_init_disc(struct sas_discovery *disc, struct sas_port *port)
+{
+	int i;
+
+	if (!end_dev_attrs[0]) {
+		for (i = 0; i < ARRAY_SIZE(dev_attrs)-1; i++)
+			end_dev_attrs[i] = &dev_attrs[i].attr;
+		end_dev_attrs[i] = NULL;
+		sas_init_ex_attr();
+		for (i = 0; i < ARRAY_SIZE(sata_attrs)-1; i++)
+			sata_dev_attrs[i] = &sata_attrs[i].attr;
+		sata_dev_attrs[i] = NULL;
+		for (i = 0; i < ARRAY_SIZE(lu_attrs)-1; i++)
+			lu_dev_attrs[i] = &lu_attrs[i].attr;
+		lu_dev_attrs[i] = NULL;
+	}
+
+	spin_lock_init(&disc->disc_event_lock);
+	INIT_LIST_HEAD(&disc->disc_event_list);
+	init_MUTEX(&disc->disc_sema);
+	disc->disc_thread = NULL;
+	disc->disc_thread_quit = 0;
+
+	for (i = 0; i < DISC_NUM_EVENTS; i++) {
+		struct sas_event *ev = &disc->disc_events[i];
+		ev->event = i;
+		INIT_LIST_HEAD(&ev->el);
+	}
+}
+
+void sas_unregister_devices(struct sas_ha_struct *sas_ha)
+{
+	int i;
+
+	for (i = 0; i < sas_ha->num_phys; i++)
+		sas_unregister_domain_devices(sas_ha->sas_port[i]);
+}
diff -urN oldtree/drivers/scsi/sas/sas_dump.c newtree/drivers/scsi/sas/sas_dump.c
--- oldtree/drivers/scsi/sas/sas_dump.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/sas/sas_dump.c	2006-02-13 19:20:00.648413728 -0500
@@ -0,0 +1,77 @@
+/*
+ * Serial Attached SCSI (SAS) Dump/Debugging routines
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/sas-class/sas_dump.c#4 $
+ */
+
+#include "sas_dump.h"
+
+#ifdef SAS_DEBUG
+
+static const char *sas_hae_str[] = {
+	[0] = "HAE_RESET",
+};
+
+static const char *sas_porte_str[] = {
+	[0] = "PORTE_BYTES_DMAED",
+	[1] = "PORTE_BROADCAST_RCVD",
+	[2] = "PORTE_LINK_RESET_ERR",
+	[3] = "PORTE_TIMER_EVENT",
+	[4] = "PORTE_HARD_RESET",
+};
+
+static const char *sas_phye_str[] = {
+	[0] = "PHYE_LOSS_OF_SIGNAL",
+	[1] = "PHYE_OOB_DONE",
+	[2] = "PHYE_OOB_ERROR",
+	[3] = "PHYE_SPINUP_HOLD",
+};
+
+void sas_dprint_porte(int phyid, enum port_event pe)
+{
+	SAS_DPRINTK("phy%d: port event: %s\n", phyid, sas_porte_str[pe]);
+}
+void sas_dprint_phye(int phyid, enum phy_event pe)
+{
+	SAS_DPRINTK("phy%d: phy event: %s\n", phyid, sas_phye_str[pe]);
+}
+
+void sas_dprint_hae(struct sas_ha_struct *sas_ha, enum ha_event he)
+{
+	SAS_DPRINTK("ha %s: %s event\n", pci_name(sas_ha->pcidev),
+		    sas_hae_str[he]);
+}
+
+void sas_dump_port(struct sas_port *port)
+{
+	SAS_DPRINTK("port%d: class:0x%x\n", port->id, port->class);
+	SAS_DPRINTK("port%d: sas_addr:%llx\n", port->id,
+		    SAS_ADDR(port->sas_addr));
+	SAS_DPRINTK("port%d: attached_sas_addr:%llx\n", port->id,
+		    SAS_ADDR(port->attached_sas_addr));
+	SAS_DPRINTK("port%d: iproto:0x%x\n", port->id, port->iproto);
+	SAS_DPRINTK("port%d: tproto:0x%x\n", port->id, port->tproto);
+	SAS_DPRINTK("port%d: oob_mode:0x%x\n", port->id, port->oob_mode);
+	SAS_DPRINTK("port%d: num_phys:%d\n", port->id, port->num_phys);
+}
+
+#endif /* SAS_DEBUG */
diff -urN oldtree/drivers/scsi/sas/sas_dump.h newtree/drivers/scsi/sas/sas_dump.h
--- oldtree/drivers/scsi/sas/sas_dump.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/sas/sas_dump.h	2006-02-13 19:20:00.648413728 -0500
@@ -0,0 +1,43 @@
+/*
+ * Serial Attached SCSI (SAS) Dump/Debugging routines header file
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/sas-class/sas_dump.h#2 $
+ */
+
+#include "sas_internal.h"
+
+#ifdef SAS_DEBUG
+
+void sas_dprint_porte(int phyid, enum port_event pe);
+void sas_dprint_phye(int phyid, enum phy_event pe);
+void sas_dprint_hae(struct sas_ha_struct *sas_ha, enum ha_event he);
+void sas_dump_port(struct sas_port *port);
+
+#else /* SAS_DEBUG */
+
+static inline void sas_dprint_porte(int phyid, enum port_event pe) { }
+static inline void sas_dprint_phye(int phyid, enum phy_event pe) { }
+static inline void sas_dprint_hae(struct sas_ha_struct *sas_ha,
+				  enum ha_event he) { }
+static inline void sas_dump_port(struct sas_port *port) { }
+
+#endif /* SAS_DEBUG */
diff -urN oldtree/drivers/scsi/sas/sas_event.c newtree/drivers/scsi/sas/sas_event.c
--- oldtree/drivers/scsi/sas/sas_event.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/sas/sas_event.c	2006-02-13 19:20:00.648413728 -0500
@@ -0,0 +1,296 @@
+/*
+ * Serial Attached SCSI (SAS) Event processing
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/sas-class/sas_event.c#26 $
+ */
+
+/**
+ * Implementation Of Priority Queue Without Duplication
+ * Luben Tuikov 2005/07/11
+ *
+ * The SAS class implements priority queue without duplication for
+ * handling ha/port/phy/discover events.  That is, we want to process
+ * the last N unique/non-duplicating events, in the order they arrived.
+ *
+ * The requirement is that insertion is O(1), and ordered removal is O(1).
+ *
+ * Suppose events are identified by integers.  Then what is required
+ * is that for a given sequence of any random integers R, to find a
+ * sorted sequence E, where
+ *     a) |E| <= |R|.  If the number of types of events is bounded,
+ *        then E is also bounded by that number, from b).
+ *     b) For all i and k, E[i] != E[k], except when i == k,
+ *        this gives us uniqueness/non duplication.
+ *     c) For all i < k, Order(E[i]) < Order(E[k]), this gives us
+ *        ordering.
+ *     d) If T(R) = E, then O(T) <= |R|, this ensures that insertion
+ *        is O(1), and ordered removal is O(1) trivially, since we
+ *        remove at the head of E.
+ *
+ * Example:
+ * If R = {4, 5, 1, 2, 5, 3, 3, 4, 4, 3, 1}, then
+ *    E = {2, 5, 4, 3, 1}.
+ *
+ * The algorithm, T, makes use of an array of list elements, indexed
+ * by event type, and an event list head which is a linked list of the
+ * elements of the array.  When the next event arrives, we index the
+ * array by the event, and add that event to the tail of the event
+ * list head, deleting it from its previous list position (if it had
+ * one).
+ *
+ * Clearly insertion is O(1).
+ *
+ * E is given by the elements of the event list, traversed from head
+ * to tail.
+ */
+
+#include <linux/sched.h>
+
+#include <scsi/scsi_host.h>
+#include "sas_internal.h"
+#include "sas_dump.h"
+#include <scsi/sas/sas_discover.h>
+
+static void sas_process_phy_event(struct sas_phy *phy)
+{
+	unsigned long flags;
+	struct sas_ha_struct *sas_ha = phy->ha;
+	enum phy_event phy_event;
+
+	spin_lock_irqsave(&sas_ha->event_lock, flags);
+	while (!list_empty(&phy->phy_event_list)) {
+		struct list_head *head = phy->phy_event_list.next;
+		phy_event = container_of(head, struct sas_event, el)->event;
+		list_del_init(head);
+		spin_unlock_irqrestore(&sas_ha->event_lock, flags);
+
+		sas_dprint_phye(phy->id, phy_event);
+
+		switch(phy_event) {
+		case PHYE_LOSS_OF_SIGNAL:
+			sas_phye_loss_of_signal(phy);
+			break;
+		case PHYE_OOB_DONE:
+			sas_phye_oob_done(phy);
+			break;
+		case PHYE_OOB_ERROR:
+			sas_phye_oob_error(phy);
+			break;
+		case PHYE_SPINUP_HOLD:
+			sas_phye_spinup_hold(phy);
+			break;
+		}
+		spin_lock_irqsave(&sas_ha->event_lock, flags);
+	}
+	/* Clear the bit in case we received events in due time. */
+	sas_ha->phye_mask &= ~(1 << phy->id);
+	spin_unlock_irqrestore(&sas_ha->event_lock, flags);
+}
+
+static void sas_process_port_event(struct sas_phy *phy)
+{
+	unsigned long flags;
+	struct sas_ha_struct *sas_ha = phy->ha;
+	enum port_event port_event;
+
+	spin_lock_irqsave(&sas_ha->event_lock, flags);
+	while (!list_empty(&phy->port_event_list)) {
+		struct list_head *head = phy->port_event_list.next;
+		port_event = container_of(head, struct sas_event, el)->event;
+		list_del_init(head);
+		spin_unlock_irqrestore(&sas_ha->event_lock, flags);
+
+		sas_dprint_porte(phy->id, port_event);
+
+		switch (port_event) {
+		case PORTE_BYTES_DMAED:
+			sas_porte_bytes_dmaed(phy);
+			break;
+		case PORTE_BROADCAST_RCVD:
+			sas_porte_broadcast_rcvd(phy);
+			break;
+		case PORTE_LINK_RESET_ERR:
+			sas_porte_link_reset_err(phy);
+			break;
+		case PORTE_TIMER_EVENT:
+			sas_porte_timer_event(phy);
+			break;
+		case PORTE_HARD_RESET:
+			sas_porte_hard_reset(phy);
+			break;
+		}
+		spin_lock_irqsave(&sas_ha->event_lock, flags);
+	}
+	/* Clear the bit in case we received events in due time. */
+	sas_ha->porte_mask &= ~(1 << phy->id);
+	spin_unlock_irqrestore(&sas_ha->event_lock, flags);
+}
+
+static void sas_process_ha_event(struct sas_ha_struct *sas_ha)
+{
+	unsigned long flags;
+	enum ha_event ha_event;
+
+	spin_lock_irqsave(&sas_ha->event_lock, flags);
+	while (!list_empty(&sas_ha->ha_event_list)) {
+		struct list_head *head = sas_ha->ha_event_list.next;
+		ha_event = container_of(head, struct sas_event, el)->event;
+		list_del_init(head);
+		spin_unlock_irqrestore(&sas_ha->event_lock, flags);
+
+		sas_dprint_hae(sas_ha, ha_event);
+
+		switch (ha_event) {
+		case HAE_RESET:
+			sas_hae_reset(sas_ha);
+			break;
+		}
+		spin_lock_irqsave(&sas_ha->event_lock, flags);
+	}
+	spin_unlock_irqrestore(&sas_ha->event_lock, flags);
+}
+
+static void sas_process_events(struct sas_ha_struct *sas_ha)
+{
+	unsigned long flags;
+	u32 porte_mask, phye_mask;
+	int p;
+
+	spin_lock_irqsave(&sas_ha->event_lock, flags);
+	phye_mask = sas_ha->phye_mask;
+	sas_ha->phye_mask = 0;
+	spin_unlock_irqrestore(&sas_ha->event_lock, flags);
+
+	for (p = 0; phye_mask != 0; phye_mask >>= 1, p++)
+		if (phye_mask & 01)
+			sas_process_phy_event(sas_ha->sas_phy[p]);
+
+	spin_lock_irqsave(&sas_ha->event_lock, flags);
+	porte_mask = sas_ha->porte_mask;
+	sas_ha->porte_mask = 0;
+	spin_unlock_irqrestore(&sas_ha->event_lock, flags);
+
+	for (p = 0; porte_mask != 0; porte_mask >>= 1, p++)
+		if (porte_mask & 01)
+			sas_process_port_event(sas_ha->sas_phy[p]);
+
+	sas_process_ha_event(sas_ha);
+}
+
+static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sas_ha->event_lock, flags);
+	list_move_tail(&sas_ha->ha_events[event].el, &sas_ha->ha_event_list);
+	up(&sas_ha->event_sema);
+	spin_unlock_irqrestore(&sas_ha->event_lock, flags);
+}
+
+static void notify_port_event(struct sas_phy *phy, enum port_event event)
+{
+	struct sas_ha_struct *ha = phy->ha;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ha->event_lock, flags);
+	list_move_tail(&phy->port_events[event].el, &phy->port_event_list);
+	ha->porte_mask |= (1 << phy->id);
+	up(&ha->event_sema);
+	spin_unlock_irqrestore(&ha->event_lock, flags);
+}
+
+static void notify_phy_event(struct sas_phy *phy, enum phy_event event)
+{
+	struct sas_ha_struct *ha = phy->ha;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ha->event_lock, flags);
+	list_move_tail(&phy->phy_events[event].el, &phy->phy_event_list);
+	ha->phye_mask |= (1 << phy->id);
+	up(&ha->event_sema);
+	spin_unlock_irqrestore(&ha->event_lock, flags);
+}
+
+static DECLARE_COMPLETION(event_th_comp);
+
+static int sas_event_thread(void *_sas_ha)
+{
+	struct sas_ha_struct *sas_ha = _sas_ha;
+
+	daemonize("sas_event_%d", sas_ha->core.shost->host_no);
+	current->flags |= PF_NOFREEZE;
+
+	complete(&event_th_comp);
+
+	while (1) {
+		down_interruptible(&sas_ha->event_sema);
+		if (sas_ha->event_thread_kill)
+			break;
+		sas_process_events(sas_ha);
+	}
+
+	complete(&event_th_comp);
+
+	return 0;
+}
+
+int sas_start_event_thread(struct sas_ha_struct *sas_ha)
+{
+	int i;
+
+	init_MUTEX_LOCKED(&sas_ha->event_sema);
+	sas_ha->event_thread_kill = 0;
+
+	spin_lock_init(&sas_ha->event_lock);
+	INIT_LIST_HEAD(&sas_ha->ha_event_list);
+	sas_ha->porte_mask = 0;
+	sas_ha->phye_mask = 0;
+
+	for (i = 0; i < HA_NUM_EVENTS; i++) {
+		struct sas_event *ev = &sas_ha->ha_events[i];
+		ev->event = i;
+		INIT_LIST_HEAD(&ev->el);
+	}
+
+	sas_ha->notify_ha_event = notify_ha_event;
+	sas_ha->notify_port_event = notify_port_event;
+	sas_ha->notify_phy_event = notify_phy_event;
+
+	i = kernel_thread(sas_event_thread, sas_ha, 0);
+	if (i >= 0)
+		wait_for_completion(&event_th_comp);
+
+	return i < 0 ? i : 0;
+}
+
+void sas_kill_event_thread(struct sas_ha_struct *sas_ha)
+{
+	int i;
+
+	init_completion(&event_th_comp);
+	sas_ha->event_thread_kill = 1;
+	up(&sas_ha->event_sema);
+	wait_for_completion(&event_th_comp);
+
+	for (i = 0; i < sas_ha->num_phys; i++)
+		sas_kill_disc_thread(sas_ha->sas_port[i]);
+}
diff -urN oldtree/drivers/scsi/sas/sas_expander.c newtree/drivers/scsi/sas/sas_expander.c
--- oldtree/drivers/scsi/sas/sas_expander.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/sas/sas_expander.c	2006-02-13 19:20:00.650413424 -0500
@@ -0,0 +1,1823 @@
+/*
+ * Serial Attached SCSI (SAS) Expander discovery and configuration
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/sas-class/sas_expander.c#60 $
+ */
+
+#include <linux/pci.h>
+#include <linux/scatterlist.h>
+
+#include "sas_internal.h"
+#include <scsi/sas/sas_task.h>
+#include <scsi/sas/sas_discover.h>
+
+static int sas_discover_expander(struct domain_device *dev);
+static int sas_configure_routing(struct domain_device *dev, u8 *sas_addr);
+static int sas_disable_routing(struct domain_device *dev,  u8 *sas_addr);
+
+static ssize_t smp_portal_read(struct kobject *, char *, loff_t, size_t);
+static ssize_t smp_portal_write(struct kobject *, char *, loff_t, size_t);
+
+/* ---------- Expander attributes ---------- */
+
+static ssize_t ex_show_change_count(struct domain_device *dev, char *page)
+{
+	return sprintf(page, "%d\n", dev->ex_dev.ex_change_count);
+}
+
+static ssize_t ex_show_max_route_indexes(struct domain_device *dev, char *page)
+{
+	return sprintf(page, "%d\n", dev->ex_dev.max_route_indexes);
+}
+
+static ssize_t ex_show_num_phys(struct domain_device *dev, char *page)
+{
+	return sprintf(page, "%d\n", dev->ex_dev.num_phys);
+}
+
+static ssize_t ex_show_enclosure_logical_id(struct domain_device *dev,
+					    char *page)
+{
+	return sprintf(page, "%llx\n",
+		       SAS_ADDR(dev->ex_dev.enclosure_logical_id));
+}
+
+static ssize_t ex_show_vendor_id(struct domain_device *dev, char *page)
+{
+	return sprintf(page, "%s\n", dev->ex_dev.vendor_id);
+}
+
+static ssize_t ex_show_product_id(struct domain_device *dev, char *page)
+{
+	return sprintf(page, "%s\n", dev->ex_dev.product_id);
+}
+
+static ssize_t ex_show_product_rev(struct domain_device *dev, char *page)
+{
+	return sprintf(page, "%s\n", dev->ex_dev.product_rev);
+}
+
+static ssize_t ex_show_component_vendor_id(struct domain_device *dev,
+					   char *page)
+{
+	return sprintf(page, "%s\n", dev->ex_dev.component_vendor_id);
+}
+
+static ssize_t ex_show_component_id(struct domain_device *dev, char *page)
+{
+	return sprintf(page, "%d\n", dev->ex_dev.component_id);
+}
+
+static ssize_t ex_show_component_revision_id(struct domain_device *dev,
+					     char *page)
+{
+	return sprintf(page, "%d\n", dev->ex_dev.component_revision_id);
+}
+
+static ssize_t ex_show_conf_route_table(struct domain_device *dev,
+					char *page)
+{
+	return sprintf(page, "%d\n", dev->ex_dev.conf_route_table);
+}
+
+static ssize_t ex_show_configuring(struct domain_device *dev,
+				   char *page)
+{
+	return sprintf(page, "%d\n", dev->ex_dev.configuring);
+}
+
+static struct domain_dev_attribute ex_attrs[] = {
+	__ATTR(dev_type, 0444, dev_show_type, NULL),
+	__ATTR(iproto, 0444, dev_show_iproto, NULL),
+	__ATTR(tproto, 0444, dev_show_tproto, NULL),
+	__ATTR(sas_addr, 0444, dev_show_sas_addr, NULL),
+	__ATTR(linkrate, 0444, dev_show_linkrate, NULL),
+	__ATTR(min_linkrate, 0444, dev_show_min_linkrate, NULL),
+	__ATTR(max_linkrate, 0444, dev_show_max_linkrate, NULL),
+	__ATTR(pathways, 0444, dev_show_pathways, NULL),
+	__ATTR(change_count, 0444, ex_show_change_count, NULL),
+	__ATTR(max_route_indexes, 0444, ex_show_max_route_indexes, NULL),
+	__ATTR(num_phys, 0444, ex_show_num_phys, NULL),
+	__ATTR(enclosure_logical_id, 0444, ex_show_enclosure_logical_id, NULL),
+	__ATTR(vendor_id, 0444, ex_show_vendor_id, NULL),
+	__ATTR(product_id, 0444, ex_show_product_id, NULL),
+	__ATTR(product_rev, 0444, ex_show_product_rev, NULL),
+	__ATTR(component_vendor_id, 0444, ex_show_component_vendor_id, NULL),
+	__ATTR(component_id, 0444, ex_show_component_id, NULL),
+	__ATTR(component_revision_id, 0444,ex_show_component_revision_id,NULL),
+	__ATTR(conf_route_table, 0444, ex_show_conf_route_table, NULL),
+	__ATTR(configuring, 0444, ex_show_configuring, NULL),
+	__ATTR_NULL,
+};
+
+static struct attribute *ex_dev_attrs[ARRAY_SIZE(ex_attrs)];
+
+static void ex_dev_release(struct kobject *obj)
+{
+	struct domain_device *dev = to_dom_device(obj);
+	SAS_DPRINTK("freeing dev %016llx\n", SAS_ADDR(dev->sas_addr));
+	sysfs_remove_bin_file(&dev->dev_obj, &dev->ex_dev.smp_bin_attr);
+	kfree(dev->ex_dev.ex_phy);
+	kfree(dev->ex_dev.smp_req);
+	kfree(dev);
+}
+
+struct kobj_type ex_dev_ktype = {
+	.release = ex_dev_release,
+	.sysfs_ops = &dev_sysfs_ops,
+	.default_attrs = ex_dev_attrs,
+};
+
+/* ---------- SMP task management ---------- */
+
+static void smp_task_timedout(unsigned long _task)
+{
+	struct sas_task *task = (void *) _task;
+	unsigned long flags;
+
+	spin_lock_irqsave(&task->task_state_lock, flags);
+	if (!(task->task_state_flags & SAS_TASK_STATE_DONE))
+		task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+	spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+	complete(&task->completion);
+}
+
+static void smp_task_done(struct sas_task *task)
+{
+	if (!del_timer(&task->timer))
+		return;
+	complete(&task->completion);
+}
+
+/* Give it some long enough timeout. In seconds. */
+#define SMP_TIMEOUT 10
+
+static int smp_execute_task(struct domain_device *dev, void *req, int req_size,
+			    void *resp, int resp_size)
+{
+	int res;
+	struct sas_task *task = sas_alloc_task(GFP_KERNEL);
+
+	if (!task)
+		return -ENOMEM;
+
+	task->dev = dev;
+	task->task_proto = dev->tproto;
+	sg_init_one(&task->smp_task.smp_req, req, req_size);
+	sg_init_one(&task->smp_task.smp_resp, resp, resp_size);
+
+	task->task_done = smp_task_done;
+
+	task->timer.data = (unsigned long) task;
+	task->timer.function = smp_task_timedout;
+	task->timer.expires = jiffies + SMP_TIMEOUT*HZ;
+	add_timer(&task->timer);
+
+	res = task->dev->port->ha->lldd_execute_task(task, 1, GFP_KERNEL);
+
+	if (res) {
+		del_timer(&task->timer);
+		SAS_DPRINTK("executing SMP task failed:%d\n", res);
+		goto ex_err;
+	}
+
+	wait_for_completion(&task->completion);
+	res = -ETASK;
+	if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
+		SAS_DPRINTK("smp task timed out or aborted\n");
+		task->dev->port->ha->lldd_abort_task(task);
+		if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
+			SAS_DPRINTK("SMP task aborted and not done\n");
+			goto ex_err;
+		}
+	}
+	if (task->task_status.resp == SAS_TASK_COMPLETE &&
+	    task->task_status.stat == SAM_GOOD)
+		res = 0;
+	else
+		SAS_DPRINTK("%s: task to dev %016llx response: 0x%x "
+			    "status 0x%x\n", __FUNCTION__,
+			    SAS_ADDR(dev->sas_addr),
+			    task->task_status.resp,
+			    task->task_status.stat);
+ex_err:
+	sas_free_task(task);
+	return res;
+}
+
+/* ---------- Allocations ---------- */
+
+static inline void *alloc_smp_req(int size)
+{
+	u8 *p = kzalloc(size, GFP_KERNEL);
+	if (p)
+		p[0] = SMP_REQUEST;
+	return p;
+}
+
+static inline void *alloc_smp_resp(int size)
+{
+	return kzalloc(size, GFP_KERNEL);
+}
+
+/* ---------- Expander configuration ---------- */
+
+static void sas_set_ex_phy(struct domain_device *dev, int phy_id,
+			   void *disc_resp)
+{
+	struct expander_device *ex = &dev->ex_dev;
+	struct ex_phy *phy = &ex->ex_phy[phy_id];
+	struct smp_resp *resp = disc_resp;
+	struct discover_resp *dr = &resp->disc;
+
+	switch (resp->result) {
+	case SMP_RESP_PHY_VACANT:
+		phy->phy_state = PHY_VACANT;
+		return;
+	default:
+		phy->phy_state = PHY_NOT_PRESENT;
+		return;
+	case SMP_RESP_FUNC_ACC:
+		phy->phy_state = PHY_EMPTY; /* do not know yet */
+		break;
+	}
+
+	phy->phy_id = phy_id;
+	phy->attached_dev_type = dr->attached_dev_type;
+	phy->linkrate = dr->linkrate;
+	phy->attached_sata_host = dr->attached_sata_host;
+	phy->attached_sata_dev  = dr->attached_sata_dev;
+	phy->attached_sata_ps   = dr->attached_sata_ps;
+	phy->attached_iproto = dr->iproto << 1;
+	phy->attached_tproto = dr->tproto << 1;
+	memcpy(phy->attached_sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE);
+	phy->attached_phy_id = dr->attached_phy_id;
+	phy->phy_change_count = dr->change_count;
+	phy->routing_attr = dr->routing_attr;
+	phy->virtual = dr->virtual;
+	phy->last_da_index = -1;
+
+	SAS_DPRINTK("ex %016llx phy%02d:%c attached: %016llx\n",
+		    SAS_ADDR(dev->sas_addr), phy->phy_id,
+		    phy->routing_attr == TABLE_ROUTING ? 'T' :
+		    phy->routing_attr == DIRECT_ROUTING ? 'D' :
+		    phy->routing_attr == SUBTRACTIVE_ROUTING ? 'S' : '?',
+		    SAS_ADDR(phy->attached_sas_addr));
+
+	return;
+}
+
+#define DISCOVER_REQ_SIZE  16
+#define DISCOVER_RESP_SIZE 56
+
+static int sas_ex_phy_discover(struct domain_device *dev, int single)
+{
+	struct expander_device *ex = &dev->ex_dev;
+	int  res = 0;
+	u8   *disc_req;
+	u8   *disc_resp;
+
+	disc_req = alloc_smp_req(DISCOVER_REQ_SIZE);
+	if (!disc_req)
+		return -ENOMEM;
+
+	disc_resp = alloc_smp_req(DISCOVER_RESP_SIZE);
+	if (!disc_resp) {
+		kfree(disc_req);
+		return -ENOMEM;
+	}
+
+	disc_req[1] = SMP_DISCOVER;
+
+	if (0 <= single && single < ex->num_phys) {
+		disc_req[9] = single;
+		res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE,
+				       disc_resp, DISCOVER_RESP_SIZE);
+		if (res)
+			goto out_err;
+		sas_set_ex_phy(dev, single, disc_resp);
+	} else {
+		int i;
+
+		for (i = 0; i < ex->num_phys; i++) {
+			disc_req[9] = i;
+			res = smp_execute_task(dev, disc_req,
+					       DISCOVER_REQ_SIZE, disc_resp,
+					       DISCOVER_RESP_SIZE);
+			if (res)
+				goto out_err;
+			sas_set_ex_phy(dev, i, disc_resp);
+		}
+	}
+out_err:
+	kfree(disc_resp);
+	kfree(disc_req);
+	return res;
+}
+
+static int sas_expander_discover(struct domain_device *dev)
+{
+	struct expander_device *ex = &dev->ex_dev;
+	int res;
+
+	ex->ex_phy = kzalloc(sizeof(*ex->ex_phy)*ex->num_phys, GFP_KERNEL);
+	if (!ex->ex_phy)
+		return -ENOMEM;
+
+	res = sas_ex_phy_discover(dev, -1);
+	if (res)
+		goto out_err;
+
+	return 0;
+out_err:
+	kfree(ex->ex_phy);
+	ex->ex_phy = NULL;
+	return res;
+}
+
+#define MAX_EXPANDER_PHYS 128
+
+static inline void ex_assign_report_general(struct domain_device *dev,
+					    struct smp_resp *resp)
+{
+	struct report_general_resp *rg = &resp->rg;
+
+	dev->ex_dev.ex_change_count = be16_to_cpu(rg->change_count);
+	dev->ex_dev.max_route_indexes = be16_to_cpu(rg->route_indexes);
+	dev->ex_dev.num_phys = min(rg->num_phys, (u8)MAX_EXPANDER_PHYS);
+	dev->ex_dev.conf_route_table = rg->conf_route_table;
+	dev->ex_dev.configuring = rg->configuring;
+	memcpy(dev->ex_dev.enclosure_logical_id, rg->enclosure_logical_id, 8);
+}
+
+#define RG_REQ_SIZE   8
+#define RG_RESP_SIZE 32
+
+static int sas_ex_general(struct domain_device *dev)
+{
+	u8 *rg_req;
+	struct smp_resp *rg_resp;
+	int res;
+	int i;
+
+	rg_req = alloc_smp_req(RG_REQ_SIZE);
+	if (!rg_req)
+		return -ENOMEM;
+
+	rg_resp = alloc_smp_resp(RG_RESP_SIZE);
+	if (!rg_resp) {
+		kfree(rg_req);
+		return -ENOMEM;
+	}
+
+	rg_req[1] = SMP_REPORT_GENERAL;
+
+	for (i = 0; i < 5; i++) {
+		res = smp_execute_task(dev, rg_req, RG_REQ_SIZE, rg_resp,
+				       RG_RESP_SIZE);
+
+		if (res) {
+			SAS_DPRINTK("RG to ex %016llx failed:0x%x\n",
+				    SAS_ADDR(dev->sas_addr), res);
+			goto out;
+		} else if (rg_resp->result != SMP_RESP_FUNC_ACC) {
+			SAS_DPRINTK("RG:ex %016llx returned SMP result:0x%x\n",
+				    SAS_ADDR(dev->sas_addr), rg_resp->result);
+			res = rg_resp->result;
+			goto out;
+		}
+
+		ex_assign_report_general(dev, rg_resp);
+
+		if (dev->ex_dev.configuring) {
+			SAS_DPRINTK("RG: ex %llx self-configuring...\n",
+				    SAS_ADDR(dev->sas_addr));
+			schedule_timeout_interruptible(5*HZ);
+		} else
+			break;
+	}
+out:
+	kfree(rg_req);
+	kfree(rg_resp);
+	return res;
+}
+
+static inline void ex_assign_manuf_info(struct domain_device *dev, void
+					*_mi_resp)
+{
+	u8 *mi_resp = _mi_resp;
+
+	memcpy(dev->ex_dev.vendor_id, mi_resp + 12, 8);
+	memcpy(dev->ex_dev.product_id, mi_resp + 20, 16);
+	memcpy(dev->ex_dev.product_rev, mi_resp + 36, 4);
+
+	if (mi_resp[8] & 1) {
+		memcpy(dev->ex_dev.component_vendor_id, mi_resp + 40, 8);
+		dev->ex_dev.component_id =
+			be16_to_cpu(*(__be16 *)(mi_resp + 48));
+		dev->ex_dev.component_revision_id = mi_resp[50];
+	}
+}
+
+#define MI_REQ_SIZE   8
+#define MI_RESP_SIZE 64
+
+static int sas_ex_manuf_info(struct domain_device *dev)
+{
+	u8 *mi_req;
+	u8 *mi_resp;
+	int res;
+
+	mi_req = alloc_smp_req(MI_REQ_SIZE);
+	if (!mi_req)
+		return -ENOMEM;
+
+	mi_resp = alloc_smp_resp(MI_RESP_SIZE);
+	if (!mi_resp) {
+		kfree(mi_req);
+		return -ENOMEM;
+	}
+
+	mi_req[1] = SMP_REPORT_MANUF_INFO;
+
+	res = smp_execute_task(dev, mi_req, MI_REQ_SIZE, mi_resp,MI_RESP_SIZE);
+	if (res) {
+		SAS_DPRINTK("MI: ex %016llx failed:0x%x\n",
+			    SAS_ADDR(dev->sas_addr), res);
+		goto out;
+	} else if (mi_resp[2] != SMP_RESP_FUNC_ACC) {
+		SAS_DPRINTK("MI ex %016llx returned SMP result:0x%x\n",
+			    SAS_ADDR(dev->sas_addr), mi_resp[2]);
+		goto out;
+	}
+
+	ex_assign_manuf_info(dev, mi_resp);
+out:
+	kfree(mi_req);
+	kfree(mi_resp);
+	return res;
+}
+
+#define PC_REQ_SIZE  44
+#define PC_RESP_SIZE 8
+
+static int smp_phy_control(struct domain_device *dev, int phy_id,
+			   enum phy_func phy_func)
+{
+	u8 *pc_req;
+	u8 *pc_resp;
+	int res;
+
+	pc_req = alloc_smp_req(PC_REQ_SIZE);
+	if (!pc_req)
+		return -ENOMEM;
+
+	pc_resp = alloc_smp_resp(PC_RESP_SIZE);
+	if (!pc_resp) {
+		kfree(pc_req);
+		return -ENOMEM;
+	}
+
+	pc_req[1] = SMP_PHY_CONTROL;
+	pc_req[9] = phy_id;
+	pc_req[10]= phy_func;
+
+	res = smp_execute_task(dev, pc_req, PC_REQ_SIZE, pc_resp,PC_RESP_SIZE);
+
+	kfree(pc_resp);
+	kfree(pc_req);
+	return res;
+}
+
+static inline void sas_ex_disable_phy(struct domain_device *dev, int phy_id)
+{
+	struct expander_device *ex = &dev->ex_dev;
+	struct ex_phy *phy = &ex->ex_phy[phy_id];
+
+	smp_phy_control(dev, phy_id, PHY_FUNC_DISABLE);
+	phy->linkrate = PHY_DISABLED;
+}
+
+static inline void sas_ex_disable_port(struct domain_device *dev, u8 *sas_addr)
+{
+	struct expander_device *ex = &dev->ex_dev;
+	int i;
+
+	for (i = 0; i < ex->num_phys; i++) {
+		struct ex_phy *phy = &ex->ex_phy[i];
+
+		if (phy->phy_state == PHY_VACANT ||
+		    phy->phy_state == PHY_NOT_PRESENT)
+			continue;
+
+		if (SAS_ADDR(phy->attached_sas_addr) == SAS_ADDR(sas_addr))
+			sas_ex_disable_phy(dev, i);
+	}
+}
+
+static inline int sas_dev_present_in_domain(struct sas_port *port,
+					    u8 *sas_addr)
+{
+	struct domain_device *dev;
+
+	if (SAS_ADDR(port->sas_addr) == SAS_ADDR(sas_addr))
+		return 1;
+	list_for_each_entry(dev, &port->dev_list, dev_list_node) {
+		if (SAS_ADDR(dev->sas_addr) == SAS_ADDR(sas_addr))
+			return 1;
+	}
+	return 0;
+}
+
+#define RPS_REQ_SIZE  16
+#define RPS_RESP_SIZE 60
+
+static inline int sas_get_report_phy_sata(struct domain_device *dev,
+					  int phy_id,
+					  struct smp_resp *rps_resp)
+{
+	int res;
+	u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE);
+
+	if (!rps_req)
+		return -ENOMEM;
+
+	rps_req[1] = SMP_REPORT_PHY_SATA;
+	rps_req[9] = phy_id;
+
+	res = smp_execute_task(dev, rps_req, RPS_REQ_SIZE,
+			            rps_resp, RPS_RESP_SIZE);
+
+	kfree(rps_req);
+	return 0;
+}
+
+static inline void sas_ex_get_linkrate(struct domain_device *parent,
+				       struct domain_device *child,
+				       struct ex_phy *parent_phy)
+{
+	struct expander_device *parent_ex = &parent->ex_dev;
+	int i;
+
+	child->pathways = 0;
+
+	for (i = 0; i < parent_ex->num_phys; i++) {
+		struct ex_phy *phy = &parent_ex->ex_phy[i];
+
+		if (phy->phy_state == PHY_VACANT ||
+		    phy->phy_state == PHY_NOT_PRESENT)
+			continue;
+
+		if (SAS_ADDR(phy->attached_sas_addr) ==
+		    SAS_ADDR(child->sas_addr)) {
+
+			child->min_linkrate = min(parent->min_linkrate,
+						  phy->linkrate);
+			child->max_linkrate = max(parent->max_linkrate,
+						  phy->linkrate);
+			child->pathways++;
+		}
+	}
+	child->linkrate = min(parent_phy->linkrate, child->max_linkrate);
+	child->pathways = min(child->pathways, parent->pathways);
+}
+
+static struct domain_device *sas_ex_discover_end_dev(
+	struct domain_device *parent, int phy_id)
+{
+	struct expander_device *parent_ex = &parent->ex_dev;
+	struct ex_phy *phy = &parent_ex->ex_phy[phy_id];
+	struct domain_device *child = NULL;
+	int res;
+
+	if (phy->attached_sata_host || phy->attached_sata_ps)
+		return NULL;
+
+	child = kzalloc(sizeof(*child), GFP_KERNEL);
+	if (!child)
+		return NULL;
+
+	child->parent = parent;
+	child->port   = parent->port;
+	child->iproto = phy->attached_iproto;
+	memcpy(child->sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE);
+	sas_hash_addr(child->hashed_sas_addr, child->sas_addr);
+	sas_ex_get_linkrate(parent, child, phy);
+
+	if ((phy->attached_tproto & SAS_PROTO_STP) || phy->attached_sata_dev) {
+		child->dev_type = SATA_DEV;
+		if (phy->attached_tproto & SAS_PROTO_STP)
+			child->tproto = phy->attached_tproto;
+		if (phy->attached_sata_dev)
+			child->tproto |= SATA_DEV;
+		res = sas_get_report_phy_sata(parent, phy_id,
+					      &child->sata_dev.rps_resp);
+		if (res) {
+			SAS_DPRINTK("report phy sata to %016llx:0x%x returned "
+				    "0x%x\n", SAS_ADDR(parent->sas_addr),
+				    phy_id, res);
+			kfree(child);
+			return NULL;
+		}
+		memcpy(child->frame_rcvd, &child->sata_dev.rps_resp.rps.fis,
+		       sizeof(struct dev_to_host_fis));
+		sas_init_dev(child);
+		res = sas_discover_sata(child);
+		if (res) {
+			SAS_DPRINTK("sas_discover_sata() for device %16llx at "
+				    "%016llx:0x%x returned 0x%x\n",
+				    SAS_ADDR(child->sas_addr),
+				    SAS_ADDR(parent->sas_addr), phy_id, res);
+			kfree(child);
+			return NULL;
+		}
+	} else if (phy->attached_tproto & SAS_PROTO_SSP) {
+		child->dev_type = SAS_END_DEV;
+		child->tproto = phy->attached_tproto;
+		sas_init_dev(child);
+		res = sas_discover_end_dev(child);
+		if (res) {
+			SAS_DPRINTK("sas_discover_end_dev() for device %16llx "
+				    "at %016llx:0x%x returned 0x%x\n",
+				    SAS_ADDR(child->sas_addr),
+				    SAS_ADDR(parent->sas_addr), phy_id, res);
+			kfree(child);
+			return NULL;
+		}
+	} else {
+		SAS_DPRINTK("target proto 0x%x at %016llx:0x%x not handled\n",
+			    phy->attached_tproto, SAS_ADDR(parent->sas_addr),
+			    phy_id);
+	}
+	list_add_tail(&child->siblings, &parent_ex->children);
+	return child;
+}
+
+static struct domain_device *sas_ex_discover_expander(
+	struct domain_device *parent, int phy_id)
+{
+	struct expander_device *parent_ex = &parent->ex_dev;
+	struct ex_phy *phy = &parent_ex->ex_phy[phy_id];
+	struct domain_device *child = NULL;
+	int res;
+
+	if (phy->routing_attr == DIRECT_ROUTING) {
+		SAS_DPRINTK("ex %016llx:0x%x:D <--> ex %016llx:0x%x is not "
+			    "allowed\n",
+			    SAS_ADDR(parent->sas_addr), phy_id,
+			    SAS_ADDR(phy->attached_sas_addr),
+			    phy->attached_phy_id);
+		return NULL;
+	}
+	child = kzalloc(sizeof(*child), GFP_KERNEL);
+	if (!child)
+		return NULL;
+	child->dev_type = phy->attached_dev_type;
+	child->parent = parent;
+	child->port = parent->port;
+	child->iproto = phy->attached_iproto;
+	child->tproto = phy->attached_tproto;
+	memcpy(child->sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE);
+	sas_hash_addr(child->hashed_sas_addr, child->sas_addr);
+	sas_ex_get_linkrate(parent, child, phy);
+	child->ex_dev.level = parent_ex->level + 1;
+	parent->port->disc.max_level = max(parent->port->disc.max_level,
+					   child->ex_dev.level);
+	sas_init_dev(child);
+	res = sas_discover_expander(child);
+	if (res) {
+		kfree(child);
+		return NULL;
+	}
+	list_add_tail(&child->siblings, &parent_ex->children);
+	return child;
+}
+
+static int sas_ex_discover_dev(struct domain_device *dev, int phy_id)
+{
+	struct expander_device *ex = &dev->ex_dev;
+	struct ex_phy *ex_phy = &ex->ex_phy[phy_id];
+	struct domain_device *child = NULL;
+	int res = 0;
+
+	/* Phy state */
+	if (ex_phy->linkrate == PHY_SPINUP_HOLD) {
+		if (!smp_phy_control(dev, phy_id, PHY_FUNC_LINK_RESET))
+			res = sas_ex_phy_discover(dev, phy_id);
+		if (res)
+			return res;
+	}
+
+	/* Parent and domain coherency */
+	if (!dev->parent && (SAS_ADDR(ex_phy->attached_sas_addr) ==
+			     SAS_ADDR(dev->port->sas_addr)))
+		return 0;
+	if (dev->parent && (SAS_ADDR(ex_phy->attached_sas_addr) ==
+			    SAS_ADDR(dev->parent->sas_addr)))
+		return 0;
+	if (sas_dev_present_in_domain(dev->port, ex_phy->attached_sas_addr))
+		sas_ex_disable_port(dev, ex_phy->attached_sas_addr);
+
+	if (ex_phy->attached_dev_type == NO_DEVICE) {
+		if (ex_phy->routing_attr == DIRECT_ROUTING) {
+			memset(ex_phy->attached_sas_addr, 0, SAS_ADDR_SIZE);
+			sas_configure_routing(dev, ex_phy->attached_sas_addr);
+		}
+		return 0;
+	} else if (ex_phy->linkrate == PHY_LINKRATE_UNKNOWN)
+		return 0;
+
+	if (ex_phy->attached_dev_type != SAS_END_DEV &&
+	    ex_phy->attached_dev_type != FANOUT_DEV &&
+	    ex_phy->attached_dev_type != EDGE_DEV) {
+		SAS_DPRINTK("unknown device type(0x%x) attached to ex %016llx "
+			    "phy 0x%x\n", ex_phy->attached_dev_type,
+			    SAS_ADDR(dev->sas_addr),
+			    phy_id);
+		return 0;
+	}
+
+	res = sas_configure_routing(dev, ex_phy->attached_sas_addr);
+	if (res) {
+		SAS_DPRINTK("configure routing for dev %016llx "
+			    "reported 0x%x. Forgotten\n",
+			    SAS_ADDR(ex_phy->attached_sas_addr), res);
+		sas_disable_routing(dev, ex_phy->attached_sas_addr);
+		return res;
+	}
+
+	switch (ex_phy->attached_dev_type) {
+	case SAS_END_DEV:
+		child = sas_ex_discover_end_dev(dev, phy_id);
+		break;
+	case FANOUT_DEV:
+		if (SAS_ADDR(dev->port->disc.fanout_sas_addr)) {
+			SAS_DPRINTK("second fanout expander %016llx phy 0x%x "
+				    "attached to ex %016llx phy 0x%x\n",
+				    SAS_ADDR(ex_phy->attached_sas_addr),
+				    ex_phy->attached_phy_id,
+				    SAS_ADDR(dev->sas_addr),
+				    phy_id);
+			sas_ex_disable_phy(dev, phy_id);
+			break;
+		} else
+			memcpy(dev->port->disc.fanout_sas_addr,
+			       ex_phy->attached_sas_addr, SAS_ADDR_SIZE);
+		/* fallthrough */
+	case EDGE_DEV:
+		child = sas_ex_discover_expander(dev, phy_id);
+		break;
+	default:
+		break;
+	}
+
+	if (child) {
+		int i;
+
+		for (i = 0; i < ex->num_phys; i++) {
+			if (ex->ex_phy[i].phy_state == PHY_VACANT ||
+			    ex->ex_phy[i].phy_state == PHY_NOT_PRESENT)
+				continue;
+
+			if (SAS_ADDR(ex->ex_phy[i].attached_sas_addr) ==
+			    SAS_ADDR(child->sas_addr))
+				ex->ex_phy[i].phy_state= PHY_DEVICE_DISCOVERED;
+		}
+	}
+
+	return res;
+}
+
+static inline int sas_find_sub_addr(struct domain_device *dev, u8 *sub_addr)
+{
+	struct expander_device *ex = &dev->ex_dev;
+	int i;
+
+	for (i = 0; i < ex->num_phys; i++) {
+		struct ex_phy *phy = &ex->ex_phy[i];
+
+		if (phy->phy_state == PHY_VACANT ||
+		    phy->phy_state == PHY_NOT_PRESENT)
+			continue;
+
+		if ((phy->attached_dev_type == EDGE_DEV ||
+		     phy->attached_dev_type == FANOUT_DEV) &&
+		    phy->routing_attr == SUBTRACTIVE_ROUTING) {
+
+			memcpy(sub_addr, phy->attached_sas_addr,SAS_ADDR_SIZE);
+
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int sas_check_level_subtractive_boundary(struct domain_device *dev)
+{
+	struct expander_device *ex = &dev->ex_dev;
+	struct domain_device *child;
+	u8 sub_addr[8] = {0, };
+
+	list_for_each_entry(child, &ex->children, siblings) {
+		if (child->dev_type != EDGE_DEV &&
+		    child->dev_type != FANOUT_DEV)
+			continue;
+		if (sub_addr[0] == 0) {
+			sas_find_sub_addr(child, sub_addr);
+			continue;
+		} else {
+			u8 s2[8];
+
+			if (sas_find_sub_addr(child, s2) &&
+			    (SAS_ADDR(sub_addr) != SAS_ADDR(s2))) {
+
+				SAS_DPRINTK("ex %016llx->%016llx-?->%016llx "
+					    "diverges from subtractive "
+					    "boundary %016llx\n",
+					    SAS_ADDR(dev->sas_addr),
+					    SAS_ADDR(child->sas_addr),
+					    SAS_ADDR(s2),
+					    SAS_ADDR(sub_addr));
+
+				sas_ex_disable_port(child, s2);
+			}
+		}
+	}
+	return 0;
+}
+/**
+ * sas_ex_discover_devices -- discover devices attached to this expander
+ * dev: pointer to the expander domain device
+ * single: if you want to do a single phy, else set to -1;
+ *
+ * Configure this expander for use with its devices and register the
+ * devices of this expander.
+ */
+static int sas_ex_discover_devices(struct domain_device *dev, int single)
+{
+	struct expander_device *ex = &dev->ex_dev;
+	int i = 0, end = ex->num_phys;
+	int res = 0;
+
+	if (0 <= single && single < end) {
+		i = single;
+		end = i+1;
+	}
+
+	for ( ; i < end; i++) {
+		struct ex_phy *ex_phy = &ex->ex_phy[i];
+
+		if (ex_phy->phy_state == PHY_VACANT ||
+		    ex_phy->phy_state == PHY_NOT_PRESENT ||
+		    ex_phy->phy_state == PHY_DEVICE_DISCOVERED)
+			continue;
+
+		switch (ex_phy->linkrate) {
+		case PHY_DISABLED:
+		case PHY_RESET_PROBLEM:
+		case PHY_PORT_SELECTOR:
+			continue;
+		default:
+			res = sas_ex_discover_dev(dev, i);
+			if (res)
+				break;
+			continue;
+		}
+	}
+
+	if (!res)
+		sas_check_level_subtractive_boundary(dev);
+
+	return res;
+}
+
+static int sas_check_ex_subtractive_boundary(struct domain_device *dev)
+{
+	struct expander_device *ex = &dev->ex_dev;
+	int i;
+	u8  *sub_sas_addr = NULL;
+
+	if (dev->dev_type != EDGE_DEV)
+		return 0;
+
+	for (i = 0; i < ex->num_phys; i++) {
+		struct ex_phy *phy = &ex->ex_phy[i];
+
+		if (phy->phy_state == PHY_VACANT ||
+		    phy->phy_state == PHY_NOT_PRESENT)
+			continue;
+
+		if ((phy->attached_dev_type == FANOUT_DEV ||
+		     phy->attached_dev_type == EDGE_DEV) &&
+		    phy->routing_attr == SUBTRACTIVE_ROUTING) {
+
+			if (!sub_sas_addr)
+				sub_sas_addr = &phy->attached_sas_addr[0];
+			else if (SAS_ADDR(sub_sas_addr) !=
+				 SAS_ADDR(phy->attached_sas_addr)) {
+
+				SAS_DPRINTK("ex %016llx phy 0x%x "
+					    "diverges(%016llx) on subtractive "
+					    "boundary(%016llx). Disabled\n",
+					    SAS_ADDR(dev->sas_addr), i,
+					    SAS_ADDR(phy->attached_sas_addr),
+					    SAS_ADDR(sub_sas_addr));
+				sas_ex_disable_phy(dev, i);
+			}
+		}
+	}
+	return 0;
+}
+
+static inline void sas_print_parent_topology_bug(struct domain_device *child,
+						 struct ex_phy *parent_phy,
+						 struct ex_phy *child_phy)
+{
+	static const char ra_char[] = {
+		[DIRECT_ROUTING] = 'D',
+		[SUBTRACTIVE_ROUTING] = 'S',
+		[TABLE_ROUTING] = 'T',
+	};
+	static const char *ex_type[] = {
+		[EDGE_DEV] = "edge",
+		[FANOUT_DEV] = "fanout",
+	};
+	struct domain_device *parent = child->parent;
+
+	sas_printk("%s ex %016llx phy 0x%x <--> %s ex %016llx phy 0x%x "
+		   "has %c:%c routing link!\n",
+
+		   ex_type[parent->dev_type],
+		   SAS_ADDR(parent->sas_addr),
+		   parent_phy->phy_id,
+
+		   ex_type[child->dev_type],
+		   SAS_ADDR(child->sas_addr),
+		   child_phy->phy_id,
+
+		   ra_char[parent_phy->routing_attr],
+		   ra_char[child_phy->routing_attr]);
+}
+
+static inline int sas_check_eeds(struct domain_device *child,
+				 struct ex_phy *parent_phy,
+				 struct ex_phy *child_phy)
+{
+	int res = 0;
+	struct domain_device *parent = child->parent;
+
+	if (SAS_ADDR(parent->port->disc.fanout_sas_addr) != 0) {
+		res = -ENODEV;
+		SAS_DPRINTK("edge ex %016llx phy S:0x%x <--> edge ex %016llx "
+			    "phy S:0x%x, while there is a fanout ex %016llx\n",
+			    SAS_ADDR(parent->sas_addr),
+			    parent_phy->phy_id,
+			    SAS_ADDR(child->sas_addr),
+			    child_phy->phy_id,
+			    SAS_ADDR(parent->port->disc.fanout_sas_addr));
+	} else if (SAS_ADDR(parent->port->disc.eeds_a) == 0) {
+		memcpy(parent->port->disc.eeds_a, parent->sas_addr,
+		       SAS_ADDR_SIZE);
+		memcpy(parent->port->disc.eeds_b, child->sas_addr,
+		       SAS_ADDR_SIZE);
+	} else if (((SAS_ADDR(parent->port->disc.eeds_a) ==
+		    SAS_ADDR(parent->sas_addr)) ||
+		   (SAS_ADDR(parent->port->disc.eeds_a) ==
+		    SAS_ADDR(child->sas_addr)))
+		   &&
+		   ((SAS_ADDR(parent->port->disc.eeds_b) ==
+		     SAS_ADDR(parent->sas_addr)) ||
+		    (SAS_ADDR(parent->port->disc.eeds_b) ==
+		     SAS_ADDR(child->sas_addr))))
+		;
+	else {
+		res = -ENODEV;
+		SAS_DPRINTK("edge ex %016llx phy 0x%x <--> edge ex %016llx "
+			    "phy 0x%x link forms a third EEDS!\n",
+			    SAS_ADDR(parent->sas_addr),
+			    parent_phy->phy_id,
+			    SAS_ADDR(child->sas_addr),
+			    child_phy->phy_id);
+	}
+
+	return res;
+}
+
+/* Here we spill over 80 columns.  It is intentional.
+ */
+static int sas_check_parent_topology(struct domain_device *child)
+{
+	struct expander_device *child_ex = &child->ex_dev;
+	struct expander_device *parent_ex;
+	int i;
+	int res = 0;
+
+	if (!child->parent)
+		return 0;
+
+	if (child->parent->dev_type != EDGE_DEV &&
+	    child->parent->dev_type != FANOUT_DEV)
+		return 0;
+
+	parent_ex = &child->parent->ex_dev;
+
+	for (i = 0; i < parent_ex->num_phys; i++) {
+		struct ex_phy *parent_phy = &parent_ex->ex_phy[i];
+		struct ex_phy *child_phy;
+
+		if (parent_phy->phy_state == PHY_VACANT ||
+		    parent_phy->phy_state == PHY_NOT_PRESENT)
+			continue;
+
+		if (SAS_ADDR(parent_phy->attached_sas_addr) != SAS_ADDR(child->sas_addr))
+			continue;
+
+		child_phy = &child_ex->ex_phy[parent_phy->attached_phy_id];
+
+		switch (child->parent->dev_type) {
+		case EDGE_DEV:
+			if (child->dev_type == FANOUT_DEV) {
+				if (parent_phy->routing_attr != SUBTRACTIVE_ROUTING ||
+				    child_phy->routing_attr != TABLE_ROUTING) {
+					sas_print_parent_topology_bug(child, parent_phy, child_phy);
+					res = -ENODEV;
+				}
+			} else if (parent_phy->routing_attr == SUBTRACTIVE_ROUTING) {
+				if (child_phy->routing_attr == SUBTRACTIVE_ROUTING) {
+					res = sas_check_eeds(child, parent_phy, child_phy);
+				} else if (child_phy->routing_attr != TABLE_ROUTING) {
+					sas_print_parent_topology_bug(child, parent_phy, child_phy);
+					res = -ENODEV;
+				}
+			} else if (parent_phy->routing_attr == TABLE_ROUTING &&
+				   child_phy->routing_attr != SUBTRACTIVE_ROUTING) {
+				sas_print_parent_topology_bug(child, parent_phy, child_phy);
+				res = -ENODEV;
+			}
+			break;
+		case FANOUT_DEV:
+			if (parent_phy->routing_attr != TABLE_ROUTING ||
+			    child_phy->routing_attr != SUBTRACTIVE_ROUTING) {
+				sas_print_parent_topology_bug(child, parent_phy, child_phy);
+				res = -ENODEV;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	return res;
+}
+
+#define RRI_REQ_SIZE  16
+#define RRI_RESP_SIZE 44
+
+static int sas_configure_present(struct domain_device *dev, int phy_id,
+				 u8 *sas_addr, int *index, int *present)
+{
+	int i, res = 0;
+	struct expander_device *ex = &dev->ex_dev;
+	struct ex_phy *phy = &ex->ex_phy[phy_id];
+	u8 *rri_req;
+	u8 *rri_resp;
+
+	*present = 0;
+	*index = 0;
+
+	rri_req = alloc_smp_req(RRI_REQ_SIZE);
+	if (!rri_req)
+		return -ENOMEM;
+
+	rri_resp = alloc_smp_resp(RRI_RESP_SIZE);
+	if (!rri_resp) {
+		kfree(rri_req);
+		return -ENOMEM;
+	}
+
+	rri_req[1] = SMP_REPORT_ROUTE_INFO;
+	rri_req[9] = phy_id;
+
+	for (i = 0; i < ex->max_route_indexes ; i++) {
+		*(__be16 *)(rri_req+6) = cpu_to_be16(i);
+		res = smp_execute_task(dev, rri_req, RRI_REQ_SIZE, rri_resp,
+				       RRI_RESP_SIZE);
+		if (res)
+			goto out;
+		res = rri_resp[2];
+		if (res == SMP_RESP_NO_INDEX) {
+			SAS_DPRINTK("overflow of indexes: dev %016llx "
+				    "phy 0x%x index 0x%x\n",
+				    SAS_ADDR(dev->sas_addr), phy_id, i);
+			goto out;
+		} else if (res != SMP_RESP_FUNC_ACC) {
+			SAS_DPRINTK("%s: dev %016llx phy 0x%x index 0x%x "
+				    "result 0x%x\n", __FUNCTION__,
+				    SAS_ADDR(dev->sas_addr), phy_id, i, res);
+			goto out;
+		}
+		if (SAS_ADDR(sas_addr) != 0) {
+			if (SAS_ADDR(rri_resp+16) == SAS_ADDR(sas_addr)) {
+				*index = i;
+				if ((rri_resp[12] & 0x80) == 0x80)
+					*present = 0;
+				else
+					*present = 1;
+				goto out;
+			} else if (SAS_ADDR(rri_resp+16) == 0) {
+				*index = i;
+				*present = 0;
+				goto out;
+			}
+		} else if (SAS_ADDR(rri_resp+16) == 0 &&
+			   phy->last_da_index < i) {
+			phy->last_da_index = i;
+			*index = i;
+			*present = 0;
+			goto out;
+		}
+	}
+	res = -1;
+out:
+	kfree(rri_req);
+	kfree(rri_resp);
+	return res;
+}
+
+#define CRI_REQ_SIZE  44
+#define CRI_RESP_SIZE  8
+
+static int sas_configure_set(struct domain_device *dev, int phy_id,
+			     u8 *sas_addr, int index, int include)
+{
+	int res;
+	u8 *cri_req;
+	u8 *cri_resp;
+
+	cri_req = alloc_smp_req(CRI_REQ_SIZE);
+	if (!cri_req)
+		return -ENOMEM;
+
+	cri_resp = alloc_smp_resp(CRI_RESP_SIZE);
+	if (!cri_resp) {
+		kfree(cri_req);
+		return -ENOMEM;
+	}
+
+	cri_req[1] = SMP_CONF_ROUTE_INFO;
+	*(__be16 *)(cri_req+6) = cpu_to_be16(index);
+	cri_req[9] = phy_id;
+	if (SAS_ADDR(sas_addr) == 0 || !include)
+		cri_req[12] |= 0x80;
+	memcpy(cri_req+16, sas_addr, SAS_ADDR_SIZE);
+
+	res = smp_execute_task(dev, cri_req, CRI_REQ_SIZE, cri_resp,
+			       CRI_RESP_SIZE);
+	if (res)
+		goto out;
+	res = cri_resp[2];
+	if (res == SMP_RESP_NO_INDEX) {
+		SAS_DPRINTK("overflow of indexes: dev %016llx phy 0x%x "
+			    "index 0x%x\n",
+			    SAS_ADDR(dev->sas_addr), phy_id, index);
+	}
+out:
+	kfree(cri_req);
+	kfree(cri_resp);
+	return res;
+}
+
+static inline int sas_configure_phy(struct domain_device *dev, int phy_id,
+				    u8 *sas_addr, int include)
+{
+	int index;
+	int present;
+	int res;
+
+	res = sas_configure_present(dev, phy_id, sas_addr, &index, &present);
+	if (res)
+		return res;
+	if (include ^ present)
+		return sas_configure_set(dev, phy_id, sas_addr, index,include);
+
+	return res;
+}
+
+/**
+ * sas_configure_parent -- configure routing table of parent
+ * parent: parent expander
+ * child: child expander
+ * sas_addr: SAS port identifier of device directly attached to child
+ */
+static int sas_configure_parent(struct domain_device *parent,
+				struct domain_device *child,
+				u8 *sas_addr, int include)
+{
+	struct expander_device *ex_parent = &parent->ex_dev;
+	int res = 0;
+	int i;
+
+	if (parent->parent) {
+		res = sas_configure_parent(parent->parent, parent, sas_addr,
+					   include);
+		if (res)
+			return res;
+	}
+
+	if (ex_parent->conf_route_table == 0) {
+		SAS_DPRINTK("ex %016llx has self-configuring routing table\n",
+			    SAS_ADDR(parent->sas_addr));
+		return 0;
+	}
+
+	for (i = 0; i < ex_parent->num_phys; i++) {
+		struct ex_phy *phy = &ex_parent->ex_phy[i];
+
+		if ((phy->routing_attr == TABLE_ROUTING) &&
+		    (SAS_ADDR(phy->attached_sas_addr) ==
+		     SAS_ADDR(child->sas_addr))) {
+			res = sas_configure_phy(parent, i, sas_addr, include);
+			if (res)
+				return res;
+		}
+	}
+
+	return res;
+}
+
+/**
+ * sas_configure_routing -- configure routing
+ * dev: expander device
+ * sas_addr: port identifier of device directly attached to the expander device
+ */
+static int sas_configure_routing(struct domain_device *dev, u8 *sas_addr)
+{
+	if (dev->parent)
+		return sas_configure_parent(dev->parent, dev, sas_addr, 1);
+	return 0;
+}
+
+static int sas_disable_routing(struct domain_device *dev,  u8 *sas_addr)
+{
+	if (dev->parent)
+		return sas_configure_parent(dev->parent, dev, sas_addr, 0);
+	return 0;
+}
+
+#define SMP_BIN_ATTR_NAME "smp_portal"
+
+static void sas_ex_smp_hook(struct domain_device *dev)
+{
+	struct expander_device *ex_dev = &dev->ex_dev;
+	struct bin_attribute *bin_attr = &ex_dev->smp_bin_attr;
+
+	memset(bin_attr, 0, sizeof(*bin_attr));
+
+	bin_attr->attr.name = SMP_BIN_ATTR_NAME;
+	bin_attr->attr.owner = THIS_MODULE;
+	bin_attr->attr.mode = 0600;
+
+	bin_attr->size = 0;
+	bin_attr->private = NULL;
+	bin_attr->read = smp_portal_read;
+	bin_attr->write= smp_portal_write;
+	bin_attr->mmap = NULL;
+
+	ex_dev->smp_portal_pid = -1;
+	init_MUTEX(&ex_dev->smp_sema);
+}
+
+/**
+ * sas_discover_expander -- expander discovery
+ * @ex: pointer to expander domain device
+ *
+ * See comment in sas_discover_sata().
+ */
+static int sas_discover_expander(struct domain_device *dev)
+{
+	int res;
+
+	res = sas_notify_lldd_dev_found(dev);
+	if (res)
+		return res;
+
+	res = sas_ex_general(dev);
+	if (res)
+		goto out_err;
+	res = sas_ex_manuf_info(dev);
+	if (res)
+		goto out_err;
+
+	res = sas_expander_discover(dev);
+	if (res) {
+		SAS_DPRINTK("expander %016llx discovery failed(0x%x)\n",
+			    SAS_ADDR(dev->sas_addr), res);
+		goto out_err;
+	}
+
+	sas_check_ex_subtractive_boundary(dev);
+	res = sas_check_parent_topology(dev);
+	if (res)
+		goto out_err;
+	sas_ex_smp_hook(dev);
+	sas_kobj_set(dev);
+	return 0;
+out_err:
+	sas_notify_lldd_dev_gone(dev);
+	return res;
+}
+
+static int sas_ex_level_discovery(struct sas_port *port, const int level)
+{
+	int res = 0;
+	struct domain_device *dev;
+
+	list_for_each_entry(dev, &port->dev_list, dev_list_node) {
+		if (dev->dev_type == EDGE_DEV ||
+		    dev->dev_type == FANOUT_DEV) {
+			struct expander_device *ex = &dev->ex_dev;
+
+			if (level == ex->level)
+				res = sas_ex_discover_devices(dev, -1);
+		}
+	}
+
+	return res;
+}
+
+static int sas_ex_bfs_disc(struct sas_port *port)
+{
+	int res;
+	int level;
+
+	do {
+		level = port->disc.max_level;
+		res = sas_ex_level_discovery(port, level);
+		mb();
+	} while (level < port->disc.max_level);
+
+	return res;
+}
+
+int sas_discover_root_expander(struct domain_device *dev)
+{
+	int res;
+
+	dev->ex_dev.level = dev->port->disc.max_level; /* 0 */
+	res = sas_discover_expander(dev);
+	if (!res)
+		sas_ex_bfs_disc(dev->port);
+
+	return res;
+}
+
+void sas_init_ex_attr(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ex_attrs)-1; i++)
+		ex_dev_attrs[i] = &ex_attrs[i].attr;
+	ex_dev_attrs[i] = NULL;
+}
+
+/* ---------- Domain revalidation ---------- */
+
+static int sas_get_phy_discover(struct domain_device *dev,
+				int phy_id, struct smp_resp *disc_resp)
+{
+	int res;
+	u8 *disc_req;
+
+	disc_req = alloc_smp_req(DISCOVER_REQ_SIZE);
+	if (!disc_req)
+		return -ENOMEM;
+
+	disc_req[1] = SMP_DISCOVER;
+	disc_req[9] = phy_id;
+
+	res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE,
+			       disc_resp, DISCOVER_RESP_SIZE);
+	if (res)
+		goto out;
+	else if (disc_resp->result != SMP_RESP_FUNC_ACC) {
+		res = disc_resp->result;
+		goto out;
+	}
+out:
+	kfree(disc_req);
+	return res;
+}
+
+static int sas_get_phy_change_count(struct domain_device *dev,
+				    int phy_id, int *pcc)
+{
+	int res;
+	struct smp_resp *disc_resp;
+
+	disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE);
+	if (!disc_resp)
+		return -ENOMEM;
+
+	res = sas_get_phy_discover(dev, phy_id, disc_resp);
+	if (!res)
+		*pcc = disc_resp->disc.change_count;
+
+	kfree(disc_resp);
+	return res;
+}
+
+static int sas_get_phy_attached_sas_addr(struct domain_device *dev,
+					 int phy_id, u8 *attached_sas_addr)
+{
+	int res;
+	struct smp_resp *disc_resp;
+
+	disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE);
+	if (!disc_resp)
+		return -ENOMEM;
+
+	res = sas_get_phy_discover(dev, phy_id, disc_resp);
+	if (!res)
+		memcpy(attached_sas_addr,disc_resp->disc.attached_sas_addr,8);
+
+	kfree(disc_resp);
+	return res;
+}
+
+static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id,
+			      int from_phy)
+{
+	struct expander_device *ex = &dev->ex_dev;
+	int res = 0;
+	int i;
+
+	for (i = from_phy; i < ex->num_phys; i++) {
+		int phy_change_count;
+
+		res = sas_get_phy_change_count(dev, i, &phy_change_count);
+		if (res)
+			goto out;
+		else if (phy_change_count != ex->ex_phy[i].phy_change_count) {
+			ex->ex_phy[i].phy_change_count = phy_change_count;
+			*phy_id = i;
+			return 0;
+		}
+	}
+out:
+	return res;
+}
+
+static int sas_get_ex_change_count(struct domain_device *dev, int *ecc)
+{
+	int res;
+	u8  *rg_req;
+	struct smp_resp  *rg_resp;
+
+	rg_req = alloc_smp_req(RG_REQ_SIZE);
+	if (!rg_req)
+		return -ENOMEM;
+
+	rg_resp = alloc_smp_resp(RG_RESP_SIZE);
+	if (!rg_resp) {
+		kfree(rg_req);
+		return -ENOMEM;
+	}
+
+	rg_req[1] = SMP_REPORT_GENERAL;
+
+	res = smp_execute_task(dev, rg_req, RG_REQ_SIZE, rg_resp,
+			       RG_RESP_SIZE);
+	if (res)
+		goto out;
+	if (rg_resp->result != SMP_RESP_FUNC_ACC) {
+		res = rg_resp->result;
+		goto out;
+	}
+
+	*ecc = be16_to_cpu(rg_resp->rg.change_count);
+out:
+	kfree(rg_resp);
+	kfree(rg_req);
+	return res;
+}
+
+static int sas_find_bcast_dev(struct domain_device *dev,
+			      struct domain_device **src_dev)
+{
+	struct expander_device *ex = &dev->ex_dev;
+	int ex_change_count = -1;
+	int res;
+
+	res = sas_get_ex_change_count(dev, &ex_change_count);
+	if (res)
+		goto out;
+	if (ex_change_count != -1 &&
+	    ex_change_count != ex->ex_change_count) {
+		*src_dev = dev;
+		ex->ex_change_count = ex_change_count;
+	} else {
+		struct domain_device *ch;
+
+		list_for_each_entry(ch, &ex->children, siblings) {
+			if (ch->dev_type == EDGE_DEV ||
+			    ch->dev_type == FANOUT_DEV) {
+				res = sas_find_bcast_dev(ch, src_dev);
+				if (src_dev)
+					return res;
+			}
+		}
+	}
+out:
+	return res;
+}
+
+static void sas_unregister_ex_tree(struct domain_device *dev)
+{
+	struct expander_device *ex = &dev->ex_dev;
+	struct domain_device *child, *n;
+
+	list_for_each_entry_safe(child, n, &ex->children, siblings) {
+		if (child->dev_type == EDGE_DEV ||
+		    child->dev_type == FANOUT_DEV)
+			sas_unregister_ex_tree(child);
+		else
+			sas_unregister_dev(child);
+	}
+	sas_unregister_dev(dev);
+}
+
+static void sas_unregister_devs_sas_addr(struct domain_device *parent,
+					 int phy_id)
+{
+	struct expander_device *ex_dev = &parent->ex_dev;
+	struct ex_phy *phy = &ex_dev->ex_phy[phy_id];
+	struct domain_device *child, *n;
+
+	list_for_each_entry_safe(child, n, &ex_dev->children, siblings) {
+		if (SAS_ADDR(child->sas_addr) ==
+		    SAS_ADDR(phy->attached_sas_addr)) {
+			if (child->dev_type == EDGE_DEV ||
+			    child->dev_type == FANOUT_DEV)
+				sas_unregister_ex_tree(child);
+			else
+				sas_unregister_dev(child);
+			break;
+		}
+	}
+	sas_disable_routing(parent, phy->attached_sas_addr);
+	memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE);
+}
+
+static int sas_discover_bfs_by_root_level(struct domain_device *root,
+					  const int level)
+{
+	struct expander_device *ex_root = &root->ex_dev;
+	struct domain_device *child;
+	int res = 0;
+
+	list_for_each_entry(child, &ex_root->children, siblings) {
+		if (child->dev_type == EDGE_DEV ||
+		    child->dev_type == FANOUT_DEV) {
+			struct expander_device *ex = &child->ex_dev;
+
+			if (level > ex->level)
+				res = sas_discover_bfs_by_root_level(child,
+								     level);
+			else if (level == ex->level)
+				res = sas_ex_discover_devices(child, -1);
+		}
+	}
+	return res;
+}
+
+static int sas_discover_bfs_by_root(struct domain_device *dev)
+{
+	int res;
+	int level = dev->ex_dev.level+1;
+
+	res = sas_ex_discover_devices(dev, -1);
+	if (res)
+		goto out;
+	do {
+		res = sas_discover_bfs_by_root_level(dev, level);
+		mb();
+		level += 1;
+	} while (level <= dev->port->disc.max_level);
+out:
+	return res;
+}
+
+static inline int sas_discover_new(struct domain_device *dev, int phy_id)
+{
+	struct ex_phy *ex_phy = &dev->ex_dev.ex_phy[phy_id];
+	struct domain_device *child;
+	int res;
+
+	SAS_DPRINTK("ex %016llx phy%d new device attached\n",
+		    SAS_ADDR(dev->sas_addr), phy_id);
+	res = sas_ex_phy_discover(dev, phy_id);
+	if (res)
+		goto out;
+	res = sas_ex_discover_devices(dev, phy_id);
+	if (res)
+		goto out;
+	list_for_each_entry(child, &dev->ex_dev.children, siblings) {
+		if (SAS_ADDR(child->sas_addr) ==
+		    SAS_ADDR(ex_phy->attached_sas_addr)) {
+			if (child->dev_type == EDGE_DEV ||
+			    child->dev_type == FANOUT_DEV)
+				res = sas_discover_bfs_by_root(child);
+			break;
+		}
+	}
+out:
+	return res;
+}
+
+static int sas_rediscover_dev(struct domain_device *dev, int phy_id)
+{
+	struct expander_device *ex = &dev->ex_dev;
+	struct ex_phy *phy = &ex->ex_phy[phy_id];
+	u8 attached_sas_addr[8];
+	int res;
+
+	res = sas_get_phy_attached_sas_addr(dev, phy_id, attached_sas_addr);
+	switch (res) {
+	case SMP_RESP_NO_PHY:
+		phy->phy_state = PHY_NOT_PRESENT;
+		sas_unregister_devs_sas_addr(dev, phy_id);
+		goto out; break;
+	case SMP_RESP_PHY_VACANT:
+		phy->phy_state = PHY_VACANT;
+		sas_unregister_devs_sas_addr(dev, phy_id);
+		goto out; break;
+	case SMP_RESP_FUNC_ACC:
+		break;
+	}
+
+	if (SAS_ADDR(attached_sas_addr) == 0) {
+		phy->phy_state = PHY_EMPTY;
+		sas_unregister_devs_sas_addr(dev, phy_id);
+	} else if (SAS_ADDR(attached_sas_addr) ==
+		   SAS_ADDR(phy->attached_sas_addr)) {
+		SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter\n",
+			    SAS_ADDR(dev->sas_addr), phy_id);
+	} else
+		res = sas_discover_new(dev, phy_id);
+out:
+	return res;
+}
+
+static int sas_rediscover(struct domain_device *dev, const int phy_id)
+{
+	struct expander_device *ex = &dev->ex_dev;
+	struct ex_phy *changed_phy = &ex->ex_phy[phy_id];
+	int res = 0;
+	int i;
+
+	SAS_DPRINTK("ex %016llx phy%d originated BROADCAST(CHANGE)\n",
+		    SAS_ADDR(dev->sas_addr), phy_id);
+
+	if (SAS_ADDR(changed_phy->attached_sas_addr) != 0) {
+		for (i = 0; i < ex->num_phys; i++) {
+			struct ex_phy *phy = &ex->ex_phy[i];
+
+			if (i == phy_id)
+				continue;
+			if (SAS_ADDR(phy->attached_sas_addr) ==
+			    SAS_ADDR(changed_phy->attached_sas_addr)) {
+				SAS_DPRINTK("phy%d part of wide port with "
+					    "phy%d\n", phy_id, i);
+				goto out;
+			}
+		}
+		res = sas_rediscover_dev(dev, phy_id);
+	} else
+		res = sas_discover_new(dev, phy_id);
+out:
+	return res;
+}
+
+/**
+ * sas_revalidate_domain -- revalidate the domain
+ * @port: port to the domain of interest
+ *
+ * NOTE: this process _must_ quit (return) as soon as any connection
+ * errors are encountered.  Connection recovery is done elsewhere.
+ * Discover process only interrogates devices in order to discover the
+ * domain.
+ */
+int sas_ex_revalidate_domain(struct domain_device *port_dev)
+{
+	int res;
+	struct domain_device *dev = NULL;
+
+	res = sas_find_bcast_dev(port_dev, &dev);
+	if (res)
+		goto out;
+	if (dev) {
+		struct expander_device *ex = &dev->ex_dev;
+		int i = 0, phy_id;
+
+		do {
+			phy_id = -1;
+			res = sas_find_bcast_phy(dev, &phy_id, i);
+			if (phy_id == -1)
+				break;
+			res = sas_rediscover(dev, phy_id);
+			i = phy_id + 1;
+		} while (i < ex->num_phys);
+	}
+out:
+	return res;
+}
+
+/* ---------- SMP portal ---------- */
+
+static ssize_t smp_portal_write(struct kobject *kobj, char *buf, loff_t offs,
+				size_t size)
+{
+	struct domain_device *dev = to_dom_device(kobj);
+	struct expander_device *ex = &dev->ex_dev;
+
+	if (offs != 0)
+		return -EFBIG;
+	else if (size == 0)
+		return 0;
+
+	down_interruptible(&ex->smp_sema);
+	if (ex->smp_req)
+		kfree(ex->smp_req);
+	ex->smp_req = kzalloc(size, GFP_USER);
+	if (!ex->smp_req) {
+		up(&ex->smp_sema);
+		return -ENOMEM;
+	}
+	memcpy(ex->smp_req, buf, size);
+	ex->smp_req_size = size;
+	ex->smp_portal_pid = current->pid;
+	up(&ex->smp_sema);
+
+	return size;
+}
+
+static ssize_t smp_portal_read(struct kobject *kobj, char *buf, loff_t offs,
+			       size_t size)
+{
+	struct domain_device *dev = to_dom_device(kobj);
+	struct expander_device *ex = &dev->ex_dev;
+	u8 *smp_resp;
+	int res = -EINVAL;
+
+	/* XXX: sysfs gives us an offset of 0x10 or 0x8 while in fact
+	 *  it should be 0.
+	 */
+
+	down_interruptible(&ex->smp_sema);
+	if (!ex->smp_req || ex->smp_portal_pid != current->pid)
+		goto out;
+
+	res = 0;
+	if (size == 0)
+		goto out;
+
+	res = -ENOMEM;
+	smp_resp = alloc_smp_resp(size);
+	if (!smp_resp)
+		goto out;
+	res = smp_execute_task(dev, ex->smp_req, ex->smp_req_size,
+			       smp_resp, size);
+	if (!res) {
+		memcpy(buf, smp_resp, size);
+		res = size;
+	}
+
+	kfree(smp_resp);
+out:
+	kfree(ex->smp_req);
+	ex->smp_req = NULL;
+	ex->smp_req_size = 0;
+	ex->smp_portal_pid = -1;
+	up(&ex->smp_sema);
+	return res;
+}
diff -urN oldtree/drivers/scsi/sas/sas_init.c newtree/drivers/scsi/sas/sas_init.c
--- oldtree/drivers/scsi/sas/sas_init.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/sas/sas_init.c	2006-02-13 19:20:00.651413272 -0500
@@ -0,0 +1,230 @@
+/*
+ * Serial Attached SCSI (SAS) Transport Layer initialization
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * $Id: //depot/sas-class/sas_init.c#45 $
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <scsi/scsi_host.h>
+
+#include "sas_internal.h"
+#include <scsi/sas/sas_task.h>
+
+kmem_cache_t *sas_task_cache;
+
+/* ---------- HA events ---------- */
+
+void sas_hae_reset(struct sas_ha_struct *sas_ha)
+{
+	;
+}
+
+/* ---------- HA attributes ---------- */
+
+static ssize_t sas_ha_name_show(struct sas_ha_struct *sas_ha, char *buf)
+{
+	if (sas_ha->sas_ha_name)
+		return sprintf(buf, "%s\n", sas_ha->sas_ha_name);
+	return 0;
+}
+
+static ssize_t sas_ha_addr_show(struct sas_ha_struct *sas_ha, char *buf)
+{
+	return sprintf(buf, "%llx\n", SAS_ADDR(sas_ha->sas_addr));
+}
+
+/* ---------- SAS HA Class ---------- */
+
+#define to_sas_ha(_obj) container_of(to_kset(_obj),struct sas_ha_struct,ha_kset)
+#define to_ha_attr(_attr) container_of(_attr, struct ha_attribute, attr)
+
+struct ha_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct sas_ha_struct *sas_ha, char *);
+	ssize_t (*store)(struct sas_ha_struct *sas_ha,const char *,size_t len);
+};
+
+static ssize_t ha_show_attr(struct kobject *kobj,
+		     struct attribute *attr,
+		     char *page)
+{
+	ssize_t ret = 0;
+	struct sas_ha_struct *sas_ha = to_sas_ha(kobj);
+	struct ha_attribute *ha_attr = to_ha_attr(attr);
+
+	if (ha_attr->show)
+		ret = ha_attr->show(sas_ha, page);
+	return ret;
+}
+
+static struct ha_attribute ha_attrs[] = {
+	__ATTR(ha_name, 0444, sas_ha_name_show, NULL),
+	__ATTR(device_name, 0444, sas_ha_addr_show, NULL),
+	__ATTR_NULL,
+};
+
+static struct attribute *def_attrs[ARRAY_SIZE(ha_attrs)];
+
+static struct sysfs_ops ha_sysfs_ops = {
+	.show = ha_show_attr,
+};
+
+static struct kobj_type ha_ktype = {
+	.sysfs_ops = &ha_sysfs_ops,
+	.default_attrs = def_attrs,
+};
+
+/* This is our "root". */
+static struct kset sas_kset = {
+	.kobj = { .name = "sas" },
+	.ktype = &ha_ktype,	  /* children are of this type */
+};
+
+int sas_register_ha(struct sas_ha_struct *sas_ha,
+		    const struct scsi_host_template *scsi_ht)
+{
+	int i, error = 0;
+
+	spin_lock_init(&sas_ha->phy_port_lock);
+	sas_hash_addr(sas_ha->hashed_sas_addr, sas_ha->sas_addr);
+
+	if (sas_ha->lldd_queue_size == 0)
+		sas_ha->lldd_queue_size = 1;
+	else if (sas_ha->lldd_queue_size == -1)
+		sas_ha->lldd_queue_size = 128; /* Sanity */
+
+	error = sas_register_scsi_host(sas_ha, scsi_ht);
+	if (error) {
+		printk(KERN_NOTICE "couldn't register scsi host\n");
+		return error;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(def_attrs)-1; i++)
+		def_attrs[i] = &ha_attrs[i].attr;
+	def_attrs[i] = NULL;
+
+	/* make sas/ appear */
+	sas_kset.kobj.parent = &sas_ha->core.shost->shost_gendev.kobj;
+	kset_register(&sas_kset);
+
+	/* make sas/ha/ appear */
+	kobject_set_name(&sas_ha->ha_kset.kobj, "%s", "ha");
+	sas_ha->ha_kset.kobj.kset = &sas_kset; /* parent */
+	sas_ha->ha_kset.kobj.ktype = sas_kset.ktype;
+	kset_register(&sas_ha->ha_kset);
+
+	error = sas_register_phys(sas_ha);
+	if (error) {
+		printk(KERN_NOTICE "couldn't register sas phys:%d\n", error);
+		goto Undo;
+	}
+
+	error = sas_register_ports(sas_ha);
+	if (error) {
+		printk(KERN_NOTICE "couldn't register sas ports:%d\n", error);
+		goto Undo_phys;
+	}
+
+	error = sas_start_event_thread(sas_ha);
+	if (error) {
+		printk(KERN_NOTICE "couldn't start event thread:%d\n", error);
+		goto Undo_ports;
+	}
+
+	if (sas_ha->lldd_max_execute_num > 1) {
+		error = sas_init_queue(sas_ha);
+		if (!error)
+			kobject_register(&sas_ha->core.scsi_core_obj);
+		else {
+			printk(KERN_NOTICE "couldn't start queue thread:%d, "
+			       "running in direct mode\n", error);
+			sas_ha->lldd_max_execute_num = 1;
+		}
+	}
+
+	return 0;
+
+Undo_ports:
+	sas_unregister_ports(sas_ha);
+Undo_phys:
+	sas_unregister_phys(sas_ha);
+Undo:
+	kset_unregister(&sas_ha->ha_kset);
+	kset_unregister(&sas_kset);
+	sas_unregister_scsi_host(sas_ha);
+
+	return error;
+}
+
+int sas_unregister_ha(struct sas_ha_struct *sas_ha)
+{
+	sas_unregister_devices(sas_ha);
+
+	if (sas_ha->lldd_max_execute_num > 1) {
+		kobject_unregister(&sas_ha->core.scsi_core_obj);
+		sas_shutdown_queue(sas_ha);
+	}
+
+	sas_kill_event_thread(sas_ha);
+
+	sas_unregister_ports(sas_ha);
+	sas_unregister_phys(sas_ha);
+
+	kset_unregister(&sas_ha->ha_kset);
+	kset_unregister(&sas_kset);
+
+	sas_unregister_scsi_host(sas_ha);
+
+	return 0;
+}
+
+/* ---------- SAS Class register/unregister ---------- */
+
+static int __init sas_class_init(void)
+{
+	sas_task_cache = kmem_cache_create("sas_task", sizeof(struct sas_task),
+					   0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (!sas_task_cache)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void __exit sas_class_exit(void)
+{
+	if (sas_task_cache)
+		kmem_cache_destroy(sas_task_cache);
+}
+
+MODULE_AUTHOR("Luben Tuikov <luben_tuikov@adaptec.com>");
+MODULE_DESCRIPTION("SAS Transport Layer");
+MODULE_LICENSE("GPL v2");
+
+module_init(sas_class_init);
+module_exit(sas_class_exit);
+
+EXPORT_SYMBOL_GPL(sas_register_ha);
+EXPORT_SYMBOL_GPL(sas_unregister_ha);
diff -urN oldtree/drivers/scsi/sas/sas_internal.h newtree/drivers/scsi/sas/sas_internal.h
--- oldtree/drivers/scsi/sas/sas_internal.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/sas/sas_internal.h	2006-02-13 19:20:00.651413272 -0500
@@ -0,0 +1,80 @@
+/*
+ * Serial Attached SCSI (SAS) class internal header file
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * $Id: //depot/sas-class/sas_internal.h#35 $
+ */
+
+#ifndef _SAS_INTERNAL_H_
+#define _SAS_INTERNAL_H_
+
+#include <scsi/sas/sas_class.h>
+
+#define sas_printk(fmt, ...) printk(KERN_NOTICE "sas: " fmt, ## __VA_ARGS__)
+
+#ifdef SAS_DEBUG
+#define SAS_DPRINTK(fmt, ...) printk(KERN_NOTICE "sas: " fmt, ## __VA_ARGS__)
+#else
+#define SAS_DPRINTK(fmt, ...)
+#endif
+
+int sas_show_class(enum sas_class class, char *buf);
+int sas_show_proto(enum sas_proto proto, char *buf);
+int sas_show_linkrate(enum sas_phy_linkrate linkrate, char *buf);
+int sas_show_oob_mode(enum sas_oob_mode oob_mode, char *buf);
+
+int  sas_register_phys(struct sas_ha_struct *sas_ha);
+void sas_unregister_phys(struct sas_ha_struct *sas_ha);
+
+int  sas_register_ports(struct sas_ha_struct *sas_ha);
+void sas_unregister_ports(struct sas_ha_struct *sas_ha);
+
+extern int  sas_register_scsi_host(struct sas_ha_struct *,
+				   const struct scsi_host_template *);
+void sas_unregister_scsi_host(struct sas_ha_struct *sas_ha);
+
+int  sas_start_event_thread(struct sas_ha_struct *sas_ha);
+void sas_kill_event_thread(struct sas_ha_struct *sas_ha);
+
+int  sas_init_queue(struct sas_ha_struct *sas_ha);
+void sas_shutdown_queue(struct sas_ha_struct *sas_ha);
+
+void sas_phye_loss_of_signal(struct sas_phy *phy);
+void sas_phye_oob_done(struct sas_phy *phy);
+void sas_phye_oob_error(struct sas_phy *phy);
+void sas_phye_spinup_hold(struct sas_phy *phy);
+
+void sas_deform_port(struct sas_phy *phy);
+
+void sas_porte_bytes_dmaed(struct sas_phy *phy);
+void sas_porte_broadcast_rcvd(struct sas_phy *phy);
+void sas_porte_link_reset_err(struct sas_phy *phy);
+void sas_porte_timer_event(struct sas_phy *phy);
+void sas_porte_hard_reset(struct sas_phy *phy);
+
+int  sas_reserve_free_id(struct sas_port *port);
+void sas_reserve_scsi_id(struct sas_port *port, int id);
+void sas_release_scsi_id(struct sas_port *port, int id);
+
+void sas_hae_reset(struct sas_ha_struct *sas_ha);
+
+#endif /* _SAS_INTERNAL_H_ */
diff -urN oldtree/drivers/scsi/sas/sas_phy.c newtree/drivers/scsi/sas/sas_phy.c
--- oldtree/drivers/scsi/sas/sas_phy.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/sas/sas_phy.c	2006-02-13 19:20:00.651413272 -0500
@@ -0,0 +1,307 @@
+/*
+ * Serial Attached SCSI (SAS) Phy class
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/sas-class/sas_phy.c#38 $
+ */
+
+#include "sas_internal.h"
+
+/* ---------- Phy events ---------- */
+
+void sas_phye_loss_of_signal(struct sas_phy *phy)
+{
+	phy->error = 0;
+	sas_deform_port(phy);
+}
+
+void sas_phye_oob_done(struct sas_phy *phy)
+{
+	phy->error = 0;
+}
+
+void sas_phye_oob_error(struct sas_phy *phy)
+{
+	struct sas_ha_struct *sas_ha = phy->ha;
+	struct sas_port *port = phy->port;
+
+	sas_deform_port(phy);
+
+	if (!port && phy->enabled && sas_ha->lldd_control_phy) {
+		phy->error++;
+		switch (phy->error) {
+		case 1:
+		case 2:
+			sas_ha->lldd_control_phy(phy, PHY_FUNC_HARD_RESET);
+			break;
+		case 3:
+		default:
+			phy->error = 0;
+			phy->enabled = 0;
+			sas_ha->lldd_control_phy(phy, PHY_FUNC_DISABLE);
+			break;
+		}
+	}
+}
+
+void sas_phye_spinup_hold(struct sas_phy *phy)
+{
+	struct sas_ha_struct *sas_ha = phy->ha;
+
+	phy->error = 0;
+	sas_ha->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD);
+}
+
+/* ---------- Phy attributes ---------- */
+
+static ssize_t sas_phy_id_show(struct sas_phy *phy, char *buf)
+{
+	return sprintf(buf, "%d\n", phy->id);
+}
+
+static ssize_t sas_phy_enabled_show(struct sas_phy *phy, char *buf)
+{
+	return sprintf(buf, "%d\n", phy->enabled);
+}
+
+static ssize_t sas_phy_enabled_store(struct sas_phy *phy, const char *buf,
+				     size_t size)
+{
+	if (size > 0) {
+		if (buf[0] == '1')
+			phy->ha->lldd_control_phy(phy, PHY_FUNC_LINK_RESET);
+	}
+	return size;
+}
+
+static ssize_t sas_phy_class_show(struct sas_phy *phy, char *buf)
+{
+	if (!phy->enabled)
+		return 0;
+	return sas_show_class(phy->class, buf);
+}
+
+static ssize_t sas_phy_iproto_show(struct sas_phy *phy, char *page)
+{
+	if (!phy->enabled)
+		return 0;
+	return sas_show_proto(phy->iproto, page);
+}
+
+static ssize_t sas_phy_tproto_show(struct sas_phy *phy, char *page)
+{
+	if (!phy->enabled)
+		return 0;
+	return sas_show_proto(phy->tproto, page);
+}
+
+static ssize_t sas_phy_type_show(struct sas_phy *phy, char *buf)
+{
+	static const char *phy_type_str[] = {
+		[PHY_TYPE_PHYSICAL] = "physical",
+		[PHY_TYPE_VIRTUAL] = "virtual",
+	};
+	if (!phy->enabled)
+		return 0;
+	return sprintf(buf, "%s\n", phy_type_str[phy->type]);
+}
+
+static ssize_t sas_phy_role_show(struct sas_phy *phy, char *page)
+{
+	static const char *phy_role_str[] = {
+		[PHY_ROLE_NONE] = "none",
+		[PHY_ROLE_TARGET] = "target",
+		[PHY_ROLE_INITIATOR] = "initiator",
+	};
+	int  v;
+	char *buf = page;
+
+	if (!phy->enabled)
+		return 0;
+
+	if (phy->role == PHY_ROLE_NONE)
+		return sprintf(buf, "%s\n", phy_role_str[PHY_ROLE_NONE]);
+
+	for (v = 1; v <= PHY_ROLE_INITIATOR; v <<= 1) {
+		if (v & phy->role) {
+			buf += sprintf(buf, "%s", phy_role_str[v]);
+			if (phy->role & ~((v<<1)-1))
+				buf += sprintf(buf, "|");
+			else
+				buf += sprintf(buf, "\n");
+		}
+	}
+	return buf-page;
+}
+
+static ssize_t sas_phy_linkrate_show(struct sas_phy *phy, char *buf)
+{
+	if (!phy->enabled)
+		return 0;
+	return sas_show_linkrate(phy->linkrate, buf);
+}
+
+static ssize_t sas_phy_addr_show(struct sas_phy *phy, char *buf)
+{
+	if (!phy->enabled)
+		return 0;
+	return sprintf(buf, "%llx\n", SAS_ADDR(phy->sas_addr));
+}
+
+static ssize_t sas_phy_oob_mode_show(struct sas_phy *phy, char *buf)
+{
+	if (!phy->enabled)
+		return 0;
+	return sas_show_oob_mode(phy->oob_mode, buf);
+}
+
+struct phy_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct sas_phy *phy, char *);
+	ssize_t (*store)(struct sas_phy *phy, const char *, size_t);
+};
+
+static struct phy_attribute phy_attrs[] = {
+	/* port is a symlink */
+	__ATTR(id, 0444, sas_phy_id_show, NULL),
+	__ATTR(enabled, 0644, sas_phy_enabled_show, sas_phy_enabled_store),
+	__ATTR(class, 0444, sas_phy_class_show, NULL),
+	__ATTR(iproto, 0444, sas_phy_iproto_show, NULL),
+	__ATTR(tproto, 0444, sas_phy_tproto_show, NULL),
+	__ATTR(type, 0444, sas_phy_type_show, NULL),
+	__ATTR(role, 0444, sas_phy_role_show, NULL),
+	__ATTR(linkrate, 0444, sas_phy_linkrate_show, NULL),
+	__ATTR(sas_addr, 0444, sas_phy_addr_show, NULL),
+	__ATTR(oob_mode, 0444, sas_phy_oob_mode_show, NULL),
+	__ATTR_NULL,
+};
+
+static struct attribute *def_attrs[ARRAY_SIZE(phy_attrs)];
+
+#define to_sas_phy(_obj) container_of(_obj, struct sas_phy, phy_kobj)
+#define to_phy_attr(_attr) container_of(_attr, struct phy_attribute, attr)
+
+static ssize_t phy_show_attr(struct kobject *kobj,
+			     struct attribute *attr,
+			     char *page)
+{
+	ssize_t ret = 0;
+	struct sas_phy *phy = to_sas_phy(kobj);
+	struct phy_attribute *phy_attr = to_phy_attr(attr);
+
+	if (phy_attr->show)
+		ret = phy_attr->show(phy, page);
+	return ret;
+}
+
+static ssize_t phy_store_attr(struct kobject *kobj,
+			      struct attribute *attr,
+			      const char *page, size_t size)
+{
+	ssize_t ret = 0;
+	struct sas_phy *phy = to_sas_phy(kobj);
+	struct phy_attribute *phy_attr = to_phy_attr(attr);
+
+	if (phy_attr->store)
+		ret = phy_attr->store(phy, page, size);
+	return ret;
+}
+
+static struct sysfs_ops phy_sysfs_ops = {
+	.show = phy_show_attr,
+	.store = phy_store_attr,
+};
+
+static struct kobj_type phy_ktype = {
+	.sysfs_ops = &phy_sysfs_ops,
+	.default_attrs = def_attrs,
+};
+
+/* ---------- Phy class registration ---------- */
+
+int sas_register_phys(struct sas_ha_struct *sas_ha)
+{
+	int i, error;
+
+	for (i = 0; i < ARRAY_SIZE(def_attrs)-1; i++)
+		def_attrs[i] = &phy_attrs[i].attr;
+	def_attrs[i] = NULL;
+
+	/* make sas/ha/phys/ appear */
+	kobject_set_name(&sas_ha->phy_kset.kobj, "%s", "phys");
+	sas_ha->phy_kset.kobj.kset = &sas_ha->ha_kset; /* parent */
+	/* we do not inherit the type of the parent */
+	sas_ha->phy_kset.kobj.ktype = NULL;
+	sas_ha->phy_kset.ktype = &phy_ktype;
+	error = kset_register(&sas_ha->phy_kset);
+	if (error)
+		return error;
+
+	/* Now register the phys. */
+	for (i = 0; i < sas_ha->num_phys; i++) {
+		int k;
+		struct sas_phy *phy = sas_ha->sas_phy[i];
+
+		phy->error = 0;
+		INIT_LIST_HEAD(&phy->port_phy_el);
+		INIT_LIST_HEAD(&phy->port_event_list);
+		INIT_LIST_HEAD(&phy->phy_event_list);
+		for (k = 0; k < PORT_NUM_EVENTS; k++) {
+			struct sas_event *ev = &phy->port_events[k];
+			ev->event = k;
+			INIT_LIST_HEAD(&ev->el);
+		}
+		for (k = 0; k < PHY_NUM_EVENTS; k++) {
+			struct sas_event *ev = &phy->phy_events[k];
+			ev->event = k;
+			INIT_LIST_HEAD(&ev->el);
+		}
+		phy->port = NULL;
+		phy->ha = sas_ha;
+		spin_lock_init(&phy->frame_rcvd_lock);
+		spin_lock_init(&phy->sas_prim_lock);
+		phy->frame_rcvd_size = 0;
+
+		kobject_set_name(&phy->phy_kobj, "%d", i);
+		phy->phy_kobj.kset = &sas_ha->phy_kset; /* parent */
+		phy->phy_kobj.ktype = sas_ha->phy_kset.ktype;
+		error = kobject_register(&phy->phy_kobj);
+		if (error)
+			goto unroll;
+	}
+
+	return 0;
+unroll:
+	for (i--; i >= 0; i--)
+		kobject_unregister(&sas_ha->sas_phy[i]->phy_kobj);
+
+	return error;
+}
+
+void sas_unregister_phys(struct sas_ha_struct *sas_ha)
+{
+	int i;
+
+	for (i = 0; i < sas_ha->num_phys; i++)
+		kobject_unregister(&sas_ha->sas_phy[i]->phy_kobj);
+
+	kset_unregister(&sas_ha->phy_kset);
+}
diff -urN oldtree/drivers/scsi/sas/sas_port.c newtree/drivers/scsi/sas/sas_port.c
--- oldtree/drivers/scsi/sas/sas_port.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/sas/sas_port.c	2006-02-13 19:20:00.652413120 -0500
@@ -0,0 +1,429 @@
+/*
+ * Serial Attached SCSI (SAS) Port class
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/sas-class/sas_port.c#44 $
+ */
+
+#include "sas_internal.h"
+#include <scsi/sas/sas_discover.h>
+
+/* called only when num_phys increments, afterwards */
+static void sas_create_port_sysfs_links(struct sas_phy *phy)
+{
+	struct sas_port *port = phy->port;
+
+	if (port->num_phys == 1) {
+		kobject_register(&port->port_kobj);
+		kset_register(&port->phy_kset);
+		kset_register(&port->dev_kset);
+	}
+	/* add port->phy link */
+	sysfs_create_link(&port->phy_kset.kobj, &phy->phy_kobj,
+			  kobject_name(&phy->phy_kobj));
+	/* add phy->port link */
+	sysfs_create_link(&phy->phy_kobj, &port->port_kobj, "port");
+}
+
+/* called only when num_phys decrements, just before it does */
+static void sas_remove_port_sysfs_links(struct sas_phy *phy)
+{
+	struct sas_port *port = phy->port;
+
+	/* remove phy->port link */
+	sysfs_remove_link(&phy->phy_kobj, "port");
+	/* remove port to phy link */
+	sysfs_remove_link(&port->phy_kset.kobj, kobject_name(&phy->phy_kobj));
+
+	if (port->num_phys == 1) {
+		kset_unregister(&port->dev_kset);
+		kset_unregister(&port->phy_kset);
+		kobject_unregister(&port->port_kobj);
+	}
+}
+
+/**
+ * sas_form_port -- add this phy to a port
+ * @phy: the phy of interest
+ *
+ * This function adds this phy to an existing port, thus creating a wide
+ * port, or it creates a port and adds the phy to the port.
+ */
+static void sas_form_port(struct sas_phy *phy)
+{
+	int i;
+	struct sas_ha_struct *sas_ha = phy->ha;
+	struct sas_port *port = phy->port;
+
+	if (port) {
+		if (memcmp(port->attached_sas_addr, phy->attached_sas_addr,
+			   SAS_ADDR_SIZE) == 0)
+			sas_deform_port(phy);
+		else {
+			SAS_DPRINTK("%s: phy%d belongs to port%d already(%d)!\n",
+				    __FUNCTION__, phy->id, phy->port->id,
+				    phy->port->num_phys);
+			return;
+		}
+	}
+
+	/* find a port */
+	spin_lock(&sas_ha->phy_port_lock);
+	for (i = 0; i < sas_ha->num_phys; i++) {
+		port = sas_ha->sas_port[i];
+		spin_lock(&port->phy_list_lock);
+		if (*(u64 *) port->sas_addr &&
+		    memcmp(port->attached_sas_addr,
+			   phy->attached_sas_addr, SAS_ADDR_SIZE) == 0 &&
+		    port->num_phys > 0) {
+			/* wide port */
+			SAS_DPRINTK("phy%d matched wide port%d\n", phy->id,
+				    port->id);
+			break;
+		} else if (*(u64 *) port->sas_addr == 0 && port->num_phys==0) {
+			memcpy(port->sas_addr, phy->sas_addr, SAS_ADDR_SIZE);
+			break;
+		}
+		spin_unlock(&port->phy_list_lock);
+	}
+
+	if (i >= sas_ha->num_phys) {
+		printk(KERN_NOTICE "%s: couldn't find a free port, bug?\n",
+		       __FUNCTION__);
+		spin_unlock(&sas_ha->phy_port_lock);
+		return;
+	}
+
+	/* add the phy to the port */
+	list_add_tail(&phy->port_phy_el, &port->phy_list);
+	phy->port = port;
+	port->num_phys++;
+	port->phy_mask |= (1U << phy->id);
+
+	SAS_DPRINTK("phy%d added to port%d, phy_mask:0x%x\n", phy->id,
+		    port->id, port->phy_mask);
+
+	if (*(u64 *)port->attached_sas_addr == 0) {
+		port->class = phy->class;
+		memcpy(port->attached_sas_addr, phy->attached_sas_addr,
+		       SAS_ADDR_SIZE);
+		port->iproto = phy->iproto;
+		port->tproto = phy->tproto;
+		port->oob_mode = phy->oob_mode;
+		port->linkrate = phy->linkrate;
+	} else
+		port->linkrate = max(port->linkrate, phy->linkrate);
+	spin_unlock(&port->phy_list_lock);
+	spin_unlock(&sas_ha->phy_port_lock);
+
+	if (port->port_dev)
+		port->port_dev->pathways = port->num_phys;
+
+	sas_create_port_sysfs_links(phy);
+	/* Tell the LLDD about this port formation. */
+	if (sas_ha->lldd_port_formed)
+		sas_ha->lldd_port_formed(phy);
+
+	sas_discover_event(phy->port, DISCE_DISCOVER_DOMAIN);
+}
+
+/**
+ * sas_deform_port -- remove this phy from the port it belongs to
+ * @phy: the phy of interest
+ *
+ * This is called when the physical link to the other phy has been
+ * lost (on this phy), in Event thread context. We cannot delay here.
+ */
+void sas_deform_port(struct sas_phy *phy)
+{
+	struct sas_ha_struct *sas_ha = phy->ha;
+	struct sas_port *port = phy->port;
+
+	if (!port)
+		return;		  /* done by a phy event */
+
+	if (port->port_dev)
+		port->port_dev->pathways--;
+
+	if (port->num_phys == 1) {
+		init_completion(&port->port_gone_completion);
+		sas_discover_event(port, DISCE_PORT_GONE);
+		wait_for_completion(&port->port_gone_completion);
+	}
+
+	if (sas_ha->lldd_port_deformed)
+		sas_ha->lldd_port_deformed(phy);
+
+	sas_remove_port_sysfs_links(phy);
+
+	spin_lock(&sas_ha->phy_port_lock);
+	spin_lock(&port->phy_list_lock);
+
+	list_del_init(&phy->port_phy_el);
+	phy->port = NULL;
+	port->num_phys--;
+	port->phy_mask &= ~(1U << phy->id);
+
+	if (port->num_phys == 0) {
+		INIT_LIST_HEAD(&port->phy_list);
+		memset(port->sas_addr, 0, SAS_ADDR_SIZE);
+		memset(port->attached_sas_addr, 0, SAS_ADDR_SIZE);
+		port->class = 0;
+		port->iproto = 0;
+		port->tproto = 0;
+		port->oob_mode = 0;
+		port->phy_mask = 0;
+	}
+	spin_unlock(&port->phy_list_lock);
+	spin_unlock(&sas_ha->phy_port_lock);
+
+	return;
+}
+
+/* ---------- SAS port events ---------- */
+
+void sas_porte_bytes_dmaed(struct sas_phy *phy)
+{
+	sas_form_port(phy);
+}
+
+void sas_porte_broadcast_rcvd(struct sas_phy *phy)
+{
+	unsigned long flags;
+	u32 prim;
+
+	spin_lock_irqsave(&phy->sas_prim_lock, flags);
+	prim = phy->sas_prim;
+	spin_unlock_irqrestore(&phy->sas_prim_lock, flags);
+
+	SAS_DPRINTK("broadcast received: %d\n", prim);
+	sas_discover_event(phy->port, DISCE_REVALIDATE_DOMAIN);
+}
+
+void sas_porte_link_reset_err(struct sas_phy *phy)
+{
+	sas_deform_port(phy);
+}
+
+void sas_porte_timer_event(struct sas_phy *phy)
+{
+	sas_deform_port(phy);
+}
+
+void sas_porte_hard_reset(struct sas_phy *phy)
+{
+	sas_deform_port(phy);
+}
+
+/* ---------- SAS port attributes ---------- */
+
+static ssize_t sas_port_id_show(struct sas_port *port, char *buf)
+{
+	return sprintf(buf, "%d\n", port->id);
+}
+
+static ssize_t sas_port_class_show(struct sas_port *port, char *buf)
+{
+	return sas_show_class(port->class, buf);
+}
+
+static ssize_t sas_port_sas_addr_show(struct sas_port *port, char *buf)
+{
+	return sprintf(buf, "%llx\n", SAS_ADDR(port->sas_addr));
+}
+
+static ssize_t sas_port_attached_sas_addr_show(struct sas_port *port,char *buf)
+{
+	return sprintf(buf, "%llx\n", SAS_ADDR(port->attached_sas_addr));
+}
+
+static ssize_t sas_port_iproto_show(struct sas_port *port, char *buf)
+{
+	return sas_show_proto(port->iproto, buf);
+}
+
+static ssize_t sas_port_tproto_show(struct sas_port *port, char *buf)
+{
+	return sas_show_proto(port->tproto, buf);
+}
+
+static ssize_t sas_port_oob_mode_show(struct sas_port *port, char *buf)
+{
+	return sas_show_oob_mode(port->oob_mode, buf);
+}
+
+struct port_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct sas_port *port, char *);
+	ssize_t (*store)(struct sas_port *port, const char *, size_t);
+};
+
+static struct port_attribute port_attrs[] = {
+	__ATTR(id, 0444, sas_port_id_show, NULL),
+	__ATTR(class, 0444, sas_port_class_show, NULL),
+	__ATTR(port_identifier, 0444, sas_port_sas_addr_show, NULL),
+	__ATTR(attached_port_identifier, 0444, sas_port_attached_sas_addr_show, NULL),
+	__ATTR(iproto, 0444, sas_port_iproto_show, NULL),
+	__ATTR(tproto, 0444, sas_port_tproto_show, NULL),
+	__ATTR(oob_mode, 0444, sas_port_oob_mode_show, NULL),
+	__ATTR_NULL,
+};
+
+static struct attribute *def_attrs[ARRAY_SIZE(port_attrs)];
+
+#define to_sas_port(_obj) container_of(_obj, struct sas_port, port_kobj)
+#define to_port_attr(_attr) container_of(_attr, struct port_attribute, attr)
+
+static ssize_t port_show_attr(struct kobject *kobj, struct attribute *attr,
+			      char *page)
+{
+	ssize_t ret = 0;
+	struct sas_port *port = to_sas_port(kobj);
+	struct port_attribute *port_attr = to_port_attr(attr);
+
+	if (port_attr->show)
+		ret = port_attr->show(port, page);
+	return ret;
+}
+
+static struct sysfs_ops port_sysfs_ops = {
+	.show = port_show_attr,
+};
+
+static struct kobj_type port_type = {
+	.sysfs_ops = &port_sysfs_ops,
+	.default_attrs = def_attrs,
+};
+
+/* ---------- SAS port registration ---------- */
+
+static void sas_init_port(struct sas_port *port,
+			  struct sas_ha_struct *sas_ha, int i,
+			  struct kset *parent_kset)
+{
+	port->id = i;
+	INIT_LIST_HEAD(&port->dev_list);
+	spin_lock_init(&port->phy_list_lock);
+	INIT_LIST_HEAD(&port->phy_list);
+	port->num_phys = 0;
+	port->phy_mask = 0;
+	port->ha = sas_ha;
+
+	memset(&port->port_kobj, 0, sizeof(port->port_kobj));
+	memset(&port->phy_kset, 0, sizeof(port->phy_kset));
+	memset(&port->dev_kset, 0, sizeof(port->dev_kset));
+
+	kobject_set_name(&port->port_kobj, "%d", port->id);
+	port->port_kobj.kset = parent_kset;
+	port->port_kobj.ktype= parent_kset->ktype;
+
+	kobject_set_name(&port->phy_kset.kobj, "%s", "phys");
+	port->phy_kset.kobj.parent = &port->port_kobj;
+	port->phy_kset.ktype = NULL;
+
+	kobject_set_name(&port->dev_kset.kobj, "%s", "domain");
+	port->dev_kset.kobj.parent = &port->port_kobj;
+	port->dev_kset.ktype = NULL;
+
+	port->id_map.max_ids = 128;
+	port->id_map.id_bitmap_size =
+		BITS_TO_LONGS(port->id_map.max_ids)*sizeof(long);
+	port->id_map.id_bitmap = kzalloc(port->id_map.id_bitmap_size,
+					 GFP_KERNEL);
+	spin_lock_init(&port->id_map.id_bitmap_lock);
+}
+
+int sas_register_ports(struct sas_ha_struct *sas_ha)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(def_attrs)-1; i++)
+		def_attrs[i] = &port_attrs[i].attr;
+	def_attrs[i] = NULL;
+
+	/* make sas/ha/ports/ appear */
+	kobject_set_name(&sas_ha->port_kset.kobj, "%s", "ports");
+	sas_ha->port_kset.kobj.kset = &sas_ha->ha_kset; /* parent */
+	/* no type inheritance */
+	sas_ha->port_kset.kobj.ktype = NULL;
+	sas_ha->port_kset.ktype = &port_type; /* children are of this type */
+
+	/* initialize the ports and discovery */
+	for (i = 0; i < sas_ha->num_phys; i++) {
+		struct sas_port *port = sas_ha->sas_port[i];
+
+		sas_init_port(port, sas_ha, i, &sas_ha->port_kset);
+		sas_init_disc(&port->disc, port);
+	}
+
+	return kset_register(&sas_ha->port_kset);
+}
+
+void sas_unregister_ports(struct sas_ha_struct *sas_ha)
+{
+	int i;
+
+	for (i = 0; i < sas_ha->num_phys; i++)
+		if (sas_ha->sas_phy[i]->port)
+			sas_deform_port(sas_ha->sas_phy[i]);
+
+	for (i = 0; i < sas_ha->num_phys; i++) {
+		kfree(sas_ha->sas_port[i]->id_map.id_bitmap);
+		sas_ha->sas_port[i]->id_map.id_bitmap = NULL;
+	}
+
+	kset_unregister(&sas_ha->port_kset);
+}
+
+int sas_reserve_free_id(struct sas_port *port)
+{
+	int id;
+
+	spin_lock(&port->id_map.id_bitmap_lock);
+	id = find_first_zero_bit(port->id_map.id_bitmap, port->id_map.max_ids);
+	if (id >= port->id_map.max_ids) {
+		id = -ENOMEM;
+		spin_unlock(&port->id_map.id_bitmap_lock);
+		goto out;
+	}
+	set_bit(id, port->id_map.id_bitmap);
+	spin_unlock(&port->id_map.id_bitmap_lock);
+out:
+	return id;
+}
+
+void sas_reserve_scsi_id(struct sas_port *port, int id)
+{
+	if (0 > id || id >= port->id_map.max_ids)
+		return;
+	spin_lock(&port->id_map.id_bitmap_lock);
+	set_bit(id, port->id_map.id_bitmap);
+	spin_unlock(&port->id_map.id_bitmap_lock);
+}
+
+void sas_release_scsi_id(struct sas_port *port, int id)
+{
+	if (0 > id || id >= port->id_map.max_ids)
+		return;
+	spin_lock(&port->id_map.id_bitmap_lock);
+	clear_bit(id, port->id_map.id_bitmap);
+	spin_unlock(&port->id_map.id_bitmap_lock);
+}
diff -urN oldtree/drivers/scsi/sas/sas_scsi_host.c newtree/drivers/scsi/sas/sas_scsi_host.c
--- oldtree/drivers/scsi/sas/sas_scsi_host.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/sas/sas_scsi_host.c	2006-02-13 19:20:00.653412968 -0500
@@ -0,0 +1,975 @@
+/*
+ * Serial Attached SCSI (SAS) class SCSI Host glue.
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * $Id: //depot/sas-class/sas_scsi_host.c#60 $
+ */
+
+#include "sas_internal.h"
+#include <scsi/sas/sas_discover.h>
+#include <scsi/sas/sas_task.h>
+
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi.h>
+
+#include <linux/err.h>
+#include <linux/blkdev.h>
+#include <linux/kobject.h>
+#include <linux/scatterlist.h>
+
+/* The SAM LUN structure should be _completely_ opaque to SCSI Core.
+ * This is why this macro here, and not using the broken
+ * scsilun_to_int().  Ideally, a SCSI LUN should be communicated in
+ * its entirety, and not as an integer.  For some unknown to myself
+ * reason, SCSI Core thinks that SCSI LUNs can be interpreted as
+ * integers.
+ */
+#define SCSI_LUN(_sam_lun)   ((unsigned int)be32_to_cpu(*(__be32 *)_sam_lun))
+
+/* ---------- SCSI Core device registration ---------- */
+
+int  sas_register_with_scsi(struct LU *lu)
+{
+	struct domain_device *dev = lu->parent;
+	lu->map.channel = dev->port->id;
+	lu->map.id      = sas_reserve_free_id(dev->port);
+	if (lu->map.id == -ENOMEM)
+		return -ENOMEM;
+
+	scsi_scan_target(&dev->port->ha->core.shost->shost_gendev,
+		         lu->map.channel, lu->map.id, ~0, 0);
+	return 0;
+}
+
+void sas_unregister_with_scsi(struct LU *lu)
+{
+	if (lu->uldd_dev) {
+		struct scsi_device *scsi_dev = lu->uldd_dev;
+		scsi_remove_device(scsi_dev);
+	}
+}
+
+/* ---------- SCSI Host glue ---------- */
+
+#define TO_SAS_TASK(_scsi_cmd)  ((void *)(_scsi_cmd)->host_scribble)
+#define ASSIGN_SAS_TASK(_sc, _t) do { (_sc)->host_scribble = (void *) _t; } while (0)
+
+static void sas_scsi_task_done(struct sas_task *task)
+{
+	struct task_status_struct *ts = &task->task_status;
+	struct scsi_cmnd *sc = task->uldd_task;
+	unsigned ts_flags = task->task_state_flags;
+	int hs = 0, stat = 0;
+
+	if (unlikely(!sc)) {
+		SAS_DPRINTK("task_done called with non existing SCSI cmnd!\n");
+		list_del_init(&task->list);
+		sas_free_task(task);
+		return;
+	}
+
+	if (ts->resp == SAS_TASK_UNDELIVERED) {
+		/* transport error */
+		hs = DID_NO_CONNECT;
+	} else { /* ts->resp == SAS_TASK_COMPLETE */
+		/* task delivered, what happened afterwards? */
+		switch (ts->stat) {
+		case SAS_DEV_NO_RESPONSE:
+		case SAS_INTERRUPTED:
+		case SAS_PHY_DOWN:
+		case SAS_NAK_R_ERR:
+		case SAS_OPEN_TO:
+			hs = DID_NO_CONNECT;
+			break;
+		case SAS_DATA_UNDERRUN:
+			sc->resid = ts->residual;
+			if (sc->request_bufflen - sc->resid < sc->underflow)
+				hs = DID_ERROR;
+			break;
+		case SAS_DATA_OVERRUN:
+			hs = DID_ERROR;
+			break;
+		case SAS_QUEUE_FULL:
+			hs = DID_SOFT_ERROR; /* retry */
+			break;
+		case SAS_DEVICE_UNKNOWN:
+			hs = DID_BAD_TARGET;
+			break;
+		case SAS_SG_ERR:
+			hs = DID_PARITY;
+			break;
+		case SAS_OPEN_REJECT:
+			if (ts->open_rej_reason == SAS_OREJ_RSVD_RETRY)
+				hs = DID_SOFT_ERROR; /* retry */
+			else
+				hs = DID_ERROR;
+			break;
+		case SAS_PROTO_RESPONSE:
+			SAS_DPRINTK("LLDD:%s sent SAS_PROTO_RESP for an SSP "
+				    "task; please report this\n",
+				    task->dev->port->ha->sas_ha_name);
+			break;
+		case SAS_ABORTED_TASK:
+			hs = DID_ABORT;
+			break;
+		case SAM_CHECK_COND:
+			memcpy(sc->sense_buffer, ts->buf,
+			       max(SCSI_SENSE_BUFFERSIZE, ts->buf_valid_size));
+			stat = SAM_CHECK_COND;
+			break;
+		default:
+			stat = ts->stat;
+			break;
+		}
+	}
+	ASSIGN_SAS_TASK(sc, NULL);
+	sc->result = (hs << 16) | stat;
+	list_del_init(&task->list);
+	sas_free_task(task);
+	/* This is very ugly but this is how SCSI Core works. */
+	if (ts_flags & SAS_TASK_STATE_ABORTED)
+		scsi_finish_command(sc);
+	else
+		sc->scsi_done(sc);
+}
+
+static inline enum task_attribute sas_scsi_get_task_attr(struct scsi_cmnd *cmd)
+{
+	enum task_attribute ta = TASK_ATTR_SIMPLE;
+	if (cmd->request && blk_rq_tagged(cmd->request)) {
+		if (cmd->device->ordered_tags &&
+		    (cmd->request->flags & REQ_HARDBARRIER))
+			ta = TASK_ATTR_HOQ;
+	}
+	return ta;
+}
+
+static inline struct sas_task *sas_create_task(struct scsi_cmnd *cmd,
+					       struct LU *lu,
+					       unsigned long gfp_flags)
+{
+	struct sas_task *task = sas_alloc_task(gfp_flags);
+
+	if (!task)
+		return NULL;
+
+	*(u32 *)cmd->sense_buffer = 0;
+	task->uldd_task = cmd;
+	ASSIGN_SAS_TASK(cmd, task);
+
+	task->dev = lu->parent;
+	task->task_proto = task->dev->tproto; /* BUG_ON(!SSP) */
+
+	task->ssp_task.retry_count = 1;
+	memcpy(task->ssp_task.LUN, lu->LUN, 8);
+	task->ssp_task.task_attr = sas_scsi_get_task_attr(cmd);
+	memcpy(task->ssp_task.cdb, cmd->cmnd, 16);
+
+	task->scatter = cmd->request_buffer;
+	task->num_scatter = cmd->use_sg;
+	task->total_xfer_len = cmd->request_bufflen;
+	task->data_dir = cmd->sc_data_direction;
+
+	task->task_done = sas_scsi_task_done;
+
+	return task;
+}
+
+static inline int sas_queue_up(struct sas_task *task)
+{
+	struct sas_ha_struct *sas_ha = task->dev->port->ha;
+	struct scsi_core *core = &sas_ha->core;
+	unsigned long flags;
+	LIST_HEAD(list);
+
+	spin_lock_irqsave(&core->task_queue_lock, flags);
+	if (sas_ha->lldd_queue_size < core->task_queue_size + 1) {
+		spin_unlock_irqrestore(&core->task_queue_lock, flags);
+		return -SAS_QUEUE_FULL;
+	}
+	list_add_tail(&task->list, &core->task_queue);
+	core->task_queue_size += 1;
+	spin_unlock_irqrestore(&core->task_queue_lock, flags);
+	up(&core->queue_thread_sema);
+
+	return 0;
+}
+
+/**
+ * sas_queuecommand -- Enqueue a command for processing
+ * @parameters: See SCSI Core documentation
+ *
+ * Note: XXX: Remove the host unlock/lock pair when SCSI Core can
+ * call us without holding an IRQ spinlock...
+ */
+int sas_queuecommand(struct scsi_cmnd *cmd,
+		     void (*scsi_done)(struct scsi_cmnd *))
+{
+	int res = 0;
+	struct LU *lu = cmd->device->hostdata;
+	struct Scsi_Host *host = cmd->device->host;
+
+	spin_unlock_irq(host->host_lock);
+	if (!lu) {
+		SAS_DPRINTK("scsi cmd 0x%p sent to non existing LU\n",
+			    cmd);
+		cmd->result = DID_BAD_TARGET << 16;
+		scsi_done(cmd);
+		goto out;
+	} else {
+		struct sas_ha_struct *sas_ha = lu->parent->port->ha;
+		struct sas_task *task;
+
+		res = -ENOMEM;
+		task = sas_create_task(cmd, lu, GFP_ATOMIC);
+		if (!task)
+			goto out;
+
+		cmd->scsi_done = scsi_done;
+		/* Queue up, Direct Mode or Task Collector Mode. */
+		if (sas_ha->lldd_max_execute_num < 2)
+			res = sas_ha->lldd_execute_task(task, 1, GFP_ATOMIC);
+		else
+			res = sas_queue_up(task);
+
+		/* Examine */
+		if (res) {
+			SAS_DPRINTK("lldd_execute_task returned: %d\n", res);
+			ASSIGN_SAS_TASK(cmd, NULL);
+			sas_free_task(task);
+			if (res == -SAS_QUEUE_FULL) {
+				cmd->result = DID_SOFT_ERROR << 16; /* retry */
+				res = 0;
+				scsi_done(cmd);
+			}
+			goto out;
+		}
+	}
+out:
+	spin_lock_irq(host->host_lock);
+	return res;
+}
+
+static void sas_scsi_clear_queue_lu(struct list_head *error_q, struct LU *lu)
+{
+	struct scsi_cmnd *cmd, *n;
+
+	list_for_each_entry_safe(cmd, n, error_q, eh_entry) {
+		struct LU *x = cmd->device->hostdata;
+
+		if (x == lu)
+			list_del_init(&cmd->eh_entry);
+	}
+}
+
+static void sas_scsi_clear_queue_I_T(struct list_head *error_q,
+				     struct domain_device *dev)
+{
+	struct scsi_cmnd *cmd, *n;
+
+	list_for_each_entry_safe(cmd, n, error_q, eh_entry) {
+		struct LU *y = cmd->device->hostdata;
+		struct domain_device *x = y->parent;
+
+		if (x == dev)
+			list_del_init(&cmd->eh_entry);
+	}
+}
+
+static void sas_scsi_clear_queue_port(struct list_head *error_q,
+				      struct sas_port *port)
+{
+	struct scsi_cmnd *cmd, *n;
+
+	list_for_each_entry_safe(cmd, n, error_q, eh_entry) {
+		struct LU *y = cmd->device->hostdata;
+		struct sas_port *x = y->parent->port;
+
+		if (x == port)
+			list_del_init(&cmd->eh_entry);
+	}
+}
+
+enum task_disposition {
+	TASK_IS_DONE,
+	TASK_IS_ABORTED,
+	TASK_IS_AT_LU,
+	TASK_IS_NOT_AT_LU,
+};
+
+static enum task_disposition sas_scsi_find_task(struct sas_task *task)
+{
+	struct sas_ha_struct *ha = task->dev->port->ha;
+	unsigned long flags;
+	int i, res;
+
+	if (ha->lldd_max_execute_num > 1) {
+		struct scsi_core *core = &ha->core;
+		struct sas_task *t, *n;
+
+		spin_lock_irqsave(&core->task_queue_lock, flags);
+		list_for_each_entry_safe(t, n, &core->task_queue, list) {
+			if (task == t) {
+				list_del_init(&t->list);
+				spin_unlock_irqrestore(&core->task_queue_lock,
+						       flags);
+				SAS_DPRINTK("%s: task 0x%p aborted from "
+					    "task_queue\n",
+					    __FUNCTION__, task);
+				return TASK_IS_ABORTED;
+			}
+		}
+		spin_unlock_irqrestore(&core->task_queue_lock, flags);
+	}
+
+	for (i = 0; i < 5; i++) {
+		SAS_DPRINTK("%s: aborting task 0x%p\n", __FUNCTION__, task);
+		res = task->dev->port->ha->lldd_abort_task(task);
+
+		spin_lock_irqsave(&task->task_state_lock, flags);
+		if (task->task_state_flags & SAS_TASK_STATE_DONE) {
+			spin_unlock_irqrestore(&task->task_state_lock, flags);
+			SAS_DPRINTK("%s: task 0x%p is done\n", __FUNCTION__,
+				    task);
+			return TASK_IS_DONE;
+		}
+		spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+		if (res == TMF_RESP_FUNC_COMPLETE) {
+			SAS_DPRINTK("%s: task 0x%p is aborted\n",
+				    __FUNCTION__, task);
+			return TASK_IS_ABORTED;
+		} else if (ha->lldd_query_task) {
+			SAS_DPRINTK("%s: querying task 0x%p\n",
+				    __FUNCTION__, task);
+			res = ha->lldd_query_task(task);
+			if (res == TMF_RESP_FUNC_SUCC) {
+				SAS_DPRINTK("%s: task 0x%p at LU\n",
+					    __FUNCTION__, task);
+				return TASK_IS_AT_LU;
+			} else if (res == TMF_RESP_FUNC_COMPLETE) {
+				SAS_DPRINTK("%s: task 0x%p not at LU\n",
+					    __FUNCTION__, task);
+				return TASK_IS_NOT_AT_LU;
+			}
+		}
+	}
+	return res;
+}
+
+static int sas_recover_lu(struct domain_device *dev, struct LU *lu)
+{
+	struct sas_ha_struct *ha = dev->port->ha;
+	int res = TMF_RESP_FUNC_FAILED;
+
+	SAS_DPRINTK("eh: device %llx LUN %llx has the task\n",
+		    SAS_ADDR(dev->sas_addr),
+		    SAS_ADDR(lu->LUN));
+
+	if (ha->lldd_abort_task_set)
+		res = ha->lldd_abort_task_set(dev, lu->LUN);
+
+	if (res == TMF_RESP_FUNC_FAILED) {
+		if (ha->lldd_clear_task_set)
+			res = ha->lldd_clear_task_set(dev, lu->LUN);
+	}
+
+	if (res == TMF_RESP_FUNC_FAILED) {
+		if (ha->lldd_lu_reset)
+			res = ha->lldd_lu_reset(dev, lu->LUN);
+	}
+
+	return res;
+}
+
+static int sas_recover_I_T(struct domain_device *dev)
+{
+	struct sas_ha_struct *ha = dev->port->ha;
+	int res = TMF_RESP_FUNC_FAILED;
+
+	SAS_DPRINTK("I_T nexus reset for dev %016llx\n",
+		    SAS_ADDR(dev->sas_addr));
+
+	if (ha->lldd_I_T_nexus_reset)
+		res = ha->lldd_I_T_nexus_reset(dev);
+
+	return res;
+}
+
+int sas_scsi_recover_host(struct Scsi_Host *shost)
+{
+	struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
+	unsigned long flags;
+	LIST_HEAD(error_q);
+	struct scsi_cmnd *cmd, *n;
+	enum task_disposition res = TASK_IS_DONE;
+	int tmf_resp;
+
+	spin_lock_irqsave(shost->host_lock, flags);
+	list_splice_init(&shost->eh_cmd_q, &error_q);
+	spin_unlock_irqrestore(shost->host_lock, flags);
+
+	SAS_DPRINTK("Enter %s\n", __FUNCTION__);
+
+	/* All tasks on this list were marked SAS_TASK_STATE_ABORTED
+	 * by sas_scsi_timed_out() callback.
+	 */
+Again:
+	SAS_DPRINTK("going over list...\n");
+	list_for_each_entry_safe(cmd, n, &error_q, eh_entry) {
+		struct sas_task *task = TO_SAS_TASK(cmd);
+		struct LU *lu = cmd->device->hostdata;
+
+		SAS_DPRINTK("trying to find task 0x%p\n", task);
+		list_del_init(&cmd->eh_entry);
+		res = sas_scsi_find_task(task);
+
+		cmd->eh_eflags = 0;
+		shost->host_failed--;
+
+		switch (res) {
+		case TASK_IS_DONE:
+			SAS_DPRINTK("%s: task 0x%p is done\n", __FUNCTION__,
+				    task);
+			task->task_done(task);
+			continue;
+		case TASK_IS_ABORTED:
+			SAS_DPRINTK("%s: task 0x%p is aborted\n",
+				    __FUNCTION__, task);
+			task->task_done(task);
+			continue;
+		case TASK_IS_AT_LU:
+			SAS_DPRINTK("task 0x%p is at LU: lu recover\n", task);
+			tmf_resp = sas_recover_lu(task->dev, lu);
+			if (tmf_resp == TMF_RESP_FUNC_COMPLETE) {
+				SAS_DPRINTK("dev %016llx LU %016llx is "
+					    "recovered\n",
+					    SAS_ADDR(task->dev),
+					    SAS_ADDR(lu->LUN));
+				task->task_done(task);
+				sas_scsi_clear_queue_lu(&error_q, lu);
+				goto Again;
+			}
+			/* fallthrough */
+		case TASK_IS_NOT_AT_LU:
+			SAS_DPRINTK("task 0x%p is not at LU: I_T recover\n",
+				    task);
+			tmf_resp = sas_recover_I_T(task->dev);
+			if (tmf_resp == TMF_RESP_FUNC_COMPLETE) {
+				SAS_DPRINTK("I_T %016llx recovered\n",
+					    SAS_ADDR(task->dev->sas_addr));
+				task->task_done(task);
+				sas_scsi_clear_queue_I_T(&error_q, task->dev);
+				goto Again;
+			}
+			/* Hammer time :-) */
+			if (ha->lldd_clear_nexus_port) {
+				struct sas_port *port = task->dev->port;
+				SAS_DPRINTK("clearing nexus for port:%d\n",
+					    port->id);
+				res = ha->lldd_clear_nexus_port(port);
+				if (res == TMF_RESP_FUNC_COMPLETE) {
+					SAS_DPRINTK("clear nexus port:%d "
+						    "succeeded\n", port->id);
+					task->task_done(task);
+					sas_scsi_clear_queue_port(&error_q,
+								  port);
+					goto Again;
+				}
+			}
+			if (ha->lldd_clear_nexus_ha) {
+				SAS_DPRINTK("clear nexus ha\n");
+				res = ha->lldd_clear_nexus_ha(ha);
+				if (res == TMF_RESP_FUNC_COMPLETE) {
+					SAS_DPRINTK("clear nexus ha "
+						    "succeeded\n");
+					task->task_done(task);
+					goto out;
+				}
+			}
+			/* If we are here -- this means that no amount
+			 * of effort could recover from errors.  Quite
+			 * possibly the HA just disappeared.
+			 */
+			SAS_DPRINTK("error from  device %llx, LUN %llx "
+				    "couldn't be recovered in any way\n",
+				    SAS_ADDR(task->dev->sas_addr),
+				    SAS_ADDR(lu->LUN));
+
+			task->task_done(task);
+			goto clear_q;
+		}
+	}
+out:
+	SAS_DPRINTK("--- Exit %s\n", __FUNCTION__);
+	return 0;
+clear_q:
+	SAS_DPRINTK("--- Exit %s -- clear_q\n", __FUNCTION__);
+	list_for_each_entry_safe(cmd, n, &error_q, eh_entry) {
+		struct sas_task *task = TO_SAS_TASK(cmd);
+		list_del_init(&cmd->eh_entry);
+		task->task_done(task);
+	}
+	return 0;
+}
+
+enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd)
+{
+	struct sas_task *task = TO_SAS_TASK(cmd);
+	unsigned long flags;
+
+	if (!task) {
+		SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_HANDLED\n",
+			    cmd, task);
+		return EH_HANDLED;
+	}
+
+	spin_lock_irqsave(&task->task_state_lock, flags);
+	if (task->task_state_flags & SAS_TASK_STATE_DONE) {
+		spin_unlock_irqrestore(&task->task_state_lock, flags);
+		SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_HANDLED\n",
+			    cmd, task);
+		return EH_HANDLED;
+	}
+	task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+	spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+	SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_NOT_HANDLED\n",
+		    cmd, task);
+
+	return EH_NOT_HANDLED;
+}
+
+/**
+ * sas_slave_alloc -- configure an LU which SCSI Core wants to poke at
+ * @scsi_dev: pointer to scsi device
+ *
+ * The kludge here is that the only token we have to go by in order to
+ * identify which device SCSI Core has just found about, is channel,
+ * id and lun/2.  Of course this is 1) incredibly broken and 2)
+ * leftover from when SCSI Core was SPI-centric.  A solution would be
+ * to pass an opaque token to scsi_add_device, which SCSI Core treats
+ * as that, an opaque token, which it sets inside scsi_dev, so we can
+ * find out which device SCSI Core is talking about.  That is, how
+ * SCSI Core is _addressing_ the device is not the business of LLDD
+ * and vice versa.  An even _better_ solution is if SCSI Core knew
+ * about a "SCSI device with Target ports" so we can register only the
+ * targets, and then it would do its own LU discovery...  See comment
+ * in sas_do_lu_discovery().
+ */
+int sas_slave_alloc(struct scsi_device *scsi_dev)
+{
+	struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(scsi_dev->host);
+	struct sas_port *port = sas_ha->sas_port[scsi_dev->channel];
+        unsigned id = scsi_dev->id;
+	unsigned lun = scsi_dev->lun;
+
+	struct domain_device *dev = NULL;
+	struct LU *lu = NULL;
+
+	scsi_dev->hostdata = NULL;
+
+	list_for_each_entry(dev, &port->dev_list, dev_list_node) {
+		if (dev->dev_type == SAS_END_DEV) {
+			list_for_each_entry(lu, &dev->end_dev.LU_list, list) {
+				if (lu->map.id == id &&
+				    SCSI_LUN(lu->LUN) == lun) {
+					scsi_dev->hostdata = lu;
+					lu->uldd_dev = scsi_dev;
+					goto out_loop;
+				}
+			}
+		}
+	}
+out_loop:
+	if (!scsi_dev->hostdata) {
+		SAS_DPRINTK("sas device not found! How is this possible?\n");
+		return -ENODEV;
+	}
+	kobject_get(&lu->lu_obj);
+	return 0;
+}
+
+#define SAS_DEF_QD 32
+#define SAS_MAX_QD 64
+
+int sas_slave_configure(struct scsi_device *scsi_dev)
+{
+	struct LU *lu = scsi_dev->hostdata;
+	struct domain_device *dev;
+	struct sas_ha_struct *sas_ha;
+
+	if (!lu) {
+		SAS_DPRINTK("slave configure and no LU?!\n");
+		return -ENODEV;
+	}
+
+	dev = lu->parent;
+	sas_ha = dev->port->ha;
+
+	if (scsi_dev->inquiry_len > 7) {
+		u8 bq = (scsi_dev->inquiry[6] & 0x80) ? 1 : 0;
+		u8 cq = (scsi_dev->inquiry[7] & 0x02) ? 1 : 0;
+
+		if (bq ^ cq) {
+			lu->tm_type = (bq<<1) | cq;
+			scsi_dev->tagged_supported = 1;
+			if (cq)
+				scsi_set_tag_type(scsi_dev, MSG_ORDERED_TAG);
+			else
+				scsi_set_tag_type(scsi_dev, MSG_SIMPLE_TAG);
+			scsi_activate_tcq(scsi_dev, SAS_DEF_QD);
+		} else {
+			SAS_DPRINTK("device %llx, LUN %llx doesn't support "
+				    "TCQ\n", SAS_ADDR(dev->sas_addr),
+				    SAS_ADDR(lu->LUN));
+			scsi_dev->tagged_supported = 0;
+			scsi_set_tag_type(scsi_dev, 0);
+			scsi_deactivate_tcq(scsi_dev, 1);
+		}
+	}
+
+	if (dev->end_dev.itnl_timeout > 0)
+		scsi_dev->timeout = HZ +
+			msecs_to_jiffies(dev->end_dev.itnl_timeout);
+
+	return 0;
+}
+
+void sas_slave_destroy(struct scsi_device *scsi_dev)
+{
+	struct LU *lu = scsi_dev->hostdata;
+
+	if (lu) {
+		scsi_dev->hostdata = NULL;
+		lu->uldd_dev = NULL;
+		kobject_put(&lu->lu_obj);
+	}
+}
+
+int sas_change_queue_depth(struct scsi_device *scsi_dev, int new_depth)
+{
+	int res = min(new_depth, SAS_MAX_QD);
+
+	if (scsi_dev->tagged_supported)
+		scsi_adjust_queue_depth(scsi_dev, scsi_get_tag_type(scsi_dev),
+					res);
+	else {
+		struct LU *lu = scsi_dev->hostdata;
+		sas_printk("device %llx LUN %llx queue depth changed to 1\n",
+			   SAS_ADDR(lu->parent->sas_addr),
+			   SAS_ADDR(lu->LUN));
+		scsi_adjust_queue_depth(scsi_dev, 0, 1);
+		res = 1;
+	}
+
+	return res;
+}
+
+int sas_change_queue_type(struct scsi_device *scsi_dev, int qt)
+{
+	struct LU *lu = scsi_dev->hostdata;
+
+	if (!scsi_dev->tagged_supported)
+		return 0;
+
+	scsi_deactivate_tcq(scsi_dev, 1);
+
+	switch (qt) {
+	case MSG_ORDERED_TAG:
+		if (lu->tm_type != TASK_MANAGEMENT_FULL)
+			qt = MSG_SIMPLE_TAG;
+		break;
+	case MSG_SIMPLE_TAG:
+	default:
+		;
+	}
+
+	scsi_set_tag_type(scsi_dev, qt);
+	scsi_activate_tcq(scsi_dev, scsi_dev->queue_depth);
+
+	return qt;
+}
+
+int sas_bios_param(struct scsi_device *scsi_dev,
+			  struct block_device *bdev,
+			  sector_t capacity, int *hsc)
+{
+	hsc[0] = 255;
+	hsc[1] = 63;
+	sector_div(capacity, 255*63);
+	hsc[2] = capacity;
+
+	return 0;
+}
+
+static inline void sas_init_host_template(struct sas_ha_struct *sas_ha,
+			      const struct scsi_host_template *scsi_ht)
+{
+	struct scsi_host_template *sht = sas_ha->core.sht;
+
+	memcpy(sht, scsi_ht, sizeof(struct scsi_host_template));
+
+	sht->name = sas_ha->sas_ha_name;
+	sht->can_queue = sas_ha->lldd_queue_size;
+	sht->cmd_per_lun = sht->can_queue;
+}
+
+int sas_register_scsi_host(struct sas_ha_struct *sas_ha,
+			   const struct scsi_host_template *scsi_ht)
+{
+	int err = -ENOMEM;
+
+	sas_ha->core.sht = kzalloc(sizeof(*sas_ha->core.sht), GFP_KERNEL);
+	if (!sas_ha->core.sht)
+		return -ENOMEM;
+
+	sas_init_host_template(sas_ha, scsi_ht);
+
+	sas_ha->core.shost = scsi_host_alloc(sas_ha->core.sht, sizeof(void *));
+	if (!sas_ha->core.shost) {
+		printk(KERN_NOTICE "couldn't allocate scsi host\n");
+		goto out_err;
+	}
+	SHOST_TO_SAS_HA(sas_ha->core.shost) = sas_ha;
+
+	/* XXX: SCSI Core should really fix this (max vs. num of) */
+	sas_ha->core.shost->max_channel = sas_ha->num_phys - 1;
+	sas_ha->core.shost->max_id = ~0 - 1;
+	sas_ha->core.shost->max_lun = ~0 - 1;
+
+	sas_ha->core.shost->max_cmd_len = 16;
+
+	err = scsi_add_host(sas_ha->core.shost, &sas_ha->pcidev->dev);
+	if (err) {
+		scsi_host_put(sas_ha->core.shost);
+		sas_ha->core.shost = NULL;
+		goto out_err;
+	}
+	return 0;
+
+out_err:
+	kfree(sas_ha->core.sht);
+	sas_ha->core.sht = NULL;
+	return err;
+}
+
+void sas_unregister_scsi_host(struct sas_ha_struct *sas_ha)
+{
+	scsi_remove_host(sas_ha->core.shost);
+	scsi_host_put(sas_ha->core.shost);
+	sas_ha->core.shost = NULL;
+	kfree(sas_ha->core.sht);
+	sas_ha->core.sht = NULL;
+}
+
+/* ---------- Task Collector Thread implementation ---------- */
+
+static void sas_queue(struct sas_ha_struct *sas_ha)
+{
+	struct scsi_core *core = &sas_ha->core;
+	unsigned long flags;
+	LIST_HEAD(q);
+	int can_queue;
+	int res;
+
+	spin_lock_irqsave(&core->task_queue_lock, flags);
+	while (!core->queue_thread_kill &&
+	       !list_empty(&core->task_queue)) {
+
+		can_queue = sas_ha->lldd_queue_size - core->task_queue_size;
+		if (can_queue >= 0) {
+			can_queue = core->task_queue_size;
+			list_splice_init(&core->task_queue, &q);
+		} else {
+			struct list_head *a, *n;
+
+			can_queue = sas_ha->lldd_queue_size;
+			list_for_each_safe(a, n, &core->task_queue) {
+				list_move_tail(a, &q);
+				if (--can_queue == 0)
+					break;
+			}
+			can_queue = sas_ha->lldd_queue_size;
+		}
+		core->task_queue_size -= can_queue;
+		spin_unlock_irqrestore(&core->task_queue_lock, flags);
+		{
+			struct sas_task *task = list_entry(q.next,
+							   struct sas_task,
+							   list);
+			list_del_init(&q);
+			res = sas_ha->lldd_execute_task(task, can_queue,
+							GFP_KERNEL);
+			if (unlikely(res))
+				__list_add(&q, task->list.prev, &task->list);
+		}
+		spin_lock_irqsave(&core->task_queue_lock, flags);
+		if (res) {
+			list_splice_init(&q, &core->task_queue); /*at head*/
+			core->task_queue_size += can_queue;
+		}
+	}
+	spin_unlock_irqrestore(&core->task_queue_lock, flags);
+}
+
+static DECLARE_COMPLETION(queue_th_comp);
+
+/**
+ * sas_queue_thread -- The Task Collector thread
+ * @_sas_ha: pointer to struct sas_ha
+ */
+static int sas_queue_thread(void *_sas_ha)
+{
+	struct sas_ha_struct *sas_ha = _sas_ha;
+	struct scsi_core *core = &sas_ha->core;
+
+	daemonize("sas_queue_%d", core->shost->host_no);
+	current->flags |= PF_NOFREEZE;
+
+	complete(&queue_th_comp);
+
+	while (1) {
+		down_interruptible(&core->queue_thread_sema);
+		sas_queue(sas_ha);
+		if (core->queue_thread_kill)
+			break;
+	}
+
+	complete(&queue_th_comp);
+
+	return 0;
+}
+
+/* ---------- SCSI Core struct attributes ---------- */
+
+static ssize_t show_task_queue_size(struct scsi_core *core, char *page)
+{
+	return sprintf(page, "%d\n", core->task_queue_size);
+}
+
+struct scsi_core_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct scsi_core *, char *);
+	ssize_t (*store)(struct scsi_core *, const char *, size_t len);
+};
+
+#define to_scsi_core(_obj) container_of((_obj), struct scsi_core, \
+					scsi_core_obj)
+#define to_sc_attr(_attr) container_of((_attr), struct scsi_core_attribute,\
+				       attr)
+
+static ssize_t sc_show_attr(struct kobject *kobj, struct attribute *attr,
+			    char *page)
+{
+	ssize_t ret = 0;
+	struct scsi_core *core = to_scsi_core(kobj);
+	struct scsi_core_attribute *sc_attr = to_sc_attr(attr);
+
+	if (sc_attr->show)
+		ret = sc_attr->show(core, page);
+	return ret;
+}
+
+static struct scsi_core_attribute sc_attrs[] = {
+	__ATTR(task_queue_size, 0444, show_task_queue_size, NULL),
+	__ATTR_NULL,
+};
+
+static struct attribute *sc_def_attrs[ARRAY_SIZE(sc_attrs)];
+
+static struct sysfs_ops sc_sysfs_ops = {
+	.show = sc_show_attr,
+};
+
+static struct kobj_type scsi_core_ktype = {
+	.sysfs_ops = &sc_sysfs_ops,
+	.default_attrs = sc_def_attrs,
+};
+
+int sas_init_queue(struct sas_ha_struct *sas_ha)
+{
+	int res;
+	struct scsi_core *core = &sas_ha->core;
+
+	spin_lock_init(&core->task_queue_lock);
+	core->task_queue_size = 0;
+	INIT_LIST_HEAD(&core->task_queue);
+	init_MUTEX_LOCKED(&core->queue_thread_sema);
+
+	res = kernel_thread(sas_queue_thread, sas_ha, 0);
+	if (res >= 0) {
+		int i;
+		wait_for_completion(&queue_th_comp);
+
+		for (i = 0; i < ARRAY_SIZE(sc_attrs)-1; i++)
+			sc_def_attrs[i] = &sc_attrs[i].attr;
+		sc_def_attrs[i] = NULL;
+
+		core->scsi_core_obj.kset = &sas_ha->ha_kset;
+		kobject_set_name(&core->scsi_core_obj, "%s", "scsi_core");
+		core->scsi_core_obj.ktype = &scsi_core_ktype;
+	}
+
+	return res < 0 ? res : 0;
+}
+
+void sas_shutdown_queue(struct sas_ha_struct *sas_ha)
+{
+	unsigned long flags;
+	struct scsi_core *core = &sas_ha->core;
+	struct sas_task *task, *n;
+
+	init_completion(&queue_th_comp);
+	core->queue_thread_kill = 1;
+	up(&core->queue_thread_sema);
+	wait_for_completion(&queue_th_comp);
+
+	if (!list_empty(&core->task_queue))
+		SAS_DPRINTK("HA: %llx: scsi core task queue is NOT empty!?\n",
+			    SAS_ADDR(sas_ha->sas_addr));
+
+	spin_lock_irqsave(&core->task_queue_lock, flags);
+	list_for_each_entry_safe(task, n, &core->task_queue, list) {
+		struct scsi_cmnd *cmd = task->uldd_task;
+
+		list_del_init(&task->list);
+
+		ASSIGN_SAS_TASK(cmd, NULL);
+		sas_free_task(task);
+		cmd->result = DID_ABORT << 16;
+		cmd->scsi_done(cmd);
+	}
+	spin_unlock_irqrestore(&core->task_queue_lock, flags);
+}
+
+EXPORT_SYMBOL_GPL(sas_queuecommand);
+EXPORT_SYMBOL_GPL(sas_scsi_recover_host);
+EXPORT_SYMBOL_GPL(sas_scsi_timed_out);
+EXPORT_SYMBOL_GPL(sas_slave_alloc);
+EXPORT_SYMBOL_GPL(sas_slave_configure);
+EXPORT_SYMBOL_GPL(sas_slave_destroy);
+EXPORT_SYMBOL_GPL(sas_change_queue_depth);
+EXPORT_SYMBOL_GPL(sas_change_queue_type);
+EXPORT_SYMBOL_GPL(sas_bios_param);
diff -urN oldtree/drivers/scsi/scsi_debug.c newtree/drivers/scsi/scsi_debug.c
--- oldtree/drivers/scsi/scsi_debug.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/scsi/scsi_debug.c	2006-02-13 19:20:00.654412816 -0500
@@ -221,8 +221,6 @@
 static struct device_driver sdebug_driverfs_driver = {
 	.name 		= sdebug_proc_name,
 	.bus		= &pseudo_lld_bus,
-	.probe          = sdebug_driver_probe,
-	.remove         = sdebug_driver_remove,
 };
 
 static const int check_condition_result =
@@ -1796,6 +1794,8 @@
 static struct bus_type pseudo_lld_bus = {
         .name = "pseudo",
         .match = pseudo_lld_bus_match,
+	.probe = sdebug_driver_probe,
+	.remove = sdebug_driver_remove,
 };
 
 static void sdebug_release_adapter(struct device * dev)
diff -urN oldtree/drivers/scsi/scsi_lib.c newtree/drivers/scsi/scsi_lib.c
--- oldtree/drivers/scsi/scsi_lib.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/scsi/scsi_lib.c	2006-02-13 19:20:29.724993416 -0500
@@ -1534,11 +1534,6 @@
 	 */
 	if (shost->ordered_tag)
 		blk_queue_ordered(q, QUEUE_ORDERED_TAG);
-	else if (shost->ordered_flush) {
-		blk_queue_ordered(q, QUEUE_ORDERED_FLUSH);
-		q->prepare_flush_fn = scsi_prepare_flush_fn;
-		q->end_flush_fn = scsi_end_flush_fn;
-	}
 
 	if (!shost->use_clustering)
 		clear_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags);
diff -urN oldtree/drivers/scsi/sr.c newtree/drivers/scsi/sr.c
--- oldtree/drivers/scsi/sr.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/scsi/sr.c	2006-02-13 19:20:00.655412664 -0500
@@ -66,7 +66,7 @@
 #define SR_CAPABILITIES \
 	(CDC_CLOSE_TRAY|CDC_OPEN_TRAY|CDC_LOCK|CDC_SELECT_SPEED| \
 	 CDC_SELECT_DISC|CDC_MULTI_SESSION|CDC_MCN|CDC_MEDIA_CHANGED| \
-	 CDC_PLAY_AUDIO|CDC_RESET|CDC_IOCTLS|CDC_DRIVE_STATUS| \
+	 CDC_PLAY_AUDIO|CDC_RESET|CDC_DRIVE_STATUS| \
 	 CDC_CD_R|CDC_CD_RW|CDC_DVD|CDC_DVD_R|CDC_DVD_RAM|CDC_GENERIC_PACKET| \
 	 CDC_MRW|CDC_MRW_W|CDC_RAM)
 
@@ -113,7 +113,6 @@
 	.get_mcn		= sr_get_mcn,
 	.reset			= sr_reset,
 	.audio_ioctl		= sr_audio_ioctl,
-	.dev_ioctl		= sr_dev_ioctl,
 	.capability		= SR_CAPABILITIES,
 	.generic_packet		= sr_packet,
 };
@@ -472,17 +471,33 @@
 {
 	struct scsi_cd *cd = scsi_cd(inode->i_bdev->bd_disk);
 	struct scsi_device *sdev = cd->device;
+	void __user *argp = (void __user *)arg;
+	int ret;
 
-        /*
-         * Send SCSI addressing ioctls directly to mid level, send other
-         * ioctls to cdrom/block level.
-         */
-        switch (cmd) {
-                case SCSI_IOCTL_GET_IDLUN:
-                case SCSI_IOCTL_GET_BUS_NUMBER:
-                        return scsi_ioctl(sdev, cmd, (void __user *)arg);
+	/*
+	 * Send SCSI addressing ioctls directly to mid level, send other
+	 * ioctls to cdrom/block level.
+	 */
+	switch (cmd) {
+	case SCSI_IOCTL_GET_IDLUN:
+	case SCSI_IOCTL_GET_BUS_NUMBER:
+		return scsi_ioctl(sdev, cmd, argp);
 	}
-	return cdrom_ioctl(file, &cd->cdi, inode, cmd, arg);
+
+	ret = cdrom_ioctl(file, &cd->cdi, inode, cmd, arg);
+	if (ret != ENOSYS)
+		return ret;
+
+	/*
+	 * ENODEV means that we didn't recognise the ioctl, or that we
+	 * cannot execute it in the current device state.  In either
+	 * case fall through to scsi_ioctl, which will return ENDOEV again
+	 * if it doesn't recognise the ioctl
+	 */
+	ret = scsi_nonblockable_ioctl(sdev, cmd, argp, NULL);
+	if (ret != -ENODEV)
+		return ret;
+	return scsi_ioctl(sdev, cmd, argp);
 }
 
 static int sr_block_media_changed(struct gendisk *disk)
diff -urN oldtree/drivers/scsi/sr.c.orig newtree/drivers/scsi/sr.c.orig
--- oldtree/drivers/scsi/sr.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/scsi/sr.c.orig	2006-02-13 19:20:00.656412512 -0500
@@ -0,0 +1,902 @@
+/*
+ *  sr.c Copyright (C) 1992 David Giller
+ *           Copyright (C) 1993, 1994, 1995, 1999 Eric Youngdale
+ *
+ *  adapted from:
+ *      sd.c Copyright (C) 1992 Drew Eckhardt
+ *      Linux scsi disk driver by
+ *              Drew Eckhardt <drew@colorado.edu>
+ *
+ *	Modified by Eric Youngdale ericy@andante.org to
+ *	add scatter-gather, multiple outstanding request, and other
+ *	enhancements.
+ *
+ *      Modified by Eric Youngdale eric@andante.org to support loadable
+ *      low-level scsi drivers.
+ *
+ *      Modified by Thomas Quinot thomas@melchior.cuivre.fdn.fr to
+ *      provide auto-eject.
+ *
+ *      Modified by Gerd Knorr <kraxel@cs.tu-berlin.de> to support the
+ *      generic cdrom interface
+ *
+ *      Modified by Jens Axboe <axboe@suse.de> - Uniform sr_packet()
+ *      interface, capabilities probe additions, ioctl cleanups, etc.
+ *
+ *	Modified by Richard Gooch <rgooch@atnf.csiro.au> to support devfs
+ *
+ *	Modified by Jens Axboe <axboe@suse.de> - support DVD-RAM
+ *	transparently and lose the GHOST hack
+ *
+ *	Modified by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *	check resource allocation in sr_init and some cleanups
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/bio.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/cdrom.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <asm/uaccess.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_driver.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_ioctl.h>	/* For the door lock/unlock commands */
+
+#include "scsi_logging.h"
+#include "sr.h"
+
+
+#define SR_DISKS	256
+
+#define MAX_RETRIES	3
+#define SR_TIMEOUT	(30 * HZ)
+#define SR_CAPABILITIES \
+	(CDC_CLOSE_TRAY|CDC_OPEN_TRAY|CDC_LOCK|CDC_SELECT_SPEED| \
+	 CDC_SELECT_DISC|CDC_MULTI_SESSION|CDC_MCN|CDC_MEDIA_CHANGED| \
+	 CDC_PLAY_AUDIO|CDC_RESET|CDC_IOCTLS|CDC_DRIVE_STATUS| \
+	 CDC_CD_R|CDC_CD_RW|CDC_DVD|CDC_DVD_R|CDC_DVD_RAM|CDC_GENERIC_PACKET| \
+	 CDC_MRW|CDC_MRW_W|CDC_RAM)
+
+static int sr_probe(struct device *);
+static int sr_remove(struct device *);
+static int sr_init_command(struct scsi_cmnd *);
+
+static struct scsi_driver sr_template = {
+	.owner			= THIS_MODULE,
+	.gendrv = {
+		.name   	= "sr",
+		.probe		= sr_probe,
+		.remove		= sr_remove,
+	},
+	.init_command		= sr_init_command,
+};
+
+static unsigned long sr_index_bits[SR_DISKS / BITS_PER_LONG];
+static DEFINE_SPINLOCK(sr_index_lock);
+
+/* This semaphore is used to mediate the 0->1 reference get in the
+ * face of object destruction (i.e. we can't allow a get on an
+ * object after last put) */
+static DECLARE_MUTEX(sr_ref_sem);
+
+static int sr_open(struct cdrom_device_info *, int);
+static void sr_release(struct cdrom_device_info *);
+
+static void get_sectorsize(struct scsi_cd *);
+static void get_capabilities(struct scsi_cd *);
+
+static int sr_media_change(struct cdrom_device_info *, int);
+static int sr_packet(struct cdrom_device_info *, struct packet_command *);
+
+static struct cdrom_device_ops sr_dops = {
+	.open			= sr_open,
+	.release	 	= sr_release,
+	.drive_status	 	= sr_drive_status,
+	.media_changed		= sr_media_change,
+	.tray_move		= sr_tray_move,
+	.lock_door		= sr_lock_door,
+	.select_speed		= sr_select_speed,
+	.get_last_session	= sr_get_last_session,
+	.get_mcn		= sr_get_mcn,
+	.reset			= sr_reset,
+	.audio_ioctl		= sr_audio_ioctl,
+	.dev_ioctl		= sr_dev_ioctl,
+	.capability		= SR_CAPABILITIES,
+	.generic_packet		= sr_packet,
+};
+
+static void sr_kref_release(struct kref *kref);
+
+static inline struct scsi_cd *scsi_cd(struct gendisk *disk)
+{
+	return container_of(disk->private_data, struct scsi_cd, driver);
+}
+
+/*
+ * The get and put routines for the struct scsi_cd.  Note this entity
+ * has a scsi_device pointer and owns a reference to this.
+ */
+static inline struct scsi_cd *scsi_cd_get(struct gendisk *disk)
+{
+	struct scsi_cd *cd = NULL;
+
+	down(&sr_ref_sem);
+	if (disk->private_data == NULL)
+		goto out;
+	cd = scsi_cd(disk);
+	kref_get(&cd->kref);
+	if (scsi_device_get(cd->device))
+		goto out_put;
+	goto out;
+
+ out_put:
+	kref_put(&cd->kref, sr_kref_release);
+	cd = NULL;
+ out:
+	up(&sr_ref_sem);
+	return cd;
+}
+
+static inline void scsi_cd_put(struct scsi_cd *cd)
+{
+	struct scsi_device *sdev = cd->device;
+
+	down(&sr_ref_sem);
+	kref_put(&cd->kref, sr_kref_release);
+	scsi_device_put(sdev);
+	up(&sr_ref_sem);
+}
+
+/*
+ * This function checks to see if the media has been changed in the
+ * CDROM drive.  It is possible that we have already sensed a change,
+ * or the drive may have sensed one and not yet reported it.  We must
+ * be ready for either case. This function always reports the current
+ * value of the changed bit.  If flag is 0, then the changed bit is reset.
+ * This function could be done as an ioctl, but we would need to have
+ * an inode for that to work, and we do not always have one.
+ */
+
+int sr_media_change(struct cdrom_device_info *cdi, int slot)
+{
+	struct scsi_cd *cd = cdi->handle;
+	int retval;
+
+	if (CDSL_CURRENT != slot) {
+		/* no changer support */
+		return -EINVAL;
+	}
+
+	retval = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES);
+	if (retval) {
+		/* Unable to test, unit probably not ready.  This usually
+		 * means there is no disc in the drive.  Mark as changed,
+		 * and we will figure it out later once the drive is
+		 * available again.  */
+		cd->device->changed = 1;
+		return 1;	/* This will force a flush, if called from
+				 * check_disk_change */
+	};
+
+	retval = cd->device->changed;
+	cd->device->changed = 0;
+	/* If the disk changed, the capacity will now be different,
+	 * so we force a re-read of this information */
+	if (retval) {
+		/* check multisession offset etc */
+		sr_cd_check(cdi);
+
+		get_sectorsize(cd);
+	}
+	return retval;
+}
+ 
+/*
+ * rw_intr is the interrupt routine for the device driver.
+ *
+ * It will be notified on the end of a SCSI read / write, and will take on
+ * of several actions based on success or failure.
+ */
+static void rw_intr(struct scsi_cmnd * SCpnt)
+{
+	int result = SCpnt->result;
+	int this_count = SCpnt->bufflen;
+	int good_bytes = (result == 0 ? this_count : 0);
+	int block_sectors = 0;
+	long error_sector;
+	struct scsi_cd *cd = scsi_cd(SCpnt->request->rq_disk);
+
+#ifdef DEBUG
+	printk("sr.c done: %x\n", result);
+#endif
+
+	/*
+	 * Handle MEDIUM ERRORs or VOLUME OVERFLOWs that indicate partial
+	 * success.  Since this is a relatively rare error condition, no
+	 * care is taken to avoid unnecessary additional work such as
+	 * memcpy's that could be avoided.
+	 */
+	if (driver_byte(result) != 0 &&		/* An error occurred */
+	    (SCpnt->sense_buffer[0] & 0x7f) == 0x70) { /* Sense current */
+		switch (SCpnt->sense_buffer[2]) {
+		case MEDIUM_ERROR:
+		case VOLUME_OVERFLOW:
+		case ILLEGAL_REQUEST:
+			if (!(SCpnt->sense_buffer[0] & 0x90))
+				break;
+			if (!blk_fs_request(SCpnt->request))
+				break;
+			error_sector = (SCpnt->sense_buffer[3] << 24) |
+				(SCpnt->sense_buffer[4] << 16) |
+				(SCpnt->sense_buffer[5] << 8) |
+				SCpnt->sense_buffer[6];
+			if (SCpnt->request->bio != NULL)
+				block_sectors =
+					bio_sectors(SCpnt->request->bio);
+			if (block_sectors < 4)
+				block_sectors = 4;
+			if (cd->device->sector_size == 2048)
+				error_sector <<= 2;
+			error_sector &= ~(block_sectors - 1);
+			good_bytes = (error_sector - SCpnt->request->sector) << 9;
+			if (good_bytes < 0 || good_bytes >= this_count)
+				good_bytes = 0;
+			/*
+			 * The SCSI specification allows for the value
+			 * returned by READ CAPACITY to be up to 75 2K
+			 * sectors past the last readable block.
+			 * Therefore, if we hit a medium error within the
+			 * last 75 2K sectors, we decrease the saved size
+			 * value.
+			 */
+			if (error_sector < get_capacity(cd->disk) &&
+			    cd->capacity - error_sector < 4 * 75)
+				set_capacity(cd->disk, error_sector);
+			break;
+
+		case RECOVERED_ERROR:
+
+			/*
+			 * An error occured, but it recovered.  Inform the
+			 * user, but make sure that it's not treated as a
+			 * hard error.
+			 */
+			scsi_print_sense("sr", SCpnt);
+			SCpnt->result = 0;
+			SCpnt->sense_buffer[0] = 0x0;
+			good_bytes = this_count;
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	/*
+	 * This calls the generic completion function, now that we know
+	 * how many actual sectors finished, and how many sectors we need
+	 * to say have failed.
+	 */
+	scsi_io_completion(SCpnt, good_bytes, block_sectors << 9);
+}
+
+static int sr_init_command(struct scsi_cmnd * SCpnt)
+{
+	int block=0, this_count, s_size, timeout = SR_TIMEOUT;
+	struct scsi_cd *cd = scsi_cd(SCpnt->request->rq_disk);
+
+	SCSI_LOG_HLQUEUE(1, printk("Doing sr request, dev = %s, block = %d\n",
+				cd->disk->disk_name, block));
+
+	if (!cd->device || !scsi_device_online(cd->device)) {
+		SCSI_LOG_HLQUEUE(2, printk("Finishing %ld sectors\n",
+					SCpnt->request->nr_sectors));
+		SCSI_LOG_HLQUEUE(2, printk("Retry with 0x%p\n", SCpnt));
+		return 0;
+	}
+
+	if (cd->device->changed) {
+		/*
+		 * quietly refuse to do anything to a changed disc until the
+		 * changed bit has been reset
+		 */
+		return 0;
+	}
+
+	/*
+	 * these are already setup, just copy cdb basically
+	 */
+	if (SCpnt->request->flags & REQ_BLOCK_PC) {
+		scsi_setup_blk_pc_cmnd(SCpnt, MAX_RETRIES);
+
+		if (SCpnt->timeout_per_command)
+			timeout = SCpnt->timeout_per_command;
+
+		goto queue;
+	}
+
+	if (!(SCpnt->request->flags & REQ_CMD)) {
+		blk_dump_rq_flags(SCpnt->request, "sr unsup command");
+		return 0;
+	}
+
+	/*
+	 * we do lazy blocksize switching (when reading XA sectors,
+	 * see CDROMREADMODE2 ioctl) 
+	 */
+	s_size = cd->device->sector_size;
+	if (s_size > 2048) {
+		if (!in_interrupt())
+			sr_set_blocklength(cd, 2048);
+		else
+			printk("sr: can't switch blocksize: in interrupt\n");
+	}
+
+	if (s_size != 512 && s_size != 1024 && s_size != 2048) {
+		scmd_printk(KERN_ERR, SCpnt, "bad sector size %d\n", s_size);
+		return 0;
+	}
+
+	if (rq_data_dir(SCpnt->request) == WRITE) {
+		if (!cd->device->writeable)
+			return 0;
+		SCpnt->cmnd[0] = WRITE_10;
+		SCpnt->sc_data_direction = DMA_TO_DEVICE;
+ 	 	cd->cdi.media_written = 1;
+	} else if (rq_data_dir(SCpnt->request) == READ) {
+		SCpnt->cmnd[0] = READ_10;
+		SCpnt->sc_data_direction = DMA_FROM_DEVICE;
+	} else {
+		blk_dump_rq_flags(SCpnt->request, "Unknown sr command");
+		return 0;
+	}
+
+	{
+		struct scatterlist *sg = SCpnt->request_buffer;
+		int i, size = 0;
+		for (i = 0; i < SCpnt->use_sg; i++)
+			size += sg[i].length;
+
+		if (size != SCpnt->request_bufflen && SCpnt->use_sg) {
+			scmd_printk(KERN_ERR, SCpnt,
+				"mismatch count %d, bytes %d\n",
+				size, SCpnt->request_bufflen);
+			if (SCpnt->request_bufflen > size)
+				SCpnt->request_bufflen = SCpnt->bufflen = size;
+		}
+	}
+
+	/*
+	 * request doesn't start on hw block boundary, add scatter pads
+	 */
+	if (((unsigned int)SCpnt->request->sector % (s_size >> 9)) ||
+	    (SCpnt->request_bufflen % s_size)) {
+		scmd_printk(KERN_NOTICE, SCpnt, "unaligned transfer\n");
+		return 0;
+	}
+
+	this_count = (SCpnt->request_bufflen >> 9) / (s_size >> 9);
+
+
+	SCSI_LOG_HLQUEUE(2, printk("%s : %s %d/%ld 512 byte blocks.\n",
+				cd->cdi.name,
+				(rq_data_dir(SCpnt->request) == WRITE) ?
+					"writing" : "reading",
+				this_count, SCpnt->request->nr_sectors));
+
+	SCpnt->cmnd[1] = 0;
+	block = (unsigned int)SCpnt->request->sector / (s_size >> 9);
+
+	if (this_count > 0xffff) {
+		this_count = 0xffff;
+		SCpnt->request_bufflen = SCpnt->bufflen =
+				this_count * s_size;
+	}
+
+	SCpnt->cmnd[2] = (unsigned char) (block >> 24) & 0xff;
+	SCpnt->cmnd[3] = (unsigned char) (block >> 16) & 0xff;
+	SCpnt->cmnd[4] = (unsigned char) (block >> 8) & 0xff;
+	SCpnt->cmnd[5] = (unsigned char) block & 0xff;
+	SCpnt->cmnd[6] = SCpnt->cmnd[9] = 0;
+	SCpnt->cmnd[7] = (unsigned char) (this_count >> 8) & 0xff;
+	SCpnt->cmnd[8] = (unsigned char) this_count & 0xff;
+
+	/*
+	 * We shouldn't disconnect in the middle of a sector, so with a dumb
+	 * host adapter, it's safe to assume that we can at least transfer
+	 * this many bytes between each connect / disconnect.
+	 */
+	SCpnt->transfersize = cd->device->sector_size;
+	SCpnt->underflow = this_count << 9;
+
+queue:
+	SCpnt->allowed = MAX_RETRIES;
+	SCpnt->timeout_per_command = timeout;
+
+	/*
+	 * This is the completion routine we use.  This is matched in terms
+	 * of capability to this function.
+	 */
+	SCpnt->done = rw_intr;
+
+	/*
+	 * This indicates that the command is ready from our end to be
+	 * queued.
+	 */
+	return 1;
+}
+
+static int sr_block_open(struct inode *inode, struct file *file)
+{
+	struct gendisk *disk = inode->i_bdev->bd_disk;
+	struct scsi_cd *cd;
+	int ret = 0;
+
+	if(!(cd = scsi_cd_get(disk)))
+		return -ENXIO;
+
+	if((ret = cdrom_open(&cd->cdi, inode, file)) != 0)
+		scsi_cd_put(cd);
+
+	return ret;
+}
+
+static int sr_block_release(struct inode *inode, struct file *file)
+{
+	int ret;
+	struct scsi_cd *cd = scsi_cd(inode->i_bdev->bd_disk);
+	ret = cdrom_release(&cd->cdi, file);
+	if(ret)
+		return ret;
+	
+	scsi_cd_put(cd);
+
+	return 0;
+}
+
+static int sr_block_ioctl(struct inode *inode, struct file *file, unsigned cmd,
+			  unsigned long arg)
+{
+	struct scsi_cd *cd = scsi_cd(inode->i_bdev->bd_disk);
+	struct scsi_device *sdev = cd->device;
+
+        /*
+         * Send SCSI addressing ioctls directly to mid level, send other
+         * ioctls to cdrom/block level.
+         */
+        switch (cmd) {
+                case SCSI_IOCTL_GET_IDLUN:
+                case SCSI_IOCTL_GET_BUS_NUMBER:
+                        return scsi_ioctl(sdev, cmd, (void __user *)arg);
+	}
+	return cdrom_ioctl(file, &cd->cdi, inode, cmd, arg);
+}
+
+static int sr_block_media_changed(struct gendisk *disk)
+{
+	struct scsi_cd *cd = scsi_cd(disk);
+	return cdrom_media_changed(&cd->cdi);
+}
+
+static struct block_device_operations sr_bdops =
+{
+	.owner		= THIS_MODULE,
+	.open		= sr_block_open,
+	.release	= sr_block_release,
+	.ioctl		= sr_block_ioctl,
+	.media_changed	= sr_block_media_changed,
+	/* 
+	 * No compat_ioctl for now because sr_block_ioctl never
+	 * seems to pass arbitary ioctls down to host drivers.
+	 */
+};
+
+static int sr_open(struct cdrom_device_info *cdi, int purpose)
+{
+	struct scsi_cd *cd = cdi->handle;
+	struct scsi_device *sdev = cd->device;
+	int retval;
+
+	/*
+	 * If the device is in error recovery, wait until it is done.
+	 * If the device is offline, then disallow any access to it.
+	 */
+	retval = -ENXIO;
+	if (!scsi_block_when_processing_errors(sdev))
+		goto error_out;
+
+	return 0;
+
+error_out:
+	return retval;	
+}
+
+static void sr_release(struct cdrom_device_info *cdi)
+{
+	struct scsi_cd *cd = cdi->handle;
+
+	if (cd->device->sector_size > 2048)
+		sr_set_blocklength(cd, 2048);
+
+}
+
+static int sr_probe(struct device *dev)
+{
+	struct scsi_device *sdev = to_scsi_device(dev);
+	struct gendisk *disk;
+	struct scsi_cd *cd;
+	int minor, error;
+
+	error = -ENODEV;
+	if (sdev->type != TYPE_ROM && sdev->type != TYPE_WORM)
+		goto fail;
+
+	error = -ENOMEM;
+	cd = kmalloc(sizeof(*cd), GFP_KERNEL);
+	if (!cd)
+		goto fail;
+	memset(cd, 0, sizeof(*cd));
+
+	kref_init(&cd->kref);
+
+	disk = alloc_disk(1);
+	if (!disk)
+		goto fail_free;
+
+	spin_lock(&sr_index_lock);
+	minor = find_first_zero_bit(sr_index_bits, SR_DISKS);
+	if (minor == SR_DISKS) {
+		spin_unlock(&sr_index_lock);
+		error = -EBUSY;
+		goto fail_put;
+	}
+	__set_bit(minor, sr_index_bits);
+	spin_unlock(&sr_index_lock);
+
+	disk->major = SCSI_CDROM_MAJOR;
+	disk->first_minor = minor;
+	sprintf(disk->disk_name, "sr%d", minor);
+	disk->fops = &sr_bdops;
+	disk->flags = GENHD_FL_CD;
+
+	cd->device = sdev;
+	cd->disk = disk;
+	cd->driver = &sr_template;
+	cd->disk = disk;
+	cd->capacity = 0x1fffff;
+	cd->device->changed = 1;	/* force recheck CD type */
+	cd->use = 1;
+	cd->readcd_known = 0;
+	cd->readcd_cdda = 0;
+
+	cd->cdi.ops = &sr_dops;
+	cd->cdi.handle = cd;
+	cd->cdi.mask = 0;
+	cd->cdi.capacity = 1;
+	sprintf(cd->cdi.name, "sr%d", minor);
+
+	sdev->sector_size = 2048;	/* A guess, just in case */
+
+	/* FIXME: need to handle a get_capabilities failure properly ?? */
+	get_capabilities(cd);
+	sr_vendor_init(cd);
+
+	snprintf(disk->devfs_name, sizeof(disk->devfs_name),
+			"%s/cd", sdev->devfs_name);
+	disk->driverfs_dev = &sdev->sdev_gendev;
+	set_capacity(disk, cd->capacity);
+	disk->private_data = &cd->driver;
+	disk->queue = sdev->request_queue;
+	cd->cdi.disk = disk;
+
+	if (register_cdrom(&cd->cdi))
+		goto fail_put;
+
+	dev_set_drvdata(dev, cd);
+	disk->flags |= GENHD_FL_REMOVABLE;
+	add_disk(disk);
+
+	sdev_printk(KERN_DEBUG, sdev,
+		    "Attached scsi CD-ROM %s\n", cd->cdi.name);
+	return 0;
+
+fail_put:
+	put_disk(disk);
+fail_free:
+	kfree(cd);
+fail:
+	return error;
+}
+
+
+static void get_sectorsize(struct scsi_cd *cd)
+{
+	unsigned char cmd[10];
+	unsigned char *buffer;
+	int the_result, retries = 3;
+	int sector_size;
+	request_queue_t *queue;
+
+	buffer = kmalloc(512, GFP_KERNEL | GFP_DMA);
+	if (!buffer)
+		goto Enomem;
+
+	do {
+		cmd[0] = READ_CAPACITY;
+		memset((void *) &cmd[1], 0, 9);
+		memset(buffer, 0, 8);
+
+		/* Do the command and wait.. */
+		the_result = scsi_execute_req(cd->device, cmd, DMA_FROM_DEVICE,
+					      buffer, 8, NULL, SR_TIMEOUT,
+					      MAX_RETRIES);
+
+		retries--;
+
+	} while (the_result && retries);
+
+
+	if (the_result) {
+		cd->capacity = 0x1fffff;
+		sector_size = 2048;	/* A guess, just in case */
+	} else {
+#if 0
+		if (cdrom_get_last_written(&cd->cdi,
+					   &cd->capacity))
+#endif
+			cd->capacity = 1 + ((buffer[0] << 24) |
+						    (buffer[1] << 16) |
+						    (buffer[2] << 8) |
+						    buffer[3]);
+		sector_size = (buffer[4] << 24) |
+		    (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
+		switch (sector_size) {
+			/*
+			 * HP 4020i CD-Recorder reports 2340 byte sectors
+			 * Philips CD-Writers report 2352 byte sectors
+			 *
+			 * Use 2k sectors for them..
+			 */
+		case 0:
+		case 2340:
+		case 2352:
+			sector_size = 2048;
+			/* fall through */
+		case 2048:
+			cd->capacity *= 4;
+			/* fall through */
+		case 512:
+			break;
+		default:
+			printk("%s: unsupported sector size %d.\n",
+			       cd->cdi.name, sector_size);
+			cd->capacity = 0;
+		}
+
+		cd->device->sector_size = sector_size;
+
+		/*
+		 * Add this so that we have the ability to correctly gauge
+		 * what the device is capable of.
+		 */
+		set_capacity(cd->disk, cd->capacity);
+	}
+
+	queue = cd->device->request_queue;
+	blk_queue_hardsect_size(queue, sector_size);
+out:
+	kfree(buffer);
+	return;
+
+Enomem:
+	cd->capacity = 0x1fffff;
+	cd->device->sector_size = 2048;	/* A guess, just in case */
+	goto out;
+}
+
+static void get_capabilities(struct scsi_cd *cd)
+{
+	unsigned char *buffer;
+	struct scsi_mode_data data;
+	unsigned char cmd[MAX_COMMAND_SIZE];
+	struct scsi_sense_hdr sshdr;
+	unsigned int the_result;
+	int retries, rc, n;
+
+	static char *loadmech[] =
+	{
+		"caddy",
+		"tray",
+		"pop-up",
+		"",
+		"changer",
+		"cartridge changer",
+		"",
+		""
+	};
+
+
+	/* allocate transfer buffer */
+	buffer = kmalloc(512, GFP_KERNEL | GFP_DMA);
+	if (!buffer) {
+		printk(KERN_ERR "sr: out of memory.\n");
+		return;
+	}
+
+	/* issue TEST_UNIT_READY until the initial startup UNIT_ATTENTION
+	 * conditions are gone, or a timeout happens
+	 */
+	retries = 0;
+	do {
+		memset((void *)cmd, 0, MAX_COMMAND_SIZE);
+		cmd[0] = TEST_UNIT_READY;
+
+		the_result = scsi_execute_req (cd->device, cmd, DMA_NONE, NULL,
+					       0, &sshdr, SR_TIMEOUT,
+					       MAX_RETRIES);
+
+		retries++;
+	} while (retries < 5 && 
+		 (!scsi_status_is_good(the_result) ||
+		  (scsi_sense_valid(&sshdr) &&
+		   sshdr.sense_key == UNIT_ATTENTION)));
+
+	/* ask for mode page 0x2a */
+	rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, 128,
+			     SR_TIMEOUT, 3, &data, NULL);
+
+	if (!scsi_status_is_good(rc)) {
+		/* failed, drive doesn't have capabilities mode page */
+		cd->cdi.speed = 1;
+		cd->cdi.mask |= (CDC_CD_R | CDC_CD_RW | CDC_DVD_R |
+					 CDC_DVD | CDC_DVD_RAM |
+					 CDC_SELECT_DISC | CDC_SELECT_SPEED);
+		kfree(buffer);
+		printk("%s: scsi-1 drive\n", cd->cdi.name);
+		return;
+	}
+
+	n = data.header_length + data.block_descriptor_length;
+	cd->cdi.speed = ((buffer[n + 8] << 8) + buffer[n + 9]) / 176;
+	cd->readcd_known = 1;
+	cd->readcd_cdda = buffer[n + 5] & 0x01;
+	/* print some capability bits */
+	printk("%s: scsi3-mmc drive: %dx/%dx %s%s%s%s%s%s\n", cd->cdi.name,
+	       ((buffer[n + 14] << 8) + buffer[n + 15]) / 176,
+	       cd->cdi.speed,
+	       buffer[n + 3] & 0x01 ? "writer " : "", /* CD Writer */
+	       buffer[n + 3] & 0x20 ? "dvd-ram " : "",
+	       buffer[n + 2] & 0x02 ? "cd/rw " : "", /* can read rewriteable */
+	       buffer[n + 4] & 0x20 ? "xa/form2 " : "",	/* can read xa/from2 */
+	       buffer[n + 5] & 0x01 ? "cdda " : "", /* can read audio data */
+	       loadmech[buffer[n + 6] >> 5]);
+	if ((buffer[n + 6] >> 5) == 0)
+		/* caddy drives can't close tray... */
+		cd->cdi.mask |= CDC_CLOSE_TRAY;
+	if ((buffer[n + 2] & 0x8) == 0)
+		/* not a DVD drive */
+		cd->cdi.mask |= CDC_DVD;
+	if ((buffer[n + 3] & 0x20) == 0) 
+		/* can't write DVD-RAM media */
+		cd->cdi.mask |= CDC_DVD_RAM;
+	if ((buffer[n + 3] & 0x10) == 0)
+		/* can't write DVD-R media */
+		cd->cdi.mask |= CDC_DVD_R;
+	if ((buffer[n + 3] & 0x2) == 0)
+		/* can't write CD-RW media */
+		cd->cdi.mask |= CDC_CD_RW;
+	if ((buffer[n + 3] & 0x1) == 0)
+		/* can't write CD-R media */
+		cd->cdi.mask |= CDC_CD_R;
+	if ((buffer[n + 6] & 0x8) == 0)
+		/* can't eject */
+		cd->cdi.mask |= CDC_OPEN_TRAY;
+
+	if ((buffer[n + 6] >> 5) == mechtype_individual_changer ||
+	    (buffer[n + 6] >> 5) == mechtype_cartridge_changer)
+		cd->cdi.capacity =
+		    cdrom_number_of_slots(&cd->cdi);
+	if (cd->cdi.capacity <= 1)
+		/* not a changer */
+		cd->cdi.mask |= CDC_SELECT_DISC;
+	/*else    I don't think it can close its tray
+		cd->cdi.mask |= CDC_CLOSE_TRAY; */
+
+	/*
+	 * if DVD-RAM, MRW-W or CD-RW, we are randomly writable
+	 */
+	if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) !=
+			(CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) {
+		cd->device->writeable = 1;
+	}
+
+	kfree(buffer);
+}
+
+/*
+ * sr_packet() is the entry point for the generic commands generated
+ * by the Uniform CD-ROM layer. 
+ */
+static int sr_packet(struct cdrom_device_info *cdi,
+		struct packet_command *cgc)
+{
+	if (cgc->timeout <= 0)
+		cgc->timeout = IOCTL_TIMEOUT;
+
+	sr_do_ioctl(cdi->handle, cgc);
+
+	return cgc->stat;
+}
+
+/**
+ *	sr_kref_release - Called to free the scsi_cd structure
+ *	@kref: pointer to embedded kref
+ *
+ *	sr_ref_sem must be held entering this routine.  Because it is
+ *	called on last put, you should always use the scsi_cd_get()
+ *	scsi_cd_put() helpers which manipulate the semaphore directly
+ *	and never do a direct kref_put().
+ **/
+static void sr_kref_release(struct kref *kref)
+{
+	struct scsi_cd *cd = container_of(kref, struct scsi_cd, kref);
+	struct gendisk *disk = cd->disk;
+
+	spin_lock(&sr_index_lock);
+	clear_bit(disk->first_minor, sr_index_bits);
+	spin_unlock(&sr_index_lock);
+
+	unregister_cdrom(&cd->cdi);
+
+	disk->private_data = NULL;
+
+	put_disk(disk);
+
+	kfree(cd);
+}
+
+static int sr_remove(struct device *dev)
+{
+	struct scsi_cd *cd = dev_get_drvdata(dev);
+
+	del_gendisk(cd->disk);
+
+	down(&sr_ref_sem);
+	kref_put(&cd->kref, sr_kref_release);
+	up(&sr_ref_sem);
+
+	return 0;
+}
+
+static int __init init_sr(void)
+{
+	int rc;
+
+	rc = register_blkdev(SCSI_CDROM_MAJOR, "sr");
+	if (rc)
+		return rc;
+	return scsi_register_driver(&sr_template.gendrv);
+}
+
+static void __exit exit_sr(void)
+{
+	scsi_unregister_driver(&sr_template.gendrv);
+	unregister_blkdev(SCSI_CDROM_MAJOR, "sr");
+}
+
+module_init(init_sr);
+module_exit(exit_sr);
+MODULE_LICENSE("GPL");
diff -urN oldtree/drivers/scsi/sr.h newtree/drivers/scsi/sr.h
--- oldtree/drivers/scsi/sr.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/scsi/sr.h	2006-02-13 19:20:00.657412360 -0500
@@ -55,7 +55,6 @@
 int sr_reset(struct cdrom_device_info *);
 int sr_select_speed(struct cdrom_device_info *cdi, int speed);
 int sr_audio_ioctl(struct cdrom_device_info *, unsigned int, void *);
-int sr_dev_ioctl(struct cdrom_device_info *, unsigned int, unsigned long);
 
 int sr_is_xa(Scsi_CD *);
 
diff -urN oldtree/drivers/scsi/sr_ioctl.c newtree/drivers/scsi/sr_ioctl.c
--- oldtree/drivers/scsi/sr_ioctl.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/scsi/sr_ioctl.c	2006-02-13 19:20:00.657412360 -0500
@@ -542,22 +542,3 @@
 #endif
 	return is_xa;
 }
-
-int sr_dev_ioctl(struct cdrom_device_info *cdi,
-		 unsigned int cmd, unsigned long arg)
-{
-	Scsi_CD *cd = cdi->handle;
-	int ret;
-	
-	ret = scsi_nonblockable_ioctl(cd->device, cmd,
-				      (void __user *)arg, NULL);
-	/*
-	 * ENODEV means that we didn't recognise the ioctl, or that we
-	 * cannot execute it in the current device state.  In either
-	 * case fall through to scsi_ioctl, which will return ENDOEV again
-	 * if it doesn't recognise the ioctl
-	 */
-	if (ret != -ENODEV)
-		return ret;
-	return scsi_ioctl(cd->device, cmd, (void __user *)arg);
-}
diff -urN oldtree/drivers/serial/68328serial.c newtree/drivers/serial/68328serial.c
--- oldtree/drivers/serial/68328serial.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/serial/68328serial.c	2006-02-13 19:20:00.658412208 -0500
@@ -143,7 +143,6 @@
  * memory if large numbers of serial ports are open.
  */
 static unsigned char tmp_buf[SERIAL_XMIT_SIZE]; /* This is cheating */
-DECLARE_MUTEX(tmp_buf_sem);
 
 static inline int serial_paranoia_check(struct m68k_serial *info,
 					char *name, const char *routine)
diff -urN oldtree/drivers/serial/8250.c newtree/drivers/serial/8250.c
--- oldtree/drivers/serial/8250.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/serial/8250.c	2006-02-13 19:20:00.659412056 -0500
@@ -1712,24 +1712,58 @@
 		serial_unlink_irq_chain(up);
 }
 
-static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud)
+static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud,
+					   unsigned int *prescaler)
 {
-	unsigned int quot;
-
-	/*
-	 * Handle magic divisors for baud rates above baud_base on
-	 * SMSC SuperIO chips.
+        /*
+	 * Use special handling only if user did not supply its own divider.
+	 * spd_cust is defined in terms of baud_base, so always use default
+	 * prescaler when spd_cust is requested.
 	 */
-	if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
-	    baud == (port->uartclk/4))
-		quot = 0x8001;
-	else if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
-		 baud == (port->uartclk/8))
-		quot = 0x8002;
-	else
-		quot = uart_get_divisor(port, baud);
 
-	return quot;
+	*prescaler = 16;
+        if (baud != 38400 || (port->flags & UPF_SPD_MASK) != UPF_SPD_CUST) {
+		unsigned int quot = port->uartclk / baud;
+
+		/*
+		 * Handle magic divisors for baud rates above baud_base on
+		 * SMSC SuperIO chips.
+		 */
+		if (port->flags & UPF_MAGIC_MULTIPLIER) {
+			if (quot == 4) {
+				return 0x8001;
+			} else if (quot == 8) {
+				return 0x8002;
+			}
+		}
+		if (port->type == PORT_16C950) {
+			/*
+			 * This computes TCR value (4 to 16), not CPR value (which can
+			 * be between 1.000 and 31.875) - chip I have uses XTAL of
+			 * 806400Hz, and so a division by 7 is required to get 115200Bd.
+			 * I'm leaving CPR disabled for now, until someone will
+			 * hit even more exotic XTAL (it is needed to get 500kbps
+			 * or 1000kbps from 18.432MHz XTAL, but I have no device
+			 * which would benefit from doing that).
+			 *
+			 * If we can use divide by 16, use it.  Otherwise look for
+			 * better prescaler, from 15 to 4.  If quotient cannot
+			 * be divided by any integer value between 4 and 15, use 4.
+			 */
+			if (quot & 0x0F) {
+				unsigned int div;
+
+				for (div = 15; div > 4; div--) {
+					if (quot % div == 0) {
+						break;
+					}
+				}
+				*prescaler = div;
+				return quot / div;
+			}
+		}
+	}
+	return uart_get_divisor(port, baud);
 }
 
 static void
@@ -1739,7 +1773,7 @@
 	struct uart_8250_port *up = (struct uart_8250_port *)port;
 	unsigned char cval, fcr = 0;
 	unsigned long flags;
-	unsigned int baud, quot;
+	unsigned int baud, quot, prescaler;
 
 	switch (termios->c_cflag & CSIZE) {
 	case CS5:
@@ -1771,8 +1805,13 @@
 	/*
 	 * Ask the core to calculate the divisor for us.
 	 */
-	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
-	quot = serial8250_get_divisor(port, baud);
+
+	if (port->type == PORT_16C950 || (port->flags & UPF_MAGIC_MULTIPLIER)) {
+		baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/4);
+	} else {
+		baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+	}
+	quot = serial8250_get_divisor(port, baud, &prescaler);
 
 	/*
 	 * Oxford Semi 952 rev B workaround
@@ -1877,6 +1916,13 @@
 	serial_outp(up, UART_DLM, quot >> 8);		/* MS of divisor */
 
 	/*
+	 * Program prescaler for 16C950 chips.
+	 */
+	if (up->port.type == PORT_16C950) {
+		serial_icr_write(up, UART_TCR, prescaler == 16 ? 0 : prescaler);
+	}
+
+	/*
 	 * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
 	 * is written without DLAB set, this mode will be disabled.
 	 */
@@ -2466,6 +2512,7 @@
 	.resume		= serial8250_resume,
 	.driver		= {
 		.name	= "serial8250",
+		.owner	= THIS_MODULE,
 	},
 };
 
@@ -2603,21 +2650,30 @@
 	if (ret)
 		goto out;
 
-	serial8250_isa_devs = platform_device_register_simple("serial8250",
-					 PLAT8250_DEV_LEGACY, NULL, 0);
-	if (IS_ERR(serial8250_isa_devs)) {
-		ret = PTR_ERR(serial8250_isa_devs);
-		goto unreg;
+	ret = platform_driver_register(&serial8250_isa_driver);
+	if (ret)
+		goto unreg_uart_drv;
+
+	serial8250_isa_devs = platform_device_alloc("serial8250",
+						    PLAT8250_DEV_LEGACY);
+	if (!serial8250_isa_devs) {
+		ret = -ENOMEM;
+		goto unreg_plat_drv;
 	}
 
+	ret = platform_device_add(serial8250_isa_devs);
+	if (ret)
+		goto put_dev;
+
 	serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);
 
-	ret = platform_driver_register(&serial8250_isa_driver);
-	if (ret == 0)
-		goto out;
+	goto out;
 
-	platform_device_unregister(serial8250_isa_devs);
- unreg:
+ put_dev:
+	platform_device_put(serial8250_isa_devs);
+ unreg_plat_drv:
+	platform_driver_unregister(&serial8250_isa_driver);
+ unreg_uart_drv:
 	uart_unregister_driver(&serial8250_reg);
  out:
 	return ret;
diff -urN oldtree/drivers/serial/8250.c.orig newtree/drivers/serial/8250.c.orig
--- oldtree/drivers/serial/8250.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/drivers/serial/8250.c.orig	2006-02-13 19:20:00.662411600 -0500
@@ -0,0 +1,2706 @@
+/*
+ *  linux/drivers/char/8250.c
+ *
+ *  Driver for 8250/16550-type serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Copyright (C) 2001 Russell King.
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *  $Id: 8250.c,v 1.90 2002/07/28 10:03:27 rmk Exp $
+ *
+ * A note about mapbase / membase
+ *
+ *  mapbase is the physical address of the IO port.
+ *  membase is an 'ioremapped' cookie.
+ */
+#include <linux/config.h>
+
+#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/mca.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_reg.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/serial_8250.h>
+#include <linux/nmi.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "8250.h"
+
+/*
+ * Configuration:
+ *   share_irqs - whether we pass SA_SHIRQ to request_irq().  This option
+ *                is unsafe when used on edge-triggered interrupts.
+ */
+static unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
+
+/*
+ * Debugging.
+ */
+#if 0
+#define DEBUG_AUTOCONF(fmt...)	printk(fmt)
+#else
+#define DEBUG_AUTOCONF(fmt...)	do { } while (0)
+#endif
+
+#if 0
+#define DEBUG_INTR(fmt...)	printk(fmt)
+#else
+#define DEBUG_INTR(fmt...)	do { } while (0)
+#endif
+
+#define PASS_LIMIT	256
+
+/*
+ * We default to IRQ0 for the "no irq" hack.   Some
+ * machine types want others as well - they're free
+ * to redefine this in their header file.
+ */
+#define is_real_interrupt(irq)	((irq) != 0)
+
+#ifdef CONFIG_SERIAL_8250_DETECT_IRQ
+#define CONFIG_SERIAL_DETECT_IRQ 1
+#endif
+#ifdef CONFIG_SERIAL_8250_MANY_PORTS
+#define CONFIG_SERIAL_MANY_PORTS 1
+#endif
+
+/*
+ * HUB6 is always on.  This will be removed once the header
+ * files have been cleaned.
+ */
+#define CONFIG_HUB6 1
+
+#include <asm/serial.h>
+
+/*
+ * SERIAL_PORT_DFNS tells us about built-in ports that have no
+ * standard enumeration mechanism.   Platforms that can find all
+ * serial ports via mechanisms like ACPI or PCI need not supply it.
+ */
+#ifndef SERIAL_PORT_DFNS
+#define SERIAL_PORT_DFNS
+#endif
+
+static const struct old_serial_port old_serial_port[] = {
+	SERIAL_PORT_DFNS /* defined in asm/serial.h */
+};
+
+#define UART_NR	CONFIG_SERIAL_8250_NR_UARTS
+
+#ifdef CONFIG_SERIAL_8250_RSA
+
+#define PORT_RSA_MAX 4
+static unsigned long probe_rsa[PORT_RSA_MAX];
+static unsigned int probe_rsa_count;
+#endif /* CONFIG_SERIAL_8250_RSA  */
+
+struct uart_8250_port {
+	struct uart_port	port;
+	struct timer_list	timer;		/* "no irq" timer */
+	struct list_head	list;		/* ports on this IRQ */
+	unsigned short		capabilities;	/* port capabilities */
+	unsigned short		bugs;		/* port bugs */
+	unsigned int		tx_loadsz;	/* transmit fifo load size */
+	unsigned char		acr;
+	unsigned char		ier;
+	unsigned char		lcr;
+	unsigned char		mcr;
+	unsigned char		mcr_mask;	/* mask of user bits */
+	unsigned char		mcr_force;	/* mask of forced bits */
+	unsigned char		lsr_break_flag;
+
+	/*
+	 * We provide a per-port pm hook.
+	 */
+	void			(*pm)(struct uart_port *port,
+				      unsigned int state, unsigned int old);
+};
+
+struct irq_info {
+	spinlock_t		lock;
+	struct list_head	*head;
+};
+
+static struct irq_info irq_lists[NR_IRQS];
+
+/*
+ * Here we define the default xmit fifo size used for each type of UART.
+ */
+static const struct serial8250_config uart_config[] = {
+	[PORT_UNKNOWN] = {
+		.name		= "unknown",
+		.fifo_size	= 1,
+		.tx_loadsz	= 1,
+	},
+	[PORT_8250] = {
+		.name		= "8250",
+		.fifo_size	= 1,
+		.tx_loadsz	= 1,
+	},
+	[PORT_16450] = {
+		.name		= "16450",
+		.fifo_size	= 1,
+		.tx_loadsz	= 1,
+	},
+	[PORT_16550] = {
+		.name		= "16550",
+		.fifo_size	= 1,
+		.tx_loadsz	= 1,
+	},
+	[PORT_16550A] = {
+		.name		= "16550A",
+		.fifo_size	= 16,
+		.tx_loadsz	= 16,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+		.flags		= UART_CAP_FIFO,
+	},
+	[PORT_CIRRUS] = {
+		.name		= "Cirrus",
+		.fifo_size	= 1,
+		.tx_loadsz	= 1,
+	},
+	[PORT_16650] = {
+		.name		= "ST16650",
+		.fifo_size	= 1,
+		.tx_loadsz	= 1,
+		.flags		= UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
+	},
+	[PORT_16650V2] = {
+		.name		= "ST16650V2",
+		.fifo_size	= 32,
+		.tx_loadsz	= 16,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 |
+				  UART_FCR_T_TRIG_00,
+		.flags		= UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
+	},
+	[PORT_16750] = {
+		.name		= "TI16750",
+		.fifo_size	= 64,
+		.tx_loadsz	= 64,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 |
+				  UART_FCR7_64BYTE,
+		.flags		= UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_AFE,
+	},
+	[PORT_STARTECH] = {
+		.name		= "Startech",
+		.fifo_size	= 1,
+		.tx_loadsz	= 1,
+	},
+	[PORT_16C950] = {
+		.name		= "16C950/954",
+		.fifo_size	= 128,
+		.tx_loadsz	= 128,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+		.flags		= UART_CAP_FIFO,
+	},
+	[PORT_16654] = {
+		.name		= "ST16654",
+		.fifo_size	= 64,
+		.tx_loadsz	= 32,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 |
+				  UART_FCR_T_TRIG_10,
+		.flags		= UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
+	},
+	[PORT_16850] = {
+		.name		= "XR16850",
+		.fifo_size	= 128,
+		.tx_loadsz	= 128,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+		.flags		= UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
+	},
+	[PORT_RSA] = {
+		.name		= "RSA",
+		.fifo_size	= 2048,
+		.tx_loadsz	= 2048,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_11,
+		.flags		= UART_CAP_FIFO,
+	},
+	[PORT_NS16550A] = {
+		.name		= "NS16550A",
+		.fifo_size	= 16,
+		.tx_loadsz	= 16,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+		.flags		= UART_CAP_FIFO | UART_NATSEMI,
+	},
+	[PORT_XSCALE] = {
+		.name		= "XScale",
+		.fifo_size	= 32,
+		.tx_loadsz	= 32,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+		.flags		= UART_CAP_FIFO | UART_CAP_UUE,
+	},
+};
+
+#ifdef CONFIG_SERIAL_8250_AU1X00
+
+/* Au1x00 UART hardware has a weird register layout */
+static const u8 au_io_in_map[] = {
+	[UART_RX]  = 0,
+	[UART_IER] = 2,
+	[UART_IIR] = 3,
+	[UART_LCR] = 5,
+	[UART_MCR] = 6,
+	[UART_LSR] = 7,
+	[UART_MSR] = 8,
+};
+
+static const u8 au_io_out_map[] = {
+	[UART_TX]  = 1,
+	[UART_IER] = 2,
+	[UART_FCR] = 4,
+	[UART_LCR] = 5,
+	[UART_MCR] = 6,
+};
+
+/* sane hardware needs no mapping */
+static inline int map_8250_in_reg(struct uart_8250_port *up, int offset)
+{
+	if (up->port.iotype != UPIO_AU)
+		return offset;
+	return au_io_in_map[offset];
+}
+
+static inline int map_8250_out_reg(struct uart_8250_port *up, int offset)
+{
+	if (up->port.iotype != UPIO_AU)
+		return offset;
+	return au_io_out_map[offset];
+}
+
+#else
+
+/* sane hardware needs no mapping */
+#define map_8250_in_reg(up, offset) (offset)
+#define map_8250_out_reg(up, offset) (offset)
+
+#endif
+
+static _INLINE_ unsigned int serial_in(struct uart_8250_port *up, int offset)
+{
+	offset = map_8250_in_reg(up, offset) << up->port.regshift;
+
+	switch (up->port.iotype) {
+	case UPIO_HUB6:
+		outb(up->port.hub6 - 1 + offset, up->port.iobase);
+		return inb(up->port.iobase + 1);
+
+	case UPIO_MEM:
+		return readb(up->port.membase + offset);
+
+	case UPIO_MEM32:
+		return readl(up->port.membase + offset);
+
+#ifdef CONFIG_SERIAL_8250_AU1X00
+	case UPIO_AU:
+		return __raw_readl(up->port.membase + offset);
+#endif
+
+	default:
+		return inb(up->port.iobase + offset);
+	}
+}
+
+static _INLINE_ void
+serial_out(struct uart_8250_port *up, int offset, int value)
+{
+	offset = map_8250_out_reg(up, offset) << up->port.regshift;
+
+	switch (up->port.iotype) {
+	case UPIO_HUB6:
+		outb(up->port.hub6 - 1 + offset, up->port.iobase);
+		outb(value, up->port.iobase + 1);
+		break;
+
+	case UPIO_MEM:
+		writeb(value, up->port.membase + offset);
+		break;
+
+	case UPIO_MEM32:
+		writel(value, up->port.membase + offset);
+		break;
+
+#ifdef CONFIG_SERIAL_8250_AU1X00
+	case UPIO_AU:
+		__raw_writel(value, up->port.membase + offset);
+		break;
+#endif
+
+	default:
+		outb(value, up->port.iobase + offset);
+	}
+}
+
+/*
+ * We used to support using pause I/O for certain machines.  We
+ * haven't supported this for a while, but just in case it's badly
+ * needed for certain old 386 machines, I've left these #define's
+ * in....
+ */
+#define serial_inp(up, offset)		serial_in(up, offset)
+#define serial_outp(up, offset, value)	serial_out(up, offset, value)
+
+
+/*
+ * For the 16C950
+ */
+static void serial_icr_write(struct uart_8250_port *up, int offset, int value)
+{
+	serial_out(up, UART_SCR, offset);
+	serial_out(up, UART_ICR, value);
+}
+
+static unsigned int serial_icr_read(struct uart_8250_port *up, int offset)
+{
+	unsigned int value;
+
+	serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD);
+	serial_out(up, UART_SCR, offset);
+	value = serial_in(up, UART_ICR);
+	serial_icr_write(up, UART_ACR, up->acr);
+
+	return value;
+}
+
+/*
+ * FIFO support.
+ */
+static inline void serial8250_clear_fifos(struct uart_8250_port *p)
+{
+	if (p->capabilities & UART_CAP_FIFO) {
+		serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO);
+		serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO |
+			       UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+		serial_outp(p, UART_FCR, 0);
+	}
+}
+
+/*
+ * IER sleep support.  UARTs which have EFRs need the "extended
+ * capability" bit enabled.  Note that on XR16C850s, we need to
+ * reset LCR to write to IER.
+ */
+static inline void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
+{
+	if (p->capabilities & UART_CAP_SLEEP) {
+		if (p->capabilities & UART_CAP_EFR) {
+			serial_outp(p, UART_LCR, 0xBF);
+			serial_outp(p, UART_EFR, UART_EFR_ECB);
+			serial_outp(p, UART_LCR, 0);
+		}
+		serial_outp(p, UART_IER, sleep ? UART_IERX_SLEEP : 0);
+		if (p->capabilities & UART_CAP_EFR) {
+			serial_outp(p, UART_LCR, 0xBF);
+			serial_outp(p, UART_EFR, 0);
+			serial_outp(p, UART_LCR, 0);
+		}
+	}
+}
+
+#ifdef CONFIG_SERIAL_8250_RSA
+/*
+ * Attempts to turn on the RSA FIFO.  Returns zero on failure.
+ * We set the port uart clock rate if we succeed.
+ */
+static int __enable_rsa(struct uart_8250_port *up)
+{
+	unsigned char mode;
+	int result;
+
+	mode = serial_inp(up, UART_RSA_MSR);
+	result = mode & UART_RSA_MSR_FIFO;
+
+	if (!result) {
+		serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO);
+		mode = serial_inp(up, UART_RSA_MSR);
+		result = mode & UART_RSA_MSR_FIFO;
+	}
+
+	if (result)
+		up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16;
+
+	return result;
+}
+
+static void enable_rsa(struct uart_8250_port *up)
+{
+	if (up->port.type == PORT_RSA) {
+		if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) {
+			spin_lock_irq(&up->port.lock);
+			__enable_rsa(up);
+			spin_unlock_irq(&up->port.lock);
+		}
+		if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16)
+			serial_outp(up, UART_RSA_FRR, 0);
+	}
+}
+
+/*
+ * Attempts to turn off the RSA FIFO.  Returns zero on failure.
+ * It is unknown why interrupts were disabled in here.  However,
+ * the caller is expected to preserve this behaviour by grabbing
+ * the spinlock before calling this function.
+ */
+static void disable_rsa(struct uart_8250_port *up)
+{
+	unsigned char mode;
+	int result;
+
+	if (up->port.type == PORT_RSA &&
+	    up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) {
+		spin_lock_irq(&up->port.lock);
+
+		mode = serial_inp(up, UART_RSA_MSR);
+		result = !(mode & UART_RSA_MSR_FIFO);
+
+		if (!result) {
+			serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO);
+			mode = serial_inp(up, UART_RSA_MSR);
+			result = !(mode & UART_RSA_MSR_FIFO);
+		}
+
+		if (result)
+			up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16;
+		spin_unlock_irq(&up->port.lock);
+	}
+}
+#endif /* CONFIG_SERIAL_8250_RSA */
+
+/*
+ * This is a quickie test to see how big the FIFO is.
+ * It doesn't work at all the time, more's the pity.
+ */
+static int size_fifo(struct uart_8250_port *up)
+{
+	unsigned char old_fcr, old_mcr, old_dll, old_dlm, old_lcr;
+	int count;
+
+	old_lcr = serial_inp(up, UART_LCR);
+	serial_outp(up, UART_LCR, 0);
+	old_fcr = serial_inp(up, UART_FCR);
+	old_mcr = serial_inp(up, UART_MCR);
+	serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+		    UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+	serial_outp(up, UART_MCR, UART_MCR_LOOP);
+	serial_outp(up, UART_LCR, UART_LCR_DLAB);
+	old_dll = serial_inp(up, UART_DLL);
+	old_dlm = serial_inp(up, UART_DLM);
+	serial_outp(up, UART_DLL, 0x01);
+	serial_outp(up, UART_DLM, 0x00);
+	serial_outp(up, UART_LCR, 0x03);
+	for (count = 0; count < 256; count++)
+		serial_outp(up, UART_TX, count);
+	mdelay(20);/* FIXME - schedule_timeout */
+	for (count = 0; (serial_inp(up, UART_LSR) & UART_LSR_DR) &&
+	     (count < 256); count++)
+		serial_inp(up, UART_RX);
+	serial_outp(up, UART_FCR, old_fcr);
+	serial_outp(up, UART_MCR, old_mcr);
+	serial_outp(up, UART_LCR, UART_LCR_DLAB);
+	serial_outp(up, UART_DLL, old_dll);
+	serial_outp(up, UART_DLM, old_dlm);
+	serial_outp(up, UART_LCR, old_lcr);
+
+	return count;
+}
+
+/*
+ * Read UART ID using the divisor method - set DLL and DLM to zero
+ * and the revision will be in DLL and device type in DLM.  We
+ * preserve the device state across this.
+ */
+static unsigned int autoconfig_read_divisor_id(struct uart_8250_port *p)
+{
+	unsigned char old_dll, old_dlm, old_lcr;
+	unsigned int id;
+
+	old_lcr = serial_inp(p, UART_LCR);
+	serial_outp(p, UART_LCR, UART_LCR_DLAB);
+
+	old_dll = serial_inp(p, UART_DLL);
+	old_dlm = serial_inp(p, UART_DLM);
+
+	serial_outp(p, UART_DLL, 0);
+	serial_outp(p, UART_DLM, 0);
+
+	id = serial_inp(p, UART_DLL) | serial_inp(p, UART_DLM) << 8;
+
+	serial_outp(p, UART_DLL, old_dll);
+	serial_outp(p, UART_DLM, old_dlm);
+	serial_outp(p, UART_LCR, old_lcr);
+
+	return id;
+}
+
+/*
+ * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's.
+ * When this function is called we know it is at least a StarTech
+ * 16650 V2, but it might be one of several StarTech UARTs, or one of
+ * its clones.  (We treat the broken original StarTech 16650 V1 as a
+ * 16550, and why not?  Startech doesn't seem to even acknowledge its
+ * existence.)
+ * 
+ * What evil have men's minds wrought...
+ */
+static void autoconfig_has_efr(struct uart_8250_port *up)
+{
+	unsigned int id1, id2, id3, rev;
+
+	/*
+	 * Everything with an EFR has SLEEP
+	 */
+	up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP;
+
+	/*
+	 * First we check to see if it's an Oxford Semiconductor UART.
+	 *
+	 * If we have to do this here because some non-National
+	 * Semiconductor clone chips lock up if you try writing to the
+	 * LSR register (which serial_icr_read does)
+	 */
+
+	/*
+	 * Check for Oxford Semiconductor 16C950.
+	 *
+	 * EFR [4] must be set else this test fails.
+	 *
+	 * This shouldn't be necessary, but Mike Hudson (Exoray@isys.ca)
+	 * claims that it's needed for 952 dual UART's (which are not
+	 * recommended for new designs).
+	 */
+	up->acr = 0;
+	serial_out(up, UART_LCR, 0xBF);
+	serial_out(up, UART_EFR, UART_EFR_ECB);
+	serial_out(up, UART_LCR, 0x00);
+	id1 = serial_icr_read(up, UART_ID1);
+	id2 = serial_icr_read(up, UART_ID2);
+	id3 = serial_icr_read(up, UART_ID3);
+	rev = serial_icr_read(up, UART_REV);
+
+	DEBUG_AUTOCONF("950id=%02x:%02x:%02x:%02x ", id1, id2, id3, rev);
+
+	if (id1 == 0x16 && id2 == 0xC9 &&
+	    (id3 == 0x50 || id3 == 0x52 || id3 == 0x54)) {
+		up->port.type = PORT_16C950;
+
+		/*
+		 * Enable work around for the Oxford Semiconductor 952 rev B
+		 * chip which causes it to seriously miscalculate baud rates
+		 * when DLL is 0.
+		 */
+		if (id3 == 0x52 && rev == 0x01)
+			up->bugs |= UART_BUG_QUOT;
+		return;
+	}
+	
+	/*
+	 * We check for a XR16C850 by setting DLL and DLM to 0, and then
+	 * reading back DLL and DLM.  The chip type depends on the DLM
+	 * value read back:
+	 *  0x10 - XR16C850 and the DLL contains the chip revision.
+	 *  0x12 - XR16C2850.
+	 *  0x14 - XR16C854.
+	 */
+	id1 = autoconfig_read_divisor_id(up);
+	DEBUG_AUTOCONF("850id=%04x ", id1);
+
+	id2 = id1 >> 8;
+	if (id2 == 0x10 || id2 == 0x12 || id2 == 0x14) {
+		up->port.type = PORT_16850;
+		return;
+	}
+
+	/*
+	 * It wasn't an XR16C850.
+	 *
+	 * We distinguish between the '654 and the '650 by counting
+	 * how many bytes are in the FIFO.  I'm using this for now,
+	 * since that's the technique that was sent to me in the
+	 * serial driver update, but I'm not convinced this works.
+	 * I've had problems doing this in the past.  -TYT
+	 */
+	if (size_fifo(up) == 64)
+		up->port.type = PORT_16654;
+	else
+		up->port.type = PORT_16650V2;
+}
+
+/*
+ * We detected a chip without a FIFO.  Only two fall into
+ * this category - the original 8250 and the 16450.  The
+ * 16450 has a scratch register (accessible with LCR=0)
+ */
+static void autoconfig_8250(struct uart_8250_port *up)
+{
+	unsigned char scratch, status1, status2;
+
+	up->port.type = PORT_8250;
+
+	scratch = serial_in(up, UART_SCR);
+	serial_outp(up, UART_SCR, 0xa5);
+	status1 = serial_in(up, UART_SCR);
+	serial_outp(up, UART_SCR, 0x5a);
+	status2 = serial_in(up, UART_SCR);
+	serial_outp(up, UART_SCR, scratch);
+
+	if (status1 == 0xa5 && status2 == 0x5a)
+		up->port.type = PORT_16450;
+}
+
+static int broken_efr(struct uart_8250_port *up)
+{
+	/*
+	 * Exar ST16C2550 "A2" devices incorrectly detect as
+	 * having an EFR, and report an ID of 0x0201.  See
+	 * http://www.exar.com/info.php?pdf=dan180_oct2004.pdf
+	 */
+	if (autoconfig_read_divisor_id(up) == 0x0201 && size_fifo(up) == 16)
+		return 1;
+
+	return 0;
+}
+
+/*
+ * We know that the chip has FIFOs.  Does it have an EFR?  The
+ * EFR is located in the same register position as the IIR and
+ * we know the top two bits of the IIR are currently set.  The
+ * EFR should contain zero.  Try to read the EFR.
+ */
+static void autoconfig_16550a(struct uart_8250_port *up)
+{
+	unsigned char status1, status2;
+	unsigned int iersave;
+
+	up->port.type = PORT_16550A;
+	up->capabilities |= UART_CAP_FIFO;
+
+	/*
+	 * Check for presence of the EFR when DLAB is set.
+	 * Only ST16C650V1 UARTs pass this test.
+	 */
+	serial_outp(up, UART_LCR, UART_LCR_DLAB);
+	if (serial_in(up, UART_EFR) == 0) {
+		serial_outp(up, UART_EFR, 0xA8);
+		if (serial_in(up, UART_EFR) != 0) {
+			DEBUG_AUTOCONF("EFRv1 ");
+			up->port.type = PORT_16650;
+			up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP;
+		} else {
+			DEBUG_AUTOCONF("Motorola 8xxx DUART ");
+		}
+		serial_outp(up, UART_EFR, 0);
+		return;
+	}
+
+	/*
+	 * Maybe it requires 0xbf to be written to the LCR.
+	 * (other ST16C650V2 UARTs, TI16C752A, etc)
+	 */
+	serial_outp(up, UART_LCR, 0xBF);
+	if (serial_in(up, UART_EFR) == 0 && !broken_efr(up)) {
+		DEBUG_AUTOCONF("EFRv2 ");
+		autoconfig_has_efr(up);
+		return;
+	}
+
+	/*
+	 * Check for a National Semiconductor SuperIO chip.
+	 * Attempt to switch to bank 2, read the value of the LOOP bit
+	 * from EXCR1. Switch back to bank 0, change it in MCR. Then
+	 * switch back to bank 2, read it from EXCR1 again and check
+	 * it's changed. If so, set baud_base in EXCR2 to 921600. -- dwmw2
+	 */
+	serial_outp(up, UART_LCR, 0);
+	status1 = serial_in(up, UART_MCR);
+	serial_outp(up, UART_LCR, 0xE0);
+	status2 = serial_in(up, 0x02); /* EXCR1 */
+
+	if (!((status2 ^ status1) & UART_MCR_LOOP)) {
+		serial_outp(up, UART_LCR, 0);
+		serial_outp(up, UART_MCR, status1 ^ UART_MCR_LOOP);
+		serial_outp(up, UART_LCR, 0xE0);
+		status2 = serial_in(up, 0x02); /* EXCR1 */
+		serial_outp(up, UART_LCR, 0);
+		serial_outp(up, UART_MCR, status1);
+
+		if ((status2 ^ status1) & UART_MCR_LOOP) {
+			unsigned short quot;
+
+			serial_outp(up, UART_LCR, 0xE0);
+
+			quot = serial_inp(up, UART_DLM) << 8;
+			quot += serial_inp(up, UART_DLL);
+			quot <<= 3;
+
+			status1 = serial_in(up, 0x04); /* EXCR1 */
+			status1 &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */
+			status1 |= 0x10;  /* 1.625 divisor for baud_base --> 921600 */
+			serial_outp(up, 0x04, status1);
+			
+			serial_outp(up, UART_DLL, quot & 0xff);
+			serial_outp(up, UART_DLM, quot >> 8);
+
+			serial_outp(up, UART_LCR, 0);
+
+			up->port.uartclk = 921600*16;
+			up->port.type = PORT_NS16550A;
+			up->capabilities |= UART_NATSEMI;
+			return;
+		}
+	}
+
+	/*
+	 * No EFR.  Try to detect a TI16750, which only sets bit 5 of
+	 * the IIR when 64 byte FIFO mode is enabled when DLAB is set.
+	 * Try setting it with and without DLAB set.  Cheap clones
+	 * set bit 5 without DLAB set.
+	 */
+	serial_outp(up, UART_LCR, 0);
+	serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
+	status1 = serial_in(up, UART_IIR) >> 5;
+	serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+	serial_outp(up, UART_LCR, UART_LCR_DLAB);
+	serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
+	status2 = serial_in(up, UART_IIR) >> 5;
+	serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+	serial_outp(up, UART_LCR, 0);
+
+	DEBUG_AUTOCONF("iir1=%d iir2=%d ", status1, status2);
+
+	if (status1 == 6 && status2 == 7) {
+		up->port.type = PORT_16750;
+		up->capabilities |= UART_CAP_AFE | UART_CAP_SLEEP;
+		return;
+	}
+
+	/*
+	 * Try writing and reading the UART_IER_UUE bit (b6).
+	 * If it works, this is probably one of the Xscale platform's
+	 * internal UARTs.
+	 * We're going to explicitly set the UUE bit to 0 before
+	 * trying to write and read a 1 just to make sure it's not
+	 * already a 1 and maybe locked there before we even start start.
+	 */
+	iersave = serial_in(up, UART_IER);
+	serial_outp(up, UART_IER, iersave & ~UART_IER_UUE);
+	if (!(serial_in(up, UART_IER) & UART_IER_UUE)) {
+		/*
+		 * OK it's in a known zero state, try writing and reading
+		 * without disturbing the current state of the other bits.
+		 */
+		serial_outp(up, UART_IER, iersave | UART_IER_UUE);
+		if (serial_in(up, UART_IER) & UART_IER_UUE) {
+			/*
+			 * It's an Xscale.
+			 * We'll leave the UART_IER_UUE bit set to 1 (enabled).
+			 */
+			DEBUG_AUTOCONF("Xscale ");
+			up->port.type = PORT_XSCALE;
+			up->capabilities |= UART_CAP_UUE;
+			return;
+		}
+	} else {
+		/*
+		 * If we got here we couldn't force the IER_UUE bit to 0.
+		 * Log it and continue.
+		 */
+		DEBUG_AUTOCONF("Couldn't force IER_UUE to 0 ");
+	}
+	serial_outp(up, UART_IER, iersave);
+}
+
+/*
+ * This routine is called by rs_init() to initialize a specific serial
+ * port.  It determines what type of UART chip this serial port is
+ * using: 8250, 16450, 16550, 16550A.  The important question is
+ * whether or not this UART is a 16550A or not, since this will
+ * determine whether or not we can use its FIFO features or not.
+ */
+static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
+{
+	unsigned char status1, scratch, scratch2, scratch3;
+	unsigned char save_lcr, save_mcr;
+	unsigned long flags;
+
+	if (!up->port.iobase && !up->port.mapbase && !up->port.membase)
+		return;
+
+	DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04x, 0x%p): ",
+			up->port.line, up->port.iobase, up->port.membase);
+
+	/*
+	 * We really do need global IRQs disabled here - we're going to
+	 * be frobbing the chips IRQ enable register to see if it exists.
+	 */
+	spin_lock_irqsave(&up->port.lock, flags);
+//	save_flags(flags); cli();
+
+	up->capabilities = 0;
+	up->bugs = 0;
+
+	if (!(up->port.flags & UPF_BUGGY_UART)) {
+		/*
+		 * Do a simple existence test first; if we fail this,
+		 * there's no point trying anything else.
+		 * 
+		 * 0x80 is used as a nonsense port to prevent against
+		 * false positives due to ISA bus float.  The
+		 * assumption is that 0x80 is a non-existent port;
+		 * which should be safe since include/asm/io.h also
+		 * makes this assumption.
+		 *
+		 * Note: this is safe as long as MCR bit 4 is clear
+		 * and the device is in "PC" mode.
+		 */
+		scratch = serial_inp(up, UART_IER);
+		serial_outp(up, UART_IER, 0);
+#ifdef __i386__
+		outb(0xff, 0x080);
+#endif
+		scratch2 = serial_inp(up, UART_IER);
+		serial_outp(up, UART_IER, 0x0F);
+#ifdef __i386__
+		outb(0, 0x080);
+#endif
+		scratch3 = serial_inp(up, UART_IER);
+		serial_outp(up, UART_IER, scratch);
+		if (scratch2 != 0 || scratch3 != 0x0F) {
+			/*
+			 * We failed; there's nothing here
+			 */
+			DEBUG_AUTOCONF("IER test failed (%02x, %02x) ",
+				       scratch2, scratch3);
+			goto out;
+		}
+	}
+
+	save_mcr = serial_in(up, UART_MCR);
+	save_lcr = serial_in(up, UART_LCR);
+
+	/* 
+	 * Check to see if a UART is really there.  Certain broken
+	 * internal modems based on the Rockwell chipset fail this
+	 * test, because they apparently don't implement the loopback
+	 * test mode.  So this test is skipped on the COM 1 through
+	 * COM 4 ports.  This *should* be safe, since no board
+	 * manufacturer would be stupid enough to design a board
+	 * that conflicts with COM 1-4 --- we hope!
+	 */
+	if (!(up->port.flags & UPF_SKIP_TEST)) {
+		serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A);
+		status1 = serial_inp(up, UART_MSR) & 0xF0;
+		serial_outp(up, UART_MCR, save_mcr);
+		if (status1 != 0x90) {
+			DEBUG_AUTOCONF("LOOP test failed (%02x) ",
+				       status1);
+			goto out;
+		}
+	}
+
+	/*
+	 * We're pretty sure there's a port here.  Lets find out what
+	 * type of port it is.  The IIR top two bits allows us to find
+	 * out if it's 8250 or 16450, 16550, 16550A or later.  This
+	 * determines what we test for next.
+	 *
+	 * We also initialise the EFR (if any) to zero for later.  The
+	 * EFR occupies the same register location as the FCR and IIR.
+	 */
+	serial_outp(up, UART_LCR, 0xBF);
+	serial_outp(up, UART_EFR, 0);
+	serial_outp(up, UART_LCR, 0);
+
+	serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+	scratch = serial_in(up, UART_IIR) >> 6;
+
+	DEBUG_AUTOCONF("iir=%d ", scratch);
+
+	switch (scratch) {
+	case 0:
+		autoconfig_8250(up);
+		break;
+	case 1:
+		up->port.type = PORT_UNKNOWN;
+		break;
+	case 2:
+		up->port.type = PORT_16550;
+		break;
+	case 3:
+		autoconfig_16550a(up);
+		break;
+	}
+
+#ifdef CONFIG_SERIAL_8250_RSA
+	/*
+	 * Only probe for RSA ports if we got the region.
+	 */
+	if (up->port.type == PORT_16550A && probeflags & PROBE_RSA) {
+		int i;
+
+		for (i = 0 ; i < probe_rsa_count; ++i) {
+			if (probe_rsa[i] == up->port.iobase &&
+			    __enable_rsa(up)) {
+				up->port.type = PORT_RSA;
+				break;
+			}
+		}
+	}
+#endif
+
+#ifdef CONFIG_SERIAL_8250_AU1X00
+	/* if access method is AU, it is a 16550 with a quirk */
+	if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU)
+		up->bugs |= UART_BUG_NOMSR;
+#endif
+
+	serial_outp(up, UART_LCR, save_lcr);
+
+	if (up->capabilities != uart_config[up->port.type].flags) {
+		printk(KERN_WARNING
+		       "ttyS%d: detected caps %08x should be %08x\n",
+			up->port.line, up->capabilities,
+			uart_config[up->port.type].flags);
+	}
+
+	up->port.fifosize = uart_config[up->port.type].fifo_size;
+	up->capabilities = uart_config[up->port.type].flags;
+	up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
+
+	if (up->port.type == PORT_UNKNOWN)
+		goto out;
+
+	/*
+	 * Reset the UART.
+	 */
+#ifdef CONFIG_SERIAL_8250_RSA
+	if (up->port.type == PORT_RSA)
+		serial_outp(up, UART_RSA_FRR, 0);
+#endif
+	serial_outp(up, UART_MCR, save_mcr);
+	serial8250_clear_fifos(up);
+	(void)serial_in(up, UART_RX);
+	if (up->capabilities & UART_CAP_UUE)
+		serial_outp(up, UART_IER, UART_IER_UUE);
+	else
+		serial_outp(up, UART_IER, 0);
+
+ out:	
+	spin_unlock_irqrestore(&up->port.lock, flags);
+//	restore_flags(flags);
+	DEBUG_AUTOCONF("type=%s\n", uart_config[up->port.type].name);
+}
+
+static void autoconfig_irq(struct uart_8250_port *up)
+{
+	unsigned char save_mcr, save_ier;
+	unsigned char save_ICP = 0;
+	unsigned int ICP = 0;
+	unsigned long irqs;
+	int irq;
+
+	if (up->port.flags & UPF_FOURPORT) {
+		ICP = (up->port.iobase & 0xfe0) | 0x1f;
+		save_ICP = inb_p(ICP);
+		outb_p(0x80, ICP);
+		(void) inb_p(ICP);
+	}
+
+	/* forget possible initially masked and pending IRQ */
+	probe_irq_off(probe_irq_on());
+	save_mcr = serial_inp(up, UART_MCR);
+	save_ier = serial_inp(up, UART_IER);
+	serial_outp(up, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2);
+	
+	irqs = probe_irq_on();
+	serial_outp(up, UART_MCR, 0);
+	udelay (10);
+	if (up->port.flags & UPF_FOURPORT)  {
+		serial_outp(up, UART_MCR,
+			    UART_MCR_DTR | UART_MCR_RTS);
+	} else {
+		serial_outp(up, UART_MCR,
+			    UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
+	}
+	serial_outp(up, UART_IER, 0x0f);	/* enable all intrs */
+	(void)serial_inp(up, UART_LSR);
+	(void)serial_inp(up, UART_RX);
+	(void)serial_inp(up, UART_IIR);
+	(void)serial_inp(up, UART_MSR);
+	serial_outp(up, UART_TX, 0xFF);
+	udelay (20);
+	irq = probe_irq_off(irqs);
+
+	serial_outp(up, UART_MCR, save_mcr);
+	serial_outp(up, UART_IER, save_ier);
+
+	if (up->port.flags & UPF_FOURPORT)
+		outb_p(save_ICP, ICP);
+
+	up->port.irq = (irq > 0) ? irq : 0;
+}
+
+static inline void __stop_tx(struct uart_8250_port *p)
+{
+	if (p->ier & UART_IER_THRI) {
+		p->ier &= ~UART_IER_THRI;
+		serial_out(p, UART_IER, p->ier);
+	}
+}
+
+static void serial8250_stop_tx(struct uart_port *port)
+{
+	struct uart_8250_port *up = (struct uart_8250_port *)port;
+
+	__stop_tx(up);
+
+	/*
+	 * We really want to stop the transmitter from sending.
+	 */
+	if (up->port.type == PORT_16C950) {
+		up->acr |= UART_ACR_TXDIS;
+		serial_icr_write(up, UART_ACR, up->acr);
+	}
+}
+
+static void transmit_chars(struct uart_8250_port *up);
+
+static void serial8250_start_tx(struct uart_port *port)
+{
+	struct uart_8250_port *up = (struct uart_8250_port *)port;
+
+	if (!(up->ier & UART_IER_THRI)) {
+		up->ier |= UART_IER_THRI;
+		serial_out(up, UART_IER, up->ier);
+
+		if (up->bugs & UART_BUG_TXEN) {
+			unsigned char lsr, iir;
+			lsr = serial_in(up, UART_LSR);
+			iir = serial_in(up, UART_IIR);
+			if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT)
+				transmit_chars(up);
+		}
+	}
+
+	/*
+	 * Re-enable the transmitter if we disabled it.
+	 */
+	if (up->port.type == PORT_16C950 && up->acr & UART_ACR_TXDIS) {
+		up->acr &= ~UART_ACR_TXDIS;
+		serial_icr_write(up, UART_ACR, up->acr);
+	}
+}
+
+static void serial8250_stop_rx(struct uart_port *port)
+{
+	struct uart_8250_port *up = (struct uart_8250_port *)port;
+
+	up->ier &= ~UART_IER_RLSI;
+	up->port.read_status_mask &= ~UART_LSR_DR;
+	serial_out(up, UART_IER, up->ier);
+}
+
+static void serial8250_enable_ms(struct uart_port *port)
+{
+	struct uart_8250_port *up = (struct uart_8250_port *)port;
+
+	/* no MSR capabilities */
+	if (up->bugs & UART_BUG_NOMSR)
+		return;
+
+	up->ier |= UART_IER_MSI;
+	serial_out(up, UART_IER, up->ier);
+}
+
+static _INLINE_ void
+receive_chars(struct uart_8250_port *up, int *status, struct pt_regs *regs)
+{
+	struct tty_struct *tty = up->port.info->tty;
+	unsigned char ch, lsr = *status;
+	int max_count = 256;
+	char flag;
+
+	do {
+		/* The following is not allowed by the tty layer and
+		   unsafe. It should be fixed ASAP */
+		if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
+			if (tty->low_latency) {
+				spin_unlock(&up->port.lock);
+				tty_flip_buffer_push(tty);
+				spin_lock(&up->port.lock);
+			}
+			/*
+			 * If this failed then we will throw away the
+			 * bytes but must do so to clear interrupts
+			 */
+		}
+		ch = serial_inp(up, UART_RX);
+		flag = TTY_NORMAL;
+		up->port.icount.rx++;
+
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+		/*
+		 * Recover the break flag from console xmit
+		 */
+		if (up->port.line == up->port.cons->index) {
+			lsr |= up->lsr_break_flag;
+			up->lsr_break_flag = 0;
+		}
+#endif
+
+		if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE |
+				    UART_LSR_FE | UART_LSR_OE))) {
+			/*
+			 * For statistics only
+			 */
+			if (lsr & UART_LSR_BI) {
+				lsr &= ~(UART_LSR_FE | UART_LSR_PE);
+				up->port.icount.brk++;
+				/*
+				 * We do the SysRQ and SAK checking
+				 * here because otherwise the break
+				 * may get masked by ignore_status_mask
+				 * or read_status_mask.
+				 */
+				if (uart_handle_break(&up->port))
+					goto ignore_char;
+			} else if (lsr & UART_LSR_PE)
+				up->port.icount.parity++;
+			else if (lsr & UART_LSR_FE)
+				up->port.icount.frame++;
+			if (lsr & UART_LSR_OE)
+				up->port.icount.overrun++;
+
+			/*
+			 * Mask off conditions which should be ignored.
+			 */
+			lsr &= up->port.read_status_mask;
+
+			if (lsr & UART_LSR_BI) {
+				DEBUG_INTR("handling break....");
+				flag = TTY_BREAK;
+			} else if (lsr & UART_LSR_PE)
+				flag = TTY_PARITY;
+			else if (lsr & UART_LSR_FE)
+				flag = TTY_FRAME;
+		}
+		if (uart_handle_sysrq_char(&up->port, ch, regs))
+			goto ignore_char;
+
+		uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag);
+
+	ignore_char:
+		lsr = serial_inp(up, UART_LSR);
+	} while ((lsr & UART_LSR_DR) && (max_count-- > 0));
+	spin_unlock(&up->port.lock);
+	tty_flip_buffer_push(tty);
+	spin_lock(&up->port.lock);
+	*status = lsr;
+}
+
+static _INLINE_ void transmit_chars(struct uart_8250_port *up)
+{
+	struct circ_buf *xmit = &up->port.info->xmit;
+	int count;
+
+	if (up->port.x_char) {
+		serial_outp(up, UART_TX, up->port.x_char);
+		up->port.icount.tx++;
+		up->port.x_char = 0;
+		return;
+	}
+	if (uart_tx_stopped(&up->port)) {
+		serial8250_stop_tx(&up->port);
+		return;
+	}
+	if (uart_circ_empty(xmit)) {
+		__stop_tx(up);
+		return;
+	}
+
+	count = up->tx_loadsz;
+	do {
+		serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		up->port.icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	} while (--count > 0);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+
+	DEBUG_INTR("THRE...");
+
+	if (uart_circ_empty(xmit))
+		__stop_tx(up);
+}
+
+static _INLINE_ void check_modem_status(struct uart_8250_port *up)
+{
+	int status;
+
+	status = serial_in(up, UART_MSR);
+
+	if ((status & UART_MSR_ANY_DELTA) == 0)
+		return;
+
+	if (status & UART_MSR_TERI)
+		up->port.icount.rng++;
+	if (status & UART_MSR_DDSR)
+		up->port.icount.dsr++;
+	if (status & UART_MSR_DDCD)
+		uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
+	if (status & UART_MSR_DCTS)
+		uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
+
+	wake_up_interruptible(&up->port.info->delta_msr_wait);
+}
+
+/*
+ * This handles the interrupt from one port.
+ */
+static inline void
+serial8250_handle_port(struct uart_8250_port *up, struct pt_regs *regs)
+{
+	unsigned int status = serial_inp(up, UART_LSR);
+
+	DEBUG_INTR("status = %x...", status);
+
+	if (status & UART_LSR_DR)
+		receive_chars(up, &status, regs);
+	check_modem_status(up);
+	if (status & UART_LSR_THRE)
+		transmit_chars(up);
+}
+
+/*
+ * This is the serial driver's interrupt routine.
+ *
+ * Arjan thinks the old way was overly complex, so it got simplified.
+ * Alan disagrees, saying that need the complexity to handle the weird
+ * nature of ISA shared interrupts.  (This is a special exception.)
+ *
+ * In order to handle ISA shared interrupts properly, we need to check
+ * that all ports have been serviced, and therefore the ISA interrupt
+ * line has been de-asserted.
+ *
+ * This means we need to loop through all ports. checking that they
+ * don't have an interrupt pending.
+ */
+static irqreturn_t serial8250_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct irq_info *i = dev_id;
+	struct list_head *l, *end = NULL;
+	int pass_counter = 0, handled = 0;
+
+	DEBUG_INTR("serial8250_interrupt(%d)...", irq);
+
+	spin_lock(&i->lock);
+
+	l = i->head;
+	do {
+		struct uart_8250_port *up;
+		unsigned int iir;
+
+		up = list_entry(l, struct uart_8250_port, list);
+
+		iir = serial_in(up, UART_IIR);
+		if (!(iir & UART_IIR_NO_INT)) {
+			spin_lock(&up->port.lock);
+			serial8250_handle_port(up, regs);
+			spin_unlock(&up->port.lock);
+
+			handled = 1;
+
+			end = NULL;
+		} else if (end == NULL)
+			end = l;
+
+		l = l->next;
+
+		if (l == i->head && pass_counter++ > PASS_LIMIT) {
+			/* If we hit this, we're dead. */
+			printk(KERN_ERR "serial8250: too much work for "
+				"irq%d\n", irq);
+			break;
+		}
+	} while (l != end);
+
+	spin_unlock(&i->lock);
+
+	DEBUG_INTR("end.\n");
+
+	return IRQ_RETVAL(handled);
+}
+
+/*
+ * To support ISA shared interrupts, we need to have one interrupt
+ * handler that ensures that the IRQ line has been deasserted
+ * before returning.  Failing to do this will result in the IRQ
+ * line being stuck active, and, since ISA irqs are edge triggered,
+ * no more IRQs will be seen.
+ */
+static void serial_do_unlink(struct irq_info *i, struct uart_8250_port *up)
+{
+	spin_lock_irq(&i->lock);
+
+	if (!list_empty(i->head)) {
+		if (i->head == &up->list)
+			i->head = i->head->next;
+		list_del(&up->list);
+	} else {
+		BUG_ON(i->head != &up->list);
+		i->head = NULL;
+	}
+
+	spin_unlock_irq(&i->lock);
+}
+
+static int serial_link_irq_chain(struct uart_8250_port *up)
+{
+	struct irq_info *i = irq_lists + up->port.irq;
+	int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? SA_SHIRQ : 0;
+
+	spin_lock_irq(&i->lock);
+
+	if (i->head) {
+		list_add(&up->list, i->head);
+		spin_unlock_irq(&i->lock);
+
+		ret = 0;
+	} else {
+		INIT_LIST_HEAD(&up->list);
+		i->head = &up->list;
+		spin_unlock_irq(&i->lock);
+
+		ret = request_irq(up->port.irq, serial8250_interrupt,
+				  irq_flags, "serial", i);
+		if (ret < 0)
+			serial_do_unlink(i, up);
+	}
+
+	return ret;
+}
+
+static void serial_unlink_irq_chain(struct uart_8250_port *up)
+{
+	struct irq_info *i = irq_lists + up->port.irq;
+
+	BUG_ON(i->head == NULL);
+
+	if (list_empty(i->head))
+		free_irq(up->port.irq, i);
+
+	serial_do_unlink(i, up);
+}
+
+/*
+ * This function is used to handle ports that do not have an
+ * interrupt.  This doesn't work very well for 16450's, but gives
+ * barely passable results for a 16550A.  (Although at the expense
+ * of much CPU overhead).
+ */
+static void serial8250_timeout(unsigned long data)
+{
+	struct uart_8250_port *up = (struct uart_8250_port *)data;
+	unsigned int timeout;
+	unsigned int iir;
+
+	iir = serial_in(up, UART_IIR);
+	if (!(iir & UART_IIR_NO_INT)) {
+		spin_lock(&up->port.lock);
+		serial8250_handle_port(up, NULL);
+		spin_unlock(&up->port.lock);
+	}
+
+	timeout = up->port.timeout;
+	timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
+	mod_timer(&up->timer, jiffies + timeout);
+}
+
+static unsigned int serial8250_tx_empty(struct uart_port *port)
+{
+	struct uart_8250_port *up = (struct uart_8250_port *)port;
+	unsigned long flags;
+	unsigned int ret;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return ret;
+}
+
+static unsigned int serial8250_get_mctrl(struct uart_port *port)
+{
+	struct uart_8250_port *up = (struct uart_8250_port *)port;
+	unsigned char status;
+	unsigned int ret;
+
+	status = serial_in(up, UART_MSR);
+
+	ret = 0;
+	if (status & UART_MSR_DCD)
+		ret |= TIOCM_CAR;
+	if (status & UART_MSR_RI)
+		ret |= TIOCM_RNG;
+	if (status & UART_MSR_DSR)
+		ret |= TIOCM_DSR;
+	if (status & UART_MSR_CTS)
+		ret |= TIOCM_CTS;
+	return ret;
+}
+
+static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_8250_port *up = (struct uart_8250_port *)port;
+	unsigned char mcr = 0;
+
+	if (mctrl & TIOCM_RTS)
+		mcr |= UART_MCR_RTS;
+	if (mctrl & TIOCM_DTR)
+		mcr |= UART_MCR_DTR;
+	if (mctrl & TIOCM_OUT1)
+		mcr |= UART_MCR_OUT1;
+	if (mctrl & TIOCM_OUT2)
+		mcr |= UART_MCR_OUT2;
+	if (mctrl & TIOCM_LOOP)
+		mcr |= UART_MCR_LOOP;
+
+	mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;
+
+	serial_out(up, UART_MCR, mcr);
+}
+
+static void serial8250_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_8250_port *up = (struct uart_8250_port *)port;
+	unsigned long flags;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	if (break_state == -1)
+		up->lcr |= UART_LCR_SBC;
+	else
+		up->lcr &= ~UART_LCR_SBC;
+	serial_out(up, UART_LCR, up->lcr);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static int serial8250_startup(struct uart_port *port)
+{
+	struct uart_8250_port *up = (struct uart_8250_port *)port;
+	unsigned long flags;
+	unsigned char lsr, iir;
+	int retval;
+
+	up->capabilities = uart_config[up->port.type].flags;
+	up->mcr = 0;
+
+	if (up->port.type == PORT_16C950) {
+		/* Wake up and initialize UART */
+		up->acr = 0;
+		serial_outp(up, UART_LCR, 0xBF);
+		serial_outp(up, UART_EFR, UART_EFR_ECB);
+		serial_outp(up, UART_IER, 0);
+		serial_outp(up, UART_LCR, 0);
+		serial_icr_write(up, UART_CSR, 0); /* Reset the UART */
+		serial_outp(up, UART_LCR, 0xBF);
+		serial_outp(up, UART_EFR, UART_EFR_ECB);
+		serial_outp(up, UART_LCR, 0);
+	}
+
+#ifdef CONFIG_SERIAL_8250_RSA
+	/*
+	 * If this is an RSA port, see if we can kick it up to the
+	 * higher speed clock.
+	 */
+	enable_rsa(up);
+#endif
+
+	/*
+	 * Clear the FIFO buffers and disable them.
+	 * (they will be reeanbled in set_termios())
+	 */
+	serial8250_clear_fifos(up);
+
+	/*
+	 * Clear the interrupt registers.
+	 */
+	(void) serial_inp(up, UART_LSR);
+	(void) serial_inp(up, UART_RX);
+	(void) serial_inp(up, UART_IIR);
+	(void) serial_inp(up, UART_MSR);
+
+	/*
+	 * At this point, there's no way the LSR could still be 0xff;
+	 * if it is, then bail out, because there's likely no UART
+	 * here.
+	 */
+	if (!(up->port.flags & UPF_BUGGY_UART) &&
+	    (serial_inp(up, UART_LSR) == 0xff)) {
+		printk("ttyS%d: LSR safety check engaged!\n", up->port.line);
+		return -ENODEV;
+	}
+
+	/*
+	 * For a XR16C850, we need to set the trigger levels
+	 */
+	if (up->port.type == PORT_16850) {
+		unsigned char fctr;
+
+		serial_outp(up, UART_LCR, 0xbf);
+
+		fctr = serial_inp(up, UART_FCTR) & ~(UART_FCTR_RX|UART_FCTR_TX);
+		serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_RX);
+		serial_outp(up, UART_TRG, UART_TRG_96);
+		serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_TX);
+		serial_outp(up, UART_TRG, UART_TRG_96);
+
+		serial_outp(up, UART_LCR, 0);
+	}
+
+	/*
+	 * If the "interrupt" for this port doesn't correspond with any
+	 * hardware interrupt, we use a timer-based system.  The original
+	 * driver used to do this with IRQ0.
+	 */
+	if (!is_real_interrupt(up->port.irq)) {
+		unsigned int timeout = up->port.timeout;
+
+		timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
+
+		up->timer.data = (unsigned long)up;
+		mod_timer(&up->timer, jiffies + timeout);
+	} else {
+		retval = serial_link_irq_chain(up);
+		if (retval)
+			return retval;
+	}
+
+	/*
+	 * Now, initialize the UART
+	 */
+	serial_outp(up, UART_LCR, UART_LCR_WLEN8);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	if (up->port.flags & UPF_FOURPORT) {
+		if (!is_real_interrupt(up->port.irq))
+			up->port.mctrl |= TIOCM_OUT1;
+	} else
+		/*
+		 * Most PC uarts need OUT2 raised to enable interrupts.
+		 */
+		if (is_real_interrupt(up->port.irq))
+			up->port.mctrl |= TIOCM_OUT2;
+
+	serial8250_set_mctrl(&up->port, up->port.mctrl);
+
+	/*
+	 * Do a quick test to see if we receive an
+	 * interrupt when we enable the TX irq.
+	 */
+	serial_outp(up, UART_IER, UART_IER_THRI);
+	lsr = serial_in(up, UART_LSR);
+	iir = serial_in(up, UART_IIR);
+	serial_outp(up, UART_IER, 0);
+
+	if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
+		if (!(up->bugs & UART_BUG_TXEN)) {
+			up->bugs |= UART_BUG_TXEN;
+			pr_debug("ttyS%d - enabling bad tx status workarounds\n",
+				 port->line);
+		}
+	} else {
+		up->bugs &= ~UART_BUG_TXEN;
+	}
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/*
+	 * Finally, enable interrupts.  Note: Modem status interrupts
+	 * are set via set_termios(), which will be occurring imminently
+	 * anyway, so we don't enable them here.
+	 */
+	up->ier = UART_IER_RLSI | UART_IER_RDI;
+	serial_outp(up, UART_IER, up->ier);
+
+	if (up->port.flags & UPF_FOURPORT) {
+		unsigned int icp;
+		/*
+		 * Enable interrupts on the AST Fourport board
+		 */
+		icp = (up->port.iobase & 0xfe0) | 0x01f;
+		outb_p(0x80, icp);
+		(void) inb_p(icp);
+	}
+
+	/*
+	 * And clear the interrupt registers again for luck.
+	 */
+	(void) serial_inp(up, UART_LSR);
+	(void) serial_inp(up, UART_RX);
+	(void) serial_inp(up, UART_IIR);
+	(void) serial_inp(up, UART_MSR);
+
+	return 0;
+}
+
+static void serial8250_shutdown(struct uart_port *port)
+{
+	struct uart_8250_port *up = (struct uart_8250_port *)port;
+	unsigned long flags;
+
+	/*
+	 * Disable interrupts from this port
+	 */
+	up->ier = 0;
+	serial_outp(up, UART_IER, 0);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	if (up->port.flags & UPF_FOURPORT) {
+		/* reset interrupts on the AST Fourport board */
+		inb((up->port.iobase & 0xfe0) | 0x1f);
+		up->port.mctrl |= TIOCM_OUT1;
+	} else
+		up->port.mctrl &= ~TIOCM_OUT2;
+
+	serial8250_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/*
+	 * Disable break condition and FIFOs
+	 */
+	serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC);
+	serial8250_clear_fifos(up);
+
+#ifdef CONFIG_SERIAL_8250_RSA
+	/*
+	 * Reset the RSA board back to 115kbps compat mode.
+	 */
+	disable_rsa(up);
+#endif
+
+	/*
+	 * Read data port to reset things, and then unlink from
+	 * the IRQ chain.
+	 */
+	(void) serial_in(up, UART_RX);
+
+	if (!is_real_interrupt(up->port.irq))
+		del_timer_sync(&up->timer);
+	else
+		serial_unlink_irq_chain(up);
+}
+
+static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud,
+					   unsigned int *prescaler)
+{
+        /*
+	 * Use special handling only if user did not supply its own divider.
+	 * spd_cust is defined in terms of baud_base, so always use default
+	 * prescaler when spd_cust is requested.
+	 */
+
+	*prescaler = 16;
+        if (baud != 38400 || (port->flags & UPF_SPD_MASK) != UPF_SPD_CUST) {
+		unsigned int quot = port->uartclk / baud;
+
+		/*
+		 * Handle magic divisors for baud rates above baud_base on
+		 * SMSC SuperIO chips.
+		 */
+		if (port->flags & UPF_MAGIC_MULTIPLIER) {
+			if (quot == 4) {
+				return 0x8001;
+			} else if (quot == 8) {
+				return 0x8002;
+			}
+		}
+		if (port->type == PORT_16C950) {
+			/*
+			 * This computes TCR value (4 to 16), not CPR value (which can
+			 * be between 1.000 and 31.875) - chip I have uses XTAL of
+			 * 806400Hz, and so a division by 7 is required to get 115200Bd.
+			 * I'm leaving CPR disabled for now, until someone will
+			 * hit even more exotic XTAL (it is needed to get 500kbps
+			 * or 1000kbps from 18.432MHz XTAL, but I have no device
+			 * which would benefit from doing that).
+			 *
+			 * If we can use divide by 16, use it.  Otherwise look for
+			 * better prescaler, from 15 to 4.  If quotient cannot
+			 * be divided by any integer value between 4 and 15, use 4.
+			 */
+			if (quot & 0x0F) {
+				unsigned int div;
+
+				for (div = 15; div > 4; div--) {
+					if (quot % div == 0) {
+						break;
+					}
+				}
+				*prescaler = div;
+				return quot / div;
+			}
+		}
+	}
+	return uart_get_divisor(port, baud);
+}
+
+static void
+serial8250_set_termios(struct uart_port *port, struct termios *termios,
+		       struct termios *old)
+{
+	struct uart_8250_port *up = (struct uart_8250_port *)port;
+	unsigned char cval, fcr = 0;
+	unsigned long flags;
+	unsigned int baud, quot, prescaler;
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		cval = UART_LCR_WLEN5;
+		break;
+	case CS6:
+		cval = UART_LCR_WLEN6;
+		break;
+	case CS7:
+		cval = UART_LCR_WLEN7;
+		break;
+	default:
+	case CS8:
+		cval = UART_LCR_WLEN8;
+		break;
+	}
+
+	if (termios->c_cflag & CSTOPB)
+		cval |= UART_LCR_STOP;
+	if (termios->c_cflag & PARENB)
+		cval |= UART_LCR_PARITY;
+	if (!(termios->c_cflag & PARODD))
+		cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+	if (termios->c_cflag & CMSPAR)
+		cval |= UART_LCR_SPAR;
+#endif
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+
+	if (port->type == PORT_16C950 || (port->flags & UPF_MAGIC_MULTIPLIER)) {
+		baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/4);
+	} else {
+		baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+	}
+	quot = serial8250_get_divisor(port, baud, &prescaler);
+
+	/*
+	 * Oxford Semi 952 rev B workaround
+	 */
+	if (up->bugs & UART_BUG_QUOT && (quot & 0xff) == 0)
+		quot ++;
+
+	if (up->capabilities & UART_CAP_FIFO && up->port.fifosize > 1) {
+		if (baud < 2400)
+			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+		else
+			fcr = uart_config[up->port.type].fcr;
+	}
+
+	/*
+	 * MCR-based auto flow control.  When AFE is enabled, RTS will be
+	 * deasserted when the receive FIFO contains more characters than
+	 * the trigger, or the MCR RTS bit is cleared.  In the case where
+	 * the remote UART is not using CTS auto flow control, we must
+	 * have sufficient FIFO entries for the latency of the remote
+	 * UART to respond.  IOW, at least 32 bytes of FIFO.
+	 */
+	if (up->capabilities & UART_CAP_AFE && up->port.fifosize >= 32) {
+		up->mcr &= ~UART_MCR_AFE;
+		if (termios->c_cflag & CRTSCTS)
+			up->mcr |= UART_MCR_AFE;
+	}
+
+	/*
+	 * Ok, we're now changing the port state.  Do it with
+	 * interrupts disabled.
+	 */
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+	if (termios->c_iflag & INPCK)
+		up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		up->port.read_status_mask |= UART_LSR_BI;
+
+	/*
+	 * Characteres to ignore
+	 */
+	up->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+	if (termios->c_iflag & IGNBRK) {
+		up->port.ignore_status_mask |= UART_LSR_BI;
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			up->port.ignore_status_mask |= UART_LSR_OE;
+	}
+
+	/*
+	 * ignore all characters if CREAD is not set
+	 */
+	if ((termios->c_cflag & CREAD) == 0)
+		up->port.ignore_status_mask |= UART_LSR_DR;
+
+	/*
+	 * CTS flow control flag and modem status interrupts
+	 */
+	up->ier &= ~UART_IER_MSI;
+	if (!(up->bugs & UART_BUG_NOMSR) &&
+			UART_ENABLE_MS(&up->port, termios->c_cflag))
+		up->ier |= UART_IER_MSI;
+	if (up->capabilities & UART_CAP_UUE)
+		up->ier |= UART_IER_UUE | UART_IER_RTOIE;
+
+	serial_out(up, UART_IER, up->ier);
+
+	if (up->capabilities & UART_CAP_EFR) {
+		unsigned char efr = 0;
+		/*
+		 * TI16C752/Startech hardware flow control.  FIXME:
+		 * - TI16C752 requires control thresholds to be set.
+		 * - UART_MCR_RTS is ineffective if auto-RTS mode is enabled.
+		 */
+		if (termios->c_cflag & CRTSCTS)
+			efr |= UART_EFR_CTS;
+
+		serial_outp(up, UART_LCR, 0xBF);
+		serial_outp(up, UART_EFR, efr);
+	}
+
+	if (up->capabilities & UART_NATSEMI) {
+		/* Switch to bank 2 not bank 1, to avoid resetting EXCR2 */
+		serial_outp(up, UART_LCR, 0xe0);
+	} else {
+		serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
+	}
+
+	serial_outp(up, UART_DLL, quot & 0xff);		/* LS of divisor */
+	serial_outp(up, UART_DLM, quot >> 8);		/* MS of divisor */
+
+	/*
+	 * Program prescaler for 16C950 chips.
+	 */
+	if (up->port.type == PORT_16C950) {
+		serial_icr_write(up, UART_TCR, prescaler == 16 ? 0 : prescaler);
+	}
+
+	/*
+	 * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
+	 * is written without DLAB set, this mode will be disabled.
+	 */
+	if (up->port.type == PORT_16750)
+		serial_outp(up, UART_FCR, fcr);
+
+	serial_outp(up, UART_LCR, cval);		/* reset DLAB */
+	up->lcr = cval;					/* Save LCR */
+	if (up->port.type != PORT_16750) {
+		if (fcr & UART_FCR_ENABLE_FIFO) {
+			/* emulated UARTs (Lucent Venus 167x) need two steps */
+			serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+		}
+		serial_outp(up, UART_FCR, fcr);		/* set fcr */
+	}
+	serial8250_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void
+serial8250_pm(struct uart_port *port, unsigned int state,
+	      unsigned int oldstate)
+{
+	struct uart_8250_port *p = (struct uart_8250_port *)port;
+
+	serial8250_set_sleep(p, state != 0);
+
+	if (p->pm)
+		p->pm(port, state, oldstate);
+}
+
+/*
+ * Resource handling.
+ */
+static int serial8250_request_std_resource(struct uart_8250_port *up)
+{
+	unsigned int size = 8 << up->port.regshift;
+	int ret = 0;
+
+	switch (up->port.iotype) {
+	case UPIO_MEM:
+		if (!up->port.mapbase)
+			break;
+
+		if (!request_mem_region(up->port.mapbase, size, "serial")) {
+			ret = -EBUSY;
+			break;
+		}
+
+		if (up->port.flags & UPF_IOREMAP) {
+			up->port.membase = ioremap(up->port.mapbase, size);
+			if (!up->port.membase) {
+				release_mem_region(up->port.mapbase, size);
+				ret = -ENOMEM;
+			}
+		}
+		break;
+
+	case UPIO_HUB6:
+	case UPIO_PORT:
+		if (!request_region(up->port.iobase, size, "serial"))
+			ret = -EBUSY;
+		break;
+	}
+	return ret;
+}
+
+static void serial8250_release_std_resource(struct uart_8250_port *up)
+{
+	unsigned int size = 8 << up->port.regshift;
+
+	switch (up->port.iotype) {
+	case UPIO_MEM:
+		if (!up->port.mapbase)
+			break;
+
+		if (up->port.flags & UPF_IOREMAP) {
+			iounmap(up->port.membase);
+			up->port.membase = NULL;
+		}
+
+		release_mem_region(up->port.mapbase, size);
+		break;
+
+	case UPIO_HUB6:
+	case UPIO_PORT:
+		release_region(up->port.iobase, size);
+		break;
+	}
+}
+
+static int serial8250_request_rsa_resource(struct uart_8250_port *up)
+{
+	unsigned long start = UART_RSA_BASE << up->port.regshift;
+	unsigned int size = 8 << up->port.regshift;
+	int ret = 0;
+
+	switch (up->port.iotype) {
+	case UPIO_MEM:
+		ret = -EINVAL;
+		break;
+
+	case UPIO_HUB6:
+	case UPIO_PORT:
+		start += up->port.iobase;
+		if (!request_region(start, size, "serial-rsa"))
+			ret = -EBUSY;
+		break;
+	}
+
+	return ret;
+}
+
+static void serial8250_release_rsa_resource(struct uart_8250_port *up)
+{
+	unsigned long offset = UART_RSA_BASE << up->port.regshift;
+	unsigned int size = 8 << up->port.regshift;
+
+	switch (up->port.iotype) {
+	case UPIO_MEM:
+		break;
+
+	case UPIO_HUB6:
+	case UPIO_PORT:
+		release_region(up->port.iobase + offset, size);
+		break;
+	}
+}
+
+static void serial8250_release_port(struct uart_port *port)
+{
+	struct uart_8250_port *up = (struct uart_8250_port *)port;
+
+	serial8250_release_std_resource(up);
+	if (up->port.type == PORT_RSA)
+		serial8250_release_rsa_resource(up);
+}
+
+static int serial8250_request_port(struct uart_port *port)
+{
+	struct uart_8250_port *up = (struct uart_8250_port *)port;
+	int ret = 0;
+
+	ret = serial8250_request_std_resource(up);
+	if (ret == 0 && up->port.type == PORT_RSA) {
+		ret = serial8250_request_rsa_resource(up);
+		if (ret < 0)
+			serial8250_release_std_resource(up);
+	}
+
+	return ret;
+}
+
+static void serial8250_config_port(struct uart_port *port, int flags)
+{
+	struct uart_8250_port *up = (struct uart_8250_port *)port;
+	int probeflags = PROBE_ANY;
+	int ret;
+
+	/*
+	 * Don't probe for MCA ports on non-MCA machines.
+	 */
+	if (up->port.flags & UPF_BOOT_ONLYMCA && !MCA_bus)
+		return;
+
+	/*
+	 * Find the region that we can probe for.  This in turn
+	 * tells us whether we can probe for the type of port.
+	 */
+	ret = serial8250_request_std_resource(up);
+	if (ret < 0)
+		return;
+
+	ret = serial8250_request_rsa_resource(up);
+	if (ret < 0)
+		probeflags &= ~PROBE_RSA;
+
+	if (flags & UART_CONFIG_TYPE)
+		autoconfig(up, probeflags);
+	if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ)
+		autoconfig_irq(up);
+
+	if (up->port.type != PORT_RSA && probeflags & PROBE_RSA)
+		serial8250_release_rsa_resource(up);
+	if (up->port.type == PORT_UNKNOWN)
+		serial8250_release_std_resource(up);
+}
+
+static int
+serial8250_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	if (ser->irq >= NR_IRQS || ser->irq < 0 ||
+	    ser->baud_base < 9600 || ser->type < PORT_UNKNOWN ||
+	    ser->type >= ARRAY_SIZE(uart_config) || ser->type == PORT_CIRRUS ||
+	    ser->type == PORT_STARTECH)
+		return -EINVAL;
+	return 0;
+}
+
+static const char *
+serial8250_type(struct uart_port *port)
+{
+	int type = port->type;
+
+	if (type >= ARRAY_SIZE(uart_config))
+		type = 0;
+	return uart_config[type].name;
+}
+
+static struct uart_ops serial8250_pops = {
+	.tx_empty	= serial8250_tx_empty,
+	.set_mctrl	= serial8250_set_mctrl,
+	.get_mctrl	= serial8250_get_mctrl,
+	.stop_tx	= serial8250_stop_tx,
+	.start_tx	= serial8250_start_tx,
+	.stop_rx	= serial8250_stop_rx,
+	.enable_ms	= serial8250_enable_ms,
+	.break_ctl	= serial8250_break_ctl,
+	.startup	= serial8250_startup,
+	.shutdown	= serial8250_shutdown,
+	.set_termios	= serial8250_set_termios,
+	.pm		= serial8250_pm,
+	.type		= serial8250_type,
+	.release_port	= serial8250_release_port,
+	.request_port	= serial8250_request_port,
+	.config_port	= serial8250_config_port,
+	.verify_port	= serial8250_verify_port,
+};
+
+static struct uart_8250_port serial8250_ports[UART_NR];
+
+static void __init serial8250_isa_init_ports(void)
+{
+	struct uart_8250_port *up;
+	static int first = 1;
+	int i;
+
+	if (!first)
+		return;
+	first = 0;
+
+	for (i = 0; i < UART_NR; i++) {
+		struct uart_8250_port *up = &serial8250_ports[i];
+
+		up->port.line = i;
+		spin_lock_init(&up->port.lock);
+
+		init_timer(&up->timer);
+		up->timer.function = serial8250_timeout;
+
+		/*
+		 * ALPHA_KLUDGE_MCR needs to be killed.
+		 */
+		up->mcr_mask = ~ALPHA_KLUDGE_MCR;
+		up->mcr_force = ALPHA_KLUDGE_MCR;
+
+		up->port.ops = &serial8250_pops;
+	}
+
+	for (i = 0, up = serial8250_ports;
+	     i < ARRAY_SIZE(old_serial_port) && i < UART_NR;
+	     i++, up++) {
+		up->port.iobase   = old_serial_port[i].port;
+		up->port.irq      = irq_canonicalize(old_serial_port[i].irq);
+		up->port.uartclk  = old_serial_port[i].baud_base * 16;
+		up->port.flags    = old_serial_port[i].flags;
+		up->port.hub6     = old_serial_port[i].hub6;
+		up->port.membase  = old_serial_port[i].iomem_base;
+		up->port.iotype   = old_serial_port[i].io_type;
+		up->port.regshift = old_serial_port[i].iomem_reg_shift;
+		if (share_irqs)
+			up->port.flags |= UPF_SHARE_IRQ;
+	}
+}
+
+static void __init
+serial8250_register_ports(struct uart_driver *drv, struct device *dev)
+{
+	int i;
+
+	serial8250_isa_init_ports();
+
+	for (i = 0; i < UART_NR; i++) {
+		struct uart_8250_port *up = &serial8250_ports[i];
+
+		up->port.dev = dev;
+		uart_add_one_port(drv, &up->port);
+	}
+}
+
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+/*
+ *	Wait for transmitter & holding register to empty
+ */
+static inline void wait_for_xmitr(struct uart_8250_port *up)
+{
+	unsigned int status, tmout = 10000;
+
+	/* Wait up to 10ms for the character(s) to be sent. */
+	do {
+		status = serial_in(up, UART_LSR);
+
+		if (status & UART_LSR_BI)
+			up->lsr_break_flag = UART_LSR_BI;
+
+		if (--tmout == 0)
+			break;
+		udelay(1);
+	} while ((status & BOTH_EMPTY) != BOTH_EMPTY);
+
+	/* Wait up to 1s for flow control if necessary */
+	if (up->port.flags & UPF_CONS_FLOW) {
+		tmout = 1000000;
+		while (--tmout &&
+		       ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
+			udelay(1);
+	}
+}
+
+/*
+ *	Print a string to the serial port trying not to disturb
+ *	any possible real use of the port...
+ *
+ *	The console_lock must be held when we get here.
+ */
+static void
+serial8250_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct uart_8250_port *up = &serial8250_ports[co->index];
+	unsigned int ier;
+	int i;
+
+	touch_nmi_watchdog();
+
+	/*
+	 *	First save the UER then disable the interrupts
+	 */
+	ier = serial_in(up, UART_IER);
+
+	if (up->capabilities & UART_CAP_UUE)
+		serial_out(up, UART_IER, UART_IER_UUE);
+	else
+		serial_out(up, UART_IER, 0);
+
+	/*
+	 *	Now, do each character
+	 */
+	for (i = 0; i < count; i++, s++) {
+		wait_for_xmitr(up);
+
+		/*
+		 *	Send the character out.
+		 *	If a LF, also do CR...
+		 */
+		serial_out(up, UART_TX, *s);
+		if (*s == 10) {
+			wait_for_xmitr(up);
+			serial_out(up, UART_TX, 13);
+		}
+	}
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and restore the IER
+	 */
+	wait_for_xmitr(up);
+	serial_out(up, UART_IER, ier);
+}
+
+static int serial8250_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	/*
+	 * Check whether an invalid uart number has been specified, and
+	 * if so, search for the first available port that does have
+	 * console support.
+	 */
+	if (co->index >= UART_NR)
+		co->index = 0;
+	port = &serial8250_ports[co->index].port;
+	if (!port->iobase && !port->membase)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver serial8250_reg;
+static struct console serial8250_console = {
+	.name		= "ttyS",
+	.write		= serial8250_console_write,
+	.device		= uart_console_device,
+	.setup		= serial8250_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &serial8250_reg,
+};
+
+static int __init serial8250_console_init(void)
+{
+	serial8250_isa_init_ports();
+	register_console(&serial8250_console);
+	return 0;
+}
+console_initcall(serial8250_console_init);
+
+static int __init find_port(struct uart_port *p)
+{
+	int line;
+	struct uart_port *port;
+
+	for (line = 0; line < UART_NR; line++) {
+		port = &serial8250_ports[line].port;
+		if (p->iotype == port->iotype &&
+		    p->iobase == port->iobase &&
+		    p->membase == port->membase)
+			return line;
+	}
+	return -ENODEV;
+}
+
+int __init serial8250_start_console(struct uart_port *port, char *options)
+{
+	int line;
+
+	line = find_port(port);
+	if (line < 0)
+		return -ENODEV;
+
+	add_preferred_console("ttyS", line, options);
+	printk("Adding console on ttyS%d at %s 0x%lx (options '%s')\n",
+		line, port->iotype == UPIO_MEM ? "MMIO" : "I/O port",
+		port->iotype == UPIO_MEM ? (unsigned long) port->mapbase :
+		    (unsigned long) port->iobase, options);
+	if (!(serial8250_console.flags & CON_ENABLED)) {
+		serial8250_console.flags &= ~CON_PRINTBUFFER;
+		register_console(&serial8250_console);
+	}
+	return line;
+}
+
+#define SERIAL8250_CONSOLE	&serial8250_console
+#else
+#define SERIAL8250_CONSOLE	NULL
+#endif
+
+static struct uart_driver serial8250_reg = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "serial",
+	.devfs_name		= "tts/",
+	.dev_name		= "ttyS",
+	.major			= TTY_MAJOR,
+	.minor			= 64,
+	.nr			= UART_NR,
+	.cons			= SERIAL8250_CONSOLE,
+};
+
+int __init early_serial_setup(struct uart_port *port)
+{
+	if (port->line >= ARRAY_SIZE(serial8250_ports))
+		return -ENODEV;
+
+	serial8250_isa_init_ports();
+	serial8250_ports[port->line].port	= *port;
+	serial8250_ports[port->line].port.ops	= &serial8250_pops;
+	return 0;
+}
+
+/**
+ *	serial8250_suspend_port - suspend one serial port
+ *	@line:  serial line number
+ *      @level: the level of port suspension, as per uart_suspend_port
+ *
+ *	Suspend one serial port.
+ */
+void serial8250_suspend_port(int line)
+{
+	uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port);
+}
+
+/**
+ *	serial8250_resume_port - resume one serial port
+ *	@line:  serial line number
+ *      @level: the level of port resumption, as per uart_resume_port
+ *
+ *	Resume one serial port.
+ */
+void serial8250_resume_port(int line)
+{
+	uart_resume_port(&serial8250_reg, &serial8250_ports[line].port);
+}
+
+/*
+ * Register a set of serial devices attached to a platform device.  The
+ * list is terminated with a zero flags entry, which means we expect
+ * all entries to have at least UPF_BOOT_AUTOCONF set.
+ */
+static int __devinit serial8250_probe(struct platform_device *dev)
+{
+	struct plat_serial8250_port *p = dev->dev.platform_data;
+	struct uart_port port;
+	int ret, i;
+
+	memset(&port, 0, sizeof(struct uart_port));
+
+	for (i = 0; p && p->flags != 0; p++, i++) {
+		port.iobase	= p->iobase;
+		port.membase	= p->membase;
+		port.irq	= p->irq;
+		port.uartclk	= p->uartclk;
+		port.regshift	= p->regshift;
+		port.iotype	= p->iotype;
+		port.flags	= p->flags;
+		port.mapbase	= p->mapbase;
+		port.hub6	= p->hub6;
+		port.dev	= &dev->dev;
+		if (share_irqs)
+			port.flags |= UPF_SHARE_IRQ;
+		ret = serial8250_register_port(&port);
+		if (ret < 0) {
+			dev_err(&dev->dev, "unable to register port at index %d "
+				"(IO%lx MEM%lx IRQ%d): %d\n", i,
+				p->iobase, p->mapbase, p->irq, ret);
+		}
+	}
+	return 0;
+}
+
+/*
+ * Remove serial ports registered against a platform device.
+ */
+static int __devexit serial8250_remove(struct platform_device *dev)
+{
+	int i;
+
+	for (i = 0; i < UART_NR; i++) {
+		struct uart_8250_port *up = &serial8250_ports[i];
+
+		if (up->port.dev == &dev->dev)
+			serial8250_unregister_port(i);
+	}
+	return 0;
+}
+
+static int serial8250_suspend(struct platform_device *dev, pm_message_t state)
+{
+	int i;
+
+	for (i = 0; i < UART_NR; i++) {
+		struct uart_8250_port *up = &serial8250_ports[i];
+
+		if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev)
+			uart_suspend_port(&serial8250_reg, &up->port);
+	}
+
+	return 0;
+}
+
+static int serial8250_resume(struct platform_device *dev)
+{
+	int i;
+
+	for (i = 0; i < UART_NR; i++) {
+		struct uart_8250_port *up = &serial8250_ports[i];
+
+		if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev)
+			uart_resume_port(&serial8250_reg, &up->port);
+	}
+
+	return 0;
+}
+
+static struct platform_driver serial8250_isa_driver = {
+	.probe		= serial8250_probe,
+	.remove		= __devexit_p(serial8250_remove),
+	.suspend	= serial8250_suspend,
+	.resume		= serial8250_resume,
+	.driver		= {
+		.name	= "serial8250",
+	},
+};
+
+/*
+ * This "device" covers _all_ ISA 8250-compatible serial devices listed
+ * in the table in include/asm/serial.h
+ */
+static struct platform_device *serial8250_isa_devs;
+
+/*
+ * serial8250_register_port and serial8250_unregister_port allows for
+ * 16x50 serial ports to be configured at run-time, to support PCMCIA
+ * modems and PCI multiport cards.
+ */
+static DECLARE_MUTEX(serial_sem);
+
+static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *port)
+{
+	int i;
+
+	/*
+	 * First, find a port entry which matches.
+	 */
+	for (i = 0; i < UART_NR; i++)
+		if (uart_match_port(&serial8250_ports[i].port, port))
+			return &serial8250_ports[i];
+
+	/*
+	 * We didn't find a matching entry, so look for the first
+	 * free entry.  We look for one which hasn't been previously
+	 * used (indicated by zero iobase).
+	 */
+	for (i = 0; i < UART_NR; i++)
+		if (serial8250_ports[i].port.type == PORT_UNKNOWN &&
+		    serial8250_ports[i].port.iobase == 0)
+			return &serial8250_ports[i];
+
+	/*
+	 * That also failed.  Last resort is to find any entry which
+	 * doesn't have a real port associated with it.
+	 */
+	for (i = 0; i < UART_NR; i++)
+		if (serial8250_ports[i].port.type == PORT_UNKNOWN)
+			return &serial8250_ports[i];
+
+	return NULL;
+}
+
+/**
+ *	serial8250_register_port - register a serial port
+ *	@port: serial port template
+ *
+ *	Configure the serial port specified by the request. If the
+ *	port exists and is in use, it is hung up and unregistered
+ *	first.
+ *
+ *	The port is then probed and if necessary the IRQ is autodetected
+ *	If this fails an error is returned.
+ *
+ *	On success the port is ready to use and the line number is returned.
+ */
+int serial8250_register_port(struct uart_port *port)
+{
+	struct uart_8250_port *uart;
+	int ret = -ENOSPC;
+
+	if (port->uartclk == 0)
+		return -EINVAL;
+
+	down(&serial_sem);
+
+	uart = serial8250_find_match_or_unused(port);
+	if (uart) {
+		uart_remove_one_port(&serial8250_reg, &uart->port);
+
+		uart->port.iobase   = port->iobase;
+		uart->port.membase  = port->membase;
+		uart->port.irq      = port->irq;
+		uart->port.uartclk  = port->uartclk;
+		uart->port.fifosize = port->fifosize;
+		uart->port.regshift = port->regshift;
+		uart->port.iotype   = port->iotype;
+		uart->port.flags    = port->flags | UPF_BOOT_AUTOCONF;
+		uart->port.mapbase  = port->mapbase;
+		if (port->dev)
+			uart->port.dev = port->dev;
+
+		ret = uart_add_one_port(&serial8250_reg, &uart->port);
+		if (ret == 0)
+			ret = uart->port.line;
+	}
+	up(&serial_sem);
+
+	return ret;
+}
+EXPORT_SYMBOL(serial8250_register_port);
+
+/**
+ *	serial8250_unregister_port - remove a 16x50 serial port at runtime
+ *	@line: serial line number
+ *
+ *	Remove one serial port.  This may not be called from interrupt
+ *	context.  We hand the port back to the our control.
+ */
+void serial8250_unregister_port(int line)
+{
+	struct uart_8250_port *uart = &serial8250_ports[line];
+
+	down(&serial_sem);
+	uart_remove_one_port(&serial8250_reg, &uart->port);
+	if (serial8250_isa_devs) {
+		uart->port.flags &= ~UPF_BOOT_AUTOCONF;
+		uart->port.type = PORT_UNKNOWN;
+		uart->port.dev = &serial8250_isa_devs->dev;
+		uart_add_one_port(&serial8250_reg, &uart->port);
+	} else {
+		uart->port.dev = NULL;
+	}
+	up(&serial_sem);
+}
+EXPORT_SYMBOL(serial8250_unregister_port);
+
+static int __init serial8250_init(void)
+{
+	int ret, i;
+
+	printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.90 $ "
+		"%d ports, IRQ sharing %sabled\n", (int) UART_NR,
+		share_irqs ? "en" : "dis");
+
+	for (i = 0; i < NR_IRQS; i++)
+		spin_lock_init(&irq_lists[i].lock);
+
+	ret = uart_register_driver(&serial8250_reg);
+	if (ret)
+		goto out;
+
+	serial8250_isa_devs = platform_device_register_simple("serial8250",
+					 PLAT8250_DEV_LEGACY, NULL, 0);
+	if (IS_ERR(serial8250_isa_devs)) {
+		ret = PTR_ERR(serial8250_isa_devs);
+		goto unreg;
+	}
+
+	serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);
+
+	ret = platform_driver_register(&serial8250_isa_driver);
+	if (ret == 0)
+		goto out;
+
+	platform_device_unregister(serial8250_isa_devs);
+ unreg:
+	uart_unregister_driver(&serial8250_reg);
+ out:
+	return ret;
+}
+
+static void __exit serial8250_exit(void)
+{
+	struct platform_device *isa_dev = serial8250_isa_devs;
+
+	/*
+	 * This tells serial8250_unregister_port() not to re-register
+	 * the ports (thereby making serial8250_isa_driver permanently
+	 * in use.)
+	 */
+	serial8250_isa_devs = NULL;
+
+	platform_driver_unregister(&serial8250_isa_driver);
+	platform_device_unregister(isa_dev);
+
+	uart_unregister_driver(&serial8250_reg);
+}
+
+module_init(serial8250_init);
+module_exit(serial8250_exit);
+
+EXPORT_SYMBOL(serial8250_suspend_port);
+EXPORT_SYMBOL(serial8250_resume_port);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic 8250/16x50 serial driver $Revision: 1.90 $");
+
+module_param(share_irqs, uint, 0644);
+MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices"
+	" (unsafe)");
+
+#ifdef CONFIG_SERIAL_8250_RSA
+module_param_array(probe_rsa, ulong, &probe_rsa_count, 0444);
+MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA");
+#endif
+MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR);
diff -urN oldtree/drivers/serial/Makefile newtree/drivers/serial/Makefile
--- oldtree/drivers/serial/Makefile	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/serial/Makefile	2006-02-13 19:20:00.663411448 -0500
@@ -8,7 +8,7 @@
 serial-8250-$(CONFIG_SERIAL_8250_ACPI) += 8250_acpi.o
 serial-8250-$(CONFIG_PNP) += 8250_pnp.o
 serial-8250-$(CONFIG_GSC) += 8250_gsc.o
-serial-8250-$(CONFIG_PCI) += 8250_pci.o
+serial-8250-$(CONFIG_SERIAL_PCI) += 8250_pci.o
 serial-8250-$(CONFIG_HP300) += 8250_hp300.o
 
 obj-$(CONFIG_SERIAL_CORE) += serial_core.o
diff -urN oldtree/drivers/usb/gadget/net2280.c newtree/drivers/usb/gadget/net2280.c
--- oldtree/drivers/usb/gadget/net2280.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/usb/gadget/net2280.c	2006-02-13 19:20:00.664411296 -0500
@@ -47,6 +47,7 @@
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/pci.h>
+#include <linux/dma-mapping.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
 #include <linux/ioport.h>
diff -urN oldtree/drivers/usb/input/pid.c newtree/drivers/usb/input/pid.c
--- oldtree/drivers/usb/input/pid.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/usb/input/pid.c	2006-02-13 19:20:29.726993112 -0500
@@ -259,7 +259,7 @@
 int hid_pid_init(struct hid_device *hid)
 {
 	struct hid_ff_pid *private;
-	struct hid_input *hidinput = list_entry(&hid->inputs, struct hid_input, list);
+	struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
 	struct input_dev *input_dev = hidinput->input;
 
 	private = hid->ff_private = kzalloc(sizeof(struct hid_ff_pid), GFP_KERNEL);
diff -urN oldtree/drivers/video/aty/atyfb_base.c newtree/drivers/video/aty/atyfb_base.c
--- oldtree/drivers/video/aty/atyfb_base.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/video/aty/atyfb_base.c	2006-02-13 19:20:29.727992960 -0500
@@ -403,7 +403,7 @@
 	{ PCI_CHIP_MACH64GM, "3D RAGE XL (Mach64 GM, AGP)", 230, 83, 63, ATI_CHIP_264XL },
 	{ PCI_CHIP_MACH64GN, "3D RAGE XL (Mach64 GN, AGP)", 230, 83, 63, ATI_CHIP_264XL },
 	{ PCI_CHIP_MACH64GO, "3D RAGE XL (Mach64 GO, PCI-66/BGA)", 230, 83, 63, ATI_CHIP_264XL },
-	{ PCI_CHIP_MACH64GR, "3D RAGE XL (Mach64 GR, PCI-33MHz)", 230, 83, 63, ATI_CHIP_264XL },
+	{ PCI_CHIP_MACH64GR, "3D RAGE XL (Mach64 GR, PCI-33MHz)", 235, 83, 63, ATI_CHIP_264XL | M64F_SDRAM_MAGIC_PLL },
 	{ PCI_CHIP_MACH64GL, "3D RAGE XL (Mach64 GL, PCI)", 230, 83, 63, ATI_CHIP_264XL },
 	{ PCI_CHIP_MACH64GS, "3D RAGE XL (Mach64 GS, PCI)", 230, 83, 63, ATI_CHIP_264XL },
 
diff -urN oldtree/drivers/video/console/vgacon.c newtree/drivers/video/console/vgacon.c
--- oldtree/drivers/video/console/vgacon.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/drivers/video/console/vgacon.c	2006-02-13 19:20:29.730992504 -0500
@@ -503,10 +503,16 @@
 {
 	unsigned long flags;
 	unsigned int scanlines = height * c->vc_font.height;
-	u8 scanlines_lo, r7, vsync_end, mode;
+	u8 scanlines_lo, r7, vsync_end, mode, max_scan;
 
 	spin_lock_irqsave(&vga_lock, flags);
 
+	outb_p(VGA_CRTC_MAX_SCAN, vga_video_port_reg);
+	max_scan = inb_p(vga_video_port_val);
+
+	if (max_scan & 0x80)
+		scanlines <<= 1;
+
 	outb_p(VGA_CRTC_MODE, vga_video_port_reg);
 	mode = inb_p(vga_video_port_val);
 
diff -urN oldtree/fs/Kconfig newtree/fs/Kconfig
--- oldtree/fs/Kconfig	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/Kconfig	2006-02-13 19:20:00.665411144 -0500
@@ -895,6 +895,53 @@
 	  To compile this file system support as a module, choose M here: the
 	  module will be called affs.  If unsure, say N.
 
+config ASFS_FS
+	tristate "Amiga SFS file system support (EXPERIMENTAL)"
+	select NLS
+	depends on EXPERIMENTAL
+	help
+
+	  The Amiga Smart FileSystem (SFS) is the file system used on hard
+	  disks by Amiga(tm) and MorphOS(tm) systems.  Say Y if you want
+	  to be able to read files from an Amiga SFS partition on your hard
+	  drive.
+
+	  For more information read <file:Documentation/filesystems/asfs.txt>
+
+	  To compile this file system support as a module, choose M here: the
+	  module will be called asfs.
+
+	  If unsure, say N.
+
+config ASFS_DEFAULT_CODEPAGE
+	string "Default codepage for SFS"
+	depends on ASFS_FS
+	default ""
+	help
+	  This option should be set to the codepage of your SFS filesystems.
+	  It can be overridden with the 'codepage' mount option. Leave it blank
+	  or enter 'none' to disable filename converting.
+
+	  Use full codepage name (for example 'cp1251' instead of '1251') here,
+	  this allows to specify any character set, not only numbered one (like
+	  'iso8859-2').
+
+	  If unsure, leave it blank.
+
+config ASFS_RW
+	bool "Amiga SFS write support (DANGEROUS)"
+	depends on ASFS_FS
+	help
+
+	  If you say Y here, you will be able to write to ASFS file
+	  systems as well as read from them. The read-write support in ASFS
+	  is in beta stage. This means that useing it to write files to SFS
+	  partitions is DANGEROUS and COULD corrupt the filesystem.
+
+	  For more information read <file:Documentation/filesystems/asfs.txt>
+
+	  If unsure, say N.
+
 config HFS_FS
 	tristate "Apple Macintosh file system support (EXPERIMENTAL)"
 	depends on EXPERIMENTAL
diff -urN oldtree/fs/Kconfig.orig newtree/fs/Kconfig.orig
--- oldtree/fs/Kconfig.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/Kconfig.orig	2006-02-13 19:20:00.668410688 -0500
@@ -0,0 +1,1791 @@
+#
+# File system configuration
+#
+
+menu "File systems"
+
+config EXT2_FS
+	tristate "Second extended fs support"
+	help
+	  Ext2 is a standard Linux file system for hard disks.
+
+	  To compile this file system support as a module, choose M here: the
+	  module will be called ext2.  Be aware however that the file system
+	  of your root partition (the one containing the directory /) cannot
+	  be compiled as a module, and so this could be dangerous.
+
+	  If unsure, say Y.
+
+config EXT2_FS_XATTR
+	bool "Ext2 extended attributes"
+	depends on EXT2_FS
+	help
+	  Extended attributes are name:value pairs associated with inodes by
+	  the kernel or by users (see the attr(5) manual page, or visit
+	  <http://acl.bestbits.at/> for details).
+
+	  If unsure, say N.
+
+config EXT2_FS_POSIX_ACL
+	bool "Ext2 POSIX Access Control Lists"
+	depends on EXT2_FS_XATTR
+	select FS_POSIX_ACL
+	help
+	  Posix Access Control Lists (ACLs) support permissions for users and
+	  groups beyond the owner/group/world scheme.
+
+	  To learn more about Access Control Lists, visit the Posix ACLs for
+	  Linux website <http://acl.bestbits.at/>.
+
+	  If you don't know what Access Control Lists are, say N
+
+config EXT2_FS_SECURITY
+	bool "Ext2 Security Labels"
+	depends on EXT2_FS_XATTR
+	help
+	  Security labels support alternative access control models
+	  implemented by security modules like SELinux.  This option
+	  enables an extended attribute handler for file security
+	  labels in the ext2 filesystem.
+
+	  If you are not using a security module that requires using
+	  extended attributes for file security labels, say N.
+
+config EXT2_FS_XIP
+	bool "Ext2 execute in place support"
+	depends on EXT2_FS
+	help
+	  Execute in place can be used on memory-backed block devices. If you
+	  enable this option, you can select to mount block devices which are
+	  capable of this feature without using the page cache.
+
+	  If you do not use a block device that is capable of using this,
+	  or if unsure, say N.
+
+config FS_XIP
+# execute in place
+	bool
+	depends on EXT2_FS_XIP
+	default y
+
+config EXT3_FS
+	tristate "Ext3 journalling file system support"
+	help
+	  This is the journaling version of the Second extended file system
+	  (often called ext3), the de facto standard Linux file system
+	  (method to organize files on a storage device) for hard disks.
+
+	  The journaling code included in this driver means you do not have
+	  to run e2fsck (file system checker) on your file systems after a
+	  crash.  The journal keeps track of any changes that were being made
+	  at the time the system crashed, and can ensure that your file system
+	  is consistent without the need for a lengthy check.
+
+	  Other than adding the journal to the file system, the on-disk format
+	  of ext3 is identical to ext2.  It is possible to freely switch
+	  between using the ext3 driver and the ext2 driver, as long as the
+	  file system has been cleanly unmounted, or e2fsck is run on the file
+	  system.
+
+	  To add a journal on an existing ext2 file system or change the
+	  behavior of ext3 file systems, you can use the tune2fs utility ("man
+	  tune2fs").  To modify attributes of files and directories on ext3
+	  file systems, use chattr ("man chattr").  You need to be using
+	  e2fsprogs version 1.20 or later in order to create ext3 journals
+	  (available at <http://sourceforge.net/projects/e2fsprogs/>).
+
+	  To compile this file system support as a module, choose M here: the
+	  module will be called ext3.  Be aware however that the file system
+	  of your root partition (the one containing the directory /) cannot
+	  be compiled as a module, and so this may be dangerous.
+
+config EXT3_FS_XATTR
+	bool "Ext3 extended attributes"
+	depends on EXT3_FS
+	default y
+	help
+	  Extended attributes are name:value pairs associated with inodes by
+	  the kernel or by users (see the attr(5) manual page, or visit
+	  <http://acl.bestbits.at/> for details).
+
+	  If unsure, say N.
+
+	  You need this for POSIX ACL support on ext3.
+
+config EXT3_FS_POSIX_ACL
+	bool "Ext3 POSIX Access Control Lists"
+	depends on EXT3_FS_XATTR
+	select FS_POSIX_ACL
+	help
+	  Posix Access Control Lists (ACLs) support permissions for users and
+	  groups beyond the owner/group/world scheme.
+
+	  To learn more about Access Control Lists, visit the Posix ACLs for
+	  Linux website <http://acl.bestbits.at/>.
+
+	  If you don't know what Access Control Lists are, say N
+
+config EXT3_FS_SECURITY
+	bool "Ext3 Security Labels"
+	depends on EXT3_FS_XATTR
+	help
+	  Security labels support alternative access control models
+	  implemented by security modules like SELinux.  This option
+	  enables an extended attribute handler for file security
+	  labels in the ext3 filesystem.
+
+	  If you are not using a security module that requires using
+	  extended attributes for file security labels, say N.
+
+config JBD
+# CONFIG_JBD could be its own option (even modular), but until there are
+# other users than ext3, we will simply make it be the same as CONFIG_EXT3_FS
+# dep_tristate '  Journal Block Device support (JBD for ext3)' CONFIG_JBD $CONFIG_EXT3_FS
+	tristate
+	default EXT3_FS
+	help
+	  This is a generic journaling layer for block devices.  It is
+	  currently used by the ext3 file system, but it could also be used to
+	  add journal support to other file systems or block devices such as
+	  RAID or LVM.
+
+	  If you are using the ext3 file system, you need to say Y here. If
+	  you are not using ext3 then you will probably want to say N.
+
+	  To compile this device as a module, choose M here: the module will be
+	  called jbd.  If you are compiling ext3 into the kernel, you cannot
+	  compile this code as a module.
+
+config JBD_DEBUG
+	bool "JBD (ext3) debugging support"
+	depends on JBD
+	help
+	  If you are using the ext3 journaled file system (or potentially any
+	  other file system/device using JBD), this option allows you to
+	  enable debugging output while the system is running, in order to
+	  help track down any problems you are having.  By default the
+	  debugging output will be turned off.
+
+	  If you select Y here, then you will be able to turn on debugging
+	  with "echo N > /proc/sys/fs/jbd-debug", where N is a number between
+	  1 and 5, the higher the number, the more debugging output is
+	  generated.  To turn debugging off again, do
+	  "echo 0 > /proc/sys/fs/jbd-debug".
+
+config FS_MBCACHE
+# Meta block cache for Extended Attributes (ext2/ext3)
+	tristate
+	depends on EXT2_FS_XATTR || EXT3_FS_XATTR
+	default y if EXT2_FS=y || EXT3_FS=y
+	default m if EXT2_FS=m || EXT3_FS=m
+
+config REISERFS_FS
+	tristate "Reiserfs support"
+	help
+	  Stores not just filenames but the files themselves in a balanced
+	  tree.  Uses journaling.
+
+	  Balanced trees are more efficient than traditional file system
+	  architectural foundations.
+
+	  In general, ReiserFS is as fast as ext2, but is very efficient with
+	  large directories and small files.  Additional patches are needed
+	  for NFS and quotas, please see <http://www.namesys.com/> for links.
+
+	  It is more easily extended to have features currently found in
+	  database and keyword search systems than block allocation based file
+	  systems are.  The next version will be so extended, and will support
+	  plugins consistent with our motto ``It takes more than a license to
+	  make source code open.''
+
+	  Read <http://www.namesys.com/> to learn more about reiserfs.
+
+	  Sponsored by Threshold Networks, Emusic.com, and Bigstorage.com.
+
+	  If you like it, you can pay us to add new features to it that you
+	  need, buy a support contract, or pay us to port it to another OS.
+
+config REISERFS_CHECK
+	bool "Enable reiserfs debug mode"
+	depends on REISERFS_FS
+	help
+	  If you set this to Y, then ReiserFS will perform every check it can
+	  possibly imagine of its internal consistency throughout its
+	  operation.  It will also go substantially slower.  More than once we
+	  have forgotten that this was on, and then gone despondent over the
+	  latest benchmarks.:-) Use of this option allows our team to go all
+	  out in checking for consistency when debugging without fear of its
+	  effect on end users.  If you are on the verge of sending in a bug
+	  report, say Y and you might get a useful error message.  Almost
+	  everyone should say N.
+
+config REISERFS_PROC_INFO
+	bool "Stats in /proc/fs/reiserfs"
+	depends on REISERFS_FS
+	help
+	  Create under /proc/fs/reiserfs a hierarchy of files, displaying
+	  various ReiserFS statistics and internal data at the expense of
+	  making your kernel or module slightly larger (+8 KB). This also
+	  increases the amount of kernel memory required for each mount.
+	  Almost everyone but ReiserFS developers and people fine-tuning
+	  reiserfs or tracing problems should say N.
+
+config REISERFS_FS_XATTR
+	bool "ReiserFS extended attributes"
+	depends on REISERFS_FS
+	help
+	  Extended attributes are name:value pairs associated with inodes by
+	  the kernel or by users (see the attr(5) manual page, or visit
+	  <http://acl.bestbits.at/> for details).
+
+	  If unsure, say N.
+
+config REISERFS_FS_POSIX_ACL
+	bool "ReiserFS POSIX Access Control Lists"
+	depends on REISERFS_FS_XATTR
+	select FS_POSIX_ACL
+	help
+	  Posix Access Control Lists (ACLs) support permissions for users and
+	  groups beyond the owner/group/world scheme.
+
+	  To learn more about Access Control Lists, visit the Posix ACLs for
+	  Linux website <http://acl.bestbits.at/>.
+
+	  If you don't know what Access Control Lists are, say N
+
+config REISERFS_FS_SECURITY
+	bool "ReiserFS Security Labels"
+	depends on REISERFS_FS_XATTR
+	help
+	  Security labels support alternative access control models
+	  implemented by security modules like SELinux.  This option
+	  enables an extended attribute handler for file security
+	  labels in the ReiserFS filesystem.
+
+	  If you are not using a security module that requires using
+	  extended attributes for file security labels, say N.
+
+config JFS_FS
+	tristate "JFS filesystem support"
+	select NLS
+	help
+	  This is a port of IBM's Journaled Filesystem .  More information is
+	  available in the file <file:Documentation/filesystems/jfs.txt>.
+
+	  If you do not intend to use the JFS filesystem, say N.
+
+config JFS_POSIX_ACL
+	bool "JFS POSIX Access Control Lists"
+	depends on JFS_FS
+	select FS_POSIX_ACL
+	help
+	  Posix Access Control Lists (ACLs) support permissions for users and
+	  groups beyond the owner/group/world scheme.
+
+	  To learn more about Access Control Lists, visit the Posix ACLs for
+	  Linux website <http://acl.bestbits.at/>.
+
+	  If you don't know what Access Control Lists are, say N
+
+config JFS_SECURITY
+	bool "JFS Security Labels"
+	depends on JFS_FS
+	help
+	  Security labels support alternative access control models
+	  implemented by security modules like SELinux.  This option
+	  enables an extended attribute handler for file security
+	  labels in the jfs filesystem.
+
+	  If you are not using a security module that requires using
+	  extended attributes for file security labels, say N.
+
+config JFS_DEBUG
+	bool "JFS debugging"
+	depends on JFS_FS
+	help
+	  If you are experiencing any problems with the JFS filesystem, say
+	  Y here.  This will result in additional debugging messages to be
+	  written to the system log.  Under normal circumstances, this
+	  results in very little overhead.
+
+config JFS_STATISTICS
+	bool "JFS statistics"
+	depends on JFS_FS
+	help
+	  Enabling this option will cause statistics from the JFS file system
+	  to be made available to the user in the /proc/fs/jfs/ directory.
+
+config FS_POSIX_ACL
+# Posix ACL utility routines (for now, only ext2/ext3/jfs/reiserfs)
+#
+# NOTE: you can implement Posix ACLs without these helpers (XFS does).
+# 	Never use this symbol for ifdefs.
+#
+	bool
+	default n
+
+source "fs/xfs/Kconfig"
+
+config MINIX_FS
+	tristate "Minix fs support"
+	help
+	  Minix is a simple operating system used in many classes about OS's.
+	  The minix file system (method to organize files on a hard disk
+	  partition or a floppy disk) was the original file system for Linux,
+	  but has been superseded by the second extended file system ext2fs.
+	  You don't want to use the minix file system on your hard disk
+	  because of certain built-in restrictions, but it is sometimes found
+	  on older Linux floppy disks.  This option will enlarge your kernel
+	  by about 28 KB. If unsure, say N.
+
+	  To compile this file system support as a module, choose M here: the
+	  module will be called minix.  Note that the file system of your root
+	  partition (the one containing the directory /) cannot be compiled as
+	  a module.
+
+config ROMFS_FS
+	tristate "ROM file system support"
+	---help---
+	  This is a very small read-only file system mainly intended for
+	  initial ram disks of installation disks, but it could be used for
+	  other read-only media as well.  Read
+	  <file:Documentation/filesystems/romfs.txt> for details.
+
+	  To compile this file system support as a module, choose M here: the
+	  module will be called romfs.  Note that the file system of your
+	  root partition (the one containing the directory /) cannot be a
+	  module.
+
+	  If you don't know whether you need it, then you don't need it:
+	  answer N.
+
+config INOTIFY
+	bool "Inotify file change notification support"
+	default y
+	---help---
+	  Say Y here to enable inotify support and the associated system
+	  calls.  Inotify is a file change notification system and a
+	  replacement for dnotify.  Inotify fixes numerous shortcomings in
+	  dnotify and introduces several new features.  It allows monitoring
+	  of both files and directories via a single open fd.  Other features
+	  include multiple file events, one-shot support, and unmount
+	  notification.
+
+	  For more information, see Documentation/filesystems/inotify.txt
+
+	  If unsure, say Y.
+
+config QUOTA
+	bool "Quota support"
+	help
+	  If you say Y here, you will be able to set per user limits for disk
+	  usage (also called disk quotas). Currently, it works for the
+	  ext2, ext3, and reiserfs file system. ext3 also supports journalled
+	  quotas for which you don't need to run quotacheck(8) after an unclean
+	  shutdown.
+	  For further details, read the Quota mini-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>, or the documentation provided
+	  with the quota tools. Probably the quota support is only useful for
+	  multi user systems. If unsure, say N.
+
+config QFMT_V1
+	tristate "Old quota format support"
+	depends on QUOTA
+	help
+	  This quota format was (is) used by kernels earlier than 2.4.22. If
+	  you have quota working and you don't want to convert to new quota
+	  format say Y here.
+
+config QFMT_V2
+	tristate "Quota format v2 support"
+	depends on QUOTA
+	help
+	  This quota format allows using quotas with 32-bit UIDs/GIDs. If you
+	  need this functionality say Y here.
+
+config QUOTACTL
+	bool
+	depends on XFS_QUOTA || QUOTA
+	default y
+
+config DNOTIFY
+	bool "Dnotify support" if EMBEDDED
+	default y
+	help
+	  Dnotify is a directory-based per-fd file change notification system
+	  that uses signals to communicate events to user-space.  There exist
+	  superior alternatives, but some applications may still rely on
+	  dnotify.
+
+	  Because of this, if unsure, say Y.
+
+config AUTOFS_FS
+	tristate "Kernel automounter support"
+	help
+	  The automounter is a tool to automatically mount remote file systems
+	  on demand. This implementation is partially kernel-based to reduce
+	  overhead in the already-mounted case; this is unlike the BSD
+	  automounter (amd), which is a pure user space daemon.
+
+	  To use the automounter you need the user-space tools from the autofs
+	  package; you can find the location in <file:Documentation/Changes>.
+	  You also want to answer Y to "NFS file system support", below.
+
+	  If you want to use the newer version of the automounter with more
+	  features, say N here and say Y to "Kernel automounter v4 support",
+	  below.
+
+	  To compile this support as a module, choose M here: the module will be
+	  called autofs.
+
+	  If you are not a part of a fairly large, distributed network, you
+	  probably do not need an automounter, and can say N here.
+
+config AUTOFS4_FS
+	tristate "Kernel automounter version 4 support (also supports v3)"
+	help
+	  The automounter is a tool to automatically mount remote file systems
+	  on demand. This implementation is partially kernel-based to reduce
+	  overhead in the already-mounted case; this is unlike the BSD
+	  automounter (amd), which is a pure user space daemon.
+
+	  To use the automounter you need the user-space tools from
+	  <ftp://ftp.kernel.org/pub/linux/daemons/autofs/v4/>; you also
+	  want to answer Y to "NFS file system support", below.
+
+	  To compile this support as a module, choose M here: the module will be
+	  called autofs4.  You will need to add "alias autofs autofs4" to your
+	  modules configuration file.
+
+	  If you are not a part of a fairly large, distributed network or
+	  don't have a laptop which needs to dynamically reconfigure to the
+	  local network, you probably do not need an automounter, and can say
+	  N here.
+
+config FUSE_FS
+	tristate "Filesystem in Userspace support"
+	help
+	  With FUSE it is possible to implement a fully functional filesystem
+	  in a userspace program.
+
+	  There's also companion library: libfuse.  This library along with
+	  utilities is available from the FUSE homepage:
+	  <http://fuse.sourceforge.net/>
+
+	  See <file:Documentation/filesystems/fuse.txt> for more information.
+	  See <file:Documentation/Changes> for needed library/utility version.
+
+	  If you want to develop a userspace FS, or if you want to use
+	  a filesystem based on FUSE, answer Y or M.
+
+menu "CD-ROM/DVD Filesystems"
+
+config ISO9660_FS
+	tristate "ISO 9660 CDROM file system support"
+	help
+	  This is the standard file system used on CD-ROMs.  It was previously
+	  known as "High Sierra File System" and is called "hsfs" on other
+	  Unix systems.  The so-called Rock-Ridge extensions which allow for
+	  long Unix filenames and symbolic links are also supported by this
+	  driver.  If you have a CD-ROM drive and want to do more with it than
+	  just listen to audio CDs and watch its LEDs, say Y (and read
+	  <file:Documentation/filesystems/isofs.txt> and the CD-ROM-HOWTO,
+	  available from <http://www.tldp.org/docs.html#howto>), thereby
+	  enlarging your kernel by about 27 KB; otherwise say N.
+
+	  To compile this file system support as a module, choose M here: the
+	  module will be called isofs.
+
+config JOLIET
+	bool "Microsoft Joliet CDROM extensions"
+	depends on ISO9660_FS
+	select NLS
+	help
+	  Joliet is a Microsoft extension for the ISO 9660 CD-ROM file system
+	  which allows for long filenames in unicode format (unicode is the
+	  new 16 bit character code, successor to ASCII, which encodes the
+	  characters of almost all languages of the world; see
+	  <http://www.unicode.org/> for more information).  Say Y here if you
+	  want to be able to read Joliet CD-ROMs under Linux.
+
+config ZISOFS
+	bool "Transparent decompression extension"
+	depends on ISO9660_FS
+	select ZLIB_INFLATE
+	help
+	  This is a Linux-specific extension to RockRidge which lets you store
+	  data in compressed form on a CD-ROM and have it transparently
+	  decompressed when the CD-ROM is accessed.  See
+	  <http://www.kernel.org/pub/linux/utils/fs/zisofs/> for the tools
+	  necessary to create such a filesystem.  Say Y here if you want to be
+	  able to read such compressed CD-ROMs.
+
+config ZISOFS_FS
+# for fs/nls/Config.in
+	tristate
+	depends on ZISOFS
+	default ISO9660_FS
+
+config UDF_FS
+	tristate "UDF file system support"
+	help
+	  This is the new file system used on some CD-ROMs and DVDs. Say Y if
+	  you intend to mount DVD discs or CDRW's written in packet mode, or
+	  if written to by other UDF utilities, such as DirectCD.
+	  Please read <file:Documentation/filesystems/udf.txt>.
+
+	  To compile this file system support as a module, choose M here: the
+	  module will be called udf.
+
+	  If unsure, say N.
+
+config UDF_NLS
+	bool
+	default y
+	depends on (UDF_FS=m && NLS) || (UDF_FS=y && NLS=y)
+
+endmenu
+
+menu "DOS/FAT/NT Filesystems"
+
+config FAT_FS
+	tristate
+	select NLS
+	help
+	  If you want to use one of the FAT-based file systems (the MS-DOS and
+	  VFAT (Windows 95) file systems), then you must say Y or M here
+	  to include FAT support. You will then be able to mount partitions or
+	  diskettes with FAT-based file systems and transparently access the
+	  files on them, i.e. MSDOS files will look and behave just like all
+	  other Unix files.
+
+	  This FAT support is not a file system in itself, it only provides
+	  the foundation for the other file systems. You will have to say Y or
+	  M to at least one of "MSDOS fs support" or "VFAT fs support" in
+	  order to make use of it.
+
+	  Another way to read and write MSDOS floppies and hard drive
+	  partitions from within Linux (but not transparently) is with the
+	  mtools ("man mtools") program suite. You don't need to say Y here in
+	  order to do that.
+
+	  If you need to move large files on floppies between a DOS and a
+	  Linux box, say Y here, mount the floppy under Linux with an MSDOS
+	  file system and use GNU tar's M option. GNU tar is a program
+	  available for Unix and DOS ("man tar" or "info tar").
+
+	  It is now also becoming possible to read and write compressed FAT
+	  file systems; read <file:Documentation/filesystems/fat_cvf.txt> for
+	  details.
+
+	  The FAT support will enlarge your kernel by about 37 KB. If unsure,
+	  say Y.
+
+	  To compile this as a module, choose M here: the module will be called
+	  fat.  Note that if you compile the FAT support as a module, you
+	  cannot compile any of the FAT-based file systems into the kernel
+	  -- they will have to be modules as well.
+
+config MSDOS_FS
+	tristate "MSDOS fs support"
+	select FAT_FS
+	help
+	  This allows you to mount MSDOS partitions of your hard drive (unless
+	  they are compressed; to access compressed MSDOS partitions under
+	  Linux, you can either use the DOS emulator DOSEMU, described in the
+	  DOSEMU-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>, or try dmsdosfs in
+	  <ftp://ibiblio.org/pub/Linux/system/filesystems/dosfs/>. If you
+	  intend to use dosemu with a non-compressed MSDOS partition, say Y
+	  here) and MSDOS floppies. This means that file access becomes
+	  transparent, i.e. the MSDOS files look and behave just like all
+	  other Unix files.
+
+	  If you have Windows 95 or Windows NT installed on your MSDOS
+	  partitions, you should use the VFAT file system (say Y to "VFAT fs
+	  support" below), or you will not be able to see the long filenames
+	  generated by Windows 95 / Windows NT.
+
+	  This option will enlarge your kernel by about 7 KB. If unsure,
+	  answer Y. This will only work if you said Y to "DOS FAT fs support"
+	  as well. To compile this as a module, choose M here: the module will
+	  be called msdos.
+
+config VFAT_FS
+	tristate "VFAT (Windows-95) fs support"
+	select FAT_FS
+	help
+	  This option provides support for normal Windows file systems with
+	  long filenames.  That includes non-compressed FAT-based file systems
+	  used by Windows 95, Windows 98, Windows NT 4.0, and the Unix
+	  programs from the mtools package.
+
+	  The VFAT support enlarges your kernel by about 10 KB and it only
+	  works if you said Y to the "DOS FAT fs support" above.  Please read
+	  the file <file:Documentation/filesystems/vfat.txt> for details.  If
+	  unsure, say Y.
+
+	  To compile this as a module, choose M here: the module will be called
+	  vfat.
+
+config FAT_DEFAULT_CODEPAGE
+	int "Default codepage for FAT"
+	depends on MSDOS_FS || VFAT_FS
+	default 437
+	help
+	  This option should be set to the codepage of your FAT filesystems.
+	  It can be overridden with the "codepage" mount option.
+	  See <file:Documentation/filesystems/vfat.txt> for more information.
+
+config FAT_DEFAULT_IOCHARSET
+	string "Default iocharset for FAT"
+	depends on VFAT_FS
+	default "iso8859-1"
+	help
+	  Set this to the default input/output character set you'd
+	  like FAT to use. It should probably match the character set
+	  that most of your FAT filesystems use, and can be overridden
+	  with the "iocharset" mount option for FAT filesystems.
+	  Note that "utf8" is not recommended for FAT filesystems.
+	  If unsure, you shouldn't set "utf8" here.
+	  See <file:Documentation/filesystems/vfat.txt> for more information.
+
+config NTFS_FS
+	tristate "NTFS file system support"
+	select NLS
+	help
+	  NTFS is the file system of Microsoft Windows NT, 2000, XP and 2003.
+
+	  Saying Y or M here enables read support.  There is partial, but
+	  safe, write support available.  For write support you must also
+	  say Y to "NTFS write support" below.
+
+	  There are also a number of user-space tools available, called
+	  ntfsprogs.  These include ntfsundelete and ntfsresize, that work
+	  without NTFS support enabled in the kernel.
+
+	  This is a rewrite from scratch of Linux NTFS support and replaced
+	  the old NTFS code starting with Linux 2.5.11.  A backport to
+	  the Linux 2.4 kernel series is separately available as a patch
+	  from the project web site.
+
+	  For more information see <file:Documentation/filesystems/ntfs.txt>
+	  and <http://linux-ntfs.sourceforge.net/>.
+
+	  To compile this file system support as a module, choose M here: the
+	  module will be called ntfs.
+
+	  If you are not using Windows NT, 2000, XP or 2003 in addition to
+	  Linux on your computer it is safe to say N.
+
+config NTFS_DEBUG
+	bool "NTFS debugging support"
+	depends on NTFS_FS
+	help
+	  If you are experiencing any problems with the NTFS file system, say
+	  Y here.  This will result in additional consistency checks to be
+	  performed by the driver as well as additional debugging messages to
+	  be written to the system log.  Note that debugging messages are
+	  disabled by default.  To enable them, supply the option debug_msgs=1
+	  at the kernel command line when booting the kernel or as an option
+	  to insmod when loading the ntfs module.  Once the driver is active,
+	  you can enable debugging messages by doing (as root):
+	  echo 1 > /proc/sys/fs/ntfs-debug
+	  Replacing the "1" with "0" would disable debug messages.
+
+	  If you leave debugging messages disabled, this results in little
+	  overhead, but enabling debug messages results in very significant
+	  slowdown of the system.
+
+	  When reporting bugs, please try to have available a full dump of
+	  debugging messages while the misbehaviour was occurring.
+
+config NTFS_RW
+	bool "NTFS write support"
+	depends on NTFS_FS
+	help
+	  This enables the partial, but safe, write support in the NTFS driver.
+
+	  The only supported operation is overwriting existing files, without
+	  changing the file length.  No file or directory creation, deletion or
+	  renaming is possible.  Note only non-resident files can be written to
+	  so you may find that some very small files (<500 bytes or so) cannot
+	  be written to.
+
+	  While we cannot guarantee that it will not damage any data, we have
+	  so far not received a single report where the driver would have
+	  damaged someones data so we assume it is perfectly safe to use.
+
+	  Note:  While write support is safe in this version (a rewrite from
+	  scratch of the NTFS support), it should be noted that the old NTFS
+	  write support, included in Linux 2.5.10 and before (since 1997),
+	  is not safe.
+
+	  This is currently useful with TopologiLinux.  TopologiLinux is run
+	  on top of any DOS/Microsoft Windows system without partitioning your
+	  hard disk.  Unlike other Linux distributions TopologiLinux does not
+	  need its own partition.  For more information see
+	  <http://topologi-linux.sourceforge.net/>
+
+	  It is perfectly safe to say N here.
+
+endmenu
+
+menu "Pseudo filesystems"
+
+config PROC_FS
+	bool "/proc file system support"
+	help
+	  This is a virtual file system providing information about the status
+	  of the system. "Virtual" means that it doesn't take up any space on
+	  your hard disk: the files are created on the fly by the kernel when
+	  you try to access them. Also, you cannot read the files with older
+	  version of the program less: you need to use more or cat.
+
+	  It's totally cool; for example, "cat /proc/interrupts" gives
+	  information about what the different IRQs are used for at the moment
+	  (there is a small number of Interrupt ReQuest lines in your computer
+	  that are used by the attached devices to gain the CPU's attention --
+	  often a source of trouble if two devices are mistakenly configured
+	  to use the same IRQ). The program procinfo to display some
+	  information about your system gathered from the /proc file system.
+
+	  Before you can use the /proc file system, it has to be mounted,
+	  meaning it has to be given a location in the directory hierarchy.
+	  That location should be /proc. A command such as "mount -t proc proc
+	  /proc" or the equivalent line in /etc/fstab does the job.
+
+	  The /proc file system is explained in the file
+	  <file:Documentation/filesystems/proc.txt> and on the proc(5) manpage
+	  ("man 5 proc").
+
+	  This option will enlarge your kernel by about 67 KB. Several
+	  programs depend on this, so everyone should say Y here.
+
+config PROC_KCORE
+	bool "/proc/kcore support" if !ARM
+	depends on PROC_FS && MMU
+
+config PROC_VMCORE
+        bool "/proc/vmcore support (EXPERIMENTAL)"
+        depends on PROC_FS && EMBEDDED && EXPERIMENTAL && CRASH_DUMP
+        help
+        Exports the dump image of crashed kernel in ELF format.
+
+config SYSFS
+	bool "sysfs file system support" if EMBEDDED
+	default y
+	help
+	The sysfs filesystem is a virtual filesystem that the kernel uses to
+	export internal kernel objects, their attributes, and their
+	relationships to one another.
+
+	Users can use sysfs to ascertain useful information about the running
+	kernel, such as the devices the kernel has discovered on each bus and
+	which driver each is bound to. sysfs can also be used to tune devices
+	and other kernel subsystems.
+
+	Some system agents rely on the information in sysfs to operate.
+	/sbin/hotplug uses device and object attributes in sysfs to assist in
+	delegating policy decisions, like persistantly naming devices.
+
+	sysfs is currently used by the block subsystem to mount the root
+	partition.  If sysfs is disabled you must specify the boot device on
+	the kernel boot command line via its major and minor numbers.  For
+	example, "root=03:01" for /dev/hda1.
+
+	Designers of embedded systems may wish to say N here to conserve space.
+
+config TMPFS
+	bool "Virtual memory file system support (former shm fs)"
+	help
+	  Tmpfs is a file system which keeps all files in virtual memory.
+
+	  Everything in tmpfs is temporary in the sense that no files will be
+	  created on your hard drive. The files live in memory and swap
+	  space. If you unmount a tmpfs instance, everything stored therein is
+	  lost.
+
+	  See <file:Documentation/filesystems/tmpfs.txt> for details.
+
+config HUGETLBFS
+	bool "HugeTLB file system support"
+	depends X86 || IA64 || PPC64 || SPARC64 || SUPERH || BROKEN
+
+config HUGETLB_PAGE
+	def_bool HUGETLBFS
+
+config RAMFS
+	bool
+	default y
+	---help---
+	  Ramfs is a file system which keeps all files in RAM. It allows
+	  read and write access.
+
+	  It is more of an programming example than a useable file system.  If
+	  you need a file system which lives in RAM with limit checking use
+	  tmpfs.
+
+	  To compile this as a module, choose M here: the module will be called
+	  ramfs.
+
+config RELAYFS_FS
+	tristate "Relayfs file system support"
+	---help---
+	  Relayfs is a high-speed data relay filesystem designed to provide
+	  an efficient mechanism for tools and facilities to relay large
+	  amounts of data from kernel space to user space.
+
+	  To compile this code as a module, choose M here: the module will be
+	  called relayfs.
+
+	  If unsure, say N.
+
+endmenu
+
+menu "Miscellaneous filesystems"
+
+config ADFS_FS
+	tristate "ADFS file system support (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	help
+	  The Acorn Disc Filing System is the standard file system of the
+	  RiscOS operating system which runs on Acorn's ARM-based Risc PC
+	  systems and the Acorn Archimedes range of machines. If you say Y
+	  here, Linux will be able to read from ADFS partitions on hard drives
+	  and from ADFS-formatted floppy discs. If you also want to be able to
+	  write to those devices, say Y to "ADFS write support" below.
+
+	  The ADFS partition should be the first partition (i.e.,
+	  /dev/[hs]d?1) on each of your drives. Please read the file
+	  <file:Documentation/filesystems/adfs.txt> for further details.
+
+	  To compile this code as a module, choose M here: the module will be
+	  called adfs.
+
+	  If unsure, say N.
+
+config ADFS_FS_RW
+	bool "ADFS write support (DANGEROUS)"
+	depends on ADFS_FS
+	help
+	  If you say Y here, you will be able to write to ADFS partitions on
+	  hard drives and ADFS-formatted floppy disks. This is experimental
+	  codes, so if you're unsure, say N.
+
+config AFFS_FS
+	tristate "Amiga FFS file system support (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	help
+	  The Fast File System (FFS) is the common file system used on hard
+	  disks by Amiga(tm) systems since AmigaOS Version 1.3 (34.20).  Say Y
+	  if you want to be able to read and write files from and to an Amiga
+	  FFS partition on your hard drive.  Amiga floppies however cannot be
+	  read with this driver due to an incompatibility of the floppy
+	  controller used in an Amiga and the standard floppy controller in
+	  PCs and workstations. Read <file:Documentation/filesystems/affs.txt>
+	  and <file:fs/affs/Changes>.
+
+	  With this driver you can also mount disk files used by Bernd
+	  Schmidt's Un*X Amiga Emulator
+	  (<http://www.freiburg.linux.de/~uae/>).
+	  If you want to do this, you will also need to say Y or M to "Loop
+	  device support", above.
+
+	  To compile this file system support as a module, choose M here: the
+	  module will be called affs.  If unsure, say N.
+
+config HFS_FS
+	tristate "Apple Macintosh file system support (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	select NLS
+	help
+	  If you say Y here, you will be able to mount Macintosh-formatted
+	  floppy disks and hard drive partitions with full read-write access.
+	  Please read <file:fs/hfs/HFS.txt> to learn about the available mount
+	  options.
+
+	  To compile this file system support as a module, choose M here: the
+	  module will be called hfs.
+
+config HFSPLUS_FS
+	tristate "Apple Extended HFS file system support"
+	select NLS
+	select NLS_UTF8
+	help
+	  If you say Y here, you will be able to mount extended format
+	  Macintosh-formatted hard drive partitions with full read-write access.
+
+	  This file system is often called HFS+ and was introduced with
+	  MacOS 8. It includes all Mac specific filesystem data such as
+	  data forks and creator codes, but it also has several UNIX
+	  style features such as file ownership and permissions.
+
+config BEFS_FS
+	tristate "BeOS file system (BeFS) support (read only) (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	select NLS
+	help
+	  The BeOS File System (BeFS) is the native file system of Be, Inc's
+	  BeOS. Notable features include support for arbitrary attributes
+	  on files and directories, and database-like indeces on selected
+	  attributes. (Also note that this driver doesn't make those features
+	  available at this time). It is a 64 bit filesystem, so it supports
+	  extremly large volumes and files.
+
+	  If you use this filesystem, you should also say Y to at least one
+	  of the NLS (native language support) options below.
+
+	  If you don't know what this is about, say N.
+
+	  To compile this as a module, choose M here: the module will be
+	  called befs.
+
+config BEFS_DEBUG
+	bool "Debug BeFS"
+	depends on BEFS_FS
+	help
+	  If you say Y here, you can use the 'debug' mount option to enable
+	  debugging output from the driver. 
+
+config BFS_FS
+	tristate "BFS file system support (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	help
+	  Boot File System (BFS) is a file system used under SCO UnixWare to
+	  allow the bootloader access to the kernel image and other important
+	  files during the boot process.  It is usually mounted under /stand
+	  and corresponds to the slice marked as "STAND" in the UnixWare
+	  partition.  You should say Y if you want to read or write the files
+	  on your /stand slice from within Linux.  You then also need to say Y
+	  to "UnixWare slices support", below.  More information about the BFS
+	  file system is contained in the file
+	  <file:Documentation/filesystems/bfs.txt>.
+
+	  If you don't know what this is about, say N.
+
+	  To compile this as a module, choose M here: the module will be called
+	  bfs.  Note that the file system of your root partition (the one
+	  containing the directory /) cannot be compiled as a module.
+
+
+
+config EFS_FS
+	tristate "EFS file system support (read only) (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	help
+	  EFS is an older file system used for non-ISO9660 CD-ROMs and hard
+	  disk partitions by SGI's IRIX operating system (IRIX 6.0 and newer
+	  uses the XFS file system for hard disk partitions however).
+
+	  This implementation only offers read-only access. If you don't know
+	  what all this is about, it's safe to say N. For more information
+	  about EFS see its home page at <http://aeschi.ch.eu.org/efs/>.
+
+	  To compile the EFS file system support as a module, choose M here: the
+	  module will be called efs.
+
+config JFFS_FS
+	tristate "Journalling Flash File System (JFFS) support"
+	depends on MTD
+	help
+	  JFFS is the Journaling Flash File System developed by Axis
+	  Communications in Sweden, aimed at providing a crash/powerdown-safe
+	  file system for disk-less embedded devices. Further information is
+	  available at (<http://developer.axis.com/software/jffs/>).
+
+config JFFS_FS_VERBOSE
+	int "JFFS debugging verbosity (0 = quiet, 3 = noisy)"
+	depends on JFFS_FS
+	default "0"
+	help
+	  Determines the verbosity level of the JFFS debugging messages.
+
+config JFFS_PROC_FS
+	bool "JFFS stats available in /proc filesystem"
+	depends on JFFS_FS && PROC_FS
+	help
+	  Enabling this option will cause statistics from mounted JFFS file systems
+	  to be made available to the user in the /proc/fs/jffs/ directory.
+
+config JFFS2_FS
+	tristate "Journalling Flash File System v2 (JFFS2) support"
+	select CRC32
+	depends on MTD
+	help
+	  JFFS2 is the second generation of the Journalling Flash File System
+	  for use on diskless embedded devices. It provides improved wear
+	  levelling, compression and support for hard links. You cannot use
+	  this on normal block devices, only on 'MTD' devices.
+
+	  Further information on the design and implementation of JFFS2 is
+	  available at <http://sources.redhat.com/jffs2/>.
+
+config JFFS2_FS_DEBUG
+	int "JFFS2 debugging verbosity (0 = quiet, 2 = noisy)"
+	depends on JFFS2_FS
+	default "0"
+	help
+	  This controls the amount of debugging messages produced by the JFFS2
+	  code. Set it to zero for use in production systems. For evaluation,
+	  testing and debugging, it's advisable to set it to one. This will
+	  enable a few assertions and will print debugging messages at the
+	  KERN_DEBUG loglevel, where they won't normally be visible. Level 2
+	  is unlikely to be useful - it enables extra debugging in certain
+	  areas which at one point needed debugging, but when the bugs were
+	  located and fixed, the detailed messages were relegated to level 2.
+
+	  If reporting bugs, please try to have available a full dump of the
+	  messages at debug level 1 while the misbehaviour was occurring.
+
+config JFFS2_FS_WRITEBUFFER
+	bool "JFFS2 write-buffering support"
+	depends on JFFS2_FS
+	default y
+	help
+	  This enables the write-buffering support in JFFS2.
+
+	  This functionality is required to support JFFS2 on the following
+	  types of flash devices:
+	    - NAND flash
+	    - NOR flash with transparent ECC
+	    - DataFlash
+
+config JFFS2_SUMMARY
+	bool "JFFS2 summary support (EXPERIMENTAL)"
+	depends on JFFS2_FS && EXPERIMENTAL
+	default n
+	help
+	  This feature makes it possible to use summary information
+	  for faster filesystem mount.
+
+	  The summary information can be inserted into a filesystem image
+	  by the utility 'sumtool'.
+
+	  If unsure, say 'N'.
+
+config JFFS2_COMPRESSION_OPTIONS
+	bool "Advanced compression options for JFFS2"
+	depends on JFFS2_FS
+	default n
+	help
+	  Enabling this option allows you to explicitly choose which
+	  compression modules, if any, are enabled in JFFS2. Removing
+	  compressors and mean you cannot read existing file systems,
+	  and enabling experimental compressors can mean that you
+	  write a file system which cannot be read by a standard kernel.
+
+	  If unsure, you should _definitely_ say 'N'.
+
+config JFFS2_ZLIB
+	bool "JFFS2 ZLIB compression support" if JFFS2_COMPRESSION_OPTIONS
+	select ZLIB_INFLATE
+	select ZLIB_DEFLATE
+	depends on JFFS2_FS
+	default y
+        help
+          Zlib is designed to be a free, general-purpose, legally unencumbered,
+          lossless data-compression library for use on virtually any computer
+          hardware and operating system. See <http://www.gzip.org/zlib/> for
+          further information.
+
+          Say 'Y' if unsure.
+
+config JFFS2_RTIME
+	bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS
+	depends on JFFS2_FS
+	default y
+        help
+          Rtime does manage to recompress already-compressed data. Say 'Y' if unsure.
+
+config JFFS2_RUBIN
+	bool "JFFS2 RUBIN compression support" if JFFS2_COMPRESSION_OPTIONS
+	depends on JFFS2_FS
+	default n
+        help
+          RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure.
+
+choice
+        prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS
+        default JFFS2_CMODE_PRIORITY
+        depends on JFFS2_FS
+        help
+          You can set here the default compression mode of JFFS2 from
+          the available compression modes. Don't touch if unsure.
+
+config JFFS2_CMODE_NONE
+        bool "no compression"
+        help
+          Uses no compression.
+
+config JFFS2_CMODE_PRIORITY
+        bool "priority"
+        help
+          Tries the compressors in a predefinied order and chooses the first
+          successful one.
+
+config JFFS2_CMODE_SIZE
+        bool "size (EXPERIMENTAL)"
+        help
+          Tries all compressors and chooses the one which has the smallest
+          result.
+
+endchoice
+
+config CRAMFS
+	tristate "Compressed ROM file system support (cramfs)"
+	select ZLIB_INFLATE
+	help
+	  Saying Y here includes support for CramFs (Compressed ROM File
+	  System).  CramFs is designed to be a simple, small, and compressed
+	  file system for ROM based embedded systems.  CramFs is read-only,
+	  limited to 256MB file systems (with 16MB files), and doesn't support
+	  16/32 bits uid/gid, hard links and timestamps.
+
+	  See <file:Documentation/filesystems/cramfs.txt> and
+	  <file:fs/cramfs/README> for further information.
+
+	  To compile this as a module, choose M here: the module will be called
+	  cramfs.  Note that the root file system (the one containing the
+	  directory /) cannot be compiled as a module.
+
+	  If unsure, say N.
+
+config VXFS_FS
+	tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)"
+	help
+	  FreeVxFS is a file system driver that support the VERITAS VxFS(TM)
+	  file system format.  VERITAS VxFS(TM) is the standard file system
+	  of SCO UnixWare (and possibly others) and optionally available
+	  for Sunsoft Solaris, HP-UX and many other operating systems.
+	  Currently only readonly access is supported.
+
+	  NOTE: the file system type as used by mount(1), mount(2) and
+	  fstab(5) is 'vxfs' as it describes the file system format, not
+	  the actual driver.
+
+	  To compile this as a module, choose M here: the module will be
+	  called freevxfs.  If unsure, say N.
+
+
+config HPFS_FS
+	tristate "OS/2 HPFS file system support"
+	help
+	  OS/2 is IBM's operating system for PC's, the same as Warp, and HPFS
+	  is the file system used for organizing files on OS/2 hard disk
+	  partitions. Say Y if you want to be able to read files from and
+	  write files to an OS/2 HPFS partition on your hard drive. OS/2
+	  floppies however are in regular MSDOS format, so you don't need this
+	  option in order to be able to read them. Read
+	  <file:Documentation/filesystems/hpfs.txt>.
+
+	  To compile this file system support as a module, choose M here: the
+	  module will be called hpfs.  If unsure, say N.
+
+
+
+config QNX4FS_FS
+	tristate "QNX4 file system support (read only)"
+	help
+	  This is the file system used by the real-time operating systems
+	  QNX 4 and QNX 6 (the latter is also called QNX RTP).
+	  Further information is available at <http://www.qnx.com/>.
+	  Say Y if you intend to mount QNX hard disks or floppies.
+	  Unless you say Y to "QNX4FS read-write support" below, you will
+	  only be able to read these file systems.
+
+	  To compile this file system support as a module, choose M here: the
+	  module will be called qnx4.
+
+	  If you don't know whether you need it, then you don't need it:
+	  answer N.
+
+config QNX4FS_RW
+	bool "QNX4FS write support (DANGEROUS)"
+	depends on QNX4FS_FS && EXPERIMENTAL && BROKEN
+	help
+	  Say Y if you want to test write support for QNX4 file systems.
+
+	  It's currently broken, so for now:
+	  answer N.
+
+
+
+config SYSV_FS
+	tristate "System V/Xenix/V7/Coherent file system support"
+	help
+	  SCO, Xenix and Coherent are commercial Unix systems for Intel
+	  machines, and Version 7 was used on the DEC PDP-11. Saying Y
+	  here would allow you to read from their floppies and hard disk
+	  partitions.
+
+	  If you have floppies or hard disk partitions like that, it is likely
+	  that they contain binaries from those other Unix systems; in order
+	  to run these binaries, you will want to install linux-abi which is a
+	  a set of kernel modules that lets you run SCO, Xenix, Wyse,
+	  UnixWare, Dell Unix and System V programs under Linux.  It is
+	  available via FTP (user: ftp) from
+	  <ftp://ftp.openlinux.org/pub/people/hch/linux-abi/>).
+	  NOTE: that will work only for binaries from Intel-based systems;
+	  PDP ones will have to wait until somebody ports Linux to -11 ;-)
+
+	  If you only intend to mount files from some other Unix over the
+	  network using NFS, you don't need the System V file system support
+	  (but you need NFS file system support obviously).
+
+	  Note that this option is generally not needed for floppies, since a
+	  good portable way to transport files and directories between unixes
+	  (and even other operating systems) is given by the tar program ("man
+	  tar" or preferably "info tar").  Note also that this option has
+	  nothing whatsoever to do with the option "System V IPC". Read about
+	  the System V file system in
+	  <file:Documentation/filesystems/sysv-fs.txt>.
+	  Saying Y here will enlarge your kernel by about 27 KB.
+
+	  To compile this as a module, choose M here: the module will be called
+	  sysv.
+
+	  If you haven't heard about all of this before, it's safe to say N.
+
+
+
+config UFS_FS
+	tristate "UFS file system support (read only)"
+	help
+	  BSD and derivate versions of Unix (such as SunOS, FreeBSD, NetBSD,
+	  OpenBSD and NeXTstep) use a file system called UFS. Some System V
+	  Unixes can create and mount hard disk partitions and diskettes using
+	  this file system as well. Saying Y here will allow you to read from
+	  these partitions; if you also want to write to them, say Y to the
+	  experimental "UFS file system write support", below. Please read the
+	  file <file:Documentation/filesystems/ufs.txt> for more information.
+
+          The recently released UFS2 variant (used in FreeBSD 5.x) is
+          READ-ONLY supported.
+
+	  If you only intend to mount files from some other Unix over the
+	  network using NFS, you don't need the UFS file system support (but
+	  you need NFS file system support obviously).
+
+	  Note that this option is generally not needed for floppies, since a
+	  good portable way to transport files and directories between unixes
+	  (and even other operating systems) is given by the tar program ("man
+	  tar" or preferably "info tar").
+
+	  When accessing NeXTstep files, you may need to convert them from the
+	  NeXT character set to the Latin1 character set; use the program
+	  recode ("info recode") for this purpose.
+
+	  To compile the UFS file system support as a module, choose M here: the
+	  module will be called ufs.
+
+	  If you haven't heard about all of this before, it's safe to say N.
+
+config UFS_FS_WRITE
+	bool "UFS file system write support (DANGEROUS)"
+	depends on UFS_FS && EXPERIMENTAL
+	help
+	  Say Y here if you want to try writing to UFS partitions. This is
+	  experimental, so you should back up your UFS partitions beforehand.
+
+endmenu
+
+menu "Network File Systems"
+	depends on NET
+
+config NFS_FS
+	tristate "NFS file system support"
+	depends on INET
+	select LOCKD
+	select SUNRPC
+	select NFS_ACL_SUPPORT if NFS_V3_ACL
+	help
+	  If you are connected to some other (usually local) Unix computer
+	  (using SLIP, PLIP, PPP or Ethernet) and want to mount files residing
+	  on that computer (the NFS server) using the Network File Sharing
+	  protocol, say Y. "Mounting files" means that the client can access
+	  the files with usual UNIX commands as if they were sitting on the
+	  client's hard disk. For this to work, the server must run the
+	  programs nfsd and mountd (but does not need to have NFS file system
+	  support enabled in its kernel). NFS is explained in the Network
+	  Administrator's Guide, available from
+	  <http://www.tldp.org/docs.html#guide>, on its man page: "man
+	  nfs", and in the NFS-HOWTO.
+
+	  A superior but less widely used alternative to NFS is provided by
+	  the Coda file system; see "Coda file system support" below.
+
+	  If you say Y here, you should have said Y to TCP/IP networking also.
+	  This option would enlarge your kernel by about 27 KB.
+
+	  To compile this file system support as a module, choose M here: the
+	  module will be called nfs.
+
+	  If you are configuring a diskless machine which will mount its root
+	  file system over NFS at boot time, say Y here and to "Kernel
+	  level IP autoconfiguration" above and to "Root file system on NFS"
+	  below. You cannot compile this driver as a module in this case.
+	  There are two packages designed for booting diskless machines over
+	  the net: netboot, available from
+	  <http://ftp1.sourceforge.net/netboot/>, and Etherboot,
+	  available from <http://ftp1.sourceforge.net/etherboot/>.
+
+	  If you don't know what all this is about, say N.
+
+config NFS_V3
+	bool "Provide NFSv3 client support"
+	depends on NFS_FS
+	help
+	  Say Y here if you want your NFS client to be able to speak version
+	  3 of the NFS protocol.
+
+	  If unsure, say Y.
+
+config NFS_V3_ACL
+	bool "Provide client support for the NFSv3 ACL protocol extension"
+	depends on NFS_V3
+	help
+	  Implement the NFSv3 ACL protocol extension for manipulating POSIX
+	  Access Control Lists.  The server should also be compiled with
+	  the NFSv3 ACL protocol extension; see the CONFIG_NFSD_V3_ACL option.
+
+	  If unsure, say N.
+
+config NFS_V4
+	bool "Provide NFSv4 client support (EXPERIMENTAL)"
+	depends on NFS_FS && EXPERIMENTAL
+	select RPCSEC_GSS_KRB5
+	help
+	  Say Y here if you want your NFS client to be able to speak the newer
+	  version 4 of the NFS protocol.
+
+	  Note: Requires auxiliary userspace daemons which may be found on
+		http://www.citi.umich.edu/projects/nfsv4/
+
+	  If unsure, say N.
+
+config NFS_DIRECTIO
+	bool "Allow direct I/O on NFS files (EXPERIMENTAL)"
+	depends on NFS_FS && EXPERIMENTAL
+	help
+	  This option enables applications to perform uncached I/O on files
+	  in NFS file systems using the O_DIRECT open() flag.  When O_DIRECT
+	  is set for a file, its data is not cached in the system's page
+	  cache.  Data is moved to and from user-level application buffers
+	  directly.  Unlike local disk-based file systems, NFS O_DIRECT has
+	  no alignment restrictions.
+
+	  Unless your program is designed to use O_DIRECT properly, you are
+	  much better off allowing the NFS client to manage data caching for
+	  you.  Misusing O_DIRECT can cause poor server performance or network
+	  storms.  This kernel build option defaults OFF to avoid exposing
+	  system administrators unwittingly to a potentially hazardous
+	  feature.
+
+	  For more details on NFS O_DIRECT, see fs/nfs/direct.c.
+
+	  If unsure, say N.  This reduces the size of the NFS client, and
+	  causes open() to return EINVAL if a file residing in NFS is
+	  opened with the O_DIRECT flag.
+
+config NFSD
+	tristate "NFS server support"
+	depends on INET
+	select LOCKD
+	select SUNRPC
+	select EXPORTFS
+	select NFS_ACL_SUPPORT if NFSD_V3_ACL || NFSD_V2_ACL
+	help
+	  If you want your Linux box to act as an NFS *server*, so that other
+	  computers on your local network which support NFS can access certain
+	  directories on your box transparently, you have two options: you can
+	  use the self-contained user space program nfsd, in which case you
+	  should say N here, or you can say Y and use the kernel based NFS
+	  server. The advantage of the kernel based solution is that it is
+	  faster.
+
+	  In either case, you will need support software; the respective
+	  locations are given in the file <file:Documentation/Changes> in the
+	  NFS section.
+
+	  If you say Y here, you will get support for version 2 of the NFS
+	  protocol (NFSv2). If you also want NFSv3, say Y to the next question
+	  as well.
+
+	  Please read the NFS-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  To compile the NFS server support as a module, choose M here: the
+	  module will be called nfsd.  If unsure, say N.
+
+config NFSD_V2_ACL
+	bool
+	depends on NFSD
+
+config NFSD_V3
+	bool "Provide NFSv3 server support"
+	depends on NFSD
+	help
+	  If you would like to include the NFSv3 server as well as the NFSv2
+	  server, say Y here.  If unsure, say Y.
+
+config NFSD_V3_ACL
+	bool "Provide server support for the NFSv3 ACL protocol extension"
+	depends on NFSD_V3
+	select NFSD_V2_ACL
+	help
+	  Implement the NFSv3 ACL protocol extension for manipulating POSIX
+	  Access Control Lists on exported file systems. NFS clients should
+	  be compiled with the NFSv3 ACL protocol extension; see the
+	  CONFIG_NFS_V3_ACL option.  If unsure, say N.
+
+config NFSD_V4
+	bool "Provide NFSv4 server support (EXPERIMENTAL)"
+	depends on NFSD_V3 && EXPERIMENTAL
+	select NFSD_TCP
+	select CRYPTO_MD5
+	select CRYPTO
+	select FS_POSIX_ACL
+	help
+	  If you would like to include the NFSv4 server as well as the NFSv2
+	  and NFSv3 servers, say Y here.  This feature is experimental, and
+	  should only be used if you are interested in helping to test NFSv4.
+	  If unsure, say N.
+
+config NFSD_TCP
+	bool "Provide NFS server over TCP support"
+	depends on NFSD
+	default y
+	help
+	  If you want your NFS server to support TCP connections, say Y here.
+	  TCP connections usually perform better than the default UDP when
+	  the network is lossy or congested.  If unsure, say Y.
+
+config ROOT_NFS
+	bool "Root file system on NFS"
+	depends on NFS_FS=y && IP_PNP
+	help
+	  If you want your Linux box to mount its whole root file system (the
+	  one containing the directory /) from some other computer over the
+	  net via NFS (presumably because your box doesn't have a hard disk),
+	  say Y. Read <file:Documentation/nfsroot.txt> for details. It is
+	  likely that in this case, you also want to say Y to "Kernel level IP
+	  autoconfiguration" so that your box can discover its network address
+	  at boot time.
+
+	  Most people say N here.
+
+config LOCKD
+	tristate
+
+config LOCKD_V4
+	bool
+	depends on NFSD_V3 || NFS_V3
+	default y
+
+config EXPORTFS
+	tristate
+
+config NFS_ACL_SUPPORT
+	tristate
+	select FS_POSIX_ACL
+
+config NFS_COMMON
+	bool
+	depends on NFSD || NFS_FS
+	default y
+
+config SUNRPC
+	tristate
+
+config SUNRPC_GSS
+	tristate
+
+config RPCSEC_GSS_KRB5
+	tristate "Secure RPC: Kerberos V mechanism (EXPERIMENTAL)"
+	depends on SUNRPC && EXPERIMENTAL
+	select SUNRPC_GSS
+	select CRYPTO
+	select CRYPTO_MD5
+	select CRYPTO_DES
+	help
+	  Provides for secure RPC calls by means of a gss-api
+	  mechanism based on Kerberos V5. This is required for
+	  NFSv4.
+
+	  Note: Requires an auxiliary userspace daemon which may be found on
+		http://www.citi.umich.edu/projects/nfsv4/
+
+	  If unsure, say N.
+
+config RPCSEC_GSS_SPKM3
+	tristate "Secure RPC: SPKM3 mechanism (EXPERIMENTAL)"
+	depends on SUNRPC && EXPERIMENTAL
+	select SUNRPC_GSS
+	select CRYPTO
+	select CRYPTO_MD5
+	select CRYPTO_DES
+	help
+	  Provides for secure RPC calls by means of a gss-api
+	  mechanism based on the SPKM3 public-key mechanism.
+
+	  Note: Requires an auxiliary userspace daemon which may be found on
+	  	http://www.citi.umich.edu/projects/nfsv4/
+
+	  If unsure, say N.
+
+config SMB_FS
+	tristate "SMB file system support (to mount Windows shares etc.)"
+	depends on INET
+	select NLS
+	help
+	  SMB (Server Message Block) is the protocol Windows for Workgroups
+	  (WfW), Windows 95/98, Windows NT and OS/2 Lan Manager use to share
+	  files and printers over local networks.  Saying Y here allows you to
+	  mount their file systems (often called "shares" in this context) and
+	  access them just like any other Unix directory.  Currently, this
+	  works only if the Windows machines use TCP/IP as the underlying
+	  transport protocol, and not NetBEUI.  For details, read
+	  <file:Documentation/filesystems/smbfs.txt> and the SMB-HOWTO,
+	  available from <http://www.tldp.org/docs.html#howto>.
+
+	  Note: if you just want your box to act as an SMB *server* and make
+	  files and printing services available to Windows clients (which need
+	  to have a TCP/IP stack), you don't need to say Y here; you can use
+	  the program SAMBA (available from <ftp://ftp.samba.org/pub/samba/>)
+	  for that.
+
+	  General information about how to connect Linux, Windows machines and
+	  Macs is on the WWW at <http://www.eats.com/linux_mac_win.html>.
+
+	  To compile the SMB support as a module, choose M here: the module will
+	  be called smbfs.  Most people say N, however.
+
+config SMB_NLS_DEFAULT
+	bool "Use a default NLS"
+	depends on SMB_FS
+	help
+	  Enabling this will make smbfs use nls translations by default. You
+	  need to specify the local charset (CONFIG_NLS_DEFAULT) in the nls
+	  settings and you need to give the default nls for the SMB server as
+	  CONFIG_SMB_NLS_REMOTE.
+
+	  The nls settings can be changed at mount time, if your smbmount
+	  supports that, using the codepage and iocharset parameters.
+
+	  smbmount from samba 2.2.0 or later supports this.
+
+config SMB_NLS_REMOTE
+	string "Default Remote NLS Option"
+	depends on SMB_NLS_DEFAULT
+	default "cp437"
+	help
+	  This setting allows you to specify a default value for which
+	  codepage the server uses. If this field is left blank no
+	  translations will be done by default. The local codepage/charset
+	  default to CONFIG_NLS_DEFAULT.
+
+	  The nls settings can be changed at mount time, if your smbmount
+	  supports that, using the codepage and iocharset parameters.
+
+	  smbmount from samba 2.2.0 or later supports this.
+
+config CIFS
+	tristate "CIFS support (advanced network filesystem for Samba, Window and other CIFS compliant servers)"
+	depends on INET
+	select NLS
+	help
+	  This is the client VFS module for the Common Internet File System
+	  (CIFS) protocol which is the successor to the Server Message Block 
+	  (SMB) protocol, the native file sharing mechanism for most early
+	  PC operating systems.  The CIFS protocol is fully supported by 
+	  file servers such as Windows 2000 (including Windows 2003, NT 4  
+	  and Windows XP) as well by Samba (which provides excellent CIFS
+	  server support for Linux and many other operating systems). Limited
+	  support for Windows ME and similar servers is provided as well. 
+	  You must use the smbfs client filesystem to access older SMB servers
+	  such as OS/2 and DOS.
+
+	  The intent of the cifs module is to provide an advanced
+	  network file system client for mounting to CIFS compliant servers, 
+	  including support for dfs (hierarchical name space), secure per-user
+	  session establishment, safe distributed caching (oplock), optional
+	  packet signing, Unicode and other internationalization improvements, 
+	  and optional Winbind (nsswitch) integration. You do not need to enable
+	  cifs if running only a (Samba) server. It is possible to enable both
+	  smbfs and cifs (e.g. if you are using CIFS for accessing Windows 2003
+	  and Samba 3 servers, and smbfs for accessing old servers). If you need 
+	  to mount to Samba or Windows from this machine, say Y.
+
+config CIFS_STATS
+        bool "CIFS statistics"
+        depends on CIFS
+        help
+          Enabling this option will cause statistics for each server share
+	  mounted by the cifs client to be displayed in /proc/fs/cifs/Stats
+
+config CIFS_STATS2
+	bool "CIFS extended statistics"
+	depends on CIFS_STATS
+	help
+	  Enabling this option will allow more detailed statistics on SMB
+	  request timing to be displayed in /proc/fs/cifs/DebugData and also
+	  allow optional logging of slow responses to dmesg (depending on the
+	  value of /proc/fs/cifs/cifsFYI, see fs/cifs/README for more details).
+	  These additional statistics may have a minor effect on performance
+	  and memory utilization.
+
+	  Unless you are a developer or are doing network performance analysis
+	  or tuning, say N.
+
+config CIFS_XATTR
+        bool "CIFS extended attributes"
+        depends on CIFS
+        help
+          Extended attributes are name:value pairs associated with inodes by
+          the kernel or by users (see the attr(5) manual page, or visit
+          <http://acl.bestbits.at/> for details).  CIFS maps the name of
+          extended attributes beginning with the user namespace prefix
+          to SMB/CIFS EAs. EAs are stored on Windows servers without the
+          user namespace prefix, but their names are seen by Linux cifs clients
+          prefaced by the user namespace prefix. The system namespace
+          (used by some filesystems to store ACLs) is not supported at
+          this time.
+
+          If unsure, say N.
+
+config CIFS_POSIX
+        bool "CIFS POSIX Extensions"
+        depends on CIFS_XATTR
+        help
+          Enabling this option will cause the cifs client to attempt to
+	  negotiate a newer dialect with servers, such as Samba 3.0.5
+	  or later, that optionally can handle more POSIX like (rather
+	  than Windows like) file behavior.  It also enables
+	  support for POSIX ACLs (getfacl and setfacl) to servers
+	  (such as Samba 3.10 and later) which can negotiate
+	  CIFS POSIX ACL support.  If unsure, say N.
+
+config CIFS_EXPERIMENTAL
+	  bool "CIFS Experimental Features (EXPERIMENTAL)"
+	  depends on CIFS && EXPERIMENTAL
+	  help
+	    Enables cifs features under testing. These features are
+	    experimental and currently include support for writepages
+	    (multipage writebehind performance improvements) and directory
+	    change notification ie fcntl(F_DNOTIFY) as well as some security
+	    improvements.  Some also depend on setting at runtime the
+	    pseudo-file /proc/fs/cifs/Experimental (which is disabled by
+	    default). See the file fs/cifs/README for more details.
+
+	    If unsure, say N.
+
+config CIFS_UPCALL
+	  bool "CIFS Kerberos/SPNEGO advanced session setup (EXPERIMENTAL)"
+	  depends on CIFS_EXPERIMENTAL
+	  select CONNECTOR
+	  help
+	    Enables an upcall mechanism for CIFS which will be used to contact
+	    userspace helper utilities to provide SPNEGO packaged Kerberos
+	    tickets which are needed to mount to certain secure servers
+	    (for which more secure Kerberos authentication is required). If
+	    unsure, say N.
+
+config NCP_FS
+	tristate "NCP file system support (to mount NetWare volumes)"
+	depends on IPX!=n || INET
+	help
+	  NCP (NetWare Core Protocol) is a protocol that runs over IPX and is
+	  used by Novell NetWare clients to talk to file servers.  It is to
+	  IPX what NFS is to TCP/IP, if that helps.  Saying Y here allows you
+	  to mount NetWare file server volumes and to access them just like
+	  any other Unix directory.  For details, please read the file
+	  <file:Documentation/filesystems/ncpfs.txt> in the kernel source and
+	  the IPX-HOWTO from <http://www.tldp.org/docs.html#howto>.
+
+	  You do not have to say Y here if you want your Linux box to act as a
+	  file *server* for Novell NetWare clients.
+
+	  General information about how to connect Linux, Windows machines and
+	  Macs is on the WWW at <http://www.eats.com/linux_mac_win.html>.
+
+	  To compile this as a module, choose M here: the module will be called
+	  ncpfs.  Say N unless you are connected to a Novell network.
+
+source "fs/ncpfs/Kconfig"
+
+config CODA_FS
+	tristate "Coda file system support (advanced network fs)"
+	depends on INET
+	help
+	  Coda is an advanced network file system, similar to NFS in that it
+	  enables you to mount file systems of a remote server and access them
+	  with regular Unix commands as if they were sitting on your hard
+	  disk.  Coda has several advantages over NFS: support for
+	  disconnected operation (e.g. for laptops), read/write server
+	  replication, security model for authentication and encryption,
+	  persistent client caches and write back caching.
+
+	  If you say Y here, your Linux box will be able to act as a Coda
+	  *client*.  You will need user level code as well, both for the
+	  client and server.  Servers are currently user level, i.e. they need
+	  no kernel support.  Please read
+	  <file:Documentation/filesystems/coda.txt> and check out the Coda
+	  home page <http://www.coda.cs.cmu.edu/>.
+
+	  To compile the coda client support as a module, choose M here: the
+	  module will be called coda.
+
+config CODA_FS_OLD_API
+	bool "Use 96-bit Coda file identifiers"
+	depends on CODA_FS
+	help
+	  A new kernel-userspace API had to be introduced for Coda v6.0
+	  to support larger 128-bit file identifiers as needed by the
+	  new realms implementation.
+
+	  However this new API is not backward compatible with older
+	  clients. If you really need to run the old Coda userspace
+	  cache manager then say Y.
+	  
+	  For most cases you probably want to say N.
+
+config AFS_FS
+# for fs/nls/Config.in
+	tristate "Andrew File System support (AFS) (Experimental)"
+	depends on INET && EXPERIMENTAL
+	select RXRPC
+	help
+	  If you say Y here, you will get an experimental Andrew File System
+	  driver. It currently only supports unsecured read-only AFS access.
+
+	  See <file:Documentation/filesystems/afs.txt> for more intormation.
+
+	  If unsure, say N.
+
+config RXRPC
+	tristate
+
+config 9P_FS
+	tristate "Plan 9 Resource Sharing Support (9P2000) (Experimental)"
+	depends on INET && EXPERIMENTAL
+	help
+	  If you say Y here, you will get experimental support for
+	  Plan 9 resource sharing via the 9P2000 protocol.
+
+	  See <http://v9fs.sf.net> for more information.
+
+	  If unsure, say N.
+
+endmenu
+
+menu "Partition Types"
+
+source "fs/partitions/Kconfig"
+
+endmenu
+
+source "fs/nls/Kconfig"
+
+endmenu
+
diff -urN oldtree/fs/Makefile newtree/fs/Makefile
--- oldtree/fs/Makefile	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/Makefile	2006-02-13 19:20:00.668410688 -0500
@@ -84,6 +84,7 @@
 obj-$(CONFIG_JFFS_FS)		+= jffs/
 obj-$(CONFIG_JFFS2_FS)		+= jffs2/
 obj-$(CONFIG_AFFS_FS)		+= affs/
+obj-$(CONFIG_ASFS_FS)		+= asfs/
 obj-$(CONFIG_ROMFS_FS)		+= romfs/
 obj-$(CONFIG_QNX4FS_FS)		+= qnx4/
 obj-$(CONFIG_AUTOFS_FS)		+= autofs/
diff -urN oldtree/fs/asfs/Changes newtree/fs/asfs/Changes
--- oldtree/fs/asfs/Changes	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/asfs/Changes	2006-02-13 19:20:00.669410536 -0500
@@ -0,0 +1,112 @@
+
+Amiga Smart File System, Linux implementation
+
+Please direct bug reports to: marek@amiga.pl
+
+History:
+
+v1.0beta10 (13.06.2005)
+- fixed ugly bug introduced in beta9 that caused kernel crash on x86
+  (thanks to Emiliano for reporting it!)
+
+v1.0beta9 (17.03.2005)
+- added NLS support (thanks to Pavel Fedin!)
+
+v1.0beta8 (07.01.2005)
+- adapted to 2.6.10 kernel VFS changes
+- added workaround for buggy Mandrake kernel headers
+
+v1.0beta7 (25.06.2004)
+- small changes in documentation
+- code clean up: bitfuncs.c, super.c, inode.c, *.h, Makefile, added
+  asfs_ prefix to function names, made some functions static
+  (big thanks to Christoph Hellwig for advice!)
+- fixed minor bugs (inode leak in super.c, not-realesed buffer during
+  object renaming in inode.c)
+- now files/dirs are created with global ownership/permission bits
+
+v1.0beta6 (04.06.2004)
+- fixed: ASFS_SB(sb)->flags was always zero in 2.6.x code
+
+v1.0beta5 (07.05.2004)
+- finally fixed a problem with file size attrib. not being written
+  to disk
+- fixed some problems with GCC 3.x and debug enabled
+
+v1.0beta4 (12.04.2004)
+- removed dummy asfs_notify_change (this fixes major bug introduced
+  in 1.0beta3 - file size wasn't written to disk) until it will
+  be implemented completely
+
+v1.0beta3 (22.03.2004) - still beta
+- updated for 2.6.x kernels VFS changes
+- code clean-up
+- added dummy asfs_notify_change (chmod now returns no errors)
+- added symlinks write support
+- fixed: ASFS_SB(sb)->flags was always zero
+
+v1.0beta2 (11.01.2004) - special version for Pegasos][ kernel
+- separated read and write functions, can be compiled also
+  as read-only fs
+
+v1.0beta1 (02.12.2003) - first public beta with write support
+- added dentry hashing/comparing routines
+- code clean-up
+
+v1.0aplha4 (30.11.2003) - preparing for first public beta
+- fixed some problems with renaming/moving files
+- fixed two major bugs, which didn't occur when fs was mounted
+  on loopback device (newly allocated blocks were not written to
+  disk and state bits were not set correctly on newly mapped file
+  blocks)
+- fixed many small bugs in io code (some buffers were not freed)
+- added/modified sb locks in asfs_lookup and asfs_getblock
+- fixed serious bug in file block allocation routines
+
+v1.0aplha3 (23.11.2003)
+- added (hopefully) all byteswap code, should now work again on
+  little-endian systems (also with write support!)
+- updated documentation
+
+v1.0alpha2 (13.11.2003)
+- now alocates file blocks in chunks during one request
+- fixed some dead-locks, other fixes
+
+v1.0alpha (02.11.2003) - first working version with full write support
+- too much to list it here ;)
+
+... (working on write support)
+
+v0.7 (12.10.2003) - internal realase
+- added asfs_breadcheck, modified asfs_get_node, asfs_search_BTree,
+  no more from_be32/16 macros, other...
+- code splitted into several files
+
+v0.6 (04.09.2003) - final read-only version
+- added support for HashTables, directory scaning should be
+  MUCH faster now
+- added checking of block IDs before reading any data from block
+
+v0.5 (19.07.2003)
+- added simple but effective extent cache - real speed-up
+  in reading large files
+- added read support for symlinks - based on AFFS symlinks
+
+v0.4 (10.07.2003)
+- third code clean-up (thanks to Roman Zippel for advice)
+- now uses generic readpage and readinode routines
+
+v0.3beta (17.06.2003)
+- second code clean-up
+
+v0.2beta2 (15.06.2003)
+- fixed yet another stupid bug - driver can't read root block on little-endian systems
+v0.2beta (15.06.2003)
+- fixed stupid bug - now files have 'file' flag (S_IFREG) set...
+- added mount options to set uid, gid and mode of files and dirs
+- made hidden files & dirs really hidden (= not listed in directories)
+- code clean-up
+
+v0.1beta (11.06.2003)
+- after many kernel crashes, finally got it!
+- first working read-only filesystem driver
diff -urN oldtree/fs/asfs/Makefile newtree/fs/asfs/Makefile
--- oldtree/fs/asfs/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/asfs/Makefile	2006-02-13 19:20:00.669410536 -0500
@@ -0,0 +1,8 @@
+#
+# Makefile for the linux asfs filesystem routines.
+#
+
+obj-$(CONFIG_ASFS_FS) += asfs.o
+
+asfs-y += dir.o extents.o file.o inode.o namei.o nodes.o objects.o super.o symlink.o
+asfs-$(CONFIG_ASFS_RW) += adminspace.o bitfuncs.o
diff -urN oldtree/fs/asfs/adminspace.c newtree/fs/asfs/adminspace.c
--- oldtree/fs/asfs/adminspace.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/asfs/adminspace.c	2006-02-13 19:20:00.670410384 -0500
@@ -0,0 +1,446 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ * version: 1.0beta7
+ *
+ * This file contains some parts of the original amiga version of
+ * SmartFilesystem source code.
+ *
+ * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx,
+ * Ralph Schmidt, Emmanuel Lesueur, David Gerber, and Marcin Kurek
+ *
+ * Adapted and modified by Marek 'March' Szyprowski <marek@amiga.pl>
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/vfs.h>
+#include "asfs_fs.h"
+#include "bitfuncs.h"
+
+#include <asm/byteorder.h>
+
+#ifdef CONFIG_ASFS_RW
+
+static int setfreeblocks(struct super_block *sb, u32 freeblocks)
+{
+	struct buffer_head *bh;
+	if ((bh = asfs_breadcheck(sb, ASFS_SB(sb)->rootobjectcontainer, ASFS_OBJECTCONTAINER_ID))) {
+		struct fsRootInfo *ri = (struct fsRootInfo *) ((u8 *) bh->b_data + sb->s_blocksize - sizeof(struct fsRootInfo));
+		ASFS_SB(sb)->freeblocks = freeblocks;
+		ri->freeblocks = cpu_to_be32(freeblocks);
+		asfs_bstore(sb, bh);
+		asfs_brelse(bh);
+		return 0;
+	}
+	return -EIO;
+}
+
+static inline int enoughspace(struct super_block *sb, u32 blocks)
+{
+	if (ASFS_SB(sb)->freeblocks - ASFS_ALWAYSFREE < blocks)
+		return FALSE;
+
+	return TRUE;
+}
+
+	/* Determines the amount of free blocks starting from block /block/.
+	   If there are no blocks found or if there was an error -1 is returned,
+	   otherwise this function will count the number of free blocks until
+	   an allocated block is encountered or until maxneeded has been
+	   exceeded. */
+
+static int availablespace(struct super_block *sb, u32 block, u32 maxneeded)
+{
+	struct buffer_head *bh = NULL;
+	struct fsBitmap *b;
+	u32 longs = ASFS_SB(sb)->blocks_inbitmap >> 5;
+	u32 maxbitmapblock = ASFS_SB(sb)->bitmapbase + ASFS_SB(sb)->blocks_bitmap;
+	int blocksfound = 0;
+	u32 bitstart;
+	int bitend;
+	u32 nextblock = ASFS_SB(sb)->bitmapbase + block / ASFS_SB(sb)->blocks_inbitmap;
+
+	bitstart = block % ASFS_SB(sb)->blocks_inbitmap;
+
+	while (nextblock < maxbitmapblock && (bh = asfs_breadcheck(sb, nextblock++, ASFS_BITMAP_ID))) {
+		b = (void *) bh->b_data;
+
+		if ((bitend = bmffz(b->bitmap, longs, bitstart)) >= 0) {
+			blocksfound += bitend - bitstart;
+			asfs_brelse(bh);
+			return blocksfound;
+		}
+		blocksfound += ASFS_SB(sb)->blocks_inbitmap - bitstart;
+		if (blocksfound >= maxneeded) {
+			asfs_brelse(bh);
+			return blocksfound;
+		}
+		bitstart = 0;
+		asfs_brelse(bh);
+	}
+
+	if (bh == NULL)
+		return (-1);
+
+	return (blocksfound);
+}
+
+int asfs_findspace(struct super_block *sb, u32 maxneeded, u32 start, u32 end, u32 * returned_block, u32 * returned_blocks)
+{
+	struct buffer_head *bh;
+	u32 longs = ASFS_SB(sb)->blocks_inbitmap >> 5;
+	u32 space = 0;
+	u32 block;
+	u32 bitmapblock = ASFS_SB(sb)->bitmapbase + start / ASFS_SB(sb)->blocks_inbitmap;
+	u32 break_point;
+	int bitstart, bitend;
+	int reads;
+
+	if (enoughspace(sb, maxneeded) == FALSE) {
+		*returned_block = 0;
+		*returned_blocks = 0;
+		return -ENOSPC;
+	}
+
+	if (start >= ASFS_SB(sb)->totalblocks)
+		start -= ASFS_SB(sb)->totalblocks;
+
+	if (end == 0)
+		end = ASFS_SB(sb)->totalblocks;
+
+	reads = ((end - 1) / ASFS_SB(sb)->blocks_inbitmap) + 1 - start / ASFS_SB(sb)->blocks_inbitmap;
+
+	if (start >= end)
+		reads += (ASFS_SB(sb)->totalblocks - 1) / ASFS_SB(sb)->blocks_inbitmap + 1;
+
+	break_point = (start < end ? end : ASFS_SB(sb)->totalblocks);
+
+	*returned_block = 0;
+	*returned_blocks = 0;
+
+	bitend = start % ASFS_SB(sb)->blocks_inbitmap;
+	block = start - bitend;
+
+	while ((bh = asfs_breadcheck(sb, bitmapblock++, ASFS_BITMAP_ID))) {
+		struct fsBitmap *b = (void *) bh->b_data;
+		u32 localbreak_point = break_point - block;
+
+		if (localbreak_point > ASFS_SB(sb)->blocks_inbitmap)
+			localbreak_point = ASFS_SB(sb)->blocks_inbitmap;
+
+		/* At this point space contains the amount of free blocks at
+		   the end of the previous bitmap block.  If there are no
+		   free blocks at the start of this bitmap block, space will
+		   be set to zero, since in that case the space isn't adjacent. */
+
+		while ((bitstart = bmffo(b->bitmap, longs, bitend)) < ASFS_SB(sb)->blocks_inbitmap) {
+			/* found the start of an empty space, now find out how large it is */
+
+			if (bitstart >= localbreak_point)
+				break;
+
+			if (bitstart != 0)
+				space = 0;
+
+			bitend = bmffz(b->bitmap, longs, bitstart);
+
+			if (bitend > localbreak_point)
+				bitend = localbreak_point;
+
+			space += bitend - bitstart;
+
+			if (*returned_blocks < space) {
+				*returned_block = block + bitend - space;
+				if (space >= maxneeded) {
+					*returned_blocks = maxneeded;
+					asfs_brelse(bh);
+					return 0;
+				}
+				*returned_blocks = space;
+			}
+
+			if (bitend >= localbreak_point)
+				break;
+		}
+
+		if (--reads == 0)
+			break;
+
+		/* no (more) empty spaces found in this block */
+
+		if (bitend != ASFS_SB(sb)->blocks_inbitmap)
+			space = 0;
+
+		bitend = 0;
+		block += ASFS_SB(sb)->blocks_inbitmap;
+
+		if (block >= ASFS_SB(sb)->totalblocks) {
+			block = 0;
+			space = 0;
+			break_point = end;
+			bitmapblock = ASFS_SB(sb)->bitmapbase;
+		}
+		asfs_brelse(bh);
+	}
+
+	if (bh == NULL)
+		return -EIO;
+
+	asfs_brelse(bh);
+
+	if (*returned_blocks == 0)
+		return -ENOSPC;
+	else
+		return 0;
+}
+
+int asfs_markspace(struct super_block *sb, u32 block, u32 blocks)
+{
+	int errorcode;
+
+	asfs_debug("markspace: Marking %d blocks from block %d\n", blocks, block);
+
+	if ((availablespace(sb, block, blocks)) < blocks) {
+		printk("ASFS: Attempted to mark %d blocks from block %d, but some of them were already full!\n", blocks, block);
+		return -EIO;
+	}
+
+	if ((errorcode = setfreeblocks(sb, ASFS_SB(sb)->freeblocks - blocks)) == 0) {
+		struct buffer_head *bh;
+		u32 skipblocks = block / ASFS_SB(sb)->blocks_inbitmap;
+		u32 longs = (sb->s_blocksize - sizeof(struct fsBitmap)) >> 2;
+		u32 bitmapblock;
+
+		block -= skipblocks * ASFS_SB(sb)->blocks_inbitmap;
+		bitmapblock = ASFS_SB(sb)->bitmapbase + skipblocks;
+
+		while (blocks > 0) {
+			if ((bh = asfs_breadcheck(sb, bitmapblock++, ASFS_BITMAP_ID))) {
+				struct fsBitmap *b = (void *) bh->b_data;
+
+				blocks -= bmclr(b->bitmap, longs, block, blocks);
+				block = 0;
+
+				asfs_bstore(sb, bh);
+				asfs_brelse(bh);
+			} else
+				return -EIO;
+		}
+	}
+
+	return (errorcode);
+}
+
+	/* This function checks the bitmap and tries to locate at least /blocksneeded/
+	   adjacent unused blocks.  If found it sets returned_block to the start block
+	   and returns no error.  If not found, ERROR_DISK_IS_FULL is returned and
+	   returned_block is set to zero.  Any other errors are returned as well. */
+
+static inline int internalfindspace(struct super_block *sb, u32 blocksneeded, u32 startblock, u32 endblock, u32 * returned_block)
+{
+	u32 blocks;
+	int errorcode;
+
+	if ((errorcode = asfs_findspace(sb, blocksneeded, startblock, endblock, returned_block, &blocks)) == 0)
+		if (blocks != blocksneeded)
+			return -ENOSPC;
+
+	return errorcode;
+}
+
+static int findandmarkspace(struct super_block *sb, u32 blocksneeded, u32 * returned_block)
+{
+	int errorcode;
+
+	if (enoughspace(sb, blocksneeded) != FALSE) {
+		if ((errorcode = internalfindspace(sb, blocksneeded, 0, ASFS_SB(sb)->totalblocks, returned_block)) == 0)
+			errorcode = asfs_markspace(sb, *returned_block, blocksneeded);
+	} else
+		errorcode = -ENOSPC;
+
+	return (errorcode);
+}
+
+/* ************************** */
+
+int asfs_freespace(struct super_block *sb, u32 block, u32 blocks)
+{
+	int errorcode;
+
+	asfs_debug("freespace: Freeing %d blocks from block %d\n", blocks, block);
+
+	if ((errorcode = setfreeblocks(sb, ASFS_SB(sb)->freeblocks + blocks)) == 0) {
+		struct buffer_head *bh;
+		u32 skipblocks = block / ASFS_SB(sb)->blocks_inbitmap;
+		u32 longs = (sb->s_blocksize - sizeof(struct fsBitmap)) >> 2;
+		u32 bitmapblock;
+
+		block -= skipblocks * ASFS_SB(sb)->blocks_inbitmap;
+		bitmapblock = ASFS_SB(sb)->bitmapbase + skipblocks;
+
+		while (blocks > 0) {
+			if ((bh = asfs_breadcheck(sb, bitmapblock++, ASFS_BITMAP_ID))) {
+				struct fsBitmap *b = (void *) bh->b_data;
+
+				blocks -= bmset(b->bitmap, longs, block, blocks);
+				block = 0;
+
+				asfs_bstore(sb, bh);
+				asfs_brelse(bh);
+			} else
+				return -EIO;
+		}
+	}
+
+	return (errorcode);
+}
+
+/*************** admin space containers ****************/
+
+int asfs_allocadminspace(struct super_block *sb, u32 *returned_block)
+{
+	struct buffer_head *bh;
+	u32 adminspaceblock = ASFS_SB(sb)->adminspacecontainer;
+	int errorcode = -EIO;
+
+	asfs_debug("allocadminspace: allocating new block\n");
+
+	while ((bh = asfs_breadcheck(sb, adminspaceblock, ASFS_ADMINSPACECONTAINER_ID))) {
+		struct fsAdminSpaceContainer *asc1 = (void *) bh->b_data;
+		struct fsAdminSpace *as1 = asc1->adminspace;
+		int adminspaces1 = (sb->s_blocksize - sizeof(struct fsAdminSpaceContainer)) / sizeof(struct fsAdminSpace);
+
+		while (adminspaces1-- > 0) {
+			s16 bitoffset;
+
+			if (as1->space != 0 && (bitoffset = bfffz(be32_to_cpu(as1->bits), 0)) >= 0) {
+				u32 emptyadminblock = be32_to_cpu(as1->space) + bitoffset;
+				as1->bits |= cpu_to_be32(1 << (31 - bitoffset));
+				asfs_bstore(sb, bh);
+				*returned_block = emptyadminblock;
+				asfs_brelse(bh);
+				asfs_debug("allocadminspace: found block %d\n", *returned_block);
+				return 0;
+			}
+			as1++;
+		}
+
+		adminspaceblock = be32_to_cpu(asc1->next);
+		asfs_brelse(bh);
+
+		if (adminspaceblock == 0) {
+			u32 startblock;
+
+			asfs_debug("allocadminspace: allocating new adminspace area\n");
+
+			/* If we get here it means current adminspace areas are all filled.
+			   We would now need to find a new area and create a fsAdminSpace
+			   structure in one of the AdminSpaceContainer blocks.  If these
+			   don't have any room left for new adminspace areas a new
+			   AdminSpaceContainer would have to be created first which is
+			   placed as the first block in the newly found admin area. */
+
+			adminspaceblock = ASFS_SB(sb)->adminspacecontainer;
+
+			if ((errorcode = findandmarkspace(sb, 32, &startblock)))
+				return errorcode;
+
+			while ((bh = asfs_breadcheck(sb, adminspaceblock, ASFS_ADMINSPACECONTAINER_ID))) {
+				struct fsAdminSpaceContainer *asc2 = (void *) bh->b_data;
+				struct fsAdminSpace *as2 = asc2->adminspace;
+				int adminspaces2 = (sb->s_blocksize - sizeof(struct fsAdminSpaceContainer)) / sizeof(struct fsAdminSpace);
+
+				while (adminspaces2-- > 0 && as2->space != 0)
+					as2++;
+
+				if (adminspaces2 >= 0) {	/* Found a unused AdminSpace in this AdminSpaceContainer! */
+					as2->space = cpu_to_be32(startblock);
+					as2->bits = 0;
+					asfs_bstore(sb, bh);
+					asfs_brelse(bh);
+					break;
+				}
+
+				if (asc2->next == 0) {
+					/* Oh-oh... we marked our new adminspace area in use, but we couldn't
+					   find space to store a fsAdminSpace structure in the existing
+					   fsAdminSpaceContainer blocks.  This means we need to create and
+					   link a new fsAdminSpaceContainer as the first block in our newly
+					   marked adminspace. */
+
+					asc2->next = cpu_to_be32(startblock);
+					asfs_bstore(sb, bh);
+					asfs_brelse(bh);
+
+					/* Now preparing new AdminSpaceContainer */
+
+					if ((bh = asfs_getzeroblk(sb, startblock)) == NULL)
+						return -EIO;
+
+					asc2 = (void *) bh->b_data;
+					asc2->bheader.id = cpu_to_be32(ASFS_ADMINSPACECONTAINER_ID);
+					asc2->bheader.ownblock = cpu_to_be32(startblock);
+					asc2->previous = cpu_to_be32(adminspaceblock);
+					asc2->adminspace[0].space = cpu_to_be32(startblock);
+					asc2->adminspace[0].bits = cpu_to_be32(0x80000000);
+					asc2->bits = 32;
+
+					asfs_bstore(sb, bh);
+					asfs_brelse(bh);
+
+					adminspaceblock = startblock;
+					break;	/* Breaks through to outer loop! */
+				}
+				adminspaceblock = be32_to_cpu(asc2->next);
+				asfs_brelse(bh);
+			}
+		}
+	}
+	return errorcode;
+}
+
+int asfs_freeadminspace(struct super_block *sb, u32 block)
+{
+	struct buffer_head *bh;
+	u32 adminspaceblock = ASFS_SB(sb)->adminspacecontainer;
+
+	asfs_debug("freeadminspace: Entry -- freeing block %d\n", block);
+
+	while ((bh = asfs_breadcheck(sb, adminspaceblock, ASFS_ADMINSPACECONTAINER_ID))) {
+		struct fsAdminSpaceContainer *asc = (void *) bh->b_data;
+		struct fsAdminSpace *as = asc->adminspace;
+		int adminspaces = (sb->s_blocksize - sizeof(struct fsAdminSpaceContainer)) / sizeof(struct fsAdminSpace);
+
+		while (adminspaces-- > 0) {
+			if (block >= be32_to_cpu(as->space) && block < be32_to_cpu(as->space) + 32) {
+				s16 bitoffset = block - be32_to_cpu(as->space);
+				asfs_debug("freeadminspace: Block to be freed is located in AdminSpaceContainer block at %d\n", adminspaceblock);
+				as->bits &= cpu_to_be32(~(1 << (31 - bitoffset)));
+				asfs_bstore(sb, bh);
+				asfs_brelse(bh);
+				return 0;
+			}
+			as++;
+		}
+
+		if ((adminspaceblock = be32_to_cpu(asc->next)) == 0)
+			break;
+
+		asfs_brelse(bh);
+	}
+
+	if (bh != NULL) {
+		asfs_brelse(bh);
+		printk("ASFS: Unable to free an administration block. The block cannot be found.");
+		return -ENOENT;
+	}
+
+	return -EIO;
+}
+
+#endif
diff -urN oldtree/fs/asfs/asfs_fs.h newtree/fs/asfs/asfs_fs.h
--- oldtree/fs/asfs/asfs_fs.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/asfs/asfs_fs.h	2006-02-13 19:20:00.670410384 -0500
@@ -0,0 +1,222 @@
+#ifndef __LINUX_ASFS_FS_H
+#define __LINUX_ASFS_FS_H
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <asm/byteorder.h>
+#include <linux/amigasfs.h>
+
+#define asfs_debug(fmt,arg...) /* no debug at all */
+//#define asfs_debug(fmt,arg...) printk(fmt,##arg)  /* general debug infos */
+
+#if !defined (__BIG_ENDIAN) && !defined (__LITTLE_ENDIAN)
+#error Endianes must be known for ASFS to work. Sorry.
+#endif
+
+#define ASFS_MAXFN_BUF (ASFS_MAXFN + 4)
+#define ASFS_DEFAULT_UID 0
+#define ASFS_DEFAULT_GID 0
+#define ASFS_DEFAULT_MODE 0644	/* default permission bits for files, dirs have same permission, but with "x" set */
+
+/* Extent structure located in RAM (e.g. inside inode structure),
+   currently used to store last used extent */
+
+struct inramExtent {
+	u32 startblock;	/* Block from begginig of the file */
+	u32 key;
+	u32 next;
+	u16 blocks;
+};
+
+/* inode in-kernel data */
+
+struct asfs_inode_info {
+	u32 firstblock;
+	u32 hashtable;
+	int modified;
+	loff_t mmu_private;
+	struct inramExtent ext_cache;
+	struct inode vfs_inode;
+};
+
+/* short cut to get to the asfs specific inode data */
+static inline struct asfs_inode_info *ASFS_I(struct inode *inode)
+{
+   return list_entry(inode, struct asfs_inode_info, vfs_inode);
+}
+
+/* Amiga SFS superblock in-core data */
+
+struct asfs_sb_info {
+	u32 totalblocks;
+	u32 rootobjectcontainer;
+	u32 extentbnoderoot;
+	u32 objectnoderoot;
+
+	u32 adminspacecontainer;
+	u32 bitmapbase;
+	u32 freeblocks;
+	u32 blocks_inbitmap;
+	u32 blocks_bitmap;
+	u32 block_rovingblockptr;
+
+	uid_t uid;
+	gid_t gid;
+	umode_t mode;
+	u16 flags;
+	char *prefix;
+	char *root_volume;		/* Volume prefix for absolute symlinks. */
+	char *iocharset;
+	char *codepage;
+	struct nls_table *nls_io;
+	struct nls_table *nls_disk;
+};
+
+/* short cut to get to the asfs specific sb data */
+static inline struct asfs_sb_info *ASFS_SB(struct super_block *sb)
+{
+	return sb->s_fs_info;
+}
+
+/* io inline code */
+
+u32 asfs_calcchecksum(void *block, u32 blocksize);
+
+static inline int
+asfs_check_block(struct fsBlockHeader *block, u32 blocksize, u32 n, u32 id)
+{
+	if (asfs_calcchecksum(block, blocksize) ==
+	    be32_to_cpu(((struct fsBlockHeader *) block)->checksum) &&
+	    n == be32_to_cpu(((struct fsBlockHeader *) block)->ownblock) &&
+	    id == be32_to_cpu(((struct fsBlockHeader *) block)->id))
+		return TRUE;
+	return FALSE;
+}
+
+/* get fs structure from block and do some checks... */
+static inline struct buffer_head *
+asfs_breadcheck(struct super_block *sb, u32 n, u32 type)
+{
+	struct buffer_head *bh;
+	if ((bh = sb_bread(sb, n))) {
+		if (asfs_check_block ((void *)bh->b_data, sb->s_blocksize, n, type)) {
+			return bh;	/* all okay */
+		}
+		brelse(bh);
+	}
+	return NULL;		/* error */
+}
+
+static inline struct buffer_head *
+asfs_getzeroblk(struct super_block *sb, int block)
+{
+	struct buffer_head *bh;
+	bh = sb_getblk(sb, block);
+	lock_buffer(bh);
+	memset(bh->b_data, 0, sb->s_blocksize);
+	set_buffer_uptodate(bh);
+	unlock_buffer(bh);
+	return bh;
+}
+
+static inline void
+asfs_bstore(struct super_block *sb, struct buffer_head *bh)
+{
+	((struct fsBlockHeader *) (bh->b_data))->checksum =
+	    cpu_to_be32(asfs_calcchecksum(bh->b_data, sb->s_blocksize));
+	mark_buffer_dirty(bh);
+}
+
+static inline void asfs_brelse(struct buffer_head *bh)
+{
+	brelse(bh);
+}
+
+static inline void dec_count(struct inode *inode)
+{
+	inode->i_nlink--;
+	mark_inode_dirty(inode);
+}
+
+/* all prototypes */
+
+/* adminspace.c */
+int asfs_allocadminspace(struct super_block *sb, u32 * block);
+int asfs_freeadminspace(struct super_block *sb, u32 block);
+int asfs_markspace(struct super_block *sb, u32 block, u32 blocks);
+int asfs_freespace(struct super_block *sb, u32 block, u32 blocks);
+int asfs_findspace(struct super_block *sb, u32 maxneeded, u32 start, u32 end,
+	      u32 * returned_block, u32 * returned_blocks);
+
+/* dir.c */
+int asfs_readdir(struct file *filp, void *dirent, filldir_t filldir);
+struct dentry *asfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd);
+
+/* extents.c */
+int asfs_getextent(struct super_block *sb, u32 key, struct buffer_head **ret_bh,
+	      struct fsExtentBNode **ret_ebn);
+int asfs_deletebnode(struct super_block *sb, struct buffer_head *cb, u32 key);
+int asfs_deleteextents(struct super_block *sb, u32 key);
+int asfs_addblocks(struct super_block *sb, u16 blocks, u32 newspace,
+	      u32 objectnode, u32 * io_lastextentbnode);
+
+/* file.c */
+int asfs_readpage(struct file *file, struct page *page);
+sector_t asfs_bmap(struct address_space *mapping, sector_t block);
+int asfs_writepage(struct page *page, struct writeback_control *wbc);
+int asfs_prepare_write(struct file *file, struct page *page, unsigned from,
+		       unsigned to);
+void asfs_truncate(struct inode *inode);
+int asfs_file_open(struct inode *inode, struct file *filp);
+int asfs_file_release(struct inode *inode, struct file *filp);
+
+/* inode.c */
+struct inode *asfs_get_root_inode(struct super_block *sb);
+void asfs_read_locked_inode(struct inode *inode, void *arg);
+int asfs_notify_change(struct dentry *dentry, struct iattr *attr);
+
+/* namei */
+u8 asfs_lowerchar(u8 c);
+int asfs_check_name(const u8 *name, int len);
+int asfs_namecmp(u8 *s, u8 *ct, int casesensitive, struct nls_table *t);
+u16 asfs_hash(u8 *name, int casesensitive);
+void asfs_translate(u8 *to, u8 *from, struct nls_table *nls_to, struct nls_table *nls_from, int limit);
+
+/* nodes */
+int asfs_getnode(struct super_block *sb, u32 nodeno,
+	    struct buffer_head **ret_bh, struct fsObjectNode **ret_node);
+int asfs_createnode(struct super_block *sb, struct buffer_head **returned_cb,
+	       struct fsNode **returned_node, u32 * returned_nodeno);
+int asfs_deletenode(struct super_block *sb, u32 objectnode);
+
+/* objects */
+struct fsObject *asfs_nextobject(struct fsObject *obj);
+struct fsObject *asfs_find_obj_by_name(struct super_block *sb,
+		struct fsObjectContainer *objcont, u8 * name);
+int asfs_readobject(struct super_block *sb, u32 objectnode,
+	       struct buffer_head **cb, struct fsObject **returned_object);
+int asfs_createobject(struct super_block *sb, struct buffer_head **io_cb,
+		 struct fsObject **io_o, struct fsObject *src_o,
+		 u8 * objname, int force);
+int asfs_deleteobject(struct super_block *sb, struct buffer_head *cb,
+		 struct fsObject *o);
+int asfs_renameobject(struct super_block *sb, struct buffer_head *cb1,
+		 struct fsObject *o1, struct buffer_head *cbparent,
+		 struct fsObject *oparent, u8 * newname);
+
+int asfs_addblockstofile(struct super_block *sb, struct buffer_head *objcb,
+		    struct fsObject *o, u32 blocks, u32 * newspace,
+		    u32 * addedblocks);
+int asfs_truncateblocksinfile(struct super_block *sb, struct buffer_head *bh,
+			 struct fsObject *o, u32 newsize);
+
+/* super.c */
+struct super_block *asfs_read_super(struct super_block *sb, void *data,
+				    int silent);
+
+/* symlink.c */
+int asfs_symlink_readpage(struct file *file, struct page *page);
+int asfs_write_symlink(struct inode *symfile, const char *symname);
+
+#endif
diff -urN oldtree/fs/asfs/bitfuncs.c newtree/fs/asfs/bitfuncs.c
--- oldtree/fs/asfs/bitfuncs.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/asfs/bitfuncs.c	2006-02-13 19:20:00.670410384 -0500
@@ -0,0 +1,171 @@
+/*
+ *
+ * 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 the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+#include "bitfuncs.h"
+
+/* Bitmap (bm) functions:
+   These functions perform bit-operations on regions of memory which
+   are a multiple of 4 bytes in length. Bitmap is in bigendian byte order.
+*/
+
+/* This function finds the first set bit in a region of memory starting
+   with /bitoffset/.  The region of memory is /longs/ longs long.  It
+   returns the bitoffset of the first set bit it finds. */
+
+int bmffo(u32 *bitmap, int longs, int bitoffset)
+{
+	u32 *scan = bitmap;
+	int longoffset, bit;
+
+	longoffset = bitoffset >> 5;
+	longs -= longoffset;
+	scan += longoffset;
+
+	bitoffset = bitoffset & 0x1F;
+
+	if (bitoffset != 0) {
+		if ((bit = bfffo(be32_to_cpu(*scan), bitoffset)) >= 0) {
+			return (bit + ((scan - bitmap) << 5));
+		}
+		scan++;
+		longs--;
+	}
+
+	while (longs-- > 0) {
+		if (*scan++ != 0) {
+			return (bfffo(be32_to_cpu(*--scan), 0) + ((scan - bitmap) << 5));
+		}
+	}
+
+	return (-1);
+}
+
+/* This function finds the first unset bit in a region of memory starting
+   with /bitoffset/.  The region of memory is /longs/ longs long.  It
+   returns the bitoffset of the first unset bit it finds. */
+
+int bmffz(u32 *bitmap, int longs, int bitoffset)
+{
+	u32 *scan = bitmap;
+	int longoffset, bit;
+
+	longoffset = bitoffset >> 5;
+	longs -= longoffset;
+	scan += longoffset;
+
+	bitoffset = bitoffset & 0x1F;
+
+	if (bitoffset != 0) {
+		if ((bit = bfffz(be32_to_cpu(*scan), bitoffset)) >= 0) {
+			return (bit + ((scan - bitmap) << 5));
+		}
+		scan++;
+		longs--;
+	}
+
+	while (longs-- > 0) {
+		if (*scan++ != 0xFFFFFFFF) {
+			return (bfffz(be32_to_cpu(*--scan), 0) + ((scan - bitmap) << 5));
+		}
+	}
+
+	return (-1);
+}
+
+/* This function clears /bits/ bits in a region of memory starting
+   with /bitoffset/.  The region of memory is /longs/ longs long.  If
+   the region of memory is too small to clear /bits/ bits then this
+   function exits after having cleared all bits till the end of the
+   memory region.  In any case it returns the number of bits which
+   were actually cleared. */
+
+int bmclr(u32 *bitmap, int longs, int bitoffset, int bits)
+{
+	u32 *scan = bitmap;
+	int longoffset;
+	int orgbits = bits;
+
+	longoffset = bitoffset >> 5;
+	longs -= longoffset;
+	scan += longoffset;
+
+	bitoffset = bitoffset & 0x1F;
+
+	if (bitoffset != 0) {
+		if (bits < 32) {
+			*scan = cpu_to_be32(bfclr(be32_to_cpu(*scan), bitoffset, bits));
+		} else {
+			*scan = cpu_to_be32(bfclr(be32_to_cpu(*scan), bitoffset, 32));
+		}
+		scan++;
+		longs--;
+		bits -= 32 - bitoffset;
+	}
+
+	while (bits > 0 && longs-- > 0) {
+		if (bits > 31) {
+			*scan++ = 0;
+		} else {
+			*scan = cpu_to_be32(bfclr(be32_to_cpu(*scan), 0, bits));
+		}
+		bits -= 32;
+	}
+
+	if (bits <= 0) {
+		return (orgbits);
+	}
+	return (orgbits - bits);
+}
+
+/* This function sets /bits/ bits in a region of memory starting
+   with /bitoffset/.  The region of memory is /longs/ longs long.  If
+   the region of memory is too small to set /bits/ bits then this
+   function exits after having set all bits till the end of the
+   memory region.  In any case it returns the number of bits which
+   were actually set. */
+
+int bmset(u32 *bitmap, int longs, int bitoffset, int bits)
+{
+	u32 *scan = bitmap;
+	int longoffset;
+	int orgbits = bits;
+
+	longoffset = bitoffset >> 5;
+	longs -= longoffset;
+	scan += longoffset;
+
+	bitoffset = bitoffset & 0x1F;
+
+	if (bitoffset != 0) {
+		if (bits < 32) {
+			*scan = cpu_to_be32(bfset(be32_to_cpu(*scan), bitoffset, bits));
+		} else {
+			*scan = cpu_to_be32(bfset(be32_to_cpu(*scan), bitoffset, 32));
+		}
+		scan++;
+		longs--;
+		bits -= 32 - bitoffset;
+	}
+
+	while (bits > 0 && longs-- > 0) {
+		if (bits > 31) {
+			*scan++ = 0xFFFFFFFF;
+		} else {
+			*scan = cpu_to_be32(bfset(be32_to_cpu(*scan), 0, bits));
+		}
+		bits -= 32;
+	}
+
+	if (bits <= 0) {
+		return (orgbits);
+	}
+	return (orgbits - bits);
+}
diff -urN oldtree/fs/asfs/bitfuncs.h newtree/fs/asfs/bitfuncs.h
--- oldtree/fs/asfs/bitfuncs.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/asfs/bitfuncs.h	2006-02-13 19:20:00.671410232 -0500
@@ -0,0 +1,59 @@
+/*
+ *
+ * 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 the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef __BITFUNCS_H
+#define __BITFUNCS_H
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+#include <asm/bitops.h>
+#include <linux/bitops.h>
+
+/* Finds first set bit in /data/ starting at /bitoffset/.  This function
+   considers the MSB to be the first bit. */
+static inline int bfffo(u32 data, int bitoffset)
+{
+	u32 mask = 0xffffffff >> bitoffset;
+	data &= mask;
+	return data == 0 ? -1 : 32-fls(data);
+}
+
+/* Finds first zero bit in /data/ starting at /bitoffset/.  This function
+   considers the MSB to be the first bit. */
+static inline int bfffz(u32 data, int bitoffset)
+{
+	return bfffo(~data, bitoffset);
+}
+
+/* Sets /bits/ bits starting from /bitoffset/ in /data/.
+   /bits/ must be between 1 and 32. */
+static inline u32 bfset(u32 data, int bitoffset, int bits)
+{
+	u32 mask = ~((1 << (32 - bits)) - 1);
+	mask >>= bitoffset;
+	return data | mask;
+}
+
+/* Clears /bits/ bits starting from /bitoffset/ in /data/.
+   /bits/ must be between 1 and 32. */
+static inline u32 bfclr(u32 data, int bitoffset, int bits)
+{
+	u32 mask = ~((1 << (32 - bits)) - 1);
+	mask >>= bitoffset;
+	return data & ~mask;
+}
+
+/* bm??? functions assumes that in-memory bitmap is in bigendian byte order */
+int bmffo(u32 *, int, int);
+int bmffz(u32 *, int, int);
+int bmclr(u32 *, int, int, int);
+int bmset(u32 *, int, int, int);
+
+#endif
diff -urN oldtree/fs/asfs/dir.c newtree/fs/asfs/dir.c
--- oldtree/fs/asfs/dir.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/asfs/dir.c	2006-02-13 19:20:00.671410232 -0500
@@ -0,0 +1,240 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ * version: 1.0beta7
+ *
+ * Copyright (C) 2003,2004  Marek 'March' Szyprowski <marek@amiga.pl>
+ *
+ *
+ * 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 the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/vfs.h>
+#include "asfs_fs.h"
+
+#include <asm/byteorder.h>
+
+extern struct dentry_operations asfs_dentry_operations;
+
+int asfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+	struct inode *dir = filp->f_dentry->d_inode;
+	struct super_block *sb = dir->i_sb;
+	struct nls_table *nls_io = ASFS_SB(sb)->nls_io;
+	struct nls_table *nls_disk = ASFS_SB(sb)->nls_disk;
+	u8 buf[512];
+	unsigned long f_pos;
+	int stored = 0;
+
+	struct buffer_head *bh;
+	struct fsObjectContainer *objcont;
+	struct fsObject *obj;
+	u32 block;
+	int startnode;
+	int add;
+
+	asfs_debug("asfs_readdir:\n");
+
+	if (filp->f_pos == ASFS_SB(sb)->totalblocks)
+		return stored;
+
+	f_pos = filp->f_pos;
+
+	if (f_pos == 0) {
+		filp->private_data = (void *)0;
+		if (filldir(dirent, ".", 1, f_pos, dir->i_ino, DT_DIR) < 0)
+			return 0;
+		filp->f_pos = f_pos = 1;
+		stored++;
+	}
+	if (f_pos == 1) {
+		if (filldir(dirent, "..", 2, f_pos, parent_ino(filp->f_dentry), DT_DIR) < 0)
+			return stored;
+		filp->f_pos = f_pos = 2;
+		stored++;
+	}
+
+	if (ASFS_I(dir)->firstblock == 0) {	/* empty directory */
+		filp->f_pos = ASFS_SB(sb)->totalblocks;
+		ASFS_I(dir)->modified = 0;
+		return stored;
+	}
+
+	if (f_pos == 2) {	/* reading directory from its beginning */
+		block = ASFS_I(dir)->firstblock;
+		add = 1;
+		startnode = 0;
+	} else {
+		startnode = (int)filp->private_data;
+		add = 0;
+		if (ASFS_I(dir)->modified == 0)
+			block = f_pos;
+		else
+			block = ASFS_I(dir)->firstblock;
+	}
+
+	do {
+		if (!(bh = asfs_breadcheck(sb, block, ASFS_OBJECTCONTAINER_ID)))
+			return stored;
+		objcont = (struct fsObjectContainer *) bh->b_data;
+		obj = &(objcont->object[0]);
+
+		while (be32_to_cpu(obj->objectnode) > 0 &&
+		      ((char *)obj - (char *)objcont) + sizeof(struct fsObject) + 2 < sb->s_blocksize) {
+
+			if (!add && be32_to_cpu(obj->objectnode) == startnode)
+				add++;
+
+			if (add && !(obj->bits & OTYPE_HIDDEN)) {
+				unsigned int type;
+				asfs_translate(buf, obj->name, nls_io, nls_disk, 512);
+				asfs_debug("ASFS: DirFilling: entry #%d \"%s\" (node %u offset %u), type %x\n", \
+				           stored, buf, be32_to_cpu(obj->objectnode), block, obj->bits);
+				filp->f_pos = block;
+
+				if (obj->bits & OTYPE_DIR)
+					type = DT_DIR;
+				else if (obj->bits & OTYPE_LINK && !(obj->bits & OTYPE_HARDLINK))
+					type = DT_LNK;
+				else
+					type = DT_REG;
+
+				if (filldir(dirent, buf, strlen(buf), block, be32_to_cpu(obj->objectnode), type) < 0) {
+					filp->private_data = (void *)be32_to_cpu(obj->objectnode);
+					ASFS_I(dir)->modified = 0;
+					asfs_debug("ASFS: DirFilling: to be continued...\n");
+					asfs_brelse(bh);
+					return stored;
+				}
+				stored++;
+			}
+			obj = asfs_nextobject(obj);
+		}
+		block = be32_to_cpu(objcont->next);
+		asfs_brelse(bh);
+
+	} while (block != 0);
+
+	filp->f_pos = ASFS_SB(sb)->totalblocks;
+	ASFS_I(dir)->modified = 0;
+
+	return stored;
+}
+
+static struct fsObject *asfs_find_obj_by_name_nls(struct super_block *sb, struct fsObjectContainer *objcont, u8 * name)
+{
+	struct fsObject *obj;
+	u8 buf[512];
+
+	obj = &(objcont->object[0]);
+	while (be32_to_cpu(obj->objectnode) > 0 && ((char *) obj - (char *) objcont) + sizeof(struct fsObject) + 2 < sb->s_blocksize) {
+		asfs_translate(buf, obj->name, ASFS_SB(sb)->nls_io, ASFS_SB(sb)->nls_disk, 512);
+		if (asfs_namecmp(buf, name, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE, ASFS_SB(sb)->nls_io) == 0) {
+			asfs_debug("Object found! Node %u, Name %s, Type %x, inCont %u\n", be32_to_cpu(obj->objectnode), obj->name, obj->bits, be32_to_cpu(objcont->bheader.ownblock));
+			return obj;
+		}
+		obj = asfs_nextobject(obj);
+	}
+	return NULL;
+}
+
+struct dentry *asfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
+{
+	int res = -EACCES;       /* placeholder for "no data here" */
+	struct inode *inode;
+	struct super_block *sb = dir->i_sb;
+	u8 *name = (u8 *) dentry->d_name.name;
+	struct buffer_head *bh;
+	struct fsObject *obj;
+	u8 bufname[ASFS_MAXFN_BUF];
+
+	asfs_translate(bufname, name, ASFS_SB(sb)->nls_disk, ASFS_SB(sb)->nls_io, ASFS_MAXFN_BUF);
+
+	asfs_debug("asfs_lookup: (searching \"%s\"...) ", name);
+
+	lock_super(sb);
+
+	if ((!strchr(name, '?')) && (ASFS_I(dir)->hashtable != 0)) {	/* hashtable block is available and name can be reverse translated, quick search */
+		struct fsObjectNode *node_p;
+		struct buffer_head *node_bh;
+		u32 node;
+		u16 hash16;
+
+		asfs_debug("(quick search) ");
+
+		if (!(bh = asfs_breadcheck(sb, ASFS_I(dir)->hashtable, ASFS_HASHTABLE_ID))) {
+			unlock_super(sb);
+			return ERR_PTR(res);
+		}
+		hash16 = asfs_hash(bufname, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE);
+		node = be32_to_cpu(((struct fsHashTable *) bh->b_data)->hashentry[HASHCHAIN(hash16)]);
+		asfs_brelse(bh);
+
+		while (node != 0) {
+			if (asfs_getnode(sb, node, &node_bh, &node_p) != 0)
+				goto not_found;
+			if (be16_to_cpu(node_p->hash16) == hash16) {
+				if (!(bh = asfs_breadcheck(sb, be32_to_cpu(node_p->node.data), ASFS_OBJECTCONTAINER_ID))) {
+					asfs_brelse(node_bh);
+					unlock_super(sb);
+					return ERR_PTR(res);
+				}
+				if ((obj = asfs_find_obj_by_name(sb, (struct fsObjectContainer *) bh->b_data, bufname)) != NULL) {
+					asfs_brelse(node_bh);
+					goto found_inode;
+				}
+				asfs_brelse(bh);
+			}
+			node = be32_to_cpu(node_p->next);
+			asfs_brelse(node_bh);
+		}
+	} else { /* hashtable not available or name can't be reverse-translated, long search */
+		struct fsObjectContainer *objcont;
+		u32 block;
+
+		asfs_debug("(long search) ");
+		block = ASFS_I(dir)->firstblock;
+		while (block != 0) {
+			if (!(bh = asfs_breadcheck(sb, block, ASFS_OBJECTCONTAINER_ID))) {
+				unlock_super(sb);
+				return ERR_PTR(res);
+			}
+			objcont = (struct fsObjectContainer *) bh->b_data;
+			if ((obj = asfs_find_obj_by_name_nls(sb, objcont, name)) != NULL)
+				goto found_inode;
+			block = be32_to_cpu(objcont->next);
+			asfs_brelse(bh);
+		}
+	}
+
+not_found:
+	unlock_super(sb);
+	inode = NULL;
+	asfs_debug("object not found.\n");
+	if (0) {
+found_inode:
+		unlock_super(sb);
+		if (!(inode = iget_locked(sb, be32_to_cpu(obj->objectnode)))) {
+			asfs_debug("ASFS: Strange - no inode allocated.\n");
+			return ERR_PTR(res);
+		}
+		if (inode->i_state & I_NEW) {
+			asfs_read_locked_inode(inode, obj);
+			unlock_new_inode(inode);
+		}
+		asfs_brelse(bh);
+	}
+	res = 0;
+	dentry->d_op = &asfs_dentry_operations;
+	d_add(dentry, inode);
+	return ERR_PTR(res);
+}
diff -urN oldtree/fs/asfs/extents.c newtree/fs/asfs/extents.c
--- oldtree/fs/asfs/extents.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/asfs/extents.c	2006-02-13 19:20:00.672410080 -0500
@@ -0,0 +1,588 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ * version: 1.0beta7
+ *
+ * This file contains some parts of the original amiga version of
+ * SmartFilesystem source code.
+ *
+ * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx,
+ * Ralph Schmidt, Emmanuel Lesueur, David Gerber and Marcin Kurek
+ *
+ * Adapted and modified by Marek 'March' Szyprowski <marek@amiga.pl>
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/vfs.h>
+#include "asfs_fs.h"
+
+#include <asm/byteorder.h>
+
+	/* This function looks for the BNode equal to the key.  If no
+	   exact match is available then the BNode which is slightly
+	   lower than key will be returned.  If no such BNode exists
+	   either, then the first BNode in this block is returned.
+
+	   This function will return the first BNode even if there
+	   are no BNode's at all in this block (this can only happen
+	   for the Root of the tree).  Be sure to check if the Root
+	   is not empty before calling this function. */
+
+static struct BNode *searchforbnode(u32 key, struct BTreeContainer *tc)
+{
+	struct BNode *tn;
+	s16 n = be16_to_cpu(tc->nodecount) - 1;
+
+	tn = (struct BNode *) ((u8 *) tc->bnode + n * tc->nodesize);
+	for (;;) {
+		if (n <= 0 || key >= be32_to_cpu(tn->key))
+			return tn;
+
+		tn = (struct BNode *) ((u8 *) tn - tc->nodesize);
+		n--;
+	}
+}
+
+/* This function finds the BNode with the given key.  If no exact match can be
+   found then this function will return either the next or previous closest
+   match (don't rely on this).
+
+   If there were no BNode's at all, then *returned_bh will be NULL. */
+
+static int findbnode(struct super_block *sb, u32 key, struct buffer_head **returned_bh, struct BNode **returned_bnode)
+{
+	u32 rootblock = ASFS_SB(sb)->extentbnoderoot;
+
+	asfs_debug("findbnode: Looking for BNode with key %d\n", key);
+
+	while ((*returned_bh = asfs_breadcheck(sb, rootblock, ASFS_BNODECONTAINER_ID))) {
+		struct fsBNodeContainer *bnc = (void *) (*returned_bh)->b_data;
+		struct BTreeContainer *btc = &bnc->btc;
+
+		if (btc->nodecount == 0) {
+			*returned_bnode = NULL;
+			break;
+		}
+
+		*returned_bnode = searchforbnode(key, btc);
+		if (btc->isleaf == TRUE)
+			break;
+
+		rootblock = be32_to_cpu((*returned_bnode)->data);
+		asfs_brelse(*returned_bh);
+	}
+
+	if (*returned_bh == NULL)
+		return -EIO;
+
+	return 0;
+}
+
+int asfs_getextent(struct super_block *sb, u32 key, struct buffer_head **ret_bh, struct fsExtentBNode **ret_ebn)
+{
+	int result;
+	if ((result = findbnode(sb, key, ret_bh, (struct BNode **)ret_ebn)) == 0)
+		if (be32_to_cpu((*ret_ebn)->key) != key) {
+			brelse(*ret_bh);
+			*ret_bh = NULL;
+			return -ENOENT;
+		}
+
+	return result;
+}
+
+#ifdef CONFIG_ASFS_RW
+
+	/* This routine inserts a node sorted into a BTreeContainer.  It does
+	   this by starting at the end, and moving the nodes one by one to
+	   a higher slot until the empty slot has the correct position for
+	   this key.  Donot use this function on completely filled
+	   BTreeContainers! */
+
+static struct BNode *insertbnode(u32 key, struct BTreeContainer *btc)
+{
+	struct BNode *bn;
+	bn = (struct BNode *) ((u8 *) btc->bnode + btc->nodesize * (be16_to_cpu(btc->nodecount) - 1));
+
+	for (;;) {
+		if (bn < btc->bnode || key > be32_to_cpu(bn->key)) {
+			bn = (struct BNode *) ((u8 *) bn + btc->nodesize);
+			bn->key = cpu_to_be32(key);
+			btc->nodecount = cpu_to_be16(be16_to_cpu(btc->nodecount) + 1);
+			break;
+		} else
+			memmove((u8 *)bn + btc->nodesize, bn, btc->nodesize);
+
+		bn = (struct BNode *) ((u8 *) bn - btc->nodesize);
+	}
+
+	return bn;
+}
+
+static int getparentbtreecontainer(struct super_block *sb, struct buffer_head *bh, struct buffer_head **parent_bh)
+{
+	u32 rootblock = ASFS_SB(sb)->extentbnoderoot;
+	u32 childkey = be32_to_cpu(((struct fsBNodeContainer *) bh->b_data)->btc.bnode[0].key);
+	u32 childblock = be32_to_cpu(((struct fsBNodeContainer *) bh->b_data)->bheader.ownblock);
+
+	asfs_debug("getparentbtreecontainer: Getting parent of block %d\n", childblock);
+
+	/* This function gets the BTreeContainer parent of the passed in buffer_head. If
+	   there is no parent this function sets dest_cont io_bh to NULL */
+
+	if (rootblock != childblock) {
+		while ((*parent_bh = asfs_breadcheck(sb, rootblock, ASFS_BNODECONTAINER_ID))) {
+			struct fsBNodeContainer *bnc = (void *) (*parent_bh)->b_data;
+			struct BTreeContainer *btc = &bnc->btc;
+			struct BNode *bn;
+			s16 n = be16_to_cpu(btc->nodecount);
+
+			if (btc->isleaf == TRUE) {
+				asfs_brelse(*parent_bh);
+				break;
+			}
+
+			while (n-- > 0)
+				if (be32_to_cpu(btc->bnode[n].data) == childblock)
+					return 0;	/* Found parent!! */
+
+			bn = searchforbnode(childkey, btc);	/* This searchforbnode() doesn't have to get EXACT key matches. */
+			rootblock = be32_to_cpu(bn->data);
+			asfs_brelse(*parent_bh);
+		}
+		if (*parent_bh == NULL)
+			return -EIO;
+	}
+
+	*parent_bh = NULL;
+	return 0;
+}
+
+/* Spits a btreecontainer. It realses passed in bh! */
+
+static int splitbtreecontainer(struct super_block *sb, struct buffer_head *bh)
+{
+	struct buffer_head *bhparent;
+	struct BNode *bn;
+	int errorcode;
+
+	asfs_debug("splitbtreecontainer: splitting block %u\n", be32_to_cpu(((struct fsBlockHeader *) bh->b_data)->ownblock));
+
+	if ((errorcode = getparentbtreecontainer(sb, bh, &bhparent)) == 0) {
+		if (bhparent == NULL) {
+			u32 newbcontblock;
+			u32 bcontblock;
+			/* We need to create Root tree-container - adding new level to extent tree */
+
+			asfs_debug("splitbtreecontainer: creating root tree-container.\n");
+
+			bhparent = bh;
+			if ((errorcode = asfs_allocadminspace(sb, &newbcontblock)) == 0 && (bh = asfs_getzeroblk(sb, newbcontblock))) {
+				struct fsBNodeContainer *bnc = (void *) bh->b_data;
+				struct fsBNodeContainer *bncparent = (void *) bhparent->b_data;
+				struct BTreeContainer *btcparent = &bncparent->btc;
+
+				bcontblock = be32_to_cpu(bncparent->bheader.ownblock);
+				memcpy(bh->b_data, bhparent->b_data, sb->s_blocksize);
+				bnc->bheader.ownblock = cpu_to_be32(newbcontblock);
+				asfs_bstore(sb, bh);
+
+				memset(bhparent->b_data, '\0', sb->s_blocksize);	/* Not strictly needed, but makes things more clear. */
+				bncparent->bheader.id = cpu_to_be32(ASFS_BNODECONTAINER_ID);
+				bncparent->bheader.ownblock = cpu_to_be32(bcontblock);
+				btcparent->isleaf = FALSE;
+				btcparent->nodesize = sizeof(struct BNode);
+				btcparent->nodecount = 0;
+
+				bn = insertbnode(0, btcparent);
+				bn->data = cpu_to_be32(newbcontblock);
+
+				asfs_bstore(sb, bhparent);
+			}
+			if (bh == NULL)
+				errorcode = -EIO;
+		}
+
+		if (errorcode == 0) {
+			struct fsBNodeContainer *bncparent = (void *) bhparent->b_data;
+			struct BTreeContainer *btcparent = &bncparent->btc;
+			int branches1 = (sb->s_blocksize - sizeof(struct fsBNodeContainer)) / btcparent->nodesize;
+
+			if (be16_to_cpu(btcparent->nodecount) == branches1) {
+				/* We need to split the parent tree-container first! */
+				if ((errorcode = splitbtreecontainer(sb, bhparent)) == 0) {
+					/* bhparent might have changed after the split and has been released */
+					if ((errorcode = getparentbtreecontainer(sb, bh, &bhparent)) == 0) {
+						bncparent = (void *) bhparent->b_data;
+						btcparent = &bncparent->btc;
+					}
+				}
+			}
+
+			if (errorcode == 0) {
+				u32 newbcontblock;
+				struct buffer_head *bhnew;
+
+				/* We can split this container and add it to the parent
+				   because the parent has enough room. */
+
+				if ((errorcode = asfs_allocadminspace(sb, &newbcontblock)) == 0 && (bhnew = asfs_getzeroblk(sb, newbcontblock))) {
+					struct fsBNodeContainer *bncnew = (void *) bhnew->b_data;
+					struct BTreeContainer *btcnew = &bncnew->btc;
+					struct fsBNodeContainer *bnc = (void *) bh->b_data;
+					struct BTreeContainer *btc = &bnc->btc;
+					int branches2 = (sb->s_blocksize - sizeof(struct fsBNodeContainer)) / btc->nodesize;
+					u32 newkey;
+
+					bncnew->bheader.id = cpu_to_be32(ASFS_BNODECONTAINER_ID);
+					bncnew->bheader.ownblock = cpu_to_be32(newbcontblock);
+
+					btcnew->isleaf = btc->isleaf;
+					btcnew->nodesize = btc->nodesize;
+
+					btcnew->nodecount = cpu_to_be16(branches2 - branches2 / 2);
+
+					memcpy(btcnew->bnode, (u8 *) btc->bnode + branches2 / 2 * btc->nodesize, (branches2 - branches2 / 2) * btc->nodesize);
+					newkey = be32_to_cpu(btcnew->bnode[0].key);
+
+					asfs_bstore(sb, bhnew);
+					asfs_brelse(bhnew);
+
+					btc->nodecount = cpu_to_be16(branches2 / 2);
+					asfs_bstore(sb, bh);
+
+					bn = insertbnode(newkey, btcparent);
+					bn->data = cpu_to_be32(newbcontblock);
+					asfs_bstore(sb, bhparent);
+				}
+			}
+		}
+		asfs_brelse(bhparent);
+	}
+	asfs_brelse(bh);
+
+	return errorcode;
+}
+
+/* Returns created extentbnode - returned_bh need to saved and realesed in caller funkction! */
+
+static int createextentbnode(struct super_block *sb, u32 key,
+			     struct buffer_head **returned_bh,
+			     struct BNode **returned_bnode)
+{
+	int errorcode;
+
+	asfs_debug("createbnode: Creating BNode with key %d\n", key);
+
+	while ((errorcode = findbnode(sb, key, returned_bh, returned_bnode)) == 0) {
+		struct fsBNodeContainer *bnc = (void *) (*returned_bh)->b_data;
+		struct BTreeContainer *btc = &bnc->btc;
+		int extbranches = (sb->s_blocksize - sizeof(struct fsBNodeContainer)) / btc->nodesize;
+
+		asfs_debug("createbnode: findbnode found block %d\n", be32_to_cpu(((struct fsBlockHeader *) (*returned_bh)->b_data)->ownblock));
+
+		if (be16_to_cpu(btc->nodecount) < extbranches) {
+			/* Simply insert new node in this BTreeContainer */
+			asfs_debug("createbnode: Simple insert\n");
+			*returned_bnode = insertbnode(key, btc);
+			break;
+		} else if ((errorcode = splitbtreecontainer(sb, *returned_bh)) != 0)
+			break;
+
+		/* Loop and try insert it the normal way again :-) */
+	}
+
+	return (errorcode);
+}
+
+
+/* This routine removes a node from a BTreeContainer indentified
+   by its key.  If no such key exists this routine does nothing.
+   It correctly handles empty BTreeContainers. */
+
+static void removebnode(u32 key, struct BTreeContainer *btc)
+{
+	struct BNode *bn = btc->bnode;
+	int n = 0;
+
+	asfs_debug("removebnode: key %d\n", key);
+
+	while (n < be16_to_cpu(btc->nodecount)) {
+		if (be32_to_cpu(bn->key) == key) {
+			btc->nodecount = cpu_to_be16(be16_to_cpu(btc->nodecount) - 1);
+			memmove(bn, (u8 *) bn + btc->nodesize, (be16_to_cpu(btc->nodecount) - n) * btc->nodesize);
+			break;
+		}
+		bn = (struct BNode *) ((u8 *) bn + btc->nodesize);
+		n++;
+	}
+}
+
+int asfs_deletebnode(struct super_block *sb, struct buffer_head *bh, u32 key)
+{
+	struct fsBNodeContainer *bnc1 = (void *) bh->b_data;
+	struct BTreeContainer *btc = &bnc1->btc;
+	u16 branches = (sb->s_blocksize - sizeof(struct fsBNodeContainer)) / btc->nodesize;
+	int errorcode = 0;
+
+	/* Deletes specified internal node. */
+
+	removebnode(key, btc);
+	asfs_bstore(sb, bh);
+
+	/* Now checks if the container still contains enough nodes,
+	   and takes action accordingly. */
+
+	asfs_debug("deletebnode: branches = %d, btc->nodecount = %d\n", branches, be16_to_cpu(btc->nodecount));
+
+	if (be16_to_cpu(btc->nodecount) < (branches + 1) / 2) {
+		struct buffer_head *bhparent;
+		struct buffer_head *bhsec;
+
+		/* nodecount has become to low.  We need to merge this Container
+		   with a neighbouring Container, or we need to steal a few nodes
+		   from a neighbouring Container. */
+
+		/* We get the parent of the container here, so we can find out what
+		   containers neighbour the container which currently hasn't got enough nodes. */
+
+		if ((errorcode = getparentbtreecontainer(sb, bh, &bhparent)) == 0) {
+			if (bhparent != NULL) {
+				struct fsBNodeContainer *bncparent = (void *) bhparent->b_data;
+				struct BTreeContainer *btcparent = &bncparent->btc;
+				s16 n;
+
+				asfs_debug("deletebnode: get parent returned block %d.\n", be32_to_cpu(((struct fsBlockHeader *) bhparent->b_data)->ownblock));
+
+				for (n = 0; n < be16_to_cpu(btcparent->nodecount); n++)
+					if (btcparent->bnode[n].data == bnc1->bheader.ownblock)
+						break;
+				/* n is now the offset of our own bnode. */
+
+				if (n < be16_to_cpu(btcparent->nodecount) - 1) {	/* Check if we have a next neighbour. */
+					asfs_debug("deletebnode: using next container - merging blocks %d and %d\n", be32_to_cpu(bnc1->bheader.ownblock), be32_to_cpu(btcparent->bnode[n+1].data));
+
+					if ((bhsec = asfs_breadcheck(sb, be32_to_cpu(btcparent->bnode[n + 1].data), ASFS_BNODECONTAINER_ID))) {
+						struct fsBNodeContainer *bnc_next = (void *) bhsec->b_data;
+						struct BTreeContainer *btc_next = &bnc_next->btc;
+
+						if (be16_to_cpu(btc_next->nodecount) + be16_to_cpu(btc->nodecount) > branches) {	/* Check if we need to steal nodes. */
+							s16 nodestosteal = (be16_to_cpu(btc_next->nodecount) + be16_to_cpu(btc->nodecount)) / 2 - be16_to_cpu(btc->nodecount);
+
+							/* Merging them is not possible.  Steal a few nodes then. */
+							memcpy((u8 *) btc->bnode + be16_to_cpu(btc->nodecount) * btc->nodesize, btc_next->bnode, nodestosteal * btc->nodesize);
+							btc->nodecount = cpu_to_be16(be16_to_cpu(btc->nodecount) + nodestosteal);
+							asfs_bstore(sb, bh);
+
+							memcpy(btc_next->bnode, (u8 *) btc_next->bnode + btc_next->nodesize * nodestosteal,
+							       btc->nodesize * (be16_to_cpu(btc_next->nodecount) - nodestosteal));
+							btc_next->nodecount = cpu_to_be16(be16_to_cpu(btc_next->nodecount) - nodestosteal);
+							asfs_bstore(sb, bhsec);
+
+							btcparent->bnode[n + 1].key = btc_next->bnode[0].key;
+							asfs_bstore(sb, bhparent);
+						} else {	/* Merging is possible. */
+							memcpy((u8 *) btc->bnode + btc->nodesize * be16_to_cpu(btc->nodecount), btc_next->bnode, btc->nodesize * be16_to_cpu(btc_next->nodecount));
+							btc->nodecount = cpu_to_be16(be16_to_cpu(btc->nodecount) + be16_to_cpu(btc_next->nodecount));
+							asfs_bstore(sb, bh);
+
+							if ((errorcode = asfs_freeadminspace(sb, be32_to_cpu(((struct fsBlockHeader *) bhsec->b_data)->ownblock))) == 0)
+								errorcode = asfs_deletebnode(sb, bhparent, be32_to_cpu(btcparent->bnode[n + 1].key));
+						}
+						asfs_brelse(bhsec);
+					}
+				} else if (n > 0) {	/* Check if we have a previous neighbour. */
+					asfs_debug("deletebnode: using prev container.\n");
+
+					if ((bhsec = asfs_breadcheck(sb, be32_to_cpu(btcparent->bnode[n - 1].data), ASFS_BNODECONTAINER_ID)) == 0) {
+						struct fsBNodeContainer *bnc2 = (void *) bhsec->b_data;
+						struct BTreeContainer *btc2 = &bnc2->btc;
+
+						if (be16_to_cpu(btc2->nodecount) + be16_to_cpu(btc->nodecount) > branches) {
+							/* Merging them is not possible.  Steal a few nodes then. */
+							s16 nodestosteal = (be16_to_cpu(btc2->nodecount) + be16_to_cpu(btc->nodecount)) / 2 - be16_to_cpu(btc->nodecount);
+
+							memmove((u8 *) btc->bnode + nodestosteal * btc->nodesize, btc->bnode, be16_to_cpu(btc->nodecount) * btc->nodesize);
+							btc->nodecount = cpu_to_be16(be16_to_cpu(btc->nodecount) + nodestosteal);
+							memcpy(btc->bnode, (u8 *) btc2->bnode + (be16_to_cpu(btc2->nodecount) - nodestosteal) * btc2->nodesize, nodestosteal * btc->nodesize);
+
+							asfs_bstore(sb, bh);
+
+							btc2->nodecount = cpu_to_be16(be16_to_cpu(btc2->nodecount) - nodestosteal);
+							asfs_bstore(sb, bhsec);
+
+							btcparent->bnode[n].key = btc->bnode[0].key;
+							asfs_bstore(sb, bhparent);
+						} else {	/* Merging is possible. */
+							memcpy((u8 *) btc2->bnode + be16_to_cpu(btc2->nodecount) * btc2->nodesize, btc->bnode, be16_to_cpu(btc->nodecount) * btc->nodesize);
+							btc2->nodecount = cpu_to_be16(be16_to_cpu(btc2->nodecount) + be16_to_cpu(btc->nodecount));
+							asfs_bstore(sb, bhsec);
+
+							if ((errorcode = asfs_freeadminspace(sb, be32_to_cpu(((struct fsBlockHeader *) bhsec->b_data)->ownblock))) == 0)
+								errorcode = asfs_deletebnode(sb, bhparent, be32_to_cpu(btcparent->bnode[n].key));
+						}
+						asfs_brelse(bhsec);
+					}
+				}
+				/*      else
+				   {
+				   // Never happens, except for root and then we don't care.
+				   } */
+			} else if (btc->nodecount == 1) {
+				/* No parent, so must be root. */
+
+				asfs_debug("deletebnode: no parent so must be root\n");
+
+				if (btc->isleaf == FALSE) {
+					struct fsBNodeContainer *bnc3 = (void *) bh->b_data;
+
+					/* The current root has only 1 node.  We now copy the data of this node into the
+					   root and promote that data to be the new root.  The rootblock number stays the
+					   same that way. */
+
+					if ((bhsec = asfs_breadcheck(sb, be32_to_cpu(btc->bnode[0].data), ASFS_BNODECONTAINER_ID))) {
+						u32 blockno = be32_to_cpu(((struct fsBlockHeader *) bh->b_data)->ownblock);
+						memcpy(bh->b_data, bhsec->b_data, sb->s_blocksize);
+						bnc3->bheader.ownblock = cpu_to_be32(blockno);
+
+						asfs_bstore(sb, bh);
+						errorcode = asfs_freeadminspace(sb, be32_to_cpu(((struct fsBlockHeader *) bhsec->b_data)->ownblock));
+						asfs_brelse(bhsec);
+					} else
+						errorcode = -EIO;
+				}
+				/* If not, then root contains leafs. */
+			}
+
+			asfs_debug("deletebnode: almost done\n");
+			/* otherwise, it must be the root, and the root is allowed
+			   to contain less than the minimum amount of nodes. */
+
+		}
+		if (bhparent != NULL)
+			asfs_brelse(bhparent);
+	}
+
+	return errorcode;
+}
+
+   /* Deletes an fsExtentBNode structure by key and any fsExtentBNodes linked to it.
+      This function DOES NOT fix the next pointer in a possible fsExtentBNode which
+      might have been pointing to the first BNode we are deleting.  Make sure you check
+      this yourself, if needed.
+
+      If key is zero, than this function does nothing. */
+
+int asfs_deleteextents(struct super_block *sb, u32 key)
+{
+	struct buffer_head *bh;
+	struct fsExtentBNode *ebn;
+	int errorcode = 0;
+
+	asfs_debug("deleteextents: Entry -- deleting extents from key %d\n", key);
+
+	while (key != 0 && (errorcode = findbnode(sb, key, &bh, (struct BNode **) &ebn)) == 0) {
+		/* node to be deleted located. */
+		key = be32_to_cpu(ebn->next);
+		if ((errorcode = asfs_freespace(sb, be32_to_cpu(ebn->key), be16_to_cpu(ebn->blocks))) != 0)
+			break;
+
+		if ((errorcode = asfs_deletebnode(sb, bh, be32_to_cpu(ebn->key))) != 0)
+			break;
+
+		asfs_brelse(bh);
+	}
+
+	return (errorcode);
+}
+
+   /* This function adds /blocks/ blocks starting at block /newspace/ to a file
+      identified by /objectnode/ and /lastextentbnode/.  /io_lastextentbnode/ can
+      be zero if there is no ExtentBNode chain attached to this file yet.
+      /blocks/ ranges from 1 to 8192.  To be able to extend Extents which are
+      almost full, it is wise to make this value no higher than 8192 blocks.
+      /io_lastextentbnode/ will contain the new lastextentbnode value when this
+      function completes.
+      If there was no chain yet, then this function will create a new one.  */
+
+int asfs_addblocks(struct super_block *sb, u16 blocks, u32 newspace, u32 objectnode, u32 *io_lastextentbnode)
+{
+	struct buffer_head *bh;
+	struct fsExtentBNode *ebn;
+	int errorcode = 0;
+
+	if (*io_lastextentbnode != 0) {
+		/* There was already a ExtentBNode chain for this file.  Extending it. */
+
+		asfs_debug("  addblocks: Extending existing ExtentBNode chain.\n");
+
+		if ((errorcode = asfs_getextent(sb, *io_lastextentbnode, &bh, &ebn)) == 0) {
+			if (be32_to_cpu(ebn->key) + be16_to_cpu(ebn->blocks) == newspace && be16_to_cpu(ebn->blocks) + blocks < 65536) {
+				/* It is possible to extent the last ExtentBNode! */
+				asfs_debug("  addblocks: Extending last ExtentBNode.\n");
+
+				ebn->blocks = cpu_to_be16(be16_to_cpu(ebn->blocks) + blocks);
+
+				asfs_bstore(sb, bh);
+				asfs_brelse(bh);
+			} else {
+				/* It isn't possible to extent the last ExtentBNode so we create
+				   a new one and link it to the last ExtentBNode. */
+
+				ebn->next = cpu_to_be32(newspace);
+				asfs_bstore(sb, bh);
+				asfs_brelse(bh);
+
+				if ((errorcode = createextentbnode(sb, newspace, &bh, (struct BNode **) &ebn)) == 0) {
+					asfs_debug("  addblocks: Created new ExtentBNode.\n");
+
+					ebn->key = cpu_to_be32(newspace);
+					ebn->prev = cpu_to_be32(*io_lastextentbnode);
+					ebn->next = 0;
+					ebn->blocks = cpu_to_be16(blocks);
+
+					*io_lastextentbnode = newspace;
+
+					asfs_bstore(sb, bh);
+					asfs_brelse(bh);
+
+					ASFS_SB(sb)->block_rovingblockptr = newspace + blocks;
+
+	/* to be changed in the future */
+/*					if (ASFS_SB(sb)->block_rovingblockptr >= ASFS_SB(sb)->totalblocks)
+						ASFS_SB(sb)->block_rovingblockptr = 0;*/
+				}
+			}
+		}
+	} else {
+		/* There is no ExtentBNode chain yet for this file.  Attaching one! */
+		if ((errorcode = createextentbnode(sb, newspace, &bh, (struct BNode **) &ebn)) == 0) {
+			asfs_debug("  addblocks: Created new ExtentBNode chain.\n");
+
+			ebn->key = cpu_to_be32(newspace);
+			ebn->prev = cpu_to_be32(objectnode + 0x80000000);
+			ebn->next = 0;
+			ebn->blocks = cpu_to_be16(blocks);
+
+			*io_lastextentbnode = newspace;
+
+			asfs_bstore(sb, bh);
+			asfs_brelse(bh);
+
+			ASFS_SB(sb)->block_rovingblockptr = newspace + blocks;
+
+/*			if (ASFS_SB(sb)->block_rovingblockptr >= ASFS_SB(sb)->totalblocks)
+				ASFS_SB(sb)->block_rovingblockptr = 0;*/
+		}
+	}
+
+	asfs_debug("  addblocks: done.\n");
+
+	return errorcode;
+}
+#endif
diff -urN oldtree/fs/asfs/file.c newtree/fs/asfs/file.c
--- oldtree/fs/asfs/file.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/asfs/file.c	2006-02-13 19:20:00.673409928 -0500
@@ -0,0 +1,251 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ * version: 1.0beta7
+ *
+ * Copyright (C) 2003,2004  Marek 'March' Szyprowski <marek@amiga.pl>
+ *
+ *
+ * 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 the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/buffer_head.h>
+#include <linux/vfs.h>
+#include "asfs_fs.h"
+
+#include <asm/byteorder.h>
+
+static int
+asfs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create)
+{
+	struct buffer_head *ebn_bh;
+	struct fsExtentBNode extent, *ebn_p;
+	u32 filedata;
+	unsigned long pos;
+	struct super_block *sb = inode->i_sb;
+#ifdef CONFIG_ASFS_RW
+	int error;
+	struct buffer_head *bh;
+	struct fsObject *obj;
+#endif
+
+	asfs_debug("ASFS: get_block(%lu, %ld, %d)\n", inode->i_ino, block, create);
+
+	if (block < 0) {
+		printk(KERN_ERR "ASFS: asfsget_block: requested block (%lld) < 0!\n", (unsigned long long)block);
+		return -EIO;
+	} else if (block >= inode->i_blocks && !create) {
+		printk(KERN_ERR "ASFS: asfsget_block: strange block request %lld!\n", (unsigned long long)block);
+		return -EIO;
+	}
+
+	if (create)
+#ifdef CONFIG_ASFS_RW
+		ASFS_I(inode)->modified = TRUE;
+#else
+		return -EROFS;
+#endif
+
+	if (block < inode->i_blocks)
+		create = 0;
+
+	lock_super(sb);
+
+#ifdef CONFIG_ASFS_RW
+	if (create) {
+		int blockstoadd;
+		u32 newspace, addedblocks;
+
+		blockstoadd = block - inode->i_blocks + 1;
+
+		if (blockstoadd < ASFS_BLOCKCHUNKS)
+			blockstoadd = ASFS_BLOCKCHUNKS;
+
+		asfs_debug("ASFS get_block: Trying to add %d blocks to file\n", blockstoadd);
+
+		if ((error = asfs_readobject(sb, inode->i_ino, &bh, &obj)) != 0) {
+			unlock_super(sb);
+			return error;
+		}
+
+		if ((error = asfs_addblockstofile(sb, bh, obj, blockstoadd, &newspace, &addedblocks)) != 0) {
+			asfs_brelse(bh);
+			unlock_super(sb);
+			return error;
+		}
+		ASFS_I(inode)->mmu_private += addedblocks * sb->s_blocksize;
+		inode->i_blocks += addedblocks;
+		ASFS_I(inode)->ext_cache.key = 0;
+		ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.file.data);
+		asfs_brelse(bh);
+	}
+#endif
+
+	if (ASFS_I(inode)->ext_cache.key > 0 && ASFS_I(inode)->ext_cache.startblock <= block) {
+		extent.key = ASFS_I(inode)->ext_cache.key;
+		extent.next = ASFS_I(inode)->ext_cache.next;
+		extent.blocks = ASFS_I(inode)->ext_cache.blocks;
+		pos = ASFS_I(inode)->ext_cache.startblock;
+	} else {
+		if (asfs_getextent(inode->i_sb, ASFS_I(inode)->firstblock, &ebn_bh, &ebn_p) != 0) {
+			unlock_super(sb);
+			return -EIO;
+		}
+		extent.key = be32_to_cpu(ebn_p->key);
+		extent.next = be32_to_cpu(ebn_p->next);
+		extent.blocks = be16_to_cpu(ebn_p->blocks);
+		pos = 0;
+		asfs_brelse(ebn_bh);
+	}
+	ebn_p = &extent;
+	filedata = ebn_p->next;
+
+	while (pos + ebn_p->blocks <= block && ebn_p->next != 0 && pos < inode->i_blocks) {
+		pos += ebn_p->blocks;
+		if (asfs_getextent(inode->i_sb, filedata, &ebn_bh, &ebn_p) != 0) {
+			unlock_super(sb);
+			return -EIO;
+		}
+		extent.key = be32_to_cpu(ebn_p->key);
+		extent.next = be32_to_cpu(ebn_p->next);
+		extent.blocks = be16_to_cpu(ebn_p->blocks);
+		ebn_p = &extent;
+		filedata = ebn_p->next;
+		asfs_brelse(ebn_bh);
+	}
+
+	unlock_super(sb);
+
+	map_bh(bh_result, inode->i_sb, (sector_t) (ebn_p->key + block - pos));
+
+	if (create)
+		set_buffer_new(bh_result);
+
+	asfs_debug("ASFS: get_block - mapped block %lu\n", ebn_p->key + block - pos);
+
+	ASFS_I(inode)->ext_cache.startblock = pos;
+	ASFS_I(inode)->ext_cache.key = ebn_p->key;
+	ASFS_I(inode)->ext_cache.next = ebn_p->next;
+	ASFS_I(inode)->ext_cache.blocks = ebn_p->blocks;
+
+	return 0;
+}
+
+int asfs_readpage(struct file *file, struct page *page)
+{
+	asfs_debug("ASFS: %s\n", __FUNCTION__);
+	return block_read_full_page(page, asfs_get_block);
+}
+
+sector_t asfs_bmap(struct address_space *mapping, sector_t block)
+{
+	asfs_debug("ASFS: %s\n", __FUNCTION__);
+	return generic_block_bmap(mapping,block,asfs_get_block);
+}
+
+#ifdef CONFIG_ASFS_RW
+
+int asfs_writepage(struct page *page, struct writeback_control *wbc)
+{
+	asfs_debug("ASFS: %s\n", __FUNCTION__);
+	return block_write_full_page(page, asfs_get_block, wbc);
+}
+
+int asfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+	asfs_debug("ASFS: %s\n", __FUNCTION__);
+	return cont_prepare_write(page, from, to, asfs_get_block, &ASFS_I(page->mapping->host)->mmu_private);
+}
+
+void asfs_truncate(struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+	struct buffer_head *bh;
+	struct fsObject *obj;
+
+	asfs_debug("AFFS: truncate(inode=%d, oldsize=%u, newsize=%u)\n",
+		 (u32)inode->i_ino, (u32)ASFS_I(inode)->mmu_private, (u32)inode->i_size);
+
+	if (inode->i_size > ASFS_I(inode)->mmu_private) {
+		printk("ASFS: enlarging file is not supported yet\n");
+		return;
+	}
+
+	lock_super(sb);
+
+	if ((asfs_readobject(sb, inode->i_ino, &bh, &obj)) != 0) {
+		unlock_super(sb);
+		return;
+	}
+
+	if (asfs_truncateblocksinfile(sb, bh, obj, inode->i_size) != 0) {
+		asfs_brelse(bh);
+		unlock_super(sb);
+		return;
+	}
+
+	obj->object.file.size = cpu_to_be32(inode->i_size);
+	ASFS_I(inode)->mmu_private = inode->i_size;
+	ASFS_I(inode)->modified = TRUE;
+	inode->i_blocks = (be32_to_cpu(obj->object.file.size) + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
+	asfs_bstore(sb, bh);
+	asfs_brelse(bh);
+
+	unlock_super(sb);
+}
+
+int asfs_file_open(struct inode *inode, struct file *filp)
+{
+	if (atomic_read(&filp->f_count) != 1)
+		return 0;
+	asfs_debug("ASFS: file open (node %d)\n", (int)inode->i_ino);
+	return 0;
+}
+
+int asfs_file_release(struct inode *inode, struct file *filp)
+{
+	int error = 0;
+
+	asfs_debug("ASFS: file release (node %d oc %d)\n", (int)inode->i_ino, atomic_read(&filp->f_count));
+
+	if (atomic_read(&filp->f_count) != 0)
+		return 0;
+
+	if (ASFS_I(inode)->modified == TRUE) {
+		struct buffer_head *bh;
+		struct fsObject *obj;
+		lock_super(inode->i_sb);
+
+		if ((error = asfs_readobject(inode->i_sb, inode->i_ino, &bh, &obj)) != 0) {
+			unlock_super(inode->i_sb);
+			return error;
+		}
+
+		obj->datemodified = cpu_to_be32(inode->i_mtime.tv_sec - (365*8+2)*24*60*60);
+		if (inode->i_mode & S_IFREG) {
+			error = asfs_truncateblocksinfile(inode->i_sb, bh, obj, (u32)inode->i_size);
+			obj->object.file.size = cpu_to_be32(inode->i_size);
+			ASFS_I(inode)->mmu_private = inode->i_size;
+			inode->i_blocks = (be32_to_cpu(obj->object.file.size) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits;
+		}
+		asfs_bstore(inode->i_sb, bh);
+
+		unlock_super(inode->i_sb);
+
+		asfs_brelse(bh);
+	}
+	ASFS_I(inode)->modified = FALSE;
+
+	return error;
+}
+
+#endif
diff -urN oldtree/fs/asfs/inode.c newtree/fs/asfs/inode.c
--- oldtree/fs/asfs/inode.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/asfs/inode.c	2006-02-13 19:20:00.673409928 -0500
@@ -0,0 +1,441 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ * version: 1.0beta8
+ *
+ * Copyright (C) 2003,2004,2005  Marek 'March' Szyprowski <marek@amiga.pl>
+ *
+ *
+ * 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 the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/smp_lock.h>
+#include <linux/time.h>
+#include <linux/buffer_head.h>
+#include <linux/vfs.h>
+#include <linux/dirent.h>
+#include "asfs_fs.h"
+
+#include <asm/byteorder.h>
+
+#ifdef CONFIG_ASFS_RW
+static int asfs_create(struct inode *dir, struct dentry *dentry,
+		       int mode, struct nameidata *nd);
+static int asfs_mkdir(struct inode *dir, struct dentry *dentry, int mode);
+static int asfs_symlink(struct inode *dir, struct dentry *dentry,
+			const char *symname);
+static int asfs_unlink(struct inode *dir, struct dentry *dentry);
+static int asfs_rmdir(struct inode *dir, struct dentry *dentry);
+static int asfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+		       struct inode *new_dir, struct dentry *new_dentry);
+#endif /*  CONFIG_ASFS_RW  */
+
+/* Mapping from our types to the kernel */
+
+static struct address_space_operations asfs_aops = {
+	.readpage	= asfs_readpage,
+	.sync_page	= block_sync_page,
+	.bmap		= asfs_bmap,
+#ifdef CONFIG_ASFS_RW
+	.writepage	= asfs_writepage,
+	.prepare_write = asfs_prepare_write,
+	.commit_write = generic_commit_write,
+#endif
+};
+
+static struct file_operations asfs_file_operations = {
+	.llseek		= generic_file_llseek,
+	.read		= generic_file_read,
+	.mmap		= generic_file_mmap,
+#ifdef CONFIG_ASFS_RW
+	.write		= generic_file_write,
+	.open		= asfs_file_open,
+	.release	= asfs_file_release,
+	.fsync		= file_fsync,
+#endif
+};
+
+static struct file_operations asfs_dir_operations = {
+	.read		= generic_read_dir,
+	.readdir	= asfs_readdir,
+};
+
+static struct inode_operations asfs_dir_inode_operations = {
+	.lookup		= asfs_lookup,
+#ifdef CONFIG_ASFS_RW
+	.create		= asfs_create,
+	.unlink		= asfs_unlink,
+	.symlink	= asfs_symlink,
+	.mkdir		= asfs_mkdir,
+	.rmdir		= asfs_rmdir,
+	.rename		= asfs_rename,
+/*	.setattr	= asfs_notify_change,*/
+#endif
+};
+
+static struct inode_operations asfs_file_inode_operations = {
+#ifdef CONFIG_ASFS_RW
+	.truncate	= asfs_truncate,
+/*	.setattr		= asfs_notify_change,*/
+#endif
+};
+
+static struct address_space_operations asfs_symlink_aops = {
+	.readpage	= asfs_symlink_readpage,
+};
+
+static struct inode_operations asfs_symlink_inode_operations = {
+	.readlink	= page_readlink,
+	.follow_link	= page_follow_link_light,
+	.put_link	= page_put_link,
+#ifdef CONFIG_ASFS_RW
+/*	.setattr	= asfs_notify_change,*/
+#endif
+};
+
+void asfs_read_locked_inode(struct inode *inode, void *arg)
+{
+	struct super_block *sb = inode->i_sb;
+	struct fsObject *obj = arg;
+
+	inode->i_mode = ASFS_SB(sb)->mode;
+	inode->i_mtime.tv_sec = inode->i_atime.tv_sec = inode->i_ctime.tv_sec = be32_to_cpu(obj->datemodified) + (365*8+2)*24*60*60;
+	/* Linux: seconds since 01-01-1970, AmigaSFS: seconds since 01-01-1978 */
+	inode->i_mtime.tv_nsec = inode->i_ctime.tv_nsec = inode->i_atime.tv_nsec = 0;
+	inode->i_uid = ASFS_SB(sb)->uid;
+	inode->i_gid = ASFS_SB(sb)->gid;
+
+	asfs_debug("asfs_read_inode2: Setting-up node %lu... ", inode->i_ino);
+
+	if (obj->bits & OTYPE_DIR) {
+		asfs_debug("dir (FirstdirBlock: %u, HashTable %u)\n", \
+		           be32_to_cpu(obj->object.dir.firstdirblock), be32_to_cpu(obj->object.dir.hashtable));
+
+		inode->i_size = 0;
+		inode->i_op = &asfs_dir_inode_operations;
+		inode->i_fop = &asfs_dir_operations;
+		inode->i_mode |= S_IFDIR | ((inode->i_mode & 0400) ? 0100 : 0) |
+		              ((inode->i_mode & 0040) ? 0010 : 0) | ((inode->i_mode & 0004) ? 0001 : 0);
+		ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.dir.firstdirblock);
+		ASFS_I(inode)->hashtable = be32_to_cpu(obj->object.dir.hashtable);
+		ASFS_I(inode)->modified = 0;
+	} else if (obj->bits & OTYPE_LINK && !(obj->bits & OTYPE_HARDLINK)) {
+		asfs_debug("symlink\n");
+		inode->i_size = 0;
+		inode->i_op = &asfs_symlink_inode_operations;
+		inode->i_mapping->a_ops = &asfs_symlink_aops;
+		inode->i_mode |= S_IFLNK | S_IRWXUGO;
+		ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.file.data);
+	} else {
+		asfs_debug("file (Size: %u, FirstBlock: %u)\n", be32_to_cpu(obj->object.file.size), be32_to_cpu(obj->object.file.data));
+		inode->i_size = be32_to_cpu(obj->object.file.size);
+		inode->i_blocks = (be32_to_cpu(obj->object.file.size) + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
+		inode->i_op = &asfs_file_inode_operations;
+		inode->i_fop = &asfs_file_operations;
+		inode->i_mapping->a_ops = &asfs_aops;
+		inode->i_mode |= S_IFREG;
+		ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.file.data);
+		ASFS_I(inode)->ext_cache.startblock = 0;
+		ASFS_I(inode)->ext_cache.key = 0;
+		ASFS_I(inode)->mmu_private = inode->i_size;
+	}
+	return;
+}
+
+struct inode *asfs_get_root_inode(struct super_block *sb)
+{
+	struct inode *result = NULL;
+	struct fsObject *obj;
+	struct buffer_head *bh;
+
+	asfs_debug("asfs_get_root_inode\n");
+
+	if ((bh = asfs_breadcheck(sb, ASFS_SB(sb)->rootobjectcontainer, ASFS_OBJECTCONTAINER_ID))) {
+		obj = &(((struct fsObjectContainer *)bh->b_data)->object[0]);
+		if (be32_to_cpu(obj->objectnode) > 0)
+			result = iget_locked(sb, be32_to_cpu(obj->objectnode));
+
+		if (result != NULL && result->i_state & I_NEW) {
+			asfs_read_locked_inode(result, obj);
+			unlock_new_inode(result);
+		}
+		asfs_brelse(bh);
+	}
+	return result;
+}
+
+#ifdef CONFIG_ASFS_RW
+
+static void asfs_sync_dir_inode(struct inode *dir, struct fsObject *obj)
+{
+	ASFS_I(dir)->firstblock = be32_to_cpu(obj->object.dir.firstdirblock);
+	ASFS_I(dir)->modified = 1;
+	dir->i_mtime = dir->i_atime = dir->i_ctime = CURRENT_TIME;
+	obj->datemodified = cpu_to_be32(dir->i_mtime.tv_sec - (365*8+2)*24*60*60);
+}
+
+enum { it_file, it_dir, it_link };
+
+static int asfs_create_object(struct inode *dir, struct dentry *dentry, int mode, int type, const char *symname)
+{
+	int error;
+	struct super_block *sb = dir->i_sb;
+	struct inode *inode;
+	struct buffer_head *bh, *dir_bh;
+	struct fsObject obj_data, *dir_obj, *obj;
+	u8 *name = (u8 *) dentry->d_name.name;
+	u8 bufname[ASFS_MAXFN_BUF];
+
+	asfs_debug("asfs_create_obj %s in dir node %d\n", name, (int)dir->i_ino);
+
+	asfs_translate(bufname, name, ASFS_SB(sb)->nls_disk, ASFS_SB(sb)->nls_io, ASFS_MAXFN_BUF);
+	if ((error = asfs_check_name(bufname, strlen(bufname))) != 0)
+		return error;
+
+	sb = dir->i_sb;
+	inode = new_inode(sb);
+	if (!inode)
+		return -ENOMEM;
+
+	memset(&obj_data, 0, sizeof(struct fsObject));
+
+	obj_data.protection = cpu_to_be32(FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE);
+	obj_data.datemodified = cpu_to_be32(inode->i_mtime.tv_sec - (365*8+2)*24*60*60);
+	switch (type) {
+	case it_dir:
+		obj_data.bits = OTYPE_DIR;
+		break;
+	case it_link:
+		obj_data.bits = OTYPE_LINK;
+		break;
+	default:
+		break;
+	}
+
+	lock_super(sb);
+
+	if ((error = asfs_readobject(sb, dir->i_ino, &dir_bh, &dir_obj)) != 0) {
+		dec_count(inode);
+		unlock_super(sb);
+		return error;
+	}
+
+	bh = dir_bh;
+	obj = dir_obj;
+
+	if ((error = asfs_createobject(sb, &bh, &obj, &obj_data, bufname, FALSE)) != 0) {
+		asfs_brelse(dir_bh);
+		dec_count(inode);
+		unlock_super(sb);
+		return error;
+	}
+
+	inode->i_ino = be32_to_cpu(obj->objectnode);
+	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+	inode->i_size = inode->i_blocks = inode->i_blksize = 0;
+	inode->i_uid = dir->i_uid;
+	inode->i_gid = dir->i_gid;
+	inode->i_mode = mode | ASFS_SB(sb)->mode;
+
+	switch (type) {
+	case it_dir:
+		inode->i_mode |= S_IFDIR;
+		inode->i_op = &asfs_dir_inode_operations;
+		inode->i_fop = &asfs_dir_operations;
+		ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.dir.firstdirblock);
+		ASFS_I(inode)->hashtable = be32_to_cpu(obj->object.dir.hashtable);
+		ASFS_I(inode)->modified = 0;
+		break;
+	case it_file:
+		inode->i_mode |= S_IFREG;
+		inode->i_op = &asfs_file_inode_operations;
+		inode->i_fop = &asfs_file_operations;
+		inode->i_mapping->a_ops = &asfs_aops;
+		ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.file.data);
+		ASFS_I(inode)->ext_cache.startblock = 0;
+		ASFS_I(inode)->ext_cache.key = 0;
+		ASFS_I(inode)->mmu_private = inode->i_size;
+		break;
+	case it_link:
+		inode->i_mode = S_IFLNK | S_IRWXUGO;
+		inode->i_op = &page_symlink_inode_operations;
+		inode->i_mapping->a_ops = &asfs_symlink_aops;
+		ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.file.data);
+		error = asfs_write_symlink(inode, symname);
+		break;
+	default:
+		break;
+	}
+
+	asfs_bstore(sb, bh);
+	insert_inode_hash(inode);
+	mark_inode_dirty(inode);
+	d_instantiate(dentry, inode);
+	asfs_sync_dir_inode(dir, dir_obj);
+	asfs_bstore(sb, dir_bh);
+
+	unlock_super(sb);
+	asfs_brelse(bh);
+	asfs_brelse(dir_bh);
+
+	return error;
+}
+
+static int asfs_create(struct inode *dir, struct dentry *dentry,
+		       int mode, struct nameidata *nd)
+{
+	return asfs_create_object(dir, dentry, mode, it_file, NULL);
+}
+
+static int asfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	return asfs_create_object(dir, dentry, mode, it_dir, NULL);
+}
+
+static int asfs_symlink(struct inode *dir, struct dentry *dentry,
+			const char *symname)
+{
+	return asfs_create_object(dir, dentry, 0, it_link, symname);
+}
+
+static int asfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	asfs_debug("ASFS: %s\n", __FUNCTION__);
+
+	if (ASFS_I(dentry->d_inode)->firstblock != 0)
+		return -ENOTEMPTY;
+
+	return asfs_unlink(dir, dentry);
+}
+
+static int asfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct inode *inode = dentry->d_inode;
+	int error;
+	struct super_block *sb = dir->i_sb;
+	struct buffer_head *bh, *dir_bh;
+	struct fsObject *dir_obj, *obj;
+
+	asfs_debug("ASFS: %s\n", __FUNCTION__);
+
+	lock_super(sb);
+
+	if ((error = asfs_readobject(sb, inode->i_ino, &bh, &obj)) != 0) {
+		unlock_super(sb);
+		return error;
+	}
+	if ((error = asfs_deleteobject(sb, bh, obj)) != 0) {
+		asfs_brelse(bh);
+		unlock_super(sb);
+		return error;
+	}
+	asfs_brelse(bh);
+
+	/* directory data could change after removing the object */
+	if ((error = asfs_readobject(sb, dir->i_ino, &dir_bh, &dir_obj)) != 0) {
+		unlock_super(sb);
+		return error;
+	}
+
+	asfs_sync_dir_inode(dir, dir_obj);
+	asfs_bstore(sb, dir_bh);
+
+	dec_count(inode);
+	unlock_super(sb);
+	asfs_brelse(dir_bh);
+
+	return 0;
+}
+
+static int asfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+		       struct inode *new_dir, struct dentry *new_dentry)
+{
+	struct super_block *sb = old_dir->i_sb;
+	struct buffer_head *src_bh, *old_bh, *new_bh;
+	int error;
+	struct fsObject *src_obj, *old_obj, *new_obj;
+	u8 bufname[ASFS_MAXFN_BUF];
+
+	asfs_debug("ASFS: rename (old=%u,\"%*s\" to new=%u,\"%*s\")\n",
+		 (u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name,
+		 (u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name);
+
+	asfs_translate(bufname, (u8 *) new_dentry->d_name.name, ASFS_SB(sb)->nls_disk, ASFS_SB(sb)->nls_io, ASFS_MAXFN_BUF);
+	if ((error = asfs_check_name(bufname, strlen(bufname))) != 0)
+		return error;
+
+
+	/* Unlink destination if it already exists */
+	if (new_dentry->d_inode)
+		if ((error = asfs_unlink(new_dir, new_dentry)) != 0)
+			return error;
+
+	lock_super(sb);
+
+	if ((error = asfs_readobject(sb, old_dentry->d_inode->i_ino, &src_bh, &src_obj)) != 0) {
+		unlock_super(sb);
+		return error;
+	}
+	if ((error = asfs_readobject(sb, new_dir->i_ino, &new_bh, &new_obj)) != 0) {
+		asfs_brelse(src_bh);
+		unlock_super(sb);
+		return error;
+	}
+
+	if ((error = asfs_renameobject(sb, src_bh, src_obj, new_bh, new_obj, bufname)) != 0) {
+		asfs_brelse(src_bh);
+		asfs_brelse(new_bh);
+		unlock_super(sb);
+		return error;
+	}
+	asfs_brelse(src_bh);
+	asfs_brelse(new_bh);
+
+	if ((error = asfs_readobject(sb, old_dir->i_ino, &old_bh, &old_obj)) != 0) {
+		unlock_super(sb);
+		return error;
+	}
+	if ((error = asfs_readobject(sb, new_dir->i_ino, &new_bh, &new_obj)) != 0) {
+		asfs_brelse(old_bh);
+		unlock_super(sb);
+		return error;
+	}
+
+	asfs_sync_dir_inode(old_dir, old_obj);
+	asfs_sync_dir_inode(new_dir, new_obj);
+
+	asfs_bstore(sb, new_bh);
+	asfs_bstore(sb, old_bh);
+
+	unlock_super(sb);
+	asfs_brelse(old_bh);
+	asfs_brelse(new_bh);
+
+	mark_inode_dirty(old_dir);
+	mark_inode_dirty(new_dir);
+
+	return 0;
+}
+
+/*
+static int asfs_notify_change(struct dentry *dentry, struct iattr *attr)
+{
+	struct inode *inode = dentry->d_inode;
+	int error = 0;
+
+	asfs_debug("ASFS: notify_change(%lu,0x%x)\n",inode->i_ino,attr->ia_valid);
+
+	error = inode_change_ok(inode,attr);
+
+	return error;
+}
+*/
+#endif
diff -urN oldtree/fs/asfs/namei.c newtree/fs/asfs/namei.c
--- oldtree/fs/asfs/namei.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/asfs/namei.c	2006-02-13 19:20:00.674409776 -0500
@@ -0,0 +1,197 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ * version: 1.0beta10
+ *
+ * Copyright (C) 2003,2004,2005  Marek 'March' Szyprowski <marek@amiga.pl>
+ *
+ *
+ * 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 the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/vfs.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include "asfs_fs.h"
+
+static inline u8 asfs_upperchar(u8 c)
+{
+	if ((c >= 224 && c <= 254 && c != 247) || (c >= 'a' && c <= 'z'))
+		c -= 32;
+	return (c);
+}
+
+u8 asfs_lowerchar(u8 c)
+{
+	if ((c >= 192 && c <= 222 && c != 215) || (c >= 'A' && c <= 'Z'))
+		c += 32;
+	return (c);
+}
+
+static inline u8 asfs_nls_upperchar(u8 c, struct nls_table *t)
+{
+	if (t) {
+		u8 nc = t->charset2upper[c];
+		return nc ? nc : c;
+	} else
+		return asfs_upperchar(c);
+}
+
+/* Check if the name is valid for a asfs object. */
+
+inline int asfs_check_name(const u8 *name, int len)
+{
+	int i;
+
+	if (len > ASFS_MAXFN)
+		return -ENAMETOOLONG;
+
+	for (i = 0; i < len; i++)
+		if (name[i] < ' ' || name[i] == ':' || (name[i] > 0x7e && name[i] < 0xa0))
+			return -EINVAL;
+
+	return 0;
+}
+
+/* Note: the dentry argument is the parent dentry. */
+
+static int asfs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+{
+	struct super_block *sb = dentry->d_inode->i_sb;
+	const u8 *name = qstr->name;
+	unsigned long hash;
+	int i;
+	struct nls_table *nls_io = ASFS_SB(sb)->nls_io;
+
+	i = asfs_check_name(qstr->name,qstr->len);
+	if (i)
+		return i;
+
+	hash = init_name_hash();
+
+	if (ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE)
+		for (i=qstr->len; i > 0; name++, i--)
+			hash = partial_name_hash(*name, hash);
+	else
+		for (i=qstr->len; i > 0; name++, i--)
+			hash = partial_name_hash(asfs_nls_upperchar(*name, nls_io), hash);
+
+	qstr->hash = end_name_hash(hash);
+
+	return 0;
+}
+
+static int asfs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+	struct super_block *sb = dentry->d_inode->i_sb;
+	const u8 *aname = a->name;
+	const u8 *bname = b->name;
+	int len;
+	struct nls_table *nls_io = ASFS_SB(sb)->nls_io;
+
+	/* 'a' is the qstr of an already existing dentry, so the name
+	 * must be valid. 'b' must be validated first.
+	 */
+
+	if (asfs_check_name(b->name,b->len))
+		return 1;
+
+	if (a->len != b->len)
+		return 1;
+
+	if (ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE) {
+		for (len=a->len; len > 0; len--)
+			if (*aname++ != *bname++)
+				return 1;
+	} else {
+		for (len=a->len; len > 0; len--)
+			if (asfs_nls_upperchar(*aname++, nls_io) != asfs_nls_upperchar(*bname++, nls_io))
+				return 1;
+	}
+
+	return 0;
+}
+
+struct dentry_operations asfs_dentry_operations = {
+	d_hash:		asfs_hash_dentry,
+	d_compare:	asfs_compare_dentry,
+};
+
+int asfs_namecmp(u8 *s, u8 *ct, int casesensitive, struct nls_table *t)
+{
+	if (casesensitive) {
+		while (*s == *ct && *ct != '\0' && *ct != '/') {
+			s++;
+			ct++;
+		}
+	} else {
+		while (asfs_nls_upperchar(*s, t) == asfs_nls_upperchar(*ct, t) && *ct != '\0'
+		       && *ct != '/') {
+			s++;
+			ct++;
+		}
+	}
+	return (*s == '\0' && (*ct == '\0' || *ct == '/')) ? 0 : *ct - *s;
+}
+
+u16 asfs_hash(u8 *name, int casesensitive)
+{
+	u16 hashval = 0;
+	while (name[hashval] != 0 && name[hashval] != '/')
+		hashval++;
+	if (casesensitive) {
+		u8 c = *name;
+		while (c != 0 && c != '/') {
+			hashval = hashval * 13 + c;
+			c = *++name;
+		}
+	} else {
+		u8 c = *name;
+		while (c != 0 && c != '/') {
+			hashval = hashval * 13 + asfs_upperchar(c);
+			c = *++name;
+		}
+	}
+	return hashval;
+}
+
+void asfs_translate(u8 *to, u8 *from, struct nls_table *nls_to, struct nls_table *nls_from, int limit)
+{
+	wchar_t uni;
+	int i, len;
+	int from_len, to_len = limit;
+
+	if (nls_to) {
+		from_len = strlen(from);
+		for (i=0; i < from_len && to_len > 1; ) {
+			len = nls_from->char2uni(&from[i], from_len-i, &uni);
+			if (len > 0) {
+				i += len;
+				len = nls_to->uni2char(uni, to, to_len);
+				if (len > 0) {
+					to += len;
+					to_len -= len;
+				}
+			} else
+				i++;
+			if (len < 0) {
+				*to++ = '?';
+				to_len--;
+			}
+		}
+		*to = '\0';
+	} else {
+		strncpy (to, from, limit);
+		to[limit-1] = '\0';
+	}
+}
diff -urN oldtree/fs/asfs/nodes.c newtree/fs/asfs/nodes.c
--- oldtree/fs/asfs/nodes.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/asfs/nodes.c	2006-02-13 19:20:00.674409776 -0500
@@ -0,0 +1,455 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ * version: 1.0beta7
+ *
+ * This file contains some parts of the original amiga version of
+ * SmartFilesystem source code.
+ *
+ * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx,
+ * Ralph Schmidt, Emmanuel Lesueur, David Gerber and Marcin Kurek
+ *
+ * Adapted and modified by Marek 'March' Szyprowski <marek@amiga.pl>
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/vfs.h>
+#include "asfs_fs.h"
+
+#include <asm/byteorder.h>
+
+/* Finds a specific node by number. */
+int asfs_getnode(struct super_block *sb, u32 nodeno, struct buffer_head **ret_bh, struct fsObjectNode **ret_node)
+{
+	struct buffer_head *bh;
+	struct fsNodeContainer *nodecont;
+	u32 nodeindex = ASFS_SB(sb)->objectnoderoot;
+
+	while ((bh = asfs_breadcheck(sb, nodeindex, ASFS_NODECONTAINER_ID))) {
+		nodecont = (struct fsNodeContainer *) bh->b_data;
+
+		if (be32_to_cpu(nodecont->nodes) == 1) {
+			*ret_node = (struct fsObjectNode *) ((u8 *) nodecont->node + NODE_STRUCT_SIZE * (nodeno - be32_to_cpu(nodecont->nodenumber)));
+			*ret_bh = bh;
+			return 0;
+		} else {
+			u16 containerentry = (nodeno - be32_to_cpu(nodecont->nodenumber)) / be32_to_cpu(nodecont->nodes);
+			nodeindex = be32_to_cpu(nodecont->node[containerentry]) >> (sb->s_blocksize_bits - ASFS_BLCKFACCURACY);
+		}
+		asfs_brelse(bh);
+	}
+	if (bh == NULL)
+		return -EIO;
+	return -ENOENT;
+}
+
+#ifdef CONFIG_ASFS_RW
+
+	/* Looks for the parent of the passed-in buffer_head (fsNodeContainer)
+	   starting from the root.  It returns an error if any error occured.
+	   If error is 0 and io_bh is NULL as well, then there was no parent (ie,
+	   you asked parent of the root).  Otherwise io_bh should contain the
+	   parent of the passed-in NodeContainer. */
+
+static int parentnodecontainer(struct super_block *sb, struct buffer_head **io_bh)
+{
+	u32 noderoot = ASFS_SB(sb)->objectnoderoot;
+	u32 childblock = be32_to_cpu(((struct fsBlockHeader *) (*io_bh)->b_data)->ownblock);
+	u32 nodenumber = be32_to_cpu(((struct fsNodeContainer *) (*io_bh)->b_data)->nodenumber);
+	int errorcode = 0;
+
+	if (noderoot == childblock) {
+		*io_bh = NULL;
+		return 0;
+	}
+
+	while ((*io_bh = asfs_breadcheck(sb, noderoot, ASFS_NODECONTAINER_ID))) {
+		struct fsNodeContainer *nc = (void *) (*io_bh)->b_data;
+
+		if (be32_to_cpu(nc->nodes) == 1) {
+			/* We've descended the tree to a leaf NodeContainer, something
+			   which should never happen if the passed-in io_bh had
+			   contained a valid fsNodeContainer. */
+			printk("ASFS: Failed to locate the parent NodeContainer - node tree is corrupted!\n");
+			*io_bh = NULL;
+			return -EIO;
+		} else {
+			u16 containerentry = (nodenumber - be32_to_cpu(nc->nodenumber)) / be32_to_cpu(nc->nodes);
+			noderoot = be32_to_cpu(nc->node[containerentry]) >> (sb->s_blocksize_bits - ASFS_BLCKFACCURACY);
+		}
+
+		if (noderoot == childblock)
+			break;
+
+		asfs_brelse(*io_bh);
+	}
+
+	if (*io_bh == NULL)
+		return -EIO;
+
+	return errorcode;
+}
+
+
+static int isfull(struct super_block *sb, struct fsNodeContainer *nc)
+{
+	u32 *p = nc->node;
+	s16 n = NODECONT_BLOCK_COUNT;
+
+	while (--n >= 0) {
+		if (*p == 0 || (be32_to_cpu(*p) & 0x00000001) == 0) {
+			break;
+		}
+		p++;
+	}
+
+	return n < 0;
+}
+
+static int markparentfull(struct super_block *sb, struct buffer_head *bh)
+{
+	u32 nodenumber = be32_to_cpu(((struct fsNodeContainer *) (bh->b_data))->nodenumber);
+	int errorcode;
+
+	if ((errorcode = parentnodecontainer(sb, &bh)) == 0 && bh != 0) {
+		struct fsNodeContainer *nc = (void *) bh->b_data;
+		u16 containerentry = (nodenumber - be32_to_cpu(nc->nodenumber)) / be32_to_cpu(nc->nodes);
+
+		nc->node[containerentry] = cpu_to_be32(be32_to_cpu(nc->node[containerentry]) | 0x00000001);
+
+		asfs_bstore(sb, bh);
+
+		if (isfull(sb, nc)) {	/* This container now is full as well!  Mark the next higher up container too then! */
+			return markparentfull(sb, bh);
+		}
+		asfs_brelse(bh);
+	}
+
+	return errorcode;
+}
+
+static int addnewnodelevel(struct super_block *sb, u16 nodesize)
+{
+	struct buffer_head *bh;
+	u32 noderoot = ASFS_SB(sb)->objectnoderoot;
+	int errorcode;
+
+	/* Adds a new level to the Node tree. */
+
+	asfs_debug("addnewnodelevel: Entry\n");
+
+	if ((bh = asfs_breadcheck(sb, noderoot, ASFS_NODECONTAINER_ID))) {
+		struct buffer_head *newbh;
+		u32 newblock;
+
+		if ((errorcode = asfs_allocadminspace(sb, &newblock)) == 0 && (newbh = asfs_getzeroblk(sb, newblock))) {
+			struct fsNodeContainer *nc = (void *) bh->b_data;
+			struct fsNodeContainer *newnc = (void *) newbh->b_data;
+
+			/* The newly allocated block will become a copy of the current root. */
+
+			newnc->bheader.id = cpu_to_be32(ASFS_NODECONTAINER_ID);
+			newnc->bheader.ownblock = cpu_to_be32(newblock);
+			newnc->nodenumber = nc->nodenumber;
+			newnc->nodes = nc->nodes;
+			memcpy(newnc->node, nc->node, sb->s_blocksize - sizeof(struct fsNodeContainer));
+
+			asfs_bstore(sb, newbh);
+			asfs_brelse(newbh);
+
+			/* The current root will now be transformed into a new root. */
+
+			if (be32_to_cpu(nc->nodes) == 1)
+				nc->nodes = cpu_to_be32((sb->s_blocksize - sizeof(struct fsNodeContainer)) / nodesize);
+			else
+				nc->nodes = cpu_to_be32(be32_to_cpu(nc->nodes) * NODECONT_BLOCK_COUNT);
+
+			nc->node[0] = cpu_to_be32((newblock << (sb->s_blocksize_bits - ASFS_BLCKFACCURACY)) + 1);	/* Tree is full from that point! */
+			memset(&nc->node[1], 0, sb->s_blocksize - sizeof(struct fsNodeContainer) - 4);
+
+			asfs_bstore(sb, bh);
+		}
+		asfs_brelse(bh);
+	} else
+		errorcode = -EIO;
+
+	return errorcode;
+}
+
+static int createnodecontainer(struct super_block *sb, u32 nodenumber, u32 nodes, u32 * returned_block)
+{
+	struct buffer_head *bh;
+	int errorcode;
+	u32 newblock;
+
+	asfs_debug("createnodecontainer: nodenumber = %u, nodes = %u\n", nodenumber, nodes);
+
+	if ((errorcode = asfs_allocadminspace(sb, &newblock)) == 0 && (bh = asfs_getzeroblk(sb, newblock))) {
+		struct fsNodeContainer *nc = (void *) bh->b_data;
+
+		nc->bheader.id = cpu_to_be32(ASFS_NODECONTAINER_ID);
+		nc->bheader.ownblock = cpu_to_be32(newblock);
+
+		nc->nodenumber = cpu_to_be32(nodenumber);
+		nc->nodes = cpu_to_be32(nodes);
+
+		asfs_bstore(sb, bh);
+		asfs_brelse(bh);
+		*returned_block = newblock;
+	}
+
+	return errorcode;
+}
+
+	/* This function creates a new fsNode structure in a fsNodeContainer.  If needed
+	   it will create a new fsNodeContainers and a new fsNodeIndexContainer. */
+
+int asfs_createnode(struct super_block *sb, struct buffer_head **returned_bh, struct fsNode **returned_node, u32 * returned_nodeno)
+{
+	u16 nodecount = (sb->s_blocksize - sizeof(struct fsNodeContainer)) / NODE_STRUCT_SIZE;
+	u32 noderoot = ASFS_SB(sb)->objectnoderoot;
+	u32 nodeindex = noderoot;
+	int errorcode = 0;
+
+	while ((*returned_bh = asfs_breadcheck(sb, nodeindex, ASFS_NODECONTAINER_ID))) {
+		struct fsNodeContainer *nc = (void *) (*returned_bh)->b_data;
+
+		if (be32_to_cpu(nc->nodes) == 1) {	/* Is it a leaf-container? */
+			struct fsNode *n;
+			s16 i = nodecount;
+
+			n = (struct fsNode *) nc->node;
+
+			while (i-- > 0) {
+				if (n->data == 0)
+					break;
+
+				n = (struct fsNode *) ((u8 *) n + NODE_STRUCT_SIZE);
+			}
+
+			if (i >= 0) {
+				/* Found an empty fsNode structure! */
+				*returned_node = n;
+				*returned_nodeno = be32_to_cpu(nc->nodenumber) + ((u8 *) n - (u8 *) nc->node) / NODE_STRUCT_SIZE;
+
+				asfs_debug("createnode: Created Node %d\n", *returned_nodeno);
+
+				/* Below we continue to look through the NodeContainer block.  We skip the entry
+				   we found to be unused, and see if there are any more unused entries.  If we
+				   do not find any more unused entries then this container is now full. */
+
+				n = (struct fsNode *) ((u8 *) n + NODE_STRUCT_SIZE);
+
+				while (i-- > 0) {
+					if (n->data == 0)
+						break;
+
+					n = (struct fsNode *) ((u8 *) n + NODE_STRUCT_SIZE);
+				}
+
+				if (i < 0) {
+					/* No more empty fsNode structures in this block.  Mark parent full. */
+					errorcode = markparentfull(sb, *returned_bh);
+				}
+
+				return errorcode;
+			} else {
+				/* What happened now is that we found a leaf-container which was
+				   completely filled.  In practice this should only happen when there
+				   is only a single NodeContainer (only this container), or when there
+				   was an error in one of the full-bits in a higher level container. */
+
+				if (noderoot != nodeindex) {
+					/*** Hmmm... it looks like there was a damaged full-bit or something.
+					     In this case we'd probably better call markcontainerfull. */
+
+					printk("ASFS: Couldn't find empty Node in NodeContainer while NodeIndexContainer indicated there should be one!\n");
+
+					errorcode = -ENOSPC;
+					break;
+				} else {
+					/* Container is completely filled. */
+
+					if ((errorcode = addnewnodelevel(sb, NODE_STRUCT_SIZE)) != 0)
+						return errorcode;
+
+					nodeindex = noderoot;
+				}
+			}
+		} else {	/* This isn't a leaf container */
+			u32 *p = nc->node;
+			s16 i = NODECONT_BLOCK_COUNT;
+
+			/* We've read a normal container */
+
+			while (i-- > 0) {
+				if (*p != 0 && (be32_to_cpu(*p) & 0x00000001) == 0)
+					break;
+
+				p++;
+			}
+
+			if (i >= 0) {
+				/* Found a not completely filled Container */
+
+				nodeindex = be32_to_cpu(*p) >> (sb->s_blocksize_bits - ASFS_BLCKFACCURACY);
+			} else {
+				/* Everything in the NodeIndexContainer was completely filled.  There possibly
+				   are some unused pointers in this block however.  */
+
+				asfs_debug("createnode: NodeContainer at block has no empty Nodes.\n");
+
+				p = nc->node;
+				i = NODECONT_BLOCK_COUNT;
+
+				while (i-- > 0) {
+					if (*p == 0)
+						break;
+
+					p++;
+				}
+
+				if (i >= 0) {
+					u32 newblock;
+					u32 nodes;
+
+					/* Found an unused Container pointer */
+
+					if (be32_to_cpu(nc->nodes) == (sb->s_blocksize - sizeof(struct fsNodeContainer)) / NODE_STRUCT_SIZE) {
+						nodes = 1;
+					} else {
+						nodes = be32_to_cpu(nc->nodes) / NODECONT_BLOCK_COUNT;
+					}
+
+					if ((errorcode = createnodecontainer(sb, be32_to_cpu(nc->nodenumber) + (p - nc->node) * be32_to_cpu(nc->nodes), nodes, &newblock)) != 0) {
+						break;
+					}
+
+					*p = cpu_to_be32(newblock << (sb->s_blocksize_bits - ASFS_BLCKFACCURACY));
+
+					asfs_bstore(sb, *returned_bh);
+				} else {
+					/* Container is completely filled.  This must be the top-level NodeIndex container
+					   as otherwise the full-bit would have been wrong! */
+
+					if ((errorcode = addnewnodelevel(sb, NODE_STRUCT_SIZE)) != 0)
+						break;
+
+					nodeindex = noderoot;
+				}
+			}
+		}
+		asfs_brelse(*returned_bh);
+	}
+
+	if (*returned_bh == NULL)
+		return -EIO;
+
+	return (errorcode);
+}
+
+static int markparentempty(struct super_block *sb, struct buffer_head *bh)
+{
+	u32 nodenumber = be32_to_cpu(((struct fsNodeContainer *) bh->b_data)->nodenumber);
+	int errorcode;
+
+	if ((errorcode = parentnodecontainer(sb, &bh)) == 0 && bh != 0) {
+		struct fsNodeContainer *nc = (void *) bh->b_data;
+		int wasfull;
+		u16 containerentry = (nodenumber - be32_to_cpu(nc->nodenumber)) / be32_to_cpu(nc->nodes);
+
+		wasfull = isfull(sb, nc);
+
+		nc->node[containerentry] = cpu_to_be32(be32_to_cpu(nc->node[containerentry]) & ~0x00000001);
+
+		asfs_bstore(sb, bh);
+
+		if (wasfull) {
+			/* This container was completely full before!  Mark the next higher up container too then! */
+			return markparentempty(sb, bh);
+		}
+		asfs_brelse(bh);
+	}
+
+	return errorcode;
+}
+
+static int freecontainer(struct super_block *sb, struct buffer_head *bh)
+{
+	u32 nodenumber = be32_to_cpu(((struct fsNodeContainer *) bh->b_data)->nodenumber);
+	int errorcode;
+
+	if ((errorcode = parentnodecontainer(sb, &bh)) == 0 && bh != NULL) {	/* This line also prevents the freeing of the noderoot. */
+		struct fsNodeContainer *nc = (void *) bh->b_data;
+		u16 containerindex = (nodenumber - be32_to_cpu(nc->nodenumber)) / be32_to_cpu(nc->nodes);
+
+		if ((errorcode = asfs_freeadminspace(sb, be32_to_cpu(nc->node[containerindex]) >> (sb->s_blocksize_bits - ASFS_BLCKFACCURACY))) == 0) {
+			u32 *p = nc->node;
+			s16 n = NODECONT_BLOCK_COUNT;
+
+			nc->node[containerindex] = 0;
+			asfs_bstore(sb, bh);
+
+			while (n-- > 0)
+				if (*p++ != 0)
+					break;
+
+			if (n < 0) {	/* This container is now completely empty!  Free this NodeIndexContainer too then! */
+				return freecontainer(sb, bh);
+			}
+		}
+		asfs_brelse(bh);
+	}
+
+	return errorcode;
+}
+
+static int internaldeletenode(struct super_block *sb, struct buffer_head *bh, struct fsNode *n)
+{
+	struct fsNodeContainer *nc = (void *) bh->b_data;
+	u16 nodecount = (sb->s_blocksize - sizeof(struct fsNodeContainer)) / NODE_STRUCT_SIZE;
+	s16 i = nodecount;
+	s16 empty = 0;
+	int errorcode = 0;
+
+	n->data = 0;
+	n = (struct fsNode *) nc->node;
+
+	while (i-- > 0) {
+		if (n->data == 0)
+			empty++;
+
+		n = (struct fsNode *) ((u8 *) n + NODE_STRUCT_SIZE);
+	}
+
+	asfs_bstore(sb, bh);
+
+	if (empty == 1)		/* NodeContainer was completely full before, so we need to mark it empty now. */
+		errorcode = markparentempty(sb, bh);
+	else if (empty == nodecount)	/* NodeContainer is now completely empty!  Free it! */
+		errorcode = freecontainer(sb, bh);
+
+	return (errorcode);
+}
+
+int asfs_deletenode(struct super_block *sb, u32 objectnode)
+{
+	struct buffer_head *bh;
+	struct fsObjectNode *on;
+	int errorcode;
+
+	asfs_debug("deletenode: Deleting Node %d\n", objectnode);
+
+	if ((errorcode = asfs_getnode(sb, objectnode, &bh, &on)) == 0)
+		errorcode = internaldeletenode(sb, bh, (struct fsNode *) on);
+
+	asfs_brelse(bh);
+	return (errorcode);
+}
+
+#endif
diff -urN oldtree/fs/asfs/objects.c newtree/fs/asfs/objects.c
--- oldtree/fs/asfs/objects.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/asfs/objects.c	2006-02-13 19:20:00.675409624 -0500
@@ -0,0 +1,767 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ * version: 1.0beta7
+ *
+ * This file contains some parts of the original amiga version of
+ * SmartFilesystem source code.
+ *
+ * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx,
+ * Ralph Schmidt, Emmanuel Lesueur, David Gerber, and Marcin Kurek
+ *
+ * Adapted and modified by Marek 'March' Szyprowski <marek@amiga.pl>
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/vfs.h>
+#include "asfs_fs.h"
+
+#include <asm/byteorder.h>
+
+struct fsObject *asfs_nextobject(struct fsObject *obj)
+{
+	int i;
+	u8 *p = obj->name;
+
+	for (i = 2; i > 0; p++)
+		if (*p == '\0')
+			i--;
+	if ((p - (u8 *) obj) & 0x01)
+		p++;
+
+	return ((struct fsObject *) p);
+}
+
+struct fsObject *asfs_find_obj_by_name(struct super_block *sb, struct fsObjectContainer *objcont, u8 * name)
+{
+	struct fsObject *obj;
+
+	obj = &(objcont->object[0]);
+	while (be32_to_cpu(obj->objectnode) > 0 && ((char *) obj - (char *) objcont) + sizeof(struct fsObject) + 2 < sb->s_blocksize) {
+		if (asfs_namecmp(obj->name, name, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE, NULL) == 0) {
+			asfs_debug("Object found! Node %u, Name %s, Type %x, inCont %u\n", be32_to_cpu(obj->objectnode), obj->name, obj->bits, be32_to_cpu(objcont->bheader.ownblock));
+			return obj;
+		}
+		obj = asfs_nextobject(obj);
+	}
+	return NULL;
+}
+
+#ifdef CONFIG_ASFS_RW
+
+static struct fsObject *find_obj_by_node(struct super_block *sb,
+					 struct fsObjectContainer *objcont,
+					 u32 objnode)
+{
+	struct fsObject *obj;
+
+	obj = &(objcont->object[0]);
+	while (be32_to_cpu(obj->objectnode) > 0 && ((char *) obj - (char *) objcont) + sizeof(struct fsObject) + 2 < sb->s_blocksize) {
+		if (be32_to_cpu(obj->objectnode) == objnode) {
+			return obj;
+		}
+		obj = asfs_nextobject(obj);
+	}
+	return NULL;
+}
+
+int asfs_readobject(struct super_block *sb, u32 objectnode, struct buffer_head **bh, struct fsObject **returned_object)
+{
+	struct fsObjectNode *on;
+	int errorcode;
+	u32 contblock;
+
+	asfs_debug("Seaching object - node %d\n", objectnode);
+
+	if ((errorcode = asfs_getnode(sb, objectnode, bh, &on)) != 0)
+		return errorcode;
+	contblock = be32_to_cpu(on->node.data);
+	asfs_brelse(*bh);
+
+	if (contblock > 0 && (*bh = asfs_breadcheck(sb, contblock, ASFS_OBJECTCONTAINER_ID))) {
+		*returned_object = find_obj_by_node(sb, (void *) (*bh)->b_data, objectnode);
+		if (*returned_object == NULL) {
+			brelse(*bh);
+			*bh = NULL;
+			return -ENOENT;
+		}
+		return 0;
+	} else
+		return -EIO;
+}
+
+static int removeobjectcontainer(struct super_block *sb, struct buffer_head *bh)
+{
+	struct fsObjectContainer *oc = (void *) bh->b_data;
+	int errorcode;
+	struct buffer_head *block;
+
+	asfs_debug("removeobjectcontainer: block %u\n", be32_to_cpu(oc->bheader.ownblock));
+
+	if (oc->next != 0 && oc->next != oc->bheader.ownblock) {
+		struct fsObjectContainer *next_oc;
+
+		if ((block = asfs_breadcheck(sb, be32_to_cpu(oc->next), ASFS_OBJECTCONTAINER_ID)) == NULL)
+			return -EIO;
+
+		next_oc = (void *) block->b_data;
+		next_oc->previous = oc->previous;
+
+		asfs_bstore(sb, block);
+		asfs_brelse(block);
+	}
+
+	if (oc->previous != 0 && oc->previous != oc->bheader.ownblock) {
+		struct fsObjectContainer *previous_oc;
+
+		if ((block = asfs_breadcheck(sb, be32_to_cpu(oc->previous), ASFS_OBJECTCONTAINER_ID)) == NULL)
+			return -EIO;
+
+		previous_oc = (void *) block->b_data;
+		previous_oc->next = oc->next;
+
+		asfs_bstore(sb, block);
+		asfs_brelse(block);
+	} else {
+		struct fsObject *parent_o;
+
+		if ((errorcode = asfs_readobject(sb, be32_to_cpu(oc->parent), &block, &parent_o)) != 0)
+			return (errorcode);
+
+		parent_o->object.dir.firstdirblock = oc->next;
+
+		asfs_bstore(sb, block);
+		asfs_brelse(block);
+	}
+
+	if ((errorcode = asfs_freeadminspace(sb, be32_to_cpu(oc->bheader.ownblock))) != 0)
+		return (errorcode);
+
+	return (0);
+}
+
+static int setrecycledinfodiff(struct super_block *sb, s32 deletedfiles, s32 deletedblocks)
+{
+	struct buffer_head *bh;
+
+	if ((bh = asfs_breadcheck(sb, ASFS_SB(sb)->rootobjectcontainer, ASFS_OBJECTCONTAINER_ID))) {
+		struct fsRootInfo *ri = (struct fsRootInfo *) ((u8 *) bh->b_data + sb->s_blocksize - sizeof(struct fsRootInfo));
+
+		ri->deletedfiles = cpu_to_be32(be32_to_cpu(ri->deletedfiles) + deletedfiles);
+		ri->deletedblocks = cpu_to_be32(be32_to_cpu(ri->deletedblocks) + deletedblocks);
+
+		asfs_bstore(sb, bh);
+		asfs_brelse(bh);
+	} else
+		return -EIO;
+	return 0;
+}
+
+	/* This function removes the fsObject structure passed in from the passed
+	   buffer_head.  If the ObjectContainer becomes completely empty it will be
+	   delinked from the ObjectContainer chain and marked free for reuse.
+	   This function doesn't delink the object from the hashchain! */
+
+static int simpleremoveobject(struct super_block *sb, struct buffer_head *bh, struct fsObject *o)
+{
+	struct fsObjectContainer *oc = (void *) bh->b_data;
+	int errorcode = 0;
+
+	asfs_debug("simpleremoveobject:\n");
+
+	if (be32_to_cpu(oc->parent) == ASFS_RECYCLEDNODE) {
+		/* This object is removed from the Recycled directory. */
+		if ((errorcode = setrecycledinfodiff(sb, -1, -((be32_to_cpu(o->object.file.size) + sb->s_blocksize - 1) >> sb->s_blocksize_bits))) != 0)
+			return errorcode;
+	}
+
+	if ((asfs_nextobject(oc->object))->name[0] == '\0')
+		errorcode = removeobjectcontainer(sb, bh);
+	else {
+		struct fsObject *nexto;
+		int objlen;
+
+		nexto = asfs_nextobject(o);
+		objlen = (u8 *) nexto - (u8 *) o;
+
+		memmove(o, nexto, sb->s_blocksize - ((u8 *) nexto - (u8 *) oc));
+		memset((u8 *) oc + sb->s_blocksize - objlen, 0, objlen);
+
+		asfs_bstore(sb, bh);
+	}
+	return errorcode;
+}
+
+/* This function delinks the passed in ObjectNode from its hash-chain.  Handy when deleting
+   the object, or when renaming/moving it. */
+
+static int dehashobjectquick(struct super_block *sb, u32 objectnode, u8 *name, u32 parentobjectnode)
+{
+	struct fsObject *o;
+	int errorcode = 0;
+	struct buffer_head *block;
+
+	asfs_debug("dehashobject: Delinking object %d (=ObjectNode) from hashchain. Parentnode = %d\n", objectnode, parentobjectnode);
+
+	if ((errorcode = asfs_readobject(sb, parentobjectnode, &block, &o)) == 0 && o->object.dir.hashtable != 0) {
+		u32 hashtable = be32_to_cpu(o->object.dir.hashtable);
+		asfs_brelse(block);
+
+		if ((block = asfs_breadcheck(sb, hashtable, ASFS_HASHTABLE_ID))) {
+			struct buffer_head *node_bh;
+			struct fsObjectNode *onptr, on;
+			struct fsHashTable *ht = (void *) block->b_data;
+			u32 nexthash;
+
+			if ((errorcode = asfs_getnode(sb, objectnode, &node_bh, &onptr)) == 0) {
+				u16 hashchain;
+
+				asfs_debug("dehashobject: Read HashTable block of parent object of object to be delinked\n");
+
+				hashchain = HASHCHAIN(asfs_hash(name, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE));
+				nexthash = be32_to_cpu(ht->hashentry[hashchain]);
+
+				if (nexthash == objectnode) {
+					/* The hashtable directly points to the fsObject to be delinked.  We simply
+					   modify the Hashtable to point to the new nexthash entry. */
+
+					asfs_debug("dehashobject: The hashtable points directly to the to be delinked object\n");
+
+					ht->hashentry[hashchain] = onptr->next;
+					asfs_bstore(sb, block);
+				} else {
+					struct fsObjectNode *onsearch = 0;
+
+					on = *onptr;
+
+					asfs_debug("dehashobject: Walking through hashchain\n");
+
+					while (nexthash != 0 && nexthash != objectnode) {
+						asfs_brelse(node_bh);
+						if ((errorcode = asfs_getnode(sb, nexthash, &node_bh, &onsearch)) != 0)
+							break;
+						nexthash = be32_to_cpu(onsearch->next);
+					}
+
+					if (errorcode == 0) {
+						if (nexthash != 0) {
+							/* Previous fsObjectNode found in hash chain.  Modify the fsObjectNode to 'skip' the
+							   ObjectNode which is being delinked from the hash chain. */
+
+							onsearch->next = on.next;
+							asfs_bstore(sb, node_bh);
+						} else {
+							printk("ASFS: Hashchain of object %d is corrupt or incorrectly linked.", objectnode);
+
+							/*** This is strange.  We have been looking for the fsObjectNode which is located before the
+							     passed in fsObjectNode in the hash-chain.  However, we never found the
+							     fsObjectNode reffered to in the hash-chain!  Has to be somekind
+							     of internal error... */
+
+							errorcode = -ENOENT;
+						}
+					}
+				}
+				asfs_brelse(node_bh);
+			}
+			asfs_brelse(block);
+		}
+	}
+	return errorcode;
+}
+
+
+	/* This function removes an object from any directory.  It takes care
+	   of delinking the object from the hashchain and also frees the
+	   objectnode number. */
+
+static int removeobject(struct super_block *sb, struct buffer_head *bh, struct fsObject *o)
+{
+	struct fsObjectContainer *oc = (void *) bh->b_data;
+	int errorcode;
+
+	asfs_debug("removeobject\n");
+
+	if ((errorcode = dehashobjectquick(sb, be32_to_cpu(o->objectnode), o->name, be32_to_cpu(oc->parent))) == 0) {
+		u32 objectnode = be32_to_cpu(o->objectnode);
+
+		if ((errorcode = simpleremoveobject(sb, bh, o)) == 0)
+			errorcode = asfs_deletenode(sb, objectnode);
+	}
+
+	return (errorcode);
+}
+
+	/* This function deletes the specified object. */
+int asfs_deleteobject(struct super_block *sb, struct buffer_head *bh, struct fsObject *o)
+{
+	int errorcode = 0;
+
+	asfs_debug("deleteobject: Entry -- deleting object %d (%s)\n", be32_to_cpu(o->objectnode), o->name);
+
+	if ((o->bits & OTYPE_DIR) == 0 || o->object.dir.firstdirblock == 0) {
+		u8 bits = o->bits;
+		u32 hashblckno = be32_to_cpu(o->object.dir.hashtable);
+		u32 extentbnode = be32_to_cpu(o->object.file.data);
+
+		if ((errorcode = removeobject(sb, bh, o)) == 0) {
+			if ((bits & OTYPE_LINK) != 0) {
+				asfs_debug("deleteobject: Object is soft link!\n");
+				errorcode = asfs_freeadminspace(sb, extentbnode);
+			} else if ((bits & OTYPE_DIR) != 0) {
+				asfs_debug("deleteobject: Object is a directory!\n");
+				errorcode = asfs_freeadminspace(sb, hashblckno);
+			} else {
+				asfs_debug("deleteobject: Object is a file\n");
+				if (extentbnode != 0)
+					errorcode = asfs_deleteextents(sb, extentbnode);
+			}
+		}
+	}
+
+	return (errorcode);
+}
+
+	/* This function takes a HashBlock pointer, an ObjectNode and an ObjectName.
+	   If there is a hashblock, then this function will correctly link the object
+	   into the hashchain.  If there isn't a hashblock (=0) then this function
+	   does nothing.  */
+
+static int hashobject(struct super_block *sb, u32 hashblock, struct fsObjectNode *on, u32 nodeno, u8 *objectname)
+{
+	struct buffer_head *hash_bh;
+
+	asfs_debug("hashobject, using hashblock %d\n", hashblock);
+	if (hashblock == 0)
+		return 0;
+
+	if ((hash_bh = asfs_breadcheck(sb, hashblock, ASFS_HASHTABLE_ID))) {
+		struct fsHashTable *ht = (void *) hash_bh->b_data;
+		u32 nexthash;
+		u16 hashvalue, hashchain;
+
+		hashvalue = asfs_hash(objectname, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE);
+		hashchain = HASHCHAIN(hashvalue);
+		nexthash = be32_to_cpu(ht->hashentry[hashchain]);
+
+		ht->hashentry[hashchain] = cpu_to_be32(nodeno);
+
+		asfs_bstore(sb, hash_bh);
+		asfs_brelse(hash_bh);
+
+		on->next = cpu_to_be32(nexthash);
+		on->hash16 = cpu_to_be16(hashvalue);
+	} else
+		return -EIO;
+
+	return 0;
+}
+
+	/* This function returns a pointer to the first unused byte in
+	   an ObjectContainer. */
+
+static u8 *emptyspaceinobjectcontainer(struct super_block *sb, struct fsObjectContainer *oc)
+{
+	struct fsObject *o = oc->object;
+	u8 *endadr;
+
+	endadr = (u8 *) oc + sb->s_blocksize - sizeof(struct fsObject) - 2;
+
+	while ((u8 *) o < endadr && o->name[0] != 0)
+		o = asfs_nextobject(o);
+
+	return (u8 *) o;
+}
+
+	/* This function will look in the directory indicated by io_o
+	   for an ObjectContainer block which contains bytesneeded free
+	   bytes.  If none is found then this function simply creates a
+	   new ObjectContainer and adds that to the indicated directory. */
+
+static int findobjectspace(struct super_block *sb, struct buffer_head **io_bh, struct fsObject **io_o, u32 bytesneeded)
+{
+	struct buffer_head *bhparent = *io_bh;
+	struct fsObject *oparent = *io_o;
+	struct buffer_head *bh;
+	u32 nextblock = be32_to_cpu(oparent->object.dir.firstdirblock);
+	int errorcode = 0;
+
+	asfs_debug("findobjectspace: Looking for %u bytes in directory with ObjectNode number %d (in block %d)\n", bytesneeded, be32_to_cpu((*io_o)->objectnode),
+		   be32_to_cpu(((struct fsBlockHeader *) (*io_bh)->b_data)->ownblock));
+
+	while (nextblock != 0 && (bh = asfs_breadcheck(sb, nextblock, ASFS_OBJECTCONTAINER_ID))) {
+		struct fsObjectContainer *oc = (void *) bh->b_data;
+		u8 *emptyspace;
+
+		/* We need to find out how much free space this ObjectContainer has */
+
+		emptyspace = emptyspaceinobjectcontainer(sb, oc);
+
+		if ((u8 *) oc + sb->s_blocksize - emptyspace >= bytesneeded) {
+			/* We found enough space in one of the ObjectContainer blocks!!
+			   We return a struct fsObject *. */
+			*io_bh = bh;
+			*io_o = (struct fsObject *) emptyspace;
+			break;
+		}
+		nextblock = be32_to_cpu(oc->next);
+		asfs_brelse(bh);
+	}
+
+	if (nextblock == 0) {
+		u32 newcontblock;
+		/* If we get here, we traversed the *entire* directory (ough!) and found no empty
+		   space large enough for our entry.  We allocate new space and add it to this
+		   directory. */
+
+		if ((errorcode = asfs_allocadminspace(sb, &newcontblock)) == 0 && (bh = asfs_getzeroblk(sb, newcontblock))) {
+			struct fsObjectContainer *oc = (void *) bh->b_data;
+			struct buffer_head *bhnext;
+
+			asfs_debug("findobjectspace: No room was found, allocated new block at %u\n", newcontblock);
+
+			/* Allocated new block.  We will now link it to the START of the directory chain
+			   so the new free space can be found quickly when more entries need to be added. */
+
+			oc->bheader.id = cpu_to_be32(ASFS_OBJECTCONTAINER_ID);
+			oc->bheader.ownblock = cpu_to_be32(newcontblock);
+			oc->parent = oparent->objectnode;
+			oc->next = oparent->object.dir.firstdirblock;
+			oc->previous = 0;
+
+			oparent->object.dir.firstdirblock = cpu_to_be32(newcontblock);
+
+			asfs_bstore(sb, bhparent);
+
+			if (oc->next != 0 && (bhnext = asfs_breadcheck(sb, be32_to_cpu(oc->next), ASFS_OBJECTCONTAINER_ID))) {
+				struct fsObjectContainer *ocnext = (void *) bhnext->b_data;
+				ocnext->previous = cpu_to_be32(newcontblock);
+				asfs_bstore(sb, bhnext);
+				asfs_brelse(bhnext);
+			}
+
+			*io_bh = bh;
+			*io_o = oc->object;
+		}
+	}
+
+	asfs_debug("findobjectspace: new object will be in container block %u\n", be32_to_cpu(((struct fsBlockHeader *) (*io_bh)->b_data)->ownblock));
+
+	return (errorcode);
+}
+
+/* io_bh & io_o refer to the direct parent of the new object.  Objectname is the
+	name of the new object (name only). Does not realese io_bh !!! */
+
+int asfs_createobject(struct super_block *sb, struct buffer_head **io_bh, struct fsObject **io_o, struct fsObject *src_o, u8 *objectname, int force)
+{
+	int errorcode;
+	u32 object_size;
+	u32 hashblock = be32_to_cpu((*io_o)->object.dir.hashtable);
+
+	asfs_debug("createobject: Creating object '%s' in dir '%s'.\n", objectname, (*io_o)->name);
+
+	if (!force && ASFS_SB(sb)->freeblocks < ASFS_ALWAYSFREE)
+		return -ENOSPC;
+
+	if (!force && be32_to_cpu((*io_o)->objectnode) == ASFS_RECYCLEDNODE)
+		return -EINVAL;
+
+	object_size = sizeof(struct fsObject) + strlen(objectname) + 2;
+
+	if ((errorcode = findobjectspace(sb, io_bh, io_o, object_size)) == 0) {
+		struct fsObject *o2 = *io_o;
+		u8 *name = o2->name;
+		u8 *objname = objectname;
+		struct buffer_head *node_bh;
+		struct fsObjectNode *on;
+		u32 nodeno;
+
+		**io_o = *src_o;	/* Copying whole object data... */
+
+		while (*objname != 0)	/* Copying name */
+			*name++ = *objname++;
+
+		*name++ = 0;
+		*name = 0;	/* zero byte for comment */
+
+		if (o2->objectnode != 0)	/* ObjectNode reuse or creation */
+			errorcode = asfs_getnode(sb, o2->objectnode, &node_bh, &on);
+		else {
+			if ((errorcode = asfs_createnode(sb, &node_bh, (struct fsNode **) &on, &nodeno)) == 0) {
+				on->hash16 = cpu_to_be16(asfs_hash(o2->name, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE));
+				o2->objectnode = cpu_to_be32(nodeno);
+			}
+			asfs_debug("createnode returned with errorcode: %d\n", errorcode);
+		}
+
+		if (errorcode == 0) {	/* in io_bh there is a container with created object */
+			on->node.data = ((struct fsBlockHeader *) (*io_bh)->b_data)->ownblock;
+			if ((errorcode = hashobject(sb, hashblock, on, be32_to_cpu(o2->objectnode), objectname)) == 0) {
+				asfs_bstore(sb, node_bh);
+				asfs_brelse(node_bh);
+			} else
+				errorcode = -EIO;
+		}
+
+		if (errorcode == 0) {	/* HashBlock reuse or creation:*/
+
+			if ((o2->bits & OTYPE_DIR) != 0 && o2->object.dir.hashtable == 0) {
+				struct buffer_head *hashbh;
+				u32 hashblock;
+
+				asfs_debug("creating Hashblock\n");
+
+				if ((errorcode = asfs_allocadminspace(sb, &hashblock)) == 0 && (hashbh = asfs_getzeroblk(sb, hashblock))) {
+					struct fsHashTable *ht = (void *) hashbh->b_data;
+
+					o2->object.dir.hashtable = cpu_to_be32(hashblock);
+
+					ht->bheader.id = cpu_to_be32(ASFS_HASHTABLE_ID);
+					ht->bheader.ownblock = cpu_to_be32(hashblock);
+					ht->parent = o2->objectnode;
+
+					asfs_bstore(sb, hashbh);
+					asfs_brelse(hashbh);
+				}
+			}
+		}
+
+		if (errorcode == 0) {	/* SoftLink creation: */
+			if ((o2->bits & (OTYPE_LINK | OTYPE_HARDLINK)) == OTYPE_LINK && o2->object.file.data == 0) {
+				struct buffer_head *bh2;
+				u32 slinkblock;
+
+				if ((errorcode = asfs_allocadminspace(sb, &slinkblock)) == 0 && (bh2 = asfs_getzeroblk(sb, slinkblock))) {
+					struct fsSoftLink *sl = (void *) bh2->b_data;
+					o2->object.file.data = cpu_to_be32(slinkblock);
+					sl->bheader.id = cpu_to_be32(ASFS_SOFTLINK_ID);
+					sl->bheader.ownblock = cpu_to_be32(slinkblock);
+					sl->parent = o2->objectnode;
+					sl->next = 0;
+					sl->previous = 0;
+					asfs_bstore(sb, bh2);
+					asfs_brelse(bh2);
+				}
+			}
+		}
+	}
+	asfs_debug("createobject: done.\n");
+
+	return (errorcode);
+}
+
+	/* This function extends the file object 'o' with a number  of blocks
+		(hopefully, if any blocks has been found!). Only new Extents will
+      be created -- the size of the file will not be altered, and changing
+		it is left up to the caller.  If the file did not have any blocks
+		yet, then the o->object.file.data will be set to the first (new)
+		ExtentBNode. It returns the number of added blocks through
+		addedblocks pointer */
+
+int asfs_addblockstofile(struct super_block *sb, struct buffer_head *objbh, struct fsObject *o, u32 blocks, u32 * newspace, u32 * addedblocks)
+{
+	u32 lastextentbnode;
+	int errorcode = 0;
+	struct fsExtentBNode *ebnp;
+	struct buffer_head *block = NULL;
+
+
+	asfs_debug("extendblocksinfile: Trying to increasing number of blocks by %d.\n", blocks);
+
+	lastextentbnode = be32_to_cpu(o->object.file.data);
+
+	if (lastextentbnode != 0) {
+		while (lastextentbnode != 0 && errorcode == 0) {
+			if (block != NULL)
+				asfs_brelse(block);
+			errorcode = asfs_getextent(sb, lastextentbnode, &block, &ebnp);
+			lastextentbnode = be32_to_cpu(ebnp->next);
+		}
+		lastextentbnode = be32_to_cpu(ebnp->key);
+	}
+
+	if (errorcode == 0) {
+		u32 searchstart;
+
+		u32 found_block;
+		u32 found_blocks;
+
+		*addedblocks = 0;
+		*newspace = 0;
+
+		if (lastextentbnode != 0)
+			searchstart = be32_to_cpu(ebnp->key) + be16_to_cpu(ebnp->blocks);
+		else
+			searchstart = 0; //ASFS_SB(sb)->block_rovingblockptr;
+
+		if ((errorcode = asfs_findspace(sb, blocks, searchstart, searchstart, &found_block, &found_blocks)) != 0) {
+			asfs_brelse(block);
+			asfs_debug("extendblocksinfile: findspace returned %s\n", errorcode == -ENOSPC ? "ENOSPC" : "error");
+			return errorcode;
+		}
+
+		blocks = found_blocks;
+		errorcode = asfs_markspace(sb, found_block, found_blocks);
+		*addedblocks = found_blocks;
+		*newspace = found_block;
+
+		asfs_debug("extendblocksinfile: block = %u, lastextentbnode = %u, extentblocks = %d\n", found_block, lastextentbnode, blocks);
+
+		if ((errorcode = asfs_addblocks(sb, blocks, found_block, be32_to_cpu(o->objectnode), &lastextentbnode)) != 0) {
+			asfs_debug("extendblocksinfile: addblocks returned errorcode %d\n", errorcode);
+			return errorcode;
+		}
+
+		if (o->object.file.data == 0)
+			o->object.file.data = cpu_to_be32(lastextentbnode);
+	}
+
+	if (block)
+		asfs_brelse(block);
+	asfs_bstore(sb, objbh);
+
+	asfs_debug("addblockstofile: done. added %d blocks\n", *addedblocks);
+
+	return errorcode;
+}
+
+	/* The Object indicated by bh1 & o1, gets renamed to newname and placed
+	   in the directory indicated by bhparent & oparent. */
+
+int asfs_renameobject(struct super_block *sb, struct buffer_head *bh1, struct fsObject *o1, struct buffer_head *bhparent, struct fsObject *oparent, u8 * newname)
+{
+	struct fsObject object;
+	u32 oldparentnode = be32_to_cpu(((struct fsObjectContainer *) bh1->b_data)->parent);
+	u8 oldname[107];
+	int errorcode;
+
+	asfs_debug("renameobject: Renaming '%s' to '%s' in dir '%s'\n", o1->name, newname, oparent->name);
+
+	object = *o1;
+	strcpy(oldname, o1->name);
+
+	if ((errorcode = dehashobjectquick(sb, be32_to_cpu(o1->objectnode), o1->name, oldparentnode)) == 0) {
+		u32 parentobjectnode = be32_to_cpu(oparent->objectnode);
+
+		if ((errorcode = simpleremoveobject(sb, bh1, o1)) == 0) {
+			struct buffer_head *bh2 = bhparent;
+			struct fsObject *o2;
+
+			/* oparent might changed after simpleremoveobject */
+			oparent = o2 = find_obj_by_node(sb, (struct fsObjectContainer *) bhparent->b_data, parentobjectnode);
+
+			/* In goes the Parent bh & o, out comes the New object's bh & o :-) */
+			if ((errorcode = asfs_createobject(sb, &bh2, &o2, &object, newname, TRUE)) == 0) {
+				asfs_bstore(sb, bh2);
+				if (be32_to_cpu(oparent->objectnode) == ASFS_RECYCLEDNODE) {
+					asfs_debug("renameobject: Updating recycled dir info\n");
+					if ((errorcode = setrecycledinfodiff(sb, 1, (be32_to_cpu(o2->object.file.size) + sb->s_blocksize - 1) >> sb->s_blocksize_bits)) != 0) {
+						brelse(bh2);
+						return errorcode;
+					}
+				}
+				brelse(bh2);
+				asfs_debug("renameobject: Succesfully created & stored new object.\n");
+			} else { /* recreate object in old place, maybe this will not fail, but who knows... */
+				asfs_debug("renameobject: Creating new object failed. Trying to recreate it in source directory.\n");
+				if (asfs_readobject(sb, oldparentnode, &bh1, &o1) == 0) {
+					struct buffer_head *bh2 = bh1;
+					if (asfs_createobject(sb, &bh2, &o1, &object, oldname, TRUE) == 0) {
+						asfs_bstore(sb, bh2);
+						if (oldparentnode == ASFS_RECYCLEDNODE) {
+							asfs_debug("renameobject: Updating recycled dir info\n");
+							setrecycledinfodiff(sb, 1, (be32_to_cpu(o1->object.file.size) + sb->s_blocksize - 1) >> sb->s_blocksize_bits);
+						}
+						brelse(bh2);
+					}
+					brelse(bh1);
+				}
+			}
+		}
+	}
+	return errorcode;
+}
+
+		/* Truncates the specified file to /newsize/ bytes */
+
+int asfs_truncateblocksinfile(struct super_block *sb, struct buffer_head *bh, struct fsObject *o, u32 newsize)
+{
+	struct buffer_head *ebh;
+	struct fsExtentBNode *ebn;
+	int errorcode;
+	u32 pos = 0;
+	u32 newblocks = (newsize + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
+	u32 filedata = be32_to_cpu(o->object.file.data);
+	u32 eprev, ekey;
+	u16 eblocks;
+
+	asfs_debug("trucateblocksinfile: newsize %u\n", newsize);
+
+	if (filedata == 0)
+		return 0;
+
+	for (;;) {
+		if ((errorcode = asfs_getextent(sb, filedata, &ebh, &ebn)) != 0)
+			return errorcode;
+		if (pos + be16_to_cpu(ebn->blocks) >= newblocks)
+			break;
+		pos += be16_to_cpu(ebn->blocks);
+		if ((filedata = be32_to_cpu(ebn->next)) == 0)
+			break;
+		asfs_brelse(ebh);
+	};
+
+	eblocks = newblocks - pos;
+	ekey = be32_to_cpu(ebn->key);
+	eprev = be32_to_cpu(ebn->prev);
+
+	if (be16_to_cpu(ebn->blocks) < eblocks) {
+		printk("ASFS: Extent chain is too short or damaged!\n");
+		asfs_brelse(ebh);
+		return -ENOENT;
+	}
+	if (be16_to_cpu(ebn->blocks) - eblocks > 0 && (errorcode = asfs_freespace(sb, be32_to_cpu(ebn->key) + eblocks, be16_to_cpu(ebn->blocks) - eblocks)) != 0) {
+		asfs_brelse(ebh);
+		return errorcode;
+	}
+	if (be32_to_cpu(ebn->next) > 0 && (errorcode = asfs_deleteextents(sb, be32_to_cpu(ebn->next))) != 0) {
+		asfs_brelse(ebh);
+		return errorcode;
+	}
+	ebn->blocks = cpu_to_be16(eblocks);
+	ebn->next = 0;
+	asfs_bstore(sb, ebh);
+
+	if (eblocks == 0) {
+		if (eprev & MSB_MASK) {
+			o->object.file.data = 0;
+			asfs_bstore(sb, bh);
+		} else {
+			struct buffer_head *ebhp;
+			struct fsExtentBNode *ebnp;
+
+			if ((errorcode = asfs_getextent(sb, eprev & !MSB_MASK, &ebhp, &ebnp)) != 0) {
+				asfs_brelse(ebh);
+				return errorcode;
+			}
+
+			ebnp->next = 0;
+			asfs_bstore(sb, ebhp);
+			asfs_brelse(ebhp);
+		}
+		if ((errorcode = asfs_deletebnode(sb, ebh, ekey)) != 0) {
+			asfs_brelse(ebh);
+			return errorcode;
+		}
+	}
+	asfs_brelse(ebh);
+
+	return 0;
+}
+#endif
diff -urN oldtree/fs/asfs/super.c newtree/fs/asfs/super.c
--- oldtree/fs/asfs/super.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/asfs/super.c	2006-02-13 19:20:00.676409472 -0500
@@ -0,0 +1,496 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ *
+ * version: 1.0beta10 for 2.6.xx kernel
+ *
+ * Copyright (C) 2003,2004,2005  Marek 'March' Szyprowski <marek@amiga.pl>
+ *
+ * NLS support by Pavel Fedin (C) 2005
+ *
+ *
+ * Thanks to Marcin Kurek (Morgoth/Dreamolers-CAPS) for help and parts
+ * of original amiga version of SmartFilesystem source code.
+ *
+ * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx,
+ * Ralph Schmidt, Emmanuel Lesueur, David Gerber and Marcin Kurek
+ *
+ *
+ * ASFS is based on the Amiga FFS filesystem for Linux
+ * Copyright (C) 1993  Ray Burr
+ * Copyright (C) 1996  Hans-Joachim Widmaier
+ *
+ * Earlier versions were based on the Linux implementation of
+ * the ROMFS file system
+ * Copyright (C) 1997-1999  Janos Farkas <chexum@shadow.banki.hu>
+ *
+ * ASFS used some parts of the smbfs filesystem:
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ * Copyright (C) 1997 by Volker Lendecke
+ *
+ * and parts of the Minix filesystem additionally
+ * Copyright (C) 1991, 1992  Linus Torvalds
+ * Copyright (C) 1996  Gertjan van Wingerde
+ *
+ *
+ * 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 the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+/* todo:
+ * - remove bugs
+ * - add missing features (maybe safe-delete, other...)
+ * - create other fs tools like mkfs.asfs and fsck.asfs, some data-recovery tools
+ */
+
+#define ASFS_VERSION "1.0beta10 (13.06.2005)"
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/buffer_head.h>
+#include <linux/vfs.h>
+#include <linux/parser.h>
+#include <linux/nls.h>
+#include "asfs_fs.h"
+
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+static char asfs_default_codepage[] = CONFIG_ASFS_DEFAULT_CODEPAGE;
+static char asfs_default_iocharset[] = CONFIG_NLS_DEFAULT;
+
+static struct inode *asfs_alloc_inode(struct super_block *sb);
+static void asfs_destroy_inode(struct inode *inode);
+static void asfs_put_super(struct super_block *sb);
+static int asfs_statfs(struct super_block *sb, struct kstatfs *buf);
+#ifdef CONFIG_ASFS_RW
+static int asfs_remount(struct super_block *sb, int *flags, char *data);
+#endif
+
+u32 asfs_calcchecksum(void *block, u32 blocksize)
+{
+	u32 *data = block, checksum = 1;
+	while (blocksize > 0) {
+		checksum += be32_to_cpu(*data++);
+		blocksize -= 4;
+	}
+	checksum -= be32_to_cpu(((struct fsBlockHeader *)block)->checksum);
+	return -checksum;
+}
+
+static struct super_operations asfs_ops = {
+	.alloc_inode	= asfs_alloc_inode,
+	.destroy_inode	= asfs_destroy_inode,
+	.put_super		= asfs_put_super,
+	.statfs			= asfs_statfs,
+#ifdef CONFIG_ASFS_RW
+	.remount_fs		= asfs_remount,
+#endif
+};
+
+extern struct dentry_operations asfs_dentry_operations;
+
+enum {
+	Opt_mode, Opt_setgid, Opt_setuid, Opt_prefix, Opt_volume,
+	Opt_lcvol, Opt_iocharset, Opt_codepage, Opt_ignore, Opt_err
+};
+
+static match_table_t tokens = {
+	{Opt_mode, "mode=%o"},
+	{Opt_setgid, "setgid=%u"},
+	{Opt_setuid, "setuid=%u"},
+	{Opt_prefix, "prefix=%s"},
+	{Opt_volume, "volume=%s"},
+	{Opt_lcvol, "lowercasevol"},
+	{Opt_iocharset, "iocharset=%s"},
+	{Opt_codepage, "codepage=%s"},
+	{Opt_ignore, "grpquota"},
+	{Opt_ignore, "noquota"},
+	{Opt_ignore, "quota"},
+	{Opt_ignore, "usrquota"},
+	{Opt_err, NULL},
+};
+
+static int asfs_parse_options(char *options, struct super_block *sb)
+{
+	char *p;
+	substring_t args[MAX_OPT_ARGS];
+
+	if (!options)
+		return 1;
+	while ((p = strsep(&options, ",")) != NULL) {
+		int token, option;
+		if (!*p)
+			continue;
+		token = match_token(p, tokens, args);
+
+		switch (token) {
+		case Opt_mode:
+			if (match_octal(&args[0], &option))
+				goto no_arg;
+			ASFS_SB(sb)->mode = option & 0777;
+			break;
+		case Opt_setgid:
+			if (match_int(&args[0], &option))
+				goto no_arg;
+			ASFS_SB(sb)->gid = option;
+			break;
+		case Opt_setuid:
+			if (match_int(&args[0], &option))
+				goto no_arg;
+			ASFS_SB(sb)->uid = option;
+			break;
+		case Opt_prefix:
+			if (ASFS_SB(sb)->prefix) {
+				kfree(ASFS_SB(sb)->prefix);
+				ASFS_SB(sb)->prefix = NULL;
+			}
+			ASFS_SB(sb)->prefix = match_strdup(&args[0]);
+			if (! ASFS_SB(sb)->prefix)
+				return 0;
+			break;
+		case Opt_volume:
+			if (ASFS_SB(sb)->root_volume) {
+				kfree(ASFS_SB(sb)->root_volume);
+				ASFS_SB(sb)->root_volume = NULL;
+			}
+			ASFS_SB(sb)->root_volume = match_strdup(&args[0]);
+			if (! ASFS_SB(sb)->root_volume)
+				return 0;
+			break;
+		case Opt_lcvol:
+			ASFS_SB(sb)->flags |= ASFS_VOL_LOWERCASE;
+			break;
+		case Opt_iocharset:
+			if (ASFS_SB(sb)->iocharset != asfs_default_iocharset)
+				kfree(ASFS_SB(sb)->iocharset);
+			ASFS_SB(sb)->iocharset = match_strdup(&args[0]);
+			if (!ASFS_SB(sb)->iocharset)
+				return 0;
+			break;
+		case Opt_codepage:
+			if (ASFS_SB(sb)->codepage != asfs_default_codepage)
+				kfree(ASFS_SB(sb)->codepage);
+			ASFS_SB(sb)->codepage = match_strdup(&args[0]);
+			if (!ASFS_SB(sb)->codepage)
+				return 0;
+		case Opt_ignore:
+		 	/* Silently ignore the quota options */
+			break;
+		default:
+no_arg:
+			printk("ASFS: Unrecognized mount option \"%s\" "
+					"or missing value\n", p);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+static int asfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+	struct asfs_sb_info *sbi;
+	struct buffer_head *bh;
+	struct fsRootBlock *rootblock;
+	struct inode *rootinode;
+
+	sbi = kmalloc(sizeof(struct asfs_sb_info), GFP_KERNEL);
+	if (!sbi)
+		return -ENOMEM;
+	sb->s_fs_info = sbi;
+
+	/* Fill in defaults */
+	ASFS_SB(sb)->uid = ASFS_DEFAULT_UID;
+	ASFS_SB(sb)->gid = ASFS_DEFAULT_GID;
+	ASFS_SB(sb)->mode = ASFS_DEFAULT_MODE;
+	ASFS_SB(sb)->prefix = NULL;
+	ASFS_SB(sb)->root_volume = NULL;
+	ASFS_SB(sb)->flags = 0;
+	ASFS_SB(sb)->iocharset = asfs_default_iocharset;
+	ASFS_SB(sb)->codepage = asfs_default_codepage;
+
+	if (!asfs_parse_options(data, sb)) {
+		printk(KERN_ERR "ASFS: Error parsing options\n");
+		return -EINVAL;
+	}
+
+	if (!sb_set_blocksize(sb, 512))
+		return -EINVAL;
+	sb->s_maxbytes = ASFS_MAXFILESIZE;
+
+	bh = sb_bread(sb, 0);
+	if (!bh) {
+		printk(KERN_ERR "ASFS: unable to read superblock\n");
+		return -EINVAL;
+	}
+
+	rootblock = (struct fsRootBlock *)bh->b_data;
+
+	if (be32_to_cpu(rootblock->bheader.id) == ASFS_ROOTID &&
+		be16_to_cpu(rootblock->version) == ASFS_STRUCTURE_VERISON) {
+
+		sb->s_blocksize = be32_to_cpu(rootblock->blocksize);
+		ASFS_SB(sb)->totalblocks = be32_to_cpu(rootblock->totalblocks);
+		ASFS_SB(sb)->rootobjectcontainer = be32_to_cpu(rootblock->rootobjectcontainer);
+		ASFS_SB(sb)->extentbnoderoot = be32_to_cpu(rootblock->extentbnoderoot);
+		ASFS_SB(sb)->objectnoderoot = be32_to_cpu(rootblock->objectnoderoot);
+		ASFS_SB(sb)->flags |= 0xff & rootblock->bits;
+		ASFS_SB(sb)->adminspacecontainer = be32_to_cpu(rootblock->adminspacecontainer);
+		ASFS_SB(sb)->bitmapbase = be32_to_cpu(rootblock->bitmapbase);
+		ASFS_SB(sb)->blocks_inbitmap = (sb->s_blocksize - sizeof(struct fsBitmap))<<3;  /* must be a multiple of 32 !! */
+		ASFS_SB(sb)->blocks_bitmap = (ASFS_SB(sb)->totalblocks + ASFS_SB(sb)->blocks_inbitmap - 1) / ASFS_SB(sb)->blocks_inbitmap;
+		ASFS_SB(sb)->block_rovingblockptr = 0;
+		asfs_brelse(bh);
+
+		if (!sb_set_blocksize(sb, sb->s_blocksize)) {
+			printk(KERN_ERR "ASFS: Found Amiga SFS RootBlock on dev %s, but blocksize %ld is not supported!\n", \
+			       sb->s_id, sb->s_blocksize);
+			return -EINVAL;
+		}
+
+		bh = sb_bread(sb, 0);
+		if (!bh) {
+			printk(KERN_ERR "ASFS: unable to read superblock\n");
+			goto out;
+		}
+		rootblock = (struct fsRootBlock *)bh->b_data;
+
+		if (asfs_check_block((void *)rootblock, sb->s_blocksize, 0, ASFS_ROOTID)) {
+#ifdef CONFIG_ASFS_RW
+			struct buffer_head *tmpbh;
+			if ((tmpbh = asfs_breadcheck(sb, ASFS_SB(sb)->rootobjectcontainer, ASFS_OBJECTCONTAINER_ID))) {
+				struct fsRootInfo *ri = (struct fsRootInfo *)((u8 *)tmpbh->b_data + sb->s_blocksize - sizeof(struct fsRootInfo));
+				ASFS_SB(sb)->freeblocks = be32_to_cpu(ri->freeblocks);
+				asfs_brelse(tmpbh);
+			} else
+				ASFS_SB(sb)->freeblocks = 0;
+
+			if ((tmpbh = asfs_breadcheck(sb, ASFS_SB(sb)->rootobjectcontainer+2, ASFS_TRANSACTIONFAILURE_ID))) {
+				printk(KERN_NOTICE "VFS: Found Amiga SFS RootBlock on dev %s, but it has unfinished transaction. Mounting read-only.\n", sb->s_id);
+				ASFS_SB(sb)->flags |= ASFS_READONLY;
+				asfs_brelse(tmpbh);
+			}
+
+			if ((tmpbh = asfs_breadcheck(sb, ASFS_SB(sb)->totalblocks-1, ASFS_ROOTID)) == NULL) {
+				printk(KERN_NOTICE "VFS: Found Amiga SFS RootBlock on dev %s, but there is no second RootBlock! Mounting read-only.\n", sb->s_id);
+				ASFS_SB(sb)->flags |= ASFS_READONLY;
+				asfs_brelse(tmpbh);
+			}
+			if (!(ASFS_SB(sb)->flags & ASFS_READONLY))
+				printk(KERN_NOTICE "VFS: Found Amiga SFS RootBlock on dev %s.\n", sb->s_id);
+#else
+			ASFS_SB(sb)->freeblocks = 0;
+			ASFS_SB(sb)->flags |= ASFS_READONLY;
+			printk(KERN_NOTICE "VFS: Found Amiga SFS RootBlock on dev %s.\n", sb->s_id);
+#endif
+		} else {
+			if (!silent)
+				printk(KERN_ERR "VFS: Found Amiga SFS RootBlock on dev %s, but it has checksum error!\n", \
+				       sb->s_id);
+			goto out;
+		}
+	} else {
+		if (!silent)
+			printk(KERN_ERR "VFS: Can't find a valid Amiga SFS filesystem on dev %s.\n", \
+			       sb->s_id);
+		goto out;
+	}
+
+	asfs_brelse(bh);
+
+	sb->s_magic = ASFS_MAGIC;
+	sb->s_flags |= MS_NODEV | MS_NOSUID;
+	if (ASFS_SB(sb)->flags & ASFS_READONLY)
+		sb->s_flags |= MS_RDONLY;
+	sb->s_op = &asfs_ops;
+	asfs_debug("Case sensitive: %s\n", (ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE) ? "yes" : "no");
+
+	if (ASFS_SB(sb)->codepage[0] != '\0' && strcmp(ASFS_SB(sb)->codepage, "none") != 0) {
+		ASFS_SB(sb)->nls_disk = load_nls(ASFS_SB(sb)->codepage);
+		if (!ASFS_SB(sb)->nls_disk) {
+			printk(KERN_ERR "ASFS: codepage %s not found\n", ASFS_SB(sb)->codepage);
+			return -EINVAL;
+		}
+		ASFS_SB(sb)->nls_io = load_nls(ASFS_SB(sb)->iocharset);
+		if (!ASFS_SB(sb)->nls_io) {
+			printk(KERN_ERR "ASFS: IO charset %s not found\n", ASFS_SB(sb)->iocharset);
+			goto out2;
+		}
+	} else {
+		ASFS_SB(sb)->nls_io = NULL;
+		ASFS_SB(sb)->nls_disk = NULL;
+	}
+
+	if ((rootinode = asfs_get_root_inode(sb))) {
+		if ((sb->s_root = d_alloc_root(rootinode))) {
+			sb->s_root->d_op = &asfs_dentry_operations;
+			return 0;
+		}
+		iput(rootinode);
+	}
+	unload_nls(ASFS_SB(sb)->nls_io);
+out2:
+	unload_nls(ASFS_SB(sb)->nls_disk);
+	return -EINVAL;
+
+out:
+	asfs_brelse(bh);
+	return -EINVAL;
+
+}
+
+#ifdef CONFIG_ASFS_RW
+static int asfs_remount(struct super_block *sb, int *flags, char *data)
+{
+	asfs_debug("ASFS: remount (flags=0x%x, opts=\"%s\")\n",*flags,data);
+
+	if (!asfs_parse_options(data,sb))
+		return -EINVAL;
+
+	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
+		return 0;
+
+	if (*flags & MS_RDONLY) {
+		sb->s_flags |= MS_RDONLY;
+	} else if (!(ASFS_SB(sb)->flags & ASFS_READONLY)) {
+		sb->s_flags &= ~MS_RDONLY;
+	} else {
+		printk("VFS: Can't remount Amiga SFS on dev %s read/write because of errors.", sb->s_id);
+		return -EINVAL;
+	}
+	return 0;
+}
+#endif
+
+static void asfs_put_super(struct super_block *sb)
+{
+	struct asfs_sb_info *sbi = ASFS_SB(sb);
+
+	if (ASFS_SB(sb)->prefix)
+		kfree(ASFS_SB(sb)->prefix);
+	if (ASFS_SB(sb)->root_volume)
+		kfree(ASFS_SB(sb)->root_volume);
+	if (ASFS_SB(sb)->nls_disk)
+		unload_nls(ASFS_SB(sb)->nls_disk);
+	if (ASFS_SB(sb)->nls_io)
+		unload_nls(ASFS_SB(sb)->nls_io);
+	if (ASFS_SB(sb)->iocharset != asfs_default_iocharset)
+		kfree(ASFS_SB(sb)->iocharset);
+	if (ASFS_SB(sb)->codepage != asfs_default_codepage)
+		kfree(ASFS_SB(sb)->codepage);
+
+	kfree(sbi);
+	sb->s_fs_info = NULL;
+	return;
+}
+
+/* That's simple too. */
+static int asfs_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+	buf->f_type = ASFS_MAGIC;
+	buf->f_bsize = sb->s_blocksize;
+	buf->f_bfree = buf->f_bavail = ASFS_SB(sb)->freeblocks;
+	buf->f_blocks = ASFS_SB(sb)->totalblocks;
+	buf->f_namelen = ASFS_MAXFN;
+	return 0;
+}
+
+/* --- new in 2.6.x --- */
+static kmem_cache_t * asfs_inode_cachep;
+
+static struct inode *asfs_alloc_inode(struct super_block *sb)
+{
+	struct asfs_inode_info *ei;
+	ei = (struct asfs_inode_info *)kmem_cache_alloc(asfs_inode_cachep, SLAB_KERNEL);
+	if (!ei)
+		return NULL;
+	return &ei->vfs_inode;
+}
+
+static void asfs_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(asfs_inode_cachep, ASFS_I(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+	struct asfs_inode_info *ei = (struct asfs_inode_info *) foo;
+
+	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR) {
+		inode_init_once(&ei->vfs_inode);
+	}
+}
+
+static int init_inodecache(void)
+{
+	asfs_inode_cachep = kmem_cache_create("asfs_inode_cache",
+					     sizeof(struct asfs_inode_info),
+					     0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT,
+					     init_once, NULL);
+	if (asfs_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static void destroy_inodecache(void)
+{
+	if (kmem_cache_destroy(asfs_inode_cachep))
+		printk(KERN_INFO "asfs_inode_cache: not all structures were freed\n");
+}
+
+static struct super_block *asfs_get_sb(struct file_system_type *fs_type,
+	int flags, const char *dev_name, void *data)
+{
+	return get_sb_bdev(fs_type, flags, dev_name, data, asfs_fill_super);
+}
+
+static struct file_system_type asfs_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "asfs",
+	.get_sb		= asfs_get_sb,
+	.kill_sb	= kill_block_super,
+	.fs_flags	= FS_REQUIRES_DEV,
+};
+
+static int __init init_asfs_fs(void)
+{
+	int err = init_inodecache();
+	if (err)
+		goto out1;
+	err = register_filesystem(&asfs_fs_type);
+	if (err)
+		goto out;
+	return 0;
+out:
+	destroy_inodecache();
+out1:
+	return err;
+}
+
+static void __exit exit_asfs_fs(void)
+{
+	unregister_filesystem(&asfs_fs_type);
+	destroy_inodecache();
+}
+
+/* Yes, works even as a module... :) */
+
+#ifdef CONFIG_ASFS_RW
+MODULE_DESCRIPTION("Amiga Smart File System (read/write) support for Linux kernel 2.6.x v" ASFS_VERSION);
+#else
+MODULE_DESCRIPTION("Amiga Smart File System (read-only) support for Linux kernel 2.6.x v" ASFS_VERSION);
+#endif
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Marek Szyprowski <marek@amiga.pl>");
+
+module_init(init_asfs_fs)
+module_exit(exit_asfs_fs)
diff -urN oldtree/fs/asfs/symlink.c newtree/fs/asfs/symlink.c
--- oldtree/fs/asfs/symlink.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/asfs/symlink.c	2006-02-13 19:20:00.677409320 -0500
@@ -0,0 +1,235 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ * version: 1.0beta9
+ *
+ * Copyright (C) 2003,2004,2005  Marek 'March' Szyprowski <marek@amiga.pl>
+ *
+ *
+ * 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 the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/vfs.h>
+#include <linux/pagemap.h>
+#include <linux/nls.h>
+#include "asfs_fs.h"
+
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+int asfs_symlink_readpage(struct file *file, struct page *page)
+{
+	struct buffer_head *bh;
+	struct fsSoftLink *slinkcont;
+	struct inode *inode = page->mapping->host;
+	struct super_block *sb = inode->i_sb;
+	struct nls_table *nls_io = ASFS_SB(sb)->nls_io;
+	struct nls_table *nls_disk = ASFS_SB(sb)->nls_disk;
+	char *link = kmap(page);
+	int i = 0, j = 0;
+	char c, lc = 0, *prefix, *lf, *p;
+	wchar_t uni;
+	int clen;
+
+	if (!(bh = asfs_breadcheck(sb, ASFS_I(inode)->firstblock, ASFS_SOFTLINK_ID))) {
+		SetPageError(page);
+		kunmap(page);
+		unlock_page(page);
+		return -EIO;
+	}
+	slinkcont = (struct fsSoftLink *) bh->b_data;
+
+	lf = slinkcont->string;
+	prefix = ASFS_SB(sb)->prefix ? ASFS_SB(sb)->prefix : "/";
+
+	if ((p = strchr(lf,':'))) {	/* Handle assign or volume name */
+		if (ASFS_SB(sb)->root_volume &&
+		    strncmp(lf, ASFS_SB(sb)->root_volume, strlen(ASFS_SB(sb)->root_volume)) == 0) {
+			/* global root volume name found */
+			link[i++] = '/';
+			lf = p+1;
+		} else {
+			/* adding volume prefix */
+			while (i < 1023 && (c = prefix[i]))
+				link[i++] = c;
+			while (i < 1023 && lf[j] != ':')
+			{
+				c = lf[j++];
+				if (ASFS_SB(sb)->flags & ASFS_VOL_LOWERCASE)
+					c = asfs_lowerchar(c);
+				if (nls_io)
+				{
+					clen = nls_disk->char2uni(&c, 1, &uni);
+					if (clen>0) {
+						clen = nls_io->uni2char(uni, &link[i], NLS_MAX_CHARSET_SIZE);
+						if (clen>0)
+							i += clen;
+					}
+					if (clen<0)
+						link[i++] = '?';
+				} else
+					link[i++] = c;
+			}
+			if (i < 1023)
+				link[i++] = '/';
+			j++;
+		}
+		lc = '/';
+	}
+
+	while (i < 1023 && (c = lf[j])) {
+		if (c == '/' && lc == '/' && i < 1020) {	/* parent dir */
+			link[i++] = '.';
+			link[i++] = '.';
+		}
+		lc = c;
+		if (nls_io)
+		{
+			clen = nls_disk->char2uni(&c, 1, &uni);
+			if (clen>0) {
+				clen = nls_io->uni2char(uni, &link[i], NLS_MAX_CHARSET_SIZE);
+				if (clen>0)
+					i += clen;
+			}
+			if (clen<0)
+				link[i++] = '?';
+		} else
+			link[i++] = c;
+		j++;
+	}
+	link[i] = '\0';
+	SetPageUptodate(page);
+	kunmap(page);
+	unlock_page(page);
+	asfs_brelse(bh);
+	return 0;
+}
+
+#ifdef CONFIG_ASFS_RW
+
+int asfs_write_symlink(struct inode *symfile, const char *symname)
+{
+	struct super_block *sb = symfile->i_sb;
+	struct buffer_head *bh;
+	struct fsSoftLink *slinkcont;
+	struct nls_table *nls_io = ASFS_SB(sb)->nls_io;
+	struct nls_table *nls_disk = ASFS_SB(sb)->nls_disk;
+	char *p, c, lc;
+	int i, maxlen, pflen;
+	wchar_t uni;
+	int clen, blen;
+
+	asfs_debug("asfs_write_symlink %s to node %d\n", symname, (int)symfile->i_ino);
+
+	if (!(bh = asfs_breadcheck(sb, ASFS_I(symfile)->firstblock, ASFS_SOFTLINK_ID))) {
+		unlock_super(sb);
+		return -EIO;
+	}
+	slinkcont = (struct fsSoftLink *) bh->b_data;
+
+	/* translating symlink target path */
+
+	maxlen = sb->s_blocksize - sizeof(struct fsSoftLink) - 2;
+	i  = 0;
+	p  = slinkcont->string;
+	lc = '/';
+
+	if (*symname == '/') {
+		while (*symname == '/')
+			symname++;
+		if (ASFS_SB(sb)->prefix &&
+		    strncmp(symname-1, ASFS_SB(sb)->prefix, (pflen = strlen(ASFS_SB(sb)->prefix))) == 0) {
+			/* found volume prefix, ommiting it */
+			symname += pflen;
+			blen = strlen(symname);
+			while (*symname != '/' && *symname != '\0') {
+				clen = nls_io->char2uni(symname, blen, &uni);
+				if (clen>0) {
+					symname += clen;
+					blen -= clen;
+					clen = nls_disk->uni2char(uni, p, NLS_MAX_CHARSET_SIZE);
+					if (clen>0)
+						p += clen;
+				}
+				else
+				{
+					symname++;
+					blen--;
+				}
+				if (clen<0)
+					*p++ = '?';
+				i++;
+			}
+			symname++;
+			*p++ = ':';
+		} else if (ASFS_SB(sb)->root_volume) {	/* adding root volume name */
+			while (ASFS_SB(sb)->root_volume[i])
+				*p++ = ASFS_SB(sb)->root_volume[i++];
+			*p++ = ':';
+		} else {	/* do nothing */
+			*p++ = '/';
+		}
+		i++;
+	}
+
+	blen = strlen(symname);
+	while (i < maxlen && (c = *symname)) {
+		if (c == '.' && lc == '/' && symname[1] == '.' && symname[2] == '/') {
+			*p++ = '/';
+			i++;
+			symname += 3;
+			blen -= 3;
+			lc = '/';
+		} else if (c == '.' && lc == '/' && symname[1] == '/') {
+			symname += 2;
+			blen -= 2;
+			lc = '/';
+		} else {
+			clen = nls_io->char2uni(symname, blen, &uni);
+			if (clen>0) {
+				symname += clen;
+				blen -= clen;
+				clen = nls_disk->uni2char(uni, p, NLS_MAX_CHARSET_SIZE);
+				if (clen>0)
+					lc = *p;
+					p += clen;
+			}
+			else
+			{
+				symname++;
+				blen--;
+			}
+			if (clen<0)
+			{
+				*p++ = '?';
+				lc = '?';
+			}
+			i++;
+		}
+		if (lc == '/')
+			while (*symname == '/')
+			{
+				symname++;
+				blen--;
+			}
+	}
+	*p = 0;
+
+	asfs_bstore(sb, bh);
+	asfs_brelse(bh);
+
+	unlock_super(sb);
+
+	return 0;
+}
+
+#endif
diff -urN oldtree/fs/bio.c newtree/fs/bio.c
--- oldtree/fs/bio.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/bio.c	2006-02-13 19:20:00.677409320 -0500
@@ -1198,11 +1198,11 @@
 		scale = 4;
 
 	/*
-	 * scale number of entries
+	 * Limit number of entries reserved -- mempools are only used when
+	 * the system is completely unable to allocate memory, so we only
+	 * need enough to make progress.
 	 */
-	bvec_pool_entries = megabytes * 2;
-	if (bvec_pool_entries > 256)
-		bvec_pool_entries = 256;
+	bvec_pool_entries = 1 + scale;
 
 	fs_bio_set = bioset_create(BIO_POOL_SIZE, bvec_pool_entries, scale);
 	if (!fs_bio_set)
diff -urN oldtree/fs/cifs/CHANGES newtree/fs/cifs/CHANGES
--- oldtree/fs/cifs/CHANGES	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/CHANGES	2006-02-13 19:20:00.678409168 -0500
@@ -1,3 +1,11 @@
+Version 1.40
+------------
+Use fsuid (fsgid) more consistently instead of uid (gid). Improve performance
+of readpages by eliminating one extra memcpy. Allow update of file size
+from remote server even if file is open for write as long as mount is
+directio.  Recognize share mode security and send NTLM encrypted password
+on tree connect if share mode negotiated.
+
 Version 1.39
 ------------
 Defer close of a file handle slightly if pending writes depend on that handle
@@ -7,6 +15,8 @@
 CIFS Unix Extensions.  Fix setfacl/getfacl on bigendian. Timeout negative
 dentries so files that the client sees as deleted but that later get created
 on the server will be recognized.  Add client side permission check on setattr.
+Timeout stuck requests better (where server has never responded or sent corrupt
+responses)
 
 Version 1.38
 ------------
diff -urN oldtree/fs/cifs/README newtree/fs/cifs/README
--- oldtree/fs/cifs/README	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/README	2006-02-13 19:20:00.678409168 -0500
@@ -436,7 +436,17 @@
 		SFU does).  In the future the bottom 9 bits of the mode
 		mode also will be emulated using queries of the security
 		descriptor (ACL).
-		
+sec		Security mode.  Allowed values are:
+			none	attempt to connection as a null user (no name)
+			krb5    Use Kerberos version 5 authentication
+			krb5i   Use Kerberos authentication and packet signing
+			ntlm    Use NTLM password hashing (default)
+			ntlmi   Use NTLM password hashing with signing (if
+				/proc/fs/cifs/PacketSigningEnabled on or if
+				server requires signing also can be the default) 
+			ntlmv2  Use NTLMv2 password hashing      
+			ntlmv2i Use NTLMv2 password hashing with packet signing
+
 The mount.cifs mount helper also accepts a few mount options before -o
 including:
 
diff -urN oldtree/fs/cifs/cifs_debug.c newtree/fs/cifs/cifs_debug.c
--- oldtree/fs/cifs/cifs_debug.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/cifs_debug.c	2006-02-13 19:20:00.679409016 -0500
@@ -219,6 +219,10 @@
 
         if (c == '1' || c == 'y' || c == 'Y' || c == '0') {
 		read_lock(&GlobalSMBSeslock);
+#ifdef CONFIG_CIFS_STATS2
+		atomic_set(&totBufAllocCount, 0);
+		atomic_set(&totSmBufAllocCount, 0);
+#endif /* CONFIG_CIFS_STATS2 */
 		list_for_each(tmp, &GlobalTreeConnectionList) {
 			tcon = list_entry(tmp, struct cifsTconInfo,
 					cifsConnectionList);
@@ -276,6 +280,14 @@
 			smBufAllocCount.counter,cifs_min_small);
 	length += item_length;
 	buf += item_length;
+#ifdef CONFIG_CIFS_STATS2
+        item_length = sprintf(buf, "Total Large %d Small %d Allocations\n",
+				atomic_read(&totBufAllocCount),
+		                atomic_read(&totSmBufAllocCount));
+	length += item_length;
+	buf += item_length;
+#endif /* CONFIG_CIFS_STATS2 */
+
 	item_length = 
 		sprintf(buf,"Operations (MIDs): %d\n",
 			midCount.counter);
@@ -389,8 +401,8 @@
 static write_proc_t ntlmv2_enabled_write;
 static read_proc_t packet_signing_enabled_read;
 static write_proc_t packet_signing_enabled_write;
-static read_proc_t quotaEnabled_read;
-static write_proc_t quotaEnabled_write;
+static read_proc_t experimEnabled_read;
+static write_proc_t experimEnabled_write;
 static read_proc_t linuxExtensionsEnabled_read;
 static write_proc_t linuxExtensionsEnabled_write;
 
@@ -430,9 +442,9 @@
 		pde->write_proc = oplockEnabled_write;
 
 	pde = create_proc_read_entry("Experimental", 0, proc_fs_cifs,
-				quotaEnabled_read, NULL);
+				experimEnabled_read, NULL);
 	if (pde)
-		pde->write_proc = quotaEnabled_write;
+		pde->write_proc = experimEnabled_write;
 
 	pde = create_proc_read_entry("LinuxExtensionsEnabled", 0, proc_fs_cifs,
 				linuxExtensionsEnabled_read, NULL);
@@ -574,14 +586,13 @@
 }
 
 static int
-quotaEnabled_read(char *page, char **start, off_t off,
+experimEnabled_read(char *page, char **start, off_t off,
                    int count, int *eof, void *data)
 {
         int len;
 
         len = sprintf(page, "%d\n", experimEnabled);
-/* could also check if quotas are enabled in kernel
-	as a whole first */
+
         len -= off;
         *start = page + off;
 
@@ -596,21 +607,23 @@
         return len;
 }
 static int
-quotaEnabled_write(struct file *file, const char __user *buffer,
+experimEnabled_write(struct file *file, const char __user *buffer,
                     unsigned long count, void *data)
 {
-        char c;
-        int rc;
+	char c;
+	int rc;
 
-        rc = get_user(c, buffer);
-        if (rc)
-                return rc;
-        if (c == '0' || c == 'n' || c == 'N')
-                experimEnabled = 0;
-        else if (c == '1' || c == 'y' || c == 'Y')
-                experimEnabled = 1;
+	rc = get_user(c, buffer);
+	if (rc)
+		return rc;
+	if (c == '0' || c == 'n' || c == 'N')
+		experimEnabled = 0;
+	else if (c == '1' || c == 'y' || c == 'Y')
+		experimEnabled = 1;
+	else if (c == '2')
+		experimEnabled = 2;
 
-        return count;
+	return count;
 }
 
 static int
@@ -620,8 +633,6 @@
         int len;
 
         len = sprintf(page, "%d\n", linuxExtEnabled);
-/* could also check if quotas are enabled in kernel
-	as a whole first */
         len -= off;
         *start = page + off;
 
diff -urN oldtree/fs/cifs/cifs_fs_sb.h newtree/fs/cifs/cifs_fs_sb.h
--- oldtree/fs/cifs/cifs_fs_sb.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/cifs_fs_sb.h	2006-02-13 19:20:00.679409016 -0500
@@ -24,9 +24,10 @@
 #define CIFS_MOUNT_DIRECT_IO    8 /* do not write nor read through page cache */
 #define CIFS_MOUNT_NO_XATTR  0x10 /* if set - disable xattr support */
 #define CIFS_MOUNT_MAP_SPECIAL_CHR 0x20 /* remap illegal chars in filenames */
-#define CIFS_MOUNT_POSIX_PATHS 0x40 /* Negotiate posix pathnames if possible. */
-#define CIFS_MOUNT_UNX_EMUL    0x80 /* Network compat with SFUnix emulation */
+#define CIFS_MOUNT_POSIX_PATHS	0x40 /* Negotiate posix pathnames if possible. */
+#define CIFS_MOUNT_UNX_EMUL	0x80 /* Network compat with SFUnix emulation */
 #define CIFS_MOUNT_NO_BRL	0x100 /* No sending byte range locks to srv */
+#define CIFS_MOUNT_CIFS_ACL	0x200 /* send ACL requests to non-POSIX srv */
 
 struct cifs_sb_info {
 	struct cifsTconInfo *tcon;	/* primary mount */
diff -urN oldtree/fs/cifs/cifsacl.h newtree/fs/cifs/cifsacl.h
--- oldtree/fs/cifs/cifsacl.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/cifs/cifsacl.h	2006-02-13 19:20:00.679409016 -0500
@@ -0,0 +1,38 @@
+/*
+ *   fs/cifs/cifsacl.h
+ *
+ *   Copyright (c) International Business Machines  Corp., 2005
+ *   Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published
+ *   by the Free Software Foundation; either version 2.1 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ *   the GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _CIFSACL_H
+#define _CIFSACL_H
+
+struct cifs_sid {
+	__u8 revision; /* revision level */
+	__u8 num_subauths;
+	__u8 authority[6];
+	__u32 sub_auth[4];
+	/* next sub_auth if any ... */
+} __attribute__((packed));
+
+/* everyone */
+extern const struct cifs_sid sid_everyone;
+/* group users */
+extern const struct cifs_sid sid_user;
+
+#endif /* _CIFSACL_H */
diff -urN oldtree/fs/cifs/cifsencrypt.c newtree/fs/cifs/cifsencrypt.c
--- oldtree/fs/cifs/cifsencrypt.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/cifsencrypt.c	2006-02-13 19:20:00.680408864 -0500
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/cifsencrypt.c
  *
- *   Copyright (C) International Business Machines  Corp., 2003
+ *   Copyright (C) International Business Machines  Corp., 2005
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -82,6 +82,59 @@
 	return rc;
 }
 
+static int cifs_calc_signature2(const struct kvec * iov, int n_vec,
+				const char * key, char * signature)
+{
+        struct  MD5Context context;
+
+        if((iov == NULL) || (signature == NULL))
+                return -EINVAL;
+
+        MD5Init(&context);
+        MD5Update(&context,key,CIFS_SESSION_KEY_SIZE+16);
+
+/*        MD5Update(&context,cifs_pdu->Protocol,cifs_pdu->smb_buf_length); */ /* BB FIXME BB */
+
+        MD5Final(signature,&context);
+
+	return -EOPNOTSUPP;
+/*        return 0; */
+}
+
+
+int cifs_sign_smb2(struct kvec * iov, int n_vec, struct TCP_Server_Info *server,
+		   __u32 * pexpected_response_sequence_number)
+{
+	int rc = 0;
+	char smb_signature[20];
+	struct smb_hdr * cifs_pdu = iov[0].iov_base;
+
+	if((cifs_pdu == NULL) || (server == NULL))
+		return -EINVAL;
+
+	if((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0)
+		return rc;
+
+        spin_lock(&GlobalMid_Lock);
+        cifs_pdu->Signature.Sequence.SequenceNumber = 
+				cpu_to_le32(server->sequence_number);
+        cifs_pdu->Signature.Sequence.Reserved = 0;
+
+        *pexpected_response_sequence_number = server->sequence_number++;
+        server->sequence_number++;
+        spin_unlock(&GlobalMid_Lock);
+
+        rc = cifs_calc_signature2(iov, n_vec, server->mac_signing_key,
+				      smb_signature);
+        if(rc)
+                memset(cifs_pdu->Signature.SecuritySignature, 0, 8);
+        else
+                memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8);
+
+        return rc;
+
+}
+
 int cifs_verify_signature(struct smb_hdr * cifs_pdu, const char * mac_key,
 	__u32 expected_sequence_number)
 {
diff -urN oldtree/fs/cifs/cifsfs.c newtree/fs/cifs/cifsfs.c
--- oldtree/fs/cifs/cifsfs.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/cifsfs.c	2006-02-13 19:20:00.681408712 -0500
@@ -513,6 +513,17 @@
 	return written;
 }
 
+static loff_t cifs_llseek(struct file *file, loff_t offset, int origin)
+{
+	/* origin == SEEK_END => we must revalidate the cached file length */
+	if (origin == 2) {
+		int retval = cifs_revalidate(file->f_dentry);
+		if (retval < 0)
+			return (loff_t)retval;
+	}
+	return remote_llseek(file, offset, origin);
+}
+
 static struct file_system_type cifs_fs_type = {
 	.owner = THIS_MODULE,
 	.name = "cifs",
@@ -586,6 +597,7 @@
 	.flush = cifs_flush,
 	.mmap  = cifs_file_mmap,
 	.sendfile = generic_file_sendfile,
+	.llseek = cifs_llseek,
 #ifdef CONFIG_CIFS_POSIX
 	.ioctl	= cifs_ioctl,
 #endif /* CONFIG_CIFS_POSIX */
@@ -609,7 +621,7 @@
 #ifdef CONFIG_CIFS_POSIX
 	.ioctl  = cifs_ioctl,
 #endif /* CONFIG_CIFS_POSIX */
-
+	.llseek = cifs_llseek,
 #ifdef CONFIG_CIFS_EXPERIMENTAL
 	.dir_notify = cifs_dir_notify,
 #endif /* CONFIG_CIFS_EXPERIMENTAL */
@@ -627,6 +639,7 @@
 	.flush = cifs_flush,
 	.mmap  = cifs_file_mmap,
 	.sendfile = generic_file_sendfile,
+	.llseek = cifs_llseek,
 #ifdef CONFIG_CIFS_POSIX
 	.ioctl	= cifs_ioctl,
 #endif /* CONFIG_CIFS_POSIX */
@@ -649,7 +662,7 @@
 #ifdef CONFIG_CIFS_POSIX
 	.ioctl  = cifs_ioctl,
 #endif /* CONFIG_CIFS_POSIX */
-
+	.llseek = cifs_llseek,
 #ifdef CONFIG_CIFS_EXPERIMENTAL
 	.dir_notify = cifs_dir_notify,
 #endif /* CONFIG_CIFS_EXPERIMENTAL */
@@ -733,7 +746,7 @@
 		kmem_cache_destroy(cifs_req_cachep);
 		return -ENOMEM;
 	}
-	/* 256 (MAX_CIFS_HDR_SIZE bytes is enough for most SMB responses and
+	/* MAX_CIFS_SMALL_BUFFER_SIZE bytes is enough for most SMB responses and
 	almost all handle based requests (but not write response, nor is it
 	sufficient for path based requests).  A smaller size would have
 	been more efficient (compacting multiple slab items on one 4k page) 
@@ -742,7 +755,8 @@
 	efficient to alloc 1 per page off the slab compared to 17K (5page) 
 	alloc of large cifs buffers even when page debugging is on */
 	cifs_sm_req_cachep = kmem_cache_create("cifs_small_rq",
-			MAX_CIFS_HDR_SIZE, 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+			MAX_CIFS_SMALL_BUFFER_SIZE, 0, SLAB_HWCACHE_ALIGN, 
+			NULL, NULL);
 	if (cifs_sm_req_cachep == NULL) {
 		mempool_destroy(cifs_req_poolp);
 		kmem_cache_destroy(cifs_req_cachep);
@@ -954,6 +968,12 @@
 	atomic_set(&tconInfoReconnectCount, 0);
 
 	atomic_set(&bufAllocCount, 0);
+	atomic_set(&smBufAllocCount, 0);
+#ifdef CONFIG_CIFS_STATS2
+	atomic_set(&totBufAllocCount, 0);
+	atomic_set(&totSmBufAllocCount, 0);
+#endif /* CONFIG_CIFS_STATS2 */
+
 	atomic_set(&midCount, 0);
 	GlobalCurrentXid = 0;
 	GlobalTotalActiveXid = 0;
diff -urN oldtree/fs/cifs/cifsfs.h newtree/fs/cifs/cifsfs.h
--- oldtree/fs/cifs/cifsfs.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/cifsfs.h	2006-02-13 19:20:00.681408712 -0500
@@ -99,5 +99,5 @@
 extern ssize_t	cifs_listxattr(struct dentry *, char *, size_t);
 extern int cifs_ioctl (struct inode * inode, struct file * filep,
 		       unsigned int command, unsigned long arg);
-#define CIFS_VERSION   "1.39"
+#define CIFS_VERSION   "1.40"
 #endif				/* _CIFSFS_H */
diff -urN oldtree/fs/cifs/cifsglob.h newtree/fs/cifs/cifsglob.h
--- oldtree/fs/cifs/cifsglob.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/cifsglob.h	2006-02-13 19:20:00.681408712 -0500
@@ -233,6 +233,8 @@
 	atomic_t num_hardlinks;
 	atomic_t num_symlinks;
 	atomic_t num_locks;
+	atomic_t num_acl_get;
+	atomic_t num_acl_set;
 #ifdef CONFIG_CIFS_STATS2
 	unsigned long long time_writes;
 	unsigned long long time_reads;
@@ -285,6 +287,7 @@
 	unsigned endOfSearch:1;
 	unsigned emptyDir:1;
 	unsigned unicode:1;
+	unsigned smallBuf:1; /* so we know which buf_release function to call */
 };
 
 struct cifsFileInfo {
@@ -420,7 +423,12 @@
 #define   MID_RESPONSE_RECEIVED 4
 #define   MID_RETRY_NEEDED      8 /* session closed while this request out */
 #define   MID_NO_RESP_NEEDED 0x10
-#define   MID_SMALL_BUFFER   0x20 /* 112 byte response buffer instead of 4K */
+
+/* Types of response buffer returned from SendReceive2 */
+#define   CIFS_NO_BUFFER        0    /* Response buffer not returned */
+#define   CIFS_SMALL_BUFFER     1
+#define   CIFS_LARGE_BUFFER     2
+#define   CIFS_IOVEC            4    /* array of response buffers */
 
 /*
  *****************************************************************
@@ -505,8 +513,12 @@
 GLOBAL_EXTERN atomic_t tconInfoReconnectCount;
 
 /* Various Debug counters to remove someday (BB) */
-GLOBAL_EXTERN atomic_t bufAllocCount;
-GLOBAL_EXTERN atomic_t smBufAllocCount;      
+GLOBAL_EXTERN atomic_t bufAllocCount;    /* current number allocated  */
+#ifdef CONFIG_CIFS_STATS2
+GLOBAL_EXTERN atomic_t totBufAllocCount; /* total allocated over all time */
+GLOBAL_EXTERN atomic_t totSmBufAllocCount;
+#endif
+GLOBAL_EXTERN atomic_t smBufAllocCount;
 GLOBAL_EXTERN atomic_t midCount;
 
 /* Misc globals */
diff -urN oldtree/fs/cifs/cifspdu.h newtree/fs/cifs/cifspdu.h
--- oldtree/fs/cifs/cifspdu.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/cifspdu.h	2006-02-13 19:20:00.683408408 -0500
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/cifspdu.h
  *
- *   Copyright (c) International Business Machines  Corp., 2002
+ *   Copyright (c) International Business Machines  Corp., 2002,2005
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -80,7 +80,11 @@
 #define NT_TRANSACT_GET_USER_QUOTA    0x07
 #define NT_TRANSACT_SET_USER_QUOTA    0x08
 
-#define MAX_CIFS_HDR_SIZE 256	/* is future chained NTCreateXReadX bigger? */
+#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */
+/* future chained NTCreateXReadX bigger, but for time being NTCreateX biggest */
+/* among the requests (NTCreateX response is bigger with wct of 34) */
+#define MAX_CIFS_HDR_SIZE 0x58 /* 4 len + 32 hdr + (2*24 wct) + 2 bct + 2 pad */
+#define CIFS_SMALL_PATH 120 /* allows for (448-88)/3 */
 
 /* internal cifs vfs structures */
 /*****************************************************************
@@ -524,7 +528,7 @@
 		/* STRING PrimaryDomain */
 		/* STRING NativeOS */
 		/* STRING NativeLanMan */
-	} __attribute__((packed)) old_req;		/* pre-NTLM (LANMAN2.1) request format */
+	} __attribute__((packed)) old_req; /* pre-NTLM (LANMAN2.1) req format */
 
 	struct {		/* default (NTLM) response format */
 		struct smb_hdr hdr;	/* wct = 3 */
@@ -536,7 +540,7 @@
 		unsigned char NativeOS[1];	/* followed by */
 /*	unsigned char * NativeLanMan; */
 /*      unsigned char * PrimaryDomain; */
-	} __attribute__((packed)) old_resp;		/* pre-NTLM (LANMAN2.1) response format */
+	} __attribute__((packed)) old_resp; /* pre-NTLM (LANMAN2.1) response */
 } __attribute__((packed)) SESSION_SETUP_ANDX;
 
 #define CIFS_NETWORK_OPSYS "CIFS VFS Client for Linux"
@@ -1003,10 +1007,49 @@
 
 /* empty wct response to setattr */
 
-/***************************************************/
-/* NT Transact structure defintions follow         */
-/* Currently only ioctl and notify are implemented */
-/***************************************************/
+/*******************************************************/
+/* NT Transact structure defintions follow             */
+/* Currently only ioctl, acl (get security descriptor) */  
+/* and notify are implemented                          */
+/*******************************************************/
+typedef struct smb_com_ntransact_req {
+        struct smb_hdr hdr; /* wct >= 19 */
+        __u8 MaxSetupCount;
+        __u16 Reserved;
+        __le32 TotalParameterCount;
+        __le32 TotalDataCount;
+        __le32 MaxParameterCount;
+        __le32 MaxDataCount;
+        __le32 ParameterCount;
+        __le32 ParameterOffset;
+        __le32 DataCount;
+        __le32 DataOffset;
+        __u8 SetupCount; /* four setup words follow subcommand */
+        /* SNIA spec incorrectly included spurious pad here */
+        __le16 SubCommand; /* 2 = IOCTL/FSCTL */
+	/* SetupCount words follow then */ 
+        __le16 ByteCount;
+        __u8 Pad[3];
+        __u8 Parms[0];
+} __attribute__((packed)) NTRANSACT_REQ;
+
+typedef struct smb_com_ntransact_rsp {
+	struct smb_hdr hdr;     /* wct = 18 */
+	__u8 Reserved[3];
+	__le32 TotalParameterCount;
+	__le32 TotalDataCount;
+	__le32 ParameterCount;
+	__le32 ParameterOffset;
+	__le32 ParameterDisplacement;
+	__le32 DataCount;
+	__le32 DataOffset;
+	__le32 DataDisplacement;
+	__u8 SetupCount;   /* 0 */
+	__u16 ByteCount;
+        /* __u8 Pad[3]; */
+	/* parms and data follow */
+} __attribute__((packed)) NTRANSACT_RSP;
+
 typedef struct smb_com_transaction_ioctl_req {
 	struct smb_hdr hdr;	/* wct = 23 */
 	__u8 MaxSetupCount;
@@ -1021,11 +1064,11 @@
 	__le32 DataOffset;
 	__u8 SetupCount; /* four setup words follow subcommand */
 	/* SNIA spec incorrectly included spurious pad here */
-	__le16 SubCommand;/* 2 = IOCTL/FSCTL */
+	__le16 SubCommand; /* 2 = IOCTL/FSCTL */
 	__le32 FunctionCode;
 	__u16 Fid;
-	__u8 IsFsctl;    /* 1 = File System Control, 0 = device control (IOCTL)*/
-	__u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS share)*/
+	__u8 IsFsctl;  /* 1 = File System Control 0 = device control (IOCTL) */
+	__u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */
 	__le16 ByteCount;
 	__u8 Pad[3];
 	__u8 Data[1];
@@ -1045,9 +1088,35 @@
 	__u8 SetupCount;	/* 1 */
 	__le16 ReturnedDataLen;
 	__u16 ByteCount;
-	__u8 Pad[3];
 } __attribute__((packed)) TRANSACT_IOCTL_RSP;
 
+#define CIFS_ACL_OWNER 1
+#define CIFS_ACL_GROUP 2
+#define CIFS_ACL_DACL  4
+#define CIFS_ACL_SACL  8
+
+typedef struct smb_com_transaction_qsec_req {
+	struct smb_hdr hdr;     /* wct = 19 */
+	__u8 MaxSetupCount;
+	__u16 Reserved;
+	__le32 TotalParameterCount;
+	__le32 TotalDataCount;
+	__le32 MaxParameterCount;
+	__le32 MaxDataCount;
+	__le32 ParameterCount;
+	__le32 ParameterOffset;
+	__le32 DataCount;
+	__le32 DataOffset;
+	__u8 SetupCount; /* no setup words follow subcommand */
+	/* SNIA spec incorrectly included spurious pad here */
+	__le16 SubCommand; /* 6 = QUERY_SECURITY_DESC */
+	__le16 ByteCount; /* bcc = 3 + 8 */
+	__u8 Pad[3];
+	__u16 Fid;
+	__u16 Reserved2;
+	__le32 AclFlags;
+} __attribute__((packed)) QUERY_SEC_DESC_REQ;
+
 typedef struct smb_com_transaction_change_notify_req {
 	struct smb_hdr hdr;     /* wct = 23 */
 	__u8 MaxSetupCount;
@@ -1068,10 +1137,12 @@
 	__u8 WatchTree;  /* 1 = Monitor subdirectories */
 	__u8 Reserved2;
 	__le16 ByteCount;
-/* __u8 Pad[3];*/
+/* 	__u8 Pad[3];*/
 /*	__u8 Data[1];*/
 } __attribute__((packed)) TRANSACT_CHANGE_NOTIFY_REQ;
 
+/* BB eventually change to use generic ntransact rsp struct 
+      and validation routine */
 typedef struct smb_com_transaction_change_notify_rsp {
 	struct smb_hdr hdr;	/* wct = 18 */
 	__u8 Reserved[3];
diff -urN oldtree/fs/cifs/cifsproto.h newtree/fs/cifs/cifsproto.h
--- oldtree/fs/cifs/cifsproto.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/cifsproto.h	2006-02-13 19:20:00.683408408 -0500
@@ -48,8 +48,8 @@
 			struct smb_hdr * /* out */ ,
 			int * /* bytes returned */ , const int long_op);
 extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *,
-			struct kvec *, int /* nvec */,
-			int * /* bytes returned */ , const int long_op);
+			struct kvec *, int /* nvec to send */, 
+			int * /* type of buf returned */ , const int long_op);
 extern int checkSMBhdr(struct smb_hdr *smb, __u16 mid);
 extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length);
 extern int is_valid_oplock_break(struct smb_hdr *smb);
@@ -93,11 +93,12 @@
 			const struct nls_table *);
 
 extern int CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
-            const char *searchName, const struct nls_table *nls_codepage,
-            __u16 *searchHandle, struct cifs_search_info * psrch_inf, int map, const char dirsep);
+		const char *searchName, const struct nls_table *nls_codepage,
+		__u16 *searchHandle, struct cifs_search_info * psrch_inf, 
+		int map, const char dirsep);
 
 extern int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
-            __u16 searchHandle, struct cifs_search_info * psrch_inf);
+		__u16 searchHandle, struct cifs_search_info * psrch_inf);
 
 extern int CIFSFindClose(const int, struct cifsTconInfo *tcon,
 			const __u16 search_handle);
@@ -230,19 +231,18 @@
 			const int smb_file_id);
 
 extern int CIFSSMBRead(const int xid, struct cifsTconInfo *tcon,
-			const int netfid, unsigned int count,
-			const __u64 lseek, unsigned int *nbytes, char **buf);
+                        const int netfid, unsigned int count,
+                        const __u64 lseek, unsigned int *nbytes, char **buf,
+			int * return_buf_type);
 extern int CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
 			const int netfid, const unsigned int count,
 			const __u64 lseek, unsigned int *nbytes,
 			const char *buf, const char __user *ubuf, 
 			const int long_op);
-#ifdef CONFIG_CIFS_EXPERIMENTAL
 extern int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
 			const int netfid, const unsigned int count,
 			const __u64 offset, unsigned int *nbytes, 
 			struct kvec *iov, const int nvec, const int long_op);
-#endif /* CONFIG_CIFS_EXPERIMENTAL */
 extern int CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
 			const unsigned char *searchName, __u64 * inode_number,
 			const struct nls_table *nls_codepage, 
@@ -269,6 +269,8 @@
 extern int cifs_reconnect(struct TCP_Server_Info *server);
 
 extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *,__u32 *);
+extern int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *,
+			  __u32 *);
 extern int cifs_verify_signature(struct smb_hdr *, const char * mac_key,
 	__u32 expected_sequence_number);
 extern int cifs_calculate_mac_key(char * key,const char * rn,const char * pass);
@@ -297,6 +299,9 @@
 		const char *fileName, const char * ea_name, 
 		const void * ea_value, const __u16 ea_value_len, 
 		const struct nls_table *nls_codepage, int remap_special_chars);
+extern int CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon,
+			__u16 fid, char *acl_inf, const int buflen,
+			const int acl_type /* ACCESS vs. DEFAULT */);
 extern int CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
 		const unsigned char *searchName,
 		char *acl_inf, const int buflen,const int acl_type,
diff -urN oldtree/fs/cifs/cifssmb.c newtree/fs/cifs/cifssmb.c
--- oldtree/fs/cifs/cifssmb.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/cifssmb.c	2006-02-13 19:20:00.686407952 -0500
@@ -37,6 +37,7 @@
 #include "cifsproto.h"
 #include "cifs_unicode.h"
 #include "cifs_debug.h"
+#include "cifsacl.h"
 
 #ifdef CONFIG_CIFS_POSIX
 static struct {
@@ -372,8 +373,10 @@
 	rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
 	if (rc == 0) {
-		server->secMode = pSMBr->SecurityMode;	
-		server->secType = NTLM; /* BB override default for 
+		server->secMode = pSMBr->SecurityMode;
+		if((server->secMode & SECMODE_USER) == 0)
+			cFYI(1,("share mode security"));
+		server->secType = NTLM; /* BB override default for
 					   NTLMv2 or kerberos v5 */
 		/* one byte - no need to convert this or EncryptionKeyLen
 		   from little endian */
@@ -383,7 +386,7 @@
 			min(le32_to_cpu(pSMBr->MaxBufferSize),
 			(__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
 		server->maxRw = le32_to_cpu(pSMBr->MaxRawSize);
-		cFYI(0, ("Max buf = %d ", ses->server->maxBuf));
+		cFYI(0, ("Max buf = %d", ses->server->maxBuf));
 		GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey);
 		server->capabilities = le32_to_cpu(pSMBr->Capabilities);
 		server->timeZone = le16_to_cpu(pSMBr->ServerTimeZone);	
@@ -411,8 +414,7 @@
 						(server->server_GUID,
 						pSMBr->u.extended_response.
 						GUID, 16) != 0) {
-						cFYI(1,
-						     ("UID of server does not match previous connection to same ip address"));
+						cFYI(1, ("server UID changed"));
 						memcpy(server->
 							server_GUID,
 							pSMBr->u.
@@ -958,21 +960,19 @@
 	return rc;
 }
 
-/* If no buffer passed in, then caller wants to do the copy
-	as in the case of readpages so the SMB buffer must be
-	freed by the caller */
-
 int
 CIFSSMBRead(const int xid, struct cifsTconInfo *tcon,
-	    const int netfid, const unsigned int count,
-	    const __u64 lseek, unsigned int *nbytes, char **buf)
+            const int netfid, const unsigned int count,
+            const __u64 lseek, unsigned int *nbytes, char **buf,
+	    int * pbuf_type)
 {
 	int rc = -EACCES;
 	READ_REQ *pSMB = NULL;
 	READ_RSP *pSMBr = NULL;
 	char *pReadData = NULL;
-	int bytes_returned;
 	int wct;
+	int resp_buf_type = 0;
+	struct kvec iov[1];
 
 	cFYI(1,("Reading %d bytes on fid %d",count,netfid));
 	if(tcon->ses->capabilities & CAP_LARGE_FILES)
@@ -981,8 +981,7 @@
 		wct = 10; /* old style read */
 
 	*nbytes = 0;
-	rc = smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB,
-		      (void **) &pSMBr);
+	rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
 	if (rc)
 		return rc;
 
@@ -990,13 +989,13 @@
 	if (tcon->ses->server == NULL)
 		return -ECONNABORTED;
 
-	pSMB->AndXCommand = 0xFF;	/* none */
+	pSMB->AndXCommand = 0xFF;       /* none */
 	pSMB->Fid = netfid;
 	pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
 	if(wct == 12)
 		pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
-        else if((lseek >> 32) > 0) /* can not handle this big offset for old */
-                return -EIO;
+	else if((lseek >> 32) > 0) /* can not handle this big offset for old */
+		return -EIO;
 
 	pSMB->Remaining = 0;
 	pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
@@ -1005,14 +1004,18 @@
 		pSMB->ByteCount = 0;  /* no need to do le conversion since 0 */
 	else {
 		/* old style read */
-		struct smb_com_readx_req * pSMBW = 
+		struct smb_com_readx_req * pSMBW =
 			(struct smb_com_readx_req *)pSMB;
-		pSMBW->ByteCount = 0;	
+		pSMBW->ByteCount = 0;
 	}
-	
-	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
-			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+
+	iov[0].iov_base = (char *)pSMB;
+	iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
+	rc = SendReceive2(xid, tcon->ses, iov, 
+			  1 /* num iovecs */,
+			  &resp_buf_type, 0); 
 	cifs_stats_inc(&tcon->num_reads);
+	pSMBr = (READ_RSP *)iov[0].iov_base;
 	if (rc) {
 		cERROR(1, ("Send error in read = %d", rc));
 	} else {
@@ -1022,33 +1025,43 @@
 		*nbytes = data_length;
 
 		/*check that DataLength would not go beyond end of SMB */
-		if ((data_length > CIFSMaxBufSize) 
+		if ((data_length > CIFSMaxBufSize)
 				|| (data_length > count)) {
 			cFYI(1,("bad length %d for count %d",data_length,count));
 			rc = -EIO;
 			*nbytes = 0;
 		} else {
-			pReadData =
-			    (char *) (&pSMBr->hdr.Protocol) +
+			pReadData = (char *) (&pSMBr->hdr.Protocol) +
 			    le16_to_cpu(pSMBr->DataOffset);
-/*			if(rc = copy_to_user(buf, pReadData, data_length)) {
-				cERROR(1,("Faulting on read rc = %d",rc));
-				rc = -EFAULT;
-			}*/ /* can not use copy_to_user when using page cache*/
+/*                      if(rc = copy_to_user(buf, pReadData, data_length)) {
+                                cERROR(1,("Faulting on read rc = %d",rc));
+                                rc = -EFAULT;
+                        }*/ /* can not use copy_to_user when using page cache*/
 			if(*buf)
-			    memcpy(*buf,pReadData,data_length);
+				memcpy(*buf,pReadData,data_length);
 		}
 	}
-	if(*buf)
-		cifs_buf_release(pSMB);
-	else
-		*buf = (char *)pSMB;
 
-	/* Note: On -EAGAIN error only caller can retry on handle based calls 
+	cifs_small_buf_release(pSMB);
+	if(*buf) {
+		if(resp_buf_type == CIFS_SMALL_BUFFER)
+			cifs_small_buf_release(iov[0].iov_base);
+		else if(resp_buf_type == CIFS_LARGE_BUFFER)
+			cifs_buf_release(iov[0].iov_base);
+	} else /* return buffer to caller to free */ /* BB FIXME how do we tell caller if it is not a large buffer */ {
+		*buf = iov[0].iov_base;
+		if(resp_buf_type == CIFS_SMALL_BUFFER)
+			*pbuf_type = CIFS_SMALL_BUFFER;
+		else if(resp_buf_type == CIFS_LARGE_BUFFER)
+			*pbuf_type = CIFS_LARGE_BUFFER;
+	}
+
+	/* Note: On -EAGAIN error only caller can retry on handle based calls
 		since file handle passed in no longer valid */
 	return rc;
 }
 
+
 int
 CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
 	     const int netfid, const unsigned int count,
@@ -1155,7 +1168,6 @@
 	return rc;
 }
 
-#ifdef CONFIG_CIFS_EXPERIMENTAL
 int
 CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
 	     const int netfid, const unsigned int count,
@@ -1164,10 +1176,10 @@
 {
 	int rc = -EACCES;
 	WRITE_REQ *pSMB = NULL;
-	int bytes_returned, wct;
+	int wct;
 	int smb_hdr_len;
+	int resp_buf_type = 0;
 
-	/* BB removeme BB */
 	cFYI(1,("write2 at %lld %d bytes", (long long)offset, count));
 
 	if(tcon->ses->capabilities & CAP_LARGE_FILES)
@@ -1210,22 +1222,34 @@
 		pSMBW->ByteCount = cpu_to_le16(count + 5);
 	}
 	iov[0].iov_base = pSMB;
-	iov[0].iov_len = smb_hdr_len + 4;
+	if(wct == 14)
+		iov[0].iov_len = smb_hdr_len + 4;
+	else /* wct == 12 pad bigger by four bytes */
+		iov[0].iov_len = smb_hdr_len + 8;
+	
 
-	rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &bytes_returned,
+	rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type,
 			  long_op);
 	cifs_stats_inc(&tcon->num_writes);
 	if (rc) {
 		cFYI(1, ("Send error Write2 = %d", rc));
 		*nbytes = 0;
+	} else if(resp_buf_type == 0) {
+		/* presumably this can not happen, but best to be safe */
+		rc = -EIO;
+		*nbytes = 0;
 	} else {
-		WRITE_RSP * pSMBr = (WRITE_RSP *)pSMB;
+		WRITE_RSP * pSMBr = (WRITE_RSP *)iov[0].iov_base;
 		*nbytes = le16_to_cpu(pSMBr->CountHigh);
 		*nbytes = (*nbytes) << 16;
 		*nbytes += le16_to_cpu(pSMBr->Count);
-	}
+	} 
 
 	cifs_small_buf_release(pSMB);
+	if(resp_buf_type == CIFS_SMALL_BUFFER)
+		cifs_small_buf_release(iov[0].iov_base);
+	else if(resp_buf_type == CIFS_LARGE_BUFFER)
+		cifs_buf_release(iov[0].iov_base);
 
 	/* Note: On -EAGAIN error only caller can retry on handle based calls 
 		since file handle passed in no longer valid */
@@ -1234,8 +1258,6 @@
 }
 
 
-#endif /* CIFS_EXPERIMENTAL */
-
 int
 CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
 	    const __u16 smb_file_id, const __u64 len,
@@ -1906,6 +1928,90 @@
 	return rc;
 }
 
+/* Initialize NT TRANSACT SMB into small smb request buffer.
+   This assumes that all NT TRANSACTS that we init here have
+   total parm and data under about 400 bytes (to fit in small cifs
+   buffer size), which is the case so far, it easily fits. NB:
+	Setup words themselves and ByteCount
+	MaxSetupCount (size of returned setup area) and
+	MaxParameterCount (returned parms size) must be set by caller */
+static int 
+smb_init_ntransact(const __u16 sub_command, const int setup_count,
+		   const int parm_len, struct cifsTconInfo *tcon,
+		   void ** ret_buf)
+{
+	int rc;
+	__u32 temp_offset;
+	struct smb_com_ntransact_req * pSMB;
+
+	rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon,
+				(void **)&pSMB);
+	if (rc)
+		return rc;
+	*ret_buf = (void *)pSMB;
+	pSMB->Reserved = 0;
+	pSMB->TotalParameterCount = cpu_to_le32(parm_len);
+	pSMB->TotalDataCount  = 0;
+	pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
+					  MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
+	pSMB->ParameterCount = pSMB->TotalParameterCount;
+	pSMB->DataCount  = pSMB->TotalDataCount;
+	temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
+			(setup_count * 2) - 4 /* for rfc1001 length itself */;
+	pSMB->ParameterOffset = cpu_to_le32(temp_offset);
+	pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len);
+	pSMB->SetupCount = setup_count; /* no need to le convert byte fields */
+	pSMB->SubCommand = cpu_to_le16(sub_command);
+	return 0;
+}
+
+static int
+validate_ntransact(char * buf, char ** ppparm, char ** ppdata,
+		   int * pdatalen, int * pparmlen)
+{
+	char * end_of_smb;
+	__u32 data_count, data_offset, parm_count, parm_offset;
+	struct smb_com_ntransact_rsp * pSMBr;
+
+	if(buf == NULL)
+		return -EINVAL;
+
+	pSMBr = (struct smb_com_ntransact_rsp *)buf;
+
+	/* ByteCount was converted from little endian in SendReceive */
+	end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount + 
+			(char *)&pSMBr->ByteCount;
+
+		
+	data_offset = le32_to_cpu(pSMBr->DataOffset);
+	data_count = le32_to_cpu(pSMBr->DataCount);
+        parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
+	parm_count = le32_to_cpu(pSMBr->ParameterCount);
+
+	*ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
+	*ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
+
+	/* should we also check that parm and data areas do not overlap? */
+	if(*ppparm > end_of_smb) {
+		cFYI(1,("parms start after end of smb"));
+		return -EINVAL;
+	} else if(parm_count + *ppparm > end_of_smb) {
+		cFYI(1,("parm end after end of smb"));
+		return -EINVAL;
+	} else if(*ppdata > end_of_smb) {
+		cFYI(1,("data starts after end of smb"));
+		return -EINVAL;
+	} else if(data_count + *ppdata > end_of_smb) {
+		cFYI(1,("data %p + count %d (%p) ends after end of smb %p start %p",
+			*ppdata, data_count, (data_count + *ppdata), end_of_smb, pSMBr));  /* BB FIXME */
+		return -EINVAL;
+	} else if(parm_count + data_count > pSMBr->ByteCount) {
+		cFYI(1,("parm count and data count larger than SMB"));
+		return -EINVAL;
+	}
+	return 0;
+}
+
 int
 CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
 			const unsigned char *searchName,
@@ -1928,7 +2034,8 @@
 	pSMB->TotalDataCount = 0;
 	pSMB->MaxParameterCount = cpu_to_le32(2);
 	/* BB find exact data count max from sess structure BB */
-	pSMB->MaxDataCount = cpu_to_le32(4000);
+	pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
+					  MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
 	pSMB->MaxSetupCount = 4;
 	pSMB->Reserved = 0;
 	pSMB->ParameterOffset = 0;
@@ -1955,7 +2062,9 @@
 			rc = -EIO;	/* bad smb */
 		else {
 			if(data_count && (data_count < 2048)) {
-				char * end_of_smb = pSMBr->ByteCount + (char *)&pSMBr->ByteCount;
+				char * end_of_smb = 2 /* sizeof byte count */ +
+						pSMBr->ByteCount +
+						(char *)&pSMBr->ByteCount;
 
 				struct reparse_data * reparse_buf = (struct reparse_data *)
 					((char *)&pSMBr->hdr.Protocol + data_offset);
@@ -2199,6 +2308,7 @@
 
 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
 		(struct smb_hdr *) pSMBr, &bytes_returned, 0);
+	cifs_stats_inc(&tcon->num_acl_get);
 	if (rc) {
 		cFYI(1, ("Send error in Query POSIX ACL = %d", rc));
 	} else {
@@ -2386,6 +2496,92 @@
 
 #endif /* CONFIG_POSIX */
 
+
+/* security id for everyone */
+const struct cifs_sid sid_everyone = {1, 1, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0}};
+/* group users */
+const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {32, 545, 0, 0}};
+
+/* Convert CIFS ACL to POSIX form */
+static int parse_sec_desc(struct cifs_sid * psec_desc, int acl_len)
+{
+	return 0;
+}
+
+/* Get Security Descriptor (by handle) from remote server for a file or dir */
+int
+CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
+         /*  BB fix up return info */ char *acl_inf, const int buflen, 
+		  const int acl_type /* ACCESS/DEFAULT not sure implication */)
+{
+	int rc = 0;
+	int buf_type = 0;
+	QUERY_SEC_DESC_REQ * pSMB;
+	struct kvec iov[1];
+
+	cFYI(1, ("GetCifsACL"));
+
+	rc = smb_init_ntransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0, 
+			8 /* parm len */, tcon, (void **) &pSMB);
+	if (rc)
+		return rc;
+
+	pSMB->MaxParameterCount = cpu_to_le32(4);
+	/* BB TEST with big acls that might need to be e.g. larger than 16K */
+	pSMB->MaxSetupCount = 0;
+	pSMB->Fid = fid; /* file handle always le */
+	pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP |
+				     CIFS_ACL_DACL);
+	pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */
+	pSMB->hdr.smb_buf_length += 11;
+	iov[0].iov_base = (char *)pSMB;
+	iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
+
+	rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type, 0);
+	cifs_stats_inc(&tcon->num_acl_get);
+	if (rc) {
+		cFYI(1, ("Send error in QuerySecDesc = %d", rc));
+	} else {                /* decode response */
+		struct sec_desc * psec_desc;
+		__le32 * parm;
+		int parm_len;
+		int data_len;
+		int acl_len;
+		struct smb_com_ntransact_rsp * pSMBr;
+
+/* validate_nttransact */
+		rc = validate_ntransact(iov[0].iov_base, (char **)&parm, 
+					(char **)&psec_desc,
+					&parm_len, &data_len);
+		
+		if(rc)
+			goto qsec_out;
+		pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base;
+
+		cERROR(1,("smb %p parm %p data %p",pSMBr,parm,psec_desc));  /* BB removeme BB */
+
+		if (le32_to_cpu(pSMBr->ParameterCount) != 4) {
+			rc = -EIO;      /* bad smb */
+			goto qsec_out;
+		}
+
+/* BB check that data area is minimum length and as big as acl_len */
+
+		acl_len = le32_to_cpu(*(__le32 *)parm);
+		/* BB check if(acl_len > bufsize) */
+
+		parse_sec_desc(psec_desc, acl_len);
+	}
+qsec_out:
+	if(buf_type == CIFS_SMALL_BUFFER)
+		cifs_small_buf_release(iov[0].iov_base);
+	else if(buf_type == CIFS_LARGE_BUFFER)
+		cifs_buf_release(iov[0].iov_base);
+	cifs_small_buf_release(pSMB);
+	return rc;
+}
+
+
 /* Legacy Query Path Information call for lookup to old servers such
    as Win9x/WinME */
 int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon,
@@ -4284,7 +4480,7 @@
 {
 	int rc = 0;
 	struct smb_com_transaction_change_notify_req * pSMB = NULL;
-	struct smb_com_transaction_change_notify_rsp * pSMBr = NULL;
+	struct smb_com_ntransaction_change_notify_rsp * pSMBr = NULL;
 	struct dir_notify_req *dnotify_req;
 	int bytes_returned;
 
@@ -4299,6 +4495,10 @@
 	pSMB->MaxParameterCount = cpu_to_le32(2);
 	/* BB find exact data count max from sess structure BB */
 	pSMB->MaxDataCount = 0; /* same in little endian or be */
+/* BB VERIFY verify which is correct for above BB */
+	pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
+					     MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
+
 	pSMB->MaxSetupCount = 4;
 	pSMB->Reserved = 0;
 	pSMB->ParameterOffset = 0;
diff -urN oldtree/fs/cifs/connect.c newtree/fs/cifs/connect.c
--- oldtree/fs/cifs/connect.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/connect.c	2006-02-13 19:20:00.688407648 -0500
@@ -76,12 +76,19 @@
 	unsigned setuids:1;
 	unsigned noperm:1;
 	unsigned no_psx_acl:1; /* set if posix acl support should be disabled */
+	unsigned cifs_acl:1;
 	unsigned no_xattr:1;   /* set if xattr (EA) support should be disabled*/
 	unsigned server_ino:1; /* use inode numbers from server ie UniqueId */
 	unsigned direct_io:1;
 	unsigned remap:1;   /* set to remap seven reserved chars in filenames */
 	unsigned posix_paths:1;   /* unset to not ask for posix pathnames. */
 	unsigned sfu_emul:1;
+	unsigned krb5:1;
+	unsigned ntlm:1;
+	unsigned ntlmv2:1;
+	unsigned nullauth:1; /* attempt to authenticate with null user */
+	unsigned sign:1;
+	unsigned seal:1;     /* encrypt */
 	unsigned nocase;     /* request case insensitive filenames */
 	unsigned nobrl;      /* disable sending byte range locks to srv */
 	unsigned int rsize;
@@ -508,7 +515,7 @@
 		/* else length ok */
 		reconnect = 0;
 
-		if(pdu_length > MAX_CIFS_HDR_SIZE - 4) {
+		if(pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
 			isLargeBuf = TRUE;
 			memcpy(bigbuf, smallbuf, 4);
 			smb_buffer = bigbuf;
@@ -777,7 +784,7 @@
 
 	/* vol->retry default is 0 (i.e. "soft" limited retry not hard retry) */
 	vol->rw = TRUE;
-
+	vol->ntlm = TRUE;
 	/* default is always to request posix paths. */
 	vol->posix_paths = 1;
 
@@ -903,6 +910,39 @@
 				printk(KERN_WARNING "CIFS: ip address too long\n");
 				return 1;
 			}
+                } else if (strnicmp(data, "sec", 3) == 0) { 
+                        if (!value || !*value) {
+				cERROR(1,("no security value specified"));
+                                continue;
+                        } else if (strnicmp(value, "krb5i", 5) == 0) {
+				vol->sign = 1;
+				vol->krb5 = 1;
+			} else if (strnicmp(value, "krb5p", 5) == 0) {
+				/* vol->seal = 1; 
+				   vol->krb5 = 1; */
+				cERROR(1,("Krb5 cifs privacy not supported"));
+				return 1;
+			} else if (strnicmp(value, "krb5", 4) == 0) {
+				vol->krb5 = 1;
+			} else if (strnicmp(value, "ntlmv2i", 7) == 0) {
+				vol->ntlmv2 = 1;
+				vol->sign = 1;
+			} else if (strnicmp(value, "ntlmv2", 6) == 0) {
+				vol->ntlmv2 = 1;
+			} else if (strnicmp(value, "ntlmi", 5) == 0) {
+				vol->ntlm = 1;
+				vol->sign = 1;
+			} else if (strnicmp(value, "ntlm", 4) == 0) {
+				/* ntlm is default so can be turned off too */
+				vol->ntlm = 1;
+			} else if (strnicmp(value, "nontlm", 6) == 0) {
+				vol->ntlm = 0;
+			} else if (strnicmp(value, "none", 4) == 0) {
+				vol->nullauth = 1; 
+                        } else {
+                                cERROR(1,("bad security option: %s", value));
+                                return 1;
+                        }
 		} else if ((strnicmp(data, "unc", 3) == 0)
 			   || (strnicmp(data, "target", 6) == 0)
 			   || (strnicmp(data, "path", 4) == 0)) {
@@ -1120,6 +1160,10 @@
 			vol->server_ino = 1;
 		} else if (strnicmp(data, "noserverino",9) == 0) {
 			vol->server_ino = 0;
+		} else if (strnicmp(data, "cifsacl",7) == 0) {
+			vol->cifs_acl = 1;
+		} else if (strnicmp(data, "nocifsacl", 9) == 0) {
+			vol->cifs_acl = 0;
 		} else if (strnicmp(data, "acl",3) == 0) {
 			vol->no_psx_acl = 0;
 		} else if (strnicmp(data, "noacl",5) == 0) {
@@ -1546,7 +1590,7 @@
 		cFYI(1, ("Username: %s ", volume_info.username));
 
 	} else {
-		cifserror("No username specified ");
+		cifserror("No username specified");
         /* In userspace mount helper we can get user name from alternate
            locations such as env variables and files on disk */
 		kfree(volume_info.UNC);
@@ -1587,7 +1631,7 @@
 		return -EINVAL;
 	} else /* which servers DFS root would we conect to */ {
 		cERROR(1,
-		       ("CIFS mount error: No UNC path (e.g. -o unc=//192.168.1.100/public) specified  "));
+		       ("CIFS mount error: No UNC path (e.g. -o unc=//192.168.1.100/public) specified"));
 		kfree(volume_info.UNC);
 		kfree(volume_info.password);
 		FreeXid(xid);
@@ -1626,7 +1670,7 @@
 
 
 	if (srvTcp) {
-		cFYI(1, ("Existing tcp session with server found "));                
+		cFYI(1, ("Existing tcp session with server found"));                
 	} else {	/* create socket */
 		if(volume_info.port)
 			sin_server.sin_port = htons(volume_info.port);
@@ -1689,11 +1733,11 @@
 
 	if (existingCifsSes) {
 		pSesInfo = existingCifsSes;
-		cFYI(1, ("Existing smb sess found "));
+		cFYI(1, ("Existing smb sess found"));
 		kfree(volume_info.password);
 		/* volume_info.UNC freed at end of function */
 	} else if (!rc) {
-		cFYI(1, ("Existing smb sess not found "));
+		cFYI(1, ("Existing smb sess not found"));
 		pSesInfo = sesInfoAlloc();
 		if (pSesInfo == NULL)
 			rc = -ENOMEM;
@@ -1751,7 +1795,8 @@
 		cifs_sb->mnt_gid = volume_info.linux_gid;
 		cifs_sb->mnt_file_mode = volume_info.file_mode;
 		cifs_sb->mnt_dir_mode = volume_info.dir_mode;
-		cFYI(1,("file mode: 0x%x  dir mode: 0x%x",cifs_sb->mnt_file_mode,cifs_sb->mnt_dir_mode));
+		cFYI(1,("file mode: 0x%x  dir mode: 0x%x",
+			cifs_sb->mnt_file_mode,cifs_sb->mnt_dir_mode));
 
 		if(volume_info.noperm)
 			cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM;
@@ -1767,6 +1812,8 @@
 			cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL;
 		if(volume_info.nobrl)
 			cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL;
+		if(volume_info.cifs_acl)
+			cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL;
 
 		if(volume_info.direct_io) {
 			cFYI(1,("mounting share using direct i/o"));
@@ -1777,7 +1824,7 @@
 		    find_unc(sin_server.sin_addr.s_addr, volume_info.UNC,
 			     volume_info.username);
 		if (tcon) {
-			cFYI(1, ("Found match on UNC path "));
+			cFYI(1, ("Found match on UNC path"));
 			/* we can have only one retry value for a connection
 			   to a share so for resources mounted more than once
 			   to the same server share the last value passed in 
@@ -1926,7 +1973,7 @@
 	__u32 capabilities;
 	__u16 count;
 
-	cFYI(1, ("In sesssetup "));
+	cFYI(1, ("In sesssetup"));
 	if(ses == NULL)
 		return -EINVAL;
 	user = ses->userName;
@@ -3202,9 +3249,26 @@
 
 	pSMB->AndXCommand = 0xFF;
 	pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO);
-	pSMB->PasswordLength = cpu_to_le16(1);	/* minimum */
 	bcc_ptr = &pSMB->Password[0];
-	bcc_ptr++;		/* skip password */
+	if((ses->server->secMode) & SECMODE_USER) {
+		pSMB->PasswordLength = cpu_to_le16(1);	/* minimum */
+		bcc_ptr++;              /* skip password */
+	} else {
+		pSMB->PasswordLength = cpu_to_le16(CIFS_SESSION_KEY_SIZE);
+		/* BB FIXME add code to fail this if NTLMv2 or Kerberos
+		   specified as required (when that support is added to
+		   the vfs in the future) as only NTLM or the much
+		   weaker LANMAN (which we do not send) is accepted
+		   by Samba (not sure whether other servers allow
+		   NTLMv2 password here) */
+		SMBNTencrypt(ses->password,
+			     ses->server->cryptKey,
+			     bcc_ptr);
+
+		bcc_ptr += CIFS_SESSION_KEY_SIZE;
+		*bcc_ptr = 0;
+		bcc_ptr++; /* align */
+	}
 
 	if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
 		smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
@@ -3222,7 +3286,6 @@
 		bcc_ptr += 2 * length;	/* convert num of 16 bit words to bytes */
 		bcc_ptr += 2;	/* skip trailing null */
 	} else {		/* ASCII */
-
 		strcpy(bcc_ptr, tree);
 		bcc_ptr += strlen(tree) + 1;
 	}
diff -urN oldtree/fs/cifs/dir.c newtree/fs/cifs/dir.c
--- oldtree/fs/cifs/dir.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/dir.c	2006-02-13 19:20:00.689407496 -0500
@@ -3,7 +3,7 @@
  *
  *   vfs operations that deal with dentries
  * 
- *   Copyright (C) International Business Machines  Corp., 2002,2003
+ *   Copyright (C) International Business Machines  Corp., 2002,2005
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -200,8 +200,8 @@
 			(oplock & CIFS_CREATE_ACTION))
 			if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
 				CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode,
-					(__u64)current->euid,
-					(__u64)current->egid,
+					(__u64)current->fsuid,
+					(__u64)current->fsgid,
 					0 /* dev */,
 					cifs_sb->local_nls, 
 					cifs_sb->mnt_cifs_flags & 
@@ -325,7 +325,7 @@
 	else if (pTcon->ses->capabilities & CAP_UNIX) {
 		if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
 			rc = CIFSSMBUnixSetPerms(xid, pTcon, full_path,
-				mode,(__u64)current->euid,(__u64)current->egid,
+				mode,(__u64)current->fsuid,(__u64)current->fsgid,
 				device_number, cifs_sb->local_nls,
 				cifs_sb->mnt_cifs_flags & 
 					CIFS_MOUNT_MAP_SPECIAL_CHR);
diff -urN oldtree/fs/cifs/file.c newtree/fs/cifs/file.c
--- oldtree/fs/cifs/file.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/file.c	2006-02-13 19:20:00.690407344 -0500
@@ -555,13 +555,13 @@
 		}
 		ptmp = pCFileStruct->srch_inf.ntwrk_buf_start;
 		if (ptmp) {
-   /* BB removeme BB */	cFYI(1, ("freeing smb buf in srch struct in closedir"));
+			cFYI(1, ("closedir free smb buf in srch struct"));
 			pCFileStruct->srch_inf.ntwrk_buf_start = NULL;
 			cifs_buf_release(ptmp);
 		}
 		ptmp = pCFileStruct->search_resume_name;
 		if (ptmp) {
-   /* BB removeme BB */	cFYI(1, ("freeing resume name in closedir"));
+			cFYI(1, ("closedir free resume name"));
 			pCFileStruct->search_resume_name = NULL;
 			kfree(ptmp);
 		}
@@ -870,10 +870,9 @@
 				if (rc != 0)
 					break;
 			}
-#ifdef CONFIG_CIFS_EXPERIMENTAL
 			/* BB FIXME We can not sign across two buffers yet */
-			if((experimEnabled) && ((pTcon->ses->server->secMode & 
-			 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) == 0)) {
+			if((pTcon->ses->server->secMode & 
+			 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) == 0) {
 				struct kvec iov[2];
 				unsigned int len;
 
@@ -889,7 +888,6 @@
 						iov, 1, long_op);
 			} else
 			/* BB FIXME fixup indentation of line below */
-#endif			
 			rc = CIFSSMBWrite(xid, pTcon,
 				 open_file->netfid,
 				 min_t(const int, cifs_sb->wsize, 
@@ -1026,7 +1024,6 @@
 	return rc;
 }
 
-#ifdef CONFIG_CIFS_EXPERIMENTAL
 static int cifs_writepages(struct address_space *mapping,
 			   struct writeback_control *wbc)
 {
@@ -1229,7 +1226,6 @@
 
 	return rc;
 }
-#endif
 
 static int cifs_writepage(struct page* page, struct writeback_control *wbc)
 {
@@ -1428,6 +1424,7 @@
 		rc = -EAGAIN;
 		smb_read_data = NULL;
 		while (rc == -EAGAIN) {
+			int buf_type = CIFS_NO_BUFFER;
 			if ((open_file->invalidHandle) && 
 			    (!open_file->closePend)) {
 				rc = cifs_reopen_file(file->f_dentry->d_inode,
@@ -1436,20 +1433,22 @@
 					break;
 			}
 			rc = CIFSSMBRead(xid, pTcon,
-					open_file->netfid,
-					current_read_size, *poffset,
-					&bytes_read, &smb_read_data);
+					 open_file->netfid,
+					 current_read_size, *poffset,
+					 &bytes_read, &smb_read_data,
+					 &buf_type);
 			pSMBr = (struct smb_com_read_rsp *)smb_read_data;
 			if (copy_to_user(current_offset, 
 					 smb_read_data + 4 /* RFC1001 hdr */
 					 + le16_to_cpu(pSMBr->DataOffset), 
 					 bytes_read)) {
 				rc = -EFAULT;
-				FreeXid(xid);
-				return rc;
-            }
+			}
 			if (smb_read_data) {
-				cifs_buf_release(smb_read_data);
+				if(buf_type == CIFS_SMALL_BUFFER)
+					cifs_small_buf_release(smb_read_data);
+				else if(buf_type == CIFS_LARGE_BUFFER)
+					cifs_buf_release(smb_read_data);
 				smb_read_data = NULL;
 			}
 		}
@@ -1482,6 +1481,7 @@
 	int xid;
 	char *current_offset;
 	struct cifsFileInfo *open_file;
+	int buf_type = CIFS_NO_BUFFER;
 
 	xid = GetXid();
 	cifs_sb = CIFS_SB(file->f_dentry->d_sb);
@@ -1518,9 +1518,10 @@
 					break;
 			}
 			rc = CIFSSMBRead(xid, pTcon,
-					open_file->netfid,
-					current_read_size, *poffset,
-					&bytes_read, &current_offset);
+					 open_file->netfid,
+					 current_read_size, *poffset,
+					 &bytes_read, &current_offset,
+					 &buf_type);
 		}
 		if (rc || (bytes_read == 0)) {
 			if (total_read) {
@@ -1618,6 +1619,7 @@
 	struct smb_com_read_rsp *pSMBr;
 	struct pagevec lru_pvec;
 	struct cifsFileInfo *open_file;
+	int buf_type = CIFS_NO_BUFFER;
 
 	xid = GetXid();
 	if (file->private_data == NULL) {
@@ -1674,14 +1676,17 @@
 			}
 
 			rc = CIFSSMBRead(xid, pTcon,
-					open_file->netfid,
-					read_size, offset,
-					&bytes_read, &smb_read_data);
-
+					 open_file->netfid,
+					 read_size, offset,
+					 &bytes_read, &smb_read_data,
+					 &buf_type);
 			/* BB more RC checks ? */
 			if (rc== -EAGAIN) {
 				if (smb_read_data) {
-					cifs_buf_release(smb_read_data);
+					if(buf_type == CIFS_SMALL_BUFFER)
+						cifs_small_buf_release(smb_read_data);
+					else if(buf_type == CIFS_LARGE_BUFFER)
+						cifs_buf_release(smb_read_data);
 					smb_read_data = NULL;
 				}
 			}
@@ -1738,7 +1743,10 @@
 			break;
 		}
 		if (smb_read_data) {
-			cifs_buf_release(smb_read_data);
+			if(buf_type == CIFS_SMALL_BUFFER)
+				cifs_small_buf_release(smb_read_data);
+			else if(buf_type == CIFS_LARGE_BUFFER)
+				cifs_buf_release(smb_read_data);
 			smb_read_data = NULL;
 		}
 		bytes_read = 0;
@@ -1827,10 +1835,20 @@
 		open_file =  find_writable_file(cifsInode);
  
 	if(open_file) {
+		struct cifs_sb_info *cifs_sb;
+
 		/* there is not actually a write pending so let
 		this handle go free and allow it to
 		be closable if needed */
 		atomic_dec(&open_file->wrtPending);
+
+		cifs_sb = CIFS_SB(cifsInode->vfs_inode.i_sb);
+		if ( cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO ) {
+			/* since no page cache to corrupt on directio 
+			we can change size safely */
+			return 1;
+		}
+
 		return 0;
 	} else
 		return 1;
@@ -1875,9 +1893,7 @@
 	.readpage = cifs_readpage,
 	.readpages = cifs_readpages,
 	.writepage = cifs_writepage,
-#ifdef CONFIG_CIFS_EXPERIMENTAL
 	.writepages = cifs_writepages,
-#endif
 	.prepare_write = cifs_prepare_write,
 	.commit_write = cifs_commit_write,
 	.set_page_dirty = __set_page_dirty_nobuffers,
diff -urN oldtree/fs/cifs/file.c.orig newtree/fs/cifs/file.c.orig
--- oldtree/fs/cifs/file.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/cifs/file.c.orig	2006-02-13 19:20:00.693406888 -0500
@@ -0,0 +1,1886 @@
+/*
+ *   fs/cifs/file.c
+ *
+ *   vfs operations that deal with files
+ * 
+ *   Copyright (C) International Business Machines  Corp., 2002,2003
+ *   Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published
+ *   by the Free Software Foundation; either version 2.1 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ *   the GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/backing-dev.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/mpage.h>
+#include <linux/pagemap.h>
+#include <linux/pagevec.h>
+#include <linux/smp_lock.h>
+#include <linux/writeback.h>
+#include <linux/delay.h>
+#include <asm/div64.h>
+#include "cifsfs.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_unicode.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+
+static inline struct cifsFileInfo *cifs_init_private(
+	struct cifsFileInfo *private_data, struct inode *inode,
+	struct file *file, __u16 netfid)
+{
+	memset(private_data, 0, sizeof(struct cifsFileInfo));
+	private_data->netfid = netfid;
+	private_data->pid = current->tgid;	
+	init_MUTEX(&private_data->fh_sem);
+	private_data->pfile = file; /* needed for writepage */
+	private_data->pInode = inode;
+	private_data->invalidHandle = FALSE;
+	private_data->closePend = FALSE;
+	/* we have to track num writers to the inode, since writepages
+	does not tell us which handle the write is for so there can
+	be a close (overlapping with write) of the filehandle that
+	cifs_writepages chose to use */
+	atomic_set(&private_data->wrtPending,0); 
+
+	return private_data;
+}
+
+static inline int cifs_convert_flags(unsigned int flags)
+{
+	if ((flags & O_ACCMODE) == O_RDONLY)
+		return GENERIC_READ;
+	else if ((flags & O_ACCMODE) == O_WRONLY)
+		return GENERIC_WRITE;
+	else if ((flags & O_ACCMODE) == O_RDWR) {
+		/* GENERIC_ALL is too much permission to request
+		   can cause unnecessary access denied on create */
+		/* return GENERIC_ALL; */
+		return (GENERIC_READ | GENERIC_WRITE);
+	}
+
+	return 0x20197;
+}
+
+static inline int cifs_get_disposition(unsigned int flags)
+{
+	if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+		return FILE_CREATE;
+	else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
+		return FILE_OVERWRITE_IF;
+	else if ((flags & O_CREAT) == O_CREAT)
+		return FILE_OPEN_IF;
+	else
+		return FILE_OPEN;
+}
+
+/* all arguments to this function must be checked for validity in caller */
+static inline int cifs_open_inode_helper(struct inode *inode, struct file *file,
+	struct cifsInodeInfo *pCifsInode, struct cifsFileInfo *pCifsFile,
+	struct cifsTconInfo *pTcon, int *oplock, FILE_ALL_INFO *buf,
+	char *full_path, int xid)
+{
+	struct timespec temp;
+	int rc;
+
+	/* want handles we can use to read with first
+	   in the list so we do not have to walk the
+	   list to search for one in prepare_write */
+	if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
+		list_add_tail(&pCifsFile->flist, 
+			      &pCifsInode->openFileList);
+	} else {
+		list_add(&pCifsFile->flist,
+			 &pCifsInode->openFileList);
+	}
+	write_unlock(&GlobalSMBSeslock);
+	write_unlock(&file->f_owner.lock);
+	if (pCifsInode->clientCanCacheRead) {
+		/* we have the inode open somewhere else
+		   no need to discard cache data */
+		goto client_can_cache;
+	}
+
+	/* BB need same check in cifs_create too? */
+	/* if not oplocked, invalidate inode pages if mtime or file
+	   size changed */
+	temp = cifs_NTtimeToUnix(le64_to_cpu(buf->LastWriteTime));
+	if (timespec_equal(&file->f_dentry->d_inode->i_mtime, &temp) && 
+			   (file->f_dentry->d_inode->i_size == 
+			    (loff_t)le64_to_cpu(buf->EndOfFile))) {
+		cFYI(1, ("inode unchanged on server"));
+	} else {
+		if (file->f_dentry->d_inode->i_mapping) {
+		/* BB no need to lock inode until after invalidate
+		   since namei code should already have it locked? */
+			filemap_fdatawrite(file->f_dentry->d_inode->i_mapping);
+			filemap_fdatawait(file->f_dentry->d_inode->i_mapping);
+		}
+		cFYI(1, ("invalidating remote inode since open detected it "
+			 "changed"));
+		invalidate_remote_inode(file->f_dentry->d_inode);
+	}
+
+client_can_cache:
+	if (pTcon->ses->capabilities & CAP_UNIX)
+		rc = cifs_get_inode_info_unix(&file->f_dentry->d_inode,
+			full_path, inode->i_sb, xid);
+	else
+		rc = cifs_get_inode_info(&file->f_dentry->d_inode,
+			full_path, buf, inode->i_sb, xid);
+
+	if ((*oplock & 0xF) == OPLOCK_EXCLUSIVE) {
+		pCifsInode->clientCanCacheAll = TRUE;
+		pCifsInode->clientCanCacheRead = TRUE;
+		cFYI(1, ("Exclusive Oplock granted on inode %p",
+			 file->f_dentry->d_inode));
+	} else if ((*oplock & 0xF) == OPLOCK_READ)
+		pCifsInode->clientCanCacheRead = TRUE;
+
+	return rc;
+}
+
+int cifs_open(struct inode *inode, struct file *file)
+{
+	int rc = -EACCES;
+	int xid, oplock;
+	struct cifs_sb_info *cifs_sb;
+	struct cifsTconInfo *pTcon;
+	struct cifsFileInfo *pCifsFile;
+	struct cifsInodeInfo *pCifsInode;
+	struct list_head *tmp;
+	char *full_path = NULL;
+	int desiredAccess;
+	int disposition;
+	__u16 netfid;
+	FILE_ALL_INFO *buf = NULL;
+
+	xid = GetXid();
+
+	cifs_sb = CIFS_SB(inode->i_sb);
+	pTcon = cifs_sb->tcon;
+
+	if (file->f_flags & O_CREAT) {
+		/* search inode for this file and fill in file->private_data */
+		pCifsInode = CIFS_I(file->f_dentry->d_inode);
+		read_lock(&GlobalSMBSeslock);
+		list_for_each(tmp, &pCifsInode->openFileList) {
+			pCifsFile = list_entry(tmp, struct cifsFileInfo,
+					       flist);
+			if ((pCifsFile->pfile == NULL) &&
+			    (pCifsFile->pid == current->tgid)) {
+				/* mode set in cifs_create */
+
+				/* needed for writepage */
+				pCifsFile->pfile = file;
+				
+				file->private_data = pCifsFile;
+				break;
+			}
+		}
+		read_unlock(&GlobalSMBSeslock);
+		if (file->private_data != NULL) {
+			rc = 0;
+			FreeXid(xid);
+			return rc;
+		} else {
+			if (file->f_flags & O_EXCL)
+				cERROR(1, ("could not find file instance for "
+					   "new file %p ", file));
+		}
+	}
+
+	down(&inode->i_sb->s_vfs_rename_sem);
+	full_path = build_path_from_dentry(file->f_dentry);
+	up(&inode->i_sb->s_vfs_rename_sem);
+	if (full_path == NULL) {
+		FreeXid(xid);
+		return -ENOMEM;
+	}
+
+	cFYI(1, (" inode = 0x%p file flags are 0x%x for %s",
+		 inode, file->f_flags, full_path));
+	desiredAccess = cifs_convert_flags(file->f_flags);
+
+/*********************************************************************
+ *  open flag mapping table:
+ *  
+ *	POSIX Flag            CIFS Disposition
+ *	----------            ---------------- 
+ *	O_CREAT               FILE_OPEN_IF
+ *	O_CREAT | O_EXCL      FILE_CREATE
+ *	O_CREAT | O_TRUNC     FILE_OVERWRITE_IF
+ *	O_TRUNC               FILE_OVERWRITE
+ *	none of the above     FILE_OPEN
+ *
+ *	Note that there is not a direct match between disposition
+ *	FILE_SUPERSEDE (ie create whether or not file exists although 
+ *	O_CREAT | O_TRUNC is similar but truncates the existing
+ *	file rather than creating a new file as FILE_SUPERSEDE does
+ *	(which uses the attributes / metadata passed in on open call)
+ *?
+ *?  O_SYNC is a reasonable match to CIFS writethrough flag  
+ *?  and the read write flags match reasonably.  O_LARGEFILE
+ *?  is irrelevant because largefile support is always used
+ *?  by this client. Flags O_APPEND, O_DIRECT, O_DIRECTORY,
+ *	 O_FASYNC, O_NOFOLLOW, O_NONBLOCK need further investigation
+ *********************************************************************/
+
+	disposition = cifs_get_disposition(file->f_flags);
+
+	if (oplockEnabled)
+		oplock = REQ_OPLOCK;
+	else
+		oplock = FALSE;
+
+	/* BB pass O_SYNC flag through on file attributes .. BB */
+
+	/* Also refresh inode by passing in file_info buf returned by SMBOpen
+	   and calling get_inode_info with returned buf (at least helps
+	   non-Unix server case) */
+
+	/* BB we can not do this if this is the second open of a file 
+	   and the first handle has writebehind data, we might be 
+	   able to simply do a filemap_fdatawrite/filemap_fdatawait first */
+	buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
+	if (!buf) {
+		rc = -ENOMEM;
+		goto out;
+	}
+	rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess,
+			 CREATE_NOT_DIR, &netfid, &oplock, buf,
+			 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
+				 & CIFS_MOUNT_MAP_SPECIAL_CHR);
+	if (rc == -EIO) {
+		/* Old server, try legacy style OpenX */
+		rc = SMBLegacyOpen(xid, pTcon, full_path, disposition,
+			desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf,
+			cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
+				& CIFS_MOUNT_MAP_SPECIAL_CHR);
+	}
+	if (rc) {
+		cFYI(1, ("cifs_open returned 0x%x ", rc));
+		goto out;
+	}
+	file->private_data =
+		kmalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
+	if (file->private_data == NULL) {
+		rc = -ENOMEM;
+		goto out;
+	}
+	pCifsFile = cifs_init_private(file->private_data, inode, file, netfid);
+	write_lock(&file->f_owner.lock);
+	write_lock(&GlobalSMBSeslock);
+	list_add(&pCifsFile->tlist, &pTcon->openFileList);
+
+	pCifsInode = CIFS_I(file->f_dentry->d_inode);
+	if (pCifsInode) {
+		rc = cifs_open_inode_helper(inode, file, pCifsInode,
+					    pCifsFile, pTcon,
+					    &oplock, buf, full_path, xid);
+	} else {
+		write_unlock(&GlobalSMBSeslock);
+		write_unlock(&file->f_owner.lock);
+	}
+
+	if (oplock & CIFS_CREATE_ACTION) {           
+		/* time to set mode which we can not set earlier due to
+		   problems creating new read-only files */
+		if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) {
+			CIFSSMBUnixSetPerms(xid, pTcon, full_path,
+					    inode->i_mode,
+					    (__u64)-1, (__u64)-1, 0 /* dev */,
+					    cifs_sb->local_nls,
+					    cifs_sb->mnt_cifs_flags & 
+						CIFS_MOUNT_MAP_SPECIAL_CHR);
+		} else {
+			/* BB implement via Windows security descriptors eg
+			   CIFSSMBWinSetPerms(xid, pTcon, full_path, mode,
+					      -1, -1, local_nls);
+			   in the meantime could set r/o dos attribute when
+			   perms are eg: mode & 0222 == 0 */
+		}
+	}
+
+out:
+	kfree(buf);
+	kfree(full_path);
+	FreeXid(xid);
+	return rc;
+}
+
+/* Try to reaquire byte range locks that were released when session */
+/* to server was lost */
+static int cifs_relock_file(struct cifsFileInfo *cifsFile)
+{
+	int rc = 0;
+
+/* BB list all locks open on this file and relock */
+
+	return rc;
+}
+
+static int cifs_reopen_file(struct inode *inode, struct file *file, 
+	int can_flush)
+{
+	int rc = -EACCES;
+	int xid, oplock;
+	struct cifs_sb_info *cifs_sb;
+	struct cifsTconInfo *pTcon;
+	struct cifsFileInfo *pCifsFile;
+	struct cifsInodeInfo *pCifsInode;
+	char *full_path = NULL;
+	int desiredAccess;
+	int disposition = FILE_OPEN;
+	__u16 netfid;
+
+	if (inode == NULL)
+		return -EBADF;
+	if (file->private_data) {
+		pCifsFile = (struct cifsFileInfo *)file->private_data;
+	} else
+		return -EBADF;
+
+	xid = GetXid();
+	down(&pCifsFile->fh_sem);
+	if (pCifsFile->invalidHandle == FALSE) {
+		up(&pCifsFile->fh_sem);
+		FreeXid(xid);
+		return 0;
+	}
+
+	if (file->f_dentry == NULL) {
+		up(&pCifsFile->fh_sem);
+		cFYI(1, ("failed file reopen, no valid name if dentry freed"));
+		FreeXid(xid);
+		return -EBADF;
+	}
+	cifs_sb = CIFS_SB(inode->i_sb);
+	pTcon = cifs_sb->tcon;
+/* can not grab rename sem here because various ops, including
+   those that already have the rename sem can end up causing writepage
+   to get called and if the server was down that means we end up here,
+   and we can never tell if the caller already has the rename_sem */
+	full_path = build_path_from_dentry(file->f_dentry);
+	if (full_path == NULL) {
+		up(&pCifsFile->fh_sem);
+		FreeXid(xid);
+		return -ENOMEM;
+	}
+
+	cFYI(1, (" inode = 0x%p file flags are 0x%x for %s",
+		 inode, file->f_flags,full_path));
+	desiredAccess = cifs_convert_flags(file->f_flags);
+
+	if (oplockEnabled)
+		oplock = REQ_OPLOCK;
+	else
+		oplock = FALSE;
+
+	/* Can not refresh inode by passing in file_info buf to be returned
+	   by SMBOpen and then calling get_inode_info with returned buf 
+	   since file might have write behind data that needs to be flushed 
+	   and server version of file size can be stale. If we knew for sure
+	   that inode was not dirty locally we could do this */
+
+/*	buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
+	if (buf == 0) {
+		up(&pCifsFile->fh_sem);
+		kfree(full_path);
+		FreeXid(xid);
+		return -ENOMEM;
+	} */
+	rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess,
+			 CREATE_NOT_DIR, &netfid, &oplock, NULL,
+			 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & 
+				CIFS_MOUNT_MAP_SPECIAL_CHR);
+	if (rc) {
+		up(&pCifsFile->fh_sem);
+		cFYI(1, ("cifs_open returned 0x%x ", rc));
+		cFYI(1, ("oplock: %d ", oplock));
+	} else {
+		pCifsFile->netfid = netfid;
+		pCifsFile->invalidHandle = FALSE;
+		up(&pCifsFile->fh_sem);
+		pCifsInode = CIFS_I(inode);
+		if (pCifsInode) {
+			if (can_flush) {
+				filemap_fdatawrite(inode->i_mapping);
+				filemap_fdatawait(inode->i_mapping);
+			/* temporarily disable caching while we
+			   go to server to get inode info */
+				pCifsInode->clientCanCacheAll = FALSE;
+				pCifsInode->clientCanCacheRead = FALSE;
+				if (pTcon->ses->capabilities & CAP_UNIX)
+					rc = cifs_get_inode_info_unix(&inode,
+						full_path, inode->i_sb, xid);
+				else
+					rc = cifs_get_inode_info(&inode,
+						full_path, NULL, inode->i_sb,
+						xid);
+			} /* else we are writing out data to server already
+			     and could deadlock if we tried to flush data, and
+			     since we do not know if we have data that would
+			     invalidate the current end of file on the server
+			     we can not go to the server to get the new inod
+			     info */
+			if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
+				pCifsInode->clientCanCacheAll = TRUE;
+				pCifsInode->clientCanCacheRead = TRUE;
+				cFYI(1, ("Exclusive Oplock granted on inode %p",
+					 file->f_dentry->d_inode));
+			} else if ((oplock & 0xF) == OPLOCK_READ) {
+				pCifsInode->clientCanCacheRead = TRUE;
+				pCifsInode->clientCanCacheAll = FALSE;
+			} else {
+				pCifsInode->clientCanCacheRead = FALSE;
+				pCifsInode->clientCanCacheAll = FALSE;
+			}
+			cifs_relock_file(pCifsFile);
+		}
+	}
+
+	kfree(full_path);
+	FreeXid(xid);
+	return rc;
+}
+
+int cifs_close(struct inode *inode, struct file *file)
+{
+	int rc = 0;
+	int xid;
+	struct cifs_sb_info *cifs_sb;
+	struct cifsTconInfo *pTcon;
+	struct cifsFileInfo *pSMBFile =
+		(struct cifsFileInfo *)file->private_data;
+
+	xid = GetXid();
+
+	cifs_sb = CIFS_SB(inode->i_sb);
+	pTcon = cifs_sb->tcon;
+	if (pSMBFile) {
+		pSMBFile->closePend = TRUE;
+		write_lock(&file->f_owner.lock);
+		if (pTcon) {
+			/* no sense reconnecting to close a file that is
+			   already closed */
+			if (pTcon->tidStatus != CifsNeedReconnect) {
+				int timeout = 2;
+				while((atomic_read(&pSMBFile->wrtPending) != 0)
+					 && (timeout < 1000) ) {
+					/* Give write a better chance to get to
+					server ahead of the close.  We do not
+					want to add a wait_q here as it would
+					increase the memory utilization as
+					the struct would be in each open file,
+					but this should give enough time to 
+					clear the socket */
+					write_unlock(&file->f_owner.lock);
+					cERROR(1,("close with pending writes"));
+					msleep(timeout);
+					write_lock(&file->f_owner.lock);
+					timeout *= 4;
+				} 
+				write_unlock(&file->f_owner.lock);
+				rc = CIFSSMBClose(xid, pTcon,
+						  pSMBFile->netfid);
+				write_lock(&file->f_owner.lock);
+			}
+		}
+		write_lock(&GlobalSMBSeslock);
+		list_del(&pSMBFile->flist);
+		list_del(&pSMBFile->tlist);
+		write_unlock(&GlobalSMBSeslock);
+		write_unlock(&file->f_owner.lock);
+		kfree(pSMBFile->search_resume_name);
+		kfree(file->private_data);
+		file->private_data = NULL;
+	} else
+		rc = -EBADF;
+
+	if (list_empty(&(CIFS_I(inode)->openFileList))) {
+		cFYI(1, ("closing last open instance for inode %p", inode));
+		/* if the file is not open we do not know if we can cache info
+		   on this inode, much less write behind and read ahead */
+		CIFS_I(inode)->clientCanCacheRead = FALSE;
+		CIFS_I(inode)->clientCanCacheAll  = FALSE;
+	}
+	if ((rc ==0) && CIFS_I(inode)->write_behind_rc)
+		rc = CIFS_I(inode)->write_behind_rc;
+	FreeXid(xid);
+	return rc;
+}
+
+int cifs_closedir(struct inode *inode, struct file *file)
+{
+	int rc = 0;
+	int xid;
+	struct cifsFileInfo *pCFileStruct =
+	    (struct cifsFileInfo *)file->private_data;
+	char *ptmp;
+
+	cFYI(1, ("Closedir inode = 0x%p with ", inode));
+
+	xid = GetXid();
+
+	if (pCFileStruct) {
+		struct cifsTconInfo *pTcon;
+		struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_dentry->d_sb);
+
+		pTcon = cifs_sb->tcon;
+
+		cFYI(1, ("Freeing private data in close dir"));
+		if ((pCFileStruct->srch_inf.endOfSearch == FALSE) &&
+		   (pCFileStruct->invalidHandle == FALSE)) {
+			pCFileStruct->invalidHandle = TRUE;
+			rc = CIFSFindClose(xid, pTcon, pCFileStruct->netfid);
+			cFYI(1, ("Closing uncompleted readdir with rc %d",
+				 rc));
+			/* not much we can do if it fails anyway, ignore rc */
+			rc = 0;
+		}
+		ptmp = pCFileStruct->srch_inf.ntwrk_buf_start;
+		if (ptmp) {
+   /* BB removeme BB */	cFYI(1, ("freeing smb buf in srch struct in closedir"));
+			pCFileStruct->srch_inf.ntwrk_buf_start = NULL;
+			cifs_buf_release(ptmp);
+		}
+		ptmp = pCFileStruct->search_resume_name;
+		if (ptmp) {
+   /* BB removeme BB */	cFYI(1, ("freeing resume name in closedir"));
+			pCFileStruct->search_resume_name = NULL;
+			kfree(ptmp);
+		}
+		kfree(file->private_data);
+		file->private_data = NULL;
+	}
+	/* BB can we lock the filestruct while this is going on? */
+	FreeXid(xid);
+	return rc;
+}
+
+int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
+{
+	int rc, xid;
+	__u32 lockType = LOCKING_ANDX_LARGE_FILES;
+	__u32 numLock = 0;
+	__u32 numUnlock = 0;
+	__u64 length;
+	int wait_flag = FALSE;
+	struct cifs_sb_info *cifs_sb;
+	struct cifsTconInfo *pTcon;
+
+	length = 1 + pfLock->fl_end - pfLock->fl_start;
+	rc = -EACCES;
+	xid = GetXid();
+
+	cFYI(1, ("Lock parm: 0x%x flockflags: "
+		 "0x%x flocktype: 0x%x start: %lld end: %lld",
+	        cmd, pfLock->fl_flags, pfLock->fl_type, pfLock->fl_start,
+	        pfLock->fl_end));
+
+	if (pfLock->fl_flags & FL_POSIX)
+		cFYI(1, ("Posix "));
+	if (pfLock->fl_flags & FL_FLOCK)
+		cFYI(1, ("Flock "));
+	if (pfLock->fl_flags & FL_SLEEP) {
+		cFYI(1, ("Blocking lock "));
+		wait_flag = TRUE;
+	}
+	if (pfLock->fl_flags & FL_ACCESS)
+		cFYI(1, ("Process suspended by mandatory locking - "
+			 "not implemented yet "));
+	if (pfLock->fl_flags & FL_LEASE)
+		cFYI(1, ("Lease on file - not implemented yet"));
+	if (pfLock->fl_flags & 
+	    (~(FL_POSIX | FL_FLOCK | FL_SLEEP | FL_ACCESS | FL_LEASE)))
+		cFYI(1, ("Unknown lock flags 0x%x", pfLock->fl_flags));
+
+	if (pfLock->fl_type == F_WRLCK) {
+		cFYI(1, ("F_WRLCK "));
+		numLock = 1;
+	} else if (pfLock->fl_type == F_UNLCK) {
+		cFYI(1, ("F_UNLCK "));
+		numUnlock = 1;
+	} else if (pfLock->fl_type == F_RDLCK) {
+		cFYI(1, ("F_RDLCK "));
+		lockType |= LOCKING_ANDX_SHARED_LOCK;
+		numLock = 1;
+	} else if (pfLock->fl_type == F_EXLCK) {
+		cFYI(1, ("F_EXLCK "));
+		numLock = 1;
+	} else if (pfLock->fl_type == F_SHLCK) {
+		cFYI(1, ("F_SHLCK "));
+		lockType |= LOCKING_ANDX_SHARED_LOCK;
+		numLock = 1;
+	} else
+		cFYI(1, ("Unknown type of lock "));
+
+	cifs_sb = CIFS_SB(file->f_dentry->d_sb);
+	pTcon = cifs_sb->tcon;
+
+	if (file->private_data == NULL) {
+		FreeXid(xid);
+		return -EBADF;
+	}
+
+	if (IS_GETLK(cmd)) {
+		rc = CIFSSMBLock(xid, pTcon,
+				 ((struct cifsFileInfo *)file->
+				  private_data)->netfid,
+				 length,
+				 pfLock->fl_start, 0, 1, lockType,
+				 0 /* wait flag */ );
+		if (rc == 0) {
+			rc = CIFSSMBLock(xid, pTcon,
+					 ((struct cifsFileInfo *) file->
+					  private_data)->netfid,
+					 length,
+					 pfLock->fl_start, 1 /* numUnlock */ ,
+					 0 /* numLock */ , lockType,
+					 0 /* wait flag */ );
+			pfLock->fl_type = F_UNLCK;
+			if (rc != 0)
+				cERROR(1, ("Error unlocking previously locked "
+					   "range %d during test of lock ",
+					   rc));
+			rc = 0;
+
+		} else {
+			/* if rc == ERR_SHARING_VIOLATION ? */
+			rc = 0;	/* do not change lock type to unlock
+				   since range in use */
+		}
+
+		FreeXid(xid);
+		return rc;
+	}
+
+	rc = CIFSSMBLock(xid, pTcon,
+			 ((struct cifsFileInfo *) file->private_data)->
+			 netfid, length,
+			 pfLock->fl_start, numUnlock, numLock, lockType,
+			 wait_flag);
+	if (pfLock->fl_flags & FL_POSIX)
+		posix_lock_file_wait(file, pfLock);
+	FreeXid(xid);
+	return rc;
+}
+
+ssize_t cifs_user_write(struct file *file, const char __user *write_data,
+	size_t write_size, loff_t *poffset)
+{
+	int rc = 0;
+	unsigned int bytes_written = 0;
+	unsigned int total_written;
+	struct cifs_sb_info *cifs_sb;
+	struct cifsTconInfo *pTcon;
+	int xid, long_op;
+	struct cifsFileInfo *open_file;
+
+	if (file->f_dentry == NULL)
+		return -EBADF;
+
+	cifs_sb = CIFS_SB(file->f_dentry->d_sb);
+	if (cifs_sb == NULL)
+		return -EBADF;
+
+	pTcon = cifs_sb->tcon;
+
+	/* cFYI(1,
+	   (" write %d bytes to offset %lld of %s", write_size,
+	   *poffset, file->f_dentry->d_name.name)); */
+
+	if (file->private_data == NULL)
+		return -EBADF;
+	else
+		open_file = (struct cifsFileInfo *) file->private_data;
+	
+	xid = GetXid();
+	if (file->f_dentry->d_inode == NULL) {
+		FreeXid(xid);
+		return -EBADF;
+	}
+
+	if (*poffset > file->f_dentry->d_inode->i_size)
+		long_op = 2; /* writes past end of file can take a long time */
+	else
+		long_op = 1;
+
+	for (total_written = 0; write_size > total_written;
+	     total_written += bytes_written) {
+		rc = -EAGAIN;
+		while (rc == -EAGAIN) {
+			if (file->private_data == NULL) {
+				/* file has been closed on us */
+				FreeXid(xid);
+			/* if we have gotten here we have written some data
+			   and blocked, and the file has been freed on us while
+			   we blocked so return what we managed to write */
+				return total_written;
+			} 
+			if (open_file->closePend) {
+				FreeXid(xid);
+				if (total_written)
+					return total_written;
+				else
+					return -EBADF;
+			}
+			if (open_file->invalidHandle) {
+				if ((file->f_dentry == NULL) ||
+				    (file->f_dentry->d_inode == NULL)) {
+					FreeXid(xid);
+					return total_written;
+				}
+				/* we could deadlock if we called
+				   filemap_fdatawait from here so tell
+				   reopen_file not to flush data to server
+				   now */
+				rc = cifs_reopen_file(file->f_dentry->d_inode,
+					file, FALSE);
+				if (rc != 0)
+					break;
+			}
+
+			rc = CIFSSMBWrite(xid, pTcon,
+				open_file->netfid,
+				min_t(const int, cifs_sb->wsize,
+				      write_size - total_written),
+				*poffset, &bytes_written,
+				NULL, write_data + total_written, long_op);
+		}
+		if (rc || (bytes_written == 0)) {
+			if (total_written)
+				break;
+			else {
+				FreeXid(xid);
+				return rc;
+			}
+		} else
+			*poffset += bytes_written;
+		long_op = FALSE; /* subsequent writes fast -
+				    15 seconds is plenty */
+	}
+
+	cifs_stats_bytes_written(pTcon, total_written);
+
+	/* since the write may have blocked check these pointers again */
+	if (file->f_dentry) {
+		if (file->f_dentry->d_inode) {
+			struct inode *inode = file->f_dentry->d_inode;
+			inode->i_ctime = inode->i_mtime =
+				current_fs_time(inode->i_sb);
+			if (total_written > 0) {
+				if (*poffset > file->f_dentry->d_inode->i_size)
+					i_size_write(file->f_dentry->d_inode,
+					*poffset);
+			}
+			mark_inode_dirty_sync(file->f_dentry->d_inode);
+		}
+	}
+	FreeXid(xid);
+	return total_written;
+}
+
+static ssize_t cifs_write(struct file *file, const char *write_data,
+	size_t write_size, loff_t *poffset)
+{
+	int rc = 0;
+	unsigned int bytes_written = 0;
+	unsigned int total_written;
+	struct cifs_sb_info *cifs_sb;
+	struct cifsTconInfo *pTcon;
+	int xid, long_op;
+	struct cifsFileInfo *open_file;
+
+	if (file->f_dentry == NULL)
+		return -EBADF;
+
+	cifs_sb = CIFS_SB(file->f_dentry->d_sb);
+	if (cifs_sb == NULL)
+		return -EBADF;
+
+	pTcon = cifs_sb->tcon;
+
+	cFYI(1,("write %zd bytes to offset %lld of %s", write_size,
+	   *poffset, file->f_dentry->d_name.name));
+
+	if (file->private_data == NULL)
+		return -EBADF;
+	else
+		open_file = (struct cifsFileInfo *)file->private_data;
+	
+	xid = GetXid();
+	if (file->f_dentry->d_inode == NULL) {
+		FreeXid(xid);
+		return -EBADF;
+	}
+
+	if (*poffset > file->f_dentry->d_inode->i_size)
+		long_op = 2; /* writes past end of file can take a long time */
+	else
+		long_op = 1;
+
+	for (total_written = 0; write_size > total_written;
+	     total_written += bytes_written) {
+		rc = -EAGAIN;
+		while (rc == -EAGAIN) {
+			if (file->private_data == NULL) {
+				/* file has been closed on us */
+				FreeXid(xid);
+			/* if we have gotten here we have written some data
+			   and blocked, and the file has been freed on us
+			   while we blocked so return what we managed to 
+			   write */
+				return total_written;
+			} 
+			if (open_file->closePend) {
+				FreeXid(xid);
+				if (total_written)
+					return total_written;
+				else
+					return -EBADF;
+			}
+			if (open_file->invalidHandle) {
+				if ((file->f_dentry == NULL) ||
+				   (file->f_dentry->d_inode == NULL)) {
+					FreeXid(xid);
+					return total_written;
+				}
+				/* we could deadlock if we called
+				   filemap_fdatawait from here so tell
+				   reopen_file not to flush data to 
+				   server now */
+				rc = cifs_reopen_file(file->f_dentry->d_inode,
+					file, FALSE);
+				if (rc != 0)
+					break;
+			}
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+			/* BB FIXME We can not sign across two buffers yet */
+			if((experimEnabled) && ((pTcon->ses->server->secMode & 
+			 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) == 0)) {
+				struct kvec iov[2];
+				unsigned int len;
+
+				len = min((size_t)cifs_sb->wsize,
+					  write_size - total_written);
+				/* iov[0] is reserved for smb header */
+				iov[1].iov_base = (char *)write_data +
+						  total_written;
+				iov[1].iov_len = len;
+				rc = CIFSSMBWrite2(xid, pTcon,
+						open_file->netfid, len,
+						*poffset, &bytes_written,
+						iov, 1, long_op);
+			} else
+			/* BB FIXME fixup indentation of line below */
+#endif			
+			rc = CIFSSMBWrite(xid, pTcon,
+				 open_file->netfid,
+				 min_t(const int, cifs_sb->wsize, 
+				       write_size - total_written),
+				 *poffset, &bytes_written,
+				 write_data + total_written, NULL, long_op);
+		}
+		if (rc || (bytes_written == 0)) {
+			if (total_written)
+				break;
+			else {
+				FreeXid(xid);
+				return rc;
+			}
+		} else
+			*poffset += bytes_written;
+		long_op = FALSE; /* subsequent writes fast - 
+				    15 seconds is plenty */
+	}
+
+	cifs_stats_bytes_written(pTcon, total_written);
+
+	/* since the write may have blocked check these pointers again */
+	if (file->f_dentry) {
+		if (file->f_dentry->d_inode) {
+			file->f_dentry->d_inode->i_ctime = 
+			file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
+			if (total_written > 0) {
+				if (*poffset > file->f_dentry->d_inode->i_size)
+					i_size_write(file->f_dentry->d_inode, 
+						     *poffset);
+			}
+			mark_inode_dirty_sync(file->f_dentry->d_inode);
+		}
+	}
+	FreeXid(xid);
+	return total_written;
+}
+
+struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
+{
+	struct cifsFileInfo *open_file;
+	int rc;
+
+	read_lock(&GlobalSMBSeslock);
+	list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
+		if (open_file->closePend)
+			continue;
+		if (open_file->pfile &&
+		    ((open_file->pfile->f_flags & O_RDWR) ||
+		     (open_file->pfile->f_flags & O_WRONLY))) {
+			atomic_inc(&open_file->wrtPending);
+			read_unlock(&GlobalSMBSeslock);
+			if((open_file->invalidHandle) && 
+			   (!open_file->closePend) /* BB fixme -since the second clause can not be true remove it BB */) {
+				rc = cifs_reopen_file(&cifs_inode->vfs_inode, 
+						      open_file->pfile, FALSE);
+				/* if it fails, try another handle - might be */
+				/* dangerous to hold up writepages with retry */
+				if(rc) {
+					cFYI(1,("failed on reopen file in wp"));
+					read_lock(&GlobalSMBSeslock);
+					/* can not use this handle, no write
+					pending on this one after all */
+					atomic_dec
+					     (&open_file->wrtPending);
+					continue;
+				}
+			}
+			return open_file;
+		}
+	}
+	read_unlock(&GlobalSMBSeslock);
+	return NULL;
+}
+
+static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
+{
+	struct address_space *mapping = page->mapping;
+	loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
+	char *write_data;
+	int rc = -EFAULT;
+	int bytes_written = 0;
+	struct cifs_sb_info *cifs_sb;
+	struct cifsTconInfo *pTcon;
+	struct inode *inode;
+	struct cifsFileInfo *open_file;
+
+	if (!mapping || !mapping->host)
+		return -EFAULT;
+
+	inode = page->mapping->host;
+	cifs_sb = CIFS_SB(inode->i_sb);
+	pTcon = cifs_sb->tcon;
+
+	offset += (loff_t)from;
+	write_data = kmap(page);
+	write_data += from;
+
+	if ((to > PAGE_CACHE_SIZE) || (from > to)) {
+		kunmap(page);
+		return -EIO;
+	}
+
+	/* racing with truncate? */
+	if (offset > mapping->host->i_size) {
+		kunmap(page);
+		return 0; /* don't care */
+	}
+
+	/* check to make sure that we are not extending the file */
+	if (mapping->host->i_size - offset < (loff_t)to)
+		to = (unsigned)(mapping->host->i_size - offset); 
+
+	open_file = find_writable_file(CIFS_I(mapping->host));
+	if (open_file) {
+		bytes_written = cifs_write(open_file->pfile, write_data,
+					   to-from, &offset);
+		atomic_dec(&open_file->wrtPending);
+		/* Does mm or vfs already set times? */
+		inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb);
+		if ((bytes_written > 0) && (offset)) {
+			rc = 0;
+		} else if (bytes_written < 0) {
+			if (rc != -EBADF)
+				rc = bytes_written;
+		}
+	} else {
+		cFYI(1, ("No writeable filehandles for inode"));
+		rc = -EIO;
+	}
+
+	kunmap(page);
+	return rc;
+}
+
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+static int cifs_writepages(struct address_space *mapping,
+			   struct writeback_control *wbc)
+{
+	struct backing_dev_info *bdi = mapping->backing_dev_info;
+	unsigned int bytes_to_write;
+	unsigned int bytes_written;
+	struct cifs_sb_info *cifs_sb;
+	int done = 0;
+	pgoff_t end = -1;
+	pgoff_t index;
+	int is_range = 0;
+	struct kvec iov[32];
+	int len;
+	int n_iov = 0;
+	pgoff_t next;
+	int nr_pages;
+	__u64 offset = 0;
+	struct cifsFileInfo *open_file;
+	struct page *page;
+	struct pagevec pvec;
+	int rc = 0;
+	int scanned = 0;
+	int xid;
+
+	cifs_sb = CIFS_SB(mapping->host->i_sb);
+	
+	/*
+	 * If wsize is smaller that the page cache size, default to writing
+	 * one page at a time via cifs_writepage
+	 */
+	if (cifs_sb->wsize < PAGE_CACHE_SIZE)
+		return generic_writepages(mapping, wbc);
+
+	/* BB FIXME we do not have code to sign across multiple buffers yet,
+	   so go to older writepage style write which we can sign if needed */
+	if((cifs_sb->tcon->ses) && (cifs_sb->tcon->ses->server))
+		if(cifs_sb->tcon->ses->server->secMode &
+                          (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+			return generic_writepages(mapping, wbc);
+
+	/*
+	 * BB: Is this meaningful for a non-block-device file system?
+	 * If it is, we should test it again after we do I/O
+	 */
+	if (wbc->nonblocking && bdi_write_congested(bdi)) {
+		wbc->encountered_congestion = 1;
+		return 0;
+	}
+
+	xid = GetXid();
+
+	pagevec_init(&pvec, 0);
+	if (wbc->sync_mode == WB_SYNC_NONE)
+		index = mapping->writeback_index; /* Start from prev offset */
+	else {
+		index = 0;
+		scanned = 1;
+	}
+	if (wbc->start || wbc->end) {
+		index = wbc->start >> PAGE_CACHE_SHIFT;
+		end = wbc->end >> PAGE_CACHE_SHIFT;
+		is_range = 1;
+		scanned = 1;
+	}
+retry:
+	while (!done && (index <= end) &&
+	       (nr_pages = pagevec_lookup_tag(&pvec, mapping, &index,
+			PAGECACHE_TAG_DIRTY,
+			min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1))) {
+		int first;
+		unsigned int i;
+
+		first = -1;
+		next = 0;
+		n_iov = 0;
+		bytes_to_write = 0;
+
+		for (i = 0; i < nr_pages; i++) {
+			page = pvec.pages[i];
+			/*
+			 * At this point we hold neither mapping->tree_lock nor
+			 * lock on the page itself: the page may be truncated or
+			 * invalidated (changing page->mapping to NULL), or even
+			 * swizzled back from swapper_space to tmpfs file
+			 * mapping
+			 */
+
+			if (first < 0)
+				lock_page(page);
+			else if (TestSetPageLocked(page))
+				break;
+
+			if (unlikely(page->mapping != mapping)) {
+				unlock_page(page);
+				break;
+			}
+
+			if (unlikely(is_range) && (page->index > end)) {
+				done = 1;
+				unlock_page(page);
+				break;
+			}
+
+			if (next && (page->index != next)) {
+				/* Not next consecutive page */
+				unlock_page(page);
+				break;
+			}
+
+			if (wbc->sync_mode != WB_SYNC_NONE)
+				wait_on_page_writeback(page);
+
+			if (PageWriteback(page) ||
+					!test_clear_page_dirty(page)) {
+				unlock_page(page);
+				break;
+			}
+
+			if (page_offset(page) >= mapping->host->i_size) {
+				done = 1;
+				unlock_page(page);
+				break;
+			}
+
+			/*
+			 * BB can we get rid of this?  pages are held by pvec
+			 */
+			page_cache_get(page);
+
+			len = min(mapping->host->i_size - page_offset(page),
+				  (loff_t)PAGE_CACHE_SIZE);
+
+			/* reserve iov[0] for the smb header */
+			n_iov++;
+			iov[n_iov].iov_base = kmap(page);
+			iov[n_iov].iov_len = len;
+			bytes_to_write += len;
+
+			if (first < 0) {
+				first = i;
+				offset = page_offset(page);
+			}
+			next = page->index + 1;
+			if (bytes_to_write + PAGE_CACHE_SIZE > cifs_sb->wsize)
+				break;
+		}
+		if (n_iov) {
+			/* Search for a writable handle every time we call
+			 * CIFSSMBWrite2.  We can't rely on the last handle
+			 * we used to still be valid
+			 */
+			open_file = find_writable_file(CIFS_I(mapping->host));
+			if (!open_file) {
+				cERROR(1, ("No writable handles for inode"));
+				rc = -EBADF;
+			} else {
+				rc = CIFSSMBWrite2(xid, cifs_sb->tcon,
+						   open_file->netfid,
+						   bytes_to_write, offset,
+						   &bytes_written, iov, n_iov,
+						   1);
+				atomic_dec(&open_file->wrtPending);
+				if (rc || bytes_written < bytes_to_write) {
+					cERROR(1,("Write2 ret %d, written = %d",
+						  rc, bytes_written));
+					/* BB what if continued retry is
+					   requested via mount flags? */
+					set_bit(AS_EIO, &mapping->flags);
+					SetPageError(page);
+				} else {
+					cifs_stats_bytes_written(cifs_sb->tcon,
+								 bytes_written);
+				}
+			}
+			for (i = 0; i < n_iov; i++) {
+				page = pvec.pages[first + i];
+				kunmap(page);
+				unlock_page(page);
+				page_cache_release(page);
+			}
+			if ((wbc->nr_to_write -= n_iov) <= 0)
+				done = 1;
+			index = next;
+		}
+		pagevec_release(&pvec);
+	}
+	if (!scanned && !done) {
+		/*
+		 * We hit the last page and there is more work to be done: wrap
+		 * back to the start of the file
+		 */
+		scanned = 1;
+		index = 0;
+		goto retry;
+	}
+	if (!is_range)
+		mapping->writeback_index = index;
+
+	FreeXid(xid);
+
+	return rc;
+}
+#endif
+
+static int cifs_writepage(struct page* page, struct writeback_control *wbc)
+{
+	int rc = -EFAULT;
+	int xid;
+
+	xid = GetXid();
+/* BB add check for wbc flags */
+	page_cache_get(page);
+        if (!PageUptodate(page)) {
+		cFYI(1, ("ppw - page not up to date"));
+	}
+	
+	rc = cifs_partialpagewrite(page, 0, PAGE_CACHE_SIZE);
+	SetPageUptodate(page); /* BB add check for error and Clearuptodate? */
+	unlock_page(page);
+	page_cache_release(page);	
+	FreeXid(xid);
+	return rc;
+}
+
+static int cifs_commit_write(struct file *file, struct page *page,
+	unsigned offset, unsigned to)
+{
+	int xid;
+	int rc = 0;
+	struct inode *inode = page->mapping->host;
+	loff_t position = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
+	char *page_data;
+
+	xid = GetXid();
+	cFYI(1, ("commit write for page %p up to position %lld for %d", 
+		 page, position, to));
+	if (position > inode->i_size) {
+		i_size_write(inode, position);
+		/* if (file->private_data == NULL) {
+			rc = -EBADF;
+		} else {
+			open_file = (struct cifsFileInfo *)file->private_data;
+			cifs_sb = CIFS_SB(inode->i_sb);
+			rc = -EAGAIN;
+			while (rc == -EAGAIN) {
+				if ((open_file->invalidHandle) && 
+				    (!open_file->closePend)) {
+					rc = cifs_reopen_file(
+						file->f_dentry->d_inode, file);
+					if (rc != 0)
+						break;
+				}
+				if (!open_file->closePend) {
+					rc = CIFSSMBSetFileSize(xid,
+						cifs_sb->tcon, position,
+						open_file->netfid,
+						open_file->pid, FALSE);
+				} else {
+					rc = -EBADF;
+					break;
+				}
+			}
+			cFYI(1, (" SetEOF (commit write) rc = %d", rc));
+		} */
+	}
+	if (!PageUptodate(page)) {
+		position =  ((loff_t)page->index << PAGE_CACHE_SHIFT) + offset;
+		/* can not rely on (or let) writepage write this data */
+		if (to < offset) {
+			cFYI(1, ("Illegal offsets, can not copy from %d to %d",
+				offset, to));
+			FreeXid(xid);
+			return rc;
+		}
+		/* this is probably better than directly calling
+		   partialpage_write since in this function the file handle is
+		   known which we might as well	leverage */
+		/* BB check if anything else missing out of ppw
+		   such as updating last write time */
+		page_data = kmap(page);
+		rc = cifs_write(file, page_data + offset, to-offset,
+				&position);
+		if (rc > 0)
+			rc = 0;
+		/* else if (rc < 0) should we set writebehind rc? */
+		kunmap(page);
+	} else {	
+		set_page_dirty(page);
+	}
+
+	FreeXid(xid);
+	return rc;
+}
+
+int cifs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+	int xid;
+	int rc = 0;
+	struct inode *inode = file->f_dentry->d_inode;
+
+	xid = GetXid();
+
+	cFYI(1, ("Sync file - name: %s datasync: 0x%x ", 
+		dentry->d_name.name, datasync));
+	
+	rc = filemap_fdatawrite(inode->i_mapping);
+	if (rc == 0)
+		CIFS_I(inode)->write_behind_rc = 0;
+	FreeXid(xid);
+	return rc;
+}
+
+/* static int cifs_sync_page(struct page *page)
+{
+	struct address_space *mapping;
+	struct inode *inode;
+	unsigned long index = page->index;
+	unsigned int rpages = 0;
+	int rc = 0;
+
+	cFYI(1, ("sync page %p",page));
+	mapping = page->mapping;
+	if (!mapping)
+		return 0;
+	inode = mapping->host;
+	if (!inode)
+		return 0; */
+
+/*	fill in rpages then 
+	result = cifs_pagein_inode(inode, index, rpages); */ /* BB finish */
+
+/*	cFYI(1, ("rpages is %d for sync page of Index %ld ", rpages, index));
+
+	if (rc < 0)
+		return rc;
+	return 0;
+} */
+
+/*
+ * As file closes, flush all cached write data for this inode checking
+ * for write behind errors.
+ */
+int cifs_flush(struct file *file)
+{
+	struct inode * inode = file->f_dentry->d_inode;
+	int rc = 0;
+
+	/* Rather than do the steps manually:
+	   lock the inode for writing
+	   loop through pages looking for write behind data (dirty pages)
+	   coalesce into contiguous 16K (or smaller) chunks to write to server
+	   send to server (prefer in parallel)
+	   deal with writebehind errors
+	   unlock inode for writing
+	   filemapfdatawrite appears easier for the time being */
+
+	rc = filemap_fdatawrite(inode->i_mapping);
+	if (!rc) /* reset wb rc if we were able to write out dirty pages */
+		CIFS_I(inode)->write_behind_rc = 0;
+		
+	cFYI(1, ("Flush inode %p file %p rc %d",inode,file,rc));
+
+	return rc;
+}
+
+ssize_t cifs_user_read(struct file *file, char __user *read_data,
+	size_t read_size, loff_t *poffset)
+{
+	int rc = -EACCES;
+	unsigned int bytes_read = 0;
+	unsigned int total_read = 0;
+	unsigned int current_read_size;
+	struct cifs_sb_info *cifs_sb;
+	struct cifsTconInfo *pTcon;
+	int xid;
+	struct cifsFileInfo *open_file;
+	char *smb_read_data;
+	char __user *current_offset;
+	struct smb_com_read_rsp *pSMBr;
+
+	xid = GetXid();
+	cifs_sb = CIFS_SB(file->f_dentry->d_sb);
+	pTcon = cifs_sb->tcon;
+
+	if (file->private_data == NULL) {
+		FreeXid(xid);
+		return -EBADF;
+	}
+	open_file = (struct cifsFileInfo *)file->private_data;
+
+	if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
+		cFYI(1, ("attempting read on write only file instance"));
+	}
+	for (total_read = 0, current_offset = read_data;
+	     read_size > total_read;
+	     total_read += bytes_read, current_offset += bytes_read) {
+		current_read_size = min_t(const int, read_size - total_read, 
+					  cifs_sb->rsize);
+		rc = -EAGAIN;
+		smb_read_data = NULL;
+		while (rc == -EAGAIN) {
+			if ((open_file->invalidHandle) && 
+			    (!open_file->closePend)) {
+				rc = cifs_reopen_file(file->f_dentry->d_inode,
+					file, TRUE);
+				if (rc != 0)
+					break;
+			}
+			rc = CIFSSMBRead(xid, pTcon,
+					open_file->netfid,
+					current_read_size, *poffset,
+					&bytes_read, &smb_read_data);
+			pSMBr = (struct smb_com_read_rsp *)smb_read_data;
+			if (copy_to_user(current_offset, 
+					 smb_read_data + 4 /* RFC1001 hdr */
+					 + le16_to_cpu(pSMBr->DataOffset), 
+					 bytes_read)) {
+				rc = -EFAULT;
+				FreeXid(xid);
+				return rc;
+            }
+			if (smb_read_data) {
+				cifs_buf_release(smb_read_data);
+				smb_read_data = NULL;
+			}
+		}
+		if (rc || (bytes_read == 0)) {
+			if (total_read) {
+				break;
+			} else {
+				FreeXid(xid);
+				return rc;
+			}
+		} else {
+			cifs_stats_bytes_read(pTcon, bytes_read);
+			*poffset += bytes_read;
+		}
+	}
+	FreeXid(xid);
+	return total_read;
+}
+
+
+static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
+	loff_t *poffset)
+{
+	int rc = -EACCES;
+	unsigned int bytes_read = 0;
+	unsigned int total_read;
+	unsigned int current_read_size;
+	struct cifs_sb_info *cifs_sb;
+	struct cifsTconInfo *pTcon;
+	int xid;
+	char *current_offset;
+	struct cifsFileInfo *open_file;
+
+	xid = GetXid();
+	cifs_sb = CIFS_SB(file->f_dentry->d_sb);
+	pTcon = cifs_sb->tcon;
+
+	if (file->private_data == NULL) {
+		FreeXid(xid);
+		return -EBADF;
+	}
+	open_file = (struct cifsFileInfo *)file->private_data;
+
+	if ((file->f_flags & O_ACCMODE) == O_WRONLY)
+		cFYI(1, ("attempting read on write only file instance"));
+
+	for (total_read = 0, current_offset = read_data; 
+	     read_size > total_read;
+	     total_read += bytes_read, current_offset += bytes_read) {
+		current_read_size = min_t(const int, read_size - total_read,
+					  cifs_sb->rsize);
+		/* For windows me and 9x we do not want to request more
+		than it negotiated since it will refuse the read then */
+		if((pTcon->ses) && 
+			!(pTcon->ses->capabilities & CAP_LARGE_FILES)) {
+			current_read_size = min_t(const int, current_read_size,
+					pTcon->ses->server->maxBuf - 128);
+		}
+		rc = -EAGAIN;
+		while (rc == -EAGAIN) {
+			if ((open_file->invalidHandle) && 
+			    (!open_file->closePend)) {
+				rc = cifs_reopen_file(file->f_dentry->d_inode,
+					file, TRUE);
+				if (rc != 0)
+					break;
+			}
+			rc = CIFSSMBRead(xid, pTcon,
+					open_file->netfid,
+					current_read_size, *poffset,
+					&bytes_read, &current_offset);
+		}
+		if (rc || (bytes_read == 0)) {
+			if (total_read) {
+				break;
+			} else {
+				FreeXid(xid);
+				return rc;
+			}
+		} else {
+			cifs_stats_bytes_read(pTcon, total_read);
+			*poffset += bytes_read;
+		}
+	}
+	FreeXid(xid);
+	return total_read;
+}
+
+int cifs_file_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct dentry *dentry = file->f_dentry;
+	int rc, xid;
+
+	xid = GetXid();
+	rc = cifs_revalidate(dentry);
+	if (rc) {
+		cFYI(1, ("Validation prior to mmap failed, error=%d", rc));
+		FreeXid(xid);
+		return rc;
+	}
+	rc = generic_file_mmap(file, vma);
+	FreeXid(xid);
+	return rc;
+}
+
+
+static void cifs_copy_cache_pages(struct address_space *mapping, 
+	struct list_head *pages, int bytes_read, char *data,
+	struct pagevec *plru_pvec)
+{
+	struct page *page;
+	char *target;
+
+	while (bytes_read > 0) {
+		if (list_empty(pages))
+			break;
+
+		page = list_entry(pages->prev, struct page, lru);
+		list_del(&page->lru);
+
+		if (add_to_page_cache(page, mapping, page->index,
+				      GFP_KERNEL)) {
+			page_cache_release(page);
+			cFYI(1, ("Add page cache failed"));
+			data += PAGE_CACHE_SIZE;
+			bytes_read -= PAGE_CACHE_SIZE;
+			continue;
+		}
+
+		target = kmap_atomic(page,KM_USER0);
+
+		if (PAGE_CACHE_SIZE > bytes_read) {
+			memcpy(target, data, bytes_read);
+			/* zero the tail end of this partial page */
+			memset(target + bytes_read, 0, 
+			       PAGE_CACHE_SIZE - bytes_read);
+			bytes_read = 0;
+		} else {
+			memcpy(target, data, PAGE_CACHE_SIZE);
+			bytes_read -= PAGE_CACHE_SIZE;
+		}
+		kunmap_atomic(target, KM_USER0);
+
+		flush_dcache_page(page);
+		SetPageUptodate(page);
+		unlock_page(page);
+		if (!pagevec_add(plru_pvec, page))
+			__pagevec_lru_add(plru_pvec);
+		data += PAGE_CACHE_SIZE;
+	}
+	return;
+}
+
+static int cifs_readpages(struct file *file, struct address_space *mapping,
+	struct list_head *page_list, unsigned num_pages)
+{
+	int rc = -EACCES;
+	int xid;
+	loff_t offset;
+	struct page *page;
+	struct cifs_sb_info *cifs_sb;
+	struct cifsTconInfo *pTcon;
+	int bytes_read = 0;
+	unsigned int read_size,i;
+	char *smb_read_data = NULL;
+	struct smb_com_read_rsp *pSMBr;
+	struct pagevec lru_pvec;
+	struct cifsFileInfo *open_file;
+
+	xid = GetXid();
+	if (file->private_data == NULL) {
+		FreeXid(xid);
+		return -EBADF;
+	}
+	open_file = (struct cifsFileInfo *)file->private_data;
+	cifs_sb = CIFS_SB(file->f_dentry->d_sb);
+	pTcon = cifs_sb->tcon;
+
+	pagevec_init(&lru_pvec, 0);
+
+	for (i = 0; i < num_pages; ) {
+		unsigned contig_pages;
+		struct page *tmp_page;
+		unsigned long expected_index;
+
+		if (list_empty(page_list))
+			break;
+
+		page = list_entry(page_list->prev, struct page, lru);
+		offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
+
+		/* count adjacent pages that we will read into */
+		contig_pages = 0;
+		expected_index = 
+			list_entry(page_list->prev, struct page, lru)->index;
+		list_for_each_entry_reverse(tmp_page,page_list,lru) {
+			if (tmp_page->index == expected_index) {
+				contig_pages++;
+				expected_index++;
+			} else
+				break; 
+		}
+		if (contig_pages + i >  num_pages)
+			contig_pages = num_pages - i;
+
+		/* for reads over a certain size could initiate async
+		   read ahead */
+
+		read_size = contig_pages * PAGE_CACHE_SIZE;
+		/* Read size needs to be in multiples of one page */
+		read_size = min_t(const unsigned int, read_size,
+				  cifs_sb->rsize & PAGE_CACHE_MASK);
+
+		rc = -EAGAIN;
+		while (rc == -EAGAIN) {
+			if ((open_file->invalidHandle) && 
+			    (!open_file->closePend)) {
+				rc = cifs_reopen_file(file->f_dentry->d_inode,
+					file, TRUE);
+				if (rc != 0)
+					break;
+			}
+
+			rc = CIFSSMBRead(xid, pTcon,
+					open_file->netfid,
+					read_size, offset,
+					&bytes_read, &smb_read_data);
+
+			/* BB more RC checks ? */
+			if (rc== -EAGAIN) {
+				if (smb_read_data) {
+					cifs_buf_release(smb_read_data);
+					smb_read_data = NULL;
+				}
+			}
+		}
+		if ((rc < 0) || (smb_read_data == NULL)) {
+			cFYI(1, ("Read error in readpages: %d", rc));
+			/* clean up remaing pages off list */
+			while (!list_empty(page_list) && (i < num_pages)) {
+				page = list_entry(page_list->prev, struct page,
+						  lru);
+				list_del(&page->lru);
+				page_cache_release(page);
+			}
+			break;
+		} else if (bytes_read > 0) {
+			pSMBr = (struct smb_com_read_rsp *)smb_read_data;
+			cifs_copy_cache_pages(mapping, page_list, bytes_read,
+				smb_read_data + 4 /* RFC1001 hdr */ +
+				le16_to_cpu(pSMBr->DataOffset), &lru_pvec);
+
+			i +=  bytes_read >> PAGE_CACHE_SHIFT;
+			cifs_stats_bytes_read(pTcon, bytes_read);
+			if ((int)(bytes_read & PAGE_CACHE_MASK) != bytes_read) {
+				i++; /* account for partial page */
+
+				/* server copy of file can have smaller size 
+				   than client */
+				/* BB do we need to verify this common case ? 
+				   this case is ok - if we are at server EOF 
+				   we will hit it on next read */
+
+			/* while (!list_empty(page_list) && (i < num_pages)) {
+					page = list_entry(page_list->prev, 
+							  struct page, list);
+					list_del(&page->list);
+					page_cache_release(page);
+				}
+				break; */
+			}
+		} else {
+			cFYI(1, ("No bytes read (%d) at offset %lld . "
+				 "Cleaning remaining pages from readahead list",
+				 bytes_read, offset));
+			/* BB turn off caching and do new lookup on 
+			   file size at server? */
+			while (!list_empty(page_list) && (i < num_pages)) {
+				page = list_entry(page_list->prev, struct page,
+						  lru);
+				list_del(&page->lru);
+
+				/* BB removeme - replace with zero of page? */
+				page_cache_release(page);
+			}
+			break;
+		}
+		if (smb_read_data) {
+			cifs_buf_release(smb_read_data);
+			smb_read_data = NULL;
+		}
+		bytes_read = 0;
+	}
+
+	pagevec_lru_add(&lru_pvec);
+
+/* need to free smb_read_data buf before exit */
+	if (smb_read_data) {
+		cifs_buf_release(smb_read_data);
+		smb_read_data = NULL;
+	} 
+
+	FreeXid(xid);
+	return rc;
+}
+
+static int cifs_readpage_worker(struct file *file, struct page *page,
+	loff_t *poffset)
+{
+	char *read_data;
+	int rc;
+
+	page_cache_get(page);
+	read_data = kmap(page);
+	/* for reads over a certain size could initiate async read ahead */
+                                                                                                                           
+	rc = cifs_read(file, read_data, PAGE_CACHE_SIZE, poffset);
+                                                                                                                           
+	if (rc < 0)
+		goto io_error;
+	else
+		cFYI(1, ("Bytes read %d ",rc));
+                                                                                                                           
+	file->f_dentry->d_inode->i_atime =
+		current_fs_time(file->f_dentry->d_inode->i_sb);
+                                                                                                                           
+	if (PAGE_CACHE_SIZE > rc)
+		memset(read_data + rc, 0, PAGE_CACHE_SIZE - rc);
+
+	flush_dcache_page(page);
+	SetPageUptodate(page);
+	rc = 0;
+                                                                                                                           
+io_error:
+        kunmap(page);
+	page_cache_release(page);
+	return rc;
+}
+
+static int cifs_readpage(struct file *file, struct page *page)
+{
+	loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
+	int rc = -EACCES;
+	int xid;
+
+	xid = GetXid();
+
+	if (file->private_data == NULL) {
+		FreeXid(xid);
+		return -EBADF;
+	}
+
+	cFYI(1, ("readpage %p at offset %d 0x%x\n", 
+		 page, (int)offset, (int)offset));
+
+	rc = cifs_readpage_worker(file, page, &offset);
+
+	unlock_page(page);
+
+	FreeXid(xid);
+	return rc;
+}
+
+/* We do not want to update the file size from server for inodes
+   open for write - to avoid races with writepage extending
+   the file - in the future we could consider allowing
+   refreshing the inode only on increases in the file size 
+   but this is tricky to do without racing with writebehind
+   page caching in the current Linux kernel design */
+int is_size_safe_to_change(struct cifsInodeInfo *cifsInode)
+{
+	struct cifsFileInfo *open_file = NULL;
+
+	if (cifsInode)
+		open_file =  find_writable_file(cifsInode);
+ 
+	if(open_file) {
+		/* there is not actually a write pending so let
+		this handle go free and allow it to
+		be closable if needed */
+		atomic_dec(&open_file->wrtPending);
+		return 0;
+	} else
+		return 1;
+}
+
+static int cifs_prepare_write(struct file *file, struct page *page,
+	unsigned from, unsigned to)
+{
+	int rc = 0;
+        loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
+	cFYI(1, ("prepare write for page %p from %d to %d",page,from,to));
+	if (!PageUptodate(page)) {
+	/*	if (to - from != PAGE_CACHE_SIZE) {
+			void *kaddr = kmap_atomic(page, KM_USER0);
+			memset(kaddr, 0, from);
+			memset(kaddr + to, 0, PAGE_CACHE_SIZE - to);
+			flush_dcache_page(page);
+			kunmap_atomic(kaddr, KM_USER0);
+		} */
+		/* If we are writing a full page it will be up to date,
+		   no need to read from the server */
+		if ((to == PAGE_CACHE_SIZE) && (from == 0))
+			SetPageUptodate(page);
+
+		/* might as well read a page, it is fast enough */
+		if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
+			rc = cifs_readpage_worker(file, page, &offset);
+		} else {
+		/* should we try using another file handle if there is one -
+		   how would we lock it to prevent close of that handle
+		   racing with this read?
+		   In any case this will be written out by commit_write */
+		}
+	}
+
+	/* BB should we pass any errors back? 
+	   e.g. if we do not have read access to the file */
+	return 0;
+}
+
+struct address_space_operations cifs_addr_ops = {
+	.readpage = cifs_readpage,
+	.readpages = cifs_readpages,
+	.writepage = cifs_writepage,
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+	.writepages = cifs_writepages,
+#endif
+	.prepare_write = cifs_prepare_write,
+	.commit_write = cifs_commit_write,
+	.set_page_dirty = __set_page_dirty_nobuffers,
+	/* .sync_page = cifs_sync_page, */
+	/* .direct_IO = */
+};
diff -urN oldtree/fs/cifs/inode.c newtree/fs/cifs/inode.c
--- oldtree/fs/cifs/inode.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/inode.c	2006-02-13 19:20:00.693406888 -0500
@@ -229,11 +229,12 @@
 			 cifs_sb->mnt_cifs_flags &
 				CIFS_MOUNT_MAP_SPECIAL_CHR);
 	if (rc==0) {
+		int buf_type = CIFS_NO_BUFFER;
 			/* Read header */
 		rc = CIFSSMBRead(xid, pTcon,
 			         netfid,
 				 24 /* length */, 0 /* offset */,
-				 &bytes_read, &pbuf);
+				 &bytes_read, &pbuf, &buf_type);
 		if((rc == 0) && (bytes_read >= 8)) {
 			if(memcmp("IntxBLK", pbuf, 8) == 0) {
 				cFYI(1,("Block device"));
@@ -267,7 +268,7 @@
 		} else {
 			inode->i_mode |= S_IFREG; /* then it is a file */
 			rc = -EOPNOTSUPP; /* or some unknown SFU type */	
-		}
+		}		
 		CIFSSMBClose(xid, pTcon, netfid);
 	}
 	return rc;
@@ -750,8 +751,8 @@
 			if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
 				CIFSSMBUnixSetPerms(xid, pTcon, full_path,
 						    mode,
-						    (__u64)current->euid,
-						    (__u64)current->egid,
+						    (__u64)current->fsuid,
+						    (__u64)current->fsgid,
 						    0 /* dev_t */,
 						    cifs_sb->local_nls,
 						    cifs_sb->mnt_cifs_flags &
diff -urN oldtree/fs/cifs/misc.c newtree/fs/cifs/misc.c
--- oldtree/fs/cifs/misc.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/misc.c	2006-02-13 19:20:00.694406736 -0500
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/misc.c
  *
- *   Copyright (C) International Business Machines  Corp., 2002,2004
+ *   Copyright (C) International Business Machines  Corp., 2002,2005
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -161,6 +161,9 @@
 	if (ret_buf) {
 		memset(ret_buf, 0, sizeof(struct smb_hdr) + 3);
 		atomic_inc(&bufAllocCount);
+#ifdef CONFIG_CIFS_STATS2
+		atomic_inc(&totBufAllocCount);
+#endif /* CONFIG_CIFS_STATS2 */
 	}
 
 	return ret_buf;
@@ -195,6 +198,10 @@
 	/* No need to clear memory here, cleared in header assemble */
 	/*	memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/
 		atomic_inc(&smBufAllocCount);
+#ifdef CONFIG_CIFS_STATS2
+		atomic_inc(&totSmBufAllocCount);
+#endif /* CONFIG_CIFS_STATS2 */
+
 	}
 	return ret_buf;
 }
@@ -292,7 +299,7 @@
 	struct cifsSesInfo * ses;
 	char *temp = (char *) buffer;
 
-	memset(temp,0,MAX_CIFS_HDR_SIZE);
+	memset(temp,0,256); /* bigger than MAX_CIFS_HDR_SIZE */
 
 	buffer->smb_buf_length =
 	    (2 * word_count) + sizeof (struct smb_hdr) -
@@ -348,12 +355,12 @@
 		/*  BB Add support for establishing new tCon and SMB Session  */
 		/*      with userid/password pairs found on the smb session   */ 
 		/*	for other target tcp/ip addresses 		BB    */
-				if(current->uid != treeCon->ses->linux_uid) {
-					cFYI(1,("Multiuser mode and UID did not match tcon uid "));
+				if(current->fsuid != treeCon->ses->linux_uid) {
+					cFYI(1,("Multiuser mode and UID did not match tcon uid"));
 					read_lock(&GlobalSMBSeslock);
 					list_for_each(temp_item, &GlobalSMBSessionList) {
 						ses = list_entry(temp_item, struct cifsSesInfo, cifsSessionList);
-						if(ses->linux_uid == current->uid) {
+						if(ses->linux_uid == current->fsuid) {
 							if(ses->server == treeCon->ses->server) {
 								cFYI(1,("found matching uid substitute right smb_uid"));  
 								buffer->Uid = ses->Suid;
diff -urN oldtree/fs/cifs/readdir.c newtree/fs/cifs/readdir.c
--- oldtree/fs/cifs/readdir.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/readdir.c	2006-02-13 19:20:00.695406584 -0500
@@ -214,8 +214,7 @@
 			tmp_inode->i_fop = &cifs_file_nobrl_ops;
 		else
 			tmp_inode->i_fop = &cifs_file_ops;
-		if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
-			tmp_inode->i_fop->lock = NULL;
+
 		tmp_inode->i_data.a_ops = &cifs_addr_ops;
 		if((cifs_sb->tcon) && (cifs_sb->tcon->ses) &&
 		   (cifs_sb->tcon->ses->server->maxBuf <
@@ -327,12 +326,18 @@
 	if (S_ISREG(tmp_inode->i_mode)) {
 		cFYI(1, ("File inode"));
 		tmp_inode->i_op = &cifs_file_inode_ops;
-		if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO)
-			tmp_inode->i_fop = &cifs_file_direct_ops;
+
+		if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) {
+			if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+				tmp_inode->i_fop = &cifs_file_direct_nobrl_ops;
+			else
+				tmp_inode->i_fop = &cifs_file_direct_ops;
+		
+		} else if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+			tmp_inode->i_fop = &cifs_file_nobrl_ops;
 		else
 			tmp_inode->i_fop = &cifs_file_ops;
-		if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
-			tmp_inode->i_fop->lock = NULL;
+
 		tmp_inode->i_data.a_ops = &cifs_addr_ops;
 		if((cifs_sb->tcon) && (cifs_sb->tcon->ses) &&
 		   (cifs_sb->tcon->ses->server->maxBuf < 
diff -urN oldtree/fs/cifs/rfc1002pdu.h newtree/fs/cifs/rfc1002pdu.h
--- oldtree/fs/cifs/rfc1002pdu.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/rfc1002pdu.h	2006-02-13 19:20:00.695406584 -0500
@@ -24,11 +24,11 @@
 /* NB: unlike smb/cifs packets, the RFC1002 structures are big endian */
 
 	/* RFC 1002 session packet types */
-#define RFC1002_SESSION_MESASAGE 0x00
+#define RFC1002_SESSION_MESSAGE 0x00
 #define RFC1002_SESSION_REQUEST  0x81
 #define RFC1002_POSITIVE_SESSION_RESPONSE 0x82
 #define RFC1002_NEGATIVE_SESSION_RESPONSE 0x83
-#define RFC1002_RETARGET_SESSION_RESPONSE 0x83
+#define RFC1002_RETARGET_SESSION_RESPONSE 0x84
 #define RFC1002_SESSION_KEEP_ALIVE 0x85
 
 	/* RFC 1002 flags (only one defined */
diff -urN oldtree/fs/cifs/transport.c newtree/fs/cifs/transport.c
--- oldtree/fs/cifs/transport.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/transport.c	2006-02-13 19:20:00.696406432 -0500
@@ -206,7 +206,6 @@
 	return rc;
 }
 
-#ifdef CONFIG_CIFS_EXPERIMENTAL
 static int
 smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec,
 	  struct sockaddr *sin)
@@ -299,7 +298,7 @@
 
 int
 SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, 
-	     struct kvec *iov, int n_vec, int *pbytes_returned,
+	     struct kvec *iov, int n_vec, int * pRespBufType /* ret */, 
 	     const int long_op)
 {
 	int rc = 0;
@@ -307,6 +306,8 @@
 	unsigned long timeout;
 	struct mid_q_entry *midQ;
 	struct smb_hdr *in_buf = iov[0].iov_base;
+	
+	*pRespBufType = CIFS_NO_BUFFER;  /* no response buf yet */
 
 	if (ses == NULL) {
 		cERROR(1,("Null smb session"));
@@ -392,8 +393,7 @@
 		return -ENOMEM;
 	}
 
-/* BB FIXME */
-/* 	rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number); */
+ 	rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number);
 
 	midQ->midState = MID_REQUEST_SUBMITTED;
 #ifdef CONFIG_CIFS_STATS2
@@ -489,21 +489,23 @@
 			receive_len, xid));
 		rc = -EIO;
 	} else {		/* rcvd frame is ok */
-
 		if (midQ->resp_buf && 
 			(midQ->midState == MID_RESPONSE_RECEIVED)) {
-			in_buf->smb_buf_length = receive_len;
-			/* BB verify that length would not overrun small buf */
-			memcpy((char *)in_buf + 4,
-			       (char *)midQ->resp_buf + 4,
-			       receive_len);
 
-			dump_smb(in_buf, 80);
+			iov[0].iov_base = (char *)midQ->resp_buf;
+			if(midQ->largeBuf)
+				*pRespBufType = CIFS_LARGE_BUFFER;
+			else
+				*pRespBufType = CIFS_SMALL_BUFFER;
+			iov[0].iov_len = receive_len + 4;
+			iov[1].iov_len = 0;
+
+			dump_smb(midQ->resp_buf, 80);
 			/* convert the length into a more usable form */
 			if((receive_len > 24) &&
 			   (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
 					SECMODE_SIGN_ENABLED))) {
-				rc = cifs_verify_signature(in_buf,
+				rc = cifs_verify_signature(midQ->resp_buf,
 						ses->server->mac_signing_key,
 						midQ->sequence_number+1);
 				if(rc) {
@@ -512,18 +514,19 @@
 				}
 			}
 
-			*pbytes_returned = in_buf->smb_buf_length;
-
 			/* BB special case reconnect tid and uid here? */
 			/* BB special case Errbadpassword and pwdexpired here */
-			rc = map_smb_to_linux_error(in_buf);
+			rc = map_smb_to_linux_error(midQ->resp_buf);
 
 			/* convert ByteCount if necessary */
 			if (receive_len >=
 			    sizeof (struct smb_hdr) -
 			    4 /* do not count RFC1001 header */  +
-			    (2 * in_buf->WordCount) + 2 /* bcc */ )
-				BCC(in_buf) = le16_to_cpu(BCC_LE(in_buf));
+			    (2 * midQ->resp_buf->WordCount) + 2 /* bcc */ )
+				BCC(midQ->resp_buf) = 
+					le16_to_cpu(BCC_LE(midQ->resp_buf));
+			midQ->resp_buf = NULL;  /* mark it so will not be freed
+						by DeleteMidQEntry */
 		} else {
 			rc = -EIO;
 			cFYI(1,("Bad MID state?"));
@@ -549,7 +552,6 @@
 
 	return rc;
 }
-#endif /* CIFS_EXPERIMENTAL */
 
 int
 SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
@@ -790,7 +792,7 @@
 				BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
 		} else {
 			rc = -EIO;
-			cERROR(1,("Bad MID state? "));
+			cERROR(1,("Bad MID state?"));
 		}
 	}
 cifs_no_response_exit:
diff -urN oldtree/fs/cifs/xattr.c newtree/fs/cifs/xattr.c
--- oldtree/fs/cifs/xattr.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/cifs/xattr.c	2006-02-13 19:20:00.696406432 -0500
@@ -254,7 +254,8 @@
 		rc = CIFSSMBQueryEA(xid,pTcon,full_path,ea_name,ea_value,
 			buf_size, cifs_sb->local_nls,
 			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
-	} else if(strncmp(ea_name,POSIX_ACL_XATTR_ACCESS,strlen(POSIX_ACL_XATTR_ACCESS)) == 0) {
+	} else if(strncmp(ea_name,POSIX_ACL_XATTR_ACCESS,
+			  strlen(POSIX_ACL_XATTR_ACCESS)) == 0) {
 #ifdef CONFIG_CIFS_POSIX
 		if(sb->s_flags & MS_POSIXACL)
 			rc = CIFSSMBGetPosixACL(xid, pTcon, full_path,
@@ -262,10 +263,27 @@
 				cifs_sb->local_nls,
 				cifs_sb->mnt_cifs_flags & 
 					CIFS_MOUNT_MAP_SPECIAL_CHR);
+/*		else if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
+			__u16 fid;
+			int oplock = FALSE;
+			rc = CIFSSMBOpen(xid, pTcon, full_path,
+					 FILE_OPEN, GENERIC_READ, 0, &fid,
+					 &oplock, NULL, cifs_sb->local_nls,
+					 cifs_sb->mnt_cifs_flags &
+					 CIFS_MOUNT_MAP_SPECIAL_CHR);
+			if(rc == 0) {
+				rc = CIFSSMBGetCIFSACL(xid, pTcon, fid,
+					ea_value, buf_size,
+					ACL_TYPE_ACCESS);
+				CIFSSMBClose(xid, pTcon, fid)
+			}
+		} */  /* BB enable after fixing up return data */
+                  		
 #else 
 		cFYI(1,("query POSIX ACL not supported yet"));
 #endif /* CONFIG_CIFS_POSIX */
-	} else if(strncmp(ea_name,POSIX_ACL_XATTR_DEFAULT,strlen(POSIX_ACL_XATTR_DEFAULT)) == 0) {
+	} else if(strncmp(ea_name,POSIX_ACL_XATTR_DEFAULT,
+			  strlen(POSIX_ACL_XATTR_DEFAULT)) == 0) {
 #ifdef CONFIG_CIFS_POSIX
 		if(sb->s_flags & MS_POSIXACL)
 			rc = CIFSSMBGetPosixACL(xid, pTcon, full_path,
diff -urN oldtree/fs/dcache.c newtree/fs/dcache.c
--- oldtree/fs/dcache.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/dcache.c	2006-02-13 19:20:29.731992352 -0500
@@ -808,10 +808,14 @@
  *
  * Fill in inode information in the entry. On success, it returns NULL.
  * If an unhashed alias of "entry" already exists, then we return the
- * aliased dentry instead.
+ * aliased dentry instead and drop one reference to inode.
  *
  * Note that in order to avoid conflicts with rename() etc, the caller
  * had better be holding the parent directory semaphore.
+ *
+ * This also assumes that the inode count has been incremented
+ * (or otherwise set) by the caller to indicate that it is now
+ * in use by the dcache.
  */
 struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode)
 {
@@ -838,6 +842,7 @@
 		dget_locked(alias);
 		spin_unlock(&dcache_lock);
 		BUG_ON(!d_unhashed(alias));
+		iput(inode);
 		return alias;
 	}
 	list_add(&entry->d_alias, &inode->i_dentry);
diff -urN oldtree/fs/ext2/namei.c newtree/fs/ext2/namei.c
--- oldtree/fs/ext2/namei.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/ext2/namei.c	2006-02-13 19:20:00.696406432 -0500
@@ -83,10 +83,7 @@
 		if (!inode)
 			return ERR_PTR(-EACCES);
 	}
-	if (inode)
-		return d_splice_alias(inode, dentry);
-	d_add(dentry, inode);
-	return NULL;
+	return d_splice_alias(inode, dentry);
 }
 
 struct dentry *ext2_get_parent(struct dentry *child)
diff -urN oldtree/fs/ext3/balloc.c newtree/fs/ext3/balloc.c
--- oldtree/fs/ext3/balloc.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/ext3/balloc.c	2006-02-13 19:20:00.697406280 -0500
@@ -654,9 +654,11 @@
  */
 static int
 ext3_try_to_allocate(struct super_block *sb, handle_t *handle, int group,
-	struct buffer_head *bitmap_bh, int goal, struct ext3_reserve_window *my_rsv)
+			struct buffer_head *bitmap_bh, int goal,
+			unsigned long *count, struct ext3_reserve_window *my_rsv)
 {
 	int group_first_block, start, end;
+	unsigned long num = 0;
 
 	/* we do allocation within the reservation window if we have a window */
 	if (my_rsv) {
@@ -714,8 +716,18 @@
 			goto fail_access;
 		goto repeat;
 	}
-	return goal;
+	num++;
+	goal++;
+	while (num < *count && goal < end
+		&& ext3_test_allocatable(goal, bitmap_bh)
+		&& claim_block(sb_bgl_lock(EXT3_SB(sb), group), goal, bitmap_bh)) {
+		num++;
+		goal++;
+	}
+	*count = num;
+	return goal - num;
 fail_access:
+	*count = num;
 	return -1;
 }
 
@@ -1000,6 +1012,31 @@
 	goto retry;
 }
 
+static void try_to_extend_reservation(struct ext3_reserve_window_node *my_rsv,
+			struct super_block *sb, int size)
+{
+	struct ext3_reserve_window_node *next_rsv;
+	struct rb_node *next;
+	spinlock_t *rsv_lock = &EXT3_SB(sb)->s_rsv_window_lock;
+
+	if (!spin_trylock(rsv_lock))
+		return;
+
+	next = rb_next(&my_rsv->rsv_node);
+
+	if (!next)
+		my_rsv->rsv_end += size;
+	else {
+		next_rsv = list_entry(next, struct ext3_reserve_window_node, rsv_node);
+
+		if ((next_rsv->rsv_start - my_rsv->rsv_end - 1) >= size)
+			my_rsv->rsv_end += size;
+		else
+			my_rsv->rsv_end = next_rsv->rsv_start - 1;
+	}
+	spin_unlock(rsv_lock);
+}
+
 /*
  * This is the main function used to allocate a new block and its reservation
  * window.
@@ -1025,11 +1062,12 @@
 ext3_try_to_allocate_with_rsv(struct super_block *sb, handle_t *handle,
 			unsigned int group, struct buffer_head *bitmap_bh,
 			int goal, struct ext3_reserve_window_node * my_rsv,
-			int *errp)
+			unsigned long *count, int *errp)
 {
 	unsigned long group_first_block;
 	int ret = 0;
 	int fatal;
+	unsigned long num = *count;
 
 	*errp = 0;
 
@@ -1052,7 +1090,8 @@
 	 * or last attempt to allocate a block with reservation turned on failed
 	 */
 	if (my_rsv == NULL ) {
-		ret = ext3_try_to_allocate(sb, handle, group, bitmap_bh, goal, NULL);
+		ret = ext3_try_to_allocate(sb, handle, group, bitmap_bh,
+						goal, count, NULL);
 		goto out;
 	}
 	/*
@@ -1082,6 +1121,8 @@
 	while (1) {
 		if (rsv_is_empty(&my_rsv->rsv_window) || (ret < 0) ||
 			!goal_in_my_reservation(&my_rsv->rsv_window, goal, group, sb)) {
+			if (my_rsv->rsv_goal_size < *count)
+				my_rsv->rsv_goal_size = *count;
 			ret = alloc_new_reservation(my_rsv, goal, sb,
 							group, bitmap_bh);
 			if (ret < 0)
@@ -1089,16 +1130,21 @@
 
 			if (!goal_in_my_reservation(&my_rsv->rsv_window, goal, group, sb))
 				goal = -1;
-		}
+		} else if (goal > 0 && (my_rsv->rsv_end-goal+1) < *count)
+			try_to_extend_reservation(my_rsv, sb,
+					*count-my_rsv->rsv_end + goal - 1);
+
 		if ((my_rsv->rsv_start >= group_first_block + EXT3_BLOCKS_PER_GROUP(sb))
 		    || (my_rsv->rsv_end < group_first_block))
 			BUG();
 		ret = ext3_try_to_allocate(sb, handle, group, bitmap_bh, goal,
-					   &my_rsv->rsv_window);
+					   &num, &my_rsv->rsv_window);
 		if (ret >= 0) {
-			my_rsv->rsv_alloc_hit++;
+			my_rsv->rsv_alloc_hit += num;
+			*count = num;
 			break;				/* succeed */
 		}
+		num = *count;
 	}
 out:
 	if (ret >= 0) {
@@ -1155,8 +1201,8 @@
  * bitmap, and then for any free bit if that fails.
  * This function also updates quota and i_blocks field.
  */
-int ext3_new_block(handle_t *handle, struct inode *inode,
-			unsigned long goal, int *errp)
+int ext3_new_blocks(handle_t *handle, struct inode *inode,
+			unsigned long goal, unsigned long *count, int *errp)
 {
 	struct buffer_head *bitmap_bh = NULL;
 	struct buffer_head *gdp_bh;
@@ -1179,6 +1225,7 @@
 	static int goal_hits, goal_attempts;
 #endif
 	unsigned long ngroups;
+	unsigned long num = *count;
 
 	*errp = -ENOSPC;
 	sb = inode->i_sb;
@@ -1190,7 +1237,7 @@
 	/*
 	 * Check quota for allocation of this block.
 	 */
-	if (DQUOT_ALLOC_BLOCK(inode, 1)) {
+	if (DQUOT_ALLOC_BLOCK(inode, num)) {
 		*errp = -EDQUOT;
 		return 0;
 	}
@@ -1245,7 +1292,7 @@
 		if (!bitmap_bh)
 			goto io_error;
 		ret_block = ext3_try_to_allocate_with_rsv(sb, handle, group_no,
-					bitmap_bh, ret_block, my_rsv, &fatal);
+					bitmap_bh, ret_block, my_rsv, &num, &fatal);
 		if (fatal)
 			goto out;
 		if (ret_block >= 0)
@@ -1282,7 +1329,7 @@
 		if (!bitmap_bh)
 			goto io_error;
 		ret_block = ext3_try_to_allocate_with_rsv(sb, handle, group_no,
-					bitmap_bh, -1, my_rsv, &fatal);
+					bitmap_bh, -1, my_rsv, &num, &fatal);
 		if (fatal)
 			goto out;
 		if (ret_block >= 0) 
@@ -1317,13 +1364,15 @@
 	target_block = ret_block + group_no * EXT3_BLOCKS_PER_GROUP(sb)
 				+ le32_to_cpu(es->s_first_data_block);
 
-	if (target_block == le32_to_cpu(gdp->bg_block_bitmap) ||
-	    target_block == le32_to_cpu(gdp->bg_inode_bitmap) ||
+	if (in_range(le32_to_cpu(gdp->bg_block_bitmap), target_block, num) ||
+	    in_range(le32_to_cpu(gdp->bg_inode_bitmap), target_block, num) ||
 	    in_range(target_block, le32_to_cpu(gdp->bg_inode_table),
+		      EXT3_SB(sb)->s_itb_per_group) ||
+	    in_range(target_block + num - 1, le32_to_cpu(gdp->bg_inode_table),
 		      EXT3_SB(sb)->s_itb_per_group))
 		ext3_error(sb, "ext3_new_block",
 			    "Allocating block in system zone - "
-			    "block = %u", target_block);
+			    "blocks from %u, length %lu", target_block, num);
 
 	performed_allocation = 1;
 
@@ -1342,10 +1391,14 @@
 	jbd_lock_bh_state(bitmap_bh);
 	spin_lock(sb_bgl_lock(sbi, group_no));
 	if (buffer_jbd(bitmap_bh) && bh2jh(bitmap_bh)->b_committed_data) {
-		if (ext3_test_bit(ret_block,
-				bh2jh(bitmap_bh)->b_committed_data)) {
-			printk("%s: block was unexpectedly set in "
-				"b_committed_data\n", __FUNCTION__);
+		int i;
+
+		for (i = 0; i < num; i++) {
+			if (ext3_test_bit(ret_block,
+					bh2jh(bitmap_bh)->b_committed_data)) {
+				printk("%s: block was unexpectedly set in "
+					"b_committed_data\n", __FUNCTION__);
+			}
 		}
 	}
 	ext3_debug("found bit %d\n", ret_block);
@@ -1356,7 +1409,7 @@
 	/* ret_block was blockgroup-relative.  Now it becomes fs-relative */
 	ret_block = target_block;
 
-	if (ret_block >= le32_to_cpu(es->s_blocks_count)) {
+	if (ret_block + num - 1 >= le32_to_cpu(es->s_blocks_count)) {
 		ext3_error(sb, "ext3_new_block",
 			    "block(%d) >= blocks count(%d) - "
 			    "block_group = %d, es == %p ", ret_block,
@@ -1374,9 +1427,9 @@
 
 	spin_lock(sb_bgl_lock(sbi, group_no));
 	gdp->bg_free_blocks_count =
-			cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count) - 1);
+			cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count) - num);
 	spin_unlock(sb_bgl_lock(sbi, group_no));
-	percpu_counter_mod(&sbi->s_freeblocks_counter, -1);
+	percpu_counter_mod(&sbi->s_freeblocks_counter, -num);
 
 	BUFFER_TRACE(gdp_bh, "journal_dirty_metadata for group descriptor");
 	err = ext3_journal_dirty_metadata(handle, gdp_bh);
@@ -1389,6 +1442,8 @@
 
 	*errp = 0;
 	brelse(bitmap_bh);
+	*count = num;
+	DQUOT_FREE_BLOCK(inode, *count-num);
 	return ret_block;
 
 io_error:
@@ -1402,11 +1457,19 @@
 	 * Undo the block allocation
 	 */
 	if (!performed_allocation)
-		DQUOT_FREE_BLOCK(inode, 1);
+		DQUOT_FREE_BLOCK(inode, *count);
 	brelse(bitmap_bh);
 	return 0;
 }
 
+int ext3_new_block(handle_t *handle, struct inode *inode,
+			unsigned long goal, int *errp)
+{
+	unsigned long count = 1;
+
+	return ext3_new_blocks(handle, inode, goal, &count, errp);
+}
+
 unsigned long ext3_count_free_blocks(struct super_block *sb)
 {
 	unsigned long desc_count;
diff -urN oldtree/fs/ext3/dir.c newtree/fs/ext3/dir.c
--- oldtree/fs/ext3/dir.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/ext3/dir.c	2006-02-13 19:20:00.698406128 -0500
@@ -95,11 +95,10 @@
 			 void * dirent, filldir_t filldir)
 {
 	int error = 0;
-	unsigned long offset, blk;
-	int i, num, stored;
-	struct buffer_head * bh, * tmp, * bha[16];
-	struct ext3_dir_entry_2 * de;
-	struct super_block * sb;
+	unsigned long offset;
+	int i, stored;
+	struct ext3_dir_entry_2 *de;
+	struct super_block *sb;
 	int err;
 	struct inode *inode = filp->f_dentry->d_inode;
 	int ret = 0;
@@ -124,12 +123,30 @@
 	}
 #endif
 	stored = 0;
-	bh = NULL;
 	offset = filp->f_pos & (sb->s_blocksize - 1);
 
 	while (!error && !stored && filp->f_pos < inode->i_size) {
-		blk = (filp->f_pos) >> EXT3_BLOCK_SIZE_BITS(sb);
-		bh = ext3_bread(NULL, inode, blk, 0, &err);
+		unsigned long blk = filp->f_pos >> EXT3_BLOCK_SIZE_BITS(sb);
+		struct buffer_head map_bh;
+		struct buffer_head *bh = NULL;
+
+		map_bh.b_state = 0;
+		err = ext3_get_blocks_handle(NULL, inode, blk, 1,
+						&map_bh, 0, 0);
+		if (err > 0) {
+			page_cache_readahead(sb->s_bdev->bd_inode->i_mapping,
+				&filp->f_ra,
+				filp,
+				map_bh.b_blocknr >>
+					(PAGE_CACHE_SHIFT - inode->i_blkbits),
+				1);
+			bh = ext3_bread(NULL, inode, blk, 0, &err);
+		}
+
+		/*
+		 * We ignore I/O errors on directories so users have a chance
+		 * of recovering data when there's a bad sector
+		 */
 		if (!bh) {
 			ext3_error (sb, "ext3_readdir",
 				"directory #%lu contains a hole at offset %lu",
@@ -138,26 +155,6 @@
 			continue;
 		}
 
-		/*
-		 * Do the readahead
-		 */
-		if (!offset) {
-			for (i = 16 >> (EXT3_BLOCK_SIZE_BITS(sb) - 9), num = 0;
-			     i > 0; i--) {
-				tmp = ext3_getblk (NULL, inode, ++blk, 0, &err);
-				if (tmp && !buffer_uptodate(tmp) &&
-						!buffer_locked(tmp))
-					bha[num++] = tmp;
-				else
-					brelse (tmp);
-			}
-			if (num) {
-				ll_rw_block (READA, num, bha);
-				for (i = 0; i < num; i++)
-					brelse (bha[i]);
-			}
-		}
-
 revalidate:
 		/* If the dir block has changed since the last call to
 		 * readdir(2), then we might be pointing to an invalid
diff -urN oldtree/fs/ext3/inode.c newtree/fs/ext3/inode.c
--- oldtree/fs/ext3/inode.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/ext3/inode.c	2006-02-13 19:20:00.700405824 -0500
@@ -235,16 +235,6 @@
 	clear_inode(inode);	/* We must guarantee clearing of inode... */
 }
 
-static int ext3_alloc_block (handle_t *handle,
-			struct inode * inode, unsigned long goal, int *err)
-{
-	unsigned long result;
-
-	result = ext3_new_block(handle, inode, goal, err);
-	return result;
-}
-
-
 typedef struct {
 	__le32	*p;
 	__le32	key;
@@ -330,7 +320,7 @@
 		ext3_warning (inode->i_sb, "ext3_block_to_path", "block > big");
 	}
 	if (boundary)
-		*boundary = (i_block & (ptrs - 1)) == (final - 1);
+		*boundary = final - 1 - (i_block & (ptrs - 1));
 	return n;
 }
 
@@ -476,15 +466,115 @@
 
 	return ext3_find_near(inode, partial);
 }
+/**
+ *	ext3_blks_to_allocate: Look up the block map and count the number
+ *	of direct blocks need to be allocated for the given branch.
+ *
+ * 	@branch: chain of indirect blocks
+ *	@k: number of blocks need for indirect blocks
+ *	@blks: number of data blocks to be mapped.
+ *	@blocks_to_boundary:  the offset in the indirect block
+ *
+ *	return the total number of blocks to be allocate, including the
+ *	direct and indirect blocks.
+ */
+static int
+ext3_blks_to_allocate(Indirect * branch, int k, unsigned long blks,
+		int blocks_to_boundary)
+{
+	unsigned long count = 0;
+
+	/*
+	 * Simple case, [t,d]Indirect block(s) has not allocated yet
+	 * then it's clear blocks on that path have not allocated
+	 */
+	if (k > 0) {
+		/* right now don't hanel cross boundary allocation */
+		if (blks < blocks_to_boundary + 1)
+			count += blks;
+		else
+			count += blocks_to_boundary + 1;
+		return count;
+	}
+
+	count++;
+	while (count < blks && count <= blocks_to_boundary
+		&& le32_to_cpu(*(branch[0].p + count)) == 0) {
+		count++;
+	}
+	return count;
+}
+/**
+ *	ext3_alloc_blocks: multiple allocate blocks needed for a branch
+ *	@indirect_blks: the number of blocks need to allocate for indirect
+ *			blocks
+ *
+ *	@new_blocks: on return it will store the new block numbers for
+ *	the indirect blocks(if needed) and the first direct block,
+ *	@blks:	on return it will store the total number of allocated
+ *		direct blocks
+ */
+static int ext3_alloc_blocks(handle_t *handle, struct inode *inode,
+			unsigned long goal, int indirect_blks, int blks,
+			unsigned long long new_blocks[4], int *err)
+{
+	int target;
+	unsigned long count;
+	int index, i;
+	unsigned long current_block = 0;
+	int ret = 0;
+
+	/*
+	 * Here we try to allocate the requested multiple blocks at once,
+	 * on a best-effort basis.
+	 * To build a branch, we should allocate blocks for
+	 * the indirect blocks(if not allocated yet), and at least
+	 * the first direct block of this branch.  That's the
+	 * minimum number of blocks need to allocate(required)
+	 *
+	 */
+	target = blks + indirect_blks;
+	index = 0; count = 0;
+
+	while (1) {
+		count = target;
+		/* allocating blocks for indirect blocks and direct blocks */
+		current_block = ext3_new_blocks(handle, inode, goal, &count, err);
+		if (*err)
+			goto failed_out;
+
+		target -= count;
+		/* allocate blocks for indirect blocks */
+		while (index < indirect_blks && count) {
+			new_blocks[index++] = current_block++;
+			count--;
+		}
+
+		if (count > 0)
+			break;
+	}
+
+	/* save the new block number for the first direct block*/
+	new_blocks[index] = current_block;
+	/* total number of blocks allocated for direct blocks */
+	ret = count;
+	*err = 0;
+	return ret;
+failed_out:
+	for (i = 0; i <index; i++)
+		ext3_free_blocks(handle, inode, new_blocks[i], 1);
+	return ret;
+}
 
 /**
  *	ext3_alloc_branch - allocate and set up a chain of blocks.
  *	@inode: owner
- *	@num: depth of the chain (number of blocks to allocate)
+ *	@indirect_blks: number of allocated indirect blocks
+ *	@blks: number of allocated direct blocks
  *	@offsets: offsets (in the blocks) to store the pointers to next.
  *	@branch: place to store the chain in.
  *
- *	This function allocates @num blocks, zeroes out all but the last one,
+ *	This function allocates blocks, zeroes out all but the last one,
  *	links them into chain and (if we are synchronous) writes them to disk.
  *	In other words, it prepares a branch that can be spliced onto the
  *	inode. It stores the information about that chain in the branch[], in
@@ -503,71 +593,79 @@
  */
 
 static int ext3_alloc_branch(handle_t *handle, struct inode *inode,
-			     int num,
-			     unsigned long goal,
-			     int *offsets,
-			     Indirect *branch)
+			int indirect_blks, int *blks, unsigned long goal,
+			int *offsets, Indirect *branch)
 {
 	int blocksize = inode->i_sb->s_blocksize;
-	int n = 0, keys = 0;
+	int i, n = 0;
 	int err = 0;
-	int i;
-	int parent = ext3_alloc_block(handle, inode, goal, &err);
-
-	branch[0].key = cpu_to_le32(parent);
-	if (parent) {
-		for (n = 1; n < num; n++) {
-			struct buffer_head *bh;
-			/* Allocate the next block */
-			int nr = ext3_alloc_block(handle, inode, parent, &err);
-			if (!nr)
-				break;
-			branch[n].key = cpu_to_le32(nr);
+	struct buffer_head *bh;
+	int num;
+	unsigned long long new_blocks[4];
+	unsigned long long current_block;
 
-			/*
-			 * Get buffer_head for parent block, zero it out
-			 * and set the pointer to new one, then send
-			 * parent to disk.  
-			 */
-			bh = sb_getblk(inode->i_sb, parent);
-			if (!bh)
-				break;
-			keys = n+1;
-			branch[n].bh = bh;
-			lock_buffer(bh);
-			BUFFER_TRACE(bh, "call get_create_access");
-			err = ext3_journal_get_create_access(handle, bh);
-			if (err) {
-				unlock_buffer(bh);
-				brelse(bh);
-				break;
-			}
+	num = ext3_alloc_blocks(handle, inode, goal, indirect_blks,
+					*blks, new_blocks, &err);
+	if (err)
+		return err;
 
-			memset(bh->b_data, 0, blocksize);
-			branch[n].p = (__le32*) bh->b_data + offsets[n];
-			*branch[n].p = branch[n].key;
-			BUFFER_TRACE(bh, "marking uptodate");
-			set_buffer_uptodate(bh);
+	branch[0].key = cpu_to_le32(new_blocks[0]);
+	/*
+	 * metadata blocks and data blocks are allocated.
+	 */
+	for (n = 1; n <= indirect_blks;  n++) {
+		/*
+		 * Get buffer_head for parent block, zero it out
+		 * and set the pointer to new one, then send
+		 * parent to disk.
+		 */
+		bh = sb_getblk(inode->i_sb, new_blocks[n-1]);
+		branch[n].bh = bh;
+		lock_buffer(bh);
+		BUFFER_TRACE(bh, "call get_create_access");
+		err = ext3_journal_get_create_access(handle, bh);
+		if (err) {
 			unlock_buffer(bh);
+			brelse(bh);
+			goto failed;
+		}
 
-			BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
-			err = ext3_journal_dirty_metadata(handle, bh);
-			if (err)
-				break;
-
-			parent = nr;
+		memset(bh->b_data, 0, blocksize);
+		branch[n].p = (__le32*) bh->b_data + offsets[n];
+		branch[n].key = cpu_to_le32(new_blocks[n]);
+		*branch[n].p = branch[n].key;
+		if ( n == indirect_blks) {
+			current_block = new_blocks[n];
+			/*
+			 * End of chain, update the last new metablock of
+			 * the chain to point to the new allocated
+			 * data blocks numbers
+			 */
+			for (i=1; i < num; i++)
+				*(branch[n].p + i) = cpu_to_le32(++current_block);
 		}
-	}
-	if (n == num)
-		return 0;
+		BUFFER_TRACE(bh, "marking uptodate");
+		set_buffer_uptodate(bh);
+		unlock_buffer(bh);
 
+		BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
+		err = ext3_journal_dirty_metadata(handle, bh);
+		if (err)
+			goto failed;
+	}
+	*blks = num;
+	return err;
+failed:
 	/* Allocation failed, free what we already allocated */
-	for (i = 1; i < keys; i++) {
+	for (i = 1; i <= n ; i++) {
 		BUFFER_TRACE(branch[i].bh, "call journal_forget");
 		ext3_journal_forget(handle, branch[i].bh);
 	}
-	for (i = 0; i < keys; i++)
-		ext3_free_blocks(handle, inode, le32_to_cpu(branch[i].key), 1);
+	for (i = 0; i <indirect_blks; i++)
+		ext3_free_blocks(handle, inode, new_blocks[i], 1);
+
+	ext3_free_blocks(handle, inode, new_blocks[i], num);
+
 	return err;
 }
 
@@ -578,7 +676,8 @@
  *	@chain: chain of indirect blocks (with a missing link - see
  *		ext3_alloc_branch)
  *	@where: location of missing link
- *	@num:   number of blocks we are adding
+ *	@num:   number of indirect blocks we are adding
+ *	@blks:  number of direct blocks we are adding
  *
  *	This function fills the missing link and does all housekeeping needed in
  *	inode (->i_blocks, etc.). In case of success we end up with the full
@@ -586,12 +685,12 @@
  */
 
 static int ext3_splice_branch(handle_t *handle, struct inode *inode, long block,
-			      Indirect chain[4], Indirect *where, int num)
+			      Indirect *where, int num, int blks)
 {
 	int i;
 	int err = 0;
 	struct ext3_block_alloc_info *block_i = EXT3_I(inode)->i_block_alloc_info;
-
+	unsigned long current_block;
 	/*
 	 * If we're splicing into a [td]indirect block (as opposed to the
 	 * inode) then we need to get write access to the [td]indirect block
@@ -606,6 +705,13 @@
 	/* That's it */
 
 	*where->p = where->key;
+	/* update host bufferhead or inode to point to
+	 * more just allocated direct blocks blocks */
+	if (num == 0 && blks > 1) {
+		current_block = le32_to_cpu(where->key + 1);
+		for (i = 1; i < blks; i++)
+			*(where->p + i ) = cpu_to_le32(current_block++);
+	}
 
 	/*
 	 * update the most recently allocated logical & physical block
@@ -613,8 +719,8 @@
 	 * allocation
 	 */
 	if (block_i) {
-		block_i->last_alloc_logical_block = block;
-		block_i->last_alloc_physical_block = le32_to_cpu(where[num-1].key);
+		block_i->last_alloc_logical_block = block + blks - 1;
+		block_i->last_alloc_physical_block = le32_to_cpu(where[num].key + blks - 1);
 	}
 
 	/* We are done with atomic stuff, now do the rest of housekeeping */
@@ -647,10 +753,13 @@
 	return err;
 
 err_out:
-	for (i = 1; i < num; i++) {
+	for (i = 1; i <= num; i++) {
 		BUFFER_TRACE(where[i].bh, "call journal_forget");
 		ext3_journal_forget(handle, where[i].bh);
+		ext3_free_blocks(handle, inode, le32_to_cpu(where[i-1].key), 1);
 	}
+	ext3_free_blocks(handle, inode, le32_to_cpu(where[num].key), blks);
+
 	return err;
 }
 
@@ -669,23 +778,30 @@
  * akpm: `handle' can be NULL if create == 0.
  *
  * The BKL may not be held on entry here.  Be sure to take it early.
+ * return > 0, # of blocks mapped or allocated
+ * return < 0, error case
  */
 
-static int
-ext3_get_block_handle(handle_t *handle, struct inode *inode, sector_t iblock,
-		struct buffer_head *bh_result, int create, int extend_disksize)
+int
+ext3_get_blocks_handle(handle_t *handle, struct inode *inode, sector_t iblock,
+		unsigned long maxblocks, struct buffer_head *bh_result,
+		int create, int extend_disksize)
 {
 	int err = -EIO;
 	int offsets[4];
 	Indirect chain[4];
 	Indirect *partial;
 	unsigned long goal;
-	int left;
-	int boundary = 0;
-	const int depth = ext3_block_to_path(inode, iblock, offsets, &boundary);
+	int indirect_blks;
+	int blocks_to_boundary = 0;
+	int depth;
 	struct ext3_inode_info *ei = EXT3_I(inode);
+	int count = 0;
+	unsigned long first_block = 0;
+
 
 	J_ASSERT(handle != NULL || create == 0);
+	depth = ext3_block_to_path(inode, iblock, offsets, &blocks_to_boundary);
 
 	if (depth == 0)
 		goto out;
@@ -694,7 +810,29 @@
 
 	/* Simplest case - block found, no allocation needed */
 	if (!partial) {
+		first_block = chain[depth - 1].key;
 		clear_buffer_new(bh_result);
+		count++;
+		/*map more blocks*/
+		while (count < maxblocks && count <= blocks_to_boundary) {
+			if (!verify_chain(chain, partial)) {
+				/*
+				 * Indirect block might be removed by
+				 * truncate while we were reading it.
+				 * Handling of that case: forget what we've
+				 * got now. Flag the err as EAGAIN, so it
+				 * will reread.
+				 */
+				err = -EAGAIN;
+				count = 0;
+				break;
+			}
+			if (le32_to_cpu(*(chain[depth-1].p+count) ==
+					(first_block + count)))
+				count++;
+			else
+				break;
+		}
 		goto got_it;
 	}
 
@@ -723,6 +861,7 @@
 		}
 		partial = ext3_get_branch(inode, depth, offsets, chain, &err);
 		if (!partial) {
+			count++;
 			up(&ei->truncate_sem);
 			if (err)
 				goto cleanup;
@@ -740,12 +879,19 @@
 
 	goal = ext3_find_goal(inode, iblock, chain, partial);
 
-	left = (chain + depth) - partial;
+	/* the number of blocks need to allocate for [d,t]indirect blocks */
+	indirect_blks = (chain + depth) - partial - 1;
 
 	/*
+	 * Next look up the indirect map to count the totoal number of
+	 * direct blocks to allocate for this branch.
+	 */
+	count = ext3_blks_to_allocate(partial, indirect_blks,
+					maxblocks, blocks_to_boundary);
+	/*
 	 * Block out ext3_truncate while we alter the tree
 	 */
-	err = ext3_alloc_branch(handle, inode, left, goal,
+	err = ext3_alloc_branch(handle, inode, indirect_blks, &count, goal,
 				offsets + (partial - chain), partial);
 
 	/*
@@ -756,8 +902,8 @@
 	 * may need to return -EAGAIN upwards in the worst case.  --sct
 	 */
 	if (!err)
-		err = ext3_splice_branch(handle, inode, iblock, chain,
-					 partial, left);
+		err = ext3_splice_branch(handle, inode, iblock,
+					partial, indirect_blks, count);
 	/*
 	 * i_disksize growing is protected by truncate_sem.  Don't forget to
 	 * protect it if you're about to implement concurrent
@@ -772,8 +918,9 @@
 	set_buffer_new(bh_result);
 got_it:
 	map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key));
-	if (boundary)
+	if (blocks_to_boundary == 0)
 		set_buffer_boundary(bh_result);
+	err = count;
 	/* Clean up and exit */
 	partial = chain + depth - 1;	/* the whole chain */
 cleanup:
@@ -787,21 +934,6 @@
 	return err;
 }
 
-static int ext3_get_block(struct inode *inode, sector_t iblock,
-			struct buffer_head *bh_result, int create)
-{
-	handle_t *handle = NULL;
-	int ret;
-
-	if (create) {
-		handle = ext3_journal_current_handle();
-		J_ASSERT(handle != 0);
-	}
-	ret = ext3_get_block_handle(handle, inode, iblock,
-				bh_result, create, 1);
-	return ret;
-}
-
 #define DIO_CREDITS (EXT3_RESERVE_TRANS_BLOCKS + 32)
 
 static int
@@ -815,6 +947,9 @@
 	if (!handle)
 		goto get_block;		/* A read */
 
+	if (max_blocks == 1)
+		goto get_block;		/* A single block get */
+
 	if (handle->h_transaction->t_state == T_LOCKED) {
 		/*
 		 * Huge direct-io writes can hold off commits for long
@@ -841,13 +976,31 @@
 	}
 
 get_block:
-	if (ret == 0)
-		ret = ext3_get_block_handle(handle, inode, iblock,
-					bh_result, create, 0);
-	bh_result->b_size = (1 << inode->i_blkbits);
+	if (ret == 0) {
+		ret = ext3_get_blocks_handle(handle, inode, iblock,
+					max_blocks, bh_result, create, 0);
+		if (ret > 0) {
+			bh_result->b_size = (ret << inode->i_blkbits);
+			ret = 0;
+		}
+	}
 	return ret;
 }
 
+static int ext3_get_blocks(struct inode *inode, sector_t iblock,
+		unsigned long maxblocks, struct buffer_head *bh_result,
+		int create)
+{
+	return ext3_direct_io_get_blocks(inode, iblock, maxblocks,
+					bh_result, create);
+}
+
+static int ext3_get_block(struct inode *inode, sector_t iblock,
+			struct buffer_head *bh_result, int create)
+{
+	return ext3_get_blocks(inode, iblock, 1, bh_result, create);
+}
+
 /*
  * `handle' can be NULL if create is zero
  */
@@ -862,8 +1015,8 @@
 	dummy.b_state = 0;
 	dummy.b_blocknr = -1000;
 	buffer_trace_init(&dummy.b_history);
-	*errp = ext3_get_block_handle(handle, inode, block, &dummy, create, 1);
-	if (!*errp && buffer_mapped(&dummy)) {
+	*errp = ext3_get_blocks_handle(handle, inode, block, 1, &dummy, create, 1);
+	if ((*errp == 1 ) && buffer_mapped(&dummy)) {
 		struct buffer_head *bh;
 		bh = sb_getblk(inode->i_sb, dummy.b_blocknr);
 		if (!bh) {
diff -urN oldtree/fs/fcntl.c newtree/fs/fcntl.c
--- oldtree/fs/fcntl.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/fcntl.c	2006-02-13 19:20:00.700405824 -0500
@@ -72,8 +72,8 @@
 	 * orig_start..fdt->next_fd
 	 */
 	start = orig_start;
-	if (start < fdt->next_fd)
-		start = fdt->next_fd;
+	if (start < files->next_fd)
+		start = files->next_fd;
 
 	newfd = start;
 	if (start < fdt->max_fdset) {
@@ -101,9 +101,8 @@
 	 * we reacquire the fdtable pointer and use it while holding
 	 * the lock, no one can free it during that time.
 	 */
-	fdt = files_fdtable(files);
-	if (start <= fdt->next_fd)
-		fdt->next_fd = newfd + 1;
+	if (start <= files->next_fd)
+		files->next_fd = newfd + 1;
 
 	error = newfd;
 	
diff -urN oldtree/fs/fcntl.c.orig newtree/fs/fcntl.c.orig
--- oldtree/fs/fcntl.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/fcntl.c.orig	2006-02-13 19:20:00.701405672 -0500
@@ -0,0 +1,621 @@
+/*
+ *  linux/fs/fcntl.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#include <linux/syscalls.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/dnotify.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/security.h>
+#include <linux/ptrace.h>
+#include <linux/signal.h>
+#include <linux/rcupdate.h>
+
+#include <asm/poll.h>
+#include <asm/siginfo.h>
+#include <asm/uaccess.h>
+
+void fastcall set_close_on_exec(unsigned int fd, int flag)
+{
+	struct files_struct *files = current->files;
+	struct fdtable *fdt;
+	spin_lock(&files->file_lock);
+	fdt = files_fdtable(files);
+	if (flag)
+		FD_SET(fd, fdt->close_on_exec);
+	else
+		FD_CLR(fd, fdt->close_on_exec);
+	spin_unlock(&files->file_lock);
+}
+
+static inline int get_close_on_exec(unsigned int fd)
+{
+	struct files_struct *files = current->files;
+	struct fdtable *fdt;
+	int res;
+	rcu_read_lock();
+	fdt = files_fdtable(files);
+	res = FD_ISSET(fd, fdt->close_on_exec);
+	rcu_read_unlock();
+	return res;
+}
+
+/*
+ * locate_fd finds a free file descriptor in the open_fds fdset,
+ * expanding the fd arrays if necessary.  Must be called with the
+ * file_lock held for write.
+ */
+
+static int locate_fd(struct files_struct *files, 
+			    struct file *file, unsigned int orig_start)
+{
+	unsigned int newfd;
+	unsigned int start;
+	int error;
+	struct fdtable *fdt;
+
+	error = -EINVAL;
+	if (orig_start >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
+		goto out;
+
+repeat:
+	fdt = files_fdtable(files);
+	/*
+	 * Someone might have closed fd's in the range
+	 * orig_start..fdt->next_fd
+	 */
+	start = orig_start;
+	if (start < fdt->next_fd)
+		start = fdt->next_fd;
+
+	newfd = start;
+	if (start < fdt->max_fdset) {
+		newfd = find_next_zero_bit(fdt->open_fds->fds_bits,
+			fdt->max_fdset, start);
+	}
+	
+	error = -EMFILE;
+	if (newfd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
+		goto out;
+
+	error = expand_files(files, newfd);
+	if (error < 0)
+		goto out;
+
+	/*
+	 * If we needed to expand the fs array we
+	 * might have blocked - try again.
+	 */
+	if (error)
+		goto repeat;
+
+	/*
+	 * We reacquired files_lock, so we are safe as long as
+	 * we reacquire the fdtable pointer and use it while holding
+	 * the lock, no one can free it during that time.
+	 */
+	fdt = files_fdtable(files);
+	if (start <= fdt->next_fd)
+		fdt->next_fd = newfd + 1;
+
+	error = newfd;
+	
+out:
+	return error;
+}
+
+static int dupfd(struct file *file, unsigned int start)
+{
+	struct files_struct * files = current->files;
+	struct fdtable *fdt;
+	int fd;
+
+	spin_lock(&files->file_lock);
+	fd = locate_fd(files, file, start);
+	if (fd >= 0) {
+		/* locate_fd() may have expanded fdtable, load the ptr */
+		fdt = files_fdtable(files);
+		FD_SET(fd, fdt->open_fds);
+		FD_CLR(fd, fdt->close_on_exec);
+		spin_unlock(&files->file_lock);
+		fd_install(fd, file);
+	} else {
+		spin_unlock(&files->file_lock);
+		fput(file);
+	}
+
+	return fd;
+}
+
+asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
+{
+	int err = -EBADF;
+	struct file * file, *tofree;
+	struct files_struct * files = current->files;
+	struct fdtable *fdt;
+
+	spin_lock(&files->file_lock);
+	if (!(file = fcheck(oldfd)))
+		goto out_unlock;
+	err = newfd;
+	if (newfd == oldfd)
+		goto out_unlock;
+	err = -EBADF;
+	if (newfd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
+		goto out_unlock;
+	get_file(file);			/* We are now finished with oldfd */
+
+	err = expand_files(files, newfd);
+	if (err < 0)
+		goto out_fput;
+
+	/* To avoid races with open() and dup(), we will mark the fd as
+	 * in-use in the open-file bitmap throughout the entire dup2()
+	 * process.  This is quite safe: do_close() uses the fd array
+	 * entry, not the bitmap, to decide what work needs to be
+	 * done.  --sct */
+	/* Doesn't work. open() might be there first. --AV */
+
+	/* Yes. It's a race. In user space. Nothing sane to do */
+	err = -EBUSY;
+	fdt = files_fdtable(files);
+	tofree = fdt->fd[newfd];
+	if (!tofree && FD_ISSET(newfd, fdt->open_fds))
+		goto out_fput;
+
+	rcu_assign_pointer(fdt->fd[newfd], file);
+	FD_SET(newfd, fdt->open_fds);
+	FD_CLR(newfd, fdt->close_on_exec);
+	spin_unlock(&files->file_lock);
+
+	if (tofree)
+		filp_close(tofree, files);
+	err = newfd;
+out:
+	return err;
+out_unlock:
+	spin_unlock(&files->file_lock);
+	goto out;
+
+out_fput:
+	spin_unlock(&files->file_lock);
+	fput(file);
+	goto out;
+}
+
+asmlinkage long sys_dup(unsigned int fildes)
+{
+	int ret = -EBADF;
+	struct file * file = fget(fildes);
+
+	if (file)
+		ret = dupfd(file, 0);
+	return ret;
+}
+
+#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | FASYNC | O_DIRECT | O_NOATIME)
+
+static int setfl(int fd, struct file * filp, unsigned long arg)
+{
+	struct inode * inode = filp->f_dentry->d_inode;
+	int error = 0;
+
+	/* O_APPEND cannot be cleared if the file is marked as append-only */
+	if (!(arg & O_APPEND) && IS_APPEND(inode))
+		return -EPERM;
+
+	/* O_NOATIME can only be set by the owner or superuser */
+	if ((arg & O_NOATIME) && !(filp->f_flags & O_NOATIME))
+		if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER))
+			return -EPERM;
+
+	/* required for strict SunOS emulation */
+	if (O_NONBLOCK != O_NDELAY)
+	       if (arg & O_NDELAY)
+		   arg |= O_NONBLOCK;
+
+	if (arg & O_DIRECT) {
+		if (!filp->f_mapping || !filp->f_mapping->a_ops ||
+			!filp->f_mapping->a_ops->direct_IO)
+				return -EINVAL;
+	}
+
+	if (filp->f_op && filp->f_op->check_flags)
+		error = filp->f_op->check_flags(arg);
+	if (error)
+		return error;
+
+	lock_kernel();
+	if ((arg ^ filp->f_flags) & FASYNC) {
+		if (filp->f_op && filp->f_op->fasync) {
+			error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
+			if (error < 0)
+				goto out;
+		}
+	}
+
+	filp->f_flags = (arg & SETFL_MASK) | (filp->f_flags & ~SETFL_MASK);
+ out:
+	unlock_kernel();
+	return error;
+}
+
+static void f_modown(struct file *filp, unsigned long pid,
+                     uid_t uid, uid_t euid, int force)
+{
+	write_lock_irq(&filp->f_owner.lock);
+	if (force || !filp->f_owner.pid) {
+		filp->f_owner.pid = pid;
+		filp->f_owner.uid = uid;
+		filp->f_owner.euid = euid;
+	}
+	write_unlock_irq(&filp->f_owner.lock);
+}
+
+int f_setown(struct file *filp, unsigned long arg, int force)
+{
+	int err;
+	
+	err = security_file_set_fowner(filp);
+	if (err)
+		return err;
+
+	f_modown(filp, arg, current->uid, current->euid, force);
+	return 0;
+}
+
+EXPORT_SYMBOL(f_setown);
+
+void f_delown(struct file *filp)
+{
+	f_modown(filp, 0, 0, 0, 1);
+}
+
+static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
+		struct file *filp)
+{
+	long err = -EINVAL;
+
+	switch (cmd) {
+	case F_DUPFD:
+		get_file(filp);
+		err = dupfd(filp, arg);
+		break;
+	case F_GETFD:
+		err = get_close_on_exec(fd) ? FD_CLOEXEC : 0;
+		break;
+	case F_SETFD:
+		err = 0;
+		set_close_on_exec(fd, arg & FD_CLOEXEC);
+		break;
+	case F_GETFL:
+		err = filp->f_flags;
+		break;
+	case F_SETFL:
+		err = setfl(fd, filp, arg);
+		break;
+	case F_GETLK:
+		err = fcntl_getlk(filp, (struct flock __user *) arg);
+		break;
+	case F_SETLK:
+	case F_SETLKW:
+		err = fcntl_setlk(fd, filp, cmd, (struct flock __user *) arg);
+		break;
+	case F_GETOWN:
+		/*
+		 * XXX If f_owner is a process group, the
+		 * negative return value will get converted
+		 * into an error.  Oops.  If we keep the
+		 * current syscall conventions, the only way
+		 * to fix this will be in libc.
+		 */
+		err = filp->f_owner.pid;
+		force_successful_syscall_return();
+		break;
+	case F_SETOWN:
+		err = f_setown(filp, arg, 1);
+		break;
+	case F_GETSIG:
+		err = filp->f_owner.signum;
+		break;
+	case F_SETSIG:
+		/* arg == 0 restores default behaviour. */
+		if (!valid_signal(arg)) {
+			break;
+		}
+		err = 0;
+		filp->f_owner.signum = arg;
+		break;
+	case F_GETLEASE:
+		err = fcntl_getlease(filp);
+		break;
+	case F_SETLEASE:
+		err = fcntl_setlease(fd, filp, arg);
+		break;
+	case F_NOTIFY:
+		err = fcntl_dirnotify(fd, filp, arg);
+		break;
+	default:
+		break;
+	}
+	return err;
+}
+
+asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
+{	
+	struct file *filp;
+	long err = -EBADF;
+
+	filp = fget(fd);
+	if (!filp)
+		goto out;
+
+	err = security_file_fcntl(filp, cmd, arg);
+	if (err) {
+		fput(filp);
+		return err;
+	}
+
+	err = do_fcntl(fd, cmd, arg, filp);
+
+ 	fput(filp);
+out:
+	return err;
+}
+
+#if BITS_PER_LONG == 32
+asmlinkage long sys_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg)
+{	
+	struct file * filp;
+	long err;
+
+	err = -EBADF;
+	filp = fget(fd);
+	if (!filp)
+		goto out;
+
+	err = security_file_fcntl(filp, cmd, arg);
+	if (err) {
+		fput(filp);
+		return err;
+	}
+	err = -EBADF;
+	
+	switch (cmd) {
+		case F_GETLK64:
+			err = fcntl_getlk64(filp, (struct flock64 __user *) arg);
+			break;
+		case F_SETLK64:
+		case F_SETLKW64:
+			err = fcntl_setlk64(fd, filp, cmd,
+					(struct flock64 __user *) arg);
+			break;
+		default:
+			err = do_fcntl(fd, cmd, arg, filp);
+			break;
+	}
+	fput(filp);
+out:
+	return err;
+}
+#endif
+
+/* Table to convert sigio signal codes into poll band bitmaps */
+
+static long band_table[NSIGPOLL] = {
+	POLLIN | POLLRDNORM,			/* POLL_IN */
+	POLLOUT | POLLWRNORM | POLLWRBAND,	/* POLL_OUT */
+	POLLIN | POLLRDNORM | POLLMSG,		/* POLL_MSG */
+	POLLERR,				/* POLL_ERR */
+	POLLPRI | POLLRDBAND,			/* POLL_PRI */
+	POLLHUP | POLLERR			/* POLL_HUP */
+};
+
+static inline int sigio_perm(struct task_struct *p,
+                             struct fown_struct *fown, int sig)
+{
+	return (((fown->euid == 0) ||
+		 (fown->euid == p->suid) || (fown->euid == p->uid) ||
+		 (fown->uid == p->suid) || (fown->uid == p->uid)) &&
+		!security_file_send_sigiotask(p, fown, sig));
+}
+
+static void send_sigio_to_task(struct task_struct *p,
+			       struct fown_struct *fown, 
+			       int fd,
+			       int reason)
+{
+	if (!sigio_perm(p, fown, fown->signum))
+		return;
+
+	switch (fown->signum) {
+		siginfo_t si;
+		default:
+			/* Queue a rt signal with the appropriate fd as its
+			   value.  We use SI_SIGIO as the source, not 
+			   SI_KERNEL, since kernel signals always get 
+			   delivered even if we can't queue.  Failure to
+			   queue in this case _should_ be reported; we fall
+			   back to SIGIO in that case. --sct */
+			si.si_signo = fown->signum;
+			si.si_errno = 0;
+		        si.si_code  = reason;
+			/* Make sure we are called with one of the POLL_*
+			   reasons, otherwise we could leak kernel stack into
+			   userspace.  */
+			if ((reason & __SI_MASK) != __SI_POLL)
+				BUG();
+			if (reason - POLL_IN >= NSIGPOLL)
+				si.si_band  = ~0L;
+			else
+				si.si_band = band_table[reason - POLL_IN];
+			si.si_fd    = fd;
+			if (!send_group_sig_info(fown->signum, &si, p))
+				break;
+		/* fall-through: fall back on the old plain SIGIO signal */
+		case 0:
+			send_group_sig_info(SIGIO, SEND_SIG_PRIV, p);
+	}
+}
+
+void send_sigio(struct fown_struct *fown, int fd, int band)
+{
+	struct task_struct *p;
+	int pid;
+	
+	read_lock(&fown->lock);
+	pid = fown->pid;
+	if (!pid)
+		goto out_unlock_fown;
+	
+	read_lock(&tasklist_lock);
+	if (pid > 0) {
+		p = find_task_by_pid(pid);
+		if (p) {
+			send_sigio_to_task(p, fown, fd, band);
+		}
+	} else {
+		do_each_task_pid(-pid, PIDTYPE_PGID, p) {
+			send_sigio_to_task(p, fown, fd, band);
+		} while_each_task_pid(-pid, PIDTYPE_PGID, p);
+	}
+	read_unlock(&tasklist_lock);
+ out_unlock_fown:
+	read_unlock(&fown->lock);
+}
+
+static void send_sigurg_to_task(struct task_struct *p,
+                                struct fown_struct *fown)
+{
+	if (sigio_perm(p, fown, SIGURG))
+		send_group_sig_info(SIGURG, SEND_SIG_PRIV, p);
+}
+
+int send_sigurg(struct fown_struct *fown)
+{
+	struct task_struct *p;
+	int pid, ret = 0;
+	
+	read_lock(&fown->lock);
+	pid = fown->pid;
+	if (!pid)
+		goto out_unlock_fown;
+
+	ret = 1;
+	
+	read_lock(&tasklist_lock);
+	if (pid > 0) {
+		p = find_task_by_pid(pid);
+		if (p) {
+			send_sigurg_to_task(p, fown);
+		}
+	} else {
+		do_each_task_pid(-pid, PIDTYPE_PGID, p) {
+			send_sigurg_to_task(p, fown);
+		} while_each_task_pid(-pid, PIDTYPE_PGID, p);
+	}
+	read_unlock(&tasklist_lock);
+ out_unlock_fown:
+	read_unlock(&fown->lock);
+	return ret;
+}
+
+static DEFINE_RWLOCK(fasync_lock);
+static kmem_cache_t *fasync_cache;
+
+/*
+ * fasync_helper() is used by some character device drivers (mainly mice)
+ * to set up the fasync queue. It returns negative on error, 0 if it did
+ * no changes and positive if it added/deleted the entry.
+ */
+int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
+{
+	struct fasync_struct *fa, **fp;
+	struct fasync_struct *new = NULL;
+	int result = 0;
+
+	if (on) {
+		new = kmem_cache_alloc(fasync_cache, SLAB_KERNEL);
+		if (!new)
+			return -ENOMEM;
+	}
+	write_lock_irq(&fasync_lock);
+	for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
+		if (fa->fa_file == filp) {
+			if(on) {
+				fa->fa_fd = fd;
+				kmem_cache_free(fasync_cache, new);
+			} else {
+				*fp = fa->fa_next;
+				kmem_cache_free(fasync_cache, fa);
+				result = 1;
+			}
+			goto out;
+		}
+	}
+
+	if (on) {
+		new->magic = FASYNC_MAGIC;
+		new->fa_file = filp;
+		new->fa_fd = fd;
+		new->fa_next = *fapp;
+		*fapp = new;
+		result = 1;
+	}
+out:
+	write_unlock_irq(&fasync_lock);
+	return result;
+}
+
+EXPORT_SYMBOL(fasync_helper);
+
+void __kill_fasync(struct fasync_struct *fa, int sig, int band)
+{
+	while (fa) {
+		struct fown_struct * fown;
+		if (fa->magic != FASYNC_MAGIC) {
+			printk(KERN_ERR "kill_fasync: bad magic number in "
+			       "fasync_struct!\n");
+			return;
+		}
+		fown = &fa->fa_file->f_owner;
+		/* Don't send SIGURG to processes which have not set a
+		   queued signum: SIGURG has its own default signalling
+		   mechanism. */
+		if (!(sig == SIGURG && fown->signum == 0))
+			send_sigio(fown, fa->fa_fd, band);
+		fa = fa->fa_next;
+	}
+}
+
+EXPORT_SYMBOL(__kill_fasync);
+
+void kill_fasync(struct fasync_struct **fp, int sig, int band)
+{
+	/* First a quick test without locking: usually
+	 * the list is empty.
+	 */
+	if (*fp) {
+		read_lock(&fasync_lock);
+		/* reread *fp after obtaining the lock */
+		__kill_fasync(*fp, sig, band);
+		read_unlock(&fasync_lock);
+	}
+}
+EXPORT_SYMBOL(kill_fasync);
+
+static int __init fasync_init(void)
+{
+	fasync_cache = kmem_cache_create("fasync_cache",
+		sizeof(struct fasync_struct), 0, SLAB_PANIC, NULL, NULL);
+	return 0;
+}
+
+module_init(fasync_init)
diff -urN oldtree/fs/file.c newtree/fs/file.c
--- oldtree/fs/file.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/file.c	2006-02-13 19:20:00.701405672 -0500
@@ -125,7 +125,8 @@
 		kmem_cache_free(files_cachep, fdt->free_files);
 		return;
 	}
-	if (fdt->max_fdset <= __FD_SETSIZE && fdt->max_fds <= NR_OPEN_DEFAULT) {
+	if (fdt->max_fdset <= 8 * sizeof(embedded_fd_set) &&
+		fdt->max_fds <= NR_OPEN_DEFAULT) {
 		/*
 		 * The fdtable was embedded
 		 */
@@ -155,8 +156,9 @@
 
 void free_fdtable(struct fdtable *fdt)
 {
-	if (fdt->free_files || fdt->max_fdset > __FD_SETSIZE ||
-					fdt->max_fds > NR_OPEN_DEFAULT)
+	if (fdt->free_files ||
+		fdt->max_fdset > 8 * sizeof(embedded_fd_set) ||
+		fdt->max_fds > NR_OPEN_DEFAULT)
 		call_rcu(&fdt->rcu, free_fdtable_rcu);
 }
 
@@ -199,7 +201,6 @@
 		       (nfdt->max_fds - fdt->max_fds) *
 					sizeof(struct file *));
 	}
-	nfdt->next_fd = fdt->next_fd;
 }
 
 /*
@@ -220,11 +221,9 @@
 
 void free_fdset(fd_set *array, int num)
 {
-	int size = num / 8;
-
-	if (num <= __FD_SETSIZE) /* Don't free an embedded fdset */
+	if (num <= 8 * sizeof(embedded_fd_set)) /* Don't free an embedded fdset */
 		return;
-	else if (size <= PAGE_SIZE)
+	else if (num <= 8 * PAGE_SIZE)
 		kfree(array);
 	else
 		vfree(array);
@@ -237,22 +236,17 @@
   	fd_set *new_openset = NULL, *new_execset = NULL;
 	struct file **new_fds;
 
-	fdt = kmalloc(sizeof(*fdt), GFP_KERNEL);
+	fdt = kzalloc(sizeof(*fdt), GFP_KERNEL);
 	if (!fdt)
   		goto out;
-	memset(fdt, 0, sizeof(*fdt));
 
-	nfds = __FD_SETSIZE;
+	nfds = 8 * L1_CACHE_BYTES;
   	/* Expand to the max in easy steps */
-  	do {
-		if (nfds < (PAGE_SIZE * 8))
-			nfds = PAGE_SIZE * 8;
-		else {
-			nfds = nfds * 2;
-			if (nfds > NR_OPEN)
-				nfds = NR_OPEN;
-		}
-	} while (nfds <= nr);
+  	while (nfds <= nr) {
+		nfds = nfds * 2;
+		if (nfds > NR_OPEN)
+			nfds = NR_OPEN;
+	}
 
   	new_openset = alloc_fdset(nfds);
   	new_execset = alloc_fdset(nfds);
diff -urN oldtree/fs/hugetlbfs/inode.c newtree/fs/hugetlbfs/inode.c
--- oldtree/fs/hugetlbfs/inode.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/hugetlbfs/inode.c	2006-02-13 19:20:00.702405520 -0500
@@ -404,7 +404,7 @@
 		inode->i_mapping->backing_dev_info =&hugetlbfs_backing_dev_info;
 		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 		info = HUGETLBFS_I(inode);
-		mpol_shared_policy_init(&info->policy);
+		mpol_shared_policy_init(&info->policy, MPOL_DEFAULT, NULL);
 		switch (mode & S_IFMT) {
 		default:
 			init_special_inode(inode, mode, dev);
diff -urN oldtree/fs/hugetlbfs/inode.c.orig newtree/fs/hugetlbfs/inode.c.orig
--- oldtree/fs/hugetlbfs/inode.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/hugetlbfs/inode.c.orig	2006-02-13 19:20:00.704405216 -0500
@@ -0,0 +1,894 @@
+/*
+ * hugetlbpage-backed filesystem.  Based on ramfs.
+ *
+ * William Irwin, 2002
+ *
+ * Copyright (C) 2002 Linus Torvalds.
+ */
+
+#include <linux/module.h>
+#include <linux/thread_info.h>
+#include <asm/current.h>
+#include <linux/sched.h>		/* remove ASAP */
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/file.h>
+#include <linux/writeback.h>
+#include <linux/pagemap.h>
+#include <linux/highmem.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/backing-dev.h>
+#include <linux/hugetlb.h>
+#include <linux/pagevec.h>
+#include <linux/quotaops.h>
+#include <linux/slab.h>
+#include <linux/dnotify.h>
+#include <linux/statfs.h>
+#include <linux/security.h>
+
+#include <asm/uaccess.h>
+
+/* some random number */
+#define HUGETLBFS_MAGIC	0x958458f6
+
+static struct super_operations hugetlbfs_ops;
+static struct address_space_operations hugetlbfs_aops;
+struct file_operations hugetlbfs_file_operations;
+static struct inode_operations hugetlbfs_dir_inode_operations;
+static struct inode_operations hugetlbfs_inode_operations;
+
+static struct backing_dev_info hugetlbfs_backing_dev_info = {
+	.ra_pages	= 0,	/* No readahead */
+	.capabilities	= BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK,
+};
+
+int sysctl_hugetlb_shm_group;
+
+static void huge_pagevec_release(struct pagevec *pvec)
+{
+	int i;
+
+	for (i = 0; i < pagevec_count(pvec); ++i)
+		put_page(pvec->pages[i]);
+
+	pagevec_reinit(pvec);
+}
+
+/*
+ * huge_pages_needed tries to determine the number of new huge pages that
+ * will be required to fully populate this VMA.  This will be equal to
+ * the size of the VMA in huge pages minus the number of huge pages
+ * (covered by this VMA) that are found in the page cache.
+ *
+ * Result is in bytes to be compatible with is_hugepage_mem_enough()
+ */
+static unsigned long
+huge_pages_needed(struct address_space *mapping, struct vm_area_struct *vma)
+{
+	int i;
+	struct pagevec pvec;
+	unsigned long start = vma->vm_start;
+	unsigned long end = vma->vm_end;
+	unsigned long hugepages = (end - start) >> HPAGE_SHIFT;
+	pgoff_t next = vma->vm_pgoff;
+	pgoff_t endpg = next + ((end - start) >> PAGE_SHIFT);
+
+	pagevec_init(&pvec, 0);
+	while (next < endpg) {
+		if (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE))
+			break;
+		for (i = 0; i < pagevec_count(&pvec); i++) {
+			struct page *page = pvec.pages[i];
+			if (page->index > next)
+				next = page->index;
+			if (page->index >= endpg)
+				break;
+			next++;
+			hugepages--;
+		}
+		huge_pagevec_release(&pvec);
+	}
+	return hugepages << HPAGE_SHIFT;
+}
+
+static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct address_space *mapping = inode->i_mapping;
+	unsigned long bytes;
+	loff_t len, vma_len;
+	int ret;
+
+	if ((vma->vm_flags & (VM_MAYSHARE | VM_WRITE)) == VM_WRITE)
+		return -EINVAL;
+
+	if (vma->vm_pgoff & (HPAGE_SIZE / PAGE_SIZE - 1))
+		return -EINVAL;
+
+	if (vma->vm_start & ~HPAGE_MASK)
+		return -EINVAL;
+
+	if (vma->vm_end & ~HPAGE_MASK)
+		return -EINVAL;
+
+	if (vma->vm_end - vma->vm_start < HPAGE_SIZE)
+		return -EINVAL;
+
+	bytes = huge_pages_needed(mapping, vma);
+	if (!is_hugepage_mem_enough(bytes))
+		return -ENOMEM;
+
+	vma_len = (loff_t)(vma->vm_end - vma->vm_start);
+
+	down(&inode->i_sem);
+	file_accessed(file);
+	vma->vm_flags |= VM_HUGETLB | VM_RESERVED;
+	vma->vm_ops = &hugetlb_vm_ops;
+
+	ret = -ENOMEM;
+	len = vma_len + ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
+	if (!(vma->vm_flags & VM_WRITE) && len > inode->i_size)
+		goto out;
+
+	ret = 0;
+	hugetlb_prefault_arch_hook(vma->vm_mm);
+	if (inode->i_size < len)
+		inode->i_size = len;
+out:
+	up(&inode->i_sem);
+
+	return ret;
+}
+
+/*
+ * Called under down_write(mmap_sem).
+ */
+
+#ifdef HAVE_ARCH_HUGETLB_UNMAPPED_AREA
+unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
+		unsigned long len, unsigned long pgoff, unsigned long flags);
+#else
+static unsigned long
+hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
+		unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+	unsigned long start_addr;
+
+	if (len & ~HPAGE_MASK)
+		return -EINVAL;
+	if (len > TASK_SIZE)
+		return -ENOMEM;
+
+	if (addr) {
+		addr = ALIGN(addr, HPAGE_SIZE);
+		vma = find_vma(mm, addr);
+		if (TASK_SIZE - len >= addr &&
+		    (!vma || addr + len <= vma->vm_start))
+			return addr;
+	}
+
+	start_addr = mm->free_area_cache;
+
+	if (len <= mm->cached_hole_size)
+		start_addr = TASK_UNMAPPED_BASE;
+
+full_search:
+	addr = ALIGN(start_addr, HPAGE_SIZE);
+
+	for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
+		/* At this point:  (!vma || addr < vma->vm_end). */
+		if (TASK_SIZE - len < addr) {
+			/*
+			 * Start a new search - just in case we missed
+			 * some holes.
+			 */
+			if (start_addr != TASK_UNMAPPED_BASE) {
+				start_addr = TASK_UNMAPPED_BASE;
+				goto full_search;
+			}
+			return -ENOMEM;
+		}
+
+		if (!vma || addr + len <= vma->vm_start)
+			return addr;
+		addr = ALIGN(vma->vm_end, HPAGE_SIZE);
+	}
+}
+#endif
+
+/*
+ * Read a page. Again trivial. If it didn't already exist
+ * in the page cache, it is zero-filled.
+ */
+static int hugetlbfs_readpage(struct file *file, struct page * page)
+{
+	unlock_page(page);
+	return -EINVAL;
+}
+
+static int hugetlbfs_prepare_write(struct file *file,
+			struct page *page, unsigned offset, unsigned to)
+{
+	return -EINVAL;
+}
+
+static int hugetlbfs_commit_write(struct file *file,
+			struct page *page, unsigned offset, unsigned to)
+{
+	return -EINVAL;
+}
+
+static void truncate_huge_page(struct page *page)
+{
+	clear_page_dirty(page);
+	ClearPageUptodate(page);
+	remove_from_page_cache(page);
+	put_page(page);
+}
+
+static void truncate_hugepages(struct address_space *mapping, loff_t lstart)
+{
+	const pgoff_t start = lstart >> HPAGE_SHIFT;
+	struct pagevec pvec;
+	pgoff_t next;
+	int i;
+
+	pagevec_init(&pvec, 0);
+	next = start;
+	while (1) {
+		if (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
+			if (next == start)
+				break;
+			next = start;
+			continue;
+		}
+
+		for (i = 0; i < pagevec_count(&pvec); ++i) {
+			struct page *page = pvec.pages[i];
+
+			lock_page(page);
+			if (page->index > next)
+				next = page->index;
+			++next;
+			truncate_huge_page(page);
+			unlock_page(page);
+			hugetlb_put_quota(mapping);
+		}
+		huge_pagevec_release(&pvec);
+	}
+	BUG_ON(!lstart && mapping->nrpages);
+}
+
+static void hugetlbfs_delete_inode(struct inode *inode)
+{
+	if (inode->i_data.nrpages)
+		truncate_hugepages(&inode->i_data, 0);
+	clear_inode(inode);
+}
+
+static void hugetlbfs_forget_inode(struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+
+	if (!hlist_unhashed(&inode->i_hash)) {
+		if (!(inode->i_state & (I_DIRTY|I_LOCK)))
+			list_move(&inode->i_list, &inode_unused);
+		inodes_stat.nr_unused++;
+		if (!sb || (sb->s_flags & MS_ACTIVE)) {
+			spin_unlock(&inode_lock);
+			return;
+		}
+		inode->i_state |= I_WILL_FREE;
+		spin_unlock(&inode_lock);
+		/*
+		 * write_inode_now is a noop as we set BDI_CAP_NO_WRITEBACK
+		 * in our backing_dev_info.
+		 */
+		write_inode_now(inode, 1);
+		spin_lock(&inode_lock);
+		inode->i_state &= ~I_WILL_FREE;
+		inodes_stat.nr_unused--;
+		hlist_del_init(&inode->i_hash);
+	}
+	list_del_init(&inode->i_list);
+	list_del_init(&inode->i_sb_list);
+	inode->i_state |= I_FREEING;
+	inodes_stat.nr_inodes--;
+	spin_unlock(&inode_lock);
+	if (inode->i_data.nrpages)
+		truncate_hugepages(&inode->i_data, 0);
+	clear_inode(inode);
+	destroy_inode(inode);
+}
+
+static void hugetlbfs_drop_inode(struct inode *inode)
+{
+	if (!inode->i_nlink)
+		generic_delete_inode(inode);
+	else
+		hugetlbfs_forget_inode(inode);
+}
+
+/*
+ * h_pgoff is in HPAGE_SIZE units.
+ * vma->vm_pgoff is in PAGE_SIZE units.
+ */
+static inline void
+hugetlb_vmtruncate_list(struct prio_tree_root *root, unsigned long h_pgoff)
+{
+	struct vm_area_struct *vma;
+	struct prio_tree_iter iter;
+
+	vma_prio_tree_foreach(vma, &iter, root, h_pgoff, ULONG_MAX) {
+		unsigned long h_vm_pgoff;
+		unsigned long v_offset;
+
+		h_vm_pgoff = vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT);
+		v_offset = (h_pgoff - h_vm_pgoff) << HPAGE_SHIFT;
+		/*
+		 * Is this VMA fully outside the truncation point?
+		 */
+		if (h_vm_pgoff >= h_pgoff)
+			v_offset = 0;
+
+		unmap_hugepage_range(vma,
+				vma->vm_start + v_offset, vma->vm_end);
+	}
+}
+
+/*
+ * Expanding truncates are not allowed.
+ */
+static int hugetlb_vmtruncate(struct inode *inode, loff_t offset)
+{
+	unsigned long pgoff;
+	struct address_space *mapping = inode->i_mapping;
+
+	if (offset > inode->i_size)
+		return -EINVAL;
+
+	BUG_ON(offset & ~HPAGE_MASK);
+	pgoff = offset >> HPAGE_SHIFT;
+
+	inode->i_size = offset;
+	spin_lock(&mapping->i_mmap_lock);
+	if (!prio_tree_empty(&mapping->i_mmap))
+		hugetlb_vmtruncate_list(&mapping->i_mmap, pgoff);
+	spin_unlock(&mapping->i_mmap_lock);
+	truncate_hugepages(mapping, offset);
+	return 0;
+}
+
+static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr)
+{
+	struct inode *inode = dentry->d_inode;
+	int error;
+	unsigned int ia_valid = attr->ia_valid;
+
+	BUG_ON(!inode);
+
+	error = inode_change_ok(inode, attr);
+	if (error)
+		goto out;
+
+	if (ia_valid & ATTR_SIZE) {
+		error = -EINVAL;
+		if (!(attr->ia_size & ~HPAGE_MASK))
+			error = hugetlb_vmtruncate(inode, attr->ia_size);
+		if (error)
+			goto out;
+		attr->ia_valid &= ~ATTR_SIZE;
+	}
+	error = inode_setattr(inode, attr);
+out:
+	return error;
+}
+
+static struct inode *hugetlbfs_get_inode(struct super_block *sb, uid_t uid, 
+					gid_t gid, int mode, dev_t dev)
+{
+	struct inode *inode;
+
+	inode = new_inode(sb);
+	if (inode) {
+		struct hugetlbfs_inode_info *info;
+		inode->i_mode = mode;
+		inode->i_uid = uid;
+		inode->i_gid = gid;
+		inode->i_blksize = HPAGE_SIZE;
+		inode->i_blocks = 0;
+		inode->i_mapping->a_ops = &hugetlbfs_aops;
+		inode->i_mapping->backing_dev_info =&hugetlbfs_backing_dev_info;
+		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+		info = HUGETLBFS_I(inode);
+		mpol_shared_policy_init(&info->policy);
+		switch (mode & S_IFMT) {
+		default:
+			init_special_inode(inode, mode, dev);
+			break;
+		case S_IFREG:
+			inode->i_op = &hugetlbfs_inode_operations;
+			inode->i_fop = &hugetlbfs_file_operations;
+			break;
+		case S_IFDIR:
+			inode->i_op = &hugetlbfs_dir_inode_operations;
+			inode->i_fop = &simple_dir_operations;
+
+			/* directory inodes start off with i_nlink == 2 (for "." entry) */
+			inode->i_nlink++;
+			break;
+		case S_IFLNK:
+			inode->i_op = &page_symlink_inode_operations;
+			break;
+		}
+	}
+	return inode;
+}
+
+/*
+ * File creation. Allocate an inode, and we're done..
+ */
+static int hugetlbfs_mknod(struct inode *dir,
+			struct dentry *dentry, int mode, dev_t dev)
+{
+	struct inode *inode;
+	int error = -ENOSPC;
+	gid_t gid;
+
+	if (dir->i_mode & S_ISGID) {
+		gid = dir->i_gid;
+		if (S_ISDIR(mode))
+			mode |= S_ISGID;
+	} else {
+		gid = current->fsgid;
+	}
+	inode = hugetlbfs_get_inode(dir->i_sb, current->fsuid, gid, mode, dev);
+	if (inode) {
+		dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+		d_instantiate(dentry, inode);
+		dget(dentry);	/* Extra count - pin the dentry in core */
+		error = 0;
+	}
+	return error;
+}
+
+static int hugetlbfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	int retval = hugetlbfs_mknod(dir, dentry, mode | S_IFDIR, 0);
+	if (!retval)
+		dir->i_nlink++;
+	return retval;
+}
+
+static int hugetlbfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd)
+{
+	return hugetlbfs_mknod(dir, dentry, mode | S_IFREG, 0);
+}
+
+static int hugetlbfs_symlink(struct inode *dir,
+			struct dentry *dentry, const char *symname)
+{
+	struct inode *inode;
+	int error = -ENOSPC;
+	gid_t gid;
+
+	if (dir->i_mode & S_ISGID)
+		gid = dir->i_gid;
+	else
+		gid = current->fsgid;
+
+	inode = hugetlbfs_get_inode(dir->i_sb, current->fsuid,
+					gid, S_IFLNK|S_IRWXUGO, 0);
+	if (inode) {
+		int l = strlen(symname)+1;
+		error = page_symlink(inode, symname, l);
+		if (!error) {
+			d_instantiate(dentry, inode);
+			dget(dentry);
+		} else
+			iput(inode);
+	}
+	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+
+	return error;
+}
+
+/*
+ * For direct-IO reads into hugetlb pages
+ */
+static int hugetlbfs_set_page_dirty(struct page *page)
+{
+	return 0;
+}
+
+static int hugetlbfs_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+	struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(sb);
+
+	buf->f_type = HUGETLBFS_MAGIC;
+	buf->f_bsize = HPAGE_SIZE;
+	if (sbinfo) {
+		spin_lock(&sbinfo->stat_lock);
+		/* If no limits set, just report 0 for max/free/used
+		 * blocks, like simple_statfs() */
+		if (sbinfo->max_blocks >= 0) {
+			buf->f_blocks = sbinfo->max_blocks;
+			buf->f_bavail = buf->f_bfree = sbinfo->free_blocks;
+			buf->f_files = sbinfo->max_inodes;
+			buf->f_ffree = sbinfo->free_inodes;
+		}
+		spin_unlock(&sbinfo->stat_lock);
+	}
+	buf->f_namelen = NAME_MAX;
+	return 0;
+}
+
+static void hugetlbfs_put_super(struct super_block *sb)
+{
+	struct hugetlbfs_sb_info *sbi = HUGETLBFS_SB(sb);
+
+	if (sbi) {
+		sb->s_fs_info = NULL;
+		kfree(sbi);
+	}
+}
+
+static inline int hugetlbfs_dec_free_inodes(struct hugetlbfs_sb_info *sbinfo)
+{
+	if (sbinfo->free_inodes >= 0) {
+		spin_lock(&sbinfo->stat_lock);
+		if (unlikely(!sbinfo->free_inodes)) {
+			spin_unlock(&sbinfo->stat_lock);
+			return 0;
+		}
+		sbinfo->free_inodes--;
+		spin_unlock(&sbinfo->stat_lock);
+	}
+
+	return 1;
+}
+
+static void hugetlbfs_inc_free_inodes(struct hugetlbfs_sb_info *sbinfo)
+{
+	if (sbinfo->free_inodes >= 0) {
+		spin_lock(&sbinfo->stat_lock);
+		sbinfo->free_inodes++;
+		spin_unlock(&sbinfo->stat_lock);
+	}
+}
+
+
+static kmem_cache_t *hugetlbfs_inode_cachep;
+
+static struct inode *hugetlbfs_alloc_inode(struct super_block *sb)
+{
+	struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(sb);
+	struct hugetlbfs_inode_info *p;
+
+	if (unlikely(!hugetlbfs_dec_free_inodes(sbinfo)))
+		return NULL;
+	p = kmem_cache_alloc(hugetlbfs_inode_cachep, SLAB_KERNEL);
+	if (unlikely(!p)) {
+		hugetlbfs_inc_free_inodes(sbinfo);
+		return NULL;
+	}
+	return &p->vfs_inode;
+}
+
+static void hugetlbfs_destroy_inode(struct inode *inode)
+{
+	hugetlbfs_inc_free_inodes(HUGETLBFS_SB(inode->i_sb));
+	mpol_free_shared_policy(&HUGETLBFS_I(inode)->policy);
+	kmem_cache_free(hugetlbfs_inode_cachep, HUGETLBFS_I(inode));
+}
+
+static struct address_space_operations hugetlbfs_aops = {
+	.readpage	= hugetlbfs_readpage,
+	.prepare_write	= hugetlbfs_prepare_write,
+	.commit_write	= hugetlbfs_commit_write,
+	.set_page_dirty	= hugetlbfs_set_page_dirty,
+};
+
+
+static void init_once(void *foo, kmem_cache_t *cachep, unsigned long flags)
+{
+	struct hugetlbfs_inode_info *ei = (struct hugetlbfs_inode_info *)foo;
+
+	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR)
+		inode_init_once(&ei->vfs_inode);
+}
+
+struct file_operations hugetlbfs_file_operations = {
+	.mmap			= hugetlbfs_file_mmap,
+	.fsync			= simple_sync_file,
+	.get_unmapped_area	= hugetlb_get_unmapped_area,
+};
+
+static struct inode_operations hugetlbfs_dir_inode_operations = {
+	.create		= hugetlbfs_create,
+	.lookup		= simple_lookup,
+	.link		= simple_link,
+	.unlink		= simple_unlink,
+	.symlink	= hugetlbfs_symlink,
+	.mkdir		= hugetlbfs_mkdir,
+	.rmdir		= simple_rmdir,
+	.mknod		= hugetlbfs_mknod,
+	.rename		= simple_rename,
+	.setattr	= hugetlbfs_setattr,
+};
+
+static struct inode_operations hugetlbfs_inode_operations = {
+	.setattr	= hugetlbfs_setattr,
+};
+
+static struct super_operations hugetlbfs_ops = {
+	.alloc_inode    = hugetlbfs_alloc_inode,
+	.destroy_inode  = hugetlbfs_destroy_inode,
+	.statfs		= hugetlbfs_statfs,
+	.delete_inode	= hugetlbfs_delete_inode,
+	.drop_inode	= hugetlbfs_drop_inode,
+	.put_super	= hugetlbfs_put_super,
+};
+
+static int
+hugetlbfs_parse_options(char *options, struct hugetlbfs_config *pconfig)
+{
+	char *opt, *value, *rest;
+
+	if (!options)
+		return 0;
+	while ((opt = strsep(&options, ",")) != NULL) {
+		if (!*opt)
+			continue;
+
+		value = strchr(opt, '=');
+		if (!value || !*value)
+			return -EINVAL;
+		else
+			*value++ = '\0';
+
+		if (!strcmp(opt, "uid"))
+			pconfig->uid = simple_strtoul(value, &value, 0);
+		else if (!strcmp(opt, "gid"))
+			pconfig->gid = simple_strtoul(value, &value, 0);
+		else if (!strcmp(opt, "mode"))
+			pconfig->mode = simple_strtoul(value,&value,0) & 0777U;
+		else if (!strcmp(opt, "size")) {
+			unsigned long long size = memparse(value, &rest);
+			if (*rest == '%') {
+				size <<= HPAGE_SHIFT;
+				size *= max_huge_pages;
+				do_div(size, 100);
+				rest++;
+			}
+			size &= HPAGE_MASK;
+			pconfig->nr_blocks = (size >> HPAGE_SHIFT);
+			value = rest;
+		} else if (!strcmp(opt,"nr_inodes")) {
+			pconfig->nr_inodes = memparse(value, &rest);
+			value = rest;
+		} else
+			return -EINVAL;
+
+		if (*value)
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static int
+hugetlbfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+	struct inode * inode;
+	struct dentry * root;
+	int ret;
+	struct hugetlbfs_config config;
+	struct hugetlbfs_sb_info *sbinfo;
+
+	config.nr_blocks = -1; /* No limit on size by default */
+	config.nr_inodes = -1; /* No limit on number of inodes by default */
+	config.uid = current->fsuid;
+	config.gid = current->fsgid;
+	config.mode = 0755;
+	ret = hugetlbfs_parse_options(data, &config);
+
+	if (ret)
+		return ret;
+
+	sbinfo = kmalloc(sizeof(struct hugetlbfs_sb_info), GFP_KERNEL);
+	if (!sbinfo)
+		return -ENOMEM;
+	sb->s_fs_info = sbinfo;
+	spin_lock_init(&sbinfo->stat_lock);
+	sbinfo->max_blocks = config.nr_blocks;
+	sbinfo->free_blocks = config.nr_blocks;
+	sbinfo->max_inodes = config.nr_inodes;
+	sbinfo->free_inodes = config.nr_inodes;
+	sb->s_maxbytes = MAX_LFS_FILESIZE;
+	sb->s_blocksize = HPAGE_SIZE;
+	sb->s_blocksize_bits = HPAGE_SHIFT;
+	sb->s_magic = HUGETLBFS_MAGIC;
+	sb->s_op = &hugetlbfs_ops;
+	sb->s_time_gran = 1;
+	inode = hugetlbfs_get_inode(sb, config.uid, config.gid,
+					S_IFDIR | config.mode, 0);
+	if (!inode)
+		goto out_free;
+
+	root = d_alloc_root(inode);
+	if (!root) {
+		iput(inode);
+		goto out_free;
+	}
+	sb->s_root = root;
+	return 0;
+out_free:
+	kfree(sbinfo);
+	return -ENOMEM;
+}
+
+int hugetlb_get_quota(struct address_space *mapping)
+{
+	int ret = 0;
+	struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(mapping->host->i_sb);
+
+	if (sbinfo->free_blocks > -1) {
+		spin_lock(&sbinfo->stat_lock);
+		if (sbinfo->free_blocks > 0)
+			sbinfo->free_blocks--;
+		else
+			ret = -ENOMEM;
+		spin_unlock(&sbinfo->stat_lock);
+	}
+
+	return ret;
+}
+
+void hugetlb_put_quota(struct address_space *mapping)
+{
+	struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(mapping->host->i_sb);
+
+	if (sbinfo->free_blocks > -1) {
+		spin_lock(&sbinfo->stat_lock);
+		sbinfo->free_blocks++;
+		spin_unlock(&sbinfo->stat_lock);
+	}
+}
+
+static struct super_block *hugetlbfs_get_sb(struct file_system_type *fs_type,
+	int flags, const char *dev_name, void *data)
+{
+	return get_sb_nodev(fs_type, flags, data, hugetlbfs_fill_super);
+}
+
+static struct file_system_type hugetlbfs_fs_type = {
+	.name		= "hugetlbfs",
+	.get_sb		= hugetlbfs_get_sb,
+	.kill_sb	= kill_litter_super,
+};
+
+static struct vfsmount *hugetlbfs_vfsmount;
+
+/*
+ * Return the next identifier for a shm file
+ */
+static unsigned long hugetlbfs_counter(void)
+{
+	static DEFINE_SPINLOCK(lock);
+	static unsigned long counter;
+	unsigned long ret;
+
+	spin_lock(&lock);
+	ret = ++counter;
+	spin_unlock(&lock);
+	return ret;
+}
+
+static int can_do_hugetlb_shm(void)
+{
+	return likely(capable(CAP_IPC_LOCK) ||
+			in_group_p(sysctl_hugetlb_shm_group) ||
+			can_do_mlock());
+}
+
+struct file *hugetlb_zero_setup(size_t size)
+{
+	int error = -ENOMEM;
+	struct file *file;
+	struct inode *inode;
+	struct dentry *dentry, *root;
+	struct qstr quick_string;
+	char buf[16];
+
+	if (!can_do_hugetlb_shm())
+		return ERR_PTR(-EPERM);
+
+	if (!is_hugepage_mem_enough(size))
+		return ERR_PTR(-ENOMEM);
+
+	if (!user_shm_lock(size, current->user))
+		return ERR_PTR(-ENOMEM);
+
+	root = hugetlbfs_vfsmount->mnt_root;
+	snprintf(buf, 16, "%lu", hugetlbfs_counter());
+	quick_string.name = buf;
+	quick_string.len = strlen(quick_string.name);
+	quick_string.hash = 0;
+	dentry = d_alloc(root, &quick_string);
+	if (!dentry)
+		goto out_shm_unlock;
+
+	error = -ENFILE;
+	file = get_empty_filp();
+	if (!file)
+		goto out_dentry;
+
+	error = -ENOSPC;
+	inode = hugetlbfs_get_inode(root->d_sb, current->fsuid,
+				current->fsgid, S_IFREG | S_IRWXUGO, 0);
+	if (!inode)
+		goto out_file;
+
+	d_instantiate(dentry, inode);
+	inode->i_size = size;
+	inode->i_nlink = 0;
+	file->f_vfsmnt = mntget(hugetlbfs_vfsmount);
+	file->f_dentry = dentry;
+	file->f_mapping = inode->i_mapping;
+	file->f_op = &hugetlbfs_file_operations;
+	file->f_mode = FMODE_WRITE | FMODE_READ;
+	return file;
+
+out_file:
+	put_filp(file);
+out_dentry:
+	dput(dentry);
+out_shm_unlock:
+	user_shm_unlock(size, current->user);
+	return ERR_PTR(error);
+}
+
+static int __init init_hugetlbfs_fs(void)
+{
+	int error;
+	struct vfsmount *vfsmount;
+
+	hugetlbfs_inode_cachep = kmem_cache_create("hugetlbfs_inode_cache",
+					sizeof(struct hugetlbfs_inode_info),
+					0, 0, init_once, NULL);
+	if (hugetlbfs_inode_cachep == NULL)
+		return -ENOMEM;
+
+	error = register_filesystem(&hugetlbfs_fs_type);
+	if (error)
+		goto out;
+
+	vfsmount = kern_mount(&hugetlbfs_fs_type);
+
+	if (!IS_ERR(vfsmount)) {
+		hugetlbfs_vfsmount = vfsmount;
+		return 0;
+	}
+
+	error = PTR_ERR(vfsmount);
+
+ out:
+	if (error)
+		kmem_cache_destroy(hugetlbfs_inode_cachep);
+	return error;
+}
+
+static void __exit exit_hugetlbfs_fs(void)
+{
+	kmem_cache_destroy(hugetlbfs_inode_cachep);
+	unregister_filesystem(&hugetlbfs_fs_type);
+}
+
+module_init(init_hugetlbfs_fs)
+module_exit(exit_hugetlbfs_fs)
+
+MODULE_LICENSE("GPL");
diff -urN oldtree/fs/isofs/namei.c newtree/fs/isofs/namei.c
--- oldtree/fs/isofs/namei.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/isofs/namei.c	2006-02-13 19:20:00.704405216 -0500
@@ -185,8 +185,5 @@
 		}
 	}
 	unlock_kernel();
-	if (inode)
-		return d_splice_alias(inode, dentry);
-	d_add(dentry, inode);
-	return NULL;
+	return d_splice_alias(inode, dentry);
 }
diff -urN oldtree/fs/namei.c newtree/fs/namei.c
--- oldtree/fs/namei.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/namei.c	2006-02-13 19:20:00.706404912 -0500
@@ -485,7 +485,7 @@
 static int __emul_lookup_dentry(const char *, struct nameidata *);
 
 /* SMP-safe */
-static inline int
+static __always_inline int
 walk_init_root(const char *name, struct nameidata *nd)
 {
 	read_lock(&current->fs->lock);
@@ -503,7 +503,7 @@
 	return 1;
 }
 
-static inline int __vfs_follow_link(struct nameidata *nd, const char *link)
+static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link)
 {
 	int res = 0;
 	char *name;
@@ -543,7 +543,7 @@
 	struct dentry *dentry;
 };
 
-static inline int __do_follow_link(struct path *path, struct nameidata *nd)
+static __always_inline int __do_follow_link(struct path *path, struct nameidata *nd)
 {
 	int error;
 	void *cookie;
@@ -689,7 +689,7 @@
 	return 0;
 }
 
-static inline void follow_dotdot(struct nameidata *nd)
+static __always_inline void follow_dotdot(struct nameidata *nd)
 {
 	while(1) {
 		struct vfsmount *parent;
@@ -1090,8 +1090,7 @@
 	current->total_link_count = 0;
 	retval = link_path_walk(name, nd);
 out:
-	if (unlikely(current->audit_context
-		     && nd && nd->dentry && nd->dentry->d_inode))
+	if (nd && nd->dentry && nd->dentry->d_inode)
 		audit_inode(name, nd->dentry->d_inode, flags);
 	return retval;
 }
@@ -1301,6 +1300,7 @@
 		return -ENOENT;
 
 	BUG_ON(victim->d_parent->d_inode != dir);
+	audit_inode_child(victim->d_name.name, victim->d_inode, dir->i_ino);
 
 	error = permission(dir,MAY_WRITE | MAY_EXEC, NULL);
 	if (error)
@@ -1420,7 +1420,7 @@
 	DQUOT_INIT(dir);
 	error = dir->i_op->create(dir, dentry, mode, nd);
 	if (!error)
-		fsnotify_create(dir, dentry->d_name.name);
+		fsnotify_create(dir, dentry);
 	return error;
 }
 
@@ -1739,7 +1739,7 @@
 	DQUOT_INIT(dir);
 	error = dir->i_op->mknod(dir, dentry, mode, dev);
 	if (!error)
-		fsnotify_create(dir, dentry->d_name.name);
+		fsnotify_create(dir, dentry);
 	return error;
 }
 
@@ -1810,7 +1810,7 @@
 	DQUOT_INIT(dir);
 	error = dir->i_op->mkdir(dir, dentry, mode);
 	if (!error)
-		fsnotify_mkdir(dir, dentry->d_name.name);
+		fsnotify_mkdir(dir, dentry);
 	return error;
 }
 
@@ -2047,7 +2047,7 @@
 	DQUOT_INIT(dir);
 	error = dir->i_op->symlink(dir, dentry, oldname);
 	if (!error)
-		fsnotify_create(dir, dentry->d_name.name);
+		fsnotify_create(dir, dentry);
 	return error;
 }
 
@@ -2118,7 +2118,7 @@
 	error = dir->i_op->link(old_dentry, dir, new_dentry);
 	up(&old_dentry->d_inode->i_sem);
 	if (!error)
-		fsnotify_create(dir, new_dentry->d_name.name);
+		fsnotify_create(dir, new_dentry);
 	return error;
 }
 
diff -urN oldtree/fs/namei.c.orig newtree/fs/namei.c.orig
--- oldtree/fs/namei.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/namei.c.orig	2006-02-13 19:20:00.709404456 -0500
@@ -0,0 +1,2586 @@
+/*
+ *  linux/fs/namei.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ * Some corrections by tytso.
+ */
+
+/* [Feb 1997 T. Schoebel-Theuer] Complete rewrite of the pathname
+ * lookup logic.
+ */
+/* [Feb-Apr 2000, AV] Rewrite to the new namespace architecture.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/quotaops.h>
+#include <linux/pagemap.h>
+#include <linux/fsnotify.h>
+#include <linux/smp_lock.h>
+#include <linux/personality.h>
+#include <linux/security.h>
+#include <linux/syscalls.h>
+#include <linux/mount.h>
+#include <linux/audit.h>
+#include <linux/file.h>
+#include <asm/namei.h>
+#include <asm/uaccess.h>
+
+#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
+
+/* [Feb-1997 T. Schoebel-Theuer]
+ * Fundamental changes in the pathname lookup mechanisms (namei)
+ * were necessary because of omirr.  The reason is that omirr needs
+ * to know the _real_ pathname, not the user-supplied one, in case
+ * of symlinks (and also when transname replacements occur).
+ *
+ * The new code replaces the old recursive symlink resolution with
+ * an iterative one (in case of non-nested symlink chains).  It does
+ * this with calls to <fs>_follow_link().
+ * As a side effect, dir_namei(), _namei() and follow_link() are now 
+ * replaced with a single function lookup_dentry() that can handle all 
+ * the special cases of the former code.
+ *
+ * With the new dcache, the pathname is stored at each inode, at least as
+ * long as the refcount of the inode is positive.  As a side effect, the
+ * size of the dcache depends on the inode cache and thus is dynamic.
+ *
+ * [29-Apr-1998 C. Scott Ananian] Updated above description of symlink
+ * resolution to correspond with current state of the code.
+ *
+ * Note that the symlink resolution is not *completely* iterative.
+ * There is still a significant amount of tail- and mid- recursion in
+ * the algorithm.  Also, note that <fs>_readlink() is not used in
+ * lookup_dentry(): lookup_dentry() on the result of <fs>_readlink()
+ * may return different results than <fs>_follow_link().  Many virtual
+ * filesystems (including /proc) exhibit this behavior.
+ */
+
+/* [24-Feb-97 T. Schoebel-Theuer] Side effects caused by new implementation:
+ * New symlink semantics: when open() is called with flags O_CREAT | O_EXCL
+ * and the name already exists in form of a symlink, try to create the new
+ * name indicated by the symlink. The old code always complained that the
+ * name already exists, due to not following the symlink even if its target
+ * is nonexistent.  The new semantics affects also mknod() and link() when
+ * the name is a symlink pointing to a non-existant name.
+ *
+ * I don't know which semantics is the right one, since I have no access
+ * to standards. But I found by trial that HP-UX 9.0 has the full "new"
+ * semantics implemented, while SunOS 4.1.1 and Solaris (SunOS 5.4) have the
+ * "old" one. Personally, I think the new semantics is much more logical.
+ * Note that "ln old new" where "new" is a symlink pointing to a non-existing
+ * file does succeed in both HP-UX and SunOs, but not in Solaris
+ * and in the old Linux semantics.
+ */
+
+/* [16-Dec-97 Kevin Buhr] For security reasons, we change some symlink
+ * semantics.  See the comments in "open_namei" and "do_link" below.
+ *
+ * [10-Sep-98 Alan Modra] Another symlink change.
+ */
+
+/* [Feb-Apr 2000 AV] Complete rewrite. Rules for symlinks:
+ *	inside the path - always follow.
+ *	in the last component in creation/removal/renaming - never follow.
+ *	if LOOKUP_FOLLOW passed - follow.
+ *	if the pathname has trailing slashes - follow.
+ *	otherwise - don't follow.
+ * (applied in that order).
+ *
+ * [Jun 2000 AV] Inconsistent behaviour of open() in case if flags==O_CREAT
+ * restored for 2.4. This is the last surviving part of old 4.2BSD bug.
+ * During the 2.4 we need to fix the userland stuff depending on it -
+ * hopefully we will be able to get rid of that wart in 2.5. So far only
+ * XEmacs seems to be relying on it...
+ */
+/*
+ * [Sep 2001 AV] Single-semaphore locking scheme (kudos to David Holland)
+ * implemented.  Let's see if raised priority of ->s_vfs_rename_sem gives
+ * any extra contention...
+ */
+
+/* In order to reduce some races, while at the same time doing additional
+ * checking and hopefully speeding things up, we copy filenames to the
+ * kernel data space before using them..
+ *
+ * POSIX.1 2.4: an empty pathname is invalid (ENOENT).
+ * PATH_MAX includes the nul terminator --RR.
+ */
+static inline int do_getname(const char __user *filename, char *page)
+{
+	int retval;
+	unsigned long len = PATH_MAX;
+
+	if (!segment_eq(get_fs(), KERNEL_DS)) {
+		if ((unsigned long) filename >= TASK_SIZE)
+			return -EFAULT;
+		if (TASK_SIZE - (unsigned long) filename < PATH_MAX)
+			len = TASK_SIZE - (unsigned long) filename;
+	}
+
+	retval = strncpy_from_user(page, filename, len);
+	if (retval > 0) {
+		if (retval < len)
+			return 0;
+		return -ENAMETOOLONG;
+	} else if (!retval)
+		retval = -ENOENT;
+	return retval;
+}
+
+char * getname(const char __user * filename)
+{
+	char *tmp, *result;
+
+	result = ERR_PTR(-ENOMEM);
+	tmp = __getname();
+	if (tmp)  {
+		int retval = do_getname(filename, tmp);
+
+		result = tmp;
+		if (retval < 0) {
+			__putname(tmp);
+			result = ERR_PTR(retval);
+		}
+	}
+	audit_getname(result);
+	return result;
+}
+
+#ifdef CONFIG_AUDITSYSCALL
+void putname(const char *name)
+{
+	if (unlikely(current->audit_context))
+		audit_putname(name);
+	else
+		__putname(name);
+}
+EXPORT_SYMBOL(putname);
+#endif
+
+
+/**
+ * generic_permission  -  check for access rights on a Posix-like filesystem
+ * @inode:	inode to check access rights for
+ * @mask:	right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
+ * @check_acl:	optional callback to check for Posix ACLs
+ *
+ * Used to check for read/write/execute permissions on a file.
+ * We use "fsuid" for this, letting us set arbitrary permissions
+ * for filesystem access without changing the "normal" uids which
+ * are used for other things..
+ */
+int generic_permission(struct inode *inode, int mask,
+		int (*check_acl)(struct inode *inode, int mask))
+{
+	umode_t			mode = inode->i_mode;
+
+	if (current->fsuid == inode->i_uid)
+		mode >>= 6;
+	else {
+		if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
+			int error = check_acl(inode, mask);
+			if (error == -EACCES)
+				goto check_capabilities;
+			else if (error != -EAGAIN)
+				return error;
+		}
+
+		if (in_group_p(inode->i_gid))
+			mode >>= 3;
+	}
+
+	/*
+	 * If the DACs are ok we don't need any capability check.
+	 */
+	if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))
+		return 0;
+
+ check_capabilities:
+	/*
+	 * Read/write DACs are always overridable.
+	 * Executable DACs are overridable if at least one exec bit is set.
+	 */
+	if (!(mask & MAY_EXEC) ||
+	    (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode))
+		if (capable(CAP_DAC_OVERRIDE))
+			return 0;
+
+	/*
+	 * Searching includes executable on directories, else just read.
+	 */
+	if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))
+		if (capable(CAP_DAC_READ_SEARCH))
+			return 0;
+
+	return -EACCES;
+}
+
+int permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+	int retval, submask;
+
+	if (mask & MAY_WRITE) {
+		umode_t mode = inode->i_mode;
+
+		/*
+		 * Nobody gets write access to a read-only fs.
+		 */
+		if (IS_RDONLY(inode) &&
+		    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
+			return -EROFS;
+
+		/*
+		 * Nobody gets write access to an immutable file.
+		 */
+		if (IS_IMMUTABLE(inode))
+			return -EACCES;
+	}
+
+
+	/* Ordinary permission routines do not understand MAY_APPEND. */
+	submask = mask & ~MAY_APPEND;
+	if (inode->i_op && inode->i_op->permission)
+		retval = inode->i_op->permission(inode, submask, nd);
+	else
+		retval = generic_permission(inode, submask, NULL);
+	if (retval)
+		return retval;
+
+	return security_inode_permission(inode, mask, nd);
+}
+
+/**
+ * vfs_permission  -  check for access rights to a given path
+ * @nd:		lookup result that describes the path
+ * @mask:	right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
+ *
+ * Used to check for read/write/execute permissions on a path.
+ * We use "fsuid" for this, letting us set arbitrary permissions
+ * for filesystem access without changing the "normal" uids which
+ * are used for other things.
+ */
+int vfs_permission(struct nameidata *nd, int mask)
+{
+	return permission(nd->dentry->d_inode, mask, nd);
+}
+
+/**
+ * file_permission  -  check for additional access rights to a given file
+ * @file:	file to check access rights for
+ * @mask:	right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
+ *
+ * Used to check for read/write/execute permissions on an already opened
+ * file.
+ *
+ * Note:
+ *	Do not use this function in new code.  All access checks should
+ *	be done using vfs_permission().
+ */
+int file_permission(struct file *file, int mask)
+{
+	return permission(file->f_dentry->d_inode, mask, NULL);
+}
+
+/*
+ * get_write_access() gets write permission for a file.
+ * put_write_access() releases this write permission.
+ * This is used for regular files.
+ * We cannot support write (and maybe mmap read-write shared) accesses and
+ * MAP_DENYWRITE mmappings simultaneously. The i_writecount field of an inode
+ * can have the following values:
+ * 0: no writers, no VM_DENYWRITE mappings
+ * < 0: (-i_writecount) vm_area_structs with VM_DENYWRITE set exist
+ * > 0: (i_writecount) users are writing to the file.
+ *
+ * Normally we operate on that counter with atomic_{inc,dec} and it's safe
+ * except for the cases where we don't hold i_writecount yet. Then we need to
+ * use {get,deny}_write_access() - these functions check the sign and refuse
+ * to do the change if sign is wrong. Exclusion between them is provided by
+ * the inode->i_lock spinlock.
+ */
+
+int get_write_access(struct inode * inode)
+{
+	spin_lock(&inode->i_lock);
+	if (atomic_read(&inode->i_writecount) < 0) {
+		spin_unlock(&inode->i_lock);
+		return -ETXTBSY;
+	}
+	atomic_inc(&inode->i_writecount);
+	spin_unlock(&inode->i_lock);
+
+	return 0;
+}
+
+int deny_write_access(struct file * file)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+
+	spin_lock(&inode->i_lock);
+	if (atomic_read(&inode->i_writecount) > 0) {
+		spin_unlock(&inode->i_lock);
+		return -ETXTBSY;
+	}
+	atomic_dec(&inode->i_writecount);
+	spin_unlock(&inode->i_lock);
+
+	return 0;
+}
+
+void path_release(struct nameidata *nd)
+{
+	dput(nd->dentry);
+	mntput(nd->mnt);
+}
+
+/*
+ * umount() mustn't call path_release()/mntput() as that would clear
+ * mnt_expiry_mark
+ */
+void path_release_on_umount(struct nameidata *nd)
+{
+	dput(nd->dentry);
+	mntput_no_expire(nd->mnt);
+}
+
+/**
+ * release_open_intent - free up open intent resources
+ * @nd: pointer to nameidata
+ */
+void release_open_intent(struct nameidata *nd)
+{
+	if (nd->intent.open.file->f_dentry == NULL)
+		put_filp(nd->intent.open.file);
+	else
+		fput(nd->intent.open.file);
+}
+
+/*
+ * Internal lookup() using the new generic dcache.
+ * SMP-safe
+ */
+static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
+{
+	struct dentry * dentry = __d_lookup(parent, name);
+
+	/* lockess __d_lookup may fail due to concurrent d_move() 
+	 * in some unrelated directory, so try with d_lookup
+	 */
+	if (!dentry)
+		dentry = d_lookup(parent, name);
+
+	if (dentry && dentry->d_op && dentry->d_op->d_revalidate) {
+		if (!dentry->d_op->d_revalidate(dentry, nd) && !d_invalidate(dentry)) {
+			dput(dentry);
+			dentry = NULL;
+		}
+	}
+	return dentry;
+}
+
+/*
+ * Short-cut version of permission(), for calling by
+ * path_walk(), when dcache lock is held.  Combines parts
+ * of permission() and generic_permission(), and tests ONLY for
+ * MAY_EXEC permission.
+ *
+ * If appropriate, check DAC only.  If not appropriate, or
+ * short-cut DAC fails, then call permission() to do more
+ * complete permission check.
+ */
+static inline int exec_permission_lite(struct inode *inode,
+				       struct nameidata *nd)
+{
+	umode_t	mode = inode->i_mode;
+
+	if (inode->i_op && inode->i_op->permission)
+		return -EAGAIN;
+
+	if (current->fsuid == inode->i_uid)
+		mode >>= 6;
+	else if (in_group_p(inode->i_gid))
+		mode >>= 3;
+
+	if (mode & MAY_EXEC)
+		goto ok;
+
+	if ((inode->i_mode & S_IXUGO) && capable(CAP_DAC_OVERRIDE))
+		goto ok;
+
+	if (S_ISDIR(inode->i_mode) && capable(CAP_DAC_OVERRIDE))
+		goto ok;
+
+	if (S_ISDIR(inode->i_mode) && capable(CAP_DAC_READ_SEARCH))
+		goto ok;
+
+	return -EACCES;
+ok:
+	return security_inode_permission(inode, MAY_EXEC, nd);
+}
+
+/*
+ * This is called when everything else fails, and we actually have
+ * to go to the low-level filesystem to find out what we should do..
+ *
+ * We get the directory semaphore, and after getting that we also
+ * make sure that nobody added the entry to the dcache in the meantime..
+ * SMP-safe
+ */
+static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
+{
+	struct dentry * result;
+	struct inode *dir = parent->d_inode;
+
+	down(&dir->i_sem);
+	/*
+	 * First re-do the cached lookup just in case it was created
+	 * while we waited for the directory semaphore..
+	 *
+	 * FIXME! This could use version numbering or similar to
+	 * avoid unnecessary cache lookups.
+	 *
+	 * The "dcache_lock" is purely to protect the RCU list walker
+	 * from concurrent renames at this point (we mustn't get false
+	 * negatives from the RCU list walk here, unlike the optimistic
+	 * fast walk).
+	 *
+	 * so doing d_lookup() (with seqlock), instead of lockfree __d_lookup
+	 */
+	result = d_lookup(parent, name);
+	if (!result) {
+		struct dentry * dentry = d_alloc(parent, name);
+		result = ERR_PTR(-ENOMEM);
+		if (dentry) {
+			result = dir->i_op->lookup(dir, dentry, nd);
+			if (result)
+				dput(dentry);
+			else
+				result = dentry;
+		}
+		up(&dir->i_sem);
+		return result;
+	}
+
+	/*
+	 * Uhhuh! Nasty case: the cache was re-populated while
+	 * we waited on the semaphore. Need to revalidate.
+	 */
+	up(&dir->i_sem);
+	if (result->d_op && result->d_op->d_revalidate) {
+		if (!result->d_op->d_revalidate(result, nd) && !d_invalidate(result)) {
+			dput(result);
+			result = ERR_PTR(-ENOENT);
+		}
+	}
+	return result;
+}
+
+static int __emul_lookup_dentry(const char *, struct nameidata *);
+
+/* SMP-safe */
+static inline int
+walk_init_root(const char *name, struct nameidata *nd)
+{
+	read_lock(&current->fs->lock);
+	if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
+		nd->mnt = mntget(current->fs->altrootmnt);
+		nd->dentry = dget(current->fs->altroot);
+		read_unlock(&current->fs->lock);
+		if (__emul_lookup_dentry(name,nd))
+			return 0;
+		read_lock(&current->fs->lock);
+	}
+	nd->mnt = mntget(current->fs->rootmnt);
+	nd->dentry = dget(current->fs->root);
+	read_unlock(&current->fs->lock);
+	return 1;
+}
+
+static inline int __vfs_follow_link(struct nameidata *nd, const char *link)
+{
+	int res = 0;
+	char *name;
+	if (IS_ERR(link))
+		goto fail;
+
+	if (*link == '/') {
+		path_release(nd);
+		if (!walk_init_root(link, nd))
+			/* weird __emul_prefix() stuff did it */
+			goto out;
+	}
+	res = link_path_walk(link, nd);
+out:
+	if (nd->depth || res || nd->last_type!=LAST_NORM)
+		return res;
+	/*
+	 * If it is an iterative symlinks resolution in open_namei() we
+	 * have to copy the last component. And all that crap because of
+	 * bloody create() on broken symlinks. Furrfu...
+	 */
+	name = __getname();
+	if (unlikely(!name)) {
+		path_release(nd);
+		return -ENOMEM;
+	}
+	strcpy(name, nd->last.name);
+	nd->last.name = name;
+	return 0;
+fail:
+	path_release(nd);
+	return PTR_ERR(link);
+}
+
+struct path {
+	struct vfsmount *mnt;
+	struct dentry *dentry;
+};
+
+static inline int __do_follow_link(struct path *path, struct nameidata *nd)
+{
+	int error;
+	void *cookie;
+	struct dentry *dentry = path->dentry;
+
+	touch_atime(path->mnt, dentry);
+	nd_set_link(nd, NULL);
+
+	if (path->mnt == nd->mnt)
+		mntget(path->mnt);
+	cookie = dentry->d_inode->i_op->follow_link(dentry, nd);
+	error = PTR_ERR(cookie);
+	if (!IS_ERR(cookie)) {
+		char *s = nd_get_link(nd);
+		error = 0;
+		if (s)
+			error = __vfs_follow_link(nd, s);
+		if (dentry->d_inode->i_op->put_link)
+			dentry->d_inode->i_op->put_link(dentry, nd, cookie);
+	}
+	dput(dentry);
+	mntput(path->mnt);
+
+	return error;
+}
+
+static inline void dput_path(struct path *path, struct nameidata *nd)
+{
+	dput(path->dentry);
+	if (path->mnt != nd->mnt)
+		mntput(path->mnt);
+}
+
+static inline void path_to_nameidata(struct path *path, struct nameidata *nd)
+{
+	dput(nd->dentry);
+	if (nd->mnt != path->mnt)
+		mntput(nd->mnt);
+	nd->mnt = path->mnt;
+	nd->dentry = path->dentry;
+}
+
+/*
+ * This limits recursive symlink follows to 8, while
+ * limiting consecutive symlinks to 40.
+ *
+ * Without that kind of total limit, nasty chains of consecutive
+ * symlinks can cause almost arbitrarily long lookups. 
+ */
+static inline int do_follow_link(struct path *path, struct nameidata *nd)
+{
+	int err = -ELOOP;
+	if (current->link_count >= MAX_NESTED_LINKS)
+		goto loop;
+	if (current->total_link_count >= 40)
+		goto loop;
+	BUG_ON(nd->depth >= MAX_NESTED_LINKS);
+	cond_resched();
+	err = security_inode_follow_link(path->dentry, nd);
+	if (err)
+		goto loop;
+	current->link_count++;
+	current->total_link_count++;
+	nd->depth++;
+	err = __do_follow_link(path, nd);
+	current->link_count--;
+	nd->depth--;
+	return err;
+loop:
+	dput_path(path, nd);
+	path_release(nd);
+	return err;
+}
+
+int follow_up(struct vfsmount **mnt, struct dentry **dentry)
+{
+	struct vfsmount *parent;
+	struct dentry *mountpoint;
+	spin_lock(&vfsmount_lock);
+	parent=(*mnt)->mnt_parent;
+	if (parent == *mnt) {
+		spin_unlock(&vfsmount_lock);
+		return 0;
+	}
+	mntget(parent);
+	mountpoint=dget((*mnt)->mnt_mountpoint);
+	spin_unlock(&vfsmount_lock);
+	dput(*dentry);
+	*dentry = mountpoint;
+	mntput(*mnt);
+	*mnt = parent;
+	return 1;
+}
+
+/* no need for dcache_lock, as serialization is taken care in
+ * namespace.c
+ */
+static int __follow_mount(struct path *path)
+{
+	int res = 0;
+	while (d_mountpoint(path->dentry)) {
+		struct vfsmount *mounted = lookup_mnt(path->mnt, path->dentry);
+		if (!mounted)
+			break;
+		dput(path->dentry);
+		if (res)
+			mntput(path->mnt);
+		path->mnt = mounted;
+		path->dentry = dget(mounted->mnt_root);
+		res = 1;
+	}
+	return res;
+}
+
+static void follow_mount(struct vfsmount **mnt, struct dentry **dentry)
+{
+	while (d_mountpoint(*dentry)) {
+		struct vfsmount *mounted = lookup_mnt(*mnt, *dentry);
+		if (!mounted)
+			break;
+		dput(*dentry);
+		mntput(*mnt);
+		*mnt = mounted;
+		*dentry = dget(mounted->mnt_root);
+	}
+}
+
+/* no need for dcache_lock, as serialization is taken care in
+ * namespace.c
+ */
+int follow_down(struct vfsmount **mnt, struct dentry **dentry)
+{
+	struct vfsmount *mounted;
+
+	mounted = lookup_mnt(*mnt, *dentry);
+	if (mounted) {
+		dput(*dentry);
+		mntput(*mnt);
+		*mnt = mounted;
+		*dentry = dget(mounted->mnt_root);
+		return 1;
+	}
+	return 0;
+}
+
+static inline void follow_dotdot(struct nameidata *nd)
+{
+	while(1) {
+		struct vfsmount *parent;
+		struct dentry *old = nd->dentry;
+
+                read_lock(&current->fs->lock);
+		if (nd->dentry == current->fs->root &&
+		    nd->mnt == current->fs->rootmnt) {
+                        read_unlock(&current->fs->lock);
+			break;
+		}
+                read_unlock(&current->fs->lock);
+		spin_lock(&dcache_lock);
+		if (nd->dentry != nd->mnt->mnt_root) {
+			nd->dentry = dget(nd->dentry->d_parent);
+			spin_unlock(&dcache_lock);
+			dput(old);
+			break;
+		}
+		spin_unlock(&dcache_lock);
+		spin_lock(&vfsmount_lock);
+		parent = nd->mnt->mnt_parent;
+		if (parent == nd->mnt) {
+			spin_unlock(&vfsmount_lock);
+			break;
+		}
+		mntget(parent);
+		nd->dentry = dget(nd->mnt->mnt_mountpoint);
+		spin_unlock(&vfsmount_lock);
+		dput(old);
+		mntput(nd->mnt);
+		nd->mnt = parent;
+	}
+	follow_mount(&nd->mnt, &nd->dentry);
+}
+
+/*
+ *  It's more convoluted than I'd like it to be, but... it's still fairly
+ *  small and for now I'd prefer to have fast path as straight as possible.
+ *  It _is_ time-critical.
+ */
+static int do_lookup(struct nameidata *nd, struct qstr *name,
+		     struct path *path)
+{
+	struct vfsmount *mnt = nd->mnt;
+	struct dentry *dentry = __d_lookup(nd->dentry, name);
+
+	if (!dentry)
+		goto need_lookup;
+	if (dentry->d_op && dentry->d_op->d_revalidate)
+		goto need_revalidate;
+done:
+	path->mnt = mnt;
+	path->dentry = dentry;
+	__follow_mount(path);
+	return 0;
+
+need_lookup:
+	dentry = real_lookup(nd->dentry, name, nd);
+	if (IS_ERR(dentry))
+		goto fail;
+	goto done;
+
+need_revalidate:
+	if (dentry->d_op->d_revalidate(dentry, nd))
+		goto done;
+	if (d_invalidate(dentry))
+		goto done;
+	dput(dentry);
+	goto need_lookup;
+
+fail:
+	return PTR_ERR(dentry);
+}
+
+/*
+ * Name resolution.
+ * This is the basic name resolution function, turning a pathname into
+ * the final dentry. We expect 'base' to be positive and a directory.
+ *
+ * Returns 0 and nd will have valid dentry and mnt on success.
+ * Returns error and drops reference to input namei data on failure.
+ */
+static fastcall int __link_path_walk(const char * name, struct nameidata *nd)
+{
+	struct path next;
+	struct inode *inode;
+	int err;
+	unsigned int lookup_flags = nd->flags;
+	
+	while (*name=='/')
+		name++;
+	if (!*name)
+		goto return_reval;
+
+	inode = nd->dentry->d_inode;
+	if (nd->depth)
+		lookup_flags = LOOKUP_FOLLOW;
+
+	/* At this point we know we have a real path component. */
+	for(;;) {
+		unsigned long hash;
+		struct qstr this;
+		unsigned int c;
+
+		nd->flags |= LOOKUP_CONTINUE;
+		err = exec_permission_lite(inode, nd);
+		if (err == -EAGAIN)
+			err = vfs_permission(nd, MAY_EXEC);
+ 		if (err)
+			break;
+
+		this.name = name;
+		c = *(const unsigned char *)name;
+
+		hash = init_name_hash();
+		do {
+			name++;
+			hash = partial_name_hash(c, hash);
+			c = *(const unsigned char *)name;
+		} while (c && (c != '/'));
+		this.len = name - (const char *) this.name;
+		this.hash = end_name_hash(hash);
+
+		/* remove trailing slashes? */
+		if (!c)
+			goto last_component;
+		while (*++name == '/');
+		if (!*name)
+			goto last_with_slashes;
+
+		/*
+		 * "." and ".." are special - ".." especially so because it has
+		 * to be able to know about the current root directory and
+		 * parent relationships.
+		 */
+		if (this.name[0] == '.') switch (this.len) {
+			default:
+				break;
+			case 2:	
+				if (this.name[1] != '.')
+					break;
+				follow_dotdot(nd);
+				inode = nd->dentry->d_inode;
+				/* fallthrough */
+			case 1:
+				continue;
+		}
+		/*
+		 * See if the low-level filesystem might want
+		 * to use its own hash..
+		 */
+		if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
+			err = nd->dentry->d_op->d_hash(nd->dentry, &this);
+			if (err < 0)
+				break;
+		}
+		/* This does the actual lookups.. */
+		err = do_lookup(nd, &this, &next);
+		if (err)
+			break;
+
+		err = -ENOENT;
+		inode = next.dentry->d_inode;
+		if (!inode)
+			goto out_dput;
+		err = -ENOTDIR; 
+		if (!inode->i_op)
+			goto out_dput;
+
+		if (inode->i_op->follow_link) {
+			err = do_follow_link(&next, nd);
+			if (err)
+				goto return_err;
+			err = -ENOENT;
+			inode = nd->dentry->d_inode;
+			if (!inode)
+				break;
+			err = -ENOTDIR; 
+			if (!inode->i_op)
+				break;
+		} else
+			path_to_nameidata(&next, nd);
+		err = -ENOTDIR; 
+		if (!inode->i_op->lookup)
+			break;
+		continue;
+		/* here ends the main loop */
+
+last_with_slashes:
+		lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
+last_component:
+		nd->flags &= ~LOOKUP_CONTINUE;
+		if (lookup_flags & LOOKUP_PARENT)
+			goto lookup_parent;
+		if (this.name[0] == '.') switch (this.len) {
+			default:
+				break;
+			case 2:	
+				if (this.name[1] != '.')
+					break;
+				follow_dotdot(nd);
+				inode = nd->dentry->d_inode;
+				/* fallthrough */
+			case 1:
+				goto return_reval;
+		}
+		if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
+			err = nd->dentry->d_op->d_hash(nd->dentry, &this);
+			if (err < 0)
+				break;
+		}
+		err = do_lookup(nd, &this, &next);
+		if (err)
+			break;
+		inode = next.dentry->d_inode;
+		if ((lookup_flags & LOOKUP_FOLLOW)
+		    && inode && inode->i_op && inode->i_op->follow_link) {
+			err = do_follow_link(&next, nd);
+			if (err)
+				goto return_err;
+			inode = nd->dentry->d_inode;
+		} else
+			path_to_nameidata(&next, nd);
+		err = -ENOENT;
+		if (!inode)
+			break;
+		if (lookup_flags & LOOKUP_DIRECTORY) {
+			err = -ENOTDIR; 
+			if (!inode->i_op || !inode->i_op->lookup)
+				break;
+		}
+		goto return_base;
+lookup_parent:
+		nd->last = this;
+		nd->last_type = LAST_NORM;
+		if (this.name[0] != '.')
+			goto return_base;
+		if (this.len == 1)
+			nd->last_type = LAST_DOT;
+		else if (this.len == 2 && this.name[1] == '.')
+			nd->last_type = LAST_DOTDOT;
+		else
+			goto return_base;
+return_reval:
+		/*
+		 * We bypassed the ordinary revalidation routines.
+		 * We may need to check the cached dentry for staleness.
+		 */
+		if (nd->dentry && nd->dentry->d_sb &&
+		    (nd->dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) {
+			err = -ESTALE;
+			/* Note: we do not d_invalidate() */
+			if (!nd->dentry->d_op->d_revalidate(nd->dentry, nd))
+				break;
+		}
+return_base:
+		return 0;
+out_dput:
+		dput_path(&next, nd);
+		break;
+	}
+	path_release(nd);
+return_err:
+	return err;
+}
+
+/*
+ * Wrapper to retry pathname resolution whenever the underlying
+ * file system returns an ESTALE.
+ *
+ * Retry the whole path once, forcing real lookup requests
+ * instead of relying on the dcache.
+ */
+int fastcall link_path_walk(const char *name, struct nameidata *nd)
+{
+	struct nameidata save = *nd;
+	int result;
+
+	/* make sure the stuff we saved doesn't go away */
+	dget(save.dentry);
+	mntget(save.mnt);
+
+	result = __link_path_walk(name, nd);
+	if (result == -ESTALE) {
+		*nd = save;
+		dget(nd->dentry);
+		mntget(nd->mnt);
+		nd->flags |= LOOKUP_REVAL;
+		result = __link_path_walk(name, nd);
+	}
+
+	dput(save.dentry);
+	mntput(save.mnt);
+
+	return result;
+}
+
+int fastcall path_walk(const char * name, struct nameidata *nd)
+{
+	current->total_link_count = 0;
+	return link_path_walk(name, nd);
+}
+
+/* 
+ * SMP-safe: Returns 1 and nd will have valid dentry and mnt, if
+ * everything is done. Returns 0 and drops input nd, if lookup failed;
+ */
+static int __emul_lookup_dentry(const char *name, struct nameidata *nd)
+{
+	if (path_walk(name, nd))
+		return 0;		/* something went wrong... */
+
+	if (!nd->dentry->d_inode || S_ISDIR(nd->dentry->d_inode->i_mode)) {
+		struct dentry *old_dentry = nd->dentry;
+		struct vfsmount *old_mnt = nd->mnt;
+		struct qstr last = nd->last;
+		int last_type = nd->last_type;
+		/*
+		 * NAME was not found in alternate root or it's a directory.  Try to find
+		 * it in the normal root:
+		 */
+		nd->last_type = LAST_ROOT;
+		read_lock(&current->fs->lock);
+		nd->mnt = mntget(current->fs->rootmnt);
+		nd->dentry = dget(current->fs->root);
+		read_unlock(&current->fs->lock);
+		if (path_walk(name, nd) == 0) {
+			if (nd->dentry->d_inode) {
+				dput(old_dentry);
+				mntput(old_mnt);
+				return 1;
+			}
+			path_release(nd);
+		}
+		nd->dentry = old_dentry;
+		nd->mnt = old_mnt;
+		nd->last = last;
+		nd->last_type = last_type;
+	}
+	return 1;
+}
+
+void set_fs_altroot(void)
+{
+	char *emul = __emul_prefix();
+	struct nameidata nd;
+	struct vfsmount *mnt = NULL, *oldmnt;
+	struct dentry *dentry = NULL, *olddentry;
+	int err;
+
+	if (!emul)
+		goto set_it;
+	err = path_lookup(emul, LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_NOALT, &nd);
+	if (!err) {
+		mnt = nd.mnt;
+		dentry = nd.dentry;
+	}
+set_it:
+	write_lock(&current->fs->lock);
+	oldmnt = current->fs->altrootmnt;
+	olddentry = current->fs->altroot;
+	current->fs->altrootmnt = mnt;
+	current->fs->altroot = dentry;
+	write_unlock(&current->fs->lock);
+	if (olddentry) {
+		dput(olddentry);
+		mntput(oldmnt);
+	}
+}
+
+/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
+int fastcall path_lookup(const char *name, unsigned int flags, struct nameidata *nd)
+{
+	int retval = 0;
+
+	nd->last_type = LAST_ROOT; /* if there are only slashes... */
+	nd->flags = flags;
+	nd->depth = 0;
+
+	read_lock(&current->fs->lock);
+	if (*name=='/') {
+		if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
+			nd->mnt = mntget(current->fs->altrootmnt);
+			nd->dentry = dget(current->fs->altroot);
+			read_unlock(&current->fs->lock);
+			if (__emul_lookup_dentry(name,nd))
+				goto out; /* found in altroot */
+			read_lock(&current->fs->lock);
+		}
+		nd->mnt = mntget(current->fs->rootmnt);
+		nd->dentry = dget(current->fs->root);
+	} else {
+		nd->mnt = mntget(current->fs->pwdmnt);
+		nd->dentry = dget(current->fs->pwd);
+	}
+	read_unlock(&current->fs->lock);
+	current->total_link_count = 0;
+	retval = link_path_walk(name, nd);
+out:
+	if (nd && nd->dentry && nd->dentry->d_inode)
+		audit_inode(name, nd->dentry->d_inode, flags);
+	return retval;
+}
+
+static int __path_lookup_intent_open(const char *name, unsigned int lookup_flags,
+		struct nameidata *nd, int open_flags, int create_mode)
+{
+	struct file *filp = get_empty_filp();
+	int err;
+
+	if (filp == NULL)
+		return -ENFILE;
+	nd->intent.open.file = filp;
+	nd->intent.open.flags = open_flags;
+	nd->intent.open.create_mode = create_mode;
+	err = path_lookup(name, lookup_flags|LOOKUP_OPEN, nd);
+	if (IS_ERR(nd->intent.open.file)) {
+		if (err == 0) {
+			err = PTR_ERR(nd->intent.open.file);
+			path_release(nd);
+		}
+	} else if (err != 0)
+		release_open_intent(nd);
+	return err;
+}
+
+/**
+ * path_lookup_open - lookup a file path with open intent
+ * @name: pointer to file name
+ * @lookup_flags: lookup intent flags
+ * @nd: pointer to nameidata
+ * @open_flags: open intent flags
+ */
+int path_lookup_open(const char *name, unsigned int lookup_flags,
+		struct nameidata *nd, int open_flags)
+{
+	return __path_lookup_intent_open(name, lookup_flags, nd,
+			open_flags, 0);
+}
+
+/**
+ * path_lookup_create - lookup a file path with open + create intent
+ * @name: pointer to file name
+ * @lookup_flags: lookup intent flags
+ * @nd: pointer to nameidata
+ * @open_flags: open intent flags
+ * @create_mode: create intent flags
+ */
+static int path_lookup_create(const char *name, unsigned int lookup_flags,
+			      struct nameidata *nd, int open_flags,
+			      int create_mode)
+{
+	return __path_lookup_intent_open(name, lookup_flags|LOOKUP_CREATE, nd,
+			open_flags, create_mode);
+}
+
+int __user_path_lookup_open(const char __user *name, unsigned int lookup_flags,
+		struct nameidata *nd, int open_flags)
+{
+	char *tmp = getname(name);
+	int err = PTR_ERR(tmp);
+
+	if (!IS_ERR(tmp)) {
+		err = __path_lookup_intent_open(tmp, lookup_flags, nd, open_flags, 0);
+		putname(tmp);
+	}
+	return err;
+}
+
+/*
+ * Restricted form of lookup. Doesn't follow links, single-component only,
+ * needs parent already locked. Doesn't follow mounts.
+ * SMP-safe.
+ */
+static struct dentry * __lookup_hash(struct qstr *name, struct dentry * base, struct nameidata *nd)
+{
+	struct dentry * dentry;
+	struct inode *inode;
+	int err;
+
+	inode = base->d_inode;
+	err = permission(inode, MAY_EXEC, nd);
+	dentry = ERR_PTR(err);
+	if (err)
+		goto out;
+
+	/*
+	 * See if the low-level filesystem might want
+	 * to use its own hash..
+	 */
+	if (base->d_op && base->d_op->d_hash) {
+		err = base->d_op->d_hash(base, name);
+		dentry = ERR_PTR(err);
+		if (err < 0)
+			goto out;
+	}
+
+	dentry = cached_lookup(base, name, nd);
+	if (!dentry) {
+		struct dentry *new = d_alloc(base, name);
+		dentry = ERR_PTR(-ENOMEM);
+		if (!new)
+			goto out;
+		dentry = inode->i_op->lookup(inode, new, nd);
+		if (!dentry)
+			dentry = new;
+		else
+			dput(new);
+	}
+out:
+	return dentry;
+}
+
+struct dentry * lookup_hash(struct nameidata *nd)
+{
+	return __lookup_hash(&nd->last, nd->dentry, nd);
+}
+
+/* SMP-safe */
+struct dentry * lookup_one_len(const char * name, struct dentry * base, int len)
+{
+	unsigned long hash;
+	struct qstr this;
+	unsigned int c;
+
+	this.name = name;
+	this.len = len;
+	if (!len)
+		goto access;
+
+	hash = init_name_hash();
+	while (len--) {
+		c = *(const unsigned char *)name++;
+		if (c == '/' || c == '\0')
+			goto access;
+		hash = partial_name_hash(c, hash);
+	}
+	this.hash = end_name_hash(hash);
+
+	return __lookup_hash(&this, base, NULL);
+access:
+	return ERR_PTR(-EACCES);
+}
+
+/*
+ *	namei()
+ *
+ * is used by most simple commands to get the inode of a specified name.
+ * Open, link etc use their own routines, but this is enough for things
+ * like 'chmod' etc.
+ *
+ * namei exists in two versions: namei/lnamei. The only difference is
+ * that namei follows links, while lnamei does not.
+ * SMP-safe
+ */
+int fastcall __user_walk(const char __user *name, unsigned flags, struct nameidata *nd)
+{
+	char *tmp = getname(name);
+	int err = PTR_ERR(tmp);
+
+	if (!IS_ERR(tmp)) {
+		err = path_lookup(tmp, flags, nd);
+		putname(tmp);
+	}
+	return err;
+}
+
+/*
+ * It's inline, so penalty for filesystems that don't use sticky bit is
+ * minimal.
+ */
+static inline int check_sticky(struct inode *dir, struct inode *inode)
+{
+	if (!(dir->i_mode & S_ISVTX))
+		return 0;
+	if (inode->i_uid == current->fsuid)
+		return 0;
+	if (dir->i_uid == current->fsuid)
+		return 0;
+	return !capable(CAP_FOWNER);
+}
+
+/*
+ *	Check whether we can remove a link victim from directory dir, check
+ *  whether the type of victim is right.
+ *  1. We can't do it if dir is read-only (done in permission())
+ *  2. We should have write and exec permissions on dir
+ *  3. We can't remove anything from append-only dir
+ *  4. We can't do anything with immutable dir (done in permission())
+ *  5. If the sticky bit on dir is set we should either
+ *	a. be owner of dir, or
+ *	b. be owner of victim, or
+ *	c. have CAP_FOWNER capability
+ *  6. If the victim is append-only or immutable we can't do antyhing with
+ *     links pointing to it.
+ *  7. If we were asked to remove a directory and victim isn't one - ENOTDIR.
+ *  8. If we were asked to remove a non-directory and victim isn't one - EISDIR.
+ *  9. We can't remove a root or mountpoint.
+ * 10. We don't allow removal of NFS sillyrenamed files; it's handled by
+ *     nfs_async_unlink().
+ */
+static inline int may_delete(struct inode *dir,struct dentry *victim,int isdir)
+{
+	int error;
+
+	if (!victim->d_inode)
+		return -ENOENT;
+
+	BUG_ON(victim->d_parent->d_inode != dir);
+	audit_inode_child(victim->d_name.name, victim->d_inode, dir->i_ino);
+
+	error = permission(dir,MAY_WRITE | MAY_EXEC, NULL);
+	if (error)
+		return error;
+	if (IS_APPEND(dir))
+		return -EPERM;
+	if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)||
+	    IS_IMMUTABLE(victim->d_inode))
+		return -EPERM;
+	if (isdir) {
+		if (!S_ISDIR(victim->d_inode->i_mode))
+			return -ENOTDIR;
+		if (IS_ROOT(victim))
+			return -EBUSY;
+	} else if (S_ISDIR(victim->d_inode->i_mode))
+		return -EISDIR;
+	if (IS_DEADDIR(dir))
+		return -ENOENT;
+	if (victim->d_flags & DCACHE_NFSFS_RENAMED)
+		return -EBUSY;
+	return 0;
+}
+
+/*	Check whether we can create an object with dentry child in directory
+ *  dir.
+ *  1. We can't do it if child already exists (open has special treatment for
+ *     this case, but since we are inlined it's OK)
+ *  2. We can't do it if dir is read-only (done in permission())
+ *  3. We should have write and exec permissions on dir
+ *  4. We can't do it if dir is immutable (done in permission())
+ */
+static inline int may_create(struct inode *dir, struct dentry *child,
+			     struct nameidata *nd)
+{
+	if (child->d_inode)
+		return -EEXIST;
+	if (IS_DEADDIR(dir))
+		return -ENOENT;
+	return permission(dir,MAY_WRITE | MAY_EXEC, nd);
+}
+
+/* 
+ * O_DIRECTORY translates into forcing a directory lookup.
+ */
+static inline int lookup_flags(unsigned int f)
+{
+	unsigned long retval = LOOKUP_FOLLOW;
+
+	if (f & O_NOFOLLOW)
+		retval &= ~LOOKUP_FOLLOW;
+	
+	if (f & O_DIRECTORY)
+		retval |= LOOKUP_DIRECTORY;
+
+	return retval;
+}
+
+/*
+ * p1 and p2 should be directories on the same fs.
+ */
+struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
+{
+	struct dentry *p;
+
+	if (p1 == p2) {
+		down(&p1->d_inode->i_sem);
+		return NULL;
+	}
+
+	down(&p1->d_inode->i_sb->s_vfs_rename_sem);
+
+	for (p = p1; p->d_parent != p; p = p->d_parent) {
+		if (p->d_parent == p2) {
+			down(&p2->d_inode->i_sem);
+			down(&p1->d_inode->i_sem);
+			return p;
+		}
+	}
+
+	for (p = p2; p->d_parent != p; p = p->d_parent) {
+		if (p->d_parent == p1) {
+			down(&p1->d_inode->i_sem);
+			down(&p2->d_inode->i_sem);
+			return p;
+		}
+	}
+
+	down(&p1->d_inode->i_sem);
+	down(&p2->d_inode->i_sem);
+	return NULL;
+}
+
+void unlock_rename(struct dentry *p1, struct dentry *p2)
+{
+	up(&p1->d_inode->i_sem);
+	if (p1 != p2) {
+		up(&p2->d_inode->i_sem);
+		up(&p1->d_inode->i_sb->s_vfs_rename_sem);
+	}
+}
+
+int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
+		struct nameidata *nd)
+{
+	int error = may_create(dir, dentry, nd);
+
+	if (error)
+		return error;
+
+	if (!dir->i_op || !dir->i_op->create)
+		return -EACCES;	/* shouldn't it be ENOSYS? */
+	mode &= S_IALLUGO;
+	mode |= S_IFREG;
+	error = security_inode_create(dir, dentry, mode);
+	if (error)
+		return error;
+	DQUOT_INIT(dir);
+	error = dir->i_op->create(dir, dentry, mode, nd);
+	if (!error)
+		fsnotify_create(dir, dentry);
+	return error;
+}
+
+int may_open(struct nameidata *nd, int acc_mode, int flag)
+{
+	struct dentry *dentry = nd->dentry;
+	struct inode *inode = dentry->d_inode;
+	int error;
+
+	if (!inode)
+		return -ENOENT;
+
+	if (S_ISLNK(inode->i_mode))
+		return -ELOOP;
+	
+	if (S_ISDIR(inode->i_mode) && (flag & FMODE_WRITE))
+		return -EISDIR;
+
+	error = vfs_permission(nd, acc_mode);
+	if (error)
+		return error;
+
+	/*
+	 * FIFO's, sockets and device files are special: they don't
+	 * actually live on the filesystem itself, and as such you
+	 * can write to them even if the filesystem is read-only.
+	 */
+	if (S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
+	    	flag &= ~O_TRUNC;
+	} else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
+		if (nd->mnt->mnt_flags & MNT_NODEV)
+			return -EACCES;
+
+		flag &= ~O_TRUNC;
+	} else if (IS_RDONLY(inode) && (flag & FMODE_WRITE))
+		return -EROFS;
+	/*
+	 * An append-only file must be opened in append mode for writing.
+	 */
+	if (IS_APPEND(inode)) {
+		if  ((flag & FMODE_WRITE) && !(flag & O_APPEND))
+			return -EPERM;
+		if (flag & O_TRUNC)
+			return -EPERM;
+	}
+
+	/* O_NOATIME can only be set by the owner or superuser */
+	if (flag & O_NOATIME)
+		if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER))
+			return -EPERM;
+
+	/*
+	 * Ensure there are no outstanding leases on the file.
+	 */
+	error = break_lease(inode, flag);
+	if (error)
+		return error;
+
+	if (flag & O_TRUNC) {
+		error = get_write_access(inode);
+		if (error)
+			return error;
+
+		/*
+		 * Refuse to truncate files with mandatory locks held on them.
+		 */
+		error = locks_verify_locked(inode);
+		if (!error) {
+			DQUOT_INIT(inode);
+			
+			error = do_truncate(dentry, 0, NULL);
+		}
+		put_write_access(inode);
+		if (error)
+			return error;
+	} else
+		if (flag & FMODE_WRITE)
+			DQUOT_INIT(inode);
+
+	return 0;
+}
+
+/*
+ *	open_namei()
+ *
+ * namei for open - this is in fact almost the whole open-routine.
+ *
+ * Note that the low bits of "flag" aren't the same as in the open
+ * system call - they are 00 - no permissions needed
+ *			  01 - read permission needed
+ *			  10 - write permission needed
+ *			  11 - read/write permissions needed
+ * which is a lot more logical, and also allows the "no perm" needed
+ * for symlinks (where the permissions are checked later).
+ * SMP-safe
+ */
+int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd)
+{
+	int acc_mode, error;
+	struct path path;
+	struct dentry *dir;
+	int count = 0;
+
+	acc_mode = ACC_MODE(flag);
+
+	/* O_TRUNC implies we need access checks for write permissions */
+	if (flag & O_TRUNC)
+		acc_mode |= MAY_WRITE;
+
+	/* Allow the LSM permission hook to distinguish append 
+	   access from general write access. */
+	if (flag & O_APPEND)
+		acc_mode |= MAY_APPEND;
+
+	/*
+	 * The simplest case - just a plain lookup.
+	 */
+	if (!(flag & O_CREAT)) {
+		error = path_lookup_open(pathname, lookup_flags(flag), nd, flag);
+		if (error)
+			return error;
+		goto ok;
+	}
+
+	/*
+	 * Create - we need to know the parent.
+	 */
+	error = path_lookup_create(pathname, LOOKUP_PARENT, nd, flag, mode);
+	if (error)
+		return error;
+
+	/*
+	 * We have the parent and last component. First of all, check
+	 * that we are not asked to creat(2) an obvious directory - that
+	 * will not do.
+	 */
+	error = -EISDIR;
+	if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len])
+		goto exit;
+
+	dir = nd->dentry;
+	nd->flags &= ~LOOKUP_PARENT;
+	down(&dir->d_inode->i_sem);
+	path.dentry = lookup_hash(nd);
+	path.mnt = nd->mnt;
+
+do_last:
+	error = PTR_ERR(path.dentry);
+	if (IS_ERR(path.dentry)) {
+		up(&dir->d_inode->i_sem);
+		goto exit;
+	}
+
+	/* Negative dentry, just create the file */
+	if (!path.dentry->d_inode) {
+		if (!IS_POSIXACL(dir->d_inode))
+			mode &= ~current->fs->umask;
+		error = vfs_create(dir->d_inode, path.dentry, mode, nd);
+		up(&dir->d_inode->i_sem);
+		dput(nd->dentry);
+		nd->dentry = path.dentry;
+		if (error)
+			goto exit;
+		/* Don't check for write permission, don't truncate */
+		acc_mode = 0;
+		flag &= ~O_TRUNC;
+		goto ok;
+	}
+
+	/*
+	 * It already exists.
+	 */
+	up(&dir->d_inode->i_sem);
+
+	error = -EEXIST;
+	if (flag & O_EXCL)
+		goto exit_dput;
+
+	if (__follow_mount(&path)) {
+		error = -ELOOP;
+		if (flag & O_NOFOLLOW)
+			goto exit_dput;
+	}
+	error = -ENOENT;
+	if (!path.dentry->d_inode)
+		goto exit_dput;
+	if (path.dentry->d_inode->i_op && path.dentry->d_inode->i_op->follow_link)
+		goto do_link;
+
+	path_to_nameidata(&path, nd);
+	error = -EISDIR;
+	if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))
+		goto exit;
+ok:
+	error = may_open(nd, acc_mode, flag);
+	if (error)
+		goto exit;
+	return 0;
+
+exit_dput:
+	dput_path(&path, nd);
+exit:
+	if (!IS_ERR(nd->intent.open.file))
+		release_open_intent(nd);
+	path_release(nd);
+	return error;
+
+do_link:
+	error = -ELOOP;
+	if (flag & O_NOFOLLOW)
+		goto exit_dput;
+	/*
+	 * This is subtle. Instead of calling do_follow_link() we do the
+	 * thing by hands. The reason is that this way we have zero link_count
+	 * and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT.
+	 * After that we have the parent and last component, i.e.
+	 * we are in the same situation as after the first path_walk().
+	 * Well, almost - if the last component is normal we get its copy
+	 * stored in nd->last.name and we will have to putname() it when we
+	 * are done. Procfs-like symlinks just set LAST_BIND.
+	 */
+	nd->flags |= LOOKUP_PARENT;
+	error = security_inode_follow_link(path.dentry, nd);
+	if (error)
+		goto exit_dput;
+	error = __do_follow_link(&path, nd);
+	if (error)
+		return error;
+	nd->flags &= ~LOOKUP_PARENT;
+	if (nd->last_type == LAST_BIND)
+		goto ok;
+	error = -EISDIR;
+	if (nd->last_type != LAST_NORM)
+		goto exit;
+	if (nd->last.name[nd->last.len]) {
+		__putname(nd->last.name);
+		goto exit;
+	}
+	error = -ELOOP;
+	if (count++==32) {
+		__putname(nd->last.name);
+		goto exit;
+	}
+	dir = nd->dentry;
+	down(&dir->d_inode->i_sem);
+	path.dentry = lookup_hash(nd);
+	path.mnt = nd->mnt;
+	__putname(nd->last.name);
+	goto do_last;
+}
+
+/**
+ * lookup_create - lookup a dentry, creating it if it doesn't exist
+ * @nd: nameidata info
+ * @is_dir: directory flag
+ *
+ * Simple function to lookup and return a dentry and create it
+ * if it doesn't exist.  Is SMP-safe.
+ *
+ * Returns with nd->dentry->d_inode->i_sem locked.
+ */
+struct dentry *lookup_create(struct nameidata *nd, int is_dir)
+{
+	struct dentry *dentry = ERR_PTR(-EEXIST);
+
+	down(&nd->dentry->d_inode->i_sem);
+	/*
+	 * Yucky last component or no last component at all?
+	 * (foo/., foo/.., /////)
+	 */
+	if (nd->last_type != LAST_NORM)
+		goto fail;
+	nd->flags &= ~LOOKUP_PARENT;
+
+	/*
+	 * Do the final lookup.
+	 */
+	dentry = lookup_hash(nd);
+	if (IS_ERR(dentry))
+		goto fail;
+
+	/*
+	 * Special case - lookup gave negative, but... we had foo/bar/
+	 * From the vfs_mknod() POV we just have a negative dentry -
+	 * all is fine. Let's be bastards - you had / on the end, you've
+	 * been asking for (non-existent) directory. -ENOENT for you.
+	 */
+	if (!is_dir && nd->last.name[nd->last.len] && !dentry->d_inode)
+		goto enoent;
+	return dentry;
+enoent:
+	dput(dentry);
+	dentry = ERR_PTR(-ENOENT);
+fail:
+	return dentry;
+}
+EXPORT_SYMBOL_GPL(lookup_create);
+
+int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+{
+	int error = may_create(dir, dentry, NULL);
+
+	if (error)
+		return error;
+
+	if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
+		return -EPERM;
+
+	if (!dir->i_op || !dir->i_op->mknod)
+		return -EPERM;
+
+	error = security_inode_mknod(dir, dentry, mode, dev);
+	if (error)
+		return error;
+
+	DQUOT_INIT(dir);
+	error = dir->i_op->mknod(dir, dentry, mode, dev);
+	if (!error)
+		fsnotify_create(dir, dentry);
+	return error;
+}
+
+asmlinkage long sys_mknod(const char __user * filename, int mode, unsigned dev)
+{
+	int error = 0;
+	char * tmp;
+	struct dentry * dentry;
+	struct nameidata nd;
+
+	if (S_ISDIR(mode))
+		return -EPERM;
+	tmp = getname(filename);
+	if (IS_ERR(tmp))
+		return PTR_ERR(tmp);
+
+	error = path_lookup(tmp, LOOKUP_PARENT, &nd);
+	if (error)
+		goto out;
+	dentry = lookup_create(&nd, 0);
+	error = PTR_ERR(dentry);
+
+	if (!IS_POSIXACL(nd.dentry->d_inode))
+		mode &= ~current->fs->umask;
+	if (!IS_ERR(dentry)) {
+		switch (mode & S_IFMT) {
+		case 0: case S_IFREG:
+			error = vfs_create(nd.dentry->d_inode,dentry,mode,&nd);
+			break;
+		case S_IFCHR: case S_IFBLK:
+			error = vfs_mknod(nd.dentry->d_inode,dentry,mode,
+					new_decode_dev(dev));
+			break;
+		case S_IFIFO: case S_IFSOCK:
+			error = vfs_mknod(nd.dentry->d_inode,dentry,mode,0);
+			break;
+		case S_IFDIR:
+			error = -EPERM;
+			break;
+		default:
+			error = -EINVAL;
+		}
+		dput(dentry);
+	}
+	up(&nd.dentry->d_inode->i_sem);
+	path_release(&nd);
+out:
+	putname(tmp);
+
+	return error;
+}
+
+int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	int error = may_create(dir, dentry, NULL);
+
+	if (error)
+		return error;
+
+	if (!dir->i_op || !dir->i_op->mkdir)
+		return -EPERM;
+
+	mode &= (S_IRWXUGO|S_ISVTX);
+	error = security_inode_mkdir(dir, dentry, mode);
+	if (error)
+		return error;
+
+	DQUOT_INIT(dir);
+	error = dir->i_op->mkdir(dir, dentry, mode);
+	if (!error)
+		fsnotify_mkdir(dir, dentry);
+	return error;
+}
+
+asmlinkage long sys_mkdir(const char __user * pathname, int mode)
+{
+	int error = 0;
+	char * tmp;
+
+	tmp = getname(pathname);
+	error = PTR_ERR(tmp);
+	if (!IS_ERR(tmp)) {
+		struct dentry *dentry;
+		struct nameidata nd;
+
+		error = path_lookup(tmp, LOOKUP_PARENT, &nd);
+		if (error)
+			goto out;
+		dentry = lookup_create(&nd, 1);
+		error = PTR_ERR(dentry);
+		if (!IS_ERR(dentry)) {
+			if (!IS_POSIXACL(nd.dentry->d_inode))
+				mode &= ~current->fs->umask;
+			error = vfs_mkdir(nd.dentry->d_inode, dentry, mode);
+			dput(dentry);
+		}
+		up(&nd.dentry->d_inode->i_sem);
+		path_release(&nd);
+out:
+		putname(tmp);
+	}
+
+	return error;
+}
+
+/*
+ * We try to drop the dentry early: we should have
+ * a usage count of 2 if we're the only user of this
+ * dentry, and if that is true (possibly after pruning
+ * the dcache), then we drop the dentry now.
+ *
+ * A low-level filesystem can, if it choses, legally
+ * do a
+ *
+ *	if (!d_unhashed(dentry))
+ *		return -EBUSY;
+ *
+ * if it cannot handle the case of removing a directory
+ * that is still in use by something else..
+ */
+void dentry_unhash(struct dentry *dentry)
+{
+	dget(dentry);
+	if (atomic_read(&dentry->d_count))
+		shrink_dcache_parent(dentry);
+	spin_lock(&dcache_lock);
+	spin_lock(&dentry->d_lock);
+	if (atomic_read(&dentry->d_count) == 2)
+		__d_drop(dentry);
+	spin_unlock(&dentry->d_lock);
+	spin_unlock(&dcache_lock);
+}
+
+int vfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	int error = may_delete(dir, dentry, 1);
+
+	if (error)
+		return error;
+
+	if (!dir->i_op || !dir->i_op->rmdir)
+		return -EPERM;
+
+	DQUOT_INIT(dir);
+
+	down(&dentry->d_inode->i_sem);
+	dentry_unhash(dentry);
+	if (d_mountpoint(dentry))
+		error = -EBUSY;
+	else {
+		error = security_inode_rmdir(dir, dentry);
+		if (!error) {
+			error = dir->i_op->rmdir(dir, dentry);
+			if (!error)
+				dentry->d_inode->i_flags |= S_DEAD;
+		}
+	}
+	up(&dentry->d_inode->i_sem);
+	if (!error) {
+		d_delete(dentry);
+	}
+	dput(dentry);
+
+	return error;
+}
+
+asmlinkage long sys_rmdir(const char __user * pathname)
+{
+	int error = 0;
+	char * name;
+	struct dentry *dentry;
+	struct nameidata nd;
+
+	name = getname(pathname);
+	if(IS_ERR(name))
+		return PTR_ERR(name);
+
+	error = path_lookup(name, LOOKUP_PARENT, &nd);
+	if (error)
+		goto exit;
+
+	switch(nd.last_type) {
+		case LAST_DOTDOT:
+			error = -ENOTEMPTY;
+			goto exit1;
+		case LAST_DOT:
+			error = -EINVAL;
+			goto exit1;
+		case LAST_ROOT:
+			error = -EBUSY;
+			goto exit1;
+	}
+	down(&nd.dentry->d_inode->i_sem);
+	dentry = lookup_hash(&nd);
+	error = PTR_ERR(dentry);
+	if (!IS_ERR(dentry)) {
+		error = vfs_rmdir(nd.dentry->d_inode, dentry);
+		dput(dentry);
+	}
+	up(&nd.dentry->d_inode->i_sem);
+exit1:
+	path_release(&nd);
+exit:
+	putname(name);
+	return error;
+}
+
+int vfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+	int error = may_delete(dir, dentry, 0);
+
+	if (error)
+		return error;
+
+	if (!dir->i_op || !dir->i_op->unlink)
+		return -EPERM;
+
+	DQUOT_INIT(dir);
+
+	down(&dentry->d_inode->i_sem);
+	if (d_mountpoint(dentry))
+		error = -EBUSY;
+	else {
+		error = security_inode_unlink(dir, dentry);
+		if (!error)
+			error = dir->i_op->unlink(dir, dentry);
+	}
+	up(&dentry->d_inode->i_sem);
+
+	/* We don't d_delete() NFS sillyrenamed files--they still exist. */
+	if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) {
+		d_delete(dentry);
+	}
+
+	return error;
+}
+
+/*
+ * Make sure that the actual truncation of the file will occur outside its
+ * directory's i_sem.  Truncate can take a long time if there is a lot of
+ * writeout happening, and we don't want to prevent access to the directory
+ * while waiting on the I/O.
+ */
+asmlinkage long sys_unlink(const char __user * pathname)
+{
+	int error = 0;
+	char * name;
+	struct dentry *dentry;
+	struct nameidata nd;
+	struct inode *inode = NULL;
+
+	name = getname(pathname);
+	if(IS_ERR(name))
+		return PTR_ERR(name);
+
+	error = path_lookup(name, LOOKUP_PARENT, &nd);
+	if (error)
+		goto exit;
+	error = -EISDIR;
+	if (nd.last_type != LAST_NORM)
+		goto exit1;
+	down(&nd.dentry->d_inode->i_sem);
+	dentry = lookup_hash(&nd);
+	error = PTR_ERR(dentry);
+	if (!IS_ERR(dentry)) {
+		/* Why not before? Because we want correct error value */
+		if (nd.last.name[nd.last.len])
+			goto slashes;
+		inode = dentry->d_inode;
+		if (inode)
+			atomic_inc(&inode->i_count);
+		error = vfs_unlink(nd.dentry->d_inode, dentry);
+	exit2:
+		dput(dentry);
+	}
+	up(&nd.dentry->d_inode->i_sem);
+	if (inode)
+		iput(inode);	/* truncate the inode here */
+exit1:
+	path_release(&nd);
+exit:
+	putname(name);
+	return error;
+
+slashes:
+	error = !dentry->d_inode ? -ENOENT :
+		S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
+	goto exit2;
+}
+
+int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, int mode)
+{
+	int error = may_create(dir, dentry, NULL);
+
+	if (error)
+		return error;
+
+	if (!dir->i_op || !dir->i_op->symlink)
+		return -EPERM;
+
+	error = security_inode_symlink(dir, dentry, oldname);
+	if (error)
+		return error;
+
+	DQUOT_INIT(dir);
+	error = dir->i_op->symlink(dir, dentry, oldname);
+	if (!error)
+		fsnotify_create(dir, dentry);
+	return error;
+}
+
+asmlinkage long sys_symlink(const char __user * oldname, const char __user * newname)
+{
+	int error = 0;
+	char * from;
+	char * to;
+
+	from = getname(oldname);
+	if(IS_ERR(from))
+		return PTR_ERR(from);
+	to = getname(newname);
+	error = PTR_ERR(to);
+	if (!IS_ERR(to)) {
+		struct dentry *dentry;
+		struct nameidata nd;
+
+		error = path_lookup(to, LOOKUP_PARENT, &nd);
+		if (error)
+			goto out;
+		dentry = lookup_create(&nd, 0);
+		error = PTR_ERR(dentry);
+		if (!IS_ERR(dentry)) {
+			error = vfs_symlink(nd.dentry->d_inode, dentry, from, S_IALLUGO);
+			dput(dentry);
+		}
+		up(&nd.dentry->d_inode->i_sem);
+		path_release(&nd);
+out:
+		putname(to);
+	}
+	putname(from);
+	return error;
+}
+
+int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
+{
+	struct inode *inode = old_dentry->d_inode;
+	int error;
+
+	if (!inode)
+		return -ENOENT;
+
+	error = may_create(dir, new_dentry, NULL);
+	if (error)
+		return error;
+
+	if (dir->i_sb != inode->i_sb)
+		return -EXDEV;
+
+	/*
+	 * A link to an append-only or immutable file cannot be created.
+	 */
+	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+		return -EPERM;
+	if (!dir->i_op || !dir->i_op->link)
+		return -EPERM;
+	if (S_ISDIR(old_dentry->d_inode->i_mode))
+		return -EPERM;
+
+	error = security_inode_link(old_dentry, dir, new_dentry);
+	if (error)
+		return error;
+
+	down(&old_dentry->d_inode->i_sem);
+	DQUOT_INIT(dir);
+	error = dir->i_op->link(old_dentry, dir, new_dentry);
+	up(&old_dentry->d_inode->i_sem);
+	if (!error)
+		fsnotify_create(dir, new_dentry);
+	return error;
+}
+
+/*
+ * Hardlinks are often used in delicate situations.  We avoid
+ * security-related surprises by not following symlinks on the
+ * newname.  --KAB
+ *
+ * We don't follow them on the oldname either to be compatible
+ * with linux 2.0, and to avoid hard-linking to directories
+ * and other special files.  --ADM
+ */
+asmlinkage long sys_link(const char __user * oldname, const char __user * newname)
+{
+	struct dentry *new_dentry;
+	struct nameidata nd, old_nd;
+	int error;
+	char * to;
+
+	to = getname(newname);
+	if (IS_ERR(to))
+		return PTR_ERR(to);
+
+	error = __user_walk(oldname, 0, &old_nd);
+	if (error)
+		goto exit;
+	error = path_lookup(to, LOOKUP_PARENT, &nd);
+	if (error)
+		goto out;
+	error = -EXDEV;
+	if (old_nd.mnt != nd.mnt)
+		goto out_release;
+	new_dentry = lookup_create(&nd, 0);
+	error = PTR_ERR(new_dentry);
+	if (!IS_ERR(new_dentry)) {
+		error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry);
+		dput(new_dentry);
+	}
+	up(&nd.dentry->d_inode->i_sem);
+out_release:
+	path_release(&nd);
+out:
+	path_release(&old_nd);
+exit:
+	putname(to);
+
+	return error;
+}
+
+/*
+ * The worst of all namespace operations - renaming directory. "Perverted"
+ * doesn't even start to describe it. Somebody in UCB had a heck of a trip...
+ * Problems:
+ *	a) we can get into loop creation. Check is done in is_subdir().
+ *	b) race potential - two innocent renames can create a loop together.
+ *	   That's where 4.4 screws up. Current fix: serialization on
+ *	   sb->s_vfs_rename_sem. We might be more accurate, but that's another
+ *	   story.
+ *	c) we have to lock _three_ objects - parents and victim (if it exists).
+ *	   And that - after we got ->i_sem on parents (until then we don't know
+ *	   whether the target exists).  Solution: try to be smart with locking
+ *	   order for inodes.  We rely on the fact that tree topology may change
+ *	   only under ->s_vfs_rename_sem _and_ that parent of the object we
+ *	   move will be locked.  Thus we can rank directories by the tree
+ *	   (ancestors first) and rank all non-directories after them.
+ *	   That works since everybody except rename does "lock parent, lookup,
+ *	   lock child" and rename is under ->s_vfs_rename_sem.
+ *	   HOWEVER, it relies on the assumption that any object with ->lookup()
+ *	   has no more than 1 dentry.  If "hybrid" objects will ever appear,
+ *	   we'd better make sure that there's no link(2) for them.
+ *	d) some filesystems don't support opened-but-unlinked directories,
+ *	   either because of layout or because they are not ready to deal with
+ *	   all cases correctly. The latter will be fixed (taking this sort of
+ *	   stuff into VFS), but the former is not going away. Solution: the same
+ *	   trick as in rmdir().
+ *	e) conversion from fhandle to dentry may come in the wrong moment - when
+ *	   we are removing the target. Solution: we will have to grab ->i_sem
+ *	   in the fhandle_to_dentry code. [FIXME - current nfsfh.c relies on
+ *	   ->i_sem on parents, which works but leads to some truely excessive
+ *	   locking].
+ */
+static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
+			  struct inode *new_dir, struct dentry *new_dentry)
+{
+	int error = 0;
+	struct inode *target;
+
+	/*
+	 * If we are going to change the parent - check write permissions,
+	 * we'll need to flip '..'.
+	 */
+	if (new_dir != old_dir) {
+		error = permission(old_dentry->d_inode, MAY_WRITE, NULL);
+		if (error)
+			return error;
+	}
+
+	error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry);
+	if (error)
+		return error;
+
+	target = new_dentry->d_inode;
+	if (target) {
+		down(&target->i_sem);
+		dentry_unhash(new_dentry);
+	}
+	if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
+		error = -EBUSY;
+	else 
+		error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+	if (target) {
+		if (!error)
+			target->i_flags |= S_DEAD;
+		up(&target->i_sem);
+		if (d_unhashed(new_dentry))
+			d_rehash(new_dentry);
+		dput(new_dentry);
+	}
+	if (!error)
+		d_move(old_dentry,new_dentry);
+	return error;
+}
+
+static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
+			    struct inode *new_dir, struct dentry *new_dentry)
+{
+	struct inode *target;
+	int error;
+
+	error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry);
+	if (error)
+		return error;
+
+	dget(new_dentry);
+	target = new_dentry->d_inode;
+	if (target)
+		down(&target->i_sem);
+	if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
+		error = -EBUSY;
+	else
+		error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+	if (!error) {
+		/* The following d_move() should become unconditional */
+		if (!(old_dir->i_sb->s_type->fs_flags & FS_ODD_RENAME))
+			d_move(old_dentry, new_dentry);
+	}
+	if (target)
+		up(&target->i_sem);
+	dput(new_dentry);
+	return error;
+}
+
+int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+	       struct inode *new_dir, struct dentry *new_dentry)
+{
+	int error;
+	int is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
+	const char *old_name;
+
+	if (old_dentry->d_inode == new_dentry->d_inode)
+ 		return 0;
+ 
+	error = may_delete(old_dir, old_dentry, is_dir);
+	if (error)
+		return error;
+
+	if (!new_dentry->d_inode)
+		error = may_create(new_dir, new_dentry, NULL);
+	else
+		error = may_delete(new_dir, new_dentry, is_dir);
+	if (error)
+		return error;
+
+	if (!old_dir->i_op || !old_dir->i_op->rename)
+		return -EPERM;
+
+	DQUOT_INIT(old_dir);
+	DQUOT_INIT(new_dir);
+
+	old_name = fsnotify_oldname_init(old_dentry->d_name.name);
+
+	if (is_dir)
+		error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
+	else
+		error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
+	if (!error) {
+		const char *new_name = old_dentry->d_name.name;
+		fsnotify_move(old_dir, new_dir, old_name, new_name, is_dir,
+			      new_dentry->d_inode, old_dentry->d_inode);
+	}
+	fsnotify_oldname_free(old_name);
+
+	return error;
+}
+
+static inline int do_rename(const char * oldname, const char * newname)
+{
+	int error = 0;
+	struct dentry * old_dir, * new_dir;
+	struct dentry * old_dentry, *new_dentry;
+	struct dentry * trap;
+	struct nameidata oldnd, newnd;
+
+	error = path_lookup(oldname, LOOKUP_PARENT, &oldnd);
+	if (error)
+		goto exit;
+
+	error = path_lookup(newname, LOOKUP_PARENT, &newnd);
+	if (error)
+		goto exit1;
+
+	error = -EXDEV;
+	if (oldnd.mnt != newnd.mnt)
+		goto exit2;
+
+	old_dir = oldnd.dentry;
+	error = -EBUSY;
+	if (oldnd.last_type != LAST_NORM)
+		goto exit2;
+
+	new_dir = newnd.dentry;
+	if (newnd.last_type != LAST_NORM)
+		goto exit2;
+
+	trap = lock_rename(new_dir, old_dir);
+
+	old_dentry = lookup_hash(&oldnd);
+	error = PTR_ERR(old_dentry);
+	if (IS_ERR(old_dentry))
+		goto exit3;
+	/* source must exist */
+	error = -ENOENT;
+	if (!old_dentry->d_inode)
+		goto exit4;
+	/* unless the source is a directory trailing slashes give -ENOTDIR */
+	if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
+		error = -ENOTDIR;
+		if (oldnd.last.name[oldnd.last.len])
+			goto exit4;
+		if (newnd.last.name[newnd.last.len])
+			goto exit4;
+	}
+	/* source should not be ancestor of target */
+	error = -EINVAL;
+	if (old_dentry == trap)
+		goto exit4;
+	new_dentry = lookup_hash(&newnd);
+	error = PTR_ERR(new_dentry);
+	if (IS_ERR(new_dentry))
+		goto exit4;
+	/* target should not be an ancestor of source */
+	error = -ENOTEMPTY;
+	if (new_dentry == trap)
+		goto exit5;
+
+	error = vfs_rename(old_dir->d_inode, old_dentry,
+				   new_dir->d_inode, new_dentry);
+exit5:
+	dput(new_dentry);
+exit4:
+	dput(old_dentry);
+exit3:
+	unlock_rename(new_dir, old_dir);
+exit2:
+	path_release(&newnd);
+exit1:
+	path_release(&oldnd);
+exit:
+	return error;
+}
+
+asmlinkage long sys_rename(const char __user * oldname, const char __user * newname)
+{
+	int error;
+	char * from;
+	char * to;
+
+	from = getname(oldname);
+	if(IS_ERR(from))
+		return PTR_ERR(from);
+	to = getname(newname);
+	error = PTR_ERR(to);
+	if (!IS_ERR(to)) {
+		error = do_rename(from,to);
+		putname(to);
+	}
+	putname(from);
+	return error;
+}
+
+int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen, const char *link)
+{
+	int len;
+
+	len = PTR_ERR(link);
+	if (IS_ERR(link))
+		goto out;
+
+	len = strlen(link);
+	if (len > (unsigned) buflen)
+		len = buflen;
+	if (copy_to_user(buffer, link, len))
+		len = -EFAULT;
+out:
+	return len;
+}
+
+/*
+ * A helper for ->readlink().  This should be used *ONLY* for symlinks that
+ * have ->follow_link() touching nd only in nd_set_link().  Using (or not
+ * using) it for any given inode is up to filesystem.
+ */
+int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
+{
+	struct nameidata nd;
+	void *cookie;
+
+	nd.depth = 0;
+	cookie = dentry->d_inode->i_op->follow_link(dentry, &nd);
+	if (!IS_ERR(cookie)) {
+		int res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd));
+		if (dentry->d_inode->i_op->put_link)
+			dentry->d_inode->i_op->put_link(dentry, &nd, cookie);
+		cookie = ERR_PTR(res);
+	}
+	return PTR_ERR(cookie);
+}
+
+int vfs_follow_link(struct nameidata *nd, const char *link)
+{
+	return __vfs_follow_link(nd, link);
+}
+
+/* get the link contents into pagecache */
+static char *page_getlink(struct dentry * dentry, struct page **ppage)
+{
+	struct page * page;
+	struct address_space *mapping = dentry->d_inode->i_mapping;
+	page = read_cache_page(mapping, 0, (filler_t *)mapping->a_ops->readpage,
+				NULL);
+	if (IS_ERR(page))
+		goto sync_fail;
+	wait_on_page_locked(page);
+	if (!PageUptodate(page))
+		goto async_fail;
+	*ppage = page;
+	return kmap(page);
+
+async_fail:
+	page_cache_release(page);
+	return ERR_PTR(-EIO);
+
+sync_fail:
+	return (char*)page;
+}
+
+int page_readlink(struct dentry *dentry, char __user *buffer, int buflen)
+{
+	struct page *page = NULL;
+	char *s = page_getlink(dentry, &page);
+	int res = vfs_readlink(dentry,buffer,buflen,s);
+	if (page) {
+		kunmap(page);
+		page_cache_release(page);
+	}
+	return res;
+}
+
+void *page_follow_link_light(struct dentry *dentry, struct nameidata *nd)
+{
+	struct page *page = NULL;
+	nd_set_link(nd, page_getlink(dentry, &page));
+	return page;
+}
+
+void page_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
+{
+	struct page *page = cookie;
+
+	if (page) {
+		kunmap(page);
+		page_cache_release(page);
+	}
+}
+
+int page_symlink(struct inode *inode, const char *symname, int len)
+{
+	struct address_space *mapping = inode->i_mapping;
+	struct page *page = grab_cache_page(mapping, 0);
+	int err = -ENOMEM;
+	char *kaddr;
+
+	if (!page)
+		goto fail;
+	err = mapping->a_ops->prepare_write(NULL, page, 0, len-1);
+	if (err)
+		goto fail_map;
+	kaddr = kmap_atomic(page, KM_USER0);
+	memcpy(kaddr, symname, len-1);
+	kunmap_atomic(kaddr, KM_USER0);
+	mapping->a_ops->commit_write(NULL, page, 0, len-1);
+	/*
+	 * Notice that we are _not_ going to block here - end of page is
+	 * unmapped, so this will only try to map the rest of page, see
+	 * that it is unmapped (typically even will not look into inode -
+	 * ->i_size will be enough for everything) and zero it out.
+	 * OTOH it's obviously correct and should make the page up-to-date.
+	 */
+	if (!PageUptodate(page)) {
+		err = mapping->a_ops->readpage(NULL, page);
+		wait_on_page_locked(page);
+	} else {
+		unlock_page(page);
+	}
+	page_cache_release(page);
+	if (err < 0)
+		goto fail;
+	mark_inode_dirty(inode);
+	return 0;
+fail_map:
+	unlock_page(page);
+	page_cache_release(page);
+fail:
+	return err;
+}
+
+struct inode_operations page_symlink_inode_operations = {
+	.readlink	= generic_readlink,
+	.follow_link	= page_follow_link_light,
+	.put_link	= page_put_link,
+};
+
+EXPORT_SYMBOL(__user_walk);
+EXPORT_SYMBOL(follow_down);
+EXPORT_SYMBOL(follow_up);
+EXPORT_SYMBOL(get_write_access); /* binfmt_aout */
+EXPORT_SYMBOL(getname);
+EXPORT_SYMBOL(lock_rename);
+EXPORT_SYMBOL(lookup_hash);
+EXPORT_SYMBOL(lookup_one_len);
+EXPORT_SYMBOL(page_follow_link_light);
+EXPORT_SYMBOL(page_put_link);
+EXPORT_SYMBOL(page_readlink);
+EXPORT_SYMBOL(page_symlink);
+EXPORT_SYMBOL(page_symlink_inode_operations);
+EXPORT_SYMBOL(path_lookup);
+EXPORT_SYMBOL(path_release);
+EXPORT_SYMBOL(path_walk);
+EXPORT_SYMBOL(permission);
+EXPORT_SYMBOL(vfs_permission);
+EXPORT_SYMBOL(file_permission);
+EXPORT_SYMBOL(unlock_rename);
+EXPORT_SYMBOL(vfs_create);
+EXPORT_SYMBOL(vfs_follow_link);
+EXPORT_SYMBOL(vfs_link);
+EXPORT_SYMBOL(vfs_mkdir);
+EXPORT_SYMBOL(vfs_mknod);
+EXPORT_SYMBOL(generic_permission);
+EXPORT_SYMBOL(vfs_readlink);
+EXPORT_SYMBOL(vfs_rename);
+EXPORT_SYMBOL(vfs_rmdir);
+EXPORT_SYMBOL(vfs_symlink);
+EXPORT_SYMBOL(vfs_unlink);
+EXPORT_SYMBOL(dentry_unhash);
+EXPORT_SYMBOL(generic_readlink);
diff -urN oldtree/fs/namespace.c newtree/fs/namespace.c
--- oldtree/fs/namespace.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/namespace.c	2006-02-13 19:20:00.709404456 -0500
@@ -47,6 +47,10 @@
 static kmem_cache_t *mnt_cache;
 static struct rw_semaphore namespace_sem;
 
+/* /sys/fs */
+decl_subsys(fs, NULL, NULL);
+EXPORT_SYMBOL(fs_subsys);
+
 static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry)
 {
 	unsigned long tmp = ((unsigned long)mnt / L1_CACHE_BYTES);
@@ -1714,6 +1718,7 @@
 		i--;
 	} while (i);
 	sysfs_init();
+	subsystem_register(&fs_subsys);
 	init_rootfs();
 	init_mount_tree();
 }
diff -urN oldtree/fs/namespace.c.orig newtree/fs/namespace.c.orig
--- oldtree/fs/namespace.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/namespace.c.orig	2006-02-13 19:20:00.711404152 -0500
@@ -0,0 +1,1734 @@
+/*
+ *  linux/fs/namespace.c
+ *
+ * (C) Copyright Al Viro 2000, 2001
+ *	Released under GPL v2.
+ *
+ * Based on code from fs/super.c, copyright Linus Torvalds and others.
+ * Heavily rewritten.
+ */
+
+#include <linux/config.h>
+#include <linux/syscalls.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/quotaops.h>
+#include <linux/acct.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/namespace.h>
+#include <linux/namei.h>
+#include <linux/security.h>
+#include <linux/mount.h>
+#include <asm/uaccess.h>
+#include <asm/unistd.h>
+#include "pnode.h"
+
+extern int __init init_rootfs(void);
+
+#ifdef CONFIG_SYSFS
+extern int __init sysfs_init(void);
+#else
+static inline int sysfs_init(void)
+{
+	return 0;
+}
+#endif
+
+/* spinlock for vfsmount related operations, inplace of dcache_lock */
+__cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock);
+
+static int event;
+
+static struct list_head *mount_hashtable;
+static int hash_mask __read_mostly, hash_bits __read_mostly;
+static kmem_cache_t *mnt_cache;
+static struct rw_semaphore namespace_sem;
+
+static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry)
+{
+	unsigned long tmp = ((unsigned long)mnt / L1_CACHE_BYTES);
+	tmp += ((unsigned long)dentry / L1_CACHE_BYTES);
+	tmp = tmp + (tmp >> hash_bits);
+	return tmp & hash_mask;
+}
+
+struct vfsmount *alloc_vfsmnt(const char *name)
+{
+	struct vfsmount *mnt = kmem_cache_alloc(mnt_cache, GFP_KERNEL);
+	if (mnt) {
+		memset(mnt, 0, sizeof(struct vfsmount));
+		atomic_set(&mnt->mnt_count, 1);
+		INIT_LIST_HEAD(&mnt->mnt_hash);
+		INIT_LIST_HEAD(&mnt->mnt_child);
+		INIT_LIST_HEAD(&mnt->mnt_mounts);
+		INIT_LIST_HEAD(&mnt->mnt_list);
+		INIT_LIST_HEAD(&mnt->mnt_expire);
+		INIT_LIST_HEAD(&mnt->mnt_share);
+		INIT_LIST_HEAD(&mnt->mnt_slave_list);
+		INIT_LIST_HEAD(&mnt->mnt_slave);
+		if (name) {
+			int size = strlen(name) + 1;
+			char *newname = kmalloc(size, GFP_KERNEL);
+			if (newname) {
+				memcpy(newname, name, size);
+				mnt->mnt_devname = newname;
+			}
+		}
+	}
+	return mnt;
+}
+
+void free_vfsmnt(struct vfsmount *mnt)
+{
+	kfree(mnt->mnt_devname);
+	kmem_cache_free(mnt_cache, mnt);
+}
+
+/*
+ * find the first or last mount at @dentry on vfsmount @mnt depending on
+ * @dir. If @dir is set return the first mount else return the last mount.
+ */
+struct vfsmount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry,
+			      int dir)
+{
+	struct list_head *head = mount_hashtable + hash(mnt, dentry);
+	struct list_head *tmp = head;
+	struct vfsmount *p, *found = NULL;
+
+	for (;;) {
+		tmp = dir ? tmp->next : tmp->prev;
+		p = NULL;
+		if (tmp == head)
+			break;
+		p = list_entry(tmp, struct vfsmount, mnt_hash);
+		if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry) {
+			found = p;
+			break;
+		}
+	}
+	return found;
+}
+
+/*
+ * lookup_mnt increments the ref count before returning
+ * the vfsmount struct.
+ */
+struct vfsmount *lookup_mnt(struct vfsmount *mnt, struct dentry *dentry)
+{
+	struct vfsmount *child_mnt;
+	spin_lock(&vfsmount_lock);
+	if ((child_mnt = __lookup_mnt(mnt, dentry, 1)))
+		mntget(child_mnt);
+	spin_unlock(&vfsmount_lock);
+	return child_mnt;
+}
+
+static inline int check_mnt(struct vfsmount *mnt)
+{
+	return mnt->mnt_namespace == current->namespace;
+}
+
+static void touch_namespace(struct namespace *ns)
+{
+	if (ns) {
+		ns->event = ++event;
+		wake_up_interruptible(&ns->poll);
+	}
+}
+
+static void __touch_namespace(struct namespace *ns)
+{
+	if (ns && ns->event != event) {
+		ns->event = event;
+		wake_up_interruptible(&ns->poll);
+	}
+}
+
+static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd)
+{
+	old_nd->dentry = mnt->mnt_mountpoint;
+	old_nd->mnt = mnt->mnt_parent;
+	mnt->mnt_parent = mnt;
+	mnt->mnt_mountpoint = mnt->mnt_root;
+	list_del_init(&mnt->mnt_child);
+	list_del_init(&mnt->mnt_hash);
+	old_nd->dentry->d_mounted--;
+}
+
+void mnt_set_mountpoint(struct vfsmount *mnt, struct dentry *dentry,
+			struct vfsmount *child_mnt)
+{
+	child_mnt->mnt_parent = mntget(mnt);
+	child_mnt->mnt_mountpoint = dget(dentry);
+	dentry->d_mounted++;
+}
+
+static void attach_mnt(struct vfsmount *mnt, struct nameidata *nd)
+{
+	mnt_set_mountpoint(nd->mnt, nd->dentry, mnt);
+	list_add_tail(&mnt->mnt_hash, mount_hashtable +
+			hash(nd->mnt, nd->dentry));
+	list_add_tail(&mnt->mnt_child, &nd->mnt->mnt_mounts);
+}
+
+/*
+ * the caller must hold vfsmount_lock
+ */
+static void commit_tree(struct vfsmount *mnt)
+{
+	struct vfsmount *parent = mnt->mnt_parent;
+	struct vfsmount *m;
+	LIST_HEAD(head);
+	struct namespace *n = parent->mnt_namespace;
+
+	BUG_ON(parent == mnt);
+
+	list_add_tail(&head, &mnt->mnt_list);
+	list_for_each_entry(m, &head, mnt_list)
+		m->mnt_namespace = n;
+	list_splice(&head, n->list.prev);
+
+	list_add_tail(&mnt->mnt_hash, mount_hashtable +
+				hash(parent, mnt->mnt_mountpoint));
+	list_add_tail(&mnt->mnt_child, &parent->mnt_mounts);
+	touch_namespace(n);
+}
+
+static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root)
+{
+	struct list_head *next = p->mnt_mounts.next;
+	if (next == &p->mnt_mounts) {
+		while (1) {
+			if (p == root)
+				return NULL;
+			next = p->mnt_child.next;
+			if (next != &p->mnt_parent->mnt_mounts)
+				break;
+			p = p->mnt_parent;
+		}
+	}
+	return list_entry(next, struct vfsmount, mnt_child);
+}
+
+static struct vfsmount *skip_mnt_tree(struct vfsmount *p)
+{
+	struct list_head *prev = p->mnt_mounts.prev;
+	while (prev != &p->mnt_mounts) {
+		p = list_entry(prev, struct vfsmount, mnt_child);
+		prev = p->mnt_mounts.prev;
+	}
+	return p;
+}
+
+static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root,
+					int flag)
+{
+	struct super_block *sb = old->mnt_sb;
+	struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname);
+
+	if (mnt) {
+		mnt->mnt_flags = old->mnt_flags;
+		atomic_inc(&sb->s_active);
+		mnt->mnt_sb = sb;
+		mnt->mnt_root = dget(root);
+		mnt->mnt_mountpoint = mnt->mnt_root;
+		mnt->mnt_parent = mnt;
+
+		if (flag & CL_SLAVE) {
+			list_add(&mnt->mnt_slave, &old->mnt_slave_list);
+			mnt->mnt_master = old;
+			CLEAR_MNT_SHARED(mnt);
+		} else {
+			if ((flag & CL_PROPAGATION) || IS_MNT_SHARED(old))
+				list_add(&mnt->mnt_share, &old->mnt_share);
+			if (IS_MNT_SLAVE(old))
+				list_add(&mnt->mnt_slave, &old->mnt_slave);
+			mnt->mnt_master = old->mnt_master;
+		}
+		if (flag & CL_MAKE_SHARED)
+			set_mnt_shared(mnt);
+
+		/* stick the duplicate mount on the same expiry list
+		 * as the original if that was on one */
+		if (flag & CL_EXPIRE) {
+			spin_lock(&vfsmount_lock);
+			if (!list_empty(&old->mnt_expire))
+				list_add(&mnt->mnt_expire, &old->mnt_expire);
+			spin_unlock(&vfsmount_lock);
+		}
+	}
+	return mnt;
+}
+
+static inline void __mntput(struct vfsmount *mnt)
+{
+	struct super_block *sb = mnt->mnt_sb;
+	dput(mnt->mnt_root);
+	free_vfsmnt(mnt);
+	deactivate_super(sb);
+}
+
+void mntput_no_expire(struct vfsmount *mnt)
+{
+repeat:
+	if (atomic_dec_and_lock(&mnt->mnt_count, &vfsmount_lock)) {
+		if (likely(!mnt->mnt_pinned)) {
+			spin_unlock(&vfsmount_lock);
+			__mntput(mnt);
+			return;
+		}
+		atomic_add(mnt->mnt_pinned + 1, &mnt->mnt_count);
+		mnt->mnt_pinned = 0;
+		spin_unlock(&vfsmount_lock);
+		acct_auto_close_mnt(mnt);
+		security_sb_umount_close(mnt);
+		goto repeat;
+	}
+}
+
+EXPORT_SYMBOL(mntput_no_expire);
+
+void mnt_pin(struct vfsmount *mnt)
+{
+	spin_lock(&vfsmount_lock);
+	mnt->mnt_pinned++;
+	spin_unlock(&vfsmount_lock);
+}
+
+EXPORT_SYMBOL(mnt_pin);
+
+void mnt_unpin(struct vfsmount *mnt)
+{
+	spin_lock(&vfsmount_lock);
+	if (mnt->mnt_pinned) {
+		atomic_inc(&mnt->mnt_count);
+		mnt->mnt_pinned--;
+	}
+	spin_unlock(&vfsmount_lock);
+}
+
+EXPORT_SYMBOL(mnt_unpin);
+
+/* iterator */
+static void *m_start(struct seq_file *m, loff_t *pos)
+{
+	struct namespace *n = m->private;
+	struct list_head *p;
+	loff_t l = *pos;
+
+	down_read(&namespace_sem);
+	list_for_each(p, &n->list)
+		if (!l--)
+			return list_entry(p, struct vfsmount, mnt_list);
+	return NULL;
+}
+
+static void *m_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	struct namespace *n = m->private;
+	struct list_head *p = ((struct vfsmount *)v)->mnt_list.next;
+	(*pos)++;
+	return p == &n->list ? NULL : list_entry(p, struct vfsmount, mnt_list);
+}
+
+static void m_stop(struct seq_file *m, void *v)
+{
+	up_read(&namespace_sem);
+}
+
+static inline void mangle(struct seq_file *m, const char *s)
+{
+	seq_escape(m, s, " \t\n\\");
+}
+
+static int show_vfsmnt(struct seq_file *m, void *v)
+{
+	struct vfsmount *mnt = v;
+	int err = 0;
+	static struct proc_fs_info {
+		int flag;
+		char *str;
+	} fs_info[] = {
+		{ MS_SYNCHRONOUS, ",sync" },
+		{ MS_DIRSYNC, ",dirsync" },
+		{ MS_MANDLOCK, ",mand" },
+		{ MS_NOATIME, ",noatime" },
+		{ MS_NODIRATIME, ",nodiratime" },
+		{ 0, NULL }
+	};
+	static struct proc_fs_info mnt_info[] = {
+		{ MNT_NOSUID, ",nosuid" },
+		{ MNT_NODEV, ",nodev" },
+		{ MNT_NOEXEC, ",noexec" },
+		{ 0, NULL }
+	};
+	struct proc_fs_info *fs_infop;
+
+	mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none");
+	seq_putc(m, ' ');
+	seq_path(m, mnt, mnt->mnt_root, " \t\n\\");
+	seq_putc(m, ' ');
+	mangle(m, mnt->mnt_sb->s_type->name);
+	seq_puts(m, mnt->mnt_sb->s_flags & MS_RDONLY ? " ro" : " rw");
+	for (fs_infop = fs_info; fs_infop->flag; fs_infop++) {
+		if (mnt->mnt_sb->s_flags & fs_infop->flag)
+			seq_puts(m, fs_infop->str);
+	}
+	for (fs_infop = mnt_info; fs_infop->flag; fs_infop++) {
+		if (mnt->mnt_flags & fs_infop->flag)
+			seq_puts(m, fs_infop->str);
+	}
+	if (mnt->mnt_sb->s_op->show_options)
+		err = mnt->mnt_sb->s_op->show_options(m, mnt);
+	seq_puts(m, " 0 0\n");
+	return err;
+}
+
+struct seq_operations mounts_op = {
+	.start	= m_start,
+	.next	= m_next,
+	.stop	= m_stop,
+	.show	= show_vfsmnt
+};
+
+/**
+ * may_umount_tree - check if a mount tree is busy
+ * @mnt: root of mount tree
+ *
+ * This is called to check if a tree of mounts has any
+ * open files, pwds, chroots or sub mounts that are
+ * busy.
+ */
+int may_umount_tree(struct vfsmount *mnt)
+{
+	int actual_refs = 0;
+	int minimum_refs = 0;
+	struct vfsmount *p;
+
+	spin_lock(&vfsmount_lock);
+	for (p = mnt; p; p = next_mnt(p, mnt)) {
+		actual_refs += atomic_read(&p->mnt_count);
+		minimum_refs += 2;
+	}
+	spin_unlock(&vfsmount_lock);
+
+	if (actual_refs > minimum_refs)
+		return -EBUSY;
+
+	return 0;
+}
+
+EXPORT_SYMBOL(may_umount_tree);
+
+/**
+ * may_umount - check if a mount point is busy
+ * @mnt: root of mount
+ *
+ * This is called to check if a mount point has any
+ * open files, pwds, chroots or sub mounts. If the
+ * mount has sub mounts this will return busy
+ * regardless of whether the sub mounts are busy.
+ *
+ * Doesn't take quota and stuff into account. IOW, in some cases it will
+ * give false negatives. The main reason why it's here is that we need
+ * a non-destructive way to look for easily umountable filesystems.
+ */
+int may_umount(struct vfsmount *mnt)
+{
+	int ret = 0;
+	spin_lock(&vfsmount_lock);
+	if (propagate_mount_busy(mnt, 2))
+		ret = -EBUSY;
+	spin_unlock(&vfsmount_lock);
+	return ret;
+}
+
+EXPORT_SYMBOL(may_umount);
+
+void release_mounts(struct list_head *head)
+{
+	struct vfsmount *mnt;
+	while(!list_empty(head)) {
+		mnt = list_entry(head->next, struct vfsmount, mnt_hash);
+		list_del_init(&mnt->mnt_hash);
+		if (mnt->mnt_parent != mnt) {
+			struct dentry *dentry;
+			struct vfsmount *m;
+			spin_lock(&vfsmount_lock);
+			dentry = mnt->mnt_mountpoint;
+			m = mnt->mnt_parent;
+			mnt->mnt_mountpoint = mnt->mnt_root;
+			mnt->mnt_parent = mnt;
+			spin_unlock(&vfsmount_lock);
+			dput(dentry);
+			mntput(m);
+		}
+		mntput(mnt);
+	}
+}
+
+void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill)
+{
+	struct vfsmount *p;
+
+	for (p = mnt; p; p = next_mnt(p, mnt)) {
+		list_del(&p->mnt_hash);
+		list_add(&p->mnt_hash, kill);
+	}
+
+	if (propagate)
+		propagate_umount(kill);
+
+	list_for_each_entry(p, kill, mnt_hash) {
+		list_del_init(&p->mnt_expire);
+		list_del_init(&p->mnt_list);
+		__touch_namespace(p->mnt_namespace);
+		p->mnt_namespace = NULL;
+		list_del_init(&p->mnt_child);
+		if (p->mnt_parent != p)
+			mnt->mnt_mountpoint->d_mounted--;
+		change_mnt_propagation(p, MS_PRIVATE);
+	}
+}
+
+static int do_umount(struct vfsmount *mnt, int flags)
+{
+	struct super_block *sb = mnt->mnt_sb;
+	int retval;
+	LIST_HEAD(umount_list);
+
+	retval = security_sb_umount(mnt, flags);
+	if (retval)
+		return retval;
+
+	/*
+	 * Allow userspace to request a mountpoint be expired rather than
+	 * unmounting unconditionally. Unmount only happens if:
+	 *  (1) the mark is already set (the mark is cleared by mntput())
+	 *  (2) the usage count == 1 [parent vfsmount] + 1 [sys_umount]
+	 */
+	if (flags & MNT_EXPIRE) {
+		if (mnt == current->fs->rootmnt ||
+		    flags & (MNT_FORCE | MNT_DETACH))
+			return -EINVAL;
+
+		if (atomic_read(&mnt->mnt_count) != 2)
+			return -EBUSY;
+
+		if (!xchg(&mnt->mnt_expiry_mark, 1))
+			return -EAGAIN;
+	}
+
+	/*
+	 * If we may have to abort operations to get out of this
+	 * mount, and they will themselves hold resources we must
+	 * allow the fs to do things. In the Unix tradition of
+	 * 'Gee thats tricky lets do it in userspace' the umount_begin
+	 * might fail to complete on the first run through as other tasks
+	 * must return, and the like. Thats for the mount program to worry
+	 * about for the moment.
+	 */
+
+	lock_kernel();
+	if ((flags & MNT_FORCE) && sb->s_op->umount_begin)
+		sb->s_op->umount_begin(sb);
+	unlock_kernel();
+
+	/*
+	 * No sense to grab the lock for this test, but test itself looks
+	 * somewhat bogus. Suggestions for better replacement?
+	 * Ho-hum... In principle, we might treat that as umount + switch
+	 * to rootfs. GC would eventually take care of the old vfsmount.
+	 * Actually it makes sense, especially if rootfs would contain a
+	 * /reboot - static binary that would close all descriptors and
+	 * call reboot(9). Then init(8) could umount root and exec /reboot.
+	 */
+	if (mnt == current->fs->rootmnt && !(flags & MNT_DETACH)) {
+		/*
+		 * Special case for "unmounting" root ...
+		 * we just try to remount it readonly.
+		 */
+		down_write(&sb->s_umount);
+		if (!(sb->s_flags & MS_RDONLY)) {
+			lock_kernel();
+			DQUOT_OFF(sb);
+			retval = do_remount_sb(sb, MS_RDONLY, NULL, 0);
+			unlock_kernel();
+		}
+		up_write(&sb->s_umount);
+		return retval;
+	}
+
+	down_write(&namespace_sem);
+	spin_lock(&vfsmount_lock);
+	event++;
+
+	retval = -EBUSY;
+	if (flags & MNT_DETACH || !propagate_mount_busy(mnt, 2)) {
+		if (!list_empty(&mnt->mnt_list))
+			umount_tree(mnt, 1, &umount_list);
+		retval = 0;
+	}
+	spin_unlock(&vfsmount_lock);
+	if (retval)
+		security_sb_umount_busy(mnt);
+	up_write(&namespace_sem);
+	release_mounts(&umount_list);
+	return retval;
+}
+
+/*
+ * Now umount can handle mount points as well as block devices.
+ * This is important for filesystems which use unnamed block devices.
+ *
+ * We now support a flag for forced unmount like the other 'big iron'
+ * unixes. Our API is identical to OSF/1 to avoid making a mess of AMD
+ */
+
+asmlinkage long sys_umount(char __user * name, int flags)
+{
+	struct nameidata nd;
+	int retval;
+
+	retval = __user_walk(name, LOOKUP_FOLLOW, &nd);
+	if (retval)
+		goto out;
+	retval = -EINVAL;
+	if (nd.dentry != nd.mnt->mnt_root)
+		goto dput_and_out;
+	if (!check_mnt(nd.mnt))
+		goto dput_and_out;
+
+	retval = -EPERM;
+	if (!capable(CAP_SYS_ADMIN))
+		goto dput_and_out;
+
+	retval = do_umount(nd.mnt, flags);
+dput_and_out:
+	path_release_on_umount(&nd);
+out:
+	return retval;
+}
+
+#ifdef __ARCH_WANT_SYS_OLDUMOUNT
+
+/*
+ *	The 2.0 compatible umount. No flags.
+ */
+asmlinkage long sys_oldumount(char __user * name)
+{
+	return sys_umount(name, 0);
+}
+
+#endif
+
+static int mount_is_safe(struct nameidata *nd)
+{
+	if (capable(CAP_SYS_ADMIN))
+		return 0;
+	return -EPERM;
+#ifdef notyet
+	if (S_ISLNK(nd->dentry->d_inode->i_mode))
+		return -EPERM;
+	if (nd->dentry->d_inode->i_mode & S_ISVTX) {
+		if (current->uid != nd->dentry->d_inode->i_uid)
+			return -EPERM;
+	}
+	if (vfs_permission(nd, MAY_WRITE))
+		return -EPERM;
+	return 0;
+#endif
+}
+
+static int lives_below_in_same_fs(struct dentry *d, struct dentry *dentry)
+{
+	while (1) {
+		if (d == dentry)
+			return 1;
+		if (d == NULL || d == d->d_parent)
+			return 0;
+		d = d->d_parent;
+	}
+}
+
+struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry,
+					int flag)
+{
+	struct vfsmount *res, *p, *q, *r, *s;
+	struct nameidata nd;
+
+	if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt))
+		return NULL;
+
+	res = q = clone_mnt(mnt, dentry, flag);
+	if (!q)
+		goto Enomem;
+	q->mnt_mountpoint = mnt->mnt_mountpoint;
+
+	p = mnt;
+	list_for_each_entry(r, &mnt->mnt_mounts, mnt_child) {
+		if (!lives_below_in_same_fs(r->mnt_mountpoint, dentry))
+			continue;
+
+		for (s = r; s; s = next_mnt(s, r)) {
+			if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(s)) {
+				s = skip_mnt_tree(s);
+				continue;
+			}
+			while (p != s->mnt_parent) {
+				p = p->mnt_parent;
+				q = q->mnt_parent;
+			}
+			p = s;
+			nd.mnt = q;
+			nd.dentry = p->mnt_mountpoint;
+			q = clone_mnt(p, p->mnt_root, flag);
+			if (!q)
+				goto Enomem;
+			spin_lock(&vfsmount_lock);
+			list_add_tail(&q->mnt_list, &res->mnt_list);
+			attach_mnt(q, &nd);
+			spin_unlock(&vfsmount_lock);
+		}
+	}
+	return res;
+Enomem:
+	if (res) {
+		LIST_HEAD(umount_list);
+		spin_lock(&vfsmount_lock);
+		umount_tree(res, 0, &umount_list);
+		spin_unlock(&vfsmount_lock);
+		release_mounts(&umount_list);
+	}
+	return NULL;
+}
+
+/*
+ *  @source_mnt : mount tree to be attached
+ *  @nd         : place the mount tree @source_mnt is attached
+ *  @parent_nd  : if non-null, detach the source_mnt from its parent and
+ *  		   store the parent mount and mountpoint dentry.
+ *  		   (done when source_mnt is moved)
+ *
+ *  NOTE: in the table below explains the semantics when a source mount
+ *  of a given type is attached to a destination mount of a given type.
+ * ---------------------------------------------------------------------------
+ * |         BIND MOUNT OPERATION                                            |
+ * |**************************************************************************
+ * | source-->| shared        |       private  |       slave    | unbindable |
+ * | dest     |               |                |                |            |
+ * |   |      |               |                |                |            |
+ * |   v      |               |                |                |            |
+ * |**************************************************************************
+ * |  shared  | shared (++)   |     shared (+) |     shared(+++)|  invalid   |
+ * |          |               |                |                |            |
+ * |non-shared| shared (+)    |      private   |      slave (*) |  invalid   |
+ * ***************************************************************************
+ * A bind operation clones the source mount and mounts the clone on the
+ * destination mount.
+ *
+ * (++)  the cloned mount is propagated to all the mounts in the propagation
+ * 	 tree of the destination mount and the cloned mount is added to
+ * 	 the peer group of the source mount.
+ * (+)   the cloned mount is created under the destination mount and is marked
+ *       as shared. The cloned mount is added to the peer group of the source
+ *       mount.
+ * (+++) the mount is propagated to all the mounts in the propagation tree
+ *       of the destination mount and the cloned mount is made slave
+ *       of the same master as that of the source mount. The cloned mount
+ *       is marked as 'shared and slave'.
+ * (*)   the cloned mount is made a slave of the same master as that of the
+ * 	 source mount.
+ *
+ * ---------------------------------------------------------------------------
+ * |         		MOVE MOUNT OPERATION                                 |
+ * |**************************************************************************
+ * | source-->| shared        |       private  |       slave    | unbindable |
+ * | dest     |               |                |                |            |
+ * |   |      |               |                |                |            |
+ * |   v      |               |                |                |            |
+ * |**************************************************************************
+ * |  shared  | shared (+)    |     shared (+) |    shared(+++) |  invalid   |
+ * |          |               |                |                |            |
+ * |non-shared| shared (+*)   |      private   |    slave (*)   | unbindable |
+ * ***************************************************************************
+ *
+ * (+)  the mount is moved to the destination. And is then propagated to
+ * 	all the mounts in the propagation tree of the destination mount.
+ * (+*)  the mount is moved to the destination.
+ * (+++)  the mount is moved to the destination and is then propagated to
+ * 	all the mounts belonging to the destination mount's propagation tree.
+ * 	the mount is marked as 'shared and slave'.
+ * (*)	the mount continues to be a slave at the new location.
+ *
+ * if the source mount is a tree, the operations explained above is
+ * applied to each mount in the tree.
+ * Must be called without spinlocks held, since this function can sleep
+ * in allocations.
+ */
+static int attach_recursive_mnt(struct vfsmount *source_mnt,
+			struct nameidata *nd, struct nameidata *parent_nd)
+{
+	LIST_HEAD(tree_list);
+	struct vfsmount *dest_mnt = nd->mnt;
+	struct dentry *dest_dentry = nd->dentry;
+	struct vfsmount *child, *p;
+
+	if (propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list))
+		return -EINVAL;
+
+	if (IS_MNT_SHARED(dest_mnt)) {
+		for (p = source_mnt; p; p = next_mnt(p, source_mnt))
+			set_mnt_shared(p);
+	}
+
+	spin_lock(&vfsmount_lock);
+	if (parent_nd) {
+		detach_mnt(source_mnt, parent_nd);
+		attach_mnt(source_mnt, nd);
+		touch_namespace(current->namespace);
+	} else {
+		mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt);
+		commit_tree(source_mnt);
+	}
+
+	list_for_each_entry_safe(child, p, &tree_list, mnt_hash) {
+		list_del_init(&child->mnt_hash);
+		commit_tree(child);
+	}
+	spin_unlock(&vfsmount_lock);
+	return 0;
+}
+
+static int graft_tree(struct vfsmount *mnt, struct nameidata *nd)
+{
+	int err;
+	if (mnt->mnt_sb->s_flags & MS_NOUSER)
+		return -EINVAL;
+
+	if (S_ISDIR(nd->dentry->d_inode->i_mode) !=
+	      S_ISDIR(mnt->mnt_root->d_inode->i_mode))
+		return -ENOTDIR;
+
+	err = -ENOENT;
+	down(&nd->dentry->d_inode->i_sem);
+	if (IS_DEADDIR(nd->dentry->d_inode))
+		goto out_unlock;
+
+	err = security_sb_check_sb(mnt, nd);
+	if (err)
+		goto out_unlock;
+
+	err = -ENOENT;
+	if (IS_ROOT(nd->dentry) || !d_unhashed(nd->dentry))
+		err = attach_recursive_mnt(mnt, nd, NULL);
+out_unlock:
+	up(&nd->dentry->d_inode->i_sem);
+	if (!err)
+		security_sb_post_addmount(mnt, nd);
+	return err;
+}
+
+/*
+ * recursively change the type of the mountpoint.
+ */
+static int do_change_type(struct nameidata *nd, int flag)
+{
+	struct vfsmount *m, *mnt = nd->mnt;
+	int recurse = flag & MS_REC;
+	int type = flag & ~MS_REC;
+
+	if (nd->dentry != nd->mnt->mnt_root)
+		return -EINVAL;
+
+	down_write(&namespace_sem);
+	spin_lock(&vfsmount_lock);
+	for (m = mnt; m; m = (recurse ? next_mnt(m, mnt) : NULL))
+		change_mnt_propagation(m, type);
+	spin_unlock(&vfsmount_lock);
+	up_write(&namespace_sem);
+	return 0;
+}
+
+/*
+ * do loopback mount.
+ */
+static int do_loopback(struct nameidata *nd, char *old_name, int recurse)
+{
+	struct nameidata old_nd;
+	struct vfsmount *mnt = NULL;
+	int err = mount_is_safe(nd);
+	if (err)
+		return err;
+	if (!old_name || !*old_name)
+		return -EINVAL;
+	err = path_lookup(old_name, LOOKUP_FOLLOW, &old_nd);
+	if (err)
+		return err;
+
+	down_write(&namespace_sem);
+	err = -EINVAL;
+	if (IS_MNT_UNBINDABLE(old_nd.mnt))
+ 		goto out;
+
+	if (!check_mnt(nd->mnt) || !check_mnt(old_nd.mnt))
+		goto out;
+
+	err = -ENOMEM;
+	if (recurse)
+		mnt = copy_tree(old_nd.mnt, old_nd.dentry, 0);
+	else
+		mnt = clone_mnt(old_nd.mnt, old_nd.dentry, 0);
+
+	if (!mnt)
+		goto out;
+
+	err = graft_tree(mnt, nd);
+	if (err) {
+		LIST_HEAD(umount_list);
+		spin_lock(&vfsmount_lock);
+		umount_tree(mnt, 0, &umount_list);
+		spin_unlock(&vfsmount_lock);
+		release_mounts(&umount_list);
+	}
+
+out:
+	up_write(&namespace_sem);
+	path_release(&old_nd);
+	return err;
+}
+
+/*
+ * change filesystem flags. dir should be a physical root of filesystem.
+ * If you've mounted a non-root directory somewhere and want to do remount
+ * on it - tough luck.
+ */
+static int do_remount(struct nameidata *nd, int flags, int mnt_flags,
+		      void *data)
+{
+	int err;
+	struct super_block *sb = nd->mnt->mnt_sb;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (!check_mnt(nd->mnt))
+		return -EINVAL;
+
+	if (nd->dentry != nd->mnt->mnt_root)
+		return -EINVAL;
+
+	down_write(&sb->s_umount);
+	err = do_remount_sb(sb, flags, data, 0);
+	if (!err)
+		nd->mnt->mnt_flags = mnt_flags;
+	up_write(&sb->s_umount);
+	if (!err)
+		security_sb_post_remount(nd->mnt, flags, data);
+	return err;
+}
+
+static inline int tree_contains_unbindable(struct vfsmount *mnt)
+{
+	struct vfsmount *p;
+	for (p = mnt; p; p = next_mnt(p, mnt)) {
+		if (IS_MNT_UNBINDABLE(p))
+			return 1;
+	}
+	return 0;
+}
+
+static int do_move_mount(struct nameidata *nd, char *old_name)
+{
+	struct nameidata old_nd, parent_nd;
+	struct vfsmount *p;
+	int err = 0;
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (!old_name || !*old_name)
+		return -EINVAL;
+	err = path_lookup(old_name, LOOKUP_FOLLOW, &old_nd);
+	if (err)
+		return err;
+
+	down_write(&namespace_sem);
+	while (d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry))
+		;
+	err = -EINVAL;
+	if (!check_mnt(nd->mnt) || !check_mnt(old_nd.mnt))
+		goto out;
+
+	err = -ENOENT;
+	down(&nd->dentry->d_inode->i_sem);
+	if (IS_DEADDIR(nd->dentry->d_inode))
+		goto out1;
+
+	if (!IS_ROOT(nd->dentry) && d_unhashed(nd->dentry))
+		goto out1;
+
+	err = -EINVAL;
+	if (old_nd.dentry != old_nd.mnt->mnt_root)
+		goto out1;
+
+	if (old_nd.mnt == old_nd.mnt->mnt_parent)
+		goto out1;
+
+	if (S_ISDIR(nd->dentry->d_inode->i_mode) !=
+	      S_ISDIR(old_nd.dentry->d_inode->i_mode))
+		goto out1;
+	/*
+	 * Don't move a mount residing in a shared parent.
+	 */
+	if (old_nd.mnt->mnt_parent && IS_MNT_SHARED(old_nd.mnt->mnt_parent))
+		goto out1;
+	/*
+	 * Don't move a mount tree containing unbindable mounts to a destination
+	 * mount which is shared.
+	 */
+	if (IS_MNT_SHARED(nd->mnt) && tree_contains_unbindable(old_nd.mnt))
+		goto out1;
+	err = -ELOOP;
+	for (p = nd->mnt; p->mnt_parent != p; p = p->mnt_parent)
+		if (p == old_nd.mnt)
+			goto out1;
+
+	if ((err = attach_recursive_mnt(old_nd.mnt, nd, &parent_nd)))
+		goto out1;
+
+	spin_lock(&vfsmount_lock);
+	/* if the mount is moved, it should no longer be expire
+	 * automatically */
+	list_del_init(&old_nd.mnt->mnt_expire);
+	spin_unlock(&vfsmount_lock);
+out1:
+	up(&nd->dentry->d_inode->i_sem);
+out:
+	up_write(&namespace_sem);
+	if (!err)
+		path_release(&parent_nd);
+	path_release(&old_nd);
+	return err;
+}
+
+/*
+ * create a new mount for userspace and request it to be added into the
+ * namespace's tree
+ */
+static int do_new_mount(struct nameidata *nd, char *type, int flags,
+			int mnt_flags, char *name, void *data)
+{
+	struct vfsmount *mnt;
+
+	if (!type || !memchr(type, 0, PAGE_SIZE))
+		return -EINVAL;
+
+	/* we need capabilities... */
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	mnt = do_kern_mount(type, flags, name, data);
+	if (IS_ERR(mnt))
+		return PTR_ERR(mnt);
+
+	return do_add_mount(mnt, nd, mnt_flags, NULL);
+}
+
+/*
+ * add a mount into a namespace's mount tree
+ * - provide the option of adding the new mount to an expiration list
+ */
+int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd,
+		 int mnt_flags, struct list_head *fslist)
+{
+	int err;
+
+	down_write(&namespace_sem);
+	/* Something was mounted here while we slept */
+	while (d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry))
+		;
+	err = -EINVAL;
+	if (!check_mnt(nd->mnt))
+		goto unlock;
+
+	/* Refuse the same filesystem on the same mount point */
+	err = -EBUSY;
+	if (nd->mnt->mnt_sb == newmnt->mnt_sb &&
+	    nd->mnt->mnt_root == nd->dentry)
+		goto unlock;
+
+	err = -EINVAL;
+	if (S_ISLNK(newmnt->mnt_root->d_inode->i_mode))
+		goto unlock;
+
+	newmnt->mnt_flags = mnt_flags;
+	if ((err = graft_tree(newmnt, nd)))
+		goto unlock;
+
+	if (fslist) {
+		/* add to the specified expiration list */
+		spin_lock(&vfsmount_lock);
+		list_add_tail(&newmnt->mnt_expire, fslist);
+		spin_unlock(&vfsmount_lock);
+	}
+	up_write(&namespace_sem);
+	return 0;
+
+unlock:
+	up_write(&namespace_sem);
+	mntput(newmnt);
+	return err;
+}
+
+EXPORT_SYMBOL_GPL(do_add_mount);
+
+static void expire_mount(struct vfsmount *mnt, struct list_head *mounts,
+				struct list_head *umounts)
+{
+	spin_lock(&vfsmount_lock);
+
+	/*
+	 * Check if mount is still attached, if not, let whoever holds it deal
+	 * with the sucker
+	 */
+	if (mnt->mnt_parent == mnt) {
+		spin_unlock(&vfsmount_lock);
+		return;
+	}
+
+	/*
+	 * Check that it is still dead: the count should now be 2 - as
+	 * contributed by the vfsmount parent and the mntget above
+	 */
+	if (!propagate_mount_busy(mnt, 2)) {
+		/* delete from the namespace */
+		touch_namespace(mnt->mnt_namespace);
+		list_del_init(&mnt->mnt_list);
+		mnt->mnt_namespace = NULL;
+		umount_tree(mnt, 1, umounts);
+		spin_unlock(&vfsmount_lock);
+	} else {
+		/*
+		 * Someone brought it back to life whilst we didn't have any
+		 * locks held so return it to the expiration list
+		 */
+		list_add_tail(&mnt->mnt_expire, mounts);
+		spin_unlock(&vfsmount_lock);
+	}
+}
+
+/*
+ * process a list of expirable mountpoints with the intent of discarding any
+ * mountpoints that aren't in use and haven't been touched since last we came
+ * here
+ */
+void mark_mounts_for_expiry(struct list_head *mounts)
+{
+	struct namespace *namespace;
+	struct vfsmount *mnt, *next;
+	LIST_HEAD(graveyard);
+
+	if (list_empty(mounts))
+		return;
+
+	spin_lock(&vfsmount_lock);
+
+	/* extract from the expiration list every vfsmount that matches the
+	 * following criteria:
+	 * - only referenced by its parent vfsmount
+	 * - still marked for expiry (marked on the last call here; marks are
+	 *   cleared by mntput())
+	 */
+	list_for_each_entry_safe(mnt, next, mounts, mnt_expire) {
+		if (!xchg(&mnt->mnt_expiry_mark, 1) ||
+		    atomic_read(&mnt->mnt_count) != 1)
+			continue;
+
+		mntget(mnt);
+		list_move(&mnt->mnt_expire, &graveyard);
+	}
+
+	/*
+	 * go through the vfsmounts we've just consigned to the graveyard to
+	 * - check that they're still dead
+	 * - delete the vfsmount from the appropriate namespace under lock
+	 * - dispose of the corpse
+	 */
+	while (!list_empty(&graveyard)) {
+		LIST_HEAD(umounts);
+		mnt = list_entry(graveyard.next, struct vfsmount, mnt_expire);
+		list_del_init(&mnt->mnt_expire);
+
+		/* don't do anything if the namespace is dead - all the
+		 * vfsmounts from it are going away anyway */
+		namespace = mnt->mnt_namespace;
+		if (!namespace || !namespace->root)
+			continue;
+		get_namespace(namespace);
+
+		spin_unlock(&vfsmount_lock);
+		down_write(&namespace_sem);
+		expire_mount(mnt, mounts, &umounts);
+		up_write(&namespace_sem);
+		release_mounts(&umounts);
+		mntput(mnt);
+		put_namespace(namespace);
+		spin_lock(&vfsmount_lock);
+	}
+
+	spin_unlock(&vfsmount_lock);
+}
+
+EXPORT_SYMBOL_GPL(mark_mounts_for_expiry);
+
+/*
+ * Some copy_from_user() implementations do not return the exact number of
+ * bytes remaining to copy on a fault.  But copy_mount_options() requires that.
+ * Note that this function differs from copy_from_user() in that it will oops
+ * on bad values of `to', rather than returning a short copy.
+ */
+static long exact_copy_from_user(void *to, const void __user * from,
+				 unsigned long n)
+{
+	char *t = to;
+	const char __user *f = from;
+	char c;
+
+	if (!access_ok(VERIFY_READ, from, n))
+		return n;
+
+	while (n) {
+		if (__get_user(c, f)) {
+			memset(t, 0, n);
+			break;
+		}
+		*t++ = c;
+		f++;
+		n--;
+	}
+	return n;
+}
+
+int copy_mount_options(const void __user * data, unsigned long *where)
+{
+	int i;
+	unsigned long page;
+	unsigned long size;
+
+	*where = 0;
+	if (!data)
+		return 0;
+
+	if (!(page = __get_free_page(GFP_KERNEL)))
+		return -ENOMEM;
+
+	/* We only care that *some* data at the address the user
+	 * gave us is valid.  Just in case, we'll zero
+	 * the remainder of the page.
+	 */
+	/* copy_from_user cannot cross TASK_SIZE ! */
+	size = TASK_SIZE - (unsigned long)data;
+	if (size > PAGE_SIZE)
+		size = PAGE_SIZE;
+
+	i = size - exact_copy_from_user((void *)page, data, size);
+	if (!i) {
+		free_page(page);
+		return -EFAULT;
+	}
+	if (i != PAGE_SIZE)
+		memset((char *)page + i, 0, PAGE_SIZE - i);
+	*where = page;
+	return 0;
+}
+
+/*
+ * Flags is a 32-bit value that allows up to 31 non-fs dependent flags to
+ * be given to the mount() call (ie: read-only, no-dev, no-suid etc).
+ *
+ * data is a (void *) that can point to any structure up to
+ * PAGE_SIZE-1 bytes, which can contain arbitrary fs-dependent
+ * information (or be NULL).
+ *
+ * Pre-0.97 versions of mount() didn't have a flags word.
+ * When the flags word was introduced its top half was required
+ * to have the magic value 0xC0ED, and this remained so until 2.4.0-test9.
+ * Therefore, if this magic number is present, it carries no information
+ * and must be discarded.
+ */
+long do_mount(char *dev_name, char *dir_name, char *type_page,
+		  unsigned long flags, void *data_page)
+{
+	struct nameidata nd;
+	int retval = 0;
+	int mnt_flags = 0;
+
+	/* Discard magic */
+	if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
+		flags &= ~MS_MGC_MSK;
+
+	/* Basic sanity checks */
+
+	if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE))
+		return -EINVAL;
+	if (dev_name && !memchr(dev_name, 0, PAGE_SIZE))
+		return -EINVAL;
+
+	if (data_page)
+		((char *)data_page)[PAGE_SIZE - 1] = 0;
+
+	/* Separate the per-mountpoint flags */
+	if (flags & MS_NOSUID)
+		mnt_flags |= MNT_NOSUID;
+	if (flags & MS_NODEV)
+		mnt_flags |= MNT_NODEV;
+	if (flags & MS_NOEXEC)
+		mnt_flags |= MNT_NOEXEC;
+	flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE);
+
+	/* ... and get the mountpoint */
+	retval = path_lookup(dir_name, LOOKUP_FOLLOW, &nd);
+	if (retval)
+		return retval;
+
+	retval = security_sb_mount(dev_name, &nd, type_page, flags, data_page);
+	if (retval)
+		goto dput_out;
+
+	if (flags & MS_REMOUNT)
+		retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags,
+				    data_page);
+	else if (flags & MS_BIND)
+		retval = do_loopback(&nd, dev_name, flags & MS_REC);
+	else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
+		retval = do_change_type(&nd, flags);
+	else if (flags & MS_MOVE)
+		retval = do_move_mount(&nd, dev_name);
+	else
+		retval = do_new_mount(&nd, type_page, flags, mnt_flags,
+				      dev_name, data_page);
+dput_out:
+	path_release(&nd);
+	return retval;
+}
+
+int copy_namespace(int flags, struct task_struct *tsk)
+{
+	struct namespace *namespace = tsk->namespace;
+	struct namespace *new_ns;
+	struct vfsmount *rootmnt = NULL, *pwdmnt = NULL, *altrootmnt = NULL;
+	struct fs_struct *fs = tsk->fs;
+	struct vfsmount *p, *q;
+
+	if (!namespace)
+		return 0;
+
+	get_namespace(namespace);
+
+	if (!(flags & CLONE_NEWNS))
+		return 0;
+
+	if (!capable(CAP_SYS_ADMIN)) {
+		put_namespace(namespace);
+		return -EPERM;
+	}
+
+	new_ns = kmalloc(sizeof(struct namespace), GFP_KERNEL);
+	if (!new_ns)
+		goto out;
+
+	atomic_set(&new_ns->count, 1);
+	INIT_LIST_HEAD(&new_ns->list);
+	init_waitqueue_head(&new_ns->poll);
+	new_ns->event = 0;
+
+	down_write(&namespace_sem);
+	/* First pass: copy the tree topology */
+	new_ns->root = copy_tree(namespace->root, namespace->root->mnt_root,
+					CL_COPY_ALL | CL_EXPIRE);
+	if (!new_ns->root) {
+		up_write(&namespace_sem);
+		kfree(new_ns);
+		goto out;
+	}
+	spin_lock(&vfsmount_lock);
+	list_add_tail(&new_ns->list, &new_ns->root->mnt_list);
+	spin_unlock(&vfsmount_lock);
+
+	/*
+	 * Second pass: switch the tsk->fs->* elements and mark new vfsmounts
+	 * as belonging to new namespace.  We have already acquired a private
+	 * fs_struct, so tsk->fs->lock is not needed.
+	 */
+	p = namespace->root;
+	q = new_ns->root;
+	while (p) {
+		q->mnt_namespace = new_ns;
+		if (fs) {
+			if (p == fs->rootmnt) {
+				rootmnt = p;
+				fs->rootmnt = mntget(q);
+			}
+			if (p == fs->pwdmnt) {
+				pwdmnt = p;
+				fs->pwdmnt = mntget(q);
+			}
+			if (p == fs->altrootmnt) {
+				altrootmnt = p;
+				fs->altrootmnt = mntget(q);
+			}
+		}
+		p = next_mnt(p, namespace->root);
+		q = next_mnt(q, new_ns->root);
+	}
+	up_write(&namespace_sem);
+
+	tsk->namespace = new_ns;
+
+	if (rootmnt)
+		mntput(rootmnt);
+	if (pwdmnt)
+		mntput(pwdmnt);
+	if (altrootmnt)
+		mntput(altrootmnt);
+
+	put_namespace(namespace);
+	return 0;
+
+out:
+	put_namespace(namespace);
+	return -ENOMEM;
+}
+
+asmlinkage long sys_mount(char __user * dev_name, char __user * dir_name,
+			  char __user * type, unsigned long flags,
+			  void __user * data)
+{
+	int retval;
+	unsigned long data_page;
+	unsigned long type_page;
+	unsigned long dev_page;
+	char *dir_page;
+
+	retval = copy_mount_options(type, &type_page);
+	if (retval < 0)
+		return retval;
+
+	dir_page = getname(dir_name);
+	retval = PTR_ERR(dir_page);
+	if (IS_ERR(dir_page))
+		goto out1;
+
+	retval = copy_mount_options(dev_name, &dev_page);
+	if (retval < 0)
+		goto out2;
+
+	retval = copy_mount_options(data, &data_page);
+	if (retval < 0)
+		goto out3;
+
+	lock_kernel();
+	retval = do_mount((char *)dev_page, dir_page, (char *)type_page,
+			  flags, (void *)data_page);
+	unlock_kernel();
+	free_page(data_page);
+
+out3:
+	free_page(dev_page);
+out2:
+	putname(dir_page);
+out1:
+	free_page(type_page);
+	return retval;
+}
+
+/*
+ * Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values.
+ * It can block. Requires the big lock held.
+ */
+void set_fs_root(struct fs_struct *fs, struct vfsmount *mnt,
+		 struct dentry *dentry)
+{
+	struct dentry *old_root;
+	struct vfsmount *old_rootmnt;
+	write_lock(&fs->lock);
+	old_root = fs->root;
+	old_rootmnt = fs->rootmnt;
+	fs->rootmnt = mntget(mnt);
+	fs->root = dget(dentry);
+	write_unlock(&fs->lock);
+	if (old_root) {
+		dput(old_root);
+		mntput(old_rootmnt);
+	}
+}
+
+/*
+ * Replace the fs->{pwdmnt,pwd} with {mnt,dentry}. Put the old values.
+ * It can block. Requires the big lock held.
+ */
+void set_fs_pwd(struct fs_struct *fs, struct vfsmount *mnt,
+		struct dentry *dentry)
+{
+	struct dentry *old_pwd;
+	struct vfsmount *old_pwdmnt;
+
+	write_lock(&fs->lock);
+	old_pwd = fs->pwd;
+	old_pwdmnt = fs->pwdmnt;
+	fs->pwdmnt = mntget(mnt);
+	fs->pwd = dget(dentry);
+	write_unlock(&fs->lock);
+
+	if (old_pwd) {
+		dput(old_pwd);
+		mntput(old_pwdmnt);
+	}
+}
+
+static void chroot_fs_refs(struct nameidata *old_nd, struct nameidata *new_nd)
+{
+	struct task_struct *g, *p;
+	struct fs_struct *fs;
+
+	read_lock(&tasklist_lock);
+	do_each_thread(g, p) {
+		task_lock(p);
+		fs = p->fs;
+		if (fs) {
+			atomic_inc(&fs->count);
+			task_unlock(p);
+			if (fs->root == old_nd->dentry
+			    && fs->rootmnt == old_nd->mnt)
+				set_fs_root(fs, new_nd->mnt, new_nd->dentry);
+			if (fs->pwd == old_nd->dentry
+			    && fs->pwdmnt == old_nd->mnt)
+				set_fs_pwd(fs, new_nd->mnt, new_nd->dentry);
+			put_fs_struct(fs);
+		} else
+			task_unlock(p);
+	} while_each_thread(g, p);
+	read_unlock(&tasklist_lock);
+}
+
+/*
+ * pivot_root Semantics:
+ * Moves the root file system of the current process to the directory put_old,
+ * makes new_root as the new root file system of the current process, and sets
+ * root/cwd of all processes which had them on the current root to new_root.
+ *
+ * Restrictions:
+ * The new_root and put_old must be directories, and  must not be on the
+ * same file  system as the current process root. The put_old  must  be
+ * underneath new_root,  i.e. adding a non-zero number of /.. to the string
+ * pointed to by put_old must yield the same directory as new_root. No other
+ * file system may be mounted on put_old. After all, new_root is a mountpoint.
+ *
+ * Notes:
+ *  - we don't move root/cwd if they are not at the root (reason: if something
+ *    cared enough to change them, it's probably wrong to force them elsewhere)
+ *  - it's okay to pick a root that isn't the root of a file system, e.g.
+ *    /nfs/my_root where /nfs is the mount point. It must be a mountpoint,
+ *    though, so you may need to say mount --bind /nfs/my_root /nfs/my_root
+ *    first.
+ */
+asmlinkage long sys_pivot_root(const char __user * new_root,
+			       const char __user * put_old)
+{
+	struct vfsmount *tmp;
+	struct nameidata new_nd, old_nd, parent_nd, root_parent, user_nd;
+	int error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	lock_kernel();
+
+	error = __user_walk(new_root, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
+			    &new_nd);
+	if (error)
+		goto out0;
+	error = -EINVAL;
+	if (!check_mnt(new_nd.mnt))
+		goto out1;
+
+	error = __user_walk(put_old, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old_nd);
+	if (error)
+		goto out1;
+
+	error = security_sb_pivotroot(&old_nd, &new_nd);
+	if (error) {
+		path_release(&old_nd);
+		goto out1;
+	}
+
+	read_lock(&current->fs->lock);
+	user_nd.mnt = mntget(current->fs->rootmnt);
+	user_nd.dentry = dget(current->fs->root);
+	read_unlock(&current->fs->lock);
+	down_write(&namespace_sem);
+	down(&old_nd.dentry->d_inode->i_sem);
+	error = -EINVAL;
+	if (IS_MNT_SHARED(old_nd.mnt) ||
+		IS_MNT_SHARED(new_nd.mnt->mnt_parent) ||
+		IS_MNT_SHARED(user_nd.mnt->mnt_parent))
+		goto out2;
+	if (!check_mnt(user_nd.mnt))
+		goto out2;
+	error = -ENOENT;
+	if (IS_DEADDIR(new_nd.dentry->d_inode))
+		goto out2;
+	if (d_unhashed(new_nd.dentry) && !IS_ROOT(new_nd.dentry))
+		goto out2;
+	if (d_unhashed(old_nd.dentry) && !IS_ROOT(old_nd.dentry))
+		goto out2;
+	error = -EBUSY;
+	if (new_nd.mnt == user_nd.mnt || old_nd.mnt == user_nd.mnt)
+		goto out2; /* loop, on the same file system  */
+	error = -EINVAL;
+	if (user_nd.mnt->mnt_root != user_nd.dentry)
+		goto out2; /* not a mountpoint */
+	if (user_nd.mnt->mnt_parent == user_nd.mnt)
+		goto out2; /* not attached */
+	if (new_nd.mnt->mnt_root != new_nd.dentry)
+		goto out2; /* not a mountpoint */
+	if (new_nd.mnt->mnt_parent == new_nd.mnt)
+		goto out2; /* not attached */
+	tmp = old_nd.mnt; /* make sure we can reach put_old from new_root */
+	spin_lock(&vfsmount_lock);
+	if (tmp != new_nd.mnt) {
+		for (;;) {
+			if (tmp->mnt_parent == tmp)
+				goto out3; /* already mounted on put_old */
+			if (tmp->mnt_parent == new_nd.mnt)
+				break;
+			tmp = tmp->mnt_parent;
+		}
+		if (!is_subdir(tmp->mnt_mountpoint, new_nd.dentry))
+			goto out3;
+	} else if (!is_subdir(old_nd.dentry, new_nd.dentry))
+		goto out3;
+	detach_mnt(new_nd.mnt, &parent_nd);
+	detach_mnt(user_nd.mnt, &root_parent);
+	attach_mnt(user_nd.mnt, &old_nd);     /* mount old root on put_old */
+	attach_mnt(new_nd.mnt, &root_parent); /* mount new_root on / */
+	touch_namespace(current->namespace);
+	spin_unlock(&vfsmount_lock);
+	chroot_fs_refs(&user_nd, &new_nd);
+	security_sb_post_pivotroot(&user_nd, &new_nd);
+	error = 0;
+	path_release(&root_parent);
+	path_release(&parent_nd);
+out2:
+	up(&old_nd.dentry->d_inode->i_sem);
+	up_write(&namespace_sem);
+	path_release(&user_nd);
+	path_release(&old_nd);
+out1:
+	path_release(&new_nd);
+out0:
+	unlock_kernel();
+	return error;
+out3:
+	spin_unlock(&vfsmount_lock);
+	goto out2;
+}
+
+static void __init init_mount_tree(void)
+{
+	struct vfsmount *mnt;
+	struct namespace *namespace;
+	struct task_struct *g, *p;
+
+	mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);
+	if (IS_ERR(mnt))
+		panic("Can't create rootfs");
+	namespace = kmalloc(sizeof(*namespace), GFP_KERNEL);
+	if (!namespace)
+		panic("Can't allocate initial namespace");
+	atomic_set(&namespace->count, 1);
+	INIT_LIST_HEAD(&namespace->list);
+	init_waitqueue_head(&namespace->poll);
+	namespace->event = 0;
+	list_add(&mnt->mnt_list, &namespace->list);
+	namespace->root = mnt;
+	mnt->mnt_namespace = namespace;
+
+	init_task.namespace = namespace;
+	read_lock(&tasklist_lock);
+	do_each_thread(g, p) {
+		get_namespace(namespace);
+		p->namespace = namespace;
+	} while_each_thread(g, p);
+	read_unlock(&tasklist_lock);
+
+	set_fs_pwd(current->fs, namespace->root, namespace->root->mnt_root);
+	set_fs_root(current->fs, namespace->root, namespace->root->mnt_root);
+}
+
+void __init mnt_init(unsigned long mempages)
+{
+	struct list_head *d;
+	unsigned int nr_hash;
+	int i;
+
+	init_rwsem(&namespace_sem);
+
+	mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount),
+			0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL, NULL);
+
+	mount_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC);
+
+	if (!mount_hashtable)
+		panic("Failed to allocate mount hash table\n");
+
+	/*
+	 * Find the power-of-two list-heads that can fit into the allocation..
+	 * We don't guarantee that "sizeof(struct list_head)" is necessarily
+	 * a power-of-two.
+	 */
+	nr_hash = PAGE_SIZE / sizeof(struct list_head);
+	hash_bits = 0;
+	do {
+		hash_bits++;
+	} while ((nr_hash >> hash_bits) != 0);
+	hash_bits--;
+
+	/*
+	 * Re-calculate the actual number of entries and the mask
+	 * from the number of bits we can fit.
+	 */
+	nr_hash = 1UL << hash_bits;
+	hash_mask = nr_hash - 1;
+
+	printk("Mount-cache hash table entries: %d\n", nr_hash);
+
+	/* And initialize the newly allocated array */
+	d = mount_hashtable;
+	i = nr_hash;
+	do {
+		INIT_LIST_HEAD(d);
+		d++;
+		i--;
+	} while (i);
+	sysfs_init();
+	init_rootfs();
+	init_mount_tree();
+}
+
+void __put_namespace(struct namespace *namespace)
+{
+	struct vfsmount *root = namespace->root;
+	LIST_HEAD(umount_list);
+	namespace->root = NULL;
+	spin_unlock(&vfsmount_lock);
+	down_write(&namespace_sem);
+	spin_lock(&vfsmount_lock);
+	umount_tree(root, 0, &umount_list);
+	spin_unlock(&vfsmount_lock);
+	up_write(&namespace_sem);
+	release_mounts(&umount_list);
+	kfree(namespace);
+}
diff -urN oldtree/fs/ncpfs/inode.c newtree/fs/ncpfs/inode.c
--- oldtree/fs/ncpfs/inode.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/ncpfs/inode.c	2006-02-13 19:20:00.712404000 -0500
@@ -716,10 +716,8 @@
 	fput(server->ncp_filp);
 	kill_proc(server->m.wdog_pid, SIGTERM, 1);
 
-	if (server->priv.data) 
-		ncp_kfree_s(server->priv.data, server->priv.len);
-	if (server->auth.object_name)
-		ncp_kfree_s(server->auth.object_name, server->auth.object_name_len);
+	kfree(server->priv.data);
+	kfree(server->auth.object_name);
 	vfree(server->packet);
 	sb->s_fs_info = NULL;
 	kfree(server);
@@ -958,11 +956,6 @@
 	return result;
 }
 
-#ifdef DEBUG_NCP_MALLOC
-int ncp_malloced;
-int ncp_current_malloced;
-#endif
-
 static struct super_block *ncp_get_sb(struct file_system_type *fs_type,
 	int flags, const char *dev_name, void *data)
 {
@@ -981,10 +974,6 @@
 	int err;
 	DPRINTK("ncpfs: init_module called\n");
 
-#ifdef DEBUG_NCP_MALLOC
-	ncp_malloced = 0;
-	ncp_current_malloced = 0;
-#endif
 	err = init_inodecache();
 	if (err)
 		goto out1;
@@ -1003,10 +992,6 @@
 	DPRINTK("ncpfs: cleanup_module called\n");
 	unregister_filesystem(&ncp_fs_type);
 	destroy_inodecache();
-#ifdef DEBUG_NCP_MALLOC
-	PRINTK("ncp_malloced: %d\n", ncp_malloced);
-	PRINTK("ncp_current_malloced: %d\n", ncp_current_malloced);
-#endif
 }
 
 module_init(init_ncp_fs)
diff -urN oldtree/fs/ncpfs/ioctl.c newtree/fs/ncpfs/ioctl.c
--- oldtree/fs/ncpfs/ioctl.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/ncpfs/ioctl.c	2006-02-13 19:20:00.713403848 -0500
@@ -517,10 +517,11 @@
 			if (user.object_name_len > NCP_OBJECT_NAME_MAX_LEN)
 				return -ENOMEM;
 			if (user.object_name_len) {
-				newname = ncp_kmalloc(user.object_name_len, GFP_USER);
-				if (!newname) return -ENOMEM;
+				newname = kmalloc(user.object_name_len, GFP_USER);
+				if (!newname)
+					return -ENOMEM;
 				if (copy_from_user(newname, user.object_name, user.object_name_len)) {
-					ncp_kfree_s(newname, user.object_name_len);
+					kfree(newname);
 					return -EFAULT;
 				}
 			} else {
@@ -539,8 +540,8 @@
 			server->priv.len = 0;
 			server->priv.data = NULL;
 			/* leave critical section */
-			if (oldprivate) ncp_kfree_s(oldprivate, oldprivatelen);
-			if (oldname) ncp_kfree_s(oldname, oldnamelen);
+			kfree(oldprivate);
+			kfree(oldname);
 			return 0;
 		}
 	case NCP_IOC_GETPRIVATEDATA:
@@ -580,10 +581,11 @@
 			if (user.len > NCP_PRIVATE_DATA_MAX_LEN)
 				return -ENOMEM;
 			if (user.len) {
-				new = ncp_kmalloc(user.len, GFP_USER);
-				if (!new) return -ENOMEM;
+				new = kmalloc(user.len, GFP_USER);
+				if (!new)
+					return -ENOMEM;
 				if (copy_from_user(new, user.data, user.len)) {
-					ncp_kfree_s(new, user.len);
+					kfree(new);
 					return -EFAULT;
 				}
 			} else {
@@ -595,7 +597,7 @@
 			server->priv.len = user.len;
 			server->priv.data = new;
 			/* leave critical section */
-			if (old) ncp_kfree_s(old, oldlen);
+			kfree(old);
 			return 0;
 		}
 
diff -urN oldtree/fs/ncpfs/ioctl.c.orig newtree/fs/ncpfs/ioctl.c.orig
--- oldtree/fs/ncpfs/ioctl.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/ncpfs/ioctl.c.orig	2006-02-13 19:20:00.714403696 -0500
@@ -0,0 +1,653 @@
+/*
+ *  ioctl.c
+ *
+ *  Copyright (C) 1995, 1996 by Volker Lendecke
+ *  Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
+ *  Modified 1998, 1999 Wolfram Pienkoss for NLS
+ *
+ */
+
+#include <linux/config.h>
+
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/highuid.h>
+#include <linux/vmalloc.h>
+
+#include <linux/ncp_fs.h>
+
+#include "ncplib_kernel.h"
+
+/* maximum limit for ncp_objectname_ioctl */
+#define NCP_OBJECT_NAME_MAX_LEN	4096
+/* maximum limit for ncp_privatedata_ioctl */
+#define NCP_PRIVATE_DATA_MAX_LEN 8192
+/* maximum negotiable packet size */
+#define NCP_PACKET_SIZE_INTERNAL 65536
+
+static int
+ncp_get_fs_info(struct ncp_server * server, struct file *file,
+		struct ncp_fs_info __user *arg)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct ncp_fs_info info;
+
+	if ((file_permission(file, MAY_WRITE) != 0)
+	    && (current->uid != server->m.mounted_uid)) {
+		return -EACCES;
+	}
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	if (info.version != NCP_GET_FS_INFO_VERSION) {
+		DPRINTK("info.version invalid: %d\n", info.version);
+		return -EINVAL;
+	}
+	/* TODO: info.addr = server->m.serv_addr; */
+	SET_UID(info.mounted_uid, server->m.mounted_uid);
+	info.connection		= server->connection;
+	info.buffer_size	= server->buffer_size;
+	info.volume_number	= NCP_FINFO(inode)->volNumber;
+	info.directory_id	= NCP_FINFO(inode)->DosDirNum;
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+static int
+ncp_get_fs_info_v2(struct ncp_server * server, struct file *file,
+		   struct ncp_fs_info_v2 __user * arg)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct ncp_fs_info_v2 info2;
+
+	if ((file_permission(file, MAY_WRITE) != 0)
+	    && (current->uid != server->m.mounted_uid)) {
+		return -EACCES;
+	}
+	if (copy_from_user(&info2, arg, sizeof(info2)))
+		return -EFAULT;
+
+	if (info2.version != NCP_GET_FS_INFO_VERSION_V2) {
+		DPRINTK("info.version invalid: %d\n", info2.version);
+		return -EINVAL;
+	}
+	info2.mounted_uid   = server->m.mounted_uid;
+	info2.connection    = server->connection;
+	info2.buffer_size   = server->buffer_size;
+	info2.volume_number = NCP_FINFO(inode)->volNumber;
+	info2.directory_id  = NCP_FINFO(inode)->DosDirNum;
+	info2.dummy1 = info2.dummy2 = info2.dummy3 = 0;
+
+	if (copy_to_user(arg, &info2, sizeof(info2)))
+		return -EFAULT;
+	return 0;
+}
+
+#ifdef CONFIG_NCPFS_NLS
+/* Here we are select the iocharset and the codepage for NLS.
+ * Thanks Petr Vandrovec for idea and many hints.
+ */
+static int
+ncp_set_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg)
+{
+	struct ncp_nls_ioctl user;
+	struct nls_table *codepage;
+	struct nls_table *iocharset;
+	struct nls_table *oldset_io;
+	struct nls_table *oldset_cp;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (server->root_setuped)
+		return -EBUSY;
+
+	if (copy_from_user(&user, arg, sizeof(user)))
+		return -EFAULT;
+
+	codepage = NULL;
+	user.codepage[NCP_IOCSNAME_LEN] = 0;
+	if (!user.codepage[0] || !strcmp(user.codepage, "default"))
+		codepage = load_nls_default();
+	else {
+		codepage = load_nls(user.codepage);
+		if (!codepage) {
+			return -EBADRQC;
+		}
+	}
+
+	iocharset = NULL;
+	user.iocharset[NCP_IOCSNAME_LEN] = 0;
+	if (!user.iocharset[0] || !strcmp(user.iocharset, "default")) {
+		iocharset = load_nls_default();
+		NCP_CLR_FLAG(server, NCP_FLAG_UTF8);
+	} else if (!strcmp(user.iocharset, "utf8")) {
+		iocharset = load_nls_default();
+		NCP_SET_FLAG(server, NCP_FLAG_UTF8);
+	} else {
+		iocharset = load_nls(user.iocharset);
+		if (!iocharset) {
+			unload_nls(codepage);
+			return -EBADRQC;
+		}
+		NCP_CLR_FLAG(server, NCP_FLAG_UTF8);
+	}
+
+	oldset_cp = server->nls_vol;
+	server->nls_vol = codepage;
+	oldset_io = server->nls_io;
+	server->nls_io = iocharset;
+
+	if (oldset_cp)
+		unload_nls(oldset_cp);
+	if (oldset_io)
+		unload_nls(oldset_io);
+
+	return 0;
+}
+
+static int
+ncp_get_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg)
+{
+	struct ncp_nls_ioctl user;
+	int len;
+
+	memset(&user, 0, sizeof(user));
+	if (server->nls_vol && server->nls_vol->charset) {
+		len = strlen(server->nls_vol->charset);
+		if (len > NCP_IOCSNAME_LEN)
+			len = NCP_IOCSNAME_LEN;
+		strncpy(user.codepage, server->nls_vol->charset, len);
+		user.codepage[len] = 0;
+	}
+
+	if (NCP_IS_FLAG(server, NCP_FLAG_UTF8))
+		strcpy(user.iocharset, "utf8");
+	else if (server->nls_io && server->nls_io->charset) {
+		len = strlen(server->nls_io->charset);
+		if (len > NCP_IOCSNAME_LEN)
+			len = NCP_IOCSNAME_LEN;
+		strncpy(user.iocharset,	server->nls_io->charset, len);
+		user.iocharset[len] = 0;
+	}
+
+	if (copy_to_user(arg, &user, sizeof(user)))
+		return -EFAULT;
+	return 0;
+}
+#endif /* CONFIG_NCPFS_NLS */
+
+int ncp_ioctl(struct inode *inode, struct file *filp,
+	      unsigned int cmd, unsigned long arg)
+{
+	struct ncp_server *server = NCP_SERVER(inode);
+	int result;
+	struct ncp_ioctl_request request;
+	char* bouncebuffer;
+	void __user *argp = (void __user *)arg;
+
+	switch (cmd) {
+	case NCP_IOC_NCPREQUEST:
+
+		if ((file_permission(filp, MAY_WRITE) != 0)
+		    && (current->uid != server->m.mounted_uid)) {
+			return -EACCES;
+		}
+		if (copy_from_user(&request, argp, sizeof(request)))
+			return -EFAULT;
+
+		if ((request.function > 255)
+		    || (request.size >
+		  NCP_PACKET_SIZE - sizeof(struct ncp_request_header))) {
+			return -EINVAL;
+		}
+		bouncebuffer = vmalloc(NCP_PACKET_SIZE_INTERNAL);
+		if (!bouncebuffer)
+			return -ENOMEM;
+		if (copy_from_user(bouncebuffer, request.data, request.size)) {
+			vfree(bouncebuffer);
+			return -EFAULT;
+		}
+		ncp_lock_server(server);
+
+		/* FIXME: We hack around in the server's structures
+		   here to be able to use ncp_request */
+
+		server->has_subfunction = 0;
+		server->current_size = request.size;
+		memcpy(server->packet, bouncebuffer, request.size);
+
+		result = ncp_request2(server, request.function, 
+			bouncebuffer, NCP_PACKET_SIZE_INTERNAL);
+		if (result < 0)
+			result = -EIO;
+		else
+			result = server->reply_size;
+		ncp_unlock_server(server);
+		DPRINTK("ncp_ioctl: copy %d bytes\n",
+			result);
+		if (result >= 0)
+			if (copy_to_user(request.data, bouncebuffer, result))
+				result = -EFAULT;
+		vfree(bouncebuffer);
+		return result;
+
+	case NCP_IOC_CONN_LOGGED_IN:
+
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
+		if (!(server->m.int_flags & NCP_IMOUNT_LOGGEDIN_POSSIBLE))
+			return -EINVAL;
+		if (server->root_setuped)
+			return -EBUSY;
+		server->root_setuped = 1;
+		return ncp_conn_logged_in(inode->i_sb);
+
+	case NCP_IOC_GET_FS_INFO:
+		return ncp_get_fs_info(server, filp, argp);
+
+	case NCP_IOC_GET_FS_INFO_V2:
+		return ncp_get_fs_info_v2(server, filp, argp);
+
+	case NCP_IOC_GETMOUNTUID2:
+		{
+			unsigned long tmp = server->m.mounted_uid;
+
+			if ((file_permission(filp, MAY_READ) != 0)
+			    && (current->uid != server->m.mounted_uid))
+			{
+				return -EACCES;
+			}
+			if (put_user(tmp, (unsigned long __user *)argp)) 
+				return -EFAULT;
+			return 0;
+		}
+
+	case NCP_IOC_GETROOT:
+		{
+			struct ncp_setroot_ioctl sr;
+
+			if ((file_permission(filp, MAY_READ) != 0)
+			    && (current->uid != server->m.mounted_uid))
+			{
+				return -EACCES;
+			}
+			if (server->m.mounted_vol[0]) {
+				struct dentry* dentry = inode->i_sb->s_root;
+
+				if (dentry) {
+					struct inode* inode = dentry->d_inode;
+				
+					if (inode) {
+						sr.volNumber = NCP_FINFO(inode)->volNumber;
+						sr.dirEntNum = NCP_FINFO(inode)->dirEntNum;
+						sr.namespace = server->name_space[sr.volNumber];
+					} else
+						DPRINTK("ncpfs: s_root->d_inode==NULL\n");
+				} else
+					DPRINTK("ncpfs: s_root==NULL\n");
+			} else {
+				sr.volNumber = -1;
+				sr.namespace = 0;
+				sr.dirEntNum = 0;
+			}
+			if (copy_to_user(argp, &sr, sizeof(sr)))
+				return -EFAULT;
+			return 0;
+		}
+	case NCP_IOC_SETROOT:
+		{
+			struct ncp_setroot_ioctl sr;
+			__u32 vnum;
+			__le32 de;
+			__le32 dosde;
+			struct dentry* dentry;
+
+			if (!capable(CAP_SYS_ADMIN))
+			{
+				return -EACCES;
+			}
+			if (server->root_setuped) return -EBUSY;
+			if (copy_from_user(&sr, argp, sizeof(sr)))
+				return -EFAULT;
+			if (sr.volNumber < 0) {
+				server->m.mounted_vol[0] = 0;
+				vnum = NCP_NUMBER_OF_VOLUMES;
+				de = 0;
+				dosde = 0;
+			} else if (sr.volNumber >= NCP_NUMBER_OF_VOLUMES) {
+				return -EINVAL;
+			} else if (ncp_mount_subdir(server, sr.volNumber,
+						sr.namespace, sr.dirEntNum,
+						&vnum, &de, &dosde)) {
+				return -ENOENT;
+			}
+			
+			dentry = inode->i_sb->s_root;
+			server->root_setuped = 1;
+			if (dentry) {
+				struct inode* inode = dentry->d_inode;
+				
+				if (inode) {
+					NCP_FINFO(inode)->volNumber = vnum;
+					NCP_FINFO(inode)->dirEntNum = de;
+					NCP_FINFO(inode)->DosDirNum = dosde;
+				} else
+					DPRINTK("ncpfs: s_root->d_inode==NULL\n");
+			} else
+				DPRINTK("ncpfs: s_root==NULL\n");
+
+			return 0;
+		}
+
+#ifdef CONFIG_NCPFS_PACKET_SIGNING	
+	case NCP_IOC_SIGN_INIT:
+		if ((file_permission(filp, MAY_WRITE) != 0)
+		    && (current->uid != server->m.mounted_uid))
+		{
+			return -EACCES;
+		}
+		if (argp) {
+			if (server->sign_wanted)
+			{
+				struct ncp_sign_init sign;
+
+				if (copy_from_user(&sign, argp, sizeof(sign)))
+					return -EFAULT;
+				memcpy(server->sign_root,sign.sign_root,8);
+				memcpy(server->sign_last,sign.sign_last,16);
+				server->sign_active = 1;
+			}
+			/* ignore when signatures not wanted */
+		} else {
+			server->sign_active = 0;
+		}
+		return 0;		
+		
+        case NCP_IOC_SIGN_WANTED:
+		if ((file_permission(filp, MAY_READ) != 0)
+		    && (current->uid != server->m.mounted_uid))
+		{
+			return -EACCES;
+		}
+		
+                if (put_user(server->sign_wanted, (int __user *)argp))
+			return -EFAULT;
+                return 0;
+	case NCP_IOC_SET_SIGN_WANTED:
+		{
+			int newstate;
+
+			if ((file_permission(filp, MAY_WRITE) != 0)
+			    && (current->uid != server->m.mounted_uid))
+			{
+				return -EACCES;
+			}
+			/* get only low 8 bits... */
+			if (get_user(newstate, (unsigned char __user *)argp))
+				return -EFAULT;
+			if (server->sign_active) {
+				/* cannot turn signatures OFF when active */
+				if (!newstate) return -EINVAL;
+			} else {
+				server->sign_wanted = newstate != 0;
+			}
+			return 0;
+		}
+
+#endif /* CONFIG_NCPFS_PACKET_SIGNING */
+
+#ifdef CONFIG_NCPFS_IOCTL_LOCKING
+	case NCP_IOC_LOCKUNLOCK:
+		if ((file_permission(filp, MAY_WRITE) != 0)
+		    && (current->uid != server->m.mounted_uid))
+		{
+			return -EACCES;
+		}
+		{
+			struct ncp_lock_ioctl	 rqdata;
+			int result;
+
+			if (copy_from_user(&rqdata, argp, sizeof(rqdata)))
+				return -EFAULT;
+			if (rqdata.origin != 0)
+				return -EINVAL;
+			/* check for cmd */
+			switch (rqdata.cmd) {
+				case NCP_LOCK_EX:
+				case NCP_LOCK_SH:
+						if (rqdata.timeout == 0)
+							rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT;
+						else if (rqdata.timeout > NCP_LOCK_MAX_TIMEOUT)
+							rqdata.timeout = NCP_LOCK_MAX_TIMEOUT;
+						break;
+				case NCP_LOCK_LOG:
+						rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT;	/* has no effect */
+				case NCP_LOCK_CLEAR:
+						break;
+				default:
+						return -EINVAL;
+			}
+			/* locking needs both read and write access */
+			if ((result = ncp_make_open(inode, O_RDWR)) != 0)
+			{
+				return result;
+			}
+			result = -EIO;
+			if (!ncp_conn_valid(server))
+				goto outrel;
+			result = -EISDIR;
+			if (!S_ISREG(inode->i_mode))
+				goto outrel;
+			if (rqdata.cmd == NCP_LOCK_CLEAR)
+			{
+				result = ncp_ClearPhysicalRecord(NCP_SERVER(inode),
+							NCP_FINFO(inode)->file_handle, 
+							rqdata.offset,
+							rqdata.length);
+				if (result > 0) result = 0;	/* no such lock */
+			}
+			else
+			{
+				int lockcmd;
+
+				switch (rqdata.cmd)
+				{
+					case NCP_LOCK_EX:  lockcmd=1; break;
+					case NCP_LOCK_SH:  lockcmd=3; break;
+					default:	   lockcmd=0; break;
+				}
+				result = ncp_LogPhysicalRecord(NCP_SERVER(inode),
+							NCP_FINFO(inode)->file_handle,
+							lockcmd,
+							rqdata.offset,
+							rqdata.length,
+							rqdata.timeout);
+				if (result > 0) result = -EAGAIN;
+			}
+outrel:			
+			ncp_inode_close(inode);
+			return result;
+		}
+#endif	/* CONFIG_NCPFS_IOCTL_LOCKING */
+
+	case NCP_IOC_GETOBJECTNAME:
+		if (current->uid != server->m.mounted_uid) {
+			return -EACCES;
+		}
+		{
+			struct ncp_objectname_ioctl user;
+			size_t outl;
+
+			if (copy_from_user(&user, argp, sizeof(user)))
+				return -EFAULT;
+			user.auth_type = server->auth.auth_type;
+			outl = user.object_name_len;
+			user.object_name_len = server->auth.object_name_len;
+			if (outl > user.object_name_len)
+				outl = user.object_name_len;
+			if (outl) {
+				if (copy_to_user(user.object_name,
+						 server->auth.object_name,
+						 outl)) return -EFAULT;
+			}
+			if (copy_to_user(argp, &user, sizeof(user)))
+				return -EFAULT;
+			return 0;
+		}
+	case NCP_IOC_SETOBJECTNAME:
+		if (current->uid != server->m.mounted_uid) {
+			return -EACCES;
+		}
+		{
+			struct ncp_objectname_ioctl user;
+			void* newname;
+			void* oldname;
+			size_t oldnamelen;
+			void* oldprivate;
+			size_t oldprivatelen;
+
+			if (copy_from_user(&user, argp, sizeof(user)))
+				return -EFAULT;
+			if (user.object_name_len > NCP_OBJECT_NAME_MAX_LEN)
+				return -ENOMEM;
+			if (user.object_name_len) {
+				newname = ncp_kmalloc(user.object_name_len, GFP_USER);
+				if (!newname) return -ENOMEM;
+				if (copy_from_user(newname, user.object_name, user.object_name_len)) {
+					ncp_kfree_s(newname, user.object_name_len);
+					return -EFAULT;
+				}
+			} else {
+				newname = NULL;
+			}
+			/* enter critical section */
+			/* maybe that kfree can sleep so do that this way */
+			/* it is at least more SMP friendly (in future...) */
+			oldname = server->auth.object_name;
+			oldnamelen = server->auth.object_name_len;
+			oldprivate = server->priv.data;
+			oldprivatelen = server->priv.len;
+			server->auth.auth_type = user.auth_type;
+			server->auth.object_name_len = user.object_name_len;
+			server->auth.object_name = newname;
+			server->priv.len = 0;
+			server->priv.data = NULL;
+			/* leave critical section */
+			if (oldprivate) ncp_kfree_s(oldprivate, oldprivatelen);
+			if (oldname) ncp_kfree_s(oldname, oldnamelen);
+			return 0;
+		}
+	case NCP_IOC_GETPRIVATEDATA:
+		if (current->uid != server->m.mounted_uid) {
+			return -EACCES;
+		}
+		{
+			struct ncp_privatedata_ioctl user;
+			size_t outl;
+
+			if (copy_from_user(&user, argp, sizeof(user)))
+				return -EFAULT;
+			outl = user.len;
+			user.len = server->priv.len;
+			if (outl > user.len) outl = user.len;
+			if (outl) {
+				if (copy_to_user(user.data,
+						 server->priv.data,
+						 outl)) return -EFAULT;
+			}
+			if (copy_to_user(argp, &user, sizeof(user)))
+				return -EFAULT;
+			return 0;
+		}
+	case NCP_IOC_SETPRIVATEDATA:
+		if (current->uid != server->m.mounted_uid) {
+			return -EACCES;
+		}
+		{
+			struct ncp_privatedata_ioctl user;
+			void* new;
+			void* old;
+			size_t oldlen;
+
+			if (copy_from_user(&user, argp, sizeof(user)))
+				return -EFAULT;
+			if (user.len > NCP_PRIVATE_DATA_MAX_LEN)
+				return -ENOMEM;
+			if (user.len) {
+				new = ncp_kmalloc(user.len, GFP_USER);
+				if (!new) return -ENOMEM;
+				if (copy_from_user(new, user.data, user.len)) {
+					ncp_kfree_s(new, user.len);
+					return -EFAULT;
+				}
+			} else {
+				new = NULL;
+			}
+			/* enter critical section */
+			old = server->priv.data;
+			oldlen = server->priv.len;
+			server->priv.len = user.len;
+			server->priv.data = new;
+			/* leave critical section */
+			if (old) ncp_kfree_s(old, oldlen);
+			return 0;
+		}
+
+#ifdef CONFIG_NCPFS_NLS
+	case NCP_IOC_SETCHARSETS:
+		return ncp_set_charsets(server, argp);
+		
+	case NCP_IOC_GETCHARSETS:
+		return ncp_get_charsets(server, argp);
+
+#endif /* CONFIG_NCPFS_NLS */
+
+	case NCP_IOC_SETDENTRYTTL:
+		if ((file_permission(filp, MAY_WRITE) != 0) &&
+				 (current->uid != server->m.mounted_uid))
+			return -EACCES;
+		{
+			u_int32_t user;
+
+			if (copy_from_user(&user, argp, sizeof(user)))
+				return -EFAULT;
+			/* 20 secs at most... */
+			if (user > 20000)
+				return -EINVAL;
+			user = (user * HZ) / 1000;
+			server->dentry_ttl = user;
+			return 0;
+		}
+		
+	case NCP_IOC_GETDENTRYTTL:
+		{
+			u_int32_t user = (server->dentry_ttl * 1000) / HZ;
+			if (copy_to_user(argp, &user, sizeof(user)))
+				return -EFAULT;
+			return 0;
+		}
+
+	}
+/* #ifdef CONFIG_UID16 */
+	/* NCP_IOC_GETMOUNTUID may be same as NCP_IOC_GETMOUNTUID2,
+           so we have this out of switch */
+	if (cmd == NCP_IOC_GETMOUNTUID) {
+		__kernel_uid_t uid = 0;
+		if ((file_permission(filp, MAY_READ) != 0)
+		    && (current->uid != server->m.mounted_uid)) {
+			return -EACCES;
+		}
+		SET_UID(uid, server->m.mounted_uid);
+		if (put_user(uid, (__kernel_uid_t __user *)argp))
+			return -EFAULT;
+		return 0;
+	}
+/* #endif */
+	return -EINVAL;
+}
diff -urN oldtree/fs/ntfs/file.c newtree/fs/ntfs/file.c
--- oldtree/fs/ntfs/file.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/ntfs/file.c	2006-02-13 19:20:00.715403544 -0500
@@ -248,7 +248,7 @@
 		 * enough to make ntfs_writepage() work.
 		 */
 		write_lock_irqsave(&ni->size_lock, flags);
-		ni->initialized_size = (index + 1) << PAGE_CACHE_SHIFT;
+		ni->initialized_size = (s64)(index + 1) << PAGE_CACHE_SHIFT;
 		if (ni->initialized_size > new_init_size)
 			ni->initialized_size = new_init_size;
 		write_unlock_irqrestore(&ni->size_lock, flags);
diff -urN oldtree/fs/open.c newtree/fs/open.c
--- oldtree/fs/open.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/open.c	2006-02-13 19:20:00.716403392 -0500
@@ -25,6 +25,7 @@
 #include <linux/pagemap.h>
 #include <linux/syscalls.h>
 #include <linux/rcupdate.h>
+#include <linux/audit.h>
 
 #include <asm/unistd.h>
 
@@ -613,6 +614,8 @@
 	dentry = file->f_dentry;
 	inode = dentry->d_inode;
 
+	audit_inode(NULL, inode, 0);
+
 	err = -EROFS;
 	if (IS_RDONLY(inode))
 		goto out_putf;
@@ -736,7 +739,10 @@
 
 	file = fget(fd);
 	if (file) {
-		error = chown_common(file->f_dentry, user, group);
+		struct dentry * dentry;
+		dentry = file->f_dentry;
+		audit_inode(NULL, dentry->d_inode, 0);
+		error = chown_common(dentry, user, group);
 		fput(file);
 	}
 	return error;
@@ -928,7 +934,7 @@
 	fdt = files_fdtable(files);
  	fd = find_next_zero_bit(fdt->open_fds->fds_bits,
 				fdt->max_fdset,
-				fdt->next_fd);
+				files->next_fd);
 
 	/*
 	 * N.B. For clone tasks sharing a files structure, this test
@@ -953,7 +959,7 @@
 
 	FD_SET(fd, fdt->open_fds);
 	FD_CLR(fd, fdt->close_on_exec);
-	fdt->next_fd = fd + 1;
+	files->next_fd = fd + 1;
 #if 1
 	/* Sanity check */
 	if (fdt->fd[fd] != NULL) {
@@ -974,8 +980,8 @@
 {
 	struct fdtable *fdt = files_fdtable(files);
 	__FD_CLR(fd, fdt->open_fds);
-	if (fd < fdt->next_fd)
-		fdt->next_fd = fd;
+	if (fd < files->next_fd)
+		files->next_fd = fd;
 }
 
 void fastcall put_unused_fd(unsigned int fd)
diff -urN oldtree/fs/open.c.orig newtree/fs/open.c.orig
--- oldtree/fs/open.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/open.c.orig	2006-02-13 19:20:00.717403240 -0500
@@ -0,0 +1,1160 @@
+/*
+ *  linux/fs/open.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/utime.h>
+#include <linux/file.h>
+#include <linux/smp_lock.h>
+#include <linux/quotaops.h>
+#include <linux/fsnotify.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/namei.h>
+#include <linux/backing-dev.h>
+#include <linux/security.h>
+#include <linux/mount.h>
+#include <linux/vfs.h>
+#include <asm/uaccess.h>
+#include <linux/fs.h>
+#include <linux/personality.h>
+#include <linux/pagemap.h>
+#include <linux/syscalls.h>
+#include <linux/rcupdate.h>
+#include <linux/audit.h>
+
+#include <asm/unistd.h>
+
+int vfs_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+	int retval = -ENODEV;
+
+	if (sb) {
+		retval = -ENOSYS;
+		if (sb->s_op->statfs) {
+			memset(buf, 0, sizeof(*buf));
+			retval = security_sb_statfs(sb);
+			if (retval)
+				return retval;
+			retval = sb->s_op->statfs(sb, buf);
+			if (retval == 0 && buf->f_frsize == 0)
+				buf->f_frsize = buf->f_bsize;
+		}
+	}
+	return retval;
+}
+
+EXPORT_SYMBOL(vfs_statfs);
+
+static int vfs_statfs_native(struct super_block *sb, struct statfs *buf)
+{
+	struct kstatfs st;
+	int retval;
+
+	retval = vfs_statfs(sb, &st);
+	if (retval)
+		return retval;
+
+	if (sizeof(*buf) == sizeof(st))
+		memcpy(buf, &st, sizeof(st));
+	else {
+		if (sizeof buf->f_blocks == 4) {
+			if ((st.f_blocks | st.f_bfree | st.f_bavail) &
+			    0xffffffff00000000ULL)
+				return -EOVERFLOW;
+			/*
+			 * f_files and f_ffree may be -1; it's okay to stuff
+			 * that into 32 bits
+			 */
+			if (st.f_files != -1 &&
+			    (st.f_files & 0xffffffff00000000ULL))
+				return -EOVERFLOW;
+			if (st.f_ffree != -1 &&
+			    (st.f_ffree & 0xffffffff00000000ULL))
+				return -EOVERFLOW;
+		}
+
+		buf->f_type = st.f_type;
+		buf->f_bsize = st.f_bsize;
+		buf->f_blocks = st.f_blocks;
+		buf->f_bfree = st.f_bfree;
+		buf->f_bavail = st.f_bavail;
+		buf->f_files = st.f_files;
+		buf->f_ffree = st.f_ffree;
+		buf->f_fsid = st.f_fsid;
+		buf->f_namelen = st.f_namelen;
+		buf->f_frsize = st.f_frsize;
+		memset(buf->f_spare, 0, sizeof(buf->f_spare));
+	}
+	return 0;
+}
+
+static int vfs_statfs64(struct super_block *sb, struct statfs64 *buf)
+{
+	struct kstatfs st;
+	int retval;
+
+	retval = vfs_statfs(sb, &st);
+	if (retval)
+		return retval;
+
+	if (sizeof(*buf) == sizeof(st))
+		memcpy(buf, &st, sizeof(st));
+	else {
+		buf->f_type = st.f_type;
+		buf->f_bsize = st.f_bsize;
+		buf->f_blocks = st.f_blocks;
+		buf->f_bfree = st.f_bfree;
+		buf->f_bavail = st.f_bavail;
+		buf->f_files = st.f_files;
+		buf->f_ffree = st.f_ffree;
+		buf->f_fsid = st.f_fsid;
+		buf->f_namelen = st.f_namelen;
+		buf->f_frsize = st.f_frsize;
+		memset(buf->f_spare, 0, sizeof(buf->f_spare));
+	}
+	return 0;
+}
+
+asmlinkage long sys_statfs(const char __user * path, struct statfs __user * buf)
+{
+	struct nameidata nd;
+	int error;
+
+	error = user_path_walk(path, &nd);
+	if (!error) {
+		struct statfs tmp;
+		error = vfs_statfs_native(nd.dentry->d_inode->i_sb, &tmp);
+		if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
+			error = -EFAULT;
+		path_release(&nd);
+	}
+	return error;
+}
+
+
+asmlinkage long sys_statfs64(const char __user *path, size_t sz, struct statfs64 __user *buf)
+{
+	struct nameidata nd;
+	long error;
+
+	if (sz != sizeof(*buf))
+		return -EINVAL;
+	error = user_path_walk(path, &nd);
+	if (!error) {
+		struct statfs64 tmp;
+		error = vfs_statfs64(nd.dentry->d_inode->i_sb, &tmp);
+		if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
+			error = -EFAULT;
+		path_release(&nd);
+	}
+	return error;
+}
+
+
+asmlinkage long sys_fstatfs(unsigned int fd, struct statfs __user * buf)
+{
+	struct file * file;
+	struct statfs tmp;
+	int error;
+
+	error = -EBADF;
+	file = fget(fd);
+	if (!file)
+		goto out;
+	error = vfs_statfs_native(file->f_dentry->d_inode->i_sb, &tmp);
+	if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
+		error = -EFAULT;
+	fput(file);
+out:
+	return error;
+}
+
+asmlinkage long sys_fstatfs64(unsigned int fd, size_t sz, struct statfs64 __user *buf)
+{
+	struct file * file;
+	struct statfs64 tmp;
+	int error;
+
+	if (sz != sizeof(*buf))
+		return -EINVAL;
+
+	error = -EBADF;
+	file = fget(fd);
+	if (!file)
+		goto out;
+	error = vfs_statfs64(file->f_dentry->d_inode->i_sb, &tmp);
+	if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
+		error = -EFAULT;
+	fput(file);
+out:
+	return error;
+}
+
+int do_truncate(struct dentry *dentry, loff_t length, struct file *filp)
+{
+	int err;
+	struct iattr newattrs;
+
+	/* Not pretty: "inode->i_size" shouldn't really be signed. But it is. */
+	if (length < 0)
+		return -EINVAL;
+
+	newattrs.ia_size = length;
+	newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
+	if (filp) {
+		newattrs.ia_file = filp;
+		newattrs.ia_valid |= ATTR_FILE;
+	}
+
+	down(&dentry->d_inode->i_sem);
+	err = notify_change(dentry, &newattrs);
+	up(&dentry->d_inode->i_sem);
+	return err;
+}
+
+static inline long do_sys_truncate(const char __user * path, loff_t length)
+{
+	struct nameidata nd;
+	struct inode * inode;
+	int error;
+
+	error = -EINVAL;
+	if (length < 0)	/* sorry, but loff_t says... */
+		goto out;
+
+	error = user_path_walk(path, &nd);
+	if (error)
+		goto out;
+	inode = nd.dentry->d_inode;
+
+	/* For directories it's -EISDIR, for other non-regulars - -EINVAL */
+	error = -EISDIR;
+	if (S_ISDIR(inode->i_mode))
+		goto dput_and_out;
+
+	error = -EINVAL;
+	if (!S_ISREG(inode->i_mode))
+		goto dput_and_out;
+
+	error = vfs_permission(&nd, MAY_WRITE);
+	if (error)
+		goto dput_and_out;
+
+	error = -EROFS;
+	if (IS_RDONLY(inode))
+		goto dput_and_out;
+
+	error = -EPERM;
+	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+		goto dput_and_out;
+
+	/*
+	 * Make sure that there are no leases.
+	 */
+	error = break_lease(inode, FMODE_WRITE);
+	if (error)
+		goto dput_and_out;
+
+	error = get_write_access(inode);
+	if (error)
+		goto dput_and_out;
+
+	error = locks_verify_truncate(inode, NULL, length);
+	if (!error) {
+		DQUOT_INIT(inode);
+		error = do_truncate(nd.dentry, length, NULL);
+	}
+	put_write_access(inode);
+
+dput_and_out:
+	path_release(&nd);
+out:
+	return error;
+}
+
+asmlinkage long sys_truncate(const char __user * path, unsigned long length)
+{
+	/* on 32-bit boxen it will cut the range 2^31--2^32-1 off */
+	return do_sys_truncate(path, (long)length);
+}
+
+static inline long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
+{
+	struct inode * inode;
+	struct dentry *dentry;
+	struct file * file;
+	int error;
+
+	error = -EINVAL;
+	if (length < 0)
+		goto out;
+	error = -EBADF;
+	file = fget(fd);
+	if (!file)
+		goto out;
+
+	/* explicitly opened as large or we are on 64-bit box */
+	if (file->f_flags & O_LARGEFILE)
+		small = 0;
+
+	dentry = file->f_dentry;
+	inode = dentry->d_inode;
+	error = -EINVAL;
+	if (!S_ISREG(inode->i_mode) || !(file->f_mode & FMODE_WRITE))
+		goto out_putf;
+
+	error = -EINVAL;
+	/* Cannot ftruncate over 2^31 bytes without large file support */
+	if (small && length > MAX_NON_LFS)
+		goto out_putf;
+
+	error = -EPERM;
+	if (IS_APPEND(inode))
+		goto out_putf;
+
+	error = locks_verify_truncate(inode, file, length);
+	if (!error)
+		error = do_truncate(dentry, length, file);
+out_putf:
+	fput(file);
+out:
+	return error;
+}
+
+asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length)
+{
+	return do_sys_ftruncate(fd, length, 1);
+}
+
+/* LFS versions of truncate are only needed on 32 bit machines */
+#if BITS_PER_LONG == 32
+asmlinkage long sys_truncate64(const char __user * path, loff_t length)
+{
+	return do_sys_truncate(path, length);
+}
+
+asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length)
+{
+	return do_sys_ftruncate(fd, length, 0);
+}
+#endif
+
+#ifdef __ARCH_WANT_SYS_UTIME
+
+/*
+ * sys_utime() can be implemented in user-level using sys_utimes().
+ * Is this for backwards compatibility?  If so, why not move it
+ * into the appropriate arch directory (for those architectures that
+ * need it).
+ */
+
+/* If times==NULL, set access and modification to current time,
+ * must be owner or have write permission.
+ * Else, update from *times, must be owner or super user.
+ */
+asmlinkage long sys_utime(char __user * filename, struct utimbuf __user * times)
+{
+	int error;
+	struct nameidata nd;
+	struct inode * inode;
+	struct iattr newattrs;
+
+	error = user_path_walk(filename, &nd);
+	if (error)
+		goto out;
+	inode = nd.dentry->d_inode;
+
+	error = -EROFS;
+	if (IS_RDONLY(inode))
+		goto dput_and_out;
+
+	/* Don't worry, the checks are done in inode_change_ok() */
+	newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
+	if (times) {
+		error = -EPERM;
+		if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+			goto dput_and_out;
+
+		error = get_user(newattrs.ia_atime.tv_sec, &times->actime);
+		newattrs.ia_atime.tv_nsec = 0;
+		if (!error) 
+			error = get_user(newattrs.ia_mtime.tv_sec, &times->modtime);
+		newattrs.ia_mtime.tv_nsec = 0;
+		if (error)
+			goto dput_and_out;
+
+		newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
+	} else {
+                error = -EACCES;
+                if (IS_IMMUTABLE(inode))
+                        goto dput_and_out;
+
+		if (current->fsuid != inode->i_uid &&
+		    (error = vfs_permission(&nd, MAY_WRITE)) != 0)
+			goto dput_and_out;
+	}
+	down(&inode->i_sem);
+	error = notify_change(nd.dentry, &newattrs);
+	up(&inode->i_sem);
+dput_and_out:
+	path_release(&nd);
+out:
+	return error;
+}
+
+#endif
+
+/* If times==NULL, set access and modification to current time,
+ * must be owner or have write permission.
+ * Else, update from *times, must be owner or super user.
+ */
+long do_utimes(char __user * filename, struct timeval * times)
+{
+	int error;
+	struct nameidata nd;
+	struct inode * inode;
+	struct iattr newattrs;
+
+	error = user_path_walk(filename, &nd);
+
+	if (error)
+		goto out;
+	inode = nd.dentry->d_inode;
+
+	error = -EROFS;
+	if (IS_RDONLY(inode))
+		goto dput_and_out;
+
+	/* Don't worry, the checks are done in inode_change_ok() */
+	newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
+	if (times) {
+		error = -EPERM;
+                if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+                        goto dput_and_out;
+
+		newattrs.ia_atime.tv_sec = times[0].tv_sec;
+		newattrs.ia_atime.tv_nsec = times[0].tv_usec * 1000;
+		newattrs.ia_mtime.tv_sec = times[1].tv_sec;
+		newattrs.ia_mtime.tv_nsec = times[1].tv_usec * 1000;
+		newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
+	} else {
+		error = -EACCES;
+                if (IS_IMMUTABLE(inode))
+                        goto dput_and_out;
+
+		if (current->fsuid != inode->i_uid &&
+		    (error = vfs_permission(&nd, MAY_WRITE)) != 0)
+			goto dput_and_out;
+	}
+	down(&inode->i_sem);
+	error = notify_change(nd.dentry, &newattrs);
+	up(&inode->i_sem);
+dput_and_out:
+	path_release(&nd);
+out:
+	return error;
+}
+
+asmlinkage long sys_utimes(char __user * filename, struct timeval __user * utimes)
+{
+	struct timeval times[2];
+
+	if (utimes && copy_from_user(&times, utimes, sizeof(times)))
+		return -EFAULT;
+	return do_utimes(filename, utimes ? times : NULL);
+}
+
+
+/*
+ * access() needs to use the real uid/gid, not the effective uid/gid.
+ * We do this by temporarily clearing all FS-related capabilities and
+ * switching the fsuid/fsgid around to the real ones.
+ */
+asmlinkage long sys_access(const char __user * filename, int mode)
+{
+	struct nameidata nd;
+	int old_fsuid, old_fsgid;
+	kernel_cap_t old_cap;
+	int res;
+
+	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+		return -EINVAL;
+
+	old_fsuid = current->fsuid;
+	old_fsgid = current->fsgid;
+	old_cap = current->cap_effective;
+
+	current->fsuid = current->uid;
+	current->fsgid = current->gid;
+
+	/*
+	 * Clear the capabilities if we switch to a non-root user
+	 *
+	 * FIXME: There is a race here against sys_capset.  The
+	 * capabilities can change yet we will restore the old
+	 * value below.  We should hold task_capabilities_lock,
+	 * but we cannot because user_path_walk can sleep.
+	 */
+	if (current->uid)
+		cap_clear(current->cap_effective);
+	else
+		current->cap_effective = current->cap_permitted;
+
+	res = __user_walk(filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd);
+	if (!res) {
+		res = vfs_permission(&nd, mode);
+		/* SuS v2 requires we report a read only fs too */
+		if(!res && (mode & S_IWOTH) && IS_RDONLY(nd.dentry->d_inode)
+		   && !special_file(nd.dentry->d_inode->i_mode))
+			res = -EROFS;
+		path_release(&nd);
+	}
+
+	current->fsuid = old_fsuid;
+	current->fsgid = old_fsgid;
+	current->cap_effective = old_cap;
+
+	return res;
+}
+
+asmlinkage long sys_chdir(const char __user * filename)
+{
+	struct nameidata nd;
+	int error;
+
+	error = __user_walk(filename, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &nd);
+	if (error)
+		goto out;
+
+	error = vfs_permission(&nd, MAY_EXEC);
+	if (error)
+		goto dput_and_out;
+
+	set_fs_pwd(current->fs, nd.mnt, nd.dentry);
+
+dput_and_out:
+	path_release(&nd);
+out:
+	return error;
+}
+
+asmlinkage long sys_fchdir(unsigned int fd)
+{
+	struct file *file;
+	struct dentry *dentry;
+	struct inode *inode;
+	struct vfsmount *mnt;
+	int error;
+
+	error = -EBADF;
+	file = fget(fd);
+	if (!file)
+		goto out;
+
+	dentry = file->f_dentry;
+	mnt = file->f_vfsmnt;
+	inode = dentry->d_inode;
+
+	error = -ENOTDIR;
+	if (!S_ISDIR(inode->i_mode))
+		goto out_putf;
+
+	error = file_permission(file, MAY_EXEC);
+	if (!error)
+		set_fs_pwd(current->fs, mnt, dentry);
+out_putf:
+	fput(file);
+out:
+	return error;
+}
+
+asmlinkage long sys_chroot(const char __user * filename)
+{
+	struct nameidata nd;
+	int error;
+
+	error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd);
+	if (error)
+		goto out;
+
+	error = vfs_permission(&nd, MAY_EXEC);
+	if (error)
+		goto dput_and_out;
+
+	error = -EPERM;
+	if (!capable(CAP_SYS_CHROOT))
+		goto dput_and_out;
+
+	set_fs_root(current->fs, nd.mnt, nd.dentry);
+	set_fs_altroot();
+	error = 0;
+dput_and_out:
+	path_release(&nd);
+out:
+	return error;
+}
+
+asmlinkage long sys_fchmod(unsigned int fd, mode_t mode)
+{
+	struct inode * inode;
+	struct dentry * dentry;
+	struct file * file;
+	int err = -EBADF;
+	struct iattr newattrs;
+
+	file = fget(fd);
+	if (!file)
+		goto out;
+
+	dentry = file->f_dentry;
+	inode = dentry->d_inode;
+
+	audit_inode(NULL, inode, 0);
+
+	err = -EROFS;
+	if (IS_RDONLY(inode))
+		goto out_putf;
+	err = -EPERM;
+	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+		goto out_putf;
+	down(&inode->i_sem);
+	if (mode == (mode_t) -1)
+		mode = inode->i_mode;
+	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
+	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+	err = notify_change(dentry, &newattrs);
+	up(&inode->i_sem);
+
+out_putf:
+	fput(file);
+out:
+	return err;
+}
+
+asmlinkage long sys_chmod(const char __user * filename, mode_t mode)
+{
+	struct nameidata nd;
+	struct inode * inode;
+	int error;
+	struct iattr newattrs;
+
+	error = user_path_walk(filename, &nd);
+	if (error)
+		goto out;
+	inode = nd.dentry->d_inode;
+
+	error = -EROFS;
+	if (IS_RDONLY(inode))
+		goto dput_and_out;
+
+	error = -EPERM;
+	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+		goto dput_and_out;
+
+	down(&inode->i_sem);
+	if (mode == (mode_t) -1)
+		mode = inode->i_mode;
+	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
+	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+	error = notify_change(nd.dentry, &newattrs);
+	up(&inode->i_sem);
+
+dput_and_out:
+	path_release(&nd);
+out:
+	return error;
+}
+
+static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
+{
+	struct inode * inode;
+	int error;
+	struct iattr newattrs;
+
+	error = -ENOENT;
+	if (!(inode = dentry->d_inode)) {
+		printk(KERN_ERR "chown_common: NULL inode\n");
+		goto out;
+	}
+	error = -EROFS;
+	if (IS_RDONLY(inode))
+		goto out;
+	error = -EPERM;
+	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+		goto out;
+	newattrs.ia_valid =  ATTR_CTIME;
+	if (user != (uid_t) -1) {
+		newattrs.ia_valid |= ATTR_UID;
+		newattrs.ia_uid = user;
+	}
+	if (group != (gid_t) -1) {
+		newattrs.ia_valid |= ATTR_GID;
+		newattrs.ia_gid = group;
+	}
+	if (!S_ISDIR(inode->i_mode))
+		newattrs.ia_valid |= ATTR_KILL_SUID|ATTR_KILL_SGID;
+	down(&inode->i_sem);
+	error = notify_change(dentry, &newattrs);
+	up(&inode->i_sem);
+out:
+	return error;
+}
+
+asmlinkage long sys_chown(const char __user * filename, uid_t user, gid_t group)
+{
+	struct nameidata nd;
+	int error;
+
+	error = user_path_walk(filename, &nd);
+	if (!error) {
+		error = chown_common(nd.dentry, user, group);
+		path_release(&nd);
+	}
+	return error;
+}
+
+asmlinkage long sys_lchown(const char __user * filename, uid_t user, gid_t group)
+{
+	struct nameidata nd;
+	int error;
+
+	error = user_path_walk_link(filename, &nd);
+	if (!error) {
+		error = chown_common(nd.dentry, user, group);
+		path_release(&nd);
+	}
+	return error;
+}
+
+
+asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group)
+{
+	struct file * file;
+	int error = -EBADF;
+
+	file = fget(fd);
+	if (file) {
+		struct dentry * dentry;
+		dentry = file->f_dentry;
+		audit_inode(NULL, dentry->d_inode, 0);
+		error = chown_common(dentry, user, group);
+		fput(file);
+	}
+	return error;
+}
+
+static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
+					int flags, struct file *f,
+					int (*open)(struct inode *, struct file *))
+{
+	struct inode *inode;
+	int error;
+
+	f->f_flags = flags;
+	f->f_mode = ((flags+1) & O_ACCMODE) | FMODE_LSEEK |
+				FMODE_PREAD | FMODE_PWRITE;
+	inode = dentry->d_inode;
+	if (f->f_mode & FMODE_WRITE) {
+		error = get_write_access(inode);
+		if (error)
+			goto cleanup_file;
+	}
+
+	f->f_mapping = inode->i_mapping;
+	f->f_dentry = dentry;
+	f->f_vfsmnt = mnt;
+	f->f_pos = 0;
+	f->f_op = fops_get(inode->i_fop);
+	file_move(f, &inode->i_sb->s_files);
+
+	if (!open && f->f_op)
+		open = f->f_op->open;
+	if (open) {
+		error = open(inode, f);
+		if (error)
+			goto cleanup_all;
+	}
+
+	f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
+
+	file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);
+
+	/* NB: we're sure to have correct a_ops only after f_op->open */
+	if (f->f_flags & O_DIRECT) {
+		if (!f->f_mapping->a_ops ||
+		    ((!f->f_mapping->a_ops->direct_IO) &&
+		    (!f->f_mapping->a_ops->get_xip_page))) {
+			fput(f);
+			f = ERR_PTR(-EINVAL);
+		}
+	}
+
+	return f;
+
+cleanup_all:
+	fops_put(f->f_op);
+	if (f->f_mode & FMODE_WRITE)
+		put_write_access(inode);
+	file_kill(f);
+	f->f_dentry = NULL;
+	f->f_vfsmnt = NULL;
+cleanup_file:
+	put_filp(f);
+	dput(dentry);
+	mntput(mnt);
+	return ERR_PTR(error);
+}
+
+/*
+ * Note that while the flag value (low two bits) for sys_open means:
+ *	00 - read-only
+ *	01 - write-only
+ *	10 - read-write
+ *	11 - special
+ * it is changed into
+ *	00 - no permissions needed
+ *	01 - read-permission
+ *	10 - write-permission
+ *	11 - read-write
+ * for the internal routines (ie open_namei()/follow_link() etc). 00 is
+ * used by symlinks.
+ */
+struct file *filp_open(const char * filename, int flags, int mode)
+{
+	int namei_flags, error;
+	struct nameidata nd;
+
+	namei_flags = flags;
+	if ((namei_flags+1) & O_ACCMODE)
+		namei_flags++;
+
+	error = open_namei(filename, namei_flags, mode, &nd);
+	if (!error)
+		return nameidata_to_filp(&nd, flags);
+
+	return ERR_PTR(error);
+}
+EXPORT_SYMBOL(filp_open);
+
+/**
+ * lookup_instantiate_filp - instantiates the open intent filp
+ * @nd: pointer to nameidata
+ * @dentry: pointer to dentry
+ * @open: open callback
+ *
+ * Helper for filesystems that want to use lookup open intents and pass back
+ * a fully instantiated struct file to the caller.
+ * This function is meant to be called from within a filesystem's
+ * lookup method.
+ * Note that in case of error, nd->intent.open.file is destroyed, but the
+ * path information remains valid.
+ * If the open callback is set to NULL, then the standard f_op->open()
+ * filesystem callback is substituted.
+ */
+struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry,
+		int (*open)(struct inode *, struct file *))
+{
+	if (IS_ERR(nd->intent.open.file))
+		goto out;
+	if (IS_ERR(dentry))
+		goto out_err;
+	nd->intent.open.file = __dentry_open(dget(dentry), mntget(nd->mnt),
+					     nd->intent.open.flags - 1,
+					     nd->intent.open.file,
+					     open);
+out:
+	return nd->intent.open.file;
+out_err:
+	release_open_intent(nd);
+	nd->intent.open.file = (struct file *)dentry;
+	goto out;
+}
+EXPORT_SYMBOL_GPL(lookup_instantiate_filp);
+
+/**
+ * nameidata_to_filp - convert a nameidata to an open filp.
+ * @nd: pointer to nameidata
+ * @flags: open flags
+ *
+ * Note that this function destroys the original nameidata
+ */
+struct file *nameidata_to_filp(struct nameidata *nd, int flags)
+{
+	struct file *filp;
+
+	/* Pick up the filp from the open intent */
+	filp = nd->intent.open.file;
+	/* Has the filesystem initialised the file for us? */
+	if (filp->f_dentry == NULL)
+		filp = __dentry_open(nd->dentry, nd->mnt, flags, filp, NULL);
+	else
+		path_release(nd);
+	return filp;
+}
+
+/*
+ * dentry_open() will have done dput(dentry) and mntput(mnt) if it returns an
+ * error.
+ */
+struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
+{
+	int error;
+	struct file *f;
+
+	error = -ENFILE;
+	f = get_empty_filp();
+	if (f == NULL) {
+		dput(dentry);
+		mntput(mnt);
+		return ERR_PTR(error);
+	}
+
+	return __dentry_open(dentry, mnt, flags, f, NULL);
+}
+EXPORT_SYMBOL(dentry_open);
+
+/*
+ * Find an empty file descriptor entry, and mark it busy.
+ */
+int get_unused_fd(void)
+{
+	struct files_struct * files = current->files;
+	int fd, error;
+	struct fdtable *fdt;
+
+  	error = -EMFILE;
+	spin_lock(&files->file_lock);
+
+repeat:
+	fdt = files_fdtable(files);
+ 	fd = find_next_zero_bit(fdt->open_fds->fds_bits,
+				fdt->max_fdset,
+				fdt->next_fd);
+
+	/*
+	 * N.B. For clone tasks sharing a files structure, this test
+	 * will limit the total number of files that can be opened.
+	 */
+	if (fd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
+		goto out;
+
+	/* Do we need to expand the fd array or fd set?  */
+	error = expand_files(files, fd);
+	if (error < 0)
+		goto out;
+
+	if (error) {
+		/*
+	 	 * If we needed to expand the fs array we
+		 * might have blocked - try again.
+		 */
+		error = -EMFILE;
+		goto repeat;
+	}
+
+	FD_SET(fd, fdt->open_fds);
+	FD_CLR(fd, fdt->close_on_exec);
+	fdt->next_fd = fd + 1;
+#if 1
+	/* Sanity check */
+	if (fdt->fd[fd] != NULL) {
+		printk(KERN_WARNING "get_unused_fd: slot %d not NULL!\n", fd);
+		fdt->fd[fd] = NULL;
+	}
+#endif
+	error = fd;
+
+out:
+	spin_unlock(&files->file_lock);
+	return error;
+}
+
+EXPORT_SYMBOL(get_unused_fd);
+
+static inline void __put_unused_fd(struct files_struct *files, unsigned int fd)
+{
+	struct fdtable *fdt = files_fdtable(files);
+	__FD_CLR(fd, fdt->open_fds);
+	if (fd < fdt->next_fd)
+		fdt->next_fd = fd;
+}
+
+void fastcall put_unused_fd(unsigned int fd)
+{
+	struct files_struct *files = current->files;
+	spin_lock(&files->file_lock);
+	__put_unused_fd(files, fd);
+	spin_unlock(&files->file_lock);
+}
+
+EXPORT_SYMBOL(put_unused_fd);
+
+/*
+ * Install a file pointer in the fd array.  
+ *
+ * The VFS is full of places where we drop the files lock between
+ * setting the open_fds bitmap and installing the file in the file
+ * array.  At any such point, we are vulnerable to a dup2() race
+ * installing a file in the array before us.  We need to detect this and
+ * fput() the struct file we are about to overwrite in this case.
+ *
+ * It should never happen - if we allow dup2() do it, _really_ bad things
+ * will follow.
+ */
+
+void fastcall fd_install(unsigned int fd, struct file * file)
+{
+	struct files_struct *files = current->files;
+	struct fdtable *fdt;
+	spin_lock(&files->file_lock);
+	fdt = files_fdtable(files);
+	BUG_ON(fdt->fd[fd] != NULL);
+	rcu_assign_pointer(fdt->fd[fd], file);
+	spin_unlock(&files->file_lock);
+}
+
+EXPORT_SYMBOL(fd_install);
+
+long do_sys_open(const char __user *filename, int flags, int mode)
+{
+	char *tmp = getname(filename);
+	int fd = PTR_ERR(tmp);
+
+	if (!IS_ERR(tmp)) {
+		fd = get_unused_fd();
+		if (fd >= 0) {
+			struct file *f = filp_open(tmp, flags, mode);
+			if (IS_ERR(f)) {
+				put_unused_fd(fd);
+				fd = PTR_ERR(f);
+			} else {
+				fsnotify_open(f->f_dentry);
+				fd_install(fd, f);
+			}
+		}
+		putname(tmp);
+	}
+	return fd;
+}
+
+asmlinkage long sys_open(const char __user *filename, int flags, int mode)
+{
+	if (force_o_largefile())
+		flags |= O_LARGEFILE;
+
+	return do_sys_open(filename, flags, mode);
+}
+EXPORT_SYMBOL_GPL(sys_open);
+
+#ifndef __alpha__
+
+/*
+ * For backward compatibility?  Maybe this should be moved
+ * into arch/i386 instead?
+ */
+asmlinkage long sys_creat(const char __user * pathname, int mode)
+{
+	return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
+}
+
+#endif
+
+/*
+ * "id" is the POSIX thread ID. We use the
+ * files pointer for this..
+ */
+int filp_close(struct file *filp, fl_owner_t id)
+{
+	int retval = 0;
+
+	if (!file_count(filp)) {
+		printk(KERN_ERR "VFS: Close: file count is 0\n");
+		return 0;
+	}
+
+	if (filp->f_op && filp->f_op->flush)
+		retval = filp->f_op->flush(filp);
+
+	dnotify_flush(filp, id);
+	locks_remove_posix(filp, id);
+	fput(filp);
+	return retval;
+}
+
+EXPORT_SYMBOL(filp_close);
+
+/*
+ * Careful here! We test whether the file pointer is NULL before
+ * releasing the fd. This ensures that one clone task can't release
+ * an fd while another clone is opening it.
+ */
+asmlinkage long sys_close(unsigned int fd)
+{
+	struct file * filp;
+	struct files_struct *files = current->files;
+	struct fdtable *fdt;
+
+	spin_lock(&files->file_lock);
+	fdt = files_fdtable(files);
+	if (fd >= fdt->max_fds)
+		goto out_unlock;
+	filp = fdt->fd[fd];
+	if (!filp)
+		goto out_unlock;
+	rcu_assign_pointer(fdt->fd[fd], NULL);
+	FD_CLR(fd, fdt->close_on_exec);
+	__put_unused_fd(files, fd);
+	spin_unlock(&files->file_lock);
+	return filp_close(filp, files);
+
+out_unlock:
+	spin_unlock(&files->file_lock);
+	return -EBADF;
+}
+
+EXPORT_SYMBOL(sys_close);
+
+/*
+ * This routine simulates a hangup on the tty, to arrange that users
+ * are given clean terminals at login time.
+ */
+asmlinkage long sys_vhangup(void)
+{
+	if (capable(CAP_SYS_TTY_CONFIG)) {
+		tty_vhangup(current->signal->tty);
+		return 0;
+	}
+	return -EPERM;
+}
+
+/*
+ * Called when an inode is about to be open.
+ * We use this to disallow opening large files on 32bit systems if
+ * the caller didn't specify O_LARGEFILE.  On 64bit systems we force
+ * on this flag in sys_open.
+ */
+int generic_file_open(struct inode * inode, struct file * filp)
+{
+	if (!(filp->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS)
+		return -EFBIG;
+	return 0;
+}
+
+EXPORT_SYMBOL(generic_file_open);
+
+/*
+ * This is used by subsystems that don't want seekable
+ * file descriptors
+ */
+int nonseekable_open(struct inode *inode, struct file *filp)
+{
+	filp->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+	return 0;
+}
+
+EXPORT_SYMBOL(nonseekable_open);
diff -urN oldtree/fs/partitions/Kconfig newtree/fs/partitions/Kconfig
--- oldtree/fs/partitions/Kconfig	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/partitions/Kconfig	2006-02-13 19:20:00.718403088 -0500
@@ -216,6 +216,13 @@
 	  given by the tar program ("man tar" or preferably "info tar"). If
 	  you don't know what all this is about, say N.
 
+config KARMA_PARTITION
+	bool "Karma Partition support"
+	depends on PARTITION_ADVANCED
+	help
+	  Say Y here if you would like to mount the Rio Karma MP3 player, as it
+	  uses a proprietary partition table.
+
 config EFI_PARTITION
 	bool "EFI GUID Partition support"
 	depends on PARTITION_ADVANCED
diff -urN oldtree/fs/partitions/Kconfig.orig newtree/fs/partitions/Kconfig.orig
--- oldtree/fs/partitions/Kconfig.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/partitions/Kconfig.orig	2006-02-13 19:20:00.718403088 -0500
@@ -0,0 +1,228 @@
+#
+# Partition configuration
+#
+config PARTITION_ADVANCED
+	bool "Advanced partition selection"
+	help
+	  Say Y here if you would like to use hard disks under Linux which
+	  were partitioned under an operating system running on a different
+	  architecture than your Linux system.
+
+	  Note that the answer to this question won't directly affect the
+	  kernel: saying N will just cause the configurator to skip all
+	  the questions about foreign partitioning schemes.
+
+	  If unsure, say N.
+
+config ACORN_PARTITION
+	bool "Acorn partition support" if PARTITION_ADVANCED
+	default y if ARCH_ACORN
+	help
+	  Support hard disks partitioned under Acorn operating systems.
+
+config ACORN_PARTITION_CUMANA
+	bool "Cumana partition support" if PARTITION_ADVANCED && ACORN_PARTITION
+	default y if ARCH_ACORN
+	help
+	  Say Y here if you would like to use hard disks under Linux which
+	  were partitioned using the Cumana interface on Acorn machines.
+
+config ACORN_PARTITION_EESOX
+	bool "EESOX partition support" if PARTITION_ADVANCED && ACORN_PARTITION
+	default y if ARCH_ACORN
+
+config ACORN_PARTITION_ICS
+	bool "ICS partition support" if PARTITION_ADVANCED && ACORN_PARTITION
+	default y if ARCH_ACORN
+	help
+	  Say Y here if you would like to use hard disks under Linux which
+	  were partitioned using the ICS interface on Acorn machines.
+
+config ACORN_PARTITION_ADFS
+	bool "Native filecore partition support" if PARTITION_ADVANCED && ACORN_PARTITION
+	default y if ARCH_ACORN
+	help
+	  The Acorn Disc Filing System is the standard file system of the
+	  RiscOS operating system which runs on Acorn's ARM-based Risc PC
+	  systems and the Acorn Archimedes range of machines.  If you say
+	  `Y' here, Linux will support disk partitions created under ADFS.
+
+config ACORN_PARTITION_POWERTEC
+	bool "PowerTec partition support" if PARTITION_ADVANCED && ACORN_PARTITION
+	default y if ARCH_ACORN
+	help
+	  Support reading partition tables created on Acorn machines using
+	  the PowerTec SCSI drive.
+
+config ACORN_PARTITION_RISCIX
+	bool "RISCiX partition support" if PARTITION_ADVANCED && ACORN_PARTITION
+	default y if ARCH_ACORN
+	help
+	  Once upon a time, there was a native Unix port for the Acorn series
+	  of machines called RISCiX.  If you say 'Y' here, Linux will be able
+	  to read disks partitioned under RISCiX.
+
+config OSF_PARTITION
+	bool "Alpha OSF partition support" if PARTITION_ADVANCED
+	default y if ALPHA
+	help
+	  Say Y here if you would like to use hard disks under Linux which
+	  were partitioned on an Alpha machine.
+
+config AMIGA_PARTITION
+	bool "Amiga partition table support" if PARTITION_ADVANCED
+	default y if (AMIGA || AFFS_FS=y)
+	help
+	  Say Y here if you would like to use hard disks under Linux which
+	  were partitioned under AmigaOS.
+
+config ATARI_PARTITION
+	bool "Atari partition table support" if PARTITION_ADVANCED
+	default y if ATARI
+	help
+	  Say Y here if you would like to use hard disks under Linux which
+	  were partitioned under the Atari OS.
+
+config IBM_PARTITION
+	bool "IBM disk label and partition support"
+	depends on PARTITION_ADVANCED && ARCH_S390
+	help
+	  Say Y here if you would like to be able to read the hard disk
+	  partition table format used by IBM DASD disks operating under CMS.
+	  Otherwise, say N.
+
+config MAC_PARTITION
+	bool "Macintosh partition map support" if PARTITION_ADVANCED
+	default y if MAC
+	help
+	  Say Y here if you would like to use hard disks under Linux which
+	  were partitioned on a Macintosh.
+
+config MSDOS_PARTITION
+	bool "PC BIOS (MSDOS partition tables) support" if PARTITION_ADVANCED
+	default y
+	help
+	  Say Y here.
+
+config BSD_DISKLABEL
+	bool "BSD disklabel (FreeBSD partition tables) support"
+	depends on PARTITION_ADVANCED && MSDOS_PARTITION
+	help
+	  FreeBSD uses its own hard disk partition scheme on your PC. It
+	  requires only one entry in the primary partition table of your disk
+	  and manages it similarly to DOS extended partitions, putting in its
+	  first sector a new partition table in BSD disklabel format. Saying Y
+	  here allows you to read these disklabels and further mount FreeBSD
+	  partitions from within Linux if you have also said Y to "UFS
+	  file system support", above. If you don't know what all this is
+	  about, say N.
+
+config MINIX_SUBPARTITION
+	bool "Minix subpartition support"
+	depends on PARTITION_ADVANCED && MSDOS_PARTITION
+	help
+	  Minix 2.0.0/2.0.2 subpartition table support for Linux.
+	  Say Y here if you want to mount and use Minix 2.0.0/2.0.2
+	  subpartitions.
+
+config SOLARIS_X86_PARTITION
+	bool "Solaris (x86) partition table support"
+	depends on PARTITION_ADVANCED && MSDOS_PARTITION
+	help
+	  Like most systems, Solaris x86 uses its own hard disk partition
+	  table format, incompatible with all others. Saying Y here allows you
+	  to read these partition tables and further mount Solaris x86
+	  partitions from within Linux if you have also said Y to "UFS
+	  file system support", above.
+
+config UNIXWARE_DISKLABEL
+	bool "Unixware slices support"
+	depends on PARTITION_ADVANCED && MSDOS_PARTITION
+	---help---
+	  Like some systems, UnixWare uses its own slice table inside a
+	  partition (VTOC - Virtual Table of Contents). Its format is
+	  incompatible with all other OSes. Saying Y here allows you to read
+	  VTOC and further mount UnixWare partitions read-only from within
+	  Linux if you have also said Y to "UFS file system support" or
+	  "System V and Coherent file system support", above.
+
+	  This is mainly used to carry data from a UnixWare box to your
+	  Linux box via a removable medium like magneto-optical, ZIP or
+	  removable IDE drives. Note, however, that a good portable way to
+	  transport files and directories between unixes (and even other
+	  operating systems) is given by the tar program ("man tar" or
+	  preferably "info tar").
+
+	  If you don't know what all this is about, say N.
+
+config LDM_PARTITION
+	bool "Windows Logical Disk Manager (Dynamic Disk) support"
+	depends on PARTITION_ADVANCED
+	---help---
+	  Say Y here if you would like to use hard disks under Linux which
+	  were partitioned using Windows 2000's or XP's Logical Disk Manager.
+	  They are also known as "Dynamic Disks".
+
+	  Windows 2000 introduced the concept of Dynamic Disks to get around
+	  the limitations of the PC's partitioning scheme.  The Logical Disk
+	  Manager allows the user to repartition a disk and create spanned,
+	  mirrored, striped or RAID volumes, all without the need for
+	  rebooting.
+
+	  Normal partitions are now called Basic Disks under Windows 2000 and
+	  XP.
+
+	  For a fuller description read <file:Documentation/ldm.txt>.
+
+	  If unsure, say N.
+
+config LDM_DEBUG
+	bool "Windows LDM extra logging"
+	depends on LDM_PARTITION
+	help
+	  Say Y here if you would like LDM to log verbosely.  This could be
+	  helpful if the driver doesn't work as expected and you'd like to
+	  report a bug.
+
+	  If unsure, say N.
+
+config SGI_PARTITION
+	bool "SGI partition support" if PARTITION_ADVANCED
+	default y if (SGI_IP22 || SGI_IP27 || ((MACH_JAZZ || SNI_RM200_PCI) && !CPU_LITTLE_ENDIAN))
+	help
+	  Say Y here if you would like to be able to read the hard disk
+	  partition table format used by SGI machines.
+
+config ULTRIX_PARTITION
+	bool "Ultrix partition table support" if PARTITION_ADVANCED
+	default y if MACH_DECSTATION
+	help
+	  Say Y here if you would like to be able to read the hard disk
+	  partition table format used by DEC (now Compaq) Ultrix machines.
+	  Otherwise, say N.
+
+config SUN_PARTITION
+	bool "Sun partition tables support" if PARTITION_ADVANCED
+	default y if (SPARC || SUN3 || SUN3X)
+	---help---
+	  Like most systems, SunOS uses its own hard disk partition table
+	  format, incompatible with all others. Saying Y here allows you to
+	  read these partition tables and further mount SunOS partitions from
+	  within Linux if you have also said Y to "UFS file system support",
+	  above. This is mainly used to carry data from a SPARC under SunOS to
+	  your Linux box via a removable medium like magneto-optical or ZIP
+	  drives; note however that a good portable way to transport files and
+	  directories between unixes (and even other operating systems) is
+	  given by the tar program ("man tar" or preferably "info tar"). If
+	  you don't know what all this is about, say N.
+
+config EFI_PARTITION
+	bool "EFI GUID Partition support"
+	depends on PARTITION_ADVANCED
+	select CRC32
+	help
+	  Say Y here if you would like to use hard disks under Linux which
+	  were partitioned using EFI GPT.  Presently only useful on the
+	  IA-64 platform.
+
+#      define_bool CONFIG_ACORN_PARTITION_CUMANA y
diff -urN oldtree/fs/partitions/Makefile newtree/fs/partitions/Makefile
--- oldtree/fs/partitions/Makefile	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/partitions/Makefile	2006-02-13 19:20:00.718403088 -0500
@@ -17,3 +17,4 @@
 obj-$(CONFIG_ULTRIX_PARTITION) += ultrix.o
 obj-$(CONFIG_IBM_PARTITION) += ibm.o
 obj-$(CONFIG_EFI_PARTITION) += efi.o
+obj-$(CONFIG_KARMA_PARTITION) += karma.o
diff -urN oldtree/fs/partitions/check.c newtree/fs/partitions/check.c
--- oldtree/fs/partitions/check.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/partitions/check.c	2006-02-13 19:20:00.719402936 -0500
@@ -35,6 +35,7 @@
 #include "ibm.h"
 #include "ultrix.h"
 #include "efi.h"
+#include "karma.h"
 
 #ifdef CONFIG_BLK_DEV_MD
 extern void md_autodetect_dev(dev_t dev);
@@ -103,6 +104,9 @@
 #ifdef CONFIG_IBM_PARTITION
 	ibm_partition,
 #endif
+#ifdef CONFIG_KARMA_PARTITION
+	karma_partition,
+#endif
 	NULL
 };
  
diff -urN oldtree/fs/partitions/karma.c newtree/fs/partitions/karma.c
--- oldtree/fs/partitions/karma.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/partitions/karma.c	2006-02-13 19:20:00.719402936 -0500
@@ -0,0 +1,57 @@
+/*
+ *  fs/partitions/karma.c
+ *  Rio Karma partition info.
+ *
+ *  Copyright (C) 2006 Bob Copeland (me@bobcopeland.com)
+ *  based on osf.c
+ */
+
+#include "check.h"
+#include "karma.h"
+
+int karma_partition(struct parsed_partitions *state, struct block_device *bdev)
+{
+	int i;
+	int slot = 1;
+	Sector sect;
+	unsigned char *data;
+	struct disklabel {
+		u8 d_reserved[270];
+		struct d_partition {
+			__le32 p_res;
+			u8 p_fstype;
+			u8 p_res2[3];
+			__le32 p_offset;
+			__le32 p_size;
+		} d_partitions[2];
+		u8 d_blank[208];
+		__le16 d_magic;
+	} __attribute__((packed)) *label;
+	struct d_partition *p;
+
+	data = read_dev_sector(bdev, 0, &sect);
+	if (!data)
+		return -1;
+
+	label = (struct disklabel *)data;
+	if (le16_to_cpu(label->d_magic) != KARMA_LABEL_MAGIC) {
+		put_dev_sector(sect);
+		return 0;
+	}
+
+	p = label->d_partitions;
+	for (i = 0 ; i < 2; i++, p++) {
+		if (slot == state->limit)
+			break;
+
+		if (p->p_fstype == 0x4d && le32_to_cpu(p->p_size)) {
+			put_partition(state, slot, le32_to_cpu(p->p_offset),
+				le32_to_cpu(p->p_size));
+		}
+		slot++;
+	}
+	printk("\n");
+	put_dev_sector(sect);
+	return 1;
+}
+
diff -urN oldtree/fs/partitions/karma.h newtree/fs/partitions/karma.h
--- oldtree/fs/partitions/karma.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/partitions/karma.h	2006-02-13 19:20:00.719402936 -0500
@@ -0,0 +1,8 @@
+/*
+ *  fs/partitions/karma.h
+ */
+
+#define KARMA_LABEL_MAGIC		0xAB56
+
+int karma_partition(struct parsed_partitions *state, struct block_device *bdev);
+
diff -urN oldtree/fs/proc/generic.c newtree/fs/proc/generic.c
--- oldtree/fs/proc/generic.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/proc/generic.c	2006-02-13 19:20:00.720402784 -0500
@@ -19,6 +19,7 @@
 #include <linux/idr.h>
 #include <linux/namei.h>
 #include <linux/bitops.h>
+#include <linux/spinlock.h>
 #include <asm/uaccess.h>
 
 static ssize_t proc_file_read(struct file *file, char __user *buf,
@@ -27,6 +28,8 @@
 			       size_t count, loff_t *ppos);
 static loff_t proc_file_lseek(struct file *, loff_t, int);
 
+DEFINE_SPINLOCK(proc_subdir_lock);
+
 int proc_match(int len, const char *name, struct proc_dir_entry *de)
 {
 	if (de->namelen != len)
@@ -275,7 +278,9 @@
 	const char     		*cp = name, *next;
 	struct proc_dir_entry	*de;
 	int			len;
+	int 			rtn = 0;
 
+	spin_lock(&proc_subdir_lock);
 	de = &proc_root;
 	while (1) {
 		next = strchr(cp, '/');
@@ -287,13 +292,17 @@
 			if (proc_match(len, cp, de))
 				break;
 		}
-		if (!de)
-			return -ENOENT;
+		if (!de) {
+			rtn = -ENOENT;
+			goto out;
+		}
 		cp += len + 1;
 	}
 	*residual = cp;
 	*ret = de;
-	return 0;
+out:
+	spin_unlock(&proc_subdir_lock);
+	return rtn;
 }
 
 static DEFINE_IDR(proc_inum_idr);
@@ -378,6 +387,7 @@
 	int error = -ENOENT;
 
 	lock_kernel();
+	spin_lock(&proc_subdir_lock);
 	de = PDE(dir);
 	if (de) {
 		for (de = de->subdir; de ; de = de->next) {
@@ -386,12 +396,15 @@
 			if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
 				unsigned int ino = de->low_ino;
 
+				spin_unlock(&proc_subdir_lock);
 				error = -EINVAL;
 				inode = proc_get_inode(dir->i_sb, ino, de);
+				spin_lock(&proc_subdir_lock);
 				break;
 			}
 		}
 	}
+	spin_unlock(&proc_subdir_lock);
 	unlock_kernel();
 
 	if (inode) {
@@ -445,11 +458,13 @@
 			filp->f_pos++;
 			/* fall through */
 		default:
+			spin_lock(&proc_subdir_lock);
 			de = de->subdir;
 			i -= 2;
 			for (;;) {
 				if (!de) {
 					ret = 1;
+					spin_unlock(&proc_subdir_lock);
 					goto out;
 				}
 				if (!i)
@@ -459,12 +474,16 @@
 			}
 
 			do {
+				/* filldir passes info to user space */
+				spin_unlock(&proc_subdir_lock);
 				if (filldir(dirent, de->name, de->namelen, filp->f_pos,
 					    de->low_ino, de->mode >> 12) < 0)
 					goto out;
+				spin_lock(&proc_subdir_lock);
 				filp->f_pos++;
 				de = de->next;
 			} while (de);
+			spin_unlock(&proc_subdir_lock);
 	}
 	ret = 1;
 out:	unlock_kernel();
@@ -498,9 +517,13 @@
 	if (i == 0)
 		return -EAGAIN;
 	dp->low_ino = i;
+
+	spin_lock(&proc_subdir_lock);
 	dp->next = dir->subdir;
 	dp->parent = dir;
 	dir->subdir = dp;
+	spin_unlock(&proc_subdir_lock);
+
 	if (S_ISDIR(dp->mode)) {
 		if (dp->proc_iops == NULL) {
 			dp->proc_fops = &proc_dir_operations;
@@ -692,6 +715,8 @@
 	if (!parent && xlate_proc_name(name, &parent, &fn) != 0)
 		goto out;
 	len = strlen(fn);
+
+	spin_lock(&proc_subdir_lock);
 	for (p = &parent->subdir; *p; p=&(*p)->next ) {
 		if (!proc_match(len, fn, *p))
 			continue;
@@ -712,6 +737,7 @@
 		}
 		break;
 	}
+	spin_unlock(&proc_subdir_lock);
 out:
 	return;
 }
diff -urN oldtree/fs/proc/generic.c.orig newtree/fs/proc/generic.c.orig
--- oldtree/fs/proc/generic.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/proc/generic.c.orig	2006-02-13 19:20:00.721402632 -0500
@@ -0,0 +1,717 @@
+/*
+ * proc/fs/generic.c --- generic routines for the proc-fs
+ *
+ * This file contains generic proc-fs routines for handling
+ * directories and files.
+ * 
+ * Copyright (C) 1991, 1992 Linus Torvalds.
+ * Copyright (C) 1997 Theodore Ts'o
+ */
+
+#include <linux/errno.h>
+#include <linux/time.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/idr.h>
+#include <linux/namei.h>
+#include <linux/bitops.h>
+#include <asm/uaccess.h>
+
+static ssize_t proc_file_read(struct file *file, char __user *buf,
+			      size_t nbytes, loff_t *ppos);
+static ssize_t proc_file_write(struct file *file, const char __user *buffer,
+			       size_t count, loff_t *ppos);
+static loff_t proc_file_lseek(struct file *, loff_t, int);
+
+int proc_match(int len, const char *name, struct proc_dir_entry *de)
+{
+	if (de->namelen != len)
+		return 0;
+	return !memcmp(name, de->name, len);
+}
+
+static struct file_operations proc_file_operations = {
+	.llseek		= proc_file_lseek,
+	.read		= proc_file_read,
+	.write		= proc_file_write,
+};
+
+/* buffer size is one page but our output routines use some slack for overruns */
+#define PROC_BLOCK_SIZE	(PAGE_SIZE - 1024)
+
+static ssize_t
+proc_file_read(struct file *file, char __user *buf, size_t nbytes,
+	       loff_t *ppos)
+{
+	struct inode * inode = file->f_dentry->d_inode;
+	char 	*page;
+	ssize_t	retval=0;
+	int	eof=0;
+	ssize_t	n, count;
+	char	*start;
+	struct proc_dir_entry * dp;
+	unsigned long long pos;
+
+	/*
+	 * Gaah, please just use "seq_file" instead. The legacy /proc
+	 * interfaces cut loff_t down to off_t for reads, and ignore
+	 * the offset entirely for writes..
+	 */
+	pos = *ppos;
+	if (pos > MAX_NON_LFS)
+		return 0;
+	if (nbytes > MAX_NON_LFS - pos)
+		nbytes = MAX_NON_LFS - pos;
+
+	dp = PDE(inode);
+	if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+		return -ENOMEM;
+
+	while ((nbytes > 0) && !eof) {
+		count = min_t(size_t, PROC_BLOCK_SIZE, nbytes);
+
+		start = NULL;
+		if (dp->get_info) {
+			/* Handle old net routines */
+			n = dp->get_info(page, &start, *ppos, count);
+			if (n < count)
+				eof = 1;
+		} else if (dp->read_proc) {
+			/*
+			 * How to be a proc read function
+			 * ------------------------------
+			 * Prototype:
+			 *    int f(char *buffer, char **start, off_t offset,
+			 *          int count, int *peof, void *dat)
+			 *
+			 * Assume that the buffer is "count" bytes in size.
+			 *
+			 * If you know you have supplied all the data you
+			 * have, set *peof.
+			 *
+			 * You have three ways to return data:
+			 * 0) Leave *start = NULL.  (This is the default.)
+			 *    Put the data of the requested offset at that
+			 *    offset within the buffer.  Return the number (n)
+			 *    of bytes there are from the beginning of the
+			 *    buffer up to the last byte of data.  If the
+			 *    number of supplied bytes (= n - offset) is 
+			 *    greater than zero and you didn't signal eof
+			 *    and the reader is prepared to take more data
+			 *    you will be called again with the requested
+			 *    offset advanced by the number of bytes 
+			 *    absorbed.  This interface is useful for files
+			 *    no larger than the buffer.
+			 * 1) Set *start = an unsigned long value less than
+			 *    the buffer address but greater than zero.
+			 *    Put the data of the requested offset at the
+			 *    beginning of the buffer.  Return the number of
+			 *    bytes of data placed there.  If this number is
+			 *    greater than zero and you didn't signal eof
+			 *    and the reader is prepared to take more data
+			 *    you will be called again with the requested
+			 *    offset advanced by *start.  This interface is
+			 *    useful when you have a large file consisting
+			 *    of a series of blocks which you want to count
+			 *    and return as wholes.
+			 *    (Hack by Paul.Russell@rustcorp.com.au)
+			 * 2) Set *start = an address within the buffer.
+			 *    Put the data of the requested offset at *start.
+			 *    Return the number of bytes of data placed there.
+			 *    If this number is greater than zero and you
+			 *    didn't signal eof and the reader is prepared to
+			 *    take more data you will be called again with the
+			 *    requested offset advanced by the number of bytes
+			 *    absorbed.
+			 */
+			n = dp->read_proc(page, &start, *ppos,
+					  count, &eof, dp->data);
+		} else
+			break;
+
+		if (n == 0)   /* end of file */
+			break;
+		if (n < 0) {  /* error */
+			if (retval == 0)
+				retval = n;
+			break;
+		}
+
+		if (start == NULL) {
+			if (n > PAGE_SIZE) {
+				printk(KERN_ERR
+				       "proc_file_read: Apparent buffer overflow!\n");
+				n = PAGE_SIZE;
+			}
+			n -= *ppos;
+			if (n <= 0)
+				break;
+			if (n > count)
+				n = count;
+			start = page + *ppos;
+		} else if (start < page) {
+			if (n > PAGE_SIZE) {
+				printk(KERN_ERR
+				       "proc_file_read: Apparent buffer overflow!\n");
+				n = PAGE_SIZE;
+			}
+			if (n > count) {
+				/*
+				 * Don't reduce n because doing so might
+				 * cut off part of a data block.
+				 */
+				printk(KERN_WARNING
+				       "proc_file_read: Read count exceeded\n");
+			}
+		} else /* start >= page */ {
+			unsigned long startoff = (unsigned long)(start - page);
+			if (n > (PAGE_SIZE - startoff)) {
+				printk(KERN_ERR
+				       "proc_file_read: Apparent buffer overflow!\n");
+				n = PAGE_SIZE - startoff;
+			}
+			if (n > count)
+				n = count;
+		}
+		
+ 		n -= copy_to_user(buf, start < page ? page : start, n);
+		if (n == 0) {
+			if (retval == 0)
+				retval = -EFAULT;
+			break;
+		}
+
+		*ppos += start < page ? (unsigned long)start : n;
+		nbytes -= n;
+		buf += n;
+		retval += n;
+	}
+	free_page((unsigned long) page);
+	return retval;
+}
+
+static ssize_t
+proc_file_write(struct file *file, const char __user *buffer,
+		size_t count, loff_t *ppos)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct proc_dir_entry * dp;
+	
+	dp = PDE(inode);
+
+	if (!dp->write_proc)
+		return -EIO;
+
+	/* FIXME: does this routine need ppos?  probably... */
+	return dp->write_proc(file, buffer, count, dp->data);
+}
+
+
+static loff_t
+proc_file_lseek(struct file *file, loff_t offset, int orig)
+{
+	loff_t retval = -EINVAL;
+	switch (orig) {
+	case 1:
+		offset += file->f_pos;
+	/* fallthrough */
+	case 0:
+		if (offset < 0 || offset > MAX_NON_LFS)
+			break;
+		file->f_pos = retval = offset;
+	}
+	return retval;
+}
+
+static int proc_notify_change(struct dentry *dentry, struct iattr *iattr)
+{
+	struct inode *inode = dentry->d_inode;
+	struct proc_dir_entry *de = PDE(inode);
+	int error;
+
+	error = inode_change_ok(inode, iattr);
+	if (error)
+		goto out;
+
+	error = inode_setattr(inode, iattr);
+	if (error)
+		goto out;
+	
+	de->uid = inode->i_uid;
+	de->gid = inode->i_gid;
+	de->mode = inode->i_mode;
+out:
+	return error;
+}
+
+static int proc_getattr(struct vfsmount *mnt, struct dentry *dentry,
+			struct kstat *stat)
+{
+	struct inode *inode = dentry->d_inode;
+	struct proc_dir_entry *de = PROC_I(inode)->pde;
+	if (de && de->nlink)
+		inode->i_nlink = de->nlink;
+
+	generic_fillattr(inode, stat);
+	return 0;
+}
+
+static struct inode_operations proc_file_inode_operations = {
+	.setattr	= proc_notify_change,
+};
+
+/*
+ * This function parses a name such as "tty/driver/serial", and
+ * returns the struct proc_dir_entry for "/proc/tty/driver", and
+ * returns "serial" in residual.
+ */
+static int xlate_proc_name(const char *name,
+			   struct proc_dir_entry **ret, const char **residual)
+{
+	const char     		*cp = name, *next;
+	struct proc_dir_entry	*de;
+	int			len;
+
+	de = &proc_root;
+	while (1) {
+		next = strchr(cp, '/');
+		if (!next)
+			break;
+
+		len = next - cp;
+		for (de = de->subdir; de ; de = de->next) {
+			if (proc_match(len, cp, de))
+				break;
+		}
+		if (!de)
+			return -ENOENT;
+		cp += len + 1;
+	}
+	*residual = cp;
+	*ret = de;
+	return 0;
+}
+
+static DEFINE_IDR(proc_inum_idr);
+static DEFINE_SPINLOCK(proc_inum_lock); /* protects the above */
+
+#define PROC_DYNAMIC_FIRST 0xF0000000UL
+
+/*
+ * Return an inode number between PROC_DYNAMIC_FIRST and
+ * 0xffffffff, or zero on failure.
+ */
+static unsigned int get_inode_number(void)
+{
+	int i, inum = 0;
+	int error;
+
+retry:
+	if (idr_pre_get(&proc_inum_idr, GFP_KERNEL) == 0)
+		return 0;
+
+	spin_lock(&proc_inum_lock);
+	error = idr_get_new(&proc_inum_idr, NULL, &i);
+	spin_unlock(&proc_inum_lock);
+	if (error == -EAGAIN)
+		goto retry;
+	else if (error)
+		return 0;
+
+	inum = (i & MAX_ID_MASK) + PROC_DYNAMIC_FIRST;
+
+	/* inum will never be more than 0xf0ffffff, so no check
+	 * for overflow.
+	 */
+
+	return inum;
+}
+
+static void release_inode_number(unsigned int inum)
+{
+	int id = (inum - PROC_DYNAMIC_FIRST) | ~MAX_ID_MASK;
+
+	spin_lock(&proc_inum_lock);
+	idr_remove(&proc_inum_idr, id);
+	spin_unlock(&proc_inum_lock);
+}
+
+static void *proc_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	nd_set_link(nd, PDE(dentry->d_inode)->data);
+	return NULL;
+}
+
+static struct inode_operations proc_link_inode_operations = {
+	.readlink	= generic_readlink,
+	.follow_link	= proc_follow_link,
+};
+
+/*
+ * As some entries in /proc are volatile, we want to 
+ * get rid of unused dentries.  This could be made 
+ * smarter: we could keep a "volatile" flag in the 
+ * inode to indicate which ones to keep.
+ */
+static int proc_delete_dentry(struct dentry * dentry)
+{
+	return 1;
+}
+
+static struct dentry_operations proc_dentry_operations =
+{
+	.d_delete	= proc_delete_dentry,
+};
+
+/*
+ * Don't create negative dentries here, return -ENOENT by hand
+ * instead.
+ */
+struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd)
+{
+	struct inode *inode = NULL;
+	struct proc_dir_entry * de;
+	int error = -ENOENT;
+
+	lock_kernel();
+	de = PDE(dir);
+	if (de) {
+		for (de = de->subdir; de ; de = de->next) {
+			if (de->namelen != dentry->d_name.len)
+				continue;
+			if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
+				unsigned int ino = de->low_ino;
+
+				error = -EINVAL;
+				inode = proc_get_inode(dir->i_sb, ino, de);
+				break;
+			}
+		}
+	}
+	unlock_kernel();
+
+	if (inode) {
+		dentry->d_op = &proc_dentry_operations;
+		d_add(dentry, inode);
+		return NULL;
+	}
+	return ERR_PTR(error);
+}
+
+/*
+ * This returns non-zero if at EOF, so that the /proc
+ * root directory can use this and check if it should
+ * continue with the <pid> entries..
+ *
+ * Note that the VFS-layer doesn't care about the return
+ * value of the readdir() call, as long as it's non-negative
+ * for success..
+ */
+int proc_readdir(struct file * filp,
+	void * dirent, filldir_t filldir)
+{
+	struct proc_dir_entry * de;
+	unsigned int ino;
+	int i;
+	struct inode *inode = filp->f_dentry->d_inode;
+	int ret = 0;
+
+	lock_kernel();
+
+	ino = inode->i_ino;
+	de = PDE(inode);
+	if (!de) {
+		ret = -EINVAL;
+		goto out;
+	}
+	i = filp->f_pos;
+	switch (i) {
+		case 0:
+			if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
+				goto out;
+			i++;
+			filp->f_pos++;
+			/* fall through */
+		case 1:
+			if (filldir(dirent, "..", 2, i,
+				    parent_ino(filp->f_dentry),
+				    DT_DIR) < 0)
+				goto out;
+			i++;
+			filp->f_pos++;
+			/* fall through */
+		default:
+			de = de->subdir;
+			i -= 2;
+			for (;;) {
+				if (!de) {
+					ret = 1;
+					goto out;
+				}
+				if (!i)
+					break;
+				de = de->next;
+				i--;
+			}
+
+			do {
+				if (filldir(dirent, de->name, de->namelen, filp->f_pos,
+					    de->low_ino, de->mode >> 12) < 0)
+					goto out;
+				filp->f_pos++;
+				de = de->next;
+			} while (de);
+	}
+	ret = 1;
+out:	unlock_kernel();
+	return ret;	
+}
+
+/*
+ * These are the generic /proc directory operations. They
+ * use the in-memory "struct proc_dir_entry" tree to parse
+ * the /proc directory.
+ */
+static struct file_operations proc_dir_operations = {
+	.read			= generic_read_dir,
+	.readdir		= proc_readdir,
+};
+
+/*
+ * proc directories can do almost nothing..
+ */
+static struct inode_operations proc_dir_inode_operations = {
+	.lookup		= proc_lookup,
+	.getattr	= proc_getattr,
+	.setattr	= proc_notify_change,
+};
+
+static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)
+{
+	unsigned int i;
+	
+	i = get_inode_number();
+	if (i == 0)
+		return -EAGAIN;
+	dp->low_ino = i;
+	dp->next = dir->subdir;
+	dp->parent = dir;
+	dir->subdir = dp;
+	if (S_ISDIR(dp->mode)) {
+		if (dp->proc_iops == NULL) {
+			dp->proc_fops = &proc_dir_operations;
+			dp->proc_iops = &proc_dir_inode_operations;
+		}
+		dir->nlink++;
+	} else if (S_ISLNK(dp->mode)) {
+		if (dp->proc_iops == NULL)
+			dp->proc_iops = &proc_link_inode_operations;
+	} else if (S_ISREG(dp->mode)) {
+		if (dp->proc_fops == NULL)
+			dp->proc_fops = &proc_file_operations;
+		if (dp->proc_iops == NULL)
+			dp->proc_iops = &proc_file_inode_operations;
+	}
+	return 0;
+}
+
+/*
+ * Kill an inode that got unregistered..
+ */
+static void proc_kill_inodes(struct proc_dir_entry *de)
+{
+	struct list_head *p;
+	struct super_block *sb = proc_mnt->mnt_sb;
+
+	/*
+	 * Actually it's a partial revoke().
+	 */
+	file_list_lock();
+	list_for_each(p, &sb->s_files) {
+		struct file * filp = list_entry(p, struct file, f_u.fu_list);
+		struct dentry * dentry = filp->f_dentry;
+		struct inode * inode;
+		struct file_operations *fops;
+
+		if (dentry->d_op != &proc_dentry_operations)
+			continue;
+		inode = dentry->d_inode;
+		if (PDE(inode) != de)
+			continue;
+		fops = filp->f_op;
+		filp->f_op = NULL;
+		fops_put(fops);
+	}
+	file_list_unlock();
+}
+
+static struct proc_dir_entry *proc_create(struct proc_dir_entry **parent,
+					  const char *name,
+					  mode_t mode,
+					  nlink_t nlink)
+{
+	struct proc_dir_entry *ent = NULL;
+	const char *fn = name;
+	int len;
+
+	/* make sure name is valid */
+	if (!name || !strlen(name)) goto out;
+
+	if (!(*parent) && xlate_proc_name(name, parent, &fn) != 0)
+		goto out;
+
+	/* At this point there must not be any '/' characters beyond *fn */
+	if (strchr(fn, '/'))
+		goto out;
+
+	len = strlen(fn);
+
+	ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL);
+	if (!ent) goto out;
+
+	memset(ent, 0, sizeof(struct proc_dir_entry));
+	memcpy(((char *) ent) + sizeof(struct proc_dir_entry), fn, len + 1);
+	ent->name = ((char *) ent) + sizeof(*ent);
+	ent->namelen = len;
+	ent->mode = mode;
+	ent->nlink = nlink;
+ out:
+	return ent;
+}
+
+struct proc_dir_entry *proc_symlink(const char *name,
+		struct proc_dir_entry *parent, const char *dest)
+{
+	struct proc_dir_entry *ent;
+
+	ent = proc_create(&parent,name,
+			  (S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO),1);
+
+	if (ent) {
+		ent->data = kmalloc((ent->size=strlen(dest))+1, GFP_KERNEL);
+		if (ent->data) {
+			strcpy((char*)ent->data,dest);
+			if (proc_register(parent, ent) < 0) {
+				kfree(ent->data);
+				kfree(ent);
+				ent = NULL;
+			}
+		} else {
+			kfree(ent);
+			ent = NULL;
+		}
+	}
+	return ent;
+}
+
+struct proc_dir_entry *proc_mkdir_mode(const char *name, mode_t mode,
+		struct proc_dir_entry *parent)
+{
+	struct proc_dir_entry *ent;
+
+	ent = proc_create(&parent, name, S_IFDIR | mode, 2);
+	if (ent) {
+		ent->proc_fops = &proc_dir_operations;
+		ent->proc_iops = &proc_dir_inode_operations;
+
+		if (proc_register(parent, ent) < 0) {
+			kfree(ent);
+			ent = NULL;
+		}
+	}
+	return ent;
+}
+
+struct proc_dir_entry *proc_mkdir(const char *name,
+		struct proc_dir_entry *parent)
+{
+	return proc_mkdir_mode(name, S_IRUGO | S_IXUGO, parent);
+}
+
+struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
+					 struct proc_dir_entry *parent)
+{
+	struct proc_dir_entry *ent;
+	nlink_t nlink;
+
+	if (S_ISDIR(mode)) {
+		if ((mode & S_IALLUGO) == 0)
+			mode |= S_IRUGO | S_IXUGO;
+		nlink = 2;
+	} else {
+		if ((mode & S_IFMT) == 0)
+			mode |= S_IFREG;
+		if ((mode & S_IALLUGO) == 0)
+			mode |= S_IRUGO;
+		nlink = 1;
+	}
+
+	ent = proc_create(&parent,name,mode,nlink);
+	if (ent) {
+		if (S_ISDIR(mode)) {
+			ent->proc_fops = &proc_dir_operations;
+			ent->proc_iops = &proc_dir_inode_operations;
+		}
+		if (proc_register(parent, ent) < 0) {
+			kfree(ent);
+			ent = NULL;
+		}
+	}
+	return ent;
+}
+
+void free_proc_entry(struct proc_dir_entry *de)
+{
+	unsigned int ino = de->low_ino;
+
+	if (ino < PROC_DYNAMIC_FIRST)
+		return;
+
+	release_inode_number(ino);
+
+	if (S_ISLNK(de->mode) && de->data)
+		kfree(de->data);
+	kfree(de);
+}
+
+/*
+ * Remove a /proc entry and free it if it's not currently in use.
+ * If it is in use, we set the 'deleted' flag.
+ */
+void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
+{
+	struct proc_dir_entry **p;
+	struct proc_dir_entry *de;
+	const char *fn = name;
+	int len;
+
+	if (!parent && xlate_proc_name(name, &parent, &fn) != 0)
+		goto out;
+	len = strlen(fn);
+	for (p = &parent->subdir; *p; p=&(*p)->next ) {
+		if (!proc_match(len, fn, *p))
+			continue;
+		de = *p;
+		*p = de->next;
+		de->next = NULL;
+		if (S_ISDIR(de->mode))
+			parent->nlink--;
+		proc_kill_inodes(de);
+		de->nlink = 0;
+		WARN_ON(de->subdir);
+		if (!atomic_read(&de->count))
+			free_proc_entry(de);
+		else {
+			de->deleted = 1;
+			printk("remove_proc_entry: %s/%s busy, count=%d\n",
+				parent->name, de->name, atomic_read(&de->count));
+		}
+		break;
+	}
+out:
+	return;
+}
diff -urN oldtree/fs/proc/proc_devtree.c newtree/fs/proc/proc_devtree.c
--- oldtree/fs/proc/proc_devtree.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/proc/proc_devtree.c	2006-02-13 19:20:00.721402632 -0500
@@ -112,9 +112,11 @@
 		 * properties are quite unimportant for us though, thus we
 		 * simply "skip" them here, but we do have to check.
 		 */
+		spin_lock(&proc_subdir_lock);
 		for (ent = de->subdir; ent != NULL; ent = ent->next)
 			if (!strcmp(ent->name, pp->name))
 				break;
+		spin_unlock(&proc_subdir_lock);
 		if (ent != NULL) {
 			printk(KERN_WARNING "device-tree: property \"%s\" name"
 			       " conflicts with node in %s\n", pp->name,
diff -urN oldtree/fs/proc/proc_misc.c newtree/fs/proc/proc_misc.c
--- oldtree/fs/proc/proc_misc.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/proc/proc_misc.c	2006-02-13 19:20:00.721402632 -0500
@@ -553,6 +553,87 @@
 };
 #endif
 
+#ifdef CONFIG_PAGE_OWNER
+#include <linux/bootmem.h>
+#include <linux/kallsyms.h>
+static ssize_t
+read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+	unsigned long pfn;
+	struct page *page;
+	char *kbuf, *modname;
+	const char *symname;
+	int ret = 0, next_idx = 1;
+	char namebuf[128];
+	unsigned long offset = 0, symsize;
+	int i;
+	ssize_t num_written = 0;
+
+	pfn = min_low_pfn + *ppos;
+	page = pfn_to_page(pfn);
+	for (; pfn < max_pfn; pfn++) {
+		if (!pfn_valid(pfn))
+			continue;
+		page = pfn_to_page(pfn);
+		if (page->order >= 0)
+			break;
+		next_idx++;
+	}
+
+	if (!pfn_valid(pfn))
+		return 0;
+
+	*ppos += next_idx;
+
+	kbuf = kmalloc(count, GFP_KERNEL);
+	if (!kbuf)
+		return -ENOMEM;
+
+	ret = snprintf(kbuf, count, "Page allocated via order %d, mask 0x%x\n",
+			page->order, page->gfp_mask);
+	if (ret >= count) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	num_written = ret;
+
+	for (i = 0; i < 8; i++) {
+		if (!page->trace[i])
+			break;
+		symname = kallsyms_lookup(page->trace[i], &symsize, &offset,
+					&modname, namebuf);
+		ret = snprintf(kbuf + num_written, count - num_written,
+				"[0x%lx] %s+%lu\n",
+				page->trace[i], namebuf, offset);
+		if (ret >= count - num_written) {
+			ret = -ENOMEM;
+			goto out;
+		}
+		num_written += ret;
+	}
+
+	ret = snprintf(kbuf + num_written, count - num_written, "\n");
+	if (ret >= count - num_written) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	num_written += ret;
+	ret = num_written;
+
+	if (copy_to_user(buf, kbuf, ret))
+		ret = -EFAULT;
+out:
+	kfree(kbuf);
+	return ret;
+}
+
+static struct file_operations proc_page_owner_operations = {
+	.read		= read_page_owner,
+};
+#endif
+
 struct proc_dir_entry *proc_root_kcore;
 
 void create_seq_entry(char *name, mode_t mode, struct file_operations *f)
@@ -629,4 +710,11 @@
 	if (entry)
 		entry->proc_fops = &proc_sysrq_trigger_operations;
 #endif
+#ifdef CONFIG_PAGE_OWNER
+	entry = create_proc_entry("page_owner", S_IWUSR | S_IRUGO, NULL);
+	if (entry) {
+		entry->proc_fops = &proc_page_owner_operations;
+		entry->size = 1024;
+	}
+#endif
 }
diff -urN oldtree/fs/proc/proc_misc.c.orig newtree/fs/proc/proc_misc.c.orig
--- oldtree/fs/proc/proc_misc.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/proc/proc_misc.c.orig	2006-02-13 19:20:00.722402480 -0500
@@ -0,0 +1,632 @@
+/*
+ *  linux/fs/proc/proc_misc.c
+ *
+ *  linux/fs/proc/array.c
+ *  Copyright (C) 1992  by Linus Torvalds
+ *  based on ideas by Darren Senn
+ *
+ *  This used to be the part of array.c. See the rest of history and credits
+ *  there. I took this into a separate file and switched the thing to generic
+ *  proc_file_inode_operations, leaving in array.c only per-process stuff.
+ *  Inumbers allocation made dynamic (via create_proc_entry()).  AV, May 1999.
+ *
+ * Changes:
+ * Fulton Green      :  Encapsulated position metric calculations.
+ *			<kernel@FultonGreen.com>
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/time.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/tty.h>
+#include <linux/string.h>
+#include <linux/mman.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/mmzone.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/signal.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/seq_file.h>
+#include <linux/times.h>
+#include <linux/profile.h>
+#include <linux/blkdev.h>
+#include <linux/hugetlb.h>
+#include <linux/jiffies.h>
+#include <linux/sysrq.h>
+#include <linux/vmalloc.h>
+#include <linux/crash_dump.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/tlb.h>
+#include <asm/div64.h>
+#include "internal.h"
+
+#define LOAD_INT(x) ((x) >> FSHIFT)
+#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
+/*
+ * Warning: stuff below (imported functions) assumes that its output will fit
+ * into one page. For some of those functions it may be wrong. Moreover, we
+ * have a way to deal with that gracefully. Right now I used straightforward
+ * wrappers, but this needs further analysis wrt potential overflows.
+ */
+extern int get_hardware_list(char *);
+extern int get_stram_list(char *);
+extern int get_chrdev_list(char *);
+extern int get_filesystem_list(char *);
+extern int get_exec_domain_list(char *);
+extern int get_dma_list(char *);
+extern int get_locks_status (char *, char **, off_t, int);
+
+static int proc_calc_metrics(char *page, char **start, off_t off,
+				 int count, int *eof, int len)
+{
+	if (len <= off+count) *eof = 1;
+	*start = page + off;
+	len -= off;
+	if (len>count) len = count;
+	if (len<0) len = 0;
+	return len;
+}
+
+static int loadavg_read_proc(char *page, char **start, off_t off,
+				 int count, int *eof, void *data)
+{
+	int a, b, c;
+	int len;
+
+	a = avenrun[0] + (FIXED_1/200);
+	b = avenrun[1] + (FIXED_1/200);
+	c = avenrun[2] + (FIXED_1/200);
+	len = sprintf(page,"%d.%02d %d.%02d %d.%02d %ld/%d %d\n",
+		LOAD_INT(a), LOAD_FRAC(a),
+		LOAD_INT(b), LOAD_FRAC(b),
+		LOAD_INT(c), LOAD_FRAC(c),
+		nr_running(), nr_threads, last_pid);
+	return proc_calc_metrics(page, start, off, count, eof, len);
+}
+
+static int uptime_read_proc(char *page, char **start, off_t off,
+				 int count, int *eof, void *data)
+{
+	struct timespec uptime;
+	struct timespec idle;
+	int len;
+	cputime_t idletime = cputime_add(init_task.utime, init_task.stime);
+
+	do_posix_clock_monotonic_gettime(&uptime);
+	cputime_to_timespec(idletime, &idle);
+	len = sprintf(page,"%lu.%02lu %lu.%02lu\n",
+			(unsigned long) uptime.tv_sec,
+			(uptime.tv_nsec / (NSEC_PER_SEC / 100)),
+			(unsigned long) idle.tv_sec,
+			(idle.tv_nsec / (NSEC_PER_SEC / 100)));
+
+	return proc_calc_metrics(page, start, off, count, eof, len);
+}
+
+static int meminfo_read_proc(char *page, char **start, off_t off,
+				 int count, int *eof, void *data)
+{
+	struct sysinfo i;
+	int len;
+	struct page_state ps;
+	unsigned long inactive;
+	unsigned long active;
+	unsigned long free;
+	unsigned long committed;
+	unsigned long allowed;
+	struct vmalloc_info vmi;
+	long cached;
+
+	get_page_state(&ps);
+	get_zone_counts(&active, &inactive, &free);
+
+/*
+ * display in kilobytes.
+ */
+#define K(x) ((x) << (PAGE_SHIFT - 10))
+	si_meminfo(&i);
+	si_swapinfo(&i);
+	committed = atomic_read(&vm_committed_space);
+	allowed = ((totalram_pages - hugetlb_total_pages())
+		* sysctl_overcommit_ratio / 100) + total_swap_pages;
+
+	cached = get_page_cache_size() - total_swapcache_pages - i.bufferram;
+	if (cached < 0)
+		cached = 0;
+
+	get_vmalloc_info(&vmi);
+
+	/*
+	 * Tagged format, for easy grepping and expansion.
+	 */
+	len = sprintf(page,
+		"MemTotal:     %8lu kB\n"
+		"MemFree:      %8lu kB\n"
+		"Buffers:      %8lu kB\n"
+		"Cached:       %8lu kB\n"
+		"SwapCached:   %8lu kB\n"
+		"Active:       %8lu kB\n"
+		"Inactive:     %8lu kB\n"
+		"HighTotal:    %8lu kB\n"
+		"HighFree:     %8lu kB\n"
+		"LowTotal:     %8lu kB\n"
+		"LowFree:      %8lu kB\n"
+		"SwapTotal:    %8lu kB\n"
+		"SwapFree:     %8lu kB\n"
+		"Dirty:        %8lu kB\n"
+		"Writeback:    %8lu kB\n"
+		"Mapped:       %8lu kB\n"
+		"Slab:         %8lu kB\n"
+		"CommitLimit:  %8lu kB\n"
+		"Committed_AS: %8lu kB\n"
+		"PageTables:   %8lu kB\n"
+		"VmallocTotal: %8lu kB\n"
+		"VmallocUsed:  %8lu kB\n"
+		"VmallocChunk: %8lu kB\n",
+		K(i.totalram),
+		K(i.freeram),
+		K(i.bufferram),
+		K(cached),
+		K(total_swapcache_pages),
+		K(active),
+		K(inactive),
+		K(i.totalhigh),
+		K(i.freehigh),
+		K(i.totalram-i.totalhigh),
+		K(i.freeram-i.freehigh),
+		K(i.totalswap),
+		K(i.freeswap),
+		K(ps.nr_dirty),
+		K(ps.nr_writeback),
+		K(ps.nr_mapped),
+		K(ps.nr_slab),
+		K(allowed),
+		K(committed),
+		K(ps.nr_page_table_pages),
+		(unsigned long)VMALLOC_TOTAL >> 10,
+		vmi.used >> 10,
+		vmi.largest_chunk >> 10
+		);
+
+		len += hugetlb_report_meminfo(page + len);
+
+	return proc_calc_metrics(page, start, off, count, eof, len);
+#undef K
+}
+
+extern struct seq_operations fragmentation_op;
+static int fragmentation_open(struct inode *inode, struct file *file)
+{
+	(void)inode;
+	return seq_open(file, &fragmentation_op);
+}
+
+static struct file_operations fragmentation_file_operations = {
+	.open		= fragmentation_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+extern struct seq_operations zoneinfo_op;
+static int zoneinfo_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &zoneinfo_op);
+}
+
+static struct file_operations proc_zoneinfo_file_operations = {
+	.open		= zoneinfo_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static int version_read_proc(char *page, char **start, off_t off,
+				 int count, int *eof, void *data)
+{
+	int len;
+
+	strcpy(page, linux_banner);
+	len = strlen(page);
+	return proc_calc_metrics(page, start, off, count, eof, len);
+}
+
+extern struct seq_operations cpuinfo_op;
+static int cpuinfo_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &cpuinfo_op);
+}
+static struct file_operations proc_cpuinfo_operations = {
+	.open		= cpuinfo_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+extern struct seq_operations vmstat_op;
+static int vmstat_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &vmstat_op);
+}
+static struct file_operations proc_vmstat_file_operations = {
+	.open		= vmstat_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+#ifdef CONFIG_PROC_HARDWARE
+static int hardware_read_proc(char *page, char **start, off_t off,
+				 int count, int *eof, void *data)
+{
+	int len = get_hardware_list(page);
+	return proc_calc_metrics(page, start, off, count, eof, len);
+}
+#endif
+
+#ifdef CONFIG_STRAM_PROC
+static int stram_read_proc(char *page, char **start, off_t off,
+				 int count, int *eof, void *data)
+{
+	int len = get_stram_list(page);
+	return proc_calc_metrics(page, start, off, count, eof, len);
+}
+#endif
+
+extern struct seq_operations partitions_op;
+static int partitions_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &partitions_op);
+}
+static struct file_operations proc_partitions_operations = {
+	.open		= partitions_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+extern struct seq_operations diskstats_op;
+static int diskstats_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &diskstats_op);
+}
+static struct file_operations proc_diskstats_operations = {
+	.open		= diskstats_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+#ifdef CONFIG_MODULES
+extern struct seq_operations modules_op;
+static int modules_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &modules_op);
+}
+static struct file_operations proc_modules_operations = {
+	.open		= modules_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+#endif
+
+extern struct seq_operations slabinfo_op;
+extern ssize_t slabinfo_write(struct file *, const char __user *, size_t, loff_t *);
+static int slabinfo_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &slabinfo_op);
+}
+static struct file_operations proc_slabinfo_operations = {
+	.open		= slabinfo_open,
+	.read		= seq_read,
+	.write		= slabinfo_write,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static int show_stat(struct seq_file *p, void *v)
+{
+	int i;
+	unsigned long jif;
+	cputime64_t user, nice, system, idle, iowait, irq, softirq, steal;
+	u64 sum = 0;
+
+	user = nice = system = idle = iowait =
+		irq = softirq = steal = cputime64_zero;
+	jif = - wall_to_monotonic.tv_sec;
+	if (wall_to_monotonic.tv_nsec)
+		--jif;
+
+	for_each_cpu(i) {
+		int j;
+
+		user = cputime64_add(user, kstat_cpu(i).cpustat.user);
+		nice = cputime64_add(nice, kstat_cpu(i).cpustat.nice);
+		system = cputime64_add(system, kstat_cpu(i).cpustat.system);
+		idle = cputime64_add(idle, kstat_cpu(i).cpustat.idle);
+		iowait = cputime64_add(iowait, kstat_cpu(i).cpustat.iowait);
+		irq = cputime64_add(irq, kstat_cpu(i).cpustat.irq);
+		softirq = cputime64_add(softirq, kstat_cpu(i).cpustat.softirq);
+		steal = cputime64_add(steal, kstat_cpu(i).cpustat.steal);
+		for (j = 0 ; j < NR_IRQS ; j++)
+			sum += kstat_cpu(i).irqs[j];
+	}
+
+	seq_printf(p, "cpu  %llu %llu %llu %llu %llu %llu %llu %llu\n",
+		(unsigned long long)cputime64_to_clock_t(user),
+		(unsigned long long)cputime64_to_clock_t(nice),
+		(unsigned long long)cputime64_to_clock_t(system),
+		(unsigned long long)cputime64_to_clock_t(idle),
+		(unsigned long long)cputime64_to_clock_t(iowait),
+		(unsigned long long)cputime64_to_clock_t(irq),
+		(unsigned long long)cputime64_to_clock_t(softirq),
+		(unsigned long long)cputime64_to_clock_t(steal));
+	for_each_online_cpu(i) {
+
+		/* Copy values here to work around gcc-2.95.3, gcc-2.96 */
+		user = kstat_cpu(i).cpustat.user;
+		nice = kstat_cpu(i).cpustat.nice;
+		system = kstat_cpu(i).cpustat.system;
+		idle = kstat_cpu(i).cpustat.idle;
+		iowait = kstat_cpu(i).cpustat.iowait;
+		irq = kstat_cpu(i).cpustat.irq;
+		softirq = kstat_cpu(i).cpustat.softirq;
+		steal = kstat_cpu(i).cpustat.steal;
+		seq_printf(p, "cpu%d %llu %llu %llu %llu %llu %llu %llu %llu\n",
+			i,
+			(unsigned long long)cputime64_to_clock_t(user),
+			(unsigned long long)cputime64_to_clock_t(nice),
+			(unsigned long long)cputime64_to_clock_t(system),
+			(unsigned long long)cputime64_to_clock_t(idle),
+			(unsigned long long)cputime64_to_clock_t(iowait),
+			(unsigned long long)cputime64_to_clock_t(irq),
+			(unsigned long long)cputime64_to_clock_t(softirq),
+			(unsigned long long)cputime64_to_clock_t(steal));
+	}
+	seq_printf(p, "intr %llu", (unsigned long long)sum);
+
+#if !defined(CONFIG_PPC64) && !defined(CONFIG_ALPHA)
+	for (i = 0; i < NR_IRQS; i++)
+		seq_printf(p, " %u", kstat_irqs(i));
+#endif
+
+	seq_printf(p,
+		"\nctxt %llu\n"
+		"btime %lu\n"
+		"processes %lu\n"
+		"procs_running %lu\n"
+		"procs_blocked %lu\n",
+		nr_context_switches(),
+		(unsigned long)jif,
+		total_forks,
+		nr_running(),
+		nr_iowait());
+
+	return 0;
+}
+
+static int stat_open(struct inode *inode, struct file *file)
+{
+	unsigned size = 4096 * (1 + num_possible_cpus() / 32);
+	char *buf;
+	struct seq_file *m;
+	int res;
+
+	/* don't ask for more than the kmalloc() max size, currently 128 KB */
+	if (size > 128 * 1024)
+		size = 128 * 1024;
+	buf = kmalloc(size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	res = single_open(file, show_stat, NULL);
+	if (!res) {
+		m = file->private_data;
+		m->buf = buf;
+		m->size = size;
+	} else
+		kfree(buf);
+	return res;
+}
+static struct file_operations proc_stat_operations = {
+	.open		= stat_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int devices_read_proc(char *page, char **start, off_t off,
+				 int count, int *eof, void *data)
+{
+	int len = get_chrdev_list(page);
+	len += get_blkdev_list(page+len, len);
+	return proc_calc_metrics(page, start, off, count, eof, len);
+}
+
+/*
+ * /proc/interrupts
+ */
+static void *int_seq_start(struct seq_file *f, loff_t *pos)
+{
+	return (*pos <= NR_IRQS) ? pos : NULL;
+}
+
+static void *int_seq_next(struct seq_file *f, void *v, loff_t *pos)
+{
+	(*pos)++;
+	if (*pos > NR_IRQS)
+		return NULL;
+	return pos;
+}
+
+static void int_seq_stop(struct seq_file *f, void *v)
+{
+	/* Nothing to do */
+}
+
+
+extern int show_interrupts(struct seq_file *f, void *v); /* In arch code */
+static struct seq_operations int_seq_ops = {
+	.start = int_seq_start,
+	.next  = int_seq_next,
+	.stop  = int_seq_stop,
+	.show  = show_interrupts
+};
+
+static int interrupts_open(struct inode *inode, struct file *filp)
+{
+	return seq_open(filp, &int_seq_ops);
+}
+
+static struct file_operations proc_interrupts_operations = {
+	.open		= interrupts_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static int filesystems_read_proc(char *page, char **start, off_t off,
+				 int count, int *eof, void *data)
+{
+	int len = get_filesystem_list(page);
+	return proc_calc_metrics(page, start, off, count, eof, len);
+}
+
+static int cmdline_read_proc(char *page, char **start, off_t off,
+				 int count, int *eof, void *data)
+{
+	int len;
+
+	len = sprintf(page, "%s\n", saved_command_line);
+	return proc_calc_metrics(page, start, off, count, eof, len);
+}
+
+static int locks_read_proc(char *page, char **start, off_t off,
+				 int count, int *eof, void *data)
+{
+	int len = get_locks_status(page, start, off, count);
+
+	if (len < count)
+		*eof = 1;
+	return len;
+}
+
+static int execdomains_read_proc(char *page, char **start, off_t off,
+				 int count, int *eof, void *data)
+{
+	int len = get_exec_domain_list(page);
+	return proc_calc_metrics(page, start, off, count, eof, len);
+}
+
+#ifdef CONFIG_MAGIC_SYSRQ
+/*
+ * writing 'C' to /proc/sysrq-trigger is like sysrq-C
+ */
+static ssize_t write_sysrq_trigger(struct file *file, const char __user *buf,
+				   size_t count, loff_t *ppos)
+{
+	if (count) {
+		char c;
+
+		if (get_user(c, buf))
+			return -EFAULT;
+		__handle_sysrq(c, NULL, NULL, 0);
+	}
+	return count;
+}
+
+static struct file_operations proc_sysrq_trigger_operations = {
+	.write		= write_sysrq_trigger,
+};
+#endif
+
+struct proc_dir_entry *proc_root_kcore;
+
+void create_seq_entry(char *name, mode_t mode, struct file_operations *f)
+{
+	struct proc_dir_entry *entry;
+	entry = create_proc_entry(name, mode, NULL);
+	if (entry)
+		entry->proc_fops = f;
+}
+
+void __init proc_misc_init(void)
+{
+	struct proc_dir_entry *entry;
+	static struct {
+		char *name;
+		int (*read_proc)(char*,char**,off_t,int,int*,void*);
+	} *p, simple_ones[] = {
+		{"loadavg",     loadavg_read_proc},
+		{"uptime",	uptime_read_proc},
+		{"meminfo",	meminfo_read_proc},
+		{"version",	version_read_proc},
+#ifdef CONFIG_PROC_HARDWARE
+		{"hardware",	hardware_read_proc},
+#endif
+#ifdef CONFIG_STRAM_PROC
+		{"stram",	stram_read_proc},
+#endif
+		{"devices",	devices_read_proc},
+		{"filesystems",	filesystems_read_proc},
+		{"cmdline",	cmdline_read_proc},
+		{"locks",	locks_read_proc},
+		{"execdomains",	execdomains_read_proc},
+		{NULL,}
+	};
+	for (p = simple_ones; p->name; p++)
+		create_proc_read_entry(p->name, 0, NULL, p->read_proc, NULL);
+
+	proc_symlink("mounts", NULL, "self/mounts");
+
+	/* And now for trickier ones */
+	entry = create_proc_entry("kmsg", S_IRUSR, &proc_root);
+	if (entry)
+		entry->proc_fops = &proc_kmsg_operations;
+	create_seq_entry("cpuinfo", 0, &proc_cpuinfo_operations);
+	create_seq_entry("partitions", 0, &proc_partitions_operations);
+	create_seq_entry("stat", 0, &proc_stat_operations);
+	create_seq_entry("interrupts", 0, &proc_interrupts_operations);
+	create_seq_entry("slabinfo",S_IWUSR|S_IRUGO,&proc_slabinfo_operations);
+	create_seq_entry("buddyinfo",S_IRUGO, &fragmentation_file_operations);
+	create_seq_entry("vmstat",S_IRUGO, &proc_vmstat_file_operations);
+	create_seq_entry("zoneinfo",S_IRUGO, &proc_zoneinfo_file_operations);
+	create_seq_entry("diskstats", 0, &proc_diskstats_operations);
+#ifdef CONFIG_MODULES
+	create_seq_entry("modules", 0, &proc_modules_operations);
+#endif
+#ifdef CONFIG_SCHEDSTATS
+	create_seq_entry("schedstat", 0, &proc_schedstat_operations);
+#endif
+#ifdef CONFIG_PROC_KCORE
+	proc_root_kcore = create_proc_entry("kcore", S_IRUSR, NULL);
+	if (proc_root_kcore) {
+		proc_root_kcore->proc_fops = &proc_kcore_operations;
+		proc_root_kcore->size =
+				(size_t)high_memory - PAGE_OFFSET + PAGE_SIZE;
+	}
+#endif
+#ifdef CONFIG_PROC_VMCORE
+	proc_vmcore = create_proc_entry("vmcore", S_IRUSR, NULL);
+	if (proc_vmcore)
+		proc_vmcore->proc_fops = &proc_vmcore_operations;
+#endif
+#ifdef CONFIG_MAGIC_SYSRQ
+	entry = create_proc_entry("sysrq-trigger", S_IWUSR, NULL);
+	if (entry)
+		entry->proc_fops = &proc_sysrq_trigger_operations;
+#endif
+}
diff -urN oldtree/fs/quota_v2.c newtree/fs/quota_v2.c
--- oldtree/fs/quota_v2.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/quota_v2.c	2006-02-13 19:20:00.723402328 -0500
@@ -35,7 +35,8 @@
  
 	size = sb->s_op->quota_read(sb, type, (char *)&dqhead, sizeof(struct v2_disk_dqheader), 0);
 	if (size != sizeof(struct v2_disk_dqheader)) {
-		printk("failed read\n");
+		printk("quota_v2: failed read expected=%d got=%d\n",
+			sizeof(struct v2_disk_dqheader), size);
 		return 0;
 	}
 	if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] ||
diff -urN oldtree/fs/reiserfs/dir.c newtree/fs/reiserfs/dir.c
--- oldtree/fs/reiserfs/dir.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/reiserfs/dir.c	2006-02-13 19:20:00.723402328 -0500
@@ -150,18 +150,15 @@
 				if (d_reclen <= 32) {
 					local_buf = small_buf;
 				} else {
-					local_buf =
-					    reiserfs_kmalloc(d_reclen, GFP_NOFS,
-							     inode->i_sb);
+					local_buf = kmalloc(d_reclen,
+							    GFP_NOFS);
 					if (!local_buf) {
 						pathrelse(&path_to_entry);
 						ret = -ENOMEM;
 						goto out;
 					}
 					if (item_moved(&tmp_ih, &path_to_entry)) {
-						reiserfs_kfree(local_buf,
-							       d_reclen,
-							       inode->i_sb);
+						kfree(local_buf);
 						goto research;
 					}
 				}
@@ -174,15 +171,12 @@
 				    (dirent, local_buf, d_reclen, d_off, d_ino,
 				     DT_UNKNOWN) < 0) {
 					if (local_buf != small_buf) {
-						reiserfs_kfree(local_buf,
-							       d_reclen,
-							       inode->i_sb);
+						kfree(local_buf);
 					}
 					goto end;
 				}
 				if (local_buf != small_buf) {
-					reiserfs_kfree(local_buf, d_reclen,
-						       inode->i_sb);
+					kfree(local_buf);
 				}
 				// next entry should be looked for with such offset
 				next_pos = deh_offset(deh) + 1;
diff -urN oldtree/fs/reiserfs/fix_node.c newtree/fs/reiserfs/fix_node.c
--- oldtree/fs/reiserfs/fix_node.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/reiserfs/fix_node.c	2006-02-13 19:20:00.724402176 -0500
@@ -2021,38 +2021,6 @@
 	return CARRY_ON;
 }
 
-#ifdef CONFIG_REISERFS_CHECK
-void *reiserfs_kmalloc(size_t size, gfp_t flags, struct super_block *s)
-{
-	void *vp;
-	static size_t malloced;
-
-	vp = kmalloc(size, flags);
-	if (vp) {
-		REISERFS_SB(s)->s_kmallocs += size;
-		if (REISERFS_SB(s)->s_kmallocs > malloced + 200000) {
-			reiserfs_warning(s,
-					 "vs-8301: reiserfs_kmalloc: allocated memory %d",
-					 REISERFS_SB(s)->s_kmallocs);
-			malloced = REISERFS_SB(s)->s_kmallocs;
-		}
-	}
-	return vp;
-}
-
-void reiserfs_kfree(const void *vp, size_t size, struct super_block *s)
-{
-	kfree(vp);
-
-	REISERFS_SB(s)->s_kmallocs -= size;
-	if (REISERFS_SB(s)->s_kmallocs < 0)
-		reiserfs_warning(s,
-				 "vs-8302: reiserfs_kfree: allocated memory %d",
-				 REISERFS_SB(s)->s_kmallocs);
-
-}
-#endif
-
 static int get_virtual_node_size(struct super_block *sb, struct buffer_head *bh)
 {
 	int max_num_of_items;
@@ -2086,7 +2054,7 @@
 		/* we have to allocate more memory for virtual node */
 		if (tb->vn_buf) {
 			/* free memory allocated before */
-			reiserfs_kfree(tb->vn_buf, tb->vn_buf_size, tb->tb_sb);
+			kfree(tb->vn_buf);
 			/* this is not needed if kfree is atomic */
 			check_fs = 1;
 		}
@@ -2095,24 +2063,15 @@
 		tb->vn_buf_size = size;
 
 		/* get memory for virtual item */
-		buf =
-		    reiserfs_kmalloc(size, GFP_ATOMIC | __GFP_NOWARN,
-				     tb->tb_sb);
+		buf = kmalloc(size, GFP_ATOMIC | __GFP_NOWARN);
 		if (!buf) {
 			/* getting memory with GFP_KERNEL priority may involve
 			   balancing now (due to indirect_to_direct conversion on
 			   dcache shrinking). So, release path and collected
 			   resources here */
 			free_buffers_in_tb(tb);
-			buf = reiserfs_kmalloc(size, GFP_NOFS, tb->tb_sb);
+			buf = kmalloc(size, GFP_NOFS);
 			if (!buf) {
-#ifdef CONFIG_REISERFS_CHECK
-				reiserfs_warning(tb->tb_sb,
-						 "vs-8345: get_mem_for_virtual_node: "
-						 "kmalloc failed. reiserfs kmalloced %d bytes",
-						 REISERFS_SB(tb->tb_sb)->
-						 s_kmallocs);
-#endif
 				tb->vn_buf_size = 0;
 			}
 			tb->vn_buf = buf;
@@ -2619,7 +2578,6 @@
 		}
 	}
 
-	if (tb->vn_buf)
-		reiserfs_kfree(tb->vn_buf, tb->vn_buf_size, tb->tb_sb);
+	kfree(tb->vn_buf);
 
 }
diff -urN oldtree/fs/reiserfs/journal.c newtree/fs/reiserfs/journal.c
--- oldtree/fs/reiserfs/journal.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/reiserfs/journal.c	2006-02-13 19:20:00.726401872 -0500
@@ -152,18 +152,16 @@
 	struct reiserfs_bitmap_node *bn;
 	static int id;
 
-	bn = reiserfs_kmalloc(sizeof(struct reiserfs_bitmap_node), GFP_NOFS,
-			      p_s_sb);
+	bn = kmalloc(sizeof(struct reiserfs_bitmap_node), GFP_NOFS);
 	if (!bn) {
 		return NULL;
 	}
-	bn->data = reiserfs_kmalloc(p_s_sb->s_blocksize, GFP_NOFS, p_s_sb);
+	bn->data = kzalloc(p_s_sb->s_blocksize, GFP_NOFS);
 	if (!bn->data) {
-		reiserfs_kfree(bn, sizeof(struct reiserfs_bitmap_node), p_s_sb);
+		kfree(bn);
 		return NULL;
 	}
 	bn->id = id++;
-	memset(bn->data, 0, p_s_sb->s_blocksize);
 	INIT_LIST_HEAD(&bn->list);
 	return bn;
 }
@@ -197,8 +195,8 @@
 	struct reiserfs_journal *journal = SB_JOURNAL(p_s_sb);
 	journal->j_used_bitmap_nodes--;
 	if (journal->j_free_bitmap_nodes > REISERFS_MAX_BITMAP_NODES) {
-		reiserfs_kfree(bn->data, p_s_sb->s_blocksize, p_s_sb);
-		reiserfs_kfree(bn, sizeof(struct reiserfs_bitmap_node), p_s_sb);
+		kfree(bn->data);
+		kfree(bn);
 	} else {
 		list_add(&bn->list, &journal->j_bitmap_nodes);
 		journal->j_free_bitmap_nodes++;
@@ -276,8 +274,8 @@
 	while (next != &journal->j_bitmap_nodes) {
 		bn = list_entry(next, struct reiserfs_bitmap_node, list);
 		list_del(next);
-		reiserfs_kfree(bn->data, p_s_sb->s_blocksize, p_s_sb);
-		reiserfs_kfree(bn, sizeof(struct reiserfs_bitmap_node), p_s_sb);
+		kfree(bn->data);
+		kfree(bn);
 		next = journal->j_bitmap_nodes.next;
 		journal->j_free_bitmap_nodes--;
 	}
@@ -581,7 +579,7 @@
 			       jl->j_trans_id, jl->j_refcount);
 	}
 	if (--jl->j_refcount == 0)
-		reiserfs_kfree(jl, sizeof(struct reiserfs_journal_list), s);
+		kfree(jl);
 }
 
 /*
@@ -1818,8 +1816,7 @@
 static void free_journal_ram(struct super_block *p_s_sb)
 {
 	struct reiserfs_journal *journal = SB_JOURNAL(p_s_sb);
-	reiserfs_kfree(journal->j_current_jl,
-		       sizeof(struct reiserfs_journal_list), p_s_sb);
+	kfree(journal->j_current_jl);
 	journal->j_num_lists--;
 
 	vfree(journal->j_cnode_free_orig);
@@ -2093,21 +2090,15 @@
 	}
 	trans_id = get_desc_trans_id(desc);
 	/* now we know we've got a good transaction, and it was inside the valid time ranges */
-	log_blocks =
-	    reiserfs_kmalloc(get_desc_trans_len(desc) *
-			     sizeof(struct buffer_head *), GFP_NOFS, p_s_sb);
-	real_blocks =
-	    reiserfs_kmalloc(get_desc_trans_len(desc) *
-			     sizeof(struct buffer_head *), GFP_NOFS, p_s_sb);
+	log_blocks = kmalloc(get_desc_trans_len(desc) *
+			     sizeof(struct buffer_head *), GFP_NOFS);
+	real_blocks = kmalloc(get_desc_trans_len(desc) *
+			      sizeof(struct buffer_head *), GFP_NOFS);
 	if (!log_blocks || !real_blocks) {
 		brelse(c_bh);
 		brelse(d_bh);
-		reiserfs_kfree(log_blocks,
-			       get_desc_trans_len(desc) *
-			       sizeof(struct buffer_head *), p_s_sb);
-		reiserfs_kfree(real_blocks,
-			       get_desc_trans_len(desc) *
-			       sizeof(struct buffer_head *), p_s_sb);
+		kfree(log_blocks);
+		kfree(real_blocks);
 		reiserfs_warning(p_s_sb,
 				 "journal-1169: kmalloc failed, unable to mount FS");
 		return -1;
@@ -2145,12 +2136,8 @@
 			brelse_array(real_blocks, i);
 			brelse(c_bh);
 			brelse(d_bh);
-			reiserfs_kfree(log_blocks,
-				       get_desc_trans_len(desc) *
-				       sizeof(struct buffer_head *), p_s_sb);
-			reiserfs_kfree(real_blocks,
-				       get_desc_trans_len(desc) *
-				       sizeof(struct buffer_head *), p_s_sb);
+			kfree(log_blocks);
+			kfree(real_blocks);
 			return -1;
 		}
 	}
@@ -2166,12 +2153,8 @@
 			brelse_array(real_blocks, get_desc_trans_len(desc));
 			brelse(c_bh);
 			brelse(d_bh);
-			reiserfs_kfree(log_blocks,
-				       get_desc_trans_len(desc) *
-				       sizeof(struct buffer_head *), p_s_sb);
-			reiserfs_kfree(real_blocks,
-				       get_desc_trans_len(desc) *
-				       sizeof(struct buffer_head *), p_s_sb);
+			kfree(log_blocks);
+			kfree(real_blocks);
 			return -1;
 		}
 		memcpy(real_blocks[i]->b_data, log_blocks[i]->b_data,
@@ -2193,12 +2176,8 @@
 				     get_desc_trans_len(desc) - i);
 			brelse(c_bh);
 			brelse(d_bh);
-			reiserfs_kfree(log_blocks,
-				       get_desc_trans_len(desc) *
-				       sizeof(struct buffer_head *), p_s_sb);
-			reiserfs_kfree(real_blocks,
-				       get_desc_trans_len(desc) *
-				       sizeof(struct buffer_head *), p_s_sb);
+			kfree(log_blocks);
+			kfree(real_blocks);
 			return -1;
 		}
 		brelse(real_blocks[i]);
@@ -2217,12 +2196,8 @@
 	journal->j_trans_id = trans_id + 1;
 	brelse(c_bh);
 	brelse(d_bh);
-	reiserfs_kfree(log_blocks,
-		       le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *),
-		       p_s_sb);
-	reiserfs_kfree(real_blocks,
-		       le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *),
-		       p_s_sb);
+	kfree(log_blocks);
+	kfree(real_blocks);
 	return 0;
 }
 
@@ -2471,14 +2446,8 @@
 static struct reiserfs_journal_list *alloc_journal_list(struct super_block *s)
 {
 	struct reiserfs_journal_list *jl;
-      retry:
-	jl = reiserfs_kmalloc(sizeof(struct reiserfs_journal_list), GFP_NOFS,
-			      s);
-	if (!jl) {
-		yield();
-		goto retry;
-	}
-	memset(jl, 0, sizeof(*jl));
+	jl = kzalloc(sizeof(struct reiserfs_journal_list),
+		     GFP_NOFS | __GFP_NOFAIL);
 	INIT_LIST_HEAD(&jl->j_list);
 	INIT_LIST_HEAD(&jl->j_working_list);
 	INIT_LIST_HEAD(&jl->j_tail_bh_list);
@@ -3042,14 +3011,12 @@
 		}
 		return th;
 	}
-	th = reiserfs_kmalloc(sizeof(struct reiserfs_transaction_handle),
-			      GFP_NOFS, s);
+	th = kmalloc(sizeof(struct reiserfs_transaction_handle), GFP_NOFS);
 	if (!th)
 		return NULL;
 	ret = journal_begin(th, s, nblocks);
 	if (ret) {
-		reiserfs_kfree(th, sizeof(struct reiserfs_transaction_handle),
-			       s);
+		kfree(th);
 		return NULL;
 	}
 
@@ -3067,8 +3034,7 @@
 		ret = -EIO;
 	if (th->t_refcount == 0) {
 		SB_JOURNAL(s)->j_persistent_trans--;
-		reiserfs_kfree(th, sizeof(struct reiserfs_transaction_handle),
-			       s);
+		kfree(th);
 	}
 	return ret;
 }
diff -urN oldtree/fs/reiserfs/namei.c newtree/fs/reiserfs/namei.c
--- oldtree/fs/reiserfs/namei.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/reiserfs/namei.c	2006-02-13 19:20:00.727401720 -0500
@@ -375,11 +375,7 @@
 		return ERR_PTR(-EIO);
 	}
 
-	if (inode)
-		return d_splice_alias(inode, dentry);
-
-	d_add(dentry, inode);
-	return NULL;
+	return d_splice_alias(inode, dentry);
 }
 
 /* 
@@ -460,7 +456,7 @@
 	/* get memory for composing the entry */
 	buflen = DEH_SIZE + ROUND_UP(namelen);
 	if (buflen > sizeof(small_buf)) {
-		buffer = reiserfs_kmalloc(buflen, GFP_NOFS, dir->i_sb);
+		buffer = kmalloc(buflen, GFP_NOFS);
 		if (buffer == 0)
 			return -ENOMEM;
 	} else
@@ -494,7 +490,7 @@
 	retval = reiserfs_find_entry(dir, name, namelen, &path, &de);
 	if (retval != NAME_NOT_FOUND) {
 		if (buffer != small_buf)
-			reiserfs_kfree(buffer, buflen, dir->i_sb);
+			kfree(buffer);
 		pathrelse(&path);
 
 		if (retval == IO_ERROR) {
@@ -519,7 +515,7 @@
 		reiserfs_warning(dir->i_sb,
 				 "reiserfs_add_entry: Congratulations! we have got hash function screwed up");
 		if (buffer != small_buf)
-			reiserfs_kfree(buffer, buflen, dir->i_sb);
+			kfree(buffer);
 		pathrelse(&path);
 		return -EBUSY;
 	}
@@ -539,7 +535,7 @@
 					 &entry_key);
 
 			if (buffer != small_buf)
-				reiserfs_kfree(buffer, buflen, dir->i_sb);
+				kfree(buffer);
 			pathrelse(&path);
 			return -EBUSY;
 		}
@@ -550,7 +546,7 @@
 	    reiserfs_paste_into_item(th, &path, &entry_key, dir, buffer,
 				     paste_size);
 	if (buffer != small_buf)
-		reiserfs_kfree(buffer, buflen, dir->i_sb);
+		kfree(buffer);
 	if (retval) {
 		reiserfs_check_path(&path);
 		return retval;
@@ -1069,7 +1065,7 @@
 		goto out_failed;
 	}
 
-	name = reiserfs_kmalloc(item_len, GFP_NOFS, parent_dir->i_sb);
+	name = kmalloc(item_len, GFP_NOFS);
 	if (!name) {
 		drop_new_inode(inode);
 		retval = -ENOMEM;
@@ -1083,14 +1079,14 @@
 	retval = journal_begin(&th, parent_dir->i_sb, jbegin_count);
 	if (retval) {
 		drop_new_inode(inode);
-		reiserfs_kfree(name, item_len, parent_dir->i_sb);
+		kfree(name);
 		goto out_failed;
 	}
 
 	retval =
 	    reiserfs_new_inode(&th, parent_dir, mode, name, strlen(symname),
 			       dentry, inode);
-	reiserfs_kfree(name, item_len, parent_dir->i_sb);
+	kfree(name);
 	if (retval) {		/* reiserfs_new_inode iputs for us */
 		goto out_failed;
 	}
diff -urN oldtree/fs/reiserfs/procfs.c newtree/fs/reiserfs/procfs.c
--- oldtree/fs/reiserfs/procfs.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/reiserfs/procfs.c	2006-02-13 19:20:00.728401568 -0500
@@ -88,7 +88,6 @@
 	seq_printf(m, "state: \t%s\n"
 		   "mount options: \t%s%s%s%s%s%s%s%s%s%s%s\n"
 		   "gen. counter: \t%i\n"
-		   "s_kmallocs: \t%i\n"
 		   "s_disk_reads: \t%i\n"
 		   "s_disk_writes: \t%i\n"
 		   "s_fix_nodes: \t%i\n"
@@ -128,7 +127,7 @@
 		   "SMALL_TAILS " : "NO_TAILS ",
 		   replay_only(sb) ? "REPLAY_ONLY " : "",
 		   convert_reiserfs(sb) ? "CONV " : "",
-		   atomic_read(&r->s_generation_counter), SF(s_kmallocs),
+		   atomic_read(&r->s_generation_counter),
 		   SF(s_disk_reads), SF(s_disk_writes), SF(s_fix_nodes),
 		   SF(s_do_balance), SF(s_unneeded_left_neighbor),
 		   SF(s_good_search_by_key_reada), SF(s_bmaps),
diff -urN oldtree/fs/reiserfs/super.c newtree/fs/reiserfs/super.c
--- oldtree/fs/reiserfs/super.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/reiserfs/super.c	2006-02-13 19:20:29.733992048 -0500
@@ -472,12 +472,6 @@
 
 	print_statistics(s);
 
-	if (REISERFS_SB(s)->s_kmallocs != 0) {
-		reiserfs_warning(s,
-				 "vs-2004: reiserfs_put_super: allocated memory left %d",
-				 REISERFS_SB(s)->s_kmallocs);
-	}
-
 	if (REISERFS_SB(s)->reserved_blocks != 0) {
 		reiserfs_warning(s,
 				 "green-2005: reiserfs_put_super: reserved blocks left %d",
@@ -1131,7 +1125,7 @@
 			REISERFS_SB(s)->s_mount_opt &= ~(1 << REISERFS_ATTRS);
 		}
 	} else if (le32_to_cpu(rs->s_flags) & reiserfs_attrs_cleared) {
-		REISERFS_SB(s)->s_mount_opt |= REISERFS_ATTRS;
+		REISERFS_SB(s)->s_mount_opt |= (1 << REISERFS_ATTRS);
 	}
 }
 
diff -urN oldtree/fs/reiserfs/super.c.orig newtree/fs/reiserfs/super.c.orig
--- oldtree/fs/reiserfs/super.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/reiserfs/super.c.orig	2006-02-13 19:20:00.729401416 -0500
@@ -0,0 +1,2313 @@
+/*
+ * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README
+ *
+ * Trivial changes by Alan Cox to add the LFS fixes
+ *
+ * Trivial Changes:
+ * Rights granted to Hans Reiser to redistribute under other terms providing
+ * he accepts all liability including but not limited to patent, fitness
+ * for purpose, and direct or indirect claims arising from failure to perform.
+ *
+ * NO WARRANTY
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/time.h>
+#include <asm/uaccess.h>
+#include <linux/reiserfs_fs.h>
+#include <linux/reiserfs_acl.h>
+#include <linux/reiserfs_xattr.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/buffer_head.h>
+#include <linux/vfs.h>
+#include <linux/namespace.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/quotaops.h>
+
+struct file_system_type reiserfs_fs_type;
+
+static const char reiserfs_3_5_magic_string[] = REISERFS_SUPER_MAGIC_STRING;
+static const char reiserfs_3_6_magic_string[] = REISER2FS_SUPER_MAGIC_STRING;
+static const char reiserfs_jr_magic_string[] = REISER2FS_JR_SUPER_MAGIC_STRING;
+
+int is_reiserfs_3_5(struct reiserfs_super_block *rs)
+{
+	return !strncmp(rs->s_v1.s_magic, reiserfs_3_5_magic_string,
+			strlen(reiserfs_3_5_magic_string));
+}
+
+int is_reiserfs_3_6(struct reiserfs_super_block *rs)
+{
+	return !strncmp(rs->s_v1.s_magic, reiserfs_3_6_magic_string,
+			strlen(reiserfs_3_6_magic_string));
+}
+
+int is_reiserfs_jr(struct reiserfs_super_block *rs)
+{
+	return !strncmp(rs->s_v1.s_magic, reiserfs_jr_magic_string,
+			strlen(reiserfs_jr_magic_string));
+}
+
+static int is_any_reiserfs_magic_string(struct reiserfs_super_block *rs)
+{
+	return (is_reiserfs_3_5(rs) || is_reiserfs_3_6(rs) ||
+		is_reiserfs_jr(rs));
+}
+
+static int reiserfs_remount(struct super_block *s, int *flags, char *data);
+static int reiserfs_statfs(struct super_block *s, struct kstatfs *buf);
+
+static int reiserfs_sync_fs(struct super_block *s, int wait)
+{
+	if (!(s->s_flags & MS_RDONLY)) {
+		struct reiserfs_transaction_handle th;
+		reiserfs_write_lock(s);
+		if (!journal_begin(&th, s, 1))
+			if (!journal_end_sync(&th, s, 1))
+				reiserfs_flush_old_commits(s);
+		s->s_dirt = 0;	/* Even if it's not true.
+				 * We'll loop forever in sync_supers otherwise */
+		reiserfs_write_unlock(s);
+	} else {
+		s->s_dirt = 0;
+	}
+	return 0;
+}
+
+static void reiserfs_write_super(struct super_block *s)
+{
+	reiserfs_sync_fs(s, 1);
+}
+
+static void reiserfs_write_super_lockfs(struct super_block *s)
+{
+	struct reiserfs_transaction_handle th;
+	reiserfs_write_lock(s);
+	if (!(s->s_flags & MS_RDONLY)) {
+		int err = journal_begin(&th, s, 1);
+		if (err) {
+			reiserfs_block_writes(&th);
+		} else {
+			reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s),
+						     1);
+			journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB(s));
+			reiserfs_block_writes(&th);
+			journal_end_sync(&th, s, 1);
+		}
+	}
+	s->s_dirt = 0;
+	reiserfs_write_unlock(s);
+}
+
+static void reiserfs_unlockfs(struct super_block *s)
+{
+	reiserfs_allow_writes(s);
+}
+
+extern const struct in_core_key MAX_IN_CORE_KEY;
+
+/* this is used to delete "save link" when there are no items of a
+   file it points to. It can either happen if unlink is completed but
+   "save unlink" removal, or if file has both unlink and truncate
+   pending and as unlink completes first (because key of "save link"
+   protecting unlink is bigger that a key lf "save link" which
+   protects truncate), so there left no items to make truncate
+   completion on */
+static int remove_save_link_only(struct super_block *s,
+				 struct reiserfs_key *key, int oid_free)
+{
+	struct reiserfs_transaction_handle th;
+	int err;
+
+	/* we are going to do one balancing */
+	err = journal_begin(&th, s, JOURNAL_PER_BALANCE_CNT);
+	if (err)
+		return err;
+
+	reiserfs_delete_solid_item(&th, NULL, key);
+	if (oid_free)
+		/* removals are protected by direct items */
+		reiserfs_release_objectid(&th, le32_to_cpu(key->k_objectid));
+
+	return journal_end(&th, s, JOURNAL_PER_BALANCE_CNT);
+}
+
+#ifdef CONFIG_QUOTA
+static int reiserfs_quota_on_mount(struct super_block *, int);
+#endif
+
+/* look for uncompleted unlinks and truncates and complete them */
+static int finish_unfinished(struct super_block *s)
+{
+	INITIALIZE_PATH(path);
+	struct cpu_key max_cpu_key, obj_key;
+	struct reiserfs_key save_link_key;
+	int retval = 0;
+	struct item_head *ih;
+	struct buffer_head *bh;
+	int item_pos;
+	char *item;
+	int done;
+	struct inode *inode;
+	int truncate;
+#ifdef CONFIG_QUOTA
+	int i;
+	int ms_active_set;
+#endif
+
+	/* compose key to look for "save" links */
+	max_cpu_key.version = KEY_FORMAT_3_5;
+	max_cpu_key.on_disk_key.k_dir_id = ~0U;
+	max_cpu_key.on_disk_key.k_objectid = ~0U;
+	set_cpu_key_k_offset(&max_cpu_key, ~0U);
+	max_cpu_key.key_length = 3;
+
+#ifdef CONFIG_QUOTA
+	/* Needed for iput() to work correctly and not trash data */
+	if (s->s_flags & MS_ACTIVE) {
+		ms_active_set = 0;
+	} else {
+		ms_active_set = 1;
+		s->s_flags |= MS_ACTIVE;
+	}
+	/* Turn on quotas so that they are updated correctly */
+	for (i = 0; i < MAXQUOTAS; i++) {
+		if (REISERFS_SB(s)->s_qf_names[i]) {
+			int ret = reiserfs_quota_on_mount(s, i);
+			if (ret < 0)
+				reiserfs_warning(s,
+						 "reiserfs: cannot turn on journalled quota: error %d",
+						 ret);
+		}
+	}
+#endif
+
+	done = 0;
+	REISERFS_SB(s)->s_is_unlinked_ok = 1;
+	while (!retval) {
+		retval = search_item(s, &max_cpu_key, &path);
+		if (retval != ITEM_NOT_FOUND) {
+			reiserfs_warning(s,
+					 "vs-2140: finish_unfinished: search_by_key returned %d",
+					 retval);
+			break;
+		}
+
+		bh = get_last_bh(&path);
+		item_pos = get_item_pos(&path);
+		if (item_pos != B_NR_ITEMS(bh)) {
+			reiserfs_warning(s,
+					 "vs-2060: finish_unfinished: wrong position found");
+			break;
+		}
+		item_pos--;
+		ih = B_N_PITEM_HEAD(bh, item_pos);
+
+		if (le32_to_cpu(ih->ih_key.k_dir_id) != MAX_KEY_OBJECTID)
+			/* there are no "save" links anymore */
+			break;
+
+		save_link_key = ih->ih_key;
+		if (is_indirect_le_ih(ih))
+			truncate = 1;
+		else
+			truncate = 0;
+
+		/* reiserfs_iget needs k_dirid and k_objectid only */
+		item = B_I_PITEM(bh, ih);
+		obj_key.on_disk_key.k_dir_id = le32_to_cpu(*(__le32 *) item);
+		obj_key.on_disk_key.k_objectid =
+		    le32_to_cpu(ih->ih_key.k_objectid);
+		obj_key.on_disk_key.k_offset = 0;
+		obj_key.on_disk_key.k_type = 0;
+
+		pathrelse(&path);
+
+		inode = reiserfs_iget(s, &obj_key);
+		if (!inode) {
+			/* the unlink almost completed, it just did not manage to remove
+			   "save" link and release objectid */
+			reiserfs_warning(s,
+					 "vs-2180: finish_unfinished: iget failed for %K",
+					 &obj_key);
+			retval = remove_save_link_only(s, &save_link_key, 1);
+			continue;
+		}
+
+		if (!truncate && inode->i_nlink) {
+			/* file is not unlinked */
+			reiserfs_warning(s,
+					 "vs-2185: finish_unfinished: file %K is not unlinked",
+					 &obj_key);
+			retval = remove_save_link_only(s, &save_link_key, 0);
+			continue;
+		}
+		DQUOT_INIT(inode);
+
+		if (truncate && S_ISDIR(inode->i_mode)) {
+			/* We got a truncate request for a dir which is impossible.
+			   The only imaginable way is to execute unfinished truncate request
+			   then boot into old kernel, remove the file and create dir with
+			   the same key. */
+			reiserfs_warning(s,
+					 "green-2101: impossible truncate on a directory %k. Please report",
+					 INODE_PKEY(inode));
+			retval = remove_save_link_only(s, &save_link_key, 0);
+			truncate = 0;
+			iput(inode);
+			continue;
+		}
+
+		if (truncate) {
+			REISERFS_I(inode)->i_flags |=
+			    i_link_saved_truncate_mask;
+			/* not completed truncate found. New size was committed together
+			   with "save" link */
+			reiserfs_info(s, "Truncating %k to %Ld ..",
+				      INODE_PKEY(inode), inode->i_size);
+			reiserfs_truncate_file(inode,
+					       0
+					       /*don't update modification time */
+					       );
+			retval = remove_save_link(inode, truncate);
+		} else {
+			REISERFS_I(inode)->i_flags |= i_link_saved_unlink_mask;
+			/* not completed unlink (rmdir) found */
+			reiserfs_info(s, "Removing %k..", INODE_PKEY(inode));
+			/* removal gets completed in iput */
+			retval = 0;
+		}
+
+		iput(inode);
+		printk("done\n");
+		done++;
+	}
+	REISERFS_SB(s)->s_is_unlinked_ok = 0;
+
+#ifdef CONFIG_QUOTA
+	/* Turn quotas off */
+	for (i = 0; i < MAXQUOTAS; i++) {
+		if (sb_dqopt(s)->files[i])
+			vfs_quota_off_mount(s, i);
+	}
+	if (ms_active_set)
+		/* Restore the flag back */
+		s->s_flags &= ~MS_ACTIVE;
+#endif
+	pathrelse(&path);
+	if (done)
+		reiserfs_info(s, "There were %d uncompleted unlinks/truncates. "
+			      "Completed\n", done);
+	return retval;
+}
+
+/* to protect file being unlinked from getting lost we "safe" link files
+   being unlinked. This link will be deleted in the same transaction with last
+   item of file. mounting the filesytem we scan all these links and remove
+   files which almost got lost */
+void add_save_link(struct reiserfs_transaction_handle *th,
+		   struct inode *inode, int truncate)
+{
+	INITIALIZE_PATH(path);
+	int retval;
+	struct cpu_key key;
+	struct item_head ih;
+	__le32 link;
+
+	BUG_ON(!th->t_trans_id);
+
+	/* file can only get one "save link" of each kind */
+	RFALSE(truncate &&
+	       (REISERFS_I(inode)->i_flags & i_link_saved_truncate_mask),
+	       "saved link already exists for truncated inode %lx",
+	       (long)inode->i_ino);
+	RFALSE(!truncate &&
+	       (REISERFS_I(inode)->i_flags & i_link_saved_unlink_mask),
+	       "saved link already exists for unlinked inode %lx",
+	       (long)inode->i_ino);
+
+	/* setup key of "save" link */
+	key.version = KEY_FORMAT_3_5;
+	key.on_disk_key.k_dir_id = MAX_KEY_OBJECTID;
+	key.on_disk_key.k_objectid = inode->i_ino;
+	if (!truncate) {
+		/* unlink, rmdir, rename */
+		set_cpu_key_k_offset(&key, 1 + inode->i_sb->s_blocksize);
+		set_cpu_key_k_type(&key, TYPE_DIRECT);
+
+		/* item head of "safe" link */
+		make_le_item_head(&ih, &key, key.version,
+				  1 + inode->i_sb->s_blocksize, TYPE_DIRECT,
+				  4 /*length */ , 0xffff /*free space */ );
+	} else {
+		/* truncate */
+		if (S_ISDIR(inode->i_mode))
+			reiserfs_warning(inode->i_sb,
+					 "green-2102: Adding a truncate savelink for a directory %k! Please report",
+					 INODE_PKEY(inode));
+		set_cpu_key_k_offset(&key, 1);
+		set_cpu_key_k_type(&key, TYPE_INDIRECT);
+
+		/* item head of "safe" link */
+		make_le_item_head(&ih, &key, key.version, 1, TYPE_INDIRECT,
+				  4 /*length */ , 0 /*free space */ );
+	}
+	key.key_length = 3;
+
+	/* look for its place in the tree */
+	retval = search_item(inode->i_sb, &key, &path);
+	if (retval != ITEM_NOT_FOUND) {
+		if (retval != -ENOSPC)
+			reiserfs_warning(inode->i_sb, "vs-2100: add_save_link:"
+					 "search_by_key (%K) returned %d", &key,
+					 retval);
+		pathrelse(&path);
+		return;
+	}
+
+	/* body of "save" link */
+	link = INODE_PKEY(inode)->k_dir_id;
+
+	/* put "save" link inot tree, don't charge quota to anyone */
+	retval =
+	    reiserfs_insert_item(th, &path, &key, &ih, NULL, (char *)&link);
+	if (retval) {
+		if (retval != -ENOSPC)
+			reiserfs_warning(inode->i_sb,
+					 "vs-2120: add_save_link: insert_item returned %d",
+					 retval);
+	} else {
+		if (truncate)
+			REISERFS_I(inode)->i_flags |=
+			    i_link_saved_truncate_mask;
+		else
+			REISERFS_I(inode)->i_flags |= i_link_saved_unlink_mask;
+	}
+}
+
+/* this opens transaction unlike add_save_link */
+int remove_save_link(struct inode *inode, int truncate)
+{
+	struct reiserfs_transaction_handle th;
+	struct reiserfs_key key;
+	int err;
+
+	/* we are going to do one balancing only */
+	err = journal_begin(&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT);
+	if (err)
+		return err;
+
+	/* setup key of "save" link */
+	key.k_dir_id = cpu_to_le32(MAX_KEY_OBJECTID);
+	key.k_objectid = INODE_PKEY(inode)->k_objectid;
+	if (!truncate) {
+		/* unlink, rmdir, rename */
+		set_le_key_k_offset(KEY_FORMAT_3_5, &key,
+				    1 + inode->i_sb->s_blocksize);
+		set_le_key_k_type(KEY_FORMAT_3_5, &key, TYPE_DIRECT);
+	} else {
+		/* truncate */
+		set_le_key_k_offset(KEY_FORMAT_3_5, &key, 1);
+		set_le_key_k_type(KEY_FORMAT_3_5, &key, TYPE_INDIRECT);
+	}
+
+	if ((truncate &&
+	     (REISERFS_I(inode)->i_flags & i_link_saved_truncate_mask)) ||
+	    (!truncate &&
+	     (REISERFS_I(inode)->i_flags & i_link_saved_unlink_mask)))
+		/* don't take quota bytes from anywhere */
+		reiserfs_delete_solid_item(&th, NULL, &key);
+	if (!truncate) {
+		reiserfs_release_objectid(&th, inode->i_ino);
+		REISERFS_I(inode)->i_flags &= ~i_link_saved_unlink_mask;
+	} else
+		REISERFS_I(inode)->i_flags &= ~i_link_saved_truncate_mask;
+
+	return journal_end(&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT);
+}
+
+static void reiserfs_put_super(struct super_block *s)
+{
+	int i;
+	struct reiserfs_transaction_handle th;
+	th.t_trans_id = 0;
+
+	if (REISERFS_SB(s)->xattr_root) {
+		d_invalidate(REISERFS_SB(s)->xattr_root);
+		dput(REISERFS_SB(s)->xattr_root);
+	}
+
+	if (REISERFS_SB(s)->priv_root) {
+		d_invalidate(REISERFS_SB(s)->priv_root);
+		dput(REISERFS_SB(s)->priv_root);
+	}
+
+	/* change file system state to current state if it was mounted with read-write permissions */
+	if (!(s->s_flags & MS_RDONLY)) {
+		if (!journal_begin(&th, s, 10)) {
+			reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s),
+						     1);
+			set_sb_umount_state(SB_DISK_SUPER_BLOCK(s),
+					    REISERFS_SB(s)->s_mount_state);
+			journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB(s));
+		}
+	}
+
+	/* note, journal_release checks for readonly mount, and can decide not
+	 ** to do a journal_end
+	 */
+	journal_release(&th, s);
+
+	for (i = 0; i < SB_BMAP_NR(s); i++)
+		brelse(SB_AP_BITMAP(s)[i].bh);
+
+	vfree(SB_AP_BITMAP(s));
+
+	brelse(SB_BUFFER_WITH_SB(s));
+
+	print_statistics(s);
+
+	if (REISERFS_SB(s)->reserved_blocks != 0) {
+		reiserfs_warning(s,
+				 "green-2005: reiserfs_put_super: reserved blocks left %d",
+				 REISERFS_SB(s)->reserved_blocks);
+	}
+
+	reiserfs_proc_info_done(s);
+
+	kfree(s->s_fs_info);
+	s->s_fs_info = NULL;
+
+	return;
+}
+
+static kmem_cache_t *reiserfs_inode_cachep;
+
+static struct inode *reiserfs_alloc_inode(struct super_block *sb)
+{
+	struct reiserfs_inode_info *ei;
+	ei = (struct reiserfs_inode_info *)
+	    kmem_cache_alloc(reiserfs_inode_cachep, SLAB_KERNEL);
+	if (!ei)
+		return NULL;
+	return &ei->vfs_inode;
+}
+
+static void reiserfs_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(reiserfs_inode_cachep, REISERFS_I(inode));
+}
+
+static void init_once(void *foo, kmem_cache_t * cachep, unsigned long flags)
+{
+	struct reiserfs_inode_info *ei = (struct reiserfs_inode_info *)foo;
+
+	if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR) {
+		INIT_LIST_HEAD(&ei->i_prealloc_list);
+		inode_init_once(&ei->vfs_inode);
+		ei->i_acl_access = NULL;
+		ei->i_acl_default = NULL;
+	}
+}
+
+static int init_inodecache(void)
+{
+	reiserfs_inode_cachep = kmem_cache_create("reiser_inode_cache",
+						  sizeof(struct
+							 reiserfs_inode_info),
+						  0, SLAB_RECLAIM_ACCOUNT,
+						  init_once, NULL);
+	if (reiserfs_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static void destroy_inodecache(void)
+{
+	if (kmem_cache_destroy(reiserfs_inode_cachep))
+		reiserfs_warning(NULL,
+				 "reiserfs_inode_cache: not all structures were freed");
+}
+
+/* we don't mark inodes dirty, we just log them */
+static void reiserfs_dirty_inode(struct inode *inode)
+{
+	struct reiserfs_transaction_handle th;
+
+	int err = 0;
+	if (inode->i_sb->s_flags & MS_RDONLY) {
+		reiserfs_warning(inode->i_sb,
+				 "clm-6006: writing inode %lu on readonly FS",
+				 inode->i_ino);
+		return;
+	}
+	reiserfs_write_lock(inode->i_sb);
+
+	/* this is really only used for atime updates, so they don't have
+	 ** to be included in O_SYNC or fsync
+	 */
+	err = journal_begin(&th, inode->i_sb, 1);
+	if (err) {
+		reiserfs_write_unlock(inode->i_sb);
+		return;
+	}
+	reiserfs_update_sd(&th, inode);
+	journal_end(&th, inode->i_sb, 1);
+	reiserfs_write_unlock(inode->i_sb);
+}
+
+static void reiserfs_clear_inode(struct inode *inode)
+{
+	struct posix_acl *acl;
+
+	acl = REISERFS_I(inode)->i_acl_access;
+	if (acl && !IS_ERR(acl))
+		posix_acl_release(acl);
+	REISERFS_I(inode)->i_acl_access = NULL;
+
+	acl = REISERFS_I(inode)->i_acl_default;
+	if (acl && !IS_ERR(acl))
+		posix_acl_release(acl);
+	REISERFS_I(inode)->i_acl_default = NULL;
+}
+
+#ifdef CONFIG_QUOTA
+static ssize_t reiserfs_quota_write(struct super_block *, int, const char *,
+				    size_t, loff_t);
+static ssize_t reiserfs_quota_read(struct super_block *, int, char *, size_t,
+				   loff_t);
+#endif
+
+static struct super_operations reiserfs_sops = {
+	.alloc_inode = reiserfs_alloc_inode,
+	.destroy_inode = reiserfs_destroy_inode,
+	.write_inode = reiserfs_write_inode,
+	.dirty_inode = reiserfs_dirty_inode,
+	.delete_inode = reiserfs_delete_inode,
+	.clear_inode = reiserfs_clear_inode,
+	.put_super = reiserfs_put_super,
+	.write_super = reiserfs_write_super,
+	.sync_fs = reiserfs_sync_fs,
+	.write_super_lockfs = reiserfs_write_super_lockfs,
+	.unlockfs = reiserfs_unlockfs,
+	.statfs = reiserfs_statfs,
+	.remount_fs = reiserfs_remount,
+#ifdef CONFIG_QUOTA
+	.quota_read = reiserfs_quota_read,
+	.quota_write = reiserfs_quota_write,
+#endif
+};
+
+#ifdef CONFIG_QUOTA
+#define QTYPE2NAME(t) ((t)==USRQUOTA?"user":"group")
+
+static int reiserfs_dquot_initialize(struct inode *, int);
+static int reiserfs_dquot_drop(struct inode *);
+static int reiserfs_write_dquot(struct dquot *);
+static int reiserfs_acquire_dquot(struct dquot *);
+static int reiserfs_release_dquot(struct dquot *);
+static int reiserfs_mark_dquot_dirty(struct dquot *);
+static int reiserfs_write_info(struct super_block *, int);
+static int reiserfs_quota_on(struct super_block *, int, int, char *);
+
+static struct dquot_operations reiserfs_quota_operations = {
+	.initialize = reiserfs_dquot_initialize,
+	.drop = reiserfs_dquot_drop,
+	.alloc_space = dquot_alloc_space,
+	.alloc_inode = dquot_alloc_inode,
+	.free_space = dquot_free_space,
+	.free_inode = dquot_free_inode,
+	.transfer = dquot_transfer,
+	.write_dquot = reiserfs_write_dquot,
+	.acquire_dquot = reiserfs_acquire_dquot,
+	.release_dquot = reiserfs_release_dquot,
+	.mark_dirty = reiserfs_mark_dquot_dirty,
+	.write_info = reiserfs_write_info,
+};
+
+static struct quotactl_ops reiserfs_qctl_operations = {
+	.quota_on = reiserfs_quota_on,
+	.quota_off = vfs_quota_off,
+	.quota_sync = vfs_quota_sync,
+	.get_info = vfs_get_dqinfo,
+	.set_info = vfs_set_dqinfo,
+	.get_dqblk = vfs_get_dqblk,
+	.set_dqblk = vfs_set_dqblk,
+};
+#endif
+
+static struct export_operations reiserfs_export_ops = {
+	.encode_fh = reiserfs_encode_fh,
+	.decode_fh = reiserfs_decode_fh,
+	.get_parent = reiserfs_get_parent,
+	.get_dentry = reiserfs_get_dentry,
+};
+
+/* this struct is used in reiserfs_getopt () for containing the value for those
+   mount options that have values rather than being toggles. */
+typedef struct {
+	char *value;
+	int setmask;		/* bitmask which is to set on mount_options bitmask when this
+				   value is found, 0 is no bits are to be changed. */
+	int clrmask;		/* bitmask which is to clear on mount_options bitmask when  this
+				   value is found, 0 is no bits are to be changed. This is
+				   applied BEFORE setmask */
+} arg_desc_t;
+
+/* Set this bit in arg_required to allow empty arguments */
+#define REISERFS_OPT_ALLOWEMPTY 31
+
+/* this struct is used in reiserfs_getopt() for describing the set of reiserfs
+   mount options */
+typedef struct {
+	char *option_name;
+	int arg_required;	/* 0 if argument is not required, not 0 otherwise */
+	const arg_desc_t *values;	/* list of values accepted by an option */
+	int setmask;		/* bitmask which is to set on mount_options bitmask when this
+				   value is found, 0 is no bits are to be changed. */
+	int clrmask;		/* bitmask which is to clear on mount_options bitmask when  this
+				   value is found, 0 is no bits are to be changed. This is
+				   applied BEFORE setmask */
+} opt_desc_t;
+
+/* possible values for -o data= */
+static const arg_desc_t logging_mode[] = {
+	{"ordered", 1 << REISERFS_DATA_ORDERED,
+	 (1 << REISERFS_DATA_LOG | 1 << REISERFS_DATA_WRITEBACK)},
+	{"journal", 1 << REISERFS_DATA_LOG,
+	 (1 << REISERFS_DATA_ORDERED | 1 << REISERFS_DATA_WRITEBACK)},
+	{"writeback", 1 << REISERFS_DATA_WRITEBACK,
+	 (1 << REISERFS_DATA_ORDERED | 1 << REISERFS_DATA_LOG)},
+	{NULL, 0}
+};
+
+/* possible values for -o barrier= */
+static const arg_desc_t barrier_mode[] = {
+	{"none", 1 << REISERFS_BARRIER_NONE, 1 << REISERFS_BARRIER_FLUSH},
+	{"flush", 1 << REISERFS_BARRIER_FLUSH, 1 << REISERFS_BARRIER_NONE},
+	{NULL, 0}
+};
+
+/* possible values for "-o block-allocator=" and bits which are to be set in
+   s_mount_opt of reiserfs specific part of in-core super block */
+static const arg_desc_t balloc[] = {
+	{"noborder", 1 << REISERFS_NO_BORDER, 0},
+	{"border", 0, 1 << REISERFS_NO_BORDER},
+	{"no_unhashed_relocation", 1 << REISERFS_NO_UNHASHED_RELOCATION, 0},
+	{"hashed_relocation", 1 << REISERFS_HASHED_RELOCATION, 0},
+	{"test4", 1 << REISERFS_TEST4, 0},
+	{"notest4", 0, 1 << REISERFS_TEST4},
+	{NULL, 0, 0}
+};
+
+static const arg_desc_t tails[] = {
+	{"on", 1 << REISERFS_LARGETAIL, 1 << REISERFS_SMALLTAIL},
+	{"off", 0, (1 << REISERFS_LARGETAIL) | (1 << REISERFS_SMALLTAIL)},
+	{"small", 1 << REISERFS_SMALLTAIL, 1 << REISERFS_LARGETAIL},
+	{NULL, 0, 0}
+};
+
+static const arg_desc_t error_actions[] = {
+	{"panic", 1 << REISERFS_ERROR_PANIC,
+	 (1 << REISERFS_ERROR_RO | 1 << REISERFS_ERROR_CONTINUE)},
+	{"ro-remount", 1 << REISERFS_ERROR_RO,
+	 (1 << REISERFS_ERROR_PANIC | 1 << REISERFS_ERROR_CONTINUE)},
+#ifdef REISERFS_JOURNAL_ERROR_ALLOWS_NO_LOG
+	{"continue", 1 << REISERFS_ERROR_CONTINUE,
+	 (1 << REISERFS_ERROR_PANIC | 1 << REISERFS_ERROR_RO)},
+#endif
+	{NULL, 0, 0},
+};
+
+int reiserfs_default_io_size = 128 * 1024;	/* Default recommended I/O size is 128k.
+						   There might be broken applications that are
+						   confused by this. Use nolargeio mount option
+						   to get usual i/o size = PAGE_SIZE.
+						 */
+
+/* proceed only one option from a list *cur - string containing of mount options
+   opts - array of options which are accepted
+   opt_arg - if option is found and requires an argument and if it is specifed
+   in the input - pointer to the argument is stored here
+   bit_flags - if option requires to set a certain bit - it is set here
+   return -1 if unknown option is found, opt->arg_required otherwise */
+static int reiserfs_getopt(struct super_block *s, char **cur, opt_desc_t * opts,
+			   char **opt_arg, unsigned long *bit_flags)
+{
+	char *p;
+	/* foo=bar, 
+	   ^   ^  ^
+	   |   |  +-- option_end
+	   |   +-- arg_start
+	   +-- option_start
+	 */
+	const opt_desc_t *opt;
+	const arg_desc_t *arg;
+
+	p = *cur;
+
+	/* assume argument cannot contain commas */
+	*cur = strchr(p, ',');
+	if (*cur) {
+		*(*cur) = '\0';
+		(*cur)++;
+	}
+
+	if (!strncmp(p, "alloc=", 6)) {
+		/* Ugly special case, probably we should redo options parser so that
+		   it can understand several arguments for some options, also so that
+		   it can fill several bitfields with option values. */
+		if (reiserfs_parse_alloc_options(s, p + 6)) {
+			return -1;
+		} else {
+			return 0;
+		}
+	}
+
+	/* for every option in the list */
+	for (opt = opts; opt->option_name; opt++) {
+		if (!strncmp(p, opt->option_name, strlen(opt->option_name))) {
+			if (bit_flags) {
+				if (opt->clrmask ==
+				    (1 << REISERFS_UNSUPPORTED_OPT))
+					reiserfs_warning(s, "%s not supported.",
+							 p);
+				else
+					*bit_flags &= ~opt->clrmask;
+				if (opt->setmask ==
+				    (1 << REISERFS_UNSUPPORTED_OPT))
+					reiserfs_warning(s, "%s not supported.",
+							 p);
+				else
+					*bit_flags |= opt->setmask;
+			}
+			break;
+		}
+	}
+	if (!opt->option_name) {
+		reiserfs_warning(s, "unknown mount option \"%s\"", p);
+		return -1;
+	}
+
+	p += strlen(opt->option_name);
+	switch (*p) {
+	case '=':
+		if (!opt->arg_required) {
+			reiserfs_warning(s,
+					 "the option \"%s\" does not require an argument",
+					 opt->option_name);
+			return -1;
+		}
+		break;
+
+	case 0:
+		if (opt->arg_required) {
+			reiserfs_warning(s,
+					 "the option \"%s\" requires an argument",
+					 opt->option_name);
+			return -1;
+		}
+		break;
+	default:
+		reiserfs_warning(s, "head of option \"%s\" is only correct",
+				 opt->option_name);
+		return -1;
+	}
+
+	/* move to the argument, or to next option if argument is not required */
+	p++;
+
+	if (opt->arg_required
+	    && !(opt->arg_required & (1 << REISERFS_OPT_ALLOWEMPTY))
+	    && !strlen(p)) {
+		/* this catches "option=," if not allowed */
+		reiserfs_warning(s, "empty argument for \"%s\"",
+				 opt->option_name);
+		return -1;
+	}
+
+	if (!opt->values) {
+		/* *=NULLopt_arg contains pointer to argument */
+		*opt_arg = p;
+		return opt->arg_required & ~(1 << REISERFS_OPT_ALLOWEMPTY);
+	}
+
+	/* values possible for this option are listed in opt->values */
+	for (arg = opt->values; arg->value; arg++) {
+		if (!strcmp(p, arg->value)) {
+			if (bit_flags) {
+				*bit_flags &= ~arg->clrmask;
+				*bit_flags |= arg->setmask;
+			}
+			return opt->arg_required;
+		}
+	}
+
+	reiserfs_warning(s, "bad value \"%s\" for option \"%s\"", p,
+			 opt->option_name);
+	return -1;
+}
+
+/* returns 0 if something is wrong in option string, 1 - otherwise */
+static int reiserfs_parse_options(struct super_block *s, char *options,	/* string given via mount's -o */
+				  unsigned long *mount_options,
+				  /* after the parsing phase, contains the
+				     collection of bitflags defining what
+				     mount options were selected. */
+				  unsigned long *blocks,	/* strtol-ed from NNN of resize=NNN */
+				  char **jdev_name,
+				  unsigned int *commit_max_age)
+{
+	int c;
+	char *arg = NULL;
+	char *pos;
+	opt_desc_t opts[] = {
+		/* Compatibility stuff, so that -o notail for old setups still work */
+		{"tails",.arg_required = 't',.values = tails},
+		{"notail",.clrmask =
+		 (1 << REISERFS_LARGETAIL) | (1 << REISERFS_SMALLTAIL)},
+		{"conv",.setmask = 1 << REISERFS_CONVERT},
+		{"attrs",.setmask = 1 << REISERFS_ATTRS},
+		{"noattrs",.clrmask = 1 << REISERFS_ATTRS},
+#ifdef CONFIG_REISERFS_FS_XATTR
+		{"user_xattr",.setmask = 1 << REISERFS_XATTRS_USER},
+		{"nouser_xattr",.clrmask = 1 << REISERFS_XATTRS_USER},
+#else
+		{"user_xattr",.setmask = 1 << REISERFS_UNSUPPORTED_OPT},
+		{"nouser_xattr",.clrmask = 1 << REISERFS_UNSUPPORTED_OPT},
+#endif
+#ifdef CONFIG_REISERFS_FS_POSIX_ACL
+		{"acl",.setmask = 1 << REISERFS_POSIXACL},
+		{"noacl",.clrmask = 1 << REISERFS_POSIXACL},
+#else
+		{"acl",.setmask = 1 << REISERFS_UNSUPPORTED_OPT},
+		{"noacl",.clrmask = 1 << REISERFS_UNSUPPORTED_OPT},
+#endif
+		{"nolog",},	/* This is unsupported */
+		{"replayonly",.setmask = 1 << REPLAYONLY},
+		{"block-allocator",.arg_required = 'a',.values = balloc},
+		{"data",.arg_required = 'd',.values = logging_mode},
+		{"barrier",.arg_required = 'b',.values = barrier_mode},
+		{"resize",.arg_required = 'r',.values = NULL},
+		{"jdev",.arg_required = 'j',.values = NULL},
+		{"nolargeio",.arg_required = 'w',.values = NULL},
+		{"commit",.arg_required = 'c',.values = NULL},
+		{"usrquota",.setmask = 1 << REISERFS_QUOTA},
+		{"grpquota",.setmask = 1 << REISERFS_QUOTA},
+		{"noquota",.clrmask = 1 << REISERFS_QUOTA},
+		{"errors",.arg_required = 'e',.values = error_actions},
+		{"usrjquota",.arg_required =
+		 'u' | (1 << REISERFS_OPT_ALLOWEMPTY),.values = NULL},
+		{"grpjquota",.arg_required =
+		 'g' | (1 << REISERFS_OPT_ALLOWEMPTY),.values = NULL},
+		{"jqfmt",.arg_required = 'f',.values = NULL},
+		{NULL,}
+	};
+
+	*blocks = 0;
+	if (!options || !*options)
+		/* use default configuration: create tails, journaling on, no
+		   conversion to newest format */
+		return 1;
+
+	for (pos = options; pos;) {
+		c = reiserfs_getopt(s, &pos, opts, &arg, mount_options);
+		if (c == -1)
+			/* wrong option is given */
+			return 0;
+
+		if (c == 'r') {
+			char *p;
+
+			p = NULL;
+			/* "resize=NNN" or "resize=auto" */
+
+			if (!strcmp(arg, "auto")) {
+				/* From JFS code, to auto-get the size. */
+				*blocks =
+				    s->s_bdev->bd_inode->i_size >> s->
+				    s_blocksize_bits;
+			} else {
+				*blocks = simple_strtoul(arg, &p, 0);
+				if (*p != '\0') {
+					/* NNN does not look like a number */
+					reiserfs_warning(s,
+							 "reiserfs_parse_options: bad value %s",
+							 arg);
+					return 0;
+				}
+			}
+		}
+
+		if (c == 'c') {
+			char *p = NULL;
+			unsigned long val = simple_strtoul(arg, &p, 0);
+			/* commit=NNN (time in seconds) */
+			if (*p != '\0' || val >= (unsigned int)-1) {
+				reiserfs_warning(s,
+						 "reiserfs_parse_options: bad value %s",
+						 arg);
+				return 0;
+			}
+			*commit_max_age = (unsigned int)val;
+		}
+
+		if (c == 'w') {
+			char *p = NULL;
+			int val = simple_strtoul(arg, &p, 0);
+
+			if (*p != '\0') {
+				reiserfs_warning(s,
+						 "reiserfs_parse_options: non-numeric value %s for nolargeio option",
+						 arg);
+				return 0;
+			}
+			if (val)
+				reiserfs_default_io_size = PAGE_SIZE;
+			else
+				reiserfs_default_io_size = 128 * 1024;
+		}
+
+		if (c == 'j') {
+			if (arg && *arg && jdev_name) {
+				if (*jdev_name) {	//Hm, already assigned?
+					reiserfs_warning(s,
+							 "reiserfs_parse_options: journal device was already  specified to be %s",
+							 *jdev_name);
+					return 0;
+				}
+				*jdev_name = arg;
+			}
+		}
+#ifdef CONFIG_QUOTA
+		if (c == 'u' || c == 'g') {
+			int qtype = c == 'u' ? USRQUOTA : GRPQUOTA;
+
+			if (sb_any_quota_enabled(s)) {
+				reiserfs_warning(s,
+						 "reiserfs_parse_options: cannot change journalled quota options when quota turned on.");
+				return 0;
+			}
+			if (*arg) {	/* Some filename specified? */
+				if (REISERFS_SB(s)->s_qf_names[qtype]
+				    && strcmp(REISERFS_SB(s)->s_qf_names[qtype],
+					      arg)) {
+					reiserfs_warning(s,
+							 "reiserfs_parse_options: %s quota file already specified.",
+							 QTYPE2NAME(qtype));
+					return 0;
+				}
+				if (strchr(arg, '/')) {
+					reiserfs_warning(s,
+							 "reiserfs_parse_options: quotafile must be on filesystem root.");
+					return 0;
+				}
+				REISERFS_SB(s)->s_qf_names[qtype] =
+				    kmalloc(strlen(arg) + 1, GFP_KERNEL);
+				if (!REISERFS_SB(s)->s_qf_names[qtype]) {
+					reiserfs_warning(s,
+							 "reiserfs_parse_options: not enough memory for storing quotafile name.");
+					return 0;
+				}
+				strcpy(REISERFS_SB(s)->s_qf_names[qtype], arg);
+				*mount_options |= 1 << REISERFS_QUOTA;
+			} else {
+				kfree(REISERFS_SB(s)->s_qf_names[qtype]);
+				REISERFS_SB(s)->s_qf_names[qtype] = NULL;
+			}
+		}
+		if (c == 'f') {
+			if (!strcmp(arg, "vfsold"))
+				REISERFS_SB(s)->s_jquota_fmt = QFMT_VFS_OLD;
+			else if (!strcmp(arg, "vfsv0"))
+				REISERFS_SB(s)->s_jquota_fmt = QFMT_VFS_V0;
+			else {
+				reiserfs_warning(s,
+						 "reiserfs_parse_options: unknown quota format specified.");
+				return 0;
+			}
+		}
+#else
+		if (c == 'u' || c == 'g' || c == 'f') {
+			reiserfs_warning(s,
+					 "reiserfs_parse_options: journalled quota options not supported.");
+			return 0;
+		}
+#endif
+	}
+
+#ifdef CONFIG_QUOTA
+	if (!REISERFS_SB(s)->s_jquota_fmt
+	    && (REISERFS_SB(s)->s_qf_names[USRQUOTA]
+		|| REISERFS_SB(s)->s_qf_names[GRPQUOTA])) {
+		reiserfs_warning(s,
+				 "reiserfs_parse_options: journalled quota format not specified.");
+		return 0;
+	}
+	/* This checking is not precise wrt the quota type but for our purposes it is sufficient */
+	if (!(*mount_options & (1 << REISERFS_QUOTA))
+	    && sb_any_quota_enabled(s)) {
+		reiserfs_warning(s,
+				 "reiserfs_parse_options: quota options must be present when quota is turned on.");
+		return 0;
+	}
+#endif
+
+	return 1;
+}
+
+static void switch_data_mode(struct super_block *s, unsigned long mode)
+{
+	REISERFS_SB(s)->s_mount_opt &= ~((1 << REISERFS_DATA_LOG) |
+					 (1 << REISERFS_DATA_ORDERED) |
+					 (1 << REISERFS_DATA_WRITEBACK));
+	REISERFS_SB(s)->s_mount_opt |= (1 << mode);
+}
+
+static void handle_data_mode(struct super_block *s, unsigned long mount_options)
+{
+	if (mount_options & (1 << REISERFS_DATA_LOG)) {
+		if (!reiserfs_data_log(s)) {
+			switch_data_mode(s, REISERFS_DATA_LOG);
+			reiserfs_info(s, "switching to journaled data mode\n");
+		}
+	} else if (mount_options & (1 << REISERFS_DATA_ORDERED)) {
+		if (!reiserfs_data_ordered(s)) {
+			switch_data_mode(s, REISERFS_DATA_ORDERED);
+			reiserfs_info(s, "switching to ordered data mode\n");
+		}
+	} else if (mount_options & (1 << REISERFS_DATA_WRITEBACK)) {
+		if (!reiserfs_data_writeback(s)) {
+			switch_data_mode(s, REISERFS_DATA_WRITEBACK);
+			reiserfs_info(s, "switching to writeback data mode\n");
+		}
+	}
+}
+
+static void handle_barrier_mode(struct super_block *s, unsigned long bits)
+{
+	int flush = (1 << REISERFS_BARRIER_FLUSH);
+	int none = (1 << REISERFS_BARRIER_NONE);
+	int all_barrier = flush | none;
+
+	if (bits & all_barrier) {
+		REISERFS_SB(s)->s_mount_opt &= ~all_barrier;
+		if (bits & flush) {
+			REISERFS_SB(s)->s_mount_opt |= flush;
+			printk("reiserfs: enabling write barrier flush mode\n");
+		} else if (bits & none) {
+			REISERFS_SB(s)->s_mount_opt |= none;
+			printk("reiserfs: write barriers turned off\n");
+		}
+	}
+}
+
+static void handle_attrs(struct super_block *s)
+{
+	struct reiserfs_super_block *rs = SB_DISK_SUPER_BLOCK(s);
+
+	if (reiserfs_attrs(s)) {
+		if (old_format_only(s)) {
+			reiserfs_warning(s,
+					 "reiserfs: cannot support attributes on 3.5.x disk format");
+			REISERFS_SB(s)->s_mount_opt &= ~(1 << REISERFS_ATTRS);
+			return;
+		}
+		if (!(le32_to_cpu(rs->s_flags) & reiserfs_attrs_cleared)) {
+			reiserfs_warning(s,
+					 "reiserfs: cannot support attributes until flag is set in super-block");
+			REISERFS_SB(s)->s_mount_opt &= ~(1 << REISERFS_ATTRS);
+		}
+	} else if (le32_to_cpu(rs->s_flags) & reiserfs_attrs_cleared) {
+		REISERFS_SB(s)->s_mount_opt |= REISERFS_ATTRS;
+	}
+}
+
+static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg)
+{
+	struct reiserfs_super_block *rs;
+	struct reiserfs_transaction_handle th;
+	unsigned long blocks;
+	unsigned long mount_options = REISERFS_SB(s)->s_mount_opt;
+	unsigned long safe_mask = 0;
+	unsigned int commit_max_age = (unsigned int)-1;
+	struct reiserfs_journal *journal = SB_JOURNAL(s);
+	int err;
+#ifdef CONFIG_QUOTA
+	int i;
+#endif
+
+	rs = SB_DISK_SUPER_BLOCK(s);
+
+	if (!reiserfs_parse_options
+	    (s, arg, &mount_options, &blocks, NULL, &commit_max_age)) {
+#ifdef CONFIG_QUOTA
+		for (i = 0; i < MAXQUOTAS; i++) {
+			kfree(REISERFS_SB(s)->s_qf_names[i]);
+			REISERFS_SB(s)->s_qf_names[i] = NULL;
+		}
+#endif
+		return -EINVAL;
+	}
+
+	handle_attrs(s);
+
+	/* Add options that are safe here */
+	safe_mask |= 1 << REISERFS_SMALLTAIL;
+	safe_mask |= 1 << REISERFS_LARGETAIL;
+	safe_mask |= 1 << REISERFS_NO_BORDER;
+	safe_mask |= 1 << REISERFS_NO_UNHASHED_RELOCATION;
+	safe_mask |= 1 << REISERFS_HASHED_RELOCATION;
+	safe_mask |= 1 << REISERFS_TEST4;
+	safe_mask |= 1 << REISERFS_ATTRS;
+	safe_mask |= 1 << REISERFS_XATTRS_USER;
+	safe_mask |= 1 << REISERFS_POSIXACL;
+	safe_mask |= 1 << REISERFS_BARRIER_FLUSH;
+	safe_mask |= 1 << REISERFS_BARRIER_NONE;
+	safe_mask |= 1 << REISERFS_ERROR_RO;
+	safe_mask |= 1 << REISERFS_ERROR_CONTINUE;
+	safe_mask |= 1 << REISERFS_ERROR_PANIC;
+	safe_mask |= 1 << REISERFS_QUOTA;
+
+	/* Update the bitmask, taking care to keep
+	 * the bits we're not allowed to change here */
+	REISERFS_SB(s)->s_mount_opt =
+	    (REISERFS_SB(s)->
+	     s_mount_opt & ~safe_mask) | (mount_options & safe_mask);
+
+	if (commit_max_age != 0 && commit_max_age != (unsigned int)-1) {
+		journal->j_max_commit_age = commit_max_age;
+		journal->j_max_trans_age = commit_max_age;
+	} else if (commit_max_age == 0) {
+		/* 0 means restore defaults. */
+		journal->j_max_commit_age = journal->j_default_max_commit_age;
+		journal->j_max_trans_age = JOURNAL_MAX_TRANS_AGE;
+	}
+
+	if (blocks) {
+		int rc = reiserfs_resize(s, blocks);
+		if (rc != 0)
+			return rc;
+	}
+
+	if (*mount_flags & MS_RDONLY) {
+		reiserfs_xattr_init(s, *mount_flags);
+		/* remount read-only */
+		if (s->s_flags & MS_RDONLY)
+			/* it is read-only already */
+			return 0;
+		/* try to remount file system with read-only permissions */
+		if (sb_umount_state(rs) == REISERFS_VALID_FS
+		    || REISERFS_SB(s)->s_mount_state != REISERFS_VALID_FS) {
+			return 0;
+		}
+
+		err = journal_begin(&th, s, 10);
+		if (err)
+			return err;
+
+		/* Mounting a rw partition read-only. */
+		reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1);
+		set_sb_umount_state(rs, REISERFS_SB(s)->s_mount_state);
+		journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB(s));
+	} else {
+		/* remount read-write */
+		if (!(s->s_flags & MS_RDONLY)) {
+			reiserfs_xattr_init(s, *mount_flags);
+			return 0;	/* We are read-write already */
+		}
+
+		if (reiserfs_is_journal_aborted(journal))
+			return journal->j_errno;
+
+		handle_data_mode(s, mount_options);
+		handle_barrier_mode(s, mount_options);
+		REISERFS_SB(s)->s_mount_state = sb_umount_state(rs);
+		s->s_flags &= ~MS_RDONLY;	/* now it is safe to call journal_begin */
+		err = journal_begin(&th, s, 10);
+		if (err)
+			return err;
+
+		/* Mount a partition which is read-only, read-write */
+		reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1);
+		REISERFS_SB(s)->s_mount_state = sb_umount_state(rs);
+		s->s_flags &= ~MS_RDONLY;
+		set_sb_umount_state(rs, REISERFS_ERROR_FS);
+		/* mark_buffer_dirty (SB_BUFFER_WITH_SB (s), 1); */
+		journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB(s));
+		REISERFS_SB(s)->s_mount_state = REISERFS_VALID_FS;
+	}
+	/* this will force a full flush of all journal lists */
+	SB_JOURNAL(s)->j_must_wait = 1;
+	err = journal_end(&th, s, 10);
+	if (err)
+		return err;
+	s->s_dirt = 0;
+
+	if (!(*mount_flags & MS_RDONLY)) {
+		finish_unfinished(s);
+		reiserfs_xattr_init(s, *mount_flags);
+	}
+
+	return 0;
+}
+
+/* load_bitmap_info_data - Sets up the reiserfs_bitmap_info structure from disk.
+ * @sb - superblock for this filesystem
+ * @bi - the bitmap info to be loaded. Requires that bi->bh is valid.
+ *
+ * This routine counts how many free bits there are, finding the first zero
+ * as a side effect. Could also be implemented as a loop of test_bit() calls, or
+ * a loop of find_first_zero_bit() calls. This implementation is similar to
+ * find_first_zero_bit(), but doesn't return after it finds the first bit.
+ * Should only be called on fs mount, but should be fairly efficient anyways.
+ *
+ * bi->first_zero_hint is considered unset if it == 0, since the bitmap itself
+ * will * invariably occupt block 0 represented in the bitmap. The only
+ * exception to this is when free_count also == 0, since there will be no
+ * free blocks at all.
+ */
+
+static void load_bitmap_info_data(struct super_block *sb,
+				  struct reiserfs_bitmap_info *bi)
+{
+	unsigned long *cur = (unsigned long *)bi->bh->b_data;
+
+	while ((char *)cur < (bi->bh->b_data + sb->s_blocksize)) {
+
+		/* No need to scan if all 0's or all 1's.
+		 * Since we're only counting 0's, we can simply ignore all 1's */
+		if (*cur == 0) {
+			if (bi->first_zero_hint == 0) {
+				bi->first_zero_hint =
+				    ((char *)cur - bi->bh->b_data) << 3;
+			}
+			bi->free_count += sizeof(unsigned long) * 8;
+		} else if (*cur != ~0L) {
+			int b;
+			for (b = 0; b < sizeof(unsigned long) * 8; b++) {
+				if (!reiserfs_test_le_bit(b, cur)) {
+					bi->free_count++;
+					if (bi->first_zero_hint == 0)
+						bi->first_zero_hint =
+						    (((char *)cur -
+						      bi->bh->b_data) << 3) + b;
+				}
+			}
+		}
+		cur++;
+	}
+
+#ifdef CONFIG_REISERFS_CHECK
+// This outputs a lot of unneded info on big FSes
+//    reiserfs_warning ("bitmap loaded from block %d: %d free blocks",
+//                    bi->bh->b_blocknr, bi->free_count);
+#endif
+}
+
+static int read_bitmaps(struct super_block *s)
+{
+	int i, bmap_nr;
+
+	SB_AP_BITMAP(s) =
+	    vmalloc(sizeof(struct reiserfs_bitmap_info) * SB_BMAP_NR(s));
+	if (SB_AP_BITMAP(s) == 0)
+		return 1;
+	memset(SB_AP_BITMAP(s), 0,
+	       sizeof(struct reiserfs_bitmap_info) * SB_BMAP_NR(s));
+	for (i = 0, bmap_nr =
+	     REISERFS_DISK_OFFSET_IN_BYTES / s->s_blocksize + 1;
+	     i < SB_BMAP_NR(s); i++, bmap_nr = s->s_blocksize * 8 * i) {
+		SB_AP_BITMAP(s)[i].bh = sb_getblk(s, bmap_nr);
+		if (!buffer_uptodate(SB_AP_BITMAP(s)[i].bh))
+			ll_rw_block(READ, 1, &SB_AP_BITMAP(s)[i].bh);
+	}
+	for (i = 0; i < SB_BMAP_NR(s); i++) {
+		wait_on_buffer(SB_AP_BITMAP(s)[i].bh);
+		if (!buffer_uptodate(SB_AP_BITMAP(s)[i].bh)) {
+			reiserfs_warning(s, "sh-2029: reiserfs read_bitmaps: "
+					 "bitmap block (#%lu) reading failed",
+					 SB_AP_BITMAP(s)[i].bh->b_blocknr);
+			for (i = 0; i < SB_BMAP_NR(s); i++)
+				brelse(SB_AP_BITMAP(s)[i].bh);
+			vfree(SB_AP_BITMAP(s));
+			SB_AP_BITMAP(s) = NULL;
+			return 1;
+		}
+		load_bitmap_info_data(s, SB_AP_BITMAP(s) + i);
+	}
+	return 0;
+}
+
+static int read_old_bitmaps(struct super_block *s)
+{
+	int i;
+	struct reiserfs_super_block *rs = SB_DISK_SUPER_BLOCK(s);
+	int bmp1 = (REISERFS_OLD_DISK_OFFSET_IN_BYTES / s->s_blocksize) + 1;	/* first of bitmap blocks */
+
+	/* read true bitmap */
+	SB_AP_BITMAP(s) =
+	    vmalloc(sizeof(struct reiserfs_buffer_info *) * sb_bmap_nr(rs));
+	if (SB_AP_BITMAP(s) == 0)
+		return 1;
+
+	memset(SB_AP_BITMAP(s), 0,
+	       sizeof(struct reiserfs_buffer_info *) * sb_bmap_nr(rs));
+
+	for (i = 0; i < sb_bmap_nr(rs); i++) {
+		SB_AP_BITMAP(s)[i].bh = sb_bread(s, bmp1 + i);
+		if (!SB_AP_BITMAP(s)[i].bh)
+			return 1;
+		load_bitmap_info_data(s, SB_AP_BITMAP(s) + i);
+	}
+
+	return 0;
+}
+
+static int read_super_block(struct super_block *s, int offset)
+{
+	struct buffer_head *bh;
+	struct reiserfs_super_block *rs;
+	int fs_blocksize;
+
+	bh = sb_bread(s, offset / s->s_blocksize);
+	if (!bh) {
+		reiserfs_warning(s, "sh-2006: read_super_block: "
+				 "bread failed (dev %s, block %lu, size %lu)",
+				 reiserfs_bdevname(s), offset / s->s_blocksize,
+				 s->s_blocksize);
+		return 1;
+	}
+
+	rs = (struct reiserfs_super_block *)bh->b_data;
+	if (!is_any_reiserfs_magic_string(rs)) {
+		brelse(bh);
+		return 1;
+	}
+	//
+	// ok, reiserfs signature (old or new) found in at the given offset
+	//    
+	fs_blocksize = sb_blocksize(rs);
+	brelse(bh);
+	sb_set_blocksize(s, fs_blocksize);
+
+	bh = sb_bread(s, offset / s->s_blocksize);
+	if (!bh) {
+		reiserfs_warning(s, "sh-2007: read_super_block: "
+				 "bread failed (dev %s, block %lu, size %lu)\n",
+				 reiserfs_bdevname(s), offset / s->s_blocksize,
+				 s->s_blocksize);
+		return 1;
+	}
+
+	rs = (struct reiserfs_super_block *)bh->b_data;
+	if (sb_blocksize(rs) != s->s_blocksize) {
+		reiserfs_warning(s, "sh-2011: read_super_block: "
+				 "can't find a reiserfs filesystem on (dev %s, block %Lu, size %lu)\n",
+				 reiserfs_bdevname(s),
+				 (unsigned long long)bh->b_blocknr,
+				 s->s_blocksize);
+		brelse(bh);
+		return 1;
+	}
+
+	if (rs->s_v1.s_root_block == cpu_to_le32(-1)) {
+		brelse(bh);
+		reiserfs_warning(s,
+				 "Unfinished reiserfsck --rebuild-tree run detected. Please run\n"
+				 "reiserfsck --rebuild-tree and wait for a completion. If that fails\n"
+				 "get newer reiserfsprogs package");
+		return 1;
+	}
+
+	SB_BUFFER_WITH_SB(s) = bh;
+	SB_DISK_SUPER_BLOCK(s) = rs;
+
+	if (is_reiserfs_jr(rs)) {
+		/* magic is of non-standard journal filesystem, look at s_version to
+		   find which format is in use */
+		if (sb_version(rs) == REISERFS_VERSION_2)
+			reiserfs_warning(s,
+					 "read_super_block: found reiserfs format \"3.6\""
+					 " with non-standard journal");
+		else if (sb_version(rs) == REISERFS_VERSION_1)
+			reiserfs_warning(s,
+					 "read_super_block: found reiserfs format \"3.5\""
+					 " with non-standard journal");
+		else {
+			reiserfs_warning(s,
+					 "sh-2012: read_super_block: found unknown "
+					 "format \"%u\" of reiserfs with non-standard magic",
+					 sb_version(rs));
+			return 1;
+		}
+	} else
+		/* s_version of standard format may contain incorrect information,
+		   so we just look at the magic string */
+		reiserfs_info(s,
+			      "found reiserfs format \"%s\" with standard journal\n",
+			      is_reiserfs_3_5(rs) ? "3.5" : "3.6");
+
+	s->s_op = &reiserfs_sops;
+	s->s_export_op = &reiserfs_export_ops;
+#ifdef CONFIG_QUOTA
+	s->s_qcop = &reiserfs_qctl_operations;
+	s->dq_op = &reiserfs_quota_operations;
+#endif
+
+	/* new format is limited by the 32 bit wide i_blocks field, want to
+	 ** be one full block below that.
+	 */
+	s->s_maxbytes = (512LL << 32) - s->s_blocksize;
+	return 0;
+}
+
+/* after journal replay, reread all bitmap and super blocks */
+static int reread_meta_blocks(struct super_block *s)
+{
+	int i;
+	ll_rw_block(READ, 1, &(SB_BUFFER_WITH_SB(s)));
+	wait_on_buffer(SB_BUFFER_WITH_SB(s));
+	if (!buffer_uptodate(SB_BUFFER_WITH_SB(s))) {
+		reiserfs_warning(s,
+				 "reread_meta_blocks, error reading the super");
+		return 1;
+	}
+
+	for (i = 0; i < SB_BMAP_NR(s); i++) {
+		ll_rw_block(READ, 1, &(SB_AP_BITMAP(s)[i].bh));
+		wait_on_buffer(SB_AP_BITMAP(s)[i].bh);
+		if (!buffer_uptodate(SB_AP_BITMAP(s)[i].bh)) {
+			reiserfs_warning(s,
+					 "reread_meta_blocks, error reading bitmap block number %d at %llu",
+					 i,
+					 (unsigned long long)SB_AP_BITMAP(s)[i].
+					 bh->b_blocknr);
+			return 1;
+		}
+	}
+	return 0;
+
+}
+
+/////////////////////////////////////////////////////
+// hash detection stuff
+
+// if root directory is empty - we set default - Yura's - hash and
+// warn about it
+// FIXME: we look for only one name in a directory. If tea and yura
+// bith have the same value - we ask user to send report to the
+// mailing list
+static __u32 find_hash_out(struct super_block *s)
+{
+	int retval;
+	struct inode *inode;
+	struct cpu_key key;
+	INITIALIZE_PATH(path);
+	struct reiserfs_dir_entry de;
+	__u32 hash = DEFAULT_HASH;
+
+	inode = s->s_root->d_inode;
+
+	do {			// Some serious "goto"-hater was there ;)
+		u32 teahash, r5hash, yurahash;
+
+		make_cpu_key(&key, inode, ~0, TYPE_DIRENTRY, 3);
+		retval = search_by_entry_key(s, &key, &path, &de);
+		if (retval == IO_ERROR) {
+			pathrelse(&path);
+			return UNSET_HASH;
+		}
+		if (retval == NAME_NOT_FOUND)
+			de.de_entry_num--;
+		set_de_name_and_namelen(&de);
+		if (deh_offset(&(de.de_deh[de.de_entry_num])) == DOT_DOT_OFFSET) {
+			/* allow override in this case */
+			if (reiserfs_rupasov_hash(s)) {
+				hash = YURA_HASH;
+			}
+			reiserfs_warning(s, "FS seems to be empty, autodetect "
+					 "is using the default hash");
+			break;
+		}
+		r5hash = GET_HASH_VALUE(r5_hash(de.de_name, de.de_namelen));
+		teahash = GET_HASH_VALUE(keyed_hash(de.de_name, de.de_namelen));
+		yurahash = GET_HASH_VALUE(yura_hash(de.de_name, de.de_namelen));
+		if (((teahash == r5hash)
+		     &&
+		     (GET_HASH_VALUE(deh_offset(&(de.de_deh[de.de_entry_num])))
+		      == r5hash)) || ((teahash == yurahash)
+				      && (yurahash ==
+					  GET_HASH_VALUE(deh_offset
+							 (&
+							  (de.
+							   de_deh[de.
+								  de_entry_num])))))
+		    || ((r5hash == yurahash)
+			&& (yurahash ==
+			    GET_HASH_VALUE(deh_offset
+					   (&(de.de_deh[de.de_entry_num])))))) {
+			reiserfs_warning(s,
+					 "Unable to automatically detect hash function. "
+					 "Please mount with -o hash={tea,rupasov,r5}",
+					 reiserfs_bdevname(s));
+			hash = UNSET_HASH;
+			break;
+		}
+		if (GET_HASH_VALUE(deh_offset(&(de.de_deh[de.de_entry_num]))) ==
+		    yurahash)
+			hash = YURA_HASH;
+		else if (GET_HASH_VALUE
+			 (deh_offset(&(de.de_deh[de.de_entry_num]))) == teahash)
+			hash = TEA_HASH;
+		else if (GET_HASH_VALUE
+			 (deh_offset(&(de.de_deh[de.de_entry_num]))) == r5hash)
+			hash = R5_HASH;
+		else {
+			reiserfs_warning(s, "Unrecognised hash function");
+			hash = UNSET_HASH;
+		}
+	} while (0);
+
+	pathrelse(&path);
+	return hash;
+}
+
+// finds out which hash names are sorted with
+static int what_hash(struct super_block *s)
+{
+	__u32 code;
+
+	code = sb_hash_function_code(SB_DISK_SUPER_BLOCK(s));
+
+	/* reiserfs_hash_detect() == true if any of the hash mount options
+	 ** were used.  We must check them to make sure the user isn't
+	 ** using a bad hash value
+	 */
+	if (code == UNSET_HASH || reiserfs_hash_detect(s))
+		code = find_hash_out(s);
+
+	if (code != UNSET_HASH && reiserfs_hash_detect(s)) {
+		/* detection has found the hash, and we must check against the 
+		 ** mount options 
+		 */
+		if (reiserfs_rupasov_hash(s) && code != YURA_HASH) {
+			reiserfs_warning(s, "Error, %s hash detected, "
+					 "unable to force rupasov hash",
+					 reiserfs_hashname(code));
+			code = UNSET_HASH;
+		} else if (reiserfs_tea_hash(s) && code != TEA_HASH) {
+			reiserfs_warning(s, "Error, %s hash detected, "
+					 "unable to force tea hash",
+					 reiserfs_hashname(code));
+			code = UNSET_HASH;
+		} else if (reiserfs_r5_hash(s) && code != R5_HASH) {
+			reiserfs_warning(s, "Error, %s hash detected, "
+					 "unable to force r5 hash",
+					 reiserfs_hashname(code));
+			code = UNSET_HASH;
+		}
+	} else {
+		/* find_hash_out was not called or could not determine the hash */
+		if (reiserfs_rupasov_hash(s)) {
+			code = YURA_HASH;
+		} else if (reiserfs_tea_hash(s)) {
+			code = TEA_HASH;
+		} else if (reiserfs_r5_hash(s)) {
+			code = R5_HASH;
+		}
+	}
+
+	/* if we are mounted RW, and we have a new valid hash code, update 
+	 ** the super
+	 */
+	if (code != UNSET_HASH &&
+	    !(s->s_flags & MS_RDONLY) &&
+	    code != sb_hash_function_code(SB_DISK_SUPER_BLOCK(s))) {
+		set_sb_hash_function_code(SB_DISK_SUPER_BLOCK(s), code);
+	}
+	return code;
+}
+
+// return pointer to appropriate function
+static hashf_t hash_function(struct super_block *s)
+{
+	switch (what_hash(s)) {
+	case TEA_HASH:
+		reiserfs_info(s, "Using tea hash to sort names\n");
+		return keyed_hash;
+	case YURA_HASH:
+		reiserfs_info(s, "Using rupasov hash to sort names\n");
+		return yura_hash;
+	case R5_HASH:
+		reiserfs_info(s, "Using r5 hash to sort names\n");
+		return r5_hash;
+	}
+	return NULL;
+}
+
+// this is used to set up correct value for old partitions
+static int function2code(hashf_t func)
+{
+	if (func == keyed_hash)
+		return TEA_HASH;
+	if (func == yura_hash)
+		return YURA_HASH;
+	if (func == r5_hash)
+		return R5_HASH;
+
+	BUG();			// should never happen
+
+	return 0;
+}
+
+#define SWARN(silent, s, ...)			\
+	if (!(silent))				\
+		reiserfs_warning (s, __VA_ARGS__)
+
+static int reiserfs_fill_super(struct super_block *s, void *data, int silent)
+{
+	struct inode *root_inode;
+	int j;
+	struct reiserfs_transaction_handle th;
+	int old_format = 0;
+	unsigned long blocks;
+	unsigned int commit_max_age = 0;
+	int jinit_done = 0;
+	struct reiserfs_iget_args args;
+	struct reiserfs_super_block *rs;
+	char *jdev_name;
+	struct reiserfs_sb_info *sbi;
+	int errval = -EINVAL;
+
+	sbi = kmalloc(sizeof(struct reiserfs_sb_info), GFP_KERNEL);
+	if (!sbi) {
+		errval = -ENOMEM;
+		goto error;
+	}
+	s->s_fs_info = sbi;
+	memset(sbi, 0, sizeof(struct reiserfs_sb_info));
+	/* Set default values for options: non-aggressive tails, RO on errors */
+	REISERFS_SB(s)->s_mount_opt |= (1 << REISERFS_SMALLTAIL);
+	REISERFS_SB(s)->s_mount_opt |= (1 << REISERFS_ERROR_RO);
+	/* no preallocation minimum, be smart in
+	   reiserfs_file_write instead */
+	REISERFS_SB(s)->s_alloc_options.preallocmin = 0;
+	/* Preallocate by 16 blocks (17-1) at once */
+	REISERFS_SB(s)->s_alloc_options.preallocsize = 17;
+	/* Initialize the rwsem for xattr dir */
+	init_rwsem(&REISERFS_SB(s)->xattr_dir_sem);
+
+	/* setup default block allocator options */
+	reiserfs_init_alloc_options(s);
+
+	jdev_name = NULL;
+	if (reiserfs_parse_options
+	    (s, (char *)data, &(sbi->s_mount_opt), &blocks, &jdev_name,
+	     &commit_max_age) == 0) {
+		goto error;
+	}
+
+	if (blocks) {
+		SWARN(silent, s, "jmacd-7: reiserfs_fill_super: resize option "
+		      "for remount only");
+		goto error;
+	}
+
+	/* try old format (undistributed bitmap, super block in 8-th 1k block of a device) */
+	if (!read_super_block(s, REISERFS_OLD_DISK_OFFSET_IN_BYTES))
+		old_format = 1;
+	/* try new format (64-th 1k block), which can contain reiserfs super block */
+	else if (read_super_block(s, REISERFS_DISK_OFFSET_IN_BYTES)) {
+		SWARN(silent, s,
+		      "sh-2021: reiserfs_fill_super: can not find reiserfs on %s",
+		      reiserfs_bdevname(s));
+		goto error;
+	}
+
+	rs = SB_DISK_SUPER_BLOCK(s);
+	/* Let's do basic sanity check to verify that underlying device is not
+	   smaller than the filesystem. If the check fails then abort and scream,
+	   because bad stuff will happen otherwise. */
+	if (s->s_bdev && s->s_bdev->bd_inode
+	    && i_size_read(s->s_bdev->bd_inode) <
+	    sb_block_count(rs) * sb_blocksize(rs)) {
+		SWARN(silent, s,
+		      "Filesystem on %s cannot be mounted because it is bigger than the device",
+		      reiserfs_bdevname(s));
+		SWARN(silent, s,
+		      "You may need to run fsck or increase size of your LVM partition");
+		SWARN(silent, s,
+		      "Or may be you forgot to reboot after fdisk when it told you to");
+		goto error;
+	}
+
+	sbi->s_mount_state = SB_REISERFS_STATE(s);
+	sbi->s_mount_state = REISERFS_VALID_FS;
+
+	if (old_format ? read_old_bitmaps(s) : read_bitmaps(s)) {
+		SWARN(silent, s,
+		      "jmacd-8: reiserfs_fill_super: unable to read bitmap");
+		goto error;
+	}
+#ifdef CONFIG_REISERFS_CHECK
+	SWARN(silent, s, "CONFIG_REISERFS_CHECK is set ON");
+	SWARN(silent, s, "- it is slow mode for debugging.");
+#endif
+
+	/* make data=ordered the default */
+	if (!reiserfs_data_log(s) && !reiserfs_data_ordered(s) &&
+	    !reiserfs_data_writeback(s)) {
+		REISERFS_SB(s)->s_mount_opt |= (1 << REISERFS_DATA_ORDERED);
+	}
+
+	if (reiserfs_data_log(s)) {
+		reiserfs_info(s, "using journaled data mode\n");
+	} else if (reiserfs_data_ordered(s)) {
+		reiserfs_info(s, "using ordered data mode\n");
+	} else {
+		reiserfs_info(s, "using writeback data mode\n");
+	}
+	if (reiserfs_barrier_flush(s)) {
+		printk("reiserfs: using flush barriers\n");
+	}
+	// set_device_ro(s->s_dev, 1) ;
+	if (journal_init(s, jdev_name, old_format, commit_max_age)) {
+		SWARN(silent, s,
+		      "sh-2022: reiserfs_fill_super: unable to initialize journal space");
+		goto error;
+	} else {
+		jinit_done = 1;	/* once this is set, journal_release must be called
+				 ** if we error out of the mount
+				 */
+	}
+	if (reread_meta_blocks(s)) {
+		SWARN(silent, s,
+		      "jmacd-9: reiserfs_fill_super: unable to reread meta blocks after journal init");
+		goto error;
+	}
+
+	if (replay_only(s))
+		goto error;
+
+	if (bdev_read_only(s->s_bdev) && !(s->s_flags & MS_RDONLY)) {
+		SWARN(silent, s,
+		      "clm-7000: Detected readonly device, marking FS readonly");
+		s->s_flags |= MS_RDONLY;
+	}
+	args.objectid = REISERFS_ROOT_OBJECTID;
+	args.dirid = REISERFS_ROOT_PARENT_OBJECTID;
+	root_inode =
+	    iget5_locked(s, REISERFS_ROOT_OBJECTID, reiserfs_find_actor,
+			 reiserfs_init_locked_inode, (void *)(&args));
+	if (!root_inode) {
+		SWARN(silent, s,
+		      "jmacd-10: reiserfs_fill_super: get root inode failed");
+		goto error;
+	}
+
+	if (root_inode->i_state & I_NEW) {
+		reiserfs_read_locked_inode(root_inode, &args);
+		unlock_new_inode(root_inode);
+	}
+
+	s->s_root = d_alloc_root(root_inode);
+	if (!s->s_root) {
+		iput(root_inode);
+		goto error;
+	}
+	// define and initialize hash function
+	sbi->s_hash_function = hash_function(s);
+	if (sbi->s_hash_function == NULL) {
+		dput(s->s_root);
+		s->s_root = NULL;
+		goto error;
+	}
+
+	if (is_reiserfs_3_5(rs)
+	    || (is_reiserfs_jr(rs) && SB_VERSION(s) == REISERFS_VERSION_1))
+		set_bit(REISERFS_3_5, &(sbi->s_properties));
+	else
+		set_bit(REISERFS_3_6, &(sbi->s_properties));
+
+	if (!(s->s_flags & MS_RDONLY)) {
+
+		errval = journal_begin(&th, s, 1);
+		if (errval) {
+			dput(s->s_root);
+			s->s_root = NULL;
+			goto error;
+		}
+		reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1);
+
+		set_sb_umount_state(rs, REISERFS_ERROR_FS);
+		set_sb_fs_state(rs, 0);
+
+		if (old_format_only(s)) {
+			/* filesystem of format 3.5 either with standard or non-standard
+			   journal */
+			if (convert_reiserfs(s)) {
+				/* and -o conv is given */
+				if (!silent)
+					reiserfs_info(s,
+						      "converting 3.5 filesystem to the 3.6 format");
+
+				if (is_reiserfs_3_5(rs))
+					/* put magic string of 3.6 format. 2.2 will not be able to
+					   mount this filesystem anymore */
+					memcpy(rs->s_v1.s_magic,
+					       reiserfs_3_6_magic_string,
+					       sizeof
+					       (reiserfs_3_6_magic_string));
+
+				set_sb_version(rs, REISERFS_VERSION_2);
+				reiserfs_convert_objectid_map_v1(s);
+				set_bit(REISERFS_3_6, &(sbi->s_properties));
+				clear_bit(REISERFS_3_5, &(sbi->s_properties));
+			} else if (!silent) {
+				reiserfs_info(s, "using 3.5.x disk format\n");
+			}
+		}
+
+		journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB(s));
+		errval = journal_end(&th, s, 1);
+		if (errval) {
+			dput(s->s_root);
+			s->s_root = NULL;
+			goto error;
+		}
+
+		if ((errval = reiserfs_xattr_init(s, s->s_flags))) {
+			dput(s->s_root);
+			s->s_root = NULL;
+			goto error;
+		}
+
+		/* look for files which were to be removed in previous session */
+		finish_unfinished(s);
+	} else {
+		if (old_format_only(s) && !silent) {
+			reiserfs_info(s, "using 3.5.x disk format\n");
+		}
+
+		if ((errval = reiserfs_xattr_init(s, s->s_flags))) {
+			dput(s->s_root);
+			s->s_root = NULL;
+			goto error;
+		}
+	}
+	// mark hash in super block: it could be unset. overwrite should be ok
+	set_sb_hash_function_code(rs, function2code(sbi->s_hash_function));
+
+	handle_attrs(s);
+
+	reiserfs_proc_info_init(s);
+
+	init_waitqueue_head(&(sbi->s_wait));
+	spin_lock_init(&sbi->bitmap_lock);
+
+	return (0);
+
+      error:
+	if (jinit_done) {	/* kill the commit thread, free journal ram */
+		journal_release_error(NULL, s);
+	}
+	if (SB_DISK_SUPER_BLOCK(s)) {
+		for (j = 0; j < SB_BMAP_NR(s); j++) {
+			if (SB_AP_BITMAP(s))
+				brelse(SB_AP_BITMAP(s)[j].bh);
+		}
+		vfree(SB_AP_BITMAP(s));
+	}
+	if (SB_BUFFER_WITH_SB(s))
+		brelse(SB_BUFFER_WITH_SB(s));
+#ifdef CONFIG_QUOTA
+	for (j = 0; j < MAXQUOTAS; j++) {
+		kfree(sbi->s_qf_names[j]);
+		sbi->s_qf_names[j] = NULL;
+	}
+#endif
+	kfree(sbi);
+
+	s->s_fs_info = NULL;
+	return errval;
+}
+
+static int reiserfs_statfs(struct super_block *s, struct kstatfs *buf)
+{
+	struct reiserfs_super_block *rs = SB_DISK_SUPER_BLOCK(s);
+
+	buf->f_namelen = (REISERFS_MAX_NAME(s->s_blocksize));
+	buf->f_bfree = sb_free_blocks(rs);
+	buf->f_bavail = buf->f_bfree;
+	buf->f_blocks = sb_block_count(rs) - sb_bmap_nr(rs) - 1;
+	buf->f_bsize = s->s_blocksize;
+	/* changed to accommodate gcc folks. */
+	buf->f_type = REISERFS_SUPER_MAGIC;
+	return 0;
+}
+
+#ifdef CONFIG_QUOTA
+static int reiserfs_dquot_initialize(struct inode *inode, int type)
+{
+	struct reiserfs_transaction_handle th;
+	int ret, err;
+
+	/* We may create quota structure so we need to reserve enough blocks */
+	reiserfs_write_lock(inode->i_sb);
+	ret =
+	    journal_begin(&th, inode->i_sb,
+			  2 * REISERFS_QUOTA_INIT_BLOCKS(inode->i_sb));
+	if (ret)
+		goto out;
+	ret = dquot_initialize(inode, type);
+	err =
+	    journal_end(&th, inode->i_sb,
+			2 * REISERFS_QUOTA_INIT_BLOCKS(inode->i_sb));
+	if (!ret && err)
+		ret = err;
+      out:
+	reiserfs_write_unlock(inode->i_sb);
+	return ret;
+}
+
+static int reiserfs_dquot_drop(struct inode *inode)
+{
+	struct reiserfs_transaction_handle th;
+	int ret, err;
+
+	/* We may delete quota structure so we need to reserve enough blocks */
+	reiserfs_write_lock(inode->i_sb);
+	ret =
+	    journal_begin(&th, inode->i_sb,
+			  2 * REISERFS_QUOTA_DEL_BLOCKS(inode->i_sb));
+	if (ret)
+		goto out;
+	ret = dquot_drop(inode);
+	err =
+	    journal_end(&th, inode->i_sb,
+			2 * REISERFS_QUOTA_DEL_BLOCKS(inode->i_sb));
+	if (!ret && err)
+		ret = err;
+      out:
+	reiserfs_write_unlock(inode->i_sb);
+	return ret;
+}
+
+static int reiserfs_write_dquot(struct dquot *dquot)
+{
+	struct reiserfs_transaction_handle th;
+	int ret, err;
+
+	reiserfs_write_lock(dquot->dq_sb);
+	ret =
+	    journal_begin(&th, dquot->dq_sb,
+			  REISERFS_QUOTA_TRANS_BLOCKS(dquot->dq_sb));
+	if (ret)
+		goto out;
+	ret = dquot_commit(dquot);
+	err =
+	    journal_end(&th, dquot->dq_sb,
+			REISERFS_QUOTA_TRANS_BLOCKS(dquot->dq_sb));
+	if (!ret && err)
+		ret = err;
+      out:
+	reiserfs_write_unlock(dquot->dq_sb);
+	return ret;
+}
+
+static int reiserfs_acquire_dquot(struct dquot *dquot)
+{
+	struct reiserfs_transaction_handle th;
+	int ret, err;
+
+	reiserfs_write_lock(dquot->dq_sb);
+	ret =
+	    journal_begin(&th, dquot->dq_sb,
+			  REISERFS_QUOTA_INIT_BLOCKS(dquot->dq_sb));
+	if (ret)
+		goto out;
+	ret = dquot_acquire(dquot);
+	err =
+	    journal_end(&th, dquot->dq_sb,
+			REISERFS_QUOTA_INIT_BLOCKS(dquot->dq_sb));
+	if (!ret && err)
+		ret = err;
+      out:
+	reiserfs_write_unlock(dquot->dq_sb);
+	return ret;
+}
+
+static int reiserfs_release_dquot(struct dquot *dquot)
+{
+	struct reiserfs_transaction_handle th;
+	int ret, err;
+
+	reiserfs_write_lock(dquot->dq_sb);
+	ret =
+	    journal_begin(&th, dquot->dq_sb,
+			  REISERFS_QUOTA_DEL_BLOCKS(dquot->dq_sb));
+	if (ret)
+		goto out;
+	ret = dquot_release(dquot);
+	err =
+	    journal_end(&th, dquot->dq_sb,
+			REISERFS_QUOTA_DEL_BLOCKS(dquot->dq_sb));
+	if (!ret && err)
+		ret = err;
+      out:
+	reiserfs_write_unlock(dquot->dq_sb);
+	return ret;
+}
+
+static int reiserfs_mark_dquot_dirty(struct dquot *dquot)
+{
+	/* Are we journalling quotas? */
+	if (REISERFS_SB(dquot->dq_sb)->s_qf_names[USRQUOTA] ||
+	    REISERFS_SB(dquot->dq_sb)->s_qf_names[GRPQUOTA]) {
+		dquot_mark_dquot_dirty(dquot);
+		return reiserfs_write_dquot(dquot);
+	} else
+		return dquot_mark_dquot_dirty(dquot);
+}
+
+static int reiserfs_write_info(struct super_block *sb, int type)
+{
+	struct reiserfs_transaction_handle th;
+	int ret, err;
+
+	/* Data block + inode block */
+	reiserfs_write_lock(sb);
+	ret = journal_begin(&th, sb, 2);
+	if (ret)
+		goto out;
+	ret = dquot_commit_info(sb, type);
+	err = journal_end(&th, sb, 2);
+	if (!ret && err)
+		ret = err;
+      out:
+	reiserfs_write_unlock(sb);
+	return ret;
+}
+
+/*
+ * Turn on quotas during mount time - we need to find the quota file and such...
+ */
+static int reiserfs_quota_on_mount(struct super_block *sb, int type)
+{
+	return vfs_quota_on_mount(sb, REISERFS_SB(sb)->s_qf_names[type],
+				  REISERFS_SB(sb)->s_jquota_fmt, type);
+}
+
+/*
+ * Standard function to be called on quota_on
+ */
+static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
+			     char *path)
+{
+	int err;
+	struct nameidata nd;
+
+	if (!(REISERFS_SB(sb)->s_mount_opt & (1 << REISERFS_QUOTA)))
+		return -EINVAL;
+	err = path_lookup(path, LOOKUP_FOLLOW, &nd);
+	if (err)
+		return err;
+	/* Quotafile not on the same filesystem? */
+	if (nd.mnt->mnt_sb != sb) {
+		path_release(&nd);
+		return -EXDEV;
+	}
+	/* We must not pack tails for quota files on reiserfs for quota IO to work */
+	if (!REISERFS_I(nd.dentry->d_inode)->i_flags & i_nopack_mask) {
+		reiserfs_warning(sb,
+				 "reiserfs: Quota file must have tail packing disabled.");
+		path_release(&nd);
+		return -EINVAL;
+	}
+	/* Not journalling quota? No more tests needed... */
+	if (!REISERFS_SB(sb)->s_qf_names[USRQUOTA] &&
+	    !REISERFS_SB(sb)->s_qf_names[GRPQUOTA]) {
+		path_release(&nd);
+		return vfs_quota_on(sb, type, format_id, path);
+	}
+	/* Quotafile not of fs root? */
+	if (nd.dentry->d_parent->d_inode != sb->s_root->d_inode)
+		reiserfs_warning(sb,
+				 "reiserfs: Quota file not on filesystem root. "
+				 "Journalled quota will not work.");
+	path_release(&nd);
+	return vfs_quota_on(sb, type, format_id, path);
+}
+
+/* Read data from quotafile - avoid pagecache and such because we cannot afford
+ * acquiring the locks... As quota files are never truncated and quota code
+ * itself serializes the operations (and noone else should touch the files)
+ * we don't have to be afraid of races */
+static ssize_t reiserfs_quota_read(struct super_block *sb, int type, char *data,
+				   size_t len, loff_t off)
+{
+	struct inode *inode = sb_dqopt(sb)->files[type];
+	unsigned long blk = off >> sb->s_blocksize_bits;
+	int err = 0, offset = off & (sb->s_blocksize - 1), tocopy;
+	size_t toread;
+	struct buffer_head tmp_bh, *bh;
+	loff_t i_size = i_size_read(inode);
+
+	if (off > i_size)
+		return 0;
+	if (off + len > i_size)
+		len = i_size - off;
+	toread = len;
+	while (toread > 0) {
+		tocopy =
+		    sb->s_blocksize - offset <
+		    toread ? sb->s_blocksize - offset : toread;
+		tmp_bh.b_state = 0;
+		/* Quota files are without tails so we can safely use this function */
+		reiserfs_write_lock(sb);
+		err = reiserfs_get_block(inode, blk, &tmp_bh, 0);
+		reiserfs_write_unlock(sb);
+		if (err)
+			return err;
+		if (!buffer_mapped(&tmp_bh))	/* A hole? */
+			memset(data, 0, tocopy);
+		else {
+			bh = sb_bread(sb, tmp_bh.b_blocknr);
+			if (!bh)
+				return -EIO;
+			memcpy(data, bh->b_data + offset, tocopy);
+			brelse(bh);
+		}
+		offset = 0;
+		toread -= tocopy;
+		data += tocopy;
+		blk++;
+	}
+	return len;
+}
+
+/* Write to quotafile (we know the transaction is already started and has
+ * enough credits) */
+static ssize_t reiserfs_quota_write(struct super_block *sb, int type,
+				    const char *data, size_t len, loff_t off)
+{
+	struct inode *inode = sb_dqopt(sb)->files[type];
+	unsigned long blk = off >> sb->s_blocksize_bits;
+	int err = 0, offset = off & (sb->s_blocksize - 1), tocopy;
+	int journal_quota = REISERFS_SB(sb)->s_qf_names[type] != NULL;
+	size_t towrite = len;
+	struct buffer_head tmp_bh, *bh;
+
+	down(&inode->i_sem);
+	while (towrite > 0) {
+		tocopy = sb->s_blocksize - offset < towrite ?
+		    sb->s_blocksize - offset : towrite;
+		tmp_bh.b_state = 0;
+		err = reiserfs_get_block(inode, blk, &tmp_bh, GET_BLOCK_CREATE);
+		if (err)
+			goto out;
+		if (offset || tocopy != sb->s_blocksize)
+			bh = sb_bread(sb, tmp_bh.b_blocknr);
+		else
+			bh = sb_getblk(sb, tmp_bh.b_blocknr);
+		if (!bh) {
+			err = -EIO;
+			goto out;
+		}
+		lock_buffer(bh);
+		memcpy(bh->b_data + offset, data, tocopy);
+		flush_dcache_page(bh->b_page);
+		set_buffer_uptodate(bh);
+		unlock_buffer(bh);
+		reiserfs_prepare_for_journal(sb, bh, 1);
+		journal_mark_dirty(current->journal_info, sb, bh);
+		if (!journal_quota)
+			reiserfs_add_ordered_list(inode, bh);
+		brelse(bh);
+		offset = 0;
+		towrite -= tocopy;
+		data += tocopy;
+		blk++;
+	}
+      out:
+	if (len == towrite)
+		return err;
+	if (inode->i_size < off + len - towrite)
+		i_size_write(inode, off + len - towrite);
+	inode->i_version++;
+	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	mark_inode_dirty(inode);
+	up(&inode->i_sem);
+	return len - towrite;
+}
+
+#endif
+
+static struct super_block *get_super_block(struct file_system_type *fs_type,
+					   int flags, const char *dev_name,
+					   void *data)
+{
+	return get_sb_bdev(fs_type, flags, dev_name, data, reiserfs_fill_super);
+}
+
+static int __init init_reiserfs_fs(void)
+{
+	int ret;
+
+	if ((ret = init_inodecache())) {
+		return ret;
+	}
+
+	if ((ret = reiserfs_xattr_register_handlers()))
+		goto failed_reiserfs_xattr_register_handlers;
+
+	reiserfs_proc_info_global_init();
+	reiserfs_proc_register_global("version",
+				      reiserfs_global_version_in_proc);
+
+	ret = register_filesystem(&reiserfs_fs_type);
+
+	if (ret == 0) {
+		return 0;
+	}
+
+	reiserfs_xattr_unregister_handlers();
+
+      failed_reiserfs_xattr_register_handlers:
+	reiserfs_proc_unregister_global("version");
+	reiserfs_proc_info_global_done();
+	destroy_inodecache();
+
+	return ret;
+}
+
+static void __exit exit_reiserfs_fs(void)
+{
+	reiserfs_xattr_unregister_handlers();
+	reiserfs_proc_unregister_global("version");
+	reiserfs_proc_info_global_done();
+	unregister_filesystem(&reiserfs_fs_type);
+	destroy_inodecache();
+}
+
+struct file_system_type reiserfs_fs_type = {
+	.owner = THIS_MODULE,
+	.name = "reiserfs",
+	.get_sb = get_super_block,
+	.kill_sb = kill_block_super,
+	.fs_flags = FS_REQUIRES_DEV,
+};
+
+MODULE_DESCRIPTION("ReiserFS journaled filesystem");
+MODULE_AUTHOR("Hans Reiser <reiser@namesys.com>");
+MODULE_LICENSE("GPL");
+
+module_init(init_reiserfs_fs);
+module_exit(exit_reiserfs_fs);
diff -urN oldtree/fs/reiserfs/xattr.c newtree/fs/reiserfs/xattr.c
--- oldtree/fs/reiserfs/xattr.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/reiserfs/xattr.c	2006-02-13 19:20:00.730401264 -0500
@@ -367,15 +367,13 @@
 		if (d_reclen <= 32) {
 			local_buf = small_buf;
 		} else {
-			local_buf =
-			    reiserfs_kmalloc(d_reclen, GFP_NOFS, inode->i_sb);
+			local_buf = kmalloc(d_reclen, GFP_NOFS);
 			if (!local_buf) {
 				pathrelse(&path_to_entry);
 				return -ENOMEM;
 			}
 			if (item_moved(&tmp_ih, &path_to_entry)) {
-				reiserfs_kfree(local_buf, d_reclen,
-					       inode->i_sb);
+				kfree(local_buf);
 
 				/* sigh, must retry.  Do this same offset again */
 				next_pos = d_off;
@@ -398,13 +396,12 @@
 		if (filldir(dirent, local_buf, d_reclen, d_off, d_ino,
 			    DT_UNKNOWN) < 0) {
 			if (local_buf != small_buf) {
-				reiserfs_kfree(local_buf, d_reclen,
-					       inode->i_sb);
+				kfree(local_buf);
 			}
 			goto end;
 		}
 		if (local_buf != small_buf) {
-			reiserfs_kfree(local_buf, d_reclen, inode->i_sb);
+			kfree(local_buf);
 		}
 	}			/* while */
 
diff -urN oldtree/fs/reiserfs/xattr.c.orig newtree/fs/reiserfs/xattr.c.orig
--- oldtree/fs/reiserfs/xattr.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/reiserfs/xattr.c.orig	2006-02-13 19:20:00.731401112 -0500
@@ -0,0 +1,1450 @@
+/*
+ * linux/fs/reiserfs/xattr.c
+ *
+ * Copyright (c) 2002 by Jeff Mahoney, <jeffm@suse.com>
+ *
+ */
+
+/*
+ * In order to implement EA/ACLs in a clean, backwards compatible manner,
+ * they are implemented as files in a "private" directory.
+ * Each EA is in it's own file, with the directory layout like so (/ is assumed
+ * to be relative to fs root). Inside the /.reiserfs_priv/xattrs directory,
+ * directories named using the capital-hex form of the objectid and
+ * generation number are used. Inside each directory are individual files
+ * named with the name of the extended attribute.
+ *
+ * So, for objectid 12648430, we could have:
+ * /.reiserfs_priv/xattrs/C0FFEE.0/system.posix_acl_access
+ * /.reiserfs_priv/xattrs/C0FFEE.0/system.posix_acl_default
+ * /.reiserfs_priv/xattrs/C0FFEE.0/user.Content-Type
+ * .. or similar.
+ *
+ * The file contents are the text of the EA. The size is known based on the
+ * stat data describing the file.
+ *
+ * In the case of system.posix_acl_access and system.posix_acl_default, since
+ * these are special cases for filesystem ACLs, they are interpreted by the
+ * kernel, in addition, they are negatively and positively cached and attached
+ * to the inode so that unnecessary lookups are avoided.
+ */
+
+#include <linux/reiserfs_fs.h>
+#include <linux/dcache.h>
+#include <linux/namei.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/pagemap.h>
+#include <linux/xattr.h>
+#include <linux/reiserfs_xattr.h>
+#include <linux/reiserfs_acl.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+#include <linux/smp_lock.h>
+#include <linux/stat.h>
+#include <asm/semaphore.h>
+
+#define FL_READONLY 128
+#define FL_DIR_SEM_HELD 256
+#define PRIVROOT_NAME ".reiserfs_priv"
+#define XAROOT_NAME   "xattrs"
+
+static struct reiserfs_xattr_handler *find_xattr_handler_prefix(const char
+								*prefix);
+
+static struct dentry *create_xa_root(struct super_block *sb)
+{
+	struct dentry *privroot = dget(REISERFS_SB(sb)->priv_root);
+	struct dentry *xaroot;
+
+	/* This needs to be created at mount-time */
+	if (!privroot)
+		return ERR_PTR(-EOPNOTSUPP);
+
+	xaroot = lookup_one_len(XAROOT_NAME, privroot, strlen(XAROOT_NAME));
+	if (IS_ERR(xaroot)) {
+		goto out;
+	} else if (!xaroot->d_inode) {
+		int err;
+		down(&privroot->d_inode->i_sem);
+		err =
+		    privroot->d_inode->i_op->mkdir(privroot->d_inode, xaroot,
+						   0700);
+		up(&privroot->d_inode->i_sem);
+
+		if (err) {
+			dput(xaroot);
+			dput(privroot);
+			return ERR_PTR(err);
+		}
+		REISERFS_SB(sb)->xattr_root = dget(xaroot);
+	}
+
+      out:
+	dput(privroot);
+	return xaroot;
+}
+
+/* This will return a dentry, or error, refering to the xa root directory.
+ * If the xa root doesn't exist yet, the dentry will be returned without
+ * an associated inode. This dentry can be used with ->mkdir to create
+ * the xa directory. */
+static struct dentry *__get_xa_root(struct super_block *s)
+{
+	struct dentry *privroot = dget(REISERFS_SB(s)->priv_root);
+	struct dentry *xaroot = NULL;
+
+	if (IS_ERR(privroot) || !privroot)
+		return privroot;
+
+	xaroot = lookup_one_len(XAROOT_NAME, privroot, strlen(XAROOT_NAME));
+	if (IS_ERR(xaroot)) {
+		goto out;
+	} else if (!xaroot->d_inode) {
+		dput(xaroot);
+		xaroot = NULL;
+		goto out;
+	}
+
+	REISERFS_SB(s)->xattr_root = dget(xaroot);
+
+      out:
+	dput(privroot);
+	return xaroot;
+}
+
+/* Returns the dentry (or NULL) referring to the root of the extended
+ * attribute directory tree. If it has already been retreived, it is used.
+ * Otherwise, we attempt to retreive it from disk. It may also return
+ * a pointer-encoded error.
+ */
+static inline struct dentry *get_xa_root(struct super_block *s)
+{
+	struct dentry *dentry = dget(REISERFS_SB(s)->xattr_root);
+
+	if (!dentry)
+		dentry = __get_xa_root(s);
+
+	return dentry;
+}
+
+/* Opens the directory corresponding to the inode's extended attribute store.
+ * If flags allow, the tree to the directory may be created. If creation is
+ * prohibited, -ENODATA is returned. */
+static struct dentry *open_xa_dir(const struct inode *inode, int flags)
+{
+	struct dentry *xaroot, *xadir;
+	char namebuf[17];
+
+	xaroot = get_xa_root(inode->i_sb);
+	if (IS_ERR(xaroot)) {
+		return xaroot;
+	} else if (!xaroot) {
+		if (flags == 0 || flags & XATTR_CREATE) {
+			xaroot = create_xa_root(inode->i_sb);
+			if (IS_ERR(xaroot))
+				return xaroot;
+		}
+		if (!xaroot)
+			return ERR_PTR(-ENODATA);
+	}
+
+	/* ok, we have xaroot open */
+
+	snprintf(namebuf, sizeof(namebuf), "%X.%X",
+		 le32_to_cpu(INODE_PKEY(inode)->k_objectid),
+		 inode->i_generation);
+	xadir = lookup_one_len(namebuf, xaroot, strlen(namebuf));
+	if (IS_ERR(xadir)) {
+		dput(xaroot);
+		return xadir;
+	}
+
+	if (!xadir->d_inode) {
+		int err;
+		if (flags == 0 || flags & XATTR_CREATE) {
+			/* Although there is nothing else trying to create this directory,
+			 * another directory with the same hash may be created, so we need
+			 * to protect against that */
+			err =
+			    xaroot->d_inode->i_op->mkdir(xaroot->d_inode, xadir,
+							 0700);
+			if (err) {
+				dput(xaroot);
+				dput(xadir);
+				return ERR_PTR(err);
+			}
+		}
+		if (!xadir->d_inode) {
+			dput(xaroot);
+			dput(xadir);
+			return ERR_PTR(-ENODATA);
+		}
+	}
+
+	dput(xaroot);
+	return xadir;
+}
+
+/* Returns a dentry corresponding to a specific extended attribute file
+ * for the inode. If flags allow, the file is created. Otherwise, a
+ * valid or negative dentry, or an error is returned. */
+static struct dentry *get_xa_file_dentry(const struct inode *inode,
+					 const char *name, int flags)
+{
+	struct dentry *xadir, *xafile;
+	int err = 0;
+
+	xadir = open_xa_dir(inode, flags);
+	if (IS_ERR(xadir)) {
+		return ERR_PTR(PTR_ERR(xadir));
+	} else if (xadir && !xadir->d_inode) {
+		dput(xadir);
+		return ERR_PTR(-ENODATA);
+	}
+
+	xafile = lookup_one_len(name, xadir, strlen(name));
+	if (IS_ERR(xafile)) {
+		dput(xadir);
+		return ERR_PTR(PTR_ERR(xafile));
+	}
+
+	if (xafile->d_inode) {	/* file exists */
+		if (flags & XATTR_CREATE) {
+			err = -EEXIST;
+			dput(xafile);
+			goto out;
+		}
+	} else if (flags & XATTR_REPLACE || flags & FL_READONLY) {
+		goto out;
+	} else {
+		/* inode->i_sem is down, so nothing else can try to create
+		 * the same xattr */
+		err = xadir->d_inode->i_op->create(xadir->d_inode, xafile,
+						   0700 | S_IFREG, NULL);
+
+		if (err) {
+			dput(xafile);
+			goto out;
+		}
+	}
+
+      out:
+	dput(xadir);
+	if (err)
+		xafile = ERR_PTR(err);
+	return xafile;
+}
+
+/* Opens a file pointer to the attribute associated with inode */
+static struct file *open_xa_file(const struct inode *inode, const char *name,
+				 int flags)
+{
+	struct dentry *xafile;
+	struct file *fp;
+
+	xafile = get_xa_file_dentry(inode, name, flags);
+	if (IS_ERR(xafile))
+		return ERR_PTR(PTR_ERR(xafile));
+	else if (!xafile->d_inode) {
+		dput(xafile);
+		return ERR_PTR(-ENODATA);
+	}
+
+	fp = dentry_open(xafile, NULL, O_RDWR);
+	/* dentry_open dputs the dentry if it fails */
+
+	return fp;
+}
+
+/*
+ * this is very similar to fs/reiserfs/dir.c:reiserfs_readdir, but
+ * we need to drop the path before calling the filldir struct.  That
+ * would be a big performance hit to the non-xattr case, so I've copied
+ * the whole thing for now. --clm
+ *
+ * the big difference is that I go backwards through the directory,
+ * and don't mess with f->f_pos, but the idea is the same.  Do some
+ * action on each and every entry in the directory.
+ *
+ * we're called with i_sem held, so there are no worries about the directory
+ * changing underneath us.
+ */
+static int __xattr_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+	struct inode *inode = filp->f_dentry->d_inode;
+	struct cpu_key pos_key;	/* key of current position in the directory (key of directory entry) */
+	INITIALIZE_PATH(path_to_entry);
+	struct buffer_head *bh;
+	int entry_num;
+	struct item_head *ih, tmp_ih;
+	int search_res;
+	char *local_buf;
+	loff_t next_pos;
+	char small_buf[32];	/* avoid kmalloc if we can */
+	struct reiserfs_de_head *deh;
+	int d_reclen;
+	char *d_name;
+	off_t d_off;
+	ino_t d_ino;
+	struct reiserfs_dir_entry de;
+
+	/* form key for search the next directory entry using f_pos field of
+	   file structure */
+	next_pos = max_reiserfs_offset(inode);
+
+	while (1) {
+	      research:
+		if (next_pos <= DOT_DOT_OFFSET)
+			break;
+		make_cpu_key(&pos_key, inode, next_pos, TYPE_DIRENTRY, 3);
+
+		search_res =
+		    search_by_entry_key(inode->i_sb, &pos_key, &path_to_entry,
+					&de);
+		if (search_res == IO_ERROR) {
+			// FIXME: we could just skip part of directory which could
+			// not be read
+			pathrelse(&path_to_entry);
+			return -EIO;
+		}
+
+		if (search_res == NAME_NOT_FOUND)
+			de.de_entry_num--;
+
+		set_de_name_and_namelen(&de);
+		entry_num = de.de_entry_num;
+		deh = &(de.de_deh[entry_num]);
+
+		bh = de.de_bh;
+		ih = de.de_ih;
+
+		if (!is_direntry_le_ih(ih)) {
+			reiserfs_warning(inode->i_sb, "not direntry %h", ih);
+			break;
+		}
+		copy_item_head(&tmp_ih, ih);
+
+		/* we must have found item, that is item of this directory, */
+		RFALSE(COMP_SHORT_KEYS(&(ih->ih_key), &pos_key),
+		       "vs-9000: found item %h does not match to dir we readdir %K",
+		       ih, &pos_key);
+
+		if (deh_offset(deh) <= DOT_DOT_OFFSET) {
+			break;
+		}
+
+		/* look for the previous entry in the directory */
+		next_pos = deh_offset(deh) - 1;
+
+		if (!de_visible(deh))
+			/* it is hidden entry */
+			continue;
+
+		d_reclen = entry_length(bh, ih, entry_num);
+		d_name = B_I_DEH_ENTRY_FILE_NAME(bh, ih, deh);
+		d_off = deh_offset(deh);
+		d_ino = deh_objectid(deh);
+
+		if (!d_name[d_reclen - 1])
+			d_reclen = strlen(d_name);
+
+		if (d_reclen > REISERFS_MAX_NAME(inode->i_sb->s_blocksize)) {
+			/* too big to send back to VFS */
+			continue;
+		}
+
+		/* Ignore the .reiserfs_priv entry */
+		if (reiserfs_xattrs(inode->i_sb) &&
+		    !old_format_only(inode->i_sb) &&
+		    deh_objectid(deh) ==
+		    le32_to_cpu(INODE_PKEY
+				(REISERFS_SB(inode->i_sb)->priv_root->d_inode)->
+				k_objectid))
+			continue;
+
+		if (d_reclen <= 32) {
+			local_buf = small_buf;
+		} else {
+			local_buf =
+			    reiserfs_kmalloc(d_reclen, GFP_NOFS, inode->i_sb);
+			if (!local_buf) {
+				pathrelse(&path_to_entry);
+				return -ENOMEM;
+			}
+			if (item_moved(&tmp_ih, &path_to_entry)) {
+				reiserfs_kfree(local_buf, d_reclen,
+					       inode->i_sb);
+
+				/* sigh, must retry.  Do this same offset again */
+				next_pos = d_off;
+				goto research;
+			}
+		}
+
+		// Note, that we copy name to user space via temporary
+		// buffer (local_buf) because filldir will block if
+		// user space buffer is swapped out. At that time
+		// entry can move to somewhere else
+		memcpy(local_buf, d_name, d_reclen);
+
+		/* the filldir function might need to start transactions,
+		 * or do who knows what.  Release the path now that we've
+		 * copied all the important stuff out of the deh
+		 */
+		pathrelse(&path_to_entry);
+
+		if (filldir(dirent, local_buf, d_reclen, d_off, d_ino,
+			    DT_UNKNOWN) < 0) {
+			if (local_buf != small_buf) {
+				reiserfs_kfree(local_buf, d_reclen,
+					       inode->i_sb);
+			}
+			goto end;
+		}
+		if (local_buf != small_buf) {
+			reiserfs_kfree(local_buf, d_reclen, inode->i_sb);
+		}
+	}			/* while */
+
+      end:
+	pathrelse(&path_to_entry);
+	return 0;
+}
+
+/*
+ * this could be done with dedicated readdir ops for the xattr files,
+ * but I want to get something working asap
+ * this is stolen from vfs_readdir
+ *
+ */
+static
+int xattr_readdir(struct file *file, filldir_t filler, void *buf)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	int res = -ENOTDIR;
+	if (!file->f_op || !file->f_op->readdir)
+		goto out;
+	down(&inode->i_sem);
+//        down(&inode->i_zombie);
+	res = -ENOENT;
+	if (!IS_DEADDIR(inode)) {
+		lock_kernel();
+		res = __xattr_readdir(file, buf, filler);
+		unlock_kernel();
+	}
+//        up(&inode->i_zombie);
+	up(&inode->i_sem);
+      out:
+	return res;
+}
+
+/* Internal operations on file data */
+static inline void reiserfs_put_page(struct page *page)
+{
+	kunmap(page);
+	page_cache_release(page);
+}
+
+static struct page *reiserfs_get_page(struct inode *dir, unsigned long n)
+{
+	struct address_space *mapping = dir->i_mapping;
+	struct page *page;
+	/* We can deadlock if we try to free dentries,
+	   and an unlink/rmdir has just occured - GFP_NOFS avoids this */
+	mapping_set_gfp_mask(mapping, GFP_NOFS);
+	page = read_cache_page(mapping, n,
+			       (filler_t *) mapping->a_ops->readpage, NULL);
+	if (!IS_ERR(page)) {
+		wait_on_page_locked(page);
+		kmap(page);
+		if (!PageUptodate(page))
+			goto fail;
+
+		if (PageError(page))
+			goto fail;
+	}
+	return page;
+
+      fail:
+	reiserfs_put_page(page);
+	return ERR_PTR(-EIO);
+}
+
+static inline __u32 xattr_hash(const char *msg, int len)
+{
+	return csum_partial(msg, len, 0);
+}
+
+/* Generic extended attribute operations that can be used by xa plugins */
+
+/*
+ * inode->i_sem: down
+ */
+int
+reiserfs_xattr_set(struct inode *inode, const char *name, const void *buffer,
+		   size_t buffer_size, int flags)
+{
+	int err = 0;
+	struct file *fp;
+	struct page *page;
+	char *data;
+	struct address_space *mapping;
+	size_t file_pos = 0;
+	size_t buffer_pos = 0;
+	struct inode *xinode;
+	struct iattr newattrs;
+	__u32 xahash = 0;
+
+	if (IS_RDONLY(inode))
+		return -EROFS;
+
+	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+		return -EPERM;
+
+	if (get_inode_sd_version(inode) == STAT_DATA_V1)
+		return -EOPNOTSUPP;
+
+	/* Empty xattrs are ok, they're just empty files, no hash */
+	if (buffer && buffer_size)
+		xahash = xattr_hash(buffer, buffer_size);
+
+      open_file:
+	fp = open_xa_file(inode, name, flags);
+	if (IS_ERR(fp)) {
+		err = PTR_ERR(fp);
+		goto out;
+	}
+
+	xinode = fp->f_dentry->d_inode;
+	REISERFS_I(inode)->i_flags |= i_has_xattr_dir;
+
+	/* we need to copy it off.. */
+	if (xinode->i_nlink > 1) {
+		fput(fp);
+		err = reiserfs_xattr_del(inode, name);
+		if (err < 0)
+			goto out;
+		/* We just killed the old one, we're not replacing anymore */
+		if (flags & XATTR_REPLACE)
+			flags &= ~XATTR_REPLACE;
+		goto open_file;
+	}
+
+	/* Resize it so we're ok to write there */
+	newattrs.ia_size = buffer_size;
+	newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
+	down(&xinode->i_sem);
+	err = notify_change(fp->f_dentry, &newattrs);
+	if (err)
+		goto out_filp;
+
+	mapping = xinode->i_mapping;
+	while (buffer_pos < buffer_size || buffer_pos == 0) {
+		size_t chunk;
+		size_t skip = 0;
+		size_t page_offset = (file_pos & (PAGE_CACHE_SIZE - 1));
+		if (buffer_size - buffer_pos > PAGE_CACHE_SIZE)
+			chunk = PAGE_CACHE_SIZE;
+		else
+			chunk = buffer_size - buffer_pos;
+
+		page = reiserfs_get_page(xinode, file_pos >> PAGE_CACHE_SHIFT);
+		if (IS_ERR(page)) {
+			err = PTR_ERR(page);
+			goto out_filp;
+		}
+
+		lock_page(page);
+		data = page_address(page);
+
+		if (file_pos == 0) {
+			struct reiserfs_xattr_header *rxh;
+			skip = file_pos = sizeof(struct reiserfs_xattr_header);
+			if (chunk + skip > PAGE_CACHE_SIZE)
+				chunk = PAGE_CACHE_SIZE - skip;
+			rxh = (struct reiserfs_xattr_header *)data;
+			rxh->h_magic = cpu_to_le32(REISERFS_XATTR_MAGIC);
+			rxh->h_hash = cpu_to_le32(xahash);
+		}
+
+		err = mapping->a_ops->prepare_write(fp, page, page_offset,
+						    page_offset + chunk + skip);
+		if (!err) {
+			if (buffer)
+				memcpy(data + skip, buffer + buffer_pos, chunk);
+			err =
+			    mapping->a_ops->commit_write(fp, page, page_offset,
+							 page_offset + chunk +
+							 skip);
+		}
+		unlock_page(page);
+		reiserfs_put_page(page);
+		buffer_pos += chunk;
+		file_pos += chunk;
+		skip = 0;
+		if (err || buffer_size == 0 || !buffer)
+			break;
+	}
+
+	/* We can't mark the inode dirty if it's not hashed. This is the case
+	 * when we're inheriting the default ACL. If we dirty it, the inode
+	 * gets marked dirty, but won't (ever) make it onto the dirty list until
+	 * it's synced explicitly to clear I_DIRTY. This is bad. */
+	if (!hlist_unhashed(&inode->i_hash)) {
+		inode->i_ctime = CURRENT_TIME_SEC;
+		mark_inode_dirty(inode);
+	}
+
+      out_filp:
+	up(&xinode->i_sem);
+	fput(fp);
+
+      out:
+	return err;
+}
+
+/*
+ * inode->i_sem: down
+ */
+int
+reiserfs_xattr_get(const struct inode *inode, const char *name, void *buffer,
+		   size_t buffer_size)
+{
+	ssize_t err = 0;
+	struct file *fp;
+	size_t isize;
+	size_t file_pos = 0;
+	size_t buffer_pos = 0;
+	struct page *page;
+	struct inode *xinode;
+	__u32 hash = 0;
+
+	if (name == NULL)
+		return -EINVAL;
+
+	/* We can't have xattrs attached to v1 items since they don't have
+	 * generation numbers */
+	if (get_inode_sd_version(inode) == STAT_DATA_V1)
+		return -EOPNOTSUPP;
+
+	fp = open_xa_file(inode, name, FL_READONLY);
+	if (IS_ERR(fp)) {
+		err = PTR_ERR(fp);
+		goto out;
+	}
+
+	xinode = fp->f_dentry->d_inode;
+	isize = xinode->i_size;
+	REISERFS_I(inode)->i_flags |= i_has_xattr_dir;
+
+	/* Just return the size needed */
+	if (buffer == NULL) {
+		err = isize - sizeof(struct reiserfs_xattr_header);
+		goto out_dput;
+	}
+
+	if (buffer_size < isize - sizeof(struct reiserfs_xattr_header)) {
+		err = -ERANGE;
+		goto out_dput;
+	}
+
+	while (file_pos < isize) {
+		size_t chunk;
+		char *data;
+		size_t skip = 0;
+		if (isize - file_pos > PAGE_CACHE_SIZE)
+			chunk = PAGE_CACHE_SIZE;
+		else
+			chunk = isize - file_pos;
+
+		page = reiserfs_get_page(xinode, file_pos >> PAGE_CACHE_SHIFT);
+		if (IS_ERR(page)) {
+			err = PTR_ERR(page);
+			goto out_dput;
+		}
+
+		lock_page(page);
+		data = page_address(page);
+		if (file_pos == 0) {
+			struct reiserfs_xattr_header *rxh =
+			    (struct reiserfs_xattr_header *)data;
+			skip = file_pos = sizeof(struct reiserfs_xattr_header);
+			chunk -= skip;
+			/* Magic doesn't match up.. */
+			if (rxh->h_magic != cpu_to_le32(REISERFS_XATTR_MAGIC)) {
+				unlock_page(page);
+				reiserfs_put_page(page);
+				reiserfs_warning(inode->i_sb,
+						 "Invalid magic for xattr (%s) "
+						 "associated with %k", name,
+						 INODE_PKEY(inode));
+				err = -EIO;
+				goto out_dput;
+			}
+			hash = le32_to_cpu(rxh->h_hash);
+		}
+		memcpy(buffer + buffer_pos, data + skip, chunk);
+		unlock_page(page);
+		reiserfs_put_page(page);
+		file_pos += chunk;
+		buffer_pos += chunk;
+		skip = 0;
+	}
+	err = isize - sizeof(struct reiserfs_xattr_header);
+
+	if (xattr_hash(buffer, isize - sizeof(struct reiserfs_xattr_header)) !=
+	    hash) {
+		reiserfs_warning(inode->i_sb,
+				 "Invalid hash for xattr (%s) associated "
+				 "with %k", name, INODE_PKEY(inode));
+		err = -EIO;
+	}
+
+      out_dput:
+	fput(fp);
+
+      out:
+	return err;
+}
+
+static int
+__reiserfs_xattr_del(struct dentry *xadir, const char *name, int namelen)
+{
+	struct dentry *dentry;
+	struct inode *dir = xadir->d_inode;
+	int err = 0;
+
+	dentry = lookup_one_len(name, xadir, namelen);
+	if (IS_ERR(dentry)) {
+		err = PTR_ERR(dentry);
+		goto out;
+	} else if (!dentry->d_inode) {
+		err = -ENODATA;
+		goto out_file;
+	}
+
+	/* Skip directories.. */
+	if (S_ISDIR(dentry->d_inode->i_mode))
+		goto out_file;
+
+	if (!is_reiserfs_priv_object(dentry->d_inode)) {
+		reiserfs_warning(dir->i_sb, "OID %08x [%.*s/%.*s] doesn't have "
+				 "priv flag set [parent is %sset].",
+				 le32_to_cpu(INODE_PKEY(dentry->d_inode)->
+					     k_objectid), xadir->d_name.len,
+				 xadir->d_name.name, namelen, name,
+				 is_reiserfs_priv_object(xadir->
+							 d_inode) ? "" :
+				 "not ");
+		dput(dentry);
+		return -EIO;
+	}
+
+	err = dir->i_op->unlink(dir, dentry);
+	if (!err)
+		d_delete(dentry);
+
+      out_file:
+	dput(dentry);
+
+      out:
+	return err;
+}
+
+int reiserfs_xattr_del(struct inode *inode, const char *name)
+{
+	struct dentry *dir;
+	int err;
+
+	if (IS_RDONLY(inode))
+		return -EROFS;
+
+	dir = open_xa_dir(inode, FL_READONLY);
+	if (IS_ERR(dir)) {
+		err = PTR_ERR(dir);
+		goto out;
+	}
+
+	err = __reiserfs_xattr_del(dir, name, strlen(name));
+	dput(dir);
+
+	if (!err) {
+		inode->i_ctime = CURRENT_TIME_SEC;
+		mark_inode_dirty(inode);
+	}
+
+      out:
+	return err;
+}
+
+/* The following are side effects of other operations that aren't explicitly
+ * modifying extended attributes. This includes operations such as permissions
+ * or ownership changes, object deletions, etc. */
+
+static int
+reiserfs_delete_xattrs_filler(void *buf, const char *name, int namelen,
+			      loff_t offset, ino_t ino, unsigned int d_type)
+{
+	struct dentry *xadir = (struct dentry *)buf;
+
+	return __reiserfs_xattr_del(xadir, name, namelen);
+
+}
+
+/* This is called w/ inode->i_sem downed */
+int reiserfs_delete_xattrs(struct inode *inode)
+{
+	struct file *fp;
+	struct dentry *dir, *root;
+	int err = 0;
+
+	/* Skip out, an xattr has no xattrs associated with it */
+	if (is_reiserfs_priv_object(inode) ||
+	    get_inode_sd_version(inode) == STAT_DATA_V1 ||
+	    !reiserfs_xattrs(inode->i_sb)) {
+		return 0;
+	}
+	reiserfs_read_lock_xattrs(inode->i_sb);
+	dir = open_xa_dir(inode, FL_READONLY);
+	reiserfs_read_unlock_xattrs(inode->i_sb);
+	if (IS_ERR(dir)) {
+		err = PTR_ERR(dir);
+		goto out;
+	} else if (!dir->d_inode) {
+		dput(dir);
+		return 0;
+	}
+
+	fp = dentry_open(dir, NULL, O_RDWR);
+	if (IS_ERR(fp)) {
+		err = PTR_ERR(fp);
+		/* dentry_open dputs the dentry if it fails */
+		goto out;
+	}
+
+	lock_kernel();
+	err = xattr_readdir(fp, reiserfs_delete_xattrs_filler, dir);
+	if (err) {
+		unlock_kernel();
+		goto out_dir;
+	}
+
+	/* Leftovers besides . and .. -- that's not good. */
+	if (dir->d_inode->i_nlink <= 2) {
+		root = get_xa_root(inode->i_sb);
+		reiserfs_write_lock_xattrs(inode->i_sb);
+		err = vfs_rmdir(root->d_inode, dir);
+		reiserfs_write_unlock_xattrs(inode->i_sb);
+		dput(root);
+	} else {
+		reiserfs_warning(inode->i_sb,
+				 "Couldn't remove all entries in directory");
+	}
+	unlock_kernel();
+
+      out_dir:
+	fput(fp);
+
+      out:
+	if (!err)
+		REISERFS_I(inode)->i_flags =
+		    REISERFS_I(inode)->i_flags & ~i_has_xattr_dir;
+	return err;
+}
+
+struct reiserfs_chown_buf {
+	struct inode *inode;
+	struct dentry *xadir;
+	struct iattr *attrs;
+};
+
+/* XXX: If there is a better way to do this, I'd love to hear about it */
+static int
+reiserfs_chown_xattrs_filler(void *buf, const char *name, int namelen,
+			     loff_t offset, ino_t ino, unsigned int d_type)
+{
+	struct reiserfs_chown_buf *chown_buf = (struct reiserfs_chown_buf *)buf;
+	struct dentry *xafile, *xadir = chown_buf->xadir;
+	struct iattr *attrs = chown_buf->attrs;
+	int err = 0;
+
+	xafile = lookup_one_len(name, xadir, namelen);
+	if (IS_ERR(xafile))
+		return PTR_ERR(xafile);
+	else if (!xafile->d_inode) {
+		dput(xafile);
+		return -ENODATA;
+	}
+
+	if (!S_ISDIR(xafile->d_inode->i_mode))
+		err = notify_change(xafile, attrs);
+	dput(xafile);
+
+	return err;
+}
+
+int reiserfs_chown_xattrs(struct inode *inode, struct iattr *attrs)
+{
+	struct file *fp;
+	struct dentry *dir;
+	int err = 0;
+	struct reiserfs_chown_buf buf;
+	unsigned int ia_valid = attrs->ia_valid;
+
+	/* Skip out, an xattr has no xattrs associated with it */
+	if (is_reiserfs_priv_object(inode) ||
+	    get_inode_sd_version(inode) == STAT_DATA_V1 ||
+	    !reiserfs_xattrs(inode->i_sb)) {
+		return 0;
+	}
+	reiserfs_read_lock_xattrs(inode->i_sb);
+	dir = open_xa_dir(inode, FL_READONLY);
+	reiserfs_read_unlock_xattrs(inode->i_sb);
+	if (IS_ERR(dir)) {
+		if (PTR_ERR(dir) != -ENODATA)
+			err = PTR_ERR(dir);
+		goto out;
+	} else if (!dir->d_inode) {
+		dput(dir);
+		goto out;
+	}
+
+	fp = dentry_open(dir, NULL, O_RDWR);
+	if (IS_ERR(fp)) {
+		err = PTR_ERR(fp);
+		/* dentry_open dputs the dentry if it fails */
+		goto out;
+	}
+
+	lock_kernel();
+
+	attrs->ia_valid &= (ATTR_UID | ATTR_GID | ATTR_CTIME);
+	buf.xadir = dir;
+	buf.attrs = attrs;
+	buf.inode = inode;
+
+	err = xattr_readdir(fp, reiserfs_chown_xattrs_filler, &buf);
+	if (err) {
+		unlock_kernel();
+		goto out_dir;
+	}
+
+	err = notify_change(dir, attrs);
+	unlock_kernel();
+
+      out_dir:
+	fput(fp);
+
+      out:
+	attrs->ia_valid = ia_valid;
+	return err;
+}
+
+/* Actual operations that are exported to VFS-land */
+
+/*
+ * Inode operation getxattr()
+ * Preliminary locking: we down dentry->d_inode->i_sem
+ */
+ssize_t
+reiserfs_getxattr(struct dentry * dentry, const char *name, void *buffer,
+		  size_t size)
+{
+	struct reiserfs_xattr_handler *xah = find_xattr_handler_prefix(name);
+	int err;
+
+	if (!xah || !reiserfs_xattrs(dentry->d_sb) ||
+	    get_inode_sd_version(dentry->d_inode) == STAT_DATA_V1)
+		return -EOPNOTSUPP;
+
+	reiserfs_read_lock_xattr_i(dentry->d_inode);
+	reiserfs_read_lock_xattrs(dentry->d_sb);
+	err = xah->get(dentry->d_inode, name, buffer, size);
+	reiserfs_read_unlock_xattrs(dentry->d_sb);
+	reiserfs_read_unlock_xattr_i(dentry->d_inode);
+	return err;
+}
+
+/*
+ * Inode operation setxattr()
+ *
+ * dentry->d_inode->i_sem down
+ */
+int
+reiserfs_setxattr(struct dentry *dentry, const char *name, const void *value,
+		  size_t size, int flags)
+{
+	struct reiserfs_xattr_handler *xah = find_xattr_handler_prefix(name);
+	int err;
+	int lock;
+
+	if (!xah || !reiserfs_xattrs(dentry->d_sb) ||
+	    get_inode_sd_version(dentry->d_inode) == STAT_DATA_V1)
+		return -EOPNOTSUPP;
+
+	if (IS_RDONLY(dentry->d_inode))
+		return -EROFS;
+
+	if (IS_IMMUTABLE(dentry->d_inode) || IS_APPEND(dentry->d_inode))
+		return -EROFS;
+
+	reiserfs_write_lock_xattr_i(dentry->d_inode);
+	lock = !has_xattr_dir(dentry->d_inode);
+	if (lock)
+		reiserfs_write_lock_xattrs(dentry->d_sb);
+	else
+		reiserfs_read_lock_xattrs(dentry->d_sb);
+	err = xah->set(dentry->d_inode, name, value, size, flags);
+	if (lock)
+		reiserfs_write_unlock_xattrs(dentry->d_sb);
+	else
+		reiserfs_read_unlock_xattrs(dentry->d_sb);
+	reiserfs_write_unlock_xattr_i(dentry->d_inode);
+	return err;
+}
+
+/*
+ * Inode operation removexattr()
+ *
+ * dentry->d_inode->i_sem down
+ */
+int reiserfs_removexattr(struct dentry *dentry, const char *name)
+{
+	int err;
+	struct reiserfs_xattr_handler *xah = find_xattr_handler_prefix(name);
+
+	if (!xah || !reiserfs_xattrs(dentry->d_sb) ||
+	    get_inode_sd_version(dentry->d_inode) == STAT_DATA_V1)
+		return -EOPNOTSUPP;
+
+	if (IS_RDONLY(dentry->d_inode))
+		return -EROFS;
+
+	if (IS_IMMUTABLE(dentry->d_inode) || IS_APPEND(dentry->d_inode))
+		return -EPERM;
+
+	reiserfs_write_lock_xattr_i(dentry->d_inode);
+	reiserfs_read_lock_xattrs(dentry->d_sb);
+
+	/* Deletion pre-operation */
+	if (xah->del) {
+		err = xah->del(dentry->d_inode, name);
+		if (err)
+			goto out;
+	}
+
+	err = reiserfs_xattr_del(dentry->d_inode, name);
+
+	dentry->d_inode->i_ctime = CURRENT_TIME_SEC;
+	mark_inode_dirty(dentry->d_inode);
+
+      out:
+	reiserfs_read_unlock_xattrs(dentry->d_sb);
+	reiserfs_write_unlock_xattr_i(dentry->d_inode);
+	return err;
+}
+
+/* This is what filldir will use:
+ * r_pos will always contain the amount of space required for the entire
+ * list. If r_pos becomes larger than r_size, we need more space and we
+ * return an error indicating this. If r_pos is less than r_size, then we've
+ * filled the buffer successfully and we return success */
+struct reiserfs_listxattr_buf {
+	int r_pos;
+	int r_size;
+	char *r_buf;
+	struct inode *r_inode;
+};
+
+static int
+reiserfs_listxattr_filler(void *buf, const char *name, int namelen,
+			  loff_t offset, ino_t ino, unsigned int d_type)
+{
+	struct reiserfs_listxattr_buf *b = (struct reiserfs_listxattr_buf *)buf;
+	int len = 0;
+	if (name[0] != '.'
+	    || (namelen != 1 && (name[1] != '.' || namelen != 2))) {
+		struct reiserfs_xattr_handler *xah =
+		    find_xattr_handler_prefix(name);
+		if (!xah)
+			return 0;	/* Unsupported xattr name, skip it */
+
+		/* We call ->list() twice because the operation isn't required to just
+		 * return the name back - we want to make sure we have enough space */
+		len += xah->list(b->r_inode, name, namelen, NULL);
+
+		if (len) {
+			if (b->r_pos + len + 1 <= b->r_size) {
+				char *p = b->r_buf + b->r_pos;
+				p += xah->list(b->r_inode, name, namelen, p);
+				*p++ = '\0';
+			}
+			b->r_pos += len + 1;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Inode operation listxattr()
+ *
+ * Preliminary locking: we down dentry->d_inode->i_sem
+ */
+ssize_t reiserfs_listxattr(struct dentry * dentry, char *buffer, size_t size)
+{
+	struct file *fp;
+	struct dentry *dir;
+	int err = 0;
+	struct reiserfs_listxattr_buf buf;
+
+	if (!dentry->d_inode)
+		return -EINVAL;
+
+	if (!reiserfs_xattrs(dentry->d_sb) ||
+	    get_inode_sd_version(dentry->d_inode) == STAT_DATA_V1)
+		return -EOPNOTSUPP;
+
+	reiserfs_read_lock_xattr_i(dentry->d_inode);
+	reiserfs_read_lock_xattrs(dentry->d_sb);
+	dir = open_xa_dir(dentry->d_inode, FL_READONLY);
+	reiserfs_read_unlock_xattrs(dentry->d_sb);
+	if (IS_ERR(dir)) {
+		err = PTR_ERR(dir);
+		if (err == -ENODATA)
+			err = 0;	/* Not an error if there aren't any xattrs */
+		goto out;
+	}
+
+	fp = dentry_open(dir, NULL, O_RDWR);
+	if (IS_ERR(fp)) {
+		err = PTR_ERR(fp);
+		/* dentry_open dputs the dentry if it fails */
+		goto out;
+	}
+
+	buf.r_buf = buffer;
+	buf.r_size = buffer ? size : 0;
+	buf.r_pos = 0;
+	buf.r_inode = dentry->d_inode;
+
+	REISERFS_I(dentry->d_inode)->i_flags |= i_has_xattr_dir;
+
+	err = xattr_readdir(fp, reiserfs_listxattr_filler, &buf);
+	if (err)
+		goto out_dir;
+
+	if (buf.r_pos > buf.r_size && buffer != NULL)
+		err = -ERANGE;
+	else
+		err = buf.r_pos;
+
+      out_dir:
+	fput(fp);
+
+      out:
+	reiserfs_read_unlock_xattr_i(dentry->d_inode);
+	return err;
+}
+
+/* This is the implementation for the xattr plugin infrastructure */
+static struct list_head xattr_handlers = LIST_HEAD_INIT(xattr_handlers);
+static DEFINE_RWLOCK(handler_lock);
+
+static struct reiserfs_xattr_handler *find_xattr_handler_prefix(const char
+								*prefix)
+{
+	struct reiserfs_xattr_handler *xah = NULL;
+	struct list_head *p;
+
+	read_lock(&handler_lock);
+	list_for_each(p, &xattr_handlers) {
+		xah = list_entry(p, struct reiserfs_xattr_handler, handlers);
+		if (strncmp(xah->prefix, prefix, strlen(xah->prefix)) == 0)
+			break;
+		xah = NULL;
+	}
+
+	read_unlock(&handler_lock);
+	return xah;
+}
+
+static void __unregister_handlers(void)
+{
+	struct reiserfs_xattr_handler *xah;
+	struct list_head *p, *tmp;
+
+	list_for_each_safe(p, tmp, &xattr_handlers) {
+		xah = list_entry(p, struct reiserfs_xattr_handler, handlers);
+		if (xah->exit)
+			xah->exit();
+
+		list_del_init(p);
+	}
+	INIT_LIST_HEAD(&xattr_handlers);
+}
+
+int __init reiserfs_xattr_register_handlers(void)
+{
+	int err = 0;
+	struct reiserfs_xattr_handler *xah;
+	struct list_head *p;
+
+	write_lock(&handler_lock);
+
+	/* If we're already initialized, nothing to do */
+	if (!list_empty(&xattr_handlers)) {
+		write_unlock(&handler_lock);
+		return 0;
+	}
+
+	/* Add the handlers */
+	list_add_tail(&user_handler.handlers, &xattr_handlers);
+	list_add_tail(&trusted_handler.handlers, &xattr_handlers);
+#ifdef CONFIG_REISERFS_FS_SECURITY
+	list_add_tail(&security_handler.handlers, &xattr_handlers);
+#endif
+#ifdef CONFIG_REISERFS_FS_POSIX_ACL
+	list_add_tail(&posix_acl_access_handler.handlers, &xattr_handlers);
+	list_add_tail(&posix_acl_default_handler.handlers, &xattr_handlers);
+#endif
+
+	/* Run initializers, if available */
+	list_for_each(p, &xattr_handlers) {
+		xah = list_entry(p, struct reiserfs_xattr_handler, handlers);
+		if (xah->init) {
+			err = xah->init();
+			if (err) {
+				list_del_init(p);
+				break;
+			}
+		}
+	}
+
+	/* Clean up other handlers, if any failed */
+	if (err)
+		__unregister_handlers();
+
+	write_unlock(&handler_lock);
+	return err;
+}
+
+void reiserfs_xattr_unregister_handlers(void)
+{
+	write_lock(&handler_lock);
+	__unregister_handlers();
+	write_unlock(&handler_lock);
+}
+
+/* This will catch lookups from the fs root to .reiserfs_priv */
+static int
+xattr_lookup_poison(struct dentry *dentry, struct qstr *q1, struct qstr *name)
+{
+	struct dentry *priv_root = REISERFS_SB(dentry->d_sb)->priv_root;
+	if (name->len == priv_root->d_name.len &&
+	    name->hash == priv_root->d_name.hash &&
+	    !memcmp(name->name, priv_root->d_name.name, name->len)) {
+		return -ENOENT;
+	} else if (q1->len == name->len &&
+		   !memcmp(q1->name, name->name, name->len))
+		return 0;
+	return 1;
+}
+
+static struct dentry_operations xattr_lookup_poison_ops = {
+	.d_compare = xattr_lookup_poison,
+};
+
+/* We need to take a copy of the mount flags since things like
+ * MS_RDONLY don't get set until *after* we're called.
+ * mount_flags != mount_options */
+int reiserfs_xattr_init(struct super_block *s, int mount_flags)
+{
+	int err = 0;
+
+	/* We need generation numbers to ensure that the oid mapping is correct
+	 * v3.5 filesystems don't have them. */
+	if (!old_format_only(s)) {
+		set_bit(REISERFS_XATTRS, &(REISERFS_SB(s)->s_mount_opt));
+	} else if (reiserfs_xattrs_optional(s)) {
+		/* Old format filesystem, but optional xattrs have been enabled
+		 * at mount time. Error out. */
+		reiserfs_warning(s, "xattrs/ACLs not supported on pre v3.6 "
+				 "format filesystem. Failing mount.");
+		err = -EOPNOTSUPP;
+		goto error;
+	} else {
+		/* Old format filesystem, but no optional xattrs have been enabled. This
+		 * means we silently disable xattrs on the filesystem. */
+		clear_bit(REISERFS_XATTRS, &(REISERFS_SB(s)->s_mount_opt));
+	}
+
+	/* If we don't have the privroot located yet - go find it */
+	if (reiserfs_xattrs(s) && !REISERFS_SB(s)->priv_root) {
+		struct dentry *dentry;
+		dentry = lookup_one_len(PRIVROOT_NAME, s->s_root,
+					strlen(PRIVROOT_NAME));
+		if (!IS_ERR(dentry)) {
+			if (!(mount_flags & MS_RDONLY) && !dentry->d_inode) {
+				struct inode *inode = dentry->d_parent->d_inode;
+				down(&inode->i_sem);
+				err = inode->i_op->mkdir(inode, dentry, 0700);
+				up(&inode->i_sem);
+				if (err) {
+					dput(dentry);
+					dentry = NULL;
+				}
+
+				if (dentry && dentry->d_inode)
+					reiserfs_warning(s,
+							 "Created %s on %s - reserved for "
+							 "xattr storage.",
+							 PRIVROOT_NAME,
+							 reiserfs_bdevname
+							 (inode->i_sb));
+			} else if (!dentry->d_inode) {
+				dput(dentry);
+				dentry = NULL;
+			}
+		} else
+			err = PTR_ERR(dentry);
+
+		if (!err && dentry) {
+			s->s_root->d_op = &xattr_lookup_poison_ops;
+			reiserfs_mark_inode_private(dentry->d_inode);
+			REISERFS_SB(s)->priv_root = dentry;
+		} else if (!(mount_flags & MS_RDONLY)) {	/* xattrs are unavailable */
+			/* If we're read-only it just means that the dir hasn't been
+			 * created. Not an error -- just no xattrs on the fs. We'll
+			 * check again if we go read-write */
+			reiserfs_warning(s, "xattrs/ACLs enabled and couldn't "
+					 "find/create .reiserfs_priv. Failing mount.");
+			err = -EOPNOTSUPP;
+		}
+	}
+
+      error:
+	/* This is only nonzero if there was an error initializing the xattr
+	 * directory or if there is a condition where we don't support them. */
+	if (err) {
+		clear_bit(REISERFS_XATTRS, &(REISERFS_SB(s)->s_mount_opt));
+		clear_bit(REISERFS_XATTRS_USER, &(REISERFS_SB(s)->s_mount_opt));
+		clear_bit(REISERFS_POSIXACL, &(REISERFS_SB(s)->s_mount_opt));
+	}
+
+	/* The super_block MS_POSIXACL must mirror the (no)acl mount option. */
+	s->s_flags = s->s_flags & ~MS_POSIXACL;
+	if (reiserfs_posixacl(s))
+		s->s_flags |= MS_POSIXACL;
+
+	return err;
+}
+
+static int
+__reiserfs_permission(struct inode *inode, int mask, struct nameidata *nd,
+		      int need_lock)
+{
+	umode_t mode = inode->i_mode;
+
+	if (mask & MAY_WRITE) {
+		/*
+		 * Nobody gets write access to a read-only fs.
+		 */
+		if (IS_RDONLY(inode) &&
+		    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
+			return -EROFS;
+
+		/*
+		 * Nobody gets write access to an immutable file.
+		 */
+		if (IS_IMMUTABLE(inode))
+			return -EACCES;
+	}
+
+	/* We don't do permission checks on the internal objects.
+	 * Permissions are determined by the "owning" object. */
+	if (is_reiserfs_priv_object(inode))
+		return 0;
+
+	if (current->fsuid == inode->i_uid) {
+		mode >>= 6;
+#ifdef CONFIG_REISERFS_FS_POSIX_ACL
+	} else if (reiserfs_posixacl(inode->i_sb) &&
+		   get_inode_sd_version(inode) != STAT_DATA_V1) {
+		struct posix_acl *acl;
+
+		/* ACL can't contain additional permissions if
+		   the ACL_MASK entry is 0 */
+		if (!(mode & S_IRWXG))
+			goto check_groups;
+
+		if (need_lock) {
+			reiserfs_read_lock_xattr_i(inode);
+			reiserfs_read_lock_xattrs(inode->i_sb);
+		}
+		acl = reiserfs_get_acl(inode, ACL_TYPE_ACCESS);
+		if (need_lock) {
+			reiserfs_read_unlock_xattrs(inode->i_sb);
+			reiserfs_read_unlock_xattr_i(inode);
+		}
+		if (IS_ERR(acl)) {
+			if (PTR_ERR(acl) == -ENODATA)
+				goto check_groups;
+			return PTR_ERR(acl);
+		}
+
+		if (acl) {
+			int err = posix_acl_permission(inode, acl, mask);
+			posix_acl_release(acl);
+			if (err == -EACCES) {
+				goto check_capabilities;
+			}
+			return err;
+		} else {
+			goto check_groups;
+		}
+#endif
+	} else {
+	      check_groups:
+		if (in_group_p(inode->i_gid))
+			mode >>= 3;
+	}
+
+	/*
+	 * If the DACs are ok we don't need any capability check.
+	 */
+	if (((mode & mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == mask))
+		return 0;
+
+      check_capabilities:
+	/*
+	 * Read/write DACs are always overridable.
+	 * Executable DACs are overridable if at least one exec bit is set.
+	 */
+	if (!(mask & MAY_EXEC) ||
+	    (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode))
+		if (capable(CAP_DAC_OVERRIDE))
+			return 0;
+
+	/*
+	 * Searching includes executable on directories, else just read.
+	 */
+	if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))
+		if (capable(CAP_DAC_READ_SEARCH))
+			return 0;
+
+	return -EACCES;
+}
+
+int reiserfs_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+	return __reiserfs_permission(inode, mask, nd, 1);
+}
+
+int
+reiserfs_permission_locked(struct inode *inode, int mask, struct nameidata *nd)
+{
+	return __reiserfs_permission(inode, mask, nd, 0);
+}
diff -urN oldtree/fs/select.c newtree/fs/select.c
--- oldtree/fs/select.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/select.c	2006-02-13 19:20:00.732400960 -0500
@@ -29,12 +29,6 @@
 #define ROUND_UP(x,y) (((x)+(y)-1)/(y))
 #define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)
 
-struct poll_table_entry {
-	struct file * filp;
-	wait_queue_t wait;
-	wait_queue_head_t * wait_address;
-};
-
 struct poll_table_page {
 	struct poll_table_page * next;
 	struct poll_table_entry * entry;
@@ -64,13 +58,23 @@
 	init_poll_funcptr(&pwq->pt, __pollwait);
 	pwq->error = 0;
 	pwq->table = NULL;
+	pwq->inline_index = 0;
 }
 
 EXPORT_SYMBOL(poll_initwait);
 
+static void free_poll_entry(struct poll_table_entry *entry)
+{
+	remove_wait_queue(entry->wait_address,&entry->wait);
+	fput(entry->filp);
+}
+
 void poll_freewait(struct poll_wqueues *pwq)
 {
 	struct poll_table_page * p = pwq->table;
+	int i;
+	for (i = 0; i < pwq->inline_index; i++)
+		free_poll_entry(pwq->inline_entries + i);
 	while (p) {
 		struct poll_table_entry * entry;
 		struct poll_table_page *old;
@@ -78,8 +82,7 @@
 		entry = p->entry;
 		do {
 			entry--;
-			remove_wait_queue(entry->wait_address,&entry->wait);
-			fput(entry->filp);
+			free_poll_entry(entry);
 		} while (entry > p->entries);
 		old = p;
 		p = p->next;
@@ -89,12 +92,14 @@
 
 EXPORT_SYMBOL(poll_freewait);
 
-static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
-		       poll_table *_p)
+static struct poll_table_entry *poll_get_entry(poll_table *_p)
 {
 	struct poll_wqueues *p = container_of(_p, struct poll_wqueues, pt);
 	struct poll_table_page *table = p->table;
 
+	if (p->inline_index < N_INLINE_POLL_ENTRIES)
+		return p->inline_entries + p->inline_index++;
+
 	if (!table || POLL_TABLE_FULL(table)) {
 		struct poll_table_page *new_table;
 
@@ -102,7 +107,7 @@
 		if (!new_table) {
 			p->error = -ENOMEM;
 			__set_current_state(TASK_RUNNING);
-			return;
+			return NULL;
 		}
 		new_table->entry = new_table->entries;
 		new_table->next = table;
@@ -110,16 +115,21 @@
 		table = new_table;
 	}
 
-	/* Add a new entry */
-	{
-		struct poll_table_entry * entry = table->entry;
-		table->entry = entry+1;
-	 	get_file(filp);
-	 	entry->filp = filp;
-		entry->wait_address = wait_address;
-		init_waitqueue_entry(&entry->wait, current);
-		add_wait_queue(wait_address,&entry->wait);
-	}
+	return table->entry++;
+}
+
+/* Add a new entry */
+static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
+		       poll_table *p)
+{
+	struct poll_table_entry *entry = poll_get_entry(p);
+	if (!entry)
+		return;
+	get_file(filp);
+	entry->filp = filp;
+	entry->wait_address = wait_address;
+	init_waitqueue_entry(&entry->wait, current);
+	add_wait_queue(wait_address,&entry->wait);
 }
 
 #define FDS_IN(fds, n)		(fds->in + n)
@@ -221,17 +231,18 @@
 			}
 
 			for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1) {
+				int fput_needed;
 				if (i >= n)
 					break;
 				if (!(bit & all_bits))
 					continue;
-				file = fget(i);
+				file = fget_light(i, &fput_needed);
 				if (file) {
 					f_op = file->f_op;
 					mask = DEFAULT_POLLMASK;
 					if (f_op && f_op->poll)
 						mask = (*f_op->poll)(file, retval ? NULL : wait);
-					fput(file);
+					fput_light(file, fput_needed);
 					if ((mask & POLLIN_SET) && (in & bit)) {
 						res_in |= bit;
 						retval++;
@@ -274,16 +285,6 @@
 	return retval;
 }
 
-static void *select_bits_alloc(int size)
-{
-	return kmalloc(6 * size, GFP_KERNEL);
-}
-
-static void select_bits_free(void *bits, int size)
-{
-	kfree(bits);
-}
-
 /*
  * We can actually return ERESTARTSYS instead of EINTR, but I'd
  * like to be certain this leads to no problems. So I return
@@ -303,6 +304,8 @@
 	long timeout;
 	int ret, size, max_fdset;
 	struct fdtable *fdt;
+	/* Allocate small arguments on the stack to save memory and be faster */
+	char stack_fds[SELECT_STACK_ALLOC];
 
 	timeout = MAX_SCHEDULE_TIMEOUT;
 	if (tvp) {
@@ -344,7 +347,10 @@
 	 */
 	ret = -ENOMEM;
 	size = FDS_BYTES(n);
-	bits = select_bits_alloc(size);
+	if (6*size < SELECT_STACK_ALLOC)
+		bits = stack_fds;
+	else
+		bits = kmalloc(6 * size, GFP_KERNEL);
 	if (!bits)
 		goto out_nofds;
 	fds.in      = (unsigned long *)  bits;
@@ -390,7 +396,8 @@
 		ret = -EFAULT;
 
 out:
-	select_bits_free(bits, size);
+	if (bits != stack_fds)
+		kfree(bits);
 out_nofds:
 	return ret;
 }
@@ -417,14 +424,15 @@
 		fdp = fdpage+i;
 		fd = fdp->fd;
 		if (fd >= 0) {
-			struct file * file = fget(fd);
+			int fput_needed;
+			struct file * file = fget_light(fd, &fput_needed);
 			mask = POLLNVAL;
 			if (file != NULL) {
 				mask = DEFAULT_POLLMASK;
 				if (file->f_op && file->f_op->poll)
 					mask = file->f_op->poll(file, *pwait);
 				mask &= fdp->events | POLLERR | POLLHUP;
-				fput(file);
+				fput_light(file, fput_needed);
 			}
 			if (mask) {
 				*pwait = NULL;
@@ -464,6 +472,8 @@
 	return count;
 }
 
+#define N_STACK_PPS ((sizeof(stack_pps) - sizeof(struct poll_list))  / sizeof(struct pollfd))
+
 asmlinkage long sys_poll(struct pollfd __user * ufds, unsigned int nfds, long timeout)
 {
 	struct poll_wqueues table;
@@ -473,6 +483,9 @@
  	struct poll_list *walk;
 	struct fdtable *fdt;
 	int max_fdset;
+	/* Allocate small arguments on the stack to save memory and be faster */
+	char stack_pps[POLL_STACK_ALLOC];
+	struct poll_list *stack_pp = NULL;
 
 	/* Do a sanity check on nfds ... */
 	rcu_read_lock();
@@ -498,14 +511,23 @@
 	err = -ENOMEM;
 	while(i!=0) {
 		struct poll_list *pp;
-		pp = kmalloc(sizeof(struct poll_list)+
-				sizeof(struct pollfd)*
-				(i>POLLFD_PER_PAGE?POLLFD_PER_PAGE:i),
-					GFP_KERNEL);
-		if(pp==NULL)
-			goto out_fds;
+		int num, size;
+		if (stack_pp == NULL)
+			num = N_STACK_PPS;
+		else
+			num = POLLFD_PER_PAGE;
+		if (num > i)
+			num = i;
+		size = sizeof(struct poll_list) + sizeof(struct pollfd)*num;
+		if (!stack_pp)
+			stack_pp = pp = (struct poll_list *)stack_pps;
+		else {
+			pp = kmalloc(size, GFP_KERNEL);
+			if (!pp)
+				goto out_fds;
+		}
 		pp->next=NULL;
-		pp->len = (i>POLLFD_PER_PAGE?POLLFD_PER_PAGE:i);
+		pp->len = num;
 		if (head == NULL)
 			head = pp;
 		else
@@ -513,7 +535,7 @@
 
 		walk = pp;
 		if (copy_from_user(pp->entries, ufds + nfds-i, 
-				sizeof(struct pollfd)*pp->len)) {
+				sizeof(struct pollfd)*num)) {
 			err = -EFAULT;
 			goto out_fds;
 		}
@@ -541,7 +563,8 @@
 	walk = head;
 	while(walk!=NULL) {
 		struct poll_list *pp = walk->next;
-		kfree(walk);
+		if (walk != stack_pp)
+			kfree(walk);
 		walk = pp;
 	}
 	poll_freewait(&table);
diff -urN oldtree/fs/select.c.orig newtree/fs/select.c.orig
--- oldtree/fs/select.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/select.c.orig	2006-02-13 19:20:00.733400808 -0500
@@ -0,0 +1,570 @@
+/*
+ * This file contains the procedures for the handling of select and poll
+ *
+ * Created for Linux based loosely upon Mathius Lattner's minix
+ * patches by Peter MacDonald. Heavily edited by Linus.
+ *
+ *  4 February 1994
+ *     COFF/ELF binary emulation. If the process has the STICKY_TIMEOUTS
+ *     flag set in its personality we do *not* modify the given timeout
+ *     parameter to reflect time remaining.
+ *
+ *  24 January 2000
+ *     Changed sys_poll()/do_poll() to use PAGE_SIZE chunk-based allocation 
+ *     of fds to overcome nfds < 16390 descriptors limit (Tigran Aivazian).
+ */
+
+#include <linux/syscalls.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/poll.h>
+#include <linux/personality.h> /* for STICKY_TIMEOUTS */
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/rcupdate.h>
+
+#include <asm/uaccess.h>
+
+#define ROUND_UP(x,y) (((x)+(y)-1)/(y))
+#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)
+
+struct poll_table_page {
+	struct poll_table_page * next;
+	struct poll_table_entry * entry;
+	struct poll_table_entry entries[0];
+};
+
+#define POLL_TABLE_FULL(table) \
+	((unsigned long)((table)->entry+1) > PAGE_SIZE + (unsigned long)(table))
+
+/*
+ * Ok, Peter made a complicated, but straightforward multiple_wait() function.
+ * I have rewritten this, taking some shortcuts: This code may not be easy to
+ * follow, but it should be free of race-conditions, and it's practical. If you
+ * understand what I'm doing here, then you understand how the linux
+ * sleep/wakeup mechanism works.
+ *
+ * Two very simple procedures, poll_wait() and poll_freewait() make all the
+ * work.  poll_wait() is an inline-function defined in <linux/poll.h>,
+ * as all select/poll functions have to call it to add an entry to the
+ * poll table.
+ */
+static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
+		       poll_table *p);
+
+void poll_initwait(struct poll_wqueues *pwq)
+{
+	init_poll_funcptr(&pwq->pt, __pollwait);
+	pwq->error = 0;
+	pwq->table = NULL;
+	pwq->inline_index = 0;
+}
+
+EXPORT_SYMBOL(poll_initwait);
+
+static void free_poll_entry(struct poll_table_entry *entry)
+{
+	remove_wait_queue(entry->wait_address,&entry->wait);
+	fput(entry->filp);
+}
+
+void poll_freewait(struct poll_wqueues *pwq)
+{
+	struct poll_table_page * p = pwq->table;
+	int i;
+	for (i = 0; i < pwq->inline_index; i++)
+		free_poll_entry(pwq->inline_entries + i);
+	while (p) {
+		struct poll_table_entry * entry;
+		struct poll_table_page *old;
+
+		entry = p->entry;
+		do {
+			entry--;
+			free_poll_entry(entry);
+		} while (entry > p->entries);
+		old = p;
+		p = p->next;
+		free_page((unsigned long) old);
+	}
+}
+
+EXPORT_SYMBOL(poll_freewait);
+
+static struct poll_table_entry *poll_get_entry(poll_table *_p)
+{
+	struct poll_wqueues *p = container_of(_p, struct poll_wqueues, pt);
+	struct poll_table_page *table = p->table;
+
+	if (p->inline_index < N_INLINE_POLL_ENTRIES)
+		return p->inline_entries + p->inline_index++;
+
+	if (!table || POLL_TABLE_FULL(table)) {
+		struct poll_table_page *new_table;
+
+		new_table = (struct poll_table_page *) __get_free_page(GFP_KERNEL);
+		if (!new_table) {
+			p->error = -ENOMEM;
+			__set_current_state(TASK_RUNNING);
+			return NULL;
+		}
+		new_table->entry = new_table->entries;
+		new_table->next = table;
+		p->table = new_table;
+		table = new_table;
+	}
+
+	return table->entry++;
+}
+
+/* Add a new entry */
+static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
+		       poll_table *p)
+{
+	struct poll_table_entry *entry = poll_get_entry(p);
+	if (!entry)
+		return;
+	get_file(filp);
+	entry->filp = filp;
+	entry->wait_address = wait_address;
+	init_waitqueue_entry(&entry->wait, current);
+	add_wait_queue(wait_address,&entry->wait);
+}
+
+#define FDS_IN(fds, n)		(fds->in + n)
+#define FDS_OUT(fds, n)		(fds->out + n)
+#define FDS_EX(fds, n)		(fds->ex + n)
+
+#define BITS(fds, n)	(*FDS_IN(fds, n)|*FDS_OUT(fds, n)|*FDS_EX(fds, n))
+
+static int max_select_fd(unsigned long n, fd_set_bits *fds)
+{
+	unsigned long *open_fds;
+	unsigned long set;
+	int max;
+	struct fdtable *fdt;
+
+	/* handle last in-complete long-word first */
+	set = ~(~0UL << (n & (__NFDBITS-1)));
+	n /= __NFDBITS;
+	fdt = files_fdtable(current->files);
+	open_fds = fdt->open_fds->fds_bits+n;
+	max = 0;
+	if (set) {
+		set &= BITS(fds, n);
+		if (set) {
+			if (!(set & ~*open_fds))
+				goto get_max;
+			return -EBADF;
+		}
+	}
+	while (n) {
+		open_fds--;
+		n--;
+		set = BITS(fds, n);
+		if (!set)
+			continue;
+		if (set & ~*open_fds)
+			return -EBADF;
+		if (max)
+			continue;
+get_max:
+		do {
+			max++;
+			set >>= 1;
+		} while (set);
+		max += n * __NFDBITS;
+	}
+
+	return max;
+}
+
+#define BIT(i)		(1UL << ((i)&(__NFDBITS-1)))
+#define MEM(i,m)	((m)+(unsigned)(i)/__NFDBITS)
+#define ISSET(i,m)	(((i)&*(m)) != 0)
+#define SET(i,m)	(*(m) |= (i))
+
+#define POLLIN_SET (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR)
+#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
+#define POLLEX_SET (POLLPRI)
+
+int do_select(int n, fd_set_bits *fds, long *timeout)
+{
+	struct poll_wqueues table;
+	poll_table *wait;
+	int retval, i;
+	long __timeout = *timeout;
+
+	rcu_read_lock();
+	retval = max_select_fd(n, fds);
+	rcu_read_unlock();
+
+	if (retval < 0)
+		return retval;
+	n = retval;
+
+	poll_initwait(&table);
+	wait = &table.pt;
+	if (!__timeout)
+		wait = NULL;
+	retval = 0;
+	for (;;) {
+		unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;
+
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		inp = fds->in; outp = fds->out; exp = fds->ex;
+		rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex;
+
+		for (i = 0; i < n; ++rinp, ++routp, ++rexp) {
+			unsigned long in, out, ex, all_bits, bit = 1, mask, j;
+			unsigned long res_in = 0, res_out = 0, res_ex = 0;
+			struct file_operations *f_op = NULL;
+			struct file *file = NULL;
+
+			in = *inp++; out = *outp++; ex = *exp++;
+			all_bits = in | out | ex;
+			if (all_bits == 0) {
+				i += __NFDBITS;
+				continue;
+			}
+
+			for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1) {
+				if (i >= n)
+					break;
+				if (!(bit & all_bits))
+					continue;
+				file = fget(i);
+				if (file) {
+					f_op = file->f_op;
+					mask = DEFAULT_POLLMASK;
+					if (f_op && f_op->poll)
+						mask = (*f_op->poll)(file, retval ? NULL : wait);
+					fput(file);
+					if ((mask & POLLIN_SET) && (in & bit)) {
+						res_in |= bit;
+						retval++;
+					}
+					if ((mask & POLLOUT_SET) && (out & bit)) {
+						res_out |= bit;
+						retval++;
+					}
+					if ((mask & POLLEX_SET) && (ex & bit)) {
+						res_ex |= bit;
+						retval++;
+					}
+				}
+				cond_resched();
+			}
+			if (res_in)
+				*rinp = res_in;
+			if (res_out)
+				*routp = res_out;
+			if (res_ex)
+				*rexp = res_ex;
+		}
+		wait = NULL;
+		if (retval || !__timeout || signal_pending(current))
+			break;
+		if(table.error) {
+			retval = table.error;
+			break;
+		}
+		__timeout = schedule_timeout(__timeout);
+	}
+	__set_current_state(TASK_RUNNING);
+
+	poll_freewait(&table);
+
+	/*
+	 * Up-to-date the caller timeout.
+	 */
+	*timeout = __timeout;
+	return retval;
+}
+
+/*
+ * We can actually return ERESTARTSYS instead of EINTR, but I'd
+ * like to be certain this leads to no problems. So I return
+ * EINTR just for safety.
+ *
+ * Update: ERESTARTSYS breaks at least the xview clock binary, so
+ * I'm trying ERESTARTNOHAND which restart only when you want to.
+ */
+#define MAX_SELECT_SECONDS \
+	((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1)
+
+asmlinkage long
+sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct timeval __user *tvp)
+{
+	fd_set_bits fds;
+	char *bits;
+	long timeout;
+	int ret, size, max_fdset;
+	struct fdtable *fdt;
+	/* Allocate small arguments on the stack to save memory and be faster */
+	char stack_fds[SELECT_STACK_ALLOC];
+
+	timeout = MAX_SCHEDULE_TIMEOUT;
+	if (tvp) {
+		time_t sec, usec;
+
+		if (!access_ok(VERIFY_READ, tvp, sizeof(*tvp))
+		    || __get_user(sec, &tvp->tv_sec)
+		    || __get_user(usec, &tvp->tv_usec)) {
+			ret = -EFAULT;
+			goto out_nofds;
+		}
+
+		ret = -EINVAL;
+		if (sec < 0 || usec < 0)
+			goto out_nofds;
+
+		if ((unsigned long) sec < MAX_SELECT_SECONDS) {
+			timeout = ROUND_UP(usec, 1000000/HZ);
+			timeout += sec * (unsigned long) HZ;
+		}
+	}
+
+	ret = -EINVAL;
+	if (n < 0)
+		goto out_nofds;
+
+	/* max_fdset can increase, so grab it once to avoid race */
+	rcu_read_lock();
+	fdt = files_fdtable(current->files);
+	max_fdset = fdt->max_fdset;
+	rcu_read_unlock();
+	if (n > max_fdset)
+		n = max_fdset;
+
+	/*
+	 * We need 6 bitmaps (in/out/ex for both incoming and outgoing),
+	 * since we used fdset we need to allocate memory in units of
+	 * long-words. 
+	 */
+	ret = -ENOMEM;
+	size = FDS_BYTES(n);
+	if (6*size < SELECT_STACK_ALLOC)
+		bits = stack_fds;
+	else
+		bits = kmalloc(6 * size, GFP_KERNEL);
+	if (!bits)
+		goto out_nofds;
+	fds.in      = (unsigned long *)  bits;
+	fds.out     = (unsigned long *) (bits +   size);
+	fds.ex      = (unsigned long *) (bits + 2*size);
+	fds.res_in  = (unsigned long *) (bits + 3*size);
+	fds.res_out = (unsigned long *) (bits + 4*size);
+	fds.res_ex  = (unsigned long *) (bits + 5*size);
+
+	if ((ret = get_fd_set(n, inp, fds.in)) ||
+	    (ret = get_fd_set(n, outp, fds.out)) ||
+	    (ret = get_fd_set(n, exp, fds.ex)))
+		goto out;
+	zero_fd_set(n, fds.res_in);
+	zero_fd_set(n, fds.res_out);
+	zero_fd_set(n, fds.res_ex);
+
+	ret = do_select(n, &fds, &timeout);
+
+	if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
+		time_t sec = 0, usec = 0;
+		if (timeout) {
+			sec = timeout / HZ;
+			usec = timeout % HZ;
+			usec *= (1000000/HZ);
+		}
+		put_user(sec, &tvp->tv_sec);
+		put_user(usec, &tvp->tv_usec);
+	}
+
+	if (ret < 0)
+		goto out;
+	if (!ret) {
+		ret = -ERESTARTNOHAND;
+		if (signal_pending(current))
+			goto out;
+		ret = 0;
+	}
+
+	if (set_fd_set(n, inp, fds.res_in) ||
+	    set_fd_set(n, outp, fds.res_out) ||
+	    set_fd_set(n, exp, fds.res_ex))
+		ret = -EFAULT;
+
+out:
+	if (bits != stack_fds)
+		kfree(bits);
+out_nofds:
+	return ret;
+}
+
+struct poll_list {
+	struct poll_list *next;
+	int len;
+	struct pollfd entries[0];
+};
+
+#define POLLFD_PER_PAGE  ((PAGE_SIZE-sizeof(struct poll_list)) / sizeof(struct pollfd))
+
+static void do_pollfd(unsigned int num, struct pollfd * fdpage,
+	poll_table ** pwait, int *count)
+{
+	int i;
+
+	for (i = 0; i < num; i++) {
+		int fd;
+		unsigned int mask;
+		struct pollfd *fdp;
+
+		mask = 0;
+		fdp = fdpage+i;
+		fd = fdp->fd;
+		if (fd >= 0) {
+			struct file * file = fget(fd);
+			mask = POLLNVAL;
+			if (file != NULL) {
+				mask = DEFAULT_POLLMASK;
+				if (file->f_op && file->f_op->poll)
+					mask = file->f_op->poll(file, *pwait);
+				mask &= fdp->events | POLLERR | POLLHUP;
+				fput(file);
+			}
+			if (mask) {
+				*pwait = NULL;
+				(*count)++;
+			}
+		}
+		fdp->revents = mask;
+	}
+}
+
+static int do_poll(unsigned int nfds,  struct poll_list *list,
+			struct poll_wqueues *wait, long timeout)
+{
+	int count = 0;
+	poll_table* pt = &wait->pt;
+
+	if (!timeout)
+		pt = NULL;
+ 
+	for (;;) {
+		struct poll_list *walk;
+		set_current_state(TASK_INTERRUPTIBLE);
+		walk = list;
+		while(walk != NULL) {
+			do_pollfd( walk->len, walk->entries, &pt, &count);
+			walk = walk->next;
+		}
+		pt = NULL;
+		if (count || !timeout || signal_pending(current))
+			break;
+		count = wait->error;
+		if (count)
+			break;
+		timeout = schedule_timeout(timeout);
+	}
+	__set_current_state(TASK_RUNNING);
+	return count;
+}
+
+#define N_STACK_PPS ((sizeof(stack_pps) - sizeof(struct poll_list))  / sizeof(struct pollfd))
+
+asmlinkage long sys_poll(struct pollfd __user * ufds, unsigned int nfds, long timeout)
+{
+	struct poll_wqueues table;
+ 	int fdcount, err;
+ 	unsigned int i;
+	struct poll_list *head;
+ 	struct poll_list *walk;
+	struct fdtable *fdt;
+	int max_fdset;
+	/* Allocate small arguments on the stack to save memory and be faster */
+	char stack_pps[POLL_STACK_ALLOC];
+	struct poll_list *stack_pp = NULL;
+
+	/* Do a sanity check on nfds ... */
+	rcu_read_lock();
+	fdt = files_fdtable(current->files);
+	max_fdset = fdt->max_fdset;
+	rcu_read_unlock();
+	if (nfds > max_fdset && nfds > OPEN_MAX)
+		return -EINVAL;
+
+	if (timeout) {
+		/* Careful about overflow in the intermediate values */
+		if ((unsigned long) timeout < MAX_SCHEDULE_TIMEOUT / HZ)
+			timeout = (unsigned long)(timeout*HZ+999)/1000+1;
+		else /* Negative or overflow */
+			timeout = MAX_SCHEDULE_TIMEOUT;
+	}
+
+	poll_initwait(&table);
+
+	head = NULL;
+	walk = NULL;
+	i = nfds;
+	err = -ENOMEM;
+	while(i!=0) {
+		struct poll_list *pp;
+		int num, size;
+		if (stack_pp == NULL)
+			num = N_STACK_PPS;
+		else
+			num = POLLFD_PER_PAGE;
+		if (num > i)
+			num = i;
+		size = sizeof(struct poll_list) + sizeof(struct pollfd)*num;
+		if (!stack_pp)
+			stack_pp = pp = (struct poll_list *)stack_pps;
+		else {
+			pp = kmalloc(size, GFP_KERNEL);
+			if (!pp)
+				goto out_fds;
+		}
+		pp->next=NULL;
+		pp->len = num;
+		if (head == NULL)
+			head = pp;
+		else
+			walk->next = pp;
+
+		walk = pp;
+		if (copy_from_user(pp->entries, ufds + nfds-i, 
+				sizeof(struct pollfd)*num)) {
+			err = -EFAULT;
+			goto out_fds;
+		}
+		i -= pp->len;
+	}
+	fdcount = do_poll(nfds, head, &table, timeout);
+
+	/* OK, now copy the revents fields back to user space. */
+	walk = head;
+	err = -EFAULT;
+	while(walk != NULL) {
+		struct pollfd *fds = walk->entries;
+		int j;
+
+		for (j=0; j < walk->len; j++, ufds++) {
+			if(__put_user(fds[j].revents, &ufds->revents))
+				goto out_fds;
+		}
+		walk = walk->next;
+  	}
+	err = fdcount;
+	if (!fdcount && signal_pending(current))
+		err = -EINTR;
+out_fds:
+	walk = head;
+	while(walk!=NULL) {
+		struct poll_list *pp = walk->next;
+		if (walk != stack_pp)
+			kfree(walk);
+		walk = pp;
+	}
+	poll_freewait(&table);
+	return err;
+}
diff -urN oldtree/fs/smbfs/Makefile newtree/fs/smbfs/Makefile
--- oldtree/fs/smbfs/Makefile	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/smbfs/Makefile	2006-02-13 19:20:00.733400808 -0500
@@ -13,7 +13,6 @@
 EXTRA_CFLAGS += -DSMBFS_PARANOIA
 #EXTRA_CFLAGS += -DSMBFS_DEBUG
 #EXTRA_CFLAGS += -DSMBFS_DEBUG_VERBOSE
-#EXTRA_CFLAGS += -DDEBUG_SMB_MALLOC
 #EXTRA_CFLAGS += -DDEBUG_SMB_TIMESTAMP
 #EXTRA_CFLAGS += -Werror
 
diff -urN oldtree/fs/smbfs/dir.c newtree/fs/smbfs/dir.c
--- oldtree/fs/smbfs/dir.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/smbfs/dir.c	2006-02-13 19:20:00.734400656 -0500
@@ -209,6 +209,8 @@
 	ctl.valid  = 1;
 read_really:
 	result = server->ops->readdir(filp, dirent, filldir, &ctl);
+	if (result == -ERESTARTSYS && page)
+		ClearPageUptodate(page);
 	if (ctl.idx == -1)
 		goto invalid_cache;	/* retry */
 	ctl.head.end = ctl.fpos - 1;
@@ -217,7 +219,8 @@
 	if (page) {
 		cache->head = ctl.head;
 		kunmap(page);
-		SetPageUptodate(page);
+		if (result != -ERESTARTSYS)
+			SetPageUptodate(page);
 		unlock_page(page);
 		page_cache_release(page);
 	}
diff -urN oldtree/fs/smbfs/inode.c newtree/fs/smbfs/inode.c
--- oldtree/fs/smbfs/inode.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/smbfs/inode.c	2006-02-13 19:20:00.734400656 -0500
@@ -487,11 +487,11 @@
 	if (server->conn_pid)
 		kill_proc(server->conn_pid, SIGTERM, 1);
 
-	smb_kfree(server->ops);
+	kfree(server->ops);
 	smb_unload_nls(server);
 	sb->s_fs_info = NULL;
 	smb_unlock_server(server);
-	smb_kfree(server);
+	kfree(server);
 }
 
 static int smb_fill_super(struct super_block *sb, void *raw_data, int silent)
@@ -519,11 +519,10 @@
 	sb->s_op = &smb_sops;
 	sb->s_time_gran = 100;
 
-	server = smb_kmalloc(sizeof(struct smb_sb_info), GFP_KERNEL);
+	server = kzalloc(sizeof(struct smb_sb_info), GFP_KERNEL);
 	if (!server)
 		goto out_no_server;
 	sb->s_fs_info = server;
-	memset(server, 0, sizeof(struct smb_sb_info));
 
 	server->super_block = sb;
 	server->mnt = NULL;
@@ -542,8 +541,8 @@
 	/* FIXME: move these to the smb_sb_info struct */
 	VERBOSE("alloc chunk = %d\n", sizeof(struct smb_ops) +
 		sizeof(struct smb_mount_data_kernel));
-	mem = smb_kmalloc(sizeof(struct smb_ops) +
-			  sizeof(struct smb_mount_data_kernel), GFP_KERNEL);
+	mem = kmalloc(sizeof(struct smb_ops) +
+		      sizeof(struct smb_mount_data_kernel), GFP_KERNEL);
 	if (!mem)
 		goto out_no_mem;
 
@@ -621,12 +620,12 @@
 out_no_smbiod:
 	smb_unload_nls(server);
 out_bad_option:
-	smb_kfree(mem);
+	kfree(mem);
 out_no_mem:
 	if (!server->mnt)
 		printk(KERN_ERR "smb_fill_super: allocation failure\n");
 	sb->s_fs_info = NULL;
-	smb_kfree(server);
+	kfree(server);
 	goto out_fail;
 out_wrong_data:
 	printk(KERN_ERR "smbfs: mount_data version %d is not supported\n", ver);
@@ -783,12 +782,6 @@
 	return error;
 }
 
-#ifdef DEBUG_SMB_MALLOC
-int smb_malloced;
-int smb_current_kmalloced;
-int smb_current_vmalloced;
-#endif
-
 static struct super_block *smb_get_sb(struct file_system_type *fs_type,
 	int flags, const char *dev_name, void *data)
 {
@@ -808,12 +801,6 @@
 	int err;
 	DEBUG1("registering ...\n");
 
-#ifdef DEBUG_SMB_MALLOC
-	smb_malloced = 0;
-	smb_current_kmalloced = 0;
-	smb_current_vmalloced = 0;
-#endif
-
 	err = init_inodecache();
 	if (err)
 		goto out_inode;
@@ -838,11 +825,6 @@
 	unregister_filesystem(&smb_fs_type);
 	smb_destroy_request_cache();
 	destroy_inodecache();
-#ifdef DEBUG_SMB_MALLOC
-	printk(KERN_DEBUG "smb_malloced: %d\n", smb_malloced);
-	printk(KERN_DEBUG "smb_current_kmalloced: %d\n",smb_current_kmalloced);
-	printk(KERN_DEBUG "smb_current_vmalloced: %d\n",smb_current_vmalloced);
-#endif
 }
 
 module_init(init_smb_fs)
diff -urN oldtree/fs/smbfs/inode.c.orig newtree/fs/smbfs/inode.c.orig
--- oldtree/fs/smbfs/inode.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/fs/smbfs/inode.c.orig	2006-02-13 19:20:00.735400504 -0500
@@ -0,0 +1,850 @@
+/*
+ *  inode.c
+ *
+ *  Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ *  Copyright (C) 1997 by Volker Lendecke
+ *
+ *  Please add a note about your changes to smbfs in the ChangeLog file.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/file.h>
+#include <linux/dcache.h>
+#include <linux/smp_lock.h>
+#include <linux/nls.h>
+#include <linux/seq_file.h>
+#include <linux/mount.h>
+#include <linux/net.h>
+#include <linux/vfs.h>
+#include <linux/highuid.h>
+#include <linux/smb_fs.h>
+#include <linux/smbno.h>
+#include <linux/smb_mount.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "smb_debug.h"
+#include "getopt.h"
+#include "proto.h"
+
+/* Always pick a default string */
+#ifdef CONFIG_SMB_NLS_REMOTE
+#define SMB_NLS_REMOTE CONFIG_SMB_NLS_REMOTE
+#else
+#define SMB_NLS_REMOTE ""
+#endif
+
+#define SMB_TTL_DEFAULT 1000
+
+static void smb_delete_inode(struct inode *);
+static void smb_put_super(struct super_block *);
+static int  smb_statfs(struct super_block *, struct kstatfs *);
+static int  smb_show_options(struct seq_file *, struct vfsmount *);
+
+static kmem_cache_t *smb_inode_cachep;
+
+static struct inode *smb_alloc_inode(struct super_block *sb)
+{
+	struct smb_inode_info *ei;
+	ei = (struct smb_inode_info *)kmem_cache_alloc(smb_inode_cachep, SLAB_KERNEL);
+	if (!ei)
+		return NULL;
+	return &ei->vfs_inode;
+}
+
+static void smb_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(smb_inode_cachep, SMB_I(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+	struct smb_inode_info *ei = (struct smb_inode_info *) foo;
+	unsigned long flagmask = SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR;
+
+	if ((flags & flagmask) == SLAB_CTOR_CONSTRUCTOR)
+		inode_init_once(&ei->vfs_inode);
+}
+ 
+static int init_inodecache(void)
+{
+	smb_inode_cachep = kmem_cache_create("smb_inode_cache",
+					     sizeof(struct smb_inode_info),
+					     0, SLAB_RECLAIM_ACCOUNT,
+					     init_once, NULL);
+	if (smb_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static void destroy_inodecache(void)
+{
+	if (kmem_cache_destroy(smb_inode_cachep))
+		printk(KERN_INFO "smb_inode_cache: not all structures were freed\n");
+}
+
+static int smb_remount(struct super_block *sb, int *flags, char *data)
+{
+	*flags |= MS_NODIRATIME;
+	return 0;
+}
+
+static struct super_operations smb_sops =
+{
+	.alloc_inode	= smb_alloc_inode,
+	.destroy_inode	= smb_destroy_inode,
+	.drop_inode	= generic_delete_inode,
+	.delete_inode	= smb_delete_inode,
+	.put_super	= smb_put_super,
+	.statfs		= smb_statfs,
+	.show_options	= smb_show_options,
+	.remount_fs	= smb_remount,
+};
+
+
+/* We are always generating a new inode here */
+struct inode *
+smb_iget(struct super_block *sb, struct smb_fattr *fattr)
+{
+	struct smb_sb_info *server = SMB_SB(sb);
+	struct inode *result;
+
+	DEBUG1("smb_iget: %p\n", fattr);
+
+	result = new_inode(sb);
+	if (!result)
+		return result;
+	result->i_ino = fattr->f_ino;
+	SMB_I(result)->open = 0;
+	SMB_I(result)->fileid = 0;
+	SMB_I(result)->access = 0;
+	SMB_I(result)->flags = 0;
+	SMB_I(result)->closed = 0;
+	SMB_I(result)->openers = 0;
+	smb_set_inode_attr(result, fattr);
+	if (S_ISREG(result->i_mode)) {
+		result->i_op = &smb_file_inode_operations;
+		result->i_fop = &smb_file_operations;
+		result->i_data.a_ops = &smb_file_aops;
+	} else if (S_ISDIR(result->i_mode)) {
+		if (server->opt.capabilities & SMB_CAP_UNIX)
+			result->i_op = &smb_dir_inode_operations_unix;
+		else
+			result->i_op = &smb_dir_inode_operations;
+		result->i_fop = &smb_dir_operations;
+	} else if (S_ISLNK(result->i_mode)) {
+		result->i_op = &smb_link_inode_operations;
+	} else {
+		init_special_inode(result, result->i_mode, fattr->f_rdev);
+	}
+	insert_inode_hash(result);
+	return result;
+}
+
+/*
+ * Copy the inode data to a smb_fattr structure.
+ */
+void
+smb_get_inode_attr(struct inode *inode, struct smb_fattr *fattr)
+{
+	memset(fattr, 0, sizeof(struct smb_fattr));
+	fattr->f_mode	= inode->i_mode;
+	fattr->f_nlink	= inode->i_nlink;
+	fattr->f_ino	= inode->i_ino;
+	fattr->f_uid	= inode->i_uid;
+	fattr->f_gid	= inode->i_gid;
+	fattr->f_size	= inode->i_size;
+	fattr->f_mtime	= inode->i_mtime;
+	fattr->f_ctime	= inode->i_ctime;
+	fattr->f_atime	= inode->i_atime;
+	fattr->f_blksize= inode->i_blksize;
+	fattr->f_blocks	= inode->i_blocks;
+
+	fattr->attr	= SMB_I(inode)->attr;
+	/*
+	 * Keep the attributes in sync with the inode permissions.
+	 */
+	if (fattr->f_mode & S_IWUSR)
+		fattr->attr &= ~aRONLY;
+	else
+		fattr->attr |= aRONLY;
+}
+
+/*
+ * Update the inode, possibly causing it to invalidate its pages if mtime/size
+ * is different from last time.
+ */
+void
+smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr)
+{
+	struct smb_inode_info *ei = SMB_I(inode);
+
+	/*
+	 * A size change should have a different mtime, or same mtime
+	 * but different size.
+	 */
+	time_t last_time = inode->i_mtime.tv_sec;
+	loff_t last_sz = inode->i_size;
+
+	inode->i_mode	= fattr->f_mode;
+	inode->i_nlink	= fattr->f_nlink;
+	inode->i_uid	= fattr->f_uid;
+	inode->i_gid	= fattr->f_gid;
+	inode->i_ctime	= fattr->f_ctime;
+	inode->i_blksize= fattr->f_blksize;
+	inode->i_blocks = fattr->f_blocks;
+	inode->i_size	= fattr->f_size;
+	inode->i_mtime	= fattr->f_mtime;
+	inode->i_atime	= fattr->f_atime;
+	ei->attr = fattr->attr;
+
+	/*
+	 * Update the "last time refreshed" field for revalidation.
+	 */
+	ei->oldmtime = jiffies;
+
+	if (inode->i_mtime.tv_sec != last_time || inode->i_size != last_sz) {
+		VERBOSE("%ld changed, old=%ld, new=%ld, oz=%ld, nz=%ld\n",
+			inode->i_ino,
+			(long) last_time, (long) inode->i_mtime,
+			(long) last_sz, (long) inode->i_size);
+
+		if (!S_ISDIR(inode->i_mode))
+			invalidate_remote_inode(inode);
+	}
+}
+
+/*
+ * This is called if the connection has gone bad ...
+ * try to kill off all the current inodes.
+ */
+void
+smb_invalidate_inodes(struct smb_sb_info *server)
+{
+	VERBOSE("\n");
+	shrink_dcache_sb(SB_of(server));
+	invalidate_inodes(SB_of(server));
+}
+
+/*
+ * This is called to update the inode attributes after
+ * we've made changes to a file or directory.
+ */
+static int
+smb_refresh_inode(struct dentry *dentry)
+{
+	struct inode *inode = dentry->d_inode;
+	int error;
+	struct smb_fattr fattr;
+
+	error = smb_proc_getattr(dentry, &fattr);
+	if (!error) {
+		smb_renew_times(dentry);
+		/*
+		 * Check whether the type part of the mode changed,
+		 * and don't update the attributes if it did.
+		 *
+		 * And don't dick with the root inode
+		 */
+		if (inode->i_ino == 2)
+			return error;
+		if (S_ISLNK(inode->i_mode))
+			return error;	/* VFS will deal with it */
+
+		if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT)) {
+			smb_set_inode_attr(inode, &fattr);
+		} else {
+			/*
+			 * Big trouble! The inode has become a new object,
+			 * so any operations attempted on it are invalid.
+			 *
+			 * To limit damage, mark the inode as bad so that
+			 * subsequent lookup validations will fail.
+			 */
+			PARANOIA("%s/%s changed mode, %07o to %07o\n",
+				 DENTRY_PATH(dentry),
+				 inode->i_mode, fattr.f_mode);
+
+			fattr.f_mode = inode->i_mode; /* save mode */
+			make_bad_inode(inode);
+			inode->i_mode = fattr.f_mode; /* restore mode */
+			/*
+			 * No need to worry about unhashing the dentry: the
+			 * lookup validation will see that the inode is bad.
+			 * But we do want to invalidate the caches ...
+			 */
+			if (!S_ISDIR(inode->i_mode))
+				invalidate_remote_inode(inode);
+			else
+				smb_invalid_dir_cache(inode);
+			error = -EIO;
+		}
+	}
+	return error;
+}
+
+/*
+ * This is called when we want to check whether the inode
+ * has changed on the server.  If it has changed, we must
+ * invalidate our local caches.
+ */
+int
+smb_revalidate_inode(struct dentry *dentry)
+{
+	struct smb_sb_info *s = server_from_dentry(dentry);
+	struct inode *inode = dentry->d_inode;
+	int error = 0;
+
+	DEBUG1("smb_revalidate_inode\n");
+	lock_kernel();
+
+	/*
+	 * Check whether we've recently refreshed the inode.
+	 */
+	if (time_before(jiffies, SMB_I(inode)->oldmtime + SMB_MAX_AGE(s))) {
+		VERBOSE("up-to-date, ino=%ld, jiffies=%lu, oldtime=%lu\n",
+			inode->i_ino, jiffies, SMB_I(inode)->oldmtime);
+		goto out;
+	}
+
+	error = smb_refresh_inode(dentry);
+out:
+	unlock_kernel();
+	return error;
+}
+
+/*
+ * This routine is called when i_nlink == 0 and i_count goes to 0.
+ * All blocking cleanup operations need to go here to avoid races.
+ */
+static void
+smb_delete_inode(struct inode *ino)
+{
+	DEBUG1("ino=%ld\n", ino->i_ino);
+	truncate_inode_pages(&ino->i_data, 0);
+	lock_kernel();
+	if (smb_close(ino))
+		PARANOIA("could not close inode %ld\n", ino->i_ino);
+	unlock_kernel();
+	clear_inode(ino);
+}
+
+static struct option opts[] = {
+	{ "version",	0, 'v' },
+	{ "win95",	SMB_MOUNT_WIN95, 1 },
+	{ "oldattr",	SMB_MOUNT_OLDATTR, 1 },
+	{ "dirattr",	SMB_MOUNT_DIRATTR, 1 },
+	{ "case",	SMB_MOUNT_CASE, 1 },
+	{ "uid",	0, 'u' },
+	{ "gid",	0, 'g' },
+	{ "file_mode",	0, 'f' },
+	{ "dir_mode",	0, 'd' },
+	{ "iocharset",	0, 'i' },
+	{ "codepage",	0, 'c' },
+	{ "ttl",	0, 't' },
+	{ NULL,		0, 0}
+};
+
+static int
+parse_options(struct smb_mount_data_kernel *mnt, char *options)
+{
+	int c;
+	unsigned long flags;
+	unsigned long value;
+	char *optarg;
+	char *optopt;
+
+	flags = 0;
+	while ( (c = smb_getopt("smbfs", &options, opts,
+				&optopt, &optarg, &flags, &value)) > 0) {
+
+		VERBOSE("'%s' -> '%s'\n", optopt, optarg ? optarg : "<none>");
+		switch (c) {
+		case 1:
+			/* got a "flag" option */
+			break;
+		case 'v':
+			if (value != SMB_MOUNT_VERSION) {
+			printk ("smbfs: Bad mount version %ld, expected %d\n",
+				value, SMB_MOUNT_VERSION);
+				return 0;
+			}
+			mnt->version = value;
+			break;
+		case 'u':
+			mnt->uid = value;
+			flags |= SMB_MOUNT_UID;
+			break;
+		case 'g':
+			mnt->gid = value;
+			flags |= SMB_MOUNT_GID;
+			break;
+		case 'f':
+			mnt->file_mode = (value & S_IRWXUGO) | S_IFREG;
+			flags |= SMB_MOUNT_FMODE;
+			break;
+		case 'd':
+			mnt->dir_mode = (value & S_IRWXUGO) | S_IFDIR;
+			flags |= SMB_MOUNT_DMODE;
+			break;
+		case 'i':
+			strlcpy(mnt->codepage.local_name, optarg, 
+				SMB_NLS_MAXNAMELEN);
+			break;
+		case 'c':
+			strlcpy(mnt->codepage.remote_name, optarg,
+				SMB_NLS_MAXNAMELEN);
+			break;
+		case 't':
+			mnt->ttl = value;
+			break;
+		default:
+			printk ("smbfs: Unrecognized mount option %s\n",
+				optopt);
+			return -1;
+		}
+	}
+	mnt->flags = flags;
+	return c;
+}
+
+/*
+ * smb_show_options() is for displaying mount options in /proc/mounts.
+ * It tries to avoid showing settings that were not changed from their
+ * defaults.
+ */
+static int
+smb_show_options(struct seq_file *s, struct vfsmount *m)
+{
+	struct smb_mount_data_kernel *mnt = SMB_SB(m->mnt_sb)->mnt;
+	int i;
+
+	for (i = 0; opts[i].name != NULL; i++)
+		if (mnt->flags & opts[i].flag)
+			seq_printf(s, ",%s", opts[i].name);
+
+	if (mnt->flags & SMB_MOUNT_UID)
+		seq_printf(s, ",uid=%d", mnt->uid);
+	if (mnt->flags & SMB_MOUNT_GID)
+		seq_printf(s, ",gid=%d", mnt->gid);
+	if (mnt->mounted_uid != 0)
+		seq_printf(s, ",mounted_uid=%d", mnt->mounted_uid);
+
+	/* 
+	 * Defaults for file_mode and dir_mode are unknown to us; they
+	 * depend on the current umask of the user doing the mount.
+	 */
+	if (mnt->flags & SMB_MOUNT_FMODE)
+		seq_printf(s, ",file_mode=%04o", mnt->file_mode & S_IRWXUGO);
+	if (mnt->flags & SMB_MOUNT_DMODE)
+		seq_printf(s, ",dir_mode=%04o", mnt->dir_mode & S_IRWXUGO);
+
+	if (strcmp(mnt->codepage.local_name, CONFIG_NLS_DEFAULT))
+		seq_printf(s, ",iocharset=%s", mnt->codepage.local_name);
+	if (strcmp(mnt->codepage.remote_name, SMB_NLS_REMOTE))
+		seq_printf(s, ",codepage=%s", mnt->codepage.remote_name);
+
+	if (mnt->ttl != SMB_TTL_DEFAULT)
+		seq_printf(s, ",ttl=%d", mnt->ttl);
+
+	return 0;
+}
+
+static void
+smb_unload_nls(struct smb_sb_info *server)
+{
+	if (server->remote_nls) {
+		unload_nls(server->remote_nls);
+		server->remote_nls = NULL;
+	}
+	if (server->local_nls) {
+		unload_nls(server->local_nls);
+		server->local_nls = NULL;
+	}
+}
+
+static void
+smb_put_super(struct super_block *sb)
+{
+	struct smb_sb_info *server = SMB_SB(sb);
+
+	smb_lock_server(server);
+	server->state = CONN_INVALID;
+	smbiod_unregister_server(server);
+
+	smb_close_socket(server);
+
+	if (server->conn_pid)
+		kill_proc(server->conn_pid, SIGTERM, 1);
+
+	smb_kfree(server->ops);
+	smb_unload_nls(server);
+	sb->s_fs_info = NULL;
+	smb_unlock_server(server);
+	smb_kfree(server);
+}
+
+static int smb_fill_super(struct super_block *sb, void *raw_data, int silent)
+{
+	struct smb_sb_info *server;
+	struct smb_mount_data_kernel *mnt;
+	struct smb_mount_data *oldmnt;
+	struct inode *root_inode;
+	struct smb_fattr root;
+	int ver;
+	void *mem;
+
+	if (!raw_data)
+		goto out_no_data;
+
+	oldmnt = (struct smb_mount_data *) raw_data;
+	ver = oldmnt->version;
+	if (ver != SMB_MOUNT_OLDVERSION && cpu_to_be32(ver) != SMB_MOUNT_ASCII)
+		goto out_wrong_data;
+
+	sb->s_flags |= MS_NODIRATIME;
+	sb->s_blocksize = 1024;	/* Eh...  Is this correct? */
+	sb->s_blocksize_bits = 10;
+	sb->s_magic = SMB_SUPER_MAGIC;
+	sb->s_op = &smb_sops;
+	sb->s_time_gran = 100;
+
+	server = smb_kmalloc(sizeof(struct smb_sb_info), GFP_KERNEL);
+	if (!server)
+		goto out_no_server;
+	sb->s_fs_info = server;
+	memset(server, 0, sizeof(struct smb_sb_info));
+
+	server->super_block = sb;
+	server->mnt = NULL;
+	server->sock_file = NULL;
+	init_waitqueue_head(&server->conn_wq);
+	init_MUTEX(&server->sem);
+	INIT_LIST_HEAD(&server->entry);
+	INIT_LIST_HEAD(&server->xmitq);
+	INIT_LIST_HEAD(&server->recvq);
+	server->conn_error = 0;
+	server->conn_pid = 0;
+	server->state = CONN_INVALID; /* no connection yet */
+	server->generation = 0;
+
+	/* Allocate the global temp buffer and some superblock helper structs */
+	/* FIXME: move these to the smb_sb_info struct */
+	VERBOSE("alloc chunk = %d\n", sizeof(struct smb_ops) +
+		sizeof(struct smb_mount_data_kernel));
+	mem = smb_kmalloc(sizeof(struct smb_ops) +
+			  sizeof(struct smb_mount_data_kernel), GFP_KERNEL);
+	if (!mem)
+		goto out_no_mem;
+
+	server->ops = mem;
+	smb_install_null_ops(server->ops);
+	server->mnt = mem + sizeof(struct smb_ops);
+
+	/* Setup NLS stuff */
+	server->remote_nls = NULL;
+	server->local_nls = NULL;
+
+	mnt = server->mnt;
+
+	memset(mnt, 0, sizeof(struct smb_mount_data_kernel));
+	strlcpy(mnt->codepage.local_name, CONFIG_NLS_DEFAULT,
+		SMB_NLS_MAXNAMELEN);
+	strlcpy(mnt->codepage.remote_name, SMB_NLS_REMOTE,
+		SMB_NLS_MAXNAMELEN);
+
+	mnt->ttl = SMB_TTL_DEFAULT;
+	if (ver == SMB_MOUNT_OLDVERSION) {
+		mnt->version = oldmnt->version;
+
+		SET_UID(mnt->uid, oldmnt->uid);
+		SET_GID(mnt->gid, oldmnt->gid);
+
+		mnt->file_mode = (oldmnt->file_mode & S_IRWXUGO) | S_IFREG;
+		mnt->dir_mode = (oldmnt->dir_mode & S_IRWXUGO) | S_IFDIR;
+
+		mnt->flags = (oldmnt->file_mode >> 9) | SMB_MOUNT_UID |
+			SMB_MOUNT_GID | SMB_MOUNT_FMODE | SMB_MOUNT_DMODE;
+	} else {
+		mnt->file_mode = S_IRWXU | S_IRGRP | S_IXGRP |
+				S_IROTH | S_IXOTH | S_IFREG;
+		mnt->dir_mode = S_IRWXU | S_IRGRP | S_IXGRP |
+				S_IROTH | S_IXOTH | S_IFDIR;
+		if (parse_options(mnt, raw_data))
+			goto out_bad_option;
+	}
+	mnt->mounted_uid = current->uid;
+	smb_setcodepage(server, &mnt->codepage);
+
+	/*
+	 * Display the enabled options
+	 * Note: smb_proc_getattr uses these in 2.4 (but was changed in 2.2)
+	 */
+	if (mnt->flags & SMB_MOUNT_OLDATTR)
+		printk("SMBFS: Using core getattr (Win 95 speedup)\n");
+	else if (mnt->flags & SMB_MOUNT_DIRATTR)
+		printk("SMBFS: Using dir ff getattr\n");
+
+	if (smbiod_register_server(server) < 0) {
+		printk(KERN_ERR "smbfs: failed to start smbiod\n");
+		goto out_no_smbiod;
+	}
+
+	/*
+	 * Keep the super block locked while we get the root inode.
+	 */
+	smb_init_root_dirent(server, &root, sb);
+	root_inode = smb_iget(sb, &root);
+	if (!root_inode)
+		goto out_no_root;
+
+	sb->s_root = d_alloc_root(root_inode);
+	if (!sb->s_root)
+		goto out_no_root;
+
+	smb_new_dentry(sb->s_root);
+
+	return 0;
+
+out_no_root:
+	iput(root_inode);
+out_no_smbiod:
+	smb_unload_nls(server);
+out_bad_option:
+	smb_kfree(mem);
+out_no_mem:
+	if (!server->mnt)
+		printk(KERN_ERR "smb_fill_super: allocation failure\n");
+	sb->s_fs_info = NULL;
+	smb_kfree(server);
+	goto out_fail;
+out_wrong_data:
+	printk(KERN_ERR "smbfs: mount_data version %d is not supported\n", ver);
+	goto out_fail;
+out_no_data:
+	printk(KERN_ERR "smb_fill_super: missing data argument\n");
+out_fail:
+	return -EINVAL;
+out_no_server:
+	printk(KERN_ERR "smb_fill_super: cannot allocate struct smb_sb_info\n");
+	return -ENOMEM;
+}
+
+static int
+smb_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+	int result;
+	
+	lock_kernel();
+
+	result = smb_proc_dskattr(sb, buf);
+
+	unlock_kernel();
+
+	buf->f_type = SMB_SUPER_MAGIC;
+	buf->f_namelen = SMB_MAXPATHLEN;
+	return result;
+}
+
+int smb_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
+{
+	int err = smb_revalidate_inode(dentry);
+	if (!err)
+		generic_fillattr(dentry->d_inode, stat);
+	return err;
+}
+
+int
+smb_notify_change(struct dentry *dentry, struct iattr *attr)
+{
+	struct inode *inode = dentry->d_inode;
+	struct smb_sb_info *server = server_from_dentry(dentry);
+	unsigned int mask = (S_IFREG | S_IFDIR | S_IRWXUGO);
+	int error, changed, refresh = 0;
+	struct smb_fattr fattr;
+
+	lock_kernel();
+
+	error = smb_revalidate_inode(dentry);
+	if (error)
+		goto out;
+
+	if ((error = inode_change_ok(inode, attr)) < 0)
+		goto out;
+
+	error = -EPERM;
+	if ((attr->ia_valid & ATTR_UID) && (attr->ia_uid != server->mnt->uid))
+		goto out;
+
+	if ((attr->ia_valid & ATTR_GID) && (attr->ia_uid != server->mnt->gid))
+		goto out;
+
+	if ((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~mask))
+		goto out;
+
+	if ((attr->ia_valid & ATTR_SIZE) != 0) {
+		VERBOSE("changing %s/%s, old size=%ld, new size=%ld\n",
+			DENTRY_PATH(dentry),
+			(long) inode->i_size, (long) attr->ia_size);
+
+		filemap_fdatawrite(inode->i_mapping);
+		filemap_fdatawait(inode->i_mapping);
+
+		error = smb_open(dentry, O_WRONLY);
+		if (error)
+			goto out;
+		error = server->ops->truncate(inode, attr->ia_size);
+		if (error)
+			goto out;
+		error = vmtruncate(inode, attr->ia_size);
+		if (error)
+			goto out;
+		refresh = 1;
+	}
+
+	if (server->opt.capabilities & SMB_CAP_UNIX) {
+		/* For now we don't want to set the size with setattr_unix */
+		attr->ia_valid &= ~ATTR_SIZE;
+		/* FIXME: only call if we actually want to set something? */
+		error = smb_proc_setattr_unix(dentry, attr, 0, 0);
+		if (!error)
+			refresh = 1;
+
+		goto out;
+	}
+
+	/*
+	 * Initialize the fattr and check for changed fields.
+	 * Note: CTIME under SMB is creation time rather than
+	 * change time, so we don't attempt to change it.
+	 */
+	smb_get_inode_attr(inode, &fattr);
+
+	changed = 0;
+	if ((attr->ia_valid & ATTR_MTIME) != 0) {
+		fattr.f_mtime = attr->ia_mtime;
+		changed = 1;
+	}
+	if ((attr->ia_valid & ATTR_ATIME) != 0) {
+		fattr.f_atime = attr->ia_atime;
+		/* Earlier protocols don't have an access time */
+		if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2)
+			changed = 1;
+	}
+	if (changed) {
+		error = smb_proc_settime(dentry, &fattr);
+		if (error)
+			goto out;
+		refresh = 1;
+	}
+
+	/*
+	 * Check for mode changes ... we're extremely limited in
+	 * what can be set for SMB servers: just the read-only bit.
+	 */
+	if ((attr->ia_valid & ATTR_MODE) != 0) {
+		VERBOSE("%s/%s mode change, old=%x, new=%x\n",
+			DENTRY_PATH(dentry), fattr.f_mode, attr->ia_mode);
+		changed = 0;
+		if (attr->ia_mode & S_IWUSR) {
+			if (fattr.attr & aRONLY) {
+				fattr.attr &= ~aRONLY;
+				changed = 1;
+			}
+		} else {
+			if (!(fattr.attr & aRONLY)) {
+				fattr.attr |= aRONLY;
+				changed = 1;
+			}
+		}
+		if (changed) {
+			error = smb_proc_setattr(dentry, &fattr);
+			if (error)
+				goto out;
+			refresh = 1;
+		}
+	}
+	error = 0;
+
+out:
+	if (refresh)
+		smb_refresh_inode(dentry);
+	unlock_kernel();
+	return error;
+}
+
+#ifdef DEBUG_SMB_MALLOC
+int smb_malloced;
+int smb_current_kmalloced;
+int smb_current_vmalloced;
+#endif
+
+static struct super_block *smb_get_sb(struct file_system_type *fs_type,
+	int flags, const char *dev_name, void *data)
+{
+	return get_sb_nodev(fs_type, flags, data, smb_fill_super);
+}
+
+static struct file_system_type smb_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "smbfs",
+	.get_sb		= smb_get_sb,
+	.kill_sb	= kill_anon_super,
+	.fs_flags	= FS_BINARY_MOUNTDATA,
+};
+
+static int __init init_smb_fs(void)
+{
+	int err;
+	DEBUG1("registering ...\n");
+
+#ifdef DEBUG_SMB_MALLOC
+	smb_malloced = 0;
+	smb_current_kmalloced = 0;
+	smb_current_vmalloced = 0;
+#endif
+
+	err = init_inodecache();
+	if (err)
+		goto out_inode;
+	err = smb_init_request_cache();
+	if (err)
+		goto out_request;
+	err = register_filesystem(&smb_fs_type);
+	if (err)
+		goto out;
+	return 0;
+out:
+	smb_destroy_request_cache();
+out_request:
+	destroy_inodecache();
+out_inode:
+	return err;
+}
+
+static void __exit exit_smb_fs(void)
+{
+	DEBUG1("unregistering ...\n");
+	unregister_filesystem(&smb_fs_type);
+	smb_destroy_request_cache();
+	destroy_inodecache();
+#ifdef DEBUG_SMB_MALLOC
+	printk(KERN_DEBUG "smb_malloced: %d\n", smb_malloced);
+	printk(KERN_DEBUG "smb_current_kmalloced: %d\n",smb_current_kmalloced);
+	printk(KERN_DEBUG "smb_current_vmalloced: %d\n",smb_current_vmalloced);
+#endif
+}
+
+module_init(init_smb_fs)
+module_exit(exit_smb_fs)
+MODULE_LICENSE("GPL");
diff -urN oldtree/fs/smbfs/request.c newtree/fs/smbfs/request.c
--- oldtree/fs/smbfs/request.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/smbfs/request.c	2006-02-13 19:20:00.736400352 -0500
@@ -68,7 +68,7 @@
 		goto out;
 
 	if (bufsize > 0) {
-		buf = smb_kmalloc(bufsize, GFP_NOFS);
+		buf = kmalloc(bufsize, GFP_NOFS);
 		if (!buf) {
 			kmem_cache_free(req_cachep, req);
 			return NULL;
@@ -124,9 +124,8 @@
 {
 	atomic_dec(&req->rq_server->nr_requests);
 	if (req->rq_buffer && !(req->rq_flags & SMB_REQ_STATIC))
-		smb_kfree(req->rq_buffer);
-	if (req->rq_trans2buffer)
-		smb_kfree(req->rq_trans2buffer);
+		kfree(req->rq_buffer);
+	kfree(req->rq_trans2buffer);
 	kmem_cache_free(req_cachep, req);
 }
 
@@ -183,8 +182,7 @@
 	req->rq_err = 0;
 	req->rq_errno = 0;
 	req->rq_fragment = 0;
-	if (req->rq_trans2buffer)
-		smb_kfree(req->rq_trans2buffer);
+	kfree(req->rq_trans2buffer);
 
 	return 0;
 }
@@ -647,10 +645,9 @@
 			goto out_too_long;
 
 		req->rq_trans2bufsize = buf_len;
-		req->rq_trans2buffer = smb_kmalloc(buf_len, GFP_NOFS);
+		req->rq_trans2buffer = kzalloc(buf_len, GFP_NOFS);
 		if (!req->rq_trans2buffer)
 			goto out_no_mem;
-		memset(req->rq_trans2buffer, 0, buf_len);
 
 		req->rq_parm = req->rq_trans2buffer;
 		req->rq_data = req->rq_trans2buffer + parm_tot;
diff -urN oldtree/fs/ufs/super.c newtree/fs/ufs/super.c
--- oldtree/fs/ufs/super.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/ufs/super.c	2006-02-13 19:20:29.735991744 -0500
@@ -1296,8 +1296,10 @@
 		blk++;
 	}
 out:
-	if (len == towrite)
+	if (len == towrite) {
+		up(&inode->i_sem);
 		return err;
+	}
 	if (inode->i_size < off+len-towrite)
 		i_size_write(inode, off+len-towrite);
 	inode->i_version++;
diff -urN oldtree/fs/ufs/util.h newtree/fs/ufs/util.h
--- oldtree/fs/ufs/util.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/ufs/util.h	2006-02-13 19:20:29.736991592 -0500
@@ -255,8 +255,8 @@
 	((struct ufs_super_block_first *)((ubh)->bh[0]->b_data))
 
 #define ubh_get_usb_second(ubh) \
-	((struct ufs_super_block_second *)(ubh)-> \
-	bh[UFS_SECTOR_SIZE >> uspi->s_fshift]->b_data + (UFS_SECTOR_SIZE & ~uspi->s_fmask))
+	((struct ufs_super_block_second *)((ubh)->\
+	bh[UFS_SECTOR_SIZE >> uspi->s_fshift]->b_data + (UFS_SECTOR_SIZE & ~uspi->s_fmask)))
 
 #define ubh_get_usb_third(ubh) \
 	((struct ufs_super_block_third *)((ubh)-> \
diff -urN oldtree/fs/xattr.c newtree/fs/xattr.c
--- oldtree/fs/xattr.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/xattr.c	2006-02-13 19:20:00.736400352 -0500
@@ -17,6 +17,7 @@
 #include <linux/syscalls.h>
 #include <linux/module.h>
 #include <linux/fsnotify.h>
+#include <linux/audit.h>
 #include <asm/uaccess.h>
 
 /*
@@ -113,12 +114,15 @@
 	      size_t size, int flags)
 {
 	struct file *f;
+	struct dentry *dentry;
 	int error = -EBADF;
 
 	f = fget(fd);
 	if (!f)
 		return error;
-	error = setxattr(f->f_dentry, name, value, size, flags);
+	dentry = f->f_dentry;
+	audit_inode(NULL, dentry->d_inode, 0);
+	error = setxattr(dentry, name, value, size, flags);
 	fput(f);
 	return error;
 }
@@ -365,12 +369,15 @@
 sys_fremovexattr(int fd, char __user *name)
 {
 	struct file *f;
+	struct dentry *dentry;
 	int error = -EBADF;
 
 	f = fget(fd);
 	if (!f)
 		return error;
-	error = removexattr(f->f_dentry, name);
+	dentry = f->f_dentry;
+	audit_inode(NULL, dentry->d_inode, 0);
+	error = removexattr(dentry, name);
 	fput(f);
 	return error;
 }
diff -urN oldtree/fs/xfs/linux-2.6/xfs_buf.c newtree/fs/xfs/linux-2.6/xfs_buf.c
--- oldtree/fs/xfs/linux-2.6/xfs_buf.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/fs/xfs/linux-2.6/xfs_buf.c	2006-02-13 19:20:29.737991440 -0500
@@ -830,6 +830,13 @@
 
 	PB_TRACE(pb, "rele", pb->pb_relse);
 
+	if (unlikely(!hash)) {
+		ASSERT(!pb->pb_relse);
+		if (atomic_dec_and_test(&pb->pb_hold))
+			xfs_buf_free(pb);
+		return;
+	}
+
 	if (atomic_dec_and_lock(&pb->pb_hold, &hash->bh_lock)) {
 		if (pb->pb_relse) {
 			atomic_inc(&pb->pb_hold);
diff -urN oldtree/include/asm-alpha/system.h newtree/include/asm-alpha/system.h
--- oldtree/include/asm-alpha/system.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/asm-alpha/system.h	2006-02-13 19:20:29.739991136 -0500
@@ -562,7 +562,7 @@
    if something tries to do an invalid cmpxchg().  */
 extern void __cmpxchg_called_with_bad_pointer(void);
 
-static inline unsigned long
+static __always_inline unsigned long
 __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size)
 {
 	switch (size) {
diff -urN oldtree/include/asm-i386/atomic.h newtree/include/asm-i386/atomic.h
--- oldtree/include/asm-i386/atomic.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/asm-i386/atomic.h	2006-02-13 19:20:00.737400200 -0500
@@ -132,6 +132,10 @@
 {
 	unsigned char c;
 
+	if (!atomic_read(v)) {
+		printk("BUG: atomic counter underflow at:\n");
+		dump_stack();
+	}
 	__asm__ __volatile__(
 		LOCK "decl %0; sete %1"
 		:"=m" (v->counter), "=qm" (c)
diff -urN oldtree/include/asm-i386/bitops.h newtree/include/asm-i386/bitops.h
--- oldtree/include/asm-i386/bitops.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/asm-i386/bitops.h	2006-02-13 19:20:00.737400200 -0500
@@ -247,7 +247,7 @@
 static int test_bit(int nr, const volatile void * addr);
 #endif
 
-static inline int constant_test_bit(int nr, const volatile unsigned long *addr)
+static __always_inline int constant_test_bit(int nr, const volatile unsigned long *addr)
 {
 	return ((1UL << (nr & 31)) & (addr[nr >> 5])) != 0;
 }
diff -urN oldtree/include/asm-i386/current.h newtree/include/asm-i386/current.h
--- oldtree/include/asm-i386/current.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/asm-i386/current.h	2006-02-13 19:20:00.737400200 -0500
@@ -5,7 +5,7 @@
 
 struct task_struct;
 
-static inline struct task_struct * get_current(void)
+static __always_inline struct task_struct * get_current(void)
 {
 	return current_thread_info()->task;
 }
diff -urN oldtree/include/asm-i386/string.h newtree/include/asm-i386/string.h
--- oldtree/include/asm-i386/string.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/asm-i386/string.h	2006-02-13 19:20:00.738400048 -0500
@@ -201,7 +201,7 @@
 return __res;
 }
 
-static inline void * __memcpy(void * to, const void * from, size_t n)
+static __always_inline void * __memcpy(void * to, const void * from, size_t n)
 {
 int d0, d1, d2;
 __asm__ __volatile__(
@@ -223,7 +223,7 @@
  * This looks ugly, but the compiler can optimize it totally,
  * as the count is constant.
  */
-static inline void * __constant_memcpy(void * to, const void * from, size_t n)
+static __always_inline void * __constant_memcpy(void * to, const void * from, size_t n)
 {
 	long esi, edi;
 	if (!n) return to;
@@ -367,7 +367,7 @@
  * things 32 bits at a time even when we don't know the size of the
  * area at compile-time..
  */
-static inline void * __constant_c_memset(void * s, unsigned long c, size_t count)
+static __always_inline void * __constant_c_memset(void * s, unsigned long c, size_t count)
 {
 int d0, d1;
 __asm__ __volatile__(
@@ -416,7 +416,7 @@
  * This looks horribly ugly, but the compiler can optimize it totally,
  * as we by now know that both pattern and count is constant..
  */
-static inline void * __constant_c_and_count_memset(void * s, unsigned long pattern, size_t count)
+static __always_inline void * __constant_c_and_count_memset(void * s, unsigned long pattern, size_t count)
 {
 	switch (count) {
 		case 0:
diff -urN oldtree/include/asm-i386/uaccess.h newtree/include/asm-i386/uaccess.h
--- oldtree/include/asm-i386/uaccess.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/asm-i386/uaccess.h	2006-02-13 19:20:00.738400048 -0500
@@ -411,7 +411,7 @@
  * Returns number of bytes that could not be copied.
  * On success, this will be zero.
  */
-static inline unsigned long __must_check
+static __always_inline unsigned long __must_check
 __copy_to_user_inatomic(void __user *to, const void *from, unsigned long n)
 {
 	if (__builtin_constant_p(n)) {
@@ -432,7 +432,7 @@
 	return __copy_to_user_ll(to, from, n);
 }
 
-static inline unsigned long __must_check
+static __always_inline unsigned long __must_check
 __copy_to_user(void __user *to, const void *from, unsigned long n)
 {
        might_sleep();
@@ -456,7 +456,7 @@
  * If some data could not be copied, this function will pad the copied
  * data to the requested size using zero bytes.
  */
-static inline unsigned long
+static __always_inline unsigned long
 __copy_from_user_inatomic(void *to, const void __user *from, unsigned long n)
 {
 	if (__builtin_constant_p(n)) {
@@ -477,7 +477,7 @@
 	return __copy_from_user_ll(to, from, n);
 }
 
-static inline unsigned long
+static __always_inline unsigned long
 __copy_from_user(void *to, const void __user *from, unsigned long n)
 {
        might_sleep();
diff -urN oldtree/include/asm-i386/uaccess.h.orig newtree/include/asm-i386/uaccess.h.orig
--- oldtree/include/asm-i386/uaccess.h.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/asm-i386/uaccess.h.orig	2006-02-13 19:20:01.201329672 -0500
@@ -0,0 +1,515 @@
+#ifndef __i386_UACCESS_H
+#define __i386_UACCESS_H
+
+/*
+ * User space memory access functions
+ */
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/thread_info.h>
+#include <linux/prefetch.h>
+#include <linux/string.h>
+#include <asm/page.h>
+
+#define VERIFY_READ 0
+#define VERIFY_WRITE 1
+
+/*
+ * The fs value determines whether argument validity checking should be
+ * performed or not.  If get_fs() == USER_DS, checking is performed, with
+ * get_fs() == KERNEL_DS, checking is bypassed.
+ *
+ * For historical reasons, these macros are grossly misnamed.
+ */
+
+#define MAKE_MM_SEG(s)	((mm_segment_t) { (s) })
+
+
+#define KERNEL_DS	MAKE_MM_SEG(0xFFFFFFFFUL)
+#define USER_DS		MAKE_MM_SEG(PAGE_OFFSET)
+
+#define get_ds()	(KERNEL_DS)
+#define get_fs()	(current_thread_info()->addr_limit)
+#define set_fs(x)	(current_thread_info()->addr_limit = (x))
+
+#define segment_eq(a,b)	((a).seg == (b).seg)
+
+/*
+ * movsl can be slow when source and dest are not both 8-byte aligned
+ */
+#ifdef CONFIG_X86_INTEL_USERCOPY
+extern struct movsl_mask {
+	int mask;
+} ____cacheline_aligned_in_smp movsl_mask;
+#endif
+
+#define __addr_ok(addr) ((unsigned long __force)(addr) < (current_thread_info()->addr_limit.seg))
+
+/*
+ * Test whether a block of memory is a valid user space address.
+ * Returns 0 if the range is valid, nonzero otherwise.
+ *
+ * This is equivalent to the following test:
+ * (u33)addr + (u33)size >= (u33)current->addr_limit.seg
+ *
+ * This needs 33-bit arithmetic. We have a carry...
+ */
+#define __range_ok(addr,size) ({ \
+	unsigned long flag,sum; \
+	__chk_user_ptr(addr); \
+	asm("addl %3,%1 ; sbbl %0,%0; cmpl %1,%4; sbbl $0,%0" \
+		:"=&r" (flag), "=r" (sum) \
+		:"1" (addr),"g" ((int)(size)),"g" (current_thread_info()->addr_limit.seg)); \
+	flag; })
+
+/**
+ * access_ok: - Checks if a user space pointer is valid
+ * @type: Type of access: %VERIFY_READ or %VERIFY_WRITE.  Note that
+ *        %VERIFY_WRITE is a superset of %VERIFY_READ - if it is safe
+ *        to write to a block, it is always safe to read from it.
+ * @addr: User space pointer to start of block to check
+ * @size: Size of block to check
+ *
+ * Context: User context only.  This function may sleep.
+ *
+ * Checks if a pointer to a block of memory in user space is valid.
+ *
+ * Returns true (nonzero) if the memory block may be valid, false (zero)
+ * if it is definitely invalid.
+ *
+ * Note that, depending on architecture, this function probably just
+ * checks that the pointer is in the user space range - after calling
+ * this function, memory access functions may still return -EFAULT.
+ */
+#define access_ok(type,addr,size) (likely(__range_ok(addr,size) == 0))
+
+/*
+ * The exception table consists of pairs of addresses: the first is the
+ * address of an instruction that is allowed to fault, and the second is
+ * the address at which the program should continue.  No registers are
+ * modified, so it is entirely up to the continuation code to figure out
+ * what to do.
+ *
+ * All the routines below use bits of fixup code that are out of line
+ * with the main instruction path.  This means when everything is well,
+ * we don't even have to jump over them.  Further, they do not intrude
+ * on our cache or tlb entries.
+ */
+
+struct exception_table_entry
+{
+	unsigned long insn, fixup;
+};
+
+extern int fixup_exception(struct pt_regs *regs);
+
+/*
+ * These are the main single-value transfer routines.  They automatically
+ * use the right size if we just have the right pointer type.
+ *
+ * This gets kind of ugly. We want to return _two_ values in "get_user()"
+ * and yet we don't want to do any pointers, because that is too much
+ * of a performance impact. Thus we have a few rather ugly macros here,
+ * and hide all the ugliness from the user.
+ *
+ * The "__xxx" versions of the user access functions are versions that
+ * do not verify the address space, that must have been done previously
+ * with a separate "access_ok()" call (this is used when we do multiple
+ * accesses to the same area of user memory).
+ */
+
+extern void __get_user_1(void);
+extern void __get_user_2(void);
+extern void __get_user_4(void);
+
+#define __get_user_x(size,ret,x,ptr) \
+	__asm__ __volatile__("call __get_user_" #size \
+		:"=a" (ret),"=d" (x) \
+		:"0" (ptr))
+
+
+/* Careful: we have to cast the result to the type of the pointer for sign reasons */
+/**
+ * get_user: - Get a simple variable from user space.
+ * @x:   Variable to store result.
+ * @ptr: Source address, in user space.
+ *
+ * Context: User context only.  This function may sleep.
+ *
+ * This macro copies a single simple variable from user space to kernel
+ * space.  It supports simple types like char and int, but not larger
+ * data types like structures or arrays.
+ *
+ * @ptr must have pointer-to-simple-variable type, and the result of
+ * dereferencing @ptr must be assignable to @x without a cast.
+ *
+ * Returns zero on success, or -EFAULT on error.
+ * On error, the variable @x is set to zero.
+ */
+#define get_user(x,ptr)							\
+({	int __ret_gu;							\
+	unsigned long __val_gu;						\
+	__chk_user_ptr(ptr);						\
+	switch(sizeof (*(ptr))) {					\
+	case 1:  __get_user_x(1,__ret_gu,__val_gu,ptr); break;		\
+	case 2:  __get_user_x(2,__ret_gu,__val_gu,ptr); break;		\
+	case 4:  __get_user_x(4,__ret_gu,__val_gu,ptr); break;		\
+	default: __get_user_x(X,__ret_gu,__val_gu,ptr); break;		\
+	}								\
+	(x) = (__typeof__(*(ptr)))__val_gu;				\
+	__ret_gu;							\
+})
+
+extern void __put_user_bad(void);
+
+/*
+ * Strange magic calling convention: pointer in %ecx,
+ * value in %eax(:%edx), return value in %eax, no clobbers.
+ */
+extern void __put_user_1(void);
+extern void __put_user_2(void);
+extern void __put_user_4(void);
+extern void __put_user_8(void);
+
+#define __put_user_1(x, ptr) __asm__ __volatile__("call __put_user_1":"=a" (__ret_pu):"0" ((typeof(*(ptr)))(x)), "c" (ptr))
+#define __put_user_2(x, ptr) __asm__ __volatile__("call __put_user_2":"=a" (__ret_pu):"0" ((typeof(*(ptr)))(x)), "c" (ptr))
+#define __put_user_4(x, ptr) __asm__ __volatile__("call __put_user_4":"=a" (__ret_pu):"0" ((typeof(*(ptr)))(x)), "c" (ptr))
+#define __put_user_8(x, ptr) __asm__ __volatile__("call __put_user_8":"=a" (__ret_pu):"A" ((typeof(*(ptr)))(x)), "c" (ptr))
+#define __put_user_X(x, ptr) __asm__ __volatile__("call __put_user_X":"=a" (__ret_pu):"c" (ptr))
+
+/**
+ * put_user: - Write a simple value into user space.
+ * @x:   Value to copy to user space.
+ * @ptr: Destination address, in user space.
+ *
+ * Context: User context only.  This function may sleep.
+ *
+ * This macro copies a single simple value from kernel space to user
+ * space.  It supports simple types like char and int, but not larger
+ * data types like structures or arrays.
+ *
+ * @ptr must have pointer-to-simple-variable type, and @x must be assignable
+ * to the result of dereferencing @ptr.
+ *
+ * Returns zero on success, or -EFAULT on error.
+ */
+#ifdef CONFIG_X86_WP_WORKS_OK
+
+#define put_user(x,ptr)						\
+({	int __ret_pu;						\
+	__chk_user_ptr(ptr);					\
+	switch(sizeof(*(ptr))) {				\
+	case 1: __put_user_1(x, ptr); break;			\
+	case 2: __put_user_2(x, ptr); break;			\
+	case 4: __put_user_4(x, ptr); break;			\
+	case 8: __put_user_8(x, ptr); break;			\
+	default:__put_user_X(x, ptr); break;			\
+	}							\
+	__ret_pu;						\
+})
+
+#else
+#define put_user(x,ptr)						\
+({								\
+ 	int __ret_pu;						\
+	__typeof__(*(ptr)) __pus_tmp = x;			\
+	__ret_pu=0;						\
+	if(unlikely(__copy_to_user_ll(ptr, &__pus_tmp,		\
+				sizeof(*(ptr))) != 0))		\
+ 		__ret_pu=-EFAULT;				\
+ 	__ret_pu;						\
+ })
+
+
+#endif
+
+/**
+ * __get_user: - Get a simple variable from user space, with less checking.
+ * @x:   Variable to store result.
+ * @ptr: Source address, in user space.
+ *
+ * Context: User context only.  This function may sleep.
+ *
+ * This macro copies a single simple variable from user space to kernel
+ * space.  It supports simple types like char and int, but not larger
+ * data types like structures or arrays.
+ *
+ * @ptr must have pointer-to-simple-variable type, and the result of
+ * dereferencing @ptr must be assignable to @x without a cast.
+ *
+ * Caller must check the pointer with access_ok() before calling this
+ * function.
+ *
+ * Returns zero on success, or -EFAULT on error.
+ * On error, the variable @x is set to zero.
+ */
+#define __get_user(x,ptr) \
+  __get_user_nocheck((x),(ptr),sizeof(*(ptr)))
+
+
+/**
+ * __put_user: - Write a simple value into user space, with less checking.
+ * @x:   Value to copy to user space.
+ * @ptr: Destination address, in user space.
+ *
+ * Context: User context only.  This function may sleep.
+ *
+ * This macro copies a single simple value from kernel space to user
+ * space.  It supports simple types like char and int, but not larger
+ * data types like structures or arrays.
+ *
+ * @ptr must have pointer-to-simple-variable type, and @x must be assignable
+ * to the result of dereferencing @ptr.
+ *
+ * Caller must check the pointer with access_ok() before calling this
+ * function.
+ *
+ * Returns zero on success, or -EFAULT on error.
+ */
+#define __put_user(x,ptr) \
+  __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr)))
+
+#define __put_user_nocheck(x,ptr,size)				\
+({								\
+	long __pu_err;						\
+	__put_user_size((x),(ptr),(size),__pu_err,-EFAULT);	\
+	__pu_err;						\
+})
+
+
+#define __put_user_u64(x, addr, err)				\
+	__asm__ __volatile__(					\
+		"1:	movl %%eax,0(%2)\n"			\
+		"2:	movl %%edx,4(%2)\n"			\
+		"3:\n"						\
+		".section .fixup,\"ax\"\n"			\
+		"4:	movl %3,%0\n"				\
+		"	jmp 3b\n"				\
+		".previous\n"					\
+		".section __ex_table,\"a\"\n"			\
+		"	.align 4\n"				\
+		"	.long 1b,4b\n"				\
+		"	.long 2b,4b\n"				\
+		".previous"					\
+		: "=r"(err)					\
+		: "A" (x), "r" (addr), "i"(-EFAULT), "0"(err))
+
+#ifdef CONFIG_X86_WP_WORKS_OK
+
+#define __put_user_size(x,ptr,size,retval,errret)			\
+do {									\
+	retval = 0;							\
+	__chk_user_ptr(ptr);						\
+	switch (size) {							\
+	case 1: __put_user_asm(x,ptr,retval,"b","b","iq",errret);break;	\
+	case 2: __put_user_asm(x,ptr,retval,"w","w","ir",errret);break; \
+	case 4: __put_user_asm(x,ptr,retval,"l","","ir",errret); break;	\
+	case 8: __put_user_u64((__typeof__(*ptr))(x),ptr,retval); break;\
+	  default: __put_user_bad();					\
+	}								\
+} while (0)
+
+#else
+
+#define __put_user_size(x,ptr,size,retval,errret)			\
+do {									\
+	__typeof__(*(ptr)) __pus_tmp = x;				\
+	retval = 0;							\
+									\
+	if(unlikely(__copy_to_user_ll(ptr, &__pus_tmp, size) != 0))	\
+		retval = errret;					\
+} while (0)
+
+#endif
+struct __large_struct { unsigned long buf[100]; };
+#define __m(x) (*(struct __large_struct __user *)(x))
+
+/*
+ * Tell gcc we read from memory instead of writing: this is because
+ * we do not write to any memory gcc knows about, so there are no
+ * aliasing issues.
+ */
+#define __put_user_asm(x, addr, err, itype, rtype, ltype, errret)	\
+	__asm__ __volatile__(						\
+		"1:	mov"itype" %"rtype"1,%2\n"			\
+		"2:\n"							\
+		".section .fixup,\"ax\"\n"				\
+		"3:	movl %3,%0\n"					\
+		"	jmp 2b\n"					\
+		".previous\n"						\
+		".section __ex_table,\"a\"\n"				\
+		"	.align 4\n"					\
+		"	.long 1b,3b\n"					\
+		".previous"						\
+		: "=r"(err)						\
+		: ltype (x), "m"(__m(addr)), "i"(errret), "0"(err))
+
+
+#define __get_user_nocheck(x,ptr,size)				\
+({								\
+	long __gu_err;						\
+	unsigned long __gu_val;					\
+	__get_user_size(__gu_val,(ptr),(size),__gu_err,-EFAULT);\
+	(x) = (__typeof__(*(ptr)))__gu_val;			\
+	__gu_err;						\
+})
+
+extern long __get_user_bad(void);
+
+#define __get_user_size(x,ptr,size,retval,errret)			\
+do {									\
+	retval = 0;							\
+	__chk_user_ptr(ptr);						\
+	switch (size) {							\
+	case 1: __get_user_asm(x,ptr,retval,"b","b","=q",errret);break;	\
+	case 2: __get_user_asm(x,ptr,retval,"w","w","=r",errret);break;	\
+	case 4: __get_user_asm(x,ptr,retval,"l","","=r",errret);break;	\
+	default: (x) = __get_user_bad();				\
+	}								\
+} while (0)
+
+#define __get_user_asm(x, addr, err, itype, rtype, ltype, errret)	\
+	__asm__ __volatile__(						\
+		"1:	mov"itype" %2,%"rtype"1\n"			\
+		"2:\n"							\
+		".section .fixup,\"ax\"\n"				\
+		"3:	movl %3,%0\n"					\
+		"	xor"itype" %"rtype"1,%"rtype"1\n"		\
+		"	jmp 2b\n"					\
+		".previous\n"						\
+		".section __ex_table,\"a\"\n"				\
+		"	.align 4\n"					\
+		"	.long 1b,3b\n"					\
+		".previous"						\
+		: "=r"(err), ltype (x)					\
+		: "m"(__m(addr)), "i"(errret), "0"(err))
+
+
+unsigned long __must_check __copy_to_user_ll(void __user *to,
+				const void *from, unsigned long n);
+unsigned long __must_check __copy_from_user_ll(void *to,
+				const void __user *from, unsigned long n);
+
+/*
+ * Here we special-case 1, 2 and 4-byte copy_*_user invocations.  On a fault
+ * we return the initial request size (1, 2 or 4), as copy_*_user should do.
+ * If a store crosses a page boundary and gets a fault, the x86 will not write
+ * anything, so this is accurate.
+ */
+
+/**
+ * __copy_to_user: - Copy a block of data into user space, with less checking.
+ * @to:   Destination address, in user space.
+ * @from: Source address, in kernel space.
+ * @n:    Number of bytes to copy.
+ *
+ * Context: User context only.  This function may sleep.
+ *
+ * Copy data from kernel space to user space.  Caller must check
+ * the specified block with access_ok() before calling this function.
+ *
+ * Returns number of bytes that could not be copied.
+ * On success, this will be zero.
+ */
+static inline unsigned long __must_check
+__copy_to_user_inatomic(void __user *to, const void *from, unsigned long n)
+{
+	if (__builtin_constant_p(n)) {
+		unsigned long ret;
+
+		switch (n) {
+		case 1:
+			__put_user_size(*(u8 *)from, (u8 __user *)to, 1, ret, 1);
+			return ret;
+		case 2:
+			__put_user_size(*(u16 *)from, (u16 __user *)to, 2, ret, 2);
+			return ret;
+		case 4:
+			__put_user_size(*(u32 *)from, (u32 __user *)to, 4, ret, 4);
+			return ret;
+		}
+	}
+	return __copy_to_user_ll(to, from, n);
+}
+
+static inline unsigned long __must_check
+__copy_to_user(void __user *to, const void *from, unsigned long n)
+{
+       might_sleep();
+       return __copy_to_user_inatomic(to, from, n);
+}
+
+/**
+ * __copy_from_user: - Copy a block of data from user space, with less checking.
+ * @to:   Destination address, in kernel space.
+ * @from: Source address, in user space.
+ * @n:    Number of bytes to copy.
+ *
+ * Context: User context only.  This function may sleep.
+ *
+ * Copy data from user space to kernel space.  Caller must check
+ * the specified block with access_ok() before calling this function.
+ *
+ * Returns number of bytes that could not be copied.
+ * On success, this will be zero.
+ *
+ * If some data could not be copied, this function will pad the copied
+ * data to the requested size using zero bytes.
+ */
+static inline unsigned long
+__copy_from_user_inatomic(void *to, const void __user *from, unsigned long n)
+{
+	if (__builtin_constant_p(n)) {
+		unsigned long ret;
+
+		switch (n) {
+		case 1:
+			__get_user_size(*(u8 *)to, from, 1, ret, 1);
+			return ret;
+		case 2:
+			__get_user_size(*(u16 *)to, from, 2, ret, 2);
+			return ret;
+		case 4:
+			__get_user_size(*(u32 *)to, from, 4, ret, 4);
+			return ret;
+		}
+	}
+	return __copy_from_user_ll(to, from, n);
+}
+
+static inline unsigned long
+__copy_from_user(void *to, const void __user *from, unsigned long n)
+{
+       might_sleep();
+       return __copy_from_user_inatomic(to, from, n);
+}
+unsigned long __must_check copy_to_user(void __user *to,
+				const void *from, unsigned long n);
+unsigned long __must_check copy_from_user(void *to,
+				const void __user *from, unsigned long n);
+long __must_check strncpy_from_user(char *dst, const char __user *src,
+				long count);
+long __must_check __strncpy_from_user(char *dst,
+				const char __user *src, long count);
+
+/**
+ * strlen_user: - Get the size of a string in user space.
+ * @str: The string to measure.
+ *
+ * Context: User context only.  This function may sleep.
+ *
+ * Get the size of a NUL-terminated string in user space.
+ *
+ * Returns the size of the string INCLUDING the terminating NUL.
+ * On exception, returns 0.
+ *
+ * If there is a limit on the length of a valid string, you may wish to
+ * consider using strnlen_user() instead.
+ */
+#define strlen_user(str) strnlen_user(str, ~0UL >> 1)
+
+long strnlen_user(const char __user *str, long n);
+unsigned long __must_check clear_user(void __user *mem, unsigned long len);
+unsigned long __must_check __clear_user(void __user *mem, unsigned long len);
+
+#endif /* __i386_UACCESS_H */
diff -urN oldtree/include/asm-s390/system.h newtree/include/asm-s390/system.h
--- oldtree/include/asm-s390/system.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/asm-s390/system.h	2006-02-13 19:20:01.205329064 -0500
@@ -105,13 +105,14 @@
 } while (0)
 
 #ifdef CONFIG_VIRT_CPU_ACCOUNTING
-extern void account_user_vtime(struct task_struct *);
+extern void account_vtime(struct task_struct *);
+extern void account_tick_vtime(struct task_struct *);
 extern void account_system_vtime(struct task_struct *);
 #endif
 
 #define finish_arch_switch(prev) do {					     \
 	set_fs(current->thread.mm_segment);				     \
-	account_system_vtime(prev);					     \
+	account_vtime(prev);						     \
 } while (0)
 
 #define nop() __asm__ __volatile__ ("nop")
diff -urN oldtree/include/asm-s390/system.h.orig newtree/include/asm-s390/system.h.orig
--- oldtree/include/asm-s390/system.h.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/asm-s390/system.h.orig	2006-02-13 19:20:01.206328912 -0500
@@ -0,0 +1,473 @@
+/*
+ *  include/asm-s390/system.h
+ *
+ *  S390 version
+ *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
+ *
+ *  Derived from "include/asm-i386/system.h"
+ */
+
+#ifndef __ASM_SYSTEM_H
+#define __ASM_SYSTEM_H
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <asm/types.h>
+#include <asm/ptrace.h>
+#include <asm/setup.h>
+#include <asm/processor.h>
+
+#ifdef __KERNEL__
+
+struct task_struct;
+
+extern struct task_struct *__switch_to(void *, void *);
+
+#ifdef __s390x__
+#define __FLAG_SHIFT 56
+#else /* ! __s390x__ */
+#define __FLAG_SHIFT 24
+#endif /* ! __s390x__ */
+
+static inline void save_fp_regs(s390_fp_regs *fpregs)
+{
+	asm volatile (
+		"   std   0,8(%1)\n"
+		"   std   2,24(%1)\n"
+		"   std   4,40(%1)\n"
+		"   std   6,56(%1)"
+		: "=m" (*fpregs) : "a" (fpregs), "m" (*fpregs) : "memory" );
+	if (!MACHINE_HAS_IEEE)
+		return;
+	asm volatile(
+		"   stfpc 0(%1)\n"
+		"   std   1,16(%1)\n"
+		"   std   3,32(%1)\n"
+		"   std   5,48(%1)\n"
+		"   std   7,64(%1)\n"
+		"   std   8,72(%1)\n"
+		"   std   9,80(%1)\n"
+		"   std   10,88(%1)\n"
+		"   std   11,96(%1)\n"
+		"   std   12,104(%1)\n"
+		"   std   13,112(%1)\n"
+		"   std   14,120(%1)\n"
+		"   std   15,128(%1)\n"
+		: "=m" (*fpregs) : "a" (fpregs), "m" (*fpregs) : "memory" );
+}
+
+static inline void restore_fp_regs(s390_fp_regs *fpregs)
+{
+	asm volatile (
+		"   ld    0,8(%0)\n"
+		"   ld    2,24(%0)\n"
+		"   ld    4,40(%0)\n"
+		"   ld    6,56(%0)"
+		: : "a" (fpregs), "m" (*fpregs) );
+	if (!MACHINE_HAS_IEEE)
+		return;
+	asm volatile(
+		"   lfpc  0(%0)\n"
+		"   ld    1,16(%0)\n"
+		"   ld    3,32(%0)\n"
+		"   ld    5,48(%0)\n"
+		"   ld    7,64(%0)\n"
+		"   ld    8,72(%0)\n"
+		"   ld    9,80(%0)\n"
+		"   ld    10,88(%0)\n"
+		"   ld    11,96(%0)\n"
+		"   ld    12,104(%0)\n"
+		"   ld    13,112(%0)\n"
+		"   ld    14,120(%0)\n"
+		"   ld    15,128(%0)\n"
+		: : "a" (fpregs), "m" (*fpregs) );
+}
+
+static inline void save_access_regs(unsigned int *acrs)
+{
+	asm volatile ("stam 0,15,0(%0)" : : "a" (acrs) : "memory" );
+}
+
+static inline void restore_access_regs(unsigned int *acrs)
+{
+	asm volatile ("lam 0,15,0(%0)" : : "a" (acrs) );
+}
+
+#define switch_to(prev,next,last) do {					     \
+	if (prev == next)						     \
+		break;							     \
+	save_fp_regs(&prev->thread.fp_regs);				     \
+	restore_fp_regs(&next->thread.fp_regs);				     \
+	save_access_regs(&prev->thread.acrs[0]);			     \
+	restore_access_regs(&next->thread.acrs[0]);			     \
+	prev = __switch_to(prev,next);					     \
+} while (0)
+
+#ifdef CONFIG_VIRT_CPU_ACCOUNTING
+extern void account_user_vtime(struct task_struct *);
+extern void account_system_vtime(struct task_struct *);
+#endif
+
+#define finish_arch_switch(prev) do {					     \
+	set_fs(current->thread.mm_segment);				     \
+	account_system_vtime(prev);					     \
+} while (0)
+
+#define nop() __asm__ __volatile__ ("nop")
+
+#define xchg(ptr,x) \
+  ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(void *)(ptr),sizeof(*(ptr))))
+
+static inline unsigned long __xchg(unsigned long x, void * ptr, int size)
+{
+	unsigned long addr, old;
+	int shift;
+
+        switch (size) {
+	case 1:
+		addr = (unsigned long) ptr;
+		shift = (3 ^ (addr & 3)) << 3;
+		addr ^= addr & 3;
+		asm volatile(
+			"    l   %0,0(%4)\n"
+			"0:  lr  0,%0\n"
+			"    nr  0,%3\n"
+			"    or  0,%2\n"
+			"    cs  %0,0,0(%4)\n"
+			"    jl  0b\n"
+			: "=&d" (old), "=m" (*(int *) addr)
+			: "d" (x << shift), "d" (~(255 << shift)), "a" (addr),
+			  "m" (*(int *) addr) : "memory", "cc", "0" );
+		x = old >> shift;
+		break;
+	case 2:
+		addr = (unsigned long) ptr;
+		shift = (2 ^ (addr & 2)) << 3;
+		addr ^= addr & 2;
+		asm volatile(
+			"    l   %0,0(%4)\n"
+			"0:  lr  0,%0\n"
+			"    nr  0,%3\n"
+			"    or  0,%2\n"
+			"    cs  %0,0,0(%4)\n"
+			"    jl  0b\n"
+			: "=&d" (old), "=m" (*(int *) addr)
+			: "d" (x << shift), "d" (~(65535 << shift)), "a" (addr),
+			  "m" (*(int *) addr) : "memory", "cc", "0" );
+		x = old >> shift;
+		break;
+	case 4:
+		asm volatile (
+			"    l   %0,0(%3)\n"
+			"0:  cs  %0,%2,0(%3)\n"
+			"    jl  0b\n"
+			: "=&d" (old), "=m" (*(int *) ptr)
+			: "d" (x), "a" (ptr), "m" (*(int *) ptr)
+			: "memory", "cc" );
+		x = old;
+		break;
+#ifdef __s390x__
+	case 8:
+		asm volatile (
+			"    lg  %0,0(%3)\n"
+			"0:  csg %0,%2,0(%3)\n"
+			"    jl  0b\n"
+			: "=&d" (old), "=m" (*(long *) ptr)
+			: "d" (x), "a" (ptr), "m" (*(long *) ptr)
+			: "memory", "cc" );
+		x = old;
+		break;
+#endif /* __s390x__ */
+        }
+        return x;
+}
+
+/*
+ * Atomic compare and exchange.  Compare OLD with MEM, if identical,
+ * store NEW in MEM.  Return the initial value in MEM.  Success is
+ * indicated by comparing RETURN with OLD.
+ */
+
+#define __HAVE_ARCH_CMPXCHG 1
+
+#define cmpxchg(ptr,o,n)\
+	((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\
+					(unsigned long)(n),sizeof(*(ptr))))
+
+static inline unsigned long
+__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size)
+{
+	unsigned long addr, prev, tmp;
+	int shift;
+
+        switch (size) {
+	case 1:
+		addr = (unsigned long) ptr;
+		shift = (3 ^ (addr & 3)) << 3;
+		addr ^= addr & 3;
+		asm volatile(
+			"    l   %0,0(%4)\n"
+			"0:  nr  %0,%5\n"
+                        "    lr  %1,%0\n"
+			"    or  %0,%2\n"
+			"    or  %1,%3\n"
+			"    cs  %0,%1,0(%4)\n"
+			"    jnl 1f\n"
+			"    xr  %1,%0\n"
+			"    nr  %1,%5\n"
+			"    jnz 0b\n"
+			"1:"
+			: "=&d" (prev), "=&d" (tmp)
+			: "d" (old << shift), "d" (new << shift), "a" (ptr),
+			  "d" (~(255 << shift))
+			: "memory", "cc" );
+		return prev >> shift;
+	case 2:
+		addr = (unsigned long) ptr;
+		shift = (2 ^ (addr & 2)) << 3;
+		addr ^= addr & 2;
+		asm volatile(
+			"    l   %0,0(%4)\n"
+			"0:  nr  %0,%5\n"
+                        "    lr  %1,%0\n"
+			"    or  %0,%2\n"
+			"    or  %1,%3\n"
+			"    cs  %0,%1,0(%4)\n"
+			"    jnl 1f\n"
+			"    xr  %1,%0\n"
+			"    nr  %1,%5\n"
+			"    jnz 0b\n"
+			"1:"
+			: "=&d" (prev), "=&d" (tmp)
+			: "d" (old << shift), "d" (new << shift), "a" (ptr),
+			  "d" (~(65535 << shift))
+			: "memory", "cc" );
+		return prev >> shift;
+	case 4:
+		asm volatile (
+			"    cs  %0,%2,0(%3)\n"
+			: "=&d" (prev) : "0" (old), "d" (new), "a" (ptr)
+			: "memory", "cc" );
+		return prev;
+#ifdef __s390x__
+	case 8:
+		asm volatile (
+			"    csg %0,%2,0(%3)\n"
+			: "=&d" (prev) : "0" (old), "d" (new), "a" (ptr)
+			: "memory", "cc" );
+		return prev;
+#endif /* __s390x__ */
+        }
+        return old;
+}
+
+/*
+ * Force strict CPU ordering.
+ * And yes, this is required on UP too when we're talking
+ * to devices.
+ *
+ * This is very similar to the ppc eieio/sync instruction in that is
+ * does a checkpoint syncronisation & makes sure that 
+ * all memory ops have completed wrt other CPU's ( see 7-15 POP  DJB ).
+ */
+
+#define eieio()  __asm__ __volatile__ ( "bcr 15,0" : : : "memory" ) 
+# define SYNC_OTHER_CORES(x)   eieio() 
+#define mb()    eieio()
+#define rmb()   eieio()
+#define wmb()   eieio()
+#define read_barrier_depends() do { } while(0)
+#define smp_mb()       mb()
+#define smp_rmb()      rmb()
+#define smp_wmb()      wmb()
+#define smp_read_barrier_depends()    read_barrier_depends()
+#define smp_mb__before_clear_bit()     smp_mb()
+#define smp_mb__after_clear_bit()      smp_mb()
+
+
+#define set_mb(var, value)      do { var = value; mb(); } while (0)
+#define set_wmb(var, value)     do { var = value; wmb(); } while (0)
+
+/* interrupt control.. */
+#define local_irq_enable() ({ \
+        unsigned long  __dummy; \
+        __asm__ __volatile__ ( \
+                "stosm 0(%1),0x03" \
+		: "=m" (__dummy) : "a" (&__dummy) : "memory" ); \
+        })
+
+#define local_irq_disable() ({ \
+        unsigned long __flags; \
+        __asm__ __volatile__ ( \
+                "stnsm 0(%1),0xfc" : "=m" (__flags) : "a" (&__flags) ); \
+        __flags; \
+        })
+
+#define local_save_flags(x) \
+        __asm__ __volatile__("stosm 0(%1),0" : "=m" (x) : "a" (&x), "m" (x) )
+
+#define local_irq_restore(x) \
+        __asm__ __volatile__("ssm   0(%0)" : : "a" (&x), "m" (x) : "memory")
+
+#define irqs_disabled()			\
+({					\
+	unsigned long flags;		\
+	local_save_flags(flags);	\
+        !((flags >> __FLAG_SHIFT) & 3);	\
+})
+
+#ifdef __s390x__
+
+#define __ctl_load(array, low, high) ({ \
+	typedef struct { char _[sizeof(array)]; } addrtype; \
+	__asm__ __volatile__ ( \
+		"   bras  1,0f\n" \
+                "   lctlg 0,0,0(%0)\n" \
+		"0: ex    %1,0(1)" \
+		: : "a" (&array), "a" (((low)<<4)+(high)), \
+		    "m" (*(addrtype *)(array)) : "1" ); \
+	})
+
+#define __ctl_store(array, low, high) ({ \
+	typedef struct { char _[sizeof(array)]; } addrtype; \
+	__asm__ __volatile__ ( \
+		"   bras  1,0f\n" \
+		"   stctg 0,0,0(%1)\n" \
+		"0: ex    %2,0(1)" \
+		: "=m" (*(addrtype *)(array)) \
+		: "a" (&array), "a" (((low)<<4)+(high)) : "1" ); \
+	})
+
+#define __ctl_set_bit(cr, bit) ({ \
+        __u8 __dummy[24]; \
+        __asm__ __volatile__ ( \
+                "    bras  1,0f\n"       /* skip indirect insns */ \
+                "    stctg 0,0,0(%1)\n" \
+                "    lctlg 0,0,0(%1)\n" \
+                "0:  ex    %2,0(1)\n"    /* execute stctl */ \
+                "    lg    0,0(%1)\n" \
+                "    ogr   0,%3\n"       /* set the bit */ \
+                "    stg   0,0(%1)\n" \
+                "1:  ex    %2,6(1)"      /* execute lctl */ \
+                : "=m" (__dummy) \
+		: "a" ((((unsigned long) &__dummy) + 7) & ~7UL), \
+		  "a" (cr*17), "a" (1L<<(bit)) \
+                : "cc", "0", "1" ); \
+        })
+
+#define __ctl_clear_bit(cr, bit) ({ \
+        __u8 __dummy[16]; \
+        __asm__ __volatile__ ( \
+                "    bras  1,0f\n"       /* skip indirect insns */ \
+                "    stctg 0,0,0(%1)\n" \
+                "    lctlg 0,0,0(%1)\n" \
+                "0:  ex    %2,0(1)\n"    /* execute stctl */ \
+                "    lg    0,0(%1)\n" \
+                "    ngr   0,%3\n"       /* set the bit */ \
+                "    stg   0,0(%1)\n" \
+                "1:  ex    %2,6(1)"      /* execute lctl */ \
+                : "=m" (__dummy) \
+		: "a" ((((unsigned long) &__dummy) + 7) & ~7UL), \
+		  "a" (cr*17), "a" (~(1L<<(bit))) \
+                : "cc", "0", "1" ); \
+        })
+
+#else /* __s390x__ */
+
+#define __ctl_load(array, low, high) ({ \
+	typedef struct { char _[sizeof(array)]; } addrtype; \
+	__asm__ __volatile__ ( \
+		"   bras  1,0f\n" \
+                "   lctl 0,0,0(%0)\n" \
+		"0: ex    %1,0(1)" \
+		: : "a" (&array), "a" (((low)<<4)+(high)), \
+		    "m" (*(addrtype *)(array)) : "1" ); \
+	})
+
+#define __ctl_store(array, low, high) ({ \
+	typedef struct { char _[sizeof(array)]; } addrtype; \
+	__asm__ __volatile__ ( \
+		"   bras  1,0f\n" \
+		"   stctl 0,0,0(%1)\n" \
+		"0: ex    %2,0(1)" \
+		: "=m" (*(addrtype *)(array)) \
+		: "a" (&array), "a" (((low)<<4)+(high)): "1" ); \
+	})
+
+#define __ctl_set_bit(cr, bit) ({ \
+        __u8 __dummy[16]; \
+        __asm__ __volatile__ ( \
+                "    bras  1,0f\n"       /* skip indirect insns */ \
+                "    stctl 0,0,0(%1)\n" \
+                "    lctl  0,0,0(%1)\n" \
+                "0:  ex    %2,0(1)\n"    /* execute stctl */ \
+                "    l     0,0(%1)\n" \
+                "    or    0,%3\n"       /* set the bit */ \
+                "    st    0,0(%1)\n" \
+                "1:  ex    %2,4(1)"      /* execute lctl */ \
+                : "=m" (__dummy) \
+		: "a" ((((unsigned long) &__dummy) + 7) & ~7UL), \
+		  "a" (cr*17), "a" (1<<(bit)) \
+                : "cc", "0", "1" ); \
+        })
+
+#define __ctl_clear_bit(cr, bit) ({ \
+        __u8 __dummy[16]; \
+        __asm__ __volatile__ ( \
+                "    bras  1,0f\n"       /* skip indirect insns */ \
+                "    stctl 0,0,0(%1)\n" \
+                "    lctl  0,0,0(%1)\n" \
+                "0:  ex    %2,0(1)\n"    /* execute stctl */ \
+                "    l     0,0(%1)\n" \
+                "    nr    0,%3\n"       /* set the bit */ \
+                "    st    0,0(%1)\n" \
+                "1:  ex    %2,4(1)"      /* execute lctl */ \
+                : "=m" (__dummy) \
+		: "a" ((((unsigned long) &__dummy) + 7) & ~7UL), \
+		  "a" (cr*17), "a" (~(1<<(bit))) \
+                : "cc", "0", "1" ); \
+        })
+#endif /* __s390x__ */
+
+/* For spinlocks etc */
+#define local_irq_save(x)	((x) = local_irq_disable())
+
+/*
+ * Use to set psw mask except for the first byte which
+ * won't be changed by this function.
+ */
+static inline void
+__set_psw_mask(unsigned long mask)
+{
+	local_save_flags(mask);
+	__load_psw_mask(mask);
+}
+
+#define local_mcck_enable()  __set_psw_mask(PSW_KERNEL_BITS)
+#define local_mcck_disable() __set_psw_mask(PSW_KERNEL_BITS & ~PSW_MASK_MCHECK)
+
+#ifdef CONFIG_SMP
+
+extern void smp_ctl_set_bit(int cr, int bit);
+extern void smp_ctl_clear_bit(int cr, int bit);
+#define ctl_set_bit(cr, bit) smp_ctl_set_bit(cr, bit)
+#define ctl_clear_bit(cr, bit) smp_ctl_clear_bit(cr, bit)
+
+#else
+
+#define ctl_set_bit(cr, bit) __ctl_set_bit(cr, bit)
+#define ctl_clear_bit(cr, bit) __ctl_clear_bit(cr, bit)
+
+#endif /* CONFIG_SMP */
+
+extern void (*_machine_restart)(char *command);
+extern void (*_machine_halt)(void);
+extern void (*_machine_power_off)(void);
+
+#define arch_align_stack(x) (x)
+
+#endif /* __KERNEL__ */
+
+#endif
+
diff -urN oldtree/include/asm-x86_64/uaccess.h newtree/include/asm-x86_64/uaccess.h
--- oldtree/include/asm-x86_64/uaccess.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/asm-x86_64/uaccess.h	2006-02-13 19:20:01.206328912 -0500
@@ -244,7 +244,7 @@
 extern unsigned long copy_from_user(void *to, const void __user *from, unsigned len); 
 extern unsigned long copy_in_user(void __user *to, const void __user *from, unsigned len); 
 
-static inline int __copy_from_user(void *dst, const void __user *src, unsigned size) 
+static __always_inline int __copy_from_user(void *dst, const void __user *src, unsigned size)
 { 
        int ret = 0;
 	if (!__builtin_constant_p(size))
@@ -273,7 +273,7 @@
 	}
 }	
 
-static inline int __copy_to_user(void __user *dst, const void *src, unsigned size) 
+static __always_inline int __copy_to_user(void __user *dst, const void *src, unsigned size)
 { 
        int ret = 0;
 	if (!__builtin_constant_p(size))
@@ -305,7 +305,7 @@
 }	
 
 
-static inline int __copy_in_user(void __user *dst, const void __user *src, unsigned size) 
+static __always_inline int __copy_in_user(void __user *dst, const void __user *src, unsigned size)
 { 
        int ret = 0;
 	if (!__builtin_constant_p(size))
diff -urN oldtree/include/linux/amigasfs.h newtree/include/linux/amigasfs.h
--- oldtree/include/linux/amigasfs.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/linux/amigasfs.h	2006-02-13 19:20:01.208328608 -0500
@@ -0,0 +1,276 @@
+#ifndef __LINUX_AMIGASFS_H
+#define __LINUX_AMIGASFS_H
+
+#include <linux/types.h>
+
+/* some helper macros... */
+#define ASFS_MAKE_ID(a,b,c,d) (((a)&0xff)<<24|((b)&0xff)<<16|((c)&0xff)<<8|((d)&0xff))
+
+/* Amiga SFS block IDs */
+#define ASFS_ROOTID                 ASFS_MAKE_ID('S','F','S','\0')
+#define ASFS_OBJECTCONTAINER_ID     ASFS_MAKE_ID('O','B','J','C')
+#define ASFS_BNODECONTAINER_ID      ASFS_MAKE_ID('B','N','D','C')
+#define ASFS_NODECONTAINER_ID       ASFS_MAKE_ID('N','D','C',' ')
+#define ASFS_HASHTABLE_ID           ASFS_MAKE_ID('H','T','A','B')
+#define ASFS_SOFTLINK_ID            ASFS_MAKE_ID('S','L','N','K')
+#define ASFS_ADMINSPACECONTAINER_ID ASFS_MAKE_ID('A','D','M','C')
+#define ASFS_BITMAP_ID              ASFS_MAKE_ID('B','T','M','P')
+#define ASFS_TRANSACTIONFAILURE_ID  ASFS_MAKE_ID('T','R','F','A')
+
+/* Amiga SFS defines and magic values */
+
+#define ASFS_MAGIC 0xa0ff
+#define ASFS_MAXFN (105u)
+#define ASFS_MAXFILESIZE 0x8FFFFFFE
+
+#define ASFS_STRUCTURE_VERISON (3)
+#define ASFS_BLCKFACCURACY	(5)
+
+#define ASFS_ROOTBITS_CASESENSITIVE (128)
+#define ASFS_READONLY (512)
+#define ASFS_VOL_LOWERCASE (1024)
+
+#define ASFS_ROOTNODE   (1)
+#define ASFS_RECYCLEDNODE (2)
+
+#define OTYPE_HIDDEN      (1)
+#define OTYPE_HARDLINK    (32)
+#define OTYPE_LINK        (64)
+#define OTYPE_DIR         (128)
+
+#define MSB_MASK (1ul << 31)
+
+#define NODE_STRUCT_SIZE (10)	/* (sizeof(struct fsObjectNode)) */
+#define NODECONT_BLOCK_COUNT ((sb->s_blocksize - sizeof(struct fsNodeContainer)) / sizeof(u32))
+
+#define ASFS_ALWAYSFREE (16)		/* keep this amount of blocks free */
+
+#define ASFS_BLOCKCHUNKS (16)		/* try to allocate this number of blocks in one request */
+
+#ifndef TRUE
+#define TRUE		1
+#endif
+#ifndef FALSE
+#define FALSE		0
+#endif
+
+/* amigados protection bits */
+
+#define FIBB_SCRIPT    6	/* program is a script (execute) file */
+#define FIBB_PURE      5	/* program is reentrant and rexecutable */
+#define FIBB_ARCHIVE   4	/* cleared whenever file is changed */
+#define FIBB_READ      3	/* ignored by old filesystem */
+#define FIBB_WRITE     2	/* ignored by old filesystem */
+#define FIBB_EXECUTE   1	/* ignored by system, used by Shell */
+#define FIBB_DELETE    0	/* prevent file from being deleted */
+
+#define FIBF_SCRIPT    (1<<FIBB_SCRIPT)
+#define FIBF_PURE      (1<<FIBB_PURE)
+#define FIBF_ARCHIVE   (1<<FIBB_ARCHIVE)
+#define FIBF_READ      (1<<FIBB_READ)
+#define FIBF_WRITE     (1<<FIBB_WRITE)
+#define FIBF_EXECUTE   (1<<FIBB_EXECUTE)
+#define FIBF_DELETE    (1<<FIBB_DELETE)
+
+/* name hashing macro */
+
+#define HASHCHAIN(x) (u16)(x % (u16)(((sb->s_blocksize) - sizeof(struct fsHashTable))>>2))
+
+/* Each block has its own header with checksum and id, its called fsBlockHeader */
+
+struct fsBlockHeader {
+	u32 id;			/* 4 character id string of this block */
+	u32 checksum;		/* The checksum */
+	u32 ownblock;		/* The blocknumber of the block this block is stored at */
+};
+
+/* On-disk "super block", called fsRootBlock */
+
+struct fsRootBlock {
+	struct fsBlockHeader bheader;
+
+	u16 version;		/* Version number of the filesystem block structure */
+	u16 sequencenumber;	/* The Root with the highest sequencenumber is valid */
+
+	u32 datecreated;	/* Creation date (when first formatted).  Cannot be changed. */
+	u8 bits;		/* various settings, see defines below. */
+	u8 pad1;
+	u16 pad2;
+
+	u32 reserved1[2];
+
+	u32 firstbyteh;		/* The first byte of our partition from the start of the */
+	u32 firstbyte;		/* disk.  firstbyteh = upper 32 bits, firstbyte = lower 32 bits. */
+
+	u32 lastbyteh;		/* The last byte of our partition, excluding this one. */
+	u32 lastbyte;
+
+	u32 totalblocks;	/* size of this partition in blocks */
+	u32 blocksize;		/* blocksize used */
+
+	u32 reserved2[2];
+	u32 reserved3[8];
+
+	u32 bitmapbase;		/* location of the bitmap */
+	u32 adminspacecontainer;	/* location of first adminspace container */
+	u32 rootobjectcontainer;	/* location of the root objectcontainer */
+	u32 extentbnoderoot;	/* location of the root of the extentbnode B-tree */
+	u32 objectnoderoot;	/* location of the root of the objectnode tree */
+
+	u32 reserved4[3];
+};
+
+/* On disk inode, called fsObject */
+
+struct fsObject {
+	u16 owneruid;
+	u16 ownergid;
+	u32 objectnode;
+	u32 protection;
+
+	union {
+		struct {
+			u32 data;
+			u32 size;
+		} file;
+
+		struct {
+			u32 hashtable;	/* for directories & root, 0 means no hashblock */
+			u32 firstdirblock;
+		} dir;
+	} object;
+
+	u32 datemodified;
+	u8 bits;
+
+	u8 name[0];
+	u8 comment[0];
+};
+
+/* On disk block containging a number of fsObjects */
+
+struct fsObjectContainer {
+	struct fsBlockHeader bheader;
+
+	u32 parent;
+	u32 next;
+	u32 previous;		/* 0 for the first block in the directory chain */
+
+	struct fsObject object[0];
+};
+
+/* BTree structures, used to collect file data position on disk */
+
+struct fsExtentBNode {
+	u32 key;		/* data! */
+	u32 next;
+	u32 prev;
+	u16 blocks;		/* The size in blocks of the region this Extent controls */
+};
+
+struct BNode {
+	u32 key;
+	u32 data;
+};
+
+struct BTreeContainer {
+	u16 nodecount;
+	u8 isleaf;
+	u8 nodesize;		/* Must be a multiple of 2 */
+
+	struct BNode bnode[0];
+};
+
+/* On disk block with BTreeContainer */
+
+struct fsBNodeContainer {
+	struct fsBlockHeader bheader;
+	struct BTreeContainer btc;
+};
+
+/* On disk block  with soft link data */
+
+struct fsSoftLink {
+	struct fsBlockHeader bheader;
+	u32 parent;
+	u32 next;
+	u32 previous;
+	u8 string[0];
+};
+
+/* On disk block with hashtable data */
+
+struct fsHashTable {
+	struct fsBlockHeader bheader;
+	u32 parent;
+	u32 hashentry[0];
+};
+
+/* On disk block with node index and some helper structures */
+
+struct fsNodeContainer {
+	struct fsBlockHeader bheader;
+	u32 nodenumber;
+	u32 nodes;
+	u32 node[0];
+};
+
+struct fsNode {
+	u32 data;
+};
+
+struct fsObjectNode {
+	struct fsNode node;
+	u32 next;
+	u16 hash16;
+} __attribute__ ((packed));
+
+/* Some adminspace and bitmap block structures */
+
+struct fsAdminSpace {
+	u32 space;
+	u32 bits;
+/* Set bits are used blocks, bit 31 is	the first block in the AdminSpace. */
+};
+
+struct fsAdminSpaceContainer {
+	struct fsBlockHeader bheader;
+
+	u32 next;
+	u32 previous;
+
+	u8 bits;
+	u8 pad1;
+	u16 pad2;
+
+	struct fsAdminSpace adminspace[0];
+};
+
+struct fsBitmap {
+	struct fsBlockHeader bheader;
+
+	u32 bitmap[0];
+
+/* Bits are 1 if the block is free, and 0 if full.
+   Bitmap must consist of an integral number of longwords. */
+};
+
+/* The fsRootInfo structure has all kinds of information about the format
+   of the disk. */
+
+struct fsRootInfo {
+	u32 deletedblocks;	/* Amount in blocks which deleted files consume. */
+	u32 deletedfiles;	/* Number of deleted files in recycled. */
+	u32 freeblocks;		/* Cached number of free blocks on disk. */
+
+	u32 datecreated;
+
+	u32 lastallocatedblock;	/* Block which was most recently allocated */
+	u32 lastallocatedadminspace;	/* AdminSpaceContainer which most recently was used to allocate a block */
+	u32 lastallocatedextentnode;	/* ExtentNode which was most recently created */
+	u32 lastallocatedobjectnode;	/* ObjectNode which was most recently created */
+
+	u32 rovingpointer;
+};
+
+#endif
diff -urN oldtree/include/linux/arcdevice.h newtree/include/linux/arcdevice.h
--- oldtree/include/linux/arcdevice.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/arcdevice.h	2006-02-13 19:20:01.209328456 -0500
@@ -206,7 +206,6 @@
 
 extern struct ArcProto *arc_proto_map[256], *arc_proto_default,
 	*arc_bcast_proto, *arc_raw_proto;
-extern struct ArcProto arc_proto_null;
 
 
 /*
@@ -334,17 +333,9 @@
 #define arcnet_dump_skb(dev,skb,desc) ;
 #endif
 
-#if (ARCNET_DEBUG_MAX & D_RX) || (ARCNET_DEBUG_MAX & D_TX)
-void arcnet_dump_packet(struct net_device *dev, int bufnum, char *desc,
-			int take_arcnet_lock);
-#else
-#define arcnet_dump_packet(dev, bufnum, desc,take_arcnet_lock) ;
-#endif
-
 void arcnet_unregister_proto(struct ArcProto *proto);
 irqreturn_t arcnet_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 struct net_device *alloc_arcdev(char *name);
-void arcnet_rx(struct net_device *dev, int bufnum);
 
 #endif				/* __KERNEL__ */
 #endif				/* _LINUX_ARCDEVICE_H */
diff -urN oldtree/include/linux/audit.h newtree/include/linux/audit.h
--- oldtree/include/linux/audit.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/audit.h	2006-02-13 19:20:01.210328304 -0500
@@ -33,11 +33,20 @@
  * 1200 - 1299 messages internal to the audit daemon
  * 1300 - 1399 audit event messages
  * 1400 - 1499 SE Linux use
- * 1500 - 1999 future use
- * 2000 is for otherwise unclassified kernel audit messages
+ * 1500 - 1599 kernel LSPP events
+ * 1600 - 1699 kernel crypto events
+ * 1700 - 1999 future kernel use (maybe integrity labels and related events)
+ * 2000 is for otherwise unclassified kernel audit messages (legacy)
+ * 2001 - 2099 unused (kernel)
+ * 2100 - 2199 user space anomaly records
+ * 2200 - 2299 user space actions taken in response to anomalies
+ * 2300 - 2399 user space generated LSPP events
+ * 2400 - 2499 user space crypto events
+ * 2500 - 2999 future user space (maybe integrity labels and related events)
  *
- * Messages from 1000-1199 are bi-directional. 1200-1299 are exclusively user
- * space. Anything over that is kernel --> user space communication.
+ * Messages from 1000-1199 are bi-directional. 1200-1299 & 2100 - 2999 are
+ * exclusively user space. 1300-2099 is kernel --> user space 
+ * communication.
  */
 #define AUDIT_GET		1000	/* Get status */
 #define AUDIT_SET		1001	/* Set status (enable/disable/auditd) */
@@ -54,6 +63,8 @@
 #define AUDIT_FIRST_USER_MSG	1100	/* Userspace messages mostly uninteresting to kernel */
 #define AUDIT_USER_AVC		1107	/* We filter this differently */
 #define AUDIT_LAST_USER_MSG	1199
+#define AUDIT_FIRST_USER_MSG2	2100	/* More user space messages */
+#define AUDIT_LAST_USER_MSG2	2999
  
 #define AUDIT_DAEMON_START      1200    /* Daemon startup record */
 #define AUDIT_DAEMON_END        1201    /* Daemon normal stop record */
@@ -72,6 +83,9 @@
 #define AUDIT_AVC		1400	/* SE Linux avc denial or grant */
 #define AUDIT_SELINUX_ERR	1401	/* Internal SE Linux Errors */
 #define AUDIT_AVC_PATH		1402	/* dentry, vfsmount pair from avc */
+#define AUDIT_MAC_POLICY_LOAD	1403	/* Policy file load */
+#define AUDIT_MAC_STATUS	1404	/* Changed enforcing,permissive,off */
+#define AUDIT_MAC_CONFIG_CHANGE	1405	/* Changes to booleans */
 
 #define AUDIT_KERNEL		2000	/* Asynchronous audit record. NOT A REQUEST. */
 
@@ -81,8 +95,9 @@
 #define AUDIT_FILTER_ENTRY	0x02	/* Apply rule at syscall entry */
 #define AUDIT_FILTER_WATCH	0x03	/* Apply rule to file system watches */
 #define AUDIT_FILTER_EXIT	0x04	/* Apply rule at syscall exit */
+#define AUDIT_FILTER_TYPE	0x05	/* Apply rule at audit_log_start */
 
-#define AUDIT_NR_FILTERS	5
+#define AUDIT_NR_FILTERS	6
 
 #define AUDIT_FILTER_PREPEND	0x10	/* Prepend to front of list */
 
@@ -98,6 +113,13 @@
 #define AUDIT_WORD(nr) ((__u32)((nr)/32))
 #define AUDIT_BIT(nr)  (1 << ((nr) - AUDIT_WORD(nr)*32))
 
+/* This bitmask is used to validate user input.  It represents all bits that
+ * are currently used in an audit field constant understood by the kernel.
+ * If you are adding a new #define AUDIT_<whatever>, please ensure that
+ * AUDIT_UNUSED_BITS is updated if need be. */
+#define AUDIT_UNUSED_BITS	0x0FFFFC00
+
+
 /* Rule fields */
 				/* These are useful when checking the
 				 * task structure at task creation time
@@ -114,6 +136,7 @@
 #define AUDIT_LOGINUID	9
 #define AUDIT_PERS	10
 #define AUDIT_ARCH	11
+#define AUDIT_MSGTYPE	12
 
 				/* These are ONLY useful when checking
 				 * at syscall exit time (AUDIT_AT_EXIT). */
@@ -128,8 +151,28 @@
 #define AUDIT_ARG2      (AUDIT_ARG0+2)
 #define AUDIT_ARG3      (AUDIT_ARG0+3)
 
-#define AUDIT_NEGATE    0x80000000
+#define AUDIT_NEGATE			0x80000000
 
+/* These are the supported operators.
+ *	4  2  1
+ *	=  >  <
+ *	-------
+ *	0  0  0		0	nonsense
+ *	0  0  1		1	<
+ *	0  1  0		2	>
+ *	0  1  1		3	!=
+ *	1  0  0		4	=
+ *	1  0  1		5	<=
+ *	1  1  0		6	>=
+ *	1  1  1		7	all operators
+ */
+#define AUDIT_LESS_THAN			0x10000000
+#define AUDIT_GREATER_THAN		0x20000000
+#define AUDIT_NOT_EQUAL			0x30000000
+#define AUDIT_EQUAL			0x40000000
+#define AUDIT_LESS_THAN_OR_EQUAL	(AUDIT_LESS_THAN|AUDIT_EQUAL)
+#define AUDIT_GREATER_THAN_OR_EQUAL	(AUDIT_GREATER_THAN|AUDIT_EQUAL)
+#define AUDIT_OPERATORS			(AUDIT_EQUAL|AUDIT_NOT_EQUAL)
 
 /* Status symbols */
 				/* Mask values */
@@ -222,22 +265,34 @@
 extern void audit_syscall_exit(struct task_struct *task, int failed, long return_code);
 extern void audit_getname(const char *name);
 extern void audit_putname(const char *name);
-extern void audit_inode(const char *name, const struct inode *inode, unsigned flags);
+extern void __audit_inode(const char *name, const struct inode *inode, unsigned flags);
+extern void __audit_inode_child(const char *dname, const struct inode *inode,
+				unsigned long pino);
+static inline void audit_inode(const char *name, const struct inode *inode,
+			       unsigned flags) {
+	if (unlikely(current->audit_context))
+		__audit_inode(name, inode, flags);
+}
+static inline void audit_inode_child(const char *dname, 
+				     const struct inode *inode, 
+				     unsigned long pino) {
+	if (unlikely(current->audit_context))
+		__audit_inode_child(dname, inode, pino);
+}
 
 				/* Private API (for audit.c only) */
-extern int  audit_receive_filter(int type, int pid, int uid, int seq,
-				 void *data, uid_t loginuid);
 extern unsigned int audit_serial(void);
 extern void auditsc_get_stamp(struct audit_context *ctx,
 			      struct timespec *t, unsigned int *serial);
 extern int  audit_set_loginuid(struct task_struct *task, uid_t loginuid);
 extern uid_t audit_get_loginuid(struct audit_context *ctx);
-extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode);
+extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp);
 extern int audit_socketcall(int nargs, unsigned long *args);
 extern int audit_sockaddr(int len, void *addr);
 extern int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt);
 extern void audit_signal_info(int sig, struct task_struct *t);
-extern int audit_filter_user(struct netlink_skb_parms *cb, int type);
+extern char *audit_ipc_context(struct kern_ipc_perm *ipcp);
+extern int audit_set_macxattr(const char *name);
 #else
 #define audit_alloc(t) ({ 0; })
 #define audit_free(t) do { ; } while (0)
@@ -245,16 +300,19 @@
 #define audit_syscall_exit(t,f,r) do { ; } while (0)
 #define audit_getname(n) do { ; } while (0)
 #define audit_putname(n) do { ; } while (0)
+#define __audit_inode(n,i,f) do { ; } while (0)
+#define __audit_inode_child(d,i,p) do { ; } while (0)
 #define audit_inode(n,i,f) do { ; } while (0)
-#define audit_receive_filter(t,p,u,s,d,l) ({ -EOPNOTSUPP; })
+#define audit_inode_child(d,i,p) do { ; } while (0)
 #define auditsc_get_stamp(c,t,s) do { BUG(); } while (0)
 #define audit_get_loginuid(c) ({ -1; })
-#define audit_ipc_perms(q,u,g,m) ({ 0; })
+#define audit_ipc_perms(q,u,g,m,i) ({ 0; })
 #define audit_socketcall(n,a) ({ 0; })
 #define audit_sockaddr(len, addr) ({ 0; })
 #define audit_avc_path(dentry, mnt) ({ 0; })
 #define audit_signal_info(s,t) do { ; } while (0)
-#define audit_filter_user(cb,t) ({ 1; })
+#define audit_ipc_context(i) do { ; } while (0)
+#define audit_set_macxattr(n) do { ; } while (0)
 #endif
 
 #ifdef CONFIG_AUDIT
@@ -278,12 +336,11 @@
 					     const char *prefix,
 					     struct dentry *dentry,
 					     struct vfsmount *vfsmnt);
-				/* Private API (for auditsc.c only) */
-extern void		    audit_send_reply(int pid, int seq, int type,
-					     int done, int multi,
-					     void *payload, int size);
-extern void		    audit_log_lost(const char *message);
-extern struct semaphore audit_netlink_sem;
+				/* Private API (for audit.c only) */
+extern int audit_filter_user(struct netlink_skb_parms *cb, int type);
+extern int audit_filter_type(int type);
+extern int  audit_receive_filter(int type, int pid, int uid, int seq,
+				 void *data, uid_t loginuid);
 #else
 #define audit_log(c,g,t,f,...) do { ; } while (0)
 #define audit_log_start(c,g,t) ({ NULL; })
@@ -293,6 +350,7 @@
 #define audit_log_hex(a,b,l) do { ; } while (0)
 #define audit_log_untrustedstring(a,s) do { ; } while (0)
 #define audit_log_d_path(b,p,d,v) do { ; } while (0)
+#define audit_panic(m) do { ; } while (0)
 #endif
 #endif
 #endif
diff -urN oldtree/include/linux/blkdev.h newtree/include/linux/blkdev.h
--- oldtree/include/linux/blkdev.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/blkdev.h	2006-02-13 19:20:29.739991136 -0500
@@ -559,7 +559,6 @@
 extern void generic_make_request(struct bio *bio);
 extern void blk_put_request(struct request *);
 extern void blk_end_sync_rq(struct request *rq);
-extern void blk_attempt_remerge(request_queue_t *, struct request *);
 extern struct request *blk_get_request(request_queue_t *, int, gfp_t);
 extern void blk_insert_request(request_queue_t *, struct request *, int, void *);
 extern void blk_requeue_request(request_queue_t *, struct request *);
diff -urN oldtree/include/linux/cdrom.h newtree/include/linux/cdrom.h
--- oldtree/include/linux/cdrom.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/cdrom.h	2006-02-13 19:20:01.212328000 -0500
@@ -378,7 +378,6 @@
 #define CDC_MEDIA_CHANGED 	0x80    /* media changed */
 #define CDC_PLAY_AUDIO		0x100   /* audio functions */
 #define CDC_RESET               0x200   /* hard reset device */
-#define CDC_IOCTLS              0x400   /* driver has non-standard ioctls */
 #define CDC_DRIVE_STATUS        0x800   /* driver implements drive status */
 #define CDC_GENERIC_PACKET	0x1000	/* driver implements generic packets */
 #define CDC_CD_R		0x2000	/* drive is a CD-R */
@@ -974,9 +973,7 @@
 	int (*reset) (struct cdrom_device_info *);
 	/* play stuff */
 	int (*audio_ioctl) (struct cdrom_device_info *,unsigned int, void *);
-	/* dev-specific */
- 	int (*dev_ioctl) (struct cdrom_device_info *,
-			  unsigned int, unsigned long);
+
 /* driver specifications */
 	const int capability;   /* capability flags */
 	int n_minors;           /* number of active minor devices */
diff -urN oldtree/include/linux/cpuset.h newtree/include/linux/cpuset.h
--- oldtree/include/linux/cpuset.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/cpuset.h	2006-02-13 19:20:01.212328000 -0500
@@ -28,6 +28,9 @@
 extern struct file_operations proc_cpuset_operations;
 extern char *cpuset_task_status_allowed(struct task_struct *task, char *buffer);
 
+extern void cpuset_lock(void);
+extern void cpuset_unlock(void);
+
 #else /* !CONFIG_CPUSETS */
 
 static inline int cpuset_init(void) { return 0; }
@@ -65,6 +68,9 @@
 	return buffer;
 }
 
+static inline void cpuset_lock(void) {}
+static inline void cpuset_unlock(void) {}
+
 #endif /* !CONFIG_CPUSETS */
 
 #endif /* _LINUX_CPUSET_H */
diff -urN oldtree/include/linux/cpuset.h.orig newtree/include/linux/cpuset.h.orig
--- oldtree/include/linux/cpuset.h.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/linux/cpuset.h.orig	2006-02-13 19:20:01.213327848 -0500
@@ -0,0 +1,70 @@
+#ifndef _LINUX_CPUSET_H
+#define _LINUX_CPUSET_H
+/*
+ *  cpuset interface
+ *
+ *  Copyright (C) 2003 BULL SA
+ *  Copyright (C) 2004 Silicon Graphics, Inc.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/cpumask.h>
+#include <linux/nodemask.h>
+
+#ifdef CONFIG_CPUSETS
+
+extern int cpuset_init(void);
+extern void cpuset_init_smp(void);
+extern void cpuset_fork(struct task_struct *p);
+extern void cpuset_exit(struct task_struct *p);
+extern cpumask_t cpuset_cpus_allowed(const struct task_struct *p);
+void cpuset_init_current_mems_allowed(void);
+void cpuset_update_current_mems_allowed(void);
+void cpuset_restrict_to_mems_allowed(unsigned long *nodes);
+int cpuset_zonelist_valid_mems_allowed(struct zonelist *zl);
+extern int cpuset_zone_allowed(struct zone *z, gfp_t gfp_mask);
+extern int cpuset_excl_nodes_overlap(const struct task_struct *p);
+extern struct file_operations proc_cpuset_operations;
+extern char *cpuset_task_status_allowed(struct task_struct *task, char *buffer);
+
+#else /* !CONFIG_CPUSETS */
+
+static inline int cpuset_init(void) { return 0; }
+static inline void cpuset_init_smp(void) {}
+static inline void cpuset_fork(struct task_struct *p) {}
+static inline void cpuset_exit(struct task_struct *p) {}
+
+static inline cpumask_t cpuset_cpus_allowed(struct task_struct *p)
+{
+	return cpu_possible_map;
+}
+
+static inline void cpuset_init_current_mems_allowed(void) {}
+static inline void cpuset_update_current_mems_allowed(void) {}
+static inline void cpuset_restrict_to_mems_allowed(unsigned long *nodes) {}
+
+static inline int cpuset_zonelist_valid_mems_allowed(struct zonelist *zl)
+{
+	return 1;
+}
+
+static inline int cpuset_zone_allowed(struct zone *z, gfp_t gfp_mask)
+{
+	return 1;
+}
+
+static inline int cpuset_excl_nodes_overlap(const struct task_struct *p)
+{
+	return 1;
+}
+
+static inline char *cpuset_task_status_allowed(struct task_struct *task,
+							char *buffer)
+{
+	return buffer;
+}
+
+#endif /* !CONFIG_CPUSETS */
+
+#endif /* _LINUX_CPUSET_H */
diff -urN oldtree/include/linux/ext3_fs.h newtree/include/linux/ext3_fs.h
--- oldtree/include/linux/ext3_fs.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/ext3_fs.h	2006-02-13 19:20:01.213327848 -0500
@@ -36,7 +36,8 @@
  * Define EXT3_RESERVATION to reserve data blocks for expanding files
  */
 #define EXT3_DEFAULT_RESERVE_BLOCKS     8
-#define EXT3_MAX_RESERVE_BLOCKS         1024
+/*max window size: 1024(direct blocks) + 3([t,d]indirect blocks) */
+#define EXT3_MAX_RESERVE_BLOCKS         1027
 #define EXT3_RESERVE_WINDOW_NOT_ALLOCATED 0
 /*
  * Always enable hashed directories
@@ -732,6 +733,8 @@
 extern int ext3_bg_has_super(struct super_block *sb, int group);
 extern unsigned long ext3_bg_num_gdb(struct super_block *sb, int group);
 extern int ext3_new_block (handle_t *, struct inode *, unsigned long, int *);
+extern int ext3_new_blocks (handle_t *, struct inode *, unsigned long,
+			unsigned long *, int *);
 extern void ext3_free_blocks (handle_t *, struct inode *, unsigned long,
 			      unsigned long);
 extern void ext3_free_blocks_sb (handle_t *, struct super_block *,
@@ -772,9 +775,12 @@
 
 
 /* inode.c */
-extern int ext3_forget(handle_t *, int, struct inode *, struct buffer_head *, int);
-extern struct buffer_head * ext3_getblk (handle_t *, struct inode *, long, int, int *);
-extern struct buffer_head * ext3_bread (handle_t *, struct inode *, int, int, int *);
+int ext3_forget(handle_t *, int, struct inode *, struct buffer_head *, int);
+struct buffer_head * ext3_getblk (handle_t *, struct inode *, long, int, int *);
+struct buffer_head * ext3_bread (handle_t *, struct inode *, int, int, int *);
+int ext3_get_blocks_handle(handle_t *handle, struct inode *inode,
+	sector_t iblock, unsigned long maxblocks, struct buffer_head *bh_result,
+	int create, int extend_disksize);
 
 extern void ext3_read_inode (struct inode *);
 extern int  ext3_write_inode (struct inode *, int);
diff -urN oldtree/include/linux/file.h newtree/include/linux/file.h
--- oldtree/include/linux/file.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/file.h	2006-02-13 19:20:01.215327544 -0500
@@ -17,10 +17,17 @@
  */
 #define NR_OPEN_DEFAULT BITS_PER_LONG
 
+/*
+ * The embedded_fd_set is a small fd_set,
+ * suitable for most tasks (which open <= BITS_PER_LONG files)
+ */
+typedef struct {
+	unsigned long fds_bits[1];
+} embedded_fd_set;
+
 struct fdtable {
 	unsigned int max_fds;
 	int max_fdset;
-	int next_fd;
 	struct file ** fd;      /* current fd array */
 	fd_set *close_on_exec;
 	fd_set *open_fds;
@@ -33,13 +40,20 @@
  * Open file table structure
  */
 struct files_struct {
+  /*
+   * read mostly part
+   */
 	atomic_t count;
 	struct fdtable *fdt;
 	struct fdtable fdtab;
-	fd_set close_on_exec_init;
-	fd_set open_fds_init;
+  /*
+   * written part on a separate cache line in SMP
+   */
+	spinlock_t file_lock ____cacheline_aligned_in_smp;
+	int next_fd;
+	embedded_fd_set close_on_exec_init;
+	embedded_fd_set open_fds_init;
 	struct file * fd_array[NR_OPEN_DEFAULT];
-	spinlock_t file_lock;     /* Protects concurrent writers.  Nests inside tsk->alloc_lock */
 };
 
 #define files_fdtable(files) (rcu_dereference((files)->fdt))
diff -urN oldtree/include/linux/fs.h newtree/include/linux/fs.h
--- oldtree/include/linux/fs.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/fs.h	2006-02-13 19:20:01.220326784 -0500
@@ -1268,6 +1268,9 @@
 
 extern int vfs_statfs(struct super_block *, struct kstatfs *);
 
+/* /sys/fs */
+extern struct subsystem fs_subsys;
+
 #define FLOCK_VERIFY_READ  1
 #define FLOCK_VERIFY_WRITE 2
 
diff -urN oldtree/include/linux/fs.h.orig newtree/include/linux/fs.h.orig
--- oldtree/include/linux/fs.h.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/linux/fs.h.orig	2006-02-13 19:20:01.224326176 -0500
@@ -0,0 +1,1800 @@
+#ifndef _LINUX_FS_H
+#define _LINUX_FS_H
+
+/*
+ * This file has definitions for some important file table
+ * structures etc.
+ */
+
+#include <linux/config.h>
+#include <linux/limits.h>
+#include <linux/ioctl.h>
+#include <linux/rcuref.h>
+
+/*
+ * It's silly to have NR_OPEN bigger than NR_FILE, but you can change
+ * the file limit at runtime and only root can increase the per-process
+ * nr_file rlimit, so it's safe to set up a ridiculously high absolute
+ * upper limit on files-per-process.
+ *
+ * Some programs (notably those using select()) may have to be 
+ * recompiled to take full advantage of the new limits..  
+ */
+
+/* Fixed constants first: */
+#undef NR_OPEN
+#define NR_OPEN (1024*1024)	/* Absolute upper limit on fd num */
+#define INR_OPEN 1024		/* Initial setting for nfile rlimits */
+
+#define BLOCK_SIZE_BITS 10
+#define BLOCK_SIZE (1<<BLOCK_SIZE_BITS)
+
+/* And dynamically-tunable limits and defaults: */
+struct files_stat_struct {
+	int nr_files;		/* read only */
+	int nr_free_files;	/* read only */
+	int max_files;		/* tunable */
+};
+extern struct files_stat_struct files_stat;
+
+struct inodes_stat_t {
+	int nr_inodes;
+	int nr_unused;
+	int dummy[5];
+};
+extern struct inodes_stat_t inodes_stat;
+
+extern int leases_enable, lease_break_time;
+
+#ifdef CONFIG_DNOTIFY
+extern int dir_notify_enable;
+#endif
+
+#define NR_FILE  8192	/* this can well be larger on a larger system */
+
+#define MAY_EXEC 1
+#define MAY_WRITE 2
+#define MAY_READ 4
+#define MAY_APPEND 8
+
+#define FMODE_READ 1
+#define FMODE_WRITE 2
+
+/* Internal kernel extensions */
+#define FMODE_LSEEK	4
+#define FMODE_PREAD	8
+#define FMODE_PWRITE	FMODE_PREAD	/* These go hand in hand */
+
+#define RW_MASK		1
+#define RWA_MASK	2
+#define READ 0
+#define WRITE 1
+#define READA 2		/* read-ahead  - don't block if no resources */
+#define SWRITE 3	/* for ll_rw_block() - wait for buffer lock */
+#define SPECIAL 4	/* For non-blockdevice requests in request queue */
+#define READ_SYNC	(READ | (1 << BIO_RW_SYNC))
+#define WRITE_SYNC	(WRITE | (1 << BIO_RW_SYNC))
+#define WRITE_BARRIER	((1 << BIO_RW) | (1 << BIO_RW_BARRIER))
+
+#define SEL_IN		1
+#define SEL_OUT		2
+#define SEL_EX		4
+
+/* public flags for file_system_type */
+#define FS_REQUIRES_DEV 1 
+#define FS_BINARY_MOUNTDATA 2
+#define FS_REVAL_DOT	16384	/* Check the paths ".", ".." for staleness */
+#define FS_ODD_RENAME	32768	/* Temporary stuff; will go away as soon
+				  * as nfs_rename() will be cleaned up
+				  */
+/*
+ * These are the fs-independent mount-flags: up to 32 flags are supported
+ */
+#define MS_RDONLY	 1	/* Mount read-only */
+#define MS_NOSUID	 2	/* Ignore suid and sgid bits */
+#define MS_NODEV	 4	/* Disallow access to device special files */
+#define MS_NOEXEC	 8	/* Disallow program execution */
+#define MS_SYNCHRONOUS	16	/* Writes are synced at once */
+#define MS_REMOUNT	32	/* Alter flags of a mounted FS */
+#define MS_MANDLOCK	64	/* Allow mandatory locks on an FS */
+#define MS_DIRSYNC	128	/* Directory modifications are synchronous */
+#define MS_NOATIME	1024	/* Do not update access times. */
+#define MS_NODIRATIME	2048	/* Do not update directory access times */
+#define MS_BIND		4096
+#define MS_MOVE		8192
+#define MS_REC		16384
+#define MS_VERBOSE	32768
+#define MS_UNBINDABLE	(1<<17)	/* change to unbindable */
+#define MS_PRIVATE	(1<<18)	/* change to private */
+#define MS_SLAVE	(1<<19)	/* change to slave */
+#define MS_SHARED	(1<<20)	/* change to shared */
+#define MS_POSIXACL	(1<<16)	/* VFS does not apply the umask */
+#define MS_ACTIVE	(1<<30)
+#define MS_NOUSER	(1<<31)
+
+/*
+ * Superblock flags that can be altered by MS_REMOUNT
+ */
+#define MS_RMT_MASK	(MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_NOATIME|\
+			 MS_NODIRATIME)
+
+/*
+ * Old magic mount flag and mask
+ */
+#define MS_MGC_VAL 0xC0ED0000
+#define MS_MGC_MSK 0xffff0000
+
+/* Inode flags - they have nothing to superblock flags now */
+
+#define S_SYNC		1	/* Writes are synced at once */
+#define S_NOATIME	2	/* Do not update access times */
+#define S_APPEND	4	/* Append-only file */
+#define S_IMMUTABLE	8	/* Immutable file */
+#define S_DEAD		16	/* removed, but still open directory */
+#define S_NOQUOTA	32	/* Inode is not counted to quota */
+#define S_DIRSYNC	64	/* Directory modifications are synchronous */
+#define S_NOCMTIME	128	/* Do not update file c/mtime */
+#define S_SWAPFILE	256	/* Do not truncate: swapon got its bmaps */
+#define S_PRIVATE	512	/* Inode is fs-internal */
+
+/*
+ * Note that nosuid etc flags are inode-specific: setting some file-system
+ * flags just means all the inodes inherit those flags by default. It might be
+ * possible to override it selectively if you really wanted to with some
+ * ioctl() that is not currently implemented.
+ *
+ * Exception: MS_RDONLY is always applied to the entire file system.
+ *
+ * Unfortunately, it is possible to change a filesystems flags with it mounted
+ * with files in use.  This means that all of the inodes will not have their
+ * i_flags updated.  Hence, i_flags no longer inherit the superblock mount
+ * flags, so these have to be checked separately. -- rmk@arm.uk.linux.org
+ */
+#define __IS_FLG(inode,flg) ((inode)->i_sb->s_flags & (flg))
+
+#define IS_RDONLY(inode) ((inode)->i_sb->s_flags & MS_RDONLY)
+#define IS_SYNC(inode)		(__IS_FLG(inode, MS_SYNCHRONOUS) || \
+					((inode)->i_flags & S_SYNC))
+#define IS_DIRSYNC(inode)	(__IS_FLG(inode, MS_SYNCHRONOUS|MS_DIRSYNC) || \
+					((inode)->i_flags & (S_SYNC|S_DIRSYNC)))
+#define IS_MANDLOCK(inode)	__IS_FLG(inode, MS_MANDLOCK)
+
+#define IS_NOQUOTA(inode)	((inode)->i_flags & S_NOQUOTA)
+#define IS_APPEND(inode)	((inode)->i_flags & S_APPEND)
+#define IS_IMMUTABLE(inode)	((inode)->i_flags & S_IMMUTABLE)
+#define IS_NOATIME(inode)	(__IS_FLG(inode, MS_NOATIME) || ((inode)->i_flags & S_NOATIME))
+#define IS_NODIRATIME(inode)	__IS_FLG(inode, MS_NODIRATIME)
+#define IS_POSIXACL(inode)	__IS_FLG(inode, MS_POSIXACL)
+
+#define IS_DEADDIR(inode)	((inode)->i_flags & S_DEAD)
+#define IS_NOCMTIME(inode)	((inode)->i_flags & S_NOCMTIME)
+#define IS_SWAPFILE(inode)	((inode)->i_flags & S_SWAPFILE)
+#define IS_PRIVATE(inode)	((inode)->i_flags & S_PRIVATE)
+
+/* the read-only stuff doesn't really belong here, but any other place is
+   probably as bad and I don't want to create yet another include file. */
+
+#define BLKROSET   _IO(0x12,93)	/* set device read-only (0 = read-write) */
+#define BLKROGET   _IO(0x12,94)	/* get read-only status (0 = read_write) */
+#define BLKRRPART  _IO(0x12,95)	/* re-read partition table */
+#define BLKGETSIZE _IO(0x12,96)	/* return device size /512 (long *arg) */
+#define BLKFLSBUF  _IO(0x12,97)	/* flush buffer cache */
+#define BLKRASET   _IO(0x12,98)	/* set read ahead for block device */
+#define BLKRAGET   _IO(0x12,99)	/* get current read ahead setting */
+#define BLKFRASET  _IO(0x12,100)/* set filesystem (mm/filemap.c) read-ahead */
+#define BLKFRAGET  _IO(0x12,101)/* get filesystem (mm/filemap.c) read-ahead */
+#define BLKSECTSET _IO(0x12,102)/* set max sectors per request (ll_rw_blk.c) */
+#define BLKSECTGET _IO(0x12,103)/* get max sectors per request (ll_rw_blk.c) */
+#define BLKSSZGET  _IO(0x12,104)/* get block device sector size */
+#if 0
+#define BLKPG      _IO(0x12,105)/* See blkpg.h */
+
+/* Some people are morons.  Do not use sizeof! */
+
+#define BLKELVGET  _IOR(0x12,106,size_t)/* elevator get */
+#define BLKELVSET  _IOW(0x12,107,size_t)/* elevator set */
+/* This was here just to show that the number is taken -
+   probably all these _IO(0x12,*) ioctls should be moved to blkpg.h. */
+#endif
+/* A jump here: 108-111 have been used for various private purposes. */
+#define BLKBSZGET  _IOR(0x12,112,size_t)
+#define BLKBSZSET  _IOW(0x12,113,size_t)
+#define BLKGETSIZE64 _IOR(0x12,114,size_t)	/* return device size in bytes (u64 *arg) */
+
+#define BMAP_IOCTL 1		/* obsolete - kept for compatibility */
+#define FIBMAP	   _IO(0x00,1)	/* bmap access */
+#define FIGETBSZ   _IO(0x00,2)	/* get the block size used for bmap */
+
+#ifdef __KERNEL__
+
+#include <linux/linkage.h>
+#include <linux/wait.h>
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+#include <linux/dcache.h>
+#include <linux/stat.h>
+#include <linux/cache.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
+#include <linux/radix-tree.h>
+#include <linux/prio_tree.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+
+#include <asm/atomic.h>
+#include <asm/semaphore.h>
+#include <asm/byteorder.h>
+
+struct iovec;
+struct nameidata;
+struct kiocb;
+struct pipe_inode_info;
+struct poll_table_struct;
+struct kstatfs;
+struct vm_area_struct;
+struct vfsmount;
+
+/* Used to be a macro which just called the function, now just a function */
+extern void update_atime (struct inode *);
+
+extern void __init inode_init(unsigned long);
+extern void __init inode_init_early(void);
+extern void __init mnt_init(unsigned long);
+extern void __init files_init(unsigned long);
+
+struct buffer_head;
+typedef int (get_block_t)(struct inode *inode, sector_t iblock,
+			struct buffer_head *bh_result, int create);
+typedef int (get_blocks_t)(struct inode *inode, sector_t iblock,
+			unsigned long max_blocks,
+			struct buffer_head *bh_result, int create);
+typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
+			ssize_t bytes, void *private);
+
+/*
+ * Attribute flags.  These should be or-ed together to figure out what
+ * has been changed!
+ */
+#define ATTR_MODE	1
+#define ATTR_UID	2
+#define ATTR_GID	4
+#define ATTR_SIZE	8
+#define ATTR_ATIME	16
+#define ATTR_MTIME	32
+#define ATTR_CTIME	64
+#define ATTR_ATIME_SET	128
+#define ATTR_MTIME_SET	256
+#define ATTR_FORCE	512	/* Not a change, but a change it */
+#define ATTR_ATTR_FLAG	1024
+#define ATTR_KILL_SUID	2048
+#define ATTR_KILL_SGID	4096
+#define ATTR_FILE	8192
+
+/*
+ * This is the Inode Attributes structure, used for notify_change().  It
+ * uses the above definitions as flags, to know which values have changed.
+ * Also, in this manner, a Filesystem can look at only the values it cares
+ * about.  Basically, these are the attributes that the VFS layer can
+ * request to change from the FS layer.
+ *
+ * Derek Atkins <warlord@MIT.EDU> 94-10-20
+ */
+struct iattr {
+	unsigned int	ia_valid;
+	umode_t		ia_mode;
+	uid_t		ia_uid;
+	gid_t		ia_gid;
+	loff_t		ia_size;
+	struct timespec	ia_atime;
+	struct timespec	ia_mtime;
+	struct timespec	ia_ctime;
+
+	/*
+	 * Not an attribute, but an auxilary info for filesystems wanting to
+	 * implement an ftruncate() like method.  NOTE: filesystem should
+	 * check for (ia_valid & ATTR_FILE), and not for (ia_file != NULL).
+	 */
+	struct file	*ia_file;
+};
+
+/*
+ * Includes for diskquotas.
+ */
+#include <linux/quota.h>
+
+/*
+ * oh the beauties of C type declarations.
+ */
+struct page;
+struct address_space;
+struct writeback_control;
+
+struct address_space_operations {
+	int (*writepage)(struct page *page, struct writeback_control *wbc);
+	int (*readpage)(struct file *, struct page *);
+	int (*sync_page)(struct page *);
+
+	/* Write back some dirty pages from this mapping. */
+	int (*writepages)(struct address_space *, struct writeback_control *);
+
+	/* Set a page dirty */
+	int (*set_page_dirty)(struct page *page);
+
+	int (*readpages)(struct file *filp, struct address_space *mapping,
+			struct list_head *pages, unsigned nr_pages);
+
+	/*
+	 * ext3 requires that a successful prepare_write() call be followed
+	 * by a commit_write() call - they must be balanced
+	 */
+	int (*prepare_write)(struct file *, struct page *, unsigned, unsigned);
+	int (*commit_write)(struct file *, struct page *, unsigned, unsigned);
+	/* Unfortunately this kludge is needed for FIBMAP. Don't use it */
+	sector_t (*bmap)(struct address_space *, sector_t);
+	int (*invalidatepage) (struct page *, unsigned long);
+	int (*releasepage) (struct page *, gfp_t);
+	ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
+			loff_t offset, unsigned long nr_segs);
+	struct page* (*get_xip_page)(struct address_space *, sector_t,
+			int);
+};
+
+struct backing_dev_info;
+struct address_space {
+	struct inode		*host;		/* owner: inode, block_device */
+	struct radix_tree_root	page_tree;	/* radix tree of all pages */
+	rwlock_t		tree_lock;	/* and rwlock protecting it */
+	unsigned int		i_mmap_writable;/* count VM_SHARED mappings */
+	struct prio_tree_root	i_mmap;		/* tree of private and shared mappings */
+	struct list_head	i_mmap_nonlinear;/*list VM_NONLINEAR mappings */
+	spinlock_t		i_mmap_lock;	/* protect tree, count, list */
+	unsigned int		truncate_count;	/* Cover race condition with truncate */
+	unsigned long		nrpages;	/* number of total pages */
+	pgoff_t			writeback_index;/* writeback starts here */
+	struct address_space_operations *a_ops;	/* methods */
+	unsigned long		flags;		/* error bits/gfp mask */
+	struct backing_dev_info *backing_dev_info; /* device readahead, etc */
+	spinlock_t		private_lock;	/* for use by the address_space */
+	struct list_head	private_list;	/* ditto */
+	struct address_space	*assoc_mapping;	/* ditto */
+} __attribute__((aligned(sizeof(long))));
+	/*
+	 * On most architectures that alignment is already the case; but
+	 * must be enforced here for CRIS, to let the least signficant bit
+	 * of struct page's "mapping" pointer be used for PAGE_MAPPING_ANON.
+	 */
+
+struct block_device {
+	dev_t			bd_dev;  /* not a kdev_t - it's a search key */
+	struct inode *		bd_inode;	/* will die */
+	int			bd_openers;
+	struct semaphore	bd_sem;	/* open/close mutex */
+	struct semaphore	bd_mount_sem;	/* mount mutex */
+	struct list_head	bd_inodes;
+	void *			bd_holder;
+	int			bd_holders;
+	struct block_device *	bd_contains;
+	unsigned		bd_block_size;
+	struct hd_struct *	bd_part;
+	/* number of times partitions within this device have been opened. */
+	unsigned		bd_part_count;
+	int			bd_invalidated;
+	struct gendisk *	bd_disk;
+	struct list_head	bd_list;
+	struct backing_dev_info *bd_inode_backing_dev_info;
+	/*
+	 * Private data.  You must have bd_claim'ed the block_device
+	 * to use this.  NOTE:  bd_claim allows an owner to claim
+	 * the same device multiple times, the owner must take special
+	 * care to not mess up bd_private for that case.
+	 */
+	unsigned long		bd_private;
+};
+
+/*
+ * Radix-tree tags, for tagging dirty and writeback pages within the pagecache
+ * radix trees
+ */
+#define PAGECACHE_TAG_DIRTY	0
+#define PAGECACHE_TAG_WRITEBACK	1
+
+int mapping_tagged(struct address_space *mapping, int tag);
+
+/*
+ * Might pages of this file be mapped into userspace?
+ */
+static inline int mapping_mapped(struct address_space *mapping)
+{
+	return	!prio_tree_empty(&mapping->i_mmap) ||
+		!list_empty(&mapping->i_mmap_nonlinear);
+}
+
+/*
+ * Might pages of this file have been modified in userspace?
+ * Note that i_mmap_writable counts all VM_SHARED vmas: do_mmap_pgoff
+ * marks vma as VM_SHARED if it is shared, and the file was opened for
+ * writing i.e. vma may be mprotected writable even if now readonly.
+ */
+static inline int mapping_writably_mapped(struct address_space *mapping)
+{
+	return mapping->i_mmap_writable != 0;
+}
+
+/*
+ * Use sequence counter to get consistent i_size on 32-bit processors.
+ */
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
+#include <linux/seqlock.h>
+#define __NEED_I_SIZE_ORDERED
+#define i_size_ordered_init(inode) seqcount_init(&inode->i_size_seqcount)
+#else
+#define i_size_ordered_init(inode) do { } while (0)
+#endif
+
+struct inode {
+	struct hlist_node	i_hash;
+	struct list_head	i_list;
+	struct list_head	i_sb_list;
+	struct list_head	i_dentry;
+	unsigned long		i_ino;
+	atomic_t		i_count;
+	umode_t			i_mode;
+	unsigned int		i_nlink;
+	uid_t			i_uid;
+	gid_t			i_gid;
+	dev_t			i_rdev;
+	loff_t			i_size;
+	struct timespec		i_atime;
+	struct timespec		i_mtime;
+	struct timespec		i_ctime;
+	unsigned int		i_blkbits;
+	unsigned long		i_blksize;
+	unsigned long		i_version;
+	unsigned long		i_blocks;
+	unsigned short          i_bytes;
+	spinlock_t		i_lock;	/* i_blocks, i_bytes, maybe i_size */
+	struct semaphore	i_sem;
+	struct rw_semaphore	i_alloc_sem;
+	struct inode_operations	*i_op;
+	struct file_operations	*i_fop;	/* former ->i_op->default_file_ops */
+	struct super_block	*i_sb;
+	struct file_lock	*i_flock;
+	struct address_space	*i_mapping;
+	struct address_space	i_data;
+#ifdef CONFIG_QUOTA
+	struct dquot		*i_dquot[MAXQUOTAS];
+#endif
+	/* These three should probably be a union */
+	struct list_head	i_devices;
+	struct pipe_inode_info	*i_pipe;
+	struct block_device	*i_bdev;
+	struct cdev		*i_cdev;
+	int			i_cindex;
+
+	__u32			i_generation;
+
+#ifdef CONFIG_DNOTIFY
+	unsigned long		i_dnotify_mask; /* Directory notify events */
+	struct dnotify_struct	*i_dnotify; /* for directory notifications */
+#endif
+
+#ifdef CONFIG_INOTIFY
+	struct list_head	inotify_watches; /* watches on this inode */
+	struct semaphore	inotify_sem;	/* protects the watches list */
+#endif
+
+	unsigned long		i_state;
+	unsigned long		dirtied_when;	/* jiffies of first dirtying */
+
+	unsigned int		i_flags;
+
+	atomic_t		i_writecount;
+	void			*i_security;
+	union {
+		void		*generic_ip;
+	} u;
+#ifdef __NEED_I_SIZE_ORDERED
+	seqcount_t		i_size_seqcount;
+#endif
+};
+
+/*
+ * NOTE: in a 32bit arch with a preemptable kernel and
+ * an UP compile the i_size_read/write must be atomic
+ * with respect to the local cpu (unlike with preempt disabled),
+ * but they don't need to be atomic with respect to other cpus like in
+ * true SMP (so they need either to either locally disable irq around
+ * the read or for example on x86 they can be still implemented as a
+ * cmpxchg8b without the need of the lock prefix). For SMP compiles
+ * and 64bit archs it makes no difference if preempt is enabled or not.
+ */
+static inline loff_t i_size_read(struct inode *inode)
+{
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
+	loff_t i_size;
+	unsigned int seq;
+
+	do {
+		seq = read_seqcount_begin(&inode->i_size_seqcount);
+		i_size = inode->i_size;
+	} while (read_seqcount_retry(&inode->i_size_seqcount, seq));
+	return i_size;
+#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPT)
+	loff_t i_size;
+
+	preempt_disable();
+	i_size = inode->i_size;
+	preempt_enable();
+	return i_size;
+#else
+	return inode->i_size;
+#endif
+}
+
+
+static inline void i_size_write(struct inode *inode, loff_t i_size)
+{
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
+	write_seqcount_begin(&inode->i_size_seqcount);
+	inode->i_size = i_size;
+	write_seqcount_end(&inode->i_size_seqcount);
+#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPT)
+	preempt_disable();
+	inode->i_size = i_size;
+	preempt_enable();
+#else
+	inode->i_size = i_size;
+#endif
+}
+
+static inline unsigned iminor(struct inode *inode)
+{
+	return MINOR(inode->i_rdev);
+}
+
+static inline unsigned imajor(struct inode *inode)
+{
+	return MAJOR(inode->i_rdev);
+}
+
+extern struct block_device *I_BDEV(struct inode *inode);
+
+struct fown_struct {
+	rwlock_t lock;          /* protects pid, uid, euid fields */
+	int pid;		/* pid or -pgrp where SIGIO should be sent */
+	uid_t uid, euid;	/* uid/euid of process setting the owner */
+	void *security;
+	int signum;		/* posix.1b rt signal to be delivered on IO */
+};
+
+/*
+ * Track a single file's readahead state
+ */
+struct file_ra_state {
+	unsigned long start;		/* Current window */
+	unsigned long size;
+	unsigned long flags;		/* ra flags RA_FLAG_xxx*/
+	unsigned long cache_hit;	/* cache hit count*/
+	unsigned long prev_page;	/* Cache last read() position */
+	unsigned long ahead_start;	/* Ahead window */
+	unsigned long ahead_size;
+	unsigned long ra_pages;		/* Maximum readahead window */
+	unsigned long mmap_hit;		/* Cache hit stat for mmap accesses */
+	unsigned long mmap_miss;	/* Cache miss stat for mmap accesses */
+};
+#define RA_FLAG_MISS 0x01	/* a cache miss occured against this file */
+#define RA_FLAG_INCACHE 0x02	/* file is already in cache */
+
+struct file {
+	/*
+	 * fu_list becomes invalid after file_free is called and queued via
+	 * fu_rcuhead for RCU freeing
+	 */
+	union {
+		struct list_head	fu_list;
+		struct rcu_head 	fu_rcuhead;
+	} f_u;
+	struct dentry		*f_dentry;
+	struct vfsmount         *f_vfsmnt;
+	struct file_operations	*f_op;
+	atomic_t		f_count;
+	unsigned int 		f_flags;
+	mode_t			f_mode;
+	loff_t			f_pos;
+	struct fown_struct	f_owner;
+	unsigned int		f_uid, f_gid;
+	struct file_ra_state	f_ra;
+
+	unsigned long		f_version;
+	void			*f_security;
+
+	/* needed for tty driver, and maybe others */
+	void			*private_data;
+
+#ifdef CONFIG_EPOLL
+	/* Used by fs/eventpoll.c to link all the hooks to this file */
+	struct list_head	f_ep_links;
+	spinlock_t		f_ep_lock;
+#endif /* #ifdef CONFIG_EPOLL */
+	struct address_space	*f_mapping;
+};
+extern spinlock_t files_lock;
+#define file_list_lock() spin_lock(&files_lock);
+#define file_list_unlock() spin_unlock(&files_lock);
+
+#define get_file(x)	rcuref_inc(&(x)->f_count)
+#define file_count(x)	atomic_read(&(x)->f_count)
+
+#define	MAX_NON_LFS	((1UL<<31) - 1)
+
+/* Page cache limit. The filesystems should put that into their s_maxbytes 
+   limits, otherwise bad things can happen in VM. */ 
+#if BITS_PER_LONG==32
+#define MAX_LFS_FILESIZE	(((u64)PAGE_CACHE_SIZE << (BITS_PER_LONG-1))-1) 
+#elif BITS_PER_LONG==64
+#define MAX_LFS_FILESIZE 	0x7fffffffffffffffUL
+#endif
+
+#define FL_POSIX	1
+#define FL_FLOCK	2
+#define FL_ACCESS	8	/* not trying to lock, just looking */
+#define FL_LOCKD	16	/* lock held by rpc.lockd */
+#define FL_LEASE	32	/* lease held on this file */
+#define FL_SLEEP	128	/* A blocking lock */
+
+/*
+ * The POSIX file lock owner is determined by
+ * the "struct files_struct" in the thread group
+ * (or NULL for no owner - BSD locks).
+ *
+ * Lockd stuffs a "host" pointer into this.
+ */
+typedef struct files_struct *fl_owner_t;
+
+struct file_lock_operations {
+	void (*fl_insert)(struct file_lock *);	/* lock insertion callback */
+	void (*fl_remove)(struct file_lock *);	/* lock removal callback */
+	void (*fl_copy_lock)(struct file_lock *, struct file_lock *);
+	void (*fl_release_private)(struct file_lock *);
+};
+
+struct lock_manager_operations {
+	int (*fl_compare_owner)(struct file_lock *, struct file_lock *);
+	void (*fl_notify)(struct file_lock *);	/* unblock callback */
+	void (*fl_copy_lock)(struct file_lock *, struct file_lock *);
+	void (*fl_release_private)(struct file_lock *);
+	void (*fl_break)(struct file_lock *);
+	int (*fl_mylease)(struct file_lock *, struct file_lock *);
+	int (*fl_change)(struct file_lock **, int);
+};
+
+/* that will die - we need it for nfs_lock_info */
+#include <linux/nfs_fs_i.h>
+
+struct file_lock {
+	struct file_lock *fl_next;	/* singly linked list for this inode  */
+	struct list_head fl_link;	/* doubly linked list of all locks */
+	struct list_head fl_block;	/* circular list of blocked processes */
+	fl_owner_t fl_owner;
+	unsigned int fl_pid;
+	wait_queue_head_t fl_wait;
+	struct file *fl_file;
+	unsigned char fl_flags;
+	unsigned char fl_type;
+	loff_t fl_start;
+	loff_t fl_end;
+
+	struct fasync_struct *	fl_fasync; /* for lease break notifications */
+	unsigned long fl_break_time;	/* for nonblocking lease breaks */
+
+	struct file_lock_operations *fl_ops;	/* Callbacks for filesystems */
+	struct lock_manager_operations *fl_lmops;	/* Callbacks for lockmanagers */
+	union {
+		struct nfs_lock_info	nfs_fl;
+		struct nfs4_lock_info	nfs4_fl;
+	} fl_u;
+};
+
+/* The following constant reflects the upper bound of the file/locking space */
+#ifndef OFFSET_MAX
+#define INT_LIMIT(x)	(~((x)1 << (sizeof(x)*8 - 1)))
+#define OFFSET_MAX	INT_LIMIT(loff_t)
+#define OFFT_OFFSET_MAX	INT_LIMIT(off_t)
+#endif
+
+extern struct list_head file_lock_list;
+
+#include <linux/fcntl.h>
+
+extern int fcntl_getlk(struct file *, struct flock __user *);
+extern int fcntl_setlk(unsigned int, struct file *, unsigned int,
+			struct flock __user *);
+
+#if BITS_PER_LONG == 32
+extern int fcntl_getlk64(struct file *, struct flock64 __user *);
+extern int fcntl_setlk64(unsigned int, struct file *, unsigned int,
+			struct flock64 __user *);
+#endif
+
+extern void send_sigio(struct fown_struct *fown, int fd, int band);
+extern int fcntl_setlease(unsigned int fd, struct file *filp, long arg);
+extern int fcntl_getlease(struct file *filp);
+
+/* fs/locks.c */
+extern void locks_init_lock(struct file_lock *);
+extern void locks_copy_lock(struct file_lock *, struct file_lock *);
+extern void locks_remove_posix(struct file *, fl_owner_t);
+extern void locks_remove_flock(struct file *);
+extern struct file_lock *posix_test_lock(struct file *, struct file_lock *);
+extern int posix_lock_file(struct file *, struct file_lock *);
+extern int posix_lock_file_wait(struct file *, struct file_lock *);
+extern void posix_block_lock(struct file_lock *, struct file_lock *);
+extern void posix_unblock_lock(struct file *, struct file_lock *);
+extern int posix_locks_deadlock(struct file_lock *, struct file_lock *);
+extern int flock_lock_file_wait(struct file *filp, struct file_lock *fl);
+extern int __break_lease(struct inode *inode, unsigned int flags);
+extern void lease_get_mtime(struct inode *, struct timespec *time);
+extern int setlease(struct file *, long, struct file_lock **);
+extern int lease_modify(struct file_lock **, int);
+extern int lock_may_read(struct inode *, loff_t start, unsigned long count);
+extern int lock_may_write(struct inode *, loff_t start, unsigned long count);
+extern void steal_locks(fl_owner_t from);
+
+struct fasync_struct {
+	int	magic;
+	int	fa_fd;
+	struct	fasync_struct	*fa_next; /* singly linked list */
+	struct	file 		*fa_file;
+};
+
+#define FASYNC_MAGIC 0x4601
+
+/* SMP safe fasync helpers: */
+extern int fasync_helper(int, struct file *, int, struct fasync_struct **);
+/* can be called from interrupts */
+extern void kill_fasync(struct fasync_struct **, int, int);
+/* only for net: no internal synchronization */
+extern void __kill_fasync(struct fasync_struct *, int, int);
+
+extern int f_setown(struct file *filp, unsigned long arg, int force);
+extern void f_delown(struct file *filp);
+extern int send_sigurg(struct fown_struct *fown);
+
+/*
+ *	Umount options
+ */
+
+#define MNT_FORCE	0x00000001	/* Attempt to forcibily umount */
+#define MNT_DETACH	0x00000002	/* Just detach from the tree */
+#define MNT_EXPIRE	0x00000004	/* Mark for expiry */
+
+extern struct list_head super_blocks;
+extern spinlock_t sb_lock;
+
+#define sb_entry(list)	list_entry((list), struct super_block, s_list)
+#define S_BIAS (1<<30)
+struct super_block {
+	struct list_head	s_list;		/* Keep this first */
+	dev_t			s_dev;		/* search index; _not_ kdev_t */
+	unsigned long		s_blocksize;
+	unsigned long		s_old_blocksize;
+	unsigned char		s_blocksize_bits;
+	unsigned char		s_dirt;
+	unsigned long long	s_maxbytes;	/* Max file size */
+	struct file_system_type	*s_type;
+	struct super_operations	*s_op;
+	struct dquot_operations	*dq_op;
+ 	struct quotactl_ops	*s_qcop;
+	struct export_operations *s_export_op;
+	unsigned long		s_flags;
+	unsigned long		s_magic;
+	struct dentry		*s_root;
+	struct rw_semaphore	s_umount;
+	struct semaphore	s_lock;
+	int			s_count;
+	int			s_syncing;
+	int			s_need_sync_fs;
+	atomic_t		s_active;
+	void                    *s_security;
+	struct xattr_handler	**s_xattr;
+
+	struct list_head	s_inodes;	/* all inodes */
+	struct list_head	s_dirty;	/* dirty inodes */
+	struct list_head	s_io;		/* parked for writeback */
+	struct hlist_head	s_anon;		/* anonymous dentries for (nfs) exporting */
+	struct list_head	s_files;
+
+	struct block_device	*s_bdev;
+	struct list_head	s_instances;
+	struct quota_info	s_dquot;	/* Diskquota specific options */
+
+	int			s_frozen;
+	wait_queue_head_t	s_wait_unfrozen;
+
+	char s_id[32];				/* Informational name */
+
+	void 			*s_fs_info;	/* Filesystem private info */
+
+	/*
+	 * The next field is for VFS *only*. No filesystems have any business
+	 * even looking at it. You had been warned.
+	 */
+	struct semaphore s_vfs_rename_sem;	/* Kludge */
+
+	/* Granuality of c/m/atime in ns.
+	   Cannot be worse than a second */
+	u32		   s_time_gran;
+};
+
+extern struct timespec current_fs_time(struct super_block *sb);
+
+/*
+ * Snapshotting support.
+ */
+enum {
+	SB_UNFROZEN = 0,
+	SB_FREEZE_WRITE	= 1,
+	SB_FREEZE_TRANS = 2,
+};
+
+#define vfs_check_frozen(sb, level) \
+	wait_event((sb)->s_wait_unfrozen, ((sb)->s_frozen < (level)))
+
+static inline void get_fs_excl(void)
+{
+	atomic_inc(&current->fs_excl);
+}
+
+static inline void put_fs_excl(void)
+{
+	atomic_dec(&current->fs_excl);
+}
+
+static inline int has_fs_excl(void)
+{
+	return atomic_read(&current->fs_excl);
+}
+
+
+/*
+ * Superblock locking.
+ */
+static inline void lock_super(struct super_block * sb)
+{
+	get_fs_excl();
+	down(&sb->s_lock);
+}
+
+static inline void unlock_super(struct super_block * sb)
+{
+	put_fs_excl();
+	up(&sb->s_lock);
+}
+
+/*
+ * VFS helper functions..
+ */
+extern int vfs_permission(struct nameidata *, int);
+extern int vfs_create(struct inode *, struct dentry *, int, struct nameidata *);
+extern int vfs_mkdir(struct inode *, struct dentry *, int);
+extern int vfs_mknod(struct inode *, struct dentry *, int, dev_t);
+extern int vfs_symlink(struct inode *, struct dentry *, const char *, int);
+extern int vfs_link(struct dentry *, struct inode *, struct dentry *);
+extern int vfs_rmdir(struct inode *, struct dentry *);
+extern int vfs_unlink(struct inode *, struct dentry *);
+extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
+
+/*
+ * VFS dentry helper functions.
+ */
+extern void dentry_unhash(struct dentry *dentry);
+
+/*
+ * VFS file helper functions.
+ */
+extern int file_permission(struct file *, int);
+
+/*
+ * File types
+ *
+ * NOTE! These match bits 12..15 of stat.st_mode
+ * (ie "(i_mode >> 12) & 15").
+ */
+#define DT_UNKNOWN	0
+#define DT_FIFO		1
+#define DT_CHR		2
+#define DT_DIR		4
+#define DT_BLK		6
+#define DT_REG		8
+#define DT_LNK		10
+#define DT_SOCK		12
+#define DT_WHT		14
+
+#define OSYNC_METADATA	(1<<0)
+#define OSYNC_DATA	(1<<1)
+#define OSYNC_INODE	(1<<2)
+int generic_osync_inode(struct inode *, struct address_space *, int);
+
+/*
+ * This is the "filldir" function type, used by readdir() to let
+ * the kernel specify what kind of dirent layout it wants to have.
+ * This allows the kernel to read directories into kernel space or
+ * to have different dirent layouts depending on the binary type.
+ */
+typedef int (*filldir_t)(void *, const char *, int, loff_t, ino_t, unsigned);
+
+struct block_device_operations {
+	int (*open) (struct inode *, struct file *);
+	int (*release) (struct inode *, struct file *);
+	int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);
+	long (*unlocked_ioctl) (struct file *, unsigned, unsigned long);
+	long (*compat_ioctl) (struct file *, unsigned, unsigned long);
+	int (*direct_access) (struct block_device *, sector_t, unsigned long *);
+	int (*media_changed) (struct gendisk *);
+	int (*revalidate_disk) (struct gendisk *);
+	struct module *owner;
+};
+
+/*
+ * "descriptor" for what we're up to with a read for sendfile().
+ * This allows us to use the same read code yet
+ * have multiple different users of the data that
+ * we read from a file.
+ *
+ * The simplest case just copies the data to user
+ * mode.
+ */
+typedef struct {
+	size_t written;
+	size_t count;
+	union {
+		char __user * buf;
+		void *data;
+	} arg;
+	int error;
+} read_descriptor_t;
+
+typedef int (*read_actor_t)(read_descriptor_t *, struct page *, unsigned long, unsigned long);
+
+/* These macros are for out of kernel modules to test that
+ * the kernel supports the unlocked_ioctl and compat_ioctl
+ * fields in struct file_operations. */
+#define HAVE_COMPAT_IOCTL 1
+#define HAVE_UNLOCKED_IOCTL 1
+
+/*
+ * NOTE:
+ * read, write, poll, fsync, readv, writev, unlocked_ioctl and compat_ioctl
+ * can be called without the big kernel lock held in all filesystems.
+ */
+struct file_operations {
+	struct module *owner;
+	loff_t (*llseek) (struct file *, loff_t, int);
+	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
+	ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
+	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
+	ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
+	int (*readdir) (struct file *, void *, filldir_t);
+	unsigned int (*poll) (struct file *, struct poll_table_struct *);
+	int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
+	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
+	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
+	int (*mmap) (struct file *, struct vm_area_struct *);
+	int (*open) (struct inode *, struct file *);
+	int (*flush) (struct file *);
+	int (*release) (struct inode *, struct file *);
+	int (*fsync) (struct file *, struct dentry *, int datasync);
+	int (*aio_fsync) (struct kiocb *, int datasync);
+	int (*fasync) (int, struct file *, int);
+	int (*lock) (struct file *, int, struct file_lock *);
+	ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
+	ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
+	ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
+	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
+	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
+	int (*check_flags)(int);
+	int (*dir_notify)(struct file *filp, unsigned long arg);
+	int (*flock) (struct file *, int, struct file_lock *);
+};
+
+struct inode_operations {
+	int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
+	struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
+	int (*link) (struct dentry *,struct inode *,struct dentry *);
+	int (*unlink) (struct inode *,struct dentry *);
+	int (*symlink) (struct inode *,struct dentry *,const char *);
+	int (*mkdir) (struct inode *,struct dentry *,int);
+	int (*rmdir) (struct inode *,struct dentry *);
+	int (*mknod) (struct inode *,struct dentry *,int,dev_t);
+	int (*rename) (struct inode *, struct dentry *,
+			struct inode *, struct dentry *);
+	int (*readlink) (struct dentry *, char __user *,int);
+	void * (*follow_link) (struct dentry *, struct nameidata *);
+	void (*put_link) (struct dentry *, struct nameidata *, void *);
+	void (*truncate) (struct inode *);
+	int (*permission) (struct inode *, int, struct nameidata *);
+	int (*setattr) (struct dentry *, struct iattr *);
+	int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
+	int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
+	ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
+	ssize_t (*listxattr) (struct dentry *, char *, size_t);
+	int (*removexattr) (struct dentry *, const char *);
+};
+
+struct seq_file;
+
+extern ssize_t vfs_read(struct file *, char __user *, size_t, loff_t *);
+extern ssize_t vfs_write(struct file *, const char __user *, size_t, loff_t *);
+extern ssize_t vfs_readv(struct file *, const struct iovec __user *,
+		unsigned long, loff_t *);
+extern ssize_t vfs_writev(struct file *, const struct iovec __user *,
+		unsigned long, loff_t *);
+
+/*
+ * NOTE: write_inode, delete_inode, clear_inode, put_inode can be called
+ * without the big kernel lock held in all filesystems.
+ */
+struct super_operations {
+   	struct inode *(*alloc_inode)(struct super_block *sb);
+	void (*destroy_inode)(struct inode *);
+
+	void (*read_inode) (struct inode *);
+  
+   	void (*dirty_inode) (struct inode *);
+	int (*write_inode) (struct inode *, int);
+	void (*put_inode) (struct inode *);
+	void (*drop_inode) (struct inode *);
+	void (*delete_inode) (struct inode *);
+	void (*put_super) (struct super_block *);
+	void (*write_super) (struct super_block *);
+	int (*sync_fs)(struct super_block *sb, int wait);
+	void (*write_super_lockfs) (struct super_block *);
+	void (*unlockfs) (struct super_block *);
+	int (*statfs) (struct super_block *, struct kstatfs *);
+	int (*remount_fs) (struct super_block *, int *, char *);
+	void (*clear_inode) (struct inode *);
+	void (*umount_begin) (struct super_block *);
+
+	int (*show_options)(struct seq_file *, struct vfsmount *);
+
+	ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
+	ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
+};
+
+/* Inode state bits.  Protected by inode_lock. */
+#define I_DIRTY_SYNC		1 /* Not dirty enough for O_DATASYNC */
+#define I_DIRTY_DATASYNC	2 /* Data-related inode changes pending */
+#define I_DIRTY_PAGES		4 /* Data-related inode changes pending */
+#define __I_LOCK		3
+#define I_LOCK			(1 << __I_LOCK)
+#define I_FREEING		16
+#define I_CLEAR			32
+#define I_NEW			64
+#define I_WILL_FREE		128
+
+#define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES)
+
+extern void __mark_inode_dirty(struct inode *, int);
+static inline void mark_inode_dirty(struct inode *inode)
+{
+	__mark_inode_dirty(inode, I_DIRTY);
+}
+
+static inline void mark_inode_dirty_sync(struct inode *inode)
+{
+	__mark_inode_dirty(inode, I_DIRTY_SYNC);
+}
+
+static inline void touch_atime(struct vfsmount *mnt, struct dentry *dentry)
+{
+	/* per-mountpoint checks will go here */
+	update_atime(dentry->d_inode);
+}
+
+static inline void file_accessed(struct file *file)
+{
+	if (!(file->f_flags & O_NOATIME))
+		touch_atime(file->f_vfsmnt, file->f_dentry);
+}
+
+int sync_inode(struct inode *inode, struct writeback_control *wbc);
+
+/**
+ * struct export_operations - for nfsd to communicate with file systems
+ * @decode_fh:      decode a file handle fragment and return a &struct dentry
+ * @encode_fh:      encode a file handle fragment from a dentry
+ * @get_name:       find the name for a given inode in a given directory
+ * @get_parent:     find the parent of a given directory
+ * @get_dentry:     find a dentry for the inode given a file handle sub-fragment
+ * @find_exported_dentry:
+ *	set by the exporting module to a standard helper function.
+ *
+ * Description:
+ *    The export_operations structure provides a means for nfsd to communicate
+ *    with a particular exported file system  - particularly enabling nfsd and
+ *    the filesystem to co-operate when dealing with file handles.
+ *
+ *    export_operations contains two basic operation for dealing with file
+ *    handles, decode_fh() and encode_fh(), and allows for some other
+ *    operations to be defined which standard helper routines use to get
+ *    specific information from the filesystem.
+ *
+ *    nfsd encodes information use to determine which filesystem a filehandle
+ *    applies to in the initial part of the file handle.  The remainder, termed
+ *    a file handle fragment, is controlled completely by the filesystem.  The
+ *    standard helper routines assume that this fragment will contain one or
+ *    two sub-fragments, one which identifies the file, and one which may be
+ *    used to identify the (a) directory containing the file.
+ *
+ *    In some situations, nfsd needs to get a dentry which is connected into a
+ *    specific part of the file tree.  To allow for this, it passes the
+ *    function acceptable() together with a @context which can be used to see
+ *    if the dentry is acceptable.  As there can be multiple dentrys for a
+ *    given file, the filesystem should check each one for acceptability before
+ *    looking for the next.  As soon as an acceptable one is found, it should
+ *    be returned.
+ *
+ * decode_fh:
+ *    @decode_fh is given a &struct super_block (@sb), a file handle fragment
+ *    (@fh, @fh_len) and an acceptability testing function (@acceptable,
+ *    @context).  It should return a &struct dentry which refers to the same
+ *    file that the file handle fragment refers to,  and which passes the
+ *    acceptability test.  If it cannot, it should return a %NULL pointer if
+ *    the file was found but no acceptable &dentries were available, or a
+ *    %ERR_PTR error code indicating why it couldn't be found (e.g. %ENOENT or
+ *    %ENOMEM).
+ *
+ * encode_fh:
+ *    @encode_fh should store in the file handle fragment @fh (using at most
+ *    @max_len bytes) information that can be used by @decode_fh to recover the
+ *    file refered to by the &struct dentry @de.  If the @connectable flag is
+ *    set, the encode_fh() should store sufficient information so that a good
+ *    attempt can be made to find not only the file but also it's place in the
+ *    filesystem.   This typically means storing a reference to de->d_parent in
+ *    the filehandle fragment.  encode_fh() should return the number of bytes
+ *    stored or a negative error code such as %-ENOSPC
+ *
+ * get_name:
+ *    @get_name should find a name for the given @child in the given @parent
+ *    directory.  The name should be stored in the @name (with the
+ *    understanding that it is already pointing to a a %NAME_MAX+1 sized
+ *    buffer.   get_name() should return %0 on success, a negative error code
+ *    or error.  @get_name will be called without @parent->i_sem held.
+ *
+ * get_parent:
+ *    @get_parent should find the parent directory for the given @child which
+ *    is also a directory.  In the event that it cannot be found, or storage
+ *    space cannot be allocated, a %ERR_PTR should be returned.
+ *
+ * get_dentry:
+ *    Given a &super_block (@sb) and a pointer to a file-system specific inode
+ *    identifier, possibly an inode number, (@inump) get_dentry() should find
+ *    the identified inode and return a dentry for that inode.  Any suitable
+ *    dentry can be returned including, if necessary, a new dentry created with
+ *    d_alloc_root.  The caller can then find any other extant dentrys by
+ *    following the d_alias links.  If a new dentry was created using
+ *    d_alloc_root, DCACHE_NFSD_DISCONNECTED should be set, and the dentry
+ *    should be d_rehash()ed.
+ *
+ *    If the inode cannot be found, either a %NULL pointer or an %ERR_PTR code
+ *    can be returned.  The @inump will be whatever was passed to
+ *    nfsd_find_fh_dentry() in either the @obj or @parent parameters.
+ *
+ * Locking rules:
+ *    get_parent is called with child->d_inode->i_sem down
+ *    get_name is not (which is possibly inconsistent)
+ */
+
+struct export_operations {
+	struct dentry *(*decode_fh)(struct super_block *sb, __u32 *fh, int fh_len, int fh_type,
+			 int (*acceptable)(void *context, struct dentry *de),
+			 void *context);
+	int (*encode_fh)(struct dentry *de, __u32 *fh, int *max_len,
+			 int connectable);
+
+	/* the following are only called from the filesystem itself */
+	int (*get_name)(struct dentry *parent, char *name,
+			struct dentry *child);
+	struct dentry * (*get_parent)(struct dentry *child);
+	struct dentry * (*get_dentry)(struct super_block *sb, void *inump);
+
+	/* This is set by the exporting module to a standard helper */
+	struct dentry * (*find_exported_dentry)(
+		struct super_block *sb, void *obj, void *parent,
+		int (*acceptable)(void *context, struct dentry *de),
+		void *context);
+
+
+};
+
+extern struct dentry *
+find_exported_dentry(struct super_block *sb, void *obj, void *parent,
+		     int (*acceptable)(void *context, struct dentry *de),
+		     void *context);
+
+struct file_system_type {
+	const char *name;
+	int fs_flags;
+	struct super_block *(*get_sb) (struct file_system_type *, int,
+				       const char *, void *);
+	void (*kill_sb) (struct super_block *);
+	struct module *owner;
+	struct file_system_type * next;
+	struct list_head fs_supers;
+};
+
+struct super_block *get_sb_bdev(struct file_system_type *fs_type,
+	int flags, const char *dev_name, void *data,
+	int (*fill_super)(struct super_block *, void *, int));
+struct super_block *get_sb_single(struct file_system_type *fs_type,
+	int flags, void *data,
+	int (*fill_super)(struct super_block *, void *, int));
+struct super_block *get_sb_nodev(struct file_system_type *fs_type,
+	int flags, void *data,
+	int (*fill_super)(struct super_block *, void *, int));
+void generic_shutdown_super(struct super_block *sb);
+void kill_block_super(struct super_block *sb);
+void kill_anon_super(struct super_block *sb);
+void kill_litter_super(struct super_block *sb);
+void deactivate_super(struct super_block *sb);
+int set_anon_super(struct super_block *s, void *data);
+struct super_block *sget(struct file_system_type *type,
+			int (*test)(struct super_block *,void *),
+			int (*set)(struct super_block *,void *),
+			void *data);
+struct super_block *get_sb_pseudo(struct file_system_type *, char *,
+			struct super_operations *ops, unsigned long);
+int __put_super(struct super_block *sb);
+int __put_super_and_need_restart(struct super_block *sb);
+void unnamed_dev_init(void);
+
+/* Alas, no aliases. Too much hassle with bringing module.h everywhere */
+#define fops_get(fops) \
+	(((fops) && try_module_get((fops)->owner) ? (fops) : NULL))
+#define fops_put(fops) \
+	do { if (fops) module_put((fops)->owner); } while(0)
+
+extern int register_filesystem(struct file_system_type *);
+extern int unregister_filesystem(struct file_system_type *);
+extern struct vfsmount *kern_mount(struct file_system_type *);
+extern int may_umount_tree(struct vfsmount *);
+extern int may_umount(struct vfsmount *);
+extern void umount_tree(struct vfsmount *, int, struct list_head *);
+extern void release_mounts(struct list_head *);
+extern long do_mount(char *, char *, char *, unsigned long, void *);
+extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int);
+extern void mnt_set_mountpoint(struct vfsmount *, struct dentry *,
+				  struct vfsmount *);
+
+extern int vfs_statfs(struct super_block *, struct kstatfs *);
+
+#define FLOCK_VERIFY_READ  1
+#define FLOCK_VERIFY_WRITE 2
+
+extern int locks_mandatory_locked(struct inode *);
+extern int locks_mandatory_area(int, struct inode *, struct file *, loff_t, size_t);
+
+/*
+ * Candidates for mandatory locking have the setgid bit set
+ * but no group execute bit -  an otherwise meaningless combination.
+ */
+#define MANDATORY_LOCK(inode) \
+	(IS_MANDLOCK(inode) && ((inode)->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+
+static inline int locks_verify_locked(struct inode *inode)
+{
+	if (MANDATORY_LOCK(inode))
+		return locks_mandatory_locked(inode);
+	return 0;
+}
+
+extern int rw_verify_area(int, struct file *, loff_t *, size_t);
+
+static inline int locks_verify_truncate(struct inode *inode,
+				    struct file *filp,
+				    loff_t size)
+{
+	if (inode->i_flock && MANDATORY_LOCK(inode))
+		return locks_mandatory_area(
+			FLOCK_VERIFY_WRITE, inode, filp,
+			size < inode->i_size ? size : inode->i_size,
+			(size < inode->i_size ? inode->i_size - size
+			 : size - inode->i_size)
+		);
+	return 0;
+}
+
+static inline int break_lease(struct inode *inode, unsigned int mode)
+{
+	if (inode->i_flock)
+		return __break_lease(inode, mode);
+	return 0;
+}
+
+/* fs/open.c */
+
+extern int do_truncate(struct dentry *, loff_t start, struct file *filp);
+extern long do_sys_open(const char __user *filename, int flags, int mode);
+extern struct file *filp_open(const char *, int, int);
+extern struct file * dentry_open(struct dentry *, struct vfsmount *, int);
+extern int filp_close(struct file *, fl_owner_t id);
+extern char * getname(const char __user *);
+
+/* fs/dcache.c */
+extern void __init vfs_caches_init_early(void);
+extern void __init vfs_caches_init(unsigned long);
+
+#define __getname()	kmem_cache_alloc(names_cachep, SLAB_KERNEL)
+#define __putname(name) kmem_cache_free(names_cachep, (void *)(name))
+#ifndef CONFIG_AUDITSYSCALL
+#define putname(name)   __putname(name)
+#else
+extern void putname(const char *name);
+#endif
+
+extern int register_blkdev(unsigned int, const char *);
+extern int unregister_blkdev(unsigned int, const char *);
+extern struct block_device *bdget(dev_t);
+extern void bd_set_size(struct block_device *, loff_t size);
+extern void bd_forget(struct inode *inode);
+extern void bdput(struct block_device *);
+extern struct block_device *open_by_devnum(dev_t, unsigned);
+extern struct file_operations def_blk_fops;
+extern struct address_space_operations def_blk_aops;
+extern struct file_operations def_chr_fops;
+extern struct file_operations bad_sock_fops;
+extern struct file_operations def_fifo_fops;
+extern int ioctl_by_bdev(struct block_device *, unsigned, unsigned long);
+extern int blkdev_ioctl(struct inode *, struct file *, unsigned, unsigned long);
+extern long compat_blkdev_ioctl(struct file *, unsigned, unsigned long);
+extern int blkdev_get(struct block_device *, mode_t, unsigned);
+extern int blkdev_put(struct block_device *);
+extern int bd_claim(struct block_device *, void *);
+extern void bd_release(struct block_device *);
+
+/* fs/char_dev.c */
+extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
+extern int register_chrdev_region(dev_t, unsigned, const char *);
+extern int register_chrdev(unsigned int, const char *,
+			   struct file_operations *);
+extern int unregister_chrdev(unsigned int, const char *);
+extern void unregister_chrdev_region(dev_t, unsigned);
+extern int chrdev_open(struct inode *, struct file *);
+
+/* fs/block_dev.c */
+#define BDEVNAME_SIZE	32	/* Largest string for a blockdev identifier */
+extern const char *__bdevname(dev_t, char *buffer);
+extern const char *bdevname(struct block_device *bdev, char *buffer);
+extern struct block_device *lookup_bdev(const char *);
+extern struct block_device *open_bdev_excl(const char *, int, void *);
+extern void close_bdev_excl(struct block_device *);
+
+extern void init_special_inode(struct inode *, umode_t, dev_t);
+
+/* Invalid inode operations -- fs/bad_inode.c */
+extern void make_bad_inode(struct inode *);
+extern int is_bad_inode(struct inode *);
+
+extern struct file_operations read_fifo_fops;
+extern struct file_operations write_fifo_fops;
+extern struct file_operations rdwr_fifo_fops;
+extern struct file_operations read_pipe_fops;
+extern struct file_operations write_pipe_fops;
+extern struct file_operations rdwr_pipe_fops;
+
+extern int fs_may_remount_ro(struct super_block *);
+
+/*
+ * return READ, READA, or WRITE
+ */
+#define bio_rw(bio)		((bio)->bi_rw & (RW_MASK | RWA_MASK))
+
+/*
+ * return data direction, READ or WRITE
+ */
+#define bio_data_dir(bio)	((bio)->bi_rw & 1)
+
+extern int check_disk_change(struct block_device *);
+extern int invalidate_inodes(struct super_block *);
+extern int __invalidate_device(struct block_device *);
+extern int invalidate_partition(struct gendisk *, int);
+unsigned long invalidate_mapping_pages(struct address_space *mapping,
+					pgoff_t start, pgoff_t end);
+unsigned long invalidate_inode_pages(struct address_space *mapping);
+static inline void invalidate_remote_inode(struct inode *inode)
+{
+	if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+	    S_ISLNK(inode->i_mode))
+		invalidate_inode_pages(inode->i_mapping);
+}
+extern int invalidate_inode_pages2(struct address_space *mapping);
+extern int invalidate_inode_pages2_range(struct address_space *mapping,
+					 pgoff_t start, pgoff_t end);
+extern int write_inode_now(struct inode *, int);
+extern int filemap_fdatawrite(struct address_space *);
+extern int filemap_flush(struct address_space *);
+extern int filemap_fdatawait(struct address_space *);
+extern int filemap_write_and_wait(struct address_space *mapping);
+extern int filemap_write_and_wait_range(struct address_space *mapping,
+				        loff_t lstart, loff_t lend);
+extern void sync_supers(void);
+extern void sync_filesystems(int wait);
+extern void emergency_sync(void);
+extern void emergency_remount(void);
+extern int do_remount_sb(struct super_block *sb, int flags,
+			 void *data, int force);
+extern sector_t bmap(struct inode *, sector_t);
+extern int notify_change(struct dentry *, struct iattr *);
+extern int permission(struct inode *, int, struct nameidata *);
+extern int generic_permission(struct inode *, int,
+		int (*check_acl)(struct inode *, int));
+
+extern int get_write_access(struct inode *);
+extern int deny_write_access(struct file *);
+static inline void put_write_access(struct inode * inode)
+{
+	atomic_dec(&inode->i_writecount);
+}
+static inline void allow_write_access(struct file *file)
+{
+	if (file)
+		atomic_inc(&file->f_dentry->d_inode->i_writecount);
+}
+extern int do_pipe(int *);
+
+extern int open_namei(const char *, int, int, struct nameidata *);
+extern int may_open(struct nameidata *, int, int);
+
+extern int kernel_read(struct file *, unsigned long, char *, unsigned long);
+extern struct file * open_exec(const char *);
+ 
+/* fs/dcache.c -- generic fs support functions */
+extern int is_subdir(struct dentry *, struct dentry *);
+extern ino_t find_inode_number(struct dentry *, struct qstr *);
+
+#include <linux/err.h>
+
+/* needed for stackable file system support */
+extern loff_t default_llseek(struct file *file, loff_t offset, int origin);
+
+extern loff_t vfs_llseek(struct file *file, loff_t offset, int origin);
+
+extern void inode_init_once(struct inode *);
+extern void iput(struct inode *);
+extern struct inode * igrab(struct inode *);
+extern ino_t iunique(struct super_block *, ino_t);
+extern int inode_needs_sync(struct inode *inode);
+extern void generic_delete_inode(struct inode *inode);
+extern void generic_drop_inode(struct inode *inode);
+
+extern struct inode *ilookup5_nowait(struct super_block *sb,
+		unsigned long hashval, int (*test)(struct inode *, void *),
+		void *data);
+extern struct inode *ilookup5(struct super_block *sb, unsigned long hashval,
+		int (*test)(struct inode *, void *), void *data);
+extern struct inode *ilookup(struct super_block *sb, unsigned long ino);
+
+extern struct inode * iget5_locked(struct super_block *, unsigned long, int (*test)(struct inode *, void *), int (*set)(struct inode *, void *), void *);
+extern struct inode * iget_locked(struct super_block *, unsigned long);
+extern void unlock_new_inode(struct inode *);
+
+static inline struct inode *iget(struct super_block *sb, unsigned long ino)
+{
+	struct inode *inode = iget_locked(sb, ino);
+	
+	if (inode && (inode->i_state & I_NEW)) {
+		sb->s_op->read_inode(inode);
+		unlock_new_inode(inode);
+	}
+
+	return inode;
+}
+
+extern void __iget(struct inode * inode);
+extern void clear_inode(struct inode *);
+extern void destroy_inode(struct inode *);
+extern struct inode *new_inode(struct super_block *);
+extern int remove_suid(struct dentry *);
+extern void remove_dquot_ref(struct super_block *, int, struct list_head *);
+extern struct semaphore iprune_sem;
+
+extern void __insert_inode_hash(struct inode *, unsigned long hashval);
+extern void remove_inode_hash(struct inode *);
+static inline void insert_inode_hash(struct inode *inode) {
+	__insert_inode_hash(inode, inode->i_ino);
+}
+
+extern struct file * get_empty_filp(void);
+extern void file_move(struct file *f, struct list_head *list);
+extern void file_kill(struct file *f);
+struct bio;
+extern void submit_bio(int, struct bio *);
+extern int bdev_read_only(struct block_device *);
+extern int set_blocksize(struct block_device *, int);
+extern int sb_set_blocksize(struct super_block *, int);
+extern int sb_min_blocksize(struct super_block *, int);
+
+extern int generic_file_mmap(struct file *, struct vm_area_struct *);
+extern int generic_file_readonly_mmap(struct file *, struct vm_area_struct *);
+extern int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size);
+extern int file_send_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size);
+extern ssize_t generic_file_read(struct file *, char __user *, size_t, loff_t *);
+int generic_write_checks(struct file *file, loff_t *pos, size_t *count, int isblk);
+extern ssize_t generic_file_write(struct file *, const char __user *, size_t, loff_t *);
+extern ssize_t generic_file_aio_read(struct kiocb *, char __user *, size_t, loff_t);
+extern ssize_t __generic_file_aio_read(struct kiocb *, const struct iovec *, unsigned long, loff_t *);
+extern ssize_t generic_file_aio_write(struct kiocb *, const char __user *, size_t, loff_t);
+extern ssize_t generic_file_aio_write_nolock(struct kiocb *, const struct iovec *,
+		unsigned long, loff_t *);
+extern ssize_t generic_file_direct_write(struct kiocb *, const struct iovec *,
+		unsigned long *, loff_t, loff_t *, size_t, size_t);
+extern ssize_t generic_file_buffered_write(struct kiocb *, const struct iovec *,
+		unsigned long, loff_t, loff_t *, size_t, ssize_t);
+extern ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos);
+extern ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos);
+ssize_t generic_file_write_nolock(struct file *file, const struct iovec *iov,
+				unsigned long nr_segs, loff_t *ppos);
+extern ssize_t generic_file_sendfile(struct file *, loff_t *, size_t, read_actor_t, void *);
+extern void do_generic_mapping_read(struct address_space *mapping,
+				    struct file_ra_state *, struct file *,
+				    loff_t *, read_descriptor_t *, read_actor_t);
+extern void
+file_ra_state_init(struct file_ra_state *ra, struct address_space *mapping);
+extern ssize_t generic_file_readv(struct file *filp, const struct iovec *iov, 
+	unsigned long nr_segs, loff_t *ppos);
+ssize_t generic_file_writev(struct file *filp, const struct iovec *iov, 
+			unsigned long nr_segs, loff_t *ppos);
+extern loff_t no_llseek(struct file *file, loff_t offset, int origin);
+extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin);
+extern loff_t remote_llseek(struct file *file, loff_t offset, int origin);
+extern int generic_file_open(struct inode * inode, struct file * filp);
+extern int nonseekable_open(struct inode * inode, struct file * filp);
+
+#ifdef CONFIG_FS_XIP
+extern ssize_t xip_file_read(struct file *filp, char __user *buf, size_t len,
+			     loff_t *ppos);
+extern ssize_t xip_file_sendfile(struct file *in_file, loff_t *ppos,
+				 size_t count, read_actor_t actor,
+				 void *target);
+extern int xip_file_mmap(struct file * file, struct vm_area_struct * vma);
+extern ssize_t xip_file_write(struct file *filp, const char __user *buf,
+			      size_t len, loff_t *ppos);
+extern int xip_truncate_page(struct address_space *mapping, loff_t from);
+#else
+static inline int xip_truncate_page(struct address_space *mapping, loff_t from)
+{
+	return 0;
+}
+#endif
+
+static inline void do_generic_file_read(struct file * filp, loff_t *ppos,
+					read_descriptor_t * desc,
+					read_actor_t actor)
+{
+	do_generic_mapping_read(filp->f_mapping,
+				&filp->f_ra,
+				filp,
+				ppos,
+				desc,
+				actor);
+}
+
+ssize_t __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
+	struct block_device *bdev, const struct iovec *iov, loff_t offset,
+	unsigned long nr_segs, get_blocks_t get_blocks, dio_iodone_t end_io,
+	int lock_type);
+
+enum {
+	DIO_LOCKING = 1, /* need locking between buffered and direct access */
+	DIO_NO_LOCKING,  /* bdev; no locking at all between buffered/direct */
+	DIO_OWN_LOCKING, /* filesystem locks buffered and direct internally */
+};
+
+static inline ssize_t blockdev_direct_IO(int rw, struct kiocb *iocb,
+	struct inode *inode, struct block_device *bdev, const struct iovec *iov,
+	loff_t offset, unsigned long nr_segs, get_blocks_t get_blocks,
+	dio_iodone_t end_io)
+{
+	return __blockdev_direct_IO(rw, iocb, inode, bdev, iov, offset,
+				nr_segs, get_blocks, end_io, DIO_LOCKING);
+}
+
+static inline ssize_t blockdev_direct_IO_no_locking(int rw, struct kiocb *iocb,
+	struct inode *inode, struct block_device *bdev, const struct iovec *iov,
+	loff_t offset, unsigned long nr_segs, get_blocks_t get_blocks,
+	dio_iodone_t end_io)
+{
+	return __blockdev_direct_IO(rw, iocb, inode, bdev, iov, offset,
+				nr_segs, get_blocks, end_io, DIO_NO_LOCKING);
+}
+
+static inline ssize_t blockdev_direct_IO_own_locking(int rw, struct kiocb *iocb,
+	struct inode *inode, struct block_device *bdev, const struct iovec *iov,
+	loff_t offset, unsigned long nr_segs, get_blocks_t get_blocks,
+	dio_iodone_t end_io)
+{
+	return __blockdev_direct_IO(rw, iocb, inode, bdev, iov, offset,
+				nr_segs, get_blocks, end_io, DIO_OWN_LOCKING);
+}
+
+extern struct file_operations generic_ro_fops;
+
+#define special_file(m) (S_ISCHR(m)||S_ISBLK(m)||S_ISFIFO(m)||S_ISSOCK(m))
+
+extern int vfs_readlink(struct dentry *, char __user *, int, const char *);
+extern int vfs_follow_link(struct nameidata *, const char *);
+extern int page_readlink(struct dentry *, char __user *, int);
+extern void *page_follow_link_light(struct dentry *, struct nameidata *);
+extern void page_put_link(struct dentry *, struct nameidata *, void *);
+extern int page_symlink(struct inode *inode, const char *symname, int len);
+extern struct inode_operations page_symlink_inode_operations;
+extern int generic_readlink(struct dentry *, char __user *, int);
+extern void generic_fillattr(struct inode *, struct kstat *);
+extern int vfs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
+void inode_add_bytes(struct inode *inode, loff_t bytes);
+void inode_sub_bytes(struct inode *inode, loff_t bytes);
+loff_t inode_get_bytes(struct inode *inode);
+void inode_set_bytes(struct inode *inode, loff_t bytes);
+
+extern int vfs_readdir(struct file *, filldir_t, void *);
+
+extern int vfs_stat(char __user *, struct kstat *);
+extern int vfs_lstat(char __user *, struct kstat *);
+extern int vfs_fstat(unsigned int, struct kstat *);
+
+extern int vfs_ioctl(struct file *, unsigned int, unsigned int, unsigned long);
+
+extern struct file_system_type *get_fs_type(const char *name);
+extern struct super_block *get_super(struct block_device *);
+extern struct super_block *user_get_super(dev_t);
+extern void drop_super(struct super_block *sb);
+
+extern int dcache_dir_open(struct inode *, struct file *);
+extern int dcache_dir_close(struct inode *, struct file *);
+extern loff_t dcache_dir_lseek(struct file *, loff_t, int);
+extern int dcache_readdir(struct file *, void *, filldir_t);
+extern int simple_getattr(struct vfsmount *, struct dentry *, struct kstat *);
+extern int simple_statfs(struct super_block *, struct kstatfs *);
+extern int simple_link(struct dentry *, struct inode *, struct dentry *);
+extern int simple_unlink(struct inode *, struct dentry *);
+extern int simple_rmdir(struct inode *, struct dentry *);
+extern int simple_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
+extern int simple_sync_file(struct file *, struct dentry *, int);
+extern int simple_empty(struct dentry *);
+extern int simple_readpage(struct file *file, struct page *page);
+extern int simple_prepare_write(struct file *file, struct page *page,
+			unsigned offset, unsigned to);
+extern int simple_commit_write(struct file *file, struct page *page,
+				unsigned offset, unsigned to);
+
+extern struct dentry *simple_lookup(struct inode *, struct dentry *, struct nameidata *);
+extern ssize_t generic_read_dir(struct file *, char __user *, size_t, loff_t *);
+extern struct file_operations simple_dir_operations;
+extern struct inode_operations simple_dir_inode_operations;
+struct tree_descr { char *name; struct file_operations *ops; int mode; };
+struct dentry *d_alloc_name(struct dentry *, const char *);
+extern int simple_fill_super(struct super_block *, int, struct tree_descr *);
+extern int simple_pin_fs(char *name, struct vfsmount **mount, int *count);
+extern void simple_release_fs(struct vfsmount **mount, int *count);
+
+extern ssize_t simple_read_from_buffer(void __user *, size_t, loff_t *, const void *, size_t);
+
+extern int inode_change_ok(struct inode *, struct iattr *);
+extern int __must_check inode_setattr(struct inode *, struct iattr *);
+
+extern void inode_update_time(struct inode *inode, int ctime_too);
+
+static inline ino_t parent_ino(struct dentry *dentry)
+{
+	ino_t res;
+
+	spin_lock(&dentry->d_lock);
+	res = dentry->d_parent->d_inode->i_ino;
+	spin_unlock(&dentry->d_lock);
+	return res;
+}
+
+/* kernel/fork.c */
+extern int unshare_files(void);
+
+/* Transaction based IO helpers */
+
+/*
+ * An argresp is stored in an allocated page and holds the
+ * size of the argument or response, along with its content
+ */
+struct simple_transaction_argresp {
+	ssize_t size;
+	char data[0];
+};
+
+#define SIMPLE_TRANSACTION_LIMIT (PAGE_SIZE - sizeof(struct simple_transaction_argresp))
+
+char *simple_transaction_get(struct file *file, const char __user *buf,
+				size_t size);
+ssize_t simple_transaction_read(struct file *file, char __user *buf,
+				size_t size, loff_t *pos);
+int simple_transaction_release(struct inode *inode, struct file *file);
+
+static inline void simple_transaction_set(struct file *file, size_t n)
+{
+	struct simple_transaction_argresp *ar = file->private_data;
+
+	BUG_ON(n > SIMPLE_TRANSACTION_LIMIT);
+
+	/*
+	 * The barrier ensures that ar->size will really remain zero until
+	 * ar->data is ready for reading.
+	 */
+	smp_mb();
+	ar->size = n;
+}
+
+/*
+ * simple attribute files
+ *
+ * These attributes behave similar to those in sysfs:
+ *
+ * Writing to an attribute immediately sets a value, an open file can be
+ * written to multiple times.
+ *
+ * Reading from an attribute creates a buffer from the value that might get
+ * read with multiple read calls. When the attribute has been read
+ * completely, no further read calls are possible until the file is opened
+ * again.
+ *
+ * All attributes contain a text representation of a numeric value
+ * that are accessed with the get() and set() functions.
+ */
+#define DEFINE_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt)		\
+static int __fops ## _open(struct inode *inode, struct file *file)	\
+{									\
+	__simple_attr_check_format(__fmt, 0ull);			\
+	return simple_attr_open(inode, file, __get, __set, __fmt);	\
+}									\
+static struct file_operations __fops = {				\
+	.owner	 = THIS_MODULE,						\
+	.open	 = __fops ## _open,					\
+	.release = simple_attr_close,					\
+	.read	 = simple_attr_read,					\
+	.write	 = simple_attr_write,					\
+};
+
+static inline void __attribute__((format(printf, 1, 2)))
+__simple_attr_check_format(const char *fmt, ...)
+{
+	/* don't do anything, just let the compiler check the arguments; */
+}
+
+int simple_attr_open(struct inode *inode, struct file *file,
+		     u64 (*get)(void *), void (*set)(void *, u64),
+		     const char *fmt);
+int simple_attr_close(struct inode *inode, struct file *file);
+ssize_t simple_attr_read(struct file *file, char __user *buf,
+			 size_t len, loff_t *ppos);
+ssize_t simple_attr_write(struct file *file, const char __user *buf,
+			  size_t len, loff_t *ppos);
+
+
+#ifdef CONFIG_SECURITY
+static inline char *alloc_secdata(void)
+{
+	return (char *)get_zeroed_page(GFP_KERNEL);
+}
+
+static inline void free_secdata(void *secdata)
+{
+	free_page((unsigned long)secdata);
+}
+#else
+static inline char *alloc_secdata(void)
+{
+	return (char *)1;
+}
+
+static inline void free_secdata(void *secdata)
+{ }
+#endif	/* CONFIG_SECURITY */
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_FS_H */
diff -urN oldtree/include/linux/fsnotify.h newtree/include/linux/fsnotify.h
--- oldtree/include/linux/fsnotify.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/fsnotify.h	2006-02-13 19:20:01.225326024 -0500
@@ -15,6 +15,7 @@
 
 #include <linux/dnotify.h>
 #include <linux/inotify.h>
+#include <linux/audit.h>
 
 /*
  * fsnotify_move - file old_name at old_dir was moved to new_name at new_dir
@@ -45,6 +46,8 @@
 	if (source) {
 		inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL);
 	}
+	audit_inode_child(old_name, source, old_dir->i_ino);
+	audit_inode_child(new_name, target, new_dir->i_ino);
 }
 
 /*
@@ -70,19 +73,22 @@
 /*
  * fsnotify_create - 'name' was linked in
  */
-static inline void fsnotify_create(struct inode *inode, const char *name)
+static inline void fsnotify_create(struct inode *inode, struct dentry *dentry)
 {
 	inode_dir_notify(inode, DN_CREATE);
-	inotify_inode_queue_event(inode, IN_CREATE, 0, name);
+	inotify_inode_queue_event(inode, IN_CREATE, 0, dentry->d_name.name);
+	audit_inode_child(dentry->d_name.name, dentry->d_inode, inode->i_ino);
 }
 
 /*
  * fsnotify_mkdir - directory 'name' was created
  */
-static inline void fsnotify_mkdir(struct inode *inode, const char *name)
+static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry)
 {
 	inode_dir_notify(inode, DN_CREATE);
-	inotify_inode_queue_event(inode, IN_CREATE | IN_ISDIR, 0, name);
+	inotify_inode_queue_event(inode, IN_CREATE | IN_ISDIR, 0, 
+				  dentry->d_name.name);
+	audit_inode_child(dentry->d_name.name, dentry->d_inode, inode->i_ino);
 }
 
 /*
diff -urN oldtree/include/linux/hardirq.h newtree/include/linux/hardirq.h
--- oldtree/include/linux/hardirq.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/hardirq.h	2006-02-13 19:20:01.225326024 -0500
@@ -93,10 +93,6 @@
 struct task_struct;
 
 #ifndef CONFIG_VIRT_CPU_ACCOUNTING
-static inline void account_user_vtime(struct task_struct *tsk)
-{
-}
-
 static inline void account_system_vtime(struct task_struct *tsk)
 {
 }
diff -urN oldtree/include/linux/init.h newtree/include/linux/init.h
--- oldtree/include/linux/init.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/init.h	2006-02-13 19:20:01.225326024 -0500
@@ -241,6 +241,18 @@
 #define __cpuexitdata	__exitdata
 #endif
 
+#ifdef CONFIG_MEMORY_HOTPLUG
+#define __meminit
+#define __meminitdata
+#define __memexit
+#define __memexitdata
+#else
+#define __meminit	__init
+#define __meminitdata __initdata
+#define __memexit __exit
+#define __memexitdata	__exitdata
+#endif
+
 /* Functions marked as __devexit may be discarded at kernel link time, depending
    on config options.  Newer versions of binutils detect references from
    retained sections to discarded sections and flag an error.  Pointers to
diff -urN oldtree/include/linux/init_task.h newtree/include/linux/init_task.h
--- oldtree/include/linux/init_task.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/init_task.h	2006-02-13 19:20:01.227325720 -0500
@@ -7,11 +7,10 @@
 #define INIT_FDTABLE \
 {							\
 	.max_fds	= NR_OPEN_DEFAULT, 		\
-	.max_fdset	= __FD_SETSIZE, 		\
-	.next_fd	= 0, 				\
+	.max_fdset	= 8 * sizeof(embedded_fd_set),	\
 	.fd		= &init_files.fd_array[0], 	\
-	.close_on_exec	= &init_files.close_on_exec_init, \
-	.open_fds	= &init_files.open_fds_init, 	\
+	.close_on_exec	= (fd_set *)&init_files.close_on_exec_init, \
+	.open_fds	= (fd_set *)&init_files.open_fds_init, 	\
 	.rcu		= RCU_HEAD_INIT, 		\
 	.free_files	= NULL,		 		\
 	.next		= NULL,		 		\
@@ -20,9 +19,10 @@
 #define INIT_FILES \
 { 							\
 	.count		= ATOMIC_INIT(1), 		\
-	.file_lock	= SPIN_LOCK_UNLOCKED, 		\
 	.fdt		= &init_files.fdtab, 		\
 	.fdtab		= INIT_FDTABLE,			\
+	.file_lock	= SPIN_LOCK_UNLOCKED, 		\
+	.next_fd	= 0, 				\
 	.close_on_exec_init = { { 0, } }, 		\
 	.open_fds_init	= { { 0, } }, 			\
 	.fd_array	= { NULL, } 			\
diff -urN oldtree/include/linux/io.h newtree/include/linux/io.h
--- oldtree/include/linux/io.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/linux/io.h	2006-02-13 19:20:01.228325568 -0500
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2006 PathScale, Inc.  All Rights Reserved.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _LINUX_IO_H
+#define _LINUX_IO_H
+
+#include <asm/io.h>
+
+void __iowrite32_copy(void __iomem *to, const void *from, size_t count);
+
+#endif /* _LINUX_IO_H */
diff -urN oldtree/include/linux/list.h newtree/include/linux/list.h
--- oldtree/include/linux/list.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/list.h	2006-02-13 19:20:01.228325568 -0500
@@ -5,7 +5,9 @@
 
 #include <linux/stddef.h>
 #include <linux/prefetch.h>
+#include <linux/kernel.h>
 #include <asm/system.h>
+#include <asm/bug.h>
 
 /*
  * These are non-NULL pointers that will result in page faults
@@ -34,9 +36,11 @@
 #define LIST_HEAD(name) \
 	struct list_head name = LIST_HEAD_INIT(name)
 
-#define INIT_LIST_HEAD(ptr) do { \
-	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
-} while (0)
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+	list->next = list;
+	list->prev = list;
+}
 
 /*
  * Insert a new entry between two known consecutive entries.
@@ -160,6 +164,8 @@
  */
 static inline void list_del(struct list_head *entry)
 {
+	BUG_ON(entry->prev->next != entry);
+	BUG_ON(entry->next->prev != entry);
 	__list_del(entry->prev, entry->next);
 	entry->next = LIST_POISON1;
 	entry->prev = LIST_POISON2;
@@ -520,7 +526,11 @@
 #define HLIST_HEAD_INIT { .first = NULL }
 #define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }
 #define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
-#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)
+static inline void INIT_HLIST_NODE(struct hlist_node *h)
+{
+	h->next = NULL;
+	h->pprev = NULL;
+}
 
 static inline int hlist_unhashed(const struct hlist_node *h)
 {
diff -urN oldtree/include/linux/list.h.orig newtree/include/linux/list.h.orig
--- oldtree/include/linux/list.h.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/linux/list.h.orig	2006-02-13 19:20:01.229325416 -0500
@@ -0,0 +1,800 @@
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#ifdef __KERNEL__
+
+#include <linux/stddef.h>
+#include <linux/prefetch.h>
+#include <linux/kernel.h>
+#include <asm/system.h>
+#include <asm/bug.h>
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1  ((void *) 0x00100100)
+#define LIST_POISON2  ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+	struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+			      struct list_head *prev,
+			      struct list_head *next)
+{
+	next->prev = new;
+	new->next = next;
+	new->prev = prev;
+	prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head->prev, head);
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add_rcu(struct list_head * new,
+		struct list_head * prev, struct list_head * next)
+{
+	new->next = next;
+	new->prev = prev;
+	smp_wmb();
+	next->prev = new;
+	prev->next = new;
+}
+
+/**
+ * list_add_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_rcu(struct list_head *new, struct list_head *head)
+{
+	__list_add_rcu(new, head, head->next);
+}
+
+/**
+ * list_add_tail_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_tail_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_tail_rcu(struct list_head *new,
+					struct list_head *head)
+{
+	__list_add_rcu(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+	BUG_ON(entry->prev->next != entry);
+	BUG_ON(entry->next->prev != entry);
+	__list_del(entry->prev, entry->next);
+	entry->next = LIST_POISON1;
+	entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_rcu - deletes entry from list without re-initialization
+ * @entry: the element to delete from the list.
+ *
+ * Note: list_empty on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_del_rcu()
+ * or list_add_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ *
+ * Note that the caller is not permitted to immediately free
+ * the newly deleted entry.  Instead, either synchronize_rcu()
+ * or call_rcu() must be used to defer freeing until an RCU
+ * grace period has elapsed.
+ */
+static inline void list_del_rcu(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	entry->prev = LIST_POISON2;
+}
+
+/*
+ * list_replace_rcu - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * The old entry will be replaced with the new entry atomically.
+ */
+static inline void list_replace_rcu(struct list_head *old,
+				struct list_head *new)
+{
+	new->next = old->next;
+	new->prev = old->prev;
+	smp_wmb();
+	new->next->prev = new;
+	new->prev->next = new;
+	old->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+        __list_del(list->prev, list->next);
+        list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+				  struct list_head *head)
+{
+        __list_del(list->prev, list->next);
+        list_add_tail(list, head);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+	return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is
+ * empty _and_ checks that no other CPU might be
+ * in the process of still modifying either member
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ *
+ * @head: the list to test.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+	struct list_head *next = head->next;
+	return (next == head) && (next == head->prev);
+}
+
+static inline void __list_splice(struct list_head *list,
+				 struct list_head *head)
+{
+	struct list_head *first = list->next;
+	struct list_head *last = list->prev;
+	struct list_head *at = head->next;
+
+	first->prev = head;
+	head->next = first;
+
+	last->next = at;
+	at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+	if (!list_empty(list))
+		__list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+				    struct list_head *head)
+{
+	if (!list_empty(list)) {
+		__list_splice(list, head);
+		INIT_LIST_HEAD(list);
+	}
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:	the &struct list_head pointer.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+	container_of(ptr, type, member)
+
+/**
+ * list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ */
+#define list_for_each(pos, head) \
+	for (pos = (head)->next; prefetch(pos->next), pos != (head); \
+        	pos = pos->next)
+
+/**
+ * __list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev	-	iterate over a list backwards
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+	for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
+        	pos = pos->prev)
+
+/**
+ * list_for_each_safe	-	iterate over a list safe against removal of list entry
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry	-	iterate over list of given type
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)				\
+	for (pos = list_entry((head)->next, typeof(*pos), member);	\
+	     prefetch(pos->member.next), &pos->member != (head); 	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)			\
+	for (pos = list_entry((head)->prev, typeof(*pos), member);	\
+	     prefetch(pos->member.prev), &pos->member != (head); 	\
+	     pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use as a start point in
+ *			list_for_each_entry_continue
+ * @pos:	the type * to use as a start point
+ * @head:	the head of the list
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_prepare_entry(pos, head, member) \
+	((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue -	iterate over list of given type
+ *			continuing after existing point
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_continue(pos, head, member) 		\
+	for (pos = list_entry(pos->member.next, typeof(*pos), member);	\
+	     prefetch(pos->member.next), &pos->member != (head);	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:	the type * to use as a loop counter.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)			\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		n = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head); 					\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_continue -	iterate over list of given type
+ *			continuing after existing point safe against removal of list entry
+ * @pos:	the type * to use as a loop counter.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe_continue(pos, n, head, member) 		\
+	for (pos = list_entry(pos->member.next, typeof(*pos), member), 		\
+		n = list_entry(pos->member.next, typeof(*pos), member);		\
+	     &pos->member != (head);						\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_rcu	-	iterate over an rcu-protected list
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_rcu(pos, head) \
+	for (pos = (head)->next; \
+		prefetch(rcu_dereference(pos)->next), pos != (head); \
+        	pos = pos->next)
+
+#define __list_for_each_rcu(pos, head) \
+	for (pos = (head)->next; \
+		rcu_dereference(pos) != (head); \
+        	pos = pos->next)
+
+/**
+ * list_for_each_safe_rcu	-	iterate over an rcu-protected list safe
+ *					against removal of list entry
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_safe_rcu(pos, n, head) \
+	for (pos = (head)->next; \
+		n = rcu_dereference(pos)->next, pos != (head); \
+		pos = n)
+
+/**
+ * list_for_each_entry_rcu	-	iterate over rcu list of given type
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_entry_rcu(pos, head, member) \
+	for (pos = list_entry((head)->next, typeof(*pos), member); \
+		prefetch(rcu_dereference(pos)->member.next), \
+			&pos->member != (head); \
+		pos = list_entry(pos->member.next, typeof(*pos), member))
+
+
+/**
+ * list_for_each_continue_rcu	-	iterate over an rcu-protected list
+ *			continuing after existing point.
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_continue_rcu(pos, head) \
+	for ((pos) = (pos)->next; \
+		prefetch(rcu_dereference((pos))->next), (pos) != (head); \
+        	(pos) = (pos)->next)
+
+/*
+ * Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+struct hlist_head {
+	struct hlist_node *first;
+};
+
+struct hlist_node {
+	struct hlist_node *next, **pprev;
+};
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)
+
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+	return !h->pprev;
+}
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+	return !h->first;
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+	struct hlist_node *next = n->next;
+	struct hlist_node **pprev = n->pprev;
+	*pprev = next;
+	if (next)
+		next->pprev = pprev;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+	__hlist_del(n);
+	n->next = LIST_POISON1;
+	n->pprev = LIST_POISON2;
+}
+
+/**
+ * hlist_del_rcu - deletes entry from hash list without re-initialization
+ * @n: the element to delete from the hash list.
+ *
+ * Note: list_unhashed() on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the hash list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry().
+ */
+static inline void hlist_del_rcu(struct hlist_node *n)
+{
+	__hlist_del(n);
+	n->pprev = LIST_POISON2;
+}
+
+static inline void hlist_del_init(struct hlist_node *n)
+{
+	if (n->pprev)  {
+		__hlist_del(n);
+		INIT_HLIST_NODE(n);
+	}
+}
+
+/*
+ * hlist_replace_rcu - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * The old entry will be replaced with the new entry atomically.
+ */
+static inline void hlist_replace_rcu(struct hlist_node *old,
+					struct hlist_node *new)
+{
+	struct hlist_node *next = old->next;
+
+	new->next = next;
+	new->pprev = old->pprev;
+	smp_wmb();
+	if (next)
+		new->next->pprev = &new->next;
+	*new->pprev = new;
+	old->pprev = LIST_POISON2;
+}
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+	struct hlist_node *first = h->first;
+	n->next = first;
+	if (first)
+		first->pprev = &n->next;
+	h->first = n;
+	n->pprev = &h->first;
+}
+
+
+/**
+ * hlist_add_head_rcu - adds the specified element to the specified hlist,
+ * while permitting racing traversals.
+ * @n: the element to add to the hash list.
+ * @h: the list to add to.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry_rcu(), used to prevent memory-consistency
+ * problems on Alpha CPUs.  Regardless of the type of CPU, the
+ * list-traversal primitive must be guarded by rcu_read_lock().
+ */
+static inline void hlist_add_head_rcu(struct hlist_node *n,
+					struct hlist_head *h)
+{
+	struct hlist_node *first = h->first;
+	n->next = first;
+	n->pprev = &h->first;
+	smp_wmb();
+	if (first)
+		first->pprev = &n->next;
+	h->first = n;
+}
+
+/* next must be != NULL */
+static inline void hlist_add_before(struct hlist_node *n,
+					struct hlist_node *next)
+{
+	n->pprev = next->pprev;
+	n->next = next;
+	next->pprev = &n->next;
+	*(n->pprev) = n;
+}
+
+static inline void hlist_add_after(struct hlist_node *n,
+					struct hlist_node *next)
+{
+	next->next = n->next;
+	n->next = next;
+	next->pprev = &n->next;
+
+	if(next->next)
+		next->next->pprev  = &next->next;
+}
+
+/**
+ * hlist_add_before_rcu - adds the specified element to the specified hlist
+ * before the specified node while permitting racing traversals.
+ * @n: the new element to add to the hash list.
+ * @next: the existing element to add the new element before.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry_rcu(), used to prevent memory-consistency
+ * problems on Alpha CPUs.
+ */
+static inline void hlist_add_before_rcu(struct hlist_node *n,
+					struct hlist_node *next)
+{
+	n->pprev = next->pprev;
+	n->next = next;
+	smp_wmb();
+	next->pprev = &n->next;
+	*(n->pprev) = n;
+}
+
+/**
+ * hlist_add_after_rcu - adds the specified element to the specified hlist
+ * after the specified node while permitting racing traversals.
+ * @prev: the existing element to add the new element after.
+ * @n: the new element to add to the hash list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry_rcu(), used to prevent memory-consistency
+ * problems on Alpha CPUs.
+ */
+static inline void hlist_add_after_rcu(struct hlist_node *prev,
+				       struct hlist_node *n)
+{
+	n->next = prev->next;
+	n->pprev = &prev->next;
+	smp_wmb();
+	prev->next = n;
+	if (n->next)
+		n->next->pprev = &n->next;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+	for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
+	     pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+	for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+	     pos = n)
+
+/**
+ * hlist_for_each_entry	- iterate over list of given type
+ * @tpos:	the type * to use as a loop counter.
+ * @pos:	the &struct hlist_node to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(tpos, pos, head, member)			 \
+	for (pos = (head)->first;					 \
+	     pos && ({ prefetch(pos->next); 1;}) &&			 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = pos->next)
+
+/**
+ * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
+ * @tpos:	the type * to use as a loop counter.
+ * @pos:	the &struct hlist_node to use as a loop counter.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(tpos, pos, member)		 \
+	for (pos = (pos)->next;						 \
+	     pos && ({ prefetch(pos->next); 1;}) &&			 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = pos->next)
+
+/**
+ * hlist_for_each_entry_from - iterate over a hlist continuing from existing point
+ * @tpos:	the type * to use as a loop counter.
+ * @pos:	the &struct hlist_node to use as a loop counter.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(tpos, pos, member)			 \
+	for (; pos && ({ prefetch(pos->next); 1;}) &&			 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = pos->next)
+
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @tpos:	the type * to use as a loop counter.
+ * @pos:	the &struct hlist_node to use as a loop counter.
+ * @n:		another &struct hlist_node to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member) 		 \
+	for (pos = (head)->first;					 \
+	     pos && ({ n = pos->next; 1; }) && 				 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = n)
+
+/**
+ * hlist_for_each_entry_rcu - iterate over rcu list of given type
+ * @tpos:	the type * to use as a loop counter.
+ * @pos:	the &struct hlist_node to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the hlist_node within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as hlist_add_head_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define hlist_for_each_entry_rcu(tpos, pos, head, member)		 \
+	for (pos = (head)->first;					 \
+	     rcu_dereference(pos) && ({ prefetch(pos->next); 1;}) &&	 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = pos->next)
+
+#else
+#warning "don't include kernel headers in userspace"
+#endif /* __KERNEL__ */
+#endif
diff -urN oldtree/include/linux/mempolicy.h newtree/include/linux/mempolicy.h
--- oldtree/include/linux/mempolicy.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/mempolicy.h	2006-02-13 19:20:01.231325112 -0500
@@ -136,12 +136,8 @@
 	spinlock_t lock;
 };
 
-static inline void mpol_shared_policy_init(struct shared_policy *info)
-{
-	info->root = RB_ROOT;
-	spin_lock_init(&info->lock);
-}
-
+void mpol_shared_policy_init(struct shared_policy *info, int policy,
+				nodemask_t *nodes);
 int mpol_set_shared_policy(struct shared_policy *info,
 				struct vm_area_struct *vma,
 				struct mempolicy *new);
@@ -202,7 +198,8 @@
 	return -EINVAL;
 }
 
-static inline void mpol_shared_policy_init(struct shared_policy *info)
+static inline void mpol_shared_policy_init(struct shared_policy *info,
+					int policy, nodemask_t *nodes)
 {
 }
 
diff -urN oldtree/include/linux/mempolicy.h.orig newtree/include/linux/mempolicy.h.orig
--- oldtree/include/linux/mempolicy.h.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/linux/mempolicy.h.orig	2006-02-13 19:20:01.232324960 -0500
@@ -0,0 +1,238 @@
+#ifndef _LINUX_MEMPOLICY_H
+#define _LINUX_MEMPOLICY_H 1
+
+#include <linux/errno.h>
+
+/*
+ * NUMA memory policies for Linux.
+ * Copyright 2003,2004 Andi Kleen SuSE Labs
+ */
+
+/* Policies */
+#define MPOL_DEFAULT	0
+#define MPOL_PREFERRED	1
+#define MPOL_BIND	2
+#define MPOL_INTERLEAVE	3
+
+#define MPOL_MAX MPOL_INTERLEAVE
+
+/* Flags for get_mem_policy */
+#define MPOL_F_NODE	(1<<0)	/* return next IL mode instead of node mask */
+#define MPOL_F_ADDR	(1<<1)	/* look up vma using address */
+
+/* Flags for mbind */
+#define MPOL_MF_STRICT	(1<<0)	/* Verify existing pages in the mapping */
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <linux/mmzone.h>
+#include <linux/slab.h>
+#include <linux/rbtree.h>
+#include <linux/spinlock.h>
+#include <linux/nodemask.h>
+
+struct vm_area_struct;
+
+#ifdef CONFIG_NUMA
+
+/*
+ * Describe a memory policy.
+ *
+ * A mempolicy can be either associated with a process or with a VMA.
+ * For VMA related allocations the VMA policy is preferred, otherwise
+ * the process policy is used. Interrupts ignore the memory policy
+ * of the current process.
+ *
+ * Locking policy for interlave:
+ * In process context there is no locking because only the process accesses
+ * its own state. All vma manipulation is somewhat protected by a down_read on
+ * mmap_sem.
+ *
+ * Freeing policy:
+ * When policy is MPOL_BIND v.zonelist is kmalloc'ed and must be kfree'd.
+ * All other policies don't have any external state. mpol_free() handles this.
+ *
+ * Copying policy objects:
+ * For MPOL_BIND the zonelist must be always duplicated. mpol_clone() does this.
+ */
+struct mempolicy {
+	atomic_t refcnt;
+	short policy; 	/* See MPOL_* above */
+	union {
+		struct zonelist  *zonelist;	/* bind */
+		short 		 preferred_node; /* preferred */
+		nodemask_t	 nodes;		/* interleave */
+		/* undefined for default */
+	} v;
+};
+
+/*
+ * Support for managing mempolicy data objects (clone, copy, destroy)
+ * The default fast path of a NULL MPOL_DEFAULT policy is always inlined.
+ */
+
+extern void __mpol_free(struct mempolicy *pol);
+static inline void mpol_free(struct mempolicy *pol)
+{
+	if (pol)
+		__mpol_free(pol);
+}
+
+extern struct mempolicy *__mpol_copy(struct mempolicy *pol);
+static inline struct mempolicy *mpol_copy(struct mempolicy *pol)
+{
+	if (pol)
+		pol = __mpol_copy(pol);
+	return pol;
+}
+
+#define vma_policy(vma) ((vma)->vm_policy)
+#define vma_set_policy(vma, pol) ((vma)->vm_policy = (pol))
+
+static inline void mpol_get(struct mempolicy *pol)
+{
+	if (pol)
+		atomic_inc(&pol->refcnt);
+}
+
+extern int __mpol_equal(struct mempolicy *a, struct mempolicy *b);
+static inline int mpol_equal(struct mempolicy *a, struct mempolicy *b)
+{
+	if (a == b)
+		return 1;
+	return __mpol_equal(a, b);
+}
+#define vma_mpol_equal(a,b) mpol_equal(vma_policy(a), vma_policy(b))
+
+/* Could later add inheritance of the process policy here. */
+
+#define mpol_set_vma_default(vma) ((vma)->vm_policy = NULL)
+
+/*
+ * Hugetlb policy. i386 hugetlb so far works with node numbers
+ * instead of zone lists, so give it special interfaces for now.
+ */
+extern int mpol_first_node(struct vm_area_struct *vma, unsigned long addr);
+extern int mpol_node_valid(int nid, struct vm_area_struct *vma,
+			unsigned long addr);
+
+/*
+ * Tree of shared policies for a shared memory region.
+ * Maintain the policies in a pseudo mm that contains vmas. The vmas
+ * carry the policy. As a special twist the pseudo mm is indexed in pages, not
+ * bytes, so that we can work with shared memory segments bigger than
+ * unsigned long.
+ */
+
+struct sp_node {
+	struct rb_node nd;
+	unsigned long start, end;
+	struct mempolicy *policy;
+};
+
+struct shared_policy {
+	struct rb_root root;
+	spinlock_t lock;
+};
+
+static inline void mpol_shared_policy_init(struct shared_policy *info)
+{
+	info->root = RB_ROOT;
+	spin_lock_init(&info->lock);
+}
+
+int mpol_set_shared_policy(struct shared_policy *info,
+				struct vm_area_struct *vma,
+				struct mempolicy *new);
+void mpol_free_shared_policy(struct shared_policy *p);
+struct mempolicy *mpol_shared_policy_lookup(struct shared_policy *sp,
+					    unsigned long idx);
+
+struct mempolicy *get_vma_policy(struct task_struct *task,
+			struct vm_area_struct *vma, unsigned long addr);
+
+extern void numa_default_policy(void);
+extern void numa_policy_init(void);
+extern void numa_policy_rebind(const nodemask_t *old, const nodemask_t *new);
+extern struct mempolicy default_policy;
+
+#else
+
+struct mempolicy {};
+
+static inline int mpol_equal(struct mempolicy *a, struct mempolicy *b)
+{
+	return 1;
+}
+#define vma_mpol_equal(a,b) 1
+
+#define mpol_set_vma_default(vma) do {} while(0)
+
+static inline void mpol_free(struct mempolicy *p)
+{
+}
+
+static inline void mpol_get(struct mempolicy *pol)
+{
+}
+
+static inline struct mempolicy *mpol_copy(struct mempolicy *old)
+{
+	return NULL;
+}
+
+static inline int mpol_first_node(struct vm_area_struct *vma, unsigned long a)
+{
+	return numa_node_id();
+}
+
+static inline int
+mpol_node_valid(int nid, struct vm_area_struct *vma, unsigned long a)
+{
+	return 1;
+}
+
+struct shared_policy {};
+
+static inline int mpol_set_shared_policy(struct shared_policy *info,
+					struct vm_area_struct *vma,
+					struct mempolicy *new)
+{
+	return -EINVAL;
+}
+
+static inline void mpol_shared_policy_init(struct shared_policy *info)
+{
+}
+
+static inline void mpol_free_shared_policy(struct shared_policy *p)
+{
+}
+
+static inline struct mempolicy *
+mpol_shared_policy_lookup(struct shared_policy *sp, unsigned long idx)
+{
+	return NULL;
+}
+
+#define vma_policy(vma) NULL
+#define vma_set_policy(vma, pol) do {} while(0)
+
+static inline void numa_policy_init(void)
+{
+}
+
+static inline void numa_default_policy(void)
+{
+}
+
+static inline void numa_policy_rebind(const nodemask_t *old,
+					const nodemask_t *new)
+{
+}
+
+#endif /* CONFIG_NUMA */
+#endif /* __KERNEL__ */
+
+#endif
diff -urN oldtree/include/linux/mm.h newtree/include/linux/mm.h
--- oldtree/include/linux/mm.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/mm.h	2006-02-13 19:20:01.234324656 -0500
@@ -259,6 +259,11 @@
 	void *virtual;			/* Kernel virtual address (NULL if
 					   not kmapped, ie. highmem) */
 #endif /* WANT_PAGE_VIRTUAL */
+#ifdef CONFIG_PAGE_OWNER
+	int order;
+	unsigned int gfp_mask;
+	unsigned long trace[8];
+#endif
 };
 
 #define page_private(page)		((page)->u.private)
@@ -507,7 +512,7 @@
 extern struct page *mem_map;
 #endif
 
-static inline void *lowmem_page_address(struct page *page)
+static __always_inline void *lowmem_page_address(struct page *page)
 {
 	return __va(page_to_pfn(page) << PAGE_SHIFT);
 }
diff -urN oldtree/include/linux/mm.h.orig newtree/include/linux/mm.h.orig
--- oldtree/include/linux/mm.h.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/linux/mm.h.orig	2006-02-13 19:20:01.237324200 -0500
@@ -0,0 +1,997 @@
+#ifndef _LINUX_MM_H
+#define _LINUX_MM_H
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <linux/gfp.h>
+#include <linux/list.h>
+#include <linux/mmzone.h>
+#include <linux/rbtree.h>
+#include <linux/prio_tree.h>
+#include <linux/fs.h>
+
+struct mempolicy;
+struct anon_vma;
+
+#ifndef CONFIG_DISCONTIGMEM          /* Don't use mapnrs, do it properly */
+extern unsigned long max_mapnr;
+#endif
+
+extern unsigned long num_physpages;
+extern void * high_memory;
+extern unsigned long vmalloc_earlyreserve;
+extern int page_cluster;
+
+#ifdef CONFIG_SYSCTL
+extern int sysctl_legacy_va_layout;
+#else
+#define sysctl_legacy_va_layout 0
+#endif
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+#include <asm/atomic.h>
+
+#define nth_page(page,n) pfn_to_page(page_to_pfn((page)) + (n))
+
+/*
+ * Linux kernel virtual memory manager primitives.
+ * The idea being to have a "virtual" mm in the same way
+ * we have a virtual fs - giving a cleaner interface to the
+ * mm details, and allowing different kinds of memory mappings
+ * (from shared memory to executable loading to arbitrary
+ * mmap() functions).
+ */
+
+/*
+ * This struct defines a memory VMM memory area. There is one of these
+ * per VM-area/task.  A VM area is any part of the process virtual memory
+ * space that has a special rule for the page-fault handlers (ie a shared
+ * library, the executable area etc).
+ */
+struct vm_area_struct {
+	struct mm_struct * vm_mm;	/* The address space we belong to. */
+	unsigned long vm_start;		/* Our start address within vm_mm. */
+	unsigned long vm_end;		/* The first byte after our end address
+					   within vm_mm. */
+
+	/* linked list of VM areas per task, sorted by address */
+	struct vm_area_struct *vm_next;
+
+	pgprot_t vm_page_prot;		/* Access permissions of this VMA. */
+	unsigned long vm_flags;		/* Flags, listed below. */
+
+	struct rb_node vm_rb;
+
+	/*
+	 * For areas with an address space and backing store,
+	 * linkage into the address_space->i_mmap prio tree, or
+	 * linkage to the list of like vmas hanging off its node, or
+	 * linkage of vma in the address_space->i_mmap_nonlinear list.
+	 */
+	union {
+		struct {
+			struct list_head list;
+			void *parent;	/* aligns with prio_tree_node parent */
+			struct vm_area_struct *head;
+		} vm_set;
+
+		struct raw_prio_tree_node prio_tree_node;
+	} shared;
+
+	/*
+	 * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
+	 * list, after a COW of one of the file pages.  A MAP_SHARED vma
+	 * can only be in the i_mmap tree.  An anonymous MAP_PRIVATE, stack
+	 * or brk vma (with NULL file) can only be in an anon_vma list.
+	 */
+	struct list_head anon_vma_node;	/* Serialized by anon_vma->lock */
+	struct anon_vma *anon_vma;	/* Serialized by page_table_lock */
+
+	/* Function pointers to deal with this struct. */
+	struct vm_operations_struct * vm_ops;
+
+	/* Information about our backing store: */
+	unsigned long vm_pgoff;		/* Offset (within vm_file) in PAGE_SIZE
+					   units, *not* PAGE_CACHE_SIZE */
+	struct file * vm_file;		/* File we map to (can be NULL). */
+	void * vm_private_data;		/* was vm_pte (shared mem) */
+	unsigned long vm_truncate_count;/* truncate_count or restart_addr */
+
+#ifndef CONFIG_MMU
+	atomic_t vm_usage;		/* refcount (VMAs shared if !MMU) */
+#endif
+#ifdef CONFIG_NUMA
+	struct mempolicy *vm_policy;	/* NUMA policy for the VMA */
+#endif
+};
+
+/*
+ * This struct defines the per-mm list of VMAs for uClinux. If CONFIG_MMU is
+ * disabled, then there's a single shared list of VMAs maintained by the
+ * system, and mm's subscribe to these individually
+ */
+struct vm_list_struct {
+	struct vm_list_struct	*next;
+	struct vm_area_struct	*vma;
+};
+
+#ifndef CONFIG_MMU
+extern struct rb_root nommu_vma_tree;
+extern struct rw_semaphore nommu_vma_sem;
+
+extern unsigned int kobjsize(const void *objp);
+#endif
+
+/*
+ * vm_flags..
+ */
+#define VM_READ		0x00000001	/* currently active flags */
+#define VM_WRITE	0x00000002
+#define VM_EXEC		0x00000004
+#define VM_SHARED	0x00000008
+
+/* mprotect() hardcodes VM_MAYREAD >> 4 == VM_READ, and so for r/w/x bits. */
+#define VM_MAYREAD	0x00000010	/* limits for mprotect() etc */
+#define VM_MAYWRITE	0x00000020
+#define VM_MAYEXEC	0x00000040
+#define VM_MAYSHARE	0x00000080
+
+#define VM_GROWSDOWN	0x00000100	/* general info on the segment */
+#define VM_GROWSUP	0x00000200
+#define VM_SHM		0x00000000	/* Means nothing: delete it later */
+#define VM_PFNMAP	0x00000400	/* Page-ranges managed without "struct page", just pure PFN */
+#define VM_DENYWRITE	0x00000800	/* ETXTBSY on write attempts.. */
+
+#define VM_EXECUTABLE	0x00001000
+#define VM_LOCKED	0x00002000
+#define VM_IO           0x00004000	/* Memory mapped I/O or similar */
+
+					/* Used by sys_madvise() */
+#define VM_SEQ_READ	0x00008000	/* App will access data sequentially */
+#define VM_RAND_READ	0x00010000	/* App will not benefit from clustered reads */
+
+#define VM_DONTCOPY	0x00020000      /* Do not copy this vma on fork */
+#define VM_DONTEXPAND	0x00040000	/* Cannot expand with mremap() */
+#define VM_RESERVED	0x00080000	/* Count as reserved_vm like IO */
+#define VM_ACCOUNT	0x00100000	/* Is a VM accounted object */
+#define VM_HUGETLB	0x00400000	/* Huge TLB Page VM */
+#define VM_NONLINEAR	0x00800000	/* Is non-linear (remap_file_pages) */
+#define VM_MAPPED_COPY	0x01000000	/* T if mapped copy of data (nommu mmap) */
+#define VM_INSERTPAGE	0x02000000	/* The vma has had "vm_insert_page()" done on it */
+
+#ifndef VM_STACK_DEFAULT_FLAGS		/* arch can override this */
+#define VM_STACK_DEFAULT_FLAGS VM_DATA_DEFAULT_FLAGS
+#endif
+
+#ifdef CONFIG_STACK_GROWSUP
+#define VM_STACK_FLAGS	(VM_GROWSUP | VM_STACK_DEFAULT_FLAGS | VM_ACCOUNT)
+#else
+#define VM_STACK_FLAGS	(VM_GROWSDOWN | VM_STACK_DEFAULT_FLAGS | VM_ACCOUNT)
+#endif
+
+#define VM_READHINTMASK			(VM_SEQ_READ | VM_RAND_READ)
+#define VM_ClearReadHint(v)		(v)->vm_flags &= ~VM_READHINTMASK
+#define VM_NormalReadHint(v)		(!((v)->vm_flags & VM_READHINTMASK))
+#define VM_SequentialReadHint(v)	((v)->vm_flags & VM_SEQ_READ)
+#define VM_RandomReadHint(v)		((v)->vm_flags & VM_RAND_READ)
+
+/*
+ * mapping from the currently active vm_flags protection bits (the
+ * low four bits) to a page protection mask..
+ */
+extern pgprot_t protection_map[16];
+
+
+/*
+ * These are the virtual MM functions - opening of an area, closing and
+ * unmapping it (needed to keep files on disk up-to-date etc), pointer
+ * to the functions called when a no-page or a wp-page exception occurs. 
+ */
+struct vm_operations_struct {
+	void (*open)(struct vm_area_struct * area);
+	void (*close)(struct vm_area_struct * area);
+	struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int *type);
+	int (*populate)(struct vm_area_struct * area, unsigned long address, unsigned long len, pgprot_t prot, unsigned long pgoff, int nonblock);
+#ifdef CONFIG_NUMA
+	int (*set_policy)(struct vm_area_struct *vma, struct mempolicy *new);
+	struct mempolicy *(*get_policy)(struct vm_area_struct *vma,
+					unsigned long addr);
+#endif
+};
+
+struct mmu_gather;
+struct inode;
+
+/*
+ * Each physical page in the system has a struct page associated with
+ * it to keep track of whatever it is we are using the page for at the
+ * moment. Note that we have no way to track which tasks are using
+ * a page.
+ */
+struct page {
+	unsigned long flags;		/* Atomic flags, some possibly
+					 * updated asynchronously */
+	atomic_t _count;		/* Usage count, see below. */
+	atomic_t _mapcount;		/* Count of ptes mapped in mms,
+					 * to show when page is mapped
+					 * & limit reverse map searches.
+					 */
+	union {
+		unsigned long private;	/* Mapping-private opaque data:
+					 * usually used for buffer_heads
+					 * if PagePrivate set; used for
+					 * swp_entry_t if PageSwapCache
+					 * When page is free, this indicates
+					 * order in the buddy system.
+					 */
+#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS
+		spinlock_t ptl;
+#endif
+	} u;
+	struct address_space *mapping;	/* If low bit clear, points to
+					 * inode address_space, or NULL.
+					 * If page mapped as anonymous
+					 * memory, low bit is set, and
+					 * it points to anon_vma object:
+					 * see PAGE_MAPPING_ANON below.
+					 */
+	pgoff_t index;			/* Our offset within mapping. */
+	struct list_head lru;		/* Pageout list, eg. active_list
+					 * protected by zone->lru_lock !
+					 */
+	/*
+	 * On machines where all RAM is mapped into kernel address space,
+	 * we can simply calculate the virtual address. On machines with
+	 * highmem some memory is mapped into kernel virtual memory
+	 * dynamically, so we need a place to store that address.
+	 * Note that this field could be 16 bits on x86 ... ;)
+	 *
+	 * Architectures with slow multiplication can define
+	 * WANT_PAGE_VIRTUAL in asm/page.h
+	 */
+#if defined(WANT_PAGE_VIRTUAL)
+	void *virtual;			/* Kernel virtual address (NULL if
+					   not kmapped, ie. highmem) */
+#endif /* WANT_PAGE_VIRTUAL */
+};
+
+#define page_private(page)		((page)->u.private)
+#define set_page_private(page, v)	((page)->u.private = (v))
+
+/*
+ * FIXME: take this include out, include page-flags.h in
+ * files which need it (119 of them)
+ */
+#include <linux/page-flags.h>
+
+/*
+ * Methods to modify the page usage count.
+ *
+ * What counts for a page usage:
+ * - cache mapping   (page->mapping)
+ * - private data    (page->private)
+ * - page mapped in a task's page tables, each mapping
+ *   is counted separately
+ *
+ * Also, many kernel routines increase the page count before a critical
+ * routine so they can be sure the page doesn't go away from under them.
+ *
+ * Since 2.6.6 (approx), a free page has ->_count = -1.  This is so that we
+ * can use atomic_add_negative(-1, page->_count) to detect when the page
+ * becomes free and so that we can also use atomic_inc_and_test to atomically
+ * detect when we just tried to grab a ref on a page which some other CPU has
+ * already deemed to be freeable.
+ *
+ * NO code should make assumptions about this internal detail!  Use the provided
+ * macros which retain the old rules: page_count(page) == 0 is a free page.
+ */
+
+/*
+ * Drop a ref, return true if the logical refcount fell to zero (the page has
+ * no users)
+ */
+#define put_page_testzero(p)				\
+	({						\
+		BUG_ON(page_count(p) == 0);		\
+		atomic_add_negative(-1, &(p)->_count);	\
+	})
+
+/*
+ * Grab a ref, return true if the page previously had a logical refcount of
+ * zero.  ie: returns true if we just grabbed an already-deemed-to-be-free page
+ */
+#define get_page_testone(p)	atomic_inc_and_test(&(p)->_count)
+
+#define set_page_count(p,v) 	atomic_set(&(p)->_count, v - 1)
+#define __put_page(p)		atomic_dec(&(p)->_count)
+
+extern void FASTCALL(__page_cache_release(struct page *));
+
+static inline int page_count(struct page *page)
+{
+	if (PageCompound(page))
+		page = (struct page *)page_private(page);
+	return atomic_read(&page->_count) + 1;
+}
+
+static inline void get_page(struct page *page)
+{
+	if (unlikely(PageCompound(page)))
+		page = (struct page *)page_private(page);
+	atomic_inc(&page->_count);
+}
+
+void put_page(struct page *page);
+
+/*
+ * Multiple processes may "see" the same page. E.g. for untouched
+ * mappings of /dev/null, all processes see the same page full of
+ * zeroes, and text pages of executables and shared libraries have
+ * only one copy in memory, at most, normally.
+ *
+ * For the non-reserved pages, page_count(page) denotes a reference count.
+ *   page_count() == 0 means the page is free. page->lru is then used for
+ *   freelist management in the buddy allocator.
+ *   page_count() == 1 means the page is used for exactly one purpose
+ *   (e.g. a private data page of one process).
+ *
+ * A page may be used for kmalloc() or anyone else who does a
+ * __get_free_page(). In this case the page_count() is at least 1, and
+ * all other fields are unused but should be 0 or NULL. The
+ * management of this page is the responsibility of the one who uses
+ * it.
+ *
+ * The other pages (we may call them "process pages") are completely
+ * managed by the Linux memory manager: I/O, buffers, swapping etc.
+ * The following discussion applies only to them.
+ *
+ * A page may belong to an inode's memory mapping. In this case,
+ * page->mapping is the pointer to the inode, and page->index is the
+ * file offset of the page, in units of PAGE_CACHE_SIZE.
+ *
+ * A page contains an opaque `private' member, which belongs to the
+ * page's address_space.  Usually, this is the address of a circular
+ * list of the page's disk buffers.
+ *
+ * For pages belonging to inodes, the page_count() is the number of
+ * attaches, plus 1 if `private' contains something, plus one for
+ * the page cache itself.
+ *
+ * Instead of keeping dirty/clean pages in per address-space lists, we instead
+ * now tag pages as dirty/under writeback in the radix tree.
+ *
+ * There is also a per-mapping radix tree mapping index to the page
+ * in memory if present. The tree is rooted at mapping->root.  
+ *
+ * All process pages can do I/O:
+ * - inode pages may need to be read from disk,
+ * - inode pages which have been modified and are MAP_SHARED may need
+ *   to be written to disk,
+ * - private pages which have been modified may need to be swapped out
+ *   to swap space and (later) to be read back into memory.
+ */
+
+/*
+ * The zone field is never updated after free_area_init_core()
+ * sets it, so none of the operations on it need to be atomic.
+ */
+
+
+/*
+ * page->flags layout:
+ *
+ * There are three possibilities for how page->flags get
+ * laid out.  The first is for the normal case, without
+ * sparsemem.  The second is for sparsemem when there is
+ * plenty of space for node and section.  The last is when
+ * we have run out of space and have to fall back to an
+ * alternate (slower) way of determining the node.
+ *
+ *        No sparsemem: |       NODE     | ZONE | ... | FLAGS |
+ * with space for node: | SECTION | NODE | ZONE | ... | FLAGS |
+ *   no space for node: | SECTION |     ZONE    | ... | FLAGS |
+ */
+#ifdef CONFIG_SPARSEMEM
+#define SECTIONS_WIDTH		SECTIONS_SHIFT
+#else
+#define SECTIONS_WIDTH		0
+#endif
+
+#define ZONES_WIDTH		ZONES_SHIFT
+
+#if SECTIONS_WIDTH+ZONES_WIDTH+NODES_SHIFT <= FLAGS_RESERVED
+#define NODES_WIDTH		NODES_SHIFT
+#else
+#define NODES_WIDTH		0
+#endif
+
+/* Page flags: | [SECTION] | [NODE] | ZONE | ... | FLAGS | */
+#define SECTIONS_PGOFF		((sizeof(unsigned long)*8) - SECTIONS_WIDTH)
+#define NODES_PGOFF		(SECTIONS_PGOFF - NODES_WIDTH)
+#define ZONES_PGOFF		(NODES_PGOFF - ZONES_WIDTH)
+
+/*
+ * We are going to use the flags for the page to node mapping if its in
+ * there.  This includes the case where there is no node, so it is implicit.
+ */
+#define FLAGS_HAS_NODE		(NODES_WIDTH > 0 || NODES_SHIFT == 0)
+
+#ifndef PFN_SECTION_SHIFT
+#define PFN_SECTION_SHIFT 0
+#endif
+
+/*
+ * Define the bit shifts to access each section.  For non-existant
+ * sections we define the shift as 0; that plus a 0 mask ensures
+ * the compiler will optimise away reference to them.
+ */
+#define SECTIONS_PGSHIFT	(SECTIONS_PGOFF * (SECTIONS_WIDTH != 0))
+#define NODES_PGSHIFT		(NODES_PGOFF * (NODES_WIDTH != 0))
+#define ZONES_PGSHIFT		(ZONES_PGOFF * (ZONES_WIDTH != 0))
+
+/* NODE:ZONE or SECTION:ZONE is used to lookup the zone from a page. */
+#if FLAGS_HAS_NODE
+#define ZONETABLE_SHIFT		(NODES_SHIFT + ZONES_SHIFT)
+#else
+#define ZONETABLE_SHIFT		(SECTIONS_SHIFT + ZONES_SHIFT)
+#endif
+#define ZONETABLE_PGSHIFT	ZONES_PGSHIFT
+
+#if SECTIONS_WIDTH+NODES_WIDTH+ZONES_WIDTH > FLAGS_RESERVED
+#error SECTIONS_WIDTH+NODES_WIDTH+ZONES_WIDTH > FLAGS_RESERVED
+#endif
+
+#define ZONES_MASK		((1UL << ZONES_WIDTH) - 1)
+#define NODES_MASK		((1UL << NODES_WIDTH) - 1)
+#define SECTIONS_MASK		((1UL << SECTIONS_WIDTH) - 1)
+#define ZONETABLE_MASK		((1UL << ZONETABLE_SHIFT) - 1)
+
+static inline unsigned long page_zonenum(struct page *page)
+{
+	return (page->flags >> ZONES_PGSHIFT) & ZONES_MASK;
+}
+
+struct zone;
+extern struct zone *zone_table[];
+
+static inline struct zone *page_zone(struct page *page)
+{
+	return zone_table[(page->flags >> ZONETABLE_PGSHIFT) &
+			ZONETABLE_MASK];
+}
+
+static inline unsigned long page_to_nid(struct page *page)
+{
+	if (FLAGS_HAS_NODE)
+		return (page->flags >> NODES_PGSHIFT) & NODES_MASK;
+	else
+		return page_zone(page)->zone_pgdat->node_id;
+}
+static inline unsigned long page_to_section(struct page *page)
+{
+	return (page->flags >> SECTIONS_PGSHIFT) & SECTIONS_MASK;
+}
+
+static inline void set_page_zone(struct page *page, unsigned long zone)
+{
+	page->flags &= ~(ZONES_MASK << ZONES_PGSHIFT);
+	page->flags |= (zone & ZONES_MASK) << ZONES_PGSHIFT;
+}
+static inline void set_page_node(struct page *page, unsigned long node)
+{
+	page->flags &= ~(NODES_MASK << NODES_PGSHIFT);
+	page->flags |= (node & NODES_MASK) << NODES_PGSHIFT;
+}
+static inline void set_page_section(struct page *page, unsigned long section)
+{
+	page->flags &= ~(SECTIONS_MASK << SECTIONS_PGSHIFT);
+	page->flags |= (section & SECTIONS_MASK) << SECTIONS_PGSHIFT;
+}
+
+static inline void set_page_links(struct page *page, unsigned long zone,
+	unsigned long node, unsigned long pfn)
+{
+	set_page_zone(page, zone);
+	set_page_node(page, node);
+	set_page_section(page, pfn_to_section_nr(pfn));
+}
+
+#ifndef CONFIG_DISCONTIGMEM
+/* The array of struct pages - for discontigmem use pgdat->lmem_map */
+extern struct page *mem_map;
+#endif
+
+static __always_inline void *lowmem_page_address(struct page *page)
+{
+	return __va(page_to_pfn(page) << PAGE_SHIFT);
+}
+
+#if defined(CONFIG_HIGHMEM) && !defined(WANT_PAGE_VIRTUAL)
+#define HASHED_PAGE_VIRTUAL
+#endif
+
+#if defined(WANT_PAGE_VIRTUAL)
+#define page_address(page) ((page)->virtual)
+#define set_page_address(page, address)			\
+	do {						\
+		(page)->virtual = (address);		\
+	} while(0)
+#define page_address_init()  do { } while(0)
+#endif
+
+#if defined(HASHED_PAGE_VIRTUAL)
+void *page_address(struct page *page);
+void set_page_address(struct page *page, void *virtual);
+void page_address_init(void);
+#endif
+
+#if !defined(HASHED_PAGE_VIRTUAL) && !defined(WANT_PAGE_VIRTUAL)
+#define page_address(page) lowmem_page_address(page)
+#define set_page_address(page, address)  do { } while(0)
+#define page_address_init()  do { } while(0)
+#endif
+
+/*
+ * On an anonymous page mapped into a user virtual memory area,
+ * page->mapping points to its anon_vma, not to a struct address_space;
+ * with the PAGE_MAPPING_ANON bit set to distinguish it.
+ *
+ * Please note that, confusingly, "page_mapping" refers to the inode
+ * address_space which maps the page from disk; whereas "page_mapped"
+ * refers to user virtual address space into which the page is mapped.
+ */
+#define PAGE_MAPPING_ANON	1
+
+extern struct address_space swapper_space;
+static inline struct address_space *page_mapping(struct page *page)
+{
+	struct address_space *mapping = page->mapping;
+
+	if (unlikely(PageSwapCache(page)))
+		mapping = &swapper_space;
+	else if (unlikely((unsigned long)mapping & PAGE_MAPPING_ANON))
+		mapping = NULL;
+	return mapping;
+}
+
+static inline int PageAnon(struct page *page)
+{
+	return ((unsigned long)page->mapping & PAGE_MAPPING_ANON) != 0;
+}
+
+/*
+ * Return the pagecache index of the passed page.  Regular pagecache pages
+ * use ->index whereas swapcache pages use ->private
+ */
+static inline pgoff_t page_index(struct page *page)
+{
+	if (unlikely(PageSwapCache(page)))
+		return page_private(page);
+	return page->index;
+}
+
+/*
+ * The atomic page->_mapcount, like _count, starts from -1:
+ * so that transitions both from it and to it can be tracked,
+ * using atomic_inc_and_test and atomic_add_negative(-1).
+ */
+static inline void reset_page_mapcount(struct page *page)
+{
+	atomic_set(&(page)->_mapcount, -1);
+}
+
+static inline int page_mapcount(struct page *page)
+{
+	return atomic_read(&(page)->_mapcount) + 1;
+}
+
+/*
+ * Return true if this page is mapped into pagetables.
+ */
+static inline int page_mapped(struct page *page)
+{
+	return atomic_read(&(page)->_mapcount) >= 0;
+}
+
+/*
+ * Error return values for the *_nopage functions
+ */
+#define NOPAGE_SIGBUS	(NULL)
+#define NOPAGE_OOM	((struct page *) (-1))
+
+/*
+ * Different kinds of faults, as returned by handle_mm_fault().
+ * Used to decide whether a process gets delivered SIGBUS or
+ * just gets major/minor fault counters bumped up.
+ */
+#define VM_FAULT_OOM	0x00
+#define VM_FAULT_SIGBUS	0x01
+#define VM_FAULT_MINOR	0x02
+#define VM_FAULT_MAJOR	0x03
+
+/* 
+ * Special case for get_user_pages.
+ * Must be in a distinct bit from the above VM_FAULT_ flags.
+ */
+#define VM_FAULT_WRITE	0x10
+
+#define offset_in_page(p)	((unsigned long)(p) & ~PAGE_MASK)
+
+extern void show_free_areas(void);
+
+#ifdef CONFIG_SHMEM
+struct page *shmem_nopage(struct vm_area_struct *vma,
+			unsigned long address, int *type);
+int shmem_set_policy(struct vm_area_struct *vma, struct mempolicy *new);
+struct mempolicy *shmem_get_policy(struct vm_area_struct *vma,
+					unsigned long addr);
+int shmem_lock(struct file *file, int lock, struct user_struct *user);
+#else
+#define shmem_nopage filemap_nopage
+#define shmem_lock(a, b, c) 	({0;})	/* always in memory, no need to lock */
+#define shmem_set_policy(a, b)	(0)
+#define shmem_get_policy(a, b)	(NULL)
+#endif
+struct file *shmem_file_setup(char *name, loff_t size, unsigned long flags);
+
+int shmem_zero_setup(struct vm_area_struct *);
+
+static inline int can_do_mlock(void)
+{
+	if (capable(CAP_IPC_LOCK))
+		return 1;
+	if (current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur != 0)
+		return 1;
+	return 0;
+}
+extern int user_shm_lock(size_t, struct user_struct *);
+extern void user_shm_unlock(size_t, struct user_struct *);
+
+/*
+ * Parameter block passed down to zap_pte_range in exceptional cases.
+ */
+struct zap_details {
+	struct vm_area_struct *nonlinear_vma;	/* Check page->index if set */
+	struct address_space *check_mapping;	/* Check page->mapping if set */
+	pgoff_t	first_index;			/* Lowest page->index to unmap */
+	pgoff_t last_index;			/* Highest page->index to unmap */
+	spinlock_t *i_mmap_lock;		/* For unmap_mapping_range: */
+	unsigned long truncate_count;		/* Compare vm_truncate_count */
+};
+
+struct page *vm_normal_page(struct vm_area_struct *, unsigned long, pte_t);
+unsigned long zap_page_range(struct vm_area_struct *vma, unsigned long address,
+		unsigned long size, struct zap_details *);
+unsigned long unmap_vmas(struct mmu_gather **tlb,
+		struct vm_area_struct *start_vma, unsigned long start_addr,
+		unsigned long end_addr, unsigned long *nr_accounted,
+		struct zap_details *);
+void free_pgd_range(struct mmu_gather **tlb, unsigned long addr,
+		unsigned long end, unsigned long floor, unsigned long ceiling);
+void free_pgtables(struct mmu_gather **tlb, struct vm_area_struct *start_vma,
+		unsigned long floor, unsigned long ceiling);
+int copy_page_range(struct mm_struct *dst, struct mm_struct *src,
+			struct vm_area_struct *vma);
+int zeromap_page_range(struct vm_area_struct *vma, unsigned long from,
+			unsigned long size, pgprot_t prot);
+void unmap_mapping_range(struct address_space *mapping,
+		loff_t const holebegin, loff_t const holelen, int even_cows);
+
+static inline void unmap_shared_mapping_range(struct address_space *mapping,
+		loff_t const holebegin, loff_t const holelen)
+{
+	unmap_mapping_range(mapping, holebegin, holelen, 0);
+}
+
+extern int vmtruncate(struct inode * inode, loff_t offset);
+extern int install_page(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long addr, struct page *page, pgprot_t prot);
+extern int install_file_pte(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long addr, unsigned long pgoff, pgprot_t prot);
+extern int __handle_mm_fault(struct mm_struct *mm,struct vm_area_struct *vma, unsigned long address, int write_access);
+
+static inline int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, int write_access)
+{
+	return __handle_mm_fault(mm, vma, address, write_access) & (~VM_FAULT_WRITE);
+}
+
+extern int make_pages_present(unsigned long addr, unsigned long end);
+extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write);
+void install_arg_page(struct vm_area_struct *, struct page *, unsigned long);
+
+int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start,
+		int len, int write, int force, struct page **pages, struct vm_area_struct **vmas);
+void print_bad_pte(struct vm_area_struct *, pte_t, unsigned long);
+
+int __set_page_dirty_buffers(struct page *page);
+int __set_page_dirty_nobuffers(struct page *page);
+int redirty_page_for_writepage(struct writeback_control *wbc,
+				struct page *page);
+int FASTCALL(set_page_dirty(struct page *page));
+int set_page_dirty_lock(struct page *page);
+int clear_page_dirty_for_io(struct page *page);
+
+extern unsigned long do_mremap(unsigned long addr,
+			       unsigned long old_len, unsigned long new_len,
+			       unsigned long flags, unsigned long new_addr);
+
+/*
+ * Prototype to add a shrinker callback for ageable caches.
+ * 
+ * These functions are passed a count `nr_to_scan' and a gfpmask.  They should
+ * scan `nr_to_scan' objects, attempting to free them.
+ *
+ * The callback must return the number of objects which remain in the cache.
+ *
+ * The callback will be passed nr_to_scan == 0 when the VM is querying the
+ * cache size, so a fastpath for that case is appropriate.
+ */
+typedef int (*shrinker_t)(int nr_to_scan, gfp_t gfp_mask);
+
+/*
+ * Add an aging callback.  The int is the number of 'seeks' it takes
+ * to recreate one of the objects that these functions age.
+ */
+
+#define DEFAULT_SEEKS 2
+struct shrinker;
+extern struct shrinker *set_shrinker(int, shrinker_t);
+extern void remove_shrinker(struct shrinker *shrinker);
+
+extern pte_t *FASTCALL(get_locked_pte(struct mm_struct *mm, unsigned long addr, spinlock_t **ptl));
+
+int __pud_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address);
+int __pmd_alloc(struct mm_struct *mm, pud_t *pud, unsigned long address);
+int __pte_alloc(struct mm_struct *mm, pmd_t *pmd, unsigned long address);
+int __pte_alloc_kernel(pmd_t *pmd, unsigned long address);
+
+/*
+ * The following ifdef needed to get the 4level-fixup.h header to work.
+ * Remove it when 4level-fixup.h has been removed.
+ */
+#if defined(CONFIG_MMU) && !defined(__ARCH_HAS_4LEVEL_HACK)
+static inline pud_t *pud_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address)
+{
+	return (unlikely(pgd_none(*pgd)) && __pud_alloc(mm, pgd, address))?
+		NULL: pud_offset(pgd, address);
+}
+
+static inline pmd_t *pmd_alloc(struct mm_struct *mm, pud_t *pud, unsigned long address)
+{
+	return (unlikely(pud_none(*pud)) && __pmd_alloc(mm, pud, address))?
+		NULL: pmd_offset(pud, address);
+}
+#endif /* CONFIG_MMU && !__ARCH_HAS_4LEVEL_HACK */
+
+#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS
+/*
+ * We tuck a spinlock to guard each pagetable page into its struct page,
+ * at page->private, with BUILD_BUG_ON to make sure that this will not
+ * overflow into the next struct page (as it might with DEBUG_SPINLOCK).
+ * When freeing, reset page->mapping so free_pages_check won't complain.
+ */
+#define __pte_lockptr(page)	&((page)->u.ptl)
+#define pte_lock_init(_page)	do {					\
+	spin_lock_init(__pte_lockptr(_page));				\
+} while (0)
+#define pte_lock_deinit(page)	((page)->mapping = NULL)
+#define pte_lockptr(mm, pmd)	({(void)(mm); __pte_lockptr(pmd_page(*(pmd)));})
+#else
+/*
+ * We use mm->page_table_lock to guard all pagetable pages of the mm.
+ */
+#define pte_lock_init(page)	do {} while (0)
+#define pte_lock_deinit(page)	do {} while (0)
+#define pte_lockptr(mm, pmd)	({(void)(pmd); &(mm)->page_table_lock;})
+#endif /* NR_CPUS < CONFIG_SPLIT_PTLOCK_CPUS */
+
+#define pte_offset_map_lock(mm, pmd, address, ptlp)	\
+({							\
+	spinlock_t *__ptl = pte_lockptr(mm, pmd);	\
+	pte_t *__pte = pte_offset_map(pmd, address);	\
+	*(ptlp) = __ptl;				\
+	spin_lock(__ptl);				\
+	__pte;						\
+})
+
+#define pte_unmap_unlock(pte, ptl)	do {		\
+	spin_unlock(ptl);				\
+	pte_unmap(pte);					\
+} while (0)
+
+#define pte_alloc_map(mm, pmd, address)			\
+	((unlikely(!pmd_present(*(pmd))) && __pte_alloc(mm, pmd, address))? \
+		NULL: pte_offset_map(pmd, address))
+
+#define pte_alloc_map_lock(mm, pmd, address, ptlp)	\
+	((unlikely(!pmd_present(*(pmd))) && __pte_alloc(mm, pmd, address))? \
+		NULL: pte_offset_map_lock(mm, pmd, address, ptlp))
+
+#define pte_alloc_kernel(pmd, address)			\
+	((unlikely(!pmd_present(*(pmd))) && __pte_alloc_kernel(pmd, address))? \
+		NULL: pte_offset_kernel(pmd, address))
+
+extern void free_area_init(unsigned long * zones_size);
+extern void free_area_init_node(int nid, pg_data_t *pgdat,
+	unsigned long * zones_size, unsigned long zone_start_pfn, 
+	unsigned long *zholes_size);
+extern void memmap_init_zone(unsigned long, int, unsigned long, unsigned long);
+extern void setup_per_zone_pages_min(void);
+extern void mem_init(void);
+extern void show_mem(void);
+extern void si_meminfo(struct sysinfo * val);
+extern void si_meminfo_node(struct sysinfo *val, int nid);
+
+#ifdef CONFIG_NUMA
+extern void setup_per_cpu_pageset(void);
+#else
+static inline void setup_per_cpu_pageset(void) {}
+#endif
+
+/* prio_tree.c */
+void vma_prio_tree_add(struct vm_area_struct *, struct vm_area_struct *old);
+void vma_prio_tree_insert(struct vm_area_struct *, struct prio_tree_root *);
+void vma_prio_tree_remove(struct vm_area_struct *, struct prio_tree_root *);
+struct vm_area_struct *vma_prio_tree_next(struct vm_area_struct *vma,
+	struct prio_tree_iter *iter);
+
+#define vma_prio_tree_foreach(vma, iter, root, begin, end)	\
+	for (prio_tree_iter_init(iter, root, begin, end), vma = NULL;	\
+		(vma = vma_prio_tree_next(vma, iter)); )
+
+static inline void vma_nonlinear_insert(struct vm_area_struct *vma,
+					struct list_head *list)
+{
+	vma->shared.vm_set.parent = NULL;
+	list_add_tail(&vma->shared.vm_set.list, list);
+}
+
+/* mmap.c */
+extern int __vm_enough_memory(long pages, int cap_sys_admin);
+extern void vma_adjust(struct vm_area_struct *vma, unsigned long start,
+	unsigned long end, pgoff_t pgoff, struct vm_area_struct *insert);
+extern struct vm_area_struct *vma_merge(struct mm_struct *,
+	struct vm_area_struct *prev, unsigned long addr, unsigned long end,
+	unsigned long vm_flags, struct anon_vma *, struct file *, pgoff_t,
+	struct mempolicy *);
+extern struct anon_vma *find_mergeable_anon_vma(struct vm_area_struct *);
+extern int split_vma(struct mm_struct *,
+	struct vm_area_struct *, unsigned long addr, int new_below);
+extern int insert_vm_struct(struct mm_struct *, struct vm_area_struct *);
+extern void __vma_link_rb(struct mm_struct *, struct vm_area_struct *,
+	struct rb_node **, struct rb_node *);
+extern void unlink_file_vma(struct vm_area_struct *);
+extern struct vm_area_struct *copy_vma(struct vm_area_struct **,
+	unsigned long addr, unsigned long len, pgoff_t pgoff);
+extern void exit_mmap(struct mm_struct *);
+extern int may_expand_vm(struct mm_struct *mm, unsigned long npages);
+
+extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
+
+extern unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
+	unsigned long len, unsigned long prot,
+	unsigned long flag, unsigned long pgoff);
+
+static inline unsigned long do_mmap(struct file *file, unsigned long addr,
+	unsigned long len, unsigned long prot,
+	unsigned long flag, unsigned long offset)
+{
+	unsigned long ret = -EINVAL;
+	if ((offset + PAGE_ALIGN(len)) < offset)
+		goto out;
+	if (!(offset & ~PAGE_MASK))
+		ret = do_mmap_pgoff(file, addr, len, prot, flag, offset >> PAGE_SHIFT);
+out:
+	return ret;
+}
+
+extern int do_munmap(struct mm_struct *, unsigned long, size_t);
+
+extern unsigned long do_brk(unsigned long, unsigned long);
+
+/* filemap.c */
+extern unsigned long page_unuse(struct page *);
+extern void truncate_inode_pages(struct address_space *, loff_t);
+
+/* generic vm_area_ops exported for stackable file systems */
+extern struct page *filemap_nopage(struct vm_area_struct *, unsigned long, int *);
+extern int filemap_populate(struct vm_area_struct *, unsigned long,
+		unsigned long, pgprot_t, unsigned long, int);
+
+/* mm/page-writeback.c */
+int write_one_page(struct page *page, int wait);
+
+/* readahead.c */
+#define VM_MAX_READAHEAD	128	/* kbytes */
+#define VM_MIN_READAHEAD	16	/* kbytes (includes current page) */
+#define VM_MAX_CACHE_HIT    	256	/* max pages in a row in cache before
+					 * turning readahead off */
+
+int do_page_cache_readahead(struct address_space *mapping, struct file *filp,
+			pgoff_t offset, unsigned long nr_to_read);
+int force_page_cache_readahead(struct address_space *mapping, struct file *filp,
+			pgoff_t offset, unsigned long nr_to_read);
+unsigned long page_cache_readahead(struct address_space *mapping,
+			  struct file_ra_state *ra,
+			  struct file *filp,
+			  pgoff_t offset,
+			  unsigned long size);
+void handle_ra_miss(struct address_space *mapping, 
+		    struct file_ra_state *ra, pgoff_t offset);
+unsigned long max_sane_readahead(unsigned long nr);
+
+/* Do stack extension */
+extern int expand_stack(struct vm_area_struct *vma, unsigned long address);
+#ifdef CONFIG_IA64
+extern int expand_upwards(struct vm_area_struct *vma, unsigned long address);
+#endif
+
+/* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
+extern struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr);
+extern struct vm_area_struct * find_vma_prev(struct mm_struct * mm, unsigned long addr,
+					     struct vm_area_struct **pprev);
+
+/* Look up the first VMA which intersects the interval start_addr..end_addr-1,
+   NULL if none.  Assume start_addr < end_addr. */
+static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr)
+{
+	struct vm_area_struct * vma = find_vma(mm,start_addr);
+
+	if (vma && end_addr <= vma->vm_start)
+		vma = NULL;
+	return vma;
+}
+
+static inline unsigned long vma_pages(struct vm_area_struct *vma)
+{
+	return (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+}
+
+struct vm_area_struct *find_extend_vma(struct mm_struct *, unsigned long addr);
+struct page *vmalloc_to_page(void *addr);
+unsigned long vmalloc_to_pfn(void *addr);
+int remap_pfn_range(struct vm_area_struct *, unsigned long addr,
+			unsigned long pfn, unsigned long size, pgprot_t);
+int vm_insert_page(struct vm_area_struct *, unsigned long addr, struct page *);
+
+struct page *follow_page(struct vm_area_struct *, unsigned long address,
+			unsigned int foll_flags);
+#define FOLL_WRITE	0x01	/* check pte is writable */
+#define FOLL_TOUCH	0x02	/* mark page accessed */
+#define FOLL_GET	0x04	/* do get_page on page */
+#define FOLL_ANON	0x08	/* give ZERO_PAGE if no pgtable */
+
+#ifdef CONFIG_PROC_FS
+void vm_stat_account(struct mm_struct *, unsigned long, struct file *, long);
+#else
+static inline void vm_stat_account(struct mm_struct *mm,
+			unsigned long flags, struct file *file, long pages)
+{
+}
+#endif /* CONFIG_PROC_FS */
+
+#ifndef CONFIG_DEBUG_PAGEALLOC
+static inline void
+kernel_map_pages(struct page *page, int numpages, int enable)
+{
+}
+#endif
+
+extern struct vm_area_struct *get_gate_vma(struct task_struct *tsk);
+#ifdef	__HAVE_ARCH_GATE_AREA
+int in_gate_area_no_task(unsigned long addr);
+int in_gate_area(struct task_struct *task, unsigned long addr);
+#else
+int in_gate_area_no_task(unsigned long addr);
+#define in_gate_area(task, addr) ({(void)task; in_gate_area_no_task(addr);})
+#endif	/* __HAVE_ARCH_GATE_AREA */
+
+/* /proc/<pid>/oom_adj set to -17 protects from the oom-killer */
+#define OOM_DISABLE -17
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_MM_H */
diff -urN oldtree/include/linux/ncp_fs.h newtree/include/linux/ncp_fs.h
--- oldtree/include/linux/ncp_fs.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/ncp_fs.h	2006-02-13 19:20:01.238324048 -0500
@@ -201,34 +201,6 @@
 	return container_of(inode, struct ncp_inode_info, vfs_inode);
 }
 
-#ifdef DEBUG_NCP_MALLOC
-
-#include <linux/slab.h>
-
-extern int ncp_malloced;
-extern int ncp_current_malloced;
-
-static inline void *
- ncp_kmalloc(unsigned int size, int priority)
-{
-	ncp_malloced += 1;
-	ncp_current_malloced += 1;
-	return kmalloc(size, priority);
-}
-
-static inline void ncp_kfree_s(void *obj, int size)
-{
-	ncp_current_malloced -= 1;
-	kfree(obj);
-}
-
-#else				/* DEBUG_NCP_MALLOC */
-
-#define ncp_kmalloc(s,p) kmalloc(s,p)
-#define ncp_kfree_s(o,s) kfree(o)
-
-#endif				/* DEBUG_NCP_MALLOC */
-
 /* linux/fs/ncpfs/inode.c */
 int ncp_notify_change(struct dentry *, struct iattr *);
 struct inode *ncp_iget(struct super_block *, struct ncp_entry_info *);
diff -urN oldtree/include/linux/page-flags.h newtree/include/linux/page-flags.h
--- oldtree/include/linux/page-flags.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/page-flags.h	2006-02-13 19:20:01.238324048 -0500
@@ -62,6 +62,7 @@
 #define PG_slab			 7	/* slab debug (Suparna wants this) */
 
 #define PG_checked		 8	/* kill me in 2.5.<early>. */
+#define PG_fs_misc		 8
 #define PG_arch_1		 9
 #define PG_reserved		10
 #define PG_private		11	/* Has something at ->private */
@@ -319,4 +320,13 @@
 	test_set_page_writeback(page);
 }
 
+/*
+ * Filesystem-specific page bit testing
+ */
+#define PageFsMisc(page)		test_bit(PG_fs_misc, &(page)->flags)
+#define SetPageFsMisc(page)		set_bit(PG_fs_misc, &(page)->flags)
+#define TestSetPageFsMisc(page)		test_and_set_bit(PG_fs_misc, &(page)->flags)
+#define ClearPageFsMisc(page)		clear_bit(PG_fs_misc, &(page)->flags)
+#define TestClearPageFsMisc(page)	test_and_clear_bit(PG_fs_misc, &(page)->flags)
+
 #endif	/* PAGE_FLAGS_H */
diff -urN oldtree/include/linux/page-flags.h.orig newtree/include/linux/page-flags.h.orig
--- oldtree/include/linux/page-flags.h.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/linux/page-flags.h.orig	2006-02-13 19:20:01.239323896 -0500
@@ -0,0 +1,322 @@
+/*
+ * Macros for manipulating and testing page->flags
+ */
+
+#ifndef PAGE_FLAGS_H
+#define PAGE_FLAGS_H
+
+#include <linux/percpu.h>
+#include <linux/cache.h>
+#include <asm/pgtable.h>
+
+/*
+ * Various page->flags bits:
+ *
+ * PG_reserved is set for special pages, which can never be swapped out. Some
+ * of them might not even exist (eg empty_bad_page)...
+ *
+ * The PG_private bitflag is set if page->private contains a valid value.
+ *
+ * During disk I/O, PG_locked is used. This bit is set before I/O and
+ * reset when I/O completes. page_waitqueue(page) is a wait queue of all tasks
+ * waiting for the I/O on this page to complete.
+ *
+ * PG_uptodate tells whether the page's contents is valid.  When a read
+ * completes, the page becomes uptodate, unless a disk I/O error happened.
+ *
+ * For choosing which pages to swap out, inode pages carry a PG_referenced bit,
+ * which is set any time the system accesses that page through the (mapping,
+ * index) hash table.  This referenced bit, together with the referenced bit
+ * in the page tables, is used to manipulate page->age and move the page across
+ * the active, inactive_dirty and inactive_clean lists.
+ *
+ * Note that the referenced bit, the page->lru list_head and the active,
+ * inactive_dirty and inactive_clean lists are protected by the
+ * zone->lru_lock, and *NOT* by the usual PG_locked bit!
+ *
+ * PG_error is set to indicate that an I/O error occurred on this page.
+ *
+ * PG_arch_1 is an architecture specific page state bit.  The generic code
+ * guarantees that this bit is cleared for a page when it first is entered into
+ * the page cache.
+ *
+ * PG_highmem pages are not permanently mapped into the kernel virtual address
+ * space, they need to be kmapped separately for doing IO on the pages.  The
+ * struct page (these bits with information) are always mapped into kernel
+ * address space...
+ */
+
+/*
+ * Don't use the *_dontuse flags.  Use the macros.  Otherwise you'll break
+ * locked- and dirty-page accounting.  The top eight bits of page->flags are
+ * used for page->zone, so putting flag bits there doesn't work.
+ */
+#define PG_locked	 	 0	/* Page is locked. Don't touch. */
+#define PG_error		 1
+#define PG_referenced		 2
+#define PG_uptodate		 3
+
+#define PG_dirty	 	 4
+#define PG_lru			 5
+#define PG_active		 6
+#define PG_slab			 7	/* slab debug (Suparna wants this) */
+
+#define PG_checked		 8	/* kill me in 2.5.<early>. */
+#define PG_arch_1		 9
+#define PG_reserved		10
+#define PG_private		11	/* Has something at ->private */
+
+#define PG_writeback		12	/* Page is under writeback */
+#define PG_nosave		13	/* Used for system suspend/resume */
+#define PG_compound		14	/* Part of a compound page */
+#define PG_swapcache		15	/* Swap page: swp_entry_t in private */
+
+#define PG_mappedtodisk		16	/* Has blocks allocated on-disk */
+#define PG_reclaim		17	/* To be reclaimed asap */
+#define PG_nosave_free		18	/* Free, should not be written */
+#define PG_uncached		19	/* Page has been mapped as uncached */
+
+/*
+ * Global page accounting.  One instance per CPU.  Only unsigned longs are
+ * allowed.
+ */
+struct page_state {
+	unsigned long nr_dirty;		/* Dirty writeable pages */
+	unsigned long nr_writeback;	/* Pages under writeback */
+	unsigned long nr_unstable;	/* NFS unstable pages */
+	unsigned long nr_page_table_pages;/* Pages used for pagetables */
+	unsigned long nr_mapped;	/* mapped into pagetables */
+	unsigned long nr_slab;		/* In slab */
+#define GET_PAGE_STATE_LAST nr_slab
+
+	/*
+	 * The below are zeroed by get_page_state().  Use get_full_page_state()
+	 * to add up all these.
+	 */
+	unsigned long pgpgin;		/* Disk reads */
+	unsigned long pgpgout;		/* Disk writes */
+	unsigned long pswpin;		/* swap reads */
+	unsigned long pswpout;		/* swap writes */
+	unsigned long pgalloc_high;	/* page allocations */
+
+	unsigned long pgalloc_normal;
+	unsigned long pgalloc_dma;
+	unsigned long pgfree;		/* page freeings */
+	unsigned long pgactivate;	/* pages moved inactive->active */
+	unsigned long pgdeactivate;	/* pages moved active->inactive */
+
+	unsigned long pgfault;		/* faults (major+minor) */
+	unsigned long pgmajfault;	/* faults (major only) */
+	unsigned long pgrefill_high;	/* inspected in refill_inactive_zone */
+	unsigned long pgrefill_normal;
+	unsigned long pgrefill_dma;
+
+	unsigned long pgsteal_high;	/* total highmem pages reclaimed */
+	unsigned long pgsteal_normal;
+	unsigned long pgsteal_dma;
+	unsigned long pgscan_kswapd_high;/* total highmem pages scanned */
+	unsigned long pgscan_kswapd_normal;
+
+	unsigned long pgscan_kswapd_dma;
+	unsigned long pgscan_direct_high;/* total highmem pages scanned */
+	unsigned long pgscan_direct_normal;
+	unsigned long pgscan_direct_dma;
+	unsigned long pginodesteal;	/* pages reclaimed via inode freeing */
+
+	unsigned long slabs_scanned;	/* slab objects scanned */
+	unsigned long kswapd_steal;	/* pages reclaimed by kswapd */
+	unsigned long kswapd_inodesteal;/* reclaimed via kswapd inode freeing */
+	unsigned long pageoutrun;	/* kswapd's calls to page reclaim */
+	unsigned long allocstall;	/* direct reclaim calls */
+
+	unsigned long pgrotated;	/* pages rotated to tail of the LRU */
+	unsigned long nr_bounce;	/* pages for bounce buffers */
+};
+
+extern void get_page_state(struct page_state *ret);
+extern void get_page_state_node(struct page_state *ret, int node);
+extern void get_full_page_state(struct page_state *ret);
+extern unsigned long __read_page_state(unsigned long offset);
+extern void __mod_page_state(unsigned long offset, unsigned long delta);
+
+#define read_page_state(member) \
+	__read_page_state(offsetof(struct page_state, member))
+
+#define mod_page_state(member, delta)	\
+	__mod_page_state(offsetof(struct page_state, member), (delta))
+
+#define inc_page_state(member)	mod_page_state(member, 1UL)
+#define dec_page_state(member)	mod_page_state(member, 0UL - 1)
+#define add_page_state(member,delta) mod_page_state(member, (delta))
+#define sub_page_state(member,delta) mod_page_state(member, 0UL - (delta))
+
+#define mod_page_state_zone(zone, member, delta)				\
+	do {									\
+		unsigned offset;						\
+		if (is_highmem(zone))						\
+			offset = offsetof(struct page_state, member##_high);	\
+		else if (is_normal(zone))					\
+			offset = offsetof(struct page_state, member##_normal);	\
+		else								\
+			offset = offsetof(struct page_state, member##_dma);	\
+		__mod_page_state(offset, (delta));				\
+	} while (0)
+
+/*
+ * Manipulation of page state flags
+ */
+#define PageLocked(page)		\
+		test_bit(PG_locked, &(page)->flags)
+#define SetPageLocked(page)		\
+		set_bit(PG_locked, &(page)->flags)
+#define TestSetPageLocked(page)		\
+		test_and_set_bit(PG_locked, &(page)->flags)
+#define ClearPageLocked(page)		\
+		clear_bit(PG_locked, &(page)->flags)
+#define TestClearPageLocked(page)	\
+		test_and_clear_bit(PG_locked, &(page)->flags)
+
+#define PageError(page)		test_bit(PG_error, &(page)->flags)
+#define SetPageError(page)	set_bit(PG_error, &(page)->flags)
+#define ClearPageError(page)	clear_bit(PG_error, &(page)->flags)
+
+#define PageReferenced(page)	test_bit(PG_referenced, &(page)->flags)
+#define SetPageReferenced(page)	set_bit(PG_referenced, &(page)->flags)
+#define ClearPageReferenced(page)	clear_bit(PG_referenced, &(page)->flags)
+#define TestClearPageReferenced(page) test_and_clear_bit(PG_referenced, &(page)->flags)
+
+#define PageUptodate(page)	test_bit(PG_uptodate, &(page)->flags)
+#ifndef SetPageUptodate
+#define SetPageUptodate(page)	set_bit(PG_uptodate, &(page)->flags)
+#endif
+#define ClearPageUptodate(page)	clear_bit(PG_uptodate, &(page)->flags)
+
+#define PageDirty(page)		test_bit(PG_dirty, &(page)->flags)
+#define SetPageDirty(page)	set_bit(PG_dirty, &(page)->flags)
+#define TestSetPageDirty(page)	test_and_set_bit(PG_dirty, &(page)->flags)
+#define ClearPageDirty(page)	clear_bit(PG_dirty, &(page)->flags)
+#define __ClearPageDirty(page)	__clear_bit(PG_dirty, &(page)->flags)
+#define TestClearPageDirty(page) test_and_clear_bit(PG_dirty, &(page)->flags)
+
+#define SetPageLRU(page)	set_bit(PG_lru, &(page)->flags)
+#define PageLRU(page)		test_bit(PG_lru, &(page)->flags)
+#define TestSetPageLRU(page)	test_and_set_bit(PG_lru, &(page)->flags)
+#define TestClearPageLRU(page)	test_and_clear_bit(PG_lru, &(page)->flags)
+
+#define PageActive(page)	test_bit(PG_active, &(page)->flags)
+#define SetPageActive(page)	set_bit(PG_active, &(page)->flags)
+#define ClearPageActive(page)	clear_bit(PG_active, &(page)->flags)
+#define TestClearPageActive(page) test_and_clear_bit(PG_active, &(page)->flags)
+#define TestSetPageActive(page) test_and_set_bit(PG_active, &(page)->flags)
+
+#define PageSlab(page)		test_bit(PG_slab, &(page)->flags)
+#define SetPageSlab(page)	set_bit(PG_slab, &(page)->flags)
+#define ClearPageSlab(page)	clear_bit(PG_slab, &(page)->flags)
+#define TestClearPageSlab(page)	test_and_clear_bit(PG_slab, &(page)->flags)
+#define TestSetPageSlab(page)	test_and_set_bit(PG_slab, &(page)->flags)
+
+#ifdef CONFIG_HIGHMEM
+#define PageHighMem(page)	is_highmem(page_zone(page))
+#else
+#define PageHighMem(page)	0 /* needed to optimize away at compile time */
+#endif
+
+#define PageChecked(page)	test_bit(PG_checked, &(page)->flags)
+#define SetPageChecked(page)	set_bit(PG_checked, &(page)->flags)
+#define ClearPageChecked(page)	clear_bit(PG_checked, &(page)->flags)
+
+#define PageReserved(page)	test_bit(PG_reserved, &(page)->flags)
+#define SetPageReserved(page)	set_bit(PG_reserved, &(page)->flags)
+#define ClearPageReserved(page)	clear_bit(PG_reserved, &(page)->flags)
+#define __ClearPageReserved(page)	__clear_bit(PG_reserved, &(page)->flags)
+
+#define SetPagePrivate(page)	set_bit(PG_private, &(page)->flags)
+#define ClearPagePrivate(page)	clear_bit(PG_private, &(page)->flags)
+#define PagePrivate(page)	test_bit(PG_private, &(page)->flags)
+#define __SetPagePrivate(page)  __set_bit(PG_private, &(page)->flags)
+#define __ClearPagePrivate(page) __clear_bit(PG_private, &(page)->flags)
+
+#define PageWriteback(page)	test_bit(PG_writeback, &(page)->flags)
+#define SetPageWriteback(page)						\
+	do {								\
+		if (!test_and_set_bit(PG_writeback,			\
+				&(page)->flags))			\
+			inc_page_state(nr_writeback);			\
+	} while (0)
+#define TestSetPageWriteback(page)					\
+	({								\
+		int ret;						\
+		ret = test_and_set_bit(PG_writeback,			\
+					&(page)->flags);		\
+		if (!ret)						\
+			inc_page_state(nr_writeback);			\
+		ret;							\
+	})
+#define ClearPageWriteback(page)					\
+	do {								\
+		if (test_and_clear_bit(PG_writeback,			\
+				&(page)->flags))			\
+			dec_page_state(nr_writeback);			\
+	} while (0)
+#define TestClearPageWriteback(page)					\
+	({								\
+		int ret;						\
+		ret = test_and_clear_bit(PG_writeback,			\
+				&(page)->flags);			\
+		if (ret)						\
+			dec_page_state(nr_writeback);			\
+		ret;							\
+	})
+
+#define PageNosave(page)	test_bit(PG_nosave, &(page)->flags)
+#define SetPageNosave(page)	set_bit(PG_nosave, &(page)->flags)
+#define TestSetPageNosave(page)	test_and_set_bit(PG_nosave, &(page)->flags)
+#define ClearPageNosave(page)		clear_bit(PG_nosave, &(page)->flags)
+#define TestClearPageNosave(page)	test_and_clear_bit(PG_nosave, &(page)->flags)
+
+#define PageNosaveFree(page)	test_bit(PG_nosave_free, &(page)->flags)
+#define SetPageNosaveFree(page)	set_bit(PG_nosave_free, &(page)->flags)
+#define ClearPageNosaveFree(page)		clear_bit(PG_nosave_free, &(page)->flags)
+
+#define PageMappedToDisk(page)	test_bit(PG_mappedtodisk, &(page)->flags)
+#define SetPageMappedToDisk(page) set_bit(PG_mappedtodisk, &(page)->flags)
+#define ClearPageMappedToDisk(page) clear_bit(PG_mappedtodisk, &(page)->flags)
+
+#define PageReclaim(page)	test_bit(PG_reclaim, &(page)->flags)
+#define SetPageReclaim(page)	set_bit(PG_reclaim, &(page)->flags)
+#define ClearPageReclaim(page)	clear_bit(PG_reclaim, &(page)->flags)
+#define TestClearPageReclaim(page) test_and_clear_bit(PG_reclaim, &(page)->flags)
+
+#define PageCompound(page)	test_bit(PG_compound, &(page)->flags)
+#define SetPageCompound(page)	set_bit(PG_compound, &(page)->flags)
+#define ClearPageCompound(page)	clear_bit(PG_compound, &(page)->flags)
+
+#ifdef CONFIG_SWAP
+#define PageSwapCache(page)	test_bit(PG_swapcache, &(page)->flags)
+#define SetPageSwapCache(page)	set_bit(PG_swapcache, &(page)->flags)
+#define ClearPageSwapCache(page) clear_bit(PG_swapcache, &(page)->flags)
+#else
+#define PageSwapCache(page)	0
+#endif
+
+#define PageUncached(page)	test_bit(PG_uncached, &(page)->flags)
+#define SetPageUncached(page)	set_bit(PG_uncached, &(page)->flags)
+#define ClearPageUncached(page)	clear_bit(PG_uncached, &(page)->flags)
+
+struct page;	/* forward declaration */
+
+int test_clear_page_dirty(struct page *page);
+int test_clear_page_writeback(struct page *page);
+int test_set_page_writeback(struct page *page);
+
+static inline void clear_page_dirty(struct page *page)
+{
+	test_clear_page_dirty(page);
+}
+
+static inline void set_page_writeback(struct page *page)
+{
+	test_set_page_writeback(page);
+}
+
+#endif	/* PAGE_FLAGS_H */
diff -urN oldtree/include/linux/pagemap.h newtree/include/linux/pagemap.h
--- oldtree/include/linux/pagemap.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/pagemap.h	2006-02-13 19:20:01.240323744 -0500
@@ -201,6 +201,17 @@
 extern void end_page_writeback(struct page *page);
 
 /*
+ * Wait for filesystem-specific page synchronisation to complete
+ */
+static inline void wait_on_page_fs_misc(struct page *page)
+{
+	if (PageFsMisc(page))
+		wait_on_page_bit(page, PG_fs_misc);
+}
+
+extern void fastcall end_page_fs_misc(struct page *page);
+
+/*
  * Fault a userspace page into pagetables.  Return non-zero on a fault.
  *
  * This assumes that two userspace pages are always sufficient.  That's
diff -urN oldtree/include/linux/pci_ids.h newtree/include/linux/pci_ids.h
--- oldtree/include/linux/pci_ids.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/pci_ids.h	2006-02-13 19:20:01.242323440 -0500
@@ -623,6 +623,7 @@
 #define PCI_DEVICE_ID_SI_965		0x0965
 #define PCI_DEVICE_ID_SI_5511		0x5511
 #define PCI_DEVICE_ID_SI_5513		0x5513
+#define PCI_DEVICE_ID_SI_5517		0x5517
 #define PCI_DEVICE_ID_SI_5518		0x5518
 #define PCI_DEVICE_ID_SI_5571		0x5571
 #define PCI_DEVICE_ID_SI_5581		0x5581
diff -urN oldtree/include/linux/pci_ids.h.orig newtree/include/linux/pci_ids.h.orig
--- oldtree/include/linux/pci_ids.h.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/linux/pci_ids.h.orig	2006-02-13 19:20:01.245322984 -0500
@@ -0,0 +1,2194 @@
+/*
+ *	PCI Class, Vendor and Device IDs
+ *
+ *	Please keep sorted.
+ */
+
+/* Device classes and subclasses */
+
+#define PCI_CLASS_NOT_DEFINED		0x0000
+#define PCI_CLASS_NOT_DEFINED_VGA	0x0001
+
+#define PCI_BASE_CLASS_STORAGE		0x01
+#define PCI_CLASS_STORAGE_SCSI		0x0100
+#define PCI_CLASS_STORAGE_IDE		0x0101
+#define PCI_CLASS_STORAGE_FLOPPY	0x0102
+#define PCI_CLASS_STORAGE_IPI		0x0103
+#define PCI_CLASS_STORAGE_RAID		0x0104
+#define PCI_CLASS_STORAGE_OTHER		0x0180
+
+#define PCI_BASE_CLASS_NETWORK		0x02
+#define PCI_CLASS_NETWORK_ETHERNET	0x0200
+#define PCI_CLASS_NETWORK_TOKEN_RING	0x0201
+#define PCI_CLASS_NETWORK_FDDI		0x0202
+#define PCI_CLASS_NETWORK_ATM		0x0203
+#define PCI_CLASS_NETWORK_OTHER		0x0280
+
+#define PCI_BASE_CLASS_DISPLAY		0x03
+#define PCI_CLASS_DISPLAY_VGA		0x0300
+#define PCI_CLASS_DISPLAY_XGA		0x0301
+#define PCI_CLASS_DISPLAY_3D		0x0302
+#define PCI_CLASS_DISPLAY_OTHER		0x0380
+
+#define PCI_BASE_CLASS_MULTIMEDIA	0x04
+#define PCI_CLASS_MULTIMEDIA_VIDEO	0x0400
+#define PCI_CLASS_MULTIMEDIA_AUDIO	0x0401
+#define PCI_CLASS_MULTIMEDIA_PHONE	0x0402
+#define PCI_CLASS_MULTIMEDIA_OTHER	0x0480
+
+#define PCI_BASE_CLASS_MEMORY		0x05
+#define PCI_CLASS_MEMORY_RAM		0x0500
+#define PCI_CLASS_MEMORY_FLASH		0x0501
+#define PCI_CLASS_MEMORY_OTHER		0x0580
+
+#define PCI_BASE_CLASS_BRIDGE		0x06
+#define PCI_CLASS_BRIDGE_HOST		0x0600
+#define PCI_CLASS_BRIDGE_ISA		0x0601
+#define PCI_CLASS_BRIDGE_EISA		0x0602
+#define PCI_CLASS_BRIDGE_MC		0x0603
+#define PCI_CLASS_BRIDGE_PCI		0x0604
+#define PCI_CLASS_BRIDGE_PCMCIA		0x0605
+#define PCI_CLASS_BRIDGE_NUBUS		0x0606
+#define PCI_CLASS_BRIDGE_CARDBUS	0x0607
+#define PCI_CLASS_BRIDGE_RACEWAY	0x0608
+#define PCI_CLASS_BRIDGE_OTHER		0x0680
+
+#define PCI_BASE_CLASS_COMMUNICATION	0x07
+#define PCI_CLASS_COMMUNICATION_SERIAL	0x0700
+#define PCI_CLASS_COMMUNICATION_PARALLEL 0x0701
+#define PCI_CLASS_COMMUNICATION_MULTISERIAL 0x0702
+#define PCI_CLASS_COMMUNICATION_MODEM	0x0703
+#define PCI_CLASS_COMMUNICATION_OTHER	0x0780
+
+#define PCI_BASE_CLASS_SYSTEM		0x08
+#define PCI_CLASS_SYSTEM_PIC		0x0800
+#define PCI_CLASS_SYSTEM_PIC_IOAPIC	0x080010
+#define PCI_CLASS_SYSTEM_PIC_IOXAPIC	0x080020
+#define PCI_CLASS_SYSTEM_DMA		0x0801
+#define PCI_CLASS_SYSTEM_TIMER		0x0802
+#define PCI_CLASS_SYSTEM_RTC		0x0803
+#define PCI_CLASS_SYSTEM_PCI_HOTPLUG	0x0804
+#define PCI_CLASS_SYSTEM_OTHER		0x0880
+
+#define PCI_BASE_CLASS_INPUT		0x09
+#define PCI_CLASS_INPUT_KEYBOARD	0x0900
+#define PCI_CLASS_INPUT_PEN		0x0901
+#define PCI_CLASS_INPUT_MOUSE		0x0902
+#define PCI_CLASS_INPUT_SCANNER		0x0903
+#define PCI_CLASS_INPUT_GAMEPORT	0x0904
+#define PCI_CLASS_INPUT_OTHER		0x0980
+
+#define PCI_BASE_CLASS_DOCKING		0x0a
+#define PCI_CLASS_DOCKING_GENERIC	0x0a00
+#define PCI_CLASS_DOCKING_OTHER		0x0a80
+
+#define PCI_BASE_CLASS_PROCESSOR	0x0b
+#define PCI_CLASS_PROCESSOR_386		0x0b00
+#define PCI_CLASS_PROCESSOR_486		0x0b01
+#define PCI_CLASS_PROCESSOR_PENTIUM	0x0b02
+#define PCI_CLASS_PROCESSOR_ALPHA	0x0b10
+#define PCI_CLASS_PROCESSOR_POWERPC	0x0b20
+#define PCI_CLASS_PROCESSOR_MIPS	0x0b30
+#define PCI_CLASS_PROCESSOR_CO		0x0b40
+
+#define PCI_BASE_CLASS_SERIAL		0x0c
+#define PCI_CLASS_SERIAL_FIREWIRE	0x0c00
+#define PCI_CLASS_SERIAL_ACCESS		0x0c01
+#define PCI_CLASS_SERIAL_SSA		0x0c02
+#define PCI_CLASS_SERIAL_USB		0x0c03
+#define PCI_CLASS_SERIAL_USB_UHCI	0x0c0300
+#define PCI_CLASS_SERIAL_USB_OHCI	0x0c0310
+#define PCI_CLASS_SERIAL_USB_EHCI	0x0c0320
+#define PCI_CLASS_SERIAL_FIBER		0x0c04
+#define PCI_CLASS_SERIAL_SMBUS		0x0c05
+
+#define PCI_BASE_CLASS_INTELLIGENT	0x0e
+#define PCI_CLASS_INTELLIGENT_I2O	0x0e00
+
+#define PCI_BASE_CLASS_SATELLITE	0x0f
+#define PCI_CLASS_SATELLITE_TV		0x0f00
+#define PCI_CLASS_SATELLITE_AUDIO	0x0f01
+#define PCI_CLASS_SATELLITE_VOICE	0x0f03
+#define PCI_CLASS_SATELLITE_DATA	0x0f04
+
+#define PCI_BASE_CLASS_CRYPT		0x10
+#define PCI_CLASS_CRYPT_NETWORK		0x1000
+#define PCI_CLASS_CRYPT_ENTERTAINMENT	0x1001
+#define PCI_CLASS_CRYPT_OTHER		0x1080
+
+#define PCI_BASE_CLASS_SIGNAL_PROCESSING 0x11
+#define PCI_CLASS_SP_DPIO		0x1100
+#define PCI_CLASS_SP_OTHER		0x1180
+
+#define PCI_CLASS_OTHERS		0xff
+
+/* Vendors and devices.  Sort key: vendor first, device next. */
+
+#define PCI_VENDOR_ID_DYNALINK		0x0675
+#define PCI_DEVICE_ID_DYNALINK_IS64PH	0x1702
+
+#define PCI_VENDOR_ID_BERKOM			0x0871
+#define PCI_DEVICE_ID_BERKOM_A1T		0xffa1
+#define PCI_DEVICE_ID_BERKOM_T_CONCEPT		0xffa2
+#define PCI_DEVICE_ID_BERKOM_A4T		0xffa4
+#define PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO	0xffa8
+
+#define PCI_VENDOR_ID_COMPAQ		0x0e11
+#define PCI_DEVICE_ID_COMPAQ_TOKENRING	0x0508
+#define PCI_DEVICE_ID_COMPAQ_TACHYON	0xa0fc
+#define PCI_DEVICE_ID_COMPAQ_SMART2P	0xae10
+#define PCI_DEVICE_ID_COMPAQ_NETEL100	0xae32
+#define PCI_DEVICE_ID_COMPAQ_NETEL10	0xae34
+#define PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE 0xae33
+#define PCI_DEVICE_ID_COMPAQ_NETFLEX3I	0xae35
+#define PCI_DEVICE_ID_COMPAQ_NETEL100D	0xae40
+#define PCI_DEVICE_ID_COMPAQ_NETEL100PI	0xae43
+#define PCI_DEVICE_ID_COMPAQ_NETEL100I	0xb011
+#define PCI_DEVICE_ID_COMPAQ_CISS	0xb060
+#define PCI_DEVICE_ID_COMPAQ_CISSB	0xb178
+#define PCI_DEVICE_ID_COMPAQ_CISSC	0x46
+#define PCI_DEVICE_ID_COMPAQ_THUNDER	0xf130
+#define PCI_DEVICE_ID_COMPAQ_NETFLEX3B	0xf150
+
+#define PCI_VENDOR_ID_NCR		0x1000
+#define PCI_VENDOR_ID_LSI_LOGIC		0x1000
+#define PCI_DEVICE_ID_NCR_53C810	0x0001
+#define PCI_DEVICE_ID_NCR_53C820	0x0002
+#define PCI_DEVICE_ID_NCR_53C825	0x0003
+#define PCI_DEVICE_ID_NCR_53C815	0x0004
+#define PCI_DEVICE_ID_LSI_53C810AP	0x0005
+#define PCI_DEVICE_ID_NCR_53C860	0x0006
+#define PCI_DEVICE_ID_LSI_53C1510	0x000a
+#define PCI_DEVICE_ID_NCR_53C896	0x000b
+#define PCI_DEVICE_ID_NCR_53C895	0x000c
+#define PCI_DEVICE_ID_NCR_53C885	0x000d
+#define PCI_DEVICE_ID_NCR_53C875	0x000f
+#define PCI_DEVICE_ID_NCR_53C1510	0x0010
+#define PCI_DEVICE_ID_LSI_53C895A	0x0012
+#define PCI_DEVICE_ID_LSI_53C875A	0x0013
+#define PCI_DEVICE_ID_LSI_53C1010_33	0x0020
+#define PCI_DEVICE_ID_LSI_53C1010_66	0x0021
+#define PCI_DEVICE_ID_LSI_53C1030	0x0030
+#define PCI_DEVICE_ID_LSI_1030_53C1035	0x0032
+#define PCI_DEVICE_ID_LSI_53C1035	0x0040
+#define PCI_DEVICE_ID_NCR_53C875J	0x008f
+#define PCI_DEVICE_ID_LSI_FC909		0x0621
+#define PCI_DEVICE_ID_LSI_FC929		0x0622
+#define PCI_DEVICE_ID_LSI_FC929_LAN	0x0623
+#define PCI_DEVICE_ID_LSI_FC919		0x0624
+#define PCI_DEVICE_ID_LSI_FC919_LAN	0x0625
+#define PCI_DEVICE_ID_LSI_FC929X	0x0626
+#define PCI_DEVICE_ID_LSI_FC939X	0x0642
+#define PCI_DEVICE_ID_LSI_FC949X	0x0640
+#define PCI_DEVICE_ID_LSI_FC919X	0x0628
+#define PCI_DEVICE_ID_NCR_YELLOWFIN	0x0701
+#define PCI_DEVICE_ID_LSI_61C102	0x0901
+#define PCI_DEVICE_ID_LSI_63C815	0x1000
+#define PCI_DEVICE_ID_LSI_SAS1064	0x0050
+#define PCI_DEVICE_ID_LSI_SAS1064R	0x0411
+#define PCI_DEVICE_ID_LSI_SAS1066	0x005E
+#define PCI_DEVICE_ID_LSI_SAS1068	0x0054
+#define PCI_DEVICE_ID_LSI_SAS1064A	0x005C
+#define PCI_DEVICE_ID_LSI_SAS1064E	0x0056
+#define PCI_DEVICE_ID_LSI_SAS1066E	0x005A
+#define PCI_DEVICE_ID_LSI_SAS1068E	0x0058
+#define PCI_DEVICE_ID_LSI_SAS1078	0x0060
+
+#define PCI_VENDOR_ID_ATI		0x1002
+/* Mach64 */
+#define PCI_DEVICE_ID_ATI_68800		0x4158
+#define PCI_DEVICE_ID_ATI_215CT222	0x4354
+#define PCI_DEVICE_ID_ATI_210888CX	0x4358
+#define PCI_DEVICE_ID_ATI_215ET222	0x4554
+/* Mach64 / Rage */
+#define PCI_DEVICE_ID_ATI_215GB		0x4742
+#define PCI_DEVICE_ID_ATI_215GD		0x4744
+#define PCI_DEVICE_ID_ATI_215GI		0x4749
+#define PCI_DEVICE_ID_ATI_215GP		0x4750
+#define PCI_DEVICE_ID_ATI_215GQ		0x4751
+#define PCI_DEVICE_ID_ATI_215XL		0x4752
+#define PCI_DEVICE_ID_ATI_215GT		0x4754
+#define PCI_DEVICE_ID_ATI_215GTB	0x4755
+#define PCI_DEVICE_ID_ATI_215_IV	0x4756
+#define PCI_DEVICE_ID_ATI_215_IW	0x4757
+#define PCI_DEVICE_ID_ATI_215_IZ	0x475A
+#define PCI_DEVICE_ID_ATI_210888GX	0x4758
+#define PCI_DEVICE_ID_ATI_215_LB	0x4c42
+#define PCI_DEVICE_ID_ATI_215_LD	0x4c44
+#define PCI_DEVICE_ID_ATI_215_LG	0x4c47
+#define PCI_DEVICE_ID_ATI_215_LI	0x4c49
+#define PCI_DEVICE_ID_ATI_215_LM	0x4c4D
+#define PCI_DEVICE_ID_ATI_215_LN	0x4c4E
+#define PCI_DEVICE_ID_ATI_215_LR	0x4c52
+#define PCI_DEVICE_ID_ATI_215_LS	0x4c53
+#define PCI_DEVICE_ID_ATI_264_LT	0x4c54
+/* Mach64 VT */
+#define PCI_DEVICE_ID_ATI_264VT		0x5654
+#define PCI_DEVICE_ID_ATI_264VU		0x5655
+#define PCI_DEVICE_ID_ATI_264VV		0x5656
+/* Rage128 GL */
+#define PCI_DEVICE_ID_ATI_RAGE128_RE	0x5245
+#define PCI_DEVICE_ID_ATI_RAGE128_RF	0x5246
+#define PCI_DEVICE_ID_ATI_RAGE128_RG	0x5247
+/* Rage128 VR */
+#define PCI_DEVICE_ID_ATI_RAGE128_RK	0x524b
+#define PCI_DEVICE_ID_ATI_RAGE128_RL	0x524c
+#define PCI_DEVICE_ID_ATI_RAGE128_SE	0x5345
+#define PCI_DEVICE_ID_ATI_RAGE128_SF	0x5346
+#define PCI_DEVICE_ID_ATI_RAGE128_SG	0x5347
+#define PCI_DEVICE_ID_ATI_RAGE128_SH	0x5348
+#define PCI_DEVICE_ID_ATI_RAGE128_SK	0x534b
+#define PCI_DEVICE_ID_ATI_RAGE128_SL	0x534c
+#define PCI_DEVICE_ID_ATI_RAGE128_SM	0x534d
+#define PCI_DEVICE_ID_ATI_RAGE128_SN	0x534e
+/* Rage128 Ultra */
+#define PCI_DEVICE_ID_ATI_RAGE128_TF	0x5446
+#define PCI_DEVICE_ID_ATI_RAGE128_TL	0x544c
+#define PCI_DEVICE_ID_ATI_RAGE128_TR	0x5452
+#define PCI_DEVICE_ID_ATI_RAGE128_TS	0x5453
+#define PCI_DEVICE_ID_ATI_RAGE128_TT	0x5454
+#define PCI_DEVICE_ID_ATI_RAGE128_TU	0x5455
+/* Rage128 M3 */
+#define PCI_DEVICE_ID_ATI_RAGE128_LE	0x4c45
+#define PCI_DEVICE_ID_ATI_RAGE128_LF	0x4c46
+/* Rage128 M4 */
+#define PCI_DEVICE_ID_ATI_RAGE128_MF    0x4d46
+#define PCI_DEVICE_ID_ATI_RAGE128_ML    0x4d4c
+/* Rage128 Pro GL */
+#define PCI_DEVICE_ID_ATI_RAGE128_PA	0x5041
+#define PCI_DEVICE_ID_ATI_RAGE128_PB	0x5042
+#define PCI_DEVICE_ID_ATI_RAGE128_PC	0x5043
+#define PCI_DEVICE_ID_ATI_RAGE128_PD	0x5044
+#define PCI_DEVICE_ID_ATI_RAGE128_PE	0x5045
+#define PCI_DEVICE_ID_ATI_RAGE128_PF	0x5046
+/* Rage128 Pro VR */
+#define PCI_DEVICE_ID_ATI_RAGE128_PG	0x5047
+#define PCI_DEVICE_ID_ATI_RAGE128_PH	0x5048
+#define PCI_DEVICE_ID_ATI_RAGE128_PI	0x5049
+#define PCI_DEVICE_ID_ATI_RAGE128_PJ	0x504A
+#define PCI_DEVICE_ID_ATI_RAGE128_PK	0x504B
+#define PCI_DEVICE_ID_ATI_RAGE128_PL	0x504C
+#define PCI_DEVICE_ID_ATI_RAGE128_PM	0x504D
+#define PCI_DEVICE_ID_ATI_RAGE128_PN	0x504E
+#define PCI_DEVICE_ID_ATI_RAGE128_PO	0x504F
+#define PCI_DEVICE_ID_ATI_RAGE128_PP	0x5050
+#define PCI_DEVICE_ID_ATI_RAGE128_PQ	0x5051
+#define PCI_DEVICE_ID_ATI_RAGE128_PR	0x5052
+#define PCI_DEVICE_ID_ATI_RAGE128_PS	0x5053
+#define PCI_DEVICE_ID_ATI_RAGE128_PT	0x5054
+#define PCI_DEVICE_ID_ATI_RAGE128_PU	0x5055
+#define PCI_DEVICE_ID_ATI_RAGE128_PV	0x5056
+#define PCI_DEVICE_ID_ATI_RAGE128_PW	0x5057
+#define PCI_DEVICE_ID_ATI_RAGE128_PX	0x5058
+/* Rage128 M4 */
+/* Radeon R100 */
+#define PCI_DEVICE_ID_ATI_RADEON_QD	0x5144
+#define PCI_DEVICE_ID_ATI_RADEON_QE	0x5145
+#define PCI_DEVICE_ID_ATI_RADEON_QF	0x5146
+#define PCI_DEVICE_ID_ATI_RADEON_QG	0x5147
+/* Radeon RV100 (VE) */
+#define PCI_DEVICE_ID_ATI_RADEON_QY	0x5159
+#define PCI_DEVICE_ID_ATI_RADEON_QZ	0x515a
+/* Radeon R200 (8500) */
+#define PCI_DEVICE_ID_ATI_RADEON_QL	0x514c
+#define PCI_DEVICE_ID_ATI_RADEON_QN	0x514e
+#define PCI_DEVICE_ID_ATI_RADEON_QO	0x514f
+#define PCI_DEVICE_ID_ATI_RADEON_Ql	0x516c
+#define PCI_DEVICE_ID_ATI_RADEON_BB	0x4242
+/* Radeon R200 (9100) */
+#define PCI_DEVICE_ID_ATI_RADEON_QM	0x514d
+/* Radeon RV200 (7500) */
+#define PCI_DEVICE_ID_ATI_RADEON_QW	0x5157
+#define PCI_DEVICE_ID_ATI_RADEON_QX	0x5158
+/* Radeon NV-100 */
+/* Radeon RV250 (9000) */
+#define PCI_DEVICE_ID_ATI_RADEON_Id	0x4964
+#define PCI_DEVICE_ID_ATI_RADEON_Ie	0x4965
+#define PCI_DEVICE_ID_ATI_RADEON_If	0x4966
+#define PCI_DEVICE_ID_ATI_RADEON_Ig	0x4967
+/* Radeon RV280 (9200) */
+#define PCI_DEVICE_ID_ATI_RADEON_Ya	0x5961
+#define PCI_DEVICE_ID_ATI_RADEON_Yd	0x5964
+/* Radeon R300 (9500) */
+/* Radeon R300 (9700) */
+#define PCI_DEVICE_ID_ATI_RADEON_ND	0x4e44
+#define PCI_DEVICE_ID_ATI_RADEON_NE	0x4e45
+#define PCI_DEVICE_ID_ATI_RADEON_NF	0x4e46
+#define PCI_DEVICE_ID_ATI_RADEON_NG	0x4e47
+/* Radeon R350 (9800) */
+/* Radeon RV350 (9600) */
+/* Radeon M6 */
+#define PCI_DEVICE_ID_ATI_RADEON_LY	0x4c59
+#define PCI_DEVICE_ID_ATI_RADEON_LZ	0x4c5a
+/* Radeon M7 */
+#define PCI_DEVICE_ID_ATI_RADEON_LW	0x4c57
+#define PCI_DEVICE_ID_ATI_RADEON_LX	0x4c58
+/* Radeon M9 */
+#define PCI_DEVICE_ID_ATI_RADEON_Ld	0x4c64
+#define PCI_DEVICE_ID_ATI_RADEON_Le	0x4c65
+#define PCI_DEVICE_ID_ATI_RADEON_Lf	0x4c66
+#define PCI_DEVICE_ID_ATI_RADEON_Lg	0x4c67
+/* Radeon */
+/* RadeonIGP */
+#define PCI_DEVICE_ID_ATI_RS100		0xcab0
+#define PCI_DEVICE_ID_ATI_RS200		0xcab2
+#define PCI_DEVICE_ID_ATI_RS200_B	0xcbb2
+#define PCI_DEVICE_ID_ATI_RS250		0xcab3
+#define PCI_DEVICE_ID_ATI_RS300_100	0x5830
+#define PCI_DEVICE_ID_ATI_RS300_133	0x5831
+#define PCI_DEVICE_ID_ATI_RS300_166	0x5832
+#define PCI_DEVICE_ID_ATI_RS300_200	0x5833
+#define PCI_DEVICE_ID_ATI_RS350_100     0x7830
+#define PCI_DEVICE_ID_ATI_RS350_133     0x7831
+#define PCI_DEVICE_ID_ATI_RS350_166     0x7832
+#define PCI_DEVICE_ID_ATI_RS350_200     0x7833
+#define PCI_DEVICE_ID_ATI_RS400_100     0x5a30
+#define PCI_DEVICE_ID_ATI_RS400_133     0x5a31
+#define PCI_DEVICE_ID_ATI_RS400_166     0x5a32
+#define PCI_DEVICE_ID_ATI_RS400_200     0x5a33
+#define PCI_DEVICE_ID_ATI_RS480         0x5950
+/* ATI IXP Chipset */
+#define PCI_DEVICE_ID_ATI_IXP200_IDE	0x4349
+#define PCI_DEVICE_ID_ATI_IXP300_IDE	0x4369
+#define PCI_DEVICE_ID_ATI_IXP300_SATA   0x436e
+#define PCI_DEVICE_ID_ATI_IXP400_IDE	0x4376
+#define PCI_DEVICE_ID_ATI_IXP400_SATA   0x4379
+
+#define PCI_VENDOR_ID_VLSI		0x1004
+#define PCI_DEVICE_ID_VLSI_82C592	0x0005
+#define PCI_DEVICE_ID_VLSI_82C593	0x0006
+#define PCI_DEVICE_ID_VLSI_82C594	0x0007
+#define PCI_DEVICE_ID_VLSI_82C597	0x0009
+#define PCI_DEVICE_ID_VLSI_82C541	0x000c
+#define PCI_DEVICE_ID_VLSI_82C543	0x000d
+#define PCI_DEVICE_ID_VLSI_82C532	0x0101
+#define PCI_DEVICE_ID_VLSI_82C534	0x0102
+#define PCI_DEVICE_ID_VLSI_82C535	0x0104
+#define PCI_DEVICE_ID_VLSI_82C147	0x0105
+#define PCI_DEVICE_ID_VLSI_VAS96011	0x0702
+
+#define PCI_VENDOR_ID_ADL		0x1005
+#define PCI_DEVICE_ID_ADL_2301		0x2301
+
+#define PCI_VENDOR_ID_NS		0x100b
+#define PCI_DEVICE_ID_NS_87415		0x0002
+#define PCI_DEVICE_ID_NS_87560_LIO	0x000e
+#define PCI_DEVICE_ID_NS_87560_USB	0x0012
+#define PCI_DEVICE_ID_NS_83815		0x0020
+#define PCI_DEVICE_ID_NS_83820		0x0022
+#define PCI_DEVICE_ID_NS_SATURN		0x0035
+#define PCI_DEVICE_ID_NS_SCx200_BRIDGE	0x0500
+#define PCI_DEVICE_ID_NS_SCx200_SMI	0x0501
+#define PCI_DEVICE_ID_NS_SCx200_IDE	0x0502
+#define PCI_DEVICE_ID_NS_SCx200_AUDIO	0x0503
+#define PCI_DEVICE_ID_NS_SCx200_VIDEO	0x0504
+#define PCI_DEVICE_ID_NS_SCx200_XBUS	0x0505
+#define PCI_DEVICE_ID_NS_SC1100_BRIDGE	0x0510
+#define PCI_DEVICE_ID_NS_SC1100_SMI	0x0511
+#define PCI_DEVICE_ID_NS_SC1100_XBUS	0x0515
+#define PCI_DEVICE_ID_NS_87410		0xd001
+#define PCI_DEVICE_ID_NS_CS5535_IDE	0x002d
+
+#define PCI_VENDOR_ID_TSENG		0x100c
+#define PCI_DEVICE_ID_TSENG_W32P_2	0x3202
+#define PCI_DEVICE_ID_TSENG_W32P_b	0x3205
+#define PCI_DEVICE_ID_TSENG_W32P_c	0x3206
+#define PCI_DEVICE_ID_TSENG_W32P_d	0x3207
+#define PCI_DEVICE_ID_TSENG_ET6000	0x3208
+
+#define PCI_VENDOR_ID_WEITEK		0x100e
+#define PCI_DEVICE_ID_WEITEK_P9000	0x9001
+#define PCI_DEVICE_ID_WEITEK_P9100	0x9100
+
+#define PCI_VENDOR_ID_DEC		0x1011
+#define PCI_DEVICE_ID_DEC_BRD		0x0001
+#define PCI_DEVICE_ID_DEC_TULIP		0x0002
+#define PCI_DEVICE_ID_DEC_TGA		0x0004
+#define PCI_DEVICE_ID_DEC_TULIP_FAST	0x0009
+#define PCI_DEVICE_ID_DEC_TGA2		0x000D
+#define PCI_DEVICE_ID_DEC_FDDI		0x000F
+#define PCI_DEVICE_ID_DEC_TULIP_PLUS	0x0014
+#define PCI_DEVICE_ID_DEC_21142		0x0019
+#define PCI_DEVICE_ID_DEC_21052		0x0021
+#define PCI_DEVICE_ID_DEC_21150		0x0022
+#define PCI_DEVICE_ID_DEC_21152		0x0024
+#define PCI_DEVICE_ID_DEC_21153		0x0025
+#define PCI_DEVICE_ID_DEC_21154		0x0026
+#define PCI_DEVICE_ID_DEC_21285		0x1065
+#define PCI_DEVICE_ID_COMPAQ_42XX	0x0046
+
+#define PCI_VENDOR_ID_CIRRUS		0x1013
+#define PCI_DEVICE_ID_CIRRUS_7548	0x0038
+#define PCI_DEVICE_ID_CIRRUS_5430	0x00a0
+#define PCI_DEVICE_ID_CIRRUS_5434_4	0x00a4
+#define PCI_DEVICE_ID_CIRRUS_5434_8	0x00a8
+#define PCI_DEVICE_ID_CIRRUS_5436	0x00ac
+#define PCI_DEVICE_ID_CIRRUS_5446	0x00b8
+#define PCI_DEVICE_ID_CIRRUS_5480	0x00bc
+#define PCI_DEVICE_ID_CIRRUS_5462	0x00d0
+#define PCI_DEVICE_ID_CIRRUS_5464	0x00d4
+#define PCI_DEVICE_ID_CIRRUS_5465	0x00d6
+#define PCI_DEVICE_ID_CIRRUS_6729	0x1100
+#define PCI_DEVICE_ID_CIRRUS_6832	0x1110
+#define PCI_DEVICE_ID_CIRRUS_7543	0x1202
+#define PCI_DEVICE_ID_CIRRUS_4610	0x6001
+#define PCI_DEVICE_ID_CIRRUS_4612	0x6003
+#define PCI_DEVICE_ID_CIRRUS_4615	0x6004
+
+#define PCI_VENDOR_ID_IBM		0x1014
+#define PCI_DEVICE_ID_IBM_TR		0x0018
+#define PCI_DEVICE_ID_IBM_TR_WAKE	0x003e
+#define PCI_DEVICE_ID_IBM_CPC710_PCI64	0x00fc
+#define PCI_DEVICE_ID_IBM_SNIPE		0x0180
+#define PCI_DEVICE_ID_IBM_CITRINE		0x028C
+#define PCI_DEVICE_ID_IBM_GEMSTONE		0xB166
+#define PCI_DEVICE_ID_IBM_OBSIDIAN		0x02BD
+#define PCI_DEVICE_ID_IBM_ICOM_DEV_ID_1	0x0031
+#define PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2	0x0219
+#define PCI_DEVICE_ID_IBM_ICOM_V2_TWO_PORTS_RVX		0x021A
+#define PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM	0x0251
+#define PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL	0x252
+
+#define PCI_VENDOR_ID_COMPEX2		0x101a /* pci.ids says "AT&T GIS (NCR)" */
+#define PCI_DEVICE_ID_COMPEX2_100VG	0x0005
+
+#define PCI_VENDOR_ID_WD		0x101c
+#define PCI_DEVICE_ID_WD_90C		0xc24a
+
+#define PCI_VENDOR_ID_AMI		0x101e
+#define PCI_DEVICE_ID_AMI_MEGARAID3	0x1960
+#define PCI_DEVICE_ID_AMI_MEGARAID	0x9010
+#define PCI_DEVICE_ID_AMI_MEGARAID2	0x9060
+
+#define PCI_VENDOR_ID_AMD		0x1022
+#define PCI_DEVICE_ID_AMD_K8_NB		0x1100
+#define PCI_DEVICE_ID_AMD_LANCE		0x2000
+#define PCI_DEVICE_ID_AMD_LANCE_HOME	0x2001
+#define PCI_DEVICE_ID_AMD_SCSI		0x2020
+#define PCI_DEVICE_ID_AMD_SERENADE	0x36c0
+#define PCI_DEVICE_ID_AMD_FE_GATE_7006	0x7006
+#define PCI_DEVICE_ID_AMD_FE_GATE_7007	0x7007
+#define PCI_DEVICE_ID_AMD_FE_GATE_700C	0x700C
+#define PCI_DEVICE_ID_AMD_FE_GATE_700E	0x700E
+#define PCI_DEVICE_ID_AMD_COBRA_7401	0x7401
+#define PCI_DEVICE_ID_AMD_VIPER_7409	0x7409
+#define PCI_DEVICE_ID_AMD_VIPER_740B	0x740B
+#define PCI_DEVICE_ID_AMD_VIPER_7410	0x7410
+#define PCI_DEVICE_ID_AMD_VIPER_7411	0x7411
+#define PCI_DEVICE_ID_AMD_VIPER_7413	0x7413
+#define PCI_DEVICE_ID_AMD_VIPER_7440	0x7440
+#define PCI_DEVICE_ID_AMD_OPUS_7441	0x7441
+#define PCI_DEVICE_ID_AMD_OPUS_7443	0x7443
+#define PCI_DEVICE_ID_AMD_VIPER_7443	0x7443
+#define PCI_DEVICE_ID_AMD_OPUS_7445	0x7445
+#define PCI_DEVICE_ID_AMD_8111_LPC	0x7468
+#define PCI_DEVICE_ID_AMD_8111_IDE	0x7469
+#define PCI_DEVICE_ID_AMD_8111_SMBUS2	0x746a
+#define PCI_DEVICE_ID_AMD_8111_SMBUS	0x746b
+#define PCI_DEVICE_ID_AMD_8111_AUDIO	0x746d
+#define PCI_DEVICE_ID_AMD_8151_0	0x7454
+#define PCI_DEVICE_ID_AMD_8131_APIC     0x7450
+
+#define PCI_DEVICE_ID_AMD_CS5536_IDE	0x209A
+
+#define PCI_VENDOR_ID_TRIDENT		0x1023
+#define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX	0x2000
+#define PCI_DEVICE_ID_TRIDENT_4DWAVE_NX	0x2001
+#define PCI_DEVICE_ID_TRIDENT_9320	0x9320
+#define PCI_DEVICE_ID_TRIDENT_9388	0x9388
+#define PCI_DEVICE_ID_TRIDENT_9397	0x9397
+#define PCI_DEVICE_ID_TRIDENT_939A	0x939A
+#define PCI_DEVICE_ID_TRIDENT_9520	0x9520
+#define PCI_DEVICE_ID_TRIDENT_9525	0x9525
+#define PCI_DEVICE_ID_TRIDENT_9420	0x9420
+#define PCI_DEVICE_ID_TRIDENT_9440	0x9440
+#define PCI_DEVICE_ID_TRIDENT_9660	0x9660
+#define PCI_DEVICE_ID_TRIDENT_9750	0x9750
+#define PCI_DEVICE_ID_TRIDENT_9850	0x9850
+#define PCI_DEVICE_ID_TRIDENT_9880	0x9880
+#define PCI_DEVICE_ID_TRIDENT_8400	0x8400
+#define PCI_DEVICE_ID_TRIDENT_8420	0x8420
+#define PCI_DEVICE_ID_TRIDENT_8500	0x8500
+
+#define PCI_VENDOR_ID_AI		0x1025
+#define PCI_DEVICE_ID_AI_M1435		0x1435
+
+#define PCI_VENDOR_ID_DELL		0x1028
+#define PCI_DEVICE_ID_DELL_RACIII	0x0008
+#define PCI_DEVICE_ID_DELL_RAC4		0x0012
+#define PCI_DEVICE_ID_DELL_PERC5	0x0015
+
+#define PCI_VENDOR_ID_MATROX		0x102B
+#define PCI_DEVICE_ID_MATROX_MGA_2	0x0518
+#define PCI_DEVICE_ID_MATROX_MIL	0x0519
+#define PCI_DEVICE_ID_MATROX_MYS	0x051A
+#define PCI_DEVICE_ID_MATROX_MIL_2	0x051b
+#define PCI_DEVICE_ID_MATROX_MYS_AGP	0x051e
+#define PCI_DEVICE_ID_MATROX_MIL_2_AGP	0x051f
+#define PCI_DEVICE_ID_MATROX_MGA_IMP	0x0d10
+#define PCI_DEVICE_ID_MATROX_G100_MM	0x1000
+#define PCI_DEVICE_ID_MATROX_G100_AGP	0x1001
+#define PCI_DEVICE_ID_MATROX_G200_PCI	0x0520
+#define PCI_DEVICE_ID_MATROX_G200_AGP	0x0521
+#define	PCI_DEVICE_ID_MATROX_G400	0x0525
+#define PCI_DEVICE_ID_MATROX_G550	0x2527
+#define PCI_DEVICE_ID_MATROX_VIA	0x4536
+
+#define PCI_VENDOR_ID_CT		0x102c
+#define PCI_DEVICE_ID_CT_69000		0x00c0
+#define PCI_DEVICE_ID_CT_65545		0x00d8
+#define PCI_DEVICE_ID_CT_65548		0x00dc
+#define PCI_DEVICE_ID_CT_65550		0x00e0
+#define PCI_DEVICE_ID_CT_65554		0x00e4
+#define PCI_DEVICE_ID_CT_65555		0x00e5
+
+#define PCI_VENDOR_ID_MIRO		0x1031
+#define PCI_DEVICE_ID_MIRO_36050	0x5601
+#define PCI_DEVICE_ID_MIRO_DC10PLUS	0x7efe
+#define PCI_DEVICE_ID_MIRO_DC30PLUS	0xd801
+
+#define PCI_VENDOR_ID_NEC		0x1033
+#define PCI_DEVICE_ID_NEC_CBUS_1	0x0001 /* PCI-Cbus Bridge */
+#define PCI_DEVICE_ID_NEC_LOCAL		0x0002 /* Local Bridge */
+#define PCI_DEVICE_ID_NEC_ATM		0x0003 /* ATM LAN Controller */
+#define PCI_DEVICE_ID_NEC_R4000		0x0004 /* R4000 Bridge */
+#define PCI_DEVICE_ID_NEC_486		0x0005 /* 486 Like Peripheral Bus Bridge */
+#define PCI_DEVICE_ID_NEC_ACCEL_1	0x0006 /* Graphic Accelerator */
+#define PCI_DEVICE_ID_NEC_UXBUS		0x0007 /* UX-Bus Bridge */
+#define PCI_DEVICE_ID_NEC_ACCEL_2	0x0008 /* Graphic Accelerator */
+#define PCI_DEVICE_ID_NEC_GRAPH		0x0009 /* PCI-CoreGraph Bridge */
+#define PCI_DEVICE_ID_NEC_VL		0x0016 /* PCI-VL Bridge */
+#define PCI_DEVICE_ID_NEC_STARALPHA2	0x002c /* STAR ALPHA2 */
+#define PCI_DEVICE_ID_NEC_CBUS_2	0x002d /* PCI-Cbus Bridge */
+#define PCI_DEVICE_ID_NEC_USB		0x0035 /* PCI-USB Host */
+#define PCI_DEVICE_ID_NEC_CBUS_3	0x003b
+#define PCI_DEVICE_ID_NEC_NAPCCARD	0x003e
+#define PCI_DEVICE_ID_NEC_PCX2		0x0046 /* PowerVR */
+#define PCI_DEVICE_ID_NEC_NILE4		0x005a
+#define PCI_DEVICE_ID_NEC_VRC5476       0x009b
+#define PCI_DEVICE_ID_NEC_VRC4173	0x00a5
+#define PCI_DEVICE_ID_NEC_VRC5477_AC97  0x00a6
+#define PCI_DEVICE_ID_NEC_PC9821CS01    0x800c /* PC-9821-CS01 */
+#define PCI_DEVICE_ID_NEC_PC9821NRB06   0x800d /* PC-9821NR-B06 */
+
+#define PCI_VENDOR_ID_FD		0x1036
+#define PCI_DEVICE_ID_FD_36C70		0x0000
+
+#define PCI_VENDOR_ID_SI		0x1039
+#define PCI_DEVICE_ID_SI_5591_AGP	0x0001
+#define PCI_DEVICE_ID_SI_6202		0x0002
+#define PCI_DEVICE_ID_SI_503		0x0008
+#define PCI_DEVICE_ID_SI_ACPI		0x0009
+#define PCI_DEVICE_ID_SI_SMBUS		0x0016
+#define PCI_DEVICE_ID_SI_LPC		0x0018
+#define PCI_DEVICE_ID_SI_5597_VGA	0x0200
+#define PCI_DEVICE_ID_SI_6205		0x0205
+#define PCI_DEVICE_ID_SI_501		0x0406
+#define PCI_DEVICE_ID_SI_496		0x0496
+#define PCI_DEVICE_ID_SI_300		0x0300
+#define PCI_DEVICE_ID_SI_315H		0x0310
+#define PCI_DEVICE_ID_SI_315		0x0315
+#define PCI_DEVICE_ID_SI_315PRO		0x0325
+#define PCI_DEVICE_ID_SI_530		0x0530
+#define PCI_DEVICE_ID_SI_540		0x0540
+#define PCI_DEVICE_ID_SI_550		0x0550
+#define PCI_DEVICE_ID_SI_540_VGA	0x5300
+#define PCI_DEVICE_ID_SI_550_VGA	0x5315
+#define PCI_DEVICE_ID_SI_620		0x0620
+#define PCI_DEVICE_ID_SI_630		0x0630
+#define PCI_DEVICE_ID_SI_633		0x0633
+#define PCI_DEVICE_ID_SI_635		0x0635
+#define PCI_DEVICE_ID_SI_640		0x0640
+#define PCI_DEVICE_ID_SI_645		0x0645
+#define PCI_DEVICE_ID_SI_646		0x0646
+#define PCI_DEVICE_ID_SI_648		0x0648
+#define PCI_DEVICE_ID_SI_650		0x0650
+#define PCI_DEVICE_ID_SI_651		0x0651
+#define PCI_DEVICE_ID_SI_655		0x0655
+#define PCI_DEVICE_ID_SI_661		0x0661
+#define PCI_DEVICE_ID_SI_730		0x0730
+#define PCI_DEVICE_ID_SI_733		0x0733
+#define PCI_DEVICE_ID_SI_630_VGA	0x6300
+#define PCI_DEVICE_ID_SI_735		0x0735
+#define PCI_DEVICE_ID_SI_740		0x0740
+#define PCI_DEVICE_ID_SI_741		0x0741
+#define PCI_DEVICE_ID_SI_745		0x0745
+#define PCI_DEVICE_ID_SI_746		0x0746
+#define PCI_DEVICE_ID_SI_755		0x0755
+#define PCI_DEVICE_ID_SI_760		0x0760
+#define PCI_DEVICE_ID_SI_900		0x0900
+#define PCI_DEVICE_ID_SI_961		0x0961
+#define PCI_DEVICE_ID_SI_962		0x0962
+#define PCI_DEVICE_ID_SI_963		0x0963
+#define PCI_DEVICE_ID_SI_965		0x0965
+#define PCI_DEVICE_ID_SI_5511		0x5511
+#define PCI_DEVICE_ID_SI_5513		0x5513
+#define PCI_DEVICE_ID_SI_5518		0x5518
+#define PCI_DEVICE_ID_SI_5571		0x5571
+#define PCI_DEVICE_ID_SI_5581		0x5581
+#define PCI_DEVICE_ID_SI_5582		0x5582
+#define PCI_DEVICE_ID_SI_5591		0x5591
+#define PCI_DEVICE_ID_SI_5596		0x5596
+#define PCI_DEVICE_ID_SI_5597		0x5597
+#define PCI_DEVICE_ID_SI_5598		0x5598
+#define PCI_DEVICE_ID_SI_5600		0x5600
+#define PCI_DEVICE_ID_SI_7012		0x7012
+#define PCI_DEVICE_ID_SI_7013		0x7013
+#define PCI_DEVICE_ID_SI_7016		0x7016
+#define PCI_DEVICE_ID_SI_7018		0x7018
+
+#define PCI_VENDOR_ID_HP		0x103c
+#define PCI_DEVICE_ID_HP_VISUALIZE_EG	0x1005
+#define PCI_DEVICE_ID_HP_VISUALIZE_FX6	0x1006
+#define PCI_DEVICE_ID_HP_VISUALIZE_FX4	0x1008
+#define PCI_DEVICE_ID_HP_VISUALIZE_FX2	0x100a
+#define PCI_DEVICE_ID_HP_TACHYON	0x1028
+#define PCI_DEVICE_ID_HP_TACHLITE	0x1029
+#define PCI_DEVICE_ID_HP_J2585A		0x1030
+#define PCI_DEVICE_ID_HP_J2585B		0x1031
+#define PCI_DEVICE_ID_HP_J2973A		0x1040
+#define PCI_DEVICE_ID_HP_J2970A		0x1042
+#define PCI_DEVICE_ID_HP_DIVA		0x1048
+#define PCI_DEVICE_ID_HP_DIVA_TOSCA1	0x1049
+#define PCI_DEVICE_ID_HP_DIVA_TOSCA2	0x104A
+#define PCI_DEVICE_ID_HP_DIVA_MAESTRO	0x104B
+#define PCI_DEVICE_ID_HP_REO_IOC	0x10f1
+#define PCI_DEVICE_ID_HP_VISUALIZE_FXE	0x108b
+#define PCI_DEVICE_ID_HP_DIVA_HALFDOME	0x1223
+#define PCI_DEVICE_ID_HP_DIVA_KEYSTONE	0x1226
+#define PCI_DEVICE_ID_HP_DIVA_POWERBAR	0x1227
+#define PCI_DEVICE_ID_HP_ZX1_IOC	0x122a
+#define PCI_DEVICE_ID_HP_PCIX_LBA	0x122e
+#define PCI_DEVICE_ID_HP_SX1000_IOC	0x127c
+#define PCI_DEVICE_ID_HP_DIVA_EVEREST	0x1282
+#define PCI_DEVICE_ID_HP_DIVA_AUX	0x1290
+#define PCI_DEVICE_ID_HP_DIVA_RMP3	0x1301
+#define PCI_DEVICE_ID_HP_DIVA_HURRICANE	0x132a
+#define PCI_DEVICE_ID_HP_CISSA		0x3220
+#define PCI_DEVICE_ID_HP_CISSC		0x3230
+#define PCI_DEVICE_ID_HP_CISSD		0x3238
+#define PCI_DEVICE_ID_HP_ZX2_IOC	0x4031
+
+#define PCI_VENDOR_ID_PCTECH		0x1042
+#define PCI_DEVICE_ID_PCTECH_RZ1000	0x1000
+#define PCI_DEVICE_ID_PCTECH_RZ1001	0x1001
+#define PCI_DEVICE_ID_PCTECH_SAMURAI_IDE 0x3020
+
+#define PCI_VENDOR_ID_ASUSTEK		0x1043
+#define PCI_DEVICE_ID_ASUSTEK_0675	0x0675
+
+#define PCI_VENDOR_ID_DPT		0x1044
+#define PCI_DEVICE_ID_DPT		0xa400
+
+#define PCI_VENDOR_ID_OPTI		0x1045
+#define PCI_DEVICE_ID_OPTI_82C558	0xc558
+#define PCI_DEVICE_ID_OPTI_82C621	0xc621
+#define PCI_DEVICE_ID_OPTI_82C700	0xc700
+#define PCI_DEVICE_ID_OPTI_82C825	0xd568
+
+#define PCI_VENDOR_ID_ELSA		0x1048
+#define PCI_DEVICE_ID_ELSA_MICROLINK	0x1000
+#define PCI_DEVICE_ID_ELSA_QS3000	0x3000
+
+
+#define PCI_VENDOR_ID_BUSLOGIC		      0x104B
+#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC 0x0140
+#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER    0x1040
+#define PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT     0x8130
+
+#define PCI_VENDOR_ID_TI		0x104c
+#define PCI_DEVICE_ID_TI_TVP4020	0x3d07
+#define PCI_DEVICE_ID_TI_4450		0x8011
+#define PCI_DEVICE_ID_TI_XX21_XX11	0x8031
+#define PCI_DEVICE_ID_TI_X515		0x8036
+#define PCI_DEVICE_ID_TI_1130		0xac12
+#define PCI_DEVICE_ID_TI_1031		0xac13
+#define PCI_DEVICE_ID_TI_1131		0xac15
+#define PCI_DEVICE_ID_TI_1250		0xac16
+#define PCI_DEVICE_ID_TI_1220		0xac17
+#define PCI_DEVICE_ID_TI_1221		0xac19
+#define PCI_DEVICE_ID_TI_1210		0xac1a
+#define PCI_DEVICE_ID_TI_1450		0xac1b
+#define PCI_DEVICE_ID_TI_1225		0xac1c
+#define PCI_DEVICE_ID_TI_1251A		0xac1d
+#define PCI_DEVICE_ID_TI_1211		0xac1e
+#define PCI_DEVICE_ID_TI_1251B		0xac1f
+#define PCI_DEVICE_ID_TI_4410		0xac41
+#define PCI_DEVICE_ID_TI_4451		0xac42
+#define PCI_DEVICE_ID_TI_4510		0xac44
+#define PCI_DEVICE_ID_TI_4520		0xac46
+#define PCI_DEVICE_ID_TI_7510		0xac47
+#define PCI_DEVICE_ID_TI_7610		0xac48
+#define PCI_DEVICE_ID_TI_7410		0xac49
+#define PCI_DEVICE_ID_TI_1410		0xac50
+#define PCI_DEVICE_ID_TI_1420		0xac51
+#define PCI_DEVICE_ID_TI_1451A		0xac52
+#define PCI_DEVICE_ID_TI_1620		0xac54
+#define PCI_DEVICE_ID_TI_1520		0xac55
+#define PCI_DEVICE_ID_TI_1510		0xac56
+#define PCI_DEVICE_ID_TI_X620		0xac8d
+#define PCI_DEVICE_ID_TI_X420		0xac8e
+
+#define PCI_VENDOR_ID_SONY		0x104d
+
+
+/* Winbond have two vendor IDs! See 0x10ad as well */
+#define PCI_VENDOR_ID_WINBOND2		0x1050
+#define PCI_DEVICE_ID_WINBOND2_89C940F	0x5a5a
+#define PCI_DEVICE_ID_WINBOND2_6692	0x6692
+
+#define PCI_VENDOR_ID_ANIGMA		0x1051
+#define PCI_DEVICE_ID_ANIGMA_MC145575	0x0100
+  
+#define PCI_VENDOR_ID_EFAR		0x1055
+#define PCI_DEVICE_ID_EFAR_SLC90E66_1	0x9130
+#define PCI_DEVICE_ID_EFAR_SLC90E66_3	0x9463
+
+#define PCI_VENDOR_ID_MOTOROLA		0x1057
+#define PCI_DEVICE_ID_MOTOROLA_MPC105	0x0001
+#define PCI_DEVICE_ID_MOTOROLA_MPC106	0x0002
+#define PCI_DEVICE_ID_MOTOROLA_MPC107	0x0004
+#define PCI_DEVICE_ID_MOTOROLA_RAVEN	0x4801
+#define PCI_DEVICE_ID_MOTOROLA_FALCON	0x4802
+#define PCI_DEVICE_ID_MOTOROLA_HAWK	0x4803
+#define PCI_DEVICE_ID_MOTOROLA_HARRIER	0x480b
+#define PCI_DEVICE_ID_MOTOROLA_MPC5200	0x5803
+
+#define PCI_VENDOR_ID_PROMISE		0x105a
+#define PCI_DEVICE_ID_PROMISE_20265	0x0d30
+#define PCI_DEVICE_ID_PROMISE_20267	0x4d30
+#define PCI_DEVICE_ID_PROMISE_20246	0x4d33
+#define PCI_DEVICE_ID_PROMISE_20262	0x4d38
+#define PCI_DEVICE_ID_PROMISE_20263	0x0D38
+#define PCI_DEVICE_ID_PROMISE_20268	0x4d68
+#define PCI_DEVICE_ID_PROMISE_20269	0x4d69
+#define PCI_DEVICE_ID_PROMISE_20270	0x6268
+#define PCI_DEVICE_ID_PROMISE_20271	0x6269
+#define PCI_DEVICE_ID_PROMISE_20275	0x1275
+#define PCI_DEVICE_ID_PROMISE_20276	0x5275
+#define PCI_DEVICE_ID_PROMISE_20277	0x7275
+
+
+#define PCI_VENDOR_ID_UMC		0x1060
+#define PCI_DEVICE_ID_UMC_UM8673F	0x0101
+#define PCI_DEVICE_ID_UMC_UM8886BF	0x673a
+#define PCI_DEVICE_ID_UMC_UM8886A	0x886a
+
+
+#define PCI_VENDOR_ID_MYLEX		0x1069
+#define PCI_DEVICE_ID_MYLEX_DAC960_P	0x0001
+#define PCI_DEVICE_ID_MYLEX_DAC960_PD	0x0002
+#define PCI_DEVICE_ID_MYLEX_DAC960_PG	0x0010
+#define PCI_DEVICE_ID_MYLEX_DAC960_LA	0x0020
+#define PCI_DEVICE_ID_MYLEX_DAC960_LP	0x0050
+#define PCI_DEVICE_ID_MYLEX_DAC960_BA	0xBA56
+#define PCI_DEVICE_ID_MYLEX_DAC960_GEM	0xB166
+
+
+#define PCI_VENDOR_ID_APPLE		0x106b
+#define PCI_DEVICE_ID_APPLE_BANDIT	0x0001
+#define PCI_DEVICE_ID_APPLE_HYDRA	0x000e
+#define PCI_DEVICE_ID_APPLE_UNI_N_FW	0x0018
+#define PCI_DEVICE_ID_APPLE_UNI_N_AGP	0x0020
+#define PCI_DEVICE_ID_APPLE_UNI_N_GMAC	0x0021
+#define PCI_DEVICE_ID_APPLE_UNI_N_GMACP	0x0024
+#define PCI_DEVICE_ID_APPLE_UNI_N_AGP_P	0x0027
+#define PCI_DEVICE_ID_APPLE_UNI_N_AGP15	0x002d
+#define PCI_DEVICE_ID_APPLE_UNI_N_PCI15	0x002e
+#define PCI_DEVICE_ID_APPLE_UNI_N_GMAC2	0x0032
+#define PCI_DEVICE_ID_APPLE_UNI_N_ATA	0x0033
+#define PCI_DEVICE_ID_APPLE_UNI_N_AGP2	0x0034
+#define PCI_DEVICE_ID_APPLE_IPID_ATA100	0x003b
+#define PCI_DEVICE_ID_APPLE_K2_ATA100	0x0043
+#define PCI_DEVICE_ID_APPLE_U3_AGP	0x004b
+#define PCI_DEVICE_ID_APPLE_K2_GMAC	0x004c
+#define PCI_DEVICE_ID_APPLE_SH_ATA      0x0050
+#define PCI_DEVICE_ID_APPLE_SH_SUNGEM   0x0051
+#define PCI_DEVICE_ID_APPLE_U3L_AGP	0x0058
+#define PCI_DEVICE_ID_APPLE_U3H_AGP	0x0059
+#define PCI_DEVICE_ID_APPLE_IPID2_AGP	0x0066
+#define PCI_DEVICE_ID_APPLE_IPID2_ATA	0x0069
+#define PCI_DEVICE_ID_APPLE_IPID2_FW	0x006a
+#define PCI_DEVICE_ID_APPLE_IPID2_GMAC	0x006b
+#define PCI_DEVICE_ID_APPLE_TIGON3	0x1645
+
+#define PCI_VENDOR_ID_YAMAHA		0x1073
+#define PCI_DEVICE_ID_YAMAHA_724	0x0004
+#define PCI_DEVICE_ID_YAMAHA_724F	0x000d
+#define PCI_DEVICE_ID_YAMAHA_740	0x000a
+#define PCI_DEVICE_ID_YAMAHA_740C	0x000c
+#define PCI_DEVICE_ID_YAMAHA_744	0x0010
+#define PCI_DEVICE_ID_YAMAHA_754	0x0012
+
+
+#define PCI_VENDOR_ID_QLOGIC		0x1077
+#define PCI_DEVICE_ID_QLOGIC_ISP1020	0x1020
+#define PCI_DEVICE_ID_QLOGIC_ISP2100	0x2100
+#define PCI_DEVICE_ID_QLOGIC_ISP2200	0x2200
+#define PCI_DEVICE_ID_QLOGIC_ISP2300	0x2300
+#define PCI_DEVICE_ID_QLOGIC_ISP2312	0x2312
+#define PCI_DEVICE_ID_QLOGIC_ISP2322	0x2322
+#define PCI_DEVICE_ID_QLOGIC_ISP6312	0x6312
+#define PCI_DEVICE_ID_QLOGIC_ISP6322	0x6322
+#define PCI_DEVICE_ID_QLOGIC_ISP2422	0x2422
+#define PCI_DEVICE_ID_QLOGIC_ISP2432	0x2432
+#define PCI_DEVICE_ID_QLOGIC_ISP2512	0x2512
+#define PCI_DEVICE_ID_QLOGIC_ISP2522	0x2522
+
+#define PCI_VENDOR_ID_CYRIX		0x1078
+#define PCI_DEVICE_ID_CYRIX_5510	0x0000
+#define PCI_DEVICE_ID_CYRIX_PCI_MASTER	0x0001
+#define PCI_DEVICE_ID_CYRIX_5520	0x0002
+#define PCI_DEVICE_ID_CYRIX_5530_LEGACY	0x0100
+#define PCI_DEVICE_ID_CYRIX_5530_IDE	0x0102
+#define PCI_DEVICE_ID_CYRIX_5530_AUDIO	0x0103
+#define PCI_DEVICE_ID_CYRIX_5530_VIDEO	0x0104
+
+
+
+#define PCI_VENDOR_ID_CONTAQ		0x1080
+#define PCI_DEVICE_ID_CONTAQ_82C693	0xc693
+
+
+#define PCI_VENDOR_ID_OLICOM		0x108d
+#define PCI_DEVICE_ID_OLICOM_OC2325	0x0012
+#define PCI_DEVICE_ID_OLICOM_OC2183	0x0013
+#define PCI_DEVICE_ID_OLICOM_OC2326	0x0014
+
+#define PCI_VENDOR_ID_SUN		0x108e
+#define PCI_DEVICE_ID_SUN_EBUS		0x1000
+#define PCI_DEVICE_ID_SUN_HAPPYMEAL	0x1001
+#define PCI_DEVICE_ID_SUN_RIO_EBUS	0x1100
+#define PCI_DEVICE_ID_SUN_RIO_GEM	0x1101
+#define PCI_DEVICE_ID_SUN_RIO_1394	0x1102
+#define PCI_DEVICE_ID_SUN_RIO_USB	0x1103
+#define PCI_DEVICE_ID_SUN_GEM		0x2bad
+#define PCI_DEVICE_ID_SUN_SIMBA		0x5000
+#define PCI_DEVICE_ID_SUN_PBM		0x8000
+#define PCI_DEVICE_ID_SUN_SCHIZO	0x8001
+#define PCI_DEVICE_ID_SUN_SABRE		0xa000
+#define PCI_DEVICE_ID_SUN_HUMMINGBIRD	0xa001
+#define PCI_DEVICE_ID_SUN_TOMATILLO	0xa801
+#define PCI_DEVICE_ID_SUN_CASSINI	0xabba
+
+#define PCI_VENDOR_ID_CMD		0x1095
+#define PCI_DEVICE_ID_CMD_643		0x0643
+#define PCI_DEVICE_ID_CMD_646		0x0646
+#define PCI_DEVICE_ID_CMD_648		0x0648
+#define PCI_DEVICE_ID_CMD_649		0x0649
+
+#define PCI_DEVICE_ID_SII_680		0x0680
+#define PCI_DEVICE_ID_SII_3112		0x3112
+#define PCI_DEVICE_ID_SII_1210SA	0x0240
+
+
+#define PCI_VENDOR_ID_BROOKTREE		0x109e
+#define PCI_DEVICE_ID_BROOKTREE_878	0x0878
+#define PCI_DEVICE_ID_BROOKTREE_879	0x0879
+
+
+#define PCI_VENDOR_ID_SGI		0x10a9
+#define PCI_DEVICE_ID_SGI_IOC3		0x0003
+#define PCI_DEVICE_ID_SGI_IOC4		0x100a
+#define PCI_VENDOR_ID_SGI_LITHIUM	0x1002
+
+
+#define PCI_VENDOR_ID_WINBOND		0x10ad
+#define PCI_DEVICE_ID_WINBOND_82C105	0x0105
+#define PCI_DEVICE_ID_WINBOND_83C553	0x0565
+
+
+#define PCI_VENDOR_ID_PLX		0x10b5
+#define PCI_DEVICE_ID_PLX_R685		0x1030
+#define PCI_DEVICE_ID_PLX_ROMULUS	0x106a
+#define PCI_DEVICE_ID_PLX_SPCOM800	0x1076
+#define PCI_DEVICE_ID_PLX_1077		0x1077
+#define PCI_DEVICE_ID_PLX_SPCOM200	0x1103
+#define PCI_DEVICE_ID_PLX_DJINN_ITOO	0x1151
+#define PCI_DEVICE_ID_PLX_R753		0x1152
+#define PCI_DEVICE_ID_PLX_OLITEC	0x1187
+#define PCI_DEVICE_ID_PLX_9050		0x9050
+#define PCI_DEVICE_ID_PLX_9080		0x9080
+#define PCI_DEVICE_ID_PLX_GTEK_SERIAL2	0xa001
+
+#define PCI_VENDOR_ID_MADGE		0x10b6
+#define PCI_DEVICE_ID_MADGE_MK2		0x0002
+
+#define PCI_VENDOR_ID_3COM		0x10b7
+#define PCI_DEVICE_ID_3COM_3C985	0x0001
+#define PCI_DEVICE_ID_3COM_3C940	0x1700
+#define PCI_DEVICE_ID_3COM_3C339	0x3390
+#define PCI_DEVICE_ID_3COM_3C359	0x3590
+#define PCI_DEVICE_ID_3COM_3C940B	0x80eb
+#define PCI_DEVICE_ID_3COM_3CR990	0x9900
+#define PCI_DEVICE_ID_3COM_3CR990_TX_95	0x9902
+#define PCI_DEVICE_ID_3COM_3CR990_TX_97	0x9903
+#define PCI_DEVICE_ID_3COM_3CR990B	0x9904
+#define PCI_DEVICE_ID_3COM_3CR990_FX	0x9905
+#define PCI_DEVICE_ID_3COM_3CR990SVR95	0x9908
+#define PCI_DEVICE_ID_3COM_3CR990SVR97	0x9909
+#define PCI_DEVICE_ID_3COM_3CR990SVR	0x990a
+
+
+#define PCI_VENDOR_ID_AL		0x10b9
+#define PCI_DEVICE_ID_AL_M1533		0x1533
+#define PCI_DEVICE_ID_AL_M1535 		0x1535
+#define PCI_DEVICE_ID_AL_M1541		0x1541
+#define PCI_DEVICE_ID_AL_M1563		0x1563
+#define PCI_DEVICE_ID_AL_M1621		0x1621
+#define PCI_DEVICE_ID_AL_M1631		0x1631
+#define PCI_DEVICE_ID_AL_M1632		0x1632
+#define PCI_DEVICE_ID_AL_M1641		0x1641
+#define PCI_DEVICE_ID_AL_M1644		0x1644
+#define PCI_DEVICE_ID_AL_M1647		0x1647
+#define PCI_DEVICE_ID_AL_M1651		0x1651
+#define PCI_DEVICE_ID_AL_M1671		0x1671
+#define PCI_DEVICE_ID_AL_M1681		0x1681
+#define PCI_DEVICE_ID_AL_M1683		0x1683
+#define PCI_DEVICE_ID_AL_M1689		0x1689
+#define PCI_DEVICE_ID_AL_M5219		0x5219
+#define PCI_DEVICE_ID_AL_M5228		0x5228
+#define PCI_DEVICE_ID_AL_M5229		0x5229
+#define PCI_DEVICE_ID_AL_M5451		0x5451
+#define PCI_DEVICE_ID_AL_M7101		0x7101
+
+
+
+#define PCI_VENDOR_ID_NEOMAGIC		0x10c8
+#define PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO 0x8005
+#define PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO 0x8006
+#define PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO 0x8016
+
+
+#define PCI_VENDOR_ID_TCONRAD		0x10da
+#define PCI_DEVICE_ID_TCONRAD_TOKENRING	0x0508
+
+
+#define PCI_VENDOR_ID_NVIDIA			0x10de
+#define PCI_DEVICE_ID_NVIDIA_TNT		0x0020
+#define PCI_DEVICE_ID_NVIDIA_TNT2		0x0028
+#define PCI_DEVICE_ID_NVIDIA_UTNT2		0x0029
+#define PCI_DEVICE_ID_NVIDIA_TNT_UNKNOWN        0x002a
+#define PCI_DEVICE_ID_NVIDIA_VTNT2		0x002C
+#define PCI_DEVICE_ID_NVIDIA_UVTNT2		0x002D
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE	0x0035
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA	0x0036
+#define PCI_DEVICE_ID_NVIDIA_NVENET_10		0x0037
+#define PCI_DEVICE_ID_NVIDIA_NVENET_11		0x0038
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2	0x003e
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_ULTRA 0x0040
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800       0x0041
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_LE    0x0042
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_GT    0x0045
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_4000     0x004E
+#define PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS	0x0052
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE	0x0053
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA	0x0054
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2	0x0055
+#define PCI_DEVICE_ID_NVIDIA_NVENET_8		0x0056
+#define PCI_DEVICE_ID_NVIDIA_NVENET_9		0x0057
+#define PCI_DEVICE_ID_NVIDIA_CK804_AUDIO	0x0059
+#define PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS	0x0064
+#define PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE	0x0065
+#define PCI_DEVICE_ID_NVIDIA_NVENET_2		0x0066
+#define PCI_DEVICE_ID_NVIDIA_MCP2_MODEM		0x0069
+#define PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO		0x006a
+#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_SMBUS	0x0084
+#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE	0x0085
+#define PCI_DEVICE_ID_NVIDIA_NVENET_4		0x0086
+#define PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM	0x0089
+#define PCI_DEVICE_ID_NVIDIA_CK8_AUDIO		0x008a
+#define PCI_DEVICE_ID_NVIDIA_NVENET_5		0x008c
+#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA	0x008e
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_7800_GT   0x0090
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_7800_GTX	0x0091
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_7800   0x0098
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_7800_GTX 0x0099
+#define PCI_DEVICE_ID_NVIDIA_ITNT2		0x00A0
+#define PCI_DEVICE_ID_GEFORCE_6800A             0x00c1
+#define PCI_DEVICE_ID_GEFORCE_6800A_LE          0x00c2
+#define PCI_DEVICE_ID_GEFORCE_GO_6800           0x00c8
+#define PCI_DEVICE_ID_GEFORCE_GO_6800_ULTRA     0x00c9
+#define PCI_DEVICE_ID_QUADRO_FX_GO1400          0x00cc
+#define PCI_DEVICE_ID_QUADRO_FX_1400            0x00ce
+#define PCI_DEVICE_ID_NVIDIA_NFORCE3		0x00d1
+#define PCI_DEVICE_ID_NVIDIA_NFORCE3_SMBUS	0x00d4
+#define PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE	0x00d5
+#define PCI_DEVICE_ID_NVIDIA_NVENET_3		0x00d6
+#define PCI_DEVICE_ID_NVIDIA_MCP3_MODEM		0x00d9
+#define PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO		0x00da
+#define PCI_DEVICE_ID_NVIDIA_NVENET_7		0x00df
+#define PCI_DEVICE_ID_NVIDIA_NFORCE3S		0x00e1
+#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA	0x00e3
+#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS	0x00e4
+#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE	0x00e5
+#define PCI_DEVICE_ID_NVIDIA_NVENET_6		0x00e6
+#define PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO		0x00ea
+#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2	0x00ee
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_SDR	0x0100
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_DDR	0x0101
+#define PCI_DEVICE_ID_NVIDIA_QUADRO		0x0103
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX	0x0110
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX2	0x0111
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GO	0x0112
+#define PCI_DEVICE_ID_NVIDIA_QUADRO2_MXR	0x0113
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6600_GT	0x0140
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6600	0x0141
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6610_XL	0x0145
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_540	0x014E
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6200	0x014F
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS	0x0150
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS2	0x0151
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_ULTRA	0x0152
+#define PCI_DEVICE_ID_NVIDIA_QUADRO2_PRO	0x0153
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6200_TURBOCACHE 0x0161
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6200    0x0164
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6250    0x0166
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6200_1  0x0167
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6250_1  0x0168
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_460	0x0170
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440	0x0171
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420	0x0172
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440_SE	0x0173
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO	0x0174
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO	0x0175
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO_M32 0x0176
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_460_GO    0x0177
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_500XGL	0x0178
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO_M64 0x0179
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_200	0x017A
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_550XGL	0x017B
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_500_GOGL	0x017C
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_410_GO_M16 0x017D
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440_8X 0x0181
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440SE_8X 0x0182
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420_8X 0x0183
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_448_GO    0x0186
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_488_GO    0x0187
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_580_XGL    0x0188
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_MAC    0x0189
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_280_NVS    0x018A
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_380_XGL    0x018B
+#define PCI_DEVICE_ID_NVIDIA_IGEFORCE2		0x01a0
+#define PCI_DEVICE_ID_NVIDIA_NFORCE		0x01a4
+#define PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO		0x01b1
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_SMBUS	0x01b4
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_IDE		0x01bc
+#define PCI_DEVICE_ID_NVIDIA_MCP1_MODEM		0x01c1
+#define PCI_DEVICE_ID_NVIDIA_NVENET_1		0x01c3
+#define PCI_DEVICE_ID_NVIDIA_NFORCE2		0x01e0
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE3		0x0200
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE3_1		0x0201
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE3_2		0x0202
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_DDC		0x0203
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B      0x0211
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B_LE   0x0212
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B_GT   0x0215
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4600	0x0250
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4400	0x0251
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4200	0x0253
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_900XGL	0x0258
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_750XGL	0x0259
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_700XGL	0x025B
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE	0x0265
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA	0x0266
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2	0x0267
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE	0x036E
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA	0x037E
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2	0x037F
+#define PCI_DEVICE_ID_NVIDIA_NVENET_12		0x0268
+#define PCI_DEVICE_ID_NVIDIA_NVENET_13		0x0269
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800	0x0280
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800_8X    0x0281
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800SE     0x0282
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_4200_GO       0x0286
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_980_XGL        0x0288
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_780_XGL        0x0289
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_700_GOGL       0x028C
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5800_ULTRA  0x0301
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5800        0x0302
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_2000         0x0308
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1000         0x0309
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600_ULTRA  0x0311
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600        0x0312
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600SE      0x0314
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5600      0x031A
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5650      0x031B
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO700        0x031C
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200        0x0320
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200_ULTRA  0x0321
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200_1      0x0322
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200SE      0x0323
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5200      0x0324
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5250      0x0325
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5500        0x0326
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5100        0x0327
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5250_32   0x0328
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO_5200	    0x0329
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_NVS_280_PCI     0x032A
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_500          0x032B
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5300      0x032C
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5100      0x032D
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900_ULTRA  0x0330
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900        0x0331
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900XT      0x0332
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5950_ULTRA  0x0333
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900ZT      0x0334
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_3000         0x0338
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_700          0x033F
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700_ULTRA  0x0341
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700        0x0342
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700LE      0x0343
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700VE      0x0344
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_1    0x0347
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_2    0x0348
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO1000       0x034C
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1100         0x034E
+#define PCI_DEVICE_ID_NVIDIA_NVENET_14              0x0372
+#define PCI_DEVICE_ID_NVIDIA_NVENET_15              0x0373
+
+#define PCI_VENDOR_ID_IMS		0x10e0
+#define PCI_DEVICE_ID_IMS_TT128		0x9128
+#define PCI_DEVICE_ID_IMS_TT3D		0x9135
+
+
+
+
+#define PCI_VENDOR_ID_INTERG		0x10ea
+#define PCI_DEVICE_ID_INTERG_1682	0x1682
+#define PCI_DEVICE_ID_INTERG_2000	0x2000
+#define PCI_DEVICE_ID_INTERG_2010	0x2010
+#define PCI_DEVICE_ID_INTERG_5000	0x5000
+#define PCI_DEVICE_ID_INTERG_5050	0x5050
+
+#define PCI_VENDOR_ID_REALTEK		0x10ec
+#define PCI_DEVICE_ID_REALTEK_8139	0x8139
+
+#define PCI_VENDOR_ID_XILINX		0x10ee
+#define PCI_DEVICE_ID_RME_DIGI96	0x3fc0
+#define PCI_DEVICE_ID_RME_DIGI96_8	0x3fc1
+#define PCI_DEVICE_ID_RME_DIGI96_8_PRO	0x3fc2
+#define PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST 0x3fc3
+#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP 0x3fc5
+#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI 0x3fc6
+
+
+#define PCI_VENDOR_ID_INIT		0x1101
+
+#define PCI_VENDOR_ID_CREATIVE		0x1102 /* duplicate: ECTIVA */
+#define PCI_DEVICE_ID_CREATIVE_EMU10K1	0x0002
+
+#define PCI_VENDOR_ID_ECTIVA		0x1102 /* duplicate: CREATIVE */
+#define PCI_DEVICE_ID_ECTIVA_EV1938	0x8938
+
+#define PCI_VENDOR_ID_TTI		0x1103
+#define PCI_DEVICE_ID_TTI_HPT343	0x0003
+#define PCI_DEVICE_ID_TTI_HPT366	0x0004
+#define PCI_DEVICE_ID_TTI_HPT372	0x0005
+#define PCI_DEVICE_ID_TTI_HPT302	0x0006
+#define PCI_DEVICE_ID_TTI_HPT371	0x0007
+#define PCI_DEVICE_ID_TTI_HPT374	0x0008
+#define PCI_DEVICE_ID_TTI_HPT372N	0x0009	/* apparently a 372N variant? */
+
+#define PCI_VENDOR_ID_VIA		0x1106
+#define PCI_DEVICE_ID_VIA_8763_0	0x0198
+#define PCI_DEVICE_ID_VIA_8380_0	0x0204
+#define PCI_DEVICE_ID_VIA_3238_0	0x0238
+#define PCI_DEVICE_ID_VIA_PT880		0x0258
+#define PCI_DEVICE_ID_VIA_PX8X0_0	0x0259
+#define PCI_DEVICE_ID_VIA_3269_0	0x0269
+#define PCI_DEVICE_ID_VIA_K8T800PRO_0	0x0282
+#define PCI_DEVICE_ID_VIA_8363_0	0x0305
+#define PCI_DEVICE_ID_VIA_P4M800CE	0x0314
+#define PCI_DEVICE_ID_VIA_8371_0	0x0391
+#define PCI_DEVICE_ID_VIA_8501_0	0x0501
+#define PCI_DEVICE_ID_VIA_82C561	0x0561
+#define PCI_DEVICE_ID_VIA_82C586_1	0x0571
+#define PCI_DEVICE_ID_VIA_82C576	0x0576
+#define PCI_DEVICE_ID_VIA_82C586_0	0x0586
+#define PCI_DEVICE_ID_VIA_82C596	0x0596
+#define PCI_DEVICE_ID_VIA_82C597_0	0x0597
+#define PCI_DEVICE_ID_VIA_82C598_0	0x0598
+#define PCI_DEVICE_ID_VIA_8601_0	0x0601
+#define PCI_DEVICE_ID_VIA_8605_0	0x0605
+#define PCI_DEVICE_ID_VIA_82C686	0x0686
+#define PCI_DEVICE_ID_VIA_82C691_0	0x0691
+#define PCI_DEVICE_ID_VIA_82C576_1	0x1571
+#define PCI_DEVICE_ID_VIA_82C586_2	0x3038
+#define PCI_DEVICE_ID_VIA_82C586_3	0x3040
+#define PCI_DEVICE_ID_VIA_82C596_3	0x3050
+#define PCI_DEVICE_ID_VIA_82C596B_3	0x3051
+#define PCI_DEVICE_ID_VIA_82C686_4	0x3057
+#define PCI_DEVICE_ID_VIA_82C686_5	0x3058
+#define PCI_DEVICE_ID_VIA_8233_5	0x3059
+#define PCI_DEVICE_ID_VIA_8233_0	0x3074
+#define PCI_DEVICE_ID_VIA_8633_0	0x3091
+#define PCI_DEVICE_ID_VIA_8367_0	0x3099
+#define PCI_DEVICE_ID_VIA_8653_0	0x3101
+#define PCI_DEVICE_ID_VIA_8622		0x3102
+#define PCI_DEVICE_ID_VIA_8233C_0	0x3109
+#define PCI_DEVICE_ID_VIA_8361		0x3112
+#define PCI_DEVICE_ID_VIA_XM266		0x3116
+#define PCI_DEVICE_ID_VIA_612X		0x3119
+#define PCI_DEVICE_ID_VIA_862X_0	0x3123
+#define PCI_DEVICE_ID_VIA_8753_0	0x3128
+#define PCI_DEVICE_ID_VIA_8233A		0x3147
+#define PCI_DEVICE_ID_VIA_8703_51_0	0x3148
+#define PCI_DEVICE_ID_VIA_8237_SATA	0x3149
+#define PCI_DEVICE_ID_VIA_XN266		0x3156
+#define PCI_DEVICE_ID_VIA_6410		0x3164
+#define PCI_DEVICE_ID_VIA_8754C_0	0x3168
+#define PCI_DEVICE_ID_VIA_8235		0x3177
+#define PCI_DEVICE_ID_VIA_8385_0	0x3188
+#define PCI_DEVICE_ID_VIA_8377_0	0x3189
+#define PCI_DEVICE_ID_VIA_8378_0	0x3205
+#define PCI_DEVICE_ID_VIA_8783_0	0x3208
+#define PCI_DEVICE_ID_VIA_8237		0x3227
+#define PCI_DEVICE_ID_VIA_8251		0x3287
+#define PCI_DEVICE_ID_VIA_3296_0	0x0296
+#define PCI_DEVICE_ID_VIA_8231		0x8231
+#define PCI_DEVICE_ID_VIA_8231_4	0x8235
+#define PCI_DEVICE_ID_VIA_8365_1	0x8305
+#define PCI_DEVICE_ID_VIA_8371_1	0x8391
+#define PCI_DEVICE_ID_VIA_82C598_1	0x8598
+#define PCI_DEVICE_ID_VIA_838X_1	0xB188
+#define PCI_DEVICE_ID_VIA_83_87XX_1	0xB198
+
+#define PCI_VENDOR_ID_SIEMENS           0x110A
+#define PCI_DEVICE_ID_SIEMENS_DSCC4     0x2102
+
+
+#define PCI_VENDOR_ID_VORTEX		0x1119
+#define PCI_DEVICE_ID_VORTEX_GDT60x0	0x0000
+#define PCI_DEVICE_ID_VORTEX_GDT6000B	0x0001
+#define PCI_DEVICE_ID_VORTEX_GDT6x10	0x0002
+#define PCI_DEVICE_ID_VORTEX_GDT6x20	0x0003
+#define PCI_DEVICE_ID_VORTEX_GDT6530	0x0004
+#define PCI_DEVICE_ID_VORTEX_GDT6550	0x0005
+#define PCI_DEVICE_ID_VORTEX_GDT6x17	0x0006
+#define PCI_DEVICE_ID_VORTEX_GDT6x27	0x0007
+#define PCI_DEVICE_ID_VORTEX_GDT6537	0x0008
+#define PCI_DEVICE_ID_VORTEX_GDT6557	0x0009
+#define PCI_DEVICE_ID_VORTEX_GDT6x15	0x000a
+#define PCI_DEVICE_ID_VORTEX_GDT6x25	0x000b
+#define PCI_DEVICE_ID_VORTEX_GDT6535	0x000c
+#define PCI_DEVICE_ID_VORTEX_GDT6555	0x000d
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP	0x0100
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP	0x0101
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP	0x0102
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP	0x0103
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP	0x0104
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP	0x0105
+
+#define PCI_VENDOR_ID_EF		0x111a
+#define PCI_DEVICE_ID_EF_ATM_FPGA	0x0000
+#define PCI_DEVICE_ID_EF_ATM_ASIC	0x0002
+#define PCI_VENDOR_ID_EF_ATM_LANAI2	0x0003
+#define PCI_VENDOR_ID_EF_ATM_LANAIHB	0x0005
+
+#define PCI_VENDOR_ID_IDT		0x111d
+#define PCI_DEVICE_ID_IDT_IDT77201	0x0001
+
+#define PCI_VENDOR_ID_FORE		0x1127
+#define PCI_DEVICE_ID_FORE_PCA200E	0x0300
+
+
+#define PCI_VENDOR_ID_PHILIPS		0x1131
+#define PCI_DEVICE_ID_PHILIPS_SAA7146	0x7146
+#define PCI_DEVICE_ID_PHILIPS_SAA9730	0x9730
+
+#define PCI_VENDOR_ID_EICON		0x1133
+#define PCI_DEVICE_ID_EICON_DIVA20	0xe002
+#define PCI_DEVICE_ID_EICON_DIVA20_U	0xe004
+#define PCI_DEVICE_ID_EICON_DIVA201	0xe005
+#define PCI_DEVICE_ID_EICON_DIVA202	0xe00b
+#define PCI_DEVICE_ID_EICON_MAESTRA	0xe010
+#define PCI_DEVICE_ID_EICON_MAESTRAQ	0xe012
+#define PCI_DEVICE_ID_EICON_MAESTRAQ_U	0xe013
+#define PCI_DEVICE_ID_EICON_MAESTRAP	0xe014
+
+#define PCI_VENDOR_ID_ZIATECH		0x1138
+#define PCI_DEVICE_ID_ZIATECH_5550_HC	0x5550
+ 
+
+
+#define PCI_VENDOR_ID_SYSKONNECT	0x1148
+#define PCI_DEVICE_ID_SYSKONNECT_TR	0x4200
+#define PCI_DEVICE_ID_SYSKONNECT_GE	0x4300
+#define PCI_DEVICE_ID_SYSKONNECT_YU	0x4320
+#define PCI_DEVICE_ID_SYSKONNECT_9DXX	0x4400
+#define PCI_DEVICE_ID_SYSKONNECT_9MXX	0x4500
+
+
+#define PCI_VENDOR_ID_DIGI		0x114f
+#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_E	0x0070
+#define PCI_DEVICE_ID_DIGI_DF_M_E	0x0071
+#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_A	0x0072
+#define PCI_DEVICE_ID_DIGI_DF_M_A	0x0073
+#define PCI_DEVICE_ID_NEO_2DB9          0x00C8
+#define PCI_DEVICE_ID_NEO_2DB9PRI       0x00C9
+#define PCI_DEVICE_ID_NEO_2RJ45         0x00CA
+#define PCI_DEVICE_ID_NEO_2RJ45PRI      0x00CB
+
+
+#define PCI_VENDOR_ID_XIRCOM		0x115d
+#define PCI_DEVICE_ID_XIRCOM_RBM56G	0x0101
+#define PCI_DEVICE_ID_XIRCOM_X3201_MDM	0x0103
+
+
+#define PCI_VENDOR_ID_SERVERWORKS	  0x1166
+#define PCI_DEVICE_ID_SERVERWORKS_HE	  0x0008
+#define PCI_DEVICE_ID_SERVERWORKS_LE	  0x0009
+#define PCI_DEVICE_ID_SERVERWORKS_GCNB_LE 0x0017
+#define PCI_DEVICE_ID_SERVERWORKS_OSB4	  0x0200
+#define PCI_DEVICE_ID_SERVERWORKS_CSB5	  0x0201
+#define PCI_DEVICE_ID_SERVERWORKS_CSB6    0x0203
+#define PCI_DEVICE_ID_SERVERWORKS_OSB4IDE 0x0211
+#define PCI_DEVICE_ID_SERVERWORKS_CSB5IDE 0x0212
+#define PCI_DEVICE_ID_SERVERWORKS_CSB6IDE 0x0213
+#define PCI_DEVICE_ID_SERVERWORKS_HT1000IDE 0x0214
+#define PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2 0x0217
+#define PCI_DEVICE_ID_SERVERWORKS_CSB6LPC 0x0227
+
+#define PCI_VENDOR_ID_SBE		0x1176
+#define PCI_DEVICE_ID_SBE_WANXL100	0x0301
+#define PCI_DEVICE_ID_SBE_WANXL200	0x0302
+#define PCI_DEVICE_ID_SBE_WANXL400	0x0104
+
+#define PCI_VENDOR_ID_TOSHIBA		0x1179
+#define PCI_DEVICE_ID_TOSHIBA_PICCOLO	0x0102
+#define PCI_DEVICE_ID_TOSHIBA_PICCOLO_1	0x0103
+#define PCI_DEVICE_ID_TOSHIBA_PICCOLO_2	0x0105
+#define PCI_DEVICE_ID_TOSHIBA_TOPIC95	0x060a
+#define PCI_DEVICE_ID_TOSHIBA_TOPIC97	0x060f
+#define PCI_DEVICE_ID_TOSHIBA_TOPIC100	0x0617
+
+#define PCI_VENDOR_ID_TOSHIBA_2		0x102f
+#define PCI_DEVICE_ID_TOSHIBA_TC35815CF	0x0030
+#define PCI_DEVICE_ID_TOSHIBA_TC86C001_MISC	0x0108
+#define PCI_DEVICE_ID_TOSHIBA_SPIDER_NET 0x01b3
+
+#define PCI_VENDOR_ID_RICOH		0x1180
+#define PCI_DEVICE_ID_RICOH_RL5C465	0x0465
+#define PCI_DEVICE_ID_RICOH_RL5C466	0x0466
+#define PCI_DEVICE_ID_RICOH_RL5C475	0x0475
+#define PCI_DEVICE_ID_RICOH_RL5C476	0x0476
+#define PCI_DEVICE_ID_RICOH_RL5C478	0x0478
+
+#define PCI_VENDOR_ID_DLINK		0x1186
+#define PCI_DEVICE_ID_DLINK_DGE510T	0x4c00
+
+#define PCI_VENDOR_ID_ARTOP		0x1191
+#define PCI_DEVICE_ID_ARTOP_ATP850UF	0x0005
+#define PCI_DEVICE_ID_ARTOP_ATP860	0x0006
+#define PCI_DEVICE_ID_ARTOP_ATP860R	0x0007
+#define PCI_DEVICE_ID_ARTOP_ATP865	0x0008
+#define PCI_DEVICE_ID_ARTOP_ATP865R	0x0009
+#define PCI_DEVICE_ID_ARTOP_AEC7610	0x8002
+#define PCI_DEVICE_ID_ARTOP_AEC7612UW	0x8010
+#define PCI_DEVICE_ID_ARTOP_AEC7612U	0x8020
+#define PCI_DEVICE_ID_ARTOP_AEC7612S	0x8030
+#define PCI_DEVICE_ID_ARTOP_AEC7612D	0x8040
+#define PCI_DEVICE_ID_ARTOP_AEC7612SUW	0x8050
+#define PCI_DEVICE_ID_ARTOP_8060	0x8060
+
+#define PCI_VENDOR_ID_ZEITNET		0x1193
+#define PCI_DEVICE_ID_ZEITNET_1221	0x0001
+#define PCI_DEVICE_ID_ZEITNET_1225	0x0002
+
+
+#define PCI_VENDOR_ID_FUJITSU_ME	0x119e
+#define PCI_DEVICE_ID_FUJITSU_FS155	0x0001
+#define PCI_DEVICE_ID_FUJITSU_FS50	0x0003
+
+#define PCI_SUBVENDOR_ID_KEYSPAN	0x11a9
+#define PCI_SUBDEVICE_ID_KEYSPAN_SX2	0x5334
+
+#define PCI_VENDOR_ID_MARVELL		0x11ab
+#define PCI_DEVICE_ID_MARVELL_GT64111	0x4146
+#define PCI_DEVICE_ID_MARVELL_GT64260	0x6430
+#define PCI_DEVICE_ID_MARVELL_MV64360	0x6460
+#define PCI_DEVICE_ID_MARVELL_MV64460	0x6480
+#define PCI_DEVICE_ID_MARVELL_GT96100	0x9652
+#define PCI_DEVICE_ID_MARVELL_GT96100A	0x9653
+
+
+#define PCI_VENDOR_ID_V3		0x11b0
+#define PCI_DEVICE_ID_V3_V960		0x0001
+#define PCI_DEVICE_ID_V3_V351		0x0002
+
+
+#define PCI_VENDOR_ID_ATT		0x11c1
+#define PCI_DEVICE_ID_ATT_VENUS_MODEM	0x480
+
+
+#define PCI_VENDOR_ID_SPECIALIX		0x11cb
+#define PCI_DEVICE_ID_SPECIALIX_IO8	0x2000
+#define PCI_DEVICE_ID_SPECIALIX_RIO	0x8000
+#define PCI_SUBDEVICE_ID_SPECIALIX_SPEED4 0xa004
+
+
+#define PCI_VENDOR_ID_ANALOG_DEVICES	0x11d4
+#define PCI_DEVICE_ID_AD1889JS		0x1889
+
+
+#define PCI_DEVICE_ID_SEGA_BBA		0x1234
+
+#define PCI_VENDOR_ID_ZORAN		0x11de
+#define PCI_DEVICE_ID_ZORAN_36057	0x6057
+#define PCI_DEVICE_ID_ZORAN_36120	0x6120
+
+
+#define PCI_VENDOR_ID_COMPEX		0x11f6
+#define PCI_DEVICE_ID_COMPEX_ENET100VG4	0x0112
+
+#define PCI_VENDOR_ID_RP		0x11fe
+#define PCI_DEVICE_ID_RP32INTF		0x0001
+#define PCI_DEVICE_ID_RP8INTF		0x0002
+#define PCI_DEVICE_ID_RP16INTF		0x0003
+#define PCI_DEVICE_ID_RP4QUAD		0x0004
+#define PCI_DEVICE_ID_RP8OCTA		0x0005
+#define PCI_DEVICE_ID_RP8J		0x0006
+#define PCI_DEVICE_ID_RP4J		0x0007
+#define PCI_DEVICE_ID_RP8SNI		0x0008	
+#define PCI_DEVICE_ID_RP16SNI		0x0009	
+#define PCI_DEVICE_ID_RPP4		0x000A
+#define PCI_DEVICE_ID_RPP8		0x000B
+#define PCI_DEVICE_ID_RP4M		0x000D
+#define PCI_DEVICE_ID_RP2_232		0x000E
+#define PCI_DEVICE_ID_RP2_422		0x000F
+#define PCI_DEVICE_ID_URP32INTF		0x0801
+#define PCI_DEVICE_ID_URP8INTF		0x0802
+#define PCI_DEVICE_ID_URP16INTF		0x0803
+#define PCI_DEVICE_ID_URP8OCTA		0x0805
+#define PCI_DEVICE_ID_UPCI_RM3_8PORT	0x080C       
+#define PCI_DEVICE_ID_UPCI_RM3_4PORT	0x080D
+#define PCI_DEVICE_ID_CRP16INTF		0x0903       
+
+#define PCI_VENDOR_ID_CYCLADES		0x120e
+#define PCI_DEVICE_ID_CYCLOM_Y_Lo	0x0100
+#define PCI_DEVICE_ID_CYCLOM_Y_Hi	0x0101
+#define PCI_DEVICE_ID_CYCLOM_4Y_Lo	0x0102
+#define PCI_DEVICE_ID_CYCLOM_4Y_Hi	0x0103
+#define PCI_DEVICE_ID_CYCLOM_8Y_Lo	0x0104
+#define PCI_DEVICE_ID_CYCLOM_8Y_Hi	0x0105
+#define PCI_DEVICE_ID_CYCLOM_Z_Lo	0x0200
+#define PCI_DEVICE_ID_CYCLOM_Z_Hi	0x0201
+#define PCI_DEVICE_ID_PC300_RX_2	0x0300
+#define PCI_DEVICE_ID_PC300_RX_1	0x0301
+#define PCI_DEVICE_ID_PC300_TE_2	0x0310
+#define PCI_DEVICE_ID_PC300_TE_1	0x0311
+#define PCI_DEVICE_ID_PC300_TE_M_2	0x0320
+#define PCI_DEVICE_ID_PC300_TE_M_1	0x0321
+
+#define PCI_VENDOR_ID_ESSENTIAL		0x120f
+#define PCI_DEVICE_ID_ESSENTIAL_ROADRUNNER	0x0001
+
+#define PCI_VENDOR_ID_O2		0x1217
+#define PCI_DEVICE_ID_O2_6729		0x6729
+#define PCI_DEVICE_ID_O2_6730		0x673a
+#define PCI_DEVICE_ID_O2_6832		0x6832
+#define PCI_DEVICE_ID_O2_6836		0x6836
+
+#define PCI_VENDOR_ID_3DFX		0x121a
+#define PCI_DEVICE_ID_3DFX_VOODOO	0x0001
+#define PCI_DEVICE_ID_3DFX_VOODOO2	0x0002
+#define PCI_DEVICE_ID_3DFX_BANSHEE	0x0003
+#define PCI_DEVICE_ID_3DFX_VOODOO3	0x0005
+#define PCI_DEVICE_ID_3DFX_VOODOO5	0x0009
+
+
+
+#define PCI_VENDOR_ID_AVM		0x1244
+#define PCI_DEVICE_ID_AVM_B1		0x0700
+#define PCI_DEVICE_ID_AVM_C4		0x0800
+#define PCI_DEVICE_ID_AVM_A1		0x0a00
+#define PCI_DEVICE_ID_AVM_A1_V2		0x0e00
+#define PCI_DEVICE_ID_AVM_C2		0x1100
+#define PCI_DEVICE_ID_AVM_T1		0x1200
+
+
+#define PCI_VENDOR_ID_STALLION		0x124d
+
+/* Allied Telesyn */
+#define PCI_VENDOR_ID_AT    		0x1259
+#define PCI_SUBDEVICE_ID_AT_2700FX	0x2701
+#define PCI_SUBDEVICE_ID_AT_2701FX	0x2703
+
+#define PCI_VENDOR_ID_ESS		0x125d
+#define PCI_DEVICE_ID_ESS_ESS1968	0x1968
+#define PCI_DEVICE_ID_ESS_ESS1978	0x1978
+#define PCI_DEVICE_ID_ESS_ALLEGRO_1	0x1988
+#define PCI_DEVICE_ID_ESS_ALLEGRO	0x1989
+#define PCI_DEVICE_ID_ESS_CANYON3D_2LE	0x1990
+#define PCI_DEVICE_ID_ESS_CANYON3D_2	0x1992
+#define PCI_DEVICE_ID_ESS_MAESTRO3	0x1998
+#define PCI_DEVICE_ID_ESS_MAESTRO3_1	0x1999
+#define PCI_DEVICE_ID_ESS_MAESTRO3_HW	0x199a
+#define PCI_DEVICE_ID_ESS_MAESTRO3_2	0x199b
+
+#define PCI_VENDOR_ID_SATSAGEM		0x1267
+#define PCI_DEVICE_ID_SATSAGEM_NICCY	0x1016
+
+
+#define PCI_VENDOR_ID_ENSONIQ		0x1274
+#define PCI_DEVICE_ID_ENSONIQ_CT5880	0x5880
+#define PCI_DEVICE_ID_ENSONIQ_ES1370	0x5000
+#define PCI_DEVICE_ID_ENSONIQ_ES1371	0x1371
+
+#define PCI_VENDOR_ID_TRANSMETA		0x1279
+#define PCI_DEVICE_ID_EFFICEON		0x0060
+
+#define PCI_VENDOR_ID_ROCKWELL		0x127A
+
+#define PCI_VENDOR_ID_ITE		0x1283
+#define PCI_DEVICE_ID_ITE_IT8172G	0x8172
+#define PCI_DEVICE_ID_ITE_IT8172G_AUDIO 0x0801
+#define PCI_DEVICE_ID_ITE_8211		0x8211
+#define PCI_DEVICE_ID_ITE_8212		0x8212
+#define PCI_DEVICE_ID_ITE_8872		0x8872
+#define PCI_DEVICE_ID_ITE_IT8330G_0	0xe886
+
+/* formerly Platform Tech */
+#define PCI_DEVICE_ID_ESS_ESS0100	0x0100
+
+#define PCI_VENDOR_ID_ALTEON		0x12ae
+
+
+#define PCI_SUBVENDOR_ID_CONNECT_TECH			0x12c4
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232		0x0001
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232		0x0002
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232		0x0003
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485		0x0004
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4	0x0005
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485		0x0006
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2	0x0007
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485		0x0008
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6	0x0009
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1	0x000A
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1	0x000B
+
+
+#define PCI_VENDOR_ID_NVIDIA_SGS	0x12d2
+#define PCI_DEVICE_ID_NVIDIA_SGS_RIVA128 0x0018
+
+#define PCI_SUBVENDOR_ID_CHASE_PCIFAST		0x12E0
+#define PCI_SUBDEVICE_ID_CHASE_PCIFAST4		0x0031
+#define PCI_SUBDEVICE_ID_CHASE_PCIFAST8		0x0021
+#define PCI_SUBDEVICE_ID_CHASE_PCIFAST16	0x0011
+#define PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC	0x0041
+#define PCI_SUBVENDOR_ID_CHASE_PCIRAS		0x124D
+#define PCI_SUBDEVICE_ID_CHASE_PCIRAS4		0xF001
+#define PCI_SUBDEVICE_ID_CHASE_PCIRAS8		0xF010
+
+#define PCI_VENDOR_ID_AUREAL		0x12eb
+#define PCI_DEVICE_ID_AUREAL_VORTEX_1	0x0001
+#define PCI_DEVICE_ID_AUREAL_VORTEX_2	0x0002
+#define PCI_DEVICE_ID_AUREAL_ADVANTAGE	0x0003
+
+#define PCI_VENDOR_ID_ELECTRONICDESIGNGMBH 0x12f8
+#define PCI_DEVICE_ID_LML_33R10		0x8a02
+
+
+#define PCI_VENDOR_ID_SIIG		0x131f
+#define PCI_SUBVENDOR_ID_SIIG		0x131f
+#define PCI_DEVICE_ID_SIIG_1S_10x_550	0x1000
+#define PCI_DEVICE_ID_SIIG_1S_10x_650	0x1001
+#define PCI_DEVICE_ID_SIIG_1S_10x_850	0x1002
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_550	0x1010
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_650	0x1011
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_850	0x1012
+#define PCI_DEVICE_ID_SIIG_1P_10x	0x1020
+#define PCI_DEVICE_ID_SIIG_2P_10x	0x1021
+#define PCI_DEVICE_ID_SIIG_2S_10x_550	0x1030
+#define PCI_DEVICE_ID_SIIG_2S_10x_650	0x1031
+#define PCI_DEVICE_ID_SIIG_2S_10x_850	0x1032
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_550	0x1034
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_650	0x1035
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_850	0x1036
+#define PCI_DEVICE_ID_SIIG_4S_10x_550	0x1050
+#define PCI_DEVICE_ID_SIIG_4S_10x_650	0x1051
+#define PCI_DEVICE_ID_SIIG_4S_10x_850	0x1052
+#define PCI_DEVICE_ID_SIIG_1S_20x_550	0x2000
+#define PCI_DEVICE_ID_SIIG_1S_20x_650	0x2001
+#define PCI_DEVICE_ID_SIIG_1S_20x_850	0x2002
+#define PCI_DEVICE_ID_SIIG_1P_20x	0x2020
+#define PCI_DEVICE_ID_SIIG_2P_20x	0x2021
+#define PCI_DEVICE_ID_SIIG_2S_20x_550	0x2030
+#define PCI_DEVICE_ID_SIIG_2S_20x_650	0x2031
+#define PCI_DEVICE_ID_SIIG_2S_20x_850	0x2032
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_550	0x2040
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_650	0x2041
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_850	0x2042
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_550	0x2010
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_650	0x2011
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_850	0x2012
+#define PCI_DEVICE_ID_SIIG_4S_20x_550	0x2050
+#define PCI_DEVICE_ID_SIIG_4S_20x_650	0x2051
+#define PCI_DEVICE_ID_SIIG_4S_20x_850	0x2052
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_550	0x2060
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_650	0x2061
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_850	0x2062
+#define PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL	0x2050
+
+#define PCI_VENDOR_ID_RADISYS		0x1331
+
+#define PCI_VENDOR_ID_DOMEX		0x134a
+#define PCI_DEVICE_ID_DOMEX_DMX3191D	0x0001
+
+#define PCI_VENDOR_ID_QUATECH		0x135C
+#define PCI_DEVICE_ID_QUATECH_QSC100	0x0010
+#define PCI_DEVICE_ID_QUATECH_DSC100	0x0020
+#define PCI_DEVICE_ID_QUATECH_ESC100D	0x0050
+#define PCI_DEVICE_ID_QUATECH_ESC100M	0x0060
+
+#define PCI_VENDOR_ID_SEALEVEL		0x135e
+#define PCI_DEVICE_ID_SEALEVEL_U530	0x7101
+#define PCI_DEVICE_ID_SEALEVEL_UCOMM2	0x7201
+#define PCI_DEVICE_ID_SEALEVEL_UCOMM422	0x7402
+#define PCI_DEVICE_ID_SEALEVEL_UCOMM232	0x7202
+#define PCI_DEVICE_ID_SEALEVEL_COMM4	0x7401
+#define PCI_DEVICE_ID_SEALEVEL_COMM8	0x7801
+#define PCI_DEVICE_ID_SEALEVEL_UCOMM8	0x7804
+
+#define PCI_VENDOR_ID_HYPERCOPE		0x1365
+#define PCI_DEVICE_ID_HYPERCOPE_PLX	0x9050
+#define PCI_SUBDEVICE_ID_HYPERCOPE_OLD_ERGO	0x0104
+#define PCI_SUBDEVICE_ID_HYPERCOPE_ERGO		0x0106
+#define PCI_SUBDEVICE_ID_HYPERCOPE_METRO	0x0107
+#define PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2	0x0108
+
+#define PCI_VENDOR_ID_KAWASAKI		0x136b
+#define PCI_DEVICE_ID_MCHIP_KL5A72002	0xff01
+
+#define PCI_VENDOR_ID_CNET		0x1371
+#define PCI_DEVICE_ID_CNET_GIGACARD	0x434e
+
+#define PCI_VENDOR_ID_LMC		0x1376
+#define PCI_DEVICE_ID_LMC_HSSI		0x0003
+#define PCI_DEVICE_ID_LMC_DS3		0x0004
+#define PCI_DEVICE_ID_LMC_SSI		0x0005
+#define PCI_DEVICE_ID_LMC_T1		0x0006
+
+
+#define PCI_VENDOR_ID_NETGEAR		0x1385
+#define PCI_DEVICE_ID_NETGEAR_GA620	0x620a
+
+#define PCI_VENDOR_ID_APPLICOM		0x1389
+#define PCI_DEVICE_ID_APPLICOM_PCIGENERIC 0x0001
+#define PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN 0x0002
+#define PCI_DEVICE_ID_APPLICOM_PCI2000PFB 0x0003
+
+#define PCI_VENDOR_ID_MOXA		0x1393
+#define PCI_DEVICE_ID_MOXA_RC7000	0x0001
+#define PCI_DEVICE_ID_MOXA_CP102	0x1020
+#define PCI_DEVICE_ID_MOXA_CP102UL	0x1021
+#define PCI_DEVICE_ID_MOXA_CP102U	0x1022
+#define PCI_DEVICE_ID_MOXA_C104		0x1040
+#define PCI_DEVICE_ID_MOXA_CP104U	0x1041
+#define PCI_DEVICE_ID_MOXA_CP104JU	0x1042
+#define PCI_DEVICE_ID_MOXA_CT114	0x1140
+#define PCI_DEVICE_ID_MOXA_CP114	0x1141
+#define PCI_DEVICE_ID_MOXA_CP118U	0x1180
+#define PCI_DEVICE_ID_MOXA_CP132	0x1320
+#define PCI_DEVICE_ID_MOXA_CP132U	0x1321
+#define PCI_DEVICE_ID_MOXA_CP134U	0x1340
+#define PCI_DEVICE_ID_MOXA_C168		0x1680
+#define PCI_DEVICE_ID_MOXA_CP168U	0x1681
+
+#define PCI_VENDOR_ID_CCD		0x1397
+#define PCI_DEVICE_ID_CCD_2BD0		0x2bd0
+#define PCI_DEVICE_ID_CCD_B000		0xb000
+#define PCI_DEVICE_ID_CCD_B006		0xb006
+#define PCI_DEVICE_ID_CCD_B007		0xb007
+#define PCI_DEVICE_ID_CCD_B008		0xb008
+#define PCI_DEVICE_ID_CCD_B009		0xb009
+#define PCI_DEVICE_ID_CCD_B00A		0xb00a
+#define PCI_DEVICE_ID_CCD_B00B		0xb00b
+#define PCI_DEVICE_ID_CCD_B00C		0xb00c
+#define PCI_DEVICE_ID_CCD_B100		0xb100
+
+#define PCI_VENDOR_ID_EXAR		0x13a8
+#define PCI_DEVICE_ID_EXAR_XR17C152	0x0152
+#define PCI_DEVICE_ID_EXAR_XR17C154	0x0154
+#define PCI_DEVICE_ID_EXAR_XR17C158	0x0158
+
+#define PCI_VENDOR_ID_MICROGATE		0x13c0
+#define PCI_DEVICE_ID_MICROGATE_USC	0x0010
+#define PCI_DEVICE_ID_MICROGATE_SCA	0x0030
+
+#define PCI_VENDOR_ID_3WARE		0x13C1
+#define PCI_DEVICE_ID_3WARE_1000	0x1000
+#define PCI_DEVICE_ID_3WARE_7000	0x1001
+#define PCI_DEVICE_ID_3WARE_9000	0x1002
+
+#define PCI_VENDOR_ID_IOMEGA		0x13ca
+#define PCI_DEVICE_ID_IOMEGA_BUZ	0x4231
+
+#define PCI_VENDOR_ID_ABOCOM		0x13D1
+#define PCI_DEVICE_ID_ABOCOM_2BD1       0x2BD1
+
+#define PCI_VENDOR_ID_CMEDIA		0x13f6
+#define PCI_DEVICE_ID_CMEDIA_CM8338A	0x0100
+#define PCI_DEVICE_ID_CMEDIA_CM8338B	0x0101
+#define PCI_DEVICE_ID_CMEDIA_CM8738	0x0111
+#define PCI_DEVICE_ID_CMEDIA_CM8738B	0x0112
+
+#define PCI_VENDOR_ID_LAVA		0x1407
+#define PCI_DEVICE_ID_LAVA_DSERIAL	0x0100 /* 2x 16550 */
+#define PCI_DEVICE_ID_LAVA_QUATRO_A	0x0101 /* 2x 16550, half of 4 port */
+#define PCI_DEVICE_ID_LAVA_QUATRO_B	0x0102 /* 2x 16550, half of 4 port */
+#define PCI_DEVICE_ID_LAVA_OCTO_A	0x0180 /* 4x 16550A, half of 8 port */
+#define PCI_DEVICE_ID_LAVA_OCTO_B	0x0181 /* 4x 16550A, half of 8 port */
+#define PCI_DEVICE_ID_LAVA_PORT_PLUS	0x0200 /* 2x 16650 */
+#define PCI_DEVICE_ID_LAVA_QUAD_A	0x0201 /* 2x 16650, half of 4 port */
+#define PCI_DEVICE_ID_LAVA_QUAD_B	0x0202 /* 2x 16650, half of 4 port */
+#define PCI_DEVICE_ID_LAVA_SSERIAL	0x0500 /* 1x 16550 */
+#define PCI_DEVICE_ID_LAVA_PORT_650	0x0600 /* 1x 16650 */
+#define PCI_DEVICE_ID_LAVA_PARALLEL	0x8000
+#define PCI_DEVICE_ID_LAVA_DUAL_PAR_A	0x8002 /* The Lava Dual Parallel is */
+#define PCI_DEVICE_ID_LAVA_DUAL_PAR_B	0x8003 /* two PCI devices on a card */
+#define PCI_DEVICE_ID_LAVA_BOCA_IOPPAR	0x8800
+
+#define PCI_VENDOR_ID_TIMEDIA		0x1409
+#define PCI_DEVICE_ID_TIMEDIA_1889	0x7168
+
+#define PCI_VENDOR_ID_ICE		0x1412
+#define PCI_DEVICE_ID_ICE_1712		0x1712
+#define PCI_DEVICE_ID_VT1724		0x1724
+
+#define PCI_VENDOR_ID_OXSEMI		0x1415
+#define PCI_DEVICE_ID_OXSEMI_12PCI840	0x8403
+#define PCI_DEVICE_ID_OXSEMI_16PCI954	0x9501
+#define PCI_DEVICE_ID_OXSEMI_16PCI95N	0x9511
+#define PCI_DEVICE_ID_OXSEMI_16PCI954PP	0x9513
+#define PCI_DEVICE_ID_OXSEMI_16PCI952	0x9521
+
+#define PCI_VENDOR_ID_SAMSUNG		0x144d
+
+
+#define PCI_VENDOR_ID_TITAN		0x14D2
+#define PCI_DEVICE_ID_TITAN_010L	0x8001
+#define PCI_DEVICE_ID_TITAN_100L	0x8010
+#define PCI_DEVICE_ID_TITAN_110L	0x8011
+#define PCI_DEVICE_ID_TITAN_200L	0x8020
+#define PCI_DEVICE_ID_TITAN_210L	0x8021
+#define PCI_DEVICE_ID_TITAN_400L	0x8040
+#define PCI_DEVICE_ID_TITAN_800L	0x8080
+#define PCI_DEVICE_ID_TITAN_100		0xA001
+#define PCI_DEVICE_ID_TITAN_200		0xA005
+#define PCI_DEVICE_ID_TITAN_400		0xA003
+#define PCI_DEVICE_ID_TITAN_800B	0xA004
+
+#define PCI_VENDOR_ID_PANACOM		0x14d4
+#define PCI_DEVICE_ID_PANACOM_QUADMODEM	0x0400
+#define PCI_DEVICE_ID_PANACOM_DUALMODEM	0x0402
+
+
+#define PCI_VENDOR_ID_AFAVLAB		0x14db
+#define PCI_DEVICE_ID_AFAVLAB_P028	0x2180
+#define PCI_DEVICE_ID_AFAVLAB_P030	0x2182
+
+#define PCI_VENDOR_ID_BROADCOM		0x14e4
+#define PCI_DEVICE_ID_TIGON3_5752	0x1600
+#define PCI_DEVICE_ID_TIGON3_5752M	0x1601
+#define PCI_DEVICE_ID_TIGON3_5700	0x1644
+#define PCI_DEVICE_ID_TIGON3_5701	0x1645
+#define PCI_DEVICE_ID_TIGON3_5702	0x1646
+#define PCI_DEVICE_ID_TIGON3_5703	0x1647
+#define PCI_DEVICE_ID_TIGON3_5704	0x1648
+#define PCI_DEVICE_ID_TIGON3_5704S_2	0x1649
+#define PCI_DEVICE_ID_NX2_5706		0x164a
+#define PCI_DEVICE_ID_NX2_5708		0x164c
+#define PCI_DEVICE_ID_TIGON3_5702FE	0x164d
+#define PCI_DEVICE_ID_TIGON3_5705	0x1653
+#define PCI_DEVICE_ID_TIGON3_5705_2	0x1654
+#define PCI_DEVICE_ID_TIGON3_5720	0x1658
+#define PCI_DEVICE_ID_TIGON3_5721	0x1659
+#define PCI_DEVICE_ID_TIGON3_5705M	0x165d
+#define PCI_DEVICE_ID_TIGON3_5705M_2	0x165e
+#define PCI_DEVICE_ID_TIGON3_5714	0x1668
+#define PCI_DEVICE_ID_TIGON3_5780	0x166a
+#define PCI_DEVICE_ID_TIGON3_5780S	0x166b
+#define PCI_DEVICE_ID_TIGON3_5705F	0x166e
+#define PCI_DEVICE_ID_TIGON3_5750	0x1676
+#define PCI_DEVICE_ID_TIGON3_5751	0x1677
+#define PCI_DEVICE_ID_TIGON3_5715	0x1678
+#define PCI_DEVICE_ID_TIGON3_5750M	0x167c
+#define PCI_DEVICE_ID_TIGON3_5751M	0x167d
+#define PCI_DEVICE_ID_TIGON3_5751F	0x167e
+#define PCI_DEVICE_ID_TIGON3_5782	0x1696
+#define PCI_DEVICE_ID_TIGON3_5788	0x169c
+#define PCI_DEVICE_ID_TIGON3_5789	0x169d
+#define PCI_DEVICE_ID_TIGON3_5702X	0x16a6
+#define PCI_DEVICE_ID_TIGON3_5703X	0x16a7
+#define PCI_DEVICE_ID_TIGON3_5704S	0x16a8
+#define PCI_DEVICE_ID_NX2_5706S		0x16aa
+#define PCI_DEVICE_ID_NX2_5708S		0x16ac
+#define PCI_DEVICE_ID_TIGON3_5702A3	0x16c6
+#define PCI_DEVICE_ID_TIGON3_5703A3	0x16c7
+#define PCI_DEVICE_ID_TIGON3_5781	0x16dd
+#define PCI_DEVICE_ID_TIGON3_5753	0x16f7
+#define PCI_DEVICE_ID_TIGON3_5753M	0x16fd
+#define PCI_DEVICE_ID_TIGON3_5753F	0x16fe
+#define PCI_DEVICE_ID_TIGON3_5901	0x170d
+#define PCI_DEVICE_ID_BCM4401B1		0x170c
+#define PCI_DEVICE_ID_TIGON3_5901_2	0x170e
+#define PCI_DEVICE_ID_BCM4401		0x4401
+#define PCI_DEVICE_ID_BCM4401B0		0x4402
+
+#define PCI_VENDOR_ID_TOPIC		0x151f
+#define PCI_DEVICE_ID_TOPIC_TP560	0x0000
+
+#define PCI_VENDOR_ID_ENE		0x1524
+#define PCI_DEVICE_ID_ENE_1211		0x1211
+#define PCI_DEVICE_ID_ENE_1225		0x1225
+#define PCI_DEVICE_ID_ENE_1410		0x1410
+#define PCI_DEVICE_ID_ENE_710		0x1411
+#define PCI_DEVICE_ID_ENE_712		0x1412
+#define PCI_DEVICE_ID_ENE_1420		0x1420
+#define PCI_DEVICE_ID_ENE_720		0x1421
+#define PCI_DEVICE_ID_ENE_722		0x1422
+
+#define PCI_VENDOR_ID_CHELSIO		0x1425
+
+
+#define PCI_VENDOR_ID_SYBA		0x1592
+#define PCI_DEVICE_ID_SYBA_2P_EPP	0x0782
+#define PCI_DEVICE_ID_SYBA_1P_ECP	0x0783
+
+#define PCI_VENDOR_ID_MORETON		0x15aa
+#define PCI_DEVICE_ID_RASTEL_2PORT	0x2000
+
+#define PCI_VENDOR_ID_ZOLTRIX		0x15b0
+#define PCI_DEVICE_ID_ZOLTRIX_2BD0	0x2bd0 
+
+#define PCI_VENDOR_ID_MELLANOX		0x15b3
+#define PCI_DEVICE_ID_MELLANOX_TAVOR	0x5a44
+#define PCI_DEVICE_ID_MELLANOX_ARBEL_COMPAT 0x6278
+#define PCI_DEVICE_ID_MELLANOX_ARBEL	0x6282
+#define PCI_DEVICE_ID_MELLANOX_SINAI_OLD 0x5e8c
+#define PCI_DEVICE_ID_MELLANOX_SINAI	0x6274
+
+#define PCI_VENDOR_ID_PDC		0x15e9
+
+
+#define PCI_VENDOR_ID_FARSITE           0x1619
+#define PCI_DEVICE_ID_FARSITE_T2P       0x0400
+#define PCI_DEVICE_ID_FARSITE_T4P       0x0440
+#define PCI_DEVICE_ID_FARSITE_T1U       0x0610
+#define PCI_DEVICE_ID_FARSITE_T2U       0x0620
+#define PCI_DEVICE_ID_FARSITE_T4U       0x0640
+#define PCI_DEVICE_ID_FARSITE_TE1       0x1610
+#define PCI_DEVICE_ID_FARSITE_TE1C      0x1612
+
+#define PCI_VENDOR_ID_SIBYTE		0x166d
+#define PCI_DEVICE_ID_BCM1250_HT	0x0002
+
+#define PCI_VENDOR_ID_NETCELL		0x169c
+#define PCI_DEVICE_ID_REVOLUTION	0x0044
+
+#define PCI_VENDOR_ID_LINKSYS		0x1737
+#define PCI_DEVICE_ID_LINKSYS_EG1064	0x1064
+
+#define PCI_VENDOR_ID_ALTIMA		0x173b
+#define PCI_DEVICE_ID_ALTIMA_AC1000	0x03e8
+#define PCI_DEVICE_ID_ALTIMA_AC1001	0x03e9
+#define PCI_DEVICE_ID_ALTIMA_AC9100	0x03ea
+#define PCI_DEVICE_ID_ALTIMA_AC1003	0x03eb
+
+#define PCI_VENDOR_ID_S2IO		0x17d5
+#define	PCI_DEVICE_ID_S2IO_WIN		0x5731
+#define	PCI_DEVICE_ID_S2IO_UNI		0x5831
+#define PCI_DEVICE_ID_HERC_WIN		0x5732
+#define PCI_DEVICE_ID_HERC_UNI		0x5832
+
+
+#define PCI_VENDOR_ID_SITECOM		0x182d
+#define PCI_DEVICE_ID_SITECOM_DC105V2	0x3069
+
+#define PCI_VENDOR_ID_TOPSPIN		0x1867
+
+#define PCI_VENDOR_ID_TDI               0x192E
+#define PCI_DEVICE_ID_TDI_EHCI          0x0101
+
+
+#define PCI_VENDOR_ID_TEKRAM		0x1de1
+#define PCI_DEVICE_ID_TEKRAM_DC290	0xdc29
+
+#define PCI_VENDOR_ID_HINT             0x3388
+#define PCI_DEVICE_ID_HINT_VXPROII_IDE 0x8013
+
+#define PCI_VENDOR_ID_3DLABS		0x3d3d
+#define PCI_DEVICE_ID_3DLABS_PERMEDIA2	0x0007
+#define PCI_DEVICE_ID_3DLABS_PERMEDIA2V	0x0009
+
+
+#define PCI_VENDOR_ID_AKS		0x416c
+#define PCI_DEVICE_ID_AKS_ALADDINCARD	0x0100
+
+
+
+#define PCI_VENDOR_ID_S3		0x5333
+#define PCI_DEVICE_ID_S3_TRIO		0x8811
+#define PCI_DEVICE_ID_S3_868		0x8880
+#define PCI_DEVICE_ID_S3_968		0x88f0
+#define PCI_DEVICE_ID_S3_SAVAGE4	0x8a25
+#define PCI_DEVICE_ID_S3_PROSAVAGE8	0x8d04
+#define PCI_DEVICE_ID_S3_SONICVIBES	0xca00
+
+#define PCI_VENDOR_ID_DUNORD		0x5544
+#define PCI_DEVICE_ID_DUNORD_I3000	0x0001
+
+
+#define PCI_VENDOR_ID_DCI		0x6666
+#define PCI_DEVICE_ID_DCI_PCCOM4	0x0001
+#define PCI_DEVICE_ID_DCI_PCCOM8	0x0002
+
+#define PCI_VENDOR_ID_INTEL		0x8086
+#define PCI_DEVICE_ID_INTEL_EESSC	0x0008
+#define PCI_DEVICE_ID_INTEL_PXHD_0	0x0320
+#define PCI_DEVICE_ID_INTEL_PXHD_1	0x0321
+#define PCI_DEVICE_ID_INTEL_PXH_0	0x0329
+#define PCI_DEVICE_ID_INTEL_PXH_1	0x032A
+#define PCI_DEVICE_ID_INTEL_PXHV	0x032C
+#define PCI_DEVICE_ID_INTEL_82375	0x0482
+#define PCI_DEVICE_ID_INTEL_82424	0x0483
+#define PCI_DEVICE_ID_INTEL_82378	0x0484
+#define PCI_DEVICE_ID_INTEL_I960	0x0960
+#define PCI_DEVICE_ID_INTEL_I960RM	0x0962
+#define PCI_DEVICE_ID_INTEL_82815_MC	0x1130
+#define PCI_DEVICE_ID_INTEL_82815_CGC	0x1132
+#define PCI_DEVICE_ID_INTEL_82092AA_0	0x1221
+#define PCI_DEVICE_ID_INTEL_7505_0	0x2550  
+#define PCI_DEVICE_ID_INTEL_7205_0	0x255d
+#define PCI_DEVICE_ID_INTEL_82437	0x122d
+#define PCI_DEVICE_ID_INTEL_82371FB_0	0x122e
+#define PCI_DEVICE_ID_INTEL_82371FB_1	0x1230
+#define PCI_DEVICE_ID_INTEL_82371MX	0x1234
+#define PCI_DEVICE_ID_INTEL_82441	0x1237
+#define PCI_DEVICE_ID_INTEL_82380FB	0x124b
+#define PCI_DEVICE_ID_INTEL_82439	0x1250
+#define PCI_DEVICE_ID_INTEL_80960_RP	0x1960
+#define PCI_DEVICE_ID_INTEL_82840_HB	0x1a21
+#define PCI_DEVICE_ID_INTEL_82845_HB	0x1a30
+#define PCI_DEVICE_ID_INTEL_82801AA_0	0x2410
+#define PCI_DEVICE_ID_INTEL_82801AA_1	0x2411
+#define PCI_DEVICE_ID_INTEL_82801AA_3	0x2413
+#define PCI_DEVICE_ID_INTEL_82801AA_5	0x2415
+#define PCI_DEVICE_ID_INTEL_82801AA_6	0x2416
+#define PCI_DEVICE_ID_INTEL_82801AA_8	0x2418
+#define PCI_DEVICE_ID_INTEL_82801AB_0	0x2420
+#define PCI_DEVICE_ID_INTEL_82801AB_1	0x2421
+#define PCI_DEVICE_ID_INTEL_82801AB_3	0x2423
+#define PCI_DEVICE_ID_INTEL_82801AB_5	0x2425
+#define PCI_DEVICE_ID_INTEL_82801AB_6	0x2426
+#define PCI_DEVICE_ID_INTEL_82801AB_8	0x2428
+#define PCI_DEVICE_ID_INTEL_82801BA_0	0x2440
+#define PCI_DEVICE_ID_INTEL_82801BA_2	0x2443
+#define PCI_DEVICE_ID_INTEL_82801BA_4	0x2445
+#define PCI_DEVICE_ID_INTEL_82801BA_6	0x2448
+#define PCI_DEVICE_ID_INTEL_82801BA_8	0x244a
+#define PCI_DEVICE_ID_INTEL_82801BA_9	0x244b
+#define PCI_DEVICE_ID_INTEL_82801BA_10	0x244c
+#define PCI_DEVICE_ID_INTEL_82801BA_11	0x244e
+#define PCI_DEVICE_ID_INTEL_82801E_0	0x2450
+#define PCI_DEVICE_ID_INTEL_82801E_11	0x245b
+#define PCI_DEVICE_ID_INTEL_82801CA_0	0x2480
+#define PCI_DEVICE_ID_INTEL_82801CA_3	0x2483
+#define PCI_DEVICE_ID_INTEL_82801CA_5	0x2485
+#define PCI_DEVICE_ID_INTEL_82801CA_6	0x2486
+#define PCI_DEVICE_ID_INTEL_82801CA_10	0x248a
+#define PCI_DEVICE_ID_INTEL_82801CA_11	0x248b
+#define PCI_DEVICE_ID_INTEL_82801CA_12	0x248c
+#define PCI_DEVICE_ID_INTEL_82801DB_0	0x24c0
+#define PCI_DEVICE_ID_INTEL_82801DB_1	0x24c1
+#define PCI_DEVICE_ID_INTEL_82801DB_3	0x24c3
+#define PCI_DEVICE_ID_INTEL_82801DB_5	0x24c5
+#define PCI_DEVICE_ID_INTEL_82801DB_6	0x24c6
+#define PCI_DEVICE_ID_INTEL_82801DB_9	0x24c9
+#define PCI_DEVICE_ID_INTEL_82801DB_10	0x24ca
+#define PCI_DEVICE_ID_INTEL_82801DB_11	0x24cb
+#define PCI_DEVICE_ID_INTEL_82801DB_12  0x24cc
+#define PCI_DEVICE_ID_INTEL_82801EB_0	0x24d0
+#define PCI_DEVICE_ID_INTEL_82801EB_1	0x24d1
+#define PCI_DEVICE_ID_INTEL_82801EB_3	0x24d3
+#define PCI_DEVICE_ID_INTEL_82801EB_5	0x24d5
+#define PCI_DEVICE_ID_INTEL_82801EB_6	0x24d6
+#define PCI_DEVICE_ID_INTEL_82801EB_11	0x24db
+#define PCI_DEVICE_ID_INTEL_ESB_1	0x25a1
+#define PCI_DEVICE_ID_INTEL_ESB_2	0x25a2
+#define PCI_DEVICE_ID_INTEL_ESB_4	0x25a4
+#define PCI_DEVICE_ID_INTEL_ESB_5	0x25a6
+#define PCI_DEVICE_ID_INTEL_ESB_9	0x25ab
+#define PCI_DEVICE_ID_INTEL_82820_HB	0x2500
+#define PCI_DEVICE_ID_INTEL_82820_UP_HB	0x2501
+#define PCI_DEVICE_ID_INTEL_82850_HB	0x2530
+#define PCI_DEVICE_ID_INTEL_82860_HB	0x2531
+#define PCI_DEVICE_ID_INTEL_82845G_HB	0x2560
+#define PCI_DEVICE_ID_INTEL_82845G_IG	0x2562
+#define PCI_DEVICE_ID_INTEL_82865_HB	0x2570
+#define PCI_DEVICE_ID_INTEL_82865_IG	0x2572
+#define PCI_DEVICE_ID_INTEL_82875_HB	0x2578
+#define PCI_DEVICE_ID_INTEL_82915G_HB	0x2580
+#define PCI_DEVICE_ID_INTEL_82915G_IG	0x2582
+#define PCI_DEVICE_ID_INTEL_82915GM_HB	0x2590
+#define PCI_DEVICE_ID_INTEL_82915GM_IG	0x2592
+#define PCI_DEVICE_ID_INTEL_82945G_HB	0x2770
+#define PCI_DEVICE_ID_INTEL_82945G_IG	0x2772
+#define PCI_DEVICE_ID_INTEL_ICH6_0	0x2640
+#define PCI_DEVICE_ID_INTEL_ICH6_1	0x2641
+#define PCI_DEVICE_ID_INTEL_ICH6_2	0x2642
+#define PCI_DEVICE_ID_INTEL_ICH6_16	0x266a
+#define PCI_DEVICE_ID_INTEL_ICH6_17	0x266d
+#define PCI_DEVICE_ID_INTEL_ICH6_18	0x266e
+#define PCI_DEVICE_ID_INTEL_ICH6_19	0x266f
+#define PCI_DEVICE_ID_INTEL_ESB2_0	0x2670
+#define PCI_DEVICE_ID_INTEL_ESB2_14	0x2698
+#define PCI_DEVICE_ID_INTEL_ESB2_17	0x269b
+#define PCI_DEVICE_ID_INTEL_ESB2_18	0x269e
+#define PCI_DEVICE_ID_INTEL_ICH7_0	0x27b8
+#define PCI_DEVICE_ID_INTEL_ICH7_1	0x27b9
+#define PCI_DEVICE_ID_INTEL_ICH7_30	0x27b0
+#define PCI_DEVICE_ID_INTEL_ICH7_31	0x27bd
+#define PCI_DEVICE_ID_INTEL_ICH7_17	0x27da
+#define PCI_DEVICE_ID_INTEL_ICH7_19	0x27dd
+#define PCI_DEVICE_ID_INTEL_ICH7_20	0x27de
+#define PCI_DEVICE_ID_INTEL_ICH7_21	0x27df
+#define PCI_DEVICE_ID_INTEL_82855PM_HB	0x3340
+#define PCI_DEVICE_ID_INTEL_82830_HB	0x3575
+#define PCI_DEVICE_ID_INTEL_82830_CGC	0x3577
+#define PCI_DEVICE_ID_INTEL_82855GM_HB	0x3580
+#define PCI_DEVICE_ID_INTEL_82855GM_IG	0x3582
+#define PCI_DEVICE_ID_INTEL_E7520_MCH	0x3590
+#define PCI_DEVICE_ID_INTEL_E7320_MCH	0x3592
+#define PCI_DEVICE_ID_INTEL_MCH_PA	0x3595
+#define PCI_DEVICE_ID_INTEL_MCH_PA1	0x3596
+#define PCI_DEVICE_ID_INTEL_MCH_PB	0x3597
+#define PCI_DEVICE_ID_INTEL_MCH_PB1	0x3598
+#define PCI_DEVICE_ID_INTEL_MCH_PC	0x3599
+#define PCI_DEVICE_ID_INTEL_MCH_PC1	0x359a
+#define PCI_DEVICE_ID_INTEL_E7525_MCH	0x359e
+#define PCI_DEVICE_ID_INTEL_82371SB_0	0x7000
+#define PCI_DEVICE_ID_INTEL_82371SB_1	0x7010
+#define PCI_DEVICE_ID_INTEL_82371SB_2	0x7020
+#define PCI_DEVICE_ID_INTEL_82437VX	0x7030
+#define PCI_DEVICE_ID_INTEL_82439TX	0x7100
+#define PCI_DEVICE_ID_INTEL_82371AB_0	0x7110
+#define PCI_DEVICE_ID_INTEL_82371AB	0x7111
+#define PCI_DEVICE_ID_INTEL_82371AB_2	0x7112
+#define PCI_DEVICE_ID_INTEL_82371AB_3	0x7113
+#define PCI_DEVICE_ID_INTEL_82810_MC1	0x7120
+#define PCI_DEVICE_ID_INTEL_82810_IG1	0x7121
+#define PCI_DEVICE_ID_INTEL_82810_MC3	0x7122
+#define PCI_DEVICE_ID_INTEL_82810_IG3	0x7123
+#define PCI_DEVICE_ID_INTEL_82810E_MC	0x7124
+#define PCI_DEVICE_ID_INTEL_82810E_IG	0x7125
+#define PCI_DEVICE_ID_INTEL_82443LX_0	0x7180
+#define PCI_DEVICE_ID_INTEL_82443LX_1	0x7181
+#define PCI_DEVICE_ID_INTEL_82443BX_0	0x7190
+#define PCI_DEVICE_ID_INTEL_82443BX_1	0x7191
+#define PCI_DEVICE_ID_INTEL_82443BX_2	0x7192
+#define PCI_DEVICE_ID_INTEL_440MX	0x7195
+#define PCI_DEVICE_ID_INTEL_440MX_6	0x7196
+#define PCI_DEVICE_ID_INTEL_82443MX_0	0x7198
+#define PCI_DEVICE_ID_INTEL_82443MX_1	0x7199
+#define PCI_DEVICE_ID_INTEL_82443MX_3	0x719b
+#define PCI_DEVICE_ID_INTEL_82443GX_0	0x71a0
+#define PCI_DEVICE_ID_INTEL_82443GX_2	0x71a2
+#define PCI_DEVICE_ID_INTEL_82372FB_1	0x7601
+#define PCI_DEVICE_ID_INTEL_82454GX	0x84c4
+#define PCI_DEVICE_ID_INTEL_82451NX	0x84ca
+#define PCI_DEVICE_ID_INTEL_82454NX     0x84cb
+#define PCI_DEVICE_ID_INTEL_84460GX	0x84ea
+#define PCI_DEVICE_ID_INTEL_IXP4XX	0x8500
+#define PCI_DEVICE_ID_INTEL_IXP2800	0x9004
+#define PCI_DEVICE_ID_INTEL_S21152BB	0xb152
+
+#define PCI_VENDOR_ID_COMPUTONE		0x8e0e
+#define PCI_DEVICE_ID_COMPUTONE_IP2EX	0x0291
+#define PCI_DEVICE_ID_COMPUTONE_PG	0x0302
+#define PCI_SUBVENDOR_ID_COMPUTONE	0x8e0e
+#define PCI_SUBDEVICE_ID_COMPUTONE_PG4	0x0001
+#define PCI_SUBDEVICE_ID_COMPUTONE_PG8	0x0002
+#define PCI_SUBDEVICE_ID_COMPUTONE_PG6	0x0003
+
+#define PCI_VENDOR_ID_KTI		0x8e2e
+
+#define PCI_VENDOR_ID_ADAPTEC		0x9004
+#define PCI_DEVICE_ID_ADAPTEC_7810	0x1078
+#define PCI_DEVICE_ID_ADAPTEC_7821	0x2178
+#define PCI_DEVICE_ID_ADAPTEC_38602	0x3860
+#define PCI_DEVICE_ID_ADAPTEC_7850	0x5078
+#define PCI_DEVICE_ID_ADAPTEC_7855	0x5578
+#define PCI_DEVICE_ID_ADAPTEC_3860	0x6038
+#define PCI_DEVICE_ID_ADAPTEC_1480A	0x6075
+#define PCI_DEVICE_ID_ADAPTEC_7860	0x6078
+#define PCI_DEVICE_ID_ADAPTEC_7861	0x6178
+#define PCI_DEVICE_ID_ADAPTEC_7870	0x7078
+#define PCI_DEVICE_ID_ADAPTEC_7871	0x7178
+#define PCI_DEVICE_ID_ADAPTEC_7872	0x7278
+#define PCI_DEVICE_ID_ADAPTEC_7873	0x7378
+#define PCI_DEVICE_ID_ADAPTEC_7874	0x7478
+#define PCI_DEVICE_ID_ADAPTEC_7895	0x7895
+#define PCI_DEVICE_ID_ADAPTEC_7880	0x8078
+#define PCI_DEVICE_ID_ADAPTEC_7881	0x8178
+#define PCI_DEVICE_ID_ADAPTEC_7882	0x8278
+#define PCI_DEVICE_ID_ADAPTEC_7883	0x8378
+#define PCI_DEVICE_ID_ADAPTEC_7884	0x8478
+#define PCI_DEVICE_ID_ADAPTEC_7885	0x8578
+#define PCI_DEVICE_ID_ADAPTEC_7886	0x8678
+#define PCI_DEVICE_ID_ADAPTEC_7887	0x8778
+#define PCI_DEVICE_ID_ADAPTEC_7888	0x8878
+
+#define PCI_VENDOR_ID_ADAPTEC2		0x9005
+#define PCI_DEVICE_ID_ADAPTEC2_2940U2	0x0010
+#define PCI_DEVICE_ID_ADAPTEC2_2930U2	0x0011
+#define PCI_DEVICE_ID_ADAPTEC2_7890B	0x0013
+#define PCI_DEVICE_ID_ADAPTEC2_7890	0x001f
+#define PCI_DEVICE_ID_ADAPTEC2_3940U2	0x0050
+#define PCI_DEVICE_ID_ADAPTEC2_3950U2D	0x0051
+#define PCI_DEVICE_ID_ADAPTEC2_7896	0x005f
+#define PCI_DEVICE_ID_ADAPTEC2_7892A	0x0080
+#define PCI_DEVICE_ID_ADAPTEC2_7892B	0x0081
+#define PCI_DEVICE_ID_ADAPTEC2_7892D	0x0083
+#define PCI_DEVICE_ID_ADAPTEC2_7892P	0x008f
+#define PCI_DEVICE_ID_ADAPTEC2_7899A	0x00c0
+#define PCI_DEVICE_ID_ADAPTEC2_7899B	0x00c1
+#define PCI_DEVICE_ID_ADAPTEC2_7899D	0x00c3
+#define PCI_DEVICE_ID_ADAPTEC2_7899P	0x00cf
+#define PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN   0x0500
+#define PCI_DEVICE_ID_ADAPTEC2_SCAMP	0x0503
+
+
+#define PCI_VENDOR_ID_HOLTEK		0x9412
+#define PCI_DEVICE_ID_HOLTEK_6565	0x6565
+
+#define PCI_VENDOR_ID_NETMOS		0x9710
+#define PCI_DEVICE_ID_NETMOS_9705	0x9705
+#define PCI_DEVICE_ID_NETMOS_9715	0x9715
+#define PCI_DEVICE_ID_NETMOS_9735	0x9735
+#define PCI_DEVICE_ID_NETMOS_9745	0x9745
+#define PCI_DEVICE_ID_NETMOS_9755	0x9755
+#define PCI_DEVICE_ID_NETMOS_9805	0x9805
+#define PCI_DEVICE_ID_NETMOS_9815	0x9815
+#define PCI_DEVICE_ID_NETMOS_9835	0x9835
+#define PCI_DEVICE_ID_NETMOS_9845	0x9845
+#define PCI_DEVICE_ID_NETMOS_9855	0x9855
+
+#define PCI_SUBVENDOR_ID_EXSYS		0xd84d
+#define PCI_SUBDEVICE_ID_EXSYS_4014	0x4014
+#define PCI_SUBDEVICE_ID_EXSYS_4055	0x4055
+
+#define PCI_VENDOR_ID_TIGERJET		0xe159
+#define PCI_DEVICE_ID_TIGERJET_300	0x0001
+#define PCI_DEVICE_ID_TIGERJET_100	0x0002
+
+#define PCI_VENDOR_ID_TTTECH		0x0357
+#define PCI_DEVICE_ID_TTTECH_MC322	0x000A
+
+#define PCI_VENDOR_ID_XILINX_RME	0xea60
+#define PCI_DEVICE_ID_RME_DIGI32	0x9896
+#define PCI_DEVICE_ID_RME_DIGI32_PRO	0x9897
+#define PCI_DEVICE_ID_RME_DIGI32_8	0x9898
+
diff -urN oldtree/include/linux/poll.h newtree/include/linux/poll.h
--- oldtree/include/linux/poll.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/poll.h	2006-02-13 19:20:01.246322832 -0500
@@ -11,6 +11,15 @@
 #include <linux/mm.h>
 #include <asm/uaccess.h>
 
+/* ~832 bytes of stack space used max in sys_select/sys_poll before allocating
+   additional memory. */
+#define MAX_STACK_ALLOC 832
+#define FRONTEND_STACK_ALLOC  256
+#define SELECT_STACK_ALLOC    FRONTEND_STACK_ALLOC
+#define POLL_STACK_ALLOC      FRONTEND_STACK_ALLOC
+#define WQUEUES_STACK_ALLOC   (MAX_STACK_ALLOC - FRONTEND_STACK_ALLOC)
+#define N_INLINE_POLL_ENTRIES (WQUEUES_STACK_ALLOC / sizeof(struct poll_table_entry))
+
 struct poll_table_struct;
 
 /* 
@@ -33,6 +42,12 @@
 	pt->qproc = qproc;
 }
 
+struct poll_table_entry {
+	struct file * filp;
+	wait_queue_t wait;
+	wait_queue_head_t * wait_address;
+};
+
 /*
  * Structures and helpers for sys_poll/sys_poll
  */
@@ -40,6 +55,8 @@
 	poll_table pt;
 	struct poll_table_page * table;
 	int error;
+	int inline_index;
+	struct poll_table_entry inline_entries[N_INLINE_POLL_ENTRIES];
 };
 
 extern void poll_initwait(struct poll_wqueues *pwq);
diff -urN oldtree/include/linux/proc_fs.h newtree/include/linux/proc_fs.h
--- oldtree/include/linux/proc_fs.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/proc_fs.h	2006-02-13 19:20:01.246322832 -0500
@@ -4,6 +4,7 @@
 #include <linux/config.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
+#include <linux/spinlock.h>
 #include <asm/atomic.h>
 
 /*
@@ -92,6 +93,8 @@
 extern struct proc_dir_entry *proc_root_driver;
 extern struct proc_dir_entry *proc_root_kcore;
 
+extern spinlock_t proc_subdir_lock;
+
 extern void proc_root_init(void);
 extern void proc_misc_init(void);
 
diff -urN oldtree/include/linux/reiserfs_fs.h newtree/include/linux/reiserfs_fs.h
--- oldtree/include/linux/reiserfs_fs.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/reiserfs_fs.h	2006-02-13 19:20:01.247322680 -0500
@@ -1971,22 +1971,6 @@
 extern struct address_space_operations reiserfs_address_space_operations;
 
 /* fix_nodes.c */
-#ifdef CONFIG_REISERFS_CHECK
-void *reiserfs_kmalloc(size_t size, gfp_t flags, struct super_block *s);
-void reiserfs_kfree(const void *vp, size_t size, struct super_block *s);
-#else
-static inline void *reiserfs_kmalloc(size_t size, int flags,
-				     struct super_block *s)
-{
-	return kmalloc(size, flags);
-}
-
-static inline void reiserfs_kfree(const void *vp, size_t size,
-				  struct super_block *s)
-{
-	kfree(vp);
-}
-#endif
 
 int fix_nodes(int n_op_mode, struct tree_balance *p_s_tb,
 	      struct item_head *p_s_ins_ih, const void *);
diff -urN oldtree/include/linux/reiserfs_fs_sb.h newtree/include/linux/reiserfs_fs_sb.h
--- oldtree/include/linux/reiserfs_fs_sb.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/reiserfs_fs_sb.h	2006-02-13 19:20:01.248322528 -0500
@@ -382,7 +382,6 @@
 					   on-disk FS format */
 
 	/* session statistics */
-	int s_kmallocs;
 	int s_disk_reads;
 	int s_disk_writes;
 	int s_fix_nodes;
diff -urN oldtree/include/linux/sched.h newtree/include/linux/sched.h
--- oldtree/include/linux/sched.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/sched.h	2006-02-13 19:20:01.248322528 -0500
@@ -38,6 +38,7 @@
 #include <linux/auxvec.h>	/* For AT_VECTOR_SIZE */
 
 struct exec_domain;
+struct bio;
 
 /*
  * cloning flags:
@@ -714,6 +715,8 @@
 	struct sched_info sched_info;
 #endif
 
+	seccomp_t seccomp;
+
 	struct list_head tasks;
 	/*
 	 * ptrace_list/ptrace_children forms the list of my children
@@ -810,7 +813,6 @@
 	
 	void *security;
 	struct audit_context *audit_context;
-	seccomp_t seccomp;
 
 /* Thread group tracking */
    	u32 parent_exec_id;
@@ -823,6 +825,9 @@
 /* journalling filesystem info */
 	void *journal_info;
 
+/* stacked block device info */
+	struct bio *bio_list, **bio_tail;
+
 /* VM state */
 	struct reclaim_state *reclaim_state;
 
diff -urN oldtree/include/linux/sched.h.orig newtree/include/linux/sched.h.orig
--- oldtree/include/linux/sched.h.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/linux/sched.h.orig	2006-02-13 19:20:01.251322072 -0500
@@ -0,0 +1,1467 @@
+#ifndef _LINUX_SCHED_H
+#define _LINUX_SCHED_H
+
+#include <asm/param.h>	/* for HZ */
+
+#include <linux/config.h>
+#include <linux/capability.h>
+#include <linux/threads.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/timex.h>
+#include <linux/jiffies.h>
+#include <linux/rbtree.h>
+#include <linux/thread_info.h>
+#include <linux/cpumask.h>
+#include <linux/errno.h>
+#include <linux/nodemask.h>
+
+#include <asm/system.h>
+#include <asm/semaphore.h>
+#include <asm/page.h>
+#include <asm/ptrace.h>
+#include <asm/mmu.h>
+#include <asm/cputime.h>
+
+#include <linux/smp.h>
+#include <linux/sem.h>
+#include <linux/signal.h>
+#include <linux/securebits.h>
+#include <linux/fs_struct.h>
+#include <linux/compiler.h>
+#include <linux/completion.h>
+#include <linux/pid.h>
+#include <linux/percpu.h>
+#include <linux/topology.h>
+#include <linux/seccomp.h>
+
+#include <linux/auxvec.h>	/* For AT_VECTOR_SIZE */
+
+struct exec_domain;
+struct bio;
+
+/*
+ * cloning flags:
+ */
+#define CSIGNAL		0x000000ff	/* signal mask to be sent at exit */
+#define CLONE_VM	0x00000100	/* set if VM shared between processes */
+#define CLONE_FS	0x00000200	/* set if fs info shared between processes */
+#define CLONE_FILES	0x00000400	/* set if open files shared between processes */
+#define CLONE_SIGHAND	0x00000800	/* set if signal handlers and blocked signals shared */
+#define CLONE_PTRACE	0x00002000	/* set if we want to let tracing continue on the child too */
+#define CLONE_VFORK	0x00004000	/* set if the parent wants the child to wake it up on mm_release */
+#define CLONE_PARENT	0x00008000	/* set if we want to have the same parent as the cloner */
+#define CLONE_THREAD	0x00010000	/* Same thread group? */
+#define CLONE_NEWNS	0x00020000	/* New namespace group? */
+#define CLONE_SYSVSEM	0x00040000	/* share system V SEM_UNDO semantics */
+#define CLONE_SETTLS	0x00080000	/* create a new TLS for the child */
+#define CLONE_PARENT_SETTID	0x00100000	/* set the TID in the parent */
+#define CLONE_CHILD_CLEARTID	0x00200000	/* clear the TID in the child */
+#define CLONE_DETACHED		0x00400000	/* Unused, ignored */
+#define CLONE_UNTRACED		0x00800000	/* set if the tracing process can't force CLONE_PTRACE on this clone */
+#define CLONE_CHILD_SETTID	0x01000000	/* set the TID in the child */
+#define CLONE_STOPPED		0x02000000	/* Start in stopped state */
+
+/*
+ * List of flags we want to share for kernel threads,
+ * if only because they are not used by them anyway.
+ */
+#define CLONE_KERNEL	(CLONE_FS | CLONE_FILES | CLONE_SIGHAND)
+
+/*
+ * These are the constant used to fake the fixed-point load-average
+ * counting. Some notes:
+ *  - 11 bit fractions expand to 22 bits by the multiplies: this gives
+ *    a load-average precision of 10 bits integer + 11 bits fractional
+ *  - if you want to count load-averages more often, you need more
+ *    precision, or rounding will get you. With 2-second counting freq,
+ *    the EXP_n values would be 1981, 2034 and 2043 if still using only
+ *    11 bit fractions.
+ */
+extern unsigned long avenrun[];		/* Load averages */
+
+#define FSHIFT		11		/* nr of bits of precision */
+#define FIXED_1		(1<<FSHIFT)	/* 1.0 as fixed-point */
+#define LOAD_FREQ	(5*HZ)		/* 5 sec intervals */
+#define EXP_1		1884		/* 1/exp(5sec/1min) as fixed-point */
+#define EXP_5		2014		/* 1/exp(5sec/5min) */
+#define EXP_15		2037		/* 1/exp(5sec/15min) */
+
+#define CALC_LOAD(load,exp,n) \
+	load *= exp; \
+	load += n*(FIXED_1-exp); \
+	load >>= FSHIFT;
+
+extern unsigned long total_forks;
+extern int nr_threads;
+extern int last_pid;
+DECLARE_PER_CPU(unsigned long, process_counts);
+extern int nr_processes(void);
+extern unsigned long nr_running(void);
+extern unsigned long nr_uninterruptible(void);
+extern unsigned long nr_iowait(void);
+
+#include <linux/time.h>
+#include <linux/param.h>
+#include <linux/resource.h>
+#include <linux/timer.h>
+
+#include <asm/processor.h>
+
+/*
+ * Task state bitmask. NOTE! These bits are also
+ * encoded in fs/proc/array.c: get_task_state().
+ *
+ * We have two separate sets of flags: task->state
+ * is about runnability, while task->exit_state are
+ * about the task exiting. Confusing, but this way
+ * modifying one set can't modify the other one by
+ * mistake.
+ */
+#define TASK_RUNNING		0
+#define TASK_INTERRUPTIBLE	1
+#define TASK_UNINTERRUPTIBLE	2
+#define TASK_STOPPED		4
+#define TASK_TRACED		8
+/* in tsk->exit_state */
+#define EXIT_ZOMBIE		16
+#define EXIT_DEAD		32
+/* in tsk->state again */
+#define TASK_NONINTERACTIVE	64
+
+#define __set_task_state(tsk, state_value)		\
+	do { (tsk)->state = (state_value); } while (0)
+#define set_task_state(tsk, state_value)		\
+	set_mb((tsk)->state, (state_value))
+
+/*
+ * set_current_state() includes a barrier so that the write of current->state
+ * is correctly serialised wrt the caller's subsequent test of whether to
+ * actually sleep:
+ *
+ *	set_current_state(TASK_UNINTERRUPTIBLE);
+ *	if (do_i_need_to_sleep())
+ *		schedule();
+ *
+ * If the caller does not need such serialisation then use __set_current_state()
+ */
+#define __set_current_state(state_value)			\
+	do { current->state = (state_value); } while (0)
+#define set_current_state(state_value)		\
+	set_mb(current->state, (state_value))
+
+/* Task command name length */
+#define TASK_COMM_LEN 16
+
+/*
+ * Scheduling policies
+ */
+#define SCHED_NORMAL		0
+#define SCHED_FIFO		1
+#define SCHED_RR		2
+
+struct sched_param {
+	int sched_priority;
+};
+
+#ifdef __KERNEL__
+
+#include <linux/spinlock.h>
+
+/*
+ * This serializes "schedule()" and also protects
+ * the run-queue from deletions/modifications (but
+ * _adding_ to the beginning of the run-queue has
+ * a separate lock).
+ */
+extern rwlock_t tasklist_lock;
+extern spinlock_t mmlist_lock;
+
+typedef struct task_struct task_t;
+
+extern void sched_init(void);
+extern void sched_init_smp(void);
+extern void init_idle(task_t *idle, int cpu);
+
+extern cpumask_t nohz_cpu_mask;
+
+extern void show_state(void);
+extern void show_regs(struct pt_regs *);
+
+/*
+ * TASK is a pointer to the task whose backtrace we want to see (or NULL for current
+ * task), SP is the stack pointer of the first frame that should be shown in the back
+ * trace (or NULL if the entire call-chain of the task should be shown).
+ */
+extern void show_stack(struct task_struct *task, unsigned long *sp);
+
+void io_schedule(void);
+long io_schedule_timeout(long timeout);
+
+extern void cpu_init (void);
+extern void trap_init(void);
+extern void update_process_times(int user);
+extern void scheduler_tick(void);
+
+#ifdef CONFIG_DETECT_SOFTLOCKUP
+extern void softlockup_tick(struct pt_regs *regs);
+extern void spawn_softlockup_task(void);
+extern void touch_softlockup_watchdog(void);
+#else
+static inline void softlockup_tick(struct pt_regs *regs)
+{
+}
+static inline void spawn_softlockup_task(void)
+{
+}
+static inline void touch_softlockup_watchdog(void)
+{
+}
+#endif
+
+
+/* Attach to any functions which should be ignored in wchan output. */
+#define __sched		__attribute__((__section__(".sched.text")))
+/* Is this address in the __sched functions? */
+extern int in_sched_functions(unsigned long addr);
+
+#define	MAX_SCHEDULE_TIMEOUT	LONG_MAX
+extern signed long FASTCALL(schedule_timeout(signed long timeout));
+extern signed long schedule_timeout_interruptible(signed long timeout);
+extern signed long schedule_timeout_uninterruptible(signed long timeout);
+asmlinkage void schedule(void);
+
+struct namespace;
+
+/* Maximum number of active map areas.. This is a random (large) number */
+#define DEFAULT_MAX_MAP_COUNT	65536
+
+extern int sysctl_max_map_count;
+
+#include <linux/aio.h>
+
+extern unsigned long
+arch_get_unmapped_area(struct file *, unsigned long, unsigned long,
+		       unsigned long, unsigned long);
+extern unsigned long
+arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
+			  unsigned long len, unsigned long pgoff,
+			  unsigned long flags);
+extern void arch_unmap_area(struct mm_struct *, unsigned long);
+extern void arch_unmap_area_topdown(struct mm_struct *, unsigned long);
+
+#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS
+/*
+ * The mm counters are not protected by its page_table_lock,
+ * so must be incremented atomically.
+ */
+#ifdef ATOMIC64_INIT
+#define set_mm_counter(mm, member, value) atomic64_set(&(mm)->_##member, value)
+#define get_mm_counter(mm, member) ((unsigned long)atomic64_read(&(mm)->_##member))
+#define add_mm_counter(mm, member, value) atomic64_add(value, &(mm)->_##member)
+#define inc_mm_counter(mm, member) atomic64_inc(&(mm)->_##member)
+#define dec_mm_counter(mm, member) atomic64_dec(&(mm)->_##member)
+typedef atomic64_t mm_counter_t;
+#else /* !ATOMIC64_INIT */
+/*
+ * The counters wrap back to 0 at 2^32 * PAGE_SIZE,
+ * that is, at 16TB if using 4kB page size.
+ */
+#define set_mm_counter(mm, member, value) atomic_set(&(mm)->_##member, value)
+#define get_mm_counter(mm, member) ((unsigned long)atomic_read(&(mm)->_##member))
+#define add_mm_counter(mm, member, value) atomic_add(value, &(mm)->_##member)
+#define inc_mm_counter(mm, member) atomic_inc(&(mm)->_##member)
+#define dec_mm_counter(mm, member) atomic_dec(&(mm)->_##member)
+typedef atomic_t mm_counter_t;
+#endif /* !ATOMIC64_INIT */
+
+#else  /* NR_CPUS < CONFIG_SPLIT_PTLOCK_CPUS */
+/*
+ * The mm counters are protected by its page_table_lock,
+ * so can be incremented directly.
+ */
+#define set_mm_counter(mm, member, value) (mm)->_##member = (value)
+#define get_mm_counter(mm, member) ((mm)->_##member)
+#define add_mm_counter(mm, member, value) (mm)->_##member += (value)
+#define inc_mm_counter(mm, member) (mm)->_##member++
+#define dec_mm_counter(mm, member) (mm)->_##member--
+typedef unsigned long mm_counter_t;
+
+#endif /* NR_CPUS < CONFIG_SPLIT_PTLOCK_CPUS */
+
+#define get_mm_rss(mm)					\
+	(get_mm_counter(mm, file_rss) + get_mm_counter(mm, anon_rss))
+#define update_hiwater_rss(mm)	do {			\
+	unsigned long _rss = get_mm_rss(mm);		\
+	if ((mm)->hiwater_rss < _rss)			\
+		(mm)->hiwater_rss = _rss;		\
+} while (0)
+#define update_hiwater_vm(mm)	do {			\
+	if ((mm)->hiwater_vm < (mm)->total_vm)		\
+		(mm)->hiwater_vm = (mm)->total_vm;	\
+} while (0)
+
+struct mm_struct {
+	struct vm_area_struct * mmap;		/* list of VMAs */
+	struct rb_root mm_rb;
+	struct vm_area_struct * mmap_cache;	/* last find_vma result */
+	unsigned long (*get_unmapped_area) (struct file *filp,
+				unsigned long addr, unsigned long len,
+				unsigned long pgoff, unsigned long flags);
+	void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
+        unsigned long mmap_base;		/* base of mmap area */
+        unsigned long cached_hole_size;         /* if non-zero, the largest hole below free_area_cache */
+	unsigned long free_area_cache;		/* first hole of size cached_hole_size or larger */
+	pgd_t * pgd;
+	atomic_t mm_users;			/* How many users with user space? */
+	atomic_t mm_count;			/* How many references to "struct mm_struct" (users count as 1) */
+	int map_count;				/* number of VMAs */
+	struct rw_semaphore mmap_sem;
+	spinlock_t page_table_lock;		/* Protects page tables and some counters */
+
+	struct list_head mmlist;		/* List of maybe swapped mm's.  These are globally strung
+						 * together off init_mm.mmlist, and are protected
+						 * by mmlist_lock
+						 */
+
+	/* Special counters, in some configurations protected by the
+	 * page_table_lock, in other configurations by being atomic.
+	 */
+	mm_counter_t _file_rss;
+	mm_counter_t _anon_rss;
+
+	unsigned long hiwater_rss;	/* High-watermark of RSS usage */
+	unsigned long hiwater_vm;	/* High-water virtual memory usage */
+
+	unsigned long total_vm, locked_vm, shared_vm, exec_vm;
+	unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;
+	unsigned long start_code, end_code, start_data, end_data;
+	unsigned long start_brk, brk, start_stack;
+	unsigned long arg_start, arg_end, env_start, env_end;
+
+	unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */
+
+	unsigned dumpable:2;
+	cpumask_t cpu_vm_mask;
+
+	/* Architecture-specific MM context */
+	mm_context_t context;
+
+	/* Token based thrashing protection. */
+	unsigned long swap_token_time;
+	char recent_pagein;
+
+	/* coredumping support */
+	int core_waiters;
+	struct completion *core_startup_done, core_done;
+
+	/* aio bits */
+	rwlock_t		ioctx_list_lock;
+	struct kioctx		*ioctx_list;
+};
+
+struct sighand_struct {
+	atomic_t		count;
+	struct k_sigaction	action[_NSIG];
+	spinlock_t		siglock;
+};
+
+/*
+ * NOTE! "signal_struct" does not have it's own
+ * locking, because a shared signal_struct always
+ * implies a shared sighand_struct, so locking
+ * sighand_struct is always a proper superset of
+ * the locking of signal_struct.
+ */
+struct signal_struct {
+	atomic_t		count;
+	atomic_t		live;
+
+	wait_queue_head_t	wait_chldexit;	/* for wait4() */
+
+	/* current thread group signal load-balancing target: */
+	task_t			*curr_target;
+
+	/* shared signal handling: */
+	struct sigpending	shared_pending;
+
+	/* thread group exit support */
+	int			group_exit_code;
+	/* overloaded:
+	 * - notify group_exit_task when ->count is equal to notify_count
+	 * - everyone except group_exit_task is stopped during signal delivery
+	 *   of fatal signals, group_exit_task processes the signal.
+	 */
+	struct task_struct	*group_exit_task;
+	int			notify_count;
+
+	/* thread group stop support, overloads group_exit_code too */
+	int			group_stop_count;
+	unsigned int		flags; /* see SIGNAL_* flags below */
+
+	/* POSIX.1b Interval Timers */
+	struct list_head posix_timers;
+
+	/* ITIMER_REAL timer for the process */
+	struct timer_list real_timer;
+	unsigned long it_real_value, it_real_incr;
+
+	/* ITIMER_PROF and ITIMER_VIRTUAL timers for the process */
+	cputime_t it_prof_expires, it_virt_expires;
+	cputime_t it_prof_incr, it_virt_incr;
+
+	/* job control IDs */
+	pid_t pgrp;
+	pid_t tty_old_pgrp;
+	pid_t session;
+	/* boolean value for session group leader */
+	int leader;
+
+	struct tty_struct *tty; /* NULL if no tty */
+
+	/*
+	 * Cumulative resource counters for dead threads in the group,
+	 * and for reaped dead child processes forked by this group.
+	 * Live threads maintain their own counters and add to these
+	 * in __exit_signal, except for the group leader.
+	 */
+	cputime_t utime, stime, cutime, cstime;
+	unsigned long nvcsw, nivcsw, cnvcsw, cnivcsw;
+	unsigned long min_flt, maj_flt, cmin_flt, cmaj_flt;
+
+	/*
+	 * Cumulative ns of scheduled CPU time for dead threads in the
+	 * group, not including a zombie group leader.  (This only differs
+	 * from jiffies_to_ns(utime + stime) if sched_clock uses something
+	 * other than jiffies.)
+	 */
+	unsigned long long sched_time;
+
+	/*
+	 * We don't bother to synchronize most readers of this at all,
+	 * because there is no reader checking a limit that actually needs
+	 * to get both rlim_cur and rlim_max atomically, and either one
+	 * alone is a single word that can safely be read normally.
+	 * getrlimit/setrlimit use task_lock(current->group_leader) to
+	 * protect this instead of the siglock, because they really
+	 * have no need to disable irqs.
+	 */
+	struct rlimit rlim[RLIM_NLIMITS];
+
+	struct list_head cpu_timers[3];
+
+	/* keep the process-shared keyrings here so that they do the right
+	 * thing in threads created with CLONE_THREAD */
+#ifdef CONFIG_KEYS
+	struct key *session_keyring;	/* keyring inherited over fork */
+	struct key *process_keyring;	/* keyring private to this process */
+#endif
+};
+
+/* Context switch must be unlocked if interrupts are to be enabled */
+#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW
+# define __ARCH_WANT_UNLOCKED_CTXSW
+#endif
+
+/*
+ * Bits in flags field of signal_struct.
+ */
+#define SIGNAL_STOP_STOPPED	0x00000001 /* job control stop in effect */
+#define SIGNAL_STOP_DEQUEUED	0x00000002 /* stop signal dequeued */
+#define SIGNAL_STOP_CONTINUED	0x00000004 /* SIGCONT since WCONTINUED reap */
+#define SIGNAL_GROUP_EXIT	0x00000008 /* group exit in progress */
+
+
+/*
+ * Priority of a process goes from 0..MAX_PRIO-1, valid RT
+ * priority is 0..MAX_RT_PRIO-1, and SCHED_NORMAL tasks are
+ * in the range MAX_RT_PRIO..MAX_PRIO-1. Priority values
+ * are inverted: lower p->prio value means higher priority.
+ *
+ * The MAX_USER_RT_PRIO value allows the actual maximum
+ * RT priority to be separate from the value exported to
+ * user-space.  This allows kernel threads to set their
+ * priority to a value higher than any user task. Note:
+ * MAX_RT_PRIO must not be smaller than MAX_USER_RT_PRIO.
+ */
+
+#define MAX_USER_RT_PRIO	100
+#define MAX_RT_PRIO		MAX_USER_RT_PRIO
+
+#define MAX_PRIO		(MAX_RT_PRIO + 40)
+
+#define rt_task(p)		(unlikely((p)->prio < MAX_RT_PRIO))
+
+/*
+ * Some day this will be a full-fledged user tracking system..
+ */
+struct user_struct {
+	atomic_t __count;	/* reference count */
+	atomic_t processes;	/* How many processes does this user have? */
+	atomic_t files;		/* How many open files does this user have? */
+	atomic_t sigpending;	/* How many pending signals does this user have? */
+#ifdef CONFIG_INOTIFY
+	atomic_t inotify_watches; /* How many inotify watches does this user have? */
+	atomic_t inotify_devs;	/* How many inotify devs does this user have opened? */
+#endif
+	/* protected by mq_lock	*/
+	unsigned long mq_bytes;	/* How many bytes can be allocated to mqueue? */
+	unsigned long locked_shm; /* How many pages of mlocked shm ? */
+
+#ifdef CONFIG_KEYS
+	struct key *uid_keyring;	/* UID specific keyring */
+	struct key *session_keyring;	/* UID's default session keyring */
+#endif
+
+	/* Hash table maintenance information */
+	struct list_head uidhash_list;
+	uid_t uid;
+};
+
+extern struct user_struct *find_user(uid_t);
+
+extern struct user_struct root_user;
+#define INIT_USER (&root_user)
+
+typedef struct prio_array prio_array_t;
+struct backing_dev_info;
+struct reclaim_state;
+
+#ifdef CONFIG_SCHEDSTATS
+struct sched_info {
+	/* cumulative counters */
+	unsigned long	cpu_time,	/* time spent on the cpu */
+			run_delay,	/* time spent waiting on a runqueue */
+			pcnt;		/* # of timeslices run on this cpu */
+
+	/* timestamps */
+	unsigned long	last_arrival,	/* when we last ran on a cpu */
+			last_queued;	/* when we were last queued to run */
+};
+
+extern struct file_operations proc_schedstat_operations;
+#endif
+
+enum idle_type
+{
+	SCHED_IDLE,
+	NOT_IDLE,
+	NEWLY_IDLE,
+	MAX_IDLE_TYPES
+};
+
+/*
+ * sched-domains (multiprocessor balancing) declarations:
+ */
+#ifdef CONFIG_SMP
+#define SCHED_LOAD_SCALE	128UL	/* increase resolution of load */
+
+#define SD_LOAD_BALANCE		1	/* Do load balancing on this domain. */
+#define SD_BALANCE_NEWIDLE	2	/* Balance when about to become idle */
+#define SD_BALANCE_EXEC		4	/* Balance on exec */
+#define SD_BALANCE_FORK		8	/* Balance on fork, clone */
+#define SD_WAKE_IDLE		16	/* Wake to idle CPU on task wakeup */
+#define SD_WAKE_AFFINE		32	/* Wake task to waking CPU */
+#define SD_WAKE_BALANCE		64	/* Perform balancing at task wakeup */
+#define SD_SHARE_CPUPOWER	128	/* Domain members share cpu power */
+
+struct sched_group {
+	struct sched_group *next;	/* Must be a circular list */
+	cpumask_t cpumask;
+
+	/*
+	 * CPU power of this group, SCHED_LOAD_SCALE being max power for a
+	 * single CPU. This is read only (except for setup, hotplug CPU).
+	 */
+	unsigned long cpu_power;
+};
+
+struct sched_domain {
+	/* These fields must be setup */
+	struct sched_domain *parent;	/* top domain must be null terminated */
+	struct sched_group *groups;	/* the balancing groups of the domain */
+	cpumask_t span;			/* span of all CPUs in this domain */
+	unsigned long min_interval;	/* Minimum balance interval ms */
+	unsigned long max_interval;	/* Maximum balance interval ms */
+	unsigned int busy_factor;	/* less balancing by factor if busy */
+	unsigned int imbalance_pct;	/* No balance until over watermark */
+	unsigned long long cache_hot_time; /* Task considered cache hot (ns) */
+	unsigned int cache_nice_tries;	/* Leave cache hot tasks for # tries */
+	unsigned int per_cpu_gain;	/* CPU % gained by adding domain cpus */
+	unsigned int busy_idx;
+	unsigned int idle_idx;
+	unsigned int newidle_idx;
+	unsigned int wake_idx;
+	unsigned int forkexec_idx;
+	int flags;			/* See SD_* */
+
+	/* Runtime fields. */
+	unsigned long last_balance;	/* init to jiffies. units in jiffies */
+	unsigned int balance_interval;	/* initialise to 1. units in ms. */
+	unsigned int nr_balance_failed; /* initialise to 0 */
+
+#ifdef CONFIG_SCHEDSTATS
+	/* load_balance() stats */
+	unsigned long lb_cnt[MAX_IDLE_TYPES];
+	unsigned long lb_failed[MAX_IDLE_TYPES];
+	unsigned long lb_balanced[MAX_IDLE_TYPES];
+	unsigned long lb_imbalance[MAX_IDLE_TYPES];
+	unsigned long lb_gained[MAX_IDLE_TYPES];
+	unsigned long lb_hot_gained[MAX_IDLE_TYPES];
+	unsigned long lb_nobusyg[MAX_IDLE_TYPES];
+	unsigned long lb_nobusyq[MAX_IDLE_TYPES];
+
+	/* Active load balancing */
+	unsigned long alb_cnt;
+	unsigned long alb_failed;
+	unsigned long alb_pushed;
+
+	/* SD_BALANCE_EXEC stats */
+	unsigned long sbe_cnt;
+	unsigned long sbe_balanced;
+	unsigned long sbe_pushed;
+
+	/* SD_BALANCE_FORK stats */
+	unsigned long sbf_cnt;
+	unsigned long sbf_balanced;
+	unsigned long sbf_pushed;
+
+	/* try_to_wake_up() stats */
+	unsigned long ttwu_wake_remote;
+	unsigned long ttwu_move_affine;
+	unsigned long ttwu_move_balance;
+#endif
+};
+
+extern void partition_sched_domains(cpumask_t *partition1,
+				    cpumask_t *partition2);
+#endif /* CONFIG_SMP */
+
+
+struct io_context;			/* See blkdev.h */
+void exit_io_context(void);
+struct cpuset;
+
+#define NGROUPS_SMALL		32
+#define NGROUPS_PER_BLOCK	((int)(PAGE_SIZE / sizeof(gid_t)))
+struct group_info {
+	int ngroups;
+	atomic_t usage;
+	gid_t small_block[NGROUPS_SMALL];
+	int nblocks;
+	gid_t *blocks[0];
+};
+
+/*
+ * get_group_info() must be called with the owning task locked (via task_lock())
+ * when task != current.  The reason being that the vast majority of callers are
+ * looking at current->group_info, which can not be changed except by the
+ * current task.  Changing current->group_info requires the task lock, too.
+ */
+#define get_group_info(group_info) do { \
+	atomic_inc(&(group_info)->usage); \
+} while (0)
+
+#define put_group_info(group_info) do { \
+	if (atomic_dec_and_test(&(group_info)->usage)) \
+		groups_free(group_info); \
+} while (0)
+
+extern struct group_info *groups_alloc(int gidsetsize);
+extern void groups_free(struct group_info *group_info);
+extern int set_current_groups(struct group_info *group_info);
+extern int groups_search(struct group_info *group_info, gid_t grp);
+/* access the groups "array" with this macro */
+#define GROUP_AT(gi, i) \
+    ((gi)->blocks[(i)/NGROUPS_PER_BLOCK][(i)%NGROUPS_PER_BLOCK])
+
+#ifdef ARCH_HAS_PREFETCH_SWITCH_STACK
+extern void prefetch_stack(struct task_struct*);
+#else
+static inline void prefetch_stack(struct task_struct *t) { }
+#endif
+
+struct audit_context;		/* See audit.c */
+struct mempolicy;
+
+struct task_struct {
+	volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
+	struct thread_info *thread_info;
+	atomic_t usage;
+	unsigned long flags;	/* per process flags, defined below */
+	unsigned long ptrace;
+
+	int lock_depth;		/* BKL lock depth */
+
+#if defined(CONFIG_SMP) && defined(__ARCH_WANT_UNLOCKED_CTXSW)
+	int oncpu;
+#endif
+	int prio, static_prio;
+	struct list_head run_list;
+	prio_array_t *array;
+
+	unsigned short ioprio;
+
+	unsigned long sleep_avg;
+	unsigned long long timestamp, last_ran;
+	unsigned long long sched_time; /* sched_clock time spent running */
+	int activated;
+
+	unsigned long policy;
+	cpumask_t cpus_allowed;
+	unsigned int time_slice, first_time_slice;
+
+#ifdef CONFIG_SCHEDSTATS
+	struct sched_info sched_info;
+#endif
+
+	struct list_head tasks;
+	/*
+	 * ptrace_list/ptrace_children forms the list of my children
+	 * that were stolen by a ptracer.
+	 */
+	struct list_head ptrace_children;
+	struct list_head ptrace_list;
+
+	struct mm_struct *mm, *active_mm;
+
+/* task state */
+	struct linux_binfmt *binfmt;
+	long exit_state;
+	int exit_code, exit_signal;
+	int pdeath_signal;  /*  The signal sent when the parent dies  */
+	/* ??? */
+	unsigned long personality;
+	unsigned did_exec:1;
+	pid_t pid;
+	pid_t tgid;
+	/* 
+	 * pointers to (original) parent process, youngest child, younger sibling,
+	 * older sibling, respectively.  (p->father can be replaced with 
+	 * p->parent->pid)
+	 */
+	struct task_struct *real_parent; /* real parent process (when being debugged) */
+	struct task_struct *parent;	/* parent process */
+	/*
+	 * children/sibling forms the list of my children plus the
+	 * tasks I'm ptracing.
+	 */
+	struct list_head children;	/* list of my children */
+	struct list_head sibling;	/* linkage in my parent's children list */
+	struct task_struct *group_leader;	/* threadgroup leader */
+
+	/* PID/PID hash table linkage. */
+	struct pid pids[PIDTYPE_MAX];
+
+	struct completion *vfork_done;		/* for vfork() */
+	int __user *set_child_tid;		/* CLONE_CHILD_SETTID */
+	int __user *clear_child_tid;		/* CLONE_CHILD_CLEARTID */
+
+	unsigned long rt_priority;
+	cputime_t utime, stime;
+	unsigned long nvcsw, nivcsw; /* context switch counts */
+	struct timespec start_time;
+/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
+	unsigned long min_flt, maj_flt;
+
+  	cputime_t it_prof_expires, it_virt_expires;
+	unsigned long long it_sched_expires;
+	struct list_head cpu_timers[3];
+
+/* process credentials */
+	uid_t uid,euid,suid,fsuid;
+	gid_t gid,egid,sgid,fsgid;
+	struct group_info *group_info;
+	kernel_cap_t   cap_effective, cap_inheritable, cap_permitted;
+	unsigned keep_capabilities:1;
+	struct user_struct *user;
+#ifdef CONFIG_KEYS
+	struct key *thread_keyring;	/* keyring private to this thread */
+	unsigned char jit_keyring;	/* default keyring to attach requested keys to */
+#endif
+	int oomkilladj; /* OOM kill score adjustment (bit shift). */
+	char comm[TASK_COMM_LEN]; /* executable name excluding path
+				     - access with [gs]et_task_comm (which lock
+				       it with task_lock())
+				     - initialized normally by flush_old_exec */
+/* file system info */
+	int link_count, total_link_count;
+/* ipc stuff */
+	struct sysv_sem sysvsem;
+/* CPU-specific state of this task */
+	struct thread_struct thread;
+/* filesystem information */
+	struct fs_struct *fs;
+/* open file information */
+	struct files_struct *files;
+/* namespace */
+	struct namespace *namespace;
+/* signal handlers */
+	struct signal_struct *signal;
+	struct sighand_struct *sighand;
+
+	sigset_t blocked, real_blocked;
+	struct sigpending pending;
+
+	unsigned long sas_ss_sp;
+	size_t sas_ss_size;
+	int (*notifier)(void *priv);
+	void *notifier_data;
+	sigset_t *notifier_mask;
+	
+	void *security;
+	struct audit_context *audit_context;
+	seccomp_t seccomp;
+
+/* Thread group tracking */
+   	u32 parent_exec_id;
+   	u32 self_exec_id;
+/* Protection of (de-)allocation: mm, files, fs, tty, keyrings */
+	spinlock_t alloc_lock;
+/* Protection of proc_dentry: nesting proc_lock, dcache_lock, write_lock_irq(&tasklist_lock); */
+	spinlock_t proc_lock;
+
+/* journalling filesystem info */
+	void *journal_info;
+
+/* stacked block device info */
+	struct bio *bio_list, **bio_tail;
+
+/* VM state */
+	struct reclaim_state *reclaim_state;
+
+	struct dentry *proc_dentry;
+	struct backing_dev_info *backing_dev_info;
+
+	struct io_context *io_context;
+
+	unsigned long ptrace_message;
+	siginfo_t *last_siginfo; /* For ptrace use.  */
+/*
+ * current io wait handle: wait queue entry to use for io waits
+ * If this thread is processing aio, this points at the waitqueue
+ * inside the currently handled kiocb. It may be NULL (i.e. default
+ * to a stack based synchronous wait) if its doing sync IO.
+ */
+	wait_queue_t *io_wait;
+/* i/o counters(bytes read/written, #syscalls */
+	u64 rchar, wchar, syscr, syscw;
+#if defined(CONFIG_BSD_PROCESS_ACCT)
+	u64 acct_rss_mem1;	/* accumulated rss usage */
+	u64 acct_vm_mem1;	/* accumulated virtual memory usage */
+	clock_t acct_stimexpd;	/* clock_t-converted stime since last update */
+#endif
+#ifdef CONFIG_NUMA
+  	struct mempolicy *mempolicy;
+	short il_next;
+#endif
+#ifdef CONFIG_CPUSETS
+	struct cpuset *cpuset;
+	nodemask_t mems_allowed;
+	int cpuset_mems_generation;
+#endif
+	atomic_t fs_excl;	/* holding fs exclusive resources */
+};
+
+static inline pid_t process_group(struct task_struct *tsk)
+{
+	return tsk->signal->pgrp;
+}
+
+/**
+ * pid_alive - check that a task structure is not stale
+ * @p: Task structure to be checked.
+ *
+ * Test if a process is not yet dead (at most zombie state)
+ * If pid_alive fails, then pointers within the task structure
+ * can be stale and must not be dereferenced.
+ */
+static inline int pid_alive(struct task_struct *p)
+{
+	return p->pids[PIDTYPE_PID].nr != 0;
+}
+
+extern void free_task(struct task_struct *tsk);
+extern void __put_task_struct(struct task_struct *tsk);
+#define get_task_struct(tsk) do { atomic_inc(&(tsk)->usage); } while(0)
+#define put_task_struct(tsk) \
+do { if (atomic_dec_and_test(&(tsk)->usage)) __put_task_struct(tsk); } while(0)
+
+/*
+ * Per process flags
+ */
+#define PF_ALIGNWARN	0x00000001	/* Print alignment warning msgs */
+					/* Not implemented yet, only for 486*/
+#define PF_STARTING	0x00000002	/* being created */
+#define PF_EXITING	0x00000004	/* getting shut down */
+#define PF_DEAD		0x00000008	/* Dead */
+#define PF_FORKNOEXEC	0x00000040	/* forked but didn't exec */
+#define PF_SUPERPRIV	0x00000100	/* used super-user privileges */
+#define PF_DUMPCORE	0x00000200	/* dumped core */
+#define PF_SIGNALED	0x00000400	/* killed by a signal */
+#define PF_MEMALLOC	0x00000800	/* Allocating memory */
+#define PF_FLUSHER	0x00001000	/* responsible for disk writeback */
+#define PF_USED_MATH	0x00002000	/* if unset the fpu must be initialized before use */
+#define PF_FREEZE	0x00004000	/* this task is being frozen for suspend now */
+#define PF_NOFREEZE	0x00008000	/* this thread should not be frozen */
+#define PF_FROZEN	0x00010000	/* frozen for system suspend */
+#define PF_FSTRANS	0x00020000	/* inside a filesystem transaction */
+#define PF_KSWAPD	0x00040000	/* I am kswapd */
+#define PF_SWAPOFF	0x00080000	/* I am in swapoff */
+#define PF_LESS_THROTTLE 0x00100000	/* Throttle me less: I clean memory */
+#define PF_SYNCWRITE	0x00200000	/* I am doing a sync write */
+#define PF_BORROWED_MM	0x00400000	/* I am a kthread doing use_mm */
+#define PF_RANDOMIZE	0x00800000	/* randomize virtual address space */
+
+/*
+ * Only the _current_ task can read/write to tsk->flags, but other
+ * tasks can access tsk->flags in readonly mode for example
+ * with tsk_used_math (like during threaded core dumping).
+ * There is however an exception to this rule during ptrace
+ * or during fork: the ptracer task is allowed to write to the
+ * child->flags of its traced child (same goes for fork, the parent
+ * can write to the child->flags), because we're guaranteed the
+ * child is not running and in turn not changing child->flags
+ * at the same time the parent does it.
+ */
+#define clear_stopped_child_used_math(child) do { (child)->flags &= ~PF_USED_MATH; } while (0)
+#define set_stopped_child_used_math(child) do { (child)->flags |= PF_USED_MATH; } while (0)
+#define clear_used_math() clear_stopped_child_used_math(current)
+#define set_used_math() set_stopped_child_used_math(current)
+#define conditional_stopped_child_used_math(condition, child) \
+	do { (child)->flags &= ~PF_USED_MATH, (child)->flags |= (condition) ? PF_USED_MATH : 0; } while (0)
+#define conditional_used_math(condition) \
+	conditional_stopped_child_used_math(condition, current)
+#define copy_to_stopped_child_used_math(child) \
+	do { (child)->flags &= ~PF_USED_MATH, (child)->flags |= current->flags & PF_USED_MATH; } while (0)
+/* NOTE: this will return 0 or PF_USED_MATH, it will never return 1 */
+#define tsk_used_math(p) ((p)->flags & PF_USED_MATH)
+#define used_math() tsk_used_math(current)
+
+#ifdef CONFIG_SMP
+extern int set_cpus_allowed(task_t *p, cpumask_t new_mask);
+#else
+static inline int set_cpus_allowed(task_t *p, cpumask_t new_mask)
+{
+	if (!cpu_isset(0, new_mask))
+		return -EINVAL;
+	return 0;
+}
+#endif
+
+extern unsigned long long sched_clock(void);
+extern unsigned long long current_sched_time(const task_t *current_task);
+
+/* sched_exec is called by processes performing an exec */
+#ifdef CONFIG_SMP
+extern void sched_exec(void);
+#else
+#define sched_exec()   {}
+#endif
+
+#ifdef CONFIG_HOTPLUG_CPU
+extern void idle_task_exit(void);
+#else
+static inline void idle_task_exit(void) {}
+#endif
+
+extern void sched_idle_next(void);
+extern void set_user_nice(task_t *p, long nice);
+extern int task_prio(const task_t *p);
+extern int task_nice(const task_t *p);
+extern int can_nice(const task_t *p, const int nice);
+extern int task_curr(const task_t *p);
+extern int idle_cpu(int cpu);
+extern int sched_setscheduler(struct task_struct *, int, struct sched_param *);
+extern task_t *idle_task(int cpu);
+extern task_t *curr_task(int cpu);
+extern void set_curr_task(int cpu, task_t *p);
+
+void yield(void);
+
+/*
+ * The default (Linux) execution domain.
+ */
+extern struct exec_domain	default_exec_domain;
+
+union thread_union {
+	struct thread_info thread_info;
+	unsigned long stack[THREAD_SIZE/sizeof(long)];
+};
+
+#ifndef __HAVE_ARCH_KSTACK_END
+static inline int kstack_end(void *addr)
+{
+	/* Reliable end of stack detection:
+	 * Some APM bios versions misalign the stack
+	 */
+	return !(((unsigned long)addr+sizeof(void*)-1) & (THREAD_SIZE-sizeof(void*)));
+}
+#endif
+
+extern union thread_union init_thread_union;
+extern struct task_struct init_task;
+
+extern struct   mm_struct init_mm;
+
+#define find_task_by_pid(nr)	find_task_by_pid_type(PIDTYPE_PID, nr)
+extern struct task_struct *find_task_by_pid_type(int type, int pid);
+extern void set_special_pids(pid_t session, pid_t pgrp);
+extern void __set_special_pids(pid_t session, pid_t pgrp);
+
+/* per-UID process charging. */
+extern struct user_struct * alloc_uid(uid_t);
+static inline struct user_struct *get_uid(struct user_struct *u)
+{
+	atomic_inc(&u->__count);
+	return u;
+}
+extern void free_uid(struct user_struct *);
+extern void switch_uid(struct user_struct *);
+
+#include <asm/current.h>
+
+extern void do_timer(struct pt_regs *);
+
+extern int FASTCALL(wake_up_state(struct task_struct * tsk, unsigned int state));
+extern int FASTCALL(wake_up_process(struct task_struct * tsk));
+extern void FASTCALL(wake_up_new_task(struct task_struct * tsk,
+						unsigned long clone_flags));
+#ifdef CONFIG_SMP
+ extern void kick_process(struct task_struct *tsk);
+#else
+ static inline void kick_process(struct task_struct *tsk) { }
+#endif
+extern void FASTCALL(sched_fork(task_t * p, int clone_flags));
+extern void FASTCALL(sched_exit(task_t * p));
+
+extern int in_group_p(gid_t);
+extern int in_egroup_p(gid_t);
+
+extern void proc_caches_init(void);
+extern void flush_signals(struct task_struct *);
+extern void flush_signal_handlers(struct task_struct *, int force_default);
+extern int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info);
+
+static inline int dequeue_signal_lock(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&tsk->sighand->siglock, flags);
+	ret = dequeue_signal(tsk, mask, info);
+	spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
+
+	return ret;
+}	
+
+extern void block_all_signals(int (*notifier)(void *priv), void *priv,
+			      sigset_t *mask);
+extern void unblock_all_signals(void);
+extern void release_task(struct task_struct * p);
+extern int send_sig_info(int, struct siginfo *, struct task_struct *);
+extern int send_group_sig_info(int, struct siginfo *, struct task_struct *);
+extern int force_sigsegv(int, struct task_struct *);
+extern int force_sig_info(int, struct siginfo *, struct task_struct *);
+extern int __kill_pg_info(int sig, struct siginfo *info, pid_t pgrp);
+extern int kill_pg_info(int, struct siginfo *, pid_t);
+extern int kill_proc_info(int, struct siginfo *, pid_t);
+extern int kill_proc_info_as_uid(int, struct siginfo *, pid_t, uid_t, uid_t);
+extern void do_notify_parent(struct task_struct *, int);
+extern void force_sig(int, struct task_struct *);
+extern void force_sig_specific(int, struct task_struct *);
+extern int send_sig(int, struct task_struct *, int);
+extern void zap_other_threads(struct task_struct *p);
+extern int kill_pg(pid_t, int, int);
+extern int kill_sl(pid_t, int, int);
+extern int kill_proc(pid_t, int, int);
+extern struct sigqueue *sigqueue_alloc(void);
+extern void sigqueue_free(struct sigqueue *);
+extern int send_sigqueue(int, struct sigqueue *,  struct task_struct *);
+extern int send_group_sigqueue(int, struct sigqueue *,  struct task_struct *);
+extern int do_sigaction(int, const struct k_sigaction *, struct k_sigaction *);
+extern int do_sigaltstack(const stack_t __user *, stack_t __user *, unsigned long);
+
+/* These can be the second arg to send_sig_info/send_group_sig_info.  */
+#define SEND_SIG_NOINFO ((struct siginfo *) 0)
+#define SEND_SIG_PRIV	((struct siginfo *) 1)
+#define SEND_SIG_FORCED	((struct siginfo *) 2)
+
+static inline int is_si_special(const struct siginfo *info)
+{
+	return info <= SEND_SIG_FORCED;
+}
+
+/* True if we are on the alternate signal stack.  */
+
+static inline int on_sig_stack(unsigned long sp)
+{
+	return (sp - current->sas_ss_sp < current->sas_ss_size);
+}
+
+static inline int sas_ss_flags(unsigned long sp)
+{
+	return (current->sas_ss_size == 0 ? SS_DISABLE
+		: on_sig_stack(sp) ? SS_ONSTACK : 0);
+}
+
+
+#ifdef CONFIG_SECURITY
+/* code is in security.c */
+extern int capable(int cap);
+#else
+static inline int capable(int cap)
+{
+	if (cap_raised(current->cap_effective, cap)) {
+		current->flags |= PF_SUPERPRIV;
+		return 1;
+	}
+	return 0;
+}
+#endif
+
+/*
+ * Routines for handling mm_structs
+ */
+extern struct mm_struct * mm_alloc(void);
+
+/* mmdrop drops the mm and the page tables */
+extern void FASTCALL(__mmdrop(struct mm_struct *));
+static inline void mmdrop(struct mm_struct * mm)
+{
+	if (atomic_dec_and_test(&mm->mm_count))
+		__mmdrop(mm);
+}
+
+/* mmput gets rid of the mappings and all user-space */
+extern void mmput(struct mm_struct *);
+/* Grab a reference to a task's mm, if it is not already going away */
+extern struct mm_struct *get_task_mm(struct task_struct *task);
+/* Remove the current tasks stale references to the old mm_struct */
+extern void mm_release(struct task_struct *, struct mm_struct *);
+
+extern int  copy_thread(int, unsigned long, unsigned long, unsigned long, struct task_struct *, struct pt_regs *);
+extern void flush_thread(void);
+extern void exit_thread(void);
+
+extern void exit_files(struct task_struct *);
+extern void exit_signal(struct task_struct *);
+extern void __exit_signal(struct task_struct *);
+extern void exit_sighand(struct task_struct *);
+extern void __exit_sighand(struct task_struct *);
+extern void exit_itimers(struct signal_struct *);
+
+extern NORET_TYPE void do_group_exit(int);
+
+extern void daemonize(const char *, ...);
+extern int allow_signal(int);
+extern int disallow_signal(int);
+extern task_t *child_reaper;
+
+extern int do_execve(char *, char __user * __user *, char __user * __user *, struct pt_regs *);
+extern long do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *);
+task_t *fork_idle(int);
+
+extern void set_task_comm(struct task_struct *tsk, char *from);
+extern void get_task_comm(char *to, struct task_struct *tsk);
+
+#ifdef CONFIG_SMP
+extern void wait_task_inactive(task_t * p);
+#else
+#define wait_task_inactive(p)	do { } while (0)
+#endif
+
+#define remove_parent(p)	list_del_init(&(p)->sibling)
+#define add_parent(p, parent)	list_add_tail(&(p)->sibling,&(parent)->children)
+
+#define REMOVE_LINKS(p) do {					\
+	if (thread_group_leader(p))				\
+		list_del_init(&(p)->tasks);			\
+	remove_parent(p);					\
+	} while (0)
+
+#define SET_LINKS(p) do {					\
+	if (thread_group_leader(p))				\
+		list_add_tail(&(p)->tasks,&init_task.tasks);	\
+	add_parent(p, (p)->parent);				\
+	} while (0)
+
+#define next_task(p)	list_entry((p)->tasks.next, struct task_struct, tasks)
+#define prev_task(p)	list_entry((p)->tasks.prev, struct task_struct, tasks)
+
+#define for_each_process(p) \
+	for (p = &init_task ; (p = next_task(p)) != &init_task ; )
+
+/*
+ * Careful: do_each_thread/while_each_thread is a double loop so
+ *          'break' will not work as expected - use goto instead.
+ */
+#define do_each_thread(g, t) \
+	for (g = t = &init_task ; (g = t = next_task(g)) != &init_task ; ) do
+
+#define while_each_thread(g, t) \
+	while ((t = next_thread(t)) != g)
+
+extern task_t * FASTCALL(next_thread(const task_t *p));
+
+#define thread_group_leader(p)	(p->pid == p->tgid)
+
+static inline int thread_group_empty(task_t *p)
+{
+	return list_empty(&p->pids[PIDTYPE_TGID].pid_list);
+}
+
+#define delay_group_leader(p) \
+		(thread_group_leader(p) && !thread_group_empty(p))
+
+extern void unhash_process(struct task_struct *p);
+
+/*
+ * Protects ->fs, ->files, ->mm, ->ptrace, ->group_info, ->comm, keyring
+ * subscriptions and synchronises with wait4().  Also used in procfs.  Also
+ * pins the final release of task.io_context.  Also protects ->cpuset.
+ *
+ * Nests both inside and outside of read_lock(&tasklist_lock).
+ * It must not be nested with write_lock_irq(&tasklist_lock),
+ * neither inside nor outside.
+ */
+static inline void task_lock(struct task_struct *p)
+{
+	spin_lock(&p->alloc_lock);
+}
+
+static inline void task_unlock(struct task_struct *p)
+{
+	spin_unlock(&p->alloc_lock);
+}
+
+#ifndef __HAVE_THREAD_FUNCTIONS
+
+#define task_thread_info(task) (task)->thread_info
+
+static inline void setup_thread_stack(struct task_struct *p, struct task_struct *org)
+{
+	*task_thread_info(p) = *task_thread_info(org);
+	task_thread_info(p)->task = p;
+}
+
+static inline unsigned long *end_of_stack(struct task_struct *p)
+{
+	return (unsigned long *)(p->thread_info + 1);
+}
+
+#endif
+
+/* set thread flags in other task's structures
+ * - see asm/thread_info.h for TIF_xxxx flags available
+ */
+static inline void set_tsk_thread_flag(struct task_struct *tsk, int flag)
+{
+	set_ti_thread_flag(task_thread_info(tsk), flag);
+}
+
+static inline void clear_tsk_thread_flag(struct task_struct *tsk, int flag)
+{
+	clear_ti_thread_flag(task_thread_info(tsk), flag);
+}
+
+static inline int test_and_set_tsk_thread_flag(struct task_struct *tsk, int flag)
+{
+	return test_and_set_ti_thread_flag(task_thread_info(tsk), flag);
+}
+
+static inline int test_and_clear_tsk_thread_flag(struct task_struct *tsk, int flag)
+{
+	return test_and_clear_ti_thread_flag(task_thread_info(tsk), flag);
+}
+
+static inline int test_tsk_thread_flag(struct task_struct *tsk, int flag)
+{
+	return test_ti_thread_flag(task_thread_info(tsk), flag);
+}
+
+static inline void set_tsk_need_resched(struct task_struct *tsk)
+{
+	set_tsk_thread_flag(tsk,TIF_NEED_RESCHED);
+}
+
+static inline void clear_tsk_need_resched(struct task_struct *tsk)
+{
+	clear_tsk_thread_flag(tsk,TIF_NEED_RESCHED);
+}
+
+static inline int signal_pending(struct task_struct *p)
+{
+	return unlikely(test_tsk_thread_flag(p,TIF_SIGPENDING));
+}
+  
+static inline int need_resched(void)
+{
+	return unlikely(test_thread_flag(TIF_NEED_RESCHED));
+}
+
+/*
+ * cond_resched() and cond_resched_lock(): latency reduction via
+ * explicit rescheduling in places that are safe. The return
+ * value indicates whether a reschedule was done in fact.
+ * cond_resched_lock() will drop the spinlock before scheduling,
+ * cond_resched_softirq() will enable bhs before scheduling.
+ */
+extern int cond_resched(void);
+extern int cond_resched_lock(spinlock_t * lock);
+extern int cond_resched_softirq(void);
+
+/*
+ * Does a critical section need to be broken due to another
+ * task waiting?:
+ */
+#if defined(CONFIG_PREEMPT) && defined(CONFIG_SMP)
+# define need_lockbreak(lock) ((lock)->break_lock)
+#else
+# define need_lockbreak(lock) 0
+#endif
+
+/*
+ * Does a critical section need to be broken due to another
+ * task waiting or preemption being signalled:
+ */
+static inline int lock_need_resched(spinlock_t *lock)
+{
+	if (need_lockbreak(lock) || need_resched())
+		return 1;
+	return 0;
+}
+
+/* Reevaluate whether the task has signals pending delivery.
+   This is required every time the blocked sigset_t changes.
+   callers must hold sighand->siglock.  */
+
+extern FASTCALL(void recalc_sigpending_tsk(struct task_struct *t));
+extern void recalc_sigpending(void);
+
+extern void signal_wake_up(struct task_struct *t, int resume_stopped);
+
+/*
+ * Wrappers for p->thread_info->cpu access. No-op on UP.
+ */
+#ifdef CONFIG_SMP
+
+static inline unsigned int task_cpu(const struct task_struct *p)
+{
+	return task_thread_info(p)->cpu;
+}
+
+static inline void set_task_cpu(struct task_struct *p, unsigned int cpu)
+{
+	task_thread_info(p)->cpu = cpu;
+}
+
+#else
+
+static inline unsigned int task_cpu(const struct task_struct *p)
+{
+	return 0;
+}
+
+static inline void set_task_cpu(struct task_struct *p, unsigned int cpu)
+{
+}
+
+#endif /* CONFIG_SMP */
+
+#ifdef HAVE_ARCH_PICK_MMAP_LAYOUT
+extern void arch_pick_mmap_layout(struct mm_struct *mm);
+#else
+static inline void arch_pick_mmap_layout(struct mm_struct *mm)
+{
+	mm->mmap_base = TASK_UNMAPPED_BASE;
+	mm->get_unmapped_area = arch_get_unmapped_area;
+	mm->unmap_area = arch_unmap_area;
+}
+#endif
+
+extern long sched_setaffinity(pid_t pid, cpumask_t new_mask);
+extern long sched_getaffinity(pid_t pid, cpumask_t *mask);
+
+#ifdef CONFIG_MAGIC_SYSRQ
+
+extern void normalize_rt_tasks(void);
+
+#endif
+
+#ifdef CONFIG_PM
+/*
+ * Check if a process has been frozen
+ */
+static inline int frozen(struct task_struct *p)
+{
+	return p->flags & PF_FROZEN;
+}
+
+/*
+ * Check if there is a request to freeze a process
+ */
+static inline int freezing(struct task_struct *p)
+{
+	return p->flags & PF_FREEZE;
+}
+
+/*
+ * Request that a process be frozen
+ * FIXME: SMP problem. We may not modify other process' flags!
+ */
+static inline void freeze(struct task_struct *p)
+{
+	p->flags |= PF_FREEZE;
+}
+
+/*
+ * Wake up a frozen process
+ */
+static inline int thaw_process(struct task_struct *p)
+{
+	if (frozen(p)) {
+		p->flags &= ~PF_FROZEN;
+		wake_up_process(p);
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * freezing is complete, mark process as frozen
+ */
+static inline void frozen_process(struct task_struct *p)
+{
+	p->flags = (p->flags & ~PF_FREEZE) | PF_FROZEN;
+}
+
+extern void refrigerator(void);
+extern int freeze_processes(void);
+extern void thaw_processes(void);
+
+static inline int try_to_freeze(void)
+{
+	if (freezing(current)) {
+		refrigerator();
+		return 1;
+	} else
+		return 0;
+}
+#else
+static inline int frozen(struct task_struct *p) { return 0; }
+static inline int freezing(struct task_struct *p) { return 0; }
+static inline void freeze(struct task_struct *p) { BUG(); }
+static inline int thaw_process(struct task_struct *p) { return 1; }
+static inline void frozen_process(struct task_struct *p) { BUG(); }
+
+static inline void refrigerator(void) {}
+static inline int freeze_processes(void) { BUG(); return 0; }
+static inline void thaw_processes(void) {}
+
+static inline int try_to_freeze(void) { return 0; }
+
+#endif /* CONFIG_PM */
+#endif /* __KERNEL__ */
+
+#endif
diff -urN oldtree/include/linux/security.h newtree/include/linux/security.h
--- oldtree/include/linux/security.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/security.h	2006-02-13 19:20:29.742990680 -0500
@@ -817,6 +817,11 @@
  *	@ipcp contains the kernel IPC permission structure
  *	@flag contains the desired (requested) permission set
  *	Return 0 if permission is granted.
+ * @ipc_getsecurity:
+ *      Copy the security label associated with the ipc object into
+ *      @buffer.  @buffer may be NULL to request the size of the buffer 
+ *      required.  @size indicates the size of @buffer in bytes. Return 
+ *      number of bytes used/required on success.
  *
  * Security hooks for individual messages held in System V IPC message queues
  * @msg_msg_alloc_security:
@@ -1116,7 +1121,8 @@
 	int (*inode_getxattr) (struct dentry *dentry, char *name);
 	int (*inode_listxattr) (struct dentry *dentry);
 	int (*inode_removexattr) (struct dentry *dentry, char *name);
-  	int (*inode_getsecurity)(struct inode *inode, const char *name, void *buffer, size_t size, int err);
+	const char *(*inode_xattr_getsuffix) (void);
+  	int (*inode_getsecurity)(const struct inode *inode, const char *name, void *buffer, size_t size, int err);
   	int (*inode_setsecurity)(struct inode *inode, const char *name, const void *value, size_t size, int flags);
   	int (*inode_listsecurity)(struct inode *inode, char *buffer, size_t buffer_size);
 
@@ -1165,6 +1171,7 @@
 	void (*task_to_inode)(struct task_struct *p, struct inode *inode);
 
 	int (*ipc_permission) (struct kern_ipc_perm * ipcp, short flag);
+	int (*ipc_getsecurity)(struct kern_ipc_perm *ipcp, void *buffer, size_t size);
 
 	int (*msg_msg_alloc_security) (struct msg_msg * msg);
 	void (*msg_msg_free_security) (struct msg_msg * msg);
@@ -1437,15 +1444,11 @@
 
 static inline int security_inode_alloc (struct inode *inode)
 {
-	if (unlikely (IS_PRIVATE (inode)))
-		return 0;
 	return security_ops->inode_alloc_security (inode);
 }
 
 static inline void security_inode_free (struct inode *inode)
 {
-	if (unlikely (IS_PRIVATE (inode)))
-		return;
 	security_ops->inode_free_security (inode);
 }
 
@@ -1616,7 +1619,12 @@
 	return security_ops->inode_removexattr (dentry, name);
 }
 
-static inline int security_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err)
+static inline const char *security_inode_xattr_getsuffix(void)
+{
+	return security_ops->inode_xattr_getsuffix();
+}
+
+static inline int security_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err)
 {
 	if (unlikely (IS_PRIVATE (inode)))
 		return 0;
@@ -1811,6 +1819,11 @@
 	return security_ops->ipc_permission (ipcp, flag);
 }
 
+static inline int security_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size)
+{
+	return security_ops->ipc_getsecurity(ipcp, buffer, size);
+}
+
 static inline int security_msg_msg_alloc (struct msg_msg * msg)
 {
 	return security_ops->msg_msg_alloc_security (msg);
@@ -2258,7 +2271,12 @@
 	return cap_inode_removexattr(dentry, name);
 }
 
-static inline int security_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err)
+static inline const char *security_inode_xattr_getsuffix (void)
+{
+	return NULL ;
+}
+
+static inline int security_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err)
 {
 	return -EOPNOTSUPP;
 }
@@ -2441,6 +2459,11 @@
 	return 0;
 }
 
+static inline int security_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size)
+{
+	return -EOPNOTSUPP;
+}
+
 static inline int security_msg_msg_alloc (struct msg_msg * msg)
 {
 	return 0;
diff -urN oldtree/include/linux/security.h.orig newtree/include/linux/security.h.orig
--- oldtree/include/linux/security.h.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/linux/security.h.orig	2006-02-13 19:20:01.252321920 -0500
@@ -0,0 +1,2868 @@
+/*
+ * Linux Security plug
+ *
+ * Copyright (C) 2001 WireX Communications, Inc <chris@wirex.com>
+ * Copyright (C) 2001 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.com>
+ * Copyright (C) 2001 James Morris <jmorris@intercode.com.au>
+ * Copyright (C) 2001 Silicon Graphics, Inc. (Trust Technology Group)
+ *
+ *	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
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	Due to this file being licensed under the GPL there is controversy over
+ *	whether this permits you to write a module that #includes this file
+ *	without placing your module under the GPL.  Please consult a lawyer for
+ *	advice before doing this.
+ *
+ */
+
+#ifndef __LINUX_SECURITY_H
+#define __LINUX_SECURITY_H
+
+#include <linux/fs.h>
+#include <linux/binfmts.h>
+#include <linux/signal.h>
+#include <linux/resource.h>
+#include <linux/sem.h>
+#include <linux/shm.h>
+#include <linux/msg.h>
+#include <linux/sched.h>
+#include <linux/key.h>
+
+struct ctl_table;
+
+/*
+ * These functions are in security/capability.c and are used
+ * as the default capabilities functions
+ */
+extern int cap_capable (struct task_struct *tsk, int cap);
+extern int cap_settime (struct timespec *ts, struct timezone *tz);
+extern int cap_ptrace (struct task_struct *parent, struct task_struct *child);
+extern int cap_capget (struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
+extern int cap_capset_check (struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
+extern void cap_capset_set (struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
+extern int cap_bprm_set_security (struct linux_binprm *bprm);
+extern void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe);
+extern int cap_bprm_secureexec(struct linux_binprm *bprm);
+extern int cap_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags);
+extern int cap_inode_removexattr(struct dentry *dentry, char *name);
+extern int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid, int flags);
+extern void cap_task_reparent_to_init (struct task_struct *p);
+extern int cap_syslog (int type);
+extern int cap_vm_enough_memory (long pages);
+
+struct msghdr;
+struct sk_buff;
+struct sock;
+struct sockaddr;
+struct socket;
+
+extern int cap_netlink_send(struct sock *sk, struct sk_buff *skb);
+extern int cap_netlink_recv(struct sk_buff *skb);
+
+/*
+ * Values used in the task_security_ops calls
+ */
+/* setuid or setgid, id0 == uid or gid */
+#define LSM_SETID_ID	1
+
+/* setreuid or setregid, id0 == real, id1 == eff */
+#define LSM_SETID_RE	2
+
+/* setresuid or setresgid, id0 == real, id1 == eff, uid2 == saved */
+#define LSM_SETID_RES	4
+
+/* setfsuid or setfsgid, id0 == fsuid or fsgid */
+#define LSM_SETID_FS	8
+
+/* forward declares to avoid warnings */
+struct nfsctl_arg;
+struct sched_param;
+struct swap_info_struct;
+
+/* bprm_apply_creds unsafe reasons */
+#define LSM_UNSAFE_SHARE	1
+#define LSM_UNSAFE_PTRACE	2
+#define LSM_UNSAFE_PTRACE_CAP	4
+
+#ifdef CONFIG_SECURITY
+
+/**
+ * struct security_operations - main security structure
+ *
+ * Security hooks for program execution operations.
+ *
+ * @bprm_alloc_security:
+ *	Allocate and attach a security structure to the @bprm->security field.
+ *	The security field is initialized to NULL when the bprm structure is
+ *	allocated.
+ *	@bprm contains the linux_binprm structure to be modified.
+ *	Return 0 if operation was successful.
+ * @bprm_free_security:
+ *	@bprm contains the linux_binprm structure to be modified.
+ *	Deallocate and clear the @bprm->security field.
+ * @bprm_apply_creds:
+ *	Compute and set the security attributes of a process being transformed
+ *	by an execve operation based on the old attributes (current->security)
+ *	and the information saved in @bprm->security by the set_security hook.
+ *	Since this hook function (and its caller) are void, this hook can not
+ *	return an error.  However, it can leave the security attributes of the
+ *	process unchanged if an access failure occurs at this point.
+ *	bprm_apply_creds is called under task_lock.  @unsafe indicates various
+ *	reasons why it may be unsafe to change security state.
+ *	@bprm contains the linux_binprm structure.
+ * @bprm_post_apply_creds:
+ *	Runs after bprm_apply_creds with the task_lock dropped, so that
+ *	functions which cannot be called safely under the task_lock can
+ *	be used.  This hook is a good place to perform state changes on
+ *	the process such as closing open file descriptors to which access
+ *	is no longer granted if the attributes were changed.
+ *	Note that a security module might need to save state between
+ *	bprm_apply_creds and bprm_post_apply_creds to store the decision
+ *	on whether the process may proceed.
+ *	@bprm contains the linux_binprm structure.
+ * @bprm_set_security:
+ *	Save security information in the bprm->security field, typically based
+ *	on information about the bprm->file, for later use by the apply_creds
+ *	hook.  This hook may also optionally check permissions (e.g. for
+ *	transitions between security domains).
+ *	This hook may be called multiple times during a single execve, e.g. for
+ *	interpreters.  The hook can tell whether it has already been called by
+ *	checking to see if @bprm->security is non-NULL.  If so, then the hook
+ *	may decide either to retain the security information saved earlier or
+ *	to replace it.
+ *	@bprm contains the linux_binprm structure.
+ *	Return 0 if the hook is successful and permission is granted.
+ * @bprm_check_security:
+ * 	This hook mediates the point when a search for a binary handler	will
+ * 	begin.  It allows a check the @bprm->security value which is set in
+ * 	the preceding set_security call.  The primary difference from
+ * 	set_security is that the argv list and envp list are reliably
+ * 	available in @bprm.  This hook may be called multiple times
+ * 	during a single execve; and in each pass set_security is called
+ * 	first.
+ * 	@bprm contains the linux_binprm structure.
+ *	Return 0 if the hook is successful and permission is granted.
+ * @bprm_secureexec:
+ *      Return a boolean value (0 or 1) indicating whether a "secure exec" 
+ *      is required.  The flag is passed in the auxiliary table
+ *      on the initial stack to the ELF interpreter to indicate whether libc 
+ *      should enable secure mode.
+ *      @bprm contains the linux_binprm structure.
+ *
+ * Security hooks for filesystem operations.
+ *
+ * @sb_alloc_security:
+ *	Allocate and attach a security structure to the sb->s_security field.
+ *	The s_security field is initialized to NULL when the structure is
+ *	allocated.
+ *	@sb contains the super_block structure to be modified.
+ *	Return 0 if operation was successful.
+ * @sb_free_security:
+ *	Deallocate and clear the sb->s_security field.
+ *	@sb contains the super_block structure to be modified.
+ * @sb_statfs:
+ *	Check permission before obtaining filesystem statistics for the @sb
+ *	filesystem.
+ *	@sb contains the super_block structure for the filesystem.
+ *	Return 0 if permission is granted.  
+ * @sb_mount:
+ *	Check permission before an object specified by @dev_name is mounted on
+ *	the mount point named by @nd.  For an ordinary mount, @dev_name
+ *	identifies a device if the file system type requires a device.  For a
+ *	remount (@flags & MS_REMOUNT), @dev_name is irrelevant.  For a
+ *	loopback/bind mount (@flags & MS_BIND), @dev_name identifies the
+ *	pathname of the object being mounted.
+ *	@dev_name contains the name for object being mounted.
+ *	@nd contains the nameidata structure for mount point object.
+ *	@type contains the filesystem type.
+ *	@flags contains the mount flags.
+ *	@data contains the filesystem-specific data.
+ *	Return 0 if permission is granted.
+ * @sb_copy_data:
+ *	Allow mount option data to be copied prior to parsing by the filesystem,
+ *	so that the security module can extract security-specific mount
+ *	options cleanly (a filesystem may modify the data e.g. with strsep()).
+ *	This also allows the original mount data to be stripped of security-
+ *	specific options to avoid having to make filesystems aware of them.
+ *	@type the type of filesystem being mounted.
+ *	@orig the original mount data copied from userspace.
+ *	@copy copied data which will be passed to the security module.
+ *	Returns 0 if the copy was successful.
+ * @sb_check_sb:
+ *	Check permission before the device with superblock @mnt->sb is mounted
+ *	on the mount point named by @nd.
+ *	@mnt contains the vfsmount for device being mounted.
+ *	@nd contains the nameidata object for the mount point.
+ *	Return 0 if permission is granted.
+ * @sb_umount:
+ *	Check permission before the @mnt file system is unmounted.
+ *	@mnt contains the mounted file system.
+ *	@flags contains the unmount flags, e.g. MNT_FORCE.
+ *	Return 0 if permission is granted.
+ * @sb_umount_close:
+ *	Close any files in the @mnt mounted filesystem that are held open by
+ *	the security module.  This hook is called during an umount operation
+ *	prior to checking whether the filesystem is still busy.
+ *	@mnt contains the mounted filesystem.
+ * @sb_umount_busy:
+ *	Handle a failed umount of the @mnt mounted filesystem, e.g.  re-opening
+ *	any files that were closed by umount_close.  This hook is called during
+ *	an umount operation if the umount fails after a call to the
+ *	umount_close hook.
+ *	@mnt contains the mounted filesystem.
+ * @sb_post_remount:
+ *	Update the security module's state when a filesystem is remounted.
+ *	This hook is only called if the remount was successful.
+ *	@mnt contains the mounted file system.
+ *	@flags contains the new filesystem flags.
+ *	@data contains the filesystem-specific data.
+ * @sb_post_mountroot:
+ *	Update the security module's state when the root filesystem is mounted.
+ *	This hook is only called if the mount was successful.
+ * @sb_post_addmount:
+ *	Update the security module's state when a filesystem is mounted.
+ *	This hook is called any time a mount is successfully grafetd to
+ *	the tree.
+ *	@mnt contains the mounted filesystem.
+ *	@mountpoint_nd contains the nameidata structure for the mount point.
+ * @sb_pivotroot:
+ *	Check permission before pivoting the root filesystem.
+ *	@old_nd contains the nameidata structure for the new location of the current root (put_old).
+ *      @new_nd contains the nameidata structure for the new root (new_root).
+ *	Return 0 if permission is granted.
+ * @sb_post_pivotroot:
+ *	Update module state after a successful pivot.
+ *	@old_nd contains the nameidata structure for the old root.
+ *      @new_nd contains the nameidata structure for the new root.
+ *
+ * Security hooks for inode operations.
+ *
+ * @inode_alloc_security:
+ *	Allocate and attach a security structure to @inode->i_security.  The
+ *	i_security field is initialized to NULL when the inode structure is
+ *	allocated.
+ *	@inode contains the inode structure.
+ *	Return 0 if operation was successful.
+ * @inode_free_security:
+ *	@inode contains the inode structure.
+ *	Deallocate the inode security structure and set @inode->i_security to
+ *	NULL. 
+ * @inode_init_security:
+ * 	Obtain the security attribute name suffix and value to set on a newly
+ *	created inode and set up the incore security field for the new inode.
+ *	This hook is called by the fs code as part of the inode creation
+ *	transaction and provides for atomic labeling of the inode, unlike
+ *	the post_create/mkdir/... hooks called by the VFS.  The hook function
+ *	is expected to allocate the name and value via kmalloc, with the caller
+ *	being responsible for calling kfree after using them.
+ *	If the security module does not use security attributes or does
+ *	not wish to put a security attribute on this particular inode,
+ *	then it should return -EOPNOTSUPP to skip this processing.
+ *	@inode contains the inode structure of the newly created inode.
+ *	@dir contains the inode structure of the parent directory.
+ *	@name will be set to the allocated name suffix (e.g. selinux).
+ *	@value will be set to the allocated attribute value.
+ *	@len will be set to the length of the value.
+ *	Returns 0 if @name and @value have been successfully set,
+ *		-EOPNOTSUPP if no security attribute is needed, or
+ *		-ENOMEM on memory allocation failure.
+ * @inode_create:
+ *	Check permission to create a regular file.
+ *	@dir contains inode structure of the parent of the new file.
+ *	@dentry contains the dentry structure for the file to be created.
+ *	@mode contains the file mode of the file to be created.
+ *	Return 0 if permission is granted.
+ * @inode_link:
+ *	Check permission before creating a new hard link to a file.
+ *	@old_dentry contains the dentry structure for an existing link to the file.
+ *	@dir contains the inode structure of the parent directory of the new link.
+ *	@new_dentry contains the dentry structure for the new link.
+ *	Return 0 if permission is granted.
+ * @inode_unlink:
+ *	Check the permission to remove a hard link to a file. 
+ *	@dir contains the inode structure of parent directory of the file.
+ *	@dentry contains the dentry structure for file to be unlinked.
+ *	Return 0 if permission is granted.
+ * @inode_symlink:
+ *	Check the permission to create a symbolic link to a file.
+ *	@dir contains the inode structure of parent directory of the symbolic link.
+ *	@dentry contains the dentry structure of the symbolic link.
+ *	@old_name contains the pathname of file.
+ *	Return 0 if permission is granted.
+ * @inode_mkdir:
+ *	Check permissions to create a new directory in the existing directory
+ *	associated with inode strcture @dir. 
+ *	@dir containst the inode structure of parent of the directory to be created.
+ *	@dentry contains the dentry structure of new directory.
+ *	@mode contains the mode of new directory.
+ *	Return 0 if permission is granted.
+ * @inode_rmdir:
+ *	Check the permission to remove a directory.
+ *	@dir contains the inode structure of parent of the directory to be removed.
+ *	@dentry contains the dentry structure of directory to be removed.
+ *	Return 0 if permission is granted.
+ * @inode_mknod:
+ *	Check permissions when creating a special file (or a socket or a fifo
+ *	file created via the mknod system call).  Note that if mknod operation
+ *	is being done for a regular file, then the create hook will be called
+ *	and not this hook.
+ *	@dir contains the inode structure of parent of the new file.
+ *	@dentry contains the dentry structure of the new file.
+ *	@mode contains the mode of the new file.
+ *	@dev contains the the device number.
+ *	Return 0 if permission is granted.
+ * @inode_rename:
+ *	Check for permission to rename a file or directory.
+ *	@old_dir contains the inode structure for parent of the old link.
+ *	@old_dentry contains the dentry structure of the old link.
+ *	@new_dir contains the inode structure for parent of the new link.
+ *	@new_dentry contains the dentry structure of the new link.
+ *	Return 0 if permission is granted.
+ * @inode_readlink:
+ *	Check the permission to read the symbolic link.
+ *	@dentry contains the dentry structure for the file link.
+ *	Return 0 if permission is granted.
+ * @inode_follow_link:
+ *	Check permission to follow a symbolic link when looking up a pathname.
+ *	@dentry contains the dentry structure for the link.
+ *	@nd contains the nameidata structure for the parent directory.
+ *	Return 0 if permission is granted.
+ * @inode_permission:
+ *	Check permission before accessing an inode.  This hook is called by the
+ *	existing Linux permission function, so a security module can use it to
+ *	provide additional checking for existing Linux permission checks.
+ *	Notice that this hook is called when a file is opened (as well as many
+ *	other operations), whereas the file_security_ops permission hook is
+ *	called when the actual read/write operations are performed.
+ *	@inode contains the inode structure to check.
+ *	@mask contains the permission mask.
+ *     @nd contains the nameidata (may be NULL).
+ *	Return 0 if permission is granted.
+ * @inode_setattr:
+ *	Check permission before setting file attributes.  Note that the kernel
+ *	call to notify_change is performed from several locations, whenever
+ *	file attributes change (such as when a file is truncated, chown/chmod
+ *	operations, transferring disk quotas, etc).
+ *	@dentry contains the dentry structure for the file.
+ *	@attr is the iattr structure containing the new file attributes.
+ *	Return 0 if permission is granted.
+ * @inode_getattr:
+ *	Check permission before obtaining file attributes.
+ *	@mnt is the vfsmount where the dentry was looked up
+ *	@dentry contains the dentry structure for the file.
+ *	Return 0 if permission is granted.
+ * @inode_delete:
+ *	@inode contains the inode structure for deleted inode.
+ *	This hook is called when a deleted inode is released (i.e. an inode
+ *	with no hard links has its use count drop to zero).  A security module
+ *	can use this hook to release any persistent label associated with the
+ *	inode.
+ * @inode_setxattr:
+ * 	Check permission before setting the extended attributes
+ * 	@value identified by @name for @dentry.
+ * 	Return 0 if permission is granted.
+ * @inode_post_setxattr:
+ * 	Update inode security field after successful setxattr operation.
+ * 	@value identified by @name for @dentry.
+ * @inode_getxattr:
+ * 	Check permission before obtaining the extended attributes
+ * 	identified by @name for @dentry.
+ * 	Return 0 if permission is granted.
+ * @inode_listxattr:
+ * 	Check permission before obtaining the list of extended attribute 
+ * 	names for @dentry.
+ * 	Return 0 if permission is granted.
+ * @inode_removexattr:
+ * 	Check permission before removing the extended attribute
+ * 	identified by @name for @dentry.
+ * 	Return 0 if permission is granted.
+ * @inode_getsecurity:
+ *	Copy the extended attribute representation of the security label 
+ *	associated with @name for @inode into @buffer.  @buffer may be
+ *	NULL to request the size of the buffer required.  @size indicates
+ *	the size of @buffer in bytes.  Note that @name is the remainder
+ *	of the attribute name after the security. prefix has been removed.
+ *	@err is the return value from the preceding fs getxattr call,
+ *	and can be used by the security module to determine whether it
+ *	should try and canonicalize the attribute value.
+ *	Return number of bytes used/required on success.
+ * @inode_setsecurity:
+ *	Set the security label associated with @name for @inode from the
+ *	extended attribute value @value.  @size indicates the size of the
+ *	@value in bytes.  @flags may be XATTR_CREATE, XATTR_REPLACE, or 0.
+ *	Note that @name is the remainder of the attribute name after the 
+ *	security. prefix has been removed.
+ *	Return 0 on success.
+ * @inode_listsecurity:
+ *	Copy the extended attribute names for the security labels
+ *	associated with @inode into @buffer.  The maximum size of @buffer
+ *	is specified by @buffer_size.  @buffer may be NULL to request
+ *	the size of the buffer required.
+ *	Returns number of bytes used/required on success.
+ *
+ * Security hooks for file operations
+ *
+ * @file_permission:
+ *	Check file permissions before accessing an open file.  This hook is
+ *	called by various operations that read or write files.  A security
+ *	module can use this hook to perform additional checking on these
+ *	operations, e.g.  to revalidate permissions on use to support privilege
+ *	bracketing or policy changes.  Notice that this hook is used when the
+ *	actual read/write operations are performed, whereas the
+ *	inode_security_ops hook is called when a file is opened (as well as
+ *	many other operations).
+ *	Caveat:  Although this hook can be used to revalidate permissions for
+ *	various system call operations that read or write files, it does not
+ *	address the revalidation of permissions for memory-mapped files.
+ *	Security modules must handle this separately if they need such
+ *	revalidation.
+ *	@file contains the file structure being accessed.
+ *	@mask contains the requested permissions.
+ *	Return 0 if permission is granted.
+ * @file_alloc_security:
+ *	Allocate and attach a security structure to the file->f_security field.
+ *	The security field is initialized to NULL when the structure is first
+ *	created.
+ *	@file contains the file structure to secure.
+ *	Return 0 if the hook is successful and permission is granted.
+ * @file_free_security:
+ *	Deallocate and free any security structures stored in file->f_security.
+ *	@file contains the file structure being modified.
+ * @file_ioctl:
+ *	@file contains the file structure.
+ *	@cmd contains the operation to perform.
+ *	@arg contains the operational arguments.
+ *	Check permission for an ioctl operation on @file.  Note that @arg can
+ *	sometimes represents a user space pointer; in other cases, it may be a
+ *	simple integer value.  When @arg represents a user space pointer, it
+ *	should never be used by the security module.
+ *	Return 0 if permission is granted.
+ * @file_mmap :
+ *	Check permissions for a mmap operation.  The @file may be NULL, e.g.
+ *	if mapping anonymous memory.
+ *	@file contains the file structure for file to map (may be NULL).
+ *	@reqprot contains the protection requested by the application.
+ *	@prot contains the protection that will be applied by the kernel.
+ *	@flags contains the operational flags.
+ *	Return 0 if permission is granted.
+ * @file_mprotect:
+ *	Check permissions before changing memory access permissions.
+ *	@vma contains the memory region to modify.
+ *	@reqprot contains the protection requested by the application.
+ *	@prot contains the protection that will be applied by the kernel.
+ *	Return 0 if permission is granted.
+ * @file_lock:
+ *	Check permission before performing file locking operations.
+ *	Note: this hook mediates both flock and fcntl style locks.
+ *	@file contains the file structure.
+ *	@cmd contains the posix-translated lock operation to perform
+ *	(e.g. F_RDLCK, F_WRLCK).
+ *	Return 0 if permission is granted.
+ * @file_fcntl:
+ *	Check permission before allowing the file operation specified by @cmd
+ *	from being performed on the file @file.  Note that @arg can sometimes
+ *	represents a user space pointer; in other cases, it may be a simple
+ *	integer value.  When @arg represents a user space pointer, it should
+ *	never be used by the security module.
+ *	@file contains the file structure.
+ *	@cmd contains the operation to be performed.
+ *	@arg contains the operational arguments.
+ *	Return 0 if permission is granted.
+ * @file_set_fowner:
+ *	Save owner security information (typically from current->security) in
+ *	file->f_security for later use by the send_sigiotask hook.
+ *	@file contains the file structure to update.
+ *	Return 0 on success.
+ * @file_send_sigiotask:
+ *	Check permission for the file owner @fown to send SIGIO or SIGURG to the
+ *	process @tsk.  Note that this hook is sometimes called from interrupt.
+ *	Note that the fown_struct, @fown, is never outside the context of a
+ *	struct file, so the file structure (and associated security information)
+ *	can always be obtained:
+ *		(struct file *)((long)fown - offsetof(struct file,f_owner));
+ * 	@tsk contains the structure of task receiving signal.
+ *	@fown contains the file owner information.
+ *	@sig is the signal that will be sent.  When 0, kernel sends SIGIO.
+ *	Return 0 if permission is granted.
+ * @file_receive:
+ *	This hook allows security modules to control the ability of a process
+ *	to receive an open file descriptor via socket IPC.
+ *	@file contains the file structure being received.
+ *	Return 0 if permission is granted.
+ *
+ * Security hooks for task operations.
+ *
+ * @task_create:
+ *	Check permission before creating a child process.  See the clone(2)
+ *	manual page for definitions of the @clone_flags.
+ *	@clone_flags contains the flags indicating what should be shared.
+ *	Return 0 if permission is granted.
+ * @task_alloc_security:
+ *	@p contains the task_struct for child process.
+ *	Allocate and attach a security structure to the p->security field. The
+ *	security field is initialized to NULL when the task structure is
+ *	allocated.
+ *	Return 0 if operation was successful.
+ * @task_free_security:
+ *	@p contains the task_struct for process.
+ *	Deallocate and clear the p->security field.
+ * @task_setuid:
+ *	Check permission before setting one or more of the user identity
+ *	attributes of the current process.  The @flags parameter indicates
+ *	which of the set*uid system calls invoked this hook and how to
+ *	interpret the @id0, @id1, and @id2 parameters.  See the LSM_SETID
+ *	definitions at the beginning of this file for the @flags values and
+ *	their meanings.
+ *	@id0 contains a uid.
+ *	@id1 contains a uid.
+ *	@id2 contains a uid.
+ *	@flags contains one of the LSM_SETID_* values.
+ *	Return 0 if permission is granted.
+ * @task_post_setuid:
+ *	Update the module's state after setting one or more of the user
+ *	identity attributes of the current process.  The @flags parameter
+ *	indicates which of the set*uid system calls invoked this hook.  If
+ *	@flags is LSM_SETID_FS, then @old_ruid is the old fs uid and the other
+ *	parameters are not used.
+ *	@old_ruid contains the old real uid (or fs uid if LSM_SETID_FS).
+ *	@old_euid contains the old effective uid (or -1 if LSM_SETID_FS).
+ *	@old_suid contains the old saved uid (or -1 if LSM_SETID_FS).
+ *	@flags contains one of the LSM_SETID_* values.
+ *	Return 0 on success.
+ * @task_setgid:
+ *	Check permission before setting one or more of the group identity
+ *	attributes of the current process.  The @flags parameter indicates
+ *	which of the set*gid system calls invoked this hook and how to
+ *	interpret the @id0, @id1, and @id2 parameters.  See the LSM_SETID
+ *	definitions at the beginning of this file for the @flags values and
+ *	their meanings.
+ *	@id0 contains a gid.
+ *	@id1 contains a gid.
+ *	@id2 contains a gid.
+ *	@flags contains one of the LSM_SETID_* values.
+ *	Return 0 if permission is granted.
+ * @task_setpgid:
+ *	Check permission before setting the process group identifier of the
+ *	process @p to @pgid.
+ *	@p contains the task_struct for process being modified.
+ *	@pgid contains the new pgid.
+ *	Return 0 if permission is granted.
+ * @task_getpgid:
+ *	Check permission before getting the process group identifier of the
+ *	process @p.
+ *	@p contains the task_struct for the process.
+ *	Return 0 if permission is granted.
+ * @task_getsid:
+ *	Check permission before getting the session identifier of the process
+ *	@p.
+ *	@p contains the task_struct for the process.
+ *	Return 0 if permission is granted.
+ * @task_setgroups:
+ *	Check permission before setting the supplementary group set of the
+ *	current process.
+ *	@group_info contains the new group information.
+ *	Return 0 if permission is granted.
+ * @task_setnice:
+ *	Check permission before setting the nice value of @p to @nice.
+ *	@p contains the task_struct of process.
+ *	@nice contains the new nice value.
+ *	Return 0 if permission is granted.
+ * @task_setrlimit:
+ *	Check permission before setting the resource limits of the current
+ *	process for @resource to @new_rlim.  The old resource limit values can
+ *	be examined by dereferencing (current->signal->rlim + resource).
+ *	@resource contains the resource whose limit is being set.
+ *	@new_rlim contains the new limits for @resource.
+ *	Return 0 if permission is granted.
+ * @task_setscheduler:
+ *	Check permission before setting scheduling policy and/or parameters of
+ *	process @p based on @policy and @lp.
+ *	@p contains the task_struct for process.
+ *	@policy contains the scheduling policy.
+ *	@lp contains the scheduling parameters.
+ *	Return 0 if permission is granted.
+ * @task_getscheduler:
+ *	Check permission before obtaining scheduling information for process
+ *	@p.
+ *	@p contains the task_struct for process.
+ *	Return 0 if permission is granted.
+ * @task_kill:
+ *	Check permission before sending signal @sig to @p.  @info can be NULL,
+ *	the constant 1, or a pointer to a siginfo structure.  If @info is 1 or
+ *	SI_FROMKERNEL(info) is true, then the signal should be viewed as coming
+ *	from the kernel and should typically be permitted.
+ *	SIGIO signals are handled separately by the send_sigiotask hook in
+ *	file_security_ops.
+ *	@p contains the task_struct for process.
+ *	@info contains the signal information.
+ *	@sig contains the signal value.
+ *	Return 0 if permission is granted.
+ * @task_wait:
+ *	Check permission before allowing a process to reap a child process @p
+ *	and collect its status information.
+ *	@p contains the task_struct for process.
+ *	Return 0 if permission is granted.
+ * @task_prctl:
+ *	Check permission before performing a process control operation on the
+ *	current process.
+ *	@option contains the operation.
+ *	@arg2 contains a argument.
+ *	@arg3 contains a argument.
+ *	@arg4 contains a argument.
+ *	@arg5 contains a argument.
+ *	Return 0 if permission is granted.
+ * @task_reparent_to_init:
+ * 	Set the security attributes in @p->security for a kernel thread that
+ * 	is being reparented to the init task.
+ *	@p contains the task_struct for the kernel thread.
+ * @task_to_inode:
+ * 	Set the security attributes for an inode based on an associated task's
+ * 	security attributes, e.g. for /proc/pid inodes.
+ *	@p contains the task_struct for the task.
+ *	@inode contains the inode structure for the inode.
+ *
+ * Security hooks for Netlink messaging.
+ *
+ * @netlink_send:
+ *	Save security information for a netlink message so that permission
+ *	checking can be performed when the message is processed.  The security
+ *	information can be saved using the eff_cap field of the
+ *      netlink_skb_parms structure.  Also may be used to provide fine
+ *	grained control over message transmission.
+ *	@sk associated sock of task sending the message.,
+ *	@skb contains the sk_buff structure for the netlink message.
+ *	Return 0 if the information was successfully saved and message
+ *	is allowed to be transmitted.
+ * @netlink_recv:
+ *	Check permission before processing the received netlink message in
+ *	@skb.
+ *	@skb contains the sk_buff structure for the netlink message.
+ *	Return 0 if permission is granted.
+ *
+ * Security hooks for Unix domain networking.
+ *
+ * @unix_stream_connect:
+ *	Check permissions before establishing a Unix domain stream connection
+ *	between @sock and @other.
+ *	@sock contains the socket structure.
+ *	@other contains the peer socket structure.
+ *	Return 0 if permission is granted.
+ * @unix_may_send:
+ *	Check permissions before connecting or sending datagrams from @sock to
+ *	@other.
+ *	@sock contains the socket structure.
+ *	@sock contains the peer socket structure.
+ *	Return 0 if permission is granted.
+ *
+ * The @unix_stream_connect and @unix_may_send hooks were necessary because
+ * Linux provides an alternative to the conventional file name space for Unix
+ * domain sockets.  Whereas binding and connecting to sockets in the file name
+ * space is mediated by the typical file permissions (and caught by the mknod
+ * and permission hooks in inode_security_ops), binding and connecting to
+ * sockets in the abstract name space is completely unmediated.  Sufficient
+ * control of Unix domain sockets in the abstract name space isn't possible
+ * using only the socket layer hooks, since we need to know the actual target
+ * socket, which is not looked up until we are inside the af_unix code.
+ *
+ * Security hooks for socket operations.
+ *
+ * @socket_create:
+ *	Check permissions prior to creating a new socket.
+ *	@family contains the requested protocol family.
+ *	@type contains the requested communications type.
+ *	@protocol contains the requested protocol.
+ *	@kern set to 1 if a kernel socket.
+ *	Return 0 if permission is granted.
+ * @socket_post_create:
+ *	This hook allows a module to update or allocate a per-socket security
+ *	structure. Note that the security field was not added directly to the
+ *	socket structure, but rather, the socket security information is stored
+ *	in the associated inode.  Typically, the inode alloc_security hook will
+ *	allocate and and attach security information to
+ *	sock->inode->i_security.  This hook may be used to update the
+ *	sock->inode->i_security field with additional information that wasn't
+ *	available when the inode was allocated.
+ *	@sock contains the newly created socket structure.
+ *	@family contains the requested protocol family.
+ *	@type contains the requested communications type.
+ *	@protocol contains the requested protocol.
+ *	@kern set to 1 if a kernel socket.
+ * @socket_bind:
+ *	Check permission before socket protocol layer bind operation is
+ *	performed and the socket @sock is bound to the address specified in the
+ *	@address parameter.
+ *	@sock contains the socket structure.
+ *	@address contains the address to bind to.
+ *	@addrlen contains the length of address.
+ *	Return 0 if permission is granted.  
+ * @socket_connect:
+ *	Check permission before socket protocol layer connect operation
+ *	attempts to connect socket @sock to a remote address, @address.
+ *	@sock contains the socket structure.
+ *	@address contains the address of remote endpoint.
+ *	@addrlen contains the length of address.
+ *	Return 0 if permission is granted.  
+ * @socket_listen:
+ *	Check permission before socket protocol layer listen operation.
+ *	@sock contains the socket structure.
+ *	@backlog contains the maximum length for the pending connection queue.
+ *	Return 0 if permission is granted.
+ * @socket_accept:
+ *	Check permission before accepting a new connection.  Note that the new
+ *	socket, @newsock, has been created and some information copied to it,
+ *	but the accept operation has not actually been performed.
+ *	@sock contains the listening socket structure.
+ *	@newsock contains the newly created server socket for connection.
+ *	Return 0 if permission is granted.
+ * @socket_post_accept:
+ *	This hook allows a security module to copy security
+ *	information into the newly created socket's inode.
+ *	@sock contains the listening socket structure.
+ *	@newsock contains the newly created server socket for connection.
+ * @socket_sendmsg:
+ *	Check permission before transmitting a message to another socket.
+ *	@sock contains the socket structure.
+ *	@msg contains the message to be transmitted.
+ *	@size contains the size of message.
+ *	Return 0 if permission is granted.
+ * @socket_recvmsg:
+ *	Check permission before receiving a message from a socket.
+ *	@sock contains the socket structure.
+ *	@msg contains the message structure.
+ *	@size contains the size of message structure.
+ *	@flags contains the operational flags.
+ *	Return 0 if permission is granted.  
+ * @socket_getsockname:
+ *	Check permission before the local address (name) of the socket object
+ *	@sock is retrieved.
+ *	@sock contains the socket structure.
+ *	Return 0 if permission is granted.
+ * @socket_getpeername:
+ *	Check permission before the remote address (name) of a socket object
+ *	@sock is retrieved.
+ *	@sock contains the socket structure.
+ *	Return 0 if permission is granted.
+ * @socket_getsockopt:
+ *	Check permissions before retrieving the options associated with socket
+ *	@sock.
+ *	@sock contains the socket structure.
+ *	@level contains the protocol level to retrieve option from.
+ *	@optname contains the name of option to retrieve.
+ *	Return 0 if permission is granted.
+ * @socket_setsockopt:
+ *	Check permissions before setting the options associated with socket
+ *	@sock.
+ *	@sock contains the socket structure.
+ *	@level contains the protocol level to set options for.
+ *	@optname contains the name of the option to set.
+ *	Return 0 if permission is granted.  
+ * @socket_shutdown:
+ *	Checks permission before all or part of a connection on the socket
+ *	@sock is shut down.
+ *	@sock contains the socket structure.
+ *	@how contains the flag indicating how future sends and receives are handled.
+ *	Return 0 if permission is granted.
+ * @socket_sock_rcv_skb:
+ *	Check permissions on incoming network packets.  This hook is distinct
+ *	from Netfilter's IP input hooks since it is the first time that the
+ *	incoming sk_buff @skb has been associated with a particular socket, @sk.
+ *	@sk contains the sock (not socket) associated with the incoming sk_buff.
+ *	@skb contains the incoming network data.
+ * @socket_getpeersec:
+ *	This hook allows the security module to provide peer socket security
+ *	state to userspace via getsockopt SO_GETPEERSEC.
+ *	@sock is the local socket.
+ *	@optval userspace memory where the security state is to be copied.
+ *	@optlen userspace int where the module should copy the actual length
+ *	of the security state.
+ *	@len as input is the maximum length to copy to userspace provided
+ *	by the caller.
+ *	Return 0 if all is well, otherwise, typical getsockopt return
+ *	values.
+ * @sk_alloc_security:
+ *      Allocate and attach a security structure to the sk->sk_security field,
+ *      which is used to copy security attributes between local stream sockets.
+ * @sk_free_security:
+ *	Deallocate security structure.
+ *
+ * Security hooks affecting all Key Management operations
+ *
+ * @key_alloc:
+ *	Permit allocation of a key and assign security data. Note that key does
+ *	not have a serial number assigned at this point.
+ *	@key points to the key.
+ *	Return 0 if permission is granted, -ve error otherwise.
+ * @key_free:
+ *	Notification of destruction; free security data.
+ *	@key points to the key.
+ *	No return value.
+ * @key_permission:
+ *	See whether a specific operational right is granted to a process on a
+ *      key.
+ *	@key_ref refers to the key (key pointer + possession attribute bit).
+ *	@context points to the process to provide the context against which to
+ *       evaluate the security data on the key.
+ *	@perm describes the combination of permissions required of this key.
+ *	Return 1 if permission granted, 0 if permission denied and -ve it the
+ *      normal permissions model should be effected.
+ *
+ * Security hooks affecting all System V IPC operations.
+ *
+ * @ipc_permission:
+ *	Check permissions for access to IPC
+ *	@ipcp contains the kernel IPC permission structure
+ *	@flag contains the desired (requested) permission set
+ *	Return 0 if permission is granted.
+ * @ipc_getsecurity:
+ *      Copy the security label associated with the ipc object into
+ *      @buffer.  @buffer may be NULL to request the size of the buffer 
+ *      required.  @size indicates the size of @buffer in bytes. Return 
+ *      number of bytes used/required on success.
+ *
+ * Security hooks for individual messages held in System V IPC message queues
+ * @msg_msg_alloc_security:
+ *	Allocate and attach a security structure to the msg->security field.
+ *	The security field is initialized to NULL when the structure is first
+ *	created.
+ *	@msg contains the message structure to be modified.
+ *	Return 0 if operation was successful and permission is granted.
+ * @msg_msg_free_security:
+ *	Deallocate the security structure for this message.
+ *	@msg contains the message structure to be modified.
+ *
+ * Security hooks for System V IPC Message Queues
+ *
+ * @msg_queue_alloc_security:
+ *	Allocate and attach a security structure to the
+ *	msq->q_perm.security field. The security field is initialized to
+ *	NULL when the structure is first created.
+ *	@msq contains the message queue structure to be modified.
+ *	Return 0 if operation was successful and permission is granted.
+ * @msg_queue_free_security:
+ *	Deallocate security structure for this message queue.
+ *	@msq contains the message queue structure to be modified.
+ * @msg_queue_associate:
+ *	Check permission when a message queue is requested through the
+ *	msgget system call.  This hook is only called when returning the
+ *	message queue identifier for an existing message queue, not when a
+ *	new message queue is created.
+ *	@msq contains the message queue to act upon.
+ *	@msqflg contains the operation control flags.
+ *	Return 0 if permission is granted.
+ * @msg_queue_msgctl:
+ *	Check permission when a message control operation specified by @cmd
+ *	is to be performed on the message queue @msq.
+ *	The @msq may be NULL, e.g. for IPC_INFO or MSG_INFO.
+ *	@msq contains the message queue to act upon.  May be NULL.
+ *	@cmd contains the operation to be performed.
+ *	Return 0 if permission is granted.  
+ * @msg_queue_msgsnd:
+ *	Check permission before a message, @msg, is enqueued on the message
+ *	queue, @msq.
+ *	@msq contains the message queue to send message to.
+ *	@msg contains the message to be enqueued.
+ *	@msqflg contains operational flags.
+ *	Return 0 if permission is granted.
+ * @msg_queue_msgrcv:
+ *	Check permission before a message, @msg, is removed from the message
+ *	queue, @msq.  The @target task structure contains a pointer to the 
+ *	process that will be receiving the message (not equal to the current 
+ *	process when inline receives are being performed).
+ *	@msq contains the message queue to retrieve message from.
+ *	@msg contains the message destination.
+ *	@target contains the task structure for recipient process.
+ *	@type contains the type of message requested.
+ *	@mode contains the operational flags.
+ *	Return 0 if permission is granted.
+ *
+ * Security hooks for System V Shared Memory Segments
+ *
+ * @shm_alloc_security:
+ *	Allocate and attach a security structure to the shp->shm_perm.security
+ *	field.  The security field is initialized to NULL when the structure is
+ *	first created.
+ *	@shp contains the shared memory structure to be modified.
+ *	Return 0 if operation was successful and permission is granted.
+ * @shm_free_security:
+ *	Deallocate the security struct for this memory segment.
+ *	@shp contains the shared memory structure to be modified.
+ * @shm_associate:
+ *	Check permission when a shared memory region is requested through the
+ *	shmget system call.  This hook is only called when returning the shared
+ *	memory region identifier for an existing region, not when a new shared
+ *	memory region is created.
+ *	@shp contains the shared memory structure to be modified.
+ *	@shmflg contains the operation control flags.
+ *	Return 0 if permission is granted.
+ * @shm_shmctl:
+ *	Check permission when a shared memory control operation specified by
+ *	@cmd is to be performed on the shared memory region @shp.
+ *	The @shp may be NULL, e.g. for IPC_INFO or SHM_INFO.
+ *	@shp contains shared memory structure to be modified.
+ *	@cmd contains the operation to be performed.
+ *	Return 0 if permission is granted.
+ * @shm_shmat:
+ *	Check permissions prior to allowing the shmat system call to attach the
+ *	shared memory segment @shp to the data segment of the calling process.
+ *	The attaching address is specified by @shmaddr.
+ *	@shp contains the shared memory structure to be modified.
+ *	@shmaddr contains the address to attach memory region to.
+ *	@shmflg contains the operational flags.
+ *	Return 0 if permission is granted.
+ *
+ * Security hooks for System V Semaphores
+ *
+ * @sem_alloc_security:
+ *	Allocate and attach a security structure to the sma->sem_perm.security
+ *	field.  The security field is initialized to NULL when the structure is
+ *	first created.
+ *	@sma contains the semaphore structure
+ *	Return 0 if operation was successful and permission is granted.
+ * @sem_free_security:
+ *	deallocate security struct for this semaphore
+ *	@sma contains the semaphore structure.
+ * @sem_associate:
+ *	Check permission when a semaphore is requested through the semget
+ *	system call.  This hook is only called when returning the semaphore
+ *	identifier for an existing semaphore, not when a new one must be
+ *	created.
+ *	@sma contains the semaphore structure.
+ *	@semflg contains the operation control flags.
+ *	Return 0 if permission is granted.
+ * @sem_semctl:
+ *	Check permission when a semaphore operation specified by @cmd is to be
+ *	performed on the semaphore @sma.  The @sma may be NULL, e.g. for 
+ *	IPC_INFO or SEM_INFO.
+ *	@sma contains the semaphore structure.  May be NULL.
+ *	@cmd contains the operation to be performed.
+ *	Return 0 if permission is granted.
+ * @sem_semop
+ *	Check permissions before performing operations on members of the
+ *	semaphore set @sma.  If the @alter flag is nonzero, the semaphore set 
+ *      may be modified.
+ *	@sma contains the semaphore structure.
+ *	@sops contains the operations to perform.
+ *	@nsops contains the number of operations to perform.
+ *	@alter contains the flag indicating whether changes are to be made.
+ *	Return 0 if permission is granted.
+ *
+ * @ptrace:
+ *	Check permission before allowing the @parent process to trace the
+ *	@child process.
+ *	Security modules may also want to perform a process tracing check
+ *	during an execve in the set_security or apply_creds hooks of
+ *	binprm_security_ops if the process is being traced and its security
+ *	attributes would be changed by the execve.
+ *	@parent contains the task_struct structure for parent process.
+ *	@child contains the task_struct structure for child process.
+ *	Return 0 if permission is granted.
+ * @capget:
+ *	Get the @effective, @inheritable, and @permitted capability sets for
+ *	the @target process.  The hook may also perform permission checking to
+ *	determine if the current process is allowed to see the capability sets
+ *	of the @target process.
+ *	@target contains the task_struct structure for target process.
+ *	@effective contains the effective capability set.
+ *	@inheritable contains the inheritable capability set.
+ *	@permitted contains the permitted capability set.
+ *	Return 0 if the capability sets were successfully obtained.
+ * @capset_check:
+ *	Check permission before setting the @effective, @inheritable, and
+ *	@permitted capability sets for the @target process.
+ *	Caveat:  @target is also set to current if a set of processes is
+ *	specified (i.e. all processes other than current and init or a
+ *	particular process group).  Hence, the capset_set hook may need to
+ *	revalidate permission to the actual target process.
+ *	@target contains the task_struct structure for target process.
+ *	@effective contains the effective capability set.
+ *	@inheritable contains the inheritable capability set.
+ *	@permitted contains the permitted capability set.
+ *	Return 0 if permission is granted.
+ * @capset_set:
+ *	Set the @effective, @inheritable, and @permitted capability sets for
+ *	the @target process.  Since capset_check cannot always check permission
+ *	to the real @target process, this hook may also perform permission
+ *	checking to determine if the current process is allowed to set the
+ *	capability sets of the @target process.  However, this hook has no way
+ *	of returning an error due to the structure of the sys_capset code.
+ *	@target contains the task_struct structure for target process.
+ *	@effective contains the effective capability set.
+ *	@inheritable contains the inheritable capability set.
+ *	@permitted contains the permitted capability set.
+ * @acct:
+ *	Check permission before enabling or disabling process accounting.  If
+ *	accounting is being enabled, then @file refers to the open file used to
+ *	store accounting records.  If accounting is being disabled, then @file
+ *	is NULL.
+ *	@file contains the file structure for the accounting file (may be NULL).
+ *	Return 0 if permission is granted.
+ * @sysctl:
+ *	Check permission before accessing the @table sysctl variable in the
+ *	manner specified by @op.
+ *	@table contains the ctl_table structure for the sysctl variable.
+ *	@op contains the operation (001 = search, 002 = write, 004 = read).
+ *	Return 0 if permission is granted.
+ * @capable:
+ *	Check whether the @tsk process has the @cap capability.
+ *	@tsk contains the task_struct for the process.
+ *	@cap contains the capability <include/linux/capability.h>.
+ *	Return 0 if the capability is granted for @tsk.
+ * @syslog:
+ *	Check permission before accessing the kernel message ring or changing
+ *	logging to the console.
+ *	See the syslog(2) manual page for an explanation of the @type values.  
+ *	@type contains the type of action.
+ *	Return 0 if permission is granted.
+ * @settime:
+ *	Check permission to change the system time.
+ *	struct timespec and timezone are defined in include/linux/time.h
+ *	@ts contains new time
+ *	@tz contains new timezone
+ *	Return 0 if permission is granted.
+ * @vm_enough_memory:
+ *	Check permissions for allocating a new virtual mapping.
+ *      @pages contains the number of pages.
+ *	Return 0 if permission is granted.
+ *
+ * @register_security:
+ * 	allow module stacking.
+ * 	@name contains the name of the security module being stacked.
+ * 	@ops contains a pointer to the struct security_operations of the module to stack.
+ * @unregister_security:
+ *	remove a stacked module.
+ *	@name contains the name of the security module being unstacked.
+ *	@ops contains a pointer to the struct security_operations of the module to unstack.
+ * 
+ * This is the main security structure.
+ */
+struct security_operations {
+	int (*ptrace) (struct task_struct * parent, struct task_struct * child);
+	int (*capget) (struct task_struct * target,
+		       kernel_cap_t * effective,
+		       kernel_cap_t * inheritable, kernel_cap_t * permitted);
+	int (*capset_check) (struct task_struct * target,
+			     kernel_cap_t * effective,
+			     kernel_cap_t * inheritable,
+			     kernel_cap_t * permitted);
+	void (*capset_set) (struct task_struct * target,
+			    kernel_cap_t * effective,
+			    kernel_cap_t * inheritable,
+			    kernel_cap_t * permitted);
+	int (*acct) (struct file * file);
+	int (*sysctl) (struct ctl_table * table, int op);
+	int (*capable) (struct task_struct * tsk, int cap);
+	int (*quotactl) (int cmds, int type, int id, struct super_block * sb);
+	int (*quota_on) (struct dentry * dentry);
+	int (*syslog) (int type);
+	int (*settime) (struct timespec *ts, struct timezone *tz);
+	int (*vm_enough_memory) (long pages);
+
+	int (*bprm_alloc_security) (struct linux_binprm * bprm);
+	void (*bprm_free_security) (struct linux_binprm * bprm);
+	void (*bprm_apply_creds) (struct linux_binprm * bprm, int unsafe);
+	void (*bprm_post_apply_creds) (struct linux_binprm * bprm);
+	int (*bprm_set_security) (struct linux_binprm * bprm);
+	int (*bprm_check_security) (struct linux_binprm * bprm);
+	int (*bprm_secureexec) (struct linux_binprm * bprm);
+
+	int (*sb_alloc_security) (struct super_block * sb);
+	void (*sb_free_security) (struct super_block * sb);
+	int (*sb_copy_data)(struct file_system_type *type,
+			    void *orig, void *copy);
+	int (*sb_kern_mount) (struct super_block *sb, void *data);
+	int (*sb_statfs) (struct super_block * sb);
+	int (*sb_mount) (char *dev_name, struct nameidata * nd,
+			 char *type, unsigned long flags, void *data);
+	int (*sb_check_sb) (struct vfsmount * mnt, struct nameidata * nd);
+	int (*sb_umount) (struct vfsmount * mnt, int flags);
+	void (*sb_umount_close) (struct vfsmount * mnt);
+	void (*sb_umount_busy) (struct vfsmount * mnt);
+	void (*sb_post_remount) (struct vfsmount * mnt,
+				 unsigned long flags, void *data);
+	void (*sb_post_mountroot) (void);
+	void (*sb_post_addmount) (struct vfsmount * mnt,
+				  struct nameidata * mountpoint_nd);
+	int (*sb_pivotroot) (struct nameidata * old_nd,
+			     struct nameidata * new_nd);
+	void (*sb_post_pivotroot) (struct nameidata * old_nd,
+				   struct nameidata * new_nd);
+
+	int (*inode_alloc_security) (struct inode *inode);	
+	void (*inode_free_security) (struct inode *inode);
+	int (*inode_init_security) (struct inode *inode, struct inode *dir,
+				    char **name, void **value, size_t *len);
+	int (*inode_create) (struct inode *dir,
+	                     struct dentry *dentry, int mode);
+	int (*inode_link) (struct dentry *old_dentry,
+	                   struct inode *dir, struct dentry *new_dentry);
+	int (*inode_unlink) (struct inode *dir, struct dentry *dentry);
+	int (*inode_symlink) (struct inode *dir,
+	                      struct dentry *dentry, const char *old_name);
+	int (*inode_mkdir) (struct inode *dir, struct dentry *dentry, int mode);
+	int (*inode_rmdir) (struct inode *dir, struct dentry *dentry);
+	int (*inode_mknod) (struct inode *dir, struct dentry *dentry,
+	                    int mode, dev_t dev);
+	int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry,
+	                     struct inode *new_dir, struct dentry *new_dentry);
+	int (*inode_readlink) (struct dentry *dentry);
+	int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd);
+	int (*inode_permission) (struct inode *inode, int mask, struct nameidata *nd);
+	int (*inode_setattr)	(struct dentry *dentry, struct iattr *attr);
+	int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry);
+        void (*inode_delete) (struct inode *inode);
+	int (*inode_setxattr) (struct dentry *dentry, char *name, void *value,
+			       size_t size, int flags);
+	void (*inode_post_setxattr) (struct dentry *dentry, char *name, void *value,
+				     size_t size, int flags);
+	int (*inode_getxattr) (struct dentry *dentry, char *name);
+	int (*inode_listxattr) (struct dentry *dentry);
+	int (*inode_removexattr) (struct dentry *dentry, char *name);
+	const char *(*inode_xattr_getsuffix) (void);
+  	int (*inode_getsecurity)(const struct inode *inode, const char *name, void *buffer, size_t size, int err);
+  	int (*inode_setsecurity)(struct inode *inode, const char *name, const void *value, size_t size, int flags);
+  	int (*inode_listsecurity)(struct inode *inode, char *buffer, size_t buffer_size);
+
+	int (*file_permission) (struct file * file, int mask);
+	int (*file_alloc_security) (struct file * file);
+	void (*file_free_security) (struct file * file);
+	int (*file_ioctl) (struct file * file, unsigned int cmd,
+			   unsigned long arg);
+	int (*file_mmap) (struct file * file,
+			  unsigned long reqprot,
+			  unsigned long prot, unsigned long flags);
+	int (*file_mprotect) (struct vm_area_struct * vma,
+			      unsigned long reqprot,
+			      unsigned long prot);
+	int (*file_lock) (struct file * file, unsigned int cmd);
+	int (*file_fcntl) (struct file * file, unsigned int cmd,
+			   unsigned long arg);
+	int (*file_set_fowner) (struct file * file);
+	int (*file_send_sigiotask) (struct task_struct * tsk,
+				    struct fown_struct * fown, int sig);
+	int (*file_receive) (struct file * file);
+
+	int (*task_create) (unsigned long clone_flags);
+	int (*task_alloc_security) (struct task_struct * p);
+	void (*task_free_security) (struct task_struct * p);
+	int (*task_setuid) (uid_t id0, uid_t id1, uid_t id2, int flags);
+	int (*task_post_setuid) (uid_t old_ruid /* or fsuid */ ,
+				 uid_t old_euid, uid_t old_suid, int flags);
+	int (*task_setgid) (gid_t id0, gid_t id1, gid_t id2, int flags);
+	int (*task_setpgid) (struct task_struct * p, pid_t pgid);
+	int (*task_getpgid) (struct task_struct * p);
+	int (*task_getsid) (struct task_struct * p);
+	int (*task_setgroups) (struct group_info *group_info);
+	int (*task_setnice) (struct task_struct * p, int nice);
+	int (*task_setrlimit) (unsigned int resource, struct rlimit * new_rlim);
+	int (*task_setscheduler) (struct task_struct * p, int policy,
+				  struct sched_param * lp);
+	int (*task_getscheduler) (struct task_struct * p);
+	int (*task_kill) (struct task_struct * p,
+			  struct siginfo * info, int sig);
+	int (*task_wait) (struct task_struct * p);
+	int (*task_prctl) (int option, unsigned long arg2,
+			   unsigned long arg3, unsigned long arg4,
+			   unsigned long arg5);
+	void (*task_reparent_to_init) (struct task_struct * p);
+	void (*task_to_inode)(struct task_struct *p, struct inode *inode);
+
+	int (*ipc_permission) (struct kern_ipc_perm * ipcp, short flag);
+	int (*ipc_getsecurity)(struct kern_ipc_perm *ipcp, void *buffer, size_t size);
+
+	int (*msg_msg_alloc_security) (struct msg_msg * msg);
+	void (*msg_msg_free_security) (struct msg_msg * msg);
+
+	int (*msg_queue_alloc_security) (struct msg_queue * msq);
+	void (*msg_queue_free_security) (struct msg_queue * msq);
+	int (*msg_queue_associate) (struct msg_queue * msq, int msqflg);
+	int (*msg_queue_msgctl) (struct msg_queue * msq, int cmd);
+	int (*msg_queue_msgsnd) (struct msg_queue * msq,
+				 struct msg_msg * msg, int msqflg);
+	int (*msg_queue_msgrcv) (struct msg_queue * msq,
+				 struct msg_msg * msg,
+				 struct task_struct * target,
+				 long type, int mode);
+
+	int (*shm_alloc_security) (struct shmid_kernel * shp);
+	void (*shm_free_security) (struct shmid_kernel * shp);
+	int (*shm_associate) (struct shmid_kernel * shp, int shmflg);
+	int (*shm_shmctl) (struct shmid_kernel * shp, int cmd);
+	int (*shm_shmat) (struct shmid_kernel * shp, 
+			  char __user *shmaddr, int shmflg);
+
+	int (*sem_alloc_security) (struct sem_array * sma);
+	void (*sem_free_security) (struct sem_array * sma);
+	int (*sem_associate) (struct sem_array * sma, int semflg);
+	int (*sem_semctl) (struct sem_array * sma, int cmd);
+	int (*sem_semop) (struct sem_array * sma, 
+			  struct sembuf * sops, unsigned nsops, int alter);
+
+	int (*netlink_send) (struct sock * sk, struct sk_buff * skb);
+	int (*netlink_recv) (struct sk_buff * skb);
+
+	/* allow module stacking */
+	int (*register_security) (const char *name,
+	                          struct security_operations *ops);
+	int (*unregister_security) (const char *name,
+	                            struct security_operations *ops);
+
+	void (*d_instantiate) (struct dentry *dentry, struct inode *inode);
+
+ 	int (*getprocattr)(struct task_struct *p, char *name, void *value, size_t size);
+ 	int (*setprocattr)(struct task_struct *p, char *name, void *value, size_t size);
+
+#ifdef CONFIG_SECURITY_NETWORK
+	int (*unix_stream_connect) (struct socket * sock,
+				    struct socket * other, struct sock * newsk);
+	int (*unix_may_send) (struct socket * sock, struct socket * other);
+
+	int (*socket_create) (int family, int type, int protocol, int kern);
+	void (*socket_post_create) (struct socket * sock, int family,
+				    int type, int protocol, int kern);
+	int (*socket_bind) (struct socket * sock,
+			    struct sockaddr * address, int addrlen);
+	int (*socket_connect) (struct socket * sock,
+			       struct sockaddr * address, int addrlen);
+	int (*socket_listen) (struct socket * sock, int backlog);
+	int (*socket_accept) (struct socket * sock, struct socket * newsock);
+	void (*socket_post_accept) (struct socket * sock,
+				    struct socket * newsock);
+	int (*socket_sendmsg) (struct socket * sock,
+			       struct msghdr * msg, int size);
+	int (*socket_recvmsg) (struct socket * sock,
+			       struct msghdr * msg, int size, int flags);
+	int (*socket_getsockname) (struct socket * sock);
+	int (*socket_getpeername) (struct socket * sock);
+	int (*socket_getsockopt) (struct socket * sock, int level, int optname);
+	int (*socket_setsockopt) (struct socket * sock, int level, int optname);
+	int (*socket_shutdown) (struct socket * sock, int how);
+	int (*socket_sock_rcv_skb) (struct sock * sk, struct sk_buff * skb);
+	int (*socket_getpeersec) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len);
+	int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority);
+	void (*sk_free_security) (struct sock *sk);
+#endif	/* CONFIG_SECURITY_NETWORK */
+
+	/* key management security hooks */
+#ifdef CONFIG_KEYS
+	int (*key_alloc)(struct key *key);
+	void (*key_free)(struct key *key);
+	int (*key_permission)(key_ref_t key_ref,
+			      struct task_struct *context,
+			      key_perm_t perm);
+
+#endif	/* CONFIG_KEYS */
+
+};
+
+/* global variables */
+extern struct security_operations *security_ops;
+
+/* inline stuff */
+static inline int security_ptrace (struct task_struct * parent, struct task_struct * child)
+{
+	return security_ops->ptrace (parent, child);
+}
+
+static inline int security_capget (struct task_struct *target,
+				   kernel_cap_t *effective,
+				   kernel_cap_t *inheritable,
+				   kernel_cap_t *permitted)
+{
+	return security_ops->capget (target, effective, inheritable, permitted);
+}
+
+static inline int security_capset_check (struct task_struct *target,
+					 kernel_cap_t *effective,
+					 kernel_cap_t *inheritable,
+					 kernel_cap_t *permitted)
+{
+	return security_ops->capset_check (target, effective, inheritable, permitted);
+}
+
+static inline void security_capset_set (struct task_struct *target,
+					kernel_cap_t *effective,
+					kernel_cap_t *inheritable,
+					kernel_cap_t *permitted)
+{
+	security_ops->capset_set (target, effective, inheritable, permitted);
+}
+
+static inline int security_acct (struct file *file)
+{
+	return security_ops->acct (file);
+}
+
+static inline int security_sysctl(struct ctl_table *table, int op)
+{
+	return security_ops->sysctl(table, op);
+}
+
+static inline int security_quotactl (int cmds, int type, int id,
+				     struct super_block *sb)
+{
+	return security_ops->quotactl (cmds, type, id, sb);
+}
+
+static inline int security_quota_on (struct dentry * dentry)
+{
+	return security_ops->quota_on (dentry);
+}
+
+static inline int security_syslog(int type)
+{
+	return security_ops->syslog(type);
+}
+
+static inline int security_settime(struct timespec *ts, struct timezone *tz)
+{
+	return security_ops->settime(ts, tz);
+}
+
+
+static inline int security_vm_enough_memory(long pages)
+{
+	return security_ops->vm_enough_memory(pages);
+}
+
+static inline int security_bprm_alloc (struct linux_binprm *bprm)
+{
+	return security_ops->bprm_alloc_security (bprm);
+}
+static inline void security_bprm_free (struct linux_binprm *bprm)
+{
+	security_ops->bprm_free_security (bprm);
+}
+static inline void security_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
+{
+	security_ops->bprm_apply_creds (bprm, unsafe);
+}
+static inline void security_bprm_post_apply_creds (struct linux_binprm *bprm)
+{
+	security_ops->bprm_post_apply_creds (bprm);
+}
+static inline int security_bprm_set (struct linux_binprm *bprm)
+{
+	return security_ops->bprm_set_security (bprm);
+}
+
+static inline int security_bprm_check (struct linux_binprm *bprm)
+{
+	return security_ops->bprm_check_security (bprm);
+}
+
+static inline int security_bprm_secureexec (struct linux_binprm *bprm)
+{
+	return security_ops->bprm_secureexec (bprm);
+}
+
+static inline int security_sb_alloc (struct super_block *sb)
+{
+	return security_ops->sb_alloc_security (sb);
+}
+
+static inline void security_sb_free (struct super_block *sb)
+{
+	security_ops->sb_free_security (sb);
+}
+
+static inline int security_sb_copy_data (struct file_system_type *type,
+					 void *orig, void *copy)
+{
+	return security_ops->sb_copy_data (type, orig, copy);
+}
+
+static inline int security_sb_kern_mount (struct super_block *sb, void *data)
+{
+	return security_ops->sb_kern_mount (sb, data);
+}
+
+static inline int security_sb_statfs (struct super_block *sb)
+{
+	return security_ops->sb_statfs (sb);
+}
+
+static inline int security_sb_mount (char *dev_name, struct nameidata *nd,
+				    char *type, unsigned long flags,
+				    void *data)
+{
+	return security_ops->sb_mount (dev_name, nd, type, flags, data);
+}
+
+static inline int security_sb_check_sb (struct vfsmount *mnt,
+					struct nameidata *nd)
+{
+	return security_ops->sb_check_sb (mnt, nd);
+}
+
+static inline int security_sb_umount (struct vfsmount *mnt, int flags)
+{
+	return security_ops->sb_umount (mnt, flags);
+}
+
+static inline void security_sb_umount_close (struct vfsmount *mnt)
+{
+	security_ops->sb_umount_close (mnt);
+}
+
+static inline void security_sb_umount_busy (struct vfsmount *mnt)
+{
+	security_ops->sb_umount_busy (mnt);
+}
+
+static inline void security_sb_post_remount (struct vfsmount *mnt,
+					     unsigned long flags, void *data)
+{
+	security_ops->sb_post_remount (mnt, flags, data);
+}
+
+static inline void security_sb_post_mountroot (void)
+{
+	security_ops->sb_post_mountroot ();
+}
+
+static inline void security_sb_post_addmount (struct vfsmount *mnt,
+					      struct nameidata *mountpoint_nd)
+{
+	security_ops->sb_post_addmount (mnt, mountpoint_nd);
+}
+
+static inline int security_sb_pivotroot (struct nameidata *old_nd,
+					 struct nameidata *new_nd)
+{
+	return security_ops->sb_pivotroot (old_nd, new_nd);
+}
+
+static inline void security_sb_post_pivotroot (struct nameidata *old_nd,
+					       struct nameidata *new_nd)
+{
+	security_ops->sb_post_pivotroot (old_nd, new_nd);
+}
+
+static inline int security_inode_alloc (struct inode *inode)
+{
+	if (unlikely (IS_PRIVATE (inode)))
+		return 0;
+	return security_ops->inode_alloc_security (inode);
+}
+
+static inline void security_inode_free (struct inode *inode)
+{
+	if (unlikely (IS_PRIVATE (inode)))
+		return;
+	security_ops->inode_free_security (inode);
+}
+
+static inline int security_inode_init_security (struct inode *inode,
+						struct inode *dir,
+						char **name,
+						void **value,
+						size_t *len)
+{
+	if (unlikely (IS_PRIVATE (inode)))
+		return -EOPNOTSUPP;
+	return security_ops->inode_init_security (inode, dir, name, value, len);
+}
+	
+static inline int security_inode_create (struct inode *dir,
+					 struct dentry *dentry,
+					 int mode)
+{
+	if (unlikely (IS_PRIVATE (dir)))
+		return 0;
+	return security_ops->inode_create (dir, dentry, mode);
+}
+
+static inline int security_inode_link (struct dentry *old_dentry,
+				       struct inode *dir,
+				       struct dentry *new_dentry)
+{
+	if (unlikely (IS_PRIVATE (old_dentry->d_inode)))
+		return 0;
+	return security_ops->inode_link (old_dentry, dir, new_dentry);
+}
+
+static inline int security_inode_unlink (struct inode *dir,
+					 struct dentry *dentry)
+{
+	if (unlikely (IS_PRIVATE (dentry->d_inode)))
+		return 0;
+	return security_ops->inode_unlink (dir, dentry);
+}
+
+static inline int security_inode_symlink (struct inode *dir,
+					  struct dentry *dentry,
+					  const char *old_name)
+{
+	if (unlikely (IS_PRIVATE (dir)))
+		return 0;
+	return security_ops->inode_symlink (dir, dentry, old_name);
+}
+
+static inline int security_inode_mkdir (struct inode *dir,
+					struct dentry *dentry,
+					int mode)
+{
+	if (unlikely (IS_PRIVATE (dir)))
+		return 0;
+	return security_ops->inode_mkdir (dir, dentry, mode);
+}
+
+static inline int security_inode_rmdir (struct inode *dir,
+					struct dentry *dentry)
+{
+	if (unlikely (IS_PRIVATE (dentry->d_inode)))
+		return 0;
+	return security_ops->inode_rmdir (dir, dentry);
+}
+
+static inline int security_inode_mknod (struct inode *dir,
+					struct dentry *dentry,
+					int mode, dev_t dev)
+{
+	if (unlikely (IS_PRIVATE (dir)))
+		return 0;
+	return security_ops->inode_mknod (dir, dentry, mode, dev);
+}
+
+static inline int security_inode_rename (struct inode *old_dir,
+					 struct dentry *old_dentry,
+					 struct inode *new_dir,
+					 struct dentry *new_dentry)
+{
+        if (unlikely (IS_PRIVATE (old_dentry->d_inode) ||
+            (new_dentry->d_inode && IS_PRIVATE (new_dentry->d_inode))))
+		return 0;
+	return security_ops->inode_rename (old_dir, old_dentry,
+					   new_dir, new_dentry);
+}
+
+static inline int security_inode_readlink (struct dentry *dentry)
+{
+	if (unlikely (IS_PRIVATE (dentry->d_inode)))
+		return 0;
+	return security_ops->inode_readlink (dentry);
+}
+
+static inline int security_inode_follow_link (struct dentry *dentry,
+					      struct nameidata *nd)
+{
+	if (unlikely (IS_PRIVATE (dentry->d_inode)))
+		return 0;
+	return security_ops->inode_follow_link (dentry, nd);
+}
+
+static inline int security_inode_permission (struct inode *inode, int mask,
+					     struct nameidata *nd)
+{
+	if (unlikely (IS_PRIVATE (inode)))
+		return 0;
+	return security_ops->inode_permission (inode, mask, nd);
+}
+
+static inline int security_inode_setattr (struct dentry *dentry,
+					  struct iattr *attr)
+{
+	if (unlikely (IS_PRIVATE (dentry->d_inode)))
+		return 0;
+	return security_ops->inode_setattr (dentry, attr);
+}
+
+static inline int security_inode_getattr (struct vfsmount *mnt,
+					  struct dentry *dentry)
+{
+	if (unlikely (IS_PRIVATE (dentry->d_inode)))
+		return 0;
+	return security_ops->inode_getattr (mnt, dentry);
+}
+
+static inline void security_inode_delete (struct inode *inode)
+{
+	if (unlikely (IS_PRIVATE (inode)))
+		return;
+	security_ops->inode_delete (inode);
+}
+
+static inline int security_inode_setxattr (struct dentry *dentry, char *name,
+					   void *value, size_t size, int flags)
+{
+	if (unlikely (IS_PRIVATE (dentry->d_inode)))
+		return 0;
+	return security_ops->inode_setxattr (dentry, name, value, size, flags);
+}
+
+static inline void security_inode_post_setxattr (struct dentry *dentry, char *name,
+						void *value, size_t size, int flags)
+{
+	if (unlikely (IS_PRIVATE (dentry->d_inode)))
+		return;
+	security_ops->inode_post_setxattr (dentry, name, value, size, flags);
+}
+
+static inline int security_inode_getxattr (struct dentry *dentry, char *name)
+{
+	if (unlikely (IS_PRIVATE (dentry->d_inode)))
+		return 0;
+	return security_ops->inode_getxattr (dentry, name);
+}
+
+static inline int security_inode_listxattr (struct dentry *dentry)
+{
+	if (unlikely (IS_PRIVATE (dentry->d_inode)))
+		return 0;
+	return security_ops->inode_listxattr (dentry);
+}
+
+static inline int security_inode_removexattr (struct dentry *dentry, char *name)
+{
+	if (unlikely (IS_PRIVATE (dentry->d_inode)))
+		return 0;
+	return security_ops->inode_removexattr (dentry, name);
+}
+
+static inline const char *security_inode_xattr_getsuffix(void)
+{
+	return security_ops->inode_xattr_getsuffix();
+}
+
+static inline int security_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err)
+{
+	if (unlikely (IS_PRIVATE (inode)))
+		return 0;
+	return security_ops->inode_getsecurity(inode, name, buffer, size, err);
+}
+
+static inline int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags)
+{
+	if (unlikely (IS_PRIVATE (inode)))
+		return 0;
+	return security_ops->inode_setsecurity(inode, name, value, size, flags);
+}
+
+static inline int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size)
+{
+	if (unlikely (IS_PRIVATE (inode)))
+		return 0;
+	return security_ops->inode_listsecurity(inode, buffer, buffer_size);
+}
+
+static inline int security_file_permission (struct file *file, int mask)
+{
+	return security_ops->file_permission (file, mask);
+}
+
+static inline int security_file_alloc (struct file *file)
+{
+	return security_ops->file_alloc_security (file);
+}
+
+static inline void security_file_free (struct file *file)
+{
+	security_ops->file_free_security (file);
+}
+
+static inline int security_file_ioctl (struct file *file, unsigned int cmd,
+				       unsigned long arg)
+{
+	return security_ops->file_ioctl (file, cmd, arg);
+}
+
+static inline int security_file_mmap (struct file *file, unsigned long reqprot,
+				      unsigned long prot,
+				      unsigned long flags)
+{
+	return security_ops->file_mmap (file, reqprot, prot, flags);
+}
+
+static inline int security_file_mprotect (struct vm_area_struct *vma,
+					  unsigned long reqprot,
+					  unsigned long prot)
+{
+	return security_ops->file_mprotect (vma, reqprot, prot);
+}
+
+static inline int security_file_lock (struct file *file, unsigned int cmd)
+{
+	return security_ops->file_lock (file, cmd);
+}
+
+static inline int security_file_fcntl (struct file *file, unsigned int cmd,
+				       unsigned long arg)
+{
+	return security_ops->file_fcntl (file, cmd, arg);
+}
+
+static inline int security_file_set_fowner (struct file *file)
+{
+	return security_ops->file_set_fowner (file);
+}
+
+static inline int security_file_send_sigiotask (struct task_struct *tsk,
+						struct fown_struct *fown,
+						int sig)
+{
+	return security_ops->file_send_sigiotask (tsk, fown, sig);
+}
+
+static inline int security_file_receive (struct file *file)
+{
+	return security_ops->file_receive (file);
+}
+
+static inline int security_task_create (unsigned long clone_flags)
+{
+	return security_ops->task_create (clone_flags);
+}
+
+static inline int security_task_alloc (struct task_struct *p)
+{
+	return security_ops->task_alloc_security (p);
+}
+
+static inline void security_task_free (struct task_struct *p)
+{
+	security_ops->task_free_security (p);
+}
+
+static inline int security_task_setuid (uid_t id0, uid_t id1, uid_t id2,
+					int flags)
+{
+	return security_ops->task_setuid (id0, id1, id2, flags);
+}
+
+static inline int security_task_post_setuid (uid_t old_ruid, uid_t old_euid,
+					     uid_t old_suid, int flags)
+{
+	return security_ops->task_post_setuid (old_ruid, old_euid, old_suid, flags);
+}
+
+static inline int security_task_setgid (gid_t id0, gid_t id1, gid_t id2,
+					int flags)
+{
+	return security_ops->task_setgid (id0, id1, id2, flags);
+}
+
+static inline int security_task_setpgid (struct task_struct *p, pid_t pgid)
+{
+	return security_ops->task_setpgid (p, pgid);
+}
+
+static inline int security_task_getpgid (struct task_struct *p)
+{
+	return security_ops->task_getpgid (p);
+}
+
+static inline int security_task_getsid (struct task_struct *p)
+{
+	return security_ops->task_getsid (p);
+}
+
+static inline int security_task_setgroups (struct group_info *group_info)
+{
+	return security_ops->task_setgroups (group_info);
+}
+
+static inline int security_task_setnice (struct task_struct *p, int nice)
+{
+	return security_ops->task_setnice (p, nice);
+}
+
+static inline int security_task_setrlimit (unsigned int resource,
+					   struct rlimit *new_rlim)
+{
+	return security_ops->task_setrlimit (resource, new_rlim);
+}
+
+static inline int security_task_setscheduler (struct task_struct *p,
+					      int policy,
+					      struct sched_param *lp)
+{
+	return security_ops->task_setscheduler (p, policy, lp);
+}
+
+static inline int security_task_getscheduler (struct task_struct *p)
+{
+	return security_ops->task_getscheduler (p);
+}
+
+static inline int security_task_kill (struct task_struct *p,
+				      struct siginfo *info, int sig)
+{
+	return security_ops->task_kill (p, info, sig);
+}
+
+static inline int security_task_wait (struct task_struct *p)
+{
+	return security_ops->task_wait (p);
+}
+
+static inline int security_task_prctl (int option, unsigned long arg2,
+				       unsigned long arg3,
+				       unsigned long arg4,
+				       unsigned long arg5)
+{
+	return security_ops->task_prctl (option, arg2, arg3, arg4, arg5);
+}
+
+static inline void security_task_reparent_to_init (struct task_struct *p)
+{
+	security_ops->task_reparent_to_init (p);
+}
+
+static inline void security_task_to_inode(struct task_struct *p, struct inode *inode)
+{
+	security_ops->task_to_inode(p, inode);
+}
+
+static inline int security_ipc_permission (struct kern_ipc_perm *ipcp,
+					   short flag)
+{
+	return security_ops->ipc_permission (ipcp, flag);
+}
+
+static inline int security_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size)
+{
+	return security_ops->ipc_getsecurity(ipcp, buffer, size);
+}
+
+static inline int security_msg_msg_alloc (struct msg_msg * msg)
+{
+	return security_ops->msg_msg_alloc_security (msg);
+}
+
+static inline void security_msg_msg_free (struct msg_msg * msg)
+{
+	security_ops->msg_msg_free_security(msg);
+}
+
+static inline int security_msg_queue_alloc (struct msg_queue *msq)
+{
+	return security_ops->msg_queue_alloc_security (msq);
+}
+
+static inline void security_msg_queue_free (struct msg_queue *msq)
+{
+	security_ops->msg_queue_free_security (msq);
+}
+
+static inline int security_msg_queue_associate (struct msg_queue * msq, 
+						int msqflg)
+{
+	return security_ops->msg_queue_associate (msq, msqflg);
+}
+
+static inline int security_msg_queue_msgctl (struct msg_queue * msq, int cmd)
+{
+	return security_ops->msg_queue_msgctl (msq, cmd);
+}
+
+static inline int security_msg_queue_msgsnd (struct msg_queue * msq,
+					     struct msg_msg * msg, int msqflg)
+{
+	return security_ops->msg_queue_msgsnd (msq, msg, msqflg);
+}
+
+static inline int security_msg_queue_msgrcv (struct msg_queue * msq,
+					     struct msg_msg * msg,
+					     struct task_struct * target,
+					     long type, int mode)
+{
+	return security_ops->msg_queue_msgrcv (msq, msg, target, type, mode);
+}
+
+static inline int security_shm_alloc (struct shmid_kernel *shp)
+{
+	return security_ops->shm_alloc_security (shp);
+}
+
+static inline void security_shm_free (struct shmid_kernel *shp)
+{
+	security_ops->shm_free_security (shp);
+}
+
+static inline int security_shm_associate (struct shmid_kernel * shp, 
+					  int shmflg)
+{
+	return security_ops->shm_associate(shp, shmflg);
+}
+
+static inline int security_shm_shmctl (struct shmid_kernel * shp, int cmd)
+{
+	return security_ops->shm_shmctl (shp, cmd);
+}
+
+static inline int security_shm_shmat (struct shmid_kernel * shp, 
+				      char __user *shmaddr, int shmflg)
+{
+	return security_ops->shm_shmat(shp, shmaddr, shmflg);
+}
+
+static inline int security_sem_alloc (struct sem_array *sma)
+{
+	return security_ops->sem_alloc_security (sma);
+}
+
+static inline void security_sem_free (struct sem_array *sma)
+{
+	security_ops->sem_free_security (sma);
+}
+
+static inline int security_sem_associate (struct sem_array * sma, int semflg)
+{
+	return security_ops->sem_associate (sma, semflg);
+}
+
+static inline int security_sem_semctl (struct sem_array * sma, int cmd)
+{
+	return security_ops->sem_semctl(sma, cmd);
+}
+
+static inline int security_sem_semop (struct sem_array * sma, 
+				      struct sembuf * sops, unsigned nsops, 
+				      int alter)
+{
+	return security_ops->sem_semop(sma, sops, nsops, alter);
+}
+
+static inline void security_d_instantiate (struct dentry *dentry, struct inode *inode)
+{
+	if (unlikely (inode && IS_PRIVATE (inode)))
+		return;
+	security_ops->d_instantiate (dentry, inode);
+}
+
+static inline int security_getprocattr(struct task_struct *p, char *name, void *value, size_t size)
+{
+	return security_ops->getprocattr(p, name, value, size);
+}
+
+static inline int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
+{
+	return security_ops->setprocattr(p, name, value, size);
+}
+
+static inline int security_netlink_send(struct sock *sk, struct sk_buff * skb)
+{
+	return security_ops->netlink_send(sk, skb);
+}
+
+static inline int security_netlink_recv(struct sk_buff * skb)
+{
+	return security_ops->netlink_recv(skb);
+}
+
+/* prototypes */
+extern int security_init	(void);
+extern int register_security	(struct security_operations *ops);
+extern int unregister_security	(struct security_operations *ops);
+extern int mod_reg_security	(const char *name, struct security_operations *ops);
+extern int mod_unreg_security	(const char *name, struct security_operations *ops);
+extern struct dentry *securityfs_create_file(const char *name, mode_t mode,
+					     struct dentry *parent, void *data,
+					     struct file_operations *fops);
+extern struct dentry *securityfs_create_dir(const char *name, struct dentry *parent);
+extern void securityfs_remove(struct dentry *dentry);
+
+
+#else /* CONFIG_SECURITY */
+
+/*
+ * This is the default capabilities functionality.  Most of these functions
+ * are just stubbed out, but a few must call the proper capable code.
+ */
+
+static inline int security_init(void)
+{
+	return 0;
+}
+
+static inline int security_ptrace (struct task_struct *parent, struct task_struct * child)
+{
+	return cap_ptrace (parent, child);
+}
+
+static inline int security_capget (struct task_struct *target,
+				   kernel_cap_t *effective,
+				   kernel_cap_t *inheritable,
+				   kernel_cap_t *permitted)
+{
+	return cap_capget (target, effective, inheritable, permitted);
+}
+
+static inline int security_capset_check (struct task_struct *target,
+					 kernel_cap_t *effective,
+					 kernel_cap_t *inheritable,
+					 kernel_cap_t *permitted)
+{
+	return cap_capset_check (target, effective, inheritable, permitted);
+}
+
+static inline void security_capset_set (struct task_struct *target,
+					kernel_cap_t *effective,
+					kernel_cap_t *inheritable,
+					kernel_cap_t *permitted)
+{
+	cap_capset_set (target, effective, inheritable, permitted);
+}
+
+static inline int security_acct (struct file *file)
+{
+	return 0;
+}
+
+static inline int security_sysctl(struct ctl_table *table, int op)
+{
+	return 0;
+}
+
+static inline int security_quotactl (int cmds, int type, int id,
+				     struct super_block * sb)
+{
+	return 0;
+}
+
+static inline int security_quota_on (struct dentry * dentry)
+{
+	return 0;
+}
+
+static inline int security_syslog(int type)
+{
+	return cap_syslog(type);
+}
+
+static inline int security_settime(struct timespec *ts, struct timezone *tz)
+{
+	return cap_settime(ts, tz);
+}
+
+static inline int security_vm_enough_memory(long pages)
+{
+	return cap_vm_enough_memory(pages);
+}
+
+static inline int security_bprm_alloc (struct linux_binprm *bprm)
+{
+	return 0;
+}
+
+static inline void security_bprm_free (struct linux_binprm *bprm)
+{ }
+
+static inline void security_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
+{ 
+	cap_bprm_apply_creds (bprm, unsafe);
+}
+
+static inline void security_bprm_post_apply_creds (struct linux_binprm *bprm)
+{
+	return;
+}
+
+static inline int security_bprm_set (struct linux_binprm *bprm)
+{
+	return cap_bprm_set_security (bprm);
+}
+
+static inline int security_bprm_check (struct linux_binprm *bprm)
+{
+	return 0;
+}
+
+static inline int security_bprm_secureexec (struct linux_binprm *bprm)
+{
+	return cap_bprm_secureexec(bprm);
+}
+
+static inline int security_sb_alloc (struct super_block *sb)
+{
+	return 0;
+}
+
+static inline void security_sb_free (struct super_block *sb)
+{ }
+
+static inline int security_sb_copy_data (struct file_system_type *type,
+					 void *orig, void *copy)
+{
+	return 0;
+}
+
+static inline int security_sb_kern_mount (struct super_block *sb, void *data)
+{
+	return 0;
+}
+
+static inline int security_sb_statfs (struct super_block *sb)
+{
+	return 0;
+}
+
+static inline int security_sb_mount (char *dev_name, struct nameidata *nd,
+				    char *type, unsigned long flags,
+				    void *data)
+{
+	return 0;
+}
+
+static inline int security_sb_check_sb (struct vfsmount *mnt,
+					struct nameidata *nd)
+{
+	return 0;
+}
+
+static inline int security_sb_umount (struct vfsmount *mnt, int flags)
+{
+	return 0;
+}
+
+static inline void security_sb_umount_close (struct vfsmount *mnt)
+{ }
+
+static inline void security_sb_umount_busy (struct vfsmount *mnt)
+{ }
+
+static inline void security_sb_post_remount (struct vfsmount *mnt,
+					     unsigned long flags, void *data)
+{ }
+
+static inline void security_sb_post_mountroot (void)
+{ }
+
+static inline void security_sb_post_addmount (struct vfsmount *mnt,
+					      struct nameidata *mountpoint_nd)
+{ }
+
+static inline int security_sb_pivotroot (struct nameidata *old_nd,
+					 struct nameidata *new_nd)
+{
+	return 0;
+}
+
+static inline void security_sb_post_pivotroot (struct nameidata *old_nd,
+					       struct nameidata *new_nd)
+{ }
+
+static inline int security_inode_alloc (struct inode *inode)
+{
+	return 0;
+}
+
+static inline void security_inode_free (struct inode *inode)
+{ }
+
+static inline int security_inode_init_security (struct inode *inode,
+						struct inode *dir,
+						char **name,
+						void **value,
+						size_t *len)
+{
+	return -EOPNOTSUPP;
+}
+	
+static inline int security_inode_create (struct inode *dir,
+					 struct dentry *dentry,
+					 int mode)
+{
+	return 0;
+}
+
+static inline int security_inode_link (struct dentry *old_dentry,
+				       struct inode *dir,
+				       struct dentry *new_dentry)
+{
+	return 0;
+}
+
+static inline int security_inode_unlink (struct inode *dir,
+					 struct dentry *dentry)
+{
+	return 0;
+}
+
+static inline int security_inode_symlink (struct inode *dir,
+					  struct dentry *dentry,
+					  const char *old_name)
+{
+	return 0;
+}
+
+static inline int security_inode_mkdir (struct inode *dir,
+					struct dentry *dentry,
+					int mode)
+{
+	return 0;
+}
+
+static inline int security_inode_rmdir (struct inode *dir,
+					struct dentry *dentry)
+{
+	return 0;
+}
+
+static inline int security_inode_mknod (struct inode *dir,
+					struct dentry *dentry,
+					int mode, dev_t dev)
+{
+	return 0;
+}
+
+static inline int security_inode_rename (struct inode *old_dir,
+					 struct dentry *old_dentry,
+					 struct inode *new_dir,
+					 struct dentry *new_dentry)
+{
+	return 0;
+}
+
+static inline int security_inode_readlink (struct dentry *dentry)
+{
+	return 0;
+}
+
+static inline int security_inode_follow_link (struct dentry *dentry,
+					      struct nameidata *nd)
+{
+	return 0;
+}
+
+static inline int security_inode_permission (struct inode *inode, int mask,
+					     struct nameidata *nd)
+{
+	return 0;
+}
+
+static inline int security_inode_setattr (struct dentry *dentry,
+					  struct iattr *attr)
+{
+	return 0;
+}
+
+static inline int security_inode_getattr (struct vfsmount *mnt,
+					  struct dentry *dentry)
+{
+	return 0;
+}
+
+static inline void security_inode_delete (struct inode *inode)
+{ }
+
+static inline int security_inode_setxattr (struct dentry *dentry, char *name,
+					   void *value, size_t size, int flags)
+{
+	return cap_inode_setxattr(dentry, name, value, size, flags);
+}
+
+static inline void security_inode_post_setxattr (struct dentry *dentry, char *name,
+						 void *value, size_t size, int flags)
+{ }
+
+static inline int security_inode_getxattr (struct dentry *dentry, char *name)
+{
+	return 0;
+}
+
+static inline int security_inode_listxattr (struct dentry *dentry)
+{
+	return 0;
+}
+
+static inline int security_inode_removexattr (struct dentry *dentry, char *name)
+{
+	return cap_inode_removexattr(dentry, name);
+}
+
+static inline const char *security_inode_xattr_getsuffix (void)
+{
+	return NULL ;
+}
+
+static inline int security_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size)
+{
+	return 0;
+}
+
+static inline int security_file_permission (struct file *file, int mask)
+{
+	return 0;
+}
+
+static inline int security_file_alloc (struct file *file)
+{
+	return 0;
+}
+
+static inline void security_file_free (struct file *file)
+{ }
+
+static inline int security_file_ioctl (struct file *file, unsigned int cmd,
+				       unsigned long arg)
+{
+	return 0;
+}
+
+static inline int security_file_mmap (struct file *file, unsigned long reqprot,
+				      unsigned long prot,
+				      unsigned long flags)
+{
+	return 0;
+}
+
+static inline int security_file_mprotect (struct vm_area_struct *vma,
+					  unsigned long reqprot,
+					  unsigned long prot)
+{
+	return 0;
+}
+
+static inline int security_file_lock (struct file *file, unsigned int cmd)
+{
+	return 0;
+}
+
+static inline int security_file_fcntl (struct file *file, unsigned int cmd,
+				       unsigned long arg)
+{
+	return 0;
+}
+
+static inline int security_file_set_fowner (struct file *file)
+{
+	return 0;
+}
+
+static inline int security_file_send_sigiotask (struct task_struct *tsk,
+						struct fown_struct *fown,
+						int sig)
+{
+	return 0;
+}
+
+static inline int security_file_receive (struct file *file)
+{
+	return 0;
+}
+
+static inline int security_task_create (unsigned long clone_flags)
+{
+	return 0;
+}
+
+static inline int security_task_alloc (struct task_struct *p)
+{
+	return 0;
+}
+
+static inline void security_task_free (struct task_struct *p)
+{ }
+
+static inline int security_task_setuid (uid_t id0, uid_t id1, uid_t id2,
+					int flags)
+{
+	return 0;
+}
+
+static inline int security_task_post_setuid (uid_t old_ruid, uid_t old_euid,
+					     uid_t old_suid, int flags)
+{
+	return cap_task_post_setuid (old_ruid, old_euid, old_suid, flags);
+}
+
+static inline int security_task_setgid (gid_t id0, gid_t id1, gid_t id2,
+					int flags)
+{
+	return 0;
+}
+
+static inline int security_task_setpgid (struct task_struct *p, pid_t pgid)
+{
+	return 0;
+}
+
+static inline int security_task_getpgid (struct task_struct *p)
+{
+	return 0;
+}
+
+static inline int security_task_getsid (struct task_struct *p)
+{
+	return 0;
+}
+
+static inline int security_task_setgroups (struct group_info *group_info)
+{
+	return 0;
+}
+
+static inline int security_task_setnice (struct task_struct *p, int nice)
+{
+	return 0;
+}
+
+static inline int security_task_setrlimit (unsigned int resource,
+					   struct rlimit *new_rlim)
+{
+	return 0;
+}
+
+static inline int security_task_setscheduler (struct task_struct *p,
+					      int policy,
+					      struct sched_param *lp)
+{
+	return 0;
+}
+
+static inline int security_task_getscheduler (struct task_struct *p)
+{
+	return 0;
+}
+
+static inline int security_task_kill (struct task_struct *p,
+				      struct siginfo *info, int sig)
+{
+	return 0;
+}
+
+static inline int security_task_wait (struct task_struct *p)
+{
+	return 0;
+}
+
+static inline int security_task_prctl (int option, unsigned long arg2,
+				       unsigned long arg3,
+				       unsigned long arg4,
+				       unsigned long arg5)
+{
+	return 0;
+}
+
+static inline void security_task_reparent_to_init (struct task_struct *p)
+{
+	cap_task_reparent_to_init (p);
+}
+
+static inline void security_task_to_inode(struct task_struct *p, struct inode *inode)
+{ }
+
+static inline int security_ipc_permission (struct kern_ipc_perm *ipcp,
+					   short flag)
+{
+	return 0;
+}
+
+static inline int security_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int security_msg_msg_alloc (struct msg_msg * msg)
+{
+	return 0;
+}
+
+static inline void security_msg_msg_free (struct msg_msg * msg)
+{ }
+
+static inline int security_msg_queue_alloc (struct msg_queue *msq)
+{
+	return 0;
+}
+
+static inline void security_msg_queue_free (struct msg_queue *msq)
+{ }
+
+static inline int security_msg_queue_associate (struct msg_queue * msq, 
+						int msqflg)
+{
+	return 0;
+}
+
+static inline int security_msg_queue_msgctl (struct msg_queue * msq, int cmd)
+{
+	return 0;
+}
+
+static inline int security_msg_queue_msgsnd (struct msg_queue * msq,
+					     struct msg_msg * msg, int msqflg)
+{
+	return 0;
+}
+
+static inline int security_msg_queue_msgrcv (struct msg_queue * msq,
+					     struct msg_msg * msg,
+					     struct task_struct * target,
+					     long type, int mode)
+{
+	return 0;
+}
+
+static inline int security_shm_alloc (struct shmid_kernel *shp)
+{
+	return 0;
+}
+
+static inline void security_shm_free (struct shmid_kernel *shp)
+{ }
+
+static inline int security_shm_associate (struct shmid_kernel * shp, 
+					  int shmflg)
+{
+	return 0;
+}
+
+static inline int security_shm_shmctl (struct shmid_kernel * shp, int cmd)
+{
+	return 0;
+}
+
+static inline int security_shm_shmat (struct shmid_kernel * shp, 
+				      char __user *shmaddr, int shmflg)
+{
+	return 0;
+}
+
+static inline int security_sem_alloc (struct sem_array *sma)
+{
+	return 0;
+}
+
+static inline void security_sem_free (struct sem_array *sma)
+{ }
+
+static inline int security_sem_associate (struct sem_array * sma, int semflg)
+{
+	return 0;
+}
+
+static inline int security_sem_semctl (struct sem_array * sma, int cmd)
+{
+	return 0;
+}
+
+static inline int security_sem_semop (struct sem_array * sma, 
+				      struct sembuf * sops, unsigned nsops, 
+				      int alter)
+{
+	return 0;
+}
+
+static inline void security_d_instantiate (struct dentry *dentry, struct inode *inode)
+{ }
+
+static inline int security_getprocattr(struct task_struct *p, char *name, void *value, size_t size)
+{
+	return -EINVAL;
+}
+
+static inline int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
+{
+	return -EINVAL;
+}
+
+static inline int security_netlink_send (struct sock *sk, struct sk_buff *skb)
+{
+	return cap_netlink_send (sk, skb);
+}
+
+static inline int security_netlink_recv (struct sk_buff *skb)
+{
+	return cap_netlink_recv (skb);
+}
+
+#endif	/* CONFIG_SECURITY */
+
+#ifdef CONFIG_SECURITY_NETWORK
+static inline int security_unix_stream_connect(struct socket * sock,
+					       struct socket * other, 
+					       struct sock * newsk)
+{
+	return security_ops->unix_stream_connect(sock, other, newsk);
+}
+
+
+static inline int security_unix_may_send(struct socket * sock, 
+					 struct socket * other)
+{
+	return security_ops->unix_may_send(sock, other);
+}
+
+static inline int security_socket_create (int family, int type,
+					  int protocol, int kern)
+{
+	return security_ops->socket_create(family, type, protocol, kern);
+}
+
+static inline void security_socket_post_create(struct socket * sock, 
+					       int family,
+					       int type, 
+					       int protocol, int kern)
+{
+	security_ops->socket_post_create(sock, family, type,
+					 protocol, kern);
+}
+
+static inline int security_socket_bind(struct socket * sock, 
+				       struct sockaddr * address, 
+				       int addrlen)
+{
+	return security_ops->socket_bind(sock, address, addrlen);
+}
+
+static inline int security_socket_connect(struct socket * sock, 
+					  struct sockaddr * address, 
+					  int addrlen)
+{
+	return security_ops->socket_connect(sock, address, addrlen);
+}
+
+static inline int security_socket_listen(struct socket * sock, int backlog)
+{
+	return security_ops->socket_listen(sock, backlog);
+}
+
+static inline int security_socket_accept(struct socket * sock, 
+					 struct socket * newsock)
+{
+	return security_ops->socket_accept(sock, newsock);
+}
+
+static inline void security_socket_post_accept(struct socket * sock, 
+					       struct socket * newsock)
+{
+	security_ops->socket_post_accept(sock, newsock);
+}
+
+static inline int security_socket_sendmsg(struct socket * sock, 
+					  struct msghdr * msg, int size)
+{
+	return security_ops->socket_sendmsg(sock, msg, size);
+}
+
+static inline int security_socket_recvmsg(struct socket * sock, 
+					  struct msghdr * msg, int size, 
+					  int flags)
+{
+	return security_ops->socket_recvmsg(sock, msg, size, flags);
+}
+
+static inline int security_socket_getsockname(struct socket * sock)
+{
+	return security_ops->socket_getsockname(sock);
+}
+
+static inline int security_socket_getpeername(struct socket * sock)
+{
+	return security_ops->socket_getpeername(sock);
+}
+
+static inline int security_socket_getsockopt(struct socket * sock, 
+					     int level, int optname)
+{
+	return security_ops->socket_getsockopt(sock, level, optname);
+}
+
+static inline int security_socket_setsockopt(struct socket * sock, 
+					     int level, int optname)
+{
+	return security_ops->socket_setsockopt(sock, level, optname);
+}
+
+static inline int security_socket_shutdown(struct socket * sock, int how)
+{
+	return security_ops->socket_shutdown(sock, how);
+}
+
+static inline int security_sock_rcv_skb (struct sock * sk, 
+					 struct sk_buff * skb)
+{
+	return security_ops->socket_sock_rcv_skb (sk, skb);
+}
+
+static inline int security_socket_getpeersec(struct socket *sock, char __user *optval,
+					     int __user *optlen, unsigned len)
+{
+	return security_ops->socket_getpeersec(sock, optval, optlen, len);
+}
+
+static inline int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
+{
+	return security_ops->sk_alloc_security(sk, family, priority);
+}
+
+static inline void security_sk_free(struct sock *sk)
+{
+	return security_ops->sk_free_security(sk);
+}
+#else	/* CONFIG_SECURITY_NETWORK */
+static inline int security_unix_stream_connect(struct socket * sock,
+					       struct socket * other, 
+					       struct sock * newsk)
+{
+	return 0;
+}
+
+static inline int security_unix_may_send(struct socket * sock, 
+					 struct socket * other)
+{
+	return 0;
+}
+
+static inline int security_socket_create (int family, int type,
+					  int protocol, int kern)
+{
+	return 0;
+}
+
+static inline void security_socket_post_create(struct socket * sock, 
+					       int family,
+					       int type, 
+					       int protocol, int kern)
+{
+}
+
+static inline int security_socket_bind(struct socket * sock, 
+				       struct sockaddr * address, 
+				       int addrlen)
+{
+	return 0;
+}
+
+static inline int security_socket_connect(struct socket * sock, 
+					  struct sockaddr * address, 
+					  int addrlen)
+{
+	return 0;
+}
+
+static inline int security_socket_listen(struct socket * sock, int backlog)
+{
+	return 0;
+}
+
+static inline int security_socket_accept(struct socket * sock, 
+					 struct socket * newsock)
+{
+	return 0;
+}
+
+static inline void security_socket_post_accept(struct socket * sock, 
+					       struct socket * newsock)
+{
+}
+
+static inline int security_socket_sendmsg(struct socket * sock, 
+					  struct msghdr * msg, int size)
+{
+	return 0;
+}
+
+static inline int security_socket_recvmsg(struct socket * sock, 
+					  struct msghdr * msg, int size, 
+					  int flags)
+{
+	return 0;
+}
+
+static inline int security_socket_getsockname(struct socket * sock)
+{
+	return 0;
+}
+
+static inline int security_socket_getpeername(struct socket * sock)
+{
+	return 0;
+}
+
+static inline int security_socket_getsockopt(struct socket * sock, 
+					     int level, int optname)
+{
+	return 0;
+}
+
+static inline int security_socket_setsockopt(struct socket * sock, 
+					     int level, int optname)
+{
+	return 0;
+}
+
+static inline int security_socket_shutdown(struct socket * sock, int how)
+{
+	return 0;
+}
+static inline int security_sock_rcv_skb (struct sock * sk, 
+					 struct sk_buff * skb)
+{
+	return 0;
+}
+
+static inline int security_socket_getpeersec(struct socket *sock, char __user *optval,
+					     int __user *optlen, unsigned len)
+{
+	return -ENOPROTOOPT;
+}
+
+static inline int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
+{
+	return 0;
+}
+
+static inline void security_sk_free(struct sock *sk)
+{
+}
+#endif	/* CONFIG_SECURITY_NETWORK */
+
+#ifdef CONFIG_KEYS
+#ifdef CONFIG_SECURITY
+static inline int security_key_alloc(struct key *key)
+{
+	return security_ops->key_alloc(key);
+}
+
+static inline void security_key_free(struct key *key)
+{
+	security_ops->key_free(key);
+}
+
+static inline int security_key_permission(key_ref_t key_ref,
+					  struct task_struct *context,
+					  key_perm_t perm)
+{
+	return security_ops->key_permission(key_ref, context, perm);
+}
+
+#else
+
+static inline int security_key_alloc(struct key *key)
+{
+	return 0;
+}
+
+static inline void security_key_free(struct key *key)
+{
+}
+
+static inline int security_key_permission(key_ref_t key_ref,
+					  struct task_struct *context,
+					  key_perm_t perm)
+{
+	return 0;
+}
+
+#endif
+#endif /* CONFIG_KEYS */
+
+#endif /* ! __LINUX_SECURITY_H */
+
diff -urN oldtree/include/linux/shmem_fs.h newtree/include/linux/shmem_fs.h
--- oldtree/include/linux/shmem_fs.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/shmem_fs.h	2006-02-13 19:20:01.253321768 -0500
@@ -26,6 +26,8 @@
 	unsigned long free_blocks;  /* How many are left for allocation */
 	unsigned long max_inodes;   /* How many inodes are allowed */
 	unsigned long free_inodes;  /* How many are left for allocation */
+	int policy;		    /* Default NUMA memory alloc policy */
+	nodemask_t policy_nodes;    /* nodemask for preferred and bind */
 	spinlock_t    stat_lock;
 };
 
diff -urN oldtree/include/linux/skbuff.h newtree/include/linux/skbuff.h
--- oldtree/include/linux/skbuff.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/skbuff.h	2006-02-13 19:20:29.745990224 -0500
@@ -927,7 +927,7 @@
  *	Increase the headroom of an empty &sk_buff by reducing the tail
  *	room. This is only allowed for an empty buffer.
  */
-static inline void skb_reserve(struct sk_buff *skb, unsigned int len)
+static inline void skb_reserve(struct sk_buff *skb, int len)
 {
 	skb->data += len;
 	skb->tail += len;
diff -urN oldtree/include/linux/smb_fs.h newtree/include/linux/smb_fs.h
--- oldtree/include/linux/smb_fs.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/smb_fs.h	2006-02-13 19:20:01.253321768 -0500
@@ -58,53 +58,6 @@
 /* where to find the base of the SMB packet proper */
 #define smb_base(buf) ((u8 *)(((u8 *)(buf))+4))
 
-#ifdef DEBUG_SMB_MALLOC
-
-#include <linux/slab.h>
-
-extern int smb_malloced;
-extern int smb_current_vmalloced;
-extern int smb_current_kmalloced;
-
-static inline void *
-smb_vmalloc(unsigned int size)
-{
-        smb_malloced += 1;
-        smb_current_vmalloced += 1;
-        return vmalloc(size);
-}
-
-static inline void
-smb_vfree(void *obj)
-{
-        smb_current_vmalloced -= 1;
-        vfree(obj);
-}
-
-static inline void *
-smb_kmalloc(size_t size, int flags)
-{
-	smb_malloced += 1;
-	smb_current_kmalloced += 1;
-	return kmalloc(size, flags);
-}
-
-static inline void
-smb_kfree(void *obj)
-{
-	smb_current_kmalloced -= 1;
-	kfree(obj);
-}
-
-#else /* DEBUG_SMB_MALLOC */
-
-#define smb_kmalloc(s,p)	kmalloc(s,p)
-#define smb_kfree(o)		kfree(o)
-#define smb_vmalloc(s)		vmalloc(s)
-#define smb_vfree(o)		vfree(o)
-
-#endif /* DEBUG_SMB_MALLOC */
-
 /*
  * Flags for the in-memory inode
  */
diff -urN oldtree/include/linux/types.h newtree/include/linux/types.h
--- oldtree/include/linux/types.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/include/linux/types.h	2006-02-13 19:20:01.253321768 -0500
@@ -8,6 +8,8 @@
 	(((bits)+BITS_PER_LONG-1)/BITS_PER_LONG)
 #define DECLARE_BITMAP(name,bits) \
 	unsigned long name[BITS_TO_LONGS(bits)]
+
+#define BITS_PER_BYTE 8
 #endif
 
 #include <linux/posix_types.h>
diff -urN oldtree/include/scsi/sas/sas.h newtree/include/scsi/sas/sas.h
--- oldtree/include/scsi/sas/sas.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/scsi/sas/sas.h	2006-02-13 19:20:01.254321616 -0500
@@ -0,0 +1,164 @@
+/*
+ * SAS structures and definitions header file
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * $Id: //depot/sas-class/sas.h#25 $
+ */
+
+#ifndef _SAS_H_
+#define _SAS_H_
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+#define SAS_ADDR_SIZE        8
+#define HASHED_SAS_ADDR_SIZE 3
+#define SAS_ADDR(_sa)   (be64_to_cpu(*(__be64 *)(_sa)))
+
+enum sas_oob_mode {
+	OOB_NOT_CONNECTED,
+	SATA_OOB_MODE,
+	SAS_OOB_MODE
+};
+
+/* See sas_discover.c if you plan on changing these.
+ */
+enum sas_dev_type {
+	NO_DEVICE   = 0,	  /* protocol */
+	SAS_END_DEV = 1,	  /* protocol */
+	EDGE_DEV    = 2,	  /* protocol */
+	FANOUT_DEV  = 3,	  /* protocol */
+	SAS_HA      = 4,
+	SATA_DEV    = 5,
+	SATA_PM     = 7,
+	SATA_PM_PORT= 8,
+};
+
+enum sas_phy_linkrate {
+	PHY_LINKRATE_NONE = 0,
+	PHY_LINKRATE_UNKNOWN = 0,
+	PHY_DISABLED,
+	PHY_RESET_PROBLEM,
+	PHY_SPINUP_HOLD,
+	PHY_PORT_SELECTOR,
+	PHY_LINKRATE_1_5 = 0x08,
+	PHY_LINKRATE_G1  = PHY_LINKRATE_1_5,
+	PHY_LINKRATE_3   = 0x09,
+	PHY_LINKRATE_G2  = PHY_LINKRATE_3,
+	PHY_LINKRATE_6   = 0x0A,
+};
+
+/* Partly from IDENTIFY address frame. */
+enum sas_proto {
+	SATA_PROTO    = 1,
+	SAS_PROTO_SMP = 2,	  /* protocol */
+	SAS_PROTO_STP = 4,	  /* protocol */
+	SAS_PROTO_SSP = 8,	  /* protocol */
+	SAS_PROTO_ALL = 0xE,
+};
+
+/* From the spec; local phys only */
+enum phy_func {
+	PHY_FUNC_NOP,
+	PHY_FUNC_LINK_RESET,		  /* Enables the phy */
+	PHY_FUNC_HARD_RESET,
+	PHY_FUNC_DISABLE,
+	PHY_FUNC_CLEAR_ERROR_LOG = 5,
+	PHY_FUNC_CLEAR_AFFIL,
+	PHY_FUNC_TX_SATA_PS_SIGNAL,
+	PHY_FUNC_RELEASE_SPINUP_HOLD = 0x10, /* LOCAL PORT ONLY! */
+};
+
+#include <scsi/sas/sas_frames.h>
+
+/* SAS LLDD would need to report only _very_few_ of those, like BROADCAST.
+ * Most of those are here for completeness.
+ */
+enum sas_prim {
+	SAS_PRIM_AIP_NORMAL = 1,
+	SAS_PRIM_AIP_R0     = 2,
+	SAS_PRIM_AIP_R1     = 3,
+	SAS_PRIM_AIP_R2     = 4,
+	SAS_PRIM_AIP_WC     = 5,
+	SAS_PRIM_AIP_WD     = 6,
+	SAS_PRIM_AIP_WP     = 7,
+	SAS_PRIM_AIP_RWP    = 8,
+
+	SAS_PRIM_BC_CH      = 9,
+	SAS_PRIM_BC_RCH0    = 10,
+	SAS_PRIM_BC_RCH1    = 11,
+	SAS_PRIM_BC_R0      = 12,
+	SAS_PRIM_BC_R1      = 13,
+	SAS_PRIM_BC_R2      = 14,
+	SAS_PRIM_BC_R3      = 15,
+	SAS_PRIM_BC_R4      = 16,
+
+	SAS_PRIM_NOTIFY_ENSP= 17,
+	SAS_PRIM_NOTIFY_R0  = 18,
+	SAS_PRIM_NOTIFY_R1  = 19,
+	SAS_PRIM_NOTIFY_R2  = 20,
+
+	SAS_PRIM_CLOSE_CLAF = 21,
+	SAS_PRIM_CLOSE_NORM = 22,
+	SAS_PRIM_CLOSE_R0   = 23,
+	SAS_PRIM_CLOSE_R1   = 24,
+
+	SAS_PRIM_OPEN_RTRY  = 25,
+	SAS_PRIM_OPEN_RJCT  = 26,
+	SAS_PRIM_OPEN_ACPT  = 27,
+
+	SAS_PRIM_DONE       = 28,
+	SAS_PRIM_BREAK      = 29,
+
+	SATA_PRIM_DMAT      = 33,
+	SATA_PRIM_PMNAK     = 34,
+	SATA_PRIM_PMACK     = 35,
+	SATA_PRIM_PMREQ_S   = 36,
+	SATA_PRIM_PMREQ_P   = 37,
+	SATA_SATA_R_ERR     = 38,
+};
+
+enum sas_open_rej_reason {
+	/* Abandon open */
+	SAS_OREJ_UNKNOWN   = 0,
+	SAS_OREJ_BAD_DEST  = 1,
+	SAS_OREJ_CONN_RATE = 2,
+	SAS_OREJ_EPROTO    = 3,
+	SAS_OREJ_RESV_AB0  = 4,
+	SAS_OREJ_RESV_AB1  = 5,
+	SAS_OREJ_RESV_AB2  = 6,
+	SAS_OREJ_RESV_AB3  = 7,
+	SAS_OREJ_WRONG_DEST= 8,
+	SAS_OREJ_STP_NORES = 9,
+
+	/* Retry open */
+	SAS_OREJ_NO_DEST   = 10,
+	SAS_OREJ_PATH_BLOCKED = 11,
+	SAS_OREJ_RSVD_CONT0 = 12,
+	SAS_OREJ_RSVD_CONT1 = 13,
+	SAS_OREJ_RSVD_INIT0 = 14,
+	SAS_OREJ_RSVD_INIT1 = 15,
+	SAS_OREJ_RSVD_STOP0 = 16,
+	SAS_OREJ_RSVD_STOP1 = 17,
+	SAS_OREJ_RSVD_RETRY = 18,
+};
+#endif /* _SAS_H_ */
diff -urN oldtree/include/scsi/sas/sas_class.h newtree/include/scsi/sas/sas_class.h
--- oldtree/include/scsi/sas/sas_class.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/scsi/sas/sas_class.h	2006-02-13 19:20:01.254321616 -0500
@@ -0,0 +1,314 @@
+/*
+ * Serial Attached SCSI (SAS) class header file
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * $Id: //depot/sas-class/sas_class.h#66 $
+ */
+
+#ifndef _SAS_CLASS_H_
+#define _SAS_CLASS_H_
+
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <asm/semaphore.h>
+#include <scsi/scsi_device.h>
+#include <scsi/sas/sas.h>
+
+enum sas_class {
+	SAS,
+	EXPANDER
+};
+
+enum sas_phy_role {
+	PHY_ROLE_NONE = 0,
+	PHY_ROLE_TARGET = 0x40,
+	PHY_ROLE_INITIATOR = 0x80,
+};
+
+enum sas_phy_type {
+        PHY_TYPE_PHYSICAL,
+        PHY_TYPE_VIRTUAL
+};
+
+/* The events are mnemonically described in sas_dump.c
+ * so when updating/adding events here, please also
+ * update the other file too.
+ */
+enum ha_event {
+	HAE_RESET = 0U,
+};
+#define HA_NUM_EVENTS 1
+
+enum port_event {
+	PORTE_BYTES_DMAED     = 0U,
+	PORTE_BROADCAST_RCVD  = 1,
+	PORTE_LINK_RESET_ERR  = 2,
+	PORTE_TIMER_EVENT     = 3,
+	PORTE_HARD_RESET      = 4,
+};
+#define PORT_NUM_EVENTS 5
+
+enum phy_event {
+	PHYE_LOSS_OF_SIGNAL   = 0U,
+	PHYE_OOB_DONE         = 1,
+	PHYE_OOB_ERROR        = 2,
+	PHYE_SPINUP_HOLD      = 3, /* hot plug SATA, no COMWAKE sent */
+};
+#define PHY_NUM_EVENTS 4
+
+enum discover_event {
+	DISCE_DISCOVER_DOMAIN   = 0U,
+	DISCE_REVALIDATE_DOMAIN = 1,
+	DISCE_PORT_GONE         = 2,
+};
+#define DISC_NUM_EVENTS 3
+
+struct sas_event {
+	int    event;
+	struct list_head el;
+};
+
+/* The phy pretty much is controlled by the LLDD.
+ * The class only reads those fields.
+ */
+struct sas_phy {
+/* private: */
+	struct kobject phy_kobj;
+
+	/* protected by ha->event_lock */
+	struct list_head   port_event_list;
+	struct list_head   phy_event_list;
+	struct sas_event   port_events[PORT_NUM_EVENTS];
+	struct sas_event   phy_events[PHY_NUM_EVENTS];
+
+	int error;
+
+/* public: */
+	/* The following are class:RO, driver:R/W */
+	int            enabled;	  /* must be set */
+
+	int            id;	  /* must be set */
+	enum sas_class class;
+	enum sas_proto iproto;
+	enum sas_proto tproto;
+
+	enum sas_phy_type  type;
+	enum sas_phy_role  role;
+	enum sas_oob_mode  oob_mode;
+	enum sas_phy_linkrate linkrate;
+
+	u8   *sas_addr;		  /* must be set */
+	u8   attached_sas_addr[SAS_ADDR_SIZE]; /* class:RO, driver: R/W */
+
+	spinlock_t     frame_rcvd_lock;
+	u8             *frame_rcvd; /* must be set */
+	int            frame_rcvd_size;
+
+	spinlock_t     sas_prim_lock;
+	u32            sas_prim;
+
+	struct list_head port_phy_el; /* driver:RO */
+	struct sas_port      *port; /* Class:RW, driver: RO */
+
+	struct sas_ha_struct *ha; /* may be set; the class sets it anyway */
+
+	void *lldd_phy;		  /* not touched by the sas_class_code */
+};
+
+struct sas_port;
+
+struct sas_discovery {
+	spinlock_t disc_event_lock;
+	int        disc_thread_quit;
+	struct list_head disc_event_list;
+	struct sas_event disc_events[DISC_NUM_EVENTS];
+	struct task_struct *disc_thread;
+	struct semaphore  disc_sema;
+
+	u8     fanout_sas_addr[8];
+	u8     eeds_a[8];
+	u8     eeds_b[8];
+	int    max_level;
+};
+
+struct scsi_id_map {
+	int         max_ids;
+	spinlock_t  id_bitmap_lock;
+	int         id_bitmap_size;
+	void       *id_bitmap;
+};
+
+struct domain_device;
+
+/* The port struct is Class:RW, driver:RO */
+struct sas_port {
+/* private: */
+	struct kobject port_kobj;
+	struct kset    phy_kset;
+	struct kset    dev_kset;
+
+	struct completion port_gone_completion;
+
+	struct sas_discovery disc;
+	struct domain_device *port_dev;
+	struct list_head dev_list;
+	enum   sas_phy_linkrate linkrate;
+
+	struct scsi_id_map id_map;
+
+/* public: */
+	int id;
+
+	enum sas_class   class;
+	u8               sas_addr[SAS_ADDR_SIZE];
+	u8               attached_sas_addr[SAS_ADDR_SIZE];
+	enum sas_proto   iproto;
+	enum sas_proto   tproto;
+
+	enum sas_oob_mode oob_mode;
+
+	spinlock_t       phy_list_lock;
+	struct list_head phy_list;
+	int              num_phys;
+	u32              phy_mask;
+
+	struct sas_ha_struct *ha;
+
+	void *lldd_port;	  /* not touched by the sas class code */
+};
+
+struct sas_task;
+
+struct scsi_core {
+	struct kobject scsi_core_obj;
+
+	struct scsi_host_template *sht;
+	struct Scsi_Host *shost;
+
+	spinlock_t        task_queue_lock;
+	struct list_head  task_queue;
+	int               task_queue_size;
+
+	struct semaphore  queue_thread_sema;
+	int               queue_thread_kill;
+};
+
+struct sas_ha_struct {
+/* private: */
+	struct kset      ha_kset; /* "this" */
+	struct kset      phy_kset;
+	struct kset      port_kset;
+
+	struct semaphore event_sema;
+	int              event_thread_kill;
+
+	spinlock_t       event_lock;
+	struct list_head ha_event_list;
+	struct sas_event ha_events[HA_NUM_EVENTS];
+	u32              porte_mask; /* mask of phys for port events */
+	u32              phye_mask; /* mask of phys for phy events */
+
+	struct scsi_core core;
+
+/* public: */
+	char *sas_ha_name;
+	struct pci_dev *pcidev;	  /* should be set */
+	struct module *lldd_module; /* should be set */
+
+	u8 *sas_addr;		  /* must be set */
+	u8 hashed_sas_addr[HASHED_SAS_ADDR_SIZE];
+
+	spinlock_t      phy_port_lock;
+	struct sas_phy  **sas_phy; /* array of valid pointers, must be set */
+	struct sas_port **sas_port; /* array of valid pointers, must be set */
+	int             num_phys; /* must be set, gt 0, static */
+
+	/* LLDD calls these to notify the class of an event. */
+	void (*notify_ha_event)(struct sas_ha_struct *, enum ha_event);
+	void (*notify_port_event)(struct sas_phy *, enum port_event);
+	void (*notify_phy_event)(struct sas_phy *, enum phy_event);
+
+	/* The class calls these to notify the LLDD of an event. */
+	void (*lldd_port_formed)(struct sas_phy *);
+	void (*lldd_port_deformed)(struct sas_phy *);
+
+	/* The class calls these when a device is found or gone. */
+	int  (*lldd_dev_found)(struct domain_device *);
+	void (*lldd_dev_gone)(struct domain_device *);
+
+	/* The class calls this to send a task for execution. */
+	int lldd_max_execute_num;
+	int lldd_queue_size;
+	int (*lldd_execute_task)(struct sas_task *, int num,
+				 unsigned long gfp_flags);
+
+	/* Task Management Functions. Must be called from process context. */
+	int (*lldd_abort_task)(struct sas_task *);
+	int (*lldd_abort_task_set)(struct domain_device *, u8 *lun);
+	int (*lldd_clear_aca)(struct domain_device *, u8 *lun);
+	int (*lldd_clear_task_set)(struct domain_device *, u8 *lun);
+	int (*lldd_I_T_nexus_reset)(struct domain_device *);
+	int (*lldd_lu_reset)(struct domain_device *, u8 *lun);
+	int (*lldd_query_task)(struct sas_task *);
+
+	/* Port and Adapter management */
+	int (*lldd_clear_nexus_port)(struct sas_port *);
+	int (*lldd_clear_nexus_ha)(struct sas_ha_struct *);
+
+	/* Phy management */
+	int (*lldd_control_phy)(struct sas_phy *, enum phy_func);
+
+	void *lldd_ha;		  /* not touched by sas class code */
+};
+
+#define SHOST_TO_SAS_HA(_shost) (*(struct sas_ha_struct **)(_shost)->hostdata)
+
+void sas_hash_addr(u8 *hashed, const u8 *sas_addr);
+
+/* Before calling a notify event, LLDD should use this function
+ * when the link is severed (possibly from its tasklet).
+ * The idea is that the Class only reads those, while the LLDD,
+ * can R/W these (thus avoiding a race).
+ */
+static inline void sas_phy_disconnected(struct sas_phy *phy)
+{
+	phy->oob_mode = OOB_NOT_CONNECTED;
+	phy->linkrate = PHY_LINKRATE_NONE;
+}
+
+extern int sas_register_ha(struct sas_ha_struct *, const struct scsi_host_template *);
+extern int sas_unregister_ha(struct sas_ha_struct *);
+
+extern int sas_queuecommand(struct scsi_cmnd *cmd,
+		     void (*scsi_done)(struct scsi_cmnd *));
+extern int sas_scsi_recover_host(struct Scsi_Host *shost);
+extern enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd);
+extern int sas_slave_alloc(struct scsi_device *scsi_dev);
+extern int sas_slave_configure(struct scsi_device *scsi_dev);
+extern void sas_slave_destroy(struct scsi_device *scsi_dev);
+extern int sas_change_queue_depth(struct scsi_device *scsi_dev, int new_depth);
+extern int sas_change_queue_type(struct scsi_device *scsi_dev, int qt);
+extern int sas_bios_param(struct scsi_device *scsi_dev,
+			  struct block_device *bdev,
+			  sector_t capacity, int *hsc);
+
+#endif /* _SAS_CLASS_H_ */
diff -urN oldtree/include/scsi/sas/sas_discover.h newtree/include/scsi/sas/sas_discover.h
--- oldtree/include/scsi/sas/sas_discover.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/scsi/sas/sas_discover.h	2006-02-13 19:20:01.255321464 -0500
@@ -0,0 +1,231 @@
+/*
+ * Serial Attached SCSI (SAS) Discover process header file
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/sas-class/sas_discover.h#41 $
+ */
+
+#ifndef _SAS_DISCOVER_H_
+#define _SAS_DISCOVER_H_
+
+#include <scsi/sas/sas_class.h>
+#include <scsi/sas/sas_frames.h>
+
+/* ---------- SMP ---------- */
+
+#define SMP_REPORT_GENERAL       0x00
+#define SMP_REPORT_MANUF_INFO    0x01
+#define SMP_READ_GPIO_REG        0x02
+#define SMP_DISCOVER             0x10
+#define SMP_REPORT_PHY_ERR_LOG   0x11
+#define SMP_REPORT_PHY_SATA      0x12
+#define SMP_REPORT_ROUTE_INFO    0x13
+#define SMP_WRITE_GPIO_REG       0x82
+#define SMP_CONF_ROUTE_INFO      0x90
+#define SMP_PHY_CONTROL          0x91
+#define SMP_PHY_TEST_FUNCTION    0x92
+
+#define SMP_RESP_FUNC_ACC        0x00
+#define SMP_RESP_FUNC_UNK        0x01
+#define SMP_RESP_FUNC_FAILED     0x02
+#define SMP_RESP_INV_FRM_LEN     0x03
+#define SMP_RESP_NO_PHY          0x10
+#define SMP_RESP_NO_INDEX        0x11
+#define SMP_RESP_PHY_NO_SATA     0x12
+#define SMP_RESP_PHY_UNK_OP      0x13
+#define SMP_RESP_PHY_UNK_TESTF   0x14
+#define SMP_RESP_PHY_TEST_INPROG 0x15
+#define SMP_RESP_PHY_VACANT      0x16
+
+/* ---------- Domain Devices ---------- */
+
+/* See sas_discover.c before changing these.
+ */
+
+/* ---------- SATA device ---------- */
+
+enum ata_command_set {
+	ATA_COMMAND_SET   = 0,
+	ATAPI_COMMAND_SET = 1,
+};
+
+struct domain_device;
+
+struct sata_device {
+	struct kset  pm_port_kset;
+	enum   ata_command_set command_set;
+	struct smp_resp        rps_resp; /* report_phy_sata_resp */
+	__le16 *identify_device;
+	__le16 *identify_packet_device;
+
+	u8     port_no;	       /* port number, if this is a PM (Port) */
+	struct list_head children; /* PM Ports if this is a PM */
+};
+
+/* ---------- SAS end device ---------- */
+
+#define SAS_INQUIRY_DATA_LEN 36
+
+struct scsi_core_mapping {
+	int  channel;
+	int  id;
+};
+
+enum task_management_type {
+	TASK_MANAGEMENT_NONE  = 0,
+	TASK_MANAGEMENT_FULL  = 1,
+	TASK_MANAGEMENT_BASIC = 2,
+};
+
+struct LU {
+	struct kobject   lu_obj;
+	struct list_head list;
+
+	struct domain_device *parent;
+
+	u8     LUN[8];
+	int    inquiry_valid_data_len;
+	u8     inquiry_data[SAS_INQUIRY_DATA_LEN];
+	struct scsi_core_mapping map;
+
+	enum task_management_type tm_type;
+
+	void  *uldd_dev;
+};
+
+struct end_device {
+	u8     ms_10:1;
+	u8     ready_led_meaning:1;
+	u8     rl_wlun:1;
+	u16    itnl_timeout; 	  /* 0 if you do not know it */
+	u16    iresp_timeout;
+
+	struct kset LU_kset;
+	struct list_head LU_list;
+};
+
+#include <scsi/sas/sas_expander.h>
+
+/* ---------- Domain device ---------- */
+
+struct domain_device {
+	struct kobject    dev_obj;
+	enum sas_dev_type dev_type;
+
+	enum sas_phy_linkrate linkrate;
+	enum sas_phy_linkrate min_linkrate;
+	enum sas_phy_linkrate max_linkrate;
+
+	int  pathways;
+
+	struct domain_device *parent;
+	struct list_head siblings; /* devices on the same level */
+	struct sas_port *port;	  /* shortcut to root of the tree */
+
+	struct list_head dev_list_node;
+
+	enum sas_proto    iproto;
+	enum sas_proto    tproto;
+
+	u8  sas_addr[SAS_ADDR_SIZE];
+	u8  hashed_sas_addr[HASHED_SAS_ADDR_SIZE];
+
+	u8  frame_rcvd[32];
+
+	union {
+		struct expander_device ex_dev;
+		struct end_device      end_dev;
+		struct sata_device     sata_dev; /* STP & directly attached */
+	};
+
+	void *lldd_dev;
+};
+
+#define list_for_each_entry_reverse_safe(pos, n, head, member)		\
+	for (pos = list_entry((head)->prev, typeof(*pos), member),	\
+		n = list_entry(pos->member.prev, typeof(*pos), member);	\
+	     &pos->member != (head); 					\
+	     pos = n, n = list_entry(n->member.prev, typeof(*n), member))
+
+
+static inline int sas_notify_lldd_dev_found(struct domain_device *dev)
+{
+	int res = 0;
+	struct sas_ha_struct *sas_ha = dev->port->ha;
+
+	if (!try_module_get(sas_ha->lldd_module))
+		return -ENOMEM;
+	if (sas_ha->lldd_dev_found) {
+		res = sas_ha->lldd_dev_found(dev);
+		if (res) {
+			printk("sas: driver on pcidev %s cannot handle "
+			       "device %llx, error:%d\n",
+			       pci_name(sas_ha->pcidev),
+			       SAS_ADDR(dev->sas_addr), res);
+		}
+	}
+	return res;
+}
+
+static inline void sas_notify_lldd_dev_gone(struct domain_device *dev)
+{
+	if (dev->port->ha->lldd_dev_gone)
+		dev->port->ha->lldd_dev_gone(dev);
+	module_put(dev->port->ha->lldd_module);
+}
+
+static inline void sas_init_dev(struct domain_device *dev)
+{
+	INIT_LIST_HEAD(&dev->siblings);
+	INIT_LIST_HEAD(&dev->dev_list_node);
+	switch (dev->dev_type) {
+	case SAS_END_DEV:
+		INIT_LIST_HEAD(&dev->end_dev.LU_list);
+		break;
+	case EDGE_DEV:
+	case FANOUT_DEV:
+		INIT_LIST_HEAD(&dev->ex_dev.children);
+		break;
+	case SATA_DEV:
+	case SATA_PM:
+	case SATA_PM_PORT:
+		INIT_LIST_HEAD(&dev->sata_dev.children);
+		break;
+	default:
+		break;
+	}
+}
+
+void sas_init_disc(struct sas_discovery *disc, struct sas_port *port);
+void sas_kill_disc_thread(struct sas_port *port);
+int  sas_discover_event(struct sas_port *sas_port, enum discover_event ev);
+
+int  sas_discover_sata(struct domain_device *dev);
+int  sas_discover_end_dev(struct domain_device *dev);
+
+void sas_unregister_dev(struct domain_device *dev);
+
+int  sas_register_with_scsi(struct LU *lu);
+void sas_unregister_with_scsi(struct LU *lu);
+
+void sas_unregister_devices(struct sas_ha_struct *sas_ha);
+
+#endif /* _SAS_DISCOVER_H_ */
diff -urN oldtree/include/scsi/sas/sas_expander.h newtree/include/scsi/sas/sas_expander.h
--- oldtree/include/scsi/sas/sas_expander.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/scsi/sas/sas_expander.h	2006-02-13 19:20:01.255321464 -0500
@@ -0,0 +1,133 @@
+/*
+ * Serial Attached SCSI (SAS) Expander discovery and configuration
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/sas-class/sas_expander.h#19 $
+ */
+
+#ifndef _SAS_EXPANDER_H_
+#define _SAS_EXPANDER_H_
+
+#define ETASK 0xFA
+
+#define to_lu_device(_obj) container_of(_obj, struct LU, lu_obj)
+#define to_lu_attr(_attr) container_of(_attr, struct lu_dev_attribute, attr)
+#define to_dom_device(_obj) container_of(_obj, struct domain_device, dev_obj)
+#define to_dev_attr(_attr)  container_of(_attr, struct domain_dev_attribute,\
+                                         attr)
+
+/* ---------- Expander device ---------- */
+
+enum routing_attribute {
+	DIRECT_ROUTING,
+	SUBTRACTIVE_ROUTING,
+	TABLE_ROUTING,
+};
+
+enum ex_phy_state {
+	PHY_EMPTY,
+	PHY_VACANT,
+	PHY_NOT_PRESENT,
+	PHY_DEVICE_DISCOVERED
+};
+
+struct ex_phy {
+	int    phy_id;
+
+	enum ex_phy_state phy_state;
+
+	enum sas_dev_type attached_dev_type;
+	enum sas_phy_linkrate linkrate;
+
+	u8   attached_sata_host:1;
+	u8   attached_sata_dev:1;
+	u8   attached_sata_ps:1;
+
+	enum sas_proto attached_tproto;
+	enum sas_proto attached_iproto;
+
+	u8   attached_sas_addr[SAS_ADDR_SIZE];
+	u8   attached_phy_id;
+
+	u8   phy_change_count;
+	enum routing_attribute routing_attr;
+	u8   virtual:1;
+
+	int  last_da_index;
+};
+
+struct expander_device {
+	struct list_head children;
+
+	int    level;
+
+	u16    ex_change_count;
+	u16    max_route_indexes;
+	u8     num_phys;
+	u8     configuring:1;
+	u8     conf_route_table:1;
+	u8     enclosure_logical_id[8];
+
+	char   vendor_id[8+1];
+	char   product_id[16+1];
+	char   product_rev[4+1];
+	char   component_vendor_id[8+1];
+	u16    component_id;
+	u8     component_revision_id;
+
+	struct ex_phy *ex_phy;
+
+	struct bin_attribute smp_bin_attr;
+	void *smp_req;
+	int   smp_req_size;
+	int   smp_portal_pid;
+	struct semaphore smp_sema;
+};
+
+/* ---------- Attributes and inlined ---------- */
+
+struct domain_dev_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct domain_device *dev, char *);
+	ssize_t (*store)(struct domain_device *dev, const char *, size_t);
+};
+
+void sas_kobj_set(struct domain_device *dev);
+
+extern struct kobj_type ex_dev_ktype;
+extern struct sysfs_ops dev_sysfs_ops;
+
+ssize_t dev_show_type(struct domain_device *dev, char *page);
+ssize_t dev_show_iproto(struct domain_device *dev, char *page);
+ssize_t dev_show_tproto(struct domain_device *dev, char *page);
+ssize_t dev_show_sas_addr(struct domain_device *dev, char *page);
+ssize_t dev_show_linkrate(struct domain_device *dev, char *page);
+ssize_t dev_show_min_linkrate(struct domain_device *dev, char *page);
+ssize_t dev_show_max_linkrate(struct domain_device *dev, char *page);
+ssize_t dev_show_pathways(struct domain_device *dev, char *page);
+
+int  sas_discover_root_expander(struct domain_device *dev);
+
+void sas_init_ex_attr(void);
+
+int  sas_ex_revalidate_domain(struct domain_device *port_dev);
+
+#endif /* _SAS_EXPANDER_H_ */
diff -urN oldtree/include/scsi/sas/sas_frames.h newtree/include/scsi/sas/sas_frames.h
--- oldtree/include/scsi/sas/sas_frames.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/scsi/sas/sas_frames.h	2006-02-13 19:20:01.255321464 -0500
@@ -0,0 +1,97 @@
+/*
+ * SAS Frames
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * $Id: //depot/sas-class/sas_frames.h#5 $
+ */
+
+#ifndef _SAS_FRAMES_
+#define _SAS_FRAMES_
+
+#define SMP_REQUEST             0x40
+#define SMP_RESPONSE            0x41
+
+#define SSP_DATA                0x01
+#define SSP_XFER_RDY            0x05
+#define SSP_COMMAND             0x06
+#define SSP_RESPONSE            0x07
+#define SSP_TASK                0x16
+
+struct  dev_to_host_fis {
+	u8     fis_type;	  /* 0x34 */
+	u8     flags;
+	u8     status;
+	u8     error;
+
+	u8     lbal;
+	union { u8 lbam; u8 byte_count_low; };
+	union { u8 lbah; u8 byte_count_high; };
+	u8     device;
+
+	u8     lbal_exp;
+	u8     lbam_exp;
+	u8     lbah_exp;
+	u8     _r_a;
+
+	union { u8  sector_count; u8 interrupt_reason; };
+	u8     sector_count_exp;
+	u8     _r_b;
+	u8     _r_c;
+
+	u32    _r_d;
+} __attribute__ ((packed));
+
+struct host_to_dev_fis {
+	u8     fis_type;	  /* 0x27 */
+	u8     flags;
+	u8     command;
+	u8     features;
+
+	u8     lbal;
+	union { u8 lbam; u8 byte_count_low; };
+	union { u8 lbah; u8 byte_count_high; };
+	u8     device;
+
+	u8     lbal_exp;
+	u8     lbam_exp;
+	u8     lbah_exp;
+	u8     features_exp;
+
+	union { u8  sector_count; u8 interrupt_reason; };
+	u8     sector_count_exp;
+	u8     _r_a;
+	u8     control;
+
+	u32    _r_b;
+} __attribute__ ((packed));
+
+/* Prefer to have code clarity over header file clarity.
+ */
+#ifdef __LITTLE_ENDIAN_BITFIELD
+#include <scsi/sas/sas_frames_le.h>
+#elif defined(__BIG_ENDIAN_BITFIELD)
+#include <scsi/sas/sas_frames_be.h>
+#else
+#error "Bitfield order not defined!"
+#endif
+
+#endif /* _SAS_FRAMES_ */
diff -urN oldtree/include/scsi/sas/sas_frames_be.h newtree/include/scsi/sas/sas_frames_be.h
--- oldtree/include/scsi/sas/sas_frames_be.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/scsi/sas/sas_frames_be.h	2006-02-13 19:20:01.256321312 -0500
@@ -0,0 +1,222 @@
+/*
+ * SAS Frames Big endian bitfield order
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * $Id: //depot/sas-class/sas_frames_be.h#11 $
+ */
+
+#ifndef _SAS_FRAMES_BE_H_
+#define _SAS_FRAMES_BE_H_
+
+#ifndef __BIG_ENDIAN_BITFIELD
+#error "Wrong header file included!"
+#endif
+
+struct sas_identify_frame {
+	/* Byte 0 */
+	u8  _un0:1;
+	u8  dev_type:3;
+	u8  frame_type:4;
+
+	/* Byte 1 */
+	u8  _un1;
+
+	/* Byte 2 */
+	union {
+		struct {
+			u8  _un247:4;
+			u8  ssp_iport:1;
+			u8  stp_iport:1;
+			u8  smp_iport:1;
+			u8  _un20:1;
+		};
+		u8 initiator_bits;
+	};
+
+	/* Byte 3 */
+	union {
+		struct {
+			u8 _un347:4;
+			u8 ssp_tport:1;
+			u8 stp_tport:1;
+			u8 smp_tport:1;
+			u8 _un30:1;
+		};
+		u8 target_bits;
+	};
+
+	/* Byte 4 - 11 */
+	u8 _un4_11[8];
+
+	/* Byte 12 - 19 */
+	u8 sas_addr[SAS_ADDR_SIZE];
+
+	/* Byte 20 */
+	u8 phy_id;
+
+	u8 _un21_27[7];
+
+	__be32 crc;
+} __attribute__ ((packed));
+
+struct ssp_frame_hdr {
+	u8     frame_type;
+	u8     hashed_dest_addr[HASHED_SAS_ADDR_SIZE];
+	u8     _r_a;
+	u8     hashed_src_addr[HASHED_SAS_ADDR_SIZE];
+	__be16 _r_b;
+
+	u8     _r_c:5;
+	u8     retry_data_frames:1;
+	u8     retransmit:1;
+	u8     changing_data_ptr:1;
+
+	u8     _r_d:6;
+	u8     num_fill_bytes:2;
+
+	u32    _r_e;
+	__be16 tag;
+	__be16 tptt;
+	__be32 data_offs;
+} __attribute__ ((packed));
+
+struct ssp_response_iu {
+	u8     _r_a[10];
+
+	u8     _r_b:6;
+	u8     datapres:2;
+
+	u8     status;
+
+	u32    _r_c;
+
+	__be32 sense_data_len;
+	__be32 response_data_len;
+
+	u8     resp_data[0];
+	u8     sense_data[0];
+} __attribute__ ((packed));
+
+/* ---------- SMP ---------- */
+
+struct report_general_resp {
+	__be16  change_count;
+	__be16  route_indexes;
+	u8      _r_a;
+	u8      num_phys;
+
+	u8      _r_b:6;
+	u8      configuring:1;
+	u8      conf_route_table:1;
+
+	u8      _r_c;
+
+	u8      enclosure_logical_id[8];
+
+	u8      _r_d[12];
+} __attribute__ ((packed));
+
+struct discover_resp {
+	u8    _r_a[5];
+
+	u8    phy_id;
+	__be16 _r_b;
+
+	u8    _r_d:1;
+	u8    attached_dev_type:3;
+	u8    _r_c:4;
+
+	u8    _r_e:4;
+	u8    linkrate:4;
+
+	u8    _r_f:4;
+	u8    iproto:3;
+	u8    attached_sata_host:1;
+
+	u8    attached_sata_ps:1;
+	u8    _r_g:3;
+	u8    tproto:3;
+	u8    attached_sata_dev:1;
+
+	u8    sas_addr[8];
+	u8    attached_sas_addr[8];
+	u8    attached_phy_id;
+
+	u8    _r_h[7];
+
+	u8    pmin_linkrate:4;
+	u8    hmin_linkrate:4;
+	u8    pmax_linkrate:4;
+	u8    hmax_linkrate:4;
+
+	u8    change_count;
+
+	u8    virtual:1;
+	u8    _r_i:3;
+	u8    pptv:4;
+
+	u8    _r_j:4;
+	u8    routing_attr:4;
+
+	u8    conn_type;
+	u8    conn_el_index;
+	u8    conn_phy_link;
+
+	u8    _r_k[8];
+} __attribute__ ((packed));
+
+struct report_phy_sata_resp {
+	u8    _r_a[5];
+
+	u8    phy_id;
+	u8    _r_b;
+
+	u8    _r_c:6;
+	u8    affil_supp:1;
+	u8    affil_valid:1;
+
+	u32   _r_d;
+
+	u8    stp_sas_addr[8];
+
+	struct dev_to_host_fis fis;
+
+	u32   _r_e;
+
+	u8    affil_stp_ini_addr[8];
+
+	__be32 crc;
+} __attribute__ ((packed));
+
+struct smp_resp {
+	u8    frame_type;
+	u8    function;
+	u8    result;
+	u8    reserved;
+	union {
+		struct report_general_resp  rg;
+		struct discover_resp        disc;
+		struct report_phy_sata_resp rps;
+	};
+} __attribute__ ((packed));
+
+#endif /* _SAS_FRAMES_BE_H_ */
diff -urN oldtree/include/scsi/sas/sas_frames_le.h newtree/include/scsi/sas/sas_frames_le.h
--- oldtree/include/scsi/sas/sas_frames_le.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/scsi/sas/sas_frames_le.h	2006-02-13 19:20:01.256321312 -0500
@@ -0,0 +1,223 @@
+/*
+ * SAS Frames Little endian bitfield order
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * $Id: //depot/sas-class/sas_frames_le.h#10 $
+ */
+
+#ifndef _SAS_FRAMES_LE_H_
+#define _SAS_FRAMES_LE_H_
+
+#ifndef __LITTLE_ENDIAN_BITFIELD
+#error "Wrong header file included!"
+#endif
+
+struct sas_identify_frame {
+	/* Byte 0 */
+	u8  frame_type:4;
+	u8  dev_type:3;
+	u8  _un0:1;
+
+	/* Byte 1 */
+	u8  _un1;
+
+	/* Byte 2 */
+	union {
+		struct {
+			u8  _un20:1;
+			u8  smp_iport:1;
+			u8  stp_iport:1;
+			u8  ssp_iport:1;
+			u8  _un247:4;
+		};
+		u8 initiator_bits;
+	};
+
+	/* Byte 3 */
+	union {
+		struct {
+			u8  _un30:1;
+			u8 smp_tport:1;
+			u8 stp_tport:1;
+			u8 ssp_tport:1;
+			u8 _un347:4;
+		};
+		u8 target_bits;
+	};
+
+	/* Byte 4 - 11 */
+	u8 _un4_11[8];
+
+	/* Byte 12 - 19 */
+	u8 sas_addr[SAS_ADDR_SIZE];
+
+	/* Byte 20 */
+	u8 phy_id;
+
+	u8 _un21_27[7];
+
+	__be32 crc;
+} __attribute__ ((packed));
+
+struct ssp_frame_hdr {
+	u8     frame_type;
+	u8     hashed_dest_addr[HASHED_SAS_ADDR_SIZE];
+	u8     _r_a;
+	u8     hashed_src_addr[HASHED_SAS_ADDR_SIZE];
+	__be16 _r_b;
+
+	u8     changing_data_ptr:1;
+	u8     retransmit:1;
+	u8     retry_data_frames:1;
+	u8     _r_c:5;
+
+	u8     num_fill_bytes:2;
+	u8     _r_d:6;
+
+	u32    _r_e;
+	__be16 tag;
+	__be16 tptt;
+	__be32 data_offs;
+} __attribute__ ((packed));
+
+struct ssp_response_iu {
+	u8     _r_a[10];
+
+	u8     datapres:2;
+	u8     _r_b:6;
+
+	u8     status;
+
+	u32    _r_c;
+
+	__be32 sense_data_len;
+	__be32 response_data_len;
+
+	u8     resp_data[0];
+	u8     sense_data[0];
+} __attribute__ ((packed));
+
+/* ---------- SMP ---------- */
+
+struct report_general_resp {
+	__be16  change_count;
+	__be16  route_indexes;
+	u8      _r_a;
+	u8      num_phys;
+
+	u8      conf_route_table:1;
+	u8      configuring:1;
+	u8      _r_b:6;
+
+	u8      _r_c;
+
+	u8      enclosure_logical_id[8];
+
+	u8      _r_d[12];
+} __attribute__ ((packed));
+
+struct discover_resp {
+	u8    _r_a[5];
+
+	u8    phy_id;
+	__be16 _r_b;
+
+	u8    _r_c:4;
+	u8    attached_dev_type:3;
+	u8    _r_d:1;
+
+	u8    linkrate:4;
+	u8    _r_e:4;
+
+	u8    attached_sata_host:1;
+	u8    iproto:3;
+	u8    _r_f:4;
+
+	u8    attached_sata_dev:1;
+	u8    tproto:3;
+	u8    _r_g:3;
+	u8    attached_sata_ps:1;
+
+	u8    sas_addr[8];
+	u8    attached_sas_addr[8];
+	u8    attached_phy_id;
+
+	u8    _r_h[7];
+
+	u8    hmin_linkrate:4;
+	u8    pmin_linkrate:4;
+	u8    hmax_linkrate:4;
+	u8    pmax_linkrate:4;
+
+	u8    change_count;
+
+	u8    pptv:4;
+	u8    _r_i:3;
+	u8    virtual:1;
+
+	u8    routing_attr:4;
+	u8    _r_j:4;
+
+	u8    conn_type;
+	u8    conn_el_index;
+	u8    conn_phy_link;
+
+	u8    _r_k[8];
+} __attribute__ ((packed));
+
+struct report_phy_sata_resp {
+	u8    _r_a[5];
+
+	u8    phy_id;
+	u8    _r_b;
+
+	u8    affil_valid:1;
+	u8    affil_supp:1;
+	u8    _r_c:6;
+
+	u32    _r_d;
+
+	u8    stp_sas_addr[8];
+
+	struct dev_to_host_fis fis;
+
+	u32   _r_e;
+
+	u8    affil_stp_ini_addr[8];
+
+	__be32 crc;
+} __attribute__ ((packed));
+
+struct smp_resp {
+	u8    frame_type;
+	u8    function;
+	u8    result;
+	u8    reserved;
+	union {
+		struct report_general_resp  rg;
+		struct discover_resp        disc;
+		struct report_phy_sata_resp rps;
+	};
+} __attribute__ ((packed));
+
+#endif /* _SAS_FRAMES_LE_H_ */
+
diff -urN oldtree/include/scsi/sas/sas_task.h newtree/include/scsi/sas/sas_task.h
--- oldtree/include/scsi/sas/sas_task.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/include/scsi/sas/sas_task.h	2006-02-13 19:20:01.257321160 -0500
@@ -0,0 +1,234 @@
+/*
+ * Serial Attached SCSI (SAS) Task interface
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/sas-class/sas_task.h#27 $
+ */
+
+#ifndef _SAS_TASK_H_
+#define _SAS_TASK_H_
+
+#include <linux/timer.h>
+#include <linux/pci.h>
+#include <scsi/sas/sas_discover.h>
+
+/* SAM TMFs */
+#define TMF_ABORT_TASK      0x01
+#define TMF_ABORT_TASK_SET  0x02
+#define TMF_CLEAR_TASK_SET  0x04
+#define TMF_LU_RESET        0x08
+#define TMF_CLEAR_ACA       0x40
+#define TMF_QUERY_TASK      0x80
+
+/* SAS TMF responses */
+#define TMF_RESP_FUNC_COMPLETE   0x00
+#define TMF_RESP_INVALID_FRAME   0x02
+#define TMF_RESP_FUNC_ESUPP      0x04
+#define TMF_RESP_FUNC_FAILED     0x05
+#define TMF_RESP_FUNC_SUCC       0x08
+#define TMF_RESP_NO_LUN          0x09
+#define TMF_RESP_OVERLAPPED_TAG  0x0A
+
+/*
+      service_response |  SAS_TASK_COMPLETE  |  SAS_TASK_UNDELIVERED |
+  exec_status          |                     |                       |
+  ---------------------+---------------------+-----------------------+
+       SAM_...         |         X           |                       |
+       DEV_NO_RESPONSE |         X           |           X           |
+       INTERRUPTED     |         X           |                       |
+       QUEUE_FULL      |                     |           X           |
+       DEVICE_UNKNOWN  |                     |           X           |
+       SG_ERR          |                     |           X           |
+  ---------------------+---------------------+-----------------------+
+ */
+
+enum service_response {
+	SAS_TASK_COMPLETE,
+	SAS_TASK_UNDELIVERED = -1,
+};
+
+enum exec_status {
+	SAM_GOOD         = 0,
+	SAM_CHECK_COND   = 2,
+	SAM_COND_MET     = 4,
+	SAM_BUSY         = 8,
+	SAM_INTERMEDIATE = 0x10,
+	SAM_IM_COND_MET  = 0x12,
+	SAM_RESV_CONFLICT= 0x14,
+	SAM_TASK_SET_FULL= 0x28,
+	SAM_ACA_ACTIVE   = 0x30,
+	SAM_TASK_ABORTED = 0x40,
+
+	SAS_DEV_NO_RESPONSE = 0x80,
+	SAS_DATA_UNDERRUN,
+	SAS_DATA_OVERRUN,
+	SAS_INTERRUPTED,
+	SAS_QUEUE_FULL,
+	SAS_DEVICE_UNKNOWN,
+	SAS_SG_ERR,
+	SAS_OPEN_REJECT,
+	SAS_OPEN_TO,
+	SAS_PROTO_RESPONSE,
+	SAS_PHY_DOWN,
+	SAS_NAK_R_ERR,
+	SAS_PENDING,
+	SAS_ABORTED_TASK,
+};
+
+/* When a task finishes with a response, the LLDD examines the
+ * response:
+ * 	- For an ATA task task_status_struct::stat is set to
+ * SAS_PROTO_RESPONSE, and the task_status_struct::buf is set to the
+ * contents of struct ata_task_resp.
+ * 	- For SSP tasks, if no data is present or status/TMF response
+ * is valid, task_status_struct::stat is set.  If data is present
+ * (SENSE data), the LLDD copies up to SAS_STATUS_BUF_SIZE, sets
+ * task_status_struct::buf_valid_size, and task_status_struct::stat is
+ * set to SAM_CHECK_COND.
+ *
+ * "buf" has format SCSI Sense for SSP task, or struct ata_task_resp
+ * for ATA task.
+ *
+ * "frame_len" is the total frame length, which could be more or less
+ * than actually copied.
+ *
+ * Tasks ending with response, always set the residual field.
+ */
+struct ata_task_resp {
+	u16  frame_len;
+	u8   ending_fis[24];	  /* dev to host or data-in */
+	u32  sstatus;
+	u32  serror;
+	u32  scontrol;
+	u32  sactive;
+};
+
+#define SAS_STATUS_BUF_SIZE 96
+
+struct task_status_struct {
+	enum service_response resp;
+	enum exec_status      stat;
+	int  buf_valid_size;
+
+	u8   buf[SAS_STATUS_BUF_SIZE];
+
+	u32  residual;
+	enum sas_open_rej_reason open_rej_reason;
+};
+
+/* ATA and ATAPI task queuable to a SAS LLDD.
+ */
+struct sas_ata_task {
+	struct host_to_dev_fis fis;
+	u8     atapi_packet[16];  /* 0 if not ATAPI task */
+
+	u8     retry_count;	  /* hardware retry, should be > 0 */
+
+	u8     dma_xfer:1;	  /* PIO:0 or DMA:1 */
+	u8     use_ncq:1;
+	u8     set_affil_pol:1;
+	u8     stp_affil_pol:1;
+
+	u8     device_control_reg_update:1;
+};
+
+struct sas_smp_task {
+	struct scatterlist smp_req;
+	struct scatterlist smp_resp;
+};
+
+enum task_attribute {
+	TASK_ATTR_SIMPLE = 0,
+	TASK_ATTR_HOQ    = 1,
+	TASK_ATTR_ORDERED= 2,
+	TASK_ATTR_ACA    = 4,
+};
+
+struct sas_ssp_task {
+	u8     retry_count;	  /* hardware retry, should be > 0 */
+
+	u8     LUN[8];
+	u8     enable_first_burst:1;
+	enum   task_attribute task_attr;
+	u8     task_prio;
+	u8     cdb[16];
+};
+
+#define SAS_TASK_STATE_PENDING  1
+#define SAS_TASK_STATE_DONE     2
+#define SAS_TASK_STATE_ABORTED  4
+
+struct sas_task {
+	struct domain_device *dev;
+	struct list_head      list;
+
+	spinlock_t   task_state_lock;
+	unsigned     task_state_flags;
+
+	enum   sas_proto      task_proto;
+
+	/* Used by the discovery code. */
+	struct timer_list     timer;
+	struct completion     completion;
+
+	union {
+		struct sas_ata_task ata_task;
+		struct sas_smp_task smp_task;
+		struct sas_ssp_task ssp_task;
+	};
+
+	struct scatterlist *scatter;
+	int    num_scatter;
+	u32    total_xfer_len;
+	u8     data_dir:2;	  /* Use PCI_DMA_... */
+
+	struct task_status_struct task_status;
+	void   (*task_done)(struct sas_task *task);
+
+	void   *lldd_task;	  /* for use by LLDDs */
+	void   *uldd_task;
+};
+
+static inline struct sas_task *sas_alloc_task(unsigned long flags)
+{
+	extern kmem_cache_t *sas_task_cache;
+	struct sas_task *task = kmem_cache_alloc(sas_task_cache, flags);
+
+	memset(task, 0, sizeof(*task));
+	INIT_LIST_HEAD(&task->list);
+	spin_lock_init(&task->task_state_lock);
+	task->task_state_flags = SAS_TASK_STATE_PENDING;
+	init_timer(&task->timer);
+	init_completion(&task->completion);
+
+	return task;
+}
+
+static inline void sas_free_task(struct sas_task *task)
+{
+	if (task) {
+		extern kmem_cache_t *sas_task_cache;
+		BUG_ON(!list_empty(&task->list));
+		kmem_cache_free(sas_task_cache, task);
+	}
+}
+
+#endif /* _SAS_TASK_H_ */
diff -urN oldtree/init/Kconfig newtree/init/Kconfig
--- oldtree/init/Kconfig	2006-01-02 22:21:10.000000000 -0500
+++ newtree/init/Kconfig	2006-02-13 19:20:01.257321160 -0500
@@ -399,6 +399,15 @@
 	  no dummy operations need be executed.
 	  Zero means use compiler's default.
 
+config SERIAL_PCI
+	depends PCI && SERIAL_8250
+	default y
+	bool "Enable standard PCI serial support" if EMBEDDED
+	help
+	  This builds standard PCI serial support. You may be able to disable
+          this feature if you only need legacy serial support.
+	  Saves about 9K.
+
 endmenu		# General setup
 
 config TINY_SHMEM
diff -urN oldtree/init/Kconfig.orig newtree/init/Kconfig.orig
--- oldtree/init/Kconfig.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/init/Kconfig.orig	2006-02-13 19:20:01.258321008 -0500
@@ -0,0 +1,508 @@
+menu "Code maturity level options"
+
+config EXPERIMENTAL
+	bool "Prompt for development and/or incomplete code/drivers"
+	---help---
+	  Some of the various things that Linux supports (such as network
+	  drivers, file systems, network protocols, etc.) can be in a state
+	  of development where the functionality, stability, or the level of
+	  testing is not yet high enough for general use. This is usually
+	  known as the "alpha-test" phase among developers. If a feature is
+	  currently in alpha-test, then the developers usually discourage
+	  uninformed widespread use of this feature by the general public to
+	  avoid "Why doesn't this work?" type mail messages. However, active
+	  testing and use of these systems is welcomed. Just be aware that it
+	  may not meet the normal level of reliability or it may fail to work
+	  in some special cases. Detailed bug reports from people familiar
+	  with the kernel internals are usually welcomed by the developers
+	  (before submitting bug reports, please read the documents
+	  <file:README>, <file:MAINTAINERS>, <file:REPORTING-BUGS>,
+	  <file:Documentation/BUG-HUNTING>, and
+	  <file:Documentation/oops-tracing.txt> in the kernel source).
+
+	  This option will also make obsoleted drivers available. These are
+	  drivers that have been replaced by something else, and/or are
+	  scheduled to be removed in a future kernel release.
+
+	  Unless you intend to help test and develop a feature or driver that
+	  falls into this category, or you have a situation that requires
+	  using these features, you should probably say N here, which will
+	  cause the configurator to present you with fewer choices. If
+	  you say Y here, you will be offered the choice of using features or
+	  drivers that are currently considered to be in the alpha-test phase.
+
+config CLEAN_COMPILE
+	bool "Select only drivers expected to compile cleanly" if EXPERIMENTAL
+	default y
+	help
+	  Select this option if you don't even want to see the option
+	  to configure known-broken drivers.
+
+	  If unsure, say Y
+
+config BROKEN
+	bool
+	depends on !CLEAN_COMPILE
+	default y
+
+config BROKEN_ON_SMP
+	bool
+	depends on BROKEN || !SMP
+	default y
+
+config LOCK_KERNEL
+	bool
+	depends on SMP || PREEMPT
+	default y
+
+config INIT_ENV_ARG_LIMIT
+	int
+	default 32 if !USERMODE
+	default 128 if USERMODE
+	help
+	  Maximum of each of the number of arguments and environment
+	  variables passed to init from the kernel command line.
+
+endmenu
+
+menu "General setup"
+
+config LOCALVERSION
+	string "Local version - append to kernel release"
+	help
+	  Append an extra string to the end of your kernel version.
+	  This will show up when you type uname, for example.
+	  The string you set here will be appended after the contents of
+	  any files with a filename matching localversion* in your
+	  object and source tree, in that order.  Your total string can
+	  be a maximum of 64 characters.
+
+config LOCALVERSION_AUTO
+	bool "Automatically append version information to the version string"
+	default y
+	help
+	  This will try to automatically determine if the current tree is a
+	  release tree by looking for git tags that
+	  belong to the current top of tree revision.
+
+	  A string of the format -gxxxxxxxx will be added to the localversion
+	  if a git based tree is found.  The string generated by this will be
+	  appended after any matching localversion* files, and after the value
+	  set in CONFIG_LOCALVERSION
+
+	  Note: This requires Perl, and a git repository, but not necessarily
+	  the git or cogito tools to be installed.
+
+config SWAP
+	bool "Support for paging of anonymous memory (swap)"
+	depends on MMU
+	default y
+	help
+	  This option allows you to choose whether you want to have support
+	  for socalled swap devices or swap files in your kernel that are
+	  used to provide more virtual memory than the actual RAM present
+	  in your computer.  If unsure say Y.
+
+config SYSVIPC
+	bool "System V IPC"
+	depends on MMU
+	---help---
+	  Inter Process Communication is a suite of library functions and
+	  system calls which let processes (running programs) synchronize and
+	  exchange information. It is generally considered to be a good thing,
+	  and some programs won't run unless you say Y here. In particular, if
+	  you want to run the DOS emulator dosemu under Linux (read the
+	  DOSEMU-HOWTO, available from <http://www.tldp.org/docs.html#howto>),
+	  you'll need to say Y here.
+
+	  You can find documentation about IPC with "info ipc" and also in
+	  section 6.4 of the Linux Programmer's Guide, available from
+	  <http://www.tldp.org/guides.html>.
+
+config POSIX_MQUEUE
+	bool "POSIX Message Queues"
+	depends on NET && EXPERIMENTAL
+	---help---
+	  POSIX variant of message queues is a part of IPC. In POSIX message
+	  queues every message has a priority which decides about succession
+	  of receiving it by a process. If you want to compile and run
+	  programs written e.g. for Solaris with use of its POSIX message
+	  queues (functions mq_*) say Y here. To use this feature you will
+	  also need mqueue library, available from
+	  <http://www.mat.uni.torun.pl/~wrona/posix_ipc/>
+
+	  POSIX message queues are visible as a filesystem called 'mqueue'
+	  and can be mounted somewhere if you want to do filesystem
+	  operations on message queues.
+
+	  If unsure, say Y.
+
+config BSD_PROCESS_ACCT
+	bool "BSD Process Accounting"
+	help
+	  If you say Y here, a user level program will be able to instruct the
+	  kernel (via a special system call) to write process accounting
+	  information to a file: whenever a process exits, information about
+	  that process will be appended to the file by the kernel.  The
+	  information includes things such as creation time, owning user,
+	  command name, memory usage, controlling terminal etc. (the complete
+	  list is in the struct acct in <file:include/linux/acct.h>).  It is
+	  up to the user level program to do useful things with this
+	  information.  This is generally a good idea, so say Y.
+
+config BSD_PROCESS_ACCT_V3
+	bool "BSD Process Accounting version 3 file format"
+	depends on BSD_PROCESS_ACCT
+	default n
+	help
+	  If you say Y here, the process accounting information is written
+	  in a new file format that also logs the process IDs of each
+	  process and it's parent. Note that this file format is incompatible
+	  with previous v0/v1/v2 file formats, so you will need updated tools
+	  for processing it. A preliminary version of these tools is available
+	  at <http://www.physik3.uni-rostock.de/tim/kernel/utils/acct/>.
+
+config SYSCTL
+	bool "Sysctl support"
+	---help---
+	  The sysctl interface provides a means of dynamically changing
+	  certain kernel parameters and variables on the fly without requiring
+	  a recompile of the kernel or reboot of the system.  The primary
+	  interface consists of a system call, but if you say Y to "/proc
+	  file system support", a tree of modifiable sysctl entries will be
+	  generated beneath the /proc/sys directory. They are explained in the
+	  files in <file:Documentation/sysctl/>.  Note that enabling this
+	  option will enlarge the kernel by at least 8 KB.
+
+	  As it is generally a good thing, you should say Y here unless
+	  building a kernel for install/rescue disks or your system is very
+	  limited in memory.
+
+config AUDIT
+	bool "Auditing support"
+	depends on NET
+	default y if SECURITY_SELINUX
+	help
+	  Enable auditing infrastructure that can be used with another
+	  kernel subsystem, such as SELinux (which requires this for
+	  logging of avc messages output).  Does not do system-call
+	  auditing without CONFIG_AUDITSYSCALL.
+
+config AUDITSYSCALL
+	bool "Enable system-call auditing support"
+	depends on AUDIT && (X86 || PPC || PPC64 || ARCH_S390 || IA64 || UML || SPARC64)
+	default y if SECURITY_SELINUX
+	help
+	  Enable low-overhead system-call auditing infrastructure that
+	  can be used independently or with another kernel subsystem,
+	  such as SELinux.
+
+config HOTPLUG
+	bool "Support for hot-pluggable devices" if !ARCH_S390
+	default ARCH_S390
+	help
+	  This option is provided for the case where no in-kernel-tree
+	  modules require HOTPLUG functionality, but a module built
+	  outside the kernel tree does. Such modules require Y here.
+
+config KOBJECT_UEVENT
+	bool "Kernel Userspace Events" if EMBEDDED
+	depends on NET
+	default y
+	help
+	  This option enables the kernel userspace event layer, which is a
+	  simple mechanism for kernel-to-user communication over a netlink
+	  socket.
+	  The goal of the kernel userspace events layer is to provide a simple
+	  and efficient events system, that notifies userspace about kobject
+	  state changes. This will enable applications to just listen for
+	  events instead of polling system devices and files.
+	  Hotplug events (kobject addition and removal) are also available on
+	  the netlink socket in addition to the execution of /sbin/hotplug if
+	  CONFIG_HOTPLUG is enabled.
+
+	  Say Y, unless you are building a system requiring minimal memory
+	  consumption.
+
+config IKCONFIG
+	bool "Kernel .config support"
+	---help---
+	  This option enables the complete Linux kernel ".config" file
+	  contents to be saved in the kernel. It provides documentation
+	  of which kernel options are used in a running kernel or in an
+	  on-disk kernel.  This information can be extracted from the kernel
+	  image file with the script scripts/extract-ikconfig and used as
+	  input to rebuild the current kernel or to build another kernel.
+	  It can also be extracted from a running kernel by reading
+	  /proc/config.gz if enabled (below).
+
+config IKCONFIG_PROC
+	bool "Enable access to .config through /proc/config.gz"
+	depends on IKCONFIG && PROC_FS
+	---help---
+	  This option enables access to the kernel configuration file
+	  through /proc/config.gz.
+
+config CPUSETS
+	bool "Cpuset support"
+	depends on SMP
+	help
+	  This option will let you create and manage CPUSETs which
+	  allow dynamically partitioning a system into sets of CPUs and
+	  Memory Nodes and assigning tasks to run only within those sets.
+	  This is primarily useful on large SMP or NUMA systems.
+
+	  Say N if unsure.
+
+source "usr/Kconfig"
+
+config CC_OPTIMIZE_FOR_SIZE
+	bool "Optimize for size (Look out for broken compilers!)"
+	default y
+	depends on ARM || H8300 || EXPERIMENTAL
+	help
+	  Enabling this option will pass "-Os" instead of "-O2" to gcc
+	  resulting in a smaller kernel.
+
+	  WARNING: some versions of gcc may generate incorrect code with this
+	  option.  If problems are observed, a gcc upgrade may be needed.
+
+	  If unsure, say N.
+
+menuconfig EMBEDDED
+	bool "Configure standard kernel features (for small systems)"
+	help
+	  This option allows certain base kernel options and settings
+          to be disabled or tweaked. This is for specialized
+          environments which can tolerate a "non-standard" kernel.
+          Only use this if you really know what you are doing.
+
+config KALLSYMS
+	 bool "Load all symbols for debugging/kksymoops" if EMBEDDED
+	 default y
+	 help
+	   Say Y here to let the kernel print out symbolic crash information and
+	   symbolic stack backtraces. This increases the size of the kernel
+	   somewhat, as all symbols have to be loaded into the kernel image.
+
+config KALLSYMS_ALL
+	bool "Include all symbols in kallsyms"
+	depends on DEBUG_KERNEL && KALLSYMS
+	help
+	   Normally kallsyms only contains the symbols of functions, for nicer
+	   OOPS messages.  Some debuggers can use kallsyms for other
+	   symbols too: say Y here to include all symbols, if you need them 
+	   and you don't care about adding 300k to the size of your kernel.
+
+	   Say N.
+
+config KALLSYMS_EXTRA_PASS
+	bool "Do an extra kallsyms pass"
+	depends on KALLSYMS
+	help
+	   If kallsyms is not working correctly, the build will fail with
+	   inconsistent kallsyms data.  If that occurs, log a bug report and
+	   turn on KALLSYMS_EXTRA_PASS which should result in a stable build.
+	   Always say N here unless you find a bug in kallsyms, which must be
+	   reported.  KALLSYMS_EXTRA_PASS is only a temporary workaround while
+	   you wait for kallsyms to be fixed.
+
+
+config PRINTK
+	default y
+	bool "Enable support for printk" if EMBEDDED
+	help
+	  This option enables normal printk support. Removing it
+	  eliminates most of the message strings from the kernel image
+	  and makes the kernel more or less silent. As this makes it
+	  very difficult to diagnose system problems, saying N here is
+	  strongly discouraged.
+
+config BUG
+	bool "BUG() support" if EMBEDDED
+	default y
+	help
+          Disabling this option eliminates support for BUG and WARN, reducing
+          the size of your kernel image and potentially quietly ignoring
+          numerous fatal conditions. You should only consider disabling this
+          option for embedded systems with no facilities for reporting errors.
+          Just say Y.
+
+config BASE_FULL
+	default y
+	bool "Enable full-sized data structures for core" if EMBEDDED
+	help
+	  Disabling this option reduces the size of miscellaneous core
+	  kernel data structures. This saves memory on small machines,
+	  but may reduce performance.
+
+config FUTEX
+	bool "Enable futex support" if EMBEDDED
+	default y
+	help
+	  Disabling this option will cause the kernel to be built without
+	  support for "fast userspace mutexes".  The resulting kernel may not
+	  run glibc-based applications correctly.
+
+config EPOLL
+	bool "Enable eventpoll support" if EMBEDDED
+	default y
+	help
+	  Disabling this option will cause the kernel to be built without
+	  support for epoll family of system calls.
+
+config SHMEM
+	bool "Use full shmem filesystem" if EMBEDDED
+	default y
+	depends on MMU
+	help
+	  The shmem is an internal filesystem used to manage shared memory.
+	  It is backed by swap and manages resource limits. It is also exported
+	  to userspace as tmpfs if TMPFS is enabled. Disabling this
+	  option replaces shmem and tmpfs with the much simpler ramfs code,
+	  which may be appropriate on small systems without swap.
+
+config CC_ALIGN_FUNCTIONS
+	int "Function alignment" if EMBEDDED
+	default 0
+	help
+	  Align the start of functions to the next power-of-two greater than n,
+	  skipping up to n bytes.  For instance, 32 aligns functions
+	  to the next 32-byte boundary, but 24 would align to the next
+	  32-byte boundary only if this can be done by skipping 23 bytes or less.
+	  Zero means use compiler's default.
+
+config CC_ALIGN_LABELS
+	int "Label alignment" if EMBEDDED
+	default 0
+	help
+	  Align all branch targets to a power-of-two boundary, skipping
+	  up to n bytes like ALIGN_FUNCTIONS.  This option can easily
+	  make code slower, because it must insert dummy operations for
+	  when the branch target is reached in the usual flow of the code.
+	  Zero means use compiler's default.
+
+config CC_ALIGN_LOOPS
+	int "Loop alignment" if EMBEDDED
+	default 0
+	help
+	  Align loops to a power-of-two boundary, skipping up to n bytes.
+	  Zero means use compiler's default.
+
+config CC_ALIGN_JUMPS
+	int "Jump alignment" if EMBEDDED
+	default 0
+	help
+	  Align branch targets to a power-of-two boundary, for branch
+	  targets where the targets can only be reached by jumping,
+	  skipping up to n bytes like ALIGN_FUNCTIONS.  In this case,
+	  no dummy operations need be executed.
+	  Zero means use compiler's default.
+
+endmenu		# General setup
+
+config TINY_SHMEM
+	default !SHMEM
+	bool
+
+config BASE_SMALL
+	int
+	default 0 if BASE_FULL
+	default 1 if !BASE_FULL
+
+menu "Loadable module support"
+
+config MODULES
+	bool "Enable loadable module support"
+	help
+	  Kernel modules are small pieces of compiled code which can
+	  be inserted in the running kernel, rather than being
+	  permanently built into the kernel.  You use the "modprobe"
+	  tool to add (and sometimes remove) them.  If you say Y here,
+	  many parts of the kernel can be built as modules (by
+	  answering M instead of Y where indicated): this is most
+	  useful for infrequently used options which are not required
+	  for booting.  For more information, see the man pages for
+	  modprobe, lsmod, modinfo, insmod and rmmod.
+
+	  If you say Y here, you will need to run "make
+	  modules_install" to put the modules under /lib/modules/
+	  where modprobe can find them (you may need to be root to do
+	  this).
+
+	  If unsure, say Y.
+
+config MODULE_UNLOAD
+	bool "Module unloading"
+	depends on MODULES
+	help
+	  Without this option you will not be able to unload any
+	  modules (note that some modules may not be unloadable
+	  anyway), which makes your kernel slightly smaller and
+	  simpler.  If unsure, say Y.
+
+config MODULE_FORCE_UNLOAD
+	bool "Forced module unloading"
+	depends on MODULE_UNLOAD && EXPERIMENTAL
+	help
+	  This option allows you to force a module to unload, even if the
+	  kernel believes it is unsafe: the kernel will remove the module
+	  without waiting for anyone to stop using it (using the -f option to
+	  rmmod).  This is mainly for kernel developers and desperate users.
+	  If unsure, say N.
+
+config OBSOLETE_MODPARM
+	bool
+	default y
+	depends on MODULES
+	help
+	  You need this option to use module parameters on modules which
+	  have not been converted to the new module parameter system yet.
+	  If unsure, say Y.
+
+config MODVERSIONS
+	bool "Module versioning support (EXPERIMENTAL)"
+	depends on MODULES && EXPERIMENTAL
+	help
+	  Usually, you have to use modules compiled with your kernel.
+	  Saying Y here makes it sometimes possible to use modules
+	  compiled for different kernels, by adding enough information
+	  to the modules to (hopefully) spot any changes which would
+	  make them incompatible with the kernel you are running.  If
+	  unsure, say N.
+
+config MODULE_SRCVERSION_ALL
+	bool "Source checksum for all modules"
+	depends on MODULES
+	help
+	  Modules which contain a MODULE_VERSION get an extra "srcversion"
+	  field inserted into their modinfo section, which contains a
+    	  sum of the source files which made it.  This helps maintainers
+	  see exactly which source was used to build a module (since
+	  others sometimes change the module source without updating
+	  the version).  With this option, such a "srcversion" field
+	  will be created for all modules.  If unsure, say N.
+
+config KMOD
+	bool "Automatic kernel module loading"
+	depends on MODULES
+	help
+	  Normally when you have selected some parts of the kernel to
+	  be created as kernel modules, you must load them (using the
+	  "modprobe" command) before you can use them. If you say Y
+	  here, some parts of the kernel will be able to load modules
+	  automatically: when a part of the kernel needs a module, it
+	  runs modprobe with the appropriate arguments, thereby
+	  loading the module if it is available.  If unsure, say Y.
+
+config STOP_MACHINE
+	bool
+	default y
+	depends on (SMP && MODULE_UNLOAD) || HOTPLUG_CPU
+	help
+	  Need stop_machine() primitive.
+endmenu
+
+menu "Block layer"
+source "block/Kconfig"
+endmenu
diff -urN oldtree/ipc/mqueue.c newtree/ipc/mqueue.c
--- oldtree/ipc/mqueue.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/ipc/mqueue.c	2006-02-13 19:20:29.746990072 -0500
@@ -598,15 +598,16 @@
 static struct file *do_create(struct dentry *dir, struct dentry *dentry,
 			int oflag, mode_t mode, struct mq_attr __user *u_attr)
 {
-	struct file *filp;
 	struct mq_attr attr;
 	int ret;
 
-	if (u_attr != NULL) {
+	if (u_attr) {
+		ret = -EFAULT;
 		if (copy_from_user(&attr, u_attr, sizeof(attr)))
-			return ERR_PTR(-EFAULT);
+			goto out;
+		ret = -EINVAL;
 		if (!mq_attr_ok(&attr))
-			return ERR_PTR(-EINVAL);
+			goto out;
 		/* store for use during create */
 		dentry->d_fsdata = &attr;
 	}
@@ -615,13 +616,14 @@
 	ret = vfs_create(dir->d_inode, dentry, mode, NULL);
 	dentry->d_fsdata = NULL;
 	if (ret)
-		return ERR_PTR(ret);
+		goto out;
 
-	filp = dentry_open(dentry, mqueue_mnt, oflag);
-	if (!IS_ERR(filp))
-		dget(dentry);
+	return dentry_open(dentry, mqueue_mnt, oflag);
 
-	return filp;
+out:
+	dput(dentry);
+	mntput(mqueue_mnt);
+	return ERR_PTR(ret);
 }
 
 /* Opens existing queue */
@@ -629,20 +631,20 @@
 {
 static int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE,
 					MAY_READ | MAY_WRITE };
-	struct file *filp;
 
-	if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY))
+	if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) {
+		dput(dentry);
+		mntput(mqueue_mnt);
 		return ERR_PTR(-EINVAL);
+	}
 
-	if (permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE], NULL))
+	if (permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE], NULL)) {
+		dput(dentry);
+		mntput(mqueue_mnt);
 		return ERR_PTR(-EACCES);
+	}
 
-	filp = dentry_open(dentry, mqueue_mnt, oflag);
-
-	if (!IS_ERR(filp))
-		dget(dentry);
-
-	return filp;
+	return dentry_open(dentry, mqueue_mnt, oflag);
 }
 
 asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode,
@@ -670,17 +672,20 @@
 
 	if (oflag & O_CREAT) {
 		if (dentry->d_inode) {	/* entry already exists */
-			filp = (oflag & O_EXCL) ? ERR_PTR(-EEXIST) :
-					do_open(dentry, oflag);
+			error = -EEXIST;
+			if (oflag & O_EXCL)
+				goto out;
+			filp = do_open(dentry, oflag);
 		} else {
 			filp = do_create(mqueue_mnt->mnt_root, dentry,
 						oflag, mode, u_attr);
 		}
-	} else
-		filp = (dentry->d_inode) ? do_open(dentry, oflag) :
-					ERR_PTR(-ENOENT);
-
-	dput(dentry);
+	} else {
+		error = -ENOENT;
+		if (!dentry->d_inode)
+			goto out;
+		filp = do_open(dentry, oflag);
+	}
 
 	if (IS_ERR(filp)) {
 		error = PTR_ERR(filp);
@@ -691,8 +696,10 @@
 	fd_install(fd, filp);
 	goto out_upsem;
 
-out_putfd:
+out:
+	dput(dentry);
 	mntput(mqueue_mnt);
+out_putfd:
 	put_unused_fd(fd);
 out_err:
 	fd = error;
diff -urN oldtree/ipc/msg.c newtree/ipc/msg.c
--- oldtree/ipc/msg.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/ipc/msg.c	2006-02-13 19:20:01.259320856 -0500
@@ -428,8 +428,6 @@
 			return -EFAULT;
 		if (copy_msqid_from_user (&setbuf, buf, version))
 			return -EFAULT;
-		if ((err = audit_ipc_perms(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode)))
-			return err;
 		break;
 	case IPC_RMID:
 		break;
@@ -460,6 +458,9 @@
 	switch (cmd) {
 	case IPC_SET:
 	{
+		if ((err = audit_ipc_perms(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode, ipcp)))
+			goto out_unlock_up;
+
 		err = -EPERM;
 		if (setbuf.qbytes > msg_ctlmnb && !capable(CAP_SYS_RESOURCE))
 			goto out_unlock_up;
diff -urN oldtree/ipc/sem.c newtree/ipc/sem.c
--- oldtree/ipc/sem.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/ipc/sem.c	2006-02-13 19:20:01.259320856 -0500
@@ -808,8 +808,6 @@
 	if(cmd == IPC_SET) {
 		if(copy_semid_from_user (&setbuf, arg.buf, version))
 			return -EFAULT;
-		if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode)))
-			return err;
 	}
 	sma = sem_lock(semid);
 	if(sma==NULL)
@@ -820,7 +818,6 @@
 		goto out_unlock;
 	}	
 	ipcp = &sma->sem_perm;
-	
 	if (current->euid != ipcp->cuid && 
 	    current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) {
 	    	err=-EPERM;
@@ -837,6 +834,8 @@
 		err = 0;
 		break;
 	case IPC_SET:
+		if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode, ipcp)))
+			goto out_unlock;
 		ipcp->uid = setbuf.uid;
 		ipcp->gid = setbuf.gid;
 		ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
diff -urN oldtree/ipc/shm.c newtree/ipc/shm.c
--- oldtree/ipc/shm.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/ipc/shm.c	2006-02-13 19:20:01.260320704 -0500
@@ -613,13 +613,13 @@
 			err = -EFAULT;
 			goto out;
 		}
-		if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode)))
-			return err;
 		down(&shm_ids.sem);
 		shp = shm_lock(shmid);
 		err=-EINVAL;
 		if(shp==NULL)
 			goto out_up;
+		if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode, &(shp->shm_perm))))
+			goto out_unlock_up;
 		err = shm_checkid(shp,shmid);
 		if(err)
 			goto out_unlock_up;
diff -urN oldtree/ipc/util.c newtree/ipc/util.c
--- oldtree/ipc/util.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/ipc/util.c	2006-02-13 19:20:01.260320704 -0500
@@ -26,6 +26,7 @@
 #include <linux/workqueue.h>
 #include <linux/seq_file.h>
 #include <linux/proc_fs.h>
+#include <linux/audit.h>
 
 #include <asm/unistd.h>
 
@@ -467,6 +468,7 @@
 {	/* flag will most probably be 0 or S_...UGO from <linux/stat.h> */
 	int requested_mode, granted_mode;
 
+	audit_ipc_context(ipcp);
 	requested_mode = (flag >> 6) | (flag >> 3) | flag;
 	granted_mode = ipcp->mode;
 	if (current->euid == ipcp->cuid || current->euid == ipcp->uid)
diff -urN oldtree/kernel/Makefile newtree/kernel/Makefile
--- oldtree/kernel/Makefile	2006-01-02 22:21:10.000000000 -0500
+++ newtree/kernel/Makefile	2006-02-13 19:20:01.260320704 -0500
@@ -23,7 +23,7 @@
 obj-$(CONFIG_CPUSETS) += cpuset.o
 obj-$(CONFIG_IKCONFIG) += configs.o
 obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
-obj-$(CONFIG_AUDIT) += audit.o
+obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
 obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
 obj-$(CONFIG_KPROBES) += kprobes.o
 obj-$(CONFIG_SYSFS) += ksysfs.o
diff -urN oldtree/kernel/audit.c newtree/kernel/audit.c
--- oldtree/kernel/audit.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/kernel/audit.c	2006-02-13 19:20:01.261320552 -0500
@@ -72,7 +72,7 @@
  * contains the (non-zero) pid. */
 int		audit_pid;
 
-/* If audit_limit is non-zero, limit the rate of sending audit records
+/* If audit_rate_limit is non-zero, limit the rate of sending audit records
  * to that number per second.  This prevents DoS attacks, but results in
  * audit records being dropped. */
 static int	audit_rate_limit;
@@ -102,7 +102,7 @@
  * than AUDIT_MAXFREE are in use, the audit buffer is freed instead of
  * being placed on the freelist). */
 static DEFINE_SPINLOCK(audit_freelist_lock);
-static int	   audit_freelist_count = 0;
+static int	   audit_freelist_count;
 static LIST_HEAD(audit_freelist);
 
 static struct sk_buff_head audit_skb_queue;
@@ -142,7 +142,7 @@
 	nlh->nlmsg_pid = pid;
 }
 
-static void audit_panic(const char *message)
+void audit_panic(const char *message)
 {
 	switch (audit_failure)
 	{
@@ -186,8 +186,14 @@
 	return retval;
 }
 
-/* Emit at least 1 message per second, even if audit_rate_check is
- * throttling. */
+/**
+ * audit_log_lost - conditionally log lost audit message event
+ * @message: the message stating reason for lost audit message
+ *
+ * Emit at least 1 message per second, even if audit_rate_check is
+ * throttling.
+ * Always increment the lost messages counter.
+*/
 void audit_log_lost(const char *message)
 {
 	static unsigned long	last_msg = 0;
@@ -218,7 +224,6 @@
 		       audit_backlog_limit);
 		audit_panic(message);
 	}
-
 }
 
 static int audit_set_rate_limit(int limit, uid_t loginuid)
@@ -300,8 +305,22 @@
 			remove_wait_queue(&kauditd_wait, &wait);
 		}
 	}
+	return 0;
 }
 
+/**
+ * audit_send_reply - send an audit reply message via netlink
+ * @pid: process id to send reply to
+ * @seq: sequence number
+ * @type: audit message type
+ * @done: done (last) flag
+ * @multi: multi-part message flag
+ * @payload: payload data
+ * @size: payload size
+ *
+ * Allocates an skb, builds the netlink message, and sends it to the pid.
+ * No failure notifications.
+ */
 void audit_send_reply(int pid, int seq, int type, int done, int multi,
 		      void *payload, int size)
 {
@@ -351,6 +370,7 @@
 		break;
 	case AUDIT_USER:
 	case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG:
+	case AUDIT_FIRST_USER_MSG2...AUDIT_LAST_USER_MSG2:
 		if (!cap_raised(eff_cap, CAP_AUDIT_WRITE))
 			err = -EPERM;
 		break;
@@ -376,7 +396,8 @@
 	if (err)
 		return err;
 
-	/* As soon as there's any sign of userspace auditd, start kauditd to talk to it */
+	/* As soon as there's any sign of userspace auditd,
+	 * start kauditd to talk to it */
 	if (!kauditd_task)
 		kauditd_task = kthread_run(kauditd_thread, NULL, "kauditd");
 	if (IS_ERR(kauditd_task)) {
@@ -430,6 +451,7 @@
 		break;
 	case AUDIT_USER:
 	case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG:
+	case AUDIT_FIRST_USER_MSG2...AUDIT_LAST_USER_MSG2:
 		if (!audit_enabled && msg_type != AUDIT_USER_AVC)
 			return 0;
 
@@ -469,9 +491,11 @@
 	return err < 0 ? err : 0;
 }
 
-/* Get message from skb (based on rtnetlink_rcv_skb).  Each message is
+/*
+ * Get message from skb (based on rtnetlink_rcv_skb).  Each message is
  * processed by audit_receive_msg.  Malformed skbs with wrong length are
- * discarded silently.  */
+ * discarded silently.
+ */
 static void audit_receive_skb(struct sk_buff *skb)
 {
 	int		err;
@@ -600,7 +624,10 @@
 	return NULL;
 }
 
-/* Compute a serial number for the audit record.  Audit records are
+/**
+ * audit_serial - compute a serial number for the audit record
+ *
+ * Compute a serial number for the audit record.  Audit records are
  * written to user-space as soon as they are generated, so a complete
  * audit record may be written in several pieces.  The timestamp of the
  * record and this serial number are used by the user-space tools to
@@ -612,8 +639,8 @@
  * audit context (for those records that have a context), and emit them
  * all at syscall exit.  However, this could delay the reporting of
  * significant errors until syscall exit (or never, if the system
- * halts). */
-
+ * halts).
+ */
 unsigned int audit_serial(void)
 {
 	static spinlock_t serial_lock = SPIN_LOCK_UNLOCKED;
@@ -649,6 +676,21 @@
  * will be written at syscall exit.  If there is no associated task, tsk
  * should be NULL. */
 
+/**
+ * audit_log_start - obtain an audit buffer
+ * @ctx: audit_context (may be NULL)
+ * @gfp_mask: type of allocation
+ * @type: audit message type
+ *
+ * Returns audit_buffer pointer on success or NULL on error.
+ *
+ * Obtain an audit buffer.  This routine does locking to obtain the
+ * audit buffer, but then no locking is required for calls to
+ * audit_log_*format.  If the task (ctx) is a task that is currently in a
+ * syscall, then the syscall is marked as auditable and an audit record
+ * will be written at syscall exit.  If there is no associated task, then
+ * task context (ctx) should be NULL.
+ */
 struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
 				     int type)
 {
@@ -661,6 +703,9 @@
 	if (!audit_initialized)
 		return NULL;
 
+	if (unlikely(audit_filter_type(type)))
+		return NULL;
+
 	if (gfp_mask & __GFP_WAIT)
 		reserve = 0;
 	else
@@ -713,6 +758,7 @@
 /**
  * audit_expand - expand skb in the audit buffer
  * @ab: audit_buffer
+ * @extra: space to add at tail of the skb
  *
  * Returns 0 (no space) on failed expansion, or available space if
  * successful.
@@ -729,10 +775,12 @@
 	return skb_tailroom(skb);
 }
 
-/* Format an audit message into the audit buffer.  If there isn't enough
+/*
+ * Format an audit message into the audit buffer.  If there isn't enough
  * room in the audit buffer, more room will be allocated and vsnprint
  * will be called a second time.  Currently, we assume that a printk
- * can't format message larger than 1024 bytes, so we don't either. */
+ * can't format message larger than 1024 bytes, so we don't either.
+ */
 static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
 			      va_list args)
 {
@@ -757,7 +805,8 @@
 		/* The printk buffer is 1024 bytes long, so if we get
 		 * here and AUDIT_BUFSIZ is at least 1024, then we can
 		 * log everything that printk could have logged. */
-		avail = audit_expand(ab, max_t(unsigned, AUDIT_BUFSIZ, 1+len-avail));
+		avail = audit_expand(ab,
+			max_t(unsigned, AUDIT_BUFSIZ, 1+len-avail));
 		if (!avail)
 			goto out;
 		len = vsnprintf(skb->tail, avail, fmt, args2);
@@ -768,8 +817,14 @@
 	return;
 }
 
-/* Format a message into the audit buffer.  All the work is done in
- * audit_log_vformat. */
+/**
+ * audit_log_format - format a message into the audit buffer.
+ * @ab: audit_buffer
+ * @fmt: format string
+ * @...: optional parameters matching @fmt string
+ *
+ * All the work is done in audit_log_vformat.
+ */
 void audit_log_format(struct audit_buffer *ab, const char *fmt, ...)
 {
 	va_list args;
@@ -781,9 +836,18 @@
 	va_end(args);
 }
 
-/* This function will take the passed buf and convert it into a string of
- * ascii hex digits. The new string is placed onto the skb. */
-void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf, 
+/**
+ * audit_log_hex - convert a buffer to hex and append it to the audit skb
+ * @ab: the audit_buffer
+ * @buf: buffer to convert to hex
+ * @len: length of @buf to be converted
+ *
+ * No return value; failure to expand is silently ignored.
+ *
+ * This function will take the passed buf and convert it into a string of
+ * ascii hex digits. The new string is placed onto the skb.
+ */
+void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf,
 		size_t len)
 {
 	int i, avail, new_len;
@@ -812,10 +876,16 @@
 	skb_put(skb, len << 1); /* new string is twice the old string */
 }
 
-/* This code will escape a string that is passed to it if the string
- * contains a control character, unprintable character, double quote mark, 
+/**
+ * audit_log_unstrustedstring - log a string that may contain random characters
+ * @ab: audit_buffer
+ * @string: string to be logged
+ *
+ * This code will escape a string that is passed to it if the string
+ * contains a control character, unprintable character, double quote mark,
  * or a space. Unescaped strings will start and end with a double quote mark.
- * Strings that are escaped are printed in hex (2 digits per char). */
+ * Strings that are escaped are printed in hex (2 digits per char).
+ */
 void audit_log_untrustedstring(struct audit_buffer *ab, const char *string)
 {
 	const unsigned char *p = string;
@@ -854,10 +924,15 @@
 	kfree(path);
 }
 
-/* The netlink_* functions cannot be called inside an irq context, so
- * the audit buffer is places on a queue and a tasklet is scheduled to
+/**
+ * audit_log_end - end one audit record
+ * @ab: the audit_buffer
+ *
+ * The netlink_* functions cannot be called inside an irq context, so
+ * the audit buffer is placed on a queue and a tasklet is scheduled to
  * remove them from the queue outside the irq context.  May be called in
- * any context. */
+ * any context.
+ */
 void audit_log_end(struct audit_buffer *ab)
 {
 	if (!ab)
@@ -878,9 +953,18 @@
 	audit_buffer_free(ab);
 }
 
-/* Log an audit record.  This is a convenience function that calls
- * audit_log_start, audit_log_vformat, and audit_log_end.  It may be
- * called in any context. */
+/**
+ * audit_log - Log an audit record
+ * @ctx: audit context
+ * @gfp_mask: type of allocation
+ * @type: audit message type
+ * @fmt: format string to use
+ * @...: variable parameters matching the format string
+ *
+ * This is a convenience function that calls audit_log_start,
+ * audit_log_vformat, and audit_log_end.  It may be called
+ * in any context.
+ */
 void audit_log(struct audit_context *ctx, gfp_t gfp_mask, int type, 
 	       const char *fmt, ...)
 {
diff -urN oldtree/kernel/audit.h newtree/kernel/audit.h
--- oldtree/kernel/audit.h	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/audit.h	2006-02-13 19:20:01.262320400 -0500
@@ -0,0 +1,70 @@
+/* audit -- definition of audit_context structure and supporting types 
+ *
+ * Copyright 2003-2004 Red Hat, Inc.
+ * Copyright 2005 Hewlett-Packard Development Company, L.P.
+ * Copyright 2005 IBM Corporation
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/fs.h>
+#include <linux/audit.h>
+
+/* 0 = no checking
+   1 = put_count checking
+   2 = verbose put_count checking
+*/
+#define AUDIT_DEBUG 0
+
+/* At task start time, the audit_state is set in the audit_context using
+   a per-task filter.  At syscall entry, the audit_state is augmented by
+   the syscall filter. */
+enum audit_state {
+	AUDIT_DISABLED,		/* Do not create per-task audit_context.
+				 * No syscall-specific audit records can
+				 * be generated. */
+	AUDIT_SETUP_CONTEXT,	/* Create the per-task audit_context,
+				 * but don't necessarily fill it in at
+				 * syscall entry time (i.e., filter
+				 * instead). */
+	AUDIT_BUILD_CONTEXT,	/* Create the per-task audit_context,
+				 * and always fill it in at syscall
+				 * entry time.  This makes a full
+				 * syscall record available if some
+				 * other part of the kernel decides it
+				 * should be recorded. */
+	AUDIT_RECORD_CONTEXT	/* Create the per-task audit_context,
+				 * always fill it in at syscall entry
+				 * time, and always write out the audit
+				 * record at syscall exit time.  */
+};
+
+/* Rule lists */
+struct audit_entry {
+	struct list_head  list;
+	struct rcu_head   rcu;
+	struct audit_rule rule;
+};
+
+
+extern int audit_pid;
+extern int audit_comparator(const u32 left, const u32 op, const u32 right);
+
+extern void		    audit_send_reply(int pid, int seq, int type,
+					     int done, int multi,
+					     void *payload, int size);
+extern void		    audit_log_lost(const char *message);
+extern void		    audit_panic(const char *message);
+extern struct semaphore audit_netlink_sem;
diff -urN oldtree/kernel/auditfilter.c newtree/kernel/auditfilter.c
--- oldtree/kernel/auditfilter.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/auditfilter.c	2006-02-13 19:20:01.262320400 -0500
@@ -0,0 +1,375 @@
+/* auditfilter.c -- filtering of audit events
+ *
+ * Copyright 2003-2004 Red Hat, Inc.
+ * Copyright 2005 Hewlett-Packard Development Company, L.P.
+ * Copyright 2005 IBM Corporation
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/audit.h>
+#include <linux/kthread.h>
+#include <linux/netlink.h>
+#include "audit.h"
+
+/* There are three lists of rules -- one to search at task creation
+ * time, one to search at syscall entry time, and another to search at
+ * syscall exit time. */
+struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
+	LIST_HEAD_INIT(audit_filter_list[0]),
+	LIST_HEAD_INIT(audit_filter_list[1]),
+	LIST_HEAD_INIT(audit_filter_list[2]),
+	LIST_HEAD_INIT(audit_filter_list[3]),
+	LIST_HEAD_INIT(audit_filter_list[4]),
+	LIST_HEAD_INIT(audit_filter_list[5]),
+#if AUDIT_NR_FILTERS != 6
+#error Fix audit_filter_list initialiser
+#endif
+};
+
+/* Copy rule from user-space to kernel-space.  Called from 
+ * audit_add_rule during AUDIT_ADD. */
+static inline int audit_copy_rule(struct audit_rule *d, struct audit_rule *s)
+{
+	int i;
+
+	if (s->action != AUDIT_NEVER
+	    && s->action != AUDIT_POSSIBLE
+	    && s->action != AUDIT_ALWAYS)
+		return -1;
+	if (s->field_count < 0 || s->field_count > AUDIT_MAX_FIELDS)
+		return -1;
+	if ((s->flags & ~AUDIT_FILTER_PREPEND) >= AUDIT_NR_FILTERS)
+		return -1;
+
+	d->flags	= s->flags;
+	d->action	= s->action;
+	d->field_count	= s->field_count;
+	for (i = 0; i < d->field_count; i++) {
+		d->fields[i] = s->fields[i];
+		d->values[i] = s->values[i];
+	}
+	for (i = 0; i < AUDIT_BITMASK_SIZE; i++) d->mask[i] = s->mask[i];
+	return 0;
+}
+
+/* Check to see if two rules are identical.  It is called from
+ * audit_add_rule during AUDIT_ADD and 
+ * audit_del_rule during AUDIT_DEL. */
+static int audit_compare_rule(struct audit_rule *a, struct audit_rule *b)
+{
+	int i;
+
+	if (a->flags != b->flags)
+		return 1;
+
+	if (a->action != b->action)
+		return 1;
+
+	if (a->field_count != b->field_count)
+		return 1;
+
+	for (i = 0; i < a->field_count; i++) {
+		if (a->fields[i] != b->fields[i]
+		    || a->values[i] != b->values[i])
+			return 1;
+	}
+
+	for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
+		if (a->mask[i] != b->mask[i])
+			return 1;
+
+	return 0;
+}
+
+/* Note that audit_add_rule and audit_del_rule are called via
+ * audit_receive() in audit.c, and are protected by
+ * audit_netlink_sem. */
+static inline int audit_add_rule(struct audit_rule *rule,
+				  struct list_head *list)
+{
+	struct audit_entry  *entry;
+	int i;
+
+	/* Do not use the _rcu iterator here, since this is the only
+	 * addition routine. */
+	list_for_each_entry(entry, list, list) {
+		if (!audit_compare_rule(rule, &entry->rule))
+			return -EEXIST;
+	}
+
+	for (i = 0; i < rule->field_count; i++) {
+		if (rule->fields[i] & AUDIT_UNUSED_BITS)
+			return -EINVAL;
+		if ( rule->fields[i] & AUDIT_NEGATE)
+			rule->fields[i] |= AUDIT_NOT_EQUAL;
+		else if ( (rule->fields[i] & AUDIT_OPERATORS) == 0 )
+			rule->fields[i] |= AUDIT_EQUAL;
+		rule->fields[i] &= ~AUDIT_NEGATE;
+	}
+
+	if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL)))
+		return -ENOMEM;
+	if (audit_copy_rule(&entry->rule, rule)) {
+		kfree(entry);
+		return -EINVAL;
+	}
+
+	if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
+		entry->rule.flags &= ~AUDIT_FILTER_PREPEND;
+		list_add_rcu(&entry->list, list);
+	} else {
+		list_add_tail_rcu(&entry->list, list);
+	}
+
+	return 0;
+}
+
+static inline void audit_free_rule(struct rcu_head *head)
+{
+	struct audit_entry *e = container_of(head, struct audit_entry, rcu);
+	kfree(e);
+}
+
+/* Note that audit_add_rule and audit_del_rule are called via
+ * audit_receive() in audit.c, and are protected by
+ * audit_netlink_sem. */
+static inline int audit_del_rule(struct audit_rule *rule,
+				 struct list_head *list)
+{
+	struct audit_entry  *e;
+
+	/* Do not use the _rcu iterator here, since this is the only
+	 * deletion routine. */
+	list_for_each_entry(e, list, list) {
+		if (!audit_compare_rule(rule, &e->rule)) {
+			list_del_rcu(&e->list);
+			call_rcu(&e->rcu, audit_free_rule);
+			return 0;
+		}
+	}
+	return -ENOENT;		/* No matching rule */
+}
+
+static int audit_list_rules(void *_dest)
+{
+	int pid, seq;
+	int *dest = _dest;
+	struct audit_entry *entry;
+	int i;
+
+	pid = dest[0];
+	seq = dest[1];
+	kfree(dest);
+
+	down(&audit_netlink_sem);
+
+	/* The *_rcu iterators not needed here because we are
+	   always called with audit_netlink_sem held. */
+	for (i=0; i<AUDIT_NR_FILTERS; i++) {
+		list_for_each_entry(entry, &audit_filter_list[i], list)
+			audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
+					 &entry->rule, sizeof(entry->rule));
+	}
+	audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
+	
+	up(&audit_netlink_sem);
+	return 0;
+}
+
+/**
+ * audit_receive_filter - apply all rules to the specified message type
+ * @type: audit message type
+ * @pid: target pid for netlink audit messages
+ * @uid: target uid for netlink audit messages
+ * @seq: netlink audit message sequence (serial) number
+ * @data: payload data
+ * @loginuid: loginuid of sender
+ */
+int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
+							uid_t loginuid)
+{
+	struct task_struct *tsk;
+	int *dest;
+	int		   err = 0;
+	unsigned listnr;
+
+	switch (type) {
+	case AUDIT_LIST:
+		/* We can't just spew out the rules here because we might fill
+		 * the available socket buffer space and deadlock waiting for
+		 * auditctl to read from it... which isn't ever going to
+		 * happen if we're actually running in the context of auditctl
+		 * trying to _send_ the stuff */
+		 
+		dest = kmalloc(2 * sizeof(int), GFP_KERNEL);
+		if (!dest)
+			return -ENOMEM;
+		dest[0] = pid;
+		dest[1] = seq;
+
+		tsk = kthread_run(audit_list_rules, dest, "audit_list_rules");
+		if (IS_ERR(tsk)) {
+			kfree(dest);
+			err = PTR_ERR(tsk);
+		}
+		break;
+	case AUDIT_ADD:
+		listnr = ((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND;
+		switch(listnr) {
+		default:
+			return -EINVAL;
+
+		case AUDIT_FILTER_USER:
+		case AUDIT_FILTER_TYPE:
+#ifdef CONFIG_AUDITSYSCALL
+		case AUDIT_FILTER_ENTRY:
+		case AUDIT_FILTER_EXIT:
+		case AUDIT_FILTER_TASK:
+#endif
+			;
+		}
+		err = audit_add_rule(data, &audit_filter_list[listnr]);
+		if (!err)
+			audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+				  "auid=%u added an audit rule\n", loginuid);
+		break;
+	case AUDIT_DEL:
+		listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND;
+		if (listnr >= AUDIT_NR_FILTERS)
+			return -EINVAL;
+
+		err = audit_del_rule(data, &audit_filter_list[listnr]);
+		if (!err)
+			audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+				  "auid=%u removed an audit rule\n", loginuid);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return err;
+}
+
+int audit_comparator(const u32 left, const u32 op, const u32 right)
+{
+	switch (op) {
+	case AUDIT_EQUAL:
+		return (left == right);
+	case AUDIT_NOT_EQUAL:
+		return (left != right);
+	case AUDIT_LESS_THAN:
+		return (left < right);
+	case AUDIT_LESS_THAN_OR_EQUAL:
+		return (left <= right);
+	case AUDIT_GREATER_THAN:
+		return (left > right);
+	case AUDIT_GREATER_THAN_OR_EQUAL:
+		return (left >= right);
+	default:
+		return -EINVAL;
+	}
+}
+
+
+
+static int audit_filter_user_rules(struct netlink_skb_parms *cb,
+				   struct audit_rule *rule,
+				   enum audit_state *state)
+{
+	int i;
+
+	for (i = 0; i < rule->field_count; i++) {
+		u32 field  = rule->fields[i] & ~AUDIT_OPERATORS;
+		u32 op  = rule->fields[i] & AUDIT_OPERATORS;
+		u32 value  = rule->values[i];
+		int result = 0;
+
+		switch (field) {
+		case AUDIT_PID:
+			result = audit_comparator(cb->creds.pid, op, value);
+			break;
+		case AUDIT_UID:
+			result = audit_comparator(cb->creds.uid, op, value);
+			break;
+		case AUDIT_GID:
+			result = audit_comparator(cb->creds.gid, op, value);
+			break;
+		case AUDIT_LOGINUID:
+			result = audit_comparator(cb->loginuid, op, value);
+			break;
+		}
+
+		if (!result)
+			return 0;
+	}
+	switch (rule->action) {
+	case AUDIT_NEVER:    *state = AUDIT_DISABLED;	    break;
+	case AUDIT_POSSIBLE: *state = AUDIT_BUILD_CONTEXT;  break;
+	case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break;
+	}
+	return 1;
+}
+
+int audit_filter_user(struct netlink_skb_parms *cb, int type)
+{
+	struct audit_entry *e;
+	enum audit_state   state;
+	int ret = 1;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) {
+		if (audit_filter_user_rules(cb, &e->rule, &state)) {
+			if (state == AUDIT_DISABLED)
+				ret = 0;
+			break;
+		}
+	}
+	rcu_read_unlock();
+
+	return ret; /* Audit by default */
+}
+
+int audit_filter_type(int type)
+{
+	struct audit_entry *e;
+	int result = 0;
+	
+	rcu_read_lock();
+	if (list_empty(&audit_filter_list[AUDIT_FILTER_TYPE]))
+		goto unlock_and_return;
+
+	list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE],
+				list) {
+		struct audit_rule *rule = &e->rule;
+		int i;
+		for (i = 0; i < rule->field_count; i++) {
+			u32 field  = rule->fields[i] & ~AUDIT_OPERATORS;
+			u32 op  = rule->fields[i] & AUDIT_OPERATORS;
+			u32 value  = rule->values[i];
+			if ( field == AUDIT_MSGTYPE ) {
+				result = audit_comparator(type, op, value); 
+				if (!result)
+					break;
+			}
+		}
+		if (result)
+			goto unlock_and_return;
+	}
+unlock_and_return:
+	rcu_read_unlock();
+	return result;
+}
diff -urN oldtree/kernel/auditsc.c newtree/kernel/auditsc.c
--- oldtree/kernel/auditsc.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/kernel/auditsc.c	2006-02-13 19:20:01.264320096 -0500
@@ -2,6 +2,8 @@
  * Handles all system-call specific auditing features.
  *
  * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina.
+ * Copyright 2005 Hewlett-Packard Development Company, L.P.
+ * Copyright (C) 2005 IBM Corporation
  * All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify
@@ -27,11 +29,21 @@
  * this file -- see entry.S) is based on a GPL'd patch written by
  * okir@suse.de and Copyright 2003 SuSE Linux AG.
  *
+ * The support of additional filter rules compares (>, <, >=, <=) was
+ * added by Dustin Kirkland <dustin.kirkland@us.ibm.com>, 2005.
+ *
+ * Modified by Amy Griffis <amy.griffis@hp.com> to collect additional
+ * filesystem information.
+ *
+ * Subject and object context labeling support added by <danjones@us.ibm.com>
+ * and <dustin.kirkland@us.ibm.com> for LSPP certification compliance.
  */
 
 #include <linux/init.h>
 #include <asm/atomic.h>
 #include <asm/types.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/mount.h>
@@ -39,16 +51,15 @@
 #include <linux/audit.h>
 #include <linux/personality.h>
 #include <linux/time.h>
-#include <linux/kthread.h>
 #include <linux/netlink.h>
 #include <linux/compiler.h>
 #include <asm/unistd.h>
+#include <linux/security.h>
+#include <linux/list.h>
+
+#include "audit.h"
 
-/* 0 = no checking
-   1 = put_count checking
-   2 = verbose put_count checking
-*/
-#define AUDIT_DEBUG 0
+extern struct list_head audit_filter_list[];
 
 /* No syscall auditing will take place unless audit_enabled != 0. */
 extern int audit_enabled;
@@ -62,29 +73,6 @@
  * path_lookup. */
 #define AUDIT_NAMES_RESERVED 7
 
-/* At task start time, the audit_state is set in the audit_context using
-   a per-task filter.  At syscall entry, the audit_state is augmented by
-   the syscall filter. */
-enum audit_state {
-	AUDIT_DISABLED,		/* Do not create per-task audit_context.
-				 * No syscall-specific audit records can
-				 * be generated. */
-	AUDIT_SETUP_CONTEXT,	/* Create the per-task audit_context,
-				 * but don't necessarily fill it in at
-				 * syscall entry time (i.e., filter
-				 * instead). */
-	AUDIT_BUILD_CONTEXT,	/* Create the per-task audit_context,
-				 * and always fill it in at syscall
-				 * entry time.  This makes a full
-				 * syscall record available if some
-				 * other part of the kernel decides it
-				 * should be recorded. */
-	AUDIT_RECORD_CONTEXT	/* Create the per-task audit_context,
-				 * always fill it in at syscall entry
-				 * time, and always write out the audit
-				 * record at syscall exit time.  */
-};
-
 /* When fs/namei.c:getname() is called, we store the pointer in name and
  * we don't let putname() free it (instead we free all of the saved
  * pointers at syscall exit time).
@@ -93,12 +81,13 @@
 struct audit_names {
 	const char	*name;
 	unsigned long	ino;
+	unsigned long	pino;
 	dev_t		dev;
 	umode_t		mode;
 	uid_t		uid;
 	gid_t		gid;
 	dev_t		rdev;
-	unsigned	flags;
+	char		*ctx;
 };
 
 struct audit_aux_data {
@@ -115,6 +104,7 @@
 	uid_t			uid;
 	gid_t			gid;
 	mode_t			mode;
+	char 			*ctx;
 };
 
 struct audit_aux_data_socketcall {
@@ -167,223 +157,6 @@
 #endif
 };
 
-				/* Public API */
-/* There are three lists of rules -- one to search at task creation
- * time, one to search at syscall entry time, and another to search at
- * syscall exit time. */
-static struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
-	LIST_HEAD_INIT(audit_filter_list[0]),
-	LIST_HEAD_INIT(audit_filter_list[1]),
-	LIST_HEAD_INIT(audit_filter_list[2]),
-	LIST_HEAD_INIT(audit_filter_list[3]),
-	LIST_HEAD_INIT(audit_filter_list[4]),
-#if AUDIT_NR_FILTERS != 5
-#error Fix audit_filter_list initialiser
-#endif
-};
-
-struct audit_entry {
-	struct list_head  list;
-	struct rcu_head   rcu;
-	struct audit_rule rule;
-};
-
-extern int audit_pid;
-
-/* Copy rule from user-space to kernel-space.  Called from 
- * audit_add_rule during AUDIT_ADD. */
-static inline int audit_copy_rule(struct audit_rule *d, struct audit_rule *s)
-{
-	int i;
-
-	if (s->action != AUDIT_NEVER
-	    && s->action != AUDIT_POSSIBLE
-	    && s->action != AUDIT_ALWAYS)
-		return -1;
-	if (s->field_count < 0 || s->field_count > AUDIT_MAX_FIELDS)
-		return -1;
-	if ((s->flags & ~AUDIT_FILTER_PREPEND) >= AUDIT_NR_FILTERS)
-		return -1;
-
-	d->flags	= s->flags;
-	d->action	= s->action;
-	d->field_count	= s->field_count;
-	for (i = 0; i < d->field_count; i++) {
-		d->fields[i] = s->fields[i];
-		d->values[i] = s->values[i];
-	}
-	for (i = 0; i < AUDIT_BITMASK_SIZE; i++) d->mask[i] = s->mask[i];
-	return 0;
-}
-
-/* Check to see if two rules are identical.  It is called from
- * audit_add_rule during AUDIT_ADD and 
- * audit_del_rule during AUDIT_DEL. */
-static inline int audit_compare_rule(struct audit_rule *a, struct audit_rule *b)
-{
-	int i;
-
-	if (a->flags != b->flags)
-		return 1;
-
-	if (a->action != b->action)
-		return 1;
-
-	if (a->field_count != b->field_count)
-		return 1;
-
-	for (i = 0; i < a->field_count; i++) {
-		if (a->fields[i] != b->fields[i]
-		    || a->values[i] != b->values[i])
-			return 1;
-	}
-
-	for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
-		if (a->mask[i] != b->mask[i])
-			return 1;
-
-	return 0;
-}
-
-/* Note that audit_add_rule and audit_del_rule are called via
- * audit_receive() in audit.c, and are protected by
- * audit_netlink_sem. */
-static inline int audit_add_rule(struct audit_rule *rule,
-				  struct list_head *list)
-{
-	struct audit_entry  *entry;
-
-	/* Do not use the _rcu iterator here, since this is the only
-	 * addition routine. */
-	list_for_each_entry(entry, list, list) {
-		if (!audit_compare_rule(rule, &entry->rule)) {
-			return -EEXIST;
-		}
-	}
-
-	if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL)))
-		return -ENOMEM;
-	if (audit_copy_rule(&entry->rule, rule)) {
-		kfree(entry);
-		return -EINVAL;
-	}
-
-	if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
-		entry->rule.flags &= ~AUDIT_FILTER_PREPEND;
-		list_add_rcu(&entry->list, list);
-	} else {
-		list_add_tail_rcu(&entry->list, list);
-	}
-
-	return 0;
-}
-
-static inline void audit_free_rule(struct rcu_head *head)
-{
-	struct audit_entry *e = container_of(head, struct audit_entry, rcu);
-	kfree(e);
-}
-
-/* Note that audit_add_rule and audit_del_rule are called via
- * audit_receive() in audit.c, and are protected by
- * audit_netlink_sem. */
-static inline int audit_del_rule(struct audit_rule *rule,
-				 struct list_head *list)
-{
-	struct audit_entry  *e;
-
-	/* Do not use the _rcu iterator here, since this is the only
-	 * deletion routine. */
-	list_for_each_entry(e, list, list) {
-		if (!audit_compare_rule(rule, &e->rule)) {
-			list_del_rcu(&e->list);
-			call_rcu(&e->rcu, audit_free_rule);
-			return 0;
-		}
-	}
-	return -ENOENT;		/* No matching rule */
-}
-
-static int audit_list_rules(void *_dest)
-{
-	int pid, seq;
-	int *dest = _dest;
-	struct audit_entry *entry;
-	int i;
-
-	pid = dest[0];
-	seq = dest[1];
-	kfree(dest);
-
-	down(&audit_netlink_sem);
-
-	/* The *_rcu iterators not needed here because we are
-	   always called with audit_netlink_sem held. */
-	for (i=0; i<AUDIT_NR_FILTERS; i++) {
-		list_for_each_entry(entry, &audit_filter_list[i], list)
-			audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
-					 &entry->rule, sizeof(entry->rule));
-	}
-	audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
-	
-	up(&audit_netlink_sem);
-	return 0;
-}
-
-int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
-							uid_t loginuid)
-{
-	struct task_struct *tsk;
-	int *dest;
-	int		   err = 0;
-	unsigned listnr;
-
-	switch (type) {
-	case AUDIT_LIST:
-		/* We can't just spew out the rules here because we might fill
-		 * the available socket buffer space and deadlock waiting for
-		 * auditctl to read from it... which isn't ever going to
-		 * happen if we're actually running in the context of auditctl
-		 * trying to _send_ the stuff */
-		 
-		dest = kmalloc(2 * sizeof(int), GFP_KERNEL);
-		if (!dest)
-			return -ENOMEM;
-		dest[0] = pid;
-		dest[1] = seq;
-
-		tsk = kthread_run(audit_list_rules, dest, "audit_list_rules");
-		if (IS_ERR(tsk)) {
-			kfree(dest);
-			err = PTR_ERR(tsk);
-		}
-		break;
-	case AUDIT_ADD:
-		listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND;
-		if (listnr >= AUDIT_NR_FILTERS)
-			return -EINVAL;
-
-		err = audit_add_rule(data, &audit_filter_list[listnr]);
-		if (!err)
-			audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
-				  "auid=%u added an audit rule\n", loginuid);
-		break;
-	case AUDIT_DEL:
-		listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND;
-		if (listnr >= AUDIT_NR_FILTERS)
-			return -EINVAL;
-
-		err = audit_del_rule(data, &audit_filter_list[listnr]);
-		if (!err)
-			audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
-				  "auid=%u removed an audit rule\n", loginuid);
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	return err;
-}
 
 /* Compare a task_struct with an audit_rule.  Return 1 on match, 0
  * otherwise. */
@@ -395,62 +168,63 @@
 	int i, j;
 
 	for (i = 0; i < rule->field_count; i++) {
-		u32 field  = rule->fields[i] & ~AUDIT_NEGATE;
+		u32 field  = rule->fields[i] & ~AUDIT_OPERATORS;
+		u32 op  = rule->fields[i] & AUDIT_OPERATORS;
 		u32 value  = rule->values[i];
 		int result = 0;
 
 		switch (field) {
 		case AUDIT_PID:
-			result = (tsk->pid == value);
+			result = audit_comparator(tsk->pid, op, value);
 			break;
 		case AUDIT_UID:
-			result = (tsk->uid == value);
+			result = audit_comparator(tsk->uid, op, value);
 			break;
 		case AUDIT_EUID:
-			result = (tsk->euid == value);
+			result = audit_comparator(tsk->euid, op, value);
 			break;
 		case AUDIT_SUID:
-			result = (tsk->suid == value);
+			result = audit_comparator(tsk->suid, op, value);
 			break;
 		case AUDIT_FSUID:
-			result = (tsk->fsuid == value);
+			result = audit_comparator(tsk->fsuid, op, value);
 			break;
 		case AUDIT_GID:
-			result = (tsk->gid == value);
+			result = audit_comparator(tsk->gid, op, value);
 			break;
 		case AUDIT_EGID:
-			result = (tsk->egid == value);
+			result = audit_comparator(tsk->egid, op, value);
 			break;
 		case AUDIT_SGID:
-			result = (tsk->sgid == value);
+			result = audit_comparator(tsk->sgid, op, value);
 			break;
 		case AUDIT_FSGID:
-			result = (tsk->fsgid == value);
+			result = audit_comparator(tsk->fsgid, op, value);
 			break;
 		case AUDIT_PERS:
-			result = (tsk->personality == value);
+			result = audit_comparator(tsk->personality, op, value);
 			break;
 		case AUDIT_ARCH:
-			if (ctx) 
-				result = (ctx->arch == value);
+ 			if (ctx)
+				result = audit_comparator(ctx->arch, op, value);
 			break;
 
 		case AUDIT_EXIT:
 			if (ctx && ctx->return_valid)
-				result = (ctx->return_code == value);
+				result = audit_comparator(ctx->return_code, op, value);
 			break;
 		case AUDIT_SUCCESS:
 			if (ctx && ctx->return_valid) {
 				if (value)
-					result = (ctx->return_valid == AUDITSC_SUCCESS);
+					result = audit_comparator(ctx->return_valid, op, AUDITSC_SUCCESS);
 				else
-					result = (ctx->return_valid == AUDITSC_FAILURE);
+					result = audit_comparator(ctx->return_valid, op, AUDITSC_FAILURE);
 			}
 			break;
 		case AUDIT_DEVMAJOR:
 			if (ctx) {
 				for (j = 0; j < ctx->name_count; j++) {
-					if (MAJOR(ctx->names[j].dev)==value) {
+					if (audit_comparator(MAJOR(ctx->names[j].dev),	op, value)) {
 						++result;
 						break;
 					}
@@ -460,7 +234,7 @@
 		case AUDIT_DEVMINOR:
 			if (ctx) {
 				for (j = 0; j < ctx->name_count; j++) {
-					if (MINOR(ctx->names[j].dev)==value) {
+					if (audit_comparator(MINOR(ctx->names[j].dev), op, value)) {
 						++result;
 						break;
 					}
@@ -470,7 +244,8 @@
 		case AUDIT_INODE:
 			if (ctx) {
 				for (j = 0; j < ctx->name_count; j++) {
-					if (ctx->names[j].ino == value) {
+					if (audit_comparator(ctx->names[j].ino, op, value) ||
+					    audit_comparator(ctx->names[j].pino, op, value)) {
 						++result;
 						break;
 					}
@@ -480,19 +255,17 @@
 		case AUDIT_LOGINUID:
 			result = 0;
 			if (ctx)
-				result = (ctx->loginuid == value);
+				result = audit_comparator(ctx->loginuid, op, value);
 			break;
 		case AUDIT_ARG0:
 		case AUDIT_ARG1:
 		case AUDIT_ARG2:
 		case AUDIT_ARG3:
 			if (ctx)
-				result = (ctx->argv[field-AUDIT_ARG0]==value);
+				result = audit_comparator(ctx->argv[field-AUDIT_ARG0], op, value);
 			break;
 		}
 
-		if (rule->fields[i] & AUDIT_NEGATE)
-			result = !result;
 		if (!result)
 			return 0;
 	}
@@ -527,7 +300,7 @@
 /* At syscall entry and exit time, this filter is called if the
  * audit_state is not low enough that auditing cannot take place, but is
  * also not high enough that we already know we have to write an audit
- * record (i.e., the state is AUDIT_SETUP_CONTEXT or  AUDIT_BUILD_CONTEXT).
+ * record (i.e., the state is AUDIT_SETUP_CONTEXT or AUDIT_BUILD_CONTEXT).
  */
 static enum audit_state audit_filter_syscall(struct task_struct *tsk,
 					     struct audit_context *ctx,
@@ -541,77 +314,19 @@
 
 	rcu_read_lock();
 	if (!list_empty(list)) {
-		    int word = AUDIT_WORD(ctx->major);
-		    int bit  = AUDIT_BIT(ctx->major);
-
-		    list_for_each_entry_rcu(e, list, list) {
-			    if ((e->rule.mask[word] & bit) == bit
-				&& audit_filter_rules(tsk, &e->rule, ctx, &state)) {
-				    rcu_read_unlock();
-				    return state;
-			    }
-		    }
-	}
-	rcu_read_unlock();
-	return AUDIT_BUILD_CONTEXT;
-}
-
-static int audit_filter_user_rules(struct netlink_skb_parms *cb,
-			      struct audit_rule *rule,
-			      enum audit_state *state)
-{
-	int i;
-
-	for (i = 0; i < rule->field_count; i++) {
-		u32 field  = rule->fields[i] & ~AUDIT_NEGATE;
-		u32 value  = rule->values[i];
-		int result = 0;
-
-		switch (field) {
-		case AUDIT_PID:
-			result = (cb->creds.pid == value);
-			break;
-		case AUDIT_UID:
-			result = (cb->creds.uid == value);
-			break;
-		case AUDIT_GID:
-			result = (cb->creds.gid == value);
-			break;
-		case AUDIT_LOGINUID:
-			result = (cb->loginuid == value);
-			break;
-		}
-
-		if (rule->fields[i] & AUDIT_NEGATE)
-			result = !result;
-		if (!result)
-			return 0;
-	}
-	switch (rule->action) {
-	case AUDIT_NEVER:    *state = AUDIT_DISABLED;	    break;
-	case AUDIT_POSSIBLE: *state = AUDIT_BUILD_CONTEXT;  break;
-	case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break;
-	}
-	return 1;
-}
-
-int audit_filter_user(struct netlink_skb_parms *cb, int type)
-{
-	struct audit_entry *e;
-	enum audit_state   state;
-	int ret = 1;
+		int word = AUDIT_WORD(ctx->major);
+		int bit  = AUDIT_BIT(ctx->major);
 
-	rcu_read_lock();
-	list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) {
-		if (audit_filter_user_rules(cb, &e->rule, &state)) {
-			if (state == AUDIT_DISABLED)
-				ret = 0;
-			break;
+		list_for_each_entry_rcu(e, list, list) {
+			if ((e->rule.mask[word] & bit) == bit
+					&& audit_filter_rules(tsk, &e->rule, ctx, &state)) {
+				rcu_read_unlock();
+				return state;
+			}
 		}
 	}
 	rcu_read_unlock();
-
-	return ret; /* Audit by default */
+	return AUDIT_BUILD_CONTEXT;
 }
 
 /* This should be called with task_lock() held. */
@@ -654,17 +369,19 @@
 #if AUDIT_DEBUG == 2
 	if (context->auditable
 	    ||context->put_count + context->ino_count != context->name_count) {
-		printk(KERN_ERR "audit.c:%d(:%d): major=%d in_syscall=%d"
+		printk(KERN_ERR "%s:%d(:%d): major=%d in_syscall=%d"
 		       " name_count=%d put_count=%d"
 		       " ino_count=%d [NOT freeing]\n",
-		       __LINE__,
+		       __FILE__, __LINE__,
 		       context->serial, context->major, context->in_syscall,
 		       context->name_count, context->put_count,
 		       context->ino_count);
-		for (i = 0; i < context->name_count; i++)
+		for (i = 0; i < context->name_count; i++) {
 			printk(KERN_ERR "names[%d] = %p = %s\n", i,
 			       context->names[i].name,
-			       context->names[i].name);
+			       context->names[i].name ?: "(null)");
+			kfree(context->names[i].ctx);
+		}
 		dump_stack();
 		return;
 	}
@@ -696,6 +413,12 @@
 			dput(axi->dentry);
 			mntput(axi->mnt);
 		}
+		if ( aux->type == AUDIT_IPC ) {
+			struct audit_aux_data_ipcctl *axi = (void *)aux;
+			if (axi->ctx)
+				kfree(axi->ctx);
+		}
+
 		context->aux = aux->next;
 		kfree(aux);
 	}
@@ -721,10 +444,15 @@
 	return context;
 }
 
-/* Filter on the task information and allocate a per-task audit context
+/**
+ * audit_alloc - allocate an audit context block for a task
+ * @tsk: task
+ *
+ * Filter on the task information and allocate a per-task audit context
  * if necessary.  Doing so turns on system call auditing for the
  * specified task.  This is called from copy_process, so no lock is
- * needed. */
+ * needed.
+ */
 int audit_alloc(struct task_struct *tsk)
 {
 	struct audit_context *context;
@@ -775,6 +503,36 @@
 		printk(KERN_ERR "audit: freed %d contexts\n", count);
 }
 
+static void audit_log_task_context(struct audit_buffer *ab)
+{
+	char *ctx = NULL;
+	ssize_t len = 0;
+
+	len = security_getprocattr(current, "current", NULL, 0);
+	if (len < 0) {
+		if (len != -EINVAL)
+			goto error_path;
+		return;
+	}
+
+	ctx = kmalloc(len, GFP_KERNEL);
+	if (!ctx)
+		goto error_path;
+
+	len = security_getprocattr(current, "current", ctx, len);
+	if (len < 0 )
+		goto error_path;
+
+	audit_log_format(ab, " subj=%s", ctx);
+	return;
+
+error_path:
+	if (ctx)
+		kfree(ctx);
+	audit_panic("error in audit_log_task_context");
+	return;
+}
+
 static void audit_log_task_info(struct audit_buffer *ab)
 {
 	char name[sizeof(current->comm)];
@@ -801,6 +559,7 @@
 		vma = vma->vm_next;
 	}
 	up_read(&mm->mmap_sem);
+	audit_log_task_context(ab);
 }
 
 static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
@@ -849,8 +608,8 @@
 		case AUDIT_IPC: {
 			struct audit_aux_data_ipcctl *axi = (void *)aux;
 			audit_log_format(ab, 
-					 " qbytes=%lx iuid=%u igid=%u mode=%x",
-					 axi->qbytes, axi->uid, axi->gid, axi->mode);
+					 " qbytes=%lx iuid=%u igid=%u mode=%x obj=%s",
+					 axi->qbytes, axi->uid, axi->gid, axi->mode, axi->ctx);
 			break; }
 
 		case AUDIT_SOCKETCALL: {
@@ -885,34 +644,50 @@
 		}
 	}
 	for (i = 0; i < context->name_count; i++) {
+		unsigned long ino  = context->names[i].ino;
+		unsigned long pino = context->names[i].pino;
+
 		ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH);
 		if (!ab)
 			continue; /* audit_panic has been called */
 
 		audit_log_format(ab, "item=%d", i);
-		if (context->names[i].name) {
-			audit_log_format(ab, " name=");
+
+		audit_log_format(ab, " name=");
+		if (context->names[i].name)
 			audit_log_untrustedstring(ab, context->names[i].name);
-		}
-		audit_log_format(ab, " flags=%x\n", context->names[i].flags);
-			 
-		if (context->names[i].ino != (unsigned long)-1)
-			audit_log_format(ab, " inode=%lu dev=%02x:%02x mode=%#o"
-					     " ouid=%u ogid=%u rdev=%02x:%02x",
-					 context->names[i].ino,
-					 MAJOR(context->names[i].dev),
-					 MINOR(context->names[i].dev),
-					 context->names[i].mode,
-					 context->names[i].uid,
-					 context->names[i].gid,
-					 MAJOR(context->names[i].rdev),
+		else
+			audit_log_format(ab, "(null)");
+
+		if (pino != (unsigned long)-1)
+			audit_log_format(ab, " parent=%lu",  pino);
+		if (ino != (unsigned long)-1)
+			audit_log_format(ab, " inode=%lu",  ino);
+		if ((pino != (unsigned long)-1) || (ino != (unsigned long)-1))
+			audit_log_format(ab, " dev=%02x:%02x mode=%#o" 
+					 " ouid=%u ogid=%u rdev=%02x:%02x", 
+					 MAJOR(context->names[i].dev), 
+					 MINOR(context->names[i].dev), 
+					 context->names[i].mode, 
+					 context->names[i].uid, 
+					 context->names[i].gid, 
+					 MAJOR(context->names[i].rdev), 
 					 MINOR(context->names[i].rdev));
+		if (context->names[i].ctx) {
+			audit_log_format(ab, " obj=%s",
+					context->names[i].ctx);
+		}
+
 		audit_log_end(ab);
 	}
 }
 
-/* Free a per-task audit context.  Called from copy_process and
- * __put_task_struct. */
+/**
+ * audit_free - free a per-task audit context
+ * @tsk: task whose audit context block to free
+ *
+ * Called from copy_process and __put_task_struct.
+ */
 void audit_free(struct task_struct *tsk)
 {
 	struct audit_context *context;
@@ -934,13 +709,24 @@
 	audit_free_context(context);
 }
 
-/* Fill in audit context at syscall entry.  This only happens if the
+/**
+ * audit_syscall_entry - fill in an audit record at syscall entry
+ * @tsk: task being audited
+ * @arch: architecture type
+ * @major: major syscall type (function)
+ * @a1: additional syscall register 1
+ * @a2: additional syscall register 2
+ * @a3: additional syscall register 3
+ * @a4: additional syscall register 4
+ *
+ * Fill in audit context at syscall entry.  This only happens if the
  * audit context was created when the task was created and the state or
  * filters demand the audit context be built.  If the state from the
  * per-task filter or from the per-syscall filter is AUDIT_RECORD_CONTEXT,
  * then the record will be written at syscall exit time (otherwise, it
  * will only be written if another part of the kernel requests that it
- * be written). */
+ * be written).
+ */
 void audit_syscall_entry(struct task_struct *tsk, int arch, int major,
 			 unsigned long a1, unsigned long a2,
 			 unsigned long a3, unsigned long a4)
@@ -950,7 +736,8 @@
 
 	BUG_ON(!context);
 
-	/* This happens only on certain architectures that make system
+	/*
+	 * This happens only on certain architectures that make system
 	 * calls in kernel_thread via the entry.S interface, instead of
 	 * with direct calls.  (If you are porting to a new
 	 * architecture, hitting this condition can indicate that you
@@ -1014,11 +801,18 @@
 	context->auditable  = !!(state == AUDIT_RECORD_CONTEXT);
 }
 
-/* Tear down after system call.  If the audit context has been marked as
+/**
+ * audit_syscall_exit - deallocate audit context after a system call
+ * @tsk: task being audited
+ * @valid: success/failure flag
+ * @return_code: syscall return value
+ *
+ * Tear down after system call.  If the audit context has been marked as
  * auditable (either because of the AUDIT_RECORD_CONTEXT state from
  * filtering, or because some other part of the kernel write an audit
  * message), then write out the syscall information.  In call cases,
- * free the names stored from getname(). */
+ * free the names stored from getname().
+ */
 void audit_syscall_exit(struct task_struct *tsk, int valid, long return_code)
 {
 	struct audit_context *context;
@@ -1053,7 +847,13 @@
 	put_task_struct(tsk);
 }
 
-/* Add a name to the list.  Called from fs/namei.c:getname(). */
+/**
+ * audit_getname - add a name to the list
+ * @name: name to add
+ *
+ * Add a name to the list of audit names for this context.
+ * Called from fs/namei.c:getname().
+ */
 void audit_getname(const char *name)
 {
 	struct audit_context *context = current->audit_context;
@@ -1082,10 +882,13 @@
 		
 }
 
-/* Intercept a putname request.  Called from
- * include/linux/fs.h:putname().  If we have stored the name from
- * getname in the audit context, then we delay the putname until syscall
- * exit. */
+/* audit_putname - intercept a putname request
+ * @name: name to intercept and delay for putname
+ *
+ * If we have stored the name from getname in the audit context,
+ * then we delay the putname until syscall exit.
+ * Called from include/linux/fs.h:putname().
+ */
 void audit_putname(const char *name)
 {
 	struct audit_context *context = current->audit_context;
@@ -1100,7 +903,7 @@
 			for (i = 0; i < context->name_count; i++)
 				printk(KERN_ERR "name[%d] = %p = %s\n", i,
 				       context->names[i].name,
-				       context->names[i].name);
+				       context->names[i].name ?: "(null)");
 		}
 #endif
 		__putname(name);
@@ -1122,9 +925,51 @@
 #endif
 }
 
-/* Store the inode and device from a lookup.  Called from
- * fs/namei.c:path_lookup(). */
-void audit_inode(const char *name, const struct inode *inode, unsigned flags)
+void audit_inode_context(int idx, const struct inode *inode)
+{
+	struct audit_context *context = current->audit_context;
+	const char *suffix = security_inode_xattr_getsuffix();
+	char *ctx = NULL;
+	int len = 0;
+
+	if (!suffix)
+		goto ret;
+
+	len = security_inode_getsecurity(inode, suffix, NULL, 0, 0);
+	if (len == -EOPNOTSUPP)
+		goto ret;
+	if (len < 0) 
+		goto error_path;
+
+	ctx = kmalloc(len, GFP_KERNEL);
+	if (!ctx) 
+		goto error_path;
+
+	len = security_inode_getsecurity(inode, suffix, ctx, len, 0);
+	if (len < 0)
+		goto error_path;
+
+	context->names[idx].ctx = ctx;
+	goto ret;
+
+error_path:
+	if (ctx)
+		kfree(ctx);
+	audit_panic("error in audit_inode_context");
+ret:
+	return;
+}
+
+
+/**
+ * audit_inode - store the inode and device from a lookup
+ * @name: name being audited
+ * @inode: inode being audited
+ * @flags: lookup flags (as used in path_lookup())
+ *
+ * Called from fs/namei.c:path_lookup().
+ */
+void __audit_inode(const char *name, const struct inode *inode, unsigned flags)
 {
 	int idx;
 	struct audit_context *context = current->audit_context;
@@ -1150,15 +995,105 @@
 		++context->ino_count;
 #endif
 	}
-	context->names[idx].flags = flags;
-	context->names[idx].ino   = inode->i_ino;
 	context->names[idx].dev	  = inode->i_sb->s_dev;
 	context->names[idx].mode  = inode->i_mode;
 	context->names[idx].uid   = inode->i_uid;
 	context->names[idx].gid   = inode->i_gid;
 	context->names[idx].rdev  = inode->i_rdev;
+	audit_inode_context(idx, inode);
+	if ((flags & LOOKUP_PARENT) && (strcmp(name, "/") != 0) && 
+	    (strcmp(name, ".") != 0)) {
+		context->names[idx].ino   = (unsigned long)-1;
+		context->names[idx].pino  = inode->i_ino;
+	} else {
+		context->names[idx].ino   = inode->i_ino;
+		context->names[idx].pino  = (unsigned long)-1;
+	}
+}
+
+/**
+ * audit_inode_child - collect inode info for created/removed objects
+ * @dname: inode's dentry name
+ * @inode: inode being audited
+ * @pino: inode number of dentry parent
+ *
+ * For syscalls that create or remove filesystem objects, audit_inode
+ * can only collect information for the filesystem object's parent.
+ * This call updates the audit context with the child's information.
+ * Syscalls that create a new filesystem object must be hooked after
+ * the object is created.  Syscalls that remove a filesystem object
+ * must be hooked prior, in order to capture the target inode during
+ * unsuccessful attempts.
+ */
+void __audit_inode_child(const char *dname, const struct inode *inode,
+			 unsigned long pino)
+{
+	int idx;
+	struct audit_context *context = current->audit_context;
+
+	if (!context->in_syscall)
+		return;
+
+	/* determine matching parent */
+	if (dname)
+		for (idx = 0; idx < context->name_count; idx++)
+			if (context->names[idx].pino == pino) {
+				const char *n;
+				const char *name = context->names[idx].name;
+				int dlen = strlen(dname);
+				int nlen = name ? strlen(name) : 0;
+
+				if (nlen < dlen)
+					continue;
+				
+				/* disregard trailing slashes */
+				n = name + nlen - 1;
+				while ((*n == '/') && (n > name))
+					n--;
+
+				/* find last path component */
+				n = n - dlen + 1;
+				if (n < name)
+					continue;
+				else if (n > name) {
+					if (*--n != '/')
+						continue;
+					else
+						n++;
+				}
+
+				if (strncmp(n, dname, dlen) == 0)
+					goto update_context;
+			}
+
+	/* catch-all in case match not found */
+	idx = context->name_count++;
+	context->names[idx].name  = NULL;
+	context->names[idx].pino  = pino;
+#if AUDIT_DEBUG
+	context->ino_count++;
+#endif
+
+update_context:
+	if (inode) {
+		context->names[idx].ino   = inode->i_ino;
+		context->names[idx].dev	  = inode->i_sb->s_dev;
+		context->names[idx].mode  = inode->i_mode;
+		context->names[idx].uid   = inode->i_uid;
+		context->names[idx].gid   = inode->i_gid;
+		context->names[idx].rdev  = inode->i_rdev;
+		audit_inode_context(idx, inode);
+	}
 }
 
+/**
+ * auditsc_get_stamp - get local copies of audit_context values
+ * @ctx: audit_context for the task
+ * @t: timespec to store time recorded in the audit_context
+ * @serial: serial value that is recorded in the audit_context
+ *
+ * Also sets the context as auditable.
+ */
 void auditsc_get_stamp(struct audit_context *ctx,
 		       struct timespec *t, unsigned int *serial)
 {
@@ -1170,6 +1105,15 @@
 	ctx->auditable = 1;
 }
 
+/**
+ * audit_set_loginuid - set a task's audit_context loginuid
+ * @task: task whose audit context is being modified
+ * @loginuid: loginuid value
+ *
+ * Returns 0.
+ *
+ * Called (set) from fs/proc/base.c::proc_loginuid_write().
+ */
 int audit_set_loginuid(struct task_struct *task, uid_t loginuid)
 {
 	if (task->audit_context) {
@@ -1188,12 +1132,27 @@
 	return 0;
 }
 
+/**
+ * audit_get_loginuid - get the loginuid for an audit_context
+ * @ctx: the audit_context
+ *
+ * Returns the context's loginuid or -1 if @ctx is NULL.
+ */
 uid_t audit_get_loginuid(struct audit_context *ctx)
 {
 	return ctx ? ctx->loginuid : -1;
 }
 
-int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
+/**
+ * audit_ipc_perms - record audit data for ipc
+ * @qbytes: msgq bytes
+ * @uid: msgq user id
+ * @gid: msgq group id
+ * @mode: msgq mode (permissions)
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp)
 {
 	struct audit_aux_data_ipcctl *ax;
 	struct audit_context *context = current->audit_context;
@@ -1201,7 +1160,7 @@
 	if (likely(!context))
 		return 0;
 
-	ax = kmalloc(sizeof(*ax), GFP_KERNEL);
+	ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
 	if (!ax)
 		return -ENOMEM;
 
@@ -1209,6 +1168,7 @@
 	ax->uid = uid;
 	ax->gid = gid;
 	ax->mode = mode;
+	ax->ctx = audit_ipc_context(ipcp);
 
 	ax->d.type = AUDIT_IPC;
 	ax->d.next = context->aux;
@@ -1216,6 +1176,45 @@
 	return 0;
 }
 
+char *audit_ipc_context(struct kern_ipc_perm *ipcp)
+{
+	struct audit_context *context = current->audit_context;
+	char *ctx = NULL;
+	int len = 0;
+
+	if (likely(!context))
+		return NULL;
+
+	len = security_ipc_getsecurity(ipcp, NULL, 0);
+	if (len == -EOPNOTSUPP)
+		goto ret;
+	if (len < 0)
+		goto error_path;
+
+	ctx = kmalloc(len, GFP_ATOMIC);
+	if (!ctx)
+		goto error_path;
+
+	len = security_ipc_getsecurity(ipcp, ctx, len);
+	if (len < 0)
+		goto error_path;
+
+	return ctx;
+
+error_path:
+	kfree(ctx);
+	audit_panic("error in audit_ipc_context");
+ret:
+	return NULL;
+}
+
+/**
+ * audit_socketcall - record audit data for sys_socketcall
+ * @nargs: number of args
+ * @args: args array
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
 int audit_socketcall(int nargs, unsigned long *args)
 {
 	struct audit_aux_data_socketcall *ax;
@@ -1237,6 +1236,13 @@
 	return 0;
 }
 
+/**
+ * audit_sockaddr - record audit data for sys_bind, sys_connect, sys_sendto
+ * @len: data length in user space
+ * @a: data address in kernel space
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
 int audit_sockaddr(int len, void *a)
 {
 	struct audit_aux_data_sockaddr *ax;
@@ -1258,6 +1264,15 @@
 	return 0;
 }
 
+/**
+ * audit_avc_path - record the granting or denial of permissions
+ * @dentry: dentry to record
+ * @mnt: mnt to record
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ *
+ * Called from security/selinux/avc.c::avc_audit()
+ */
 int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt)
 {
 	struct audit_aux_data_path *ax;
@@ -1279,6 +1294,14 @@
 	return 0;
 }
 
+/**
+ * audit_signal_info - record signal info for shutting down audit subsystem
+ * @sig: signal value
+ * @t: task being signaled
+ *
+ * If the audit subsystem is being terminated, record the task (pid)
+ * and uid that is doing that.
+ */
 void audit_signal_info(int sig, struct task_struct *t)
 {
 	extern pid_t audit_sig_pid;
@@ -1295,4 +1318,3 @@
 		}
 	}
 }
-
diff -urN oldtree/kernel/cpuset.c newtree/kernel/cpuset.c
--- oldtree/kernel/cpuset.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/kernel/cpuset.c	2006-02-13 19:20:01.265319944 -0500
@@ -1825,6 +1825,33 @@
 }
 
 /**
+ * cpuset_lock - lock out any changes to cpuset structures
+ *
+ * The out of memory (oom) code needs to lock down cpusets
+ * from being changed while it scans the tasklist looking for a
+ * task in an overlapping cpuset.  Expose callback_sem via this
+ * cpuset_lock() routine, so the oom code can lock it, before
+ * locking the task list.  The tasklist_lock is a spinlock, so
+ * must be taken inside callback_sem.
+ */
+
+void cpuset_lock(void)
+{
+	down(&callback_sem);
+}
+
+/**
+ * cpuset_unlock - release lock on cpuset changes
+ *
+ * Undo the lock taken in a previous cpuset_lock() call.
+ */
+
+void cpuset_unlock(void)
+{
+	up(&callback_sem);
+}
+
+/**
  * cpuset_excl_nodes_overlap - Do we overlap @p's mem_exclusive ancestors?
  * @p: pointer to task_struct of some other task.
  *
@@ -1833,7 +1860,7 @@
  * determine if task @p's memory usage might impact the memory
  * available to the current task.
  *
- * Acquires callback_sem - not suitable for calling from a fast path.
+ * Call while holding callback_sem.
  **/
 
 int cpuset_excl_nodes_overlap(const struct task_struct *p)
@@ -1841,8 +1868,6 @@
 	const struct cpuset *cs1, *cs2;	/* my and p's cpuset ancestors */
 	int overlap = 0;		/* do cpusets overlap? */
 
-	down(&callback_sem);
-
 	task_lock(current);
 	if (current->flags & PF_EXITING) {
 		task_unlock(current);
@@ -1861,8 +1886,6 @@
 
 	overlap = nodes_intersects(cs1->mems_allowed, cs2->mems_allowed);
 done:
-	up(&callback_sem);
-
 	return overlap;
 }
 
diff -urN oldtree/kernel/cpuset.c.orig newtree/kernel/cpuset.c.orig
--- oldtree/kernel/cpuset.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/cpuset.c.orig	2006-02-13 19:20:01.268319488 -0500
@@ -0,0 +1,1932 @@
+/*
+ *  kernel/cpuset.c
+ *
+ *  Processor and Memory placement constraints for sets of tasks.
+ *
+ *  Copyright (C) 2003 BULL SA.
+ *  Copyright (C) 2004 Silicon Graphics, Inc.
+ *
+ *  Portions derived from Patrick Mochel's sysfs code.
+ *  sysfs is Copyright (c) 2001-3 Patrick Mochel
+ *  Portions Copyright (c) 2004 Silicon Graphics, Inc.
+ *
+ *  2003-10-10 Written by Simon Derr <simon.derr@bull.net>
+ *  2003-10-22 Updates by Stephen Hemminger.
+ *  2004 May-July Rework by Paul Jackson <pj@sgi.com>
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of the Linux
+ *  distribution for more details.
+ */
+
+#include <linux/config.h>
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/cpuset.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/list.h>
+#include <linux/mempolicy.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/pagemap.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <linux/backing-dev.h>
+#include <linux/sort.h>
+
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <asm/semaphore.h>
+
+#define CPUSET_SUPER_MAGIC 		0x27e0eb
+
+struct cpuset {
+	unsigned long flags;		/* "unsigned long" so bitops work */
+	cpumask_t cpus_allowed;		/* CPUs allowed to tasks in cpuset */
+	nodemask_t mems_allowed;	/* Memory Nodes allowed to tasks */
+
+	/*
+	 * Count is atomic so can incr (fork) or decr (exit) without a lock.
+	 */
+	atomic_t count;			/* count tasks using this cpuset */
+
+	/*
+	 * We link our 'sibling' struct into our parents 'children'.
+	 * Our children link their 'sibling' into our 'children'.
+	 */
+	struct list_head sibling;	/* my parents children */
+	struct list_head children;	/* my children */
+
+	struct cpuset *parent;		/* my parent */
+	struct dentry *dentry;		/* cpuset fs entry */
+
+	/*
+	 * Copy of global cpuset_mems_generation as of the most
+	 * recent time this cpuset changed its mems_allowed.
+	 */
+	 int mems_generation;
+};
+
+/* bits in struct cpuset flags field */
+typedef enum {
+	CS_CPU_EXCLUSIVE,
+	CS_MEM_EXCLUSIVE,
+	CS_REMOVED,
+	CS_NOTIFY_ON_RELEASE
+} cpuset_flagbits_t;
+
+/* convenient tests for these bits */
+static inline int is_cpu_exclusive(const struct cpuset *cs)
+{
+	return !!test_bit(CS_CPU_EXCLUSIVE, &cs->flags);
+}
+
+static inline int is_mem_exclusive(const struct cpuset *cs)
+{
+	return !!test_bit(CS_MEM_EXCLUSIVE, &cs->flags);
+}
+
+static inline int is_removed(const struct cpuset *cs)
+{
+	return !!test_bit(CS_REMOVED, &cs->flags);
+}
+
+static inline int notify_on_release(const struct cpuset *cs)
+{
+	return !!test_bit(CS_NOTIFY_ON_RELEASE, &cs->flags);
+}
+
+/*
+ * Increment this atomic integer everytime any cpuset changes its
+ * mems_allowed value.  Users of cpusets can track this generation
+ * number, and avoid having to lock and reload mems_allowed unless
+ * the cpuset they're using changes generation.
+ *
+ * A single, global generation is needed because attach_task() could
+ * reattach a task to a different cpuset, which must not have its
+ * generation numbers aliased with those of that tasks previous cpuset.
+ *
+ * Generations are needed for mems_allowed because one task cannot
+ * modify anothers memory placement.  So we must enable every task,
+ * on every visit to __alloc_pages(), to efficiently check whether
+ * its current->cpuset->mems_allowed has changed, requiring an update
+ * of its current->mems_allowed.
+ */
+static atomic_t cpuset_mems_generation = ATOMIC_INIT(1);
+
+static struct cpuset top_cpuset = {
+	.flags = ((1 << CS_CPU_EXCLUSIVE) | (1 << CS_MEM_EXCLUSIVE)),
+	.cpus_allowed = CPU_MASK_ALL,
+	.mems_allowed = NODE_MASK_ALL,
+	.count = ATOMIC_INIT(0),
+	.sibling = LIST_HEAD_INIT(top_cpuset.sibling),
+	.children = LIST_HEAD_INIT(top_cpuset.children),
+	.parent = NULL,
+	.dentry = NULL,
+	.mems_generation = 0,
+};
+
+static struct vfsmount *cpuset_mount;
+static struct super_block *cpuset_sb = NULL;
+
+/*
+ * We have two global cpuset semaphores below.  They can nest.
+ * It is ok to first take manage_sem, then nest callback_sem.  We also
+ * require taking task_lock() when dereferencing a tasks cpuset pointer.
+ * See "The task_lock() exception", at the end of this comment.
+ *
+ * A task must hold both semaphores to modify cpusets.  If a task
+ * holds manage_sem, then it blocks others wanting that semaphore,
+ * ensuring that it is the only task able to also acquire callback_sem
+ * and be able to modify cpusets.  It can perform various checks on
+ * the cpuset structure first, knowing nothing will change.  It can
+ * also allocate memory while just holding manage_sem.  While it is
+ * performing these checks, various callback routines can briefly
+ * acquire callback_sem to query cpusets.  Once it is ready to make
+ * the changes, it takes callback_sem, blocking everyone else.
+ *
+ * Calls to the kernel memory allocator can not be made while holding
+ * callback_sem, as that would risk double tripping on callback_sem
+ * from one of the callbacks into the cpuset code from within
+ * __alloc_pages().
+ *
+ * If a task is only holding callback_sem, then it has read-only
+ * access to cpusets.
+ *
+ * The task_struct fields mems_allowed and mems_generation may only
+ * be accessed in the context of that task, so require no locks.
+ *
+ * Any task can increment and decrement the count field without lock.
+ * So in general, code holding manage_sem or callback_sem can't rely
+ * on the count field not changing.  However, if the count goes to
+ * zero, then only attach_task(), which holds both semaphores, can
+ * increment it again.  Because a count of zero means that no tasks
+ * are currently attached, therefore there is no way a task attached
+ * to that cpuset can fork (the other way to increment the count).
+ * So code holding manage_sem or callback_sem can safely assume that
+ * if the count is zero, it will stay zero.  Similarly, if a task
+ * holds manage_sem or callback_sem on a cpuset with zero count, it
+ * knows that the cpuset won't be removed, as cpuset_rmdir() needs
+ * both of those semaphores.
+ *
+ * A possible optimization to improve parallelism would be to make
+ * callback_sem a R/W semaphore (rwsem), allowing the callback routines
+ * to proceed in parallel, with read access, until the holder of
+ * manage_sem needed to take this rwsem for exclusive write access
+ * and modify some cpusets.
+ *
+ * The cpuset_common_file_write handler for operations that modify
+ * the cpuset hierarchy holds manage_sem across the entire operation,
+ * single threading all such cpuset modifications across the system.
+ *
+ * The cpuset_common_file_read() handlers only hold callback_sem across
+ * small pieces of code, such as when reading out possibly multi-word
+ * cpumasks and nodemasks.
+ *
+ * The fork and exit callbacks cpuset_fork() and cpuset_exit(), don't
+ * (usually) take either semaphore.  These are the two most performance
+ * critical pieces of code here.  The exception occurs on cpuset_exit(),
+ * when a task in a notify_on_release cpuset exits.  Then manage_sem
+ * is taken, and if the cpuset count is zero, a usermode call made
+ * to /sbin/cpuset_release_agent with the name of the cpuset (path
+ * relative to the root of cpuset file system) as the argument.
+ *
+ * A cpuset can only be deleted if both its 'count' of using tasks
+ * is zero, and its list of 'children' cpusets is empty.  Since all
+ * tasks in the system use _some_ cpuset, and since there is always at
+ * least one task in the system (init, pid == 1), therefore, top_cpuset
+ * always has either children cpusets and/or using tasks.  So we don't
+ * need a special hack to ensure that top_cpuset cannot be deleted.
+ *
+ * The above "Tale of Two Semaphores" would be complete, but for:
+ *
+ *	The task_lock() exception
+ *
+ * The need for this exception arises from the action of attach_task(),
+ * which overwrites one tasks cpuset pointer with another.  It does
+ * so using both semaphores, however there are several performance
+ * critical places that need to reference task->cpuset without the
+ * expense of grabbing a system global semaphore.  Therefore except as
+ * noted below, when dereferencing or, as in attach_task(), modifying
+ * a tasks cpuset pointer we use task_lock(), which acts on a spinlock
+ * (task->alloc_lock) already in the task_struct routinely used for
+ * such matters.
+ */
+
+static DECLARE_MUTEX(manage_sem);
+static DECLARE_MUTEX(callback_sem);
+
+/*
+ * A couple of forward declarations required, due to cyclic reference loop:
+ *  cpuset_mkdir -> cpuset_create -> cpuset_populate_dir -> cpuset_add_file
+ *  -> cpuset_create_file -> cpuset_dir_inode_operations -> cpuset_mkdir.
+ */
+
+static int cpuset_mkdir(struct inode *dir, struct dentry *dentry, int mode);
+static int cpuset_rmdir(struct inode *unused_dir, struct dentry *dentry);
+
+static struct backing_dev_info cpuset_backing_dev_info = {
+	.ra_pages = 0,		/* No readahead */
+	.capabilities	= BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK,
+};
+
+static struct inode *cpuset_new_inode(mode_t mode)
+{
+	struct inode *inode = new_inode(cpuset_sb);
+
+	if (inode) {
+		inode->i_mode = mode;
+		inode->i_uid = current->fsuid;
+		inode->i_gid = current->fsgid;
+		inode->i_blksize = PAGE_CACHE_SIZE;
+		inode->i_blocks = 0;
+		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+		inode->i_mapping->backing_dev_info = &cpuset_backing_dev_info;
+	}
+	return inode;
+}
+
+static void cpuset_diput(struct dentry *dentry, struct inode *inode)
+{
+	/* is dentry a directory ? if so, kfree() associated cpuset */
+	if (S_ISDIR(inode->i_mode)) {
+		struct cpuset *cs = dentry->d_fsdata;
+		BUG_ON(!(is_removed(cs)));
+		kfree(cs);
+	}
+	iput(inode);
+}
+
+static struct dentry_operations cpuset_dops = {
+	.d_iput = cpuset_diput,
+};
+
+static struct dentry *cpuset_get_dentry(struct dentry *parent, const char *name)
+{
+	struct dentry *d = lookup_one_len(name, parent, strlen(name));
+	if (!IS_ERR(d))
+		d->d_op = &cpuset_dops;
+	return d;
+}
+
+static void remove_dir(struct dentry *d)
+{
+	struct dentry *parent = dget(d->d_parent);
+
+	d_delete(d);
+	simple_rmdir(parent->d_inode, d);
+	dput(parent);
+}
+
+/*
+ * NOTE : the dentry must have been dget()'ed
+ */
+static void cpuset_d_remove_dir(struct dentry *dentry)
+{
+	struct list_head *node;
+
+	spin_lock(&dcache_lock);
+	node = dentry->d_subdirs.next;
+	while (node != &dentry->d_subdirs) {
+		struct dentry *d = list_entry(node, struct dentry, d_child);
+		list_del_init(node);
+		if (d->d_inode) {
+			d = dget_locked(d);
+			spin_unlock(&dcache_lock);
+			d_delete(d);
+			simple_unlink(dentry->d_inode, d);
+			dput(d);
+			spin_lock(&dcache_lock);
+		}
+		node = dentry->d_subdirs.next;
+	}
+	list_del_init(&dentry->d_child);
+	spin_unlock(&dcache_lock);
+	remove_dir(dentry);
+}
+
+static struct super_operations cpuset_ops = {
+	.statfs = simple_statfs,
+	.drop_inode = generic_delete_inode,
+};
+
+static int cpuset_fill_super(struct super_block *sb, void *unused_data,
+							int unused_silent)
+{
+	struct inode *inode;
+	struct dentry *root;
+
+	sb->s_blocksize = PAGE_CACHE_SIZE;
+	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+	sb->s_magic = CPUSET_SUPER_MAGIC;
+	sb->s_op = &cpuset_ops;
+	cpuset_sb = sb;
+
+	inode = cpuset_new_inode(S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR);
+	if (inode) {
+		inode->i_op = &simple_dir_inode_operations;
+		inode->i_fop = &simple_dir_operations;
+		/* directories start off with i_nlink == 2 (for "." entry) */
+		inode->i_nlink++;
+	} else {
+		return -ENOMEM;
+	}
+
+	root = d_alloc_root(inode);
+	if (!root) {
+		iput(inode);
+		return -ENOMEM;
+	}
+	sb->s_root = root;
+	return 0;
+}
+
+static struct super_block *cpuset_get_sb(struct file_system_type *fs_type,
+					int flags, const char *unused_dev_name,
+					void *data)
+{
+	return get_sb_single(fs_type, flags, data, cpuset_fill_super);
+}
+
+static struct file_system_type cpuset_fs_type = {
+	.name = "cpuset",
+	.get_sb = cpuset_get_sb,
+	.kill_sb = kill_litter_super,
+};
+
+/* struct cftype:
+ *
+ * The files in the cpuset filesystem mostly have a very simple read/write
+ * handling, some common function will take care of it. Nevertheless some cases
+ * (read tasks) are special and therefore I define this structure for every
+ * kind of file.
+ *
+ *
+ * When reading/writing to a file:
+ *	- the cpuset to use in file->f_dentry->d_parent->d_fsdata
+ *	- the 'cftype' of the file is file->f_dentry->d_fsdata
+ */
+
+struct cftype {
+	char *name;
+	int private;
+	int (*open) (struct inode *inode, struct file *file);
+	ssize_t (*read) (struct file *file, char __user *buf, size_t nbytes,
+							loff_t *ppos);
+	int (*write) (struct file *file, const char __user *buf, size_t nbytes,
+							loff_t *ppos);
+	int (*release) (struct inode *inode, struct file *file);
+};
+
+static inline struct cpuset *__d_cs(struct dentry *dentry)
+{
+	return dentry->d_fsdata;
+}
+
+static inline struct cftype *__d_cft(struct dentry *dentry)
+{
+	return dentry->d_fsdata;
+}
+
+/*
+ * Call with manage_sem held.  Writes path of cpuset into buf.
+ * Returns 0 on success, -errno on error.
+ */
+
+static int cpuset_path(const struct cpuset *cs, char *buf, int buflen)
+{
+	char *start;
+
+	start = buf + buflen;
+
+	*--start = '\0';
+	for (;;) {
+		int len = cs->dentry->d_name.len;
+		if ((start -= len) < buf)
+			return -ENAMETOOLONG;
+		memcpy(start, cs->dentry->d_name.name, len);
+		cs = cs->parent;
+		if (!cs)
+			break;
+		if (!cs->parent)
+			continue;
+		if (--start < buf)
+			return -ENAMETOOLONG;
+		*start = '/';
+	}
+	memmove(buf, start, buf + buflen - start);
+	return 0;
+}
+
+/*
+ * Notify userspace when a cpuset is released, by running
+ * /sbin/cpuset_release_agent with the name of the cpuset (path
+ * relative to the root of cpuset file system) as the argument.
+ *
+ * Most likely, this user command will try to rmdir this cpuset.
+ *
+ * This races with the possibility that some other task will be
+ * attached to this cpuset before it is removed, or that some other
+ * user task will 'mkdir' a child cpuset of this cpuset.  That's ok.
+ * The presumed 'rmdir' will fail quietly if this cpuset is no longer
+ * unused, and this cpuset will be reprieved from its death sentence,
+ * to continue to serve a useful existence.  Next time it's released,
+ * we will get notified again, if it still has 'notify_on_release' set.
+ *
+ * The final arg to call_usermodehelper() is 0, which means don't
+ * wait.  The separate /sbin/cpuset_release_agent task is forked by
+ * call_usermodehelper(), then control in this thread returns here,
+ * without waiting for the release agent task.  We don't bother to
+ * wait because the caller of this routine has no use for the exit
+ * status of the /sbin/cpuset_release_agent task, so no sense holding
+ * our caller up for that.
+ *
+ * When we had only one cpuset semaphore, we had to call this
+ * without holding it, to avoid deadlock when call_usermodehelper()
+ * allocated memory.  With two locks, we could now call this while
+ * holding manage_sem, but we still don't, so as to minimize
+ * the time manage_sem is held.
+ */
+
+static void cpuset_release_agent(const char *pathbuf)
+{
+	char *argv[3], *envp[3];
+	int i;
+
+	if (!pathbuf)
+		return;
+
+	i = 0;
+	argv[i++] = "/sbin/cpuset_release_agent";
+	argv[i++] = (char *)pathbuf;
+	argv[i] = NULL;
+
+	i = 0;
+	/* minimal command environment */
+	envp[i++] = "HOME=/";
+	envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+	envp[i] = NULL;
+
+	call_usermodehelper(argv[0], argv, envp, 0);
+	kfree(pathbuf);
+}
+
+/*
+ * Either cs->count of using tasks transitioned to zero, or the
+ * cs->children list of child cpusets just became empty.  If this
+ * cs is notify_on_release() and now both the user count is zero and
+ * the list of children is empty, prepare cpuset path in a kmalloc'd
+ * buffer, to be returned via ppathbuf, so that the caller can invoke
+ * cpuset_release_agent() with it later on, once manage_sem is dropped.
+ * Call here with manage_sem held.
+ *
+ * This check_for_release() routine is responsible for kmalloc'ing
+ * pathbuf.  The above cpuset_release_agent() is responsible for
+ * kfree'ing pathbuf.  The caller of these routines is responsible
+ * for providing a pathbuf pointer, initialized to NULL, then
+ * calling check_for_release() with manage_sem held and the address
+ * of the pathbuf pointer, then dropping manage_sem, then calling
+ * cpuset_release_agent() with pathbuf, as set by check_for_release().
+ */
+
+static void check_for_release(struct cpuset *cs, char **ppathbuf)
+{
+	if (notify_on_release(cs) && atomic_read(&cs->count) == 0 &&
+	    list_empty(&cs->children)) {
+		char *buf;
+
+		buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+		if (!buf)
+			return;
+		if (cpuset_path(cs, buf, PAGE_SIZE) < 0)
+			kfree(buf);
+		else
+			*ppathbuf = buf;
+	}
+}
+
+/*
+ * Return in *pmask the portion of a cpusets's cpus_allowed that
+ * are online.  If none are online, walk up the cpuset hierarchy
+ * until we find one that does have some online cpus.  If we get
+ * all the way to the top and still haven't found any online cpus,
+ * return cpu_online_map.  Or if passed a NULL cs from an exit'ing
+ * task, return cpu_online_map.
+ *
+ * One way or another, we guarantee to return some non-empty subset
+ * of cpu_online_map.
+ *
+ * Call with callback_sem held.
+ */
+
+static void guarantee_online_cpus(const struct cpuset *cs, cpumask_t *pmask)
+{
+	while (cs && !cpus_intersects(cs->cpus_allowed, cpu_online_map))
+		cs = cs->parent;
+	if (cs)
+		cpus_and(*pmask, cs->cpus_allowed, cpu_online_map);
+	else
+		*pmask = cpu_online_map;
+	BUG_ON(!cpus_intersects(*pmask, cpu_online_map));
+}
+
+/*
+ * Return in *pmask the portion of a cpusets's mems_allowed that
+ * are online.  If none are online, walk up the cpuset hierarchy
+ * until we find one that does have some online mems.  If we get
+ * all the way to the top and still haven't found any online mems,
+ * return node_online_map.
+ *
+ * One way or another, we guarantee to return some non-empty subset
+ * of node_online_map.
+ *
+ * Call with callback_sem held.
+ */
+
+static void guarantee_online_mems(const struct cpuset *cs, nodemask_t *pmask)
+{
+	while (cs && !nodes_intersects(cs->mems_allowed, node_online_map))
+		cs = cs->parent;
+	if (cs)
+		nodes_and(*pmask, cs->mems_allowed, node_online_map);
+	else
+		*pmask = node_online_map;
+	BUG_ON(!nodes_intersects(*pmask, node_online_map));
+}
+
+/*
+ * Refresh current tasks mems_allowed and mems_generation from current
+ * tasks cpuset.
+ *
+ * Call without callback_sem or task_lock() held.  May be called with
+ * or without manage_sem held.  Will acquire task_lock() and might
+ * acquire callback_sem during call.
+ *
+ * The task_lock() is required to dereference current->cpuset safely.
+ * Without it, we could pick up the pointer value of current->cpuset
+ * in one instruction, and then attach_task could give us a different
+ * cpuset, and then the cpuset we had could be removed and freed,
+ * and then on our next instruction, we could dereference a no longer
+ * valid cpuset pointer to get its mems_generation field.
+ *
+ * This routine is needed to update the per-task mems_allowed data,
+ * within the tasks context, when it is trying to allocate memory
+ * (in various mm/mempolicy.c routines) and notices that some other
+ * task has been modifying its cpuset.
+ */
+
+static void refresh_mems(void)
+{
+	int my_cpusets_mem_gen;
+
+	task_lock(current);
+	my_cpusets_mem_gen = current->cpuset->mems_generation;
+	task_unlock(current);
+
+	if (current->cpuset_mems_generation != my_cpusets_mem_gen) {
+		struct cpuset *cs;
+		nodemask_t oldmem = current->mems_allowed;
+
+		down(&callback_sem);
+		task_lock(current);
+		cs = current->cpuset;
+		guarantee_online_mems(cs, &current->mems_allowed);
+		current->cpuset_mems_generation = cs->mems_generation;
+		task_unlock(current);
+		up(&callback_sem);
+		if (!nodes_equal(oldmem, current->mems_allowed))
+			numa_policy_rebind(&oldmem, &current->mems_allowed);
+	}
+}
+
+/*
+ * is_cpuset_subset(p, q) - Is cpuset p a subset of cpuset q?
+ *
+ * One cpuset is a subset of another if all its allowed CPUs and
+ * Memory Nodes are a subset of the other, and its exclusive flags
+ * are only set if the other's are set.  Call holding manage_sem.
+ */
+
+static int is_cpuset_subset(const struct cpuset *p, const struct cpuset *q)
+{
+	return	cpus_subset(p->cpus_allowed, q->cpus_allowed) &&
+		nodes_subset(p->mems_allowed, q->mems_allowed) &&
+		is_cpu_exclusive(p) <= is_cpu_exclusive(q) &&
+		is_mem_exclusive(p) <= is_mem_exclusive(q);
+}
+
+/*
+ * validate_change() - Used to validate that any proposed cpuset change
+ *		       follows the structural rules for cpusets.
+ *
+ * If we replaced the flag and mask values of the current cpuset
+ * (cur) with those values in the trial cpuset (trial), would
+ * our various subset and exclusive rules still be valid?  Presumes
+ * manage_sem held.
+ *
+ * 'cur' is the address of an actual, in-use cpuset.  Operations
+ * such as list traversal that depend on the actual address of the
+ * cpuset in the list must use cur below, not trial.
+ *
+ * 'trial' is the address of bulk structure copy of cur, with
+ * perhaps one or more of the fields cpus_allowed, mems_allowed,
+ * or flags changed to new, trial values.
+ *
+ * Return 0 if valid, -errno if not.
+ */
+
+static int validate_change(const struct cpuset *cur, const struct cpuset *trial)
+{
+	struct cpuset *c, *par;
+
+	/* Each of our child cpusets must be a subset of us */
+	list_for_each_entry(c, &cur->children, sibling) {
+		if (!is_cpuset_subset(c, trial))
+			return -EBUSY;
+	}
+
+	/* Remaining checks don't apply to root cpuset */
+	if ((par = cur->parent) == NULL)
+		return 0;
+
+	/* We must be a subset of our parent cpuset */
+	if (!is_cpuset_subset(trial, par))
+		return -EACCES;
+
+	/* If either I or some sibling (!= me) is exclusive, we can't overlap */
+	list_for_each_entry(c, &par->children, sibling) {
+		if ((is_cpu_exclusive(trial) || is_cpu_exclusive(c)) &&
+		    c != cur &&
+		    cpus_intersects(trial->cpus_allowed, c->cpus_allowed))
+			return -EINVAL;
+		if ((is_mem_exclusive(trial) || is_mem_exclusive(c)) &&
+		    c != cur &&
+		    nodes_intersects(trial->mems_allowed, c->mems_allowed))
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * For a given cpuset cur, partition the system as follows
+ * a. All cpus in the parent cpuset's cpus_allowed that are not part of any
+ *    exclusive child cpusets
+ * b. All cpus in the current cpuset's cpus_allowed that are not part of any
+ *    exclusive child cpusets
+ * Build these two partitions by calling partition_sched_domains
+ *
+ * Call with manage_sem held.  May nest a call to the
+ * lock_cpu_hotplug()/unlock_cpu_hotplug() pair.
+ */
+
+static void update_cpu_domains(struct cpuset *cur)
+{
+	struct cpuset *c, *par = cur->parent;
+	cpumask_t pspan, cspan;
+
+	if (par == NULL || cpus_empty(cur->cpus_allowed))
+		return;
+
+	/*
+	 * Get all cpus from parent's cpus_allowed not part of exclusive
+	 * children
+	 */
+	pspan = par->cpus_allowed;
+	list_for_each_entry(c, &par->children, sibling) {
+		if (is_cpu_exclusive(c))
+			cpus_andnot(pspan, pspan, c->cpus_allowed);
+	}
+	if (is_removed(cur) || !is_cpu_exclusive(cur)) {
+		cpus_or(pspan, pspan, cur->cpus_allowed);
+		if (cpus_equal(pspan, cur->cpus_allowed))
+			return;
+		cspan = CPU_MASK_NONE;
+	} else {
+		if (cpus_empty(pspan))
+			return;
+		cspan = cur->cpus_allowed;
+		/*
+		 * Get all cpus from current cpuset's cpus_allowed not part
+		 * of exclusive children
+		 */
+		list_for_each_entry(c, &cur->children, sibling) {
+			if (is_cpu_exclusive(c))
+				cpus_andnot(cspan, cspan, c->cpus_allowed);
+		}
+	}
+
+	lock_cpu_hotplug();
+	partition_sched_domains(&pspan, &cspan);
+	unlock_cpu_hotplug();
+}
+
+/*
+ * Call with manage_sem held.  May take callback_sem during call.
+ */
+
+static int update_cpumask(struct cpuset *cs, char *buf)
+{
+	struct cpuset trialcs;
+	int retval, cpus_unchanged;
+
+	trialcs = *cs;
+	retval = cpulist_parse(buf, trialcs.cpus_allowed);
+	if (retval < 0)
+		return retval;
+	cpus_and(trialcs.cpus_allowed, trialcs.cpus_allowed, cpu_online_map);
+	if (cpus_empty(trialcs.cpus_allowed))
+		return -ENOSPC;
+	retval = validate_change(cs, &trialcs);
+	if (retval < 0)
+		return retval;
+	cpus_unchanged = cpus_equal(cs->cpus_allowed, trialcs.cpus_allowed);
+	down(&callback_sem);
+	cs->cpus_allowed = trialcs.cpus_allowed;
+	up(&callback_sem);
+	if (is_cpu_exclusive(cs) && !cpus_unchanged)
+		update_cpu_domains(cs);
+	return 0;
+}
+
+/*
+ * Call with manage_sem held.  May take callback_sem during call.
+ */
+
+static int update_nodemask(struct cpuset *cs, char *buf)
+{
+	struct cpuset trialcs;
+	int retval;
+
+	trialcs = *cs;
+	retval = nodelist_parse(buf, trialcs.mems_allowed);
+	if (retval < 0)
+		return retval;
+	nodes_and(trialcs.mems_allowed, trialcs.mems_allowed, node_online_map);
+	if (nodes_empty(trialcs.mems_allowed))
+		return -ENOSPC;
+	retval = validate_change(cs, &trialcs);
+	if (retval == 0) {
+		down(&callback_sem);
+		cs->mems_allowed = trialcs.mems_allowed;
+		atomic_inc(&cpuset_mems_generation);
+		cs->mems_generation = atomic_read(&cpuset_mems_generation);
+		up(&callback_sem);
+	}
+	return retval;
+}
+
+/*
+ * update_flag - read a 0 or a 1 in a file and update associated flag
+ * bit:	the bit to update (CS_CPU_EXCLUSIVE, CS_MEM_EXCLUSIVE,
+ *						CS_NOTIFY_ON_RELEASE)
+ * cs:	the cpuset to update
+ * buf:	the buffer where we read the 0 or 1
+ *
+ * Call with manage_sem held.
+ */
+
+static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs, char *buf)
+{
+	int turning_on;
+	struct cpuset trialcs;
+	int err, cpu_exclusive_changed;
+
+	turning_on = (simple_strtoul(buf, NULL, 10) != 0);
+
+	trialcs = *cs;
+	if (turning_on)
+		set_bit(bit, &trialcs.flags);
+	else
+		clear_bit(bit, &trialcs.flags);
+
+	err = validate_change(cs, &trialcs);
+	if (err < 0)
+		return err;
+	cpu_exclusive_changed =
+		(is_cpu_exclusive(cs) != is_cpu_exclusive(&trialcs));
+	down(&callback_sem);
+	if (turning_on)
+		set_bit(bit, &cs->flags);
+	else
+		clear_bit(bit, &cs->flags);
+	up(&callback_sem);
+
+	if (cpu_exclusive_changed)
+                update_cpu_domains(cs);
+	return 0;
+}
+
+/*
+ * Attack task specified by pid in 'pidbuf' to cpuset 'cs', possibly
+ * writing the path of the old cpuset in 'ppathbuf' if it needs to be
+ * notified on release.
+ *
+ * Call holding manage_sem.  May take callback_sem and task_lock of
+ * the task 'pid' during call.
+ */
+
+static int attach_task(struct cpuset *cs, char *pidbuf, char **ppathbuf)
+{
+	pid_t pid;
+	struct task_struct *tsk;
+	struct cpuset *oldcs;
+	cpumask_t cpus;
+
+	if (sscanf(pidbuf, "%d", &pid) != 1)
+		return -EIO;
+	if (cpus_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed))
+		return -ENOSPC;
+
+	if (pid) {
+		read_lock(&tasklist_lock);
+
+		tsk = find_task_by_pid(pid);
+		if (!tsk || tsk->flags & PF_EXITING) {
+			read_unlock(&tasklist_lock);
+			return -ESRCH;
+		}
+
+		get_task_struct(tsk);
+		read_unlock(&tasklist_lock);
+
+		if ((current->euid) && (current->euid != tsk->uid)
+		    && (current->euid != tsk->suid)) {
+			put_task_struct(tsk);
+			return -EACCES;
+		}
+	} else {
+		tsk = current;
+		get_task_struct(tsk);
+	}
+
+	down(&callback_sem);
+
+	task_lock(tsk);
+	oldcs = tsk->cpuset;
+	if (!oldcs) {
+		task_unlock(tsk);
+		up(&callback_sem);
+		put_task_struct(tsk);
+		return -ESRCH;
+	}
+	atomic_inc(&cs->count);
+	tsk->cpuset = cs;
+	task_unlock(tsk);
+
+	guarantee_online_cpus(cs, &cpus);
+	set_cpus_allowed(tsk, cpus);
+
+	up(&callback_sem);
+	put_task_struct(tsk);
+	if (atomic_dec_and_test(&oldcs->count))
+		check_for_release(oldcs, ppathbuf);
+	return 0;
+}
+
+/* The various types of files and directories in a cpuset file system */
+
+typedef enum {
+	FILE_ROOT,
+	FILE_DIR,
+	FILE_CPULIST,
+	FILE_MEMLIST,
+	FILE_CPU_EXCLUSIVE,
+	FILE_MEM_EXCLUSIVE,
+	FILE_NOTIFY_ON_RELEASE,
+	FILE_TASKLIST,
+} cpuset_filetype_t;
+
+static ssize_t cpuset_common_file_write(struct file *file, const char __user *userbuf,
+					size_t nbytes, loff_t *unused_ppos)
+{
+	struct cpuset *cs = __d_cs(file->f_dentry->d_parent);
+	struct cftype *cft = __d_cft(file->f_dentry);
+	cpuset_filetype_t type = cft->private;
+	char *buffer;
+	char *pathbuf = NULL;
+	int retval = 0;
+
+	/* Crude upper limit on largest legitimate cpulist user might write. */
+	if (nbytes > 100 + 6 * NR_CPUS)
+		return -E2BIG;
+
+	/* +1 for nul-terminator */
+	if ((buffer = kmalloc(nbytes + 1, GFP_KERNEL)) == 0)
+		return -ENOMEM;
+
+	if (copy_from_user(buffer, userbuf, nbytes)) {
+		retval = -EFAULT;
+		goto out1;
+	}
+	buffer[nbytes] = 0;	/* nul-terminate */
+
+	down(&manage_sem);
+
+	if (is_removed(cs)) {
+		retval = -ENODEV;
+		goto out2;
+	}
+
+	switch (type) {
+	case FILE_CPULIST:
+		retval = update_cpumask(cs, buffer);
+		break;
+	case FILE_MEMLIST:
+		retval = update_nodemask(cs, buffer);
+		break;
+	case FILE_CPU_EXCLUSIVE:
+		retval = update_flag(CS_CPU_EXCLUSIVE, cs, buffer);
+		break;
+	case FILE_MEM_EXCLUSIVE:
+		retval = update_flag(CS_MEM_EXCLUSIVE, cs, buffer);
+		break;
+	case FILE_NOTIFY_ON_RELEASE:
+		retval = update_flag(CS_NOTIFY_ON_RELEASE, cs, buffer);
+		break;
+	case FILE_TASKLIST:
+		retval = attach_task(cs, buffer, &pathbuf);
+		break;
+	default:
+		retval = -EINVAL;
+		goto out2;
+	}
+
+	if (retval == 0)
+		retval = nbytes;
+out2:
+	up(&manage_sem);
+	cpuset_release_agent(pathbuf);
+out1:
+	kfree(buffer);
+	return retval;
+}
+
+static ssize_t cpuset_file_write(struct file *file, const char __user *buf,
+						size_t nbytes, loff_t *ppos)
+{
+	ssize_t retval = 0;
+	struct cftype *cft = __d_cft(file->f_dentry);
+	if (!cft)
+		return -ENODEV;
+
+	/* special function ? */
+	if (cft->write)
+		retval = cft->write(file, buf, nbytes, ppos);
+	else
+		retval = cpuset_common_file_write(file, buf, nbytes, ppos);
+
+	return retval;
+}
+
+/*
+ * These ascii lists should be read in a single call, by using a user
+ * buffer large enough to hold the entire map.  If read in smaller
+ * chunks, there is no guarantee of atomicity.  Since the display format
+ * used, list of ranges of sequential numbers, is variable length,
+ * and since these maps can change value dynamically, one could read
+ * gibberish by doing partial reads while a list was changing.
+ * A single large read to a buffer that crosses a page boundary is
+ * ok, because the result being copied to user land is not recomputed
+ * across a page fault.
+ */
+
+static int cpuset_sprintf_cpulist(char *page, struct cpuset *cs)
+{
+	cpumask_t mask;
+
+	down(&callback_sem);
+	mask = cs->cpus_allowed;
+	up(&callback_sem);
+
+	return cpulist_scnprintf(page, PAGE_SIZE, mask);
+}
+
+static int cpuset_sprintf_memlist(char *page, struct cpuset *cs)
+{
+	nodemask_t mask;
+
+	down(&callback_sem);
+	mask = cs->mems_allowed;
+	up(&callback_sem);
+
+	return nodelist_scnprintf(page, PAGE_SIZE, mask);
+}
+
+static ssize_t cpuset_common_file_read(struct file *file, char __user *buf,
+				size_t nbytes, loff_t *ppos)
+{
+	struct cftype *cft = __d_cft(file->f_dentry);
+	struct cpuset *cs = __d_cs(file->f_dentry->d_parent);
+	cpuset_filetype_t type = cft->private;
+	char *page;
+	ssize_t retval = 0;
+	char *s;
+
+	if (!(page = (char *)__get_free_page(GFP_KERNEL)))
+		return -ENOMEM;
+
+	s = page;
+
+	switch (type) {
+	case FILE_CPULIST:
+		s += cpuset_sprintf_cpulist(s, cs);
+		break;
+	case FILE_MEMLIST:
+		s += cpuset_sprintf_memlist(s, cs);
+		break;
+	case FILE_CPU_EXCLUSIVE:
+		*s++ = is_cpu_exclusive(cs) ? '1' : '0';
+		break;
+	case FILE_MEM_EXCLUSIVE:
+		*s++ = is_mem_exclusive(cs) ? '1' : '0';
+		break;
+	case FILE_NOTIFY_ON_RELEASE:
+		*s++ = notify_on_release(cs) ? '1' : '0';
+		break;
+	default:
+		retval = -EINVAL;
+		goto out;
+	}
+	*s++ = '\n';
+
+	retval = simple_read_from_buffer(buf, nbytes, ppos, page, s - page);
+out:
+	free_page((unsigned long)page);
+	return retval;
+}
+
+static ssize_t cpuset_file_read(struct file *file, char __user *buf, size_t nbytes,
+								loff_t *ppos)
+{
+	ssize_t retval = 0;
+	struct cftype *cft = __d_cft(file->f_dentry);
+	if (!cft)
+		return -ENODEV;
+
+	/* special function ? */
+	if (cft->read)
+		retval = cft->read(file, buf, nbytes, ppos);
+	else
+		retval = cpuset_common_file_read(file, buf, nbytes, ppos);
+
+	return retval;
+}
+
+static int cpuset_file_open(struct inode *inode, struct file *file)
+{
+	int err;
+	struct cftype *cft;
+
+	err = generic_file_open(inode, file);
+	if (err)
+		return err;
+
+	cft = __d_cft(file->f_dentry);
+	if (!cft)
+		return -ENODEV;
+	if (cft->open)
+		err = cft->open(inode, file);
+	else
+		err = 0;
+
+	return err;
+}
+
+static int cpuset_file_release(struct inode *inode, struct file *file)
+{
+	struct cftype *cft = __d_cft(file->f_dentry);
+	if (cft->release)
+		return cft->release(inode, file);
+	return 0;
+}
+
+/*
+ * cpuset_rename - Only allow simple rename of directories in place.
+ */
+static int cpuset_rename(struct inode *old_dir, struct dentry *old_dentry,
+                  struct inode *new_dir, struct dentry *new_dentry)
+{
+	if (!S_ISDIR(old_dentry->d_inode->i_mode))
+		return -ENOTDIR;
+	if (new_dentry->d_inode)
+		return -EEXIST;
+	if (old_dir != new_dir)
+		return -EIO;
+	return simple_rename(old_dir, old_dentry, new_dir, new_dentry);
+}
+
+static struct file_operations cpuset_file_operations = {
+	.read = cpuset_file_read,
+	.write = cpuset_file_write,
+	.llseek = generic_file_llseek,
+	.open = cpuset_file_open,
+	.release = cpuset_file_release,
+};
+
+static struct inode_operations cpuset_dir_inode_operations = {
+	.lookup = simple_lookup,
+	.mkdir = cpuset_mkdir,
+	.rmdir = cpuset_rmdir,
+	.rename = cpuset_rename,
+};
+
+static int cpuset_create_file(struct dentry *dentry, int mode)
+{
+	struct inode *inode;
+
+	if (!dentry)
+		return -ENOENT;
+	if (dentry->d_inode)
+		return -EEXIST;
+
+	inode = cpuset_new_inode(mode);
+	if (!inode)
+		return -ENOMEM;
+
+	if (S_ISDIR(mode)) {
+		inode->i_op = &cpuset_dir_inode_operations;
+		inode->i_fop = &simple_dir_operations;
+
+		/* start off with i_nlink == 2 (for "." entry) */
+		inode->i_nlink++;
+	} else if (S_ISREG(mode)) {
+		inode->i_size = 0;
+		inode->i_fop = &cpuset_file_operations;
+	}
+
+	d_instantiate(dentry, inode);
+	dget(dentry);	/* Extra count - pin the dentry in core */
+	return 0;
+}
+
+/*
+ *	cpuset_create_dir - create a directory for an object.
+ *	cs: 	the cpuset we create the directory for.
+ *		It must have a valid ->parent field
+ *		And we are going to fill its ->dentry field.
+ *	name:	The name to give to the cpuset directory. Will be copied.
+ *	mode:	mode to set on new directory.
+ */
+
+static int cpuset_create_dir(struct cpuset *cs, const char *name, int mode)
+{
+	struct dentry *dentry = NULL;
+	struct dentry *parent;
+	int error = 0;
+
+	parent = cs->parent->dentry;
+	dentry = cpuset_get_dentry(parent, name);
+	if (IS_ERR(dentry))
+		return PTR_ERR(dentry);
+	error = cpuset_create_file(dentry, S_IFDIR | mode);
+	if (!error) {
+		dentry->d_fsdata = cs;
+		parent->d_inode->i_nlink++;
+		cs->dentry = dentry;
+	}
+	dput(dentry);
+
+	return error;
+}
+
+static int cpuset_add_file(struct dentry *dir, const struct cftype *cft)
+{
+	struct dentry *dentry;
+	int error;
+
+	down(&dir->d_inode->i_sem);
+	dentry = cpuset_get_dentry(dir, cft->name);
+	if (!IS_ERR(dentry)) {
+		error = cpuset_create_file(dentry, 0644 | S_IFREG);
+		if (!error)
+			dentry->d_fsdata = (void *)cft;
+		dput(dentry);
+	} else
+		error = PTR_ERR(dentry);
+	up(&dir->d_inode->i_sem);
+	return error;
+}
+
+/*
+ * Stuff for reading the 'tasks' file.
+ *
+ * Reading this file can return large amounts of data if a cpuset has
+ * *lots* of attached tasks. So it may need several calls to read(),
+ * but we cannot guarantee that the information we produce is correct
+ * unless we produce it entirely atomically.
+ *
+ * Upon tasks file open(), a struct ctr_struct is allocated, that
+ * will have a pointer to an array (also allocated here).  The struct
+ * ctr_struct * is stored in file->private_data.  Its resources will
+ * be freed by release() when the file is closed.  The array is used
+ * to sprintf the PIDs and then used by read().
+ */
+
+/* cpusets_tasks_read array */
+
+struct ctr_struct {
+	char *buf;
+	int bufsz;
+};
+
+/*
+ * Load into 'pidarray' up to 'npids' of the tasks using cpuset 'cs'.
+ * Return actual number of pids loaded.  No need to task_lock(p)
+ * when reading out p->cpuset, as we don't really care if it changes
+ * on the next cycle, and we are not going to try to dereference it.
+ */
+static inline int pid_array_load(pid_t *pidarray, int npids, struct cpuset *cs)
+{
+	int n = 0;
+	struct task_struct *g, *p;
+
+	read_lock(&tasklist_lock);
+
+	do_each_thread(g, p) {
+		if (p->cpuset == cs) {
+			pidarray[n++] = p->pid;
+			if (unlikely(n == npids))
+				goto array_full;
+		}
+	} while_each_thread(g, p);
+
+array_full:
+	read_unlock(&tasklist_lock);
+	return n;
+}
+
+static int cmppid(const void *a, const void *b)
+{
+	return *(pid_t *)a - *(pid_t *)b;
+}
+
+/*
+ * Convert array 'a' of 'npids' pid_t's to a string of newline separated
+ * decimal pids in 'buf'.  Don't write more than 'sz' chars, but return
+ * count 'cnt' of how many chars would be written if buf were large enough.
+ */
+static int pid_array_to_buf(char *buf, int sz, pid_t *a, int npids)
+{
+	int cnt = 0;
+	int i;
+
+	for (i = 0; i < npids; i++)
+		cnt += snprintf(buf + cnt, max(sz - cnt, 0), "%d\n", a[i]);
+	return cnt;
+}
+
+/*
+ * Handle an open on 'tasks' file.  Prepare a buffer listing the
+ * process id's of tasks currently attached to the cpuset being opened.
+ *
+ * Does not require any specific cpuset semaphores, and does not take any.
+ */
+static int cpuset_tasks_open(struct inode *unused, struct file *file)
+{
+	struct cpuset *cs = __d_cs(file->f_dentry->d_parent);
+	struct ctr_struct *ctr;
+	pid_t *pidarray;
+	int npids;
+	char c;
+
+	if (!(file->f_mode & FMODE_READ))
+		return 0;
+
+	ctr = kmalloc(sizeof(*ctr), GFP_KERNEL);
+	if (!ctr)
+		goto err0;
+
+	/*
+	 * If cpuset gets more users after we read count, we won't have
+	 * enough space - tough.  This race is indistinguishable to the
+	 * caller from the case that the additional cpuset users didn't
+	 * show up until sometime later on.
+	 */
+	npids = atomic_read(&cs->count);
+	pidarray = kmalloc(npids * sizeof(pid_t), GFP_KERNEL);
+	if (!pidarray)
+		goto err1;
+
+	npids = pid_array_load(pidarray, npids, cs);
+	sort(pidarray, npids, sizeof(pid_t), cmppid, NULL);
+
+	/* Call pid_array_to_buf() twice, first just to get bufsz */
+	ctr->bufsz = pid_array_to_buf(&c, sizeof(c), pidarray, npids) + 1;
+	ctr->buf = kmalloc(ctr->bufsz, GFP_KERNEL);
+	if (!ctr->buf)
+		goto err2;
+	ctr->bufsz = pid_array_to_buf(ctr->buf, ctr->bufsz, pidarray, npids);
+
+	kfree(pidarray);
+	file->private_data = ctr;
+	return 0;
+
+err2:
+	kfree(pidarray);
+err1:
+	kfree(ctr);
+err0:
+	return -ENOMEM;
+}
+
+static ssize_t cpuset_tasks_read(struct file *file, char __user *buf,
+						size_t nbytes, loff_t *ppos)
+{
+	struct ctr_struct *ctr = file->private_data;
+
+	if (*ppos + nbytes > ctr->bufsz)
+		nbytes = ctr->bufsz - *ppos;
+	if (copy_to_user(buf, ctr->buf + *ppos, nbytes))
+		return -EFAULT;
+	*ppos += nbytes;
+	return nbytes;
+}
+
+static int cpuset_tasks_release(struct inode *unused_inode, struct file *file)
+{
+	struct ctr_struct *ctr;
+
+	if (file->f_mode & FMODE_READ) {
+		ctr = file->private_data;
+		kfree(ctr->buf);
+		kfree(ctr);
+	}
+	return 0;
+}
+
+/*
+ * for the common functions, 'private' gives the type of file
+ */
+
+static struct cftype cft_tasks = {
+	.name = "tasks",
+	.open = cpuset_tasks_open,
+	.read = cpuset_tasks_read,
+	.release = cpuset_tasks_release,
+	.private = FILE_TASKLIST,
+};
+
+static struct cftype cft_cpus = {
+	.name = "cpus",
+	.private = FILE_CPULIST,
+};
+
+static struct cftype cft_mems = {
+	.name = "mems",
+	.private = FILE_MEMLIST,
+};
+
+static struct cftype cft_cpu_exclusive = {
+	.name = "cpu_exclusive",
+	.private = FILE_CPU_EXCLUSIVE,
+};
+
+static struct cftype cft_mem_exclusive = {
+	.name = "mem_exclusive",
+	.private = FILE_MEM_EXCLUSIVE,
+};
+
+static struct cftype cft_notify_on_release = {
+	.name = "notify_on_release",
+	.private = FILE_NOTIFY_ON_RELEASE,
+};
+
+static int cpuset_populate_dir(struct dentry *cs_dentry)
+{
+	int err;
+
+	if ((err = cpuset_add_file(cs_dentry, &cft_cpus)) < 0)
+		return err;
+	if ((err = cpuset_add_file(cs_dentry, &cft_mems)) < 0)
+		return err;
+	if ((err = cpuset_add_file(cs_dentry, &cft_cpu_exclusive)) < 0)
+		return err;
+	if ((err = cpuset_add_file(cs_dentry, &cft_mem_exclusive)) < 0)
+		return err;
+	if ((err = cpuset_add_file(cs_dentry, &cft_notify_on_release)) < 0)
+		return err;
+	if ((err = cpuset_add_file(cs_dentry, &cft_tasks)) < 0)
+		return err;
+	return 0;
+}
+
+/*
+ *	cpuset_create - create a cpuset
+ *	parent:	cpuset that will be parent of the new cpuset.
+ *	name:		name of the new cpuset. Will be strcpy'ed.
+ *	mode:		mode to set on new inode
+ *
+ *	Must be called with the semaphore on the parent inode held
+ */
+
+static long cpuset_create(struct cpuset *parent, const char *name, int mode)
+{
+	struct cpuset *cs;
+	int err;
+
+	cs = kmalloc(sizeof(*cs), GFP_KERNEL);
+	if (!cs)
+		return -ENOMEM;
+
+	down(&manage_sem);
+	refresh_mems();
+	cs->flags = 0;
+	if (notify_on_release(parent))
+		set_bit(CS_NOTIFY_ON_RELEASE, &cs->flags);
+	cs->cpus_allowed = CPU_MASK_NONE;
+	cs->mems_allowed = NODE_MASK_NONE;
+	atomic_set(&cs->count, 0);
+	INIT_LIST_HEAD(&cs->sibling);
+	INIT_LIST_HEAD(&cs->children);
+	atomic_inc(&cpuset_mems_generation);
+	cs->mems_generation = atomic_read(&cpuset_mems_generation);
+
+	cs->parent = parent;
+
+	down(&callback_sem);
+	list_add(&cs->sibling, &cs->parent->children);
+	up(&callback_sem);
+
+	err = cpuset_create_dir(cs, name, mode);
+	if (err < 0)
+		goto err;
+
+	/*
+	 * Release manage_sem before cpuset_populate_dir() because it
+	 * will down() this new directory's i_sem and if we race with
+	 * another mkdir, we might deadlock.
+	 */
+	up(&manage_sem);
+
+	err = cpuset_populate_dir(cs->dentry);
+	/* If err < 0, we have a half-filled directory - oh well ;) */
+	return 0;
+err:
+	list_del(&cs->sibling);
+	up(&manage_sem);
+	kfree(cs);
+	return err;
+}
+
+static int cpuset_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	struct cpuset *c_parent = dentry->d_parent->d_fsdata;
+
+	/* the vfs holds inode->i_sem already */
+	return cpuset_create(c_parent, dentry->d_name.name, mode | S_IFDIR);
+}
+
+static int cpuset_rmdir(struct inode *unused_dir, struct dentry *dentry)
+{
+	struct cpuset *cs = dentry->d_fsdata;
+	struct dentry *d;
+	struct cpuset *parent;
+	char *pathbuf = NULL;
+
+	/* the vfs holds both inode->i_sem already */
+
+	down(&manage_sem);
+	refresh_mems();
+	if (atomic_read(&cs->count) > 0) {
+		up(&manage_sem);
+		return -EBUSY;
+	}
+	if (!list_empty(&cs->children)) {
+		up(&manage_sem);
+		return -EBUSY;
+	}
+	parent = cs->parent;
+	down(&callback_sem);
+	set_bit(CS_REMOVED, &cs->flags);
+	if (is_cpu_exclusive(cs))
+		update_cpu_domains(cs);
+	list_del(&cs->sibling);	/* delete my sibling from parent->children */
+	spin_lock(&cs->dentry->d_lock);
+	d = dget(cs->dentry);
+	cs->dentry = NULL;
+	spin_unlock(&d->d_lock);
+	cpuset_d_remove_dir(d);
+	dput(d);
+	up(&callback_sem);
+	if (list_empty(&parent->children))
+		check_for_release(parent, &pathbuf);
+	up(&manage_sem);
+	cpuset_release_agent(pathbuf);
+	return 0;
+}
+
+/**
+ * cpuset_init - initialize cpusets at system boot
+ *
+ * Description: Initialize top_cpuset and the cpuset internal file system,
+ **/
+
+int __init cpuset_init(void)
+{
+	struct dentry *root;
+	int err;
+
+	top_cpuset.cpus_allowed = CPU_MASK_ALL;
+	top_cpuset.mems_allowed = NODE_MASK_ALL;
+
+	atomic_inc(&cpuset_mems_generation);
+	top_cpuset.mems_generation = atomic_read(&cpuset_mems_generation);
+
+	init_task.cpuset = &top_cpuset;
+
+	err = register_filesystem(&cpuset_fs_type);
+	if (err < 0)
+		goto out;
+	cpuset_mount = kern_mount(&cpuset_fs_type);
+	if (IS_ERR(cpuset_mount)) {
+		printk(KERN_ERR "cpuset: could not mount!\n");
+		err = PTR_ERR(cpuset_mount);
+		cpuset_mount = NULL;
+		goto out;
+	}
+	root = cpuset_mount->mnt_sb->s_root;
+	root->d_fsdata = &top_cpuset;
+	root->d_inode->i_nlink++;
+	top_cpuset.dentry = root;
+	root->d_inode->i_op = &cpuset_dir_inode_operations;
+	err = cpuset_populate_dir(root);
+out:
+	return err;
+}
+
+/**
+ * cpuset_init_smp - initialize cpus_allowed
+ *
+ * Description: Finish top cpuset after cpu, node maps are initialized
+ **/
+
+void __init cpuset_init_smp(void)
+{
+	top_cpuset.cpus_allowed = cpu_online_map;
+	top_cpuset.mems_allowed = node_online_map;
+}
+
+/**
+ * cpuset_fork - attach newly forked task to its parents cpuset.
+ * @tsk: pointer to task_struct of forking parent process.
+ *
+ * Description: A task inherits its parent's cpuset at fork().
+ *
+ * A pointer to the shared cpuset was automatically copied in fork.c
+ * by dup_task_struct().  However, we ignore that copy, since it was
+ * not made under the protection of task_lock(), so might no longer be
+ * a valid cpuset pointer.  attach_task() might have already changed
+ * current->cpuset, allowing the previously referenced cpuset to
+ * be removed and freed.  Instead, we task_lock(current) and copy
+ * its present value of current->cpuset for our freshly forked child.
+ *
+ * At the point that cpuset_fork() is called, 'current' is the parent
+ * task, and the passed argument 'child' points to the child task.
+ **/
+
+void cpuset_fork(struct task_struct *child)
+{
+	task_lock(current);
+	child->cpuset = current->cpuset;
+	atomic_inc(&child->cpuset->count);
+	task_unlock(current);
+}
+
+/**
+ * cpuset_exit - detach cpuset from exiting task
+ * @tsk: pointer to task_struct of exiting process
+ *
+ * Description: Detach cpuset from @tsk and release it.
+ *
+ * Note that cpusets marked notify_on_release force every task in
+ * them to take the global manage_sem semaphore when exiting.
+ * This could impact scaling on very large systems.  Be reluctant to
+ * use notify_on_release cpusets where very high task exit scaling
+ * is required on large systems.
+ *
+ * Don't even think about derefencing 'cs' after the cpuset use count
+ * goes to zero, except inside a critical section guarded by manage_sem
+ * or callback_sem.   Otherwise a zero cpuset use count is a license to
+ * any other task to nuke the cpuset immediately, via cpuset_rmdir().
+ *
+ * This routine has to take manage_sem, not callback_sem, because
+ * it is holding that semaphore while calling check_for_release(),
+ * which calls kmalloc(), so can't be called holding callback__sem().
+ *
+ * We don't need to task_lock() this reference to tsk->cpuset,
+ * because tsk is already marked PF_EXITING, so attach_task() won't
+ * mess with it.
+ **/
+
+void cpuset_exit(struct task_struct *tsk)
+{
+	struct cpuset *cs;
+
+	BUG_ON(!(tsk->flags & PF_EXITING));
+
+	cs = tsk->cpuset;
+	tsk->cpuset = NULL;
+
+	if (notify_on_release(cs)) {
+		char *pathbuf = NULL;
+
+		down(&manage_sem);
+		if (atomic_dec_and_test(&cs->count))
+			check_for_release(cs, &pathbuf);
+		up(&manage_sem);
+		cpuset_release_agent(pathbuf);
+	} else {
+		atomic_dec(&cs->count);
+	}
+}
+
+/**
+ * cpuset_cpus_allowed - return cpus_allowed mask from a tasks cpuset.
+ * @tsk: pointer to task_struct from which to obtain cpuset->cpus_allowed.
+ *
+ * Description: Returns the cpumask_t cpus_allowed of the cpuset
+ * attached to the specified @tsk.  Guaranteed to return some non-empty
+ * subset of cpu_online_map, even if this means going outside the
+ * tasks cpuset.
+ **/
+
+cpumask_t cpuset_cpus_allowed(const struct task_struct *tsk)
+{
+	cpumask_t mask;
+
+	down(&callback_sem);
+	task_lock((struct task_struct *)tsk);
+	guarantee_online_cpus(tsk->cpuset, &mask);
+	task_unlock((struct task_struct *)tsk);
+	up(&callback_sem);
+
+	return mask;
+}
+
+void cpuset_init_current_mems_allowed(void)
+{
+	current->mems_allowed = NODE_MASK_ALL;
+}
+
+/**
+ * cpuset_update_current_mems_allowed - update mems parameters to new values
+ *
+ * If the current tasks cpusets mems_allowed changed behind our backs,
+ * update current->mems_allowed and mems_generation to the new value.
+ * Do not call this routine if in_interrupt().
+ *
+ * Call without callback_sem or task_lock() held.  May be called
+ * with or without manage_sem held.  Unless exiting, it will acquire
+ * task_lock().  Also might acquire callback_sem during call to
+ * refresh_mems().
+ */
+
+void cpuset_update_current_mems_allowed(void)
+{
+	struct cpuset *cs;
+	int need_to_refresh = 0;
+
+	task_lock(current);
+	cs = current->cpuset;
+	if (!cs)
+		goto done;
+	if (current->cpuset_mems_generation != cs->mems_generation)
+		need_to_refresh = 1;
+done:
+	task_unlock(current);
+	if (need_to_refresh)
+		refresh_mems();
+}
+
+/**
+ * cpuset_restrict_to_mems_allowed - limit nodes to current mems_allowed
+ * @nodes: pointer to a node bitmap that is and-ed with mems_allowed
+ */
+void cpuset_restrict_to_mems_allowed(unsigned long *nodes)
+{
+	bitmap_and(nodes, nodes, nodes_addr(current->mems_allowed),
+							MAX_NUMNODES);
+}
+
+/**
+ * cpuset_zonelist_valid_mems_allowed - check zonelist vs. curremt mems_allowed
+ * @zl: the zonelist to be checked
+ *
+ * Are any of the nodes on zonelist zl allowed in current->mems_allowed?
+ */
+int cpuset_zonelist_valid_mems_allowed(struct zonelist *zl)
+{
+	int i;
+
+	for (i = 0; zl->zones[i]; i++) {
+		int nid = zl->zones[i]->zone_pgdat->node_id;
+
+		if (node_isset(nid, current->mems_allowed))
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * nearest_exclusive_ancestor() - Returns the nearest mem_exclusive
+ * ancestor to the specified cpuset.  Call holding callback_sem.
+ * If no ancestor is mem_exclusive (an unusual configuration), then
+ * returns the root cpuset.
+ */
+static const struct cpuset *nearest_exclusive_ancestor(const struct cpuset *cs)
+{
+	while (!is_mem_exclusive(cs) && cs->parent)
+		cs = cs->parent;
+	return cs;
+}
+
+/**
+ * cpuset_zone_allowed - Can we allocate memory on zone z's memory node?
+ * @z: is this zone on an allowed node?
+ * @gfp_mask: memory allocation flags (we use __GFP_HARDWALL)
+ *
+ * If we're in interrupt, yes, we can always allocate.  If zone
+ * z's node is in our tasks mems_allowed, yes.  If it's not a
+ * __GFP_HARDWALL request and this zone's nodes is in the nearest
+ * mem_exclusive cpuset ancestor to this tasks cpuset, yes.
+ * Otherwise, no.
+ *
+ * GFP_USER allocations are marked with the __GFP_HARDWALL bit,
+ * and do not allow allocations outside the current tasks cpuset.
+ * GFP_KERNEL allocations are not so marked, so can escape to the
+ * nearest mem_exclusive ancestor cpuset.
+ *
+ * Scanning up parent cpusets requires callback_sem.  The __alloc_pages()
+ * routine only calls here with __GFP_HARDWALL bit _not_ set if
+ * it's a GFP_KERNEL allocation, and all nodes in the current tasks
+ * mems_allowed came up empty on the first pass over the zonelist.
+ * So only GFP_KERNEL allocations, if all nodes in the cpuset are
+ * short of memory, might require taking the callback_sem semaphore.
+ *
+ * The first loop over the zonelist in mm/page_alloc.c:__alloc_pages()
+ * calls here with __GFP_HARDWALL always set in gfp_mask, enforcing
+ * hardwall cpusets - no allocation on a node outside the cpuset is
+ * allowed (unless in interrupt, of course).
+ *
+ * The second loop doesn't even call here for GFP_ATOMIC requests
+ * (if the __alloc_pages() local variable 'wait' is set).  That check
+ * and the checks below have the combined affect in the second loop of
+ * the __alloc_pages() routine that:
+ *	in_interrupt - any node ok (current task context irrelevant)
+ *	GFP_ATOMIC   - any node ok
+ *	GFP_KERNEL   - any node in enclosing mem_exclusive cpuset ok
+ *	GFP_USER     - only nodes in current tasks mems allowed ok.
+ **/
+
+int cpuset_zone_allowed(struct zone *z, gfp_t gfp_mask)
+{
+	int node;			/* node that zone z is on */
+	const struct cpuset *cs;	/* current cpuset ancestors */
+	int allowed = 1;		/* is allocation in zone z allowed? */
+
+	if (in_interrupt())
+		return 1;
+	node = z->zone_pgdat->node_id;
+	if (node_isset(node, current->mems_allowed))
+		return 1;
+	if (gfp_mask & __GFP_HARDWALL)	/* If hardwall request, stop here */
+		return 0;
+
+	if (current->flags & PF_EXITING) /* Let dying task have memory */
+		return 1;
+
+	/* Not hardwall and node outside mems_allowed: scan up cpusets */
+	down(&callback_sem);
+
+	task_lock(current);
+	cs = nearest_exclusive_ancestor(current->cpuset);
+	task_unlock(current);
+
+	allowed = node_isset(node, cs->mems_allowed);
+	up(&callback_sem);
+	return allowed;
+}
+
+/**
+ * cpuset_excl_nodes_overlap - Do we overlap @p's mem_exclusive ancestors?
+ * @p: pointer to task_struct of some other task.
+ *
+ * Description: Return true if the nearest mem_exclusive ancestor
+ * cpusets of tasks @p and current overlap.  Used by oom killer to
+ * determine if task @p's memory usage might impact the memory
+ * available to the current task.
+ *
+ * Acquires callback_sem - not suitable for calling from a fast path.
+ **/
+
+int cpuset_excl_nodes_overlap(const struct task_struct *p)
+{
+	const struct cpuset *cs1, *cs2;	/* my and p's cpuset ancestors */
+	int overlap = 0;		/* do cpusets overlap? */
+
+	down(&callback_sem);
+
+	task_lock(current);
+	if (current->flags & PF_EXITING) {
+		task_unlock(current);
+		goto done;
+	}
+	cs1 = nearest_exclusive_ancestor(current->cpuset);
+	task_unlock(current);
+
+	task_lock((struct task_struct *)p);
+	if (p->flags & PF_EXITING) {
+		task_unlock((struct task_struct *)p);
+		goto done;
+	}
+	cs2 = nearest_exclusive_ancestor(p->cpuset);
+	task_unlock((struct task_struct *)p);
+
+	overlap = nodes_intersects(cs1->mems_allowed, cs2->mems_allowed);
+done:
+	up(&callback_sem);
+
+	return overlap;
+}
+
+/*
+ * proc_cpuset_show()
+ *  - Print tasks cpuset path into seq_file.
+ *  - Used for /proc/<pid>/cpuset.
+ *  - No need to task_lock(tsk) on this tsk->cpuset reference, as it
+ *    doesn't really matter if tsk->cpuset changes after we read it,
+ *    and we take manage_sem, keeping attach_task() from changing it
+ *    anyway.
+ */
+
+static int proc_cpuset_show(struct seq_file *m, void *v)
+{
+	struct cpuset *cs;
+	struct task_struct *tsk;
+	char *buf;
+	int retval = 0;
+
+	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	tsk = m->private;
+	down(&manage_sem);
+	cs = tsk->cpuset;
+	if (!cs) {
+		retval = -EINVAL;
+		goto out;
+	}
+
+	retval = cpuset_path(cs, buf, PAGE_SIZE);
+	if (retval < 0)
+		goto out;
+	seq_puts(m, buf);
+	seq_putc(m, '\n');
+out:
+	up(&manage_sem);
+	kfree(buf);
+	return retval;
+}
+
+static int cpuset_open(struct inode *inode, struct file *file)
+{
+	struct task_struct *tsk = PROC_I(inode)->task;
+	return single_open(file, proc_cpuset_show, tsk);
+}
+
+struct file_operations proc_cpuset_operations = {
+	.open		= cpuset_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+/* Display task cpus_allowed, mems_allowed in /proc/<pid>/status file. */
+char *cpuset_task_status_allowed(struct task_struct *task, char *buffer)
+{
+	buffer += sprintf(buffer, "Cpus_allowed:\t");
+	buffer += cpumask_scnprintf(buffer, PAGE_SIZE, task->cpus_allowed);
+	buffer += sprintf(buffer, "\n");
+	buffer += sprintf(buffer, "Mems_allowed:\t");
+	buffer += nodemask_scnprintf(buffer, PAGE_SIZE, task->mems_allowed);
+	buffer += sprintf(buffer, "\n");
+	return buffer;
+}
diff -urN oldtree/kernel/fork.c newtree/kernel/fork.c
--- oldtree/kernel/fork.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/kernel/fork.c	2006-02-13 19:20:01.268319488 -0500
@@ -581,12 +581,12 @@
 	atomic_set(&newf->count, 1);
 
 	spin_lock_init(&newf->file_lock);
+	newf->next_fd = 0;
 	fdt = &newf->fdtab;
-	fdt->next_fd = 0;
 	fdt->max_fds = NR_OPEN_DEFAULT;
-	fdt->max_fdset = __FD_SETSIZE;
-	fdt->close_on_exec = &newf->close_on_exec_init;
-	fdt->open_fds = &newf->open_fds_init;
+	fdt->max_fdset = 8 * sizeof(embedded_fd_set);
+	fdt->close_on_exec = (fd_set *)&newf->close_on_exec_init;
+	fdt->open_fds = (fd_set *)&newf->open_fds_init;
 	fdt->fd = &newf->fd_array[0];
 	INIT_RCU_HEAD(&fdt->rcu);
 	fdt->free_files = NULL;
diff -urN oldtree/kernel/fork.c.orig newtree/kernel/fork.c.orig
--- oldtree/kernel/fork.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/fork.c.orig	2006-02-13 19:20:01.270319184 -0500
@@ -0,0 +1,1313 @@
+/*
+ *  linux/kernel/fork.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ *  'fork.c' contains the help-routines for the 'fork' system call
+ * (see also entry.S and others).
+ * Fork is rather simple, once you get the hang of it, but the memory
+ * management can be a bitch. See 'mm/memory.c': 'copy_page_range()'
+ */
+
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/unistd.h>
+#include <linux/smp_lock.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/completion.h>
+#include <linux/namespace.h>
+#include <linux/personality.h>
+#include <linux/mempolicy.h>
+#include <linux/sem.h>
+#include <linux/file.h>
+#include <linux/key.h>
+#include <linux/binfmts.h>
+#include <linux/mman.h>
+#include <linux/fs.h>
+#include <linux/cpu.h>
+#include <linux/cpuset.h>
+#include <linux/security.h>
+#include <linux/swap.h>
+#include <linux/syscalls.h>
+#include <linux/jiffies.h>
+#include <linux/futex.h>
+#include <linux/rcupdate.h>
+#include <linux/ptrace.h>
+#include <linux/mount.h>
+#include <linux/audit.h>
+#include <linux/profile.h>
+#include <linux/rmap.h>
+#include <linux/acct.h>
+#include <linux/cn_proc.h>
+
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/uaccess.h>
+#include <asm/mmu_context.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+
+/*
+ * Protected counters by write_lock_irq(&tasklist_lock)
+ */
+unsigned long total_forks;	/* Handle normal Linux uptimes. */
+int nr_threads; 		/* The idle threads do not count.. */
+
+int max_threads;		/* tunable limit on nr_threads */
+
+DEFINE_PER_CPU(unsigned long, process_counts) = 0;
+
+ __cacheline_aligned DEFINE_RWLOCK(tasklist_lock);  /* outer */
+
+EXPORT_SYMBOL(tasklist_lock);
+
+int nr_processes(void)
+{
+	int cpu;
+	int total = 0;
+
+	for_each_online_cpu(cpu)
+		total += per_cpu(process_counts, cpu);
+
+	return total;
+}
+
+#ifndef __HAVE_ARCH_TASK_STRUCT_ALLOCATOR
+# define alloc_task_struct()	kmem_cache_alloc(task_struct_cachep, GFP_KERNEL)
+# define free_task_struct(tsk)	kmem_cache_free(task_struct_cachep, (tsk))
+static kmem_cache_t *task_struct_cachep;
+#endif
+
+/* SLAB cache for signal_struct structures (tsk->signal) */
+kmem_cache_t *signal_cachep;
+
+/* SLAB cache for sighand_struct structures (tsk->sighand) */
+kmem_cache_t *sighand_cachep;
+
+/* SLAB cache for files_struct structures (tsk->files) */
+kmem_cache_t *files_cachep;
+
+/* SLAB cache for fs_struct structures (tsk->fs) */
+kmem_cache_t *fs_cachep;
+
+/* SLAB cache for vm_area_struct structures */
+kmem_cache_t *vm_area_cachep;
+
+/* SLAB cache for mm_struct structures (tsk->mm) */
+static kmem_cache_t *mm_cachep;
+
+void free_task(struct task_struct *tsk)
+{
+	free_thread_info(tsk->thread_info);
+	free_task_struct(tsk);
+}
+EXPORT_SYMBOL(free_task);
+
+void __put_task_struct(struct task_struct *tsk)
+{
+	WARN_ON(!(tsk->exit_state & (EXIT_DEAD | EXIT_ZOMBIE)));
+	WARN_ON(atomic_read(&tsk->usage));
+	WARN_ON(tsk == current);
+
+	if (unlikely(tsk->audit_context))
+		audit_free(tsk);
+	security_task_free(tsk);
+	free_uid(tsk->user);
+	put_group_info(tsk->group_info);
+
+	if (!profile_handoff_task(tsk))
+		free_task(tsk);
+}
+
+void __init fork_init(unsigned long mempages)
+{
+#ifndef __HAVE_ARCH_TASK_STRUCT_ALLOCATOR
+#ifndef ARCH_MIN_TASKALIGN
+#define ARCH_MIN_TASKALIGN	L1_CACHE_BYTES
+#endif
+	/* create a slab on which task_structs can be allocated */
+	task_struct_cachep =
+		kmem_cache_create("task_struct", sizeof(struct task_struct),
+			ARCH_MIN_TASKALIGN, SLAB_PANIC, NULL, NULL);
+#endif
+
+	/*
+	 * The default maximum number of threads is set to a safe
+	 * value: the thread structures can take up at most half
+	 * of memory.
+	 */
+	max_threads = mempages / (8 * THREAD_SIZE / PAGE_SIZE);
+
+	/*
+	 * we need to allow at least 20 threads to boot a system
+	 */
+	if(max_threads < 20)
+		max_threads = 20;
+
+	init_task.signal->rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;
+	init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
+	init_task.signal->rlim[RLIMIT_SIGPENDING] =
+		init_task.signal->rlim[RLIMIT_NPROC];
+}
+
+static struct task_struct *dup_task_struct(struct task_struct *orig)
+{
+	struct task_struct *tsk;
+	struct thread_info *ti;
+
+	prepare_to_copy(orig);
+
+	tsk = alloc_task_struct();
+	if (!tsk)
+		return NULL;
+
+	ti = alloc_thread_info(tsk);
+	if (!ti) {
+		free_task_struct(tsk);
+		return NULL;
+	}
+
+	*tsk = *orig;
+	tsk->thread_info = ti;
+	setup_thread_stack(tsk, orig);
+
+	/* One for us, one for whoever does the "release_task()" (usually parent) */
+	atomic_set(&tsk->usage,2);
+	atomic_set(&tsk->fs_excl, 0);
+	return tsk;
+}
+
+#ifdef CONFIG_MMU
+static inline int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
+{
+	struct vm_area_struct *mpnt, *tmp, **pprev;
+	struct rb_node **rb_link, *rb_parent;
+	int retval;
+	unsigned long charge;
+	struct mempolicy *pol;
+
+	down_write(&oldmm->mmap_sem);
+	flush_cache_mm(oldmm);
+	down_write(&mm->mmap_sem);
+
+	mm->locked_vm = 0;
+	mm->mmap = NULL;
+	mm->mmap_cache = NULL;
+	mm->free_area_cache = oldmm->mmap_base;
+	mm->cached_hole_size = ~0UL;
+	mm->map_count = 0;
+	cpus_clear(mm->cpu_vm_mask);
+	mm->mm_rb = RB_ROOT;
+	rb_link = &mm->mm_rb.rb_node;
+	rb_parent = NULL;
+	pprev = &mm->mmap;
+
+	for (mpnt = oldmm->mmap; mpnt; mpnt = mpnt->vm_next) {
+		struct file *file;
+
+		if (mpnt->vm_flags & VM_DONTCOPY) {
+			long pages = vma_pages(mpnt);
+			mm->total_vm -= pages;
+			vm_stat_account(mm, mpnt->vm_flags, mpnt->vm_file,
+								-pages);
+			continue;
+		}
+		charge = 0;
+		if (mpnt->vm_flags & VM_ACCOUNT) {
+			unsigned int len = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
+			if (security_vm_enough_memory(len))
+				goto fail_nomem;
+			charge = len;
+		}
+		tmp = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+		if (!tmp)
+			goto fail_nomem;
+		*tmp = *mpnt;
+		pol = mpol_copy(vma_policy(mpnt));
+		retval = PTR_ERR(pol);
+		if (IS_ERR(pol))
+			goto fail_nomem_policy;
+		vma_set_policy(tmp, pol);
+		tmp->vm_flags &= ~VM_LOCKED;
+		tmp->vm_mm = mm;
+		tmp->vm_next = NULL;
+		anon_vma_link(tmp);
+		file = tmp->vm_file;
+		if (file) {
+			struct inode *inode = file->f_dentry->d_inode;
+			get_file(file);
+			if (tmp->vm_flags & VM_DENYWRITE)
+				atomic_dec(&inode->i_writecount);
+      
+			/* insert tmp into the share list, just after mpnt */
+			spin_lock(&file->f_mapping->i_mmap_lock);
+			tmp->vm_truncate_count = mpnt->vm_truncate_count;
+			flush_dcache_mmap_lock(file->f_mapping);
+			vma_prio_tree_add(tmp, mpnt);
+			flush_dcache_mmap_unlock(file->f_mapping);
+			spin_unlock(&file->f_mapping->i_mmap_lock);
+		}
+
+		/*
+		 * Link in the new vma and copy the page table entries.
+		 */
+		*pprev = tmp;
+		pprev = &tmp->vm_next;
+
+		__vma_link_rb(mm, tmp, rb_link, rb_parent);
+		rb_link = &tmp->vm_rb.rb_right;
+		rb_parent = &tmp->vm_rb;
+
+		mm->map_count++;
+		retval = copy_page_range(mm, oldmm, mpnt);
+
+		if (tmp->vm_ops && tmp->vm_ops->open)
+			tmp->vm_ops->open(tmp);
+
+		if (retval)
+			goto out;
+	}
+	retval = 0;
+out:
+	up_write(&mm->mmap_sem);
+	flush_tlb_mm(oldmm);
+	up_write(&oldmm->mmap_sem);
+	return retval;
+fail_nomem_policy:
+	kmem_cache_free(vm_area_cachep, tmp);
+fail_nomem:
+	retval = -ENOMEM;
+	vm_unacct_memory(charge);
+	goto out;
+}
+
+static inline int mm_alloc_pgd(struct mm_struct * mm)
+{
+	mm->pgd = pgd_alloc(mm);
+	if (unlikely(!mm->pgd))
+		return -ENOMEM;
+	return 0;
+}
+
+static inline void mm_free_pgd(struct mm_struct * mm)
+{
+	pgd_free(mm->pgd);
+}
+#else
+#define dup_mmap(mm, oldmm)	(0)
+#define mm_alloc_pgd(mm)	(0)
+#define mm_free_pgd(mm)
+#endif /* CONFIG_MMU */
+
+ __cacheline_aligned_in_smp DEFINE_SPINLOCK(mmlist_lock);
+
+#define allocate_mm()	(kmem_cache_alloc(mm_cachep, SLAB_KERNEL))
+#define free_mm(mm)	(kmem_cache_free(mm_cachep, (mm)))
+
+#include <linux/init_task.h>
+
+static struct mm_struct * mm_init(struct mm_struct * mm)
+{
+	atomic_set(&mm->mm_users, 1);
+	atomic_set(&mm->mm_count, 1);
+	init_rwsem(&mm->mmap_sem);
+	INIT_LIST_HEAD(&mm->mmlist);
+	mm->core_waiters = 0;
+	mm->nr_ptes = 0;
+	set_mm_counter(mm, file_rss, 0);
+	set_mm_counter(mm, anon_rss, 0);
+	spin_lock_init(&mm->page_table_lock);
+	rwlock_init(&mm->ioctx_list_lock);
+	mm->ioctx_list = NULL;
+	mm->free_area_cache = TASK_UNMAPPED_BASE;
+	mm->cached_hole_size = ~0UL;
+
+	if (likely(!mm_alloc_pgd(mm))) {
+		mm->def_flags = 0;
+		return mm;
+	}
+	free_mm(mm);
+	return NULL;
+}
+
+/*
+ * Allocate and initialize an mm_struct.
+ */
+struct mm_struct * mm_alloc(void)
+{
+	struct mm_struct * mm;
+
+	mm = allocate_mm();
+	if (mm) {
+		memset(mm, 0, sizeof(*mm));
+		mm = mm_init(mm);
+	}
+	return mm;
+}
+
+/*
+ * Called when the last reference to the mm
+ * is dropped: either by a lazy thread or by
+ * mmput. Free the page directory and the mm.
+ */
+void fastcall __mmdrop(struct mm_struct *mm)
+{
+	BUG_ON(mm == &init_mm);
+	mm_free_pgd(mm);
+	destroy_context(mm);
+	free_mm(mm);
+}
+
+/*
+ * Decrement the use count and release all resources for an mm.
+ */
+void mmput(struct mm_struct *mm)
+{
+	if (atomic_dec_and_test(&mm->mm_users)) {
+		exit_aio(mm);
+		exit_mmap(mm);
+		if (!list_empty(&mm->mmlist)) {
+			spin_lock(&mmlist_lock);
+			list_del(&mm->mmlist);
+			spin_unlock(&mmlist_lock);
+		}
+		put_swap_token(mm);
+		mmdrop(mm);
+	}
+}
+EXPORT_SYMBOL_GPL(mmput);
+
+/**
+ * get_task_mm - acquire a reference to the task's mm
+ *
+ * Returns %NULL if the task has no mm.  Checks PF_BORROWED_MM (meaning
+ * this kernel workthread has transiently adopted a user mm with use_mm,
+ * to do its AIO) is not set and if so returns a reference to it, after
+ * bumping up the use count.  User must release the mm via mmput()
+ * after use.  Typically used by /proc and ptrace.
+ */
+struct mm_struct *get_task_mm(struct task_struct *task)
+{
+	struct mm_struct *mm;
+
+	task_lock(task);
+	mm = task->mm;
+	if (mm) {
+		if (task->flags & PF_BORROWED_MM)
+			mm = NULL;
+		else
+			atomic_inc(&mm->mm_users);
+	}
+	task_unlock(task);
+	return mm;
+}
+EXPORT_SYMBOL_GPL(get_task_mm);
+
+/* Please note the differences between mmput and mm_release.
+ * mmput is called whenever we stop holding onto a mm_struct,
+ * error success whatever.
+ *
+ * mm_release is called after a mm_struct has been removed
+ * from the current process.
+ *
+ * This difference is important for error handling, when we
+ * only half set up a mm_struct for a new process and need to restore
+ * the old one.  Because we mmput the new mm_struct before
+ * restoring the old one. . .
+ * Eric Biederman 10 January 1998
+ */
+void mm_release(struct task_struct *tsk, struct mm_struct *mm)
+{
+	struct completion *vfork_done = tsk->vfork_done;
+
+	/* Get rid of any cached register state */
+	deactivate_mm(tsk, mm);
+
+	/* notify parent sleeping on vfork() */
+	if (vfork_done) {
+		tsk->vfork_done = NULL;
+		complete(vfork_done);
+	}
+	if (tsk->clear_child_tid && atomic_read(&mm->mm_users) > 1) {
+		u32 __user * tidptr = tsk->clear_child_tid;
+		tsk->clear_child_tid = NULL;
+
+		/*
+		 * We don't check the error code - if userspace has
+		 * not set up a proper pointer then tough luck.
+		 */
+		put_user(0, tidptr);
+		sys_futex(tidptr, FUTEX_WAKE, 1, NULL, NULL, 0);
+	}
+}
+
+static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
+{
+	struct mm_struct * mm, *oldmm;
+	int retval;
+
+	tsk->min_flt = tsk->maj_flt = 0;
+	tsk->nvcsw = tsk->nivcsw = 0;
+
+	tsk->mm = NULL;
+	tsk->active_mm = NULL;
+
+	/*
+	 * Are we cloning a kernel thread?
+	 *
+	 * We need to steal a active VM for that..
+	 */
+	oldmm = current->mm;
+	if (!oldmm)
+		return 0;
+
+	if (clone_flags & CLONE_VM) {
+		atomic_inc(&oldmm->mm_users);
+		mm = oldmm;
+		goto good_mm;
+	}
+
+	retval = -ENOMEM;
+	mm = allocate_mm();
+	if (!mm)
+		goto fail_nomem;
+
+	/* Copy the current MM stuff.. */
+	memcpy(mm, oldmm, sizeof(*mm));
+	if (!mm_init(mm))
+		goto fail_nomem;
+
+	if (init_new_context(tsk,mm))
+		goto fail_nocontext;
+
+	retval = dup_mmap(mm, oldmm);
+	if (retval)
+		goto free_pt;
+
+	mm->hiwater_rss = get_mm_rss(mm);
+	mm->hiwater_vm = mm->total_vm;
+
+good_mm:
+	tsk->mm = mm;
+	tsk->active_mm = mm;
+	return 0;
+
+free_pt:
+	mmput(mm);
+fail_nomem:
+	return retval;
+
+fail_nocontext:
+	/*
+	 * If init_new_context() failed, we cannot use mmput() to free the mm
+	 * because it calls destroy_context()
+	 */
+	mm_free_pgd(mm);
+	free_mm(mm);
+	return retval;
+}
+
+static inline struct fs_struct *__copy_fs_struct(struct fs_struct *old)
+{
+	struct fs_struct *fs = kmem_cache_alloc(fs_cachep, GFP_KERNEL);
+	/* We don't need to lock fs - think why ;-) */
+	if (fs) {
+		atomic_set(&fs->count, 1);
+		rwlock_init(&fs->lock);
+		fs->umask = old->umask;
+		read_lock(&old->lock);
+		fs->rootmnt = mntget(old->rootmnt);
+		fs->root = dget(old->root);
+		fs->pwdmnt = mntget(old->pwdmnt);
+		fs->pwd = dget(old->pwd);
+		if (old->altroot) {
+			fs->altrootmnt = mntget(old->altrootmnt);
+			fs->altroot = dget(old->altroot);
+		} else {
+			fs->altrootmnt = NULL;
+			fs->altroot = NULL;
+		}
+		read_unlock(&old->lock);
+	}
+	return fs;
+}
+
+struct fs_struct *copy_fs_struct(struct fs_struct *old)
+{
+	return __copy_fs_struct(old);
+}
+
+EXPORT_SYMBOL_GPL(copy_fs_struct);
+
+static inline int copy_fs(unsigned long clone_flags, struct task_struct * tsk)
+{
+	if (clone_flags & CLONE_FS) {
+		atomic_inc(&current->fs->count);
+		return 0;
+	}
+	tsk->fs = __copy_fs_struct(current->fs);
+	if (!tsk->fs)
+		return -ENOMEM;
+	return 0;
+}
+
+static int count_open_files(struct fdtable *fdt)
+{
+	int size = fdt->max_fdset;
+	int i;
+
+	/* Find the last open fd */
+	for (i = size/(8*sizeof(long)); i > 0; ) {
+		if (fdt->open_fds->fds_bits[--i])
+			break;
+	}
+	i = (i+1) * 8 * sizeof(long);
+	return i;
+}
+
+static struct files_struct *alloc_files(void)
+{
+	struct files_struct *newf;
+	struct fdtable *fdt;
+
+	newf = kmem_cache_alloc(files_cachep, SLAB_KERNEL);
+	if (!newf)
+		goto out;
+
+	atomic_set(&newf->count, 1);
+
+	spin_lock_init(&newf->file_lock);
+	fdt = &newf->fdtab;
+	fdt->next_fd = 0;
+	fdt->max_fds = NR_OPEN_DEFAULT;
+	fdt->max_fdset = __FD_SETSIZE;
+	fdt->close_on_exec = &newf->close_on_exec_init;
+	fdt->open_fds = &newf->open_fds_init;
+	fdt->fd = &newf->fd_array[0];
+	INIT_RCU_HEAD(&fdt->rcu);
+	fdt->free_files = NULL;
+	fdt->next = NULL;
+	rcu_assign_pointer(newf->fdt, fdt);
+out:
+	return newf;
+}
+
+static int copy_files(unsigned long clone_flags, struct task_struct * tsk)
+{
+	struct files_struct *oldf, *newf;
+	struct file **old_fds, **new_fds;
+	int open_files, size, i, error = 0, expand;
+	struct fdtable *old_fdt, *new_fdt;
+
+	/*
+	 * A background process may not have any files ...
+	 */
+	oldf = current->files;
+	if (!oldf)
+		goto out;
+
+	if (clone_flags & CLONE_FILES) {
+		atomic_inc(&oldf->count);
+		goto out;
+	}
+
+	/*
+	 * Note: we may be using current for both targets (See exec.c)
+	 * This works because we cache current->files (old) as oldf. Don't
+	 * break this.
+	 */
+	tsk->files = NULL;
+	error = -ENOMEM;
+	newf = alloc_files();
+	if (!newf)
+		goto out;
+
+	spin_lock(&oldf->file_lock);
+	old_fdt = files_fdtable(oldf);
+	new_fdt = files_fdtable(newf);
+	size = old_fdt->max_fdset;
+	open_files = count_open_files(old_fdt);
+	expand = 0;
+
+	/*
+	 * Check whether we need to allocate a larger fd array or fd set.
+	 * Note: we're not a clone task, so the open count won't  change.
+	 */
+	if (open_files > new_fdt->max_fdset) {
+		new_fdt->max_fdset = 0;
+		expand = 1;
+	}
+	if (open_files > new_fdt->max_fds) {
+		new_fdt->max_fds = 0;
+		expand = 1;
+	}
+
+	/* if the old fdset gets grown now, we'll only copy up to "size" fds */
+	if (expand) {
+		spin_unlock(&oldf->file_lock);
+		spin_lock(&newf->file_lock);
+		error = expand_files(newf, open_files-1);
+		spin_unlock(&newf->file_lock);
+		if (error < 0)
+			goto out_release;
+		new_fdt = files_fdtable(newf);
+		/*
+		 * Reacquire the oldf lock and a pointer to its fd table
+		 * who knows it may have a new bigger fd table. We need
+		 * the latest pointer.
+		 */
+		spin_lock(&oldf->file_lock);
+		old_fdt = files_fdtable(oldf);
+	}
+
+	old_fds = old_fdt->fd;
+	new_fds = new_fdt->fd;
+
+	memcpy(new_fdt->open_fds->fds_bits, old_fdt->open_fds->fds_bits, open_files/8);
+	memcpy(new_fdt->close_on_exec->fds_bits, old_fdt->close_on_exec->fds_bits, open_files/8);
+
+	for (i = open_files; i != 0; i--) {
+		struct file *f = *old_fds++;
+		if (f) {
+			get_file(f);
+		} else {
+			/*
+			 * The fd may be claimed in the fd bitmap but not yet
+			 * instantiated in the files array if a sibling thread
+			 * is partway through open().  So make sure that this
+			 * fd is available to the new process.
+			 */
+			FD_CLR(open_files - i, new_fdt->open_fds);
+		}
+		rcu_assign_pointer(*new_fds++, f);
+	}
+	spin_unlock(&oldf->file_lock);
+
+	/* compute the remainder to be cleared */
+	size = (new_fdt->max_fds - open_files) * sizeof(struct file *);
+
+	/* This is long word aligned thus could use a optimized version */ 
+	memset(new_fds, 0, size); 
+
+	if (new_fdt->max_fdset > open_files) {
+		int left = (new_fdt->max_fdset-open_files)/8;
+		int start = open_files / (8 * sizeof(unsigned long));
+
+		memset(&new_fdt->open_fds->fds_bits[start], 0, left);
+		memset(&new_fdt->close_on_exec->fds_bits[start], 0, left);
+	}
+
+	tsk->files = newf;
+	error = 0;
+out:
+	return error;
+
+out_release:
+	free_fdset (new_fdt->close_on_exec, new_fdt->max_fdset);
+	free_fdset (new_fdt->open_fds, new_fdt->max_fdset);
+	free_fd_array(new_fdt->fd, new_fdt->max_fds);
+	kmem_cache_free(files_cachep, newf);
+	goto out;
+}
+
+/*
+ *	Helper to unshare the files of the current task.
+ *	We don't want to expose copy_files internals to
+ *	the exec layer of the kernel.
+ */
+
+int unshare_files(void)
+{
+	struct files_struct *files  = current->files;
+	int rc;
+
+	if(!files)
+		BUG();
+
+	/* This can race but the race causes us to copy when we don't
+	   need to and drop the copy */
+	if(atomic_read(&files->count) == 1)
+	{
+		atomic_inc(&files->count);
+		return 0;
+	}
+	rc = copy_files(0, current);
+	if(rc)
+		current->files = files;
+	return rc;
+}
+
+EXPORT_SYMBOL(unshare_files);
+
+static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk)
+{
+	struct sighand_struct *sig;
+
+	if (clone_flags & (CLONE_SIGHAND | CLONE_THREAD)) {
+		atomic_inc(&current->sighand->count);
+		return 0;
+	}
+	sig = kmem_cache_alloc(sighand_cachep, GFP_KERNEL);
+	tsk->sighand = sig;
+	if (!sig)
+		return -ENOMEM;
+	spin_lock_init(&sig->siglock);
+	atomic_set(&sig->count, 1);
+	memcpy(sig->action, current->sighand->action, sizeof(sig->action));
+	return 0;
+}
+
+static inline int copy_signal(unsigned long clone_flags, struct task_struct * tsk)
+{
+	struct signal_struct *sig;
+	int ret;
+
+	if (clone_flags & CLONE_THREAD) {
+		atomic_inc(&current->signal->count);
+		atomic_inc(&current->signal->live);
+		return 0;
+	}
+	sig = kmem_cache_alloc(signal_cachep, GFP_KERNEL);
+	tsk->signal = sig;
+	if (!sig)
+		return -ENOMEM;
+
+	ret = copy_thread_group_keys(tsk);
+	if (ret < 0) {
+		kmem_cache_free(signal_cachep, sig);
+		return ret;
+	}
+
+	atomic_set(&sig->count, 1);
+	atomic_set(&sig->live, 1);
+	init_waitqueue_head(&sig->wait_chldexit);
+	sig->flags = 0;
+	sig->group_exit_code = 0;
+	sig->group_exit_task = NULL;
+	sig->group_stop_count = 0;
+	sig->curr_target = NULL;
+	init_sigpending(&sig->shared_pending);
+	INIT_LIST_HEAD(&sig->posix_timers);
+
+	sig->it_real_value = sig->it_real_incr = 0;
+	sig->real_timer.function = it_real_fn;
+	sig->real_timer.data = (unsigned long) tsk;
+	init_timer(&sig->real_timer);
+
+	sig->it_virt_expires = cputime_zero;
+	sig->it_virt_incr = cputime_zero;
+	sig->it_prof_expires = cputime_zero;
+	sig->it_prof_incr = cputime_zero;
+
+	sig->tty = current->signal->tty;
+	sig->pgrp = process_group(current);
+	sig->session = current->signal->session;
+	sig->leader = 0;	/* session leadership doesn't inherit */
+	sig->tty_old_pgrp = 0;
+
+	sig->utime = sig->stime = sig->cutime = sig->cstime = cputime_zero;
+	sig->nvcsw = sig->nivcsw = sig->cnvcsw = sig->cnivcsw = 0;
+	sig->min_flt = sig->maj_flt = sig->cmin_flt = sig->cmaj_flt = 0;
+	sig->sched_time = 0;
+	INIT_LIST_HEAD(&sig->cpu_timers[0]);
+	INIT_LIST_HEAD(&sig->cpu_timers[1]);
+	INIT_LIST_HEAD(&sig->cpu_timers[2]);
+
+	task_lock(current->group_leader);
+	memcpy(sig->rlim, current->signal->rlim, sizeof sig->rlim);
+	task_unlock(current->group_leader);
+
+	if (sig->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) {
+		/*
+		 * New sole thread in the process gets an expiry time
+		 * of the whole CPU time limit.
+		 */
+		tsk->it_prof_expires =
+			secs_to_cputime(sig->rlim[RLIMIT_CPU].rlim_cur);
+	}
+
+	return 0;
+}
+
+static inline void copy_flags(unsigned long clone_flags, struct task_struct *p)
+{
+	unsigned long new_flags = p->flags;
+
+	new_flags &= ~(PF_SUPERPRIV | PF_NOFREEZE);
+	new_flags |= PF_FORKNOEXEC;
+	if (!(clone_flags & CLONE_PTRACE))
+		p->ptrace = 0;
+	p->flags = new_flags;
+}
+
+asmlinkage long sys_set_tid_address(int __user *tidptr)
+{
+	current->clear_child_tid = tidptr;
+
+	return current->pid;
+}
+
+/*
+ * This creates a new process as a copy of the old one,
+ * but does not actually start it yet.
+ *
+ * It copies the registers, and all the appropriate
+ * parts of the process environment (as per the clone
+ * flags). The actual kick-off is left to the caller.
+ */
+static task_t *copy_process(unsigned long clone_flags,
+				 unsigned long stack_start,
+				 struct pt_regs *regs,
+				 unsigned long stack_size,
+				 int __user *parent_tidptr,
+				 int __user *child_tidptr,
+				 int pid)
+{
+	int retval;
+	struct task_struct *p = NULL;
+
+	if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
+		return ERR_PTR(-EINVAL);
+
+	/*
+	 * Thread groups must share signals as well, and detached threads
+	 * can only be started up within the thread group.
+	 */
+	if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))
+		return ERR_PTR(-EINVAL);
+
+	/*
+	 * Shared signal handlers imply shared VM. By way of the above,
+	 * thread groups also imply shared VM. Blocking this case allows
+	 * for various simplifications in other code.
+	 */
+	if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))
+		return ERR_PTR(-EINVAL);
+
+	retval = security_task_create(clone_flags);
+	if (retval)
+		goto fork_out;
+
+	retval = -ENOMEM;
+	p = dup_task_struct(current);
+	if (!p)
+		goto fork_out;
+
+	retval = -EAGAIN;
+	if (atomic_read(&p->user->processes) >=
+			p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
+		if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
+				p->user != &root_user)
+			goto bad_fork_free;
+	}
+
+	atomic_inc(&p->user->__count);
+	atomic_inc(&p->user->processes);
+	get_group_info(p->group_info);
+
+	/*
+	 * If multiple threads are within copy_process(), then this check
+	 * triggers too late. This doesn't hurt, the check is only there
+	 * to stop root fork bombs.
+	 */
+	if (nr_threads >= max_threads)
+		goto bad_fork_cleanup_count;
+
+	if (!try_module_get(task_thread_info(p)->exec_domain->module))
+		goto bad_fork_cleanup_count;
+
+	if (p->binfmt && !try_module_get(p->binfmt->module))
+		goto bad_fork_cleanup_put_domain;
+
+	p->did_exec = 0;
+	copy_flags(clone_flags, p);
+	p->pid = pid;
+	retval = -EFAULT;
+	if (clone_flags & CLONE_PARENT_SETTID)
+		if (put_user(p->pid, parent_tidptr))
+			goto bad_fork_cleanup;
+
+	p->proc_dentry = NULL;
+
+	INIT_LIST_HEAD(&p->children);
+	INIT_LIST_HEAD(&p->sibling);
+	p->vfork_done = NULL;
+	spin_lock_init(&p->alloc_lock);
+	spin_lock_init(&p->proc_lock);
+
+	clear_tsk_thread_flag(p, TIF_SIGPENDING);
+	init_sigpending(&p->pending);
+
+	p->utime = cputime_zero;
+	p->stime = cputime_zero;
+ 	p->sched_time = 0;
+	p->rchar = 0;		/* I/O counter: bytes read */
+	p->wchar = 0;		/* I/O counter: bytes written */
+	p->syscr = 0;		/* I/O counter: read syscalls */
+	p->syscw = 0;		/* I/O counter: write syscalls */
+	acct_clear_integrals(p);
+
+ 	p->it_virt_expires = cputime_zero;
+	p->it_prof_expires = cputime_zero;
+ 	p->it_sched_expires = 0;
+ 	INIT_LIST_HEAD(&p->cpu_timers[0]);
+ 	INIT_LIST_HEAD(&p->cpu_timers[1]);
+ 	INIT_LIST_HEAD(&p->cpu_timers[2]);
+
+	p->lock_depth = -1;		/* -1 = no lock */
+	do_posix_clock_monotonic_gettime(&p->start_time);
+	p->security = NULL;
+	p->io_context = NULL;
+	p->io_wait = NULL;
+	p->audit_context = NULL;
+#ifdef CONFIG_NUMA
+ 	p->mempolicy = mpol_copy(p->mempolicy);
+ 	if (IS_ERR(p->mempolicy)) {
+ 		retval = PTR_ERR(p->mempolicy);
+ 		p->mempolicy = NULL;
+ 		goto bad_fork_cleanup;
+ 	}
+#endif
+
+	p->tgid = p->pid;
+	if (clone_flags & CLONE_THREAD)
+		p->tgid = current->tgid;
+
+	if ((retval = security_task_alloc(p)))
+		goto bad_fork_cleanup_policy;
+	if ((retval = audit_alloc(p)))
+		goto bad_fork_cleanup_security;
+	/* copy all the process information */
+	if ((retval = copy_semundo(clone_flags, p)))
+		goto bad_fork_cleanup_audit;
+	if ((retval = copy_files(clone_flags, p)))
+		goto bad_fork_cleanup_semundo;
+	if ((retval = copy_fs(clone_flags, p)))
+		goto bad_fork_cleanup_files;
+	if ((retval = copy_sighand(clone_flags, p)))
+		goto bad_fork_cleanup_fs;
+	if ((retval = copy_signal(clone_flags, p)))
+		goto bad_fork_cleanup_sighand;
+	if ((retval = copy_mm(clone_flags, p)))
+		goto bad_fork_cleanup_signal;
+	if ((retval = copy_keys(clone_flags, p)))
+		goto bad_fork_cleanup_mm;
+	if ((retval = copy_namespace(clone_flags, p)))
+		goto bad_fork_cleanup_keys;
+	retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
+	if (retval)
+		goto bad_fork_cleanup_namespace;
+
+	p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
+	/*
+	 * Clear TID on mm_release()?
+	 */
+	p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr: NULL;
+
+	/*
+	 * Syscall tracing should be turned off in the child regardless
+	 * of CLONE_PTRACE.
+	 */
+	clear_tsk_thread_flag(p, TIF_SYSCALL_TRACE);
+#ifdef TIF_SYSCALL_EMU
+	clear_tsk_thread_flag(p, TIF_SYSCALL_EMU);
+#endif
+
+	/* Our parent execution domain becomes current domain
+	   These must match for thread signalling to apply */
+	   
+	p->parent_exec_id = p->self_exec_id;
+
+	/* ok, now we should be set up.. */
+	p->exit_signal = (clone_flags & CLONE_THREAD) ? -1 : (clone_flags & CSIGNAL);
+	p->pdeath_signal = 0;
+	p->exit_state = 0;
+
+	/*
+	 * Ok, make it visible to the rest of the system.
+	 * We dont wake it up yet.
+	 */
+	p->group_leader = p;
+	INIT_LIST_HEAD(&p->ptrace_children);
+	INIT_LIST_HEAD(&p->ptrace_list);
+
+	/* Perform scheduler related setup. Assign this task to a CPU. */
+	sched_fork(p, clone_flags);
+
+	/* Need tasklist lock for parent etc handling! */
+	write_lock_irq(&tasklist_lock);
+
+	/*
+	 * The task hasn't been attached yet, so its cpus_allowed mask will
+	 * not be changed, nor will its assigned CPU.
+	 *
+	 * The cpus_allowed mask of the parent may have changed after it was
+	 * copied first time - so re-copy it here, then check the child's CPU
+	 * to ensure it is on a valid CPU (and if not, just force it back to
+	 * parent's CPU). This avoids alot of nasty races.
+	 */
+	p->cpus_allowed = current->cpus_allowed;
+	if (unlikely(!cpu_isset(task_cpu(p), p->cpus_allowed) ||
+			!cpu_online(task_cpu(p))))
+		set_task_cpu(p, smp_processor_id());
+
+	/*
+	 * Check for pending SIGKILL! The new thread should not be allowed
+	 * to slip out of an OOM kill. (or normal SIGKILL.)
+	 */
+	if (sigismember(&current->pending.signal, SIGKILL)) {
+		write_unlock_irq(&tasklist_lock);
+		retval = -EINTR;
+		goto bad_fork_cleanup_namespace;
+	}
+
+	/* CLONE_PARENT re-uses the old parent */
+	if (clone_flags & (CLONE_PARENT|CLONE_THREAD))
+		p->real_parent = current->real_parent;
+	else
+		p->real_parent = current;
+	p->parent = p->real_parent;
+
+	if (clone_flags & CLONE_THREAD) {
+		spin_lock(&current->sighand->siglock);
+		/*
+		 * Important: if an exit-all has been started then
+		 * do not create this new thread - the whole thread
+		 * group is supposed to exit anyway.
+		 */
+		if (current->signal->flags & SIGNAL_GROUP_EXIT) {
+			spin_unlock(&current->sighand->siglock);
+			write_unlock_irq(&tasklist_lock);
+			retval = -EAGAIN;
+			goto bad_fork_cleanup_namespace;
+		}
+		p->group_leader = current->group_leader;
+
+		if (current->signal->group_stop_count > 0) {
+			/*
+			 * There is an all-stop in progress for the group.
+			 * We ourselves will stop as soon as we check signals.
+			 * Make the new thread part of that group stop too.
+			 */
+			current->signal->group_stop_count++;
+			set_tsk_thread_flag(p, TIF_SIGPENDING);
+		}
+
+		if (!cputime_eq(current->signal->it_virt_expires,
+				cputime_zero) ||
+		    !cputime_eq(current->signal->it_prof_expires,
+				cputime_zero) ||
+		    current->signal->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY ||
+		    !list_empty(&current->signal->cpu_timers[0]) ||
+		    !list_empty(&current->signal->cpu_timers[1]) ||
+		    !list_empty(&current->signal->cpu_timers[2])) {
+			/*
+			 * Have child wake up on its first tick to check
+			 * for process CPU timers.
+			 */
+			p->it_prof_expires = jiffies_to_cputime(1);
+		}
+
+		spin_unlock(&current->sighand->siglock);
+	}
+
+	/*
+	 * inherit ioprio
+	 */
+	p->ioprio = current->ioprio;
+
+	SET_LINKS(p);
+	if (unlikely(p->ptrace & PT_PTRACED))
+		__ptrace_link(p, current->parent);
+
+	attach_pid(p, PIDTYPE_PID, p->pid);
+	attach_pid(p, PIDTYPE_TGID, p->tgid);
+	if (thread_group_leader(p)) {
+		attach_pid(p, PIDTYPE_PGID, process_group(p));
+		attach_pid(p, PIDTYPE_SID, p->signal->session);
+		if (p->pid)
+			__get_cpu_var(process_counts)++;
+	}
+
+	if (!current->signal->tty && p->signal->tty)
+		p->signal->tty = NULL;
+
+	nr_threads++;
+	total_forks++;
+	write_unlock_irq(&tasklist_lock);
+	proc_fork_connector(p);
+	cpuset_fork(p);
+	retval = 0;
+
+fork_out:
+	if (retval)
+		return ERR_PTR(retval);
+	return p;
+
+bad_fork_cleanup_namespace:
+	exit_namespace(p);
+bad_fork_cleanup_keys:
+	exit_keys(p);
+bad_fork_cleanup_mm:
+	if (p->mm)
+		mmput(p->mm);
+bad_fork_cleanup_signal:
+	exit_signal(p);
+bad_fork_cleanup_sighand:
+	exit_sighand(p);
+bad_fork_cleanup_fs:
+	exit_fs(p); /* blocking */
+bad_fork_cleanup_files:
+	exit_files(p); /* blocking */
+bad_fork_cleanup_semundo:
+	exit_sem(p);
+bad_fork_cleanup_audit:
+	audit_free(p);
+bad_fork_cleanup_security:
+	security_task_free(p);
+bad_fork_cleanup_policy:
+#ifdef CONFIG_NUMA
+	mpol_free(p->mempolicy);
+#endif
+bad_fork_cleanup:
+	if (p->binfmt)
+		module_put(p->binfmt->module);
+bad_fork_cleanup_put_domain:
+	module_put(task_thread_info(p)->exec_domain->module);
+bad_fork_cleanup_count:
+	put_group_info(p->group_info);
+	atomic_dec(&p->user->processes);
+	free_uid(p->user);
+bad_fork_free:
+	free_task(p);
+	goto fork_out;
+}
+
+struct pt_regs * __devinit __attribute__((weak)) idle_regs(struct pt_regs *regs)
+{
+	memset(regs, 0, sizeof(struct pt_regs));
+	return regs;
+}
+
+task_t * __devinit fork_idle(int cpu)
+{
+	task_t *task;
+	struct pt_regs regs;
+
+	task = copy_process(CLONE_VM, 0, idle_regs(&regs), 0, NULL, NULL, 0);
+	if (!task)
+		return ERR_PTR(-ENOMEM);
+	init_idle(task, cpu);
+	unhash_process(task);
+	return task;
+}
+
+static inline int fork_traceflag (unsigned clone_flags)
+{
+	if (clone_flags & CLONE_UNTRACED)
+		return 0;
+	else if (clone_flags & CLONE_VFORK) {
+		if (current->ptrace & PT_TRACE_VFORK)
+			return PTRACE_EVENT_VFORK;
+	} else if ((clone_flags & CSIGNAL) != SIGCHLD) {
+		if (current->ptrace & PT_TRACE_CLONE)
+			return PTRACE_EVENT_CLONE;
+	} else if (current->ptrace & PT_TRACE_FORK)
+		return PTRACE_EVENT_FORK;
+
+	return 0;
+}
+
+/*
+ *  Ok, this is the main fork-routine.
+ *
+ * It copies the process, and if successful kick-starts
+ * it and waits for it to finish using the VM if required.
+ */
+long do_fork(unsigned long clone_flags,
+	      unsigned long stack_start,
+	      struct pt_regs *regs,
+	      unsigned long stack_size,
+	      int __user *parent_tidptr,
+	      int __user *child_tidptr)
+{
+	struct task_struct *p;
+	int trace = 0;
+	long pid = alloc_pidmap();
+
+	if (pid < 0)
+		return -EAGAIN;
+	if (unlikely(current->ptrace)) {
+		trace = fork_traceflag (clone_flags);
+		if (trace)
+			clone_flags |= CLONE_PTRACE;
+	}
+
+	p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid);
+	/*
+	 * Do this prior waking up the new thread - the thread pointer
+	 * might get invalid after that point, if the thread exits quickly.
+	 */
+	if (!IS_ERR(p)) {
+		struct completion vfork;
+
+		if (clone_flags & CLONE_VFORK) {
+			p->vfork_done = &vfork;
+			init_completion(&vfork);
+		}
+
+		if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) {
+			/*
+			 * We'll start up with an immediate SIGSTOP.
+			 */
+			sigaddset(&p->pending.signal, SIGSTOP);
+			set_tsk_thread_flag(p, TIF_SIGPENDING);
+		}
+
+		if (!(clone_flags & CLONE_STOPPED))
+			wake_up_new_task(p, clone_flags);
+		else
+			p->state = TASK_STOPPED;
+
+		if (unlikely (trace)) {
+			current->ptrace_message = pid;
+			ptrace_notify ((trace << 8) | SIGTRAP);
+		}
+
+		if (clone_flags & CLONE_VFORK) {
+			wait_for_completion(&vfork);
+			if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE))
+				ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP);
+		}
+	} else {
+		free_pidmap(pid);
+		pid = PTR_ERR(p);
+	}
+	return pid;
+}
+
+void __init proc_caches_init(void)
+{
+	sighand_cachep = kmem_cache_create("sighand_cache",
+			sizeof(struct sighand_struct), 0,
+			SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
+	signal_cachep = kmem_cache_create("signal_cache",
+			sizeof(struct signal_struct), 0,
+			SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
+	files_cachep = kmem_cache_create("files_cache", 
+			sizeof(struct files_struct), 0,
+			SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
+	fs_cachep = kmem_cache_create("fs_cache", 
+			sizeof(struct fs_struct), 0,
+			SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
+	vm_area_cachep = kmem_cache_create("vm_area_struct",
+			sizeof(struct vm_area_struct), 0,
+			SLAB_PANIC, NULL, NULL);
+	mm_cachep = kmem_cache_create("mm_struct",
+			sizeof(struct mm_struct), 0,
+			SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
+}
diff -urN oldtree/kernel/irq/manage.c newtree/kernel/irq/manage.c
--- oldtree/kernel/irq/manage.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/kernel/irq/manage.c	2006-02-13 19:20:01.270319184 -0500
@@ -238,6 +238,10 @@
 	return 0;
 }
 
+#ifdef CONFIG_DEBUG_SHIRQ
+static struct pt_regs shirq_fakeregs;
+#endif
+
 /**
  *	free_irq - free an interrupt
  *	@irq: Interrupt line to free
@@ -257,6 +261,7 @@
 	struct irq_desc *desc;
 	struct irqaction **p;
 	unsigned long flags;
+	irqreturn_t (*handler)(int, void *, struct pt_regs *) = NULL;
 
 	if (irq >= NR_IRQS)
 		return;
@@ -295,6 +300,8 @@
 
 			/* Make sure it's not being used on another CPU */
 			synchronize_irq(irq);
+			if (action->flags & SA_SHIRQ)
+				handler = action->handler;
 			kfree(action);
 			return;
 		}
@@ -302,6 +309,15 @@
 		spin_unlock_irqrestore(&desc->lock,flags);
 		return;
 	}
+#ifdef CONFIG_DEBUG_SHIRQ
+	if (handler) {
+		/* It's a shared IRQ -- the driver ought to be prepared for it to happen
+		   even now it's being freed, so let's make sure....
+		   We do this after actually deregistering it, to make sure that a 'real'
+		   IRQ doesn't run in parallel with our fake. */
+		handler(irq, dev_id, &shirq_fakeregs);
+	}
+#endif
 }
 
 EXPORT_SYMBOL(free_irq);
@@ -366,6 +382,16 @@
 	action->next = NULL;
 	action->dev_id = dev_id;
 
+#ifdef CONFIG_DEBUG_SHIRQ
+	if (irqflags & SA_SHIRQ) {
+		/* It's a shared IRQ -- the driver ought to be prepared for it to happen
+		   immediately, so let's make sure....
+		   We do this before actually registering it, to make sure that a 'real'
+		   IRQ doesn't run in parallel with our fake. */
+		handler(irq, dev_id, &shirq_fakeregs);
+	}
+#endif
+
 	retval = setup_irq(irq, action);
 	if (retval)
 		kfree(action);
diff -urN oldtree/kernel/irq/manage.c.orig newtree/kernel/irq/manage.c.orig
--- oldtree/kernel/irq/manage.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/kernel/irq/manage.c.orig	2006-02-13 19:20:01.271319032 -0500
@@ -0,0 +1,377 @@
+/*
+ * linux/kernel/irq/manage.c
+ *
+ * Copyright (C) 1992, 1998-2004 Linus Torvalds, Ingo Molnar
+ *
+ * This file contains driver APIs to the irq subsystem.
+ */
+
+#include <linux/config.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/interrupt.h>
+
+#include "internals.h"
+
+#ifdef CONFIG_SMP
+
+cpumask_t irq_affinity[NR_IRQS] = { [0 ... NR_IRQS-1] = CPU_MASK_ALL };
+
+#if defined (CONFIG_GENERIC_PENDING_IRQ) || defined (CONFIG_IRQBALANCE)
+cpumask_t __cacheline_aligned pending_irq_cpumask[NR_IRQS];
+#endif
+
+/**
+ *	synchronize_irq - wait for pending IRQ handlers (on other CPUs)
+ *	@irq: interrupt number to wait for
+ *
+ *	This function waits for any pending IRQ handlers for this interrupt
+ *	to complete before returning. If you use this function while
+ *	holding a resource the IRQ handler may need you will deadlock.
+ *
+ *	This function may be called - with care - from IRQ context.
+ */
+void synchronize_irq(unsigned int irq)
+{
+	struct irq_desc *desc = irq_desc + irq;
+
+	if (irq >= NR_IRQS)
+		return;
+
+	while (desc->status & IRQ_INPROGRESS)
+		cpu_relax();
+}
+
+EXPORT_SYMBOL(synchronize_irq);
+
+#endif
+
+/**
+ *	disable_irq_nosync - disable an irq without waiting
+ *	@irq: Interrupt to disable
+ *
+ *	Disable the selected interrupt line.  Disables and Enables are
+ *	nested.
+ *	Unlike disable_irq(), this function does not ensure existing
+ *	instances of the IRQ handler have completed before returning.
+ *
+ *	This function may be called from IRQ context.
+ */
+void disable_irq_nosync(unsigned int irq)
+{
+	irq_desc_t *desc = irq_desc + irq;
+	unsigned long flags;
+
+	if (irq >= NR_IRQS)
+		return;
+
+	spin_lock_irqsave(&desc->lock, flags);
+	if (!desc->depth++) {
+		desc->status |= IRQ_DISABLED;
+		desc->handler->disable(irq);
+	}
+	spin_unlock_irqrestore(&desc->lock, flags);
+}
+
+EXPORT_SYMBOL(disable_irq_nosync);
+
+/**
+ *	disable_irq - disable an irq and wait for completion
+ *	@irq: Interrupt to disable
+ *
+ *	Disable the selected interrupt line.  Enables and Disables are
+ *	nested.
+ *	This function waits for any pending IRQ handlers for this interrupt
+ *	to complete before returning. If you use this function while
+ *	holding a resource the IRQ handler may need you will deadlock.
+ *
+ *	This function may be called - with care - from IRQ context.
+ */
+void disable_irq(unsigned int irq)
+{
+	irq_desc_t *desc = irq_desc + irq;
+
+	if (irq >= NR_IRQS)
+		return;
+
+	disable_irq_nosync(irq);
+	if (desc->action)
+		synchronize_irq(irq);
+}
+
+EXPORT_SYMBOL(disable_irq);
+
+/**
+ *	enable_irq - enable handling of an irq
+ *	@irq: Interrupt to enable
+ *
+ *	Undoes the effect of one call to disable_irq().  If this
+ *	matches the last disable, processing of interrupts on this
+ *	IRQ line is re-enabled.
+ *
+ *	This function may be called from IRQ context.
+ */
+void enable_irq(unsigned int irq)
+{
+	irq_desc_t *desc = irq_desc + irq;
+	unsigned long flags;
+
+	if (irq >= NR_IRQS)
+		return;
+
+	spin_lock_irqsave(&desc->lock, flags);
+	switch (desc->depth) {
+	case 0:
+		WARN_ON(1);
+		break;
+	case 1: {
+		unsigned int status = desc->status & ~IRQ_DISABLED;
+
+		desc->status = status;
+		if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) {
+			desc->status = status | IRQ_REPLAY;
+			hw_resend_irq(desc->handler,irq);
+		}
+		desc->handler->enable(irq);
+		/* fall-through */
+	}
+	default:
+		desc->depth--;
+	}
+	spin_unlock_irqrestore(&desc->lock, flags);
+}
+
+EXPORT_SYMBOL(enable_irq);
+
+/*
+ * Internal function that tells the architecture code whether a
+ * particular irq has been exclusively allocated or is available
+ * for driver use.
+ */
+int can_request_irq(unsigned int irq, unsigned long irqflags)
+{
+	struct irqaction *action;
+
+	if (irq >= NR_IRQS)
+		return 0;
+
+	action = irq_desc[irq].action;
+	if (action)
+		if (irqflags & action->flags & SA_SHIRQ)
+			action = NULL;
+
+	return !action;
+}
+
+/*
+ * Internal function to register an irqaction - typically used to
+ * allocate special interrupts that are part of the architecture.
+ */
+int setup_irq(unsigned int irq, struct irqaction * new)
+{
+	struct irq_desc *desc = irq_desc + irq;
+	struct irqaction *old, **p;
+	unsigned long flags;
+	int shared = 0;
+
+	if (irq >= NR_IRQS)
+		return -EINVAL;
+
+	if (desc->handler == &no_irq_type)
+		return -ENOSYS;
+	/*
+	 * Some drivers like serial.c use request_irq() heavily,
+	 * so we have to be careful not to interfere with a
+	 * running system.
+	 */
+	if (new->flags & SA_SAMPLE_RANDOM) {
+		/*
+		 * This function might sleep, we want to call it first,
+		 * outside of the atomic block.
+		 * Yes, this might clear the entropy pool if the wrong
+		 * driver is attempted to be loaded, without actually
+		 * installing a new handler, but is this really a problem,
+		 * only the sysadmin is able to do this.
+		 */
+		rand_initialize_irq(irq);
+	}
+
+	/*
+	 * The following block of code has to be executed atomically
+	 */
+	spin_lock_irqsave(&desc->lock,flags);
+	p = &desc->action;
+	if ((old = *p) != NULL) {
+		/* Can't share interrupts unless both agree to */
+		if (!(old->flags & new->flags & SA_SHIRQ)) {
+			spin_unlock_irqrestore(&desc->lock,flags);
+			return -EBUSY;
+		}
+
+		/* add new interrupt at end of irq queue */
+		do {
+			p = &old->next;
+			old = *p;
+		} while (old);
+		shared = 1;
+	}
+
+	*p = new;
+
+	if (!shared) {
+		desc->depth = 0;
+		desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT |
+				  IRQ_WAITING | IRQ_INPROGRESS);
+		if (desc->handler->startup)
+			desc->handler->startup(irq);
+		else
+			desc->handler->enable(irq);
+	}
+	spin_unlock_irqrestore(&desc->lock,flags);
+
+	new->irq = irq;
+	register_irq_proc(irq);
+	new->dir = NULL;
+	register_handler_proc(irq, new);
+
+	return 0;
+}
+
+/**
+ *	free_irq - free an interrupt
+ *	@irq: Interrupt line to free
+ *	@dev_id: Device identity to free
+ *
+ *	Remove an interrupt handler. The handler is removed and if the
+ *	interrupt line is no longer in use by any driver it is disabled.
+ *	On a shared IRQ the caller must ensure the interrupt is disabled
+ *	on the card it drives before calling this function. The function
+ *	does not return until any executing interrupts for this IRQ
+ *	have completed.
+ *
+ *	This function must not be called from interrupt context.
+ */
+void free_irq(unsigned int irq, void *dev_id)
+{
+	struct irq_desc *desc;
+	struct irqaction **p;
+	unsigned long flags;
+
+	if (irq >= NR_IRQS)
+		return;
+
+	desc = irq_desc + irq;
+	spin_lock_irqsave(&desc->lock,flags);
+	p = &desc->action;
+	for (;;) {
+		struct irqaction * action = *p;
+
+		if (action) {
+			struct irqaction **pp = p;
+
+			p = &action->next;
+			if (action->dev_id != dev_id)
+				continue;
+
+			/* Found it - now remove it from the list of entries */
+			*pp = action->next;
+
+			/* Currently used only by UML, might disappear one day.*/
+#ifdef CONFIG_IRQ_RELEASE_METHOD
+			if (desc->handler->release)
+				desc->handler->release(irq, dev_id);
+#endif
+
+			if (!desc->action) {
+				desc->status |= IRQ_DISABLED;
+				if (desc->handler->shutdown)
+					desc->handler->shutdown(irq);
+				else
+					desc->handler->disable(irq);
+			}
+			spin_unlock_irqrestore(&desc->lock,flags);
+			unregister_handler_proc(irq, action);
+
+			/* Make sure it's not being used on another CPU */
+			synchronize_irq(irq);
+			kfree(action);
+			return;
+		}
+		printk(KERN_ERR "Trying to free free IRQ%d\n",irq);
+		spin_unlock_irqrestore(&desc->lock,flags);
+		return;
+	}
+}
+
+EXPORT_SYMBOL(free_irq);
+
+/**
+ *	request_irq - allocate an interrupt line
+ *	@irq: Interrupt line to allocate
+ *	@handler: Function to be called when the IRQ occurs
+ *	@irqflags: Interrupt type flags
+ *	@devname: An ascii name for the claiming device
+ *	@dev_id: A cookie passed back to the handler function
+ *
+ *	This call allocates interrupt resources and enables the
+ *	interrupt line and IRQ handling. From the point this
+ *	call is made your handler function may be invoked. Since
+ *	your handler function must clear any interrupt the board
+ *	raises, you must take care both to initialise your hardware
+ *	and to set up the interrupt handler in the right order.
+ *
+ *	Dev_id must be globally unique. Normally the address of the
+ *	device data structure is used as the cookie. Since the handler
+ *	receives this value it makes sense to use it.
+ *
+ *	If your interrupt is shared you must pass a non NULL dev_id
+ *	as this is required when freeing the interrupt.
+ *
+ *	Flags:
+ *
+ *	SA_SHIRQ		Interrupt is shared
+ *	SA_INTERRUPT		Disable local interrupts while processing
+ *	SA_SAMPLE_RANDOM	The interrupt can be used for entropy
+ *
+ */
+int request_irq(unsigned int irq,
+		irqreturn_t (*handler)(int, void *, struct pt_regs *),
+		unsigned long irqflags, const char * devname, void *dev_id)
+{
+	struct irqaction * action;
+	int retval;
+
+	/*
+	 * Sanity-check: shared interrupts must pass in a real dev-ID,
+	 * otherwise we'll have trouble later trying to figure out
+	 * which interrupt is which (messes up the interrupt freeing
+	 * logic etc).
+	 */
+	if ((irqflags & SA_SHIRQ) && !dev_id)
+		return -EINVAL;
+	if (irq >= NR_IRQS)
+		return -EINVAL;
+	if (!handler)
+		return -EINVAL;
+
+	action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
+	if (!action)
+		return -ENOMEM;
+
+	action->handler = handler;
+	action->flags = irqflags;
+	cpus_clear(action->mask);
+	action->name = devname;
+	action->next = NULL;
+	action->dev_id = dev_id;
+
+	retval = setup_irq(irq, action);
+	if (retval)
+		kfree(action);
+
+	return retval;
+}
+
+EXPORT_SYMBOL(request_irq);
+
diff -urN oldtree/kernel/resource.c newtree/kernel/resource.c
--- oldtree/kernel/resource.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/kernel/resource.c	2006-02-13 19:20:01.272318880 -0500
@@ -181,6 +181,8 @@
 {
 	struct resource *tmp, **p;
 
+	WARN_ON(old->child);
+
 	p = &old->parent->child;
 	for (;;) {
 		tmp = *p;
diff -urN oldtree/kernel/workqueue.c newtree/kernel/workqueue.c
--- oldtree/kernel/workqueue.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/kernel/workqueue.c	2006-02-13 19:20:29.748989768 -0500
@@ -29,7 +29,8 @@
 #include <linux/kthread.h>
 
 /*
- * The per-CPU workqueue (if single thread, we always use cpu 0's).
+ * The per-CPU workqueue (if single thread, we always use the first
+ * possible cpu).
  *
  * The sequence counters are for flush_scheduled_work().  It wants to wait
  * until until all currently-scheduled works are completed, but it doesn't
@@ -69,6 +70,8 @@
 static DEFINE_SPINLOCK(workqueue_lock);
 static LIST_HEAD(workqueues);
 
+static int singlethread_cpu;
+
 /* If it's single threaded, it isn't in the list of workqueues. */
 static inline int is_single_threaded(struct workqueue_struct *wq)
 {
@@ -102,7 +105,7 @@
 
 	if (!test_and_set_bit(0, &work->pending)) {
 		if (unlikely(is_single_threaded(wq)))
-			cpu = any_online_cpu(cpu_online_map);
+			cpu = singlethread_cpu;
 		BUG_ON(!list_empty(&work->entry));
 		__queue_work(per_cpu_ptr(wq->cpu_wq, cpu), work);
 		ret = 1;
@@ -118,7 +121,7 @@
 	int cpu = smp_processor_id();
 
 	if (unlikely(is_single_threaded(wq)))
-		cpu = any_online_cpu(cpu_online_map);
+		cpu = singlethread_cpu;
 
 	__queue_work(per_cpu_ptr(wq->cpu_wq, cpu), work);
 }
@@ -267,7 +270,7 @@
 
 	if (is_single_threaded(wq)) {
 		/* Always use first cpu's area. */
-		flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, any_online_cpu(cpu_online_map)));
+		flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, singlethread_cpu));
 	} else {
 		int cpu;
 
@@ -320,7 +323,7 @@
 	lock_cpu_hotplug();
 	if (singlethread) {
 		INIT_LIST_HEAD(&wq->list);
-		p = create_workqueue_thread(wq, any_online_cpu(cpu_online_map));
+		p = create_workqueue_thread(wq, singlethread_cpu);
 		if (!p)
 			destroy = 1;
 		else
@@ -374,7 +377,7 @@
 	/* We don't need the distraction of CPUs appearing and vanishing. */
 	lock_cpu_hotplug();
 	if (is_single_threaded(wq))
-		cleanup_workqueue_thread(wq, any_online_cpu(cpu_online_map));
+		cleanup_workqueue_thread(wq, singlethread_cpu);
 	else {
 		for_each_online_cpu(cpu)
 			cleanup_workqueue_thread(wq, cpu);
@@ -543,6 +546,7 @@
 
 void init_workqueues(void)
 {
+	singlethread_cpu = first_cpu(cpu_possible_map);
 	hotcpu_notifier(workqueue_cpu_callback, 0);
 	keventd_wq = create_workqueue("events");
 	BUG_ON(!keventd_wq);
diff -urN oldtree/lib/Kconfig.debug newtree/lib/Kconfig.debug
--- oldtree/lib/Kconfig.debug	2006-01-02 22:21:10.000000000 -0500
+++ newtree/lib/Kconfig.debug	2006-02-13 19:20:01.272318880 -0500
@@ -158,6 +158,16 @@
 	  automatically, but we'd like to make it more efficient by not
 	  having to do that.
 
+config PAGE_OWNER
+	bool "Track page owner"
+	depends on DEBUG_KERNEL && X86
+	help
+	  This keeps track of what call chain is the owner of a page, may
+	  help to find bare alloc_page(s) leaks. Eats a fair amount of memory.
+	  See Documentation/page_owner.c for user-space helper.
+
+	  If unsure, say N.
+
 config DEBUG_FS
 	bool "Debug Filesystem"
 	depends on DEBUG_KERNEL && SYSFS
@@ -176,10 +186,19 @@
 
 	  If unsure, say N.
 
+config DEBUG_SHIRQ
+       bool "Debug shared IRQ handlers"
+       depends on GENERIC_HARDIRQS
+       help
+         Enable this to generate a spurious interrupt as soon as a shared interrupt
+	 handler is registered, and just before one is deregistered. Drivers ought
+	 to be able to handle interrupts coming in at those some; some don't and
+	 need to be caught.
+
 config FRAME_POINTER
 	bool "Compile the kernel with frame pointers"
 	depends on DEBUG_KERNEL && (X86 || CRIS || M68K || M68KNOMMU || FRV || UML)
-	default y if DEBUG_INFO && UML
+	default y
 	help
 	  If you say Y here the resulting kernel image will be slightly larger
 	  and slower, but it might give very useful debugging information on
diff -urN oldtree/lib/Kconfig.debug.orig newtree/lib/Kconfig.debug.orig
--- oldtree/lib/Kconfig.debug.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/lib/Kconfig.debug.orig	2006-02-13 19:20:01.273318728 -0500
@@ -0,0 +1,210 @@
+
+config PRINTK_TIME
+	bool "Show timing information on printks"
+	help
+	  Selecting this option causes timing information to be
+	  included in printk output.  This allows you to measure
+	  the interval between kernel operations, including bootup
+	  operations.  This is useful for identifying long delays
+	  in kernel startup.
+
+
+config DEBUG_KERNEL
+	bool "Kernel debugging"
+	help
+	  Say Y here if you are developing drivers or trying to debug and
+	  identify kernel problems.
+
+config MAGIC_SYSRQ
+	bool "Magic SysRq key"
+	depends on DEBUG_KERNEL && !UML
+	help
+	  If you say Y here, you will have some control over the system even
+	  if the system crashes for example during kernel debugging (e.g., you
+	  will be able to flush the buffer cache to disk, reboot the system
+	  immediately or dump some status information). This is accomplished
+	  by pressing various keys while holding SysRq (Alt+PrintScreen). It
+	  also works on a serial console (on PC hardware at least), if you
+	  send a BREAK and then within 5 seconds a command keypress. The
+	  keys are documented in <file:Documentation/sysrq.txt>. Don't say Y
+	  unless you really know what this hack does.
+
+config LOG_BUF_SHIFT
+	int "Kernel log buffer size (16 => 64KB, 17 => 128KB)" if DEBUG_KERNEL
+	range 12 21
+	default 17 if ARCH_S390
+	default 16 if X86_NUMAQ || IA64
+	default 15 if SMP
+	default 14
+	help
+	  Select kernel log buffer size as a power of 2.
+	  Defaults and Examples:
+	  	     17 => 128 KB for S/390
+		     16 => 64 KB for x86 NUMAQ or IA-64
+	             15 => 32 KB for SMP
+	             14 => 16 KB for uniprocessor
+		     13 =>  8 KB
+		     12 =>  4 KB
+
+config DETECT_SOFTLOCKUP
+	bool "Detect Soft Lockups"
+	depends on DEBUG_KERNEL
+	default y
+	help
+	  Say Y here to enable the kernel to detect "soft lockups",
+	  which are bugs that cause the kernel to loop in kernel
+	  mode for more than 10 seconds, without giving other tasks a
+	  chance to run.
+
+	  When a soft-lockup is detected, the kernel will print the
+	  current stack trace (which you should report), but the
+	  system will stay locked up. This feature has negligible
+	  overhead.
+
+	  (Note that "hard lockups" are separate type of bugs that
+	   can be detected via the NMI-watchdog, on platforms that
+	   support it.)
+
+config SCHEDSTATS
+	bool "Collect scheduler statistics"
+	depends on DEBUG_KERNEL && PROC_FS
+	help
+	  If you say Y here, additional code will be inserted into the
+	  scheduler and related routines to collect statistics about
+	  scheduler behavior and provide them in /proc/schedstat.  These
+	  stats may be useful for both tuning and debugging the scheduler
+	  If you aren't debugging the scheduler or trying to tune a specific
+	  application, you can say N to avoid the very slight overhead
+	  this adds.
+
+config DEBUG_SLAB
+	bool "Debug memory allocations"
+	depends on DEBUG_KERNEL
+	help
+	  Say Y here to have the kernel do limited verification on memory
+	  allocation as well as poisoning memory on free to catch use of freed
+	  memory. This can make kmalloc/kfree-intensive workloads much slower.
+
+config DEBUG_PREEMPT
+	bool "Debug preemptible kernel"
+	depends on DEBUG_KERNEL && PREEMPT
+	default y
+	help
+	  If you say Y here then the kernel will use a debug variant of the
+	  commonly used smp_processor_id() function and will print warnings
+	  if kernel code uses it in a preemption-unsafe way. Also, the kernel
+	  will detect preemption count underflows.
+
+config DEBUG_SPINLOCK
+	bool "Spinlock debugging"
+	depends on DEBUG_KERNEL
+	help
+	  Say Y here and build SMP to catch missing spinlock initialization
+	  and certain other kinds of spinlock errors commonly made.  This is
+	  best used in conjunction with the NMI watchdog so that spinlock
+	  deadlocks are also debuggable.
+
+config DEBUG_SPINLOCK_SLEEP
+	bool "Sleep-inside-spinlock checking"
+	depends on DEBUG_KERNEL
+	help
+	  If you say Y here, various routines which may sleep will become very
+	  noisy if they are called with a spinlock held.
+
+config DEBUG_KOBJECT
+	bool "kobject debugging"
+	depends on DEBUG_KERNEL
+	help
+	  If you say Y here, some extra kobject debugging messages will be sent
+	  to the syslog. 
+
+config DEBUG_HIGHMEM
+	bool "Highmem debugging"
+	depends on DEBUG_KERNEL && HIGHMEM
+	help
+	  This options enables addition error checking for high memory systems.
+	  Disable for production systems.
+
+config DEBUG_BUGVERBOSE
+	bool "Verbose BUG() reporting (adds 70K)" if DEBUG_KERNEL && EMBEDDED
+	depends on BUG
+	depends on ARM || ARM26 || M32R || M68K || SPARC32 || SPARC64 || X86_32 || FRV
+	default !EMBEDDED
+	help
+	  Say Y here to make BUG() panics output the file name and line number
+	  of the BUG call as well as the EIP and oops trace.  This aids
+	  debugging but costs about 70-100K of memory.
+
+config DEBUG_INFO
+	bool "Compile the kernel with debug info"
+	depends on DEBUG_KERNEL
+	help
+          If you say Y here the resulting kernel image will include
+	  debugging info resulting in a larger kernel image.
+	  Say Y here only if you plan to debug the kernel.
+
+	  If unsure, say N.
+
+config DEBUG_IOREMAP
+	bool "Enable ioremap() debugging"
+	depends on DEBUG_KERNEL && PARISC
+	help
+	  Enabling this option will cause the kernel to distinguish between
+	  ioremapped and physical addresses.  It will print a backtrace (at
+	  most one every 10 seconds), hopefully allowing you to see which
+	  drivers need work.  Fixing all these problems is a prerequisite
+	  for turning on USE_HPPA_IOREMAP.  The warnings are harmless;
+	  the kernel has enough information to fix the broken drivers
+	  automatically, but we'd like to make it more efficient by not
+	  having to do that.
+
+config DEBUG_FS
+	bool "Debug Filesystem"
+	depends on DEBUG_KERNEL && SYSFS
+	help
+	  debugfs is a virtual file system that kernel developers use to put
+	  debugging files into.  Enable this option to be able to read and
+	  write to these files.
+
+	  If unsure, say N.
+
+config DEBUG_VM
+	bool "Debug VM"
+	depends on DEBUG_KERNEL
+	help
+	  Enable this to debug the virtual-memory system.
+
+	  If unsure, say N.
+
+config DEBUG_SHIRQ
+       bool "Debug shared IRQ handlers"
+       depends on GENERIC_HARDIRQS
+       help
+         Enable this to generate a spurious interrupt as soon as a shared interrupt
+	 handler is registered, and just before one is deregistered. Drivers ought
+	 to be able to handle interrupts coming in at those some; some don't and
+	 need to be caught.
+
+config FRAME_POINTER
+	bool "Compile the kernel with frame pointers"
+	depends on DEBUG_KERNEL && (X86 || CRIS || M68K || M68KNOMMU || FRV || UML)
+	default y
+	help
+	  If you say Y here the resulting kernel image will be slightly larger
+	  and slower, but it might give very useful debugging information on
+	  some architectures or if you use external debuggers.
+	  If you don't debug the kernel, you can say N.
+
+config RCU_TORTURE_TEST
+	tristate "torture tests for RCU"
+	depends on DEBUG_KERNEL
+	default n
+	help
+	  This option provides a kernel module that runs torture tests
+	  on the RCU infrastructure.  The kernel module may be built
+	  after the fact on the running kernel to be tested, if desired.
+
+	  Say Y here if you want RCU torture tests to start automatically
+	  at boot time (you probably don't).
+	  Say M if you want the RCU torture tests to build as a module.
+	  Say N if you are unsure.
diff -urN oldtree/lib/Makefile newtree/lib/Makefile
--- oldtree/lib/Makefile	2006-01-02 22:21:10.000000000 -0500
+++ newtree/lib/Makefile	2006-02-13 19:20:01.273318728 -0500
@@ -9,7 +9,7 @@
 
 lib-y	+= kobject.o kref.o kobject_uevent.o klist.o
 
-obj-y += sort.o parser.o halfmd4.o
+obj-y += sort.o parser.o halfmd4.o iomap_copy.o
 
 ifeq ($(CONFIG_DEBUG_KOBJECT),y)
 CFLAGS_kobject.o += -DDEBUG
diff -urN oldtree/lib/iomap_copy.c newtree/lib/iomap_copy.c
--- oldtree/lib/iomap_copy.c	1969-12-31 19:00:00.000000000 -0500
+++ newtree/lib/iomap_copy.c	2006-02-13 19:20:01.273318728 -0500
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2006 PathScale, Inc.  All Rights Reserved.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+
+/**
+ * __iowrite32_copy - copy data to MMIO space, in 32-bit units
+ * @to: destination, in MMIO space (must be 32-bit aligned)
+ * @from: source (must be 32-bit aligned)
+ * @count: number of 32-bit quantities to copy
+ *
+ * Copy data from kernel space to MMIO space, in units of 32 bits at a
+ * time.  Order of access is not guaranteed, nor is a memory barrier
+ * performed afterwards.
+ */
+void __attribute__((weak)) __iowrite32_copy(void __iomem *to,
+					    const void *from,
+					    size_t count)
+{
+	u32 __iomem *dst = to;
+	const u32 *src = from;
+	const u32 *end = src + count;
+
+	while (src < end)
+		__raw_writel(*src++, dst++);
+}
+EXPORT_SYMBOL_GPL(__iowrite32_copy);
diff -urN oldtree/lib/kobject.c newtree/lib/kobject.c
--- oldtree/lib/kobject.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/lib/kobject.c	2006-02-13 19:20:01.273318728 -0500
@@ -72,6 +72,8 @@
 	 * Add 1 to strlen for leading '/' of each level.
 	 */
 	do {
+		if (kobject_name(parent) == NULL)
+			return 0;
 		length += strlen(kobject_name(parent)) + 1;
 		parent = parent->parent;
 	} while (parent);
@@ -107,6 +109,8 @@
 	int len;
 
 	len = get_kobj_path_length(kobj);
+	if (len == 0)
+		return NULL;
 	path = kmalloc(len, gfp_mask);
 	if (!path)
 		return NULL;
diff -urN oldtree/mm/filemap.c newtree/mm/filemap.c
--- oldtree/mm/filemap.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/mm/filemap.c	2006-02-13 19:20:01.275318424 -0500
@@ -498,6 +498,23 @@
 EXPORT_SYMBOL(__lock_page);
 
 /*
+ * Note completion of filesystem specific page synchronisation
+ *
+ * This is used to allow a page to be written to a filesystem cache in the
+ * background without holding up the completion of readpage
+ */
+void fastcall end_page_fs_misc(struct page *page)
+{
+	smp_mb__before_clear_bit();
+	if (!TestClearPageFsMisc(page))
+		BUG();
+	smp_mb__after_clear_bit();
+	__wake_up_bit(page_waitqueue(page), &page->flags, PG_fs_misc);
+}
+
+EXPORT_SYMBOL(end_page_fs_misc);
+
+/*
  * a rather lightweight function, finding and getting a reference to a
  * hashed page atomically.
  */
diff -urN oldtree/mm/filemap.c.orig newtree/mm/filemap.c.orig
--- oldtree/mm/filemap.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/mm/filemap.c.orig	2006-02-13 19:20:01.277318120 -0500
@@ -0,0 +1,2270 @@
+/*
+ *	linux/mm/filemap.c
+ *
+ * Copyright (C) 1994-1999  Linus Torvalds
+ */
+
+/*
+ * This file handles the generic file mmap semantics used by
+ * most "normal" filesystems (but you don't /have/ to use this:
+ * the NFS filesystem used to do this differently, for example)
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/compiler.h>
+#include <linux/fs.h>
+#include <linux/aio.h>
+#include <linux/kernel_stat.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/mman.h>
+#include <linux/pagemap.h>
+#include <linux/file.h>
+#include <linux/uio.h>
+#include <linux/hash.h>
+#include <linux/writeback.h>
+#include <linux/pagevec.h>
+#include <linux/blkdev.h>
+#include <linux/security.h>
+#include <linux/syscalls.h>
+#include "filemap.h"
+/*
+ * FIXME: remove all knowledge of the buffer layer from the core VM
+ */
+#include <linux/buffer_head.h> /* for generic_osync_inode */
+
+#include <asm/uaccess.h>
+#include <asm/mman.h>
+
+static ssize_t
+generic_file_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
+	loff_t offset, unsigned long nr_segs);
+
+/*
+ * Shared mappings implemented 30.11.1994. It's not fully working yet,
+ * though.
+ *
+ * Shared mappings now work. 15.8.1995  Bruno.
+ *
+ * finished 'unifying' the page and buffer cache and SMP-threaded the
+ * page-cache, 21.05.1999, Ingo Molnar <mingo@redhat.com>
+ *
+ * SMP-threaded pagemap-LRU 1999, Andrea Arcangeli <andrea@suse.de>
+ */
+
+/*
+ * Lock ordering:
+ *
+ *  ->i_mmap_lock		(vmtruncate)
+ *    ->private_lock		(__free_pte->__set_page_dirty_buffers)
+ *      ->swap_lock		(exclusive_swap_page, others)
+ *        ->mapping->tree_lock
+ *
+ *  ->i_sem
+ *    ->i_mmap_lock		(truncate->unmap_mapping_range)
+ *
+ *  ->mmap_sem
+ *    ->i_mmap_lock
+ *      ->page_table_lock or pte_lock	(various, mainly in memory.c)
+ *        ->mapping->tree_lock	(arch-dependent flush_dcache_mmap_lock)
+ *
+ *  ->mmap_sem
+ *    ->lock_page		(access_process_vm)
+ *
+ *  ->mmap_sem
+ *    ->i_sem			(msync)
+ *
+ *  ->i_sem
+ *    ->i_alloc_sem             (various)
+ *
+ *  ->inode_lock
+ *    ->sb_lock			(fs/fs-writeback.c)
+ *    ->mapping->tree_lock	(__sync_single_inode)
+ *
+ *  ->i_mmap_lock
+ *    ->anon_vma.lock		(vma_adjust)
+ *
+ *  ->anon_vma.lock
+ *    ->page_table_lock or pte_lock	(anon_vma_prepare and various)
+ *
+ *  ->page_table_lock or pte_lock
+ *    ->swap_lock		(try_to_unmap_one)
+ *    ->private_lock		(try_to_unmap_one)
+ *    ->tree_lock		(try_to_unmap_one)
+ *    ->zone.lru_lock		(follow_page->mark_page_accessed)
+ *    ->private_lock		(page_remove_rmap->set_page_dirty)
+ *    ->tree_lock		(page_remove_rmap->set_page_dirty)
+ *    ->inode_lock		(page_remove_rmap->set_page_dirty)
+ *    ->inode_lock		(zap_pte_range->set_page_dirty)
+ *    ->private_lock		(zap_pte_range->__set_page_dirty_buffers)
+ *
+ *  ->task->proc_lock
+ *    ->dcache_lock		(proc_pid_lookup)
+ */
+
+/*
+ * Remove a page from the page cache and free it. Caller has to make
+ * sure the page is locked and that nobody else uses it - or that usage
+ * is safe.  The caller must hold a write_lock on the mapping's tree_lock.
+ */
+void __remove_from_page_cache(struct page *page)
+{
+	struct address_space *mapping = page->mapping;
+
+	radix_tree_delete(&mapping->page_tree, page->index);
+	page->mapping = NULL;
+	mapping->nrpages--;
+	pagecache_acct(-1);
+}
+
+void remove_from_page_cache(struct page *page)
+{
+	struct address_space *mapping = page->mapping;
+
+	BUG_ON(!PageLocked(page));
+
+	write_lock_irq(&mapping->tree_lock);
+	__remove_from_page_cache(page);
+	write_unlock_irq(&mapping->tree_lock);
+}
+
+static int sync_page(void *word)
+{
+	struct address_space *mapping;
+	struct page *page;
+
+	page = container_of((unsigned long *)word, struct page, flags);
+
+	/*
+	 * page_mapping() is being called without PG_locked held.
+	 * Some knowledge of the state and use of the page is used to
+	 * reduce the requirements down to a memory barrier.
+	 * The danger here is of a stale page_mapping() return value
+	 * indicating a struct address_space different from the one it's
+	 * associated with when it is associated with one.
+	 * After smp_mb(), it's either the correct page_mapping() for
+	 * the page, or an old page_mapping() and the page's own
+	 * page_mapping() has gone NULL.
+	 * The ->sync_page() address_space operation must tolerate
+	 * page_mapping() going NULL. By an amazing coincidence,
+	 * this comes about because none of the users of the page
+	 * in the ->sync_page() methods make essential use of the
+	 * page_mapping(), merely passing the page down to the backing
+	 * device's unplug functions when it's non-NULL, which in turn
+	 * ignore it for all cases but swap, where only page_private(page) is
+	 * of interest. When page_mapping() does go NULL, the entire
+	 * call stack gracefully ignores the page and returns.
+	 * -- wli
+	 */
+	smp_mb();
+	mapping = page_mapping(page);
+	if (mapping && mapping->a_ops && mapping->a_ops->sync_page)
+		mapping->a_ops->sync_page(page);
+	io_schedule();
+	return 0;
+}
+
+/**
+ * filemap_fdatawrite_range - start writeback against all of a mapping's
+ * dirty pages that lie within the byte offsets <start, end>
+ * @mapping:	address space structure to write
+ * @start:	offset in bytes where the range starts
+ * @end:	offset in bytes where the range ends
+ * @sync_mode:	enable synchronous operation
+ *
+ * If sync_mode is WB_SYNC_ALL then this is a "data integrity" operation, as
+ * opposed to a regular memory * cleansing writeback.  The difference between
+ * these two operations is that if a dirty page/buffer is encountered, it must
+ * be waited upon, and not just skipped over.
+ */
+static int __filemap_fdatawrite_range(struct address_space *mapping,
+	loff_t start, loff_t end, int sync_mode)
+{
+	int ret;
+	struct writeback_control wbc = {
+		.sync_mode = sync_mode,
+		.nr_to_write = mapping->nrpages * 2,
+		.start = start,
+		.end = end,
+	};
+
+	if (!mapping_cap_writeback_dirty(mapping))
+		return 0;
+
+	ret = do_writepages(mapping, &wbc);
+	return ret;
+}
+
+static inline int __filemap_fdatawrite(struct address_space *mapping,
+	int sync_mode)
+{
+	return __filemap_fdatawrite_range(mapping, 0, 0, sync_mode);
+}
+
+int filemap_fdatawrite(struct address_space *mapping)
+{
+	return __filemap_fdatawrite(mapping, WB_SYNC_ALL);
+}
+EXPORT_SYMBOL(filemap_fdatawrite);
+
+static int filemap_fdatawrite_range(struct address_space *mapping,
+	loff_t start, loff_t end)
+{
+	return __filemap_fdatawrite_range(mapping, start, end, WB_SYNC_ALL);
+}
+
+/*
+ * This is a mostly non-blocking flush.  Not suitable for data-integrity
+ * purposes - I/O may not be started against all dirty pages.
+ */
+int filemap_flush(struct address_space *mapping)
+{
+	return __filemap_fdatawrite(mapping, WB_SYNC_NONE);
+}
+EXPORT_SYMBOL(filemap_flush);
+
+/*
+ * Wait for writeback to complete against pages indexed by start->end
+ * inclusive
+ */
+static int wait_on_page_writeback_range(struct address_space *mapping,
+				pgoff_t start, pgoff_t end)
+{
+	struct pagevec pvec;
+	int nr_pages;
+	int ret = 0;
+	pgoff_t index;
+
+	if (end < start)
+		return 0;
+
+	pagevec_init(&pvec, 0);
+	index = start;
+	while ((index <= end) &&
+			(nr_pages = pagevec_lookup_tag(&pvec, mapping, &index,
+			PAGECACHE_TAG_WRITEBACK,
+			min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1)) != 0) {
+		unsigned i;
+
+		for (i = 0; i < nr_pages; i++) {
+			struct page *page = pvec.pages[i];
+
+			/* until radix tree lookup accepts end_index */
+			if (page->index > end)
+				continue;
+
+			wait_on_page_writeback(page);
+			if (PageError(page))
+				ret = -EIO;
+		}
+		pagevec_release(&pvec);
+		cond_resched();
+	}
+
+	/* Check for outstanding write errors */
+	if (test_and_clear_bit(AS_ENOSPC, &mapping->flags))
+		ret = -ENOSPC;
+	if (test_and_clear_bit(AS_EIO, &mapping->flags))
+		ret = -EIO;
+
+	return ret;
+}
+
+/*
+ * Write and wait upon all the pages in the passed range.  This is a "data
+ * integrity" operation.  It waits upon in-flight writeout before starting and
+ * waiting upon new writeout.  If there was an IO error, return it.
+ *
+ * We need to re-take i_sem during the generic_osync_inode list walk because
+ * it is otherwise livelockable.
+ */
+int sync_page_range(struct inode *inode, struct address_space *mapping,
+			loff_t pos, size_t count)
+{
+	pgoff_t start = pos >> PAGE_CACHE_SHIFT;
+	pgoff_t end = (pos + count - 1) >> PAGE_CACHE_SHIFT;
+	int ret;
+
+	if (!mapping_cap_writeback_dirty(mapping) || !count)
+		return 0;
+	ret = filemap_fdatawrite_range(mapping, pos, pos + count - 1);
+	if (ret == 0) {
+		down(&inode->i_sem);
+		ret = generic_osync_inode(inode, mapping, OSYNC_METADATA);
+		up(&inode->i_sem);
+	}
+	if (ret == 0)
+		ret = wait_on_page_writeback_range(mapping, start, end);
+	return ret;
+}
+EXPORT_SYMBOL(sync_page_range);
+
+/*
+ * Note: Holding i_sem across sync_page_range_nolock is not a good idea
+ * as it forces O_SYNC writers to different parts of the same file
+ * to be serialised right until io completion.
+ */
+static int sync_page_range_nolock(struct inode *inode,
+				  struct address_space *mapping,
+				  loff_t pos, size_t count)
+{
+	pgoff_t start = pos >> PAGE_CACHE_SHIFT;
+	pgoff_t end = (pos + count - 1) >> PAGE_CACHE_SHIFT;
+	int ret;
+
+	if (!mapping_cap_writeback_dirty(mapping) || !count)
+		return 0;
+	ret = filemap_fdatawrite_range(mapping, pos, pos + count - 1);
+	if (ret == 0)
+		ret = generic_osync_inode(inode, mapping, OSYNC_METADATA);
+	if (ret == 0)
+		ret = wait_on_page_writeback_range(mapping, start, end);
+	return ret;
+}
+
+/**
+ * filemap_fdatawait - walk the list of under-writeback pages of the given
+ *     address space and wait for all of them.
+ *
+ * @mapping: address space structure to wait for
+ */
+int filemap_fdatawait(struct address_space *mapping)
+{
+	loff_t i_size = i_size_read(mapping->host);
+
+	if (i_size == 0)
+		return 0;
+
+	return wait_on_page_writeback_range(mapping, 0,
+				(i_size - 1) >> PAGE_CACHE_SHIFT);
+}
+EXPORT_SYMBOL(filemap_fdatawait);
+
+int filemap_write_and_wait(struct address_space *mapping)
+{
+	int retval = 0;
+
+	if (mapping->nrpages) {
+		retval = filemap_fdatawrite(mapping);
+		if (retval == 0)
+			retval = filemap_fdatawait(mapping);
+	}
+	return retval;
+}
+
+int filemap_write_and_wait_range(struct address_space *mapping,
+				 loff_t lstart, loff_t lend)
+{
+	int retval = 0;
+
+	if (mapping->nrpages) {
+		retval = __filemap_fdatawrite_range(mapping, lstart, lend,
+						    WB_SYNC_ALL);
+		if (retval == 0)
+			retval = wait_on_page_writeback_range(mapping,
+						    lstart >> PAGE_CACHE_SHIFT,
+						    lend >> PAGE_CACHE_SHIFT);
+	}
+	return retval;
+}
+
+/*
+ * This function is used to add newly allocated pagecache pages:
+ * the page is new, so we can just run SetPageLocked() against it.
+ * The other page state flags were set by rmqueue().
+ *
+ * This function does not add the page to the LRU.  The caller must do that.
+ */
+int add_to_page_cache(struct page *page, struct address_space *mapping,
+		pgoff_t offset, gfp_t gfp_mask)
+{
+	int error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM);
+
+	if (error == 0) {
+		write_lock_irq(&mapping->tree_lock);
+		error = radix_tree_insert(&mapping->page_tree, offset, page);
+		if (!error) {
+			page_cache_get(page);
+			SetPageLocked(page);
+			page->mapping = mapping;
+			page->index = offset;
+			mapping->nrpages++;
+			pagecache_acct(1);
+		}
+		write_unlock_irq(&mapping->tree_lock);
+		radix_tree_preload_end();
+	}
+	return error;
+}
+
+EXPORT_SYMBOL(add_to_page_cache);
+
+int add_to_page_cache_lru(struct page *page, struct address_space *mapping,
+				pgoff_t offset, gfp_t gfp_mask)
+{
+	int ret = add_to_page_cache(page, mapping, offset, gfp_mask);
+	if (ret == 0)
+		lru_cache_add(page);
+	return ret;
+}
+
+/*
+ * In order to wait for pages to become available there must be
+ * waitqueues associated with pages. By using a hash table of
+ * waitqueues where the bucket discipline is to maintain all
+ * waiters on the same queue and wake all when any of the pages
+ * become available, and for the woken contexts to check to be
+ * sure the appropriate page became available, this saves space
+ * at a cost of "thundering herd" phenomena during rare hash
+ * collisions.
+ */
+static wait_queue_head_t *page_waitqueue(struct page *page)
+{
+	const struct zone *zone = page_zone(page);
+
+	return &zone->wait_table[hash_ptr(page, zone->wait_table_bits)];
+}
+
+static inline void wake_up_page(struct page *page, int bit)
+{
+	__wake_up_bit(page_waitqueue(page), &page->flags, bit);
+}
+
+void fastcall wait_on_page_bit(struct page *page, int bit_nr)
+{
+	DEFINE_WAIT_BIT(wait, &page->flags, bit_nr);
+
+	if (test_bit(bit_nr, &page->flags))
+		__wait_on_bit(page_waitqueue(page), &wait, sync_page,
+							TASK_UNINTERRUPTIBLE);
+}
+EXPORT_SYMBOL(wait_on_page_bit);
+
+/**
+ * unlock_page() - unlock a locked page
+ *
+ * @page: the page
+ *
+ * Unlocks the page and wakes up sleepers in ___wait_on_page_locked().
+ * Also wakes sleepers in wait_on_page_writeback() because the wakeup
+ * mechananism between PageLocked pages and PageWriteback pages is shared.
+ * But that's OK - sleepers in wait_on_page_writeback() just go back to sleep.
+ *
+ * The first mb is necessary to safely close the critical section opened by the
+ * TestSetPageLocked(), the second mb is necessary to enforce ordering between
+ * the clear_bit and the read of the waitqueue (to avoid SMP races with a
+ * parallel wait_on_page_locked()).
+ */
+void fastcall unlock_page(struct page *page)
+{
+	smp_mb__before_clear_bit();
+	if (!TestClearPageLocked(page))
+		BUG();
+	smp_mb__after_clear_bit(); 
+	wake_up_page(page, PG_locked);
+}
+EXPORT_SYMBOL(unlock_page);
+
+/*
+ * End writeback against a page.
+ */
+void end_page_writeback(struct page *page)
+{
+	if (!TestClearPageReclaim(page) || rotate_reclaimable_page(page)) {
+		if (!test_clear_page_writeback(page))
+			BUG();
+	}
+	smp_mb__after_clear_bit();
+	wake_up_page(page, PG_writeback);
+}
+EXPORT_SYMBOL(end_page_writeback);
+
+/*
+ * Get a lock on the page, assuming we need to sleep to get it.
+ *
+ * Ugly: running sync_page() in state TASK_UNINTERRUPTIBLE is scary.  If some
+ * random driver's requestfn sets TASK_RUNNING, we could busywait.  However
+ * chances are that on the second loop, the block layer's plug list is empty,
+ * so sync_page() will then return in state TASK_UNINTERRUPTIBLE.
+ */
+void fastcall __lock_page(struct page *page)
+{
+	DEFINE_WAIT_BIT(wait, &page->flags, PG_locked);
+
+	__wait_on_bit_lock(page_waitqueue(page), &wait, sync_page,
+							TASK_UNINTERRUPTIBLE);
+}
+EXPORT_SYMBOL(__lock_page);
+
+/*
+ * a rather lightweight function, finding and getting a reference to a
+ * hashed page atomically.
+ */
+struct page * find_get_page(struct address_space *mapping, unsigned long offset)
+{
+	struct page *page;
+
+	read_lock_irq(&mapping->tree_lock);
+	page = radix_tree_lookup(&mapping->page_tree, offset);
+	if (page)
+		page_cache_get(page);
+	read_unlock_irq(&mapping->tree_lock);
+	return page;
+}
+
+EXPORT_SYMBOL(find_get_page);
+
+/*
+ * Same as above, but trylock it instead of incrementing the count.
+ */
+struct page *find_trylock_page(struct address_space *mapping, unsigned long offset)
+{
+	struct page *page;
+
+	read_lock_irq(&mapping->tree_lock);
+	page = radix_tree_lookup(&mapping->page_tree, offset);
+	if (page && TestSetPageLocked(page))
+		page = NULL;
+	read_unlock_irq(&mapping->tree_lock);
+	return page;
+}
+
+EXPORT_SYMBOL(find_trylock_page);
+
+/**
+ * find_lock_page - locate, pin and lock a pagecache page
+ *
+ * @mapping: the address_space to search
+ * @offset: the page index
+ *
+ * Locates the desired pagecache page, locks it, increments its reference
+ * count and returns its address.
+ *
+ * Returns zero if the page was not present. find_lock_page() may sleep.
+ */
+struct page *find_lock_page(struct address_space *mapping,
+				unsigned long offset)
+{
+	struct page *page;
+
+	read_lock_irq(&mapping->tree_lock);
+repeat:
+	page = radix_tree_lookup(&mapping->page_tree, offset);
+	if (page) {
+		page_cache_get(page);
+		if (TestSetPageLocked(page)) {
+			read_unlock_irq(&mapping->tree_lock);
+			lock_page(page);
+			read_lock_irq(&mapping->tree_lock);
+
+			/* Has the page been truncated while we slept? */
+			if (page->mapping != mapping || page->index != offset) {
+				unlock_page(page);
+				page_cache_release(page);
+				goto repeat;
+			}
+		}
+	}
+	read_unlock_irq(&mapping->tree_lock);
+	return page;
+}
+
+EXPORT_SYMBOL(find_lock_page);
+
+/**
+ * find_or_create_page - locate or add a pagecache page
+ *
+ * @mapping: the page's address_space
+ * @index: the page's index into the mapping
+ * @gfp_mask: page allocation mode
+ *
+ * Locates a page in the pagecache.  If the page is not present, a new page
+ * is allocated using @gfp_mask and is added to the pagecache and to the VM's
+ * LRU list.  The returned page is locked and has its reference count
+ * incremented.
+ *
+ * find_or_create_page() may sleep, even if @gfp_flags specifies an atomic
+ * allocation!
+ *
+ * find_or_create_page() returns the desired page's address, or zero on
+ * memory exhaustion.
+ */
+struct page *find_or_create_page(struct address_space *mapping,
+		unsigned long index, gfp_t gfp_mask)
+{
+	struct page *page, *cached_page = NULL;
+	int err;
+repeat:
+	page = find_lock_page(mapping, index);
+	if (!page) {
+		if (!cached_page) {
+			cached_page = alloc_page(gfp_mask);
+			if (!cached_page)
+				return NULL;
+		}
+		err = add_to_page_cache_lru(cached_page, mapping,
+					index, gfp_mask);
+		if (!err) {
+			page = cached_page;
+			cached_page = NULL;
+		} else if (err == -EEXIST)
+			goto repeat;
+	}
+	if (cached_page)
+		page_cache_release(cached_page);
+	return page;
+}
+
+EXPORT_SYMBOL(find_or_create_page);
+
+/**
+ * find_get_pages - gang pagecache lookup
+ * @mapping:	The address_space to search
+ * @start:	The starting page index
+ * @nr_pages:	The maximum number of pages
+ * @pages:	Where the resulting pages are placed
+ *
+ * find_get_pages() will search for and return a group of up to
+ * @nr_pages pages in the mapping.  The pages are placed at @pages.
+ * find_get_pages() takes a reference against the returned pages.
+ *
+ * The search returns a group of mapping-contiguous pages with ascending
+ * indexes.  There may be holes in the indices due to not-present pages.
+ *
+ * find_get_pages() returns the number of pages which were found.
+ */
+unsigned find_get_pages(struct address_space *mapping, pgoff_t start,
+			    unsigned int nr_pages, struct page **pages)
+{
+	unsigned int i;
+	unsigned int ret;
+
+	read_lock_irq(&mapping->tree_lock);
+	ret = radix_tree_gang_lookup(&mapping->page_tree,
+				(void **)pages, start, nr_pages);
+	for (i = 0; i < ret; i++)
+		page_cache_get(pages[i]);
+	read_unlock_irq(&mapping->tree_lock);
+	return ret;
+}
+
+/*
+ * Like find_get_pages, except we only return pages which are tagged with
+ * `tag'.   We update *index to index the next page for the traversal.
+ */
+unsigned find_get_pages_tag(struct address_space *mapping, pgoff_t *index,
+			int tag, unsigned int nr_pages, struct page **pages)
+{
+	unsigned int i;
+	unsigned int ret;
+
+	read_lock_irq(&mapping->tree_lock);
+	ret = radix_tree_gang_lookup_tag(&mapping->page_tree,
+				(void **)pages, *index, nr_pages, tag);
+	for (i = 0; i < ret; i++)
+		page_cache_get(pages[i]);
+	if (ret)
+		*index = pages[ret - 1]->index + 1;
+	read_unlock_irq(&mapping->tree_lock);
+	return ret;
+}
+
+/*
+ * Same as grab_cache_page, but do not wait if the page is unavailable.
+ * This is intended for speculative data generators, where the data can
+ * be regenerated if the page couldn't be grabbed.  This routine should
+ * be safe to call while holding the lock for another page.
+ *
+ * Clear __GFP_FS when allocating the page to avoid recursion into the fs
+ * and deadlock against the caller's locked page.
+ */
+struct page *
+grab_cache_page_nowait(struct address_space *mapping, unsigned long index)
+{
+	struct page *page = find_get_page(mapping, index);
+	gfp_t gfp_mask;
+
+	if (page) {
+		if (!TestSetPageLocked(page))
+			return page;
+		page_cache_release(page);
+		return NULL;
+	}
+	gfp_mask = mapping_gfp_mask(mapping) & ~__GFP_FS;
+	page = alloc_pages(gfp_mask, 0);
+	if (page && add_to_page_cache_lru(page, mapping, index, gfp_mask)) {
+		page_cache_release(page);
+		page = NULL;
+	}
+	return page;
+}
+
+EXPORT_SYMBOL(grab_cache_page_nowait);
+
+/*
+ * This is a generic file read routine, and uses the
+ * mapping->a_ops->readpage() function for the actual low-level
+ * stuff.
+ *
+ * This is really ugly. But the goto's actually try to clarify some
+ * of the logic when it comes to error handling etc.
+ *
+ * Note the struct file* is only passed for the use of readpage.  It may be
+ * NULL.
+ */
+void do_generic_mapping_read(struct address_space *mapping,
+			     struct file_ra_state *_ra,
+			     struct file *filp,
+			     loff_t *ppos,
+			     read_descriptor_t *desc,
+			     read_actor_t actor)
+{
+	struct inode *inode = mapping->host;
+	unsigned long index;
+	unsigned long end_index;
+	unsigned long offset;
+	unsigned long last_index;
+	unsigned long next_index;
+	unsigned long prev_index;
+	loff_t isize;
+	struct page *cached_page;
+	int error;
+	struct file_ra_state ra = *_ra;
+
+	cached_page = NULL;
+	index = *ppos >> PAGE_CACHE_SHIFT;
+	next_index = index;
+	prev_index = ra.prev_page;
+	last_index = (*ppos + desc->count + PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT;
+	offset = *ppos & ~PAGE_CACHE_MASK;
+
+	isize = i_size_read(inode);
+	if (!isize)
+		goto out;
+
+	end_index = (isize - 1) >> PAGE_CACHE_SHIFT;
+	for (;;) {
+		struct page *page;
+		unsigned long nr, ret;
+
+		/* nr is the maximum number of bytes to copy from this page */
+		nr = PAGE_CACHE_SIZE;
+		if (index >= end_index) {
+			if (index > end_index)
+				goto out;
+			nr = ((isize - 1) & ~PAGE_CACHE_MASK) + 1;
+			if (nr <= offset) {
+				goto out;
+			}
+		}
+		nr = nr - offset;
+
+		cond_resched();
+		if (index == next_index)
+			next_index = page_cache_readahead(mapping, &ra, filp,
+					index, last_index - index);
+
+find_page:
+		page = find_get_page(mapping, index);
+		if (unlikely(page == NULL)) {
+			handle_ra_miss(mapping, &ra, index);
+			goto no_cached_page;
+		}
+		if (!PageUptodate(page))
+			goto page_not_up_to_date;
+page_ok:
+
+		/* If users can be writing to this page using arbitrary
+		 * virtual addresses, take care about potential aliasing
+		 * before reading the page on the kernel side.
+		 */
+		if (mapping_writably_mapped(mapping))
+			flush_dcache_page(page);
+
+		/*
+		 * When (part of) the same page is read multiple times
+		 * in succession, only mark it as accessed the first time.
+		 */
+		if (prev_index != index)
+			mark_page_accessed(page);
+		prev_index = index;
+
+		/*
+		 * Ok, we have the page, and it's up-to-date, so
+		 * now we can copy it to user space...
+		 *
+		 * The actor routine returns how many bytes were actually used..
+		 * NOTE! This may not be the same as how much of a user buffer
+		 * we filled up (we may be padding etc), so we can only update
+		 * "pos" here (the actor routine has to update the user buffer
+		 * pointers and the remaining count).
+		 */
+		ret = actor(desc, page, offset, nr);
+		offset += ret;
+		index += offset >> PAGE_CACHE_SHIFT;
+		offset &= ~PAGE_CACHE_MASK;
+
+		page_cache_release(page);
+		if (ret == nr && desc->count)
+			continue;
+		goto out;
+
+page_not_up_to_date:
+		/* Get exclusive access to the page ... */
+		lock_page(page);
+
+		/* Did it get unhashed before we got the lock? */
+		if (!page->mapping) {
+			unlock_page(page);
+			page_cache_release(page);
+			continue;
+		}
+
+		/* Did somebody else fill it already? */
+		if (PageUptodate(page)) {
+			unlock_page(page);
+			goto page_ok;
+		}
+
+readpage:
+		/* Start the actual read. The read will unlock the page. */
+		error = mapping->a_ops->readpage(filp, page);
+
+		if (unlikely(error))
+			goto readpage_error;
+
+		if (!PageUptodate(page)) {
+			lock_page(page);
+			if (!PageUptodate(page)) {
+				if (page->mapping == NULL) {
+					/*
+					 * invalidate_inode_pages got it
+					 */
+					unlock_page(page);
+					page_cache_release(page);
+					goto find_page;
+				}
+				unlock_page(page);
+				error = -EIO;
+				goto readpage_error;
+			}
+			unlock_page(page);
+		}
+
+		/*
+		 * i_size must be checked after we have done ->readpage.
+		 *
+		 * Checking i_size after the readpage allows us to calculate
+		 * the correct value for "nr", which means the zero-filled
+		 * part of the page is not copied back to userspace (unless
+		 * another truncate extends the file - this is desired though).
+		 */
+		isize = i_size_read(inode);
+		end_index = (isize - 1) >> PAGE_CACHE_SHIFT;
+		if (unlikely(!isize || index > end_index)) {
+			page_cache_release(page);
+			goto out;
+		}
+
+		/* nr is the maximum number of bytes to copy from this page */
+		nr = PAGE_CACHE_SIZE;
+		if (index == end_index) {
+			nr = ((isize - 1) & ~PAGE_CACHE_MASK) + 1;
+			if (nr <= offset) {
+				page_cache_release(page);
+				goto out;
+			}
+		}
+		nr = nr - offset;
+		goto page_ok;
+
+readpage_error:
+		/* UHHUH! A synchronous read error occurred. Report it */
+		desc->error = error;
+		page_cache_release(page);
+		goto out;
+
+no_cached_page:
+		/*
+		 * Ok, it wasn't cached, so we need to create a new
+		 * page..
+		 */
+		if (!cached_page) {
+			cached_page = page_cache_alloc_cold(mapping);
+			if (!cached_page) {
+				desc->error = -ENOMEM;
+				goto out;
+			}
+		}
+		error = add_to_page_cache_lru(cached_page, mapping,
+						index, GFP_KERNEL);
+		if (error) {
+			if (error == -EEXIST)
+				goto find_page;
+			desc->error = error;
+			goto out;
+		}
+		page = cached_page;
+		cached_page = NULL;
+		goto readpage;
+	}
+
+out:
+	*_ra = ra;
+
+	*ppos = ((loff_t) index << PAGE_CACHE_SHIFT) + offset;
+	if (cached_page)
+		page_cache_release(cached_page);
+	if (filp)
+		file_accessed(filp);
+}
+
+EXPORT_SYMBOL(do_generic_mapping_read);
+
+int file_read_actor(read_descriptor_t *desc, struct page *page,
+			unsigned long offset, unsigned long size)
+{
+	char *kaddr;
+	unsigned long left, count = desc->count;
+
+	if (size > count)
+		size = count;
+
+	/*
+	 * Faults on the destination of a read are common, so do it before
+	 * taking the kmap.
+	 */
+	if (!fault_in_pages_writeable(desc->arg.buf, size)) {
+		kaddr = kmap_atomic(page, KM_USER0);
+		left = __copy_to_user_inatomic(desc->arg.buf,
+						kaddr + offset, size);
+		kunmap_atomic(kaddr, KM_USER0);
+		if (left == 0)
+			goto success;
+	}
+
+	/* Do it the slow way */
+	kaddr = kmap(page);
+	left = __copy_to_user(desc->arg.buf, kaddr + offset, size);
+	kunmap(page);
+
+	if (left) {
+		size -= left;
+		desc->error = -EFAULT;
+	}
+success:
+	desc->count = count - size;
+	desc->written += size;
+	desc->arg.buf += size;
+	return size;
+}
+
+/*
+ * This is the "read()" routine for all filesystems
+ * that can use the page cache directly.
+ */
+ssize_t
+__generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
+		unsigned long nr_segs, loff_t *ppos)
+{
+	struct file *filp = iocb->ki_filp;
+	ssize_t retval;
+	unsigned long seg;
+	size_t count;
+
+	count = 0;
+	for (seg = 0; seg < nr_segs; seg++) {
+		const struct iovec *iv = &iov[seg];
+
+		/*
+		 * If any segment has a negative length, or the cumulative
+		 * length ever wraps negative then return -EINVAL.
+		 */
+		count += iv->iov_len;
+		if (unlikely((ssize_t)(count|iv->iov_len) < 0))
+			return -EINVAL;
+		if (access_ok(VERIFY_WRITE, iv->iov_base, iv->iov_len))
+			continue;
+		if (seg == 0)
+			return -EFAULT;
+		nr_segs = seg;
+		count -= iv->iov_len;	/* This segment is no good */
+		break;
+	}
+
+	/* coalesce the iovecs and go direct-to-BIO for O_DIRECT */
+	if (filp->f_flags & O_DIRECT) {
+		loff_t pos = *ppos, size;
+		struct address_space *mapping;
+		struct inode *inode;
+
+		mapping = filp->f_mapping;
+		inode = mapping->host;
+		retval = 0;
+		if (!count)
+			goto out; /* skip atime */
+		size = i_size_read(inode);
+		if (pos < size) {
+			retval = generic_file_direct_IO(READ, iocb,
+						iov, pos, nr_segs);
+			if (retval > 0 && !is_sync_kiocb(iocb))
+				retval = -EIOCBQUEUED;
+			if (retval > 0)
+				*ppos = pos + retval;
+		}
+		file_accessed(filp);
+		goto out;
+	}
+
+	retval = 0;
+	if (count) {
+		for (seg = 0; seg < nr_segs; seg++) {
+			read_descriptor_t desc;
+
+			desc.written = 0;
+			desc.arg.buf = iov[seg].iov_base;
+			desc.count = iov[seg].iov_len;
+			if (desc.count == 0)
+				continue;
+			desc.error = 0;
+			do_generic_file_read(filp,ppos,&desc,file_read_actor);
+			retval += desc.written;
+			if (desc.error) {
+				retval = retval ?: desc.error;
+				break;
+			}
+		}
+	}
+out:
+	return retval;
+}
+
+EXPORT_SYMBOL(__generic_file_aio_read);
+
+ssize_t
+generic_file_aio_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t pos)
+{
+	struct iovec local_iov = { .iov_base = buf, .iov_len = count };
+
+	BUG_ON(iocb->ki_pos != pos);
+	return __generic_file_aio_read(iocb, &local_iov, 1, &iocb->ki_pos);
+}
+
+EXPORT_SYMBOL(generic_file_aio_read);
+
+ssize_t
+generic_file_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
+{
+	struct iovec local_iov = { .iov_base = buf, .iov_len = count };
+	struct kiocb kiocb;
+	ssize_t ret;
+
+	init_sync_kiocb(&kiocb, filp);
+	ret = __generic_file_aio_read(&kiocb, &local_iov, 1, ppos);
+	if (-EIOCBQUEUED == ret)
+		ret = wait_on_sync_kiocb(&kiocb);
+	return ret;
+}
+
+EXPORT_SYMBOL(generic_file_read);
+
+int file_send_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size)
+{
+	ssize_t written;
+	unsigned long count = desc->count;
+	struct file *file = desc->arg.data;
+
+	if (size > count)
+		size = count;
+
+	written = file->f_op->sendpage(file, page, offset,
+				       size, &file->f_pos, size<count);
+	if (written < 0) {
+		desc->error = written;
+		written = 0;
+	}
+	desc->count = count - written;
+	desc->written += written;
+	return written;
+}
+
+ssize_t generic_file_sendfile(struct file *in_file, loff_t *ppos,
+			 size_t count, read_actor_t actor, void *target)
+{
+	read_descriptor_t desc;
+
+	if (!count)
+		return 0;
+
+	desc.written = 0;
+	desc.count = count;
+	desc.arg.data = target;
+	desc.error = 0;
+
+	do_generic_file_read(in_file, ppos, &desc, actor);
+	if (desc.written)
+		return desc.written;
+	return desc.error;
+}
+
+EXPORT_SYMBOL(generic_file_sendfile);
+
+static ssize_t
+do_readahead(struct address_space *mapping, struct file *filp,
+	     unsigned long index, unsigned long nr)
+{
+	if (!mapping || !mapping->a_ops || !mapping->a_ops->readpage)
+		return -EINVAL;
+
+	force_page_cache_readahead(mapping, filp, index,
+					max_sane_readahead(nr));
+	return 0;
+}
+
+asmlinkage ssize_t sys_readahead(int fd, loff_t offset, size_t count)
+{
+	ssize_t ret;
+	struct file *file;
+
+	ret = -EBADF;
+	file = fget(fd);
+	if (file) {
+		if (file->f_mode & FMODE_READ) {
+			struct address_space *mapping = file->f_mapping;
+			unsigned long start = offset >> PAGE_CACHE_SHIFT;
+			unsigned long end = (offset + count - 1) >> PAGE_CACHE_SHIFT;
+			unsigned long len = end - start + 1;
+			ret = do_readahead(mapping, file, start, len);
+		}
+		fput(file);
+	}
+	return ret;
+}
+
+#ifdef CONFIG_MMU
+/*
+ * This adds the requested page to the page cache if it isn't already there,
+ * and schedules an I/O to read in its contents from disk.
+ */
+static int FASTCALL(page_cache_read(struct file * file, unsigned long offset));
+static int fastcall page_cache_read(struct file * file, unsigned long offset)
+{
+	struct address_space *mapping = file->f_mapping;
+	struct page *page; 
+	int error;
+
+	page = page_cache_alloc_cold(mapping);
+	if (!page)
+		return -ENOMEM;
+
+	error = add_to_page_cache_lru(page, mapping, offset, GFP_KERNEL);
+	if (!error) {
+		error = mapping->a_ops->readpage(file, page);
+		page_cache_release(page);
+		return error;
+	}
+
+	/*
+	 * We arrive here in the unlikely event that someone 
+	 * raced with us and added our page to the cache first
+	 * or we are out of memory for radix-tree nodes.
+	 */
+	page_cache_release(page);
+	return error == -EEXIST ? 0 : error;
+}
+
+#define MMAP_LOTSAMISS  (100)
+
+/*
+ * filemap_nopage() is invoked via the vma operations vector for a
+ * mapped memory region to read in file data during a page fault.
+ *
+ * The goto's are kind of ugly, but this streamlines the normal case of having
+ * it in the page cache, and handles the special cases reasonably without
+ * having a lot of duplicated code.
+ */
+struct page *filemap_nopage(struct vm_area_struct *area,
+				unsigned long address, int *type)
+{
+	int error;
+	struct file *file = area->vm_file;
+	struct address_space *mapping = file->f_mapping;
+	struct file_ra_state *ra = &file->f_ra;
+	struct inode *inode = mapping->host;
+	struct page *page;
+	unsigned long size, pgoff;
+	int did_readaround = 0, majmin = VM_FAULT_MINOR;
+
+	pgoff = ((address-area->vm_start) >> PAGE_CACHE_SHIFT) + area->vm_pgoff;
+
+retry_all:
+	size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+	if (pgoff >= size)
+		goto outside_data_content;
+
+	/* If we don't want any read-ahead, don't bother */
+	if (VM_RandomReadHint(area))
+		goto no_cached_page;
+
+	/*
+	 * The readahead code wants to be told about each and every page
+	 * so it can build and shrink its windows appropriately
+	 *
+	 * For sequential accesses, we use the generic readahead logic.
+	 */
+	if (VM_SequentialReadHint(area))
+		page_cache_readahead(mapping, ra, file, pgoff, 1);
+
+	/*
+	 * Do we have something in the page cache already?
+	 */
+retry_find:
+	page = find_get_page(mapping, pgoff);
+	if (!page) {
+		unsigned long ra_pages;
+
+		if (VM_SequentialReadHint(area)) {
+			handle_ra_miss(mapping, ra, pgoff);
+			goto no_cached_page;
+		}
+		ra->mmap_miss++;
+
+		/*
+		 * Do we miss much more than hit in this file? If so,
+		 * stop bothering with read-ahead. It will only hurt.
+		 */
+		if (ra->mmap_miss > ra->mmap_hit + MMAP_LOTSAMISS)
+			goto no_cached_page;
+
+		/*
+		 * To keep the pgmajfault counter straight, we need to
+		 * check did_readaround, as this is an inner loop.
+		 */
+		if (!did_readaround) {
+			majmin = VM_FAULT_MAJOR;
+			inc_page_state(pgmajfault);
+		}
+		did_readaround = 1;
+		ra_pages = max_sane_readahead(file->f_ra.ra_pages);
+		if (ra_pages) {
+			pgoff_t start = 0;
+
+			if (pgoff > ra_pages / 2)
+				start = pgoff - ra_pages / 2;
+			do_page_cache_readahead(mapping, file, start, ra_pages);
+		}
+		page = find_get_page(mapping, pgoff);
+		if (!page)
+			goto no_cached_page;
+	}
+
+	if (!did_readaround)
+		ra->mmap_hit++;
+
+	/*
+	 * Ok, found a page in the page cache, now we need to check
+	 * that it's up-to-date.
+	 */
+	if (!PageUptodate(page))
+		goto page_not_uptodate;
+
+success:
+	/*
+	 * Found the page and have a reference on it.
+	 */
+	mark_page_accessed(page);
+	if (type)
+		*type = majmin;
+	return page;
+
+outside_data_content:
+	/*
+	 * An external ptracer can access pages that normally aren't
+	 * accessible..
+	 */
+	if (area->vm_mm == current->mm)
+		return NULL;
+	/* Fall through to the non-read-ahead case */
+no_cached_page:
+	/*
+	 * We're only likely to ever get here if MADV_RANDOM is in
+	 * effect.
+	 */
+	error = page_cache_read(file, pgoff);
+	grab_swap_token();
+
+	/*
+	 * The page we want has now been added to the page cache.
+	 * In the unlikely event that someone removed it in the
+	 * meantime, we'll just come back here and read it again.
+	 */
+	if (error >= 0)
+		goto retry_find;
+
+	/*
+	 * An error return from page_cache_read can result if the
+	 * system is low on memory, or a problem occurs while trying
+	 * to schedule I/O.
+	 */
+	if (error == -ENOMEM)
+		return NOPAGE_OOM;
+	return NULL;
+
+page_not_uptodate:
+	if (!did_readaround) {
+		majmin = VM_FAULT_MAJOR;
+		inc_page_state(pgmajfault);
+	}
+	lock_page(page);
+
+	/* Did it get unhashed while we waited for it? */
+	if (!page->mapping) {
+		unlock_page(page);
+		page_cache_release(page);
+		goto retry_all;
+	}
+
+	/* Did somebody else get it up-to-date? */
+	if (PageUptodate(page)) {
+		unlock_page(page);
+		goto success;
+	}
+
+	if (!mapping->a_ops->readpage(file, page)) {
+		wait_on_page_locked(page);
+		if (PageUptodate(page))
+			goto success;
+	}
+
+	/*
+	 * Umm, take care of errors if the page isn't up-to-date.
+	 * Try to re-read it _once_. We do this synchronously,
+	 * because there really aren't any performance issues here
+	 * and we need to check for errors.
+	 */
+	lock_page(page);
+
+	/* Somebody truncated the page on us? */
+	if (!page->mapping) {
+		unlock_page(page);
+		page_cache_release(page);
+		goto retry_all;
+	}
+
+	/* Somebody else successfully read it in? */
+	if (PageUptodate(page)) {
+		unlock_page(page);
+		goto success;
+	}
+	ClearPageError(page);
+	if (!mapping->a_ops->readpage(file, page)) {
+		wait_on_page_locked(page);
+		if (PageUptodate(page))
+			goto success;
+	}
+
+	/*
+	 * Things didn't work out. Return zero to tell the
+	 * mm layer so, possibly freeing the page cache page first.
+	 */
+	page_cache_release(page);
+	return NULL;
+}
+
+EXPORT_SYMBOL(filemap_nopage);
+
+static struct page * filemap_getpage(struct file *file, unsigned long pgoff,
+					int nonblock)
+{
+	struct address_space *mapping = file->f_mapping;
+	struct page *page;
+	int error;
+
+	/*
+	 * Do we have something in the page cache already?
+	 */
+retry_find:
+	page = find_get_page(mapping, pgoff);
+	if (!page) {
+		if (nonblock)
+			return NULL;
+		goto no_cached_page;
+	}
+
+	/*
+	 * Ok, found a page in the page cache, now we need to check
+	 * that it's up-to-date.
+	 */
+	if (!PageUptodate(page)) {
+		if (nonblock) {
+			page_cache_release(page);
+			return NULL;
+		}
+		goto page_not_uptodate;
+	}
+
+success:
+	/*
+	 * Found the page and have a reference on it.
+	 */
+	mark_page_accessed(page);
+	return page;
+
+no_cached_page:
+	error = page_cache_read(file, pgoff);
+
+	/*
+	 * The page we want has now been added to the page cache.
+	 * In the unlikely event that someone removed it in the
+	 * meantime, we'll just come back here and read it again.
+	 */
+	if (error >= 0)
+		goto retry_find;
+
+	/*
+	 * An error return from page_cache_read can result if the
+	 * system is low on memory, or a problem occurs while trying
+	 * to schedule I/O.
+	 */
+	return NULL;
+
+page_not_uptodate:
+	lock_page(page);
+
+	/* Did it get unhashed while we waited for it? */
+	if (!page->mapping) {
+		unlock_page(page);
+		goto err;
+	}
+
+	/* Did somebody else get it up-to-date? */
+	if (PageUptodate(page)) {
+		unlock_page(page);
+		goto success;
+	}
+
+	if (!mapping->a_ops->readpage(file, page)) {
+		wait_on_page_locked(page);
+		if (PageUptodate(page))
+			goto success;
+	}
+
+	/*
+	 * Umm, take care of errors if the page isn't up-to-date.
+	 * Try to re-read it _once_. We do this synchronously,
+	 * because there really aren't any performance issues here
+	 * and we need to check for errors.
+	 */
+	lock_page(page);
+
+	/* Somebody truncated the page on us? */
+	if (!page->mapping) {
+		unlock_page(page);
+		goto err;
+	}
+	/* Somebody else successfully read it in? */
+	if (PageUptodate(page)) {
+		unlock_page(page);
+		goto success;
+	}
+
+	ClearPageError(page);
+	if (!mapping->a_ops->readpage(file, page)) {
+		wait_on_page_locked(page);
+		if (PageUptodate(page))
+			goto success;
+	}
+
+	/*
+	 * Things didn't work out. Return zero to tell the
+	 * mm layer so, possibly freeing the page cache page first.
+	 */
+err:
+	page_cache_release(page);
+
+	return NULL;
+}
+
+int filemap_populate(struct vm_area_struct *vma, unsigned long addr,
+		unsigned long len, pgprot_t prot, unsigned long pgoff,
+		int nonblock)
+{
+	struct file *file = vma->vm_file;
+	struct address_space *mapping = file->f_mapping;
+	struct inode *inode = mapping->host;
+	unsigned long size;
+	struct mm_struct *mm = vma->vm_mm;
+	struct page *page;
+	int err;
+
+	if (!nonblock)
+		force_page_cache_readahead(mapping, vma->vm_file,
+					pgoff, len >> PAGE_CACHE_SHIFT);
+
+repeat:
+	size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+	if (pgoff + (len >> PAGE_CACHE_SHIFT) > size)
+		return -EINVAL;
+
+	page = filemap_getpage(file, pgoff, nonblock);
+
+	/* XXX: This is wrong, a filesystem I/O error may have happened. Fix that as
+	 * done in shmem_populate calling shmem_getpage */
+	if (!page && !nonblock)
+		return -ENOMEM;
+
+	if (page) {
+		err = install_page(mm, vma, addr, page, prot);
+		if (err) {
+			page_cache_release(page);
+			return err;
+		}
+	} else if (vma->vm_flags & VM_NONLINEAR) {
+		/* No page was found just because we can't read it in now (being
+		 * here implies nonblock != 0), but the page may exist, so set
+		 * the PTE to fault it in later. */
+		err = install_file_pte(mm, vma, addr, pgoff, prot);
+		if (err)
+			return err;
+	}
+
+	len -= PAGE_SIZE;
+	addr += PAGE_SIZE;
+	pgoff++;
+	if (len)
+		goto repeat;
+
+	return 0;
+}
+EXPORT_SYMBOL(filemap_populate);
+
+struct vm_operations_struct generic_file_vm_ops = {
+	.nopage		= filemap_nopage,
+	.populate	= filemap_populate,
+};
+
+/* This is used for a general mmap of a disk file */
+
+int generic_file_mmap(struct file * file, struct vm_area_struct * vma)
+{
+	struct address_space *mapping = file->f_mapping;
+
+	if (!mapping->a_ops->readpage)
+		return -ENOEXEC;
+	file_accessed(file);
+	vma->vm_ops = &generic_file_vm_ops;
+	return 0;
+}
+
+/*
+ * This is for filesystems which do not implement ->writepage.
+ */
+int generic_file_readonly_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
+		return -EINVAL;
+	return generic_file_mmap(file, vma);
+}
+#else
+int generic_file_mmap(struct file * file, struct vm_area_struct * vma)
+{
+	return -ENOSYS;
+}
+int generic_file_readonly_mmap(struct file * file, struct vm_area_struct * vma)
+{
+	return -ENOSYS;
+}
+#endif /* CONFIG_MMU */
+
+EXPORT_SYMBOL(generic_file_mmap);
+EXPORT_SYMBOL(generic_file_readonly_mmap);
+
+static inline struct page *__read_cache_page(struct address_space *mapping,
+				unsigned long index,
+				int (*filler)(void *,struct page*),
+				void *data)
+{
+	struct page *page, *cached_page = NULL;
+	int err;
+repeat:
+	page = find_get_page(mapping, index);
+	if (!page) {
+		if (!cached_page) {
+			cached_page = page_cache_alloc_cold(mapping);
+			if (!cached_page)
+				return ERR_PTR(-ENOMEM);
+		}
+		err = add_to_page_cache_lru(cached_page, mapping,
+					index, GFP_KERNEL);
+		if (err == -EEXIST)
+			goto repeat;
+		if (err < 0) {
+			/* Presumably ENOMEM for radix tree node */
+			page_cache_release(cached_page);
+			return ERR_PTR(err);
+		}
+		page = cached_page;
+		cached_page = NULL;
+		err = filler(data, page);
+		if (err < 0) {
+			page_cache_release(page);
+			page = ERR_PTR(err);
+		}
+	}
+	if (cached_page)
+		page_cache_release(cached_page);
+	return page;
+}
+
+/*
+ * Read into the page cache. If a page already exists,
+ * and PageUptodate() is not set, try to fill the page.
+ */
+struct page *read_cache_page(struct address_space *mapping,
+				unsigned long index,
+				int (*filler)(void *,struct page*),
+				void *data)
+{
+	struct page *page;
+	int err;
+
+retry:
+	page = __read_cache_page(mapping, index, filler, data);
+	if (IS_ERR(page))
+		goto out;
+	mark_page_accessed(page);
+	if (PageUptodate(page))
+		goto out;
+
+	lock_page(page);
+	if (!page->mapping) {
+		unlock_page(page);
+		page_cache_release(page);
+		goto retry;
+	}
+	if (PageUptodate(page)) {
+		unlock_page(page);
+		goto out;
+	}
+	err = filler(data, page);
+	if (err < 0) {
+		page_cache_release(page);
+		page = ERR_PTR(err);
+	}
+ out:
+	return page;
+}
+
+EXPORT_SYMBOL(read_cache_page);
+
+/*
+ * If the page was newly created, increment its refcount and add it to the
+ * caller's lru-buffering pagevec.  This function is specifically for
+ * generic_file_write().
+ */
+static inline struct page *
+__grab_cache_page(struct address_space *mapping, unsigned long index,
+			struct page **cached_page, struct pagevec *lru_pvec)
+{
+	int err;
+	struct page *page;
+repeat:
+	page = find_lock_page(mapping, index);
+	if (!page) {
+		if (!*cached_page) {
+			*cached_page = page_cache_alloc(mapping);
+			if (!*cached_page)
+				return NULL;
+		}
+		err = add_to_page_cache(*cached_page, mapping,
+					index, GFP_KERNEL);
+		if (err == -EEXIST)
+			goto repeat;
+		if (err == 0) {
+			page = *cached_page;
+			page_cache_get(page);
+			if (!pagevec_add(lru_pvec, page))
+				__pagevec_lru_add(lru_pvec);
+			*cached_page = NULL;
+		}
+	}
+	return page;
+}
+
+/*
+ * The logic we want is
+ *
+ *	if suid or (sgid and xgrp)
+ *		remove privs
+ */
+int remove_suid(struct dentry *dentry)
+{
+	mode_t mode = dentry->d_inode->i_mode;
+	int kill = 0;
+	int result = 0;
+
+	/* suid always must be killed */
+	if (unlikely(mode & S_ISUID))
+		kill = ATTR_KILL_SUID;
+
+	/*
+	 * sgid without any exec bits is just a mandatory locking mark; leave
+	 * it alone.  If some exec bits are set, it's a real sgid; kill it.
+	 */
+	if (unlikely((mode & S_ISGID) && (mode & S_IXGRP)))
+		kill |= ATTR_KILL_SGID;
+
+	if (unlikely(kill && !capable(CAP_FSETID))) {
+		struct iattr newattrs;
+
+		newattrs.ia_valid = ATTR_FORCE | kill;
+		result = notify_change(dentry, &newattrs);
+	}
+	return result;
+}
+EXPORT_SYMBOL(remove_suid);
+
+size_t
+__filemap_copy_from_user_iovec(char *vaddr, 
+			const struct iovec *iov, size_t base, size_t bytes)
+{
+	size_t copied = 0, left = 0;
+
+	while (bytes) {
+		char __user *buf = iov->iov_base + base;
+		int copy = min(bytes, iov->iov_len - base);
+
+		base = 0;
+		left = __copy_from_user_inatomic(vaddr, buf, copy);
+		copied += copy;
+		bytes -= copy;
+		vaddr += copy;
+		iov++;
+
+		if (unlikely(left)) {
+			/* zero the rest of the target like __copy_from_user */
+			if (bytes)
+				memset(vaddr, 0, bytes);
+			break;
+		}
+	}
+	return copied - left;
+}
+
+/*
+ * Performs necessary checks before doing a write
+ *
+ * Can adjust writing position aor amount of bytes to write.
+ * Returns appropriate error code that caller should return or
+ * zero in case that write should be allowed.
+ */
+inline int generic_write_checks(struct file *file, loff_t *pos, size_t *count, int isblk)
+{
+	struct inode *inode = file->f_mapping->host;
+	unsigned long limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
+
+        if (unlikely(*pos < 0))
+                return -EINVAL;
+
+	if (!isblk) {
+		/* FIXME: this is for backwards compatibility with 2.4 */
+		if (file->f_flags & O_APPEND)
+                        *pos = i_size_read(inode);
+
+		if (limit != RLIM_INFINITY) {
+			if (*pos >= limit) {
+				send_sig(SIGXFSZ, current, 0);
+				return -EFBIG;
+			}
+			if (*count > limit - (typeof(limit))*pos) {
+				*count = limit - (typeof(limit))*pos;
+			}
+		}
+	}
+
+	/*
+	 * LFS rule
+	 */
+	if (unlikely(*pos + *count > MAX_NON_LFS &&
+				!(file->f_flags & O_LARGEFILE))) {
+		if (*pos >= MAX_NON_LFS) {
+			send_sig(SIGXFSZ, current, 0);
+			return -EFBIG;
+		}
+		if (*count > MAX_NON_LFS - (unsigned long)*pos) {
+			*count = MAX_NON_LFS - (unsigned long)*pos;
+		}
+	}
+
+	/*
+	 * Are we about to exceed the fs block limit ?
+	 *
+	 * If we have written data it becomes a short write.  If we have
+	 * exceeded without writing data we send a signal and return EFBIG.
+	 * Linus frestrict idea will clean these up nicely..
+	 */
+	if (likely(!isblk)) {
+		if (unlikely(*pos >= inode->i_sb->s_maxbytes)) {
+			if (*count || *pos > inode->i_sb->s_maxbytes) {
+				send_sig(SIGXFSZ, current, 0);
+				return -EFBIG;
+			}
+			/* zero-length writes at ->s_maxbytes are OK */
+		}
+
+		if (unlikely(*pos + *count > inode->i_sb->s_maxbytes))
+			*count = inode->i_sb->s_maxbytes - *pos;
+	} else {
+		loff_t isize;
+		if (bdev_read_only(I_BDEV(inode)))
+			return -EPERM;
+		isize = i_size_read(inode);
+		if (*pos >= isize) {
+			if (*count || *pos > isize)
+				return -ENOSPC;
+		}
+
+		if (*pos + *count > isize)
+			*count = isize - *pos;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(generic_write_checks);
+
+ssize_t
+generic_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
+		unsigned long *nr_segs, loff_t pos, loff_t *ppos,
+		size_t count, size_t ocount)
+{
+	struct file	*file = iocb->ki_filp;
+	struct address_space *mapping = file->f_mapping;
+	struct inode	*inode = mapping->host;
+	ssize_t		written;
+
+	if (count != ocount)
+		*nr_segs = iov_shorten((struct iovec *)iov, *nr_segs, count);
+
+	written = generic_file_direct_IO(WRITE, iocb, iov, pos, *nr_segs);
+	if (written > 0) {
+		loff_t end = pos + written;
+		if (end > i_size_read(inode) && !S_ISBLK(inode->i_mode)) {
+			i_size_write(inode,  end);
+			mark_inode_dirty(inode);
+		}
+		*ppos = end;
+	}
+
+	/*
+	 * Sync the fs metadata but not the minor inode changes and
+	 * of course not the data as we did direct DMA for the IO.
+	 * i_sem is held, which protects generic_osync_inode() from
+	 * livelocking.
+	 */
+	if (written >= 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
+		int err = generic_osync_inode(inode, mapping, OSYNC_METADATA);
+		if (err < 0)
+			written = err;
+	}
+	if (written == count && !is_sync_kiocb(iocb))
+		written = -EIOCBQUEUED;
+	return written;
+}
+EXPORT_SYMBOL(generic_file_direct_write);
+
+ssize_t
+generic_file_buffered_write(struct kiocb *iocb, const struct iovec *iov,
+		unsigned long nr_segs, loff_t pos, loff_t *ppos,
+		size_t count, ssize_t written)
+{
+	struct file *file = iocb->ki_filp;
+	struct address_space * mapping = file->f_mapping;
+	struct address_space_operations *a_ops = mapping->a_ops;
+	struct inode 	*inode = mapping->host;
+	long		status = 0;
+	struct page	*page;
+	struct page	*cached_page = NULL;
+	size_t		bytes;
+	struct pagevec	lru_pvec;
+	const struct iovec *cur_iov = iov; /* current iovec */
+	size_t		iov_base = 0;	   /* offset in the current iovec */
+	char __user	*buf;
+
+	pagevec_init(&lru_pvec, 0);
+
+	/*
+	 * handle partial DIO write.  Adjust cur_iov if needed.
+	 */
+	if (likely(nr_segs == 1))
+		buf = iov->iov_base + written;
+	else {
+		filemap_set_next_iovec(&cur_iov, &iov_base, written);
+		buf = cur_iov->iov_base + iov_base;
+	}
+
+	do {
+		unsigned long index;
+		unsigned long offset;
+		unsigned long maxlen;
+		size_t copied;
+
+		offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */
+		index = pos >> PAGE_CACHE_SHIFT;
+		bytes = PAGE_CACHE_SIZE - offset;
+		if (bytes > count)
+			bytes = count;
+
+		/*
+		 * Bring in the user page that we will copy from _first_.
+		 * Otherwise there's a nasty deadlock on copying from the
+		 * same page as we're writing to, without it being marked
+		 * up-to-date.
+		 */
+		maxlen = cur_iov->iov_len - iov_base;
+		if (maxlen > bytes)
+			maxlen = bytes;
+		fault_in_pages_readable(buf, maxlen);
+
+		page = __grab_cache_page(mapping,index,&cached_page,&lru_pvec);
+		if (!page) {
+			status = -ENOMEM;
+			break;
+		}
+
+		status = a_ops->prepare_write(file, page, offset, offset+bytes);
+		if (unlikely(status)) {
+			loff_t isize = i_size_read(inode);
+			/*
+			 * prepare_write() may have instantiated a few blocks
+			 * outside i_size.  Trim these off again.
+			 */
+			unlock_page(page);
+			page_cache_release(page);
+			if (pos + bytes > isize)
+				vmtruncate(inode, isize);
+			break;
+		}
+		if (likely(nr_segs == 1))
+			copied = filemap_copy_from_user(page, offset,
+							buf, bytes);
+		else
+			copied = filemap_copy_from_user_iovec(page, offset,
+						cur_iov, iov_base, bytes);
+		flush_dcache_page(page);
+		status = a_ops->commit_write(file, page, offset, offset+bytes);
+		if (likely(copied > 0)) {
+			if (!status)
+				status = copied;
+
+			if (status >= 0) {
+				written += status;
+				count -= status;
+				pos += status;
+				buf += status;
+				if (unlikely(nr_segs > 1)) {
+					filemap_set_next_iovec(&cur_iov,
+							&iov_base, status);
+					if (count)
+						buf = cur_iov->iov_base +
+							iov_base;
+				} else {
+					iov_base += status;
+				}
+			}
+		}
+		if (unlikely(copied != bytes))
+			if (status >= 0)
+				status = -EFAULT;
+		unlock_page(page);
+		mark_page_accessed(page);
+		page_cache_release(page);
+		if (status < 0)
+			break;
+		balance_dirty_pages_ratelimited(mapping);
+		cond_resched();
+	} while (count);
+	*ppos = pos;
+
+	if (cached_page)
+		page_cache_release(cached_page);
+
+	/*
+	 * For now, when the user asks for O_SYNC, we'll actually give O_DSYNC
+	 */
+	if (likely(status >= 0)) {
+		if (unlikely((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
+			if (!a_ops->writepage || !is_sync_kiocb(iocb))
+				status = generic_osync_inode(inode, mapping,
+						OSYNC_METADATA|OSYNC_DATA);
+		}
+  	}
+	
+	/*
+	 * If we get here for O_DIRECT writes then we must have fallen through
+	 * to buffered writes (block instantiation inside i_size).  So we sync
+	 * the file data here, to try to honour O_DIRECT expectations.
+	 */
+	if (unlikely(file->f_flags & O_DIRECT) && written)
+		status = filemap_write_and_wait(mapping);
+
+	pagevec_lru_add(&lru_pvec);
+	return written ? written : status;
+}
+EXPORT_SYMBOL(generic_file_buffered_write);
+
+static ssize_t
+__generic_file_aio_write_nolock(struct kiocb *iocb, const struct iovec *iov,
+				unsigned long nr_segs, loff_t *ppos)
+{
+	struct file *file = iocb->ki_filp;
+	struct address_space * mapping = file->f_mapping;
+	size_t ocount;		/* original count */
+	size_t count;		/* after file limit checks */
+	struct inode 	*inode = mapping->host;
+	unsigned long	seg;
+	loff_t		pos;
+	ssize_t		written;
+	ssize_t		err;
+
+	ocount = 0;
+	for (seg = 0; seg < nr_segs; seg++) {
+		const struct iovec *iv = &iov[seg];
+
+		/*
+		 * If any segment has a negative length, or the cumulative
+		 * length ever wraps negative then return -EINVAL.
+		 */
+		ocount += iv->iov_len;
+		if (unlikely((ssize_t)(ocount|iv->iov_len) < 0))
+			return -EINVAL;
+		if (access_ok(VERIFY_READ, iv->iov_base, iv->iov_len))
+			continue;
+		if (seg == 0)
+			return -EFAULT;
+		nr_segs = seg;
+		ocount -= iv->iov_len;	/* This segment is no good */
+		break;
+	}
+
+	count = ocount;
+	pos = *ppos;
+
+	vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
+
+	/* We can write back this queue in page reclaim */
+	current->backing_dev_info = mapping->backing_dev_info;
+	written = 0;
+
+	err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode));
+	if (err)
+		goto out;
+
+	if (count == 0)
+		goto out;
+
+	err = remove_suid(file->f_dentry);
+	if (err)
+		goto out;
+
+	inode_update_time(inode, 1);
+
+	/* coalesce the iovecs and go direct-to-BIO for O_DIRECT */
+	if (unlikely(file->f_flags & O_DIRECT)) {
+		written = generic_file_direct_write(iocb, iov,
+				&nr_segs, pos, ppos, count, ocount);
+		if (written < 0 || written == count)
+			goto out;
+		/*
+		 * direct-io write to a hole: fall through to buffered I/O
+		 * for completing the rest of the request.
+		 */
+		pos += written;
+		count -= written;
+	}
+
+	written = generic_file_buffered_write(iocb, iov, nr_segs,
+			pos, ppos, count, written);
+out:
+	current->backing_dev_info = NULL;
+	return written ? written : err;
+}
+EXPORT_SYMBOL(generic_file_aio_write_nolock);
+
+ssize_t
+generic_file_aio_write_nolock(struct kiocb *iocb, const struct iovec *iov,
+				unsigned long nr_segs, loff_t *ppos)
+{
+	struct file *file = iocb->ki_filp;
+	struct address_space *mapping = file->f_mapping;
+	struct inode *inode = mapping->host;
+	ssize_t ret;
+	loff_t pos = *ppos;
+
+	ret = __generic_file_aio_write_nolock(iocb, iov, nr_segs, ppos);
+
+	if (ret > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
+		int err;
+
+		err = sync_page_range_nolock(inode, mapping, pos, ret);
+		if (err < 0)
+			ret = err;
+	}
+	return ret;
+}
+
+static ssize_t
+__generic_file_write_nolock(struct file *file, const struct iovec *iov,
+				unsigned long nr_segs, loff_t *ppos)
+{
+	struct kiocb kiocb;
+	ssize_t ret;
+
+	init_sync_kiocb(&kiocb, file);
+	ret = __generic_file_aio_write_nolock(&kiocb, iov, nr_segs, ppos);
+	if (ret == -EIOCBQUEUED)
+		ret = wait_on_sync_kiocb(&kiocb);
+	return ret;
+}
+
+ssize_t
+generic_file_write_nolock(struct file *file, const struct iovec *iov,
+				unsigned long nr_segs, loff_t *ppos)
+{
+	struct kiocb kiocb;
+	ssize_t ret;
+
+	init_sync_kiocb(&kiocb, file);
+	ret = generic_file_aio_write_nolock(&kiocb, iov, nr_segs, ppos);
+	if (-EIOCBQUEUED == ret)
+		ret = wait_on_sync_kiocb(&kiocb);
+	return ret;
+}
+EXPORT_SYMBOL(generic_file_write_nolock);
+
+ssize_t generic_file_aio_write(struct kiocb *iocb, const char __user *buf,
+			       size_t count, loff_t pos)
+{
+	struct file *file = iocb->ki_filp;
+	struct address_space *mapping = file->f_mapping;
+	struct inode *inode = mapping->host;
+	ssize_t ret;
+	struct iovec local_iov = { .iov_base = (void __user *)buf,
+					.iov_len = count };
+
+	BUG_ON(iocb->ki_pos != pos);
+
+	down(&inode->i_sem);
+	ret = __generic_file_aio_write_nolock(iocb, &local_iov, 1,
+						&iocb->ki_pos);
+	up(&inode->i_sem);
+
+	if (ret > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
+		ssize_t err;
+
+		err = sync_page_range(inode, mapping, pos, ret);
+		if (err < 0)
+			ret = err;
+	}
+	return ret;
+}
+EXPORT_SYMBOL(generic_file_aio_write);
+
+ssize_t generic_file_write(struct file *file, const char __user *buf,
+			   size_t count, loff_t *ppos)
+{
+	struct address_space *mapping = file->f_mapping;
+	struct inode *inode = mapping->host;
+	ssize_t	ret;
+	struct iovec local_iov = { .iov_base = (void __user *)buf,
+					.iov_len = count };
+
+	down(&inode->i_sem);
+	ret = __generic_file_write_nolock(file, &local_iov, 1, ppos);
+	up(&inode->i_sem);
+
+	if (ret > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
+		ssize_t err;
+
+		err = sync_page_range(inode, mapping, *ppos - ret, ret);
+		if (err < 0)
+			ret = err;
+	}
+	return ret;
+}
+EXPORT_SYMBOL(generic_file_write);
+
+ssize_t generic_file_readv(struct file *filp, const struct iovec *iov,
+			unsigned long nr_segs, loff_t *ppos)
+{
+	struct kiocb kiocb;
+	ssize_t ret;
+
+	init_sync_kiocb(&kiocb, filp);
+	ret = __generic_file_aio_read(&kiocb, iov, nr_segs, ppos);
+	if (-EIOCBQUEUED == ret)
+		ret = wait_on_sync_kiocb(&kiocb);
+	return ret;
+}
+EXPORT_SYMBOL(generic_file_readv);
+
+ssize_t generic_file_writev(struct file *file, const struct iovec *iov,
+			unsigned long nr_segs, loff_t *ppos)
+{
+	struct address_space *mapping = file->f_mapping;
+	struct inode *inode = mapping->host;
+	ssize_t ret;
+
+	down(&inode->i_sem);
+	ret = __generic_file_write_nolock(file, iov, nr_segs, ppos);
+	up(&inode->i_sem);
+
+	if (ret > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
+		int err;
+
+		err = sync_page_range(inode, mapping, *ppos - ret, ret);
+		if (err < 0)
+			ret = err;
+	}
+	return ret;
+}
+EXPORT_SYMBOL(generic_file_writev);
+
+/*
+ * Called under i_sem for writes to S_ISREG files.   Returns -EIO if something
+ * went wrong during pagecache shootdown.
+ */
+static ssize_t
+generic_file_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
+	loff_t offset, unsigned long nr_segs)
+{
+	struct file *file = iocb->ki_filp;
+	struct address_space *mapping = file->f_mapping;
+	ssize_t retval;
+	size_t write_len = 0;
+
+	/*
+	 * If it's a write, unmap all mmappings of the file up-front.  This
+	 * will cause any pte dirty bits to be propagated into the pageframes
+	 * for the subsequent filemap_write_and_wait().
+	 */
+	if (rw == WRITE) {
+		write_len = iov_length(iov, nr_segs);
+	       	if (mapping_mapped(mapping))
+			unmap_mapping_range(mapping, offset, write_len, 0);
+	}
+
+	retval = filemap_write_and_wait(mapping);
+	if (retval == 0) {
+		retval = mapping->a_ops->direct_IO(rw, iocb, iov,
+						offset, nr_segs);
+		if (rw == WRITE && mapping->nrpages) {
+			pgoff_t end = (offset + write_len - 1)
+						>> PAGE_CACHE_SHIFT;
+			int err = invalidate_inode_pages2_range(mapping,
+					offset >> PAGE_CACHE_SHIFT, end);
+			if (err)
+				retval = err;
+		}
+	}
+	return retval;
+}
diff -urN oldtree/mm/mempolicy.c newtree/mm/mempolicy.c
--- oldtree/mm/mempolicy.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/mm/mempolicy.c	2006-02-13 19:20:01.278317968 -0500
@@ -1141,6 +1141,30 @@
 	return 0;
 }
 
+void mpol_shared_policy_init(struct shared_policy *info, int policy,
+				nodemask_t *policy_nodes)
+{
+	info->root = RB_ROOT;
+	spin_lock_init(&info->lock);
+
+	if (policy != MPOL_DEFAULT) {
+		struct mempolicy *newpol;
+
+		/* Falls back to MPOL_DEFAULT on any error */
+		newpol = mpol_new(policy, policy_nodes);
+		if (!IS_ERR(newpol)) {
+			/* Create pseudo-vma that contains just the policy */
+			struct vm_area_struct pvma;
+
+			memset(&pvma, 0, sizeof(struct vm_area_struct));
+			/* Policy covers entire file */
+			pvma.vm_end = TASK_SIZE;
+			mpol_set_shared_policy(info, &pvma, newpol);
+			mpol_free(newpol);
+		}
+	}
+}
+
 int mpol_set_shared_policy(struct shared_policy *info,
 			struct vm_area_struct *vma, struct mempolicy *npol)
 {
diff -urN oldtree/mm/mempolicy.c.orig newtree/mm/mempolicy.c.orig
--- oldtree/mm/mempolicy.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/mm/mempolicy.c.orig	2006-02-13 19:20:01.279317816 -0500
@@ -0,0 +1,1272 @@
+/*
+ * Simple NUMA memory policy for the Linux kernel.
+ *
+ * Copyright 2003,2004 Andi Kleen, SuSE Labs.
+ * (C) Copyright 2005 Christoph Lameter, Silicon Graphics, Inc.
+ * Subject to the GNU Public License, version 2.
+ *
+ * NUMA policy allows the user to give hints in which node(s) memory should
+ * be allocated.
+ *
+ * Support four policies per VMA and per process:
+ *
+ * The VMA policy has priority over the process policy for a page fault.
+ *
+ * interleave     Allocate memory interleaved over a set of nodes,
+ *                with normal fallback if it fails.
+ *                For VMA based allocations this interleaves based on the
+ *                offset into the backing object or offset into the mapping
+ *                for anonymous memory. For process policy an process counter
+ *                is used.
+ *
+ * bind           Only allocate memory on a specific set of nodes,
+ *                no fallback.
+ *                FIXME: memory is allocated starting with the first node
+ *                to the last. It would be better if bind would truly restrict
+ *                the allocation to memory nodes instead
+ *
+ * preferred       Try a specific node first before normal fallback.
+ *                As a special case node -1 here means do the allocation
+ *                on the local CPU. This is normally identical to default,
+ *                but useful to set in a VMA when you have a non default
+ *                process policy.
+ *
+ * default        Allocate on the local node first, or when on a VMA
+ *                use the process policy. This is what Linux always did
+ *		  in a NUMA aware kernel and still does by, ahem, default.
+ *
+ * The process policy is applied for most non interrupt memory allocations
+ * in that process' context. Interrupts ignore the policies and always
+ * try to allocate on the local CPU. The VMA policy is only applied for memory
+ * allocations for a VMA in the VM.
+ *
+ * Currently there are a few corner cases in swapping where the policy
+ * is not applied, but the majority should be handled. When process policy
+ * is used it is not remembered over swap outs/swap ins.
+ *
+ * Only the highest zone in the zone hierarchy gets policied. Allocations
+ * requesting a lower zone just use default policy. This implies that
+ * on systems with highmem kernel lowmem allocation don't get policied.
+ * Same with GFP_DMA allocations.
+ *
+ * For shmfs/tmpfs/hugetlbfs shared memory the policy is shared between
+ * all users and remembered even when nobody has memory mapped.
+ */
+
+/* Notebook:
+   fix mmap readahead to honour policy and enable policy for any page cache
+   object
+   statistics for bigpages
+   global policy for page cache? currently it uses process policy. Requires
+   first item above.
+   handle mremap for shared memory (currently ignored for the policy)
+   grows down?
+   make bind policy root only? It can trigger oom much faster and the
+   kernel is not always grateful with that.
+   could replace all the switch()es with a mempolicy_ops structure.
+*/
+
+#include <linux/mempolicy.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/hugetlb.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/nodemask.h>
+#include <linux/cpuset.h>
+#include <linux/gfp.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/compat.h>
+#include <linux/mempolicy.h>
+#include <asm/tlbflush.h>
+#include <asm/uaccess.h>
+
+static kmem_cache_t *policy_cache;
+static kmem_cache_t *sn_cache;
+
+#define PDprintk(fmt...)
+
+/* Highest zone. An specific allocation for a zone below that is not
+   policied. */
+static int policy_zone;
+
+struct mempolicy default_policy = {
+	.refcnt = ATOMIC_INIT(1), /* never free it */
+	.policy = MPOL_DEFAULT,
+};
+
+/* Do sanity checking on a policy */
+static int mpol_check_policy(int mode, nodemask_t *nodes)
+{
+	int empty = nodes_empty(*nodes);
+
+	switch (mode) {
+	case MPOL_DEFAULT:
+		if (!empty)
+			return -EINVAL;
+		break;
+	case MPOL_BIND:
+	case MPOL_INTERLEAVE:
+		/* Preferred will only use the first bit, but allow
+		   more for now. */
+		if (empty)
+			return -EINVAL;
+		break;
+	}
+	return nodes_subset(*nodes, node_online_map) ? 0 : -EINVAL;
+}
+/* Generate a custom zonelist for the BIND policy. */
+static struct zonelist *bind_zonelist(nodemask_t *nodes)
+{
+	struct zonelist *zl;
+	int num, max, nd;
+
+	max = 1 + MAX_NR_ZONES * nodes_weight(*nodes);
+	zl = kmalloc(sizeof(void *) * max, GFP_KERNEL);
+	if (!zl)
+		return NULL;
+	num = 0;
+	for_each_node_mask(nd, *nodes) {
+		int k;
+		for (k = MAX_NR_ZONES-1; k >= 0; k--) {
+			struct zone *z = &NODE_DATA(nd)->node_zones[k];
+			if (!z->present_pages)
+				continue;
+			zl->zones[num++] = z;
+			if (k > policy_zone)
+				policy_zone = k;
+		}
+	}
+	zl->zones[num] = NULL;
+	return zl;
+}
+
+/* Create a new policy */
+static struct mempolicy *mpol_new(int mode, nodemask_t *nodes)
+{
+	struct mempolicy *policy;
+
+	PDprintk("setting mode %d nodes[0] %lx\n", mode, nodes_addr(*nodes)[0]);
+	if (mode == MPOL_DEFAULT)
+		return NULL;
+	policy = kmem_cache_alloc(policy_cache, GFP_KERNEL);
+	if (!policy)
+		return ERR_PTR(-ENOMEM);
+	atomic_set(&policy->refcnt, 1);
+	switch (mode) {
+	case MPOL_INTERLEAVE:
+		policy->v.nodes = *nodes;
+		if (nodes_weight(*nodes) == 0) {
+			kmem_cache_free(policy_cache, policy);
+			return ERR_PTR(-EINVAL);
+		}
+		break;
+	case MPOL_PREFERRED:
+		policy->v.preferred_node = first_node(*nodes);
+		if (policy->v.preferred_node >= MAX_NUMNODES)
+			policy->v.preferred_node = -1;
+		break;
+	case MPOL_BIND:
+		policy->v.zonelist = bind_zonelist(nodes);
+		if (policy->v.zonelist == NULL) {
+			kmem_cache_free(policy_cache, policy);
+			return ERR_PTR(-ENOMEM);
+		}
+		break;
+	}
+	policy->policy = mode;
+	return policy;
+}
+
+/* Ensure all existing pages follow the policy. */
+static int check_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
+		unsigned long addr, unsigned long end, nodemask_t *nodes)
+{
+	pte_t *orig_pte;
+	pte_t *pte;
+	spinlock_t *ptl;
+
+	orig_pte = pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
+	do {
+		struct page *page;
+		unsigned int nid;
+
+		if (!pte_present(*pte))
+			continue;
+		page = vm_normal_page(vma, addr, *pte);
+		if (!page)
+			continue;
+		nid = page_to_nid(page);
+		if (!node_isset(nid, *nodes))
+			break;
+	} while (pte++, addr += PAGE_SIZE, addr != end);
+	pte_unmap_unlock(orig_pte, ptl);
+	return addr != end;
+}
+
+static inline int check_pmd_range(struct vm_area_struct *vma, pud_t *pud,
+		unsigned long addr, unsigned long end, nodemask_t *nodes)
+{
+	pmd_t *pmd;
+	unsigned long next;
+
+	pmd = pmd_offset(pud, addr);
+	do {
+		next = pmd_addr_end(addr, end);
+		if (pmd_none_or_clear_bad(pmd))
+			continue;
+		if (check_pte_range(vma, pmd, addr, next, nodes))
+			return -EIO;
+	} while (pmd++, addr = next, addr != end);
+	return 0;
+}
+
+static inline int check_pud_range(struct vm_area_struct *vma, pgd_t *pgd,
+		unsigned long addr, unsigned long end, nodemask_t *nodes)
+{
+	pud_t *pud;
+	unsigned long next;
+
+	pud = pud_offset(pgd, addr);
+	do {
+		next = pud_addr_end(addr, end);
+		if (pud_none_or_clear_bad(pud))
+			continue;
+		if (check_pmd_range(vma, pud, addr, next, nodes))
+			return -EIO;
+	} while (pud++, addr = next, addr != end);
+	return 0;
+}
+
+static inline int check_pgd_range(struct vm_area_struct *vma,
+		unsigned long addr, unsigned long end, nodemask_t *nodes)
+{
+	pgd_t *pgd;
+	unsigned long next;
+
+	pgd = pgd_offset(vma->vm_mm, addr);
+	do {
+		next = pgd_addr_end(addr, end);
+		if (pgd_none_or_clear_bad(pgd))
+			continue;
+		if (check_pud_range(vma, pgd, addr, next, nodes))
+			return -EIO;
+	} while (pgd++, addr = next, addr != end);
+	return 0;
+}
+
+/* Step 1: check the range */
+static struct vm_area_struct *
+check_range(struct mm_struct *mm, unsigned long start, unsigned long end,
+	    nodemask_t *nodes, unsigned long flags)
+{
+	int err;
+	struct vm_area_struct *first, *vma, *prev;
+
+	first = find_vma(mm, start);
+	if (!first)
+		return ERR_PTR(-EFAULT);
+	prev = NULL;
+	for (vma = first; vma && vma->vm_start < end; vma = vma->vm_next) {
+		if (!vma->vm_next && vma->vm_end < end)
+			return ERR_PTR(-EFAULT);
+		if (prev && prev->vm_end < vma->vm_start)
+			return ERR_PTR(-EFAULT);
+		if ((flags & MPOL_MF_STRICT) && !is_vm_hugetlb_page(vma)) {
+			unsigned long endvma = vma->vm_end;
+			if (endvma > end)
+				endvma = end;
+			if (vma->vm_start > start)
+				start = vma->vm_start;
+			err = check_pgd_range(vma, start, endvma, nodes);
+			if (err) {
+				first = ERR_PTR(err);
+				break;
+			}
+		}
+		prev = vma;
+	}
+	return first;
+}
+
+/* Apply policy to a single VMA */
+static int policy_vma(struct vm_area_struct *vma, struct mempolicy *new)
+{
+	int err = 0;
+	struct mempolicy *old = vma->vm_policy;
+
+	PDprintk("vma %lx-%lx/%lx vm_ops %p vm_file %p set_policy %p\n",
+		 vma->vm_start, vma->vm_end, vma->vm_pgoff,
+		 vma->vm_ops, vma->vm_file,
+		 vma->vm_ops ? vma->vm_ops->set_policy : NULL);
+
+	if (vma->vm_ops && vma->vm_ops->set_policy)
+		err = vma->vm_ops->set_policy(vma, new);
+	if (!err) {
+		mpol_get(new);
+		vma->vm_policy = new;
+		mpol_free(old);
+	}
+	return err;
+}
+
+/* Step 2: apply policy to a range and do splits. */
+static int mbind_range(struct vm_area_struct *vma, unsigned long start,
+		       unsigned long end, struct mempolicy *new)
+{
+	struct vm_area_struct *next;
+	int err;
+
+	err = 0;
+	for (; vma && vma->vm_start < end; vma = next) {
+		next = vma->vm_next;
+		if (vma->vm_start < start)
+			err = split_vma(vma->vm_mm, vma, start, 1);
+		if (!err && vma->vm_end > end)
+			err = split_vma(vma->vm_mm, vma, end, 0);
+		if (!err)
+			err = policy_vma(vma, new);
+		if (err)
+			break;
+	}
+	return err;
+}
+
+static int contextualize_policy(int mode, nodemask_t *nodes)
+{
+	if (!nodes)
+		return 0;
+
+	/* Update current mems_allowed */
+	cpuset_update_current_mems_allowed();
+	/* Ignore nodes not set in current->mems_allowed */
+	cpuset_restrict_to_mems_allowed(nodes->bits);
+	return mpol_check_policy(mode, nodes);
+}
+
+long do_mbind(unsigned long start, unsigned long len,
+		unsigned long mode, nodemask_t *nmask, unsigned long flags)
+{
+	struct vm_area_struct *vma;
+	struct mm_struct *mm = current->mm;
+	struct mempolicy *new;
+	unsigned long end;
+	int err;
+
+	if ((flags & ~(unsigned long)(MPOL_MF_STRICT)) || mode > MPOL_MAX)
+		return -EINVAL;
+	if (start & ~PAGE_MASK)
+		return -EINVAL;
+	if (mode == MPOL_DEFAULT)
+		flags &= ~MPOL_MF_STRICT;
+	len = (len + PAGE_SIZE - 1) & PAGE_MASK;
+	end = start + len;
+	if (end < start)
+		return -EINVAL;
+	if (end == start)
+		return 0;
+	if (mpol_check_policy(mode, nmask))
+		return -EINVAL;
+	new = mpol_new(mode, nmask);
+	if (IS_ERR(new))
+		return PTR_ERR(new);
+
+	PDprintk("mbind %lx-%lx mode:%ld nodes:%lx\n",start,start+len,
+			mode,nodes_addr(nodes)[0]);
+
+	down_write(&mm->mmap_sem);
+	vma = check_range(mm, start, end, nmask, flags);
+	err = PTR_ERR(vma);
+	if (!IS_ERR(vma))
+		err = mbind_range(vma, start, end, new);
+	up_write(&mm->mmap_sem);
+	mpol_free(new);
+	return err;
+}
+
+/* Set the process memory policy */
+long do_set_mempolicy(int mode, nodemask_t *nodes)
+{
+	struct mempolicy *new;
+
+	if (contextualize_policy(mode, nodes))
+		return -EINVAL;
+	new = mpol_new(mode, nodes);
+	if (IS_ERR(new))
+		return PTR_ERR(new);
+	mpol_free(current->mempolicy);
+	current->mempolicy = new;
+	if (new && new->policy == MPOL_INTERLEAVE)
+		current->il_next = first_node(new->v.nodes);
+	return 0;
+}
+
+/* Fill a zone bitmap for a policy */
+static void get_zonemask(struct mempolicy *p, nodemask_t *nodes)
+{
+	int i;
+
+	nodes_clear(*nodes);
+	switch (p->policy) {
+	case MPOL_BIND:
+		for (i = 0; p->v.zonelist->zones[i]; i++)
+			node_set(p->v.zonelist->zones[i]->zone_pgdat->node_id,
+				*nodes);
+		break;
+	case MPOL_DEFAULT:
+		break;
+	case MPOL_INTERLEAVE:
+		*nodes = p->v.nodes;
+		break;
+	case MPOL_PREFERRED:
+		/* or use current node instead of online map? */
+		if (p->v.preferred_node < 0)
+			*nodes = node_online_map;
+		else
+			node_set(p->v.preferred_node, *nodes);
+		break;
+	default:
+		BUG();
+	}
+}
+
+static int lookup_node(struct mm_struct *mm, unsigned long addr)
+{
+	struct page *p;
+	int err;
+
+	err = get_user_pages(current, mm, addr & PAGE_MASK, 1, 0, 0, &p, NULL);
+	if (err >= 0) {
+		err = page_to_nid(p);
+		put_page(p);
+	}
+	return err;
+}
+
+/* Retrieve NUMA policy */
+long do_get_mempolicy(int *policy, nodemask_t *nmask,
+			unsigned long addr, unsigned long flags)
+{
+	int err;
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma = NULL;
+	struct mempolicy *pol = current->mempolicy;
+
+	cpuset_update_current_mems_allowed();
+	if (flags & ~(unsigned long)(MPOL_F_NODE|MPOL_F_ADDR))
+		return -EINVAL;
+	if (flags & MPOL_F_ADDR) {
+		down_read(&mm->mmap_sem);
+		vma = find_vma_intersection(mm, addr, addr+1);
+		if (!vma) {
+			up_read(&mm->mmap_sem);
+			return -EFAULT;
+		}
+		if (vma->vm_ops && vma->vm_ops->get_policy)
+			pol = vma->vm_ops->get_policy(vma, addr);
+		else
+			pol = vma->vm_policy;
+	} else if (addr)
+		return -EINVAL;
+
+	if (!pol)
+		pol = &default_policy;
+
+	if (flags & MPOL_F_NODE) {
+		if (flags & MPOL_F_ADDR) {
+			err = lookup_node(mm, addr);
+			if (err < 0)
+				goto out;
+			*policy = err;
+		} else if (pol == current->mempolicy &&
+				pol->policy == MPOL_INTERLEAVE) {
+			*policy = current->il_next;
+		} else {
+			err = -EINVAL;
+			goto out;
+		}
+	} else
+		*policy = pol->policy;
+
+	if (vma) {
+		up_read(&current->mm->mmap_sem);
+		vma = NULL;
+	}
+
+	err = 0;
+	if (nmask)
+		get_zonemask(pol, nmask);
+
+ out:
+	if (vma)
+		up_read(&current->mm->mmap_sem);
+	return err;
+}
+
+/*
+ * User space interface with variable sized bitmaps for nodelists.
+ */
+
+/* Copy a node mask from user space. */
+static int get_nodes(nodemask_t *nodes, unsigned long __user *nmask,
+		     unsigned long maxnode)
+{
+	unsigned long k;
+	unsigned long nlongs;
+	unsigned long endmask;
+
+	--maxnode;
+	nodes_clear(*nodes);
+	if (maxnode == 0 || !nmask)
+		return 0;
+
+	nlongs = BITS_TO_LONGS(maxnode);
+	if ((maxnode % BITS_PER_LONG) == 0)
+		endmask = ~0UL;
+	else
+		endmask = (1UL << (maxnode % BITS_PER_LONG)) - 1;
+
+	/* When the user specified more nodes than supported just check
+	   if the non supported part is all zero. */
+	if (nlongs > BITS_TO_LONGS(MAX_NUMNODES)) {
+		if (nlongs > PAGE_SIZE/sizeof(long))
+			return -EINVAL;
+		for (k = BITS_TO_LONGS(MAX_NUMNODES); k < nlongs; k++) {
+			unsigned long t;
+			if (get_user(t, nmask + k))
+				return -EFAULT;
+			if (k == nlongs - 1) {
+				if (t & endmask)
+					return -EINVAL;
+			} else if (t)
+				return -EINVAL;
+		}
+		nlongs = BITS_TO_LONGS(MAX_NUMNODES);
+		endmask = ~0UL;
+	}
+
+	if (copy_from_user(nodes_addr(*nodes), nmask, nlongs*sizeof(unsigned long)))
+		return -EFAULT;
+	nodes_addr(*nodes)[nlongs-1] &= endmask;
+	return 0;
+}
+
+/* Copy a kernel node mask to user space */
+static int copy_nodes_to_user(unsigned long __user *mask, unsigned long maxnode,
+			      nodemask_t *nodes)
+{
+	unsigned long copy = ALIGN(maxnode-1, 64) / 8;
+	const int nbytes = BITS_TO_LONGS(MAX_NUMNODES) * sizeof(long);
+
+	if (copy > nbytes) {
+		if (copy > PAGE_SIZE)
+			return -EINVAL;
+		if (clear_user((char __user *)mask + nbytes, copy - nbytes))
+			return -EFAULT;
+		copy = nbytes;
+	}
+	return copy_to_user(mask, nodes_addr(*nodes), copy) ? -EFAULT : 0;
+}
+
+asmlinkage long sys_mbind(unsigned long start, unsigned long len,
+			unsigned long mode,
+			unsigned long __user *nmask, unsigned long maxnode,
+			unsigned flags)
+{
+	nodemask_t nodes;
+	int err;
+
+	err = get_nodes(&nodes, nmask, maxnode);
+	if (err)
+		return err;
+	return do_mbind(start, len, mode, &nodes, flags);
+}
+
+/* Set the process memory policy */
+asmlinkage long sys_set_mempolicy(int mode, unsigned long __user *nmask,
+		unsigned long maxnode)
+{
+	int err;
+	nodemask_t nodes;
+
+	if (mode < 0 || mode > MPOL_MAX)
+		return -EINVAL;
+	err = get_nodes(&nodes, nmask, maxnode);
+	if (err)
+		return err;
+	return do_set_mempolicy(mode, &nodes);
+}
+
+/* Retrieve NUMA policy */
+asmlinkage long sys_get_mempolicy(int __user *policy,
+				unsigned long __user *nmask,
+				unsigned long maxnode,
+				unsigned long addr, unsigned long flags)
+{
+	int err, pval;
+	nodemask_t nodes;
+
+	if (nmask != NULL && maxnode < MAX_NUMNODES)
+		return -EINVAL;
+
+	err = do_get_mempolicy(&pval, &nodes, addr, flags);
+
+	if (err)
+		return err;
+
+	if (policy && put_user(pval, policy))
+		return -EFAULT;
+
+	if (nmask)
+		err = copy_nodes_to_user(nmask, maxnode, &nodes);
+
+	return err;
+}
+
+#ifdef CONFIG_COMPAT
+
+asmlinkage long compat_sys_get_mempolicy(int __user *policy,
+				     compat_ulong_t __user *nmask,
+				     compat_ulong_t maxnode,
+				     compat_ulong_t addr, compat_ulong_t flags)
+{
+	long err;
+	unsigned long __user *nm = NULL;
+	unsigned long nr_bits, alloc_size;
+	DECLARE_BITMAP(bm, MAX_NUMNODES);
+
+	nr_bits = min_t(unsigned long, maxnode-1, MAX_NUMNODES);
+	alloc_size = ALIGN(nr_bits, BITS_PER_LONG) / 8;
+
+	if (nmask)
+		nm = compat_alloc_user_space(alloc_size);
+
+	err = sys_get_mempolicy(policy, nm, nr_bits+1, addr, flags);
+
+	if (!err && nmask) {
+		err = copy_from_user(bm, nm, alloc_size);
+		/* ensure entire bitmap is zeroed */
+		err |= clear_user(nmask, ALIGN(maxnode-1, 8) / 8);
+		err |= compat_put_bitmap(nmask, bm, nr_bits);
+	}
+
+	return err;
+}
+
+asmlinkage long compat_sys_set_mempolicy(int mode, compat_ulong_t __user *nmask,
+				     compat_ulong_t maxnode)
+{
+	long err = 0;
+	unsigned long __user *nm = NULL;
+	unsigned long nr_bits, alloc_size;
+	DECLARE_BITMAP(bm, MAX_NUMNODES);
+
+	nr_bits = min_t(unsigned long, maxnode-1, MAX_NUMNODES);
+	alloc_size = ALIGN(nr_bits, BITS_PER_LONG) / 8;
+
+	if (nmask) {
+		err = compat_get_bitmap(bm, nmask, nr_bits);
+		nm = compat_alloc_user_space(alloc_size);
+		err |= copy_to_user(nm, bm, alloc_size);
+	}
+
+	if (err)
+		return -EFAULT;
+
+	return sys_set_mempolicy(mode, nm, nr_bits+1);
+}
+
+asmlinkage long compat_sys_mbind(compat_ulong_t start, compat_ulong_t len,
+			     compat_ulong_t mode, compat_ulong_t __user *nmask,
+			     compat_ulong_t maxnode, compat_ulong_t flags)
+{
+	long err = 0;
+	unsigned long __user *nm = NULL;
+	unsigned long nr_bits, alloc_size;
+	nodemask_t bm;
+
+	nr_bits = min_t(unsigned long, maxnode-1, MAX_NUMNODES);
+	alloc_size = ALIGN(nr_bits, BITS_PER_LONG) / 8;
+
+	if (nmask) {
+		err = compat_get_bitmap(nodes_addr(bm), nmask, nr_bits);
+		nm = compat_alloc_user_space(alloc_size);
+		err |= copy_to_user(nm, nodes_addr(bm), alloc_size);
+	}
+
+	if (err)
+		return -EFAULT;
+
+	return sys_mbind(start, len, mode, nm, nr_bits+1, flags);
+}
+
+#endif
+
+/* Return effective policy for a VMA */
+struct mempolicy *
+get_vma_policy(struct task_struct *task, struct vm_area_struct *vma, unsigned long addr)
+{
+	struct mempolicy *pol = task->mempolicy;
+
+	if (vma) {
+		if (vma->vm_ops && vma->vm_ops->get_policy)
+			pol = vma->vm_ops->get_policy(vma, addr);
+		else if (vma->vm_policy &&
+				vma->vm_policy->policy != MPOL_DEFAULT)
+			pol = vma->vm_policy;
+	}
+	if (!pol)
+		pol = &default_policy;
+	return pol;
+}
+
+/* Return a zonelist representing a mempolicy */
+static struct zonelist *zonelist_policy(gfp_t gfp, struct mempolicy *policy)
+{
+	int nd;
+
+	switch (policy->policy) {
+	case MPOL_PREFERRED:
+		nd = policy->v.preferred_node;
+		if (nd < 0)
+			nd = numa_node_id();
+		break;
+	case MPOL_BIND:
+		/* Lower zones don't get a policy applied */
+		/* Careful: current->mems_allowed might have moved */
+		if (gfp_zone(gfp) >= policy_zone)
+			if (cpuset_zonelist_valid_mems_allowed(policy->v.zonelist))
+				return policy->v.zonelist;
+		/*FALL THROUGH*/
+	case MPOL_INTERLEAVE: /* should not happen */
+	case MPOL_DEFAULT:
+		nd = numa_node_id();
+		break;
+	default:
+		nd = 0;
+		BUG();
+	}
+	return NODE_DATA(nd)->node_zonelists + gfp_zone(gfp);
+}
+
+/* Do dynamic interleaving for a process */
+static unsigned interleave_nodes(struct mempolicy *policy)
+{
+	unsigned nid, next;
+	struct task_struct *me = current;
+
+	nid = me->il_next;
+	next = next_node(nid, policy->v.nodes);
+	if (next >= MAX_NUMNODES)
+		next = first_node(policy->v.nodes);
+	me->il_next = next;
+	return nid;
+}
+
+/* Do static interleaving for a VMA with known offset. */
+static unsigned offset_il_node(struct mempolicy *pol,
+		struct vm_area_struct *vma, unsigned long off)
+{
+	unsigned nnodes = nodes_weight(pol->v.nodes);
+	unsigned target = (unsigned)off % nnodes;
+	int c;
+	int nid = -1;
+
+	c = 0;
+	do {
+		nid = next_node(nid, pol->v.nodes);
+		c++;
+	} while (c <= target);
+	return nid;
+}
+
+/* Allocate a page in interleaved policy.
+   Own path because it needs to do special accounting. */
+static struct page *alloc_page_interleave(gfp_t gfp, unsigned order,
+					unsigned nid)
+{
+	struct zonelist *zl;
+	struct page *page;
+
+	zl = NODE_DATA(nid)->node_zonelists + gfp_zone(gfp);
+	page = __alloc_pages(gfp, order, zl);
+	if (page && page_zone(page) == zl->zones[0]) {
+		zone_pcp(zl->zones[0],get_cpu())->interleave_hit++;
+		put_cpu();
+	}
+	return page;
+}
+
+/**
+ * 	alloc_page_vma	- Allocate a page for a VMA.
+ *
+ * 	@gfp:
+ *      %GFP_USER    user allocation.
+ *      %GFP_KERNEL  kernel allocations,
+ *      %GFP_HIGHMEM highmem/user allocations,
+ *      %GFP_FS      allocation should not call back into a file system.
+ *      %GFP_ATOMIC  don't sleep.
+ *
+ * 	@vma:  Pointer to VMA or NULL if not available.
+ *	@addr: Virtual Address of the allocation. Must be inside the VMA.
+ *
+ * 	This function allocates a page from the kernel page pool and applies
+ *	a NUMA policy associated with the VMA or the current process.
+ *	When VMA is not NULL caller must hold down_read on the mmap_sem of the
+ *	mm_struct of the VMA to prevent it from going away. Should be used for
+ *	all allocations for pages that will be mapped into
+ * 	user space. Returns NULL when no page can be allocated.
+ *
+ *	Should be called with the mm_sem of the vma hold.
+ */
+struct page *
+alloc_page_vma(gfp_t gfp, struct vm_area_struct *vma, unsigned long addr)
+{
+	struct mempolicy *pol = get_vma_policy(current, vma, addr);
+
+	cpuset_update_current_mems_allowed();
+
+	if (unlikely(pol->policy == MPOL_INTERLEAVE)) {
+		unsigned nid;
+		if (vma) {
+			unsigned long off;
+			off = vma->vm_pgoff;
+			off += (addr - vma->vm_start) >> PAGE_SHIFT;
+			nid = offset_il_node(pol, vma, off);
+		} else {
+			/* fall back to process interleaving */
+			nid = interleave_nodes(pol);
+		}
+		return alloc_page_interleave(gfp, 0, nid);
+	}
+	return __alloc_pages(gfp, 0, zonelist_policy(gfp, pol));
+}
+
+/**
+ * 	alloc_pages_current - Allocate pages.
+ *
+ *	@gfp:
+ *		%GFP_USER   user allocation,
+ *      	%GFP_KERNEL kernel allocation,
+ *      	%GFP_HIGHMEM highmem allocation,
+ *      	%GFP_FS     don't call back into a file system.
+ *      	%GFP_ATOMIC don't sleep.
+ *	@order: Power of two of allocation size in pages. 0 is a single page.
+ *
+ *	Allocate a page from the kernel page pool.  When not in
+ *	interrupt context and apply the current process NUMA policy.
+ *	Returns NULL when no page can be allocated.
+ *
+ *	Don't call cpuset_update_current_mems_allowed() unless
+ *	1) it's ok to take cpuset_sem (can WAIT), and
+ *	2) allocating for current task (not interrupt).
+ */
+struct page *alloc_pages_current(gfp_t gfp, unsigned order)
+{
+	struct mempolicy *pol = current->mempolicy;
+
+	if ((gfp & __GFP_WAIT) && !in_interrupt())
+		cpuset_update_current_mems_allowed();
+	if (!pol || in_interrupt())
+		pol = &default_policy;
+	if (pol->policy == MPOL_INTERLEAVE)
+		return alloc_page_interleave(gfp, order, interleave_nodes(pol));
+	return __alloc_pages(gfp, order, zonelist_policy(gfp, pol));
+}
+EXPORT_SYMBOL(alloc_pages_current);
+
+/* Slow path of a mempolicy copy */
+struct mempolicy *__mpol_copy(struct mempolicy *old)
+{
+	struct mempolicy *new = kmem_cache_alloc(policy_cache, GFP_KERNEL);
+
+	if (!new)
+		return ERR_PTR(-ENOMEM);
+	*new = *old;
+	atomic_set(&new->refcnt, 1);
+	if (new->policy == MPOL_BIND) {
+		int sz = ksize(old->v.zonelist);
+		new->v.zonelist = kmalloc(sz, SLAB_KERNEL);
+		if (!new->v.zonelist) {
+			kmem_cache_free(policy_cache, new);
+			return ERR_PTR(-ENOMEM);
+		}
+		memcpy(new->v.zonelist, old->v.zonelist, sz);
+	}
+	return new;
+}
+
+/* Slow path of a mempolicy comparison */
+int __mpol_equal(struct mempolicy *a, struct mempolicy *b)
+{
+	if (!a || !b)
+		return 0;
+	if (a->policy != b->policy)
+		return 0;
+	switch (a->policy) {
+	case MPOL_DEFAULT:
+		return 1;
+	case MPOL_INTERLEAVE:
+		return nodes_equal(a->v.nodes, b->v.nodes);
+	case MPOL_PREFERRED:
+		return a->v.preferred_node == b->v.preferred_node;
+	case MPOL_BIND: {
+		int i;
+		for (i = 0; a->v.zonelist->zones[i]; i++)
+			if (a->v.zonelist->zones[i] != b->v.zonelist->zones[i])
+				return 0;
+		return b->v.zonelist->zones[i] == NULL;
+	}
+	default:
+		BUG();
+		return 0;
+	}
+}
+
+/* Slow path of a mpol destructor. */
+void __mpol_free(struct mempolicy *p)
+{
+	if (!atomic_dec_and_test(&p->refcnt))
+		return;
+	if (p->policy == MPOL_BIND)
+		kfree(p->v.zonelist);
+	p->policy = MPOL_DEFAULT;
+	kmem_cache_free(policy_cache, p);
+}
+
+/*
+ * Hugetlb policy. Same as above, just works with node numbers instead of
+ * zonelists.
+ */
+
+/* Find first node suitable for an allocation */
+int mpol_first_node(struct vm_area_struct *vma, unsigned long addr)
+{
+	struct mempolicy *pol = get_vma_policy(current, vma, addr);
+
+	switch (pol->policy) {
+	case MPOL_DEFAULT:
+		return numa_node_id();
+	case MPOL_BIND:
+		return pol->v.zonelist->zones[0]->zone_pgdat->node_id;
+	case MPOL_INTERLEAVE:
+		return interleave_nodes(pol);
+	case MPOL_PREFERRED:
+		return pol->v.preferred_node >= 0 ?
+				pol->v.preferred_node : numa_node_id();
+	}
+	BUG();
+	return 0;
+}
+
+/* Find secondary valid nodes for an allocation */
+int mpol_node_valid(int nid, struct vm_area_struct *vma, unsigned long addr)
+{
+	struct mempolicy *pol = get_vma_policy(current, vma, addr);
+
+	switch (pol->policy) {
+	case MPOL_PREFERRED:
+	case MPOL_DEFAULT:
+	case MPOL_INTERLEAVE:
+		return 1;
+	case MPOL_BIND: {
+		struct zone **z;
+		for (z = pol->v.zonelist->zones; *z; z++)
+			if ((*z)->zone_pgdat->node_id == nid)
+				return 1;
+		return 0;
+	}
+	default:
+		BUG();
+		return 0;
+	}
+}
+
+/*
+ * Shared memory backing store policy support.
+ *
+ * Remember policies even when nobody has shared memory mapped.
+ * The policies are kept in Red-Black tree linked from the inode.
+ * They are protected by the sp->lock spinlock, which should be held
+ * for any accesses to the tree.
+ */
+
+/* lookup first element intersecting start-end */
+/* Caller holds sp->lock */
+static struct sp_node *
+sp_lookup(struct shared_policy *sp, unsigned long start, unsigned long end)
+{
+	struct rb_node *n = sp->root.rb_node;
+
+	while (n) {
+		struct sp_node *p = rb_entry(n, struct sp_node, nd);
+
+		if (start >= p->end)
+			n = n->rb_right;
+		else if (end <= p->start)
+			n = n->rb_left;
+		else
+			break;
+	}
+	if (!n)
+		return NULL;
+	for (;;) {
+		struct sp_node *w = NULL;
+		struct rb_node *prev = rb_prev(n);
+		if (!prev)
+			break;
+		w = rb_entry(prev, struct sp_node, nd);
+		if (w->end <= start)
+			break;
+		n = prev;
+	}
+	return rb_entry(n, struct sp_node, nd);
+}
+
+/* Insert a new shared policy into the list. */
+/* Caller holds sp->lock */
+static void sp_insert(struct shared_policy *sp, struct sp_node *new)
+{
+	struct rb_node **p = &sp->root.rb_node;
+	struct rb_node *parent = NULL;
+	struct sp_node *nd;
+
+	while (*p) {
+		parent = *p;
+		nd = rb_entry(parent, struct sp_node, nd);
+		if (new->start < nd->start)
+			p = &(*p)->rb_left;
+		else if (new->end > nd->end)
+			p = &(*p)->rb_right;
+		else
+			BUG();
+	}
+	rb_link_node(&new->nd, parent, p);
+	rb_insert_color(&new->nd, &sp->root);
+	PDprintk("inserting %lx-%lx: %d\n", new->start, new->end,
+		 new->policy ? new->policy->policy : 0);
+}
+
+/* Find shared policy intersecting idx */
+struct mempolicy *
+mpol_shared_policy_lookup(struct shared_policy *sp, unsigned long idx)
+{
+	struct mempolicy *pol = NULL;
+	struct sp_node *sn;
+
+	if (!sp->root.rb_node)
+		return NULL;
+	spin_lock(&sp->lock);
+	sn = sp_lookup(sp, idx, idx+1);
+	if (sn) {
+		mpol_get(sn->policy);
+		pol = sn->policy;
+	}
+	spin_unlock(&sp->lock);
+	return pol;
+}
+
+static void sp_delete(struct shared_policy *sp, struct sp_node *n)
+{
+	PDprintk("deleting %lx-l%x\n", n->start, n->end);
+	rb_erase(&n->nd, &sp->root);
+	mpol_free(n->policy);
+	kmem_cache_free(sn_cache, n);
+}
+
+struct sp_node *
+sp_alloc(unsigned long start, unsigned long end, struct mempolicy *pol)
+{
+	struct sp_node *n = kmem_cache_alloc(sn_cache, GFP_KERNEL);
+
+	if (!n)
+		return NULL;
+	n->start = start;
+	n->end = end;
+	mpol_get(pol);
+	n->policy = pol;
+	return n;
+}
+
+/* Replace a policy range. */
+static int shared_policy_replace(struct shared_policy *sp, unsigned long start,
+				 unsigned long end, struct sp_node *new)
+{
+	struct sp_node *n, *new2 = NULL;
+
+restart:
+	spin_lock(&sp->lock);
+	n = sp_lookup(sp, start, end);
+	/* Take care of old policies in the same range. */
+	while (n && n->start < end) {
+		struct rb_node *next = rb_next(&n->nd);
+		if (n->start >= start) {
+			if (n->end <= end)
+				sp_delete(sp, n);
+			else
+				n->start = end;
+		} else {
+			/* Old policy spanning whole new range. */
+			if (n->end > end) {
+				if (!new2) {
+					spin_unlock(&sp->lock);
+					new2 = sp_alloc(end, n->end, n->policy);
+					if (!new2)
+						return -ENOMEM;
+					goto restart;
+				}
+				n->end = start;
+				sp_insert(sp, new2);
+				new2 = NULL;
+				break;
+			} else
+				n->end = start;
+		}
+		if (!next)
+			break;
+		n = rb_entry(next, struct sp_node, nd);
+	}
+	if (new)
+		sp_insert(sp, new);
+	spin_unlock(&sp->lock);
+	if (new2) {
+		mpol_free(new2->policy);
+		kmem_cache_free(sn_cache, new2);
+	}
+	return 0;
+}
+
+int mpol_set_shared_policy(struct shared_policy *info,
+			struct vm_area_struct *vma, struct mempolicy *npol)
+{
+	int err;
+	struct sp_node *new = NULL;
+	unsigned long sz = vma_pages(vma);
+
+	PDprintk("set_shared_policy %lx sz %lu %d %lx\n",
+		 vma->vm_pgoff,
+		 sz, npol? npol->policy : -1,
+		npol ? nodes_addr(npol->v.nodes)[0] : -1);
+
+	if (npol) {
+		new = sp_alloc(vma->vm_pgoff, vma->vm_pgoff + sz, npol);
+		if (!new)
+			return -ENOMEM;
+	}
+	err = shared_policy_replace(info, vma->vm_pgoff, vma->vm_pgoff+sz, new);
+	if (err && new)
+		kmem_cache_free(sn_cache, new);
+	return err;
+}
+
+/* Free a backing policy store on inode delete. */
+void mpol_free_shared_policy(struct shared_policy *p)
+{
+	struct sp_node *n;
+	struct rb_node *next;
+
+	if (!p->root.rb_node)
+		return;
+	spin_lock(&p->lock);
+	next = rb_first(&p->root);
+	while (next) {
+		n = rb_entry(next, struct sp_node, nd);
+		next = rb_next(&n->nd);
+		rb_erase(&n->nd, &p->root);
+		mpol_free(n->policy);
+		kmem_cache_free(sn_cache, n);
+	}
+	spin_unlock(&p->lock);
+}
+
+/* assumes fs == KERNEL_DS */
+void __init numa_policy_init(void)
+{
+	policy_cache = kmem_cache_create("numa_policy",
+					 sizeof(struct mempolicy),
+					 0, SLAB_PANIC, NULL, NULL);
+
+	sn_cache = kmem_cache_create("shared_policy_node",
+				     sizeof(struct sp_node),
+				     0, SLAB_PANIC, NULL, NULL);
+
+	/* Set interleaving policy for system init. This way not all
+	   the data structures allocated at system boot end up in node zero. */
+
+	if (do_set_mempolicy(MPOL_INTERLEAVE, &node_online_map))
+		printk("numa_policy_init: interleaving failed\n");
+}
+
+/* Reset policy of current process to default */
+void numa_default_policy(void)
+{
+	do_set_mempolicy(MPOL_DEFAULT, NULL);
+}
+
+/* Migrate a policy to a different set of nodes */
+static void rebind_policy(struct mempolicy *pol, const nodemask_t *old,
+							const nodemask_t *new)
+{
+	nodemask_t tmp;
+
+	if (!pol)
+		return;
+
+	switch (pol->policy) {
+	case MPOL_DEFAULT:
+		break;
+	case MPOL_INTERLEAVE:
+		nodes_remap(tmp, pol->v.nodes, *old, *new);
+		pol->v.nodes = tmp;
+		current->il_next = node_remap(current->il_next, *old, *new);
+		break;
+	case MPOL_PREFERRED:
+		pol->v.preferred_node = node_remap(pol->v.preferred_node,
+								*old, *new);
+		break;
+	case MPOL_BIND: {
+		nodemask_t nodes;
+		struct zone **z;
+		struct zonelist *zonelist;
+
+		nodes_clear(nodes);
+		for (z = pol->v.zonelist->zones; *z; z++)
+			node_set((*z)->zone_pgdat->node_id, nodes);
+		nodes_remap(tmp, nodes, *old, *new);
+		nodes = tmp;
+
+		zonelist = bind_zonelist(&nodes);
+
+		/* If no mem, then zonelist is NULL and we keep old zonelist.
+		 * If that old zonelist has no remaining mems_allowed nodes,
+		 * then zonelist_policy() will "FALL THROUGH" to MPOL_DEFAULT.
+		 */
+
+		if (zonelist) {
+			/* Good - got mem - substitute new zonelist */
+			kfree(pol->v.zonelist);
+			pol->v.zonelist = zonelist;
+		}
+		break;
+	}
+	default:
+		BUG();
+		break;
+	}
+}
+
+/*
+ * Someone moved this task to different nodes.  Fixup mempolicies.
+ *
+ * TODO - fixup current->mm->vma and shmfs/tmpfs/hugetlbfs policies as well,
+ * once we have a cpuset mechanism to mark which cpuset subtree is migrating.
+ */
+void numa_policy_rebind(const nodemask_t *old, const nodemask_t *new)
+{
+	rebind_policy(current->mempolicy, old, new);
+}
diff -urN oldtree/mm/oom_kill.c newtree/mm/oom_kill.c
--- oldtree/mm/oom_kill.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/mm/oom_kill.c	2006-02-13 19:20:01.280317664 -0500
@@ -274,6 +274,7 @@
 		show_mem();
 	}
 
+	cpuset_lock();
 	read_lock(&tasklist_lock);
 retry:
 	p = select_bad_process();
@@ -284,6 +285,7 @@
 	/* Found nothing?!?! Either we hang forever, or we panic. */
 	if (!p) {
 		read_unlock(&tasklist_lock);
+		cpuset_unlock();
 		panic("Out of memory and no killable processes...\n");
 	}
 
@@ -293,6 +295,7 @@
 
  out:
 	read_unlock(&tasklist_lock);
+	cpuset_unlock();
 	if (mm)
 		mmput(mm);
 
diff -urN oldtree/mm/page_alloc.c newtree/mm/page_alloc.c
--- oldtree/mm/page_alloc.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/mm/page_alloc.c	2006-02-13 19:20:01.281317512 -0500
@@ -854,6 +854,58 @@
 	return page;
 }
 
+#ifdef CONFIG_PAGE_OWNER
+static inline int valid_stack_ptr(struct thread_info *tinfo, void *p)
+{
+	return	p > (void *)tinfo &&
+		p < (void *)tinfo + THREAD_SIZE - 3;
+}
+
+static inline void __stack_trace(struct page *page, unsigned long *stack,
+			unsigned long bp)
+{
+	int i = 0;
+	unsigned long addr;
+	struct thread_info *tinfo = (struct thread_info *)
+		((unsigned long)stack & (~(THREAD_SIZE - 1)));
+
+	memset(page->trace, 0, sizeof(long) * 8);
+
+#ifdef CONFIG_FRAME_POINTER
+	while (valid_stack_ptr(tinfo, (void *)bp)) {
+		addr = *(unsigned long *)(bp + sizeof(long));
+		page->trace[i] = addr;
+		if (++i >= 8)
+			break;
+		bp = *(unsigned long *)bp;
+	}
+#else
+	while (valid_stack_ptr(tinfo, stack)) {
+		addr = *stack++;
+		if (__kernel_text_address(addr)) {
+			page->trace[i] = addr;
+			if (++i >= 8)
+				break;
+		}
+	}
+#endif
+}
+
+static inline void set_page_owner(struct page *page,
+			unsigned int order, unsigned int gfp_mask)
+{
+	unsigned long address, bp;
+#ifdef CONFIG_X86_64
+	asm ("movq %%rbp, %0" : "=r" (bp) : );
+#else
+	asm ("movl %%ebp, %0" : "=r" (bp) : );
+#endif
+	page->order = (int) order;
+	page->gfp_mask = gfp_mask;
+	__stack_trace(page, &address, bp);
+}
+#endif /* CONFIG_PAGE_OWNER */
+
 /*
  * This is the 'heart' of the zoned buddy allocator.
  */
@@ -1005,6 +1057,9 @@
 		show_mem();
 	}
 got_pg:
+#ifdef CONFIG_PAGE_OWNER
+	set_page_owner(page, order, gfp_mask);
+#endif
 	return page;
 }
 
@@ -1057,6 +1112,9 @@
 			free_hot_page(page);
 		else
 			__free_pages_ok(page, order);
+#ifdef CONFIG_PAGE_OWNER
+		page->order = -1;
+#endif
 	}
 }
 
@@ -1699,7 +1757,7 @@
  * up by free_all_bootmem() once the early boot process is
  * done. Non-atomic initialization, single-pass.
  */
-void __devinit memmap_init_zone(unsigned long size, int nid, unsigned long zone,
+void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone,
 		unsigned long start_pfn)
 {
 	struct page *page;
@@ -1722,6 +1780,9 @@
 		if (!is_highmem_idx(zone))
 			set_page_address(page, __va(pfn << PAGE_SHIFT));
 #endif
+#ifdef CONFIG_PAGE_OWNER
+		page->order = -1;
+#endif
 	}
 }
 
@@ -1754,7 +1815,7 @@
 	memmap_init_zone((size), (nid), (zone), (start_pfn))
 #endif
 
-static int __devinit zone_batchsize(struct zone *zone)
+static int __meminit zone_batchsize(struct zone *zone)
 {
 	int batch;
 
@@ -1832,7 +1893,7 @@
  * Dynamically allocate memory for the
  * per cpu pageset array in struct zone.
  */
-static int __devinit process_zones(int cpu)
+static int __meminit process_zones(int cpu)
 {
 	struct zone *zone, *dzone;
 
@@ -1871,7 +1932,7 @@
 #endif
 }
 
-static int __devinit pageset_cpuup_callback(struct notifier_block *nfb,
+static int __meminit pageset_cpuup_callback(struct notifier_block *nfb,
 		unsigned long action,
 		void *hcpu)
 {
@@ -1911,7 +1972,7 @@
 
 #endif
 
-static __devinit
+static __meminit
 void zone_wait_table_init(struct zone *zone, unsigned long zone_size_pages)
 {
 	int i;
@@ -1931,7 +1992,7 @@
 		init_waitqueue_head(zone->wait_table + i);
 }
 
-static __devinit void zone_pcp_init(struct zone *zone)
+static __meminit void zone_pcp_init(struct zone *zone)
 {
 	int cpu;
 	unsigned long batch = zone_batchsize(zone);
@@ -1949,7 +2010,7 @@
 		zone->name, zone->present_pages, batch);
 }
 
-static __devinit void init_currently_empty_zone(struct zone *zone,
+static __meminit void init_currently_empty_zone(struct zone *zone,
 		unsigned long zone_start_pfn, unsigned long size)
 {
 	struct pglist_data *pgdat = zone->zone_pgdat;
diff -urN oldtree/mm/page_alloc.c.orig newtree/mm/page_alloc.c.orig
--- oldtree/mm/page_alloc.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/mm/page_alloc.c.orig	2006-02-13 19:20:01.284317056 -0500
@@ -0,0 +1,2692 @@
+/*
+ *  linux/mm/page_alloc.c
+ *
+ *  Manages the free list, the system allocates free pages here.
+ *  Note that kmalloc() lives in slab.c
+ *
+ *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
+ *  Swap reorganised 29.12.95, Stephen Tweedie
+ *  Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999
+ *  Reshaped it to be a zoned allocator, Ingo Molnar, Red Hat, 1999
+ *  Discontiguous memory support, Kanoj Sarcar, SGI, Nov 1999
+ *  Zone balancing, Kanoj Sarcar, SGI, Jan 2000
+ *  Per cpu hot/cold page lists, bulk allocation, Martin J. Bligh, Sept 2002
+ *          (lots of bits borrowed from Ingo Molnar & Andrew Morton)
+ */
+
+#include <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/interrupt.h>
+#include <linux/pagemap.h>
+#include <linux/bootmem.h>
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/suspend.h>
+#include <linux/pagevec.h>
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+#include <linux/notifier.h>
+#include <linux/topology.h>
+#include <linux/sysctl.h>
+#include <linux/cpu.h>
+#include <linux/cpuset.h>
+#include <linux/memory_hotplug.h>
+#include <linux/nodemask.h>
+#include <linux/vmalloc.h>
+
+#include <asm/tlbflush.h>
+#include "internal.h"
+
+/*
+ * MCD - HACK: Find somewhere to initialize this EARLY, or make this
+ * initializer cleaner
+ */
+nodemask_t node_online_map __read_mostly = { { [0] = 1UL } };
+EXPORT_SYMBOL(node_online_map);
+nodemask_t node_possible_map __read_mostly = NODE_MASK_ALL;
+EXPORT_SYMBOL(node_possible_map);
+struct pglist_data *pgdat_list __read_mostly;
+unsigned long totalram_pages __read_mostly;
+unsigned long totalhigh_pages __read_mostly;
+long nr_swap_pages;
+
+/*
+ * results with 256, 32 in the lowmem_reserve sysctl:
+ *	1G machine -> (16M dma, 800M-16M normal, 1G-800M high)
+ *	1G machine -> (16M dma, 784M normal, 224M high)
+ *	NORMAL allocation will leave 784M/256 of ram reserved in the ZONE_DMA
+ *	HIGHMEM allocation will leave 224M/32 of ram reserved in ZONE_NORMAL
+ *	HIGHMEM allocation will (224M+784M)/256 of ram reserved in ZONE_DMA
+ *
+ * TBD: should special case ZONE_DMA32 machines here - in those we normally
+ * don't need any ZONE_NORMAL reservation
+ */
+int sysctl_lowmem_reserve_ratio[MAX_NR_ZONES-1] = { 256, 256, 32 };
+
+EXPORT_SYMBOL(totalram_pages);
+
+/*
+ * Used by page_zone() to look up the address of the struct zone whose
+ * id is encoded in the upper bits of page->flags
+ */
+struct zone *zone_table[1 << ZONETABLE_SHIFT] __read_mostly;
+EXPORT_SYMBOL(zone_table);
+
+static char *zone_names[MAX_NR_ZONES] = { "DMA", "DMA32", "Normal", "HighMem" };
+int min_free_kbytes = 1024;
+
+unsigned long __initdata nr_kernel_pages;
+unsigned long __initdata nr_all_pages;
+
+static int page_outside_zone_boundaries(struct zone *zone, struct page *page)
+{
+	int ret = 0;
+	unsigned seq;
+	unsigned long pfn = page_to_pfn(page);
+
+	do {
+		seq = zone_span_seqbegin(zone);
+		if (pfn >= zone->zone_start_pfn + zone->spanned_pages)
+			ret = 1;
+		else if (pfn < zone->zone_start_pfn)
+			ret = 1;
+	} while (zone_span_seqretry(zone, seq));
+
+	return ret;
+}
+
+static int page_is_consistent(struct zone *zone, struct page *page)
+{
+#ifdef CONFIG_HOLES_IN_ZONE
+	if (!pfn_valid(page_to_pfn(page)))
+		return 0;
+#endif
+	if (zone != page_zone(page))
+		return 0;
+
+	return 1;
+}
+/*
+ * Temporary debugging check for pages not lying within a given zone.
+ */
+static int bad_range(struct zone *zone, struct page *page)
+{
+	if (page_outside_zone_boundaries(zone, page))
+		return 1;
+	if (!page_is_consistent(zone, page))
+		return 1;
+
+	return 0;
+}
+
+static void bad_page(const char *function, struct page *page)
+{
+	printk(KERN_EMERG "Bad page state at %s (in process '%s', page %p)\n",
+		function, current->comm, page);
+	printk(KERN_EMERG "flags:0x%0*lx mapping:%p mapcount:%d count:%d\n",
+		(int)(2*sizeof(unsigned long)), (unsigned long)page->flags,
+		page->mapping, page_mapcount(page), page_count(page));
+	printk(KERN_EMERG "Backtrace:\n");
+	dump_stack();
+	printk(KERN_EMERG "Trying to fix it up, but a reboot is needed\n");
+	page->flags &= ~(1 << PG_lru	|
+			1 << PG_private |
+			1 << PG_locked	|
+			1 << PG_active	|
+			1 << PG_dirty	|
+			1 << PG_reclaim |
+			1 << PG_slab    |
+			1 << PG_swapcache |
+			1 << PG_writeback );
+	set_page_count(page, 0);
+	reset_page_mapcount(page);
+	page->mapping = NULL;
+	add_taint(TAINT_BAD_PAGE);
+}
+
+/*
+ * Higher-order pages are called "compound pages".  They are structured thusly:
+ *
+ * The first PAGE_SIZE page is called the "head page".
+ *
+ * The remaining PAGE_SIZE pages are called "tail pages".
+ *
+ * All pages have PG_compound set.  All pages have their ->private pointing at
+ * the head page (even the head page has this).
+ *
+ * The first tail page's ->mapping, if non-zero, holds the address of the
+ * compound page's put_page() function.
+ *
+ * The order of the allocation is stored in the first tail page's ->index
+ * This is only for debug at present.  This usage means that zero-order pages
+ * may not be compound.
+ */
+static void prep_compound_page(struct page *page, unsigned long order)
+{
+	int i;
+	int nr_pages = 1 << order;
+
+	page[1].mapping = NULL;
+	page[1].index = order;
+	for (i = 0; i < nr_pages; i++) {
+		struct page *p = page + i;
+
+		SetPageCompound(p);
+		set_page_private(p, (unsigned long)page);
+	}
+}
+
+static void destroy_compound_page(struct page *page, unsigned long order)
+{
+	int i;
+	int nr_pages = 1 << order;
+
+	if (!PageCompound(page))
+		return;
+
+	if (page[1].index != order)
+		bad_page(__FUNCTION__, page);
+
+	for (i = 0; i < nr_pages; i++) {
+		struct page *p = page + i;
+
+		if (!PageCompound(p))
+			bad_page(__FUNCTION__, page);
+		if (page_private(p) != (unsigned long)page)
+			bad_page(__FUNCTION__, page);
+		ClearPageCompound(p);
+	}
+}
+
+/*
+ * function for dealing with page's order in buddy system.
+ * zone->lock is already acquired when we use these.
+ * So, we don't need atomic page->flags operations here.
+ */
+static inline unsigned long page_order(struct page *page) {
+	return page_private(page);
+}
+
+static inline void set_page_order(struct page *page, int order) {
+	set_page_private(page, order);
+	__SetPagePrivate(page);
+}
+
+static inline void rmv_page_order(struct page *page)
+{
+	__ClearPagePrivate(page);
+	set_page_private(page, 0);
+}
+
+/*
+ * Locate the struct page for both the matching buddy in our
+ * pair (buddy1) and the combined O(n+1) page they form (page).
+ *
+ * 1) Any buddy B1 will have an order O twin B2 which satisfies
+ * the following equation:
+ *     B2 = B1 ^ (1 << O)
+ * For example, if the starting buddy (buddy2) is #8 its order
+ * 1 buddy is #10:
+ *     B2 = 8 ^ (1 << 1) = 8 ^ 2 = 10
+ *
+ * 2) Any buddy B will have an order O+1 parent P which
+ * satisfies the following equation:
+ *     P = B & ~(1 << O)
+ *
+ * Assumption: *_mem_map is contigious at least up to MAX_ORDER
+ */
+static inline struct page *
+__page_find_buddy(struct page *page, unsigned long page_idx, unsigned int order)
+{
+	unsigned long buddy_idx = page_idx ^ (1 << order);
+
+	return page + (buddy_idx - page_idx);
+}
+
+static inline unsigned long
+__find_combined_index(unsigned long page_idx, unsigned int order)
+{
+	return (page_idx & ~(1 << order));
+}
+
+/*
+ * This function checks whether a page is free && is the buddy
+ * we can do coalesce a page and its buddy if
+ * (a) the buddy is free &&
+ * (b) the buddy is on the buddy system &&
+ * (c) a page and its buddy have the same order.
+ * for recording page's order, we use page_private(page) and PG_private.
+ *
+ */
+static inline int page_is_buddy(struct page *page, int order)
+{
+       if (PagePrivate(page)           &&
+           (page_order(page) == order) &&
+            page_count(page) == 0)
+               return 1;
+       return 0;
+}
+
+/*
+ * Freeing function for a buddy system allocator.
+ *
+ * The concept of a buddy system is to maintain direct-mapped table
+ * (containing bit values) for memory blocks of various "orders".
+ * The bottom level table contains the map for the smallest allocatable
+ * units of memory (here, pages), and each level above it describes
+ * pairs of units from the levels below, hence, "buddies".
+ * At a high level, all that happens here is marking the table entry
+ * at the bottom level available, and propagating the changes upward
+ * as necessary, plus some accounting needed to play nicely with other
+ * parts of the VM system.
+ * At each level, we keep a list of pages, which are heads of continuous
+ * free pages of length of (1 << order) and marked with PG_Private.Page's
+ * order is recorded in page_private(page) field.
+ * So when we are allocating or freeing one, we can derive the state of the
+ * other.  That is, if we allocate a small block, and both were   
+ * free, the remainder of the region must be split into blocks.   
+ * If a block is freed, and its buddy is also free, then this
+ * triggers coalescing into a block of larger size.            
+ *
+ * -- wli
+ */
+
+static inline void __free_pages_bulk (struct page *page,
+		struct zone *zone, unsigned int order)
+{
+	unsigned long page_idx;
+	int order_size = 1 << order;
+
+	if (unlikely(order))
+		destroy_compound_page(page, order);
+
+	page_idx = page_to_pfn(page) & ((1 << MAX_ORDER) - 1);
+
+	BUG_ON(page_idx & (order_size - 1));
+	BUG_ON(bad_range(zone, page));
+
+	zone->free_pages += order_size;
+	while (order < MAX_ORDER-1) {
+		unsigned long combined_idx;
+		struct free_area *area;
+		struct page *buddy;
+
+		combined_idx = __find_combined_index(page_idx, order);
+		buddy = __page_find_buddy(page, page_idx, order);
+
+		if (bad_range(zone, buddy))
+			break;
+		if (!page_is_buddy(buddy, order))
+			break;		/* Move the buddy up one level. */
+		list_del(&buddy->lru);
+		area = zone->free_area + order;
+		area->nr_free--;
+		rmv_page_order(buddy);
+		page = page + (combined_idx - page_idx);
+		page_idx = combined_idx;
+		order++;
+	}
+	set_page_order(page, order);
+	list_add(&page->lru, &zone->free_area[order].free_list);
+	zone->free_area[order].nr_free++;
+}
+
+static inline int free_pages_check(const char *function, struct page *page)
+{
+	if (	page_mapcount(page) ||
+		page->mapping != NULL ||
+		page_count(page) != 0 ||
+		(page->flags & (
+			1 << PG_lru	|
+			1 << PG_private |
+			1 << PG_locked	|
+			1 << PG_active	|
+			1 << PG_reclaim	|
+			1 << PG_slab	|
+			1 << PG_swapcache |
+			1 << PG_writeback |
+			1 << PG_reserved )))
+		bad_page(function, page);
+	if (PageDirty(page))
+		__ClearPageDirty(page);
+	/*
+	 * For now, we report if PG_reserved was found set, but do not
+	 * clear it, and do not free the page.  But we shall soon need
+	 * to do more, for when the ZERO_PAGE count wraps negative.
+	 */
+	return PageReserved(page);
+}
+
+/*
+ * Frees a list of pages. 
+ * Assumes all pages on list are in same zone, and of same order.
+ * count is the number of pages to free.
+ *
+ * If the zone was previously in an "all pages pinned" state then look to
+ * see if this freeing clears that state.
+ *
+ * And clear the zone's pages_scanned counter, to hold off the "all pages are
+ * pinned" detection logic.
+ */
+static int
+free_pages_bulk(struct zone *zone, int count,
+		struct list_head *list, unsigned int order)
+{
+	unsigned long flags;
+	struct page *page = NULL;
+	int ret = 0;
+
+	spin_lock_irqsave(&zone->lock, flags);
+	zone->all_unreclaimable = 0;
+	zone->pages_scanned = 0;
+	while (!list_empty(list) && count--) {
+		page = list_entry(list->prev, struct page, lru);
+		/* have to delete it as __free_pages_bulk list manipulates */
+		list_del(&page->lru);
+		__free_pages_bulk(page, zone, order);
+		ret++;
+	}
+	spin_unlock_irqrestore(&zone->lock, flags);
+	return ret;
+}
+
+void __free_pages_ok(struct page *page, unsigned int order)
+{
+	LIST_HEAD(list);
+	int i;
+	int reserved = 0;
+
+	arch_free_page(page, order);
+
+#ifndef CONFIG_MMU
+	if (order > 0)
+		for (i = 1 ; i < (1 << order) ; ++i)
+			__put_page(page + i);
+#endif
+
+	for (i = 0 ; i < (1 << order) ; ++i)
+		reserved += free_pages_check(__FUNCTION__, page + i);
+	if (reserved)
+		return;
+
+	list_add(&page->lru, &list);
+	mod_page_state(pgfree, 1 << order);
+	kernel_map_pages(page, 1<<order, 0);
+	free_pages_bulk(page_zone(page), 1, &list, order);
+}
+
+
+/*
+ * The order of subdivision here is critical for the IO subsystem.
+ * Please do not alter this order without good reasons and regression
+ * testing. Specifically, as large blocks of memory are subdivided,
+ * the order in which smaller blocks are delivered depends on the order
+ * they're subdivided in this function. This is the primary factor
+ * influencing the order in which pages are delivered to the IO
+ * subsystem according to empirical testing, and this is also justified
+ * by considering the behavior of a buddy system containing a single
+ * large block of memory acted on by a series of small allocations.
+ * This behavior is a critical factor in sglist merging's success.
+ *
+ * -- wli
+ */
+static inline struct page *
+expand(struct zone *zone, struct page *page,
+ 	int low, int high, struct free_area *area)
+{
+	unsigned long size = 1 << high;
+
+	while (high > low) {
+		area--;
+		high--;
+		size >>= 1;
+		BUG_ON(bad_range(zone, &page[size]));
+		list_add(&page[size].lru, &area->free_list);
+		area->nr_free++;
+		set_page_order(&page[size], high);
+	}
+	return page;
+}
+
+void set_page_refs(struct page *page, int order)
+{
+#ifdef CONFIG_MMU
+	set_page_count(page, 1);
+#else
+	int i;
+
+	/*
+	 * We need to reference all the pages for this order, otherwise if
+	 * anyone accesses one of the pages with (get/put) it will be freed.
+	 * - eg: access_process_vm()
+	 */
+	for (i = 0; i < (1 << order); i++)
+		set_page_count(page + i, 1);
+#endif /* CONFIG_MMU */
+}
+
+/*
+ * This page is about to be returned from the page allocator
+ */
+static int prep_new_page(struct page *page, int order)
+{
+	if (	page_mapcount(page) ||
+		page->mapping != NULL ||
+		page_count(page) != 0 ||
+		(page->flags & (
+			1 << PG_lru	|
+			1 << PG_private	|
+			1 << PG_locked	|
+			1 << PG_active	|
+			1 << PG_dirty	|
+			1 << PG_reclaim	|
+			1 << PG_slab    |
+			1 << PG_swapcache |
+			1 << PG_writeback |
+			1 << PG_reserved )))
+		bad_page(__FUNCTION__, page);
+
+	/*
+	 * For now, we report if PG_reserved was found set, but do not
+	 * clear it, and do not allocate the page: as a safety net.
+	 */
+	if (PageReserved(page))
+		return 1;
+
+	page->flags &= ~(1 << PG_uptodate | 1 << PG_error |
+			1 << PG_referenced | 1 << PG_arch_1 |
+			1 << PG_checked | 1 << PG_mappedtodisk);
+	set_page_private(page, 0);
+	set_page_refs(page, order);
+	kernel_map_pages(page, 1 << order, 1);
+	return 0;
+}
+
+/* 
+ * Do the hard work of removing an element from the buddy allocator.
+ * Call me with the zone->lock already held.
+ */
+static struct page *__rmqueue(struct zone *zone, unsigned int order)
+{
+	struct free_area * area;
+	unsigned int current_order;
+	struct page *page;
+
+	for (current_order = order; current_order < MAX_ORDER; ++current_order) {
+		area = zone->free_area + current_order;
+		if (list_empty(&area->free_list))
+			continue;
+
+		page = list_entry(area->free_list.next, struct page, lru);
+		list_del(&page->lru);
+		rmv_page_order(page);
+		area->nr_free--;
+		zone->free_pages -= 1UL << order;
+		return expand(zone, page, order, current_order, area);
+	}
+
+	return NULL;
+}
+
+/* 
+ * Obtain a specified number of elements from the buddy allocator, all under
+ * a single hold of the lock, for efficiency.  Add them to the supplied list.
+ * Returns the number of new pages which were placed at *list.
+ */
+static int rmqueue_bulk(struct zone *zone, unsigned int order, 
+			unsigned long count, struct list_head *list)
+{
+	unsigned long flags;
+	int i;
+	int allocated = 0;
+	struct page *page;
+	
+	spin_lock_irqsave(&zone->lock, flags);
+	for (i = 0; i < count; ++i) {
+		page = __rmqueue(zone, order);
+		if (page == NULL)
+			break;
+		allocated++;
+		list_add_tail(&page->lru, list);
+	}
+	spin_unlock_irqrestore(&zone->lock, flags);
+	return allocated;
+}
+
+#ifdef CONFIG_NUMA
+/* Called from the slab reaper to drain remote pagesets */
+void drain_remote_pages(void)
+{
+	struct zone *zone;
+	int i;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	for_each_zone(zone) {
+		struct per_cpu_pageset *pset;
+
+		/* Do not drain local pagesets */
+		if (zone->zone_pgdat->node_id == numa_node_id())
+			continue;
+
+		pset = zone->pageset[smp_processor_id()];
+		for (i = 0; i < ARRAY_SIZE(pset->pcp); i++) {
+			struct per_cpu_pages *pcp;
+
+			pcp = &pset->pcp[i];
+			if (pcp->count)
+				pcp->count -= free_pages_bulk(zone, pcp->count,
+						&pcp->list, 0);
+		}
+	}
+	local_irq_restore(flags);
+}
+#endif
+
+#if defined(CONFIG_PM) || defined(CONFIG_HOTPLUG_CPU)
+static void __drain_pages(unsigned int cpu)
+{
+	struct zone *zone;
+	int i;
+
+	for_each_zone(zone) {
+		struct per_cpu_pageset *pset;
+
+		pset = zone_pcp(zone, cpu);
+		for (i = 0; i < ARRAY_SIZE(pset->pcp); i++) {
+			struct per_cpu_pages *pcp;
+
+			pcp = &pset->pcp[i];
+			pcp->count -= free_pages_bulk(zone, pcp->count,
+						&pcp->list, 0);
+		}
+	}
+}
+#endif /* CONFIG_PM || CONFIG_HOTPLUG_CPU */
+
+#ifdef CONFIG_PM
+
+void mark_free_pages(struct zone *zone)
+{
+	unsigned long zone_pfn, flags;
+	int order;
+	struct list_head *curr;
+
+	if (!zone->spanned_pages)
+		return;
+
+	spin_lock_irqsave(&zone->lock, flags);
+	for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn)
+		ClearPageNosaveFree(pfn_to_page(zone_pfn + zone->zone_start_pfn));
+
+	for (order = MAX_ORDER - 1; order >= 0; --order)
+		list_for_each(curr, &zone->free_area[order].free_list) {
+			unsigned long start_pfn, i;
+
+			start_pfn = page_to_pfn(list_entry(curr, struct page, lru));
+
+			for (i=0; i < (1<<order); i++)
+				SetPageNosaveFree(pfn_to_page(start_pfn+i));
+	}
+	spin_unlock_irqrestore(&zone->lock, flags);
+}
+
+/*
+ * Spill all of this CPU's per-cpu pages back into the buddy allocator.
+ */
+void drain_local_pages(void)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);	
+	__drain_pages(smp_processor_id());
+	local_irq_restore(flags);	
+}
+#endif /* CONFIG_PM */
+
+static void zone_statistics(struct zonelist *zonelist, struct zone *z)
+{
+#ifdef CONFIG_NUMA
+	unsigned long flags;
+	int cpu;
+	pg_data_t *pg = z->zone_pgdat;
+	pg_data_t *orig = zonelist->zones[0]->zone_pgdat;
+	struct per_cpu_pageset *p;
+
+	local_irq_save(flags);
+	cpu = smp_processor_id();
+	p = zone_pcp(z,cpu);
+	if (pg == orig) {
+		p->numa_hit++;
+	} else {
+		p->numa_miss++;
+		zone_pcp(zonelist->zones[0], cpu)->numa_foreign++;
+	}
+	if (pg == NODE_DATA(numa_node_id()))
+		p->local_node++;
+	else
+		p->other_node++;
+	local_irq_restore(flags);
+#endif
+}
+
+/*
+ * Free a 0-order page
+ */
+static void FASTCALL(free_hot_cold_page(struct page *page, int cold));
+static void fastcall free_hot_cold_page(struct page *page, int cold)
+{
+	struct zone *zone = page_zone(page);
+	struct per_cpu_pages *pcp;
+	unsigned long flags;
+
+	arch_free_page(page, 0);
+
+	if (PageAnon(page))
+		page->mapping = NULL;
+	if (free_pages_check(__FUNCTION__, page))
+		return;
+
+	inc_page_state(pgfree);
+	kernel_map_pages(page, 1, 0);
+
+	pcp = &zone_pcp(zone, get_cpu())->pcp[cold];
+	local_irq_save(flags);
+	list_add(&page->lru, &pcp->list);
+	pcp->count++;
+	if (pcp->count >= pcp->high)
+		pcp->count -= free_pages_bulk(zone, pcp->batch, &pcp->list, 0);
+	local_irq_restore(flags);
+	put_cpu();
+}
+
+void fastcall free_hot_page(struct page *page)
+{
+	free_hot_cold_page(page, 0);
+}
+	
+void fastcall free_cold_page(struct page *page)
+{
+	free_hot_cold_page(page, 1);
+}
+
+static inline void prep_zero_page(struct page *page, int order, gfp_t gfp_flags)
+{
+	int i;
+
+	BUG_ON((gfp_flags & (__GFP_WAIT | __GFP_HIGHMEM)) == __GFP_HIGHMEM);
+	for(i = 0; i < (1 << order); i++)
+		clear_highpage(page + i);
+}
+
+/*
+ * Really, prep_compound_page() should be called from __rmqueue_bulk().  But
+ * we cheat by calling it from here, in the order > 0 path.  Saves a branch
+ * or two.
+ */
+static struct page *
+buffered_rmqueue(struct zone *zone, int order, gfp_t gfp_flags)
+{
+	unsigned long flags;
+	struct page *page;
+	int cold = !!(gfp_flags & __GFP_COLD);
+
+again:
+	if (order == 0) {
+		struct per_cpu_pages *pcp;
+
+		page = NULL;
+		pcp = &zone_pcp(zone, get_cpu())->pcp[cold];
+		local_irq_save(flags);
+		if (pcp->count <= pcp->low)
+			pcp->count += rmqueue_bulk(zone, 0,
+						pcp->batch, &pcp->list);
+		if (pcp->count) {
+			page = list_entry(pcp->list.next, struct page, lru);
+			list_del(&page->lru);
+			pcp->count--;
+		}
+		local_irq_restore(flags);
+		put_cpu();
+	} else {
+		spin_lock_irqsave(&zone->lock, flags);
+		page = __rmqueue(zone, order);
+		spin_unlock_irqrestore(&zone->lock, flags);
+	}
+
+	if (page != NULL) {
+		BUG_ON(bad_range(zone, page));
+		mod_page_state_zone(zone, pgalloc, 1 << order);
+		if (prep_new_page(page, order))
+			goto again;
+
+		if (gfp_flags & __GFP_ZERO)
+			prep_zero_page(page, order, gfp_flags);
+
+		if (order && (gfp_flags & __GFP_COMP))
+			prep_compound_page(page, order);
+	}
+	return page;
+}
+
+#define ALLOC_NO_WATERMARKS	0x01 /* don't check watermarks at all */
+#define ALLOC_WMARK_MIN		0x02 /* use pages_min watermark */
+#define ALLOC_WMARK_LOW		0x04 /* use pages_low watermark */
+#define ALLOC_WMARK_HIGH	0x08 /* use pages_high watermark */
+#define ALLOC_HARDER		0x10 /* try to alloc harder */
+#define ALLOC_HIGH		0x20 /* __GFP_HIGH set */
+#define ALLOC_CPUSET		0x40 /* check for correct cpuset */
+
+/*
+ * Return 1 if free pages are above 'mark'. This takes into account the order
+ * of the allocation.
+ */
+int zone_watermark_ok(struct zone *z, int order, unsigned long mark,
+		      int classzone_idx, int alloc_flags)
+{
+	/* free_pages my go negative - that's OK */
+	long min = mark, free_pages = z->free_pages - (1 << order) + 1;
+	int o;
+
+	if (alloc_flags & ALLOC_HIGH)
+		min -= min / 2;
+	if (alloc_flags & ALLOC_HARDER)
+		min -= min / 4;
+
+	if (free_pages <= min + z->lowmem_reserve[classzone_idx])
+		return 0;
+	for (o = 0; o < order; o++) {
+		/* At the next order, this order's pages become unavailable */
+		free_pages -= z->free_area[o].nr_free << o;
+
+		/* Require fewer higher order pages to be free */
+		min >>= 1;
+
+		if (free_pages <= min)
+			return 0;
+	}
+	return 1;
+}
+
+/*
+ * get_page_from_freeliest goes through the zonelist trying to allocate
+ * a page.
+ */
+static struct page *
+get_page_from_freelist(gfp_t gfp_mask, unsigned int order,
+		struct zonelist *zonelist, int alloc_flags)
+{
+	struct zone **z = zonelist->zones;
+	struct page *page = NULL;
+	int classzone_idx = zone_idx(*z);
+
+	/*
+	 * Go through the zonelist once, looking for a zone with enough free.
+	 * See also cpuset_zone_allowed() comment in kernel/cpuset.c.
+	 */
+	do {
+		if ((alloc_flags & ALLOC_CPUSET) &&
+				!cpuset_zone_allowed(*z, gfp_mask))
+			continue;
+
+		if (!(alloc_flags & ALLOC_NO_WATERMARKS)) {
+			unsigned long mark;
+			if (alloc_flags & ALLOC_WMARK_MIN)
+				mark = (*z)->pages_min;
+			else if (alloc_flags & ALLOC_WMARK_LOW)
+				mark = (*z)->pages_low;
+			else
+				mark = (*z)->pages_high;
+			if (!zone_watermark_ok(*z, order, mark,
+				    classzone_idx, alloc_flags))
+				continue;
+		}
+
+		page = buffered_rmqueue(*z, order, gfp_mask);
+		if (page) {
+			zone_statistics(zonelist, *z);
+			break;
+		}
+	} while (*(++z) != NULL);
+	return page;
+}
+
+#ifdef CONFIG_PAGE_OWNER
+static inline int valid_stack_ptr(struct thread_info *tinfo, void *p)
+{
+	return	p > (void *)tinfo &&
+		p < (void *)tinfo + THREAD_SIZE - 3;
+}
+
+static inline void __stack_trace(struct page *page, unsigned long *stack,
+			unsigned long bp)
+{
+	int i = 0;
+	unsigned long addr;
+	struct thread_info *tinfo = (struct thread_info *)
+		((unsigned long)stack & (~(THREAD_SIZE - 1)));
+
+	memset(page->trace, 0, sizeof(long) * 8);
+
+#ifdef CONFIG_FRAME_POINTER
+	while (valid_stack_ptr(tinfo, (void *)bp)) {
+		addr = *(unsigned long *)(bp + sizeof(long));
+		page->trace[i] = addr;
+		if (++i >= 8)
+			break;
+		bp = *(unsigned long *)bp;
+	}
+#else
+	while (valid_stack_ptr(tinfo, stack)) {
+		addr = *stack++;
+		if (__kernel_text_address(addr)) {
+			page->trace[i] = addr;
+			if (++i >= 8)
+				break;
+		}
+	}
+#endif
+}
+
+static inline void set_page_owner(struct page *page,
+			unsigned int order, unsigned int gfp_mask)
+{
+	unsigned long address, bp;
+#ifdef CONFIG_X86_64
+	asm ("movq %%rbp, %0" : "=r" (bp) : );
+#else
+	asm ("movl %%ebp, %0" : "=r" (bp) : );
+#endif
+	page->order = (int) order;
+	page->gfp_mask = gfp_mask;
+	__stack_trace(page, &address, bp);
+}
+#endif /* CONFIG_PAGE_OWNER */
+
+/*
+ * This is the 'heart' of the zoned buddy allocator.
+ */
+struct page * fastcall
+__alloc_pages(gfp_t gfp_mask, unsigned int order,
+		struct zonelist *zonelist)
+{
+	const gfp_t wait = gfp_mask & __GFP_WAIT;
+	struct zone **z;
+	struct page *page;
+	struct reclaim_state reclaim_state;
+	struct task_struct *p = current;
+	int do_retry;
+	int alloc_flags;
+	int did_some_progress;
+
+	might_sleep_if(wait);
+
+restart:
+	z = zonelist->zones;  /* the list of zones suitable for gfp_mask */
+
+	if (unlikely(*z == NULL)) {
+		/* Should this ever happen?? */
+		return NULL;
+	}
+
+	page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, order,
+				zonelist, ALLOC_WMARK_LOW|ALLOC_CPUSET);
+	if (page)
+		goto got_pg;
+
+	do {
+		wakeup_kswapd(*z, order);
+	} while (*(++z));
+
+	/*
+	 * OK, we're below the kswapd watermark and have kicked background
+	 * reclaim. Now things get more complex, so set up alloc_flags according
+	 * to how we want to proceed.
+	 *
+	 * The caller may dip into page reserves a bit more if the caller
+	 * cannot run direct reclaim, or if the caller has realtime scheduling
+	 * policy.
+	 */
+	alloc_flags = ALLOC_WMARK_MIN;
+	if ((unlikely(rt_task(p)) && !in_interrupt()) || !wait)
+		alloc_flags |= ALLOC_HARDER;
+	if (gfp_mask & __GFP_HIGH)
+		alloc_flags |= ALLOC_HIGH;
+	if (wait)
+		alloc_flags |= ALLOC_CPUSET;
+
+	/*
+	 * Go through the zonelist again. Let __GFP_HIGH and allocations
+	 * coming from realtime tasks go deeper into reserves.
+	 *
+	 * This is the last chance, in general, before the goto nopage.
+	 * Ignore cpuset if GFP_ATOMIC (!wait) rather than fail alloc.
+	 * See also cpuset_zone_allowed() comment in kernel/cpuset.c.
+	 */
+	page = get_page_from_freelist(gfp_mask, order, zonelist, alloc_flags);
+	if (page)
+		goto got_pg;
+
+	/* This allocation should allow future memory freeing. */
+
+	if (((p->flags & PF_MEMALLOC) || unlikely(test_thread_flag(TIF_MEMDIE)))
+			&& !in_interrupt()) {
+		if (!(gfp_mask & __GFP_NOMEMALLOC)) {
+nofail_alloc:
+			/* go through the zonelist yet again, ignoring mins */
+			page = get_page_from_freelist(gfp_mask, order,
+				zonelist, ALLOC_NO_WATERMARKS|ALLOC_CPUSET);
+			if (page)
+				goto got_pg;
+			if (gfp_mask & __GFP_NOFAIL) {
+				blk_congestion_wait(WRITE, HZ/50);
+				goto nofail_alloc;
+			}
+		}
+		goto nopage;
+	}
+
+	/* Atomic allocations - we can't balance anything */
+	if (!wait)
+		goto nopage;
+
+rebalance:
+	cond_resched();
+
+	/* We now go into synchronous reclaim */
+	p->flags |= PF_MEMALLOC;
+	reclaim_state.reclaimed_slab = 0;
+	p->reclaim_state = &reclaim_state;
+
+	did_some_progress = try_to_free_pages(zonelist->zones, gfp_mask);
+
+	p->reclaim_state = NULL;
+	p->flags &= ~PF_MEMALLOC;
+
+	cond_resched();
+
+	if (likely(did_some_progress)) {
+		page = get_page_from_freelist(gfp_mask, order,
+						zonelist, alloc_flags);
+		if (page)
+			goto got_pg;
+	} else if ((gfp_mask & __GFP_FS) && !(gfp_mask & __GFP_NORETRY)) {
+		/*
+		 * Go through the zonelist yet one more time, keep
+		 * very high watermark here, this is only to catch
+		 * a parallel oom killing, we must fail if we're still
+		 * under heavy pressure.
+		 */
+		page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, order,
+				zonelist, ALLOC_WMARK_HIGH|ALLOC_CPUSET);
+		if (page)
+			goto got_pg;
+
+		out_of_memory(gfp_mask, order);
+		goto restart;
+	}
+
+	/*
+	 * Don't let big-order allocations loop unless the caller explicitly
+	 * requests that.  Wait for some write requests to complete then retry.
+	 *
+	 * In this implementation, __GFP_REPEAT means __GFP_NOFAIL for order
+	 * <= 3, but that may not be true in other implementations.
+	 */
+	do_retry = 0;
+	if (!(gfp_mask & __GFP_NORETRY)) {
+		if ((order <= 3) || (gfp_mask & __GFP_REPEAT))
+			do_retry = 1;
+		if (gfp_mask & __GFP_NOFAIL)
+			do_retry = 1;
+	}
+	if (do_retry) {
+		blk_congestion_wait(WRITE, HZ/50);
+		goto rebalance;
+	}
+
+nopage:
+	if (!(gfp_mask & __GFP_NOWARN) && printk_ratelimit()) {
+		printk(KERN_WARNING "%s: page allocation failure."
+			" order:%d, mode:0x%x\n",
+			p->comm, order, gfp_mask);
+		dump_stack();
+		show_mem();
+	}
+got_pg:
+#ifdef CONFIG_PAGE_OWNER
+	set_page_owner(page, order, gfp_mask);
+#endif
+	return page;
+}
+
+EXPORT_SYMBOL(__alloc_pages);
+
+/*
+ * Common helper functions.
+ */
+fastcall unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
+{
+	struct page * page;
+	page = alloc_pages(gfp_mask, order);
+	if (!page)
+		return 0;
+	return (unsigned long) page_address(page);
+}
+
+EXPORT_SYMBOL(__get_free_pages);
+
+fastcall unsigned long get_zeroed_page(gfp_t gfp_mask)
+{
+	struct page * page;
+
+	/*
+	 * get_zeroed_page() returns a 32-bit address, which cannot represent
+	 * a highmem page
+	 */
+	BUG_ON((gfp_mask & __GFP_HIGHMEM) != 0);
+
+	page = alloc_pages(gfp_mask | __GFP_ZERO, 0);
+	if (page)
+		return (unsigned long) page_address(page);
+	return 0;
+}
+
+EXPORT_SYMBOL(get_zeroed_page);
+
+void __pagevec_free(struct pagevec *pvec)
+{
+	int i = pagevec_count(pvec);
+
+	while (--i >= 0)
+		free_hot_cold_page(pvec->pages[i], pvec->cold);
+}
+
+fastcall void __free_pages(struct page *page, unsigned int order)
+{
+	if (put_page_testzero(page)) {
+		if (order == 0)
+			free_hot_page(page);
+		else
+			__free_pages_ok(page, order);
+#ifdef CONFIG_PAGE_OWNER
+		page->order = -1;
+#endif
+	}
+}
+
+EXPORT_SYMBOL(__free_pages);
+
+fastcall void free_pages(unsigned long addr, unsigned int order)
+{
+	if (addr != 0) {
+		BUG_ON(!virt_addr_valid((void *)addr));
+		__free_pages(virt_to_page((void *)addr), order);
+	}
+}
+
+EXPORT_SYMBOL(free_pages);
+
+/*
+ * Total amount of free (allocatable) RAM:
+ */
+unsigned int nr_free_pages(void)
+{
+	unsigned int sum = 0;
+	struct zone *zone;
+
+	for_each_zone(zone)
+		sum += zone->free_pages;
+
+	return sum;
+}
+
+EXPORT_SYMBOL(nr_free_pages);
+
+#ifdef CONFIG_NUMA
+unsigned int nr_free_pages_pgdat(pg_data_t *pgdat)
+{
+	unsigned int i, sum = 0;
+
+	for (i = 0; i < MAX_NR_ZONES; i++)
+		sum += pgdat->node_zones[i].free_pages;
+
+	return sum;
+}
+#endif
+
+static unsigned int nr_free_zone_pages(int offset)
+{
+	/* Just pick one node, since fallback list is circular */
+	pg_data_t *pgdat = NODE_DATA(numa_node_id());
+	unsigned int sum = 0;
+
+	struct zonelist *zonelist = pgdat->node_zonelists + offset;
+	struct zone **zonep = zonelist->zones;
+	struct zone *zone;
+
+	for (zone = *zonep++; zone; zone = *zonep++) {
+		unsigned long size = zone->present_pages;
+		unsigned long high = zone->pages_high;
+		if (size > high)
+			sum += size - high;
+	}
+
+	return sum;
+}
+
+/*
+ * Amount of free RAM allocatable within ZONE_DMA and ZONE_NORMAL
+ */
+unsigned int nr_free_buffer_pages(void)
+{
+	return nr_free_zone_pages(gfp_zone(GFP_USER));
+}
+
+/*
+ * Amount of free RAM allocatable within all zones
+ */
+unsigned int nr_free_pagecache_pages(void)
+{
+	return nr_free_zone_pages(gfp_zone(GFP_HIGHUSER));
+}
+
+#ifdef CONFIG_HIGHMEM
+unsigned int nr_free_highpages (void)
+{
+	pg_data_t *pgdat;
+	unsigned int pages = 0;
+
+	for_each_pgdat(pgdat)
+		pages += pgdat->node_zones[ZONE_HIGHMEM].free_pages;
+
+	return pages;
+}
+#endif
+
+#ifdef CONFIG_NUMA
+static void show_node(struct zone *zone)
+{
+	printk("Node %d ", zone->zone_pgdat->node_id);
+}
+#else
+#define show_node(zone)	do { } while (0)
+#endif
+
+/*
+ * Accumulate the page_state information across all CPUs.
+ * The result is unavoidably approximate - it can change
+ * during and after execution of this function.
+ */
+static DEFINE_PER_CPU(struct page_state, page_states) = {0};
+
+atomic_t nr_pagecache = ATOMIC_INIT(0);
+EXPORT_SYMBOL(nr_pagecache);
+#ifdef CONFIG_SMP
+DEFINE_PER_CPU(long, nr_pagecache_local) = 0;
+#endif
+
+void __get_page_state(struct page_state *ret, int nr, cpumask_t *cpumask)
+{
+	int cpu = 0;
+
+	memset(ret, 0, sizeof(*ret));
+	cpus_and(*cpumask, *cpumask, cpu_online_map);
+
+	cpu = first_cpu(*cpumask);
+	while (cpu < NR_CPUS) {
+		unsigned long *in, *out, off;
+
+		in = (unsigned long *)&per_cpu(page_states, cpu);
+
+		cpu = next_cpu(cpu, *cpumask);
+
+		if (cpu < NR_CPUS)
+			prefetch(&per_cpu(page_states, cpu));
+
+		out = (unsigned long *)ret;
+		for (off = 0; off < nr; off++)
+			*out++ += *in++;
+	}
+}
+
+void get_page_state_node(struct page_state *ret, int node)
+{
+	int nr;
+	cpumask_t mask = node_to_cpumask(node);
+
+	nr = offsetof(struct page_state, GET_PAGE_STATE_LAST);
+	nr /= sizeof(unsigned long);
+
+	__get_page_state(ret, nr+1, &mask);
+}
+
+void get_page_state(struct page_state *ret)
+{
+	int nr;
+	cpumask_t mask = CPU_MASK_ALL;
+
+	nr = offsetof(struct page_state, GET_PAGE_STATE_LAST);
+	nr /= sizeof(unsigned long);
+
+	__get_page_state(ret, nr + 1, &mask);
+}
+
+void get_full_page_state(struct page_state *ret)
+{
+	cpumask_t mask = CPU_MASK_ALL;
+
+	__get_page_state(ret, sizeof(*ret) / sizeof(unsigned long), &mask);
+}
+
+unsigned long __read_page_state(unsigned long offset)
+{
+	unsigned long ret = 0;
+	int cpu;
+
+	for_each_online_cpu(cpu) {
+		unsigned long in;
+
+		in = (unsigned long)&per_cpu(page_states, cpu) + offset;
+		ret += *((unsigned long *)in);
+	}
+	return ret;
+}
+
+void __mod_page_state(unsigned long offset, unsigned long delta)
+{
+	unsigned long flags;
+	void* ptr;
+
+	local_irq_save(flags);
+	ptr = &__get_cpu_var(page_states);
+	*(unsigned long*)(ptr + offset) += delta;
+	local_irq_restore(flags);
+}
+
+EXPORT_SYMBOL(__mod_page_state);
+
+void __get_zone_counts(unsigned long *active, unsigned long *inactive,
+			unsigned long *free, struct pglist_data *pgdat)
+{
+	struct zone *zones = pgdat->node_zones;
+	int i;
+
+	*active = 0;
+	*inactive = 0;
+	*free = 0;
+	for (i = 0; i < MAX_NR_ZONES; i++) {
+		*active += zones[i].nr_active;
+		*inactive += zones[i].nr_inactive;
+		*free += zones[i].free_pages;
+	}
+}
+
+void get_zone_counts(unsigned long *active,
+		unsigned long *inactive, unsigned long *free)
+{
+	struct pglist_data *pgdat;
+
+	*active = 0;
+	*inactive = 0;
+	*free = 0;
+	for_each_pgdat(pgdat) {
+		unsigned long l, m, n;
+		__get_zone_counts(&l, &m, &n, pgdat);
+		*active += l;
+		*inactive += m;
+		*free += n;
+	}
+}
+
+void si_meminfo(struct sysinfo *val)
+{
+	val->totalram = totalram_pages;
+	val->sharedram = 0;
+	val->freeram = nr_free_pages();
+	val->bufferram = nr_blockdev_pages();
+#ifdef CONFIG_HIGHMEM
+	val->totalhigh = totalhigh_pages;
+	val->freehigh = nr_free_highpages();
+#else
+	val->totalhigh = 0;
+	val->freehigh = 0;
+#endif
+	val->mem_unit = PAGE_SIZE;
+}
+
+EXPORT_SYMBOL(si_meminfo);
+
+#ifdef CONFIG_NUMA
+void si_meminfo_node(struct sysinfo *val, int nid)
+{
+	pg_data_t *pgdat = NODE_DATA(nid);
+
+	val->totalram = pgdat->node_present_pages;
+	val->freeram = nr_free_pages_pgdat(pgdat);
+	val->totalhigh = pgdat->node_zones[ZONE_HIGHMEM].present_pages;
+	val->freehigh = pgdat->node_zones[ZONE_HIGHMEM].free_pages;
+	val->mem_unit = PAGE_SIZE;
+}
+#endif
+
+#define K(x) ((x) << (PAGE_SHIFT-10))
+
+/*
+ * Show free area list (used inside shift_scroll-lock stuff)
+ * We also calculate the percentage fragmentation. We do this by counting the
+ * memory on each free list with the exception of the first item on the list.
+ */
+void show_free_areas(void)
+{
+	struct page_state ps;
+	int cpu, temperature;
+	unsigned long active;
+	unsigned long inactive;
+	unsigned long free;
+	struct zone *zone;
+
+	for_each_zone(zone) {
+		show_node(zone);
+		printk("%s per-cpu:", zone->name);
+
+		if (!zone->present_pages) {
+			printk(" empty\n");
+			continue;
+		} else
+			printk("\n");
+
+		for_each_online_cpu(cpu) {
+			struct per_cpu_pageset *pageset;
+
+			pageset = zone_pcp(zone, cpu);
+
+			for (temperature = 0; temperature < 2; temperature++)
+				printk("cpu %d %s: low %d, high %d, batch %d used:%d\n",
+					cpu,
+					temperature ? "cold" : "hot",
+					pageset->pcp[temperature].low,
+					pageset->pcp[temperature].high,
+					pageset->pcp[temperature].batch,
+					pageset->pcp[temperature].count);
+		}
+	}
+
+	get_page_state(&ps);
+	get_zone_counts(&active, &inactive, &free);
+
+	printk("Free pages: %11ukB (%ukB HighMem)\n",
+		K(nr_free_pages()),
+		K(nr_free_highpages()));
+
+	printk("Active:%lu inactive:%lu dirty:%lu writeback:%lu "
+		"unstable:%lu free:%u slab:%lu mapped:%lu pagetables:%lu\n",
+		active,
+		inactive,
+		ps.nr_dirty,
+		ps.nr_writeback,
+		ps.nr_unstable,
+		nr_free_pages(),
+		ps.nr_slab,
+		ps.nr_mapped,
+		ps.nr_page_table_pages);
+
+	for_each_zone(zone) {
+		int i;
+
+		show_node(zone);
+		printk("%s"
+			" free:%lukB"
+			" min:%lukB"
+			" low:%lukB"
+			" high:%lukB"
+			" active:%lukB"
+			" inactive:%lukB"
+			" present:%lukB"
+			" pages_scanned:%lu"
+			" all_unreclaimable? %s"
+			"\n",
+			zone->name,
+			K(zone->free_pages),
+			K(zone->pages_min),
+			K(zone->pages_low),
+			K(zone->pages_high),
+			K(zone->nr_active),
+			K(zone->nr_inactive),
+			K(zone->present_pages),
+			zone->pages_scanned,
+			(zone->all_unreclaimable ? "yes" : "no")
+			);
+		printk("lowmem_reserve[]:");
+		for (i = 0; i < MAX_NR_ZONES; i++)
+			printk(" %lu", zone->lowmem_reserve[i]);
+		printk("\n");
+	}
+
+	for_each_zone(zone) {
+ 		unsigned long nr, flags, order, total = 0;
+
+		show_node(zone);
+		printk("%s: ", zone->name);
+		if (!zone->present_pages) {
+			printk("empty\n");
+			continue;
+		}
+
+		spin_lock_irqsave(&zone->lock, flags);
+		for (order = 0; order < MAX_ORDER; order++) {
+			nr = zone->free_area[order].nr_free;
+			total += nr << order;
+			printk("%lu*%lukB ", nr, K(1UL) << order);
+		}
+		spin_unlock_irqrestore(&zone->lock, flags);
+		printk("= %lukB\n", K(total));
+	}
+
+	show_swap_cache_info();
+}
+
+/*
+ * Builds allocation fallback zone lists.
+ */
+static int __init build_zonelists_node(pg_data_t *pgdat, struct zonelist *zonelist, int j, int k)
+{
+	switch (k) {
+		struct zone *zone;
+	default:
+		BUG();
+	case ZONE_HIGHMEM:
+		zone = pgdat->node_zones + ZONE_HIGHMEM;
+		if (zone->present_pages) {
+#ifndef CONFIG_HIGHMEM
+			BUG();
+#endif
+			zonelist->zones[j++] = zone;
+		}
+	case ZONE_NORMAL:
+		zone = pgdat->node_zones + ZONE_NORMAL;
+		if (zone->present_pages)
+			zonelist->zones[j++] = zone;
+	case ZONE_DMA32:
+		zone = pgdat->node_zones + ZONE_DMA32;
+		if (zone->present_pages)
+			zonelist->zones[j++] = zone;
+	case ZONE_DMA:
+		zone = pgdat->node_zones + ZONE_DMA;
+		if (zone->present_pages)
+			zonelist->zones[j++] = zone;
+	}
+
+	return j;
+}
+
+static inline int highest_zone(int zone_bits)
+{
+	int res = ZONE_NORMAL;
+	if (zone_bits & (__force int)__GFP_HIGHMEM)
+		res = ZONE_HIGHMEM;
+	if (zone_bits & (__force int)__GFP_DMA32)
+		res = ZONE_DMA32;
+	if (zone_bits & (__force int)__GFP_DMA)
+		res = ZONE_DMA;
+	return res;
+}
+
+#ifdef CONFIG_NUMA
+#define MAX_NODE_LOAD (num_online_nodes())
+static int __initdata node_load[MAX_NUMNODES];
+/**
+ * find_next_best_node - find the next node that should appear in a given node's fallback list
+ * @node: node whose fallback list we're appending
+ * @used_node_mask: nodemask_t of already used nodes
+ *
+ * We use a number of factors to determine which is the next node that should
+ * appear on a given node's fallback list.  The node should not have appeared
+ * already in @node's fallback list, and it should be the next closest node
+ * according to the distance array (which contains arbitrary distance values
+ * from each node to each node in the system), and should also prefer nodes
+ * with no CPUs, since presumably they'll have very little allocation pressure
+ * on them otherwise.
+ * It returns -1 if no node is found.
+ */
+static int __init find_next_best_node(int node, nodemask_t *used_node_mask)
+{
+	int i, n, val;
+	int min_val = INT_MAX;
+	int best_node = -1;
+
+	for_each_online_node(i) {
+		cpumask_t tmp;
+
+		/* Start from local node */
+		n = (node+i) % num_online_nodes();
+
+		/* Don't want a node to appear more than once */
+		if (node_isset(n, *used_node_mask))
+			continue;
+
+		/* Use the local node if we haven't already */
+		if (!node_isset(node, *used_node_mask)) {
+			best_node = node;
+			break;
+		}
+
+		/* Use the distance array to find the distance */
+		val = node_distance(node, n);
+
+		/* Give preference to headless and unused nodes */
+		tmp = node_to_cpumask(n);
+		if (!cpus_empty(tmp))
+			val += PENALTY_FOR_NODE_WITH_CPUS;
+
+		/* Slight preference for less loaded node */
+		val *= (MAX_NODE_LOAD*MAX_NUMNODES);
+		val += node_load[n];
+
+		if (val < min_val) {
+			min_val = val;
+			best_node = n;
+		}
+	}
+
+	if (best_node >= 0)
+		node_set(best_node, *used_node_mask);
+
+	return best_node;
+}
+
+static void __init build_zonelists(pg_data_t *pgdat)
+{
+	int i, j, k, node, local_node;
+	int prev_node, load;
+	struct zonelist *zonelist;
+	nodemask_t used_mask;
+
+	/* initialize zonelists */
+	for (i = 0; i < GFP_ZONETYPES; i++) {
+		zonelist = pgdat->node_zonelists + i;
+		zonelist->zones[0] = NULL;
+	}
+
+	/* NUMA-aware ordering of nodes */
+	local_node = pgdat->node_id;
+	load = num_online_nodes();
+	prev_node = local_node;
+	nodes_clear(used_mask);
+	while ((node = find_next_best_node(local_node, &used_mask)) >= 0) {
+		/*
+		 * We don't want to pressure a particular node.
+		 * So adding penalty to the first node in same
+		 * distance group to make it round-robin.
+		 */
+		if (node_distance(local_node, node) !=
+				node_distance(local_node, prev_node))
+			node_load[node] += load;
+		prev_node = node;
+		load--;
+		for (i = 0; i < GFP_ZONETYPES; i++) {
+			zonelist = pgdat->node_zonelists + i;
+			for (j = 0; zonelist->zones[j] != NULL; j++);
+
+			k = highest_zone(i);
+
+	 		j = build_zonelists_node(NODE_DATA(node), zonelist, j, k);
+			zonelist->zones[j] = NULL;
+		}
+	}
+}
+
+#else	/* CONFIG_NUMA */
+
+static void __init build_zonelists(pg_data_t *pgdat)
+{
+	int i, j, k, node, local_node;
+
+	local_node = pgdat->node_id;
+	for (i = 0; i < GFP_ZONETYPES; i++) {
+		struct zonelist *zonelist;
+
+		zonelist = pgdat->node_zonelists + i;
+
+		j = 0;
+		k = highest_zone(i);
+ 		j = build_zonelists_node(pgdat, zonelist, j, k);
+ 		/*
+ 		 * Now we build the zonelist so that it contains the zones
+ 		 * of all the other nodes.
+ 		 * We don't want to pressure a particular node, so when
+ 		 * building the zones for node N, we make sure that the
+ 		 * zones coming right after the local ones are those from
+ 		 * node N+1 (modulo N)
+ 		 */
+		for (node = local_node + 1; node < MAX_NUMNODES; node++) {
+			if (!node_online(node))
+				continue;
+			j = build_zonelists_node(NODE_DATA(node), zonelist, j, k);
+		}
+		for (node = 0; node < local_node; node++) {
+			if (!node_online(node))
+				continue;
+			j = build_zonelists_node(NODE_DATA(node), zonelist, j, k);
+		}
+
+		zonelist->zones[j] = NULL;
+	}
+}
+
+#endif	/* CONFIG_NUMA */
+
+void __init build_all_zonelists(void)
+{
+	int i;
+
+	for_each_online_node(i)
+		build_zonelists(NODE_DATA(i));
+	printk("Built %i zonelists\n", num_online_nodes());
+	cpuset_init_current_mems_allowed();
+}
+
+/*
+ * Helper functions to size the waitqueue hash table.
+ * Essentially these want to choose hash table sizes sufficiently
+ * large so that collisions trying to wait on pages are rare.
+ * But in fact, the number of active page waitqueues on typical
+ * systems is ridiculously low, less than 200. So this is even
+ * conservative, even though it seems large.
+ *
+ * The constant PAGES_PER_WAITQUEUE specifies the ratio of pages to
+ * waitqueues, i.e. the size of the waitq table given the number of pages.
+ */
+#define PAGES_PER_WAITQUEUE	256
+
+static inline unsigned long wait_table_size(unsigned long pages)
+{
+	unsigned long size = 1;
+
+	pages /= PAGES_PER_WAITQUEUE;
+
+	while (size < pages)
+		size <<= 1;
+
+	/*
+	 * Once we have dozens or even hundreds of threads sleeping
+	 * on IO we've got bigger problems than wait queue collision.
+	 * Limit the size of the wait table to a reasonable size.
+	 */
+	size = min(size, 4096UL);
+
+	return max(size, 4UL);
+}
+
+/*
+ * This is an integer logarithm so that shifts can be used later
+ * to extract the more random high bits from the multiplicative
+ * hash function before the remainder is taken.
+ */
+static inline unsigned long wait_table_bits(unsigned long size)
+{
+	return ffz(~size);
+}
+
+#define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))
+
+static void __init calculate_zone_totalpages(struct pglist_data *pgdat,
+		unsigned long *zones_size, unsigned long *zholes_size)
+{
+	unsigned long realtotalpages, totalpages = 0;
+	int i;
+
+	for (i = 0; i < MAX_NR_ZONES; i++)
+		totalpages += zones_size[i];
+	pgdat->node_spanned_pages = totalpages;
+
+	realtotalpages = totalpages;
+	if (zholes_size)
+		for (i = 0; i < MAX_NR_ZONES; i++)
+			realtotalpages -= zholes_size[i];
+	pgdat->node_present_pages = realtotalpages;
+	printk(KERN_DEBUG "On node %d totalpages: %lu\n", pgdat->node_id, realtotalpages);
+}
+
+
+/*
+ * Initially all pages are reserved - free ones are freed
+ * up by free_all_bootmem() once the early boot process is
+ * done. Non-atomic initialization, single-pass.
+ */
+void __devinit memmap_init_zone(unsigned long size, int nid, unsigned long zone,
+		unsigned long start_pfn)
+{
+	struct page *page;
+	unsigned long end_pfn = start_pfn + size;
+	unsigned long pfn;
+
+	for (pfn = start_pfn; pfn < end_pfn; pfn++, page++) {
+		if (!early_pfn_valid(pfn))
+			continue;
+		if (!early_pfn_in_nid(pfn, nid))
+			continue;
+		page = pfn_to_page(pfn);
+		set_page_links(page, zone, nid, pfn);
+		set_page_count(page, 1);
+		reset_page_mapcount(page);
+		SetPageReserved(page);
+		INIT_LIST_HEAD(&page->lru);
+#ifdef WANT_PAGE_VIRTUAL
+		/* The shift won't overflow because ZONE_NORMAL is below 4G. */
+		if (!is_highmem_idx(zone))
+			set_page_address(page, __va(pfn << PAGE_SHIFT));
+#endif
+#ifdef CONFIG_PAGE_OWNER
+		page->order = -1;
+#endif
+	}
+}
+
+void zone_init_free_lists(struct pglist_data *pgdat, struct zone *zone,
+				unsigned long size)
+{
+	int order;
+	for (order = 0; order < MAX_ORDER ; order++) {
+		INIT_LIST_HEAD(&zone->free_area[order].free_list);
+		zone->free_area[order].nr_free = 0;
+	}
+}
+
+#define ZONETABLE_INDEX(x, zone_nr)	((x << ZONES_SHIFT) | zone_nr)
+void zonetable_add(struct zone *zone, int nid, int zid, unsigned long pfn,
+		unsigned long size)
+{
+	unsigned long snum = pfn_to_section_nr(pfn);
+	unsigned long end = pfn_to_section_nr(pfn + size);
+
+	if (FLAGS_HAS_NODE)
+		zone_table[ZONETABLE_INDEX(nid, zid)] = zone;
+	else
+		for (; snum <= end; snum++)
+			zone_table[ZONETABLE_INDEX(snum, zid)] = zone;
+}
+
+#ifndef __HAVE_ARCH_MEMMAP_INIT
+#define memmap_init(size, nid, zone, start_pfn) \
+	memmap_init_zone((size), (nid), (zone), (start_pfn))
+#endif
+
+static int __devinit zone_batchsize(struct zone *zone)
+{
+	int batch;
+
+	/*
+	 * The per-cpu-pages pools are set to around 1000th of the
+	 * size of the zone.  But no more than 1/2 of a meg.
+	 *
+	 * OK, so we don't know how big the cache is.  So guess.
+	 */
+	batch = zone->present_pages / 1024;
+	if (batch * PAGE_SIZE > 512 * 1024)
+		batch = (512 * 1024) / PAGE_SIZE;
+	batch /= 4;		/* We effectively *= 4 below */
+	if (batch < 1)
+		batch = 1;
+
+	/*
+	 * Clamp the batch to a 2^n - 1 value. Having a power
+	 * of 2 value was found to be more likely to have
+	 * suboptimal cache aliasing properties in some cases.
+	 *
+	 * For example if 2 tasks are alternately allocating
+	 * batches of pages, one task can end up with a lot
+	 * of pages of one half of the possible page colors
+	 * and the other with pages of the other colors.
+	 */
+	batch = (1 << (fls(batch + batch/2)-1)) - 1;
+
+	return batch;
+}
+
+inline void setup_pageset(struct per_cpu_pageset *p, unsigned long batch)
+{
+	struct per_cpu_pages *pcp;
+
+	memset(p, 0, sizeof(*p));
+
+	pcp = &p->pcp[0];		/* hot */
+	pcp->count = 0;
+	pcp->low = 0;
+	pcp->high = 6 * batch;
+	pcp->batch = max(1UL, 1 * batch);
+	INIT_LIST_HEAD(&pcp->list);
+
+	pcp = &p->pcp[1];		/* cold*/
+	pcp->count = 0;
+	pcp->low = 0;
+	pcp->high = 2 * batch;
+	pcp->batch = max(1UL, batch/2);
+	INIT_LIST_HEAD(&pcp->list);
+}
+
+#ifdef CONFIG_NUMA
+/*
+ * Boot pageset table. One per cpu which is going to be used for all
+ * zones and all nodes. The parameters will be set in such a way
+ * that an item put on a list will immediately be handed over to
+ * the buddy list. This is safe since pageset manipulation is done
+ * with interrupts disabled.
+ *
+ * Some NUMA counter updates may also be caught by the boot pagesets.
+ *
+ * The boot_pagesets must be kept even after bootup is complete for
+ * unused processors and/or zones. They do play a role for bootstrapping
+ * hotplugged processors.
+ *
+ * zoneinfo_show() and maybe other functions do
+ * not check if the processor is online before following the pageset pointer.
+ * Other parts of the kernel may not check if the zone is available.
+ */
+static struct per_cpu_pageset
+	boot_pageset[NR_CPUS];
+
+/*
+ * Dynamically allocate memory for the
+ * per cpu pageset array in struct zone.
+ */
+static int __devinit process_zones(int cpu)
+{
+	struct zone *zone, *dzone;
+
+	for_each_zone(zone) {
+
+		zone->pageset[cpu] = kmalloc_node(sizeof(struct per_cpu_pageset),
+					 GFP_KERNEL, cpu_to_node(cpu));
+		if (!zone->pageset[cpu])
+			goto bad;
+
+		setup_pageset(zone->pageset[cpu], zone_batchsize(zone));
+	}
+
+	return 0;
+bad:
+	for_each_zone(dzone) {
+		if (dzone == zone)
+			break;
+		kfree(dzone->pageset[cpu]);
+		dzone->pageset[cpu] = NULL;
+	}
+	return -ENOMEM;
+}
+
+static inline void free_zone_pagesets(int cpu)
+{
+#ifdef CONFIG_NUMA
+	struct zone *zone;
+
+	for_each_zone(zone) {
+		struct per_cpu_pageset *pset = zone_pcp(zone, cpu);
+
+		zone_pcp(zone, cpu) = NULL;
+		kfree(pset);
+	}
+#endif
+}
+
+static int __devinit pageset_cpuup_callback(struct notifier_block *nfb,
+		unsigned long action,
+		void *hcpu)
+{
+	int cpu = (long)hcpu;
+	int ret = NOTIFY_OK;
+
+	switch (action) {
+		case CPU_UP_PREPARE:
+			if (process_zones(cpu))
+				ret = NOTIFY_BAD;
+			break;
+		case CPU_UP_CANCELED:
+		case CPU_DEAD:
+			free_zone_pagesets(cpu);
+			break;
+		default:
+			break;
+	}
+	return ret;
+}
+
+static struct notifier_block pageset_notifier =
+	{ &pageset_cpuup_callback, NULL, 0 };
+
+void __init setup_per_cpu_pageset(void)
+{
+	int err;
+
+	/* Initialize per_cpu_pageset for cpu 0.
+	 * A cpuup callback will do this for every cpu
+	 * as it comes online
+	 */
+	err = process_zones(smp_processor_id());
+	BUG_ON(err);
+	register_cpu_notifier(&pageset_notifier);
+}
+
+#endif
+
+static __devinit
+void zone_wait_table_init(struct zone *zone, unsigned long zone_size_pages)
+{
+	int i;
+	struct pglist_data *pgdat = zone->zone_pgdat;
+
+	/*
+	 * The per-page waitqueue mechanism uses hashed waitqueues
+	 * per zone.
+	 */
+	zone->wait_table_size = wait_table_size(zone_size_pages);
+	zone->wait_table_bits =	wait_table_bits(zone->wait_table_size);
+	zone->wait_table = (wait_queue_head_t *)
+		alloc_bootmem_node(pgdat, zone->wait_table_size
+					* sizeof(wait_queue_head_t));
+
+	for(i = 0; i < zone->wait_table_size; ++i)
+		init_waitqueue_head(zone->wait_table + i);
+}
+
+static __devinit void zone_pcp_init(struct zone *zone)
+{
+	int cpu;
+	unsigned long batch = zone_batchsize(zone);
+
+	for (cpu = 0; cpu < NR_CPUS; cpu++) {
+#ifdef CONFIG_NUMA
+		/* Early boot. Slab allocator not functional yet */
+		zone->pageset[cpu] = &boot_pageset[cpu];
+		setup_pageset(&boot_pageset[cpu],0);
+#else
+		setup_pageset(zone_pcp(zone,cpu), batch);
+#endif
+	}
+	printk(KERN_DEBUG "  %s zone: %lu pages, LIFO batch:%lu\n",
+		zone->name, zone->present_pages, batch);
+}
+
+static __devinit void init_currently_empty_zone(struct zone *zone,
+		unsigned long zone_start_pfn, unsigned long size)
+{
+	struct pglist_data *pgdat = zone->zone_pgdat;
+
+	zone_wait_table_init(zone, size);
+	pgdat->nr_zones = zone_idx(zone) + 1;
+
+	zone->zone_mem_map = pfn_to_page(zone_start_pfn);
+	zone->zone_start_pfn = zone_start_pfn;
+
+	memmap_init(size, pgdat->node_id, zone_idx(zone), zone_start_pfn);
+
+	zone_init_free_lists(pgdat, zone, zone->spanned_pages);
+}
+
+/*
+ * Set up the zone data structures:
+ *   - mark all pages reserved
+ *   - mark all memory queues empty
+ *   - clear the memory bitmaps
+ */
+static void __init free_area_init_core(struct pglist_data *pgdat,
+		unsigned long *zones_size, unsigned long *zholes_size)
+{
+	unsigned long j;
+	int nid = pgdat->node_id;
+	unsigned long zone_start_pfn = pgdat->node_start_pfn;
+
+	pgdat_resize_init(pgdat);
+	pgdat->nr_zones = 0;
+	init_waitqueue_head(&pgdat->kswapd_wait);
+	pgdat->kswapd_max_order = 0;
+	
+	for (j = 0; j < MAX_NR_ZONES; j++) {
+		struct zone *zone = pgdat->node_zones + j;
+		unsigned long size, realsize;
+
+		realsize = size = zones_size[j];
+		if (zholes_size)
+			realsize -= zholes_size[j];
+
+		if (j < ZONE_HIGHMEM)
+			nr_kernel_pages += realsize;
+		nr_all_pages += realsize;
+
+		zone->spanned_pages = size;
+		zone->present_pages = realsize;
+		zone->name = zone_names[j];
+		spin_lock_init(&zone->lock);
+		spin_lock_init(&zone->lru_lock);
+		zone_seqlock_init(zone);
+		zone->zone_pgdat = pgdat;
+		zone->free_pages = 0;
+
+		zone->temp_priority = zone->prev_priority = DEF_PRIORITY;
+
+		zone_pcp_init(zone);
+		INIT_LIST_HEAD(&zone->active_list);
+		INIT_LIST_HEAD(&zone->inactive_list);
+		zone->nr_scan_active = 0;
+		zone->nr_scan_inactive = 0;
+		zone->nr_active = 0;
+		zone->nr_inactive = 0;
+		atomic_set(&zone->reclaim_in_progress, 0);
+		if (!size)
+			continue;
+
+		zonetable_add(zone, nid, j, zone_start_pfn, size);
+		init_currently_empty_zone(zone, zone_start_pfn, size);
+		zone_start_pfn += size;
+	}
+}
+
+static void __init alloc_node_mem_map(struct pglist_data *pgdat)
+{
+	/* Skip empty nodes */
+	if (!pgdat->node_spanned_pages)
+		return;
+
+#ifdef CONFIG_FLAT_NODE_MEM_MAP
+	/* ia64 gets its own node_mem_map, before this, without bootmem */
+	if (!pgdat->node_mem_map) {
+		unsigned long size;
+		struct page *map;
+
+		size = (pgdat->node_spanned_pages + 1) * sizeof(struct page);
+		map = alloc_remap(pgdat->node_id, size);
+		if (!map)
+			map = alloc_bootmem_node(pgdat, size);
+		pgdat->node_mem_map = map;
+	}
+#ifdef CONFIG_FLATMEM
+	/*
+	 * With no DISCONTIG, the global mem_map is just set as node 0's
+	 */
+	if (pgdat == NODE_DATA(0))
+		mem_map = NODE_DATA(0)->node_mem_map;
+#endif
+#endif /* CONFIG_FLAT_NODE_MEM_MAP */
+}
+
+void __init free_area_init_node(int nid, struct pglist_data *pgdat,
+		unsigned long *zones_size, unsigned long node_start_pfn,
+		unsigned long *zholes_size)
+{
+	pgdat->node_id = nid;
+	pgdat->node_start_pfn = node_start_pfn;
+	calculate_zone_totalpages(pgdat, zones_size, zholes_size);
+
+	alloc_node_mem_map(pgdat);
+
+	free_area_init_core(pgdat, zones_size, zholes_size);
+}
+
+#ifndef CONFIG_NEED_MULTIPLE_NODES
+static bootmem_data_t contig_bootmem_data;
+struct pglist_data contig_page_data = { .bdata = &contig_bootmem_data };
+
+EXPORT_SYMBOL(contig_page_data);
+#endif
+
+void __init free_area_init(unsigned long *zones_size)
+{
+	free_area_init_node(0, NODE_DATA(0), zones_size,
+			__pa(PAGE_OFFSET) >> PAGE_SHIFT, NULL);
+}
+
+#ifdef CONFIG_PROC_FS
+
+#include <linux/seq_file.h>
+
+static void *frag_start(struct seq_file *m, loff_t *pos)
+{
+	pg_data_t *pgdat;
+	loff_t node = *pos;
+
+	for (pgdat = pgdat_list; pgdat && node; pgdat = pgdat->pgdat_next)
+		--node;
+
+	return pgdat;
+}
+
+static void *frag_next(struct seq_file *m, void *arg, loff_t *pos)
+{
+	pg_data_t *pgdat = (pg_data_t *)arg;
+
+	(*pos)++;
+	return pgdat->pgdat_next;
+}
+
+static void frag_stop(struct seq_file *m, void *arg)
+{
+}
+
+/* 
+ * This walks the free areas for each zone.
+ */
+static int frag_show(struct seq_file *m, void *arg)
+{
+	pg_data_t *pgdat = (pg_data_t *)arg;
+	struct zone *zone;
+	struct zone *node_zones = pgdat->node_zones;
+	unsigned long flags;
+	int order;
+
+	for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) {
+		if (!zone->present_pages)
+			continue;
+
+		spin_lock_irqsave(&zone->lock, flags);
+		seq_printf(m, "Node %d, zone %8s ", pgdat->node_id, zone->name);
+		for (order = 0; order < MAX_ORDER; ++order)
+			seq_printf(m, "%6lu ", zone->free_area[order].nr_free);
+		spin_unlock_irqrestore(&zone->lock, flags);
+		seq_putc(m, '\n');
+	}
+	return 0;
+}
+
+struct seq_operations fragmentation_op = {
+	.start	= frag_start,
+	.next	= frag_next,
+	.stop	= frag_stop,
+	.show	= frag_show,
+};
+
+/*
+ * Output information about zones in @pgdat.
+ */
+static int zoneinfo_show(struct seq_file *m, void *arg)
+{
+	pg_data_t *pgdat = arg;
+	struct zone *zone;
+	struct zone *node_zones = pgdat->node_zones;
+	unsigned long flags;
+
+	for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; zone++) {
+		int i;
+
+		if (!zone->present_pages)
+			continue;
+
+		spin_lock_irqsave(&zone->lock, flags);
+		seq_printf(m, "Node %d, zone %8s", pgdat->node_id, zone->name);
+		seq_printf(m,
+			   "\n  pages free     %lu"
+			   "\n        min      %lu"
+			   "\n        low      %lu"
+			   "\n        high     %lu"
+			   "\n        active   %lu"
+			   "\n        inactive %lu"
+			   "\n        scanned  %lu (a: %lu i: %lu)"
+			   "\n        spanned  %lu"
+			   "\n        present  %lu",
+			   zone->free_pages,
+			   zone->pages_min,
+			   zone->pages_low,
+			   zone->pages_high,
+			   zone->nr_active,
+			   zone->nr_inactive,
+			   zone->pages_scanned,
+			   zone->nr_scan_active, zone->nr_scan_inactive,
+			   zone->spanned_pages,
+			   zone->present_pages);
+		seq_printf(m,
+			   "\n        protection: (%lu",
+			   zone->lowmem_reserve[0]);
+		for (i = 1; i < ARRAY_SIZE(zone->lowmem_reserve); i++)
+			seq_printf(m, ", %lu", zone->lowmem_reserve[i]);
+		seq_printf(m,
+			   ")"
+			   "\n  pagesets");
+		for (i = 0; i < ARRAY_SIZE(zone->pageset); i++) {
+			struct per_cpu_pageset *pageset;
+			int j;
+
+			pageset = zone_pcp(zone, i);
+			for (j = 0; j < ARRAY_SIZE(pageset->pcp); j++) {
+				if (pageset->pcp[j].count)
+					break;
+			}
+			if (j == ARRAY_SIZE(pageset->pcp))
+				continue;
+			for (j = 0; j < ARRAY_SIZE(pageset->pcp); j++) {
+				seq_printf(m,
+					   "\n    cpu: %i pcp: %i"
+					   "\n              count: %i"
+					   "\n              low:   %i"
+					   "\n              high:  %i"
+					   "\n              batch: %i",
+					   i, j,
+					   pageset->pcp[j].count,
+					   pageset->pcp[j].low,
+					   pageset->pcp[j].high,
+					   pageset->pcp[j].batch);
+			}
+#ifdef CONFIG_NUMA
+			seq_printf(m,
+				   "\n            numa_hit:       %lu"
+				   "\n            numa_miss:      %lu"
+				   "\n            numa_foreign:   %lu"
+				   "\n            interleave_hit: %lu"
+				   "\n            local_node:     %lu"
+				   "\n            other_node:     %lu",
+				   pageset->numa_hit,
+				   pageset->numa_miss,
+				   pageset->numa_foreign,
+				   pageset->interleave_hit,
+				   pageset->local_node,
+				   pageset->other_node);
+#endif
+		}
+		seq_printf(m,
+			   "\n  all_unreclaimable: %u"
+			   "\n  prev_priority:     %i"
+			   "\n  temp_priority:     %i"
+			   "\n  start_pfn:         %lu",
+			   zone->all_unreclaimable,
+			   zone->prev_priority,
+			   zone->temp_priority,
+			   zone->zone_start_pfn);
+		spin_unlock_irqrestore(&zone->lock, flags);
+		seq_putc(m, '\n');
+	}
+	return 0;
+}
+
+struct seq_operations zoneinfo_op = {
+	.start	= frag_start, /* iterate over all zones. The same as in
+			       * fragmentation. */
+	.next	= frag_next,
+	.stop	= frag_stop,
+	.show	= zoneinfo_show,
+};
+
+static char *vmstat_text[] = {
+	"nr_dirty",
+	"nr_writeback",
+	"nr_unstable",
+	"nr_page_table_pages",
+	"nr_mapped",
+	"nr_slab",
+
+	"pgpgin",
+	"pgpgout",
+	"pswpin",
+	"pswpout",
+	"pgalloc_high",
+
+	"pgalloc_normal",
+	"pgalloc_dma",
+	"pgfree",
+	"pgactivate",
+	"pgdeactivate",
+
+	"pgfault",
+	"pgmajfault",
+	"pgrefill_high",
+	"pgrefill_normal",
+	"pgrefill_dma",
+
+	"pgsteal_high",
+	"pgsteal_normal",
+	"pgsteal_dma",
+	"pgscan_kswapd_high",
+	"pgscan_kswapd_normal",
+
+	"pgscan_kswapd_dma",
+	"pgscan_direct_high",
+	"pgscan_direct_normal",
+	"pgscan_direct_dma",
+	"pginodesteal",
+
+	"slabs_scanned",
+	"kswapd_steal",
+	"kswapd_inodesteal",
+	"pageoutrun",
+	"allocstall",
+
+	"pgrotated",
+	"nr_bounce",
+};
+
+static void *vmstat_start(struct seq_file *m, loff_t *pos)
+{
+	struct page_state *ps;
+
+	if (*pos >= ARRAY_SIZE(vmstat_text))
+		return NULL;
+
+	ps = kmalloc(sizeof(*ps), GFP_KERNEL);
+	m->private = ps;
+	if (!ps)
+		return ERR_PTR(-ENOMEM);
+	get_full_page_state(ps);
+	ps->pgpgin /= 2;		/* sectors -> kbytes */
+	ps->pgpgout /= 2;
+	return (unsigned long *)ps + *pos;
+}
+
+static void *vmstat_next(struct seq_file *m, void *arg, loff_t *pos)
+{
+	(*pos)++;
+	if (*pos >= ARRAY_SIZE(vmstat_text))
+		return NULL;
+	return (unsigned long *)m->private + *pos;
+}
+
+static int vmstat_show(struct seq_file *m, void *arg)
+{
+	unsigned long *l = arg;
+	unsigned long off = l - (unsigned long *)m->private;
+
+	seq_printf(m, "%s %lu\n", vmstat_text[off], *l);
+	return 0;
+}
+
+static void vmstat_stop(struct seq_file *m, void *arg)
+{
+	kfree(m->private);
+	m->private = NULL;
+}
+
+struct seq_operations vmstat_op = {
+	.start	= vmstat_start,
+	.next	= vmstat_next,
+	.stop	= vmstat_stop,
+	.show	= vmstat_show,
+};
+
+#endif /* CONFIG_PROC_FS */
+
+#ifdef CONFIG_HOTPLUG_CPU
+static int page_alloc_cpu_notify(struct notifier_block *self,
+				 unsigned long action, void *hcpu)
+{
+	int cpu = (unsigned long)hcpu;
+	long *count;
+	unsigned long *src, *dest;
+
+	if (action == CPU_DEAD) {
+		int i;
+
+		/* Drain local pagecache count. */
+		count = &per_cpu(nr_pagecache_local, cpu);
+		atomic_add(*count, &nr_pagecache);
+		*count = 0;
+		local_irq_disable();
+		__drain_pages(cpu);
+
+		/* Add dead cpu's page_states to our own. */
+		dest = (unsigned long *)&__get_cpu_var(page_states);
+		src = (unsigned long *)&per_cpu(page_states, cpu);
+
+		for (i = 0; i < sizeof(struct page_state)/sizeof(unsigned long);
+				i++) {
+			dest[i] += src[i];
+			src[i] = 0;
+		}
+
+		local_irq_enable();
+	}
+	return NOTIFY_OK;
+}
+#endif /* CONFIG_HOTPLUG_CPU */
+
+void __init page_alloc_init(void)
+{
+	hotcpu_notifier(page_alloc_cpu_notify, 0);
+}
+
+/*
+ * setup_per_zone_lowmem_reserve - called whenever
+ *	sysctl_lower_zone_reserve_ratio changes.  Ensures that each zone
+ *	has a correct pages reserved value, so an adequate number of
+ *	pages are left in the zone after a successful __alloc_pages().
+ */
+static void setup_per_zone_lowmem_reserve(void)
+{
+	struct pglist_data *pgdat;
+	int j, idx;
+
+	for_each_pgdat(pgdat) {
+		for (j = 0; j < MAX_NR_ZONES; j++) {
+			struct zone *zone = pgdat->node_zones + j;
+			unsigned long present_pages = zone->present_pages;
+
+			zone->lowmem_reserve[j] = 0;
+
+			for (idx = j-1; idx >= 0; idx--) {
+				struct zone *lower_zone;
+
+				if (sysctl_lowmem_reserve_ratio[idx] < 1)
+					sysctl_lowmem_reserve_ratio[idx] = 1;
+
+				lower_zone = pgdat->node_zones + idx;
+				lower_zone->lowmem_reserve[j] = present_pages /
+					sysctl_lowmem_reserve_ratio[idx];
+				present_pages += lower_zone->present_pages;
+			}
+		}
+	}
+}
+
+/*
+ * setup_per_zone_pages_min - called when min_free_kbytes changes.  Ensures 
+ *	that the pages_{min,low,high} values for each zone are set correctly 
+ *	with respect to min_free_kbytes.
+ */
+void setup_per_zone_pages_min(void)
+{
+	unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT - 10);
+	unsigned long lowmem_pages = 0;
+	struct zone *zone;
+	unsigned long flags;
+
+	/* Calculate total number of !ZONE_HIGHMEM pages */
+	for_each_zone(zone) {
+		if (!is_highmem(zone))
+			lowmem_pages += zone->present_pages;
+	}
+
+	for_each_zone(zone) {
+		unsigned long tmp;
+		spin_lock_irqsave(&zone->lru_lock, flags);
+		tmp = (pages_min * zone->present_pages) / lowmem_pages;
+		if (is_highmem(zone)) {
+			/*
+			 * __GFP_HIGH and PF_MEMALLOC allocations usually don't
+			 * need highmem pages, so cap pages_min to a small
+			 * value here.
+			 *
+			 * The (pages_high-pages_low) and (pages_low-pages_min)
+			 * deltas controls asynch page reclaim, and so should
+			 * not be capped for highmem.
+			 */
+			int min_pages;
+
+			min_pages = zone->present_pages / 1024;
+			if (min_pages < SWAP_CLUSTER_MAX)
+				min_pages = SWAP_CLUSTER_MAX;
+			if (min_pages > 128)
+				min_pages = 128;
+			zone->pages_min = min_pages;
+		} else {
+			/*
+			 * If it's a lowmem zone, reserve a number of pages
+			 * proportionate to the zone's size.
+			 */
+			zone->pages_min = tmp;
+		}
+
+		zone->pages_low   = zone->pages_min + tmp / 4;
+		zone->pages_high  = zone->pages_min + tmp / 2;
+		spin_unlock_irqrestore(&zone->lru_lock, flags);
+	}
+}
+
+/*
+ * Initialise min_free_kbytes.
+ *
+ * For small machines we want it small (128k min).  For large machines
+ * we want it large (64MB max).  But it is not linear, because network
+ * bandwidth does not increase linearly with machine size.  We use
+ *
+ * 	min_free_kbytes = 4 * sqrt(lowmem_kbytes), for better accuracy:
+ *	min_free_kbytes = sqrt(lowmem_kbytes * 16)
+ *
+ * which yields
+ *
+ * 16MB:	512k
+ * 32MB:	724k
+ * 64MB:	1024k
+ * 128MB:	1448k
+ * 256MB:	2048k
+ * 512MB:	2896k
+ * 1024MB:	4096k
+ * 2048MB:	5792k
+ * 4096MB:	8192k
+ * 8192MB:	11584k
+ * 16384MB:	16384k
+ */
+static int __init init_per_zone_pages_min(void)
+{
+	unsigned long lowmem_kbytes;
+
+	lowmem_kbytes = nr_free_buffer_pages() * (PAGE_SIZE >> 10);
+
+	min_free_kbytes = int_sqrt(lowmem_kbytes * 16);
+	if (min_free_kbytes < 128)
+		min_free_kbytes = 128;
+	if (min_free_kbytes > 65536)
+		min_free_kbytes = 65536;
+	setup_per_zone_pages_min();
+	setup_per_zone_lowmem_reserve();
+	return 0;
+}
+module_init(init_per_zone_pages_min)
+
+/*
+ * min_free_kbytes_sysctl_handler - just a wrapper around proc_dointvec() so 
+ *	that we can call two helper functions whenever min_free_kbytes
+ *	changes.
+ */
+int min_free_kbytes_sysctl_handler(ctl_table *table, int write, 
+	struct file *file, void __user *buffer, size_t *length, loff_t *ppos)
+{
+	proc_dointvec(table, write, file, buffer, length, ppos);
+	setup_per_zone_pages_min();
+	return 0;
+}
+
+/*
+ * lowmem_reserve_ratio_sysctl_handler - just a wrapper around
+ *	proc_dointvec() so that we can call setup_per_zone_lowmem_reserve()
+ *	whenever sysctl_lowmem_reserve_ratio changes.
+ *
+ * The reserve ratio obviously has absolutely no relation with the
+ * pages_min watermarks. The lowmem reserve ratio can only make sense
+ * if in function of the boot time zone sizes.
+ */
+int lowmem_reserve_ratio_sysctl_handler(ctl_table *table, int write,
+	struct file *file, void __user *buffer, size_t *length, loff_t *ppos)
+{
+	proc_dointvec_minmax(table, write, file, buffer, length, ppos);
+	setup_per_zone_lowmem_reserve();
+	return 0;
+}
+
+__initdata int hashdist = HASHDIST_DEFAULT;
+
+#ifdef CONFIG_NUMA
+static int __init set_hashdist(char *str)
+{
+	if (!str)
+		return 0;
+	hashdist = simple_strtoul(str, &str, 0);
+	return 1;
+}
+__setup("hashdist=", set_hashdist);
+#endif
+
+/*
+ * allocate a large system hash table from bootmem
+ * - it is assumed that the hash table must contain an exact power-of-2
+ *   quantity of entries
+ * - limit is the number of hash buckets, not the total allocation size
+ */
+void *__init alloc_large_system_hash(const char *tablename,
+				     unsigned long bucketsize,
+				     unsigned long numentries,
+				     int scale,
+				     int flags,
+				     unsigned int *_hash_shift,
+				     unsigned int *_hash_mask,
+				     unsigned long limit)
+{
+	unsigned long long max = limit;
+	unsigned long log2qty, size;
+	void *table = NULL;
+
+	/* allow the kernel cmdline to have a say */
+	if (!numentries) {
+		/* round applicable memory size up to nearest megabyte */
+		numentries = (flags & HASH_HIGHMEM) ? nr_all_pages : nr_kernel_pages;
+		numentries += (1UL << (20 - PAGE_SHIFT)) - 1;
+		numentries >>= 20 - PAGE_SHIFT;
+		numentries <<= 20 - PAGE_SHIFT;
+
+		/* limit to 1 bucket per 2^scale bytes of low memory */
+		if (scale > PAGE_SHIFT)
+			numentries >>= (scale - PAGE_SHIFT);
+		else
+			numentries <<= (PAGE_SHIFT - scale);
+	}
+	/* rounded up to nearest power of 2 in size */
+	numentries = 1UL << (long_log2(numentries) + 1);
+
+	/* limit allocation size to 1/16 total memory by default */
+	if (max == 0) {
+		max = ((unsigned long long)nr_all_pages << PAGE_SHIFT) >> 4;
+		do_div(max, bucketsize);
+	}
+
+	if (numentries > max)
+		numentries = max;
+
+	log2qty = long_log2(numentries);
+
+	do {
+		size = bucketsize << log2qty;
+		if (flags & HASH_EARLY)
+			table = alloc_bootmem(size);
+		else if (hashdist)
+			table = __vmalloc(size, GFP_ATOMIC, PAGE_KERNEL);
+		else {
+			unsigned long order;
+			for (order = 0; ((1UL << order) << PAGE_SHIFT) < size; order++)
+				;
+			table = (void*) __get_free_pages(GFP_ATOMIC, order);
+		}
+	} while (!table && size > PAGE_SIZE && --log2qty);
+
+	if (!table)
+		panic("Failed to allocate %s hash table\n", tablename);
+
+	printk("%s hash table entries: %d (order: %d, %lu bytes)\n",
+	       tablename,
+	       (1U << log2qty),
+	       long_log2(size) - PAGE_SHIFT,
+	       size);
+
+	if (_hash_shift)
+		*_hash_shift = log2qty;
+	if (_hash_mask)
+		*_hash_mask = (1 << log2qty) - 1;
+
+	return table;
+}
diff -urN oldtree/mm/readahead.c newtree/mm/readahead.c
--- oldtree/mm/readahead.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/mm/readahead.c	2006-02-13 19:20:01.285316904 -0500
@@ -38,6 +38,7 @@
 	ra->ra_pages = mapping->backing_dev_info->ra_pages;
 	ra->prev_page = -1;
 }
+EXPORT_SYMBOL_GPL(file_ra_state_init);
 
 /*
  * Return max readahead size for this inode in number-of-pages.
diff -urN oldtree/mm/shmem.c newtree/mm/shmem.c
--- oldtree/mm/shmem.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/mm/shmem.c	2006-02-13 19:20:01.286316752 -0500
@@ -1301,7 +1301,8 @@
 		case S_IFREG:
 			inode->i_op = &shmem_inode_operations;
 			inode->i_fop = &shmem_file_operations;
-			mpol_shared_policy_init(&info->policy);
+			mpol_shared_policy_init(&info->policy, sbinfo->policy,
+							&sbinfo->policy_nodes);
 			break;
 		case S_IFDIR:
 			inode->i_nlink++;
@@ -1315,7 +1316,8 @@
 			 * Must not load anything in the rbtree,
 			 * mpol_free_shared_policy will not be called.
 			 */
-			mpol_shared_policy_init(&info->policy);
+			mpol_shared_policy_init(&info->policy, MPOL_DEFAULT,
+						NULL);
 			break;
 		}
 	} else if (sbinfo->max_inodes) {
@@ -1828,7 +1830,9 @@
 	.put_link	= shmem_put_link,
 };
 
-static int shmem_parse_options(char *options, int *mode, uid_t *uid, gid_t *gid, unsigned long *blocks, unsigned long *inodes)
+static int shmem_parse_options(char *options, int *mode, uid_t *uid,
+	gid_t *gid, unsigned long *blocks, unsigned long *inodes,
+	int *policy, nodemask_t *policy_nodes)
 {
 	char *this_char, *value, *rest;
 
@@ -1882,6 +1886,19 @@
 			*gid = simple_strtoul(value,&rest,0);
 			if (*rest)
 				goto bad_val;
+		} else if (!strcmp(this_char,"mpol")) {
+			if (!strcmp(value,"default"))
+				*policy = MPOL_DEFAULT;
+			else if (!strcmp(value,"preferred"))
+				*policy = MPOL_PREFERRED;
+			else if (!strcmp(value,"bind"))
+				*policy = MPOL_BIND;
+			else if (!strcmp(value,"interleave"))
+				*policy = MPOL_INTERLEAVE;
+			else
+				goto bad_val;
+		} else if (!strcmp(this_char,"mpol_nodelist")) {
+			nodelist_parse(value, *policy_nodes);
 		} else {
 			printk(KERN_ERR "tmpfs: Bad mount option %s\n",
 			       this_char);
@@ -1902,12 +1919,14 @@
 	struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
 	unsigned long max_blocks = sbinfo->max_blocks;
 	unsigned long max_inodes = sbinfo->max_inodes;
+	int policy = sbinfo->policy;
+	nodemask_t policy_nodes = sbinfo->policy_nodes;
 	unsigned long blocks;
 	unsigned long inodes;
 	int error = -EINVAL;
 
-	if (shmem_parse_options(data, NULL, NULL, NULL,
-				&max_blocks, &max_inodes))
+	if (shmem_parse_options(data, NULL, NULL, NULL, &max_blocks,
+				&max_inodes, &policy, &policy_nodes))
 		return error;
 
 	spin_lock(&sbinfo->stat_lock);
@@ -1933,6 +1952,8 @@
 	sbinfo->free_blocks = max_blocks - blocks;
 	sbinfo->max_inodes  = max_inodes;
 	sbinfo->free_inodes = max_inodes - inodes;
+	sbinfo->policy = policy;
+	sbinfo->policy_nodes = policy_nodes;
 out:
 	spin_unlock(&sbinfo->stat_lock);
 	return error;
@@ -1957,6 +1978,8 @@
 	struct shmem_sb_info *sbinfo;
 	unsigned long blocks = 0;
 	unsigned long inodes = 0;
+	int policy = MPOL_DEFAULT;
+	nodemask_t policy_nodes = node_online_map;
 
 #ifdef CONFIG_TMPFS
 	/*
@@ -1969,8 +1992,8 @@
 		inodes = totalram_pages - totalhigh_pages;
 		if (inodes > blocks)
 			inodes = blocks;
-		if (shmem_parse_options(data, &mode, &uid, &gid,
-					&blocks, &inodes))
+		if (shmem_parse_options(data, &mode, &uid, &gid, &blocks,
+					&inodes, &policy, &policy_nodes))
 			return -EINVAL;
 	}
 #else
@@ -1988,6 +2011,8 @@
 	sbinfo->free_blocks = blocks;
 	sbinfo->max_inodes = inodes;
 	sbinfo->free_inodes = inodes;
+	sbinfo->policy = policy;
+	sbinfo->policy_nodes = policy_nodes;
 
 	sb->s_fs_info = sbinfo;
 	sb->s_maxbytes = SHMEM_MAX_BYTES;
diff -urN oldtree/mm/shmem.c.orig newtree/mm/shmem.c.orig
--- oldtree/mm/shmem.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/mm/shmem.c.orig	2006-02-13 19:20:01.289316296 -0500
@@ -0,0 +1,2255 @@
+/*
+ * Resizable virtual memory filesystem for Linux.
+ *
+ * Copyright (C) 2000 Linus Torvalds.
+ *		 2000 Transmeta Corp.
+ *		 2000-2001 Christoph Rohland
+ *		 2000-2001 SAP AG
+ *		 2002 Red Hat Inc.
+ * Copyright (C) 2002-2005 Hugh Dickins.
+ * Copyright (C) 2002-2005 VERITAS Software Corporation.
+ * Copyright (C) 2004 Andi Kleen, SuSE Labs
+ *
+ * Extended attribute support for tmpfs:
+ * Copyright (c) 2004, Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+ * Copyright (c) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ *
+ * This file is released under the GPL.
+ */
+
+/*
+ * This virtual memory filesystem is heavily based on the ramfs. It
+ * extends ramfs by the ability to use swap and honor resource limits
+ * which makes it a completely usable filesystem.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/file.h>
+#include <linux/swap.h>
+#include <linux/pagemap.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/backing-dev.h>
+#include <linux/shmem_fs.h>
+#include <linux/mount.h>
+#include <linux/writeback.h>
+#include <linux/vfs.h>
+#include <linux/blkdev.h>
+#include <linux/security.h>
+#include <linux/swapops.h>
+#include <linux/mempolicy.h>
+#include <linux/namei.h>
+#include <asm/uaccess.h>
+#include <asm/div64.h>
+#include <asm/pgtable.h>
+
+/* This magic number is used in glibc for posix shared memory */
+#define TMPFS_MAGIC	0x01021994
+
+#define ENTRIES_PER_PAGE (PAGE_CACHE_SIZE/sizeof(unsigned long))
+#define ENTRIES_PER_PAGEPAGE (ENTRIES_PER_PAGE*ENTRIES_PER_PAGE)
+#define BLOCKS_PER_PAGE  (PAGE_CACHE_SIZE/512)
+
+#define SHMEM_MAX_INDEX  (SHMEM_NR_DIRECT + (ENTRIES_PER_PAGEPAGE/2) * (ENTRIES_PER_PAGE+1))
+#define SHMEM_MAX_BYTES  ((unsigned long long)SHMEM_MAX_INDEX << PAGE_CACHE_SHIFT)
+
+#define VM_ACCT(size)    (PAGE_CACHE_ALIGN(size) >> PAGE_SHIFT)
+
+/* info->flags needs VM_flags to handle pagein/truncate races efficiently */
+#define SHMEM_PAGEIN	 VM_READ
+#define SHMEM_TRUNCATE	 VM_WRITE
+
+/* Definition to limit shmem_truncate's steps between cond_rescheds */
+#define LATENCY_LIMIT	 64
+
+/* Pretend that each entry is of this size in directory's i_size */
+#define BOGO_DIRENT_SIZE 20
+
+/* Flag allocation requirements to shmem_getpage and shmem_swp_alloc */
+enum sgp_type {
+	SGP_QUICK,	/* don't try more than file page cache lookup */
+	SGP_READ,	/* don't exceed i_size, don't allocate page */
+	SGP_CACHE,	/* don't exceed i_size, may allocate page */
+	SGP_WRITE,	/* may exceed i_size, may allocate page */
+};
+
+static int shmem_getpage(struct inode *inode, unsigned long idx,
+			 struct page **pagep, enum sgp_type sgp, int *type);
+
+static inline struct page *shmem_dir_alloc(gfp_t gfp_mask)
+{
+	/*
+	 * The above definition of ENTRIES_PER_PAGE, and the use of
+	 * BLOCKS_PER_PAGE on indirect pages, assume PAGE_CACHE_SIZE:
+	 * might be reconsidered if it ever diverges from PAGE_SIZE.
+	 */
+	return alloc_pages(gfp_mask, PAGE_CACHE_SHIFT-PAGE_SHIFT);
+}
+
+static inline void shmem_dir_free(struct page *page)
+{
+	__free_pages(page, PAGE_CACHE_SHIFT-PAGE_SHIFT);
+}
+
+static struct page **shmem_dir_map(struct page *page)
+{
+	return (struct page **)kmap_atomic(page, KM_USER0);
+}
+
+static inline void shmem_dir_unmap(struct page **dir)
+{
+	kunmap_atomic(dir, KM_USER0);
+}
+
+static swp_entry_t *shmem_swp_map(struct page *page)
+{
+	return (swp_entry_t *)kmap_atomic(page, KM_USER1);
+}
+
+static inline void shmem_swp_balance_unmap(void)
+{
+	/*
+	 * When passing a pointer to an i_direct entry, to code which
+	 * also handles indirect entries and so will shmem_swp_unmap,
+	 * we must arrange for the preempt count to remain in balance.
+	 * What kmap_atomic of a lowmem page does depends on config
+	 * and architecture, so pretend to kmap_atomic some lowmem page.
+	 */
+	(void) kmap_atomic(ZERO_PAGE(0), KM_USER1);
+}
+
+static inline void shmem_swp_unmap(swp_entry_t *entry)
+{
+	kunmap_atomic(entry, KM_USER1);
+}
+
+static inline struct shmem_sb_info *SHMEM_SB(struct super_block *sb)
+{
+	return sb->s_fs_info;
+}
+
+/*
+ * shmem_file_setup pre-accounts the whole fixed size of a VM object,
+ * for shared memory and for shared anonymous (/dev/zero) mappings
+ * (unless MAP_NORESERVE and sysctl_overcommit_memory <= 1),
+ * consistent with the pre-accounting of private mappings ...
+ */
+static inline int shmem_acct_size(unsigned long flags, loff_t size)
+{
+	return (flags & VM_ACCOUNT)?
+		security_vm_enough_memory(VM_ACCT(size)): 0;
+}
+
+static inline void shmem_unacct_size(unsigned long flags, loff_t size)
+{
+	if (flags & VM_ACCOUNT)
+		vm_unacct_memory(VM_ACCT(size));
+}
+
+/*
+ * ... whereas tmpfs objects are accounted incrementally as
+ * pages are allocated, in order to allow huge sparse files.
+ * shmem_getpage reports shmem_acct_block failure as -ENOSPC not -ENOMEM,
+ * so that a failure on a sparse tmpfs mapping will give SIGBUS not OOM.
+ */
+static inline int shmem_acct_block(unsigned long flags)
+{
+	return (flags & VM_ACCOUNT)?
+		0: security_vm_enough_memory(VM_ACCT(PAGE_CACHE_SIZE));
+}
+
+static inline void shmem_unacct_blocks(unsigned long flags, long pages)
+{
+	if (!(flags & VM_ACCOUNT))
+		vm_unacct_memory(pages * VM_ACCT(PAGE_CACHE_SIZE));
+}
+
+static struct super_operations shmem_ops;
+static struct address_space_operations shmem_aops;
+static struct file_operations shmem_file_operations;
+static struct inode_operations shmem_inode_operations;
+static struct inode_operations shmem_dir_inode_operations;
+static struct vm_operations_struct shmem_vm_ops;
+
+static struct backing_dev_info shmem_backing_dev_info  __read_mostly = {
+	.ra_pages	= 0,	/* No readahead */
+	.capabilities	= BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK,
+	.unplug_io_fn	= default_unplug_io_fn,
+};
+
+static LIST_HEAD(shmem_swaplist);
+static DEFINE_SPINLOCK(shmem_swaplist_lock);
+
+static void shmem_free_blocks(struct inode *inode, long pages)
+{
+	struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
+	if (sbinfo->max_blocks) {
+		spin_lock(&sbinfo->stat_lock);
+		sbinfo->free_blocks += pages;
+		inode->i_blocks -= pages*BLOCKS_PER_PAGE;
+		spin_unlock(&sbinfo->stat_lock);
+	}
+}
+
+/*
+ * shmem_recalc_inode - recalculate the size of an inode
+ *
+ * @inode: inode to recalc
+ *
+ * We have to calculate the free blocks since the mm can drop
+ * undirtied hole pages behind our back.
+ *
+ * But normally   info->alloced == inode->i_mapping->nrpages + info->swapped
+ * So mm freed is info->alloced - (inode->i_mapping->nrpages + info->swapped)
+ *
+ * It has to be called with the spinlock held.
+ */
+static void shmem_recalc_inode(struct inode *inode)
+{
+	struct shmem_inode_info *info = SHMEM_I(inode);
+	long freed;
+
+	freed = info->alloced - info->swapped - inode->i_mapping->nrpages;
+	if (freed > 0) {
+		info->alloced -= freed;
+		shmem_unacct_blocks(info->flags, freed);
+		shmem_free_blocks(inode, freed);
+	}
+}
+
+/*
+ * shmem_swp_entry - find the swap vector position in the info structure
+ *
+ * @info:  info structure for the inode
+ * @index: index of the page to find
+ * @page:  optional page to add to the structure. Has to be preset to
+ *         all zeros
+ *
+ * If there is no space allocated yet it will return NULL when
+ * page is NULL, else it will use the page for the needed block,
+ * setting it to NULL on return to indicate that it has been used.
+ *
+ * The swap vector is organized the following way:
+ *
+ * There are SHMEM_NR_DIRECT entries directly stored in the
+ * shmem_inode_info structure. So small files do not need an addional
+ * allocation.
+ *
+ * For pages with index > SHMEM_NR_DIRECT there is the pointer
+ * i_indirect which points to a page which holds in the first half
+ * doubly indirect blocks, in the second half triple indirect blocks:
+ *
+ * For an artificial ENTRIES_PER_PAGE = 4 this would lead to the
+ * following layout (for SHMEM_NR_DIRECT == 16):
+ *
+ * i_indirect -> dir --> 16-19
+ * 	      |	     +-> 20-23
+ * 	      |
+ * 	      +-->dir2 --> 24-27
+ * 	      |	       +-> 28-31
+ * 	      |	       +-> 32-35
+ * 	      |	       +-> 36-39
+ * 	      |
+ * 	      +-->dir3 --> 40-43
+ * 	       	       +-> 44-47
+ * 	      	       +-> 48-51
+ * 	      	       +-> 52-55
+ */
+static swp_entry_t *shmem_swp_entry(struct shmem_inode_info *info, unsigned long index, struct page **page)
+{
+	unsigned long offset;
+	struct page **dir;
+	struct page *subdir;
+
+	if (index < SHMEM_NR_DIRECT) {
+		shmem_swp_balance_unmap();
+		return info->i_direct+index;
+	}
+	if (!info->i_indirect) {
+		if (page) {
+			info->i_indirect = *page;
+			*page = NULL;
+		}
+		return NULL;			/* need another page */
+	}
+
+	index -= SHMEM_NR_DIRECT;
+	offset = index % ENTRIES_PER_PAGE;
+	index /= ENTRIES_PER_PAGE;
+	dir = shmem_dir_map(info->i_indirect);
+
+	if (index >= ENTRIES_PER_PAGE/2) {
+		index -= ENTRIES_PER_PAGE/2;
+		dir += ENTRIES_PER_PAGE/2 + index/ENTRIES_PER_PAGE;
+		index %= ENTRIES_PER_PAGE;
+		subdir = *dir;
+		if (!subdir) {
+			if (page) {
+				*dir = *page;
+				*page = NULL;
+			}
+			shmem_dir_unmap(dir);
+			return NULL;		/* need another page */
+		}
+		shmem_dir_unmap(dir);
+		dir = shmem_dir_map(subdir);
+	}
+
+	dir += index;
+	subdir = *dir;
+	if (!subdir) {
+		if (!page || !(subdir = *page)) {
+			shmem_dir_unmap(dir);
+			return NULL;		/* need a page */
+		}
+		*dir = subdir;
+		*page = NULL;
+	}
+	shmem_dir_unmap(dir);
+	return shmem_swp_map(subdir) + offset;
+}
+
+static void shmem_swp_set(struct shmem_inode_info *info, swp_entry_t *entry, unsigned long value)
+{
+	long incdec = value? 1: -1;
+
+	entry->val = value;
+	info->swapped += incdec;
+	if ((unsigned long)(entry - info->i_direct) >= SHMEM_NR_DIRECT) {
+		struct page *page = kmap_atomic_to_page(entry);
+		set_page_private(page, page_private(page) + incdec);
+	}
+}
+
+/*
+ * shmem_swp_alloc - get the position of the swap entry for the page.
+ *                   If it does not exist allocate the entry.
+ *
+ * @info:	info structure for the inode
+ * @index:	index of the page to find
+ * @sgp:	check and recheck i_size? skip allocation?
+ */
+static swp_entry_t *shmem_swp_alloc(struct shmem_inode_info *info, unsigned long index, enum sgp_type sgp)
+{
+	struct inode *inode = &info->vfs_inode;
+	struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
+	struct page *page = NULL;
+	swp_entry_t *entry;
+
+	if (sgp != SGP_WRITE &&
+	    ((loff_t) index << PAGE_CACHE_SHIFT) >= i_size_read(inode))
+		return ERR_PTR(-EINVAL);
+
+	while (!(entry = shmem_swp_entry(info, index, &page))) {
+		if (sgp == SGP_READ)
+			return shmem_swp_map(ZERO_PAGE(0));
+		/*
+		 * Test free_blocks against 1 not 0, since we have 1 data
+		 * page (and perhaps indirect index pages) yet to allocate:
+		 * a waste to allocate index if we cannot allocate data.
+		 */
+		if (sbinfo->max_blocks) {
+			spin_lock(&sbinfo->stat_lock);
+			if (sbinfo->free_blocks <= 1) {
+				spin_unlock(&sbinfo->stat_lock);
+				return ERR_PTR(-ENOSPC);
+			}
+			sbinfo->free_blocks--;
+			inode->i_blocks += BLOCKS_PER_PAGE;
+			spin_unlock(&sbinfo->stat_lock);
+		}
+
+		spin_unlock(&info->lock);
+		page = shmem_dir_alloc(mapping_gfp_mask(inode->i_mapping) | __GFP_ZERO);
+		if (page)
+			set_page_private(page, 0);
+		spin_lock(&info->lock);
+
+		if (!page) {
+			shmem_free_blocks(inode, 1);
+			return ERR_PTR(-ENOMEM);
+		}
+		if (sgp != SGP_WRITE &&
+		    ((loff_t) index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) {
+			entry = ERR_PTR(-EINVAL);
+			break;
+		}
+		if (info->next_index <= index)
+			info->next_index = index + 1;
+	}
+	if (page) {
+		/* another task gave its page, or truncated the file */
+		shmem_free_blocks(inode, 1);
+		shmem_dir_free(page);
+	}
+	if (info->next_index <= index && !IS_ERR(entry))
+		info->next_index = index + 1;
+	return entry;
+}
+
+/*
+ * shmem_free_swp - free some swap entries in a directory
+ *
+ * @dir:   pointer to the directory
+ * @edir:  pointer after last entry of the directory
+ */
+static int shmem_free_swp(swp_entry_t *dir, swp_entry_t *edir)
+{
+	swp_entry_t *ptr;
+	int freed = 0;
+
+	for (ptr = dir; ptr < edir; ptr++) {
+		if (ptr->val) {
+			free_swap_and_cache(*ptr);
+			*ptr = (swp_entry_t){0};
+			freed++;
+		}
+	}
+	return freed;
+}
+
+static int shmem_map_and_free_swp(struct page *subdir,
+		int offset, int limit, struct page ***dir)
+{
+	swp_entry_t *ptr;
+	int freed = 0;
+
+	ptr = shmem_swp_map(subdir);
+	for (; offset < limit; offset += LATENCY_LIMIT) {
+		int size = limit - offset;
+		if (size > LATENCY_LIMIT)
+			size = LATENCY_LIMIT;
+		freed += shmem_free_swp(ptr+offset, ptr+offset+size);
+		if (need_resched()) {
+			shmem_swp_unmap(ptr);
+			if (*dir) {
+				shmem_dir_unmap(*dir);
+				*dir = NULL;
+			}
+			cond_resched();
+			ptr = shmem_swp_map(subdir);
+		}
+	}
+	shmem_swp_unmap(ptr);
+	return freed;
+}
+
+static void shmem_free_pages(struct list_head *next)
+{
+	struct page *page;
+	int freed = 0;
+
+	do {
+		page = container_of(next, struct page, lru);
+		next = next->next;
+		shmem_dir_free(page);
+		freed++;
+		if (freed >= LATENCY_LIMIT) {
+			cond_resched();
+			freed = 0;
+		}
+	} while (next);
+}
+
+static void shmem_truncate(struct inode *inode)
+{
+	struct shmem_inode_info *info = SHMEM_I(inode);
+	unsigned long idx;
+	unsigned long size;
+	unsigned long limit;
+	unsigned long stage;
+	unsigned long diroff;
+	struct page **dir;
+	struct page *topdir;
+	struct page *middir;
+	struct page *subdir;
+	swp_entry_t *ptr;
+	LIST_HEAD(pages_to_free);
+	long nr_pages_to_free = 0;
+	long nr_swaps_freed = 0;
+	int offset;
+	int freed;
+
+	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+	idx = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+	if (idx >= info->next_index)
+		return;
+
+	spin_lock(&info->lock);
+	info->flags |= SHMEM_TRUNCATE;
+	limit = info->next_index;
+	info->next_index = idx;
+	topdir = info->i_indirect;
+	if (topdir && idx <= SHMEM_NR_DIRECT) {
+		info->i_indirect = NULL;
+		nr_pages_to_free++;
+		list_add(&topdir->lru, &pages_to_free);
+	}
+	spin_unlock(&info->lock);
+
+	if (info->swapped && idx < SHMEM_NR_DIRECT) {
+		ptr = info->i_direct;
+		size = limit;
+		if (size > SHMEM_NR_DIRECT)
+			size = SHMEM_NR_DIRECT;
+		nr_swaps_freed = shmem_free_swp(ptr+idx, ptr+size);
+	}
+	if (!topdir)
+		goto done2;
+
+	BUG_ON(limit <= SHMEM_NR_DIRECT);
+	limit -= SHMEM_NR_DIRECT;
+	idx = (idx > SHMEM_NR_DIRECT)? (idx - SHMEM_NR_DIRECT): 0;
+	offset = idx % ENTRIES_PER_PAGE;
+	idx -= offset;
+
+	dir = shmem_dir_map(topdir);
+	stage = ENTRIES_PER_PAGEPAGE/2;
+	if (idx < ENTRIES_PER_PAGEPAGE/2) {
+		middir = topdir;
+		diroff = idx/ENTRIES_PER_PAGE;
+	} else {
+		dir += ENTRIES_PER_PAGE/2;
+		dir += (idx - ENTRIES_PER_PAGEPAGE/2)/ENTRIES_PER_PAGEPAGE;
+		while (stage <= idx)
+			stage += ENTRIES_PER_PAGEPAGE;
+		middir = *dir;
+		if (*dir) {
+			diroff = ((idx - ENTRIES_PER_PAGEPAGE/2) %
+				ENTRIES_PER_PAGEPAGE) / ENTRIES_PER_PAGE;
+			if (!diroff && !offset) {
+				*dir = NULL;
+				nr_pages_to_free++;
+				list_add(&middir->lru, &pages_to_free);
+			}
+			shmem_dir_unmap(dir);
+			dir = shmem_dir_map(middir);
+		} else {
+			diroff = 0;
+			offset = 0;
+			idx = stage;
+		}
+	}
+
+	for (; idx < limit; idx += ENTRIES_PER_PAGE, diroff++) {
+		if (unlikely(idx == stage)) {
+			shmem_dir_unmap(dir);
+			dir = shmem_dir_map(topdir) +
+			    ENTRIES_PER_PAGE/2 + idx/ENTRIES_PER_PAGEPAGE;
+			while (!*dir) {
+				dir++;
+				idx += ENTRIES_PER_PAGEPAGE;
+				if (idx >= limit)
+					goto done1;
+			}
+			stage = idx + ENTRIES_PER_PAGEPAGE;
+			middir = *dir;
+			*dir = NULL;
+			nr_pages_to_free++;
+			list_add(&middir->lru, &pages_to_free);
+			shmem_dir_unmap(dir);
+			cond_resched();
+			dir = shmem_dir_map(middir);
+			diroff = 0;
+		}
+		subdir = dir[diroff];
+		if (subdir && page_private(subdir)) {
+			size = limit - idx;
+			if (size > ENTRIES_PER_PAGE)
+				size = ENTRIES_PER_PAGE;
+			freed = shmem_map_and_free_swp(subdir,
+						offset, size, &dir);
+			if (!dir)
+				dir = shmem_dir_map(middir);
+			nr_swaps_freed += freed;
+			if (offset)
+				spin_lock(&info->lock);
+			set_page_private(subdir, page_private(subdir) - freed);
+			if (offset)
+				spin_unlock(&info->lock);
+			BUG_ON(page_private(subdir) > offset);
+		}
+		if (offset)
+			offset = 0;
+		else if (subdir) {
+			dir[diroff] = NULL;
+			nr_pages_to_free++;
+			list_add(&subdir->lru, &pages_to_free);
+		}
+	}
+done1:
+	shmem_dir_unmap(dir);
+done2:
+	if (inode->i_mapping->nrpages && (info->flags & SHMEM_PAGEIN)) {
+		/*
+		 * Call truncate_inode_pages again: racing shmem_unuse_inode
+		 * may have swizzled a page in from swap since vmtruncate or
+		 * generic_delete_inode did it, before we lowered next_index.
+		 * Also, though shmem_getpage checks i_size before adding to
+		 * cache, no recheck after: so fix the narrow window there too.
+		 */
+		truncate_inode_pages(inode->i_mapping, inode->i_size);
+	}
+
+	spin_lock(&info->lock);
+	info->flags &= ~SHMEM_TRUNCATE;
+	info->swapped -= nr_swaps_freed;
+	if (nr_pages_to_free)
+		shmem_free_blocks(inode, nr_pages_to_free);
+	shmem_recalc_inode(inode);
+	spin_unlock(&info->lock);
+
+	/*
+	 * Empty swap vector directory pages to be freed?
+	 */
+	if (!list_empty(&pages_to_free)) {
+		pages_to_free.prev->next = NULL;
+		shmem_free_pages(pages_to_free.next);
+	}
+}
+
+static int shmem_notify_change(struct dentry *dentry, struct iattr *attr)
+{
+	struct inode *inode = dentry->d_inode;
+	struct page *page = NULL;
+	int error;
+
+	if (attr->ia_valid & ATTR_SIZE) {
+		if (attr->ia_size < inode->i_size) {
+			/*
+			 * If truncating down to a partial page, then
+			 * if that page is already allocated, hold it
+			 * in memory until the truncation is over, so
+			 * truncate_partial_page cannnot miss it were
+			 * it assigned to swap.
+			 */
+			if (attr->ia_size & (PAGE_CACHE_SIZE-1)) {
+				(void) shmem_getpage(inode,
+					attr->ia_size>>PAGE_CACHE_SHIFT,
+						&page, SGP_READ, NULL);
+			}
+			/*
+			 * Reset SHMEM_PAGEIN flag so that shmem_truncate can
+			 * detect if any pages might have been added to cache
+			 * after truncate_inode_pages.  But we needn't bother
+			 * if it's being fully truncated to zero-length: the
+			 * nrpages check is efficient enough in that case.
+			 */
+			if (attr->ia_size) {
+				struct shmem_inode_info *info = SHMEM_I(inode);
+				spin_lock(&info->lock);
+				info->flags &= ~SHMEM_PAGEIN;
+				spin_unlock(&info->lock);
+			}
+		}
+	}
+
+	error = inode_change_ok(inode, attr);
+	if (!error)
+		error = inode_setattr(inode, attr);
+	if (page)
+		page_cache_release(page);
+	return error;
+}
+
+static void shmem_delete_inode(struct inode *inode)
+{
+	struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
+	struct shmem_inode_info *info = SHMEM_I(inode);
+
+	if (inode->i_op->truncate == shmem_truncate) {
+		truncate_inode_pages(inode->i_mapping, 0);
+		shmem_unacct_size(info->flags, inode->i_size);
+		inode->i_size = 0;
+		shmem_truncate(inode);
+		if (!list_empty(&info->swaplist)) {
+			spin_lock(&shmem_swaplist_lock);
+			list_del_init(&info->swaplist);
+			spin_unlock(&shmem_swaplist_lock);
+		}
+	}
+	BUG_ON(inode->i_blocks);
+	if (sbinfo->max_inodes) {
+		spin_lock(&sbinfo->stat_lock);
+		sbinfo->free_inodes++;
+		spin_unlock(&sbinfo->stat_lock);
+	}
+	clear_inode(inode);
+}
+
+static inline int shmem_find_swp(swp_entry_t entry, swp_entry_t *dir, swp_entry_t *edir)
+{
+	swp_entry_t *ptr;
+
+	for (ptr = dir; ptr < edir; ptr++) {
+		if (ptr->val == entry.val)
+			return ptr - dir;
+	}
+	return -1;
+}
+
+static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, struct page *page)
+{
+	struct inode *inode;
+	unsigned long idx;
+	unsigned long size;
+	unsigned long limit;
+	unsigned long stage;
+	struct page **dir;
+	struct page *subdir;
+	swp_entry_t *ptr;
+	int offset;
+
+	idx = 0;
+	ptr = info->i_direct;
+	spin_lock(&info->lock);
+	limit = info->next_index;
+	size = limit;
+	if (size > SHMEM_NR_DIRECT)
+		size = SHMEM_NR_DIRECT;
+	offset = shmem_find_swp(entry, ptr, ptr+size);
+	if (offset >= 0) {
+		shmem_swp_balance_unmap();
+		goto found;
+	}
+	if (!info->i_indirect)
+		goto lost2;
+
+	dir = shmem_dir_map(info->i_indirect);
+	stage = SHMEM_NR_DIRECT + ENTRIES_PER_PAGEPAGE/2;
+
+	for (idx = SHMEM_NR_DIRECT; idx < limit; idx += ENTRIES_PER_PAGE, dir++) {
+		if (unlikely(idx == stage)) {
+			shmem_dir_unmap(dir-1);
+			dir = shmem_dir_map(info->i_indirect) +
+			    ENTRIES_PER_PAGE/2 + idx/ENTRIES_PER_PAGEPAGE;
+			while (!*dir) {
+				dir++;
+				idx += ENTRIES_PER_PAGEPAGE;
+				if (idx >= limit)
+					goto lost1;
+			}
+			stage = idx + ENTRIES_PER_PAGEPAGE;
+			subdir = *dir;
+			shmem_dir_unmap(dir);
+			dir = shmem_dir_map(subdir);
+		}
+		subdir = *dir;
+		if (subdir && page_private(subdir)) {
+			ptr = shmem_swp_map(subdir);
+			size = limit - idx;
+			if (size > ENTRIES_PER_PAGE)
+				size = ENTRIES_PER_PAGE;
+			offset = shmem_find_swp(entry, ptr, ptr+size);
+			if (offset >= 0) {
+				shmem_dir_unmap(dir);
+				goto found;
+			}
+			shmem_swp_unmap(ptr);
+		}
+	}
+lost1:
+	shmem_dir_unmap(dir-1);
+lost2:
+	spin_unlock(&info->lock);
+	return 0;
+found:
+	idx += offset;
+	inode = &info->vfs_inode;
+	if (move_from_swap_cache(page, idx, inode->i_mapping) == 0) {
+		info->flags |= SHMEM_PAGEIN;
+		shmem_swp_set(info, ptr + offset, 0);
+	}
+	shmem_swp_unmap(ptr);
+	spin_unlock(&info->lock);
+	/*
+	 * Decrement swap count even when the entry is left behind:
+	 * try_to_unuse will skip over mms, then reincrement count.
+	 */
+	swap_free(entry);
+	return 1;
+}
+
+/*
+ * shmem_unuse() search for an eventually swapped out shmem page.
+ */
+int shmem_unuse(swp_entry_t entry, struct page *page)
+{
+	struct list_head *p, *next;
+	struct shmem_inode_info *info;
+	int found = 0;
+
+	spin_lock(&shmem_swaplist_lock);
+	list_for_each_safe(p, next, &shmem_swaplist) {
+		info = list_entry(p, struct shmem_inode_info, swaplist);
+		if (!info->swapped)
+			list_del_init(&info->swaplist);
+		else if (shmem_unuse_inode(info, entry, page)) {
+			/* move head to start search for next from here */
+			list_move_tail(&shmem_swaplist, &info->swaplist);
+			found = 1;
+			break;
+		}
+	}
+	spin_unlock(&shmem_swaplist_lock);
+	return found;
+}
+
+/*
+ * Move the page from the page cache to the swap cache.
+ */
+static int shmem_writepage(struct page *page, struct writeback_control *wbc)
+{
+	struct shmem_inode_info *info;
+	swp_entry_t *entry, swap;
+	struct address_space *mapping;
+	unsigned long index;
+	struct inode *inode;
+
+	BUG_ON(!PageLocked(page));
+	BUG_ON(page_mapped(page));
+
+	mapping = page->mapping;
+	index = page->index;
+	inode = mapping->host;
+	info = SHMEM_I(inode);
+	if (info->flags & VM_LOCKED)
+		goto redirty;
+	swap = get_swap_page();
+	if (!swap.val)
+		goto redirty;
+
+	spin_lock(&info->lock);
+	shmem_recalc_inode(inode);
+	if (index >= info->next_index) {
+		BUG_ON(!(info->flags & SHMEM_TRUNCATE));
+		goto unlock;
+	}
+	entry = shmem_swp_entry(info, index, NULL);
+	BUG_ON(!entry);
+	BUG_ON(entry->val);
+
+	if (move_to_swap_cache(page, swap) == 0) {
+		shmem_swp_set(info, entry, swap.val);
+		shmem_swp_unmap(entry);
+		spin_unlock(&info->lock);
+		if (list_empty(&info->swaplist)) {
+			spin_lock(&shmem_swaplist_lock);
+			/* move instead of add in case we're racing */
+			list_move_tail(&info->swaplist, &shmem_swaplist);
+			spin_unlock(&shmem_swaplist_lock);
+		}
+		unlock_page(page);
+		return 0;
+	}
+
+	shmem_swp_unmap(entry);
+unlock:
+	spin_unlock(&info->lock);
+	swap_free(swap);
+redirty:
+	set_page_dirty(page);
+	return WRITEPAGE_ACTIVATE;	/* Return with the page locked */
+}
+
+#ifdef CONFIG_NUMA
+static struct page *shmem_swapin_async(struct shared_policy *p,
+				       swp_entry_t entry, unsigned long idx)
+{
+	struct page *page;
+	struct vm_area_struct pvma;
+
+	/* Create a pseudo vma that just contains the policy */
+	memset(&pvma, 0, sizeof(struct vm_area_struct));
+	pvma.vm_end = PAGE_SIZE;
+	pvma.vm_pgoff = idx;
+	pvma.vm_policy = mpol_shared_policy_lookup(p, idx);
+	page = read_swap_cache_async(entry, &pvma, 0);
+	mpol_free(pvma.vm_policy);
+	return page;
+}
+
+struct page *shmem_swapin(struct shmem_inode_info *info, swp_entry_t entry,
+			  unsigned long idx)
+{
+	struct shared_policy *p = &info->policy;
+	int i, num;
+	struct page *page;
+	unsigned long offset;
+
+	num = valid_swaphandles(entry, &offset);
+	for (i = 0; i < num; offset++, i++) {
+		page = shmem_swapin_async(p,
+				swp_entry(swp_type(entry), offset), idx);
+		if (!page)
+			break;
+		page_cache_release(page);
+	}
+	lru_add_drain();	/* Push any new pages onto the LRU now */
+	return shmem_swapin_async(p, entry, idx);
+}
+
+static struct page *
+shmem_alloc_page(gfp_t gfp, struct shmem_inode_info *info,
+		 unsigned long idx)
+{
+	struct vm_area_struct pvma;
+	struct page *page;
+
+	memset(&pvma, 0, sizeof(struct vm_area_struct));
+	pvma.vm_policy = mpol_shared_policy_lookup(&info->policy, idx);
+	pvma.vm_pgoff = idx;
+	pvma.vm_end = PAGE_SIZE;
+	page = alloc_page_vma(gfp | __GFP_ZERO, &pvma, 0);
+	mpol_free(pvma.vm_policy);
+	return page;
+}
+#else
+static inline struct page *
+shmem_swapin(struct shmem_inode_info *info,swp_entry_t entry,unsigned long idx)
+{
+	swapin_readahead(entry, 0, NULL);
+	return read_swap_cache_async(entry, NULL, 0);
+}
+
+static inline struct page *
+shmem_alloc_page(gfp_t gfp,struct shmem_inode_info *info, unsigned long idx)
+{
+	return alloc_page(gfp | __GFP_ZERO);
+}
+#endif
+
+/*
+ * shmem_getpage - either get the page from swap or allocate a new one
+ *
+ * If we allocate a new one we do not mark it dirty. That's up to the
+ * vm. If we swap it in we mark it dirty since we also free the swap
+ * entry since a page cannot live in both the swap and page cache
+ */
+static int shmem_getpage(struct inode *inode, unsigned long idx,
+			struct page **pagep, enum sgp_type sgp, int *type)
+{
+	struct address_space *mapping = inode->i_mapping;
+	struct shmem_inode_info *info = SHMEM_I(inode);
+	struct shmem_sb_info *sbinfo;
+	struct page *filepage = *pagep;
+	struct page *swappage;
+	swp_entry_t *entry;
+	swp_entry_t swap;
+	int error;
+
+	if (idx >= SHMEM_MAX_INDEX)
+		return -EFBIG;
+	/*
+	 * Normally, filepage is NULL on entry, and either found
+	 * uptodate immediately, or allocated and zeroed, or read
+	 * in under swappage, which is then assigned to filepage.
+	 * But shmem_prepare_write passes in a locked filepage,
+	 * which may be found not uptodate by other callers too,
+	 * and may need to be copied from the swappage read in.
+	 */
+repeat:
+	if (!filepage)
+		filepage = find_lock_page(mapping, idx);
+	if (filepage && PageUptodate(filepage))
+		goto done;
+	error = 0;
+	if (sgp == SGP_QUICK)
+		goto failed;
+
+	spin_lock(&info->lock);
+	shmem_recalc_inode(inode);
+	entry = shmem_swp_alloc(info, idx, sgp);
+	if (IS_ERR(entry)) {
+		spin_unlock(&info->lock);
+		error = PTR_ERR(entry);
+		goto failed;
+	}
+	swap = *entry;
+
+	if (swap.val) {
+		/* Look it up and read it in.. */
+		swappage = lookup_swap_cache(swap);
+		if (!swappage) {
+			shmem_swp_unmap(entry);
+			spin_unlock(&info->lock);
+			/* here we actually do the io */
+			if (type && *type == VM_FAULT_MINOR) {
+				inc_page_state(pgmajfault);
+				*type = VM_FAULT_MAJOR;
+			}
+			swappage = shmem_swapin(info, swap, idx);
+			if (!swappage) {
+				spin_lock(&info->lock);
+				entry = shmem_swp_alloc(info, idx, sgp);
+				if (IS_ERR(entry))
+					error = PTR_ERR(entry);
+				else {
+					if (entry->val == swap.val)
+						error = -ENOMEM;
+					shmem_swp_unmap(entry);
+				}
+				spin_unlock(&info->lock);
+				if (error)
+					goto failed;
+				goto repeat;
+			}
+			wait_on_page_locked(swappage);
+			page_cache_release(swappage);
+			goto repeat;
+		}
+
+		/* We have to do this with page locked to prevent races */
+		if (TestSetPageLocked(swappage)) {
+			shmem_swp_unmap(entry);
+			spin_unlock(&info->lock);
+			wait_on_page_locked(swappage);
+			page_cache_release(swappage);
+			goto repeat;
+		}
+		if (PageWriteback(swappage)) {
+			shmem_swp_unmap(entry);
+			spin_unlock(&info->lock);
+			wait_on_page_writeback(swappage);
+			unlock_page(swappage);
+			page_cache_release(swappage);
+			goto repeat;
+		}
+		if (!PageUptodate(swappage)) {
+			shmem_swp_unmap(entry);
+			spin_unlock(&info->lock);
+			unlock_page(swappage);
+			page_cache_release(swappage);
+			error = -EIO;
+			goto failed;
+		}
+
+		if (filepage) {
+			shmem_swp_set(info, entry, 0);
+			shmem_swp_unmap(entry);
+			delete_from_swap_cache(swappage);
+			spin_unlock(&info->lock);
+			copy_highpage(filepage, swappage);
+			unlock_page(swappage);
+			page_cache_release(swappage);
+			flush_dcache_page(filepage);
+			SetPageUptodate(filepage);
+			set_page_dirty(filepage);
+			swap_free(swap);
+		} else if (!(error = move_from_swap_cache(
+				swappage, idx, mapping))) {
+			info->flags |= SHMEM_PAGEIN;
+			shmem_swp_set(info, entry, 0);
+			shmem_swp_unmap(entry);
+			spin_unlock(&info->lock);
+			filepage = swappage;
+			swap_free(swap);
+		} else {
+			shmem_swp_unmap(entry);
+			spin_unlock(&info->lock);
+			unlock_page(swappage);
+			page_cache_release(swappage);
+			if (error == -ENOMEM) {
+				/* let kswapd refresh zone for GFP_ATOMICs */
+				blk_congestion_wait(WRITE, HZ/50);
+			}
+			goto repeat;
+		}
+	} else if (sgp == SGP_READ && !filepage) {
+		shmem_swp_unmap(entry);
+		filepage = find_get_page(mapping, idx);
+		if (filepage &&
+		    (!PageUptodate(filepage) || TestSetPageLocked(filepage))) {
+			spin_unlock(&info->lock);
+			wait_on_page_locked(filepage);
+			page_cache_release(filepage);
+			filepage = NULL;
+			goto repeat;
+		}
+		spin_unlock(&info->lock);
+	} else {
+		shmem_swp_unmap(entry);
+		sbinfo = SHMEM_SB(inode->i_sb);
+		if (sbinfo->max_blocks) {
+			spin_lock(&sbinfo->stat_lock);
+			if (sbinfo->free_blocks == 0 ||
+			    shmem_acct_block(info->flags)) {
+				spin_unlock(&sbinfo->stat_lock);
+				spin_unlock(&info->lock);
+				error = -ENOSPC;
+				goto failed;
+			}
+			sbinfo->free_blocks--;
+			inode->i_blocks += BLOCKS_PER_PAGE;
+			spin_unlock(&sbinfo->stat_lock);
+		} else if (shmem_acct_block(info->flags)) {
+			spin_unlock(&info->lock);
+			error = -ENOSPC;
+			goto failed;
+		}
+
+		if (!filepage) {
+			spin_unlock(&info->lock);
+			filepage = shmem_alloc_page(mapping_gfp_mask(mapping),
+						    info,
+						    idx);
+			if (!filepage) {
+				shmem_unacct_blocks(info->flags, 1);
+				shmem_free_blocks(inode, 1);
+				error = -ENOMEM;
+				goto failed;
+			}
+
+			spin_lock(&info->lock);
+			entry = shmem_swp_alloc(info, idx, sgp);
+			if (IS_ERR(entry))
+				error = PTR_ERR(entry);
+			else {
+				swap = *entry;
+				shmem_swp_unmap(entry);
+			}
+			if (error || swap.val || 0 != add_to_page_cache_lru(
+					filepage, mapping, idx, GFP_ATOMIC)) {
+				spin_unlock(&info->lock);
+				page_cache_release(filepage);
+				shmem_unacct_blocks(info->flags, 1);
+				shmem_free_blocks(inode, 1);
+				filepage = NULL;
+				if (error)
+					goto failed;
+				goto repeat;
+			}
+			info->flags |= SHMEM_PAGEIN;
+		}
+
+		info->alloced++;
+		spin_unlock(&info->lock);
+		flush_dcache_page(filepage);
+		SetPageUptodate(filepage);
+	}
+done:
+	if (*pagep != filepage) {
+		unlock_page(filepage);
+		*pagep = filepage;
+	}
+	return 0;
+
+failed:
+	if (*pagep != filepage) {
+		unlock_page(filepage);
+		page_cache_release(filepage);
+	}
+	return error;
+}
+
+struct page *shmem_nopage(struct vm_area_struct *vma, unsigned long address, int *type)
+{
+	struct inode *inode = vma->vm_file->f_dentry->d_inode;
+	struct page *page = NULL;
+	unsigned long idx;
+	int error;
+
+	idx = (address - vma->vm_start) >> PAGE_SHIFT;
+	idx += vma->vm_pgoff;
+	idx >>= PAGE_CACHE_SHIFT - PAGE_SHIFT;
+	if (((loff_t) idx << PAGE_CACHE_SHIFT) >= i_size_read(inode))
+		return NOPAGE_SIGBUS;
+
+	error = shmem_getpage(inode, idx, &page, SGP_CACHE, type);
+	if (error)
+		return (error == -ENOMEM)? NOPAGE_OOM: NOPAGE_SIGBUS;
+
+	mark_page_accessed(page);
+	return page;
+}
+
+static int shmem_populate(struct vm_area_struct *vma,
+	unsigned long addr, unsigned long len,
+	pgprot_t prot, unsigned long pgoff, int nonblock)
+{
+	struct inode *inode = vma->vm_file->f_dentry->d_inode;
+	struct mm_struct *mm = vma->vm_mm;
+	enum sgp_type sgp = nonblock? SGP_QUICK: SGP_CACHE;
+	unsigned long size;
+
+	size = (i_size_read(inode) + PAGE_SIZE - 1) >> PAGE_SHIFT;
+	if (pgoff >= size || pgoff + (len >> PAGE_SHIFT) > size)
+		return -EINVAL;
+
+	while ((long) len > 0) {
+		struct page *page = NULL;
+		int err;
+		/*
+		 * Will need changing if PAGE_CACHE_SIZE != PAGE_SIZE
+		 */
+		err = shmem_getpage(inode, pgoff, &page, sgp, NULL);
+		if (err)
+			return err;
+		/* Page may still be null, but only if nonblock was set. */
+		if (page) {
+			mark_page_accessed(page);
+			err = install_page(mm, vma, addr, page, prot);
+			if (err) {
+				page_cache_release(page);
+				return err;
+			}
+		} else if (vma->vm_flags & VM_NONLINEAR) {
+			/* No page was found just because we can't read it in
+			 * now (being here implies nonblock != 0), but the page
+			 * may exist, so set the PTE to fault it in later. */
+    			err = install_file_pte(mm, vma, addr, pgoff, prot);
+			if (err)
+	    			return err;
+		}
+
+		len -= PAGE_SIZE;
+		addr += PAGE_SIZE;
+		pgoff++;
+	}
+	return 0;
+}
+
+#ifdef CONFIG_NUMA
+int shmem_set_policy(struct vm_area_struct *vma, struct mempolicy *new)
+{
+	struct inode *i = vma->vm_file->f_dentry->d_inode;
+	return mpol_set_shared_policy(&SHMEM_I(i)->policy, vma, new);
+}
+
+struct mempolicy *
+shmem_get_policy(struct vm_area_struct *vma, unsigned long addr)
+{
+	struct inode *i = vma->vm_file->f_dentry->d_inode;
+	unsigned long idx;
+
+	idx = ((addr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+	return mpol_shared_policy_lookup(&SHMEM_I(i)->policy, idx);
+}
+#endif
+
+int shmem_lock(struct file *file, int lock, struct user_struct *user)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct shmem_inode_info *info = SHMEM_I(inode);
+	int retval = -ENOMEM;
+
+	spin_lock(&info->lock);
+	if (lock && !(info->flags & VM_LOCKED)) {
+		if (!user_shm_lock(inode->i_size, user))
+			goto out_nomem;
+		info->flags |= VM_LOCKED;
+	}
+	if (!lock && (info->flags & VM_LOCKED) && user) {
+		user_shm_unlock(inode->i_size, user);
+		info->flags &= ~VM_LOCKED;
+	}
+	retval = 0;
+out_nomem:
+	spin_unlock(&info->lock);
+	return retval;
+}
+
+static int shmem_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	file_accessed(file);
+	vma->vm_ops = &shmem_vm_ops;
+	return 0;
+}
+
+static struct inode *
+shmem_get_inode(struct super_block *sb, int mode, dev_t dev)
+{
+	struct inode *inode;
+	struct shmem_inode_info *info;
+	struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
+
+	if (sbinfo->max_inodes) {
+		spin_lock(&sbinfo->stat_lock);
+		if (!sbinfo->free_inodes) {
+			spin_unlock(&sbinfo->stat_lock);
+			return NULL;
+		}
+		sbinfo->free_inodes--;
+		spin_unlock(&sbinfo->stat_lock);
+	}
+
+	inode = new_inode(sb);
+	if (inode) {
+		inode->i_mode = mode;
+		inode->i_uid = current->fsuid;
+		inode->i_gid = current->fsgid;
+		inode->i_blksize = PAGE_CACHE_SIZE;
+		inode->i_blocks = 0;
+		inode->i_mapping->a_ops = &shmem_aops;
+		inode->i_mapping->backing_dev_info = &shmem_backing_dev_info;
+		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+		info = SHMEM_I(inode);
+		memset(info, 0, (char *)inode - (char *)info);
+		spin_lock_init(&info->lock);
+		INIT_LIST_HEAD(&info->swaplist);
+
+		switch (mode & S_IFMT) {
+		default:
+			init_special_inode(inode, mode, dev);
+			break;
+		case S_IFREG:
+			inode->i_op = &shmem_inode_operations;
+			inode->i_fop = &shmem_file_operations;
+			mpol_shared_policy_init(&info->policy);
+			break;
+		case S_IFDIR:
+			inode->i_nlink++;
+			/* Some things misbehave if size == 0 on a directory */
+			inode->i_size = 2 * BOGO_DIRENT_SIZE;
+			inode->i_op = &shmem_dir_inode_operations;
+			inode->i_fop = &simple_dir_operations;
+			break;
+		case S_IFLNK:
+			/*
+			 * Must not load anything in the rbtree,
+			 * mpol_free_shared_policy will not be called.
+			 */
+			mpol_shared_policy_init(&info->policy);
+			break;
+		}
+	} else if (sbinfo->max_inodes) {
+		spin_lock(&sbinfo->stat_lock);
+		sbinfo->free_inodes++;
+		spin_unlock(&sbinfo->stat_lock);
+	}
+	return inode;
+}
+
+#ifdef CONFIG_TMPFS
+static struct inode_operations shmem_symlink_inode_operations;
+static struct inode_operations shmem_symlink_inline_operations;
+
+/*
+ * Normally tmpfs makes no use of shmem_prepare_write, but it
+ * lets a tmpfs file be used read-write below the loop driver.
+ */
+static int
+shmem_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+{
+	struct inode *inode = page->mapping->host;
+	return shmem_getpage(inode, page->index, &page, SGP_WRITE, NULL);
+}
+
+static ssize_t
+shmem_file_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct inode	*inode = file->f_dentry->d_inode;
+	loff_t		pos;
+	unsigned long	written;
+	ssize_t		err;
+
+	if ((ssize_t) count < 0)
+		return -EINVAL;
+
+	if (!access_ok(VERIFY_READ, buf, count))
+		return -EFAULT;
+
+	down(&inode->i_sem);
+
+	pos = *ppos;
+	written = 0;
+
+	err = generic_write_checks(file, &pos, &count, 0);
+	if (err || !count)
+		goto out;
+
+	err = remove_suid(file->f_dentry);
+	if (err)
+		goto out;
+
+	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+
+	do {
+		struct page *page = NULL;
+		unsigned long bytes, index, offset;
+		char *kaddr;
+		int left;
+
+		offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */
+		index = pos >> PAGE_CACHE_SHIFT;
+		bytes = PAGE_CACHE_SIZE - offset;
+		if (bytes > count)
+			bytes = count;
+
+		/*
+		 * We don't hold page lock across copy from user -
+		 * what would it guard against? - so no deadlock here.
+		 * But it still may be a good idea to prefault below.
+		 */
+
+		err = shmem_getpage(inode, index, &page, SGP_WRITE, NULL);
+		if (err)
+			break;
+
+		left = bytes;
+		if (PageHighMem(page)) {
+			volatile unsigned char dummy;
+			__get_user(dummy, buf);
+			__get_user(dummy, buf + bytes - 1);
+
+			kaddr = kmap_atomic(page, KM_USER0);
+			left = __copy_from_user_inatomic(kaddr + offset,
+							buf, bytes);
+			kunmap_atomic(kaddr, KM_USER0);
+		}
+		if (left) {
+			kaddr = kmap(page);
+			left = __copy_from_user(kaddr + offset, buf, bytes);
+			kunmap(page);
+		}
+
+		written += bytes;
+		count -= bytes;
+		pos += bytes;
+		buf += bytes;
+		if (pos > inode->i_size)
+			i_size_write(inode, pos);
+
+		flush_dcache_page(page);
+		set_page_dirty(page);
+		mark_page_accessed(page);
+		page_cache_release(page);
+
+		if (left) {
+			pos -= left;
+			written -= left;
+			err = -EFAULT;
+			break;
+		}
+
+		/*
+		 * Our dirty pages are not counted in nr_dirty,
+		 * and we do not attempt to balance dirty pages.
+		 */
+
+		cond_resched();
+	} while (count);
+
+	*ppos = pos;
+	if (written)
+		err = written;
+out:
+	up(&inode->i_sem);
+	return err;
+}
+
+static void do_shmem_file_read(struct file *filp, loff_t *ppos, read_descriptor_t *desc, read_actor_t actor)
+{
+	struct inode *inode = filp->f_dentry->d_inode;
+	struct address_space *mapping = inode->i_mapping;
+	unsigned long index, offset;
+
+	index = *ppos >> PAGE_CACHE_SHIFT;
+	offset = *ppos & ~PAGE_CACHE_MASK;
+
+	for (;;) {
+		struct page *page = NULL;
+		unsigned long end_index, nr, ret;
+		loff_t i_size = i_size_read(inode);
+
+		end_index = i_size >> PAGE_CACHE_SHIFT;
+		if (index > end_index)
+			break;
+		if (index == end_index) {
+			nr = i_size & ~PAGE_CACHE_MASK;
+			if (nr <= offset)
+				break;
+		}
+
+		desc->error = shmem_getpage(inode, index, &page, SGP_READ, NULL);
+		if (desc->error) {
+			if (desc->error == -EINVAL)
+				desc->error = 0;
+			break;
+		}
+
+		/*
+		 * We must evaluate after, since reads (unlike writes)
+		 * are called without i_sem protection against truncate
+		 */
+		nr = PAGE_CACHE_SIZE;
+		i_size = i_size_read(inode);
+		end_index = i_size >> PAGE_CACHE_SHIFT;
+		if (index == end_index) {
+			nr = i_size & ~PAGE_CACHE_MASK;
+			if (nr <= offset) {
+				if (page)
+					page_cache_release(page);
+				break;
+			}
+		}
+		nr -= offset;
+
+		if (page) {
+			/*
+			 * If users can be writing to this page using arbitrary
+			 * virtual addresses, take care about potential aliasing
+			 * before reading the page on the kernel side.
+			 */
+			if (mapping_writably_mapped(mapping))
+				flush_dcache_page(page);
+			/*
+			 * Mark the page accessed if we read the beginning.
+			 */
+			if (!offset)
+				mark_page_accessed(page);
+		} else {
+			page = ZERO_PAGE(0);
+			page_cache_get(page);
+		}
+
+		/*
+		 * Ok, we have the page, and it's up-to-date, so
+		 * now we can copy it to user space...
+		 *
+		 * The actor routine returns how many bytes were actually used..
+		 * NOTE! This may not be the same as how much of a user buffer
+		 * we filled up (we may be padding etc), so we can only update
+		 * "pos" here (the actor routine has to update the user buffer
+		 * pointers and the remaining count).
+		 */
+		ret = actor(desc, page, offset, nr);
+		offset += ret;
+		index += offset >> PAGE_CACHE_SHIFT;
+		offset &= ~PAGE_CACHE_MASK;
+
+		page_cache_release(page);
+		if (ret != nr || !desc->count)
+			break;
+
+		cond_resched();
+	}
+
+	*ppos = ((loff_t) index << PAGE_CACHE_SHIFT) + offset;
+	file_accessed(filp);
+}
+
+static ssize_t shmem_file_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
+{
+	read_descriptor_t desc;
+
+	if ((ssize_t) count < 0)
+		return -EINVAL;
+	if (!access_ok(VERIFY_WRITE, buf, count))
+		return -EFAULT;
+	if (!count)
+		return 0;
+
+	desc.written = 0;
+	desc.count = count;
+	desc.arg.buf = buf;
+	desc.error = 0;
+
+	do_shmem_file_read(filp, ppos, &desc, file_read_actor);
+	if (desc.written)
+		return desc.written;
+	return desc.error;
+}
+
+static ssize_t shmem_file_sendfile(struct file *in_file, loff_t *ppos,
+			 size_t count, read_actor_t actor, void *target)
+{
+	read_descriptor_t desc;
+
+	if (!count)
+		return 0;
+
+	desc.written = 0;
+	desc.count = count;
+	desc.arg.data = target;
+	desc.error = 0;
+
+	do_shmem_file_read(in_file, ppos, &desc, actor);
+	if (desc.written)
+		return desc.written;
+	return desc.error;
+}
+
+static int shmem_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+	struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
+
+	buf->f_type = TMPFS_MAGIC;
+	buf->f_bsize = PAGE_CACHE_SIZE;
+	buf->f_namelen = NAME_MAX;
+	spin_lock(&sbinfo->stat_lock);
+	if (sbinfo->max_blocks) {
+		buf->f_blocks = sbinfo->max_blocks;
+		buf->f_bavail = buf->f_bfree = sbinfo->free_blocks;
+	}
+	if (sbinfo->max_inodes) {
+		buf->f_files = sbinfo->max_inodes;
+		buf->f_ffree = sbinfo->free_inodes;
+	}
+	/* else leave those fields 0 like simple_statfs */
+	spin_unlock(&sbinfo->stat_lock);
+	return 0;
+}
+
+/*
+ * File creation. Allocate an inode, and we're done..
+ */
+static int
+shmem_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+{
+	struct inode *inode = shmem_get_inode(dir->i_sb, mode, dev);
+	int error = -ENOSPC;
+
+	if (inode) {
+		error = security_inode_init_security(inode, dir, NULL, NULL,
+						     NULL);
+		if (error) {
+			if (error != -EOPNOTSUPP) {
+				iput(inode);
+				return error;
+			}
+			error = 0;
+		}
+		if (dir->i_mode & S_ISGID) {
+			inode->i_gid = dir->i_gid;
+			if (S_ISDIR(mode))
+				inode->i_mode |= S_ISGID;
+		}
+		dir->i_size += BOGO_DIRENT_SIZE;
+		dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+		d_instantiate(dentry, inode);
+		dget(dentry); /* Extra count - pin the dentry in core */
+	}
+	return error;
+}
+
+static int shmem_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	int error;
+
+	if ((error = shmem_mknod(dir, dentry, mode | S_IFDIR, 0)))
+		return error;
+	dir->i_nlink++;
+	return 0;
+}
+
+static int shmem_create(struct inode *dir, struct dentry *dentry, int mode,
+		struct nameidata *nd)
+{
+	return shmem_mknod(dir, dentry, mode | S_IFREG, 0);
+}
+
+/*
+ * Link a file..
+ */
+static int shmem_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
+{
+	struct inode *inode = old_dentry->d_inode;
+	struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
+
+	/*
+	 * No ordinary (disk based) filesystem counts links as inodes;
+	 * but each new link needs a new dentry, pinning lowmem, and
+	 * tmpfs dentries cannot be pruned until they are unlinked.
+	 */
+	if (sbinfo->max_inodes) {
+		spin_lock(&sbinfo->stat_lock);
+		if (!sbinfo->free_inodes) {
+			spin_unlock(&sbinfo->stat_lock);
+			return -ENOSPC;
+		}
+		sbinfo->free_inodes--;
+		spin_unlock(&sbinfo->stat_lock);
+	}
+
+	dir->i_size += BOGO_DIRENT_SIZE;
+	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	inode->i_nlink++;
+	atomic_inc(&inode->i_count);	/* New dentry reference */
+	dget(dentry);		/* Extra pinning count for the created dentry */
+	d_instantiate(dentry, inode);
+	return 0;
+}
+
+static int shmem_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct inode *inode = dentry->d_inode;
+
+	if (inode->i_nlink > 1 && !S_ISDIR(inode->i_mode)) {
+		struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
+		if (sbinfo->max_inodes) {
+			spin_lock(&sbinfo->stat_lock);
+			sbinfo->free_inodes++;
+			spin_unlock(&sbinfo->stat_lock);
+		}
+	}
+
+	dir->i_size -= BOGO_DIRENT_SIZE;
+	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	inode->i_nlink--;
+	dput(dentry);	/* Undo the count from "create" - this does all the work */
+	return 0;
+}
+
+static int shmem_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	if (!simple_empty(dentry))
+		return -ENOTEMPTY;
+
+	dir->i_nlink--;
+	return shmem_unlink(dir, dentry);
+}
+
+/*
+ * The VFS layer already does all the dentry stuff for rename,
+ * we just have to decrement the usage count for the target if
+ * it exists so that the VFS layer correctly free's it when it
+ * gets overwritten.
+ */
+static int shmem_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry)
+{
+	struct inode *inode = old_dentry->d_inode;
+	int they_are_dirs = S_ISDIR(inode->i_mode);
+
+	if (!simple_empty(new_dentry))
+		return -ENOTEMPTY;
+
+	if (new_dentry->d_inode) {
+		(void) shmem_unlink(new_dir, new_dentry);
+		if (they_are_dirs)
+			old_dir->i_nlink--;
+	} else if (they_are_dirs) {
+		old_dir->i_nlink--;
+		new_dir->i_nlink++;
+	}
+
+	old_dir->i_size -= BOGO_DIRENT_SIZE;
+	new_dir->i_size += BOGO_DIRENT_SIZE;
+	old_dir->i_ctime = old_dir->i_mtime =
+	new_dir->i_ctime = new_dir->i_mtime =
+	inode->i_ctime = CURRENT_TIME;
+	return 0;
+}
+
+static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+{
+	int error;
+	int len;
+	struct inode *inode;
+	struct page *page = NULL;
+	char *kaddr;
+	struct shmem_inode_info *info;
+
+	len = strlen(symname) + 1;
+	if (len > PAGE_CACHE_SIZE)
+		return -ENAMETOOLONG;
+
+	inode = shmem_get_inode(dir->i_sb, S_IFLNK|S_IRWXUGO, 0);
+	if (!inode)
+		return -ENOSPC;
+
+	error = security_inode_init_security(inode, dir, NULL, NULL,
+					     NULL);
+	if (error) {
+		if (error != -EOPNOTSUPP) {
+			iput(inode);
+			return error;
+		}
+		error = 0;
+	}
+
+	info = SHMEM_I(inode);
+	inode->i_size = len-1;
+	if (len <= (char *)inode - (char *)info) {
+		/* do it inline */
+		memcpy(info, symname, len);
+		inode->i_op = &shmem_symlink_inline_operations;
+	} else {
+		error = shmem_getpage(inode, 0, &page, SGP_WRITE, NULL);
+		if (error) {
+			iput(inode);
+			return error;
+		}
+		inode->i_op = &shmem_symlink_inode_operations;
+		kaddr = kmap_atomic(page, KM_USER0);
+		memcpy(kaddr, symname, len);
+		kunmap_atomic(kaddr, KM_USER0);
+		set_page_dirty(page);
+		page_cache_release(page);
+	}
+	if (dir->i_mode & S_ISGID)
+		inode->i_gid = dir->i_gid;
+	dir->i_size += BOGO_DIRENT_SIZE;
+	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	d_instantiate(dentry, inode);
+	dget(dentry);
+	return 0;
+}
+
+static void *shmem_follow_link_inline(struct dentry *dentry, struct nameidata *nd)
+{
+	nd_set_link(nd, (char *)SHMEM_I(dentry->d_inode));
+	return NULL;
+}
+
+static void *shmem_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	struct page *page = NULL;
+	int res = shmem_getpage(dentry->d_inode, 0, &page, SGP_READ, NULL);
+	nd_set_link(nd, res ? ERR_PTR(res) : kmap(page));
+	return page;
+}
+
+static void shmem_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
+{
+	if (!IS_ERR(nd_get_link(nd))) {
+		struct page *page = cookie;
+		kunmap(page);
+		mark_page_accessed(page);
+		page_cache_release(page);
+	}
+}
+
+static struct inode_operations shmem_symlink_inline_operations = {
+	.readlink	= generic_readlink,
+	.follow_link	= shmem_follow_link_inline,
+};
+
+static struct inode_operations shmem_symlink_inode_operations = {
+	.truncate	= shmem_truncate,
+	.readlink	= generic_readlink,
+	.follow_link	= shmem_follow_link,
+	.put_link	= shmem_put_link,
+};
+
+static int shmem_parse_options(char *options, int *mode, uid_t *uid, gid_t *gid, unsigned long *blocks, unsigned long *inodes)
+{
+	char *this_char, *value, *rest;
+
+	while ((this_char = strsep(&options, ",")) != NULL) {
+		if (!*this_char)
+			continue;
+		if ((value = strchr(this_char,'=')) != NULL) {
+			*value++ = 0;
+		} else {
+			printk(KERN_ERR
+			    "tmpfs: No value for mount option '%s'\n",
+			    this_char);
+			return 1;
+		}
+
+		if (!strcmp(this_char,"size")) {
+			unsigned long long size;
+			size = memparse(value,&rest);
+			if (*rest == '%') {
+				size <<= PAGE_SHIFT;
+				size *= totalram_pages;
+				do_div(size, 100);
+				rest++;
+			}
+			if (*rest)
+				goto bad_val;
+			*blocks = size >> PAGE_CACHE_SHIFT;
+		} else if (!strcmp(this_char,"nr_blocks")) {
+			*blocks = memparse(value,&rest);
+			if (*rest)
+				goto bad_val;
+		} else if (!strcmp(this_char,"nr_inodes")) {
+			*inodes = memparse(value,&rest);
+			if (*rest)
+				goto bad_val;
+		} else if (!strcmp(this_char,"mode")) {
+			if (!mode)
+				continue;
+			*mode = simple_strtoul(value,&rest,8);
+			if (*rest)
+				goto bad_val;
+		} else if (!strcmp(this_char,"uid")) {
+			if (!uid)
+				continue;
+			*uid = simple_strtoul(value,&rest,0);
+			if (*rest)
+				goto bad_val;
+		} else if (!strcmp(this_char,"gid")) {
+			if (!gid)
+				continue;
+			*gid = simple_strtoul(value,&rest,0);
+			if (*rest)
+				goto bad_val;
+		} else {
+			printk(KERN_ERR "tmpfs: Bad mount option %s\n",
+			       this_char);
+			return 1;
+		}
+	}
+	return 0;
+
+bad_val:
+	printk(KERN_ERR "tmpfs: Bad value '%s' for mount option '%s'\n",
+	       value, this_char);
+	return 1;
+
+}
+
+static int shmem_remount_fs(struct super_block *sb, int *flags, char *data)
+{
+	struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
+	unsigned long max_blocks = sbinfo->max_blocks;
+	unsigned long max_inodes = sbinfo->max_inodes;
+	unsigned long blocks;
+	unsigned long inodes;
+	int error = -EINVAL;
+
+	if (shmem_parse_options(data, NULL, NULL, NULL,
+				&max_blocks, &max_inodes))
+		return error;
+
+	spin_lock(&sbinfo->stat_lock);
+	blocks = sbinfo->max_blocks - sbinfo->free_blocks;
+	inodes = sbinfo->max_inodes - sbinfo->free_inodes;
+	if (max_blocks < blocks)
+		goto out;
+	if (max_inodes < inodes)
+		goto out;
+	/*
+	 * Those tests also disallow limited->unlimited while any are in
+	 * use, so i_blocks will always be zero when max_blocks is zero;
+	 * but we must separately disallow unlimited->limited, because
+	 * in that case we have no record of how much is already in use.
+	 */
+	if (max_blocks && !sbinfo->max_blocks)
+		goto out;
+	if (max_inodes && !sbinfo->max_inodes)
+		goto out;
+
+	error = 0;
+	sbinfo->max_blocks  = max_blocks;
+	sbinfo->free_blocks = max_blocks - blocks;
+	sbinfo->max_inodes  = max_inodes;
+	sbinfo->free_inodes = max_inodes - inodes;
+out:
+	spin_unlock(&sbinfo->stat_lock);
+	return error;
+}
+#endif
+
+static void shmem_put_super(struct super_block *sb)
+{
+	kfree(sb->s_fs_info);
+	sb->s_fs_info = NULL;
+}
+
+static int shmem_fill_super(struct super_block *sb,
+			    void *data, int silent)
+{
+	struct inode *inode;
+	struct dentry *root;
+	int mode   = S_IRWXUGO | S_ISVTX;
+	uid_t uid = current->fsuid;
+	gid_t gid = current->fsgid;
+	int err = -ENOMEM;
+	struct shmem_sb_info *sbinfo;
+	unsigned long blocks = 0;
+	unsigned long inodes = 0;
+
+#ifdef CONFIG_TMPFS
+	/*
+	 * Per default we only allow half of the physical ram per
+	 * tmpfs instance, limiting inodes to one per page of lowmem;
+	 * but the internal instance is left unlimited.
+	 */
+	if (!(sb->s_flags & MS_NOUSER)) {
+		blocks = totalram_pages / 2;
+		inodes = totalram_pages - totalhigh_pages;
+		if (inodes > blocks)
+			inodes = blocks;
+		if (shmem_parse_options(data, &mode, &uid, &gid,
+					&blocks, &inodes))
+			return -EINVAL;
+	}
+#else
+	sb->s_flags |= MS_NOUSER;
+#endif
+
+	/* Round up to L1_CACHE_BYTES to resist false sharing */
+	sbinfo = kmalloc(max((int)sizeof(struct shmem_sb_info),
+				L1_CACHE_BYTES), GFP_KERNEL);
+	if (!sbinfo)
+		return -ENOMEM;
+
+	spin_lock_init(&sbinfo->stat_lock);
+	sbinfo->max_blocks = blocks;
+	sbinfo->free_blocks = blocks;
+	sbinfo->max_inodes = inodes;
+	sbinfo->free_inodes = inodes;
+
+	sb->s_fs_info = sbinfo;
+	sb->s_maxbytes = SHMEM_MAX_BYTES;
+	sb->s_blocksize = PAGE_CACHE_SIZE;
+	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+	sb->s_magic = TMPFS_MAGIC;
+	sb->s_op = &shmem_ops;
+
+	inode = shmem_get_inode(sb, S_IFDIR | mode, 0);
+	if (!inode)
+		goto failed;
+	inode->i_uid = uid;
+	inode->i_gid = gid;
+	root = d_alloc_root(inode);
+	if (!root)
+		goto failed_iput;
+	sb->s_root = root;
+	return 0;
+
+failed_iput:
+	iput(inode);
+failed:
+	shmem_put_super(sb);
+	return err;
+}
+
+static kmem_cache_t *shmem_inode_cachep;
+
+static struct inode *shmem_alloc_inode(struct super_block *sb)
+{
+	struct shmem_inode_info *p;
+	p = (struct shmem_inode_info *)kmem_cache_alloc(shmem_inode_cachep, SLAB_KERNEL);
+	if (!p)
+		return NULL;
+	return &p->vfs_inode;
+}
+
+static void shmem_destroy_inode(struct inode *inode)
+{
+	if ((inode->i_mode & S_IFMT) == S_IFREG) {
+		/* only struct inode is valid if it's an inline symlink */
+		mpol_free_shared_policy(&SHMEM_I(inode)->policy);
+	}
+	kmem_cache_free(shmem_inode_cachep, SHMEM_I(inode));
+}
+
+static void init_once(void *foo, kmem_cache_t *cachep, unsigned long flags)
+{
+	struct shmem_inode_info *p = (struct shmem_inode_info *) foo;
+
+	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR) {
+		inode_init_once(&p->vfs_inode);
+	}
+}
+
+static int init_inodecache(void)
+{
+	shmem_inode_cachep = kmem_cache_create("shmem_inode_cache",
+				sizeof(struct shmem_inode_info),
+				0, 0, init_once, NULL);
+	if (shmem_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static void destroy_inodecache(void)
+{
+	if (kmem_cache_destroy(shmem_inode_cachep))
+		printk(KERN_INFO "shmem_inode_cache: not all structures were freed\n");
+}
+
+static struct address_space_operations shmem_aops = {
+	.writepage	= shmem_writepage,
+	.set_page_dirty	= __set_page_dirty_nobuffers,
+#ifdef CONFIG_TMPFS
+	.prepare_write	= shmem_prepare_write,
+	.commit_write	= simple_commit_write,
+#endif
+};
+
+static struct file_operations shmem_file_operations = {
+	.mmap		= shmem_mmap,
+#ifdef CONFIG_TMPFS
+	.llseek		= generic_file_llseek,
+	.read		= shmem_file_read,
+	.write		= shmem_file_write,
+	.fsync		= simple_sync_file,
+	.sendfile	= shmem_file_sendfile,
+#endif
+};
+
+static struct inode_operations shmem_inode_operations = {
+	.truncate	= shmem_truncate,
+	.setattr	= shmem_notify_change,
+};
+
+static struct inode_operations shmem_dir_inode_operations = {
+#ifdef CONFIG_TMPFS
+	.create		= shmem_create,
+	.lookup		= simple_lookup,
+	.link		= shmem_link,
+	.unlink		= shmem_unlink,
+	.symlink	= shmem_symlink,
+	.mkdir		= shmem_mkdir,
+	.rmdir		= shmem_rmdir,
+	.mknod		= shmem_mknod,
+	.rename		= shmem_rename,
+#endif
+};
+
+static struct super_operations shmem_ops = {
+	.alloc_inode	= shmem_alloc_inode,
+	.destroy_inode	= shmem_destroy_inode,
+#ifdef CONFIG_TMPFS
+	.statfs		= shmem_statfs,
+	.remount_fs	= shmem_remount_fs,
+#endif
+	.delete_inode	= shmem_delete_inode,
+	.drop_inode	= generic_delete_inode,
+	.put_super	= shmem_put_super,
+};
+
+static struct vm_operations_struct shmem_vm_ops = {
+	.nopage		= shmem_nopage,
+	.populate	= shmem_populate,
+#ifdef CONFIG_NUMA
+	.set_policy     = shmem_set_policy,
+	.get_policy     = shmem_get_policy,
+#endif
+};
+
+
+static struct super_block *shmem_get_sb(struct file_system_type *fs_type,
+	int flags, const char *dev_name, void *data)
+{
+	return get_sb_nodev(fs_type, flags, data, shmem_fill_super);
+}
+
+static struct file_system_type tmpfs_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "tmpfs",
+	.get_sb		= shmem_get_sb,
+	.kill_sb	= kill_litter_super,
+};
+static struct vfsmount *shm_mnt;
+
+static int __init init_tmpfs(void)
+{
+	int error;
+
+	error = init_inodecache();
+	if (error)
+		goto out3;
+
+	error = register_filesystem(&tmpfs_fs_type);
+	if (error) {
+		printk(KERN_ERR "Could not register tmpfs\n");
+		goto out2;
+	}
+#ifdef CONFIG_TMPFS
+	devfs_mk_dir("shm");
+#endif
+	shm_mnt = do_kern_mount(tmpfs_fs_type.name, MS_NOUSER,
+				tmpfs_fs_type.name, NULL);
+	if (IS_ERR(shm_mnt)) {
+		error = PTR_ERR(shm_mnt);
+		printk(KERN_ERR "Could not kern_mount tmpfs\n");
+		goto out1;
+	}
+	return 0;
+
+out1:
+	unregister_filesystem(&tmpfs_fs_type);
+out2:
+	destroy_inodecache();
+out3:
+	shm_mnt = ERR_PTR(error);
+	return error;
+}
+module_init(init_tmpfs)
+
+/*
+ * shmem_file_setup - get an unlinked file living in tmpfs
+ *
+ * @name: name for dentry (to be seen in /proc/<pid>/maps
+ * @size: size to be set for the file
+ *
+ */
+struct file *shmem_file_setup(char *name, loff_t size, unsigned long flags)
+{
+	int error;
+	struct file *file;
+	struct inode *inode;
+	struct dentry *dentry, *root;
+	struct qstr this;
+
+	if (IS_ERR(shm_mnt))
+		return (void *)shm_mnt;
+
+	if (size < 0 || size > SHMEM_MAX_BYTES)
+		return ERR_PTR(-EINVAL);
+
+	if (shmem_acct_size(flags, size))
+		return ERR_PTR(-ENOMEM);
+
+	error = -ENOMEM;
+	this.name = name;
+	this.len = strlen(name);
+	this.hash = 0; /* will go */
+	root = shm_mnt->mnt_root;
+	dentry = d_alloc(root, &this);
+	if (!dentry)
+		goto put_memory;
+
+	error = -ENFILE;
+	file = get_empty_filp();
+	if (!file)
+		goto put_dentry;
+
+	error = -ENOSPC;
+	inode = shmem_get_inode(root->d_sb, S_IFREG | S_IRWXUGO, 0);
+	if (!inode)
+		goto close_file;
+
+	SHMEM_I(inode)->flags = flags & VM_ACCOUNT;
+	d_instantiate(dentry, inode);
+	inode->i_size = size;
+	inode->i_nlink = 0;	/* It is unlinked */
+	file->f_vfsmnt = mntget(shm_mnt);
+	file->f_dentry = dentry;
+	file->f_mapping = inode->i_mapping;
+	file->f_op = &shmem_file_operations;
+	file->f_mode = FMODE_WRITE | FMODE_READ;
+	return file;
+
+close_file:
+	put_filp(file);
+put_dentry:
+	dput(dentry);
+put_memory:
+	shmem_unacct_size(flags, size);
+	return ERR_PTR(error);
+}
+
+/*
+ * shmem_zero_setup - setup a shared anonymous mapping
+ *
+ * @vma: the vma to be mmapped is prepared by do_mmap_pgoff
+ */
+int shmem_zero_setup(struct vm_area_struct *vma)
+{
+	struct file *file;
+	loff_t size = vma->vm_end - vma->vm_start;
+
+	file = shmem_file_setup("dev/zero", size, vma->vm_flags);
+	if (IS_ERR(file))
+		return PTR_ERR(file);
+
+	if (vma->vm_file)
+		fput(vma->vm_file);
+	vma->vm_file = file;
+	vma->vm_ops = &shmem_vm_ops;
+	return 0;
+}
diff -urN oldtree/net/bluetooth/hci_event.c newtree/net/bluetooth/hci_event.c
--- oldtree/net/bluetooth/hci_event.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/net/bluetooth/hci_event.c	2006-02-13 19:20:01.289316296 -0500
@@ -322,7 +322,7 @@
 		hdev->acl_mtu  = __le16_to_cpu(bs->acl_mtu);
 		hdev->sco_mtu  = bs->sco_mtu ? bs->sco_mtu : 64;
 		hdev->acl_pkts = hdev->acl_cnt = __le16_to_cpu(bs->acl_max_pkt);
-		hdev->sco_pkts = hdev->sco_cnt = __le16_to_cpu(bs->sco_max_pkt);
+		hdev->sco_pkts = hdev->sco_cnt = (bs->sco_max_pkt ? __le16_to_cpu(bs->sco_max_pkt) : 8);
 
 		BT_DBG("%s mtu: acl %d, sco %d max_pkt: acl %d, sco %d", hdev->name,
 			hdev->acl_mtu, hdev->sco_mtu, hdev->acl_pkts, hdev->sco_pkts);
diff -urN oldtree/net/bridge/br_if.c newtree/net/bridge/br_if.c
--- oldtree/net/bridge/br_if.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/net/bridge/br_if.c	2006-02-13 19:20:30.102935960 -0500
@@ -99,7 +99,6 @@
 	struct net_bridge *br = p->br;
 	struct net_device *dev = p->dev;
 
-	dev->br_port = NULL;
 	dev_set_promiscuity(dev, -1);
 
 	spin_lock_bh(&br->lock);
@@ -110,9 +109,7 @@
 
 	list_del_rcu(&p->list);
 
-	del_timer_sync(&p->message_age_timer);
-	del_timer_sync(&p->forward_delay_timer);
-	del_timer_sync(&p->hold_timer);
+	rcu_assign_pointer(dev->br_port, NULL);
 	
 	call_rcu(&p->rcu, destroy_nbp_rcu);
 }
@@ -217,7 +214,6 @@
 	p->dev = dev;
 	p->path_cost = cost;
  	p->priority = 0x8000 >> BR_PORT_BITS;
-	dev->br_port = p;
 	p->port_no = index;
 	br_init_port(p);
 	p->state = BR_STATE_DISABLED;
@@ -360,6 +356,7 @@
 	else if ((err = br_sysfs_addif(p)))
 		del_nbp(p);
 	else {
+		rcu_assign_pointer(dev->br_port, p);
 		dev_set_promiscuity(dev, 1);
 
 		list_add_rcu(&p->list, &br->port_list);
diff -urN oldtree/net/bridge/br_input.c newtree/net/bridge/br_input.c
--- oldtree/net/bridge/br_input.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/net/bridge/br_input.c	2006-02-13 19:20:30.102935960 -0500
@@ -45,11 +45,17 @@
 int br_handle_frame_finish(struct sk_buff *skb)
 {
 	const unsigned char *dest = eth_hdr(skb)->h_dest;
-	struct net_bridge_port *p = skb->dev->br_port;
-	struct net_bridge *br = p->br;
+	struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
+	struct net_bridge *br;
 	struct net_bridge_fdb_entry *dst;
 	int passedup = 0;
 
+	if (unlikely(!p || p->state == BR_STATE_DISABLED)) {
+		kfree_skb(skb);
+		return 0;
+	}
+
+	br = p->br;
 	/* insert into forwarding database after filtering to avoid spoofing */
 	br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
 
diff -urN oldtree/net/bridge/br_netfilter.c newtree/net/bridge/br_netfilter.c
--- oldtree/net/bridge/br_netfilter.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/net/bridge/br_netfilter.c	2006-02-13 19:20:30.103935808 -0500
@@ -47,9 +47,6 @@
 #define store_orig_dstaddr(skb)	 (skb_origaddr(skb) = (skb)->nh.iph->daddr)
 #define dnat_took_place(skb)	 (skb_origaddr(skb) != (skb)->nh.iph->daddr)
 
-#define has_bridge_parent(device)	((device)->br_port != NULL)
-#define bridge_parent(device)		((device)->br_port->br->dev)
-
 #ifdef CONFIG_SYSCTL
 static struct ctl_table_header *brnf_sysctl_header;
 static int brnf_call_iptables = 1;
@@ -94,6 +91,12 @@
 	.rt_flags	= 0,
 };
 
+static inline struct net_device *bridge_parent(const struct net_device *dev)
+{
+	struct net_bridge_port *port = rcu_dereference(dev->br_port);
+
+	return port ? port->br->dev : NULL;
+}
 
 /* PF_BRIDGE/PRE_ROUTING *********************************************/
 /* Undo the changes made for ip6tables PREROUTING and continue the
@@ -185,11 +188,15 @@
 	skb->nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
 
 	skb->dev = bridge_parent(skb->dev);
-	if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
-		skb_pull(skb, VLAN_HLEN);
-		skb->nh.raw += VLAN_HLEN;
+	if (!skb->dev)
+		kfree_skb(skb);
+	else {
+		if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
+			skb_pull(skb, VLAN_HLEN);
+			skb->nh.raw += VLAN_HLEN;
+		}
+		skb->dst->output(skb);
 	}
-	skb->dst->output(skb);
 	return 0;
 }
 
@@ -266,7 +273,7 @@
 }
 
 /* Some common code for IPv4/IPv6 */
-static void setup_pre_routing(struct sk_buff *skb)
+static struct net_device *setup_pre_routing(struct sk_buff *skb)
 {
 	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
 
@@ -278,6 +285,8 @@
 	nf_bridge->mask |= BRNF_NF_BRIDGE_PREROUTING;
 	nf_bridge->physindev = skb->dev;
 	skb->dev = bridge_parent(skb->dev);
+
+	return skb->dev;
 }
 
 /* We only check the length. A bridge shouldn't do any hop-by-hop stuff anyway */
@@ -372,7 +381,8 @@
  	nf_bridge_put(skb->nf_bridge);
 	if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
 		return NF_DROP;
-	setup_pre_routing(skb);
+	if (!setup_pre_routing(skb))
+		return NF_DROP;
 
 	NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL,
 		br_nf_pre_routing_finish_ipv6);
@@ -409,7 +419,6 @@
 
 		if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
 			skb_pull(skb, VLAN_HLEN);
-			(skb)->nh.raw += VLAN_HLEN;
 		}
 		return br_nf_pre_routing_ipv6(hook, skb, in, out, okfn);
 	}
@@ -426,7 +435,6 @@
 
 	if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
 		skb_pull(skb, VLAN_HLEN);
-		(skb)->nh.raw += VLAN_HLEN;
 	}
 
 	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
@@ -456,7 +464,8 @@
  	nf_bridge_put(skb->nf_bridge);
 	if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
 		return NF_DROP;
-	setup_pre_routing(skb);
+	if (!setup_pre_routing(skb))
+		return NF_DROP;
 	store_orig_dstaddr(skb);
 
 	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
@@ -530,11 +539,16 @@
 	struct sk_buff *skb = *pskb;
 	struct nf_bridge_info *nf_bridge;
 	struct vlan_ethhdr *hdr = vlan_eth_hdr(skb);
+	struct net_device *parent;
 	int pf;
 
 	if (!skb->nf_bridge)
 		return NF_ACCEPT;
 
+	parent = bridge_parent(out);
+	if (!parent)
+		return NF_DROP;
+
 	if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP)
 		pf = PF_INET;
 	else
@@ -555,8 +569,8 @@
 	nf_bridge->mask |= BRNF_BRIDGED;
 	nf_bridge->physoutdev = skb->dev;
 
-	NF_HOOK(pf, NF_IP_FORWARD, skb, bridge_parent(in),
-		bridge_parent(out), br_nf_forward_finish);
+	NF_HOOK(pf, NF_IP_FORWARD, skb, bridge_parent(in), parent,
+		br_nf_forward_finish);
 
 	return NF_STOLEN;
 }
@@ -679,6 +693,8 @@
 		goto out;
 	}
 	realoutdev = bridge_parent(skb->dev);
+	if (!realoutdev)
+		return NF_DROP;
 
 #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
 	/* iptables should match -o br0.x */
@@ -692,9 +708,11 @@
 	/* IP forwarded traffic has a physindev, locally
 	 * generated traffic hasn't. */
 	if (realindev != NULL) {
-		if (!(nf_bridge->mask & BRNF_DONT_TAKE_PARENT) &&
-		    has_bridge_parent(realindev))
-			realindev = bridge_parent(realindev);
+		if (!(nf_bridge->mask & BRNF_DONT_TAKE_PARENT) ) {
+			struct net_device *parent = bridge_parent(realindev);
+			if (parent)
+				realindev = parent;
+		}
 
 		NF_HOOK_THRESH(pf, NF_IP_FORWARD, skb, realindev,
 			       realoutdev, br_nf_local_out_finish,
@@ -734,6 +752,9 @@
 	if (!nf_bridge)
 		return NF_ACCEPT;
 
+	if (!realoutdev)
+		return NF_DROP;
+
 	if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP)
 		pf = PF_INET;
 	else
diff -urN oldtree/net/bridge/br_stp_bpdu.c newtree/net/bridge/br_stp_bpdu.c
--- oldtree/net/bridge/br_stp_bpdu.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/net/bridge/br_stp_bpdu.c	2006-02-13 19:20:30.105935504 -0500
@@ -136,10 +136,13 @@
 /* NO locks */
 int br_stp_handle_bpdu(struct sk_buff *skb)
 {
-	struct net_bridge_port *p = skb->dev->br_port;
-	struct net_bridge *br = p->br;
+	struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
+	struct net_bridge *br;
 	unsigned char *buf;
 
+	if (!p)
+		goto err;
+
 	/* insert into forwarding database after filtering to avoid spoofing */
 	br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
 
@@ -150,6 +153,7 @@
 
 	buf = skb_pull(skb, sizeof(header));
 
+	br = p->br;
 	spin_lock_bh(&br->lock);
 	if (p->state == BR_STATE_DISABLED 
 	    || !(br->dev->flags & IFF_UP)
diff -urN oldtree/net/bridge/br_stp_if.c newtree/net/bridge/br_stp_if.c
--- oldtree/net/bridge/br_stp_if.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/net/bridge/br_stp_if.c	2006-02-13 19:20:30.105935504 -0500
@@ -158,7 +158,7 @@
 
 	list_for_each_entry(p, &br->port_list, list) {
 		if (addr == br_mac_zero ||
-		    compare_ether_addr(p->dev->dev_addr, addr) < 0)
+		    memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0)
 			addr = p->dev->dev_addr;
 
 	}
diff -urN oldtree/net/bridge/netfilter/ebt_ip.c newtree/net/bridge/netfilter/ebt_ip.c
--- oldtree/net/bridge/netfilter/ebt_ip.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/net/bridge/netfilter/ebt_ip.c	2006-02-13 19:20:30.105935504 -0500
@@ -15,6 +15,7 @@
 #include <linux/netfilter_bridge/ebtables.h>
 #include <linux/netfilter_bridge/ebt_ip.h>
 #include <linux/ip.h>
+#include <net/ip.h>
 #include <linux/in.h>
 #include <linux/module.h>
 
@@ -51,6 +52,8 @@
 		if (!(info->bitmask & EBT_IP_DPORT) &&
 		    !(info->bitmask & EBT_IP_SPORT))
 			return EBT_MATCH;
+		if (ntohs(ih->frag_off) & IP_OFFSET)
+			return EBT_NOMATCH;
 		pptr = skb_header_pointer(skb, ih->ihl*4,
 					  sizeof(_ports), &_ports);
 		if (pptr == NULL)
diff -urN oldtree/net/bridge/netfilter/ebt_stp.c newtree/net/bridge/netfilter/ebt_stp.c
--- oldtree/net/bridge/netfilter/ebt_stp.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/net/bridge/netfilter/ebt_stp.c	2006-02-13 19:20:01.290316144 -0500
@@ -11,6 +11,7 @@
 #include <linux/netfilter_bridge/ebtables.h>
 #include <linux/netfilter_bridge/ebt_stp.h>
 #include <linux/module.h>
+#include <linux/etherdevice.h>
 
 #define BPDU_TYPE_CONFIG 0
 #define BPDU_TYPE_TCN 0x80
diff -urN oldtree/net/core/net-sysfs.c newtree/net/core/net-sysfs.c
--- oldtree/net/core/net-sysfs.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/net/core/net-sysfs.c	2006-02-13 19:20:30.106935352 -0500
@@ -16,6 +16,7 @@
 #include <net/sock.h>
 #include <linux/rtnetlink.h>
 #include <linux/wireless.h>
+#include <net/iw_handler.h>
 
 #define to_class_dev(obj) container_of(obj,struct class_device,kobj)
 #define to_net_dev(class) container_of(class, struct net_device, class_dev)
@@ -313,13 +314,19 @@
 					       char *))
 {
 	struct net_device *dev = to_net_dev(cd);
-	const struct iw_statistics *iw;
+	const struct iw_statistics *iw = NULL;
 	ssize_t ret = -EINVAL;
 	
 	read_lock(&dev_base_lock);
-	if (dev_isalive(dev) && dev->get_wireless_stats 
-	    && (iw = dev->get_wireless_stats(dev)) != NULL) 
-		ret = (*format)(iw, buf);
+	if (dev_isalive(dev)) {
+		if(dev->wireless_handlers &&
+		   dev->wireless_handlers->get_wireless_stats)
+			iw = dev->wireless_handlers->get_wireless_stats(dev);
+		else if (dev->get_wireless_stats)
+			iw = dev->get_wireless_stats(dev);
+		if (iw != NULL)
+			ret = (*format)(iw, buf);
+	}
 	read_unlock(&dev_base_lock);
 
 	return ret;
@@ -420,7 +427,8 @@
 		sysfs_remove_group(&class_dev->kobj, &netstat_group);
 
 #ifdef WIRELESS_EXT
-	if (net->get_wireless_stats)
+	if (net->get_wireless_stats || (net->wireless_handlers &&
+			net->wireless_handlers->get_wireless_stats))
 		sysfs_remove_group(&class_dev->kobj, &wireless_group);
 #endif
 	class_device_del(class_dev);
@@ -453,10 +461,12 @@
 		goto out_unreg; 
 
 #ifdef WIRELESS_EXT
-	if (net->get_wireless_stats &&
-	    (ret = sysfs_create_group(&class_dev->kobj, &wireless_group)))
-		goto out_cleanup; 
-
+	if (net->get_wireless_stats || (net->wireless_handlers &&
+			net->wireless_handlers->get_wireless_stats)) {
+		ret = sysfs_create_group(&class_dev->kobj, &wireless_group);
+		if (ret)
+			goto out_cleanup;
+	}
 	return 0;
 out_cleanup:
 	if (net->get_stats)
diff -urN oldtree/net/ipv4/icmp.c newtree/net/ipv4/icmp.c
--- oldtree/net/ipv4/icmp.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/net/ipv4/icmp.c	2006-02-13 19:20:30.108935048 -0500
@@ -524,7 +524,7 @@
 					  iph->tos;
 
 	if (ip_options_echo(&icmp_param.replyopts, skb_in))
-		goto ende;
+		goto out_unlock;
 
 
 	/*
diff -urN oldtree/net/ipv4/netfilter/ip_nat_helper_pptp.c newtree/net/ipv4/netfilter/ip_nat_helper_pptp.c
--- oldtree/net/ipv4/netfilter/ip_nat_helper_pptp.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/net/ipv4/netfilter/ip_nat_helper_pptp.c	2006-02-13 19:20:30.108935048 -0500
@@ -148,14 +148,14 @@
 {
 	struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info;
 	struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
-
-	u_int16_t msg, *cid = NULL, new_callid;
+	u_int16_t msg, new_callid;
+	unsigned int cid_off;
 
 	new_callid = htons(ct_pptp_info->pns_call_id);
 	
 	switch (msg = ntohs(ctlh->messageType)) {
 		case PPTP_OUT_CALL_REQUEST:
-			cid = &pptpReq->ocreq.callID;
+			cid_off = offsetof(union pptp_ctrl_union, ocreq.callID);
 			/* FIXME: ideally we would want to reserve a call ID
 			 * here.  current netfilter NAT core is not able to do
 			 * this :( For now we use TCP source port. This breaks
@@ -172,10 +172,10 @@
 			ct_pptp_info->pns_call_id = ntohs(new_callid);
 			break;
 		case PPTP_IN_CALL_REPLY:
-			cid = &pptpReq->icreq.callID;
+			cid_off = offsetof(union pptp_ctrl_union, icreq.callID);
 			break;
 		case PPTP_CALL_CLEAR_REQUEST:
-			cid = &pptpReq->clrreq.callID;
+			cid_off = offsetof(union pptp_ctrl_union, clrreq.callID);
 			break;
 		default:
 			DEBUGP("unknown outbound packet 0x%04x:%s\n", msg,
@@ -197,18 +197,15 @@
 
 	/* only OUT_CALL_REQUEST, IN_CALL_REPLY, CALL_CLEAR_REQUEST pass
 	 * down to here */
-
-	IP_NF_ASSERT(cid);
-
 	DEBUGP("altering call id from 0x%04x to 0x%04x\n",
-		ntohs(*cid), ntohs(new_callid));
+		ntohs(*(u_int16_t *)pptpReq + cid_off), ntohs(new_callid));
 
 	/* mangle packet */
 	if (ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
-		(void *)cid - ((void *)ctlh - sizeof(struct pptp_pkt_hdr)),
-				 	sizeof(new_callid), 
-					(char *)&new_callid,
-				 	sizeof(new_callid)) == 0)
+	                             cid_off + sizeof(struct pptp_pkt_hdr) +
+	                             sizeof(struct PptpControlHeader),
+	                             sizeof(new_callid), (char *)&new_callid,
+	                             sizeof(new_callid)) == 0)
 		return NF_DROP;
 
 	return NF_ACCEPT;
@@ -299,7 +296,8 @@
 		 union pptp_ctrl_union *pptpReq)
 {
 	struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info;
-	u_int16_t msg, new_cid = 0, new_pcid, *pcid = NULL, *cid = NULL;
+	u_int16_t msg, new_cid = 0, new_pcid;
+	unsigned int pcid_off, cid_off = 0;
 
 	int ret = NF_ACCEPT, rv;
 
@@ -307,23 +305,23 @@
 
 	switch (msg = ntohs(ctlh->messageType)) {
 	case PPTP_OUT_CALL_REPLY:
-		pcid = &pptpReq->ocack.peersCallID;	
-		cid = &pptpReq->ocack.callID;
+		pcid_off = offsetof(union pptp_ctrl_union, ocack.peersCallID);
+		cid_off = offsetof(union pptp_ctrl_union, ocack.callID);
 		break;
 	case PPTP_IN_CALL_CONNECT:
-		pcid = &pptpReq->iccon.peersCallID;
+		pcid_off = offsetof(union pptp_ctrl_union, iccon.peersCallID);
 		break;
 	case PPTP_IN_CALL_REQUEST:
 		/* only need to nat in case PAC is behind NAT box */
-		break;
+		return NF_ACCEPT;
 	case PPTP_WAN_ERROR_NOTIFY:
-		pcid = &pptpReq->wanerr.peersCallID;
+		pcid_off = offsetof(union pptp_ctrl_union, wanerr.peersCallID);
 		break;
 	case PPTP_CALL_DISCONNECT_NOTIFY:
-		pcid = &pptpReq->disc.callID;
+		pcid_off = offsetof(union pptp_ctrl_union, disc.callID);
 		break;
 	case PPTP_SET_LINK_INFO:
-		pcid = &pptpReq->setlink.peersCallID;
+		pcid_off = offsetof(union pptp_ctrl_union, setlink.peersCallID);
 		break;
 
 	default:
@@ -345,25 +343,24 @@
 	 * WAN_ERROR_NOTIFY, CALL_DISCONNECT_NOTIFY pass down here */
 
 	/* mangle packet */
-	IP_NF_ASSERT(pcid);
 	DEBUGP("altering peer call id from 0x%04x to 0x%04x\n",
-		ntohs(*pcid), ntohs(new_pcid));
+		ntohs(*(u_int16_t *)pptpReq + pcid_off), ntohs(new_pcid));
 	
-	rv = ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, 
-				      (void *)pcid - ((void *)ctlh - sizeof(struct pptp_pkt_hdr)),
+	rv = ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
+	                              pcid_off + sizeof(struct pptp_pkt_hdr) +
+				      sizeof(struct PptpControlHeader),
 				      sizeof(new_pcid), (char *)&new_pcid, 
 				      sizeof(new_pcid));
 	if (rv != NF_ACCEPT) 
 		return rv;
 
 	if (new_cid) {
-		IP_NF_ASSERT(cid);
 		DEBUGP("altering call id from 0x%04x to 0x%04x\n",
-			ntohs(*cid), ntohs(new_cid));
-		rv = ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, 
-					      (void *)cid - ((void *)ctlh - sizeof(struct pptp_pkt_hdr)), 
-					      sizeof(new_cid),
-					      (char *)&new_cid, 
+			ntohs(*(u_int16_t *)pptpReq + cid_off), ntohs(new_cid));
+		rv = ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
+		                              cid_off + sizeof(struct pptp_pkt_hdr) +
+					      sizeof(struct PptpControlHeader),
+					      sizeof(new_cid), (char *)&new_cid, 
 					      sizeof(new_cid));
 		if (rv != NF_ACCEPT)
 			return rv;
diff -urN oldtree/net/key/af_key.c newtree/net/key/af_key.c
--- oldtree/net/key/af_key.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/net/key/af_key.c	2006-02-13 19:20:01.291315992 -0500
@@ -1526,6 +1526,7 @@
 	if (!skb)
 		return -ENOBUFS;
 	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
+	hdr->sadb_msg_type = SADB_FLUSH;
 	hdr->sadb_msg_satype = pfkey_proto2satype(c->data.proto);
 	hdr->sadb_msg_seq = c->seq;
 	hdr->sadb_msg_pid = c->pid;
@@ -2227,6 +2228,7 @@
 	if (!skb_out)
 		return -ENOBUFS;
 	hdr = (struct sadb_msg *) skb_put(skb_out, sizeof(struct sadb_msg));
+	hdr->sadb_msg_type = SADB_X_SPDFLUSH;
 	hdr->sadb_msg_seq = c->seq;
 	hdr->sadb_msg_pid = c->pid;
 	hdr->sadb_msg_version = PF_KEY_V2;
diff -urN oldtree/net/key/af_key.c.orig newtree/net/key/af_key.c.orig
--- oldtree/net/key/af_key.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ newtree/net/key/af_key.c.orig	2006-02-13 19:20:01.295315384 -0500
@@ -0,0 +1,3082 @@
+/*
+ * net/key/af_key.c	An implementation of PF_KEYv2 sockets.
+ *
+ *		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 the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Authors:	Maxim Giryaev	<gem@asplinux.ru>
+ *		David S. Miller	<davem@redhat.com>
+ *		Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+ *		Kunihiro Ishiguro <kunihiro@ipinfusion.com>
+ *		Kazunori MIYAZAWA / USAGI Project <miyazawa@linux-ipv6.org>
+ *		Derek Atkins <derek@ihtfp.com>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/socket.h>
+#include <linux/pfkeyv2.h>
+#include <linux/ipsec.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <net/xfrm.h>
+
+#include <net/sock.h>
+
+#define _X2KEY(x) ((x) == XFRM_INF ? 0 : (x))
+#define _KEY2X(x) ((x) == 0 ? XFRM_INF : (x))
+
+
+/* List of all pfkey sockets. */
+static HLIST_HEAD(pfkey_table);
+static DECLARE_WAIT_QUEUE_HEAD(pfkey_table_wait);
+static DEFINE_RWLOCK(pfkey_table_lock);
+static atomic_t pfkey_table_users = ATOMIC_INIT(0);
+
+static atomic_t pfkey_socks_nr = ATOMIC_INIT(0);
+
+struct pfkey_sock {
+	/* struct sock must be the first member of struct pfkey_sock */
+	struct sock	sk;
+	int		registered;
+	int		promisc;
+};
+
+static inline struct pfkey_sock *pfkey_sk(struct sock *sk)
+{
+	return (struct pfkey_sock *)sk;
+}
+
+static void pfkey_sock_destruct(struct sock *sk)
+{
+	skb_queue_purge(&sk->sk_receive_queue);
+
+	if (!sock_flag(sk, SOCK_DEAD)) {
+		printk("Attempt to release alive pfkey socket: %p\n", sk);
+		return;
+	}
+
+	BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));
+	BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
+
+	atomic_dec(&pfkey_socks_nr);
+}
+
+static void pfkey_table_grab(void)
+{
+	write_lock_bh(&pfkey_table_lock);
+
+	if (atomic_read(&pfkey_table_users)) {
+		DECLARE_WAITQUEUE(wait, current);
+
+		add_wait_queue_exclusive(&pfkey_table_wait, &wait);
+		for(;;) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			if (atomic_read(&pfkey_table_users) == 0)
+				break;
+			write_unlock_bh(&pfkey_table_lock);
+			schedule();
+			write_lock_bh(&pfkey_table_lock);
+		}
+
+		__set_current_state(TASK_RUNNING);
+		remove_wait_queue(&pfkey_table_wait, &wait);
+	}
+}
+
+static __inline__ void pfkey_table_ungrab(void)
+{
+	write_unlock_bh(&pfkey_table_lock);
+	wake_up(&pfkey_table_wait);
+}
+
+static __inline__ void pfkey_lock_table(void)
+{
+	/* read_lock() synchronizes us to pfkey_table_grab */
+
+	read_lock(&pfkey_table_lock);
+	atomic_inc(&pfkey_table_users);
+	read_unlock(&pfkey_table_lock);
+}
+
+static __inline__ void pfkey_unlock_table(void)
+{
+	if (atomic_dec_and_test(&pfkey_table_users))
+		wake_up(&pfkey_table_wait);
+}
+
+
+static struct proto_ops pfkey_ops;
+
+static void pfkey_insert(struct sock *sk)
+{
+	pfkey_table_grab();
+	sk_add_node(sk, &pfkey_table);
+	pfkey_table_ungrab();
+}
+
+static void pfkey_remove(struct sock *sk)
+{
+	pfkey_table_grab();
+	sk_del_node_init(sk);
+	pfkey_table_ungrab();
+}
+
+static struct proto key_proto = {
+	.name	  = "KEY",
+	.owner	  = THIS_MODULE,
+	.obj_size = sizeof(struct pfkey_sock),
+};
+
+static int pfkey_create(struct socket *sock, int protocol)
+{
+	struct sock *sk;
+	int err;
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+	if (sock->type != SOCK_RAW)
+		return -ESOCKTNOSUPPORT;
+	if (protocol != PF_KEY_V2)
+		return -EPROTONOSUPPORT;
+
+	err = -ENOMEM;
+	sk = sk_alloc(PF_KEY, GFP_KERNEL, &key_proto, 1);
+	if (sk == NULL)
+		goto out;
+	
+	sock->ops = &pfkey_ops;
+	sock_init_data(sock, sk);
+
+	sk->sk_family = PF_KEY;
+	sk->sk_destruct = pfkey_sock_destruct;
+
+	atomic_inc(&pfkey_socks_nr);
+
+	pfkey_insert(sk);
+
+	return 0;
+out:
+	return err;
+}
+
+static int pfkey_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	if (!sk)
+		return 0;
+
+	pfkey_remove(sk);
+
+	sock_orphan(sk);
+	sock->sk = NULL;
+	skb_queue_purge(&sk->sk_write_queue);
+	sock_put(sk);
+
+	return 0;
+}
+
+static int pfkey_broadcast_one(struct sk_buff *skb, struct sk_buff **skb2,
+			       gfp_t allocation, struct sock *sk)
+{
+	int err = -ENOBUFS;
+
+	sock_hold(sk);
+	if (*skb2 == NULL) {
+		if (atomic_read(&skb->users) != 1) {
+			*skb2 = skb_clone(skb, allocation);
+		} else {
+			*skb2 = skb;
+			atomic_inc(&skb->users);
+		}
+	}
+	if (*skb2 != NULL) {
+		if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf) {
+			skb_orphan(*skb2);
+			skb_set_owner_r(*skb2, sk);
+			skb_queue_tail(&sk->sk_receive_queue, *skb2);
+			sk->sk_data_ready(sk, (*skb2)->len);
+			*skb2 = NULL;
+			err = 0;
+		}
+	}
+	sock_put(sk);
+	return err;
+}
+
+/* Send SKB to all pfkey sockets matching selected criteria.  */
+#define BROADCAST_ALL		0
+#define BROADCAST_ONE		1
+#define BROADCAST_REGISTERED	2
+#define BROADCAST_PROMISC_ONLY	4
+static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
+			   int broadcast_flags, struct sock *one_sk)
+{
+	struct sock *sk;
+	struct hlist_node *node;
+	struct sk_buff *skb2 = NULL;
+	int err = -ESRCH;
+
+	/* XXX Do we need something like netlink_overrun?  I think
+	 * XXX PF_KEY socket apps will not mind current behavior.
+	 */
+	if (!skb)
+		return -ENOMEM;
+
+	pfkey_lock_table();
+	sk_for_each(sk, node, &pfkey_table) {
+		struct pfkey_sock *pfk = pfkey_sk(sk);
+		int err2;
+
+		/* Yes, it means that if you are meant to receive this
+		 * pfkey message you receive it twice as promiscuous
+		 * socket.
+		 */
+		if (pfk->promisc)
+			pfkey_broadcast_one(skb, &skb2, allocation, sk);
+
+		/* the exact target will be processed later */
+		if (sk == one_sk)
+			continue;
+		if (broadcast_flags != BROADCAST_ALL) {
+			if (broadcast_flags & BROADCAST_PROMISC_ONLY)
+				continue;
+			if ((broadcast_flags & BROADCAST_REGISTERED) &&
+			    !pfk->registered)
+				continue;
+			if (broadcast_flags & BROADCAST_ONE)
+				continue;
+		}
+
+		err2 = pfkey_broadcast_one(skb, &skb2, allocation, sk);
+
+		/* Error is cleare after succecful sending to at least one
+		 * registered KM */
+		if ((broadcast_flags & BROADCAST_REGISTERED) && err)
+			err = err2;
+	}
+	pfkey_unlock_table();
+
+	if (one_sk != NULL)
+		err = pfkey_broadcast_one(skb, &skb2, allocation, one_sk);
+
+	if (skb2)
+		kfree_skb(skb2);
+	kfree_skb(skb);
+	return err;
+}
+
+static inline void pfkey_hdr_dup(struct sadb_msg *new, struct sadb_msg *orig)
+{
+	*new = *orig;
+}
+
+static int pfkey_error(struct sadb_msg *orig, int err, struct sock *sk)
+{
+	struct sk_buff *skb = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_KERNEL);
+	struct sadb_msg *hdr;
+
+	if (!skb)
+		return -ENOBUFS;
+
+	/* Woe be to the platform trying to support PFKEY yet
+	 * having normal errnos outside the 1-255 range, inclusive.
+	 */
+	err = -err;
+	if (err == ERESTARTSYS ||
+	    err == ERESTARTNOHAND ||
+	    err == ERESTARTNOINTR)
+		err = EINTR;
+	if (err >= 512)
+		err = EINVAL;
+	if (err <= 0 || err >= 256)
+		BUG();
+
+	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
+	pfkey_hdr_dup(hdr, orig);
+	hdr->sadb_msg_errno = (uint8_t) err;
+	hdr->sadb_msg_len = (sizeof(struct sadb_msg) /
+			     sizeof(uint64_t));
+
+	pfkey_broadcast(skb, GFP_KERNEL, BROADCAST_ONE, sk);
+
+	return 0;
+}
+
+static u8 sadb_ext_min_len[] = {
+	[SADB_EXT_RESERVED]		= (u8) 0,
+	[SADB_EXT_SA]			= (u8) sizeof(struct sadb_sa),
+	[SADB_EXT_LIFETIME_CURRENT]	= (u8) sizeof(struct sadb_lifetime),
+	[SADB_EXT_LIFETIME_HARD]	= (u8) sizeof(struct sadb_lifetime),
+	[SADB_EXT_LIFETIME_SOFT]	= (u8) sizeof(struct sadb_lifetime),
+	[SADB_EXT_ADDRESS_SRC]		= (u8) sizeof(struct sadb_address),
+	[SADB_EXT_ADDRESS_DST]		= (u8) sizeof(struct sadb_address),
+	[SADB_EXT_ADDRESS_PROXY]	= (u8) sizeof(struct sadb_address),
+	[SADB_EXT_KEY_AUTH]		= (u8) sizeof(struct sadb_key),
+	[SADB_EXT_KEY_ENCRYPT]		= (u8) sizeof(struct sadb_key),
+	[SADB_EXT_IDENTITY_SRC]		= (u8) sizeof(struct sadb_ident),
+	[SADB_EXT_IDENTITY_DST]		= (u8) sizeof(struct sadb_ident),
+	[SADB_EXT_SENSITIVITY]		= (u8) sizeof(struct sadb_sens),
+	[SADB_EXT_PROPOSAL]		= (u8) sizeof(struct sadb_prop),
+	[SADB_EXT_SUPPORTED_AUTH]	= (u8) sizeof(struct sadb_supported),
+	[SADB_EXT_SUPPORTED_ENCRYPT]	= (u8) sizeof(struct sadb_supported),
+	[SADB_EXT_SPIRANGE]		= (u8) sizeof(struct sadb_spirange),
+	[SADB_X_EXT_KMPRIVATE]		= (u8) sizeof(struct sadb_x_kmprivate),
+	[SADB_X_EXT_POLICY]		= (u8) sizeof(struct sadb_x_policy),
+	[SADB_X_EXT_SA2]		= (u8) sizeof(struct sadb_x_sa2),
+	[SADB_X_EXT_NAT_T_TYPE]		= (u8) sizeof(struct sadb_x_nat_t_type),
+	[SADB_X_EXT_NAT_T_SPORT]	= (u8) sizeof(struct sadb_x_nat_t_port),
+	[SADB_X_EXT_NAT_T_DPORT]	= (u8) sizeof(struct sadb_x_nat_t_port),
+	[SADB_X_EXT_NAT_T_OA]		= (u8) sizeof(struct sadb_address),
+};
+
+/* Verify sadb_address_{len,prefixlen} against sa_family.  */
+static int verify_address_len(void *p)
+{
+	struct sadb_address *sp = p;
+	struct sockaddr *addr = (struct sockaddr *)(sp + 1);
+	struct sockaddr_in *sin;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	struct sockaddr_in6 *sin6;
+#endif
+	int len;
+
+	switch (addr->sa_family) {
+	case AF_INET:
+		len  = sizeof(*sp) + sizeof(*sin) + (sizeof(uint64_t) - 1);
+		len /= sizeof(uint64_t);
+		if (sp->sadb_address_len != len ||
+		    sp->sadb_address_prefixlen > 32)
+			return -EINVAL;
+		break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	case AF_INET6:
+		len  = sizeof(*sp) + sizeof(*sin6) + (sizeof(uint64_t) - 1);
+		len /= sizeof(uint64_t);
+		if (sp->sadb_address_len != len ||
+		    sp->sadb_address_prefixlen > 128)
+			return -EINVAL;
+		break;
+#endif
+	default:
+		/* It is user using kernel to keep track of security
+		 * associations for another protocol, such as
+		 * OSPF/RSVP/RIPV2/MIP.  It is user's job to verify
+		 * lengths.
+		 *
+		 * XXX Actually, association/policy database is not yet
+		 * XXX able to cope with arbitrary sockaddr families.
+		 * XXX When it can, remove this -EINVAL.  -DaveM
+		 */
+		return -EINVAL;
+		break;
+	};
+
+	return 0;
+}
+
+static int present_and_same_family(struct sadb_address *src,
+				   struct sadb_address *dst)
+{
+	struct sockaddr *s_addr, *d_addr;
+
+	if (!src || !dst)
+		return 0;
+
+	s_addr = (struct sockaddr *)(src + 1);
+	d_addr = (struct sockaddr *)(dst + 1);
+	if (s_addr->sa_family != d_addr->sa_family)
+		return 0;
+	if (s_addr->sa_family != AF_INET
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	    && s_addr->sa_family != AF_INET6
+#endif
+		)
+		return 0;
+
+	return 1;
+}
+
+static int parse_exthdrs(struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
+{
+	char *p = (char *) hdr;
+	int len = skb->len;
+
+	len -= sizeof(*hdr);
+	p += sizeof(*hdr);
+	while (len > 0) {
+		struct sadb_ext *ehdr = (struct sadb_ext *) p;
+		uint16_t ext_type;
+		int ext_len;
+
+		ext_len  = ehdr->sadb_ext_len;
+		ext_len *= sizeof(uint64_t);
+		ext_type = ehdr->sadb_ext_type;
+		if (ext_len < sizeof(uint64_t) ||
+		    ext_len > len ||
+		    ext_type == SADB_EXT_RESERVED)
+			return -EINVAL;
+
+		if (ext_type <= SADB_EXT_MAX) {
+			int min = (int) sadb_ext_min_len[ext_type];
+			if (ext_len < min)
+				return -EINVAL;
+			if (ext_hdrs[ext_type-1] != NULL)
+				return -EINVAL;
+			if (ext_type == SADB_EXT_ADDRESS_SRC ||
+			    ext_type == SADB_EXT_ADDRESS_DST ||
+			    ext_type == SADB_EXT_ADDRESS_PROXY ||
+			    ext_type == SADB_X_EXT_NAT_T_OA) {
+				if (verify_address_len(p))
+					return -EINVAL;
+			}				
+			ext_hdrs[ext_type-1] = p;
+		}
+		p   += ext_len;
+		len -= ext_len;
+	}
+
+	return 0;
+}
+
+static uint16_t
+pfkey_satype2proto(uint8_t satype)
+{
+	switch (satype) {
+	case SADB_SATYPE_UNSPEC:
+		return IPSEC_PROTO_ANY;
+	case SADB_SATYPE_AH:
+		return IPPROTO_AH;
+	case SADB_SATYPE_ESP:
+		return IPPROTO_ESP;
+	case SADB_X_SATYPE_IPCOMP:
+		return IPPROTO_COMP;
+		break;
+	default:
+		return 0;
+	}
+	/* NOTREACHED */
+}
+
+static uint8_t
+pfkey_proto2satype(uint16_t proto)
+{
+	switch (proto) {
+	case IPPROTO_AH:
+		return SADB_SATYPE_AH;
+	case IPPROTO_ESP:
+		return SADB_SATYPE_ESP;
+	case IPPROTO_COMP:
+		return SADB_X_SATYPE_IPCOMP;
+		break;
+	default:
+		return 0;
+	}
+	/* NOTREACHED */
+}
+
+/* BTW, this scheme means that there is no way with PFKEY2 sockets to
+ * say specifically 'just raw sockets' as we encode them as 255.
+ */
+
+static uint8_t pfkey_proto_to_xfrm(uint8_t proto)
+{
+	return (proto == IPSEC_PROTO_ANY ? 0 : proto);
+}
+
+static uint8_t pfkey_proto_from_xfrm(uint8_t proto)
+{
+	return (proto ? proto : IPSEC_PROTO_ANY);
+}
+
+static int pfkey_sadb_addr2xfrm_addr(struct sadb_address *addr,
+				     xfrm_address_t *xaddr)
+{
+	switch (((struct sockaddr*)(addr + 1))->sa_family) {
+	case AF_INET:
+		xaddr->a4 = 
+			((struct sockaddr_in *)(addr + 1))->sin_addr.s_addr;
+		return AF_INET;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	case AF_INET6:
+		memcpy(xaddr->a6, 
+		       &((struct sockaddr_in6 *)(addr + 1))->sin6_addr,
+		       sizeof(struct in6_addr));
+		return AF_INET6;
+#endif
+	default:
+		return 0;
+	}
+	/* NOTREACHED */
+}
+
+static struct  xfrm_state *pfkey_xfrm_state_lookup(struct sadb_msg *hdr, void **ext_hdrs)
+{
+	struct sadb_sa *sa;
+	struct sadb_address *addr;
+	uint16_t proto;
+	unsigned short family;
+	xfrm_address_t *xaddr;
+
+	sa = (struct sadb_sa *) ext_hdrs[SADB_EXT_SA-1];
+	if (sa == NULL)
+		return NULL;
+
+	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
+	if (proto == 0)
+		return NULL;
+
+	/* sadb_address_len should be checked by caller */
+	addr = (struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_DST-1];
+	if (addr == NULL)
+		return NULL;
+
+	family = ((struct sockaddr *)(addr + 1))->sa_family;
+	switch (family) {
+	case AF_INET:
+		xaddr = (xfrm_address_t *)&((struct sockaddr_in *)(addr + 1))->sin_addr;
+		break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	case AF_INET6:
+		xaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(addr + 1))->sin6_addr;
+		break;
+#endif
+	default:
+		xaddr = NULL;
+	}
+
+	if (!xaddr)
+		return NULL;
+
+	return xfrm_state_lookup(xaddr, sa->sadb_sa_spi, proto, family);
+}
+
+#define PFKEY_ALIGN8(a) (1 + (((a) - 1) | (8 - 1)))
+static int
+pfkey_sockaddr_size(sa_family_t family)
+{
+	switch (family) {
+	case AF_INET:
+		return PFKEY_ALIGN8(sizeof(struct sockaddr_in));
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	case AF_INET6:
+		return PFKEY_ALIGN8(sizeof(struct sockaddr_in6));
+#endif
+	default:
+		return 0;
+	}
+	/* NOTREACHED */
+}
+
+static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, int hsc)
+{
+	struct sk_buff *skb;
+	struct sadb_msg *hdr;
+	struct sadb_sa *sa;
+	struct sadb_lifetime *lifetime;
+	struct sadb_address *addr;
+	struct sadb_key *key;
+	struct sadb_x_sa2 *sa2;
+	struct sockaddr_in *sin;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	struct sockaddr_in6 *sin6;
+#endif
+	int size;
+	int auth_key_size = 0;
+	int encrypt_key_size = 0;
+	int sockaddr_size;
+	struct xfrm_encap_tmpl *natt = NULL;
+
+	/* address family check */
+	sockaddr_size = pfkey_sockaddr_size(x->props.family);
+	if (!sockaddr_size)
+		return ERR_PTR(-EINVAL);
+
+	/* base, SA, (lifetime (HSC),) address(SD), (address(P),)
+	   key(AE), (identity(SD),) (sensitivity)> */
+	size = sizeof(struct sadb_msg) +sizeof(struct sadb_sa) + 
+		sizeof(struct sadb_lifetime) +
+		((hsc & 1) ? sizeof(struct sadb_lifetime) : 0) +
+		((hsc & 2) ? sizeof(struct sadb_lifetime) : 0) +
+			sizeof(struct sadb_address)*2 + 
+				sockaddr_size*2 +
+					sizeof(struct sadb_x_sa2);
+	/* identity & sensitivity */
+
+	if ((x->props.family == AF_INET &&
+	     x->sel.saddr.a4 != x->props.saddr.a4)
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	    || (x->props.family == AF_INET6 &&
+		memcmp (x->sel.saddr.a6, x->props.saddr.a6, sizeof (struct in6_addr)))
+#endif
+		)
+		size += sizeof(struct sadb_address) + sockaddr_size;
+
+	if (add_keys) {
+		if (x->aalg && x->aalg->alg_key_len) {
+			auth_key_size = 
+				PFKEY_ALIGN8((x->aalg->alg_key_len + 7) / 8); 
+			size += sizeof(struct sadb_key) + auth_key_size;
+		}
+		if (x->ealg && x->ealg->alg_key_len) {
+			encrypt_key_size = 
+				PFKEY_ALIGN8((x->ealg->alg_key_len+7) / 8); 
+			size += sizeof(struct sadb_key) + encrypt_key_size;
+		}
+	}
+	if (x->encap)
+		natt = x->encap;
+
+	if (natt && natt->encap_type) {
+		size += sizeof(struct sadb_x_nat_t_type);
+		size += sizeof(struct sadb_x_nat_t_port);
+		size += sizeof(struct sadb_x_nat_t_port);
+	}
+
+	skb =  alloc_skb(size + 16, GFP_ATOMIC);
+	if (skb == NULL)
+		return ERR_PTR(-ENOBUFS);
+
+	/* call should fill header later */
+	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
+	memset(hdr, 0, size);	/* XXX do we need this ? */
+	hdr->sadb_msg_len = size / sizeof(uint64_t);
+
+	/* sa */
+	sa = (struct sadb_sa *)  skb_put(skb, sizeof(struct sadb_sa));
+	sa->sadb_sa_len = sizeof(struct sadb_sa)/sizeof(uint64_t);
+	sa->sadb_sa_exttype = SADB_EXT_SA;
+	sa->sadb_sa_spi = x->id.spi;
+	sa->sadb_sa_replay = x->props.replay_window;
+	switch (x->km.state) {
+	case XFRM_STATE_VALID:
+		sa->sadb_sa_state = x->km.dying ?
+			SADB_SASTATE_DYING : SADB_SASTATE_MATURE;
+		break;
+	case XFRM_STATE_ACQ:
+		sa->sadb_sa_state = SADB_SASTATE_LARVAL;
+		break;
+	default:
+		sa->sadb_sa_state = SADB_SASTATE_DEAD;
+		break;
+	}
+	sa->sadb_sa_auth = 0;
+	if (x->aalg) {
+		struct xfrm_algo_desc *a = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
+		sa->sadb_sa_auth = a ? a->desc.sadb_alg_id : 0;
+	}
+	sa->sadb_sa_encrypt = 0;
+	BUG_ON(x->ealg && x->calg);
+	if (x->ealg) {
+		struct xfrm_algo_desc *a = xfrm_ealg_get_byname(x->ealg->alg_name, 0);
+		sa->sadb_sa_encrypt = a ? a->desc.sadb_alg_id : 0;
+	}
+	/* KAME compatible: sadb_sa_encrypt is overloaded with calg id */
+	if (x->calg) {
+		struct xfrm_algo_desc *a = xfrm_calg_get_byname(x->calg->alg_name, 0);
+		sa->sadb_sa_encrypt = a ? a->desc.sadb_alg_id : 0;
+	}
+
+	sa->sadb_sa_flags = 0;
+	if (x->props.flags & XFRM_STATE_NOECN)
+		sa->sadb_sa_flags |= SADB_SAFLAGS_NOECN;
+	if (x->props.flags & XFRM_STATE_DECAP_DSCP)
+		sa->sadb_sa_flags |= SADB_SAFLAGS_DECAP_DSCP;
+	if (x->props.flags & XFRM_STATE_NOPMTUDISC)
+		sa->sadb_sa_flags |= SADB_SAFLAGS_NOPMTUDISC;
+
+	/* hard time */
+	if (hsc & 2) {
+		lifetime = (struct sadb_lifetime *)  skb_put(skb, 
+							     sizeof(struct sadb_lifetime));
+		lifetime->sadb_lifetime_len =
+			sizeof(struct sadb_lifetime)/sizeof(uint64_t);
+		lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
+		lifetime->sadb_lifetime_allocations =  _X2KEY(x->lft.hard_packet_limit);
+		lifetime->sadb_lifetime_bytes = _X2KEY(x->lft.hard_byte_limit);
+		lifetime->sadb_lifetime_addtime = x->lft.hard_add_expires_seconds;
+		lifetime->sadb_lifetime_usetime = x->lft.hard_use_expires_seconds;
+	}
+	/* soft time */
+	if (hsc & 1) {
+		lifetime = (struct sadb_lifetime *)  skb_put(skb, 
+							     sizeof(struct sadb_lifetime));
+		lifetime->sadb_lifetime_len =
+			sizeof(struct sadb_lifetime)/sizeof(uint64_t);
+		lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
+		lifetime->sadb_lifetime_allocations =  _X2KEY(x->lft.soft_packet_limit);
+		lifetime->sadb_lifetime_bytes = _X2KEY(x->lft.soft_byte_limit);
+		lifetime->sadb_lifetime_addtime = x->lft.soft_add_expires_seconds;
+		lifetime->sadb_lifetime_usetime = x->lft.soft_use_expires_seconds;
+	}
+	/* current time */
+	lifetime = (struct sadb_lifetime *)  skb_put(skb,
+						     sizeof(struct sadb_lifetime));
+	lifetime->sadb_lifetime_len =
+		sizeof(struct sadb_lifetime)/sizeof(uint64_t);
+	lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
+	lifetime->sadb_lifetime_allocations = x->curlft.packets;
+	lifetime->sadb_lifetime_bytes = x->curlft.bytes;
+	lifetime->sadb_lifetime_addtime = x->curlft.add_time;
+	lifetime->sadb_lifetime_usetime = x->curlft.use_time;
+	/* src address */
+	addr = (struct sadb_address*) skb_put(skb, 
+					      sizeof(struct sadb_address)+sockaddr_size);
+	addr->sadb_address_len = 
+		(sizeof(struct sadb_address)+sockaddr_size)/
+			sizeof(uint64_t);
+	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+	/* "if the ports are non-zero, then the sadb_address_proto field, 
+	   normally zero, MUST be filled in with the transport 
+	   protocol's number." - RFC2367 */
+	addr->sadb_address_proto = 0; 
+	addr->sadb_address_reserved = 0;
+	if (x->props.family == AF_INET) {
+		addr->sadb_address_prefixlen = 32;
+
+		sin = (struct sockaddr_in *) (addr + 1);
+		sin->sin_family = AF_INET;
+		sin->sin_addr.s_addr = x->props.saddr.a4;
+		sin->sin_port = 0;
+		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+	}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	else if (x->props.family == AF_INET6) {
+ 		addr->sadb_address_prefixlen = 128;
+
+		sin6 = (struct sockaddr_in6 *) (addr + 1);
+		sin6->sin6_family = AF_INET6;
+		sin6->sin6_port = 0;
+		sin6->sin6_flowinfo = 0;
+ 		memcpy(&sin6->sin6_addr, x->props.saddr.a6,
+		       sizeof(struct in6_addr));
+		sin6->sin6_scope_id = 0;
+ 	}
+#endif
+	else
+		BUG();
+
+	/* dst address */
+	addr = (struct sadb_address*) skb_put(skb, 
+					      sizeof(struct sadb_address)+sockaddr_size);
+	addr->sadb_address_len = 
+		(sizeof(struct sadb_address)+sockaddr_size)/
+			sizeof(uint64_t);
+	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+	addr->sadb_address_proto = 0; 
+	addr->sadb_address_prefixlen = 32; /* XXX */ 
+	addr->sadb_address_reserved = 0;
+	if (x->props.family == AF_INET) {
+		sin = (struct sockaddr_in *) (addr + 1);
+		sin->sin_family = AF_INET;
+		sin->sin_addr.s_addr = x->id.daddr.a4;
+		sin->sin_port = 0;
+		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+
+		if (x->sel.saddr.a4 != x->props.saddr.a4) {
+			addr = (struct sadb_address*) skb_put(skb, 
+				sizeof(struct sadb_address)+sockaddr_size);
+			addr->sadb_address_len = 
+				(sizeof(struct sadb_address)+sockaddr_size)/
+				sizeof(uint64_t);
+			addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
+			addr->sadb_address_proto =
+				pfkey_proto_from_xfrm(x->sel.proto);
+			addr->sadb_address_prefixlen = x->sel.prefixlen_s;
+			addr->sadb_address_reserved = 0;
+
+			sin = (struct sockaddr_in *) (addr + 1);
+			sin->sin_family = AF_INET;
+			sin->sin_addr.s_addr = x->sel.saddr.a4;
+			sin->sin_port = x->sel.sport;
+			memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+		}
+	}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	else if (x->props.family == AF_INET6) {
+		addr->sadb_address_prefixlen = 128;
+
+		sin6 = (struct sockaddr_in6 *) (addr + 1);
+		sin6->sin6_family = AF_INET6;
+		sin6->sin6_port = 0;
+		sin6->sin6_flowinfo = 0;
+		memcpy(&sin6->sin6_addr, x->id.daddr.a6, sizeof(struct in6_addr));
+		sin6->sin6_scope_id = 0;
+
+		if (memcmp (x->sel.saddr.a6, x->props.saddr.a6,
+			    sizeof(struct in6_addr))) {
+			addr = (struct sadb_address *) skb_put(skb, 
+				sizeof(struct sadb_address)+sockaddr_size);
+			addr->sadb_address_len = 
+				(sizeof(struct sadb_address)+sockaddr_size)/
+				sizeof(uint64_t);
+			addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
+			addr->sadb_address_proto =
+				pfkey_proto_from_xfrm(x->sel.proto);
+			addr->sadb_address_prefixlen = x->sel.prefixlen_s;
+			addr->sadb_address_reserved = 0;
+
+			sin6 = (struct sockaddr_in6 *) (addr + 1);
+			sin6->sin6_family = AF_INET6;
+			sin6->sin6_port = x->sel.sport;
+			sin6->sin6_flowinfo = 0;
+			memcpy(&sin6->sin6_addr, x->sel.saddr.a6,
+			       sizeof(struct in6_addr));
+			sin6->sin6_scope_id = 0;
+		}
+	}
+#endif
+	else
+		BUG();
+
+	/* auth key */
+	if (add_keys && auth_key_size) {
+		key = (struct sadb_key *) skb_put(skb, 
+						  sizeof(struct sadb_key)+auth_key_size);
+		key->sadb_key_len = (sizeof(struct sadb_key) + auth_key_size) /
+			sizeof(uint64_t);
+		key->sadb_key_exttype = SADB_EXT_KEY_AUTH;
+		key->sadb_key_bits = x->aalg->alg_key_len;
+		key->sadb_key_reserved = 0;
+		memcpy(key + 1, x->aalg->alg_key, (x->aalg->alg_key_len+7)/8);
+	}
+	/* encrypt key */
+	if (add_keys && encrypt_key_size) {
+		key = (struct sadb_key *) skb_put(skb, 
+						  sizeof(struct sadb_key)+encrypt_key_size);
+		key->sadb_key_len = (sizeof(struct sadb_key) + 
+				     encrypt_key_size) / sizeof(uint64_t);
+		key->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
+		key->sadb_key_bits = x->ealg->alg_key_len;
+		key->sadb_key_reserved = 0;
+		memcpy(key + 1, x->ealg->alg_key, 
+		       (x->ealg->alg_key_len+7)/8);
+	}
+
+	/* sa */
+	sa2 = (struct sadb_x_sa2 *)  skb_put(skb, sizeof(struct sadb_x_sa2));
+	sa2->sadb_x_sa2_len = sizeof(struct sadb_x_sa2)/sizeof(uint64_t);
+	sa2->sadb_x_sa2_exttype = SADB_X_EXT_SA2;
+	sa2->sadb_x_sa2_mode = x->props.mode + 1;
+	sa2->sadb_x_sa2_reserved1 = 0;
+	sa2->sadb_x_sa2_reserved2 = 0;
+	sa2->sadb_x_sa2_sequence = 0;
+	sa2->sadb_x_sa2_reqid = x->props.reqid;
+
+	if (natt && natt->encap_type) {
+		struct sadb_x_nat_t_type *n_type;
+		struct sadb_x_nat_t_port *n_port;
+
+		/* type */
+		n_type = (struct sadb_x_nat_t_type*) skb_put(skb, sizeof(*n_type));
+		n_type->sadb_x_nat_t_type_len = sizeof(*n_type)/sizeof(uint64_t);
+		n_type->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE;
+		n_type->sadb_x_nat_t_type_type = natt->encap_type;
+		n_type->sadb_x_nat_t_type_reserved[0] = 0;
+		n_type->sadb_x_nat_t_type_reserved[1] = 0;
+		n_type->sadb_x_nat_t_type_reserved[2] = 0;
+
+		/* source port */
+		n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
+		n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
+		n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT;
+		n_port->sadb_x_nat_t_port_port = natt->encap_sport;
+		n_port->sadb_x_nat_t_port_reserved = 0;
+
+		/* dest port */
+		n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
+		n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
+		n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT;
+		n_port->sadb_x_nat_t_port_port = natt->encap_dport;
+		n_port->sadb_x_nat_t_port_reserved = 0;
+	}
+
+	return skb;
+}
+
+static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr, 
+						void **ext_hdrs)
+{
+	struct xfrm_state *x; 
+	struct sadb_lifetime *lifetime;
+	struct sadb_sa *sa;
+	struct sadb_key *key;
+	uint16_t proto;
+	int err;
+	
+
+	sa = (struct sadb_sa *) ext_hdrs[SADB_EXT_SA-1];
+	if (!sa ||
+	    !present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
+				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
+		return ERR_PTR(-EINVAL);
+	if (hdr->sadb_msg_satype == SADB_SATYPE_ESP &&
+	    !ext_hdrs[SADB_EXT_KEY_ENCRYPT-1])
+		return ERR_PTR(-EINVAL);
+	if (hdr->sadb_msg_satype == SADB_SATYPE_AH &&
+	    !ext_hdrs[SADB_EXT_KEY_AUTH-1])
+		return ERR_PTR(-EINVAL);
+	if (!!ext_hdrs[SADB_EXT_LIFETIME_HARD-1] !=
+	    !!ext_hdrs[SADB_EXT_LIFETIME_SOFT-1])
+		return ERR_PTR(-EINVAL);
+
+	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
+	if (proto == 0)
+		return ERR_PTR(-EINVAL);
+
+	/* default error is no buffer space */
+	err = -ENOBUFS;
+
+	/* RFC2367:
+
+   Only SADB_SASTATE_MATURE SAs may be submitted in an SADB_ADD message.
+   SADB_SASTATE_LARVAL SAs are created by SADB_GETSPI and it is not
+   sensible to add a new SA in the DYING or SADB_SASTATE_DEAD state.
+   Therefore, the sadb_sa_state field of all submitted SAs MUST be
+   SADB_SASTATE_MATURE and the kernel MUST return an error if this is
+   not true.
+
+           However, KAME setkey always uses SADB_SASTATE_LARVAL.
+	   Hence, we have to _ignore_ sadb_sa_state, which is also reasonable.
+	 */
+	if (sa->sadb_sa_auth > SADB_AALG_MAX ||
+	    (hdr->sadb_msg_satype == SADB_X_SATYPE_IPCOMP &&
+	     sa->sadb_sa_encrypt > SADB_X_CALG_MAX) ||
+	    sa->sadb_sa_encrypt > SADB_EALG_MAX)
+		return ERR_PTR(-EINVAL);
+	key = (struct sadb_key*) ext_hdrs[SADB_EXT_KEY_AUTH-1];
+	if (key != NULL &&
+	    sa->sadb_sa_auth != SADB_X_AALG_NULL &&
+	    ((key->sadb_key_bits+7) / 8 == 0 ||
+	     (key->sadb_key_bits+7) / 8 > key->sadb_key_len * sizeof(uint64_t)))
+		return ERR_PTR(-EINVAL);
+	key = ext_hdrs[SADB_EXT_KEY_ENCRYPT-1];
+	if (key != NULL &&
+	    sa->sadb_sa_encrypt != SADB_EALG_NULL &&
+	    ((key->sadb_key_bits+7) / 8 == 0 ||
+	     (key->sadb_key_bits+7) / 8 > key->sadb_key_len * sizeof(uint64_t)))
+		return ERR_PTR(-EINVAL);
+
+	x = xfrm_state_alloc();
+	if (x == NULL)
+		return ERR_PTR(-ENOBUFS);
+
+	x->id.proto = proto;
+	x->id.spi = sa->sadb_sa_spi;
+	x->props.replay_window = sa->sadb_sa_replay;
+	if (sa->sadb_sa_flags & SADB_SAFLAGS_NOECN)
+		x->props.flags |= XFRM_STATE_NOECN;
+	if (sa->sadb_sa_flags & SADB_SAFLAGS_DECAP_DSCP)
+		x->props.flags |= XFRM_STATE_DECAP_DSCP;
+	if (sa->sadb_sa_flags & SADB_SAFLAGS_NOPMTUDISC)
+		x->props.flags |= XFRM_STATE_NOPMTUDISC;
+
+	lifetime = (struct sadb_lifetime*) ext_hdrs[SADB_EXT_LIFETIME_HARD-1];
+	if (lifetime != NULL) {
+		x->lft.hard_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations);
+		x->lft.hard_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes);
+		x->lft.hard_add_expires_seconds = lifetime->sadb_lifetime_addtime;
+		x->lft.hard_use_expires_seconds = lifetime->sadb_lifetime_usetime;
+	}
+	lifetime = (struct sadb_lifetime*) ext_hdrs[SADB_EXT_LIFETIME_SOFT-1];
+	if (lifetime != NULL) {
+		x->lft.soft_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations);
+		x->lft.soft_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes);
+		x->lft.soft_add_expires_seconds = lifetime->sadb_lifetime_addtime;
+		x->lft.soft_use_expires_seconds = lifetime->sadb_lifetime_usetime;
+	}
+	key = (struct sadb_key*) ext_hdrs[SADB_EXT_KEY_AUTH-1];
+	if (sa->sadb_sa_auth) {
+		int keysize = 0;
+		struct xfrm_algo_desc *a = xfrm_aalg_get_byid(sa->sadb_sa_auth);
+		if (!a) {
+			err = -ENOSYS;
+			goto out;
+		}
+		if (key)
+			keysize = (key->sadb_key_bits + 7) / 8;
+		x->aalg = kmalloc(sizeof(*x->aalg) + keysize, GFP_KERNEL);
+		if (!x->aalg)
+			goto out;
+		strcpy(x->aalg->alg_name, a->name);
+		x->aalg->alg_key_len = 0;
+		if (key) {
+			x->aalg->alg_key_len = key->sadb_key_bits;
+			memcpy(x->aalg->alg_key, key+1, keysize);
+		}
+		x->props.aalgo = sa->sadb_sa_auth;
+		/* x->algo.flags = sa->sadb_sa_flags; */
+	}
+	if (sa->sadb_sa_encrypt) {
+		if (hdr->sadb_msg_satype == SADB_X_SATYPE_IPCOMP) {
+			struct xfrm_algo_desc *a = xfrm_calg_get_byid(sa->sadb_sa_encrypt);
+			if (!a) {
+				err = -ENOSYS;
+				goto out;
+			}
+			x->calg = kmalloc(sizeof(*x->calg), GFP_KERNEL);
+			if (!x->calg)
+				goto out;
+			strcpy(x->calg->alg_name, a->name);
+			x->props.calgo = sa->sadb_sa_encrypt;
+		} else {
+			int keysize = 0;
+			struct xfrm_algo_desc *a = xfrm_ealg_get_byid(sa->sadb_sa_encrypt);
+			if (!a) {
+				err = -ENOSYS;
+				goto out;
+			}
+			key = (struct sadb_key*) ext_hdrs[SADB_EXT_KEY_ENCRYPT-1];
+			if (key)
+				keysize = (key->sadb_key_bits + 7) / 8;
+			x->ealg = kmalloc(sizeof(*x->ealg) + keysize, GFP_KERNEL);
+			if (!x->ealg)
+				goto out;
+			strcpy(x->ealg->alg_name, a->name);
+			x->ealg->alg_key_len = 0;
+			if (key) {
+				x->ealg->alg_key_len = key->sadb_key_bits;
+				memcpy(x->ealg->alg_key, key+1, keysize);
+			}
+			x->props.ealgo = sa->sadb_sa_encrypt;
+		}
+	}
+	/* x->algo.flags = sa->sadb_sa_flags; */
+
+	x->props.family = pfkey_sadb_addr2xfrm_addr((struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_SRC-1], 
+						    &x->props.saddr);
+	if (!x->props.family) {
+		err = -EAFNOSUPPORT;
+		goto out;
+	}
+	pfkey_sadb_addr2xfrm_addr((struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_DST-1], 
+				  &x->id.daddr);
+
+	if (ext_hdrs[SADB_X_EXT_SA2-1]) {
+		struct sadb_x_sa2 *sa2 = (void*)ext_hdrs[SADB_X_EXT_SA2-1];
+		x->props.mode = sa2->sadb_x_sa2_mode;
+		if (x->props.mode)
+			x->props.mode--;
+		x->props.reqid = sa2->sadb_x_sa2_reqid;
+	}
+
+	if (ext_hdrs[SADB_EXT_ADDRESS_PROXY-1]) {
+		struct sadb_address *addr = ext_hdrs[SADB_EXT_ADDRESS_PROXY-1];
+
+		/* Nobody uses this, but we try. */
+		x->sel.family = pfkey_sadb_addr2xfrm_addr(addr, &x->sel.saddr);
+		x->sel.prefixlen_s = addr->sadb_address_prefixlen;
+	}
+
+	if (ext_hdrs[SADB_X_EXT_NAT_T_TYPE-1]) {
+		struct sadb_x_nat_t_type* n_type;
+		struct xfrm_encap_tmpl *natt;
+
+		x->encap = kmalloc(sizeof(*x->encap), GFP_KERNEL);
+		if (!x->encap)
+			goto out;
+
+		natt = x->encap;
+		n_type = ext_hdrs[SADB_X_EXT_NAT_T_TYPE-1];
+		natt->encap_type = n_type->sadb_x_nat_t_type_type;
+
+		if (ext_hdrs[SADB_X_EXT_NAT_T_SPORT-1]) {
+			struct sadb_x_nat_t_port* n_port =
+				ext_hdrs[SADB_X_EXT_NAT_T_SPORT-1];
+			natt->encap_sport = n_port->sadb_x_nat_t_port_port;
+		}
+		if (ext_hdrs[SADB_X_EXT_NAT_T_DPORT-1]) {
+			struct sadb_x_nat_t_port* n_port =
+				ext_hdrs[SADB_X_EXT_NAT_T_DPORT-1];
+			natt->encap_dport = n_port->sadb_x_nat_t_port_port;
+		}
+	}
+
+	err = xfrm_init_state(x);
+	if (err)
+		goto out;
+
+	x->km.seq = hdr->sadb_msg_seq;
+	return x;
+
+out:
+	x->km.state = XFRM_STATE_DEAD;
+	xfrm_state_put(x);
+	return ERR_PTR(err);
+}
+
+static int pfkey_reserved(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
+{
+	return -EOPNOTSUPP;
+}
+
+static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
+{
+	struct sk_buff *resp_skb;
+	struct sadb_x_sa2 *sa2;
+	struct sadb_address *saddr, *daddr;
+	struct sadb_msg *out_hdr;
+	struct xfrm_state *x = NULL;
+	u8 mode;
+	u32 reqid;
+	u8 proto;
+	unsigned short family;
+	xfrm_address_t *xsaddr = NULL, *xdaddr = NULL;
+
+	if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
+				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
+		return -EINVAL;
+
+	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
+	if (proto == 0)
+		return -EINVAL;
+
+	if ((sa2 = ext_hdrs[SADB_X_EXT_SA2-1]) != NULL) {
+		mode = sa2->sadb_x_sa2_mode - 1;
+		reqid = sa2->sadb_x_sa2_reqid;
+	} else {
+		mode = 0;
+		reqid = 0;
+	}
+
+	saddr = ext_hdrs[SADB_EXT_ADDRESS_SRC-1];
+	daddr = ext_hdrs[SADB_EXT_ADDRESS_DST-1];
+
+	family = ((struct sockaddr *)(saddr + 1))->sa_family;
+	switch (family) {
+	case AF_INET:
+		xdaddr = (xfrm_address_t *)&((struct sockaddr_in *)(daddr + 1))->sin_addr.s_addr;
+		xsaddr = (xfrm_address_t *)&((struct sockaddr_in *)(saddr + 1))->sin_addr.s_addr;
+		break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	case AF_INET6:
+		xdaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(daddr + 1))->sin6_addr;
+		xsaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(saddr + 1))->sin6_addr;
+		break;
+#endif
+	}
+
+	if (hdr->sadb_msg_seq) {
+		x = xfrm_find_acq_byseq(hdr->sadb_msg_seq);
+		if (x && xfrm_addr_cmp(&x->id.daddr, xdaddr, family)) {
+			xfrm_state_put(x);
+			x = NULL;
+		}
+	}
+
+	if (!x)
+		x = xfrm_find_acq(mode, reqid, proto, xdaddr, xsaddr, 1, family);
+
+	if (x == NULL)
+		return -ENOENT;
+
+	resp_skb = ERR_PTR(-ENOENT);
+
+	spin_lock_bh(&x->lock);
+	if (x->km.state != XFRM_STATE_DEAD) {
+		struct sadb_spirange *range = ext_hdrs[SADB_EXT_SPIRANGE-1];
+		u32 min_spi, max_spi;
+
+		if (range != NULL) {
+			min_spi = range->sadb_spirange_min;
+			max_spi = range->sadb_spirange_max;
+		} else {
+			min_spi = 0x100;
+			max_spi = 0x0fffffff;
+		}
+		xfrm_alloc_spi(x, htonl(min_spi), htonl(max_spi));
+		if (x->id.spi)
+			resp_skb = pfkey_xfrm_state2msg(x, 0, 3);
+	}
+	spin_unlock_bh(&x->lock);
+
+	if (IS_ERR(resp_skb)) {
+		xfrm_state_put(x);
+		return  PTR_ERR(resp_skb);
+	}
+
+	out_hdr = (struct sadb_msg *) resp_skb->data;
+	out_hdr->sadb_msg_version = hdr->sadb_msg_version;
+	out_hdr->sadb_msg_type = SADB_GETSPI;
+	out_hdr->sadb_msg_satype = pfkey_proto2satype(proto);
+	out_hdr->sadb_msg_errno = 0;
+	out_hdr->sadb_msg_reserved = 0;
+	out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
+	out_hdr->sadb_msg_pid = hdr->sadb_msg_pid;
+
+	xfrm_state_put(x);
+
+	pfkey_broadcast(resp_skb, GFP_KERNEL, BROADCAST_ONE, sk);
+
+	return 0;
+}
+
+static int pfkey_acquire(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
+{
+	struct xfrm_state *x;
+
+	if (hdr->sadb_msg_len != sizeof(struct sadb_msg)/8)
+		return -EOPNOTSUPP;
+
+	if (hdr->sadb_msg_seq == 0 || hdr->sadb_msg_errno == 0)
+		return 0;
+
+	x = xfrm_find_acq_byseq(hdr->sadb_msg_seq);
+	if (x == NULL)
+		return 0;
+
+	spin_lock_bh(&x->lock);
+	if (x->km.state == XFRM_STATE_ACQ) {
+		x->km.state = XFRM_STATE_ERROR;
+		wake_up(&km_waitq);
+	}
+	spin_unlock_bh(&x->lock);
+	xfrm_state_put(x);
+	return 0;
+}
+
+static inline int event2poltype(int event)
+{
+	switch (event) {
+	case XFRM_MSG_DELPOLICY:
+		return SADB_X_SPDDELETE;
+	case XFRM_MSG_NEWPOLICY:
+		return SADB_X_SPDADD;
+	case XFRM_MSG_UPDPOLICY:
+		return SADB_X_SPDUPDATE;
+	case XFRM_MSG_POLEXPIRE:
+	//	return SADB_X_SPDEXPIRE;
+	default:
+		printk("pfkey: Unknown policy event %d\n", event);
+		break;
+	}
+
+	return 0;
+}
+
+static inline int event2keytype(int event)
+{
+	switch (event) {
+	case XFRM_MSG_DELSA:
+		return SADB_DELETE;
+	case XFRM_MSG_NEWSA:
+		return SADB_ADD;
+	case XFRM_MSG_UPDSA:
+		return SADB_UPDATE;
+	case XFRM_MSG_EXPIRE:
+		return SADB_EXPIRE;
+	default:
+		printk("pfkey: Unknown SA event %d\n", event);
+		break;
+	}
+
+	return 0;
+}
+
+/* ADD/UPD/DEL */
+static int key_notify_sa(struct xfrm_state *x, struct km_event *c)
+{
+	struct sk_buff *skb;
+	struct sadb_msg *hdr;
+	int hsc = 3;
+
+	if (c->event == XFRM_MSG_DELSA)
+		hsc = 0;
+
+	skb = pfkey_xfrm_state2msg(x, 0, hsc);
+
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	hdr = (struct sadb_msg *) skb->data;
+	hdr->sadb_msg_version = PF_KEY_V2;
+	hdr->sadb_msg_type = event2keytype(c->event);
+	hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
+	hdr->sadb_msg_errno = 0;
+	hdr->sadb_msg_reserved = 0;
+	hdr->sadb_msg_seq = c->seq;
+	hdr->sadb_msg_pid = c->pid;
+
+	pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL);
+
+	return 0;
+}
+
+static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
+{
+	struct xfrm_state *x;
+	int err;
+	struct km_event c;
+
+	xfrm_probe_algs();
+	
+	x = pfkey_msg2xfrm_state(hdr, ext_hdrs);
+	if (IS_ERR(x))
+		return PTR_ERR(x);
+
+	xfrm_state_hold(x);
+	if (hdr->sadb_msg_type == SADB_ADD)
+		err = xfrm_state_add(x);
+	else
+		err = xfrm_state_update(x);
+
+	if (err < 0) {
+		x->km.state = XFRM_STATE_DEAD;
+		xfrm_state_put(x);
+		goto out;
+	}
+
+	if (hdr->sadb_msg_type == SADB_ADD)
+		c.event = XFRM_MSG_NEWSA;
+	else
+		c.event = XFRM_MSG_UPDSA;
+	c.seq = hdr->sadb_msg_seq;
+	c.pid = hdr->sadb_msg_pid;
+	km_state_notify(x, &c);
+out:
+	xfrm_state_put(x);
+	return err;
+}
+
+static int pfkey_delete(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
+{
+	struct xfrm_state *x;
+	struct km_event c;
+	int err;
+
+	if (!ext_hdrs[SADB_EXT_SA-1] ||
+	    !present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
+				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
+		return -EINVAL;
+
+	x = pfkey_xfrm_state_lookup(hdr, ext_hdrs);
+	if (x == NULL)
+		return -ESRCH;
+
+	if (xfrm_state_kern(x)) {
+		xfrm_state_put(x);
+		return -EPERM;
+	}
+	
+	err = xfrm_state_delete(x);
+	if (err < 0) {
+		xfrm_state_put(x);
+		return err;
+	}
+
+	c.seq = hdr->sadb_msg_seq;
+	c.pid = hdr->sadb_msg_pid;
+	c.event = XFRM_MSG_DELSA;
+	km_state_notify(x, &c);
+	xfrm_state_put(x);
+
+	return err;
+}
+
+static int pfkey_get(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
+{
+	__u8 proto;
+	struct sk_buff *out_skb;
+	struct sadb_msg *out_hdr;
+	struct xfrm_state *x;
+
+	if (!ext_hdrs[SADB_EXT_SA-1] ||
+	    !present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
+				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
+		return -EINVAL;
+
+	x = pfkey_xfrm_state_lookup(hdr, ext_hdrs);
+	if (x == NULL)
+		return -ESRCH;
+
+	out_skb = pfkey_xfrm_state2msg(x, 1, 3);
+	proto = x->id.proto;
+	xfrm_state_put(x);
+	if (IS_ERR(out_skb))
+		return  PTR_ERR(out_skb);
+
+	out_hdr = (struct sadb_msg *) out_skb->data;
+	out_hdr->sadb_msg_version = hdr->sadb_msg_version;
+	out_hdr->sadb_msg_type = SADB_DUMP;
+	out_hdr->sadb_msg_satype = pfkey_proto2satype(proto);
+	out_hdr->sadb_msg_errno = 0;
+	out_hdr->sadb_msg_reserved = 0;
+	out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
+	out_hdr->sadb_msg_pid = hdr->sadb_msg_pid;
+	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk);
+
+	return 0;
+}
+
+static struct sk_buff *compose_sadb_supported(struct sadb_msg *orig,
+					      gfp_t allocation)
+{
+	struct sk_buff *skb;
+	struct sadb_msg *hdr;
+	int len, auth_len, enc_len, i;
+
+	auth_len = xfrm_count_auth_supported();
+	if (auth_len) {
+		auth_len *= sizeof(struct sadb_alg);
+		auth_len += sizeof(struct sadb_supported);
+	}
+	
+	enc_len = xfrm_count_enc_supported();
+	if (enc_len) {
+		enc_len *= sizeof(struct sadb_alg);
+		enc_len += sizeof(struct sadb_supported);
+	}
+	
+	len = enc_len + auth_len + sizeof(struct sadb_msg);
+
+	skb = alloc_skb(len + 16, allocation);
+	if (!skb)
+		goto out_put_algs;
+
+	hdr = (struct sadb_msg *) skb_put(skb, sizeof(*hdr));
+	pfkey_hdr_dup(hdr, orig);
+	hdr->sadb_msg_errno = 0;
+	hdr->sadb_msg_len = len / sizeof(uint64_t);
+
+	if (auth_len) {
+		struct sadb_supported *sp;
+		struct sadb_alg *ap;
+
+		sp = (struct sadb_supported *) skb_put(skb, auth_len);
+		ap = (struct sadb_alg *) (sp + 1);
+
+		sp->sadb_supported_len = auth_len / sizeof(uint64_t);
+		sp->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH;
+
+		for (i = 0; ; i++) {
+			struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(i);
+			if (!aalg)
+				break;
+			if (aalg->available)
+				*ap++ = aalg->desc;
+		}
+	}
+
+	if (enc_len) {
+		struct sadb_supported *sp;
+		struct sadb_alg *ap;
+
+		sp = (struct sadb_supported *) skb_put(skb, enc_len);
+		ap = (struct sadb_alg *) (sp + 1);
+
+		sp->sadb_supported_len = enc_len / sizeof(uint64_t);
+		sp->sadb_supported_exttype = SADB_EXT_SUPPORTED_ENCRYPT;
+
+		for (i = 0; ; i++) {
+			struct xfrm_algo_desc *ealg = xfrm_ealg_get_byidx(i);
+			if (!ealg)
+				break;
+			if (ealg->available)
+				*ap++ = ealg->desc;
+		}
+	}
+
+out_put_algs:
+	return skb;
+}
+
+static int pfkey_register(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
+{
+	struct pfkey_sock *pfk = pfkey_sk(sk);
+	struct sk_buff *supp_skb;
+
+	if (hdr->sadb_msg_satype > SADB_SATYPE_MAX)
+		return -EINVAL;
+
+	if (hdr->sadb_msg_satype != SADB_SATYPE_UNSPEC) {
+		if (pfk->registered&(1<<hdr->sadb_msg_satype))
+			return -EEXIST;
+		pfk->registered |= (1<<hdr->sadb_msg_satype);
+	}
+
+	xfrm_probe_algs();
+	
+	supp_skb = compose_sadb_supported(hdr, GFP_KERNEL);
+	if (!supp_skb) {
+		if (hdr->sadb_msg_satype != SADB_SATYPE_UNSPEC)
+			pfk->registered &= ~(1<<hdr->sadb_msg_satype);
+
+		return -ENOBUFS;
+	}
+
+	pfkey_broadcast(supp_skb, GFP_KERNEL, BROADCAST_REGISTERED, sk);
+
+	return 0;
+}
+
+static int key_notify_sa_flush(struct km_event *c)
+{
+	struct sk_buff *skb;
+	struct sadb_msg *hdr;
+
+	skb = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_ATOMIC);
+	if (!skb)
+		return -ENOBUFS;
+	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
+	hdr->sadb_msg_satype = pfkey_proto2satype(c->data.proto);
+	hdr->sadb_msg_seq = c->seq;
+	hdr->sadb_msg_pid = c->pid;
+	hdr->sadb_msg_version = PF_KEY_V2;
+	hdr->sadb_msg_errno = (uint8_t) 0;
+	hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
+
+	pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL);
+
+	return 0;
+}
+
+static int pfkey_flush(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
+{
+	unsigned proto;
+	struct km_event c;
+
+	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
+	if (proto == 0)
+		return -EINVAL;
+
+	xfrm_state_flush(proto);
+	c.data.proto = proto;
+	c.seq = hdr->sadb_msg_seq;
+	c.pid = hdr->sadb_msg_pid;
+	c.event = XFRM_MSG_FLUSHSA;
+	km_state_notify(NULL, &c);
+
+	return 0;
+}
+
+struct pfkey_dump_data
+{
+	struct sk_buff *skb;
+	struct sadb_msg *hdr;
+	struct sock *sk;
+};
+
+static int dump_sa(struct xfrm_state *x, int count, void *ptr)
+{
+	struct pfkey_dump_data *data = ptr;
+	struct sk_buff *out_skb;
+	struct sadb_msg *out_hdr;
+
+	out_skb = pfkey_xfrm_state2msg(x, 1, 3);
+	if (IS_ERR(out_skb))
+		return PTR_ERR(out_skb);
+
+	out_hdr = (struct sadb_msg *) out_skb->data;
+	out_hdr->sadb_msg_version = data->hdr->sadb_msg_version;
+	out_hdr->sadb_msg_type = SADB_DUMP;
+	out_hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
+	out_hdr->sadb_msg_errno = 0;
+	out_hdr->sadb_msg_reserved = 0;
+	out_hdr->sadb_msg_seq = count;
+	out_hdr->sadb_msg_pid = data->hdr->sadb_msg_pid;
+	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, data->sk);
+	return 0;
+}
+
+static int pfkey_dump(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
+{
+	u8 proto;
+	struct pfkey_dump_data data = { .skb = skb, .hdr = hdr, .sk = sk };
+
+	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
+	if (proto == 0)
+		return -EINVAL;
+
+	return xfrm_state_walk(proto, dump_sa, &data);
+}
+
+static int pfkey_promisc(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
+{
+	struct pfkey_sock *pfk = pfkey_sk(sk);
+	int satype = hdr->sadb_msg_satype;
+
+	if (hdr->sadb_msg_len == (sizeof(*hdr) / sizeof(uint64_t))) {
+		/* XXX we mangle packet... */
+		hdr->sadb_msg_errno = 0;
+		if (satype != 0 && satype != 1)
+			return -EINVAL;
+		pfk->promisc = satype;
+	}
+	pfkey_broadcast(skb_clone(skb, GFP_KERNEL), GFP_KERNEL, BROADCAST_ALL, NULL);
+	return 0;
+}
+
+static int check_reqid(struct xfrm_policy *xp, int dir, int count, void *ptr)
+{
+	int i;
+	u32 reqid = *(u32*)ptr;
+
+	for (i=0; i<xp->xfrm_nr; i++) {
+		if (xp->xfrm_vec[i].reqid == reqid)
+			return -EEXIST;
+	}
+	return 0;
+}
+
+static u32 gen_reqid(void)
+{
+	u32 start;
+	static u32 reqid = IPSEC_MANUAL_REQID_MAX;
+
+	start = reqid;
+	do {
+		++reqid;
+		if (reqid == 0)
+			reqid = IPSEC_MANUAL_REQID_MAX+1;
+		if (xfrm_policy_walk(check_reqid, (void*)&reqid) != -EEXIST)
+			return reqid;
+	} while (reqid != start);
+	return 0;
+}
+
+static int
+parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq)
+{
+	struct xfrm_tmpl *t = xp->xfrm_vec + xp->xfrm_nr;
+	struct sockaddr_in *sin;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	struct sockaddr_in6 *sin6;
+#endif
+
+	if (xp->xfrm_nr >= XFRM_MAX_DEPTH)
+		return -ELOOP;
+
+	if (rq->sadb_x_ipsecrequest_mode == 0)
+		return -EINVAL;
+
+	t->id.proto = rq->sadb_x_ipsecrequest_proto; /* XXX check proto */
+	t->mode = rq->sadb_x_ipsecrequest_mode-1;
+	if (rq->sadb_x_ipsecrequest_level == IPSEC_LEVEL_USE)
+		t->optional = 1;
+	else if (rq->sadb_x_ipsecrequest_level == IPSEC_LEVEL_UNIQUE) {
+		t->reqid = rq->sadb_x_ipsecrequest_reqid;
+		if (t->reqid > IPSEC_MANUAL_REQID_MAX)
+			t->reqid = 0;
+		if (!t->reqid && !(t->reqid = gen_reqid()))
+			return -ENOBUFS;
+	}
+
+	/* addresses present only in tunnel mode */
+	if (t->mode) {
+		switch (xp->family) {
+		case AF_INET:
+			sin = (void*)(rq+1);
+			if (sin->sin_family != AF_INET)
+				return -EINVAL;
+			t->saddr.a4 = sin->sin_addr.s_addr;
+			sin++;
+			if (sin->sin_family != AF_INET)
+				return -EINVAL;
+			t->id.daddr.a4 = sin->sin_addr.s_addr;
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case AF_INET6:
+			sin6 = (void *)(rq+1);
+			if (sin6->sin6_family != AF_INET6)
+				return -EINVAL;
+			memcpy(t->saddr.a6, &sin6->sin6_addr, sizeof(struct in6_addr));
+			sin6++;
+			if (sin6->sin6_family != AF_INET6)
+				return -EINVAL;
+			memcpy(t->id.daddr.a6, &sin6->sin6_addr, sizeof(struct in6_addr));
+			break;
+#endif
+		default:
+			return -EINVAL;
+		}
+	}
+	/* No way to set this via kame pfkey */
+	t->aalgos = t->ealgos = t->calgos = ~0;
+	xp->xfrm_nr++;
+	return 0;
+}
+
+static int
+parse_ipsecrequests(struct xfrm_policy *xp, struct sadb_x_policy *pol)
+{
+	int err;
+	int len = pol->sadb_x_policy_len*8 - sizeof(struct sadb_x_policy);
+	struct sadb_x_ipsecrequest *rq = (void*)(pol+1);
+
+	while (len >= sizeof(struct sadb_x_ipsecrequest)) {
+		if ((err = parse_ipsecrequest(xp, rq)) < 0)
+			return err;
+		len -= rq->sadb_x_ipsecrequest_len;
+		rq = (void*)((u8*)rq + rq->sadb_x_ipsecrequest_len);
+	}
+	return 0;
+}
+
+static int pfkey_xfrm_policy2msg_size(struct xfrm_policy *xp)
+{
+	int sockaddr_size = pfkey_sockaddr_size(xp->family);
+	int socklen = (xp->family == AF_INET ?
+		       sizeof(struct sockaddr_in) :
+		       sizeof(struct sockaddr_in6));
+
+	return sizeof(struct sadb_msg) +
+		(sizeof(struct sadb_lifetime) * 3) +
+		(sizeof(struct sadb_address) * 2) + 
+		(sockaddr_size * 2) +
+		sizeof(struct sadb_x_policy) +
+		(xp->xfrm_nr * (sizeof(struct sadb_x_ipsecrequest) +
+				(socklen * 2)));
+}
+
+static struct sk_buff * pfkey_xfrm_policy2msg_prep(struct xfrm_policy *xp)
+{
+	struct sk_buff *skb;
+	int size;
+
+	size = pfkey_xfrm_policy2msg_size(xp);
+
+	skb =  alloc_skb(size + 16, GFP_ATOMIC);
+	if (skb == NULL)
+		return ERR_PTR(-ENOBUFS);
+
+	return skb;
+}
+
+static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, int dir)
+{
+	struct sadb_msg *hdr;
+	struct sadb_address *addr;
+	struct sadb_lifetime *lifetime;
+	struct sadb_x_policy *pol;
+	struct sockaddr_in   *sin;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	struct sockaddr_in6  *sin6;
+#endif
+	int i;
+	int size;
+	int sockaddr_size = pfkey_sockaddr_size(xp->family);
+	int socklen = (xp->family == AF_INET ?
+		       sizeof(struct sockaddr_in) :
+		       sizeof(struct sockaddr_in6));
+
+	size = pfkey_xfrm_policy2msg_size(xp);
+
+	/* call should fill header later */
+	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
+	memset(hdr, 0, size);	/* XXX do we need this ? */
+
+	/* src address */
+	addr = (struct sadb_address*) skb_put(skb, 
+					      sizeof(struct sadb_address)+sockaddr_size);
+	addr->sadb_address_len = 
+		(sizeof(struct sadb_address)+sockaddr_size)/
+			sizeof(uint64_t);
+	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+	addr->sadb_address_proto = pfkey_proto_from_xfrm(xp->selector.proto);
+	addr->sadb_address_prefixlen = xp->selector.prefixlen_s;
+	addr->sadb_address_reserved = 0;
+	/* src address */
+	if (xp->family == AF_INET) {
+		sin = (struct sockaddr_in *) (addr + 1);
+		sin->sin_family = AF_INET;
+		sin->sin_addr.s_addr = xp->selector.saddr.a4;
+		sin->sin_port = xp->selector.sport;
+		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+	}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	else if (xp->family == AF_INET6) {
+		sin6 = (struct sockaddr_in6 *) (addr + 1);
+		sin6->sin6_family = AF_INET6;
+		sin6->sin6_port = xp->selector.sport;
+		sin6->sin6_flowinfo = 0;
+		memcpy(&sin6->sin6_addr, xp->selector.saddr.a6,
+		       sizeof(struct in6_addr));
+		sin6->sin6_scope_id = 0;
+	}
+#endif
+	else
+		BUG();
+
+	/* dst address */
+	addr = (struct sadb_address*) skb_put(skb, 
+					      sizeof(struct sadb_address)+sockaddr_size);
+	addr->sadb_address_len =
+		(sizeof(struct sadb_address)+sockaddr_size)/
+			sizeof(uint64_t);
+	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+	addr->sadb_address_proto = pfkey_proto_from_xfrm(xp->selector.proto);
+	addr->sadb_address_prefixlen = xp->selector.prefixlen_d; 
+	addr->sadb_address_reserved = 0;
+	if (xp->family == AF_INET) {
+		sin = (struct sockaddr_in *) (addr + 1);
+		sin->sin_family = AF_INET;
+		sin->sin_addr.s_addr = xp->selector.daddr.a4;
+		sin->sin_port = xp->selector.dport;
+		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+	}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	else if (xp->family == AF_INET6) {
+		sin6 = (struct sockaddr_in6 *) (addr + 1);
+		sin6->sin6_family = AF_INET6;
+		sin6->sin6_port = xp->selector.dport;
+		sin6->sin6_flowinfo = 0;
+		memcpy(&sin6->sin6_addr, xp->selector.daddr.a6,
+		       sizeof(struct in6_addr));
+		sin6->sin6_scope_id = 0;
+	}
+#endif
+	else
+		BUG();
+
+	/* hard time */
+	lifetime = (struct sadb_lifetime *)  skb_put(skb, 
+						     sizeof(struct sadb_lifetime));
+	lifetime->sadb_lifetime_len =
+		sizeof(struct sadb_lifetime)/sizeof(uint64_t);
+	lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
+	lifetime->sadb_lifetime_allocations =  _X2KEY(xp->lft.hard_packet_limit);
+	lifetime->sadb_lifetime_bytes = _X2KEY(xp->lft.hard_byte_limit);
+	lifetime->sadb_lifetime_addtime = xp->lft.hard_add_expires_seconds;
+	lifetime->sadb_lifetime_usetime = xp->lft.hard_use_expires_seconds;
+	/* soft time */
+	lifetime = (struct sadb_lifetime *)  skb_put(skb, 
+						     sizeof(struct sadb_lifetime));
+	lifetime->sadb_lifetime_len =
+		sizeof(struct sadb_lifetime)/sizeof(uint64_t);
+	lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
+	lifetime->sadb_lifetime_allocations =  _X2KEY(xp->lft.soft_packet_limit);
+	lifetime->sadb_lifetime_bytes = _X2KEY(xp->lft.soft_byte_limit);
+	lifetime->sadb_lifetime_addtime = xp->lft.soft_add_expires_seconds;
+	lifetime->sadb_lifetime_usetime = xp->lft.soft_use_expires_seconds;
+	/* current time */
+	lifetime = (struct sadb_lifetime *)  skb_put(skb, 
+						     sizeof(struct sadb_lifetime));
+	lifetime->sadb_lifetime_len =
+		sizeof(struct sadb_lifetime)/sizeof(uint64_t);
+	lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
+	lifetime->sadb_lifetime_allocations = xp->curlft.packets;
+	lifetime->sadb_lifetime_bytes = xp->curlft.bytes;
+	lifetime->sadb_lifetime_addtime = xp->curlft.add_time;
+	lifetime->sadb_lifetime_usetime = xp->curlft.use_time;
+
+	pol = (struct sadb_x_policy *)  skb_put(skb, sizeof(struct sadb_x_policy));
+	pol->sadb_x_policy_len = sizeof(struct sadb_x_policy)/sizeof(uint64_t);
+	pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
+	pol->sadb_x_policy_type = IPSEC_POLICY_DISCARD;
+	if (xp->action == XFRM_POLICY_ALLOW) {
+		if (xp->xfrm_nr)
+			pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
+		else
+			pol->sadb_x_policy_type = IPSEC_POLICY_NONE;
+	}
+	pol->sadb_x_policy_dir = dir+1;
+	pol->sadb_x_policy_id = xp->index;
+	pol->sadb_x_policy_priority = xp->priority;
+
+	for (i=0; i<xp->xfrm_nr; i++) {
+		struct sadb_x_ipsecrequest *rq;
+		struct xfrm_tmpl *t = xp->xfrm_vec + i;
+		int req_size;
+
+		req_size = sizeof(struct sadb_x_ipsecrequest);
+		if (t->mode)
+			req_size += 2*socklen;
+		else
+			size -= 2*socklen;
+		rq = (void*)skb_put(skb, req_size);
+		pol->sadb_x_policy_len += req_size/8;
+		memset(rq, 0, sizeof(*rq));
+		rq->sadb_x_ipsecrequest_len = req_size;
+		rq->sadb_x_ipsecrequest_proto = t->id.proto;
+		rq->sadb_x_ipsecrequest_mode = t->mode+1;
+		rq->sadb_x_ipsecrequest_level = IPSEC_LEVEL_REQUIRE;
+		if (t->reqid)
+			rq->sadb_x_ipsecrequest_level = IPSEC_LEVEL_UNIQUE;
+		if (t->optional)
+			rq->sadb_x_ipsecrequest_level = IPSEC_LEVEL_USE;
+		rq->sadb_x_ipsecrequest_reqid = t->reqid;
+		if (t->mode) {
+			switch (xp->family) {
+			case AF_INET:
+				sin = (void*)(rq+1);
+				sin->sin_family = AF_INET;
+				sin->sin_addr.s_addr = t->saddr.a4;
+				sin->sin_port = 0;
+				memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+				sin++;
+				sin->sin_family = AF_INET;
+				sin->sin_addr.s_addr = t->id.daddr.a4;
+				sin->sin_port = 0;
+				memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+				break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+			case AF_INET6:
+				sin6 = (void*)(rq+1);
+				sin6->sin6_family = AF_INET6;
+				sin6->sin6_port = 0;
+				sin6->sin6_flowinfo = 0;
+				memcpy(&sin6->sin6_addr, t->saddr.a6,
+				       sizeof(struct in6_addr));
+				sin6->sin6_scope_id = 0;
+
+				sin6++;
+				sin6->sin6_family = AF_INET6;
+				sin6->sin6_port = 0;
+				sin6->sin6_flowinfo = 0;
+				memcpy(&sin6->sin6_addr, t->id.daddr.a6,
+				       sizeof(struct in6_addr));
+				sin6->sin6_scope_id = 0;
+				break;
+#endif
+			default:
+				break;
+			}
+		}
+	}
+	hdr->sadb_msg_len = size / sizeof(uint64_t);
+	hdr->sadb_msg_reserved = atomic_read(&xp->refcnt);
+}
+
+static int key_notify_policy(struct xfrm_policy *xp, int dir, struct km_event *c)
+{
+	struct sk_buff *out_skb;
+	struct sadb_msg *out_hdr;
+	int err;
+
+	out_skb = pfkey_xfrm_policy2msg_prep(xp);
+	if (IS_ERR(out_skb)) {
+		err = PTR_ERR(out_skb);
+		goto out;
+	}
+	pfkey_xfrm_policy2msg(out_skb, xp, dir);
+
+	out_hdr = (struct sadb_msg *) out_skb->data;
+	out_hdr->sadb_msg_version = PF_KEY_V2;
+
+	if (c->data.byid && c->event == XFRM_MSG_DELPOLICY)
+		out_hdr->sadb_msg_type = SADB_X_SPDDELETE2;
+	else
+		out_hdr->sadb_msg_type = event2poltype(c->event);
+	out_hdr->sadb_msg_errno = 0;
+	out_hdr->sadb_msg_seq = c->seq;
+	out_hdr->sadb_msg_pid = c->pid;
+	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ALL, NULL);
+out:
+	return 0;
+
+}
+
+static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
+{
+	int err;
+	struct sadb_lifetime *lifetime;
+	struct sadb_address *sa;
+	struct sadb_x_policy *pol;
+	struct xfrm_policy *xp;
+	struct km_event c;
+
+	if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
+				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]) ||
+	    !ext_hdrs[SADB_X_EXT_POLICY-1])
+		return -EINVAL;
+
+	pol = ext_hdrs[SADB_X_EXT_POLICY-1];
+	if (pol->sadb_x_policy_type > IPSEC_POLICY_IPSEC)
+		return -EINVAL;
+	if (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir >= IPSEC_DIR_MAX)
+		return -EINVAL;
+
+	xp = xfrm_policy_alloc(GFP_KERNEL);
+	if (xp == NULL)
+		return -ENOBUFS;
+
+	xp->action = (pol->sadb_x_policy_type == IPSEC_POLICY_DISCARD ?
+		      XFRM_POLICY_BLOCK : XFRM_POLICY_ALLOW);
+	xp->priority = pol->sadb_x_policy_priority;
+
+	sa = ext_hdrs[SADB_EXT_ADDRESS_SRC-1], 
+	xp->family = pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.saddr);
+	if (!xp->family) {
+		err = -EINVAL;
+		goto out;
+	}
+	xp->selector.family = xp->family;
+	xp->selector.prefixlen_s = sa->sadb_address_prefixlen;
+	xp->selector.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
+	xp->selector.sport = ((struct sockaddr_in *)(sa+1))->sin_port;
+	if (xp->selector.sport)
+		xp->selector.sport_mask = ~0;
+
+	sa = ext_hdrs[SADB_EXT_ADDRESS_DST-1], 
+	pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.daddr);
+	xp->selector.prefixlen_d = sa->sadb_address_prefixlen;
+
+	/* Amusing, we set this twice.  KAME apps appear to set same value
+	 * in both addresses.
+	 */
+	xp->selector.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
+
+	xp->selector.dport = ((struct sockaddr_in *)(sa+1))->sin_port;
+	if (xp->selector.dport)
+		xp->selector.dport_mask = ~0;
+
+	xp->lft.soft_byte_limit = XFRM_INF;
+	xp->lft.hard_byte_limit = XFRM_INF;
+	xp->lft.soft_packet_limit = XFRM_INF;
+	xp->lft.hard_packet_limit = XFRM_INF;
+	if ((lifetime = ext_hdrs[SADB_EXT_LIFETIME_HARD-1]) != NULL) {
+		xp->lft.hard_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations);
+		xp->lft.hard_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes);
+		xp->lft.hard_add_expires_seconds = lifetime->sadb_lifetime_addtime;
+		xp->lft.hard_use_expires_seconds = lifetime->sadb_lifetime_usetime;
+	}
+	if ((lifetime = ext_hdrs[SADB_EXT_LIFETIME_SOFT-1]) != NULL) {
+		xp->lft.soft_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations);
+		xp->lft.soft_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes);
+		xp->lft.soft_add_expires_seconds = lifetime->sadb_lifetime_addtime;
+		xp->lft.soft_use_expires_seconds = lifetime->sadb_lifetime_usetime;
+	}
+	xp->xfrm_nr = 0;
+	if (pol->sadb_x_policy_type == IPSEC_POLICY_IPSEC &&
+	    (err = parse_ipsecrequests(xp, pol)) < 0)
+		goto out;
+
+	err = xfrm_policy_insert(pol->sadb_x_policy_dir-1, xp,
+				 hdr->sadb_msg_type != SADB_X_SPDUPDATE);
+	if (err) {
+		kfree(xp);
+		return err;
+	}
+
+	if (hdr->sadb_msg_type == SADB_X_SPDUPDATE)
+		c.event = XFRM_MSG_UPDPOLICY;
+	else 
+		c.event = XFRM_MSG_NEWPOLICY;
+
+	c.seq = hdr->sadb_msg_seq;
+	c.pid = hdr->sadb_msg_pid;
+
+	km_policy_notify(xp, pol->sadb_x_policy_dir-1, &c);
+	xfrm_pol_put(xp);
+	return 0;
+
+out:
+	kfree(xp);
+	return err;
+}
+
+static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
+{
+	int err;
+	struct sadb_address *sa;
+	struct sadb_x_policy *pol;
+	struct xfrm_policy *xp;
+	struct xfrm_selector sel;
+	struct km_event c;
+
+	if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
+				     ext_hdrs[SADB_EXT_ADDRESS_DST-1]) ||
+	    !ext_hdrs[SADB_X_EXT_POLICY-1])
+		return -EINVAL;
+
+	pol = ext_hdrs[SADB_X_EXT_POLICY-1];
+	if (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir >= IPSEC_DIR_MAX)
+		return -EINVAL;
+
+	memset(&sel, 0, sizeof(sel));
+
+	sa = ext_hdrs[SADB_EXT_ADDRESS_SRC-1], 
+	sel.family = pfkey_sadb_addr2xfrm_addr(sa, &sel.saddr);
+	sel.prefixlen_s = sa->sadb_address_prefixlen;
+	sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
+	sel.sport = ((struct sockaddr_in *)(sa+1))->sin_port;
+	if (sel.sport)
+		sel.sport_mask = ~0;
+
+	sa = ext_hdrs[SADB_EXT_ADDRESS_DST-1], 
+	pfkey_sadb_addr2xfrm_addr(sa, &sel.daddr);
+	sel.prefixlen_d = sa->sadb_address_prefixlen;
+	sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
+	sel.dport = ((struct sockaddr_in *)(sa+1))->sin_port;
+	if (sel.dport)
+		sel.dport_mask = ~0;
+
+	xp = xfrm_policy_bysel(pol->sadb_x_policy_dir-1, &sel, 1);
+	if (xp == NULL)
+		return -ENOENT;
+
+	err = 0;
+
+	c.seq = hdr->sadb_msg_seq;
+	c.pid = hdr->sadb_msg_pid;
+	c.event = XFRM_MSG_DELPOLICY;
+	km_policy_notify(xp, pol->sadb_x_policy_dir-1, &c);
+
+	xfrm_pol_put(xp);
+	return err;
+}
+
+static int key_pol_get_resp(struct sock *sk, struct xfrm_policy *xp, struct sadb_msg *hdr, int dir)
+{
+	int err;
+	struct sk_buff *out_skb;
+	struct sadb_msg *out_hdr;
+	err = 0;
+
+	out_skb = pfkey_xfrm_policy2msg_prep(xp);
+	if (IS_ERR(out_skb)) {
+		err =  PTR_ERR(out_skb);
+		goto out;
+	}
+	pfkey_xfrm_policy2msg(out_skb, xp, dir);
+
+	out_hdr = (struct sadb_msg *) out_skb->data;
+	out_hdr->sadb_msg_version = hdr->sadb_msg_version;
+	out_hdr->sadb_msg_type = hdr->sadb_msg_type;
+	out_hdr->sadb_msg_satype = 0;
+	out_hdr->sadb_msg_errno = 0;
+	out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
+	out_hdr->sadb_msg_pid = hdr->sadb_msg_pid;
+	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk);
+	err = 0;
+
+out:
+	return err;
+}
+
+static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
+{
+	unsigned int dir;
+	int err;
+	struct sadb_x_policy *pol;
+	struct xfrm_policy *xp;
+	struct km_event c;
+
+	if ((pol = ext_hdrs[SADB_X_EXT_POLICY-1]) == NULL)
+		return -EINVAL;
+
+	dir = xfrm_policy_id2dir(pol->sadb_x_policy_id);
+	if (dir >= XFRM_POLICY_MAX)
+		return -EINVAL;
+
+	xp = xfrm_policy_byid(dir, pol->sadb_x_policy_id,
+			      hdr->sadb_msg_type == SADB_X_SPDDELETE2);
+	if (xp == NULL)
+		return -ENOENT;
+
+	err = 0;
+
+	c.seq = hdr->sadb_msg_seq;
+	c.pid = hdr->sadb_msg_pid;
+	if (hdr->sadb_msg_type == SADB_X_SPDDELETE2) {
+		c.data.byid = 1;
+		c.event = XFRM_MSG_DELPOLICY;
+		km_policy_notify(xp, dir, &c);
+	} else {
+		err = key_pol_get_resp(sk, xp, hdr, dir);
+	}
+
+	xfrm_pol_put(xp);
+	return err;
+}
+
+static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr)
+{
+	struct pfkey_dump_data *data = ptr;
+	struct sk_buff *out_skb;
+	struct sadb_msg *out_hdr;
+
+	out_skb = pfkey_xfrm_policy2msg_prep(xp);
+	if (IS_ERR(out_skb))
+		return PTR_ERR(out_skb);
+
+	pfkey_xfrm_policy2msg(out_skb, xp, dir);
+
+	out_hdr = (struct sadb_msg *) out_skb->data;
+	out_hdr->sadb_msg_version = data->hdr->sadb_msg_version;
+	out_hdr->sadb_msg_type = SADB_X_SPDDUMP;
+	out_hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC;
+	out_hdr->sadb_msg_errno = 0;
+	out_hdr->sadb_msg_seq = count;
+	out_hdr->sadb_msg_pid = data->hdr->sadb_msg_pid;
+	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, data->sk);
+	return 0;
+}
+
+static int pfkey_spddump(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
+{
+	struct pfkey_dump_data data = { .skb = skb, .hdr = hdr, .sk = sk };
+
+	return xfrm_policy_walk(dump_sp, &data);
+}
+
+static int key_notify_policy_flush(struct km_event *c)
+{
+	struct sk_buff *skb_out;
+	struct sadb_msg *hdr;
+
+	skb_out = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_ATOMIC);
+	if (!skb_out)
+		return -ENOBUFS;
+	hdr = (struct sadb_msg *) skb_put(skb_out, sizeof(struct sadb_msg));
+	hdr->sadb_msg_seq = c->seq;
+	hdr->sadb_msg_pid = c->pid;
+	hdr->sadb_msg_version = PF_KEY_V2;
+	hdr->sadb_msg_errno = (uint8_t) 0;
+	hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
+	pfkey_broadcast(skb_out, GFP_ATOMIC, BROADCAST_ALL, NULL);
+	return 0;
+
+}
+
+static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
+{
+	struct km_event c;
+
+	xfrm_policy_flush();
+	c.event = XFRM_MSG_FLUSHPOLICY;
+	c.pid = hdr->sadb_msg_pid;
+	c.seq = hdr->sadb_msg_seq;
+	km_policy_notify(NULL, 0, &c);
+
+	return 0;
+}
+
+typedef int (*pfkey_handler)(struct sock *sk, struct sk_buff *skb,
+			     struct sadb_msg *hdr, void **ext_hdrs);
+static pfkey_handler pfkey_funcs[SADB_MAX + 1] = {
+	[SADB_RESERVED]		= pfkey_reserved,
+	[SADB_GETSPI]		= pfkey_getspi,
+	[SADB_UPDATE]		= pfkey_add,
+	[SADB_ADD]		= pfkey_add,
+	[SADB_DELETE]		= pfkey_delete,
+	[SADB_GET]		= pfkey_get,
+	[SADB_ACQUIRE]		= pfkey_acquire,
+	[SADB_REGISTER]		= pfkey_register,
+	[SADB_EXPIRE]		= NULL,
+	[SADB_FLUSH]		= pfkey_flush,
+	[SADB_DUMP]		= pfkey_dump,
+	[SADB_X_PROMISC]	= pfkey_promisc,
+	[SADB_X_PCHANGE]	= NULL,
+	[SADB_X_SPDUPDATE]	= pfkey_spdadd,
+	[SADB_X_SPDADD]		= pfkey_spdadd,
+	[SADB_X_SPDDELETE]	= pfkey_spddelete,
+	[SADB_X_SPDGET]		= pfkey_spdget,
+	[SADB_X_SPDACQUIRE]	= NULL,
+	[SADB_X_SPDDUMP]	= pfkey_spddump,
+	[SADB_X_SPDFLUSH]	= pfkey_spdflush,
+	[SADB_X_SPDSETIDX]	= pfkey_spdadd,
+	[SADB_X_SPDDELETE2]	= pfkey_spdget,
+};
+
+static int pfkey_process(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr)
+{
+	void *ext_hdrs[SADB_EXT_MAX];
+	int err;
+
+	pfkey_broadcast(skb_clone(skb, GFP_KERNEL), GFP_KERNEL,
+			BROADCAST_PROMISC_ONLY, NULL);
+
+	memset(ext_hdrs, 0, sizeof(ext_hdrs));
+	err = parse_exthdrs(skb, hdr, ext_hdrs);
+	if (!err) {
+		err = -EOPNOTSUPP;
+		if (pfkey_funcs[hdr->sadb_msg_type])
+			err = pfkey_funcs[hdr->sadb_msg_type](sk, skb, hdr, ext_hdrs);
+	}
+	return err;
+}
+
+static struct sadb_msg *pfkey_get_base_msg(struct sk_buff *skb, int *errp)
+{
+	struct sadb_msg *hdr = NULL;
+
+	if (skb->len < sizeof(*hdr)) {
+		*errp = -EMSGSIZE;
+	} else {
+		hdr = (struct sadb_msg *) skb->data;
+		if (hdr->sadb_msg_version != PF_KEY_V2 ||
+		    hdr->sadb_msg_reserved != 0 ||
+		    (hdr->sadb_msg_type <= SADB_RESERVED ||
+		     hdr->sadb_msg_type > SADB_MAX)) {
+			hdr = NULL;
+			*errp = -EINVAL;
+		} else if (hdr->sadb_msg_len != (skb->len /
+						 sizeof(uint64_t)) ||
+			   hdr->sadb_msg_len < (sizeof(struct sadb_msg) /
+						sizeof(uint64_t))) {
+			hdr = NULL;
+			*errp = -EMSGSIZE;
+		} else {
+			*errp = 0;
+		}
+	}
+	return hdr;
+}
+
+static inline int aalg_tmpl_set(struct xfrm_tmpl *t, struct xfrm_algo_desc *d)
+{
+	return t->aalgos & (1 << d->desc.sadb_alg_id);
+}
+
+static inline int ealg_tmpl_set(struct xfrm_tmpl *t, struct xfrm_algo_desc *d)
+{
+	return t->ealgos & (1 << d->desc.sadb_alg_id);
+}
+
+static int count_ah_combs(struct xfrm_tmpl *t)
+{
+	int i, sz = 0;
+
+	for (i = 0; ; i++) {
+		struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(i);
+		if (!aalg)
+			break;
+		if (aalg_tmpl_set(t, aalg) && aalg->available)
+			sz += sizeof(struct sadb_comb);
+	}
+	return sz + sizeof(struct sadb_prop);
+}
+
+static int count_esp_combs(struct xfrm_tmpl *t)
+{
+	int i, k, sz = 0;
+
+	for (i = 0; ; i++) {
+		struct xfrm_algo_desc *ealg = xfrm_ealg_get_byidx(i);
+		if (!ealg)
+			break;
+			
+		if (!(ealg_tmpl_set(t, ealg) && ealg->available))
+			continue;
+			
+		for (k = 1; ; k++) {
+			struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(k);
+			if (!aalg)
+				break;
+				
+			if (aalg_tmpl_set(t, aalg) && aalg->available)
+				sz += sizeof(struct sadb_comb);
+		}
+	}
+	return sz + sizeof(struct sadb_prop);
+}
+
+static void dump_ah_combs(struct sk_buff *skb, struct xfrm_tmpl *t)
+{
+	struct sadb_prop *p;
+	int i;
+
+	p = (struct sadb_prop*)skb_put(skb, sizeof(struct sadb_prop));
+	p->sadb_prop_len = sizeof(struct sadb_prop)/8;
+	p->sadb_prop_exttype = SADB_EXT_PROPOSAL;
+	p->sadb_prop_replay = 32;
+	memset(p->sadb_prop_reserved, 0, sizeof(p->sadb_prop_reserved));
+
+	for (i = 0; ; i++) {
+		struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(i);
+		if (!aalg)
+			break;
+
+		if (aalg_tmpl_set(t, aalg) && aalg->available) {
+			struct sadb_comb *c;
+			c = (struct sadb_comb*)skb_put(skb, sizeof(struct sadb_comb));
+			memset(c, 0, sizeof(*c));
+			p->sadb_prop_len += sizeof(struct sadb_comb)/8;
+			c->sadb_comb_auth = aalg->desc.sadb_alg_id;
+			c->sadb_comb_auth_minbits = aalg->desc.sadb_alg_minbits;
+			c->sadb_comb_auth_maxbits = aalg->desc.sadb_alg_maxbits;
+			c->sadb_comb_hard_addtime = 24*60*60;
+			c->sadb_comb_soft_addtime = 20*60*60;
+			c->sadb_comb_hard_usetime = 8*60*60;
+			c->sadb_comb_soft_usetime = 7*60*60;
+		}
+	}
+}
+
+static void dump_esp_combs(struct sk_buff *skb, struct xfrm_tmpl *t)
+{
+	struct sadb_prop *p;
+	int i, k;
+
+	p = (struct sadb_prop*)skb_put(skb, sizeof(struct sadb_prop));
+	p->sadb_prop_len = sizeof(struct sadb_prop)/8;
+	p->sadb_prop_exttype = SADB_EXT_PROPOSAL;
+	p->sadb_prop_replay = 32;
+	memset(p->sadb_prop_reserved, 0, sizeof(p->sadb_prop_reserved));
+
+	for (i=0; ; i++) {
+		struct xfrm_algo_desc *ealg = xfrm_ealg_get_byidx(i);
+		if (!ealg)
+			break;
+	
+		if (!(ealg_tmpl_set(t, ealg) && ealg->available))
+			continue;
+			
+		for (k = 1; ; k++) {
+			struct sadb_comb *c;
+			struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(k);
+			if (!aalg)
+				break;
+			if (!(aalg_tmpl_set(t, aalg) && aalg->available))
+				continue;
+			c = (struct sadb_comb*)skb_put(skb, sizeof(struct sadb_comb));
+			memset(c, 0, sizeof(*c));
+			p->sadb_prop_len += sizeof(struct sadb_comb)/8;
+			c->sadb_comb_auth = aalg->desc.sadb_alg_id;
+			c->sadb_comb_auth_minbits = aalg->desc.sadb_alg_minbits;
+			c->sadb_comb_auth_maxbits = aalg->desc.sadb_alg_maxbits;
+			c->sadb_comb_encrypt = ealg->desc.sadb_alg_id;
+			c->sadb_comb_encrypt_minbits = ealg->desc.sadb_alg_minbits;
+			c->sadb_comb_encrypt_maxbits = ealg->desc.sadb_alg_maxbits;
+			c->sadb_comb_hard_addtime = 24*60*60;
+			c->sadb_comb_soft_addtime = 20*60*60;
+			c->sadb_comb_hard_usetime = 8*60*60;
+			c->sadb_comb_soft_usetime = 7*60*60;
+		}
+	}
+}
+
+static int key_notify_policy_expire(struct xfrm_policy *xp, struct km_event *c)
+{
+	return 0;
+}
+
+static int key_notify_sa_expire(struct xfrm_state *x, struct km_event *c)
+{
+	struct sk_buff *out_skb;
+	struct sadb_msg *out_hdr;
+	int hard;
+	int hsc;
+
+	hard = c->data.hard;
+	if (hard)
+		hsc = 2;
+	else
+		hsc = 1;
+
+	out_skb = pfkey_xfrm_state2msg(x, 0, hsc);
+	if (IS_ERR(out_skb))
+		return PTR_ERR(out_skb);
+
+	out_hdr = (struct sadb_msg *) out_skb->data;
+	out_hdr->sadb_msg_version = PF_KEY_V2;
+	out_hdr->sadb_msg_type = SADB_EXPIRE;
+	out_hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
+	out_hdr->sadb_msg_errno = 0;
+	out_hdr->sadb_msg_reserved = 0;
+	out_hdr->sadb_msg_seq = 0;
+	out_hdr->sadb_msg_pid = 0;
+
+	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL);
+	return 0;
+}
+
+static int pfkey_send_notify(struct xfrm_state *x, struct km_event *c)
+{
+	switch (c->event) {
+	case XFRM_MSG_EXPIRE:
+		return key_notify_sa_expire(x, c);
+	case XFRM_MSG_DELSA:
+	case XFRM_MSG_NEWSA:
+	case XFRM_MSG_UPDSA:
+		return key_notify_sa(x, c);
+	case XFRM_MSG_FLUSHSA:
+		return key_notify_sa_flush(c);
+	default:
+		printk("pfkey: Unknown SA event %d\n", c->event);
+		break;
+	}
+
+	return 0;
+}
+
+static int pfkey_send_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
+{
+	switch (c->event) {
+	case XFRM_MSG_POLEXPIRE:
+		return key_notify_policy_expire(xp, c);
+	case XFRM_MSG_DELPOLICY:
+	case XFRM_MSG_NEWPOLICY:
+	case XFRM_MSG_UPDPOLICY:
+		return key_notify_policy(xp, dir, c);
+	case XFRM_MSG_FLUSHPOLICY:
+		return key_notify_policy_flush(c);
+	default:
+		printk("pfkey: Unknown policy event %d\n", c->event);
+		break;
+	}
+
+	return 0;
+}
+
+static u32 get_acqseq(void)
+{
+	u32 res;
+	static u32 acqseq;
+	static DEFINE_SPINLOCK(acqseq_lock);
+
+	spin_lock_bh(&acqseq_lock);
+	res = (++acqseq ? : ++acqseq);
+	spin_unlock_bh(&acqseq_lock);
+	return res;
+}
+
+static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *xp, int dir)
+{
+	struct sk_buff *skb;
+	struct sadb_msg *hdr;
+	struct sadb_address *addr;
+	struct sadb_x_policy *pol;
+	struct sockaddr_in *sin;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	struct sockaddr_in6 *sin6;
+#endif
+	int sockaddr_size;
+	int size;
+	
+	sockaddr_size = pfkey_sockaddr_size(x->props.family);
+	if (!sockaddr_size)
+		return -EINVAL;
+
+	size = sizeof(struct sadb_msg) +
+		(sizeof(struct sadb_address) * 2) +
+		(sockaddr_size * 2) +
+		sizeof(struct sadb_x_policy);
+	
+	if (x->id.proto == IPPROTO_AH)
+		size += count_ah_combs(t);
+	else if (x->id.proto == IPPROTO_ESP)
+		size += count_esp_combs(t);
+
+	skb =  alloc_skb(size + 16, GFP_ATOMIC);
+	if (skb == NULL)
+		return -ENOMEM;
+	
+	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
+	hdr->sadb_msg_version = PF_KEY_V2;
+	hdr->sadb_msg_type = SADB_ACQUIRE;
+	hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
+	hdr->sadb_msg_len = size / sizeof(uint64_t);
+	hdr->sadb_msg_errno = 0;
+	hdr->sadb_msg_reserved = 0;
+	hdr->sadb_msg_seq = x->km.seq = get_acqseq();
+	hdr->sadb_msg_pid = 0;
+
+	/* src address */
+	addr = (struct sadb_address*) skb_put(skb, 
+					      sizeof(struct sadb_address)+sockaddr_size);
+	addr->sadb_address_len = 
+		(sizeof(struct sadb_address)+sockaddr_size)/
+			sizeof(uint64_t);
+	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+	addr->sadb_address_proto = 0;
+	addr->sadb_address_reserved = 0;
+	if (x->props.family == AF_INET) {
+		addr->sadb_address_prefixlen = 32;
+
+		sin = (struct sockaddr_in *) (addr + 1);
+		sin->sin_family = AF_INET;
+		sin->sin_addr.s_addr = x->props.saddr.a4;
+		sin->sin_port = 0;
+		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+	}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	else if (x->props.family == AF_INET6) {
+		addr->sadb_address_prefixlen = 128;
+
+		sin6 = (struct sockaddr_in6 *) (addr + 1);
+		sin6->sin6_family = AF_INET6;
+		sin6->sin6_port = 0;
+		sin6->sin6_flowinfo = 0;
+		memcpy(&sin6->sin6_addr,
+		       x->props.saddr.a6, sizeof(struct in6_addr));
+		sin6->sin6_scope_id = 0;
+	}
+#endif
+	else
+		BUG();
+	
+	/* dst address */
+	addr = (struct sadb_address*) skb_put(skb, 
+					      sizeof(struct sadb_address)+sockaddr_size);
+	addr->sadb_address_len =
+		(sizeof(struct sadb_address)+sockaddr_size)/
+			sizeof(uint64_t);
+	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+	addr->sadb_address_proto = 0;
+	addr->sadb_address_reserved = 0;
+	if (x->props.family == AF_INET) {
+		addr->sadb_address_prefixlen = 32; 
+
+		sin = (struct sockaddr_in *) (addr + 1);
+		sin->sin_family = AF_INET;
+		sin->sin_addr.s_addr = x->id.daddr.a4;
+		sin->sin_port = 0;
+		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+	}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	else if (x->props.family == AF_INET6) {
+		addr->sadb_address_prefixlen = 128; 
+
+		sin6 = (struct sockaddr_in6 *) (addr + 1);
+		sin6->sin6_family = AF_INET6;
+		sin6->sin6_port = 0;
+		sin6->sin6_flowinfo = 0;
+		memcpy(&sin6->sin6_addr,
+		       x->id.daddr.a6, sizeof(struct in6_addr));
+		sin6->sin6_scope_id = 0;
+	}
+#endif
+	else
+		BUG();
+
+	pol = (struct sadb_x_policy *)  skb_put(skb, sizeof(struct sadb_x_policy));
+	pol->sadb_x_policy_len = sizeof(struct sadb_x_policy)/sizeof(uint64_t);
+	pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
+	pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
+	pol->sadb_x_policy_dir = dir+1;
+	pol->sadb_x_policy_id = xp->index;
+
+	/* Set sadb_comb's. */
+	if (x->id.proto == IPPROTO_AH)
+		dump_ah_combs(skb, t);
+	else if (x->id.proto == IPPROTO_ESP)
+		dump_esp_combs(skb, t);
+
+	return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL);
+}
+
+static struct xfrm_policy *pfkey_compile_policy(u16 family, int opt,
+                                                u8 *data, int len, int *dir)
+{
+	struct xfrm_policy *xp;
+	struct sadb_x_policy *pol = (struct sadb_x_policy*)data;
+
+	switch (family) {
+	case AF_INET:
+		if (opt != IP_IPSEC_POLICY) {
+			*dir = -EOPNOTSUPP;
+			return NULL;
+		}
+		break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	case AF_INET6:
+		if (opt != IPV6_IPSEC_POLICY) {
+			*dir = -EOPNOTSUPP;
+			return NULL;
+		}
+		break;
+#endif
+	default:
+		*dir = -EINVAL;
+		return NULL;
+	}
+
+	*dir = -EINVAL;
+
+	if (len < sizeof(struct sadb_x_policy) ||
+	    pol->sadb_x_policy_len*8 > len ||
+	    pol->sadb_x_policy_type > IPSEC_POLICY_BYPASS ||
+	    (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir > IPSEC_DIR_OUTBOUND))
+		return NULL;
+
+	xp = xfrm_policy_alloc(GFP_ATOMIC);
+	if (xp == NULL) {
+		*dir = -ENOBUFS;
+		return NULL;
+	}
+
+	xp->action = (pol->sadb_x_policy_type == IPSEC_POLICY_DISCARD ?
+		      XFRM_POLICY_BLOCK : XFRM_POLICY_ALLOW);
+
+	xp->lft.soft_byte_limit = XFRM_INF;
+	xp->lft.hard_byte_limit = XFRM_INF;
+	xp->lft.soft_packet_limit = XFRM_INF;
+	xp->lft.hard_packet_limit = XFRM_INF;
+	xp->family = family;
+
+	xp->xfrm_nr = 0;
+	if (pol->sadb_x_policy_type == IPSEC_POLICY_IPSEC &&
+	    (*dir = parse_ipsecrequests(xp, pol)) < 0)
+		goto out;
+
+	*dir = pol->sadb_x_policy_dir-1;
+	return xp;
+
+out:
+	kfree(xp);
+	return NULL;
+}
+
+static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport)
+{
+	struct sk_buff *skb;
+	struct sadb_msg *hdr;
+	struct sadb_sa *sa;
+	struct sadb_address *addr;
+	struct sadb_x_nat_t_port *n_port;
+	struct sockaddr_in *sin;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	struct sockaddr_in6 *sin6;
+#endif
+	int sockaddr_size;
+	int size;
+	__u8 satype = (x->id.proto == IPPROTO_ESP ? SADB_SATYPE_ESP : 0);
+	struct xfrm_encap_tmpl *natt = NULL;
+
+	sockaddr_size = pfkey_sockaddr_size(x->props.family);
+	if (!sockaddr_size)
+		return -EINVAL;
+
+	if (!satype)
+		return -EINVAL;
+
+	if (!x->encap)
+		return -EINVAL;
+
+	natt = x->encap;
+
+	/* Build an SADB_X_NAT_T_NEW_MAPPING message:
+	 *
+	 * HDR | SA | ADDRESS_SRC (old addr) | NAT_T_SPORT (old port) |
+	 * ADDRESS_DST (new addr) | NAT_T_DPORT (new port)
+	 */
+	
+	size = sizeof(struct sadb_msg) +
+		sizeof(struct sadb_sa) +
+		(sizeof(struct sadb_address) * 2) +
+		(sockaddr_size * 2) +
+		(sizeof(struct sadb_x_nat_t_port) * 2);
+	
+	skb =  alloc_skb(size + 16, GFP_ATOMIC);
+	if (skb == NULL)
+		return -ENOMEM;
+	
+	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
+	hdr->sadb_msg_version = PF_KEY_V2;
+	hdr->sadb_msg_type = SADB_X_NAT_T_NEW_MAPPING;
+	hdr->sadb_msg_satype = satype;
+	hdr->sadb_msg_len = size / sizeof(uint64_t);
+	hdr->sadb_msg_errno = 0;
+	hdr->sadb_msg_reserved = 0;
+	hdr->sadb_msg_seq = x->km.seq = get_acqseq();
+	hdr->sadb_msg_pid = 0;
+
+	/* SA */
+	sa = (struct sadb_sa *) skb_put(skb, sizeof(struct sadb_sa));
+	sa->sadb_sa_len = sizeof(struct sadb_sa)/sizeof(uint64_t);
+	sa->sadb_sa_exttype = SADB_EXT_SA;
+	sa->sadb_sa_spi = x->id.spi;
+	sa->sadb_sa_replay = 0;
+	sa->sadb_sa_state = 0;
+	sa->sadb_sa_auth = 0;
+	sa->sadb_sa_encrypt = 0;
+	sa->sadb_sa_flags = 0;
+
+	/* ADDRESS_SRC (old addr) */
+	addr = (struct sadb_address*)
+		skb_put(skb, sizeof(struct sadb_address)+sockaddr_size);
+	addr->sadb_address_len = 
+		(sizeof(struct sadb_address)+sockaddr_size)/
+			sizeof(uint64_t);
+	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+	addr->sadb_address_proto = 0;
+	addr->sadb_address_reserved = 0;
+	if (x->props.family == AF_INET) {
+		addr->sadb_address_prefixlen = 32;
+
+		sin = (struct sockaddr_in *) (addr + 1);
+		sin->sin_family = AF_INET;
+		sin->sin_addr.s_addr = x->props.saddr.a4;
+		sin->sin_port = 0;
+		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+	}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	else if (x->props.family == AF_INET6) {
+		addr->sadb_address_prefixlen = 128;
+
+		sin6 = (struct sockaddr_in6 *) (addr + 1);
+		sin6->sin6_family = AF_INET6;
+		sin6->sin6_port = 0;
+		sin6->sin6_flowinfo = 0;
+		memcpy(&sin6->sin6_addr,
+		       x->props.saddr.a6, sizeof(struct in6_addr));
+		sin6->sin6_scope_id = 0;
+	}
+#endif
+	else
+		BUG();
+
+	/* NAT_T_SPORT (old port) */
+	n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
+	n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
+	n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT;
+	n_port->sadb_x_nat_t_port_port = natt->encap_sport;
+	n_port->sadb_x_nat_t_port_reserved = 0;
+
+	/* ADDRESS_DST (new addr) */
+	addr = (struct sadb_address*)
+		skb_put(skb, sizeof(struct sadb_address)+sockaddr_size);
+	addr->sadb_address_len = 
+		(sizeof(struct sadb_address)+sockaddr_size)/
+			sizeof(uint64_t);
+	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+	addr->sadb_address_proto = 0;
+	addr->sadb_address_reserved = 0;
+	if (x->props.family == AF_INET) {
+		addr->sadb_address_prefixlen = 32;
+
+		sin = (struct sockaddr_in *) (addr + 1);
+		sin->sin_family = AF_INET;
+		sin->sin_addr.s_addr = ipaddr->a4;
+		sin->sin_port = 0;
+		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+	}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	else if (x->props.family == AF_INET6) {
+		addr->sadb_address_prefixlen = 128;
+
+		sin6 = (struct sockaddr_in6 *) (addr + 1);
+		sin6->sin6_family = AF_INET6;
+		sin6->sin6_port = 0;
+		sin6->sin6_flowinfo = 0;
+		memcpy(&sin6->sin6_addr, &ipaddr->a6, sizeof(struct in6_addr));
+		sin6->sin6_scope_id = 0;
+	}
+#endif
+	else
+		BUG();
+
+	/* NAT_T_DPORT (new port) */
+	n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
+	n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
+	n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT;
+	n_port->sadb_x_nat_t_port_port = sport;
+	n_port->sadb_x_nat_t_port_reserved = 0;
+
+	return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL);
+}
+
+static int pfkey_sendmsg(struct kiocb *kiocb,
+			 struct socket *sock, struct msghdr *msg, size_t len)
+{
+	struct sock *sk = sock->sk;
+	struct sk_buff *skb = NULL;
+	struct sadb_msg *hdr = NULL;
+	int err;
+
+	err = -EOPNOTSUPP;
+	if (msg->msg_flags & MSG_OOB)
+		goto out;
+
+	err = -EMSGSIZE;
+	if ((unsigned)len > sk->sk_sndbuf - 32)
+		goto out;
+
+	err = -ENOBUFS;
+	skb = alloc_skb(len, GFP_KERNEL);
+	if (skb == NULL)
+		goto out;
+
+	err = -EFAULT;
+	if (memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len))
+		goto out;
+
+	hdr = pfkey_get_base_msg(skb, &err);
+	if (!hdr)
+		goto out;
+
+	down(&xfrm_cfg_sem);
+	err = pfkey_process(sk, skb, hdr);
+	up(&xfrm_cfg_sem);
+
+out:
+	if (err && hdr && pfkey_error(hdr, err, sk) == 0)
+		err = 0;
+	if (skb)
+		kfree_skb(skb);
+
+	return err ? : len;
+}
+
+static int pfkey_recvmsg(struct kiocb *kiocb,
+			 struct socket *sock, struct msghdr *msg, size_t len,
+			 int flags)
+{
+	struct sock *sk = sock->sk;
+	struct sk_buff *skb;
+	int copied, err;
+
+	err = -EINVAL;
+	if (flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT))
+		goto out;
+
+	msg->msg_namelen = 0;
+	skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err);
+	if (skb == NULL)
+		goto out;
+
+	copied = skb->len;
+	if (copied > len) {
+		msg->msg_flags |= MSG_TRUNC;
+		copied = len;
+	}
+
+	skb->h.raw = skb->data;
+	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+	if (err)
+		goto out_free;
+
+	sock_recv_timestamp(msg, sk, skb);
+
+	err = (flags & MSG_TRUNC) ? skb->len : copied;
+
+out_free:
+	skb_free_datagram(sk, skb);
+out:
+	return err;
+}
+
+static struct proto_ops pfkey_ops = {
+	.family		=	PF_KEY,
+	.owner		=	THIS_MODULE,
+	/* Operations that make no sense on pfkey sockets. */
+	.bind		=	sock_no_bind,
+	.connect	=	sock_no_connect,
+	.socketpair	=	sock_no_socketpair,
+	.accept		=	sock_no_accept,
+	.getname	=	sock_no_getname,
+	.ioctl		=	sock_no_ioctl,
+	.listen		=	sock_no_listen,
+	.shutdown	=	sock_no_shutdown,
+	.setsockopt	=	sock_no_setsockopt,
+	.getsockopt	=	sock_no_getsockopt,
+	.mmap		=	sock_no_mmap,
+	.sendpage	=	sock_no_sendpage,
+
+	/* Now the operations that really occur. */
+	.release	=	pfkey_release,
+	.poll		=	datagram_poll,
+	.sendmsg	=	pfkey_sendmsg,
+	.recvmsg	=	pfkey_recvmsg,
+};
+
+static struct net_proto_family pfkey_family_ops = {
+	.family	=	PF_KEY,
+	.create	=	pfkey_create,
+	.owner	=	THIS_MODULE,
+};
+
+#ifdef CONFIG_PROC_FS
+static int pfkey_read_proc(char *buffer, char **start, off_t offset,
+			   int length, int *eof, void *data)
+{
+	off_t pos = 0;
+	off_t begin = 0;
+	int len = 0;
+	struct sock *s;
+	struct hlist_node *node;
+
+	len += sprintf(buffer,"sk       RefCnt Rmem   Wmem   User   Inode\n");
+
+	read_lock(&pfkey_table_lock);
+
+	sk_for_each(s, node, &pfkey_table) {
+		len += sprintf(buffer+len,"%p %-6d %-6u %-6u %-6u %-6lu",
+			       s,
+			       atomic_read(&s->sk_refcnt),
+			       atomic_read(&s->sk_rmem_alloc),
+			       atomic_read(&s->sk_wmem_alloc),
+			       sock_i_uid(s),
+			       sock_i_ino(s)
+			       );
+
+		buffer[len++] = '\n';
+		
+		pos = begin + len;
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+		if(pos > offset + length)
+			goto done;
+	}
+	*eof = 1;
+
+done:
+	read_unlock(&pfkey_table_lock);
+
+	*start = buffer + (offset - begin);
+	len -= (offset - begin);
+
+	if (len > length)
+		len = length;
+	if (len < 0)
+		len = 0;
+
+	return len;
+}
+#endif
+
+static struct xfrm_mgr pfkeyv2_mgr =
+{
+	.id		= "pfkeyv2",
+	.notify		= pfkey_send_notify,
+	.acquire	= pfkey_send_acquire,
+	.compile_policy	= pfkey_compile_policy,
+	.new_mapping	= pfkey_send_new_mapping,
+	.notify_policy	= pfkey_send_policy_notify,
+};
+
+static void __exit ipsec_pfkey_exit(void)
+{
+	xfrm_unregister_km(&pfkeyv2_mgr);
+	remove_proc_entry("net/pfkey", NULL);
+	sock_unregister(PF_KEY);
+	proto_unregister(&key_proto);
+}
+
+static int __init ipsec_pfkey_init(void)
+{
+	int err = proto_register(&key_proto, 0);
+
+	if (err != 0)
+		goto out;
+
+	err = sock_register(&pfkey_family_ops);
+	if (err != 0)
+		goto out_unregister_key_proto;
+#ifdef CONFIG_PROC_FS
+	err = -ENOMEM;
+	if (create_proc_read_entry("net/pfkey", 0, NULL, pfkey_read_proc, NULL) == NULL)
+		goto out_sock_unregister;
+#endif
+	err = xfrm_register_km(&pfkeyv2_mgr);
+	if (err != 0)
+		goto out_remove_proc_entry;
+out:
+	return err;
+out_remove_proc_entry:
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry("net/pfkey", NULL);
+out_sock_unregister:
+#endif
+	sock_unregister(PF_KEY);
+out_unregister_key_proto:
+	proto_unregister(&key_proto);
+	goto out;
+}
+
+module_init(ipsec_pfkey_init);
+module_exit(ipsec_pfkey_exit);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(PF_KEY);
diff -urN oldtree/net/netlink/af_netlink.c newtree/net/netlink/af_netlink.c
--- oldtree/net/netlink/af_netlink.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/net/netlink/af_netlink.c	2006-02-13 19:20:30.109934896 -0500
@@ -402,7 +402,7 @@
 	groups = nl_table[protocol].groups;
 	netlink_unlock_table();
 
-	if ((err = __netlink_create(sock, protocol) < 0))
+	if ((err = __netlink_create(sock, protocol)) < 0)
 		goto out_module;
 
 	nlk = nlk_sk(sock->sk);
@@ -1422,7 +1422,7 @@
 	while (skb->len >= nlmsg_total_size(0)) {
 		nlh = (struct nlmsghdr *) skb->data;
 
-		if (skb->len < nlh->nlmsg_len)
+		if (nlh->nlmsg_len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len)
 			return 0;
 
 		total_len = min(NLMSG_ALIGN(nlh->nlmsg_len), skb->len);
diff -urN oldtree/scripts/lxdialog/colors.h newtree/scripts/lxdialog/colors.h
--- oldtree/scripts/lxdialog/colors.h	2006-01-02 22:21:10.000000000 -0500
+++ newtree/scripts/lxdialog/colors.h	2006-02-13 19:20:01.295315384 -0500
@@ -38,7 +38,7 @@
 #define DIALOG_BG                    COLOR_WHITE
 #define DIALOG_HL                    FALSE
 
-#define TITLE_FG                     COLOR_YELLOW
+#define TITLE_FG                     COLOR_BLUE
 #define TITLE_BG                     COLOR_WHITE
 #define TITLE_HL                     TRUE
 
@@ -110,7 +110,7 @@
 #define ITEM_SELECTED_BG             COLOR_BLUE
 #define ITEM_SELECTED_HL             TRUE
 
-#define TAG_FG                       COLOR_YELLOW
+#define TAG_FG                       COLOR_BLUE
 #define TAG_BG                       COLOR_WHITE
 #define TAG_HL                       TRUE
 
@@ -118,7 +118,7 @@
 #define TAG_SELECTED_BG              COLOR_BLUE
 #define TAG_SELECTED_HL              TRUE
 
-#define TAG_KEY_FG                   COLOR_YELLOW
+#define TAG_KEY_FG                   COLOR_BLUE
 #define TAG_KEY_BG                   COLOR_WHITE
 #define TAG_KEY_HL                   TRUE
 
diff -urN oldtree/scripts/ort.sh newtree/scripts/ort.sh
--- oldtree/scripts/ort.sh	1969-12-31 19:00:00.000000000 -0500
+++ newtree/scripts/ort.sh	2006-02-13 19:20:01.296315232 -0500
@@ -0,0 +1,1089 @@
+#!/bin/sh
+
+# Copyright (C) 2005  Michal Piotrowski <piotrowskim@trex.wsi.edu.pl>
+#                                       <michal.k.k.piotrowski@gmail.com>
+# Copyright (C) 2005  Paolo Ciarrocchi <paolo.ciarrocchi@gmail.com>
+# Copyright (C) 2005  Paul TT <paultt@bilug.linux.it>
+# Copyright (C) 2005  Randy Dunlap <rdunlap@xenotime.net>
+# Copyright (C) 2005  Jesper Juhl <jesper.juhl@gmail.com>
+# Copyright (C) 2005  Cal Peake <cp@absolutedigital.net>
+#
+# 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
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+VER=v.b6
+
+ORT_D=`date +%F`
+ORT_F=ort_oops-$ORT_D.txt
+ORT_O=$1
+
+ORT_P_COPY_D=proc_copy
+ORT_P_COPY_F=$ORT_P_COPY_D-$ORT_D.tar
+ORT_P_COPY_FC=$ORT_P_COPY_F.bz2
+
+ORT_S_COPY_D=sys_copy
+ORT_S_COPY_F=$ORT_S_COPY_D-$ORT_D.tar
+ORT_S_COPY_FC=$ORT_S_COPY_F.bz2
+
+M_EMAIL=nobody@example.org
+U_EMAIL=nobody@example.org
+LKML=linux-kernel@vger.kernel.org
+TOPIC="Automagically generated bug report (ORT)"
+
+KVER=`uname -r`
+
+help() {
+        echo "Usage: $0 oops.txt"
+        exit 1
+}
+
+yes_no() {
+        while true
+        do
+        echo -en "[Y(es)/N(o)] "
+        read REPLY
+        case $REPLY in
+                y | Y | yes | Yes | YES)
+                REPLY=1
+                break
+                ;;
+                n | N | no | No | NO)
+                REPLY=0
+                break
+                ;;
+                *)
+                echo -e "Please answer Y(es) or N(o)"
+                ;;
+        esac
+        done
+}
+
+root_notice() {
+        echo "This step requires root privileges. If you choose \"yes\" you will be prompted"
+        echo "for your root password. If you choose \"no\" your report won't be as detailed."
+        echo "Do you wish to perform with this step?"
+}
+
+cmd_line() {
+        if [[ -z "$ORT_O" || "$ORT_O" = "-h" || "$ORT_O" = "--help" ]]
+        then
+                help
+        elif [[ ! -r "$ORT_O" ]]
+        then
+                echo "ERROR: You must provide a proper file with the Oops text"
+                help
+        fi
+}
+
+rm_ortmp() {
+        rm -f ortmp.txt
+        rm -f t.txt
+        rm -f topic.txt
+}
+
+logo() {
+        clear
+        echo "OOPS Reporting Tool $VER"
+}
+
+check_which() {
+        WHICH=`which $1`
+        if [ "$WHICH" != "" ]
+        then
+                echo -e " [available]"
+        else
+                echo
+        fi
+}
+
+choose_editor() {
+        if [ ! -z "$EDITOR" ]
+        then
+                return
+        fi
+        while true
+        do
+        echo -e "\nChoose your favourite text editor:"
+        echo -e -n "1 - vim"
+        check_which vim
+        echo -e -n "2 - emacs"
+        check_which emacs
+        echo -e -n "3 - mcedit"
+        check_which mcedit
+        echo -e -n "4 - vi"
+        check_which vi
+        echo -e -n "5 - elvis"
+        check_which elvis
+        echo -e -n "6 - joe"
+        check_which joe
+        echo -e -n "7 - jove"
+        check_which jove
+        echo -e -n "8 - nano"
+        check_which nano
+        echo -e -n "9 - pico"
+        check_which pico
+        echo -e "o - other"
+        echo -en ": "
+        read TXT
+        case "$TXT" in
+        1 )
+                EDITOR=vim
+                break
+                ;;
+        2 )
+                EDITOR=emacs
+                break
+                ;;
+        3 )
+                EDITOR=mcedit
+                break
+                ;;
+        4 )
+                EDITOR=vi
+                break
+                ;;
+        5 )
+                EDITOR=elvis
+                break
+                ;;
+        6 )
+                EDITOR=joe
+                break
+                ;;
+        7 )
+                EDITOR=jove
+                break
+                ;;
+        8 )
+                EDITOR=nano
+                break
+                ;;
+        9 )
+                EDITOR=pico
+                break
+                ;;
+        o | O )
+                while true
+                do
+                echo -en "\nWrite editor name: "
+                read EDITOR
+                echo -e "You wrote < $EDITOR > is this correct?"
+                yes_no
+                if [ $REPLY = 1 ]
+                then
+                        break
+                fi
+                done
+                break
+                ;;
+        esac
+        done
+}
+
+choose_pager() {
+        if [ ! -z "$PAGER" ]
+        then
+                return
+        fi
+        while true
+        do
+        echo -e "\nChoose your favourite pager:"
+        echo -e -n "1 - less"
+        check_which less
+        echo -e -n "2 - more"
+        check_which more
+        echo -e -n "3 - most"
+        check_which most
+        echo -e "o - other"
+        echo -en ": "
+        read TXT
+        case "$TXT" in
+        1 )
+                PAGER=less
+                break
+                ;;
+        2 )
+                PAGER=more
+                break
+                ;;
+        3 )
+                PAGER=most
+                break
+                ;;
+        o | O )
+                while true
+                do
+                echo -en "\nWrite pager name: "
+                read PAGER
+                echo -e "You wrote < $PAGER > is this correct?"
+                yes_no
+                if [ $REPLY = 1 ]
+                then
+                        break
+                fi
+                done
+                break
+                ;;
+        esac
+        done
+}
+
+choose_em_cli() {
+        while true
+        do
+        echo -e "\nChoose your favourite (and configured) email client:"
+        echo -e -n "1 - mutt"
+        check_which mutt
+        echo -e -n "2 - pine"
+        check_which pine
+        echo -e -n "3 - nail"
+        check_which nail
+        echo -e -n "4 - sendmail"
+        check_which sendmail
+        echo -e "o - other"
+        echo -en ": "
+        read TXT
+        case "$TXT" in
+        1 )
+                MUA=send_mutt
+                break
+                ;;
+        2 )
+                MUA=send_pine
+                break
+                ;;
+        3 )
+                MUA=send_nail
+                break
+                ;;
+        4 )
+                MUA=send_sendmail
+                break
+                ;;
+        o | O )
+                while true
+                do
+                echo -en "\nWrite email client name: "
+                read EM_CLI
+                echo -e "You wrote < $EM_CLI > is this correct?"
+                yes_no
+                if [ $REPLY = 1 ]
+                then
+                        break
+                fi
+                done
+                break
+                ;;
+        esac
+        done
+}
+
+txt_read() {
+        while true
+        do
+        read TXT
+        if [ "$TXT" == "." ]
+        then
+                break
+        fi
+        echo "$TXT" >> $ORT_F
+        done
+}
+
+txt_read_ed() {
+        read TXT
+        echo $TXT > ortmp.txt
+        $EDITOR ortmp.txt
+        cat ortmp.txt >> $ORT_F
+}
+
+point_1() {
+        echo -e "\n[1.] One or more line summary of the problem: (end with a line containing only a '.')"
+        echo -e "\n[1.] One line summary of the problem:" > $ORT_F
+        txt_read
+}
+
+point_2() {
+        echo "[2.] Full description of the problem/report: (end with a line containing only a '.')"
+        echo -e "\n[2.] Full description of the problem/report:" >> $ORT_F
+        txt_read
+}
+
+point_3() {
+        echo "[3.] Keywords (i.e., modules, networking, kernel): (press Return to continue)"
+        echo -e "\n[3.] Keywords (i.e., modules, networking, kernel):"  >> $ORT_F
+        txt_read_ed
+        cp ortmp.txt topic.txt
+}
+
+point_4() {
+        echo -e "\n[4.] Kernel version (from /proc/version):"
+        echo -e "\n[4.] Kernel version (from /proc/version):" >> $ORT_F
+        cat /proc/version >> $ORT_F
+}
+
+point_5() {
+        echo -e "\n[5.] Output of Oops"
+        echo -e "\n[5.] Output of Oops" >> $ORT_F
+        cat $ORT_O >> $ORT_F
+        TAINTED=`cat $ORT_O | grep "Tainted:"`
+        NULL="t.txt";
+        if [ "$TAINTED" > "$NULL" ]
+        then
+                echo "Tainted kernel!!!"
+                echo "Please reproduce with an untainted kernel before you send the report to LKML."
+                echo "This script will not allow you to send a report for a tainted kernel."
+                LKML=banned@banned.org
+        fi
+}
+
+point_6() {
+        echo -e "\n[6.] A small shell script or example program which triggers the problem (if possible): (end with a line containing only a '.')"
+        echo -e "\n\n[6.] A small shell script or example program which triggers the problem (if possible):" >> $ORT_F
+        txt_read
+}
+
+point_7_1() {
+        echo -e "\n[7.] Environment"
+        echo -e "\n[7.] Environment" >> $ORT_F
+        echo -e "\n[7.1.] Software" >> $ORT_F
+        /lib/modules/$KVER/build/scripts/ver_linux >> $ORT_F
+}
+
+point_7_2() {
+        echo -e "\n[7.2.] Processor information" >> $ORT_F
+        cat /proc/cpuinfo >> $ORT_F
+}
+
+point_7_3() {
+        echo -e "\n[7.3.] Module information" >> $ORT_F
+        cat /proc/modules >> $ORT_F
+}
+
+point_7_4() {
+        echo -e "\n[7.4.] Loaded driver and hardware information" >> $ORT_F
+        cat /proc/ioports >> $ORT_F
+        cat /proc/iomem >> $ORT_F
+}
+
+point_7_5() {
+        echo -e "\n[7.5.] PCI information"
+        echo -e "\n[7.5.] PCI information" >> $ORT_F
+        root_notice
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+                su -c "lspci -vvv >> $ORT_F"
+        else
+                /sbin/lspci >> $ORT_F
+        fi
+}
+
+point_7_6() {
+        if [ -f /proc/scsi/scsi ]; then
+                echo -e "\n[7.6.] SCSI information" >> $ORT_F
+                cat /proc/scsi/scsi >> $ORT_F
+        fi
+}
+
+point_7_7() {
+        echo -e "\n[7.7.] USB information"
+        echo -e "\n[7.7.] USB information" >> $ORT_F
+        root_notice
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+        su -c "lsusb -v >> $ORT_F"
+        else
+                /sbin/lsusb >> $ORT_F
+        fi
+}
+
+point_7_8() {
+        echo -e "\n[7.8.] dmesg log" >> $ORT_F
+        dmesg -s 100000 >> $ORT_F
+}
+
+point_7_9() {
+        echo -e "\n[7.9.] /proc copy"
+        echo -e "\nDo you want to create a copy of /proc ?"
+        yes_no
+        if [ $REPLY = 0 ]
+        then
+                return
+        fi
+        if [ -d $ORT_P_COPY_D ]
+        then
+                rm -rf $ORT_P_COPY_D
+        fi
+        if [ -f $ORT_P_COPY_F ]
+        then
+                rm $ORT_P_COPY_F
+        fi
+        if [ -f $ORT_P_COPY_FC ]
+        then
+                rm $ORT_P_COPY_FC
+        fi
+        mkdir -p $ORT_P_COPY_D
+#cp -R /proc/acpi $ORT_P_COPY_D &>/dev/null
+        cp -R /proc/asound $ORT_P_COPY_D &>/dev/null
+        cp -R /proc/bus $ORT_P_COPY_D &>/dev/null
+        cp -R /proc/driver $ORT_P_COPY_D &>/dev/null
+        cp -R /proc/fs $ORT_P_COPY_D &>/dev/null
+        cp -R /proc/ide $ORT_P_COPY_D &>/dev/null
+        cp -R /proc/irq $ORT_P_COPY_D &>/dev/null
+        cp -R /proc/net $ORT_P_COPY_D &>/dev/null
+        cp -R /proc/scsi $ORT_P_COPY_D &>/dev/null
+        cp -R /proc/sys/debug $ORT_P_COPY_D/sys &>/dev/null
+        cp -R /proc/sys/dev $ORT_P_COPY_D/sys &>/dev/null
+        cp -R /proc/sys/fs $ORT_P_COPY_D/sys &>/dev/null
+        cp -R /proc/sys/kernel $ORT_P_COPY_D/sys &>/dev/null
+        cp -R /proc/sys/proc $ORT_P_COPY_D/sys &>/dev/null
+        cp -R /proc/sys/vm $ORT_P_COPY_D/sys &>/dev/null
+        cp -R /proc/sysvipc $ORT_P_COPY_D &>/dev/null
+        cp -R /proc/tty $ORT_P_COPY_D &>/dev/null
+        cp /proc/buddyinfo $ORT_P_COPY_D &>/dev/null
+        cp /proc/cmdline $ORT_P_COPY_D &>/dev/null
+        cp /proc/config.gz $ORT_P_COPY_D &>/dev/null
+        cp /proc/cpuinfo $ORT_P_COPY_D &>/dev/null
+        cp /proc/crypto $ORT_P_COPY_D &>/dev/null
+        cp /proc/devices $ORT_P_COPY_D &>/dev/null
+        cp /proc/diskstats $ORT_P_COPY_D &>/dev/null
+        cp /proc/dma $ORT_P_COPY_D &>/dev/null
+        cp /proc/execdomains $ORT_P_COPY_D &>/dev/null
+        cp /proc/filesystems $ORT_P_COPY_D &>/dev/null
+        cp /proc/interrupts $ORT_P_COPY_D &>/dev/null
+        cp /proc/iomem $ORT_P_COPY_D &>/dev/null
+        cp /proc/ioports $ORT_P_COPY_D &>/dev/null
+        cp /proc/kallsyms $ORT_P_COPY_D &>/dev/null
+        cp /proc/key-users $ORT_P_COPY_D &>/dev/null
+        cp /proc/keys $ORT_P_COPY_D &>/dev/null
+        cp /proc/loadavg $ORT_P_COPY_D &>/dev/null
+        cp /proc/locks $ORT_P_COPY_D &>/dev/null
+        cp /proc/meminfo $ORT_P_COPY_D &>/dev/null
+        cp /proc/misc $ORT_P_COPY_D &>/dev/null
+        cp /proc/modules $ORT_P_COPY_D &>/dev/null
+        cp /proc/mounts $ORT_P_COPY_D &>/dev/null
+        cp /proc/mtrr $ORT_P_COPY_D &>/dev/null
+        cp /proc/partitions $ORT_P_COPY_D &>/dev/null
+        cp /proc/pci $ORT_P_COPY_D &>/dev/null
+        cp /proc/schedstat $ORT_P_COPY_D &>/dev/null
+        cp /proc/slabinfo $ORT_P_COPY_D &>/dev/null
+        cp /proc/stat $ORT_P_COPY_D &>/dev/null
+        cp /proc/swaps $ORT_P_COPY_D &>/dev/null
+        cp /proc/sysrq-trigger $ORT_P_COPY_D &>/dev/null
+        cp /proc/uptime $ORT_P_COPY_D &>/dev/null
+        cp /proc/version $ORT_P_COPY_D &>/dev/null
+        cp /proc/vmstat $ORT_P_COPY_D &>/dev/null
+        tar cf $ORT_P_COPY_F $ORT_P_COPY_D
+        bzip2 -9 $ORT_P_COPY_F
+        echo "/proc copy = $ORT_P_COPY_FC"
+}
+
+point_7_10() {
+        echo -e "\n[7.10.] /sys copy"
+        echo -e "\nDo you want to create a copy of /sys ?"
+        yes_no
+        if [ $REPLY = 0 ]
+        then
+                return
+        fi
+        if [ -d $ORT_S_COPY_D ]
+        then
+                rm -rf $ORT_S_COPY_D
+        fi
+        if [ -f $ORT_S_COPY_F ]
+        then
+                rm -f $ORT_S_COPY_F
+        fi
+        if [ -f $ORT_S_COPY_FC ]
+        then
+                rm -f $ORT_S_COPY_FC
+        fi
+        mkdir -p $ORT_S_COPY_D
+        cp -R /sys/ $ORT_S_COPY_D &>/dev/null
+        tar cf $ORT_S_COPY_F $ORT_S_COPY_D
+        bzip2 -9 $ORT_S_COPY_F
+        echo "/sys copy = $ORT_S_COPY_FC"
+}
+
+point_7_11() {
+        echo -e "\n[7.11.] sysctl -A" >> $ORT_F
+        sysctl -A >> $ORT_F
+}
+
+point_8() {
+        echo -e "\n[8.] Config file"
+        echo -e "\n[8.] Config file" >> $ORT_F
+        while true
+        do
+        echo -e "\nDo you want to manually enter the path to the kernel's .config file?"
+        echo -en "[A(utomagic)/Y(es)/S(kip)] "
+        read TXT
+        if [ $TXT = "Y" ] || [ $TXT = "y" ]
+        then
+                echo "Enter path: (eg. /usr/src/linux-2.6)"
+                read TXT
+                if [ -r $TXT/.config ]
+                then
+                        cat $TXT/.config >> $ORT_F
+                        break
+                else
+                        echo "ERROR: Invalid path"
+                fi
+        elif [ $TXT = "A" ] || [ $TXT = "a" ]
+        then
+                if [ -r /proc/config.gz ];
+                then
+                        gzip -cd /proc/config.gz >> $ORT_F
+                        break
+                elif [ -r /boot/config-$KVER ]
+                then
+                        cat /boot/config-$KVER >> $ORT_F
+                        break
+                elif [ -r /lib/modules/$KVER/build/.config ]
+                then
+                        cat /lib/modules/$KVER/build/.config >> $ORT_F
+                        break
+                else
+                        echo "ERROR: Can't find kernel config file"
+                fi
+        elif [ $TXT = "S" ] || [ $TXT = "s" ]
+        then
+                break
+        fi
+        done
+}
+
+point_9() {
+        echo -e "\n[9.] Other notes, patches, fixes, workarounds: (end with a line containing only a '.')"
+        echo -e "\n[9.] Other notes, patches, fixes, workarounds:" >> $ORT_F
+        txt_read
+}
+
+r_type_short() {
+        point_1
+        point_2
+        point_3
+        point_4
+        point_5
+}
+
+r_type_long() {
+        r_type_short
+
+        point_6
+        point_7_1
+        point_7_2
+        point_7_3
+        point_7_4
+        point_7_5
+        point_7_6
+        point_7_7
+        point_7_8
+        point_7_9
+        point_7_10
+        point_7_11
+        point_8
+        point_9
+}
+
+r_type_custom() {
+        r_type_short
+
+        while true
+        do
+        echo -e "\n[6.] A small shell script..."
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+                point_6
+                break
+        else
+                break
+        fi
+        done
+
+        while true
+        do
+        echo -e "\n[7.1.] Software..."
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+                point_7_1
+                break
+        else
+                break
+        fi
+        done
+
+        while true
+        do
+        echo -e "\n[7.2.] Processor..."
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+                point_7_2
+                break
+        else
+                break
+        fi
+        done
+
+        while true
+        do
+        echo -e "\n[7.3.] Module..."
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+                point_7_3
+                break
+        else
+                break
+        fi
+        done
+
+        while true
+        do
+        echo -e "\n[7.4.] Loaded driver..."
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+                point_7_4
+                break
+        else
+                break
+        fi
+        done
+
+        while true
+        do
+        echo -e "\n[7.5.] PCI information..."
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+                point_7_5
+                break
+        else
+                break
+        fi
+        done
+
+        while true
+        do
+        echo -e "\n[7.6.] SCSI..."
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+                point_7_6
+                break
+        else
+                break
+        fi
+        done
+
+        while true
+        do
+        echo -e "\n[7.7.] USB..."
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+                point_7_7
+                break
+        else
+                break
+        fi
+        done
+
+        while true
+        do
+        echo -e "\n[7.8.] dmesg..."
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+                point_7_8
+                break
+        else
+                break
+        fi
+        done
+
+        while true
+        do
+        echo -e "\n[7.9.] /proc copy..."
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+                point_7_9
+                break
+        else
+                break
+        fi
+        done
+
+        while true
+        do
+        echo -e "\n[7.10.] /sys copy..."
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+                point_7_10
+                break
+        else
+                break
+        fi
+        done
+
+        while true
+        do
+        echo -e "\n[7.11.] sysctl..."
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+                point_7_11
+                break
+        else
+                break
+        fi
+        done
+
+        while true
+        do
+        echo -e "\n[8.] Config..."
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+                point_8
+                break
+        else
+                break
+        fi
+        done
+
+        while true
+        do
+        echo -e "\n[9.] Other notes..."
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+                point_9
+                break
+        else
+                break
+        fi
+        done
+}
+
+r_type_template_pci() {
+        r_type_short
+
+        point_7_1
+        point_7_3
+        point_7_4
+        point_7_5
+}
+
+r_type_template_usb() {
+        r_type_short
+
+        point_7_1
+        point_7_3
+        point_7_4
+        point_7_7
+}
+
+r_type_template() {
+        while true
+        do
+        echo -e "\nChoose template"
+        echo -e "1 - PCI"
+        echo -e "2 - USB"
+        read TXT
+        case "$TXT" in
+        1 )
+                r_type_template_pci
+                break
+                ;;
+        2 )
+                r_type_template_usb
+                break
+                ;;
+        * )
+                ;;
+        esac
+        done
+}
+
+choose_r_type() {
+        while true
+        do
+        echo -e "\nChoose report type"
+        echo -e "(S)hort/(L)ong/(C)ustom/(T)emplate"
+        read TXT
+        case "$TXT" in
+        s | S )
+                r_type_short
+                break
+                ;;
+        l | L )
+                r_type_long
+                break
+                ;;
+        c | C )
+                r_type_custom
+                break
+                ;;
+        t | T )
+                r_type_template
+                break
+                ;;
+        * )
+                ;;
+        esac
+        done
+}
+
+ort_sig() {
+cat << EOF >> $ORT_F
+
+
+OOPS Reporting Tool $VER
+www.wsi.edu.pl/~piotrowskim/
+/files/ort/beta/
+EOF
+}
+
+less_r() {
+        echo -e "\nDo you want to see $ORT_F?"
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+                $PAGER $ORT_F
+        fi
+}
+
+edit_r() {
+        echo -e "\nDo you want to edit $ORT_F in $EDITOR?"
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+                $EDITOR $ORT_F
+        fi
+}
+
+get_m_email() {
+        echo -e "\nDo you want to list the MAINTAINERS file to locate the proper maintainer?"
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+                if [ -f /lib/modules/$KVER/build/MAINTAINERS ]
+                then
+                        $PAGER /lib/modules/$KVER/build/MAINTAINERS
+                else
+                        echo "ERROR: Can't find MAINTAINERS file"
+                fi
+        fi
+
+        while true
+        do
+        echo -en "\nWrite maintainer e-mail: "
+        read M_EMAIL
+        echo -e "You wrote < $M_EMAIL > is this correct?"
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+                break
+        fi
+        done
+}
+
+get_u_email() {
+        while true
+        do
+        echo -e "\nWrite your e-mail (to be used as from address)"
+        echo -e "Please make sure it's correct so people can get back to you."
+        echo -en ": "
+        read U_EMAIL
+        echo -e "You wrote < $U_EMAIL > is this correct?"
+        yes_no
+        if [ $REPLY = 1 ]
+        then
+        break
+        fi
+        done
+}
+
+# send_MUA(subject, body, recipient[, recipient ...])
+
+send_mutt() {
+        if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]
+        then
+                echo "Failed"
+                return
+        fi
+
+        SUBJ="$1"
+        shift
+        BODY="$1"
+        shift
+
+        while [ ! -z "$1" ]
+        do
+        RCPTS="$RCPTS $1"
+        shift
+        done
+
+# XXX: this needs to be tested
+        mutt -s "$SUBJ" -i $BODY $RCPTS
+}
+
+send_pine() {
+        if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]
+        then
+                echo "Failed"
+                return
+        fi
+
+        SUBJ="$1"
+        shift
+        BODY="$1"
+        shift
+
+        while [ ! -z "$1" ]
+        do
+        RCPTS="$RCPTS,$1"
+        shift
+        done
+
+# pine can't specify the subject nor the body from the command line,
+# we can attach however, but should we?
+        pine -attach $BODY "$RCPTS"
+}
+
+send_nail() {
+        if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]
+        then
+                echo "Failed"
+                return
+        fi
+
+        SUBJ="$1"
+        shift
+        BODY="$1"
+        shift
+
+        while [ ! -z "$1" ]
+        do
+        RCPTS="$RCPTS $1"
+        shift
+        done
+
+# XXX: do we want to set the from for nail ?
+#get_u_email
+#nail -s "$SUBJ" -q $BODY -r $U_EMAIL "$RCPTS"
+
+        nail -s "$SUBJ" -q $BODY "$RCPTS"
+}
+
+send_sendmail() {
+        if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]
+        then
+                echo "Failed"
+                return
+        fi
+
+        SUBJ="$1"
+        shift
+        BODY="$1"
+        shift
+
+        while [ ! -z "$1" ]
+        do
+        TO="$TO,$1"
+        RCPTS="$RCPTS $1"
+        shift
+        done
+
+        get_u_email
+        echo -e "To:${TO}\nSubject: ${SUBJ}\n`cat ${ORT_F}`\n" | sendmail -f $U_EMAIL $RCPTS
+}
+
+send_custom() {
+# XXX: untested but this should work for a number of mail clients
+        $EM_CLI "mailto:${RCPTS}?subject=${SUBJ}&body=`cat ${BODY}`"
+}
+
+send_r() {
+        while true
+        do
+        echo -e "\nDo you want to send $ORT_F ?"
+        yes_no
+        if [ $REPLY = 0 ]
+        then
+                echo -e "\nPlease email $ORT_F to $LKML"
+                echo -e "or some other appropriate Linux mailing list."
+                break
+        fi
+        if [ ! -z "$EM_CLI" ]
+        then
+                MUA=send_custom
+        fi
+        while true
+        do
+        echo -e "1 - send to LKML"
+        echo -e "2 - send to maintainer"
+        echo -e "3 - send to maintainer and LKML"
+        read TXT_1
+        case "$TXT_1" in
+        1 )
+                $MUA "[OOPS] $TOPIC" $ORT_F $LKML
+                break
+                ;;
+        2 )
+                get_m_email
+                $MUA "[OOPS] $TOPIC" $ORT_F $M_EMAIL
+                break
+                ;;
+        3 )
+                get_m_email
+                $MUA "[OOPS] $TOPIC" $ORT_F $LKML $M_EMAIL
+                break
+                ;;
+        esac
+        done
+        break
+        done
+        echo -e "And please, use this informations to file a BUG in the kernel Bugzilla you'll"
+        echo -e "find at bugme.osdl.org.\nThanks\n"
+}
+
+cmd_line
+rm_ortmp
+logo
+
+choose_editor
+choose_pager
+choose_em_cli
+
+choose_r_type
+
+ort_sig
+
+less_r
+edit_r
+send_r
+
+rm_ortmp
+
+# end
diff -urN oldtree/security/dummy.c newtree/security/dummy.c
--- oldtree/security/dummy.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/security/dummy.c	2006-02-13 19:20:01.297315080 -0500
@@ -377,7 +377,7 @@
 	return 0;
 }
 
-static int dummy_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err)
+static int dummy_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err)
 {
 	return -EOPNOTSUPP;
 }
@@ -392,6 +392,11 @@
 	return 0;
 }
 
+static const char *dummy_inode_xattr_getsuffix(void)
+{
+	return NULL;
+}
+
 static int dummy_file_permission (struct file *file, int mask)
 {
 	return 0;
@@ -557,6 +562,11 @@
 	return 0;
 }
 
+static int dummy_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size)
+{
+	return -EOPNOTSUPP;
+}
+
 static int dummy_msg_msg_alloc_security (struct msg_msg *msg)
 {
 	return 0;
@@ -890,6 +900,7 @@
 	set_to_dummy_if_null(ops, inode_getxattr);
 	set_to_dummy_if_null(ops, inode_listxattr);
 	set_to_dummy_if_null(ops, inode_removexattr);
+	set_to_dummy_if_null(ops, inode_xattr_getsuffix);
 	set_to_dummy_if_null(ops, inode_getsecurity);
 	set_to_dummy_if_null(ops, inode_setsecurity);
 	set_to_dummy_if_null(ops, inode_listsecurity);
@@ -924,6 +935,7 @@
 	set_to_dummy_if_null(ops, task_reparent_to_init);
  	set_to_dummy_if_null(ops, task_to_inode);
 	set_to_dummy_if_null(ops, ipc_permission);
+	set_to_dummy_if_null(ops, ipc_getsecurity);
 	set_to_dummy_if_null(ops, msg_msg_alloc_security);
 	set_to_dummy_if_null(ops, msg_msg_free_security);
 	set_to_dummy_if_null(ops, msg_queue_alloc_security);
diff -urN oldtree/security/keys/keyctl.c newtree/security/keys/keyctl.c
--- oldtree/security/keys/keyctl.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/security/keys/keyctl.c	2006-02-13 19:20:30.111934592 -0500
@@ -66,9 +66,10 @@
 	description = kmalloc(dlen + 1, GFP_KERNEL);
 	if (!description)
 		goto error;
+	description[dlen] = '\0';
 
 	ret = -EFAULT;
-	if (copy_from_user(description, _description, dlen + 1) != 0)
+	if (copy_from_user(description, _description, dlen) != 0)
 		goto error2;
 
 	/* pull the payload in if one was supplied */
@@ -160,9 +161,10 @@
 	description = kmalloc(dlen + 1, GFP_KERNEL);
 	if (!description)
 		goto error;
+	description[dlen] = '\0';
 
 	ret = -EFAULT;
-	if (copy_from_user(description, _description, dlen + 1) != 0)
+	if (copy_from_user(description, _description, dlen) != 0)
 		goto error2;
 
 	/* pull the callout info into kernel space */
@@ -181,9 +183,10 @@
 		callout_info = kmalloc(dlen + 1, GFP_KERNEL);
 		if (!callout_info)
 			goto error2;
+		callout_info[dlen] = '\0';
 
 		ret = -EFAULT;
-		if (copy_from_user(callout_info, _callout_info, dlen + 1) != 0)
+		if (copy_from_user(callout_info, _callout_info, dlen) != 0)
 			goto error3;
 	}
 
@@ -278,9 +281,10 @@
 		name = kmalloc(nlen + 1, GFP_KERNEL);
 		if (!name)
 			goto error;
+		name[nlen] = '\0';
 
 		ret = -EFAULT;
-		if (copy_from_user(name, _name, nlen + 1) != 0)
+		if (copy_from_user(name, _name, nlen) != 0)
 			goto error2;
 	}
 
@@ -582,9 +586,10 @@
 	description = kmalloc(dlen + 1, GFP_KERNEL);
 	if (!description)
 		goto error;
+	description[dlen] = '\0';
 
 	ret = -EFAULT;
-	if (copy_from_user(description, _description, dlen + 1) != 0)
+	if (copy_from_user(description, _description, dlen) != 0)
 		goto error2;
 
 	/* get the keyring at which to begin the search */
diff -urN oldtree/security/seclvl.c newtree/security/seclvl.c
--- oldtree/security/seclvl.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/security/seclvl.c	2006-02-13 19:20:30.112934440 -0500
@@ -369,7 +369,7 @@
 static int seclvl_settime(struct timespec *tv, struct timezone *tz)
 {
 	struct timespec now;
-	if (seclvl > 1) {
+	if (tv && seclvl > 1) {
 		now = current_kernel_time();
 		if (tv->tv_sec < now.tv_sec ||
 		    (tv->tv_sec == now.tv_sec && tv->tv_nsec < now.tv_nsec)) {
diff -urN oldtree/security/selinux/hooks.c newtree/security/selinux/hooks.c
--- oldtree/security/selinux/hooks.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/security/selinux/hooks.c	2006-02-13 19:20:01.299314776 -0500
@@ -116,6 +116,32 @@
 static LIST_HEAD(superblock_security_head);
 static DEFINE_SPINLOCK(sb_security_lock);
 
+/* Return security context for a given sid or just the context 
+   length if the buffer is null or length is 0 */
+static int selinux_getsecurity(u32 sid, void *buffer, size_t size)
+{
+	char *context;
+	unsigned len;
+	int rc;
+
+	rc = security_sid_to_context(sid, &context, &len);
+	if (rc)
+		return rc;
+
+	if (!buffer || !size)
+		goto getsecurity_exit;
+
+	if (size < len) {
+		len = -ERANGE;
+		goto getsecurity_exit;
+	}
+	memcpy(buffer, context, len);
+
+getsecurity_exit:
+	kfree(context);
+	return len;
+}
+
 /* Allocate and free functions for each kind of security blob. */
 
 static int task_alloc_security(struct task_struct *task)
@@ -2238,6 +2264,11 @@
 	return -EACCES;
 }
 
+static const char *selinux_inode_xattr_getsuffix(void)
+{
+      return XATTR_SELINUX_SUFFIX;
+}
+
 /*
  * Copy the in-core inode security context value to the user.  If the
  * getxattr() prior to this succeeded, check to see if we need to
@@ -2245,47 +2276,14 @@
  *
  * Permission check is handled by selinux_inode_getxattr hook.
  */
-static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err)
+static int selinux_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err)
 {
 	struct inode_security_struct *isec = inode->i_security;
-	char *context;
-	unsigned len;
-	int rc;
-
-	if (strcmp(name, XATTR_SELINUX_SUFFIX)) {
-		rc = -EOPNOTSUPP;
-		goto out;
-	}
-
-	rc = security_sid_to_context(isec->sid, &context, &len);
-	if (rc)
-		goto out;
-
-	/* Probe for required buffer size */
-	if (!buffer || !size) {
-		rc = len;
-		goto out_free;
-	}
 
-	if (size < len) {
-		rc = -ERANGE;
-		goto out_free;
-	}
+	if (strcmp(name, XATTR_SELINUX_SUFFIX))
+		return -EOPNOTSUPP;
 
-	if (err > 0) {
-		if ((len == err) && !(memcmp(context, buffer, len))) {
-			/* Don't need to canonicalize value */
-			rc = err;
-			goto out_free;
-		}
-		memset(buffer, 0, size);
-	}
-	memcpy(buffer, context, len);
-	rc = len;
-out_free:
-	kfree(context);
-out:
-	return rc;
+	return selinux_getsecurity(isec->sid, buffer, size);
 }
 
 static int selinux_inode_setsecurity(struct inode *inode, const char *name,
@@ -4054,6 +4052,13 @@
 	return ipc_has_perm(ipcp, av);
 }
 
+static int selinux_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size)
+{
+	struct ipc_security_struct *isec = ipcp->security;
+
+	return selinux_getsecurity(isec->sid, buffer, size);
+}
+
 /* module stacking operations */
 static int selinux_register_security (const char *name, struct security_operations *ops)
 {
@@ -4095,8 +4100,7 @@
 			       char *name, void *value, size_t size)
 {
 	struct task_security_struct *tsec;
-	u32 sid, len;
-	char *context;
+	u32 sid;
 	int error;
 
 	if (current != p) {
@@ -4105,9 +4109,6 @@
 			return error;
 	}
 
-	if (!size)
-		return -ERANGE;
-
 	tsec = p->security;
 
 	if (!strcmp(name, "current"))
@@ -4124,16 +4125,7 @@
 	if (!sid)
 		return 0;
 
-	error = security_sid_to_context(sid, &context, &len);
-	if (error)
-		return error;
-	if (len > size) {
-		kfree(context);
-		return -ERANGE;
-	}
-	memcpy(value, context, len);
-	kfree(context);
-	return len;
+	return selinux_getsecurity(sid, value, size);
 }
 
 static int selinux_setprocattr(struct task_struct *p,
@@ -4291,6 +4283,7 @@
 	.inode_getxattr =		selinux_inode_getxattr,
 	.inode_listxattr =		selinux_inode_listxattr,
 	.inode_removexattr =		selinux_inode_removexattr,
+	.inode_xattr_getsuffix =        selinux_inode_xattr_getsuffix,
 	.inode_getsecurity =            selinux_inode_getsecurity,
 	.inode_setsecurity =            selinux_inode_setsecurity,
 	.inode_listsecurity =           selinux_inode_listsecurity,
@@ -4328,6 +4321,7 @@
 	.task_to_inode =                selinux_task_to_inode,
 
 	.ipc_permission =		selinux_ipc_permission,
+	.ipc_getsecurity =		selinux_ipc_getsecurity,
 
 	.msg_msg_alloc_security =	selinux_msg_msg_alloc_security,
 	.msg_msg_free_security =	selinux_msg_msg_free_security,
diff -urN oldtree/security/selinux/nlmsgtab.c newtree/security/selinux/nlmsgtab.c
--- oldtree/security/selinux/nlmsgtab.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/security/selinux/nlmsgtab.c	2006-02-13 19:20:01.299314776 -0500
@@ -145,8 +145,10 @@
 		break;
 
 	case SECCLASS_NETLINK_AUDIT_SOCKET:
-		if (nlmsg_type >= AUDIT_FIRST_USER_MSG &&
-		    nlmsg_type <= AUDIT_LAST_USER_MSG) {
+		if ((nlmsg_type >= AUDIT_FIRST_USER_MSG &&
+		     nlmsg_type <= AUDIT_LAST_USER_MSG) ||
+		    (nlmsg_type >= AUDIT_FIRST_USER_MSG2 &&
+                     nlmsg_type <= AUDIT_LAST_USER_MSG2)) {
 			*perm = NETLINK_AUDIT_SOCKET__NLMSG_RELAY;
 		} else {
 			err = nlmsg_perm(nlmsg_type, perm, nlmsg_audit_perms,
diff -urN oldtree/security/selinux/selinuxfs.c newtree/security/selinux/selinuxfs.c
--- oldtree/security/selinux/selinuxfs.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/security/selinux/selinuxfs.c	2006-02-13 19:20:01.300314624 -0500
@@ -21,6 +21,7 @@
 #include <linux/major.h>
 #include <linux/seq_file.h>
 #include <linux/percpu.h>
+#include <linux/audit.h>
 #include <asm/uaccess.h>
 #include <asm/semaphore.h>
 
@@ -126,6 +127,10 @@
 		length = task_has_security(current, SECURITY__SETENFORCE);
 		if (length)
 			goto out;
+		audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS,
+			"enforcing=%d old_enforcing=%d auid=%u", new_value, 
+			selinux_enforcing,
+			audit_get_loginuid(current->audit_context));
 		selinux_enforcing = new_value;
 		if (selinux_enforcing)
 			avc_ss_reset(0);
@@ -176,6 +181,9 @@
 		length = selinux_disable();
 		if (length < 0)
 			goto out;
+		audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS,
+			"selinux=0 auid=%u",
+			audit_get_loginuid(current->audit_context));
 	}
 
 	length = count;
@@ -261,6 +269,9 @@
 		length = ret;
 	else
 		length = count;
+	audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD,
+		"policy loaded auid=%u",
+		audit_get_loginuid(current->audit_context));
 out:
 	up(&sel_sem);
 	vfree(data);
diff -urN oldtree/security/selinux/ss/services.c newtree/security/selinux/ss/services.c
--- oldtree/security/selinux/ss/services.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/security/selinux/ss/services.c	2006-02-13 19:20:01.301314472 -0500
@@ -1758,19 +1758,22 @@
 		goto out;
 	}
 
-	printk(KERN_INFO "security: committed booleans { ");
 	for (i = 0; i < len; i++) {
+		if (!!values[i] != policydb.bool_val_to_struct[i]->state) {
+			audit_log(current->audit_context, GFP_ATOMIC,
+				AUDIT_MAC_CONFIG_CHANGE,
+				"bool=%s val=%d old_val=%d auid=%u",
+				policydb.p_bool_val_to_name[i],
+				!!values[i],
+				policydb.bool_val_to_struct[i]->state,
+				audit_get_loginuid(current->audit_context));
+		}
 		if (values[i]) {
 			policydb.bool_val_to_struct[i]->state = 1;
 		} else {
 			policydb.bool_val_to_struct[i]->state = 0;
 		}
-		if (i != 0)
-			printk(", ");
-		printk("%s:%d", policydb.p_bool_val_to_name[i],
-		       policydb.bool_val_to_struct[i]->state);
 	}
-	printk(" }\n");
 
 	for (cur = policydb.cond_list; cur != NULL; cur = cur->next) {
 		rc = evaluate_cond_node(&policydb, cur);
diff -urN oldtree/sound/core/info.c newtree/sound/core/info.c
--- oldtree/sound/core/info.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/sound/core/info.c	2006-02-13 19:20:01.301314472 -0500
@@ -444,8 +444,8 @@
 	return mask;
 }
 
-static inline int _snd_info_entry_ioctl(struct inode *inode, struct file *file,
-					unsigned int cmd, unsigned long arg)
+static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
+				unsigned long arg)
 {
 	snd_info_private_data_t *data;
 	struct snd_info_entry *entry;
@@ -465,17 +465,6 @@
 	return -ENOTTY;
 }
 
-/* FIXME: need to unlock BKL to allow preemption */
-static int snd_info_entry_ioctl(struct inode *inode, struct file *file,
-				unsigned int cmd, unsigned long arg)
-{
-	int err;
-	unlock_kernel();
-	err = _snd_info_entry_ioctl(inode, file, cmd, arg);
-	lock_kernel();
-	return err;
-}
-
 static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	struct inode *inode = file->f_dentry->d_inode;
@@ -499,15 +488,15 @@
 
 static struct file_operations snd_info_entry_operations =
 {
-	.owner =	THIS_MODULE,
-	.llseek =	snd_info_entry_llseek,
-	.read =		snd_info_entry_read,
-	.write =	snd_info_entry_write,
-	.poll =		snd_info_entry_poll,
-	.ioctl =	snd_info_entry_ioctl,
-	.mmap =		snd_info_entry_mmap,
-	.open =		snd_info_entry_open,
-	.release =	snd_info_entry_release,
+	.owner =		THIS_MODULE,
+	.llseek =		snd_info_entry_llseek,
+	.read =			snd_info_entry_read,
+	.write =		snd_info_entry_write,
+	.poll =			snd_info_entry_poll,
+	.unlocked_ioctl =	snd_info_entry_ioctl,
+	.mmap =			snd_info_entry_mmap,
+	.open =			snd_info_entry_open,
+	.release =		snd_info_entry_release,
 };
 
 /**
diff -urN oldtree/sound/oss/trident.c newtree/sound/oss/trident.c
--- oldtree/sound/oss/trident.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/sound/oss/trident.c	2006-02-13 19:20:01.303314168 -0500
@@ -278,16 +278,14 @@
 };
 
 static struct pci_device_id trident_pci_tbl[] = {
-	{PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX,
-	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, TRIDENT_4D_DX},
-	{PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX,
-	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, TRIDENT_4D_NX},
-	{PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7018,
-	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_7018},
-	{PCI_VENDOR_ID_ALI, PCI_DEVICE_ID_ALI_5451,
-	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, ALI_5451},
-	{PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_5050,
-	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, CYBER5050},
+	{PCI_DEVICE(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX),
+		PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TRIDENT_4D_DX},
+	{PCI_DEVICE(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX),
+		0, 0, TRIDENT_4D_NX},
+	{PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7018), 0, 0, SIS_7018},
+	{PCI_DEVICE(PCI_VENDOR_ID_ALI, PCI_DEVICE_ID_ALI_5451), 0, 0, ALI_5451},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_5050),
+		0, 0, CYBER5050},
 	{0,}
 };
 
diff -urN oldtree/sound/pci/ali5451/ali5451.c newtree/sound/pci/ali5451/ali5451.c
--- oldtree/sound/pci/ali5451/ali5451.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/sound/pci/ali5451/ali5451.c	2006-02-13 19:20:01.304314016 -0500
@@ -279,7 +279,7 @@
 };
 
 static struct pci_device_id snd_ali_ids[] = {
-	{0x10b9, 0x5451, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+	{PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5451), 0, 0, 0},
 	{0, }
 };
 MODULE_DEVICE_TABLE(pci, snd_ali_ids);
diff -urN oldtree/sound/pci/emu10k1/emumixer.c newtree/sound/pci/emu10k1/emumixer.c
--- oldtree/sound/pci/emu10k1/emumixer.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/sound/pci/emu10k1/emumixer.c	2006-02-13 19:20:30.112934440 -0500
@@ -750,6 +750,8 @@
 		"Master Mono Playback Volume",
 		"PCM Out Path & Mute",
 		"Mono Output Select",
+		"Front Playback Switch",
+		"Front Playback Volume",
 		"Surround Playback Switch",
 		"Surround Playback Volume",
 		"Center Playback Switch",
diff -urN oldtree/sound/pci/hda/hda_intel.c newtree/sound/pci/hda/hda_intel.c
--- oldtree/sound/pci/hda/hda_intel.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/sound/pci/hda/hda_intel.c	2006-02-13 19:20:01.305313864 -0500
@@ -70,6 +70,7 @@
 			 "{Intel, ICH6M},"
 			 "{Intel, ICH7},"
 			 "{Intel, ESB2},"
+			 "{Intel, ICH8 },"
 			 "{ATI, SB450},"
 			 "{VIA, VT8251},"
 			 "{VIA, VT8237A},"
@@ -1603,6 +1604,7 @@
 	{ 0x8086, 0x2668, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH6 */
 	{ 0x8086, 0x27d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH7 */
 	{ 0x8086, 0x269a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ESB2 */
+	{ 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH8 */
 	{ 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB450 */
 	{ 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_VIA }, /* VIA VT8251/VT8237A */
 	{ 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SIS }, /* SIS966 */
diff -urN oldtree/sound/usb/usbaudio.c newtree/sound/usb/usbaudio.c
--- oldtree/sound/usb/usbaudio.c	2006-01-02 22:21:10.000000000 -0500
+++ newtree/sound/usb/usbaudio.c	2006-02-13 19:20:30.115933984 -0500
@@ -480,22 +480,38 @@
 /*
  * Prepare urb for streaming before playback starts.
  *
- * We don't care about (or have) any data, so we just send a transfer delimiter.
+ * We don't yet have data, so we send a frame of silence.
  */
 static int prepare_startup_playback_urb(snd_usb_substream_t *subs,
 					snd_pcm_runtime_t *runtime,
 					struct urb *urb)
 {
-	unsigned int i;
+	unsigned int i, offs, counts;
 	snd_urb_ctx_t *ctx = urb->context;
+	int stride = runtime->frame_bits >> 3;
 
+	offs = 0;
 	urb->dev = ctx->subs->dev;
 	urb->number_of_packets = subs->packs_per_ms;
 	for (i = 0; i < subs->packs_per_ms; ++i) {
-		urb->iso_frame_desc[i].offset = 0;
-		urb->iso_frame_desc[i].length = 0;
+		/* calculate the size of a packet */
+		if (subs->fill_max)
+			counts = subs->maxframesize; /* fixed */
+		else {
+			subs->phase = (subs->phase & 0xffff)
+				+ (subs->freqm << subs->datainterval);
+			counts = subs->phase >> 16;
+			if (counts > subs->maxframesize)
+				counts = subs->maxframesize;
+		}
+		urb->iso_frame_desc[i].offset = offs * stride;
+		urb->iso_frame_desc[i].length = counts * stride;
+		offs += counts;
 	}
-	urb->transfer_buffer_length = 0;
+	urb->transfer_buffer_length = offs * stride;
+	memset(urb->transfer_buffer,
+	       subs->cur_audiofmt->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0,
+	       offs * stride);
 	return 0;
 }
 
