## Automatically generated incremental diff
## From:   linux-2.4.27-rc1
## To:     linux-2.4.27-rc2
## Robot:  $Id: make-incremental-diff,v 1.12 2004/01/06 07:19:36 hpa Exp $

diff -urN linux-2.4.27-rc1/Documentation/Configure.help linux-2.4.27-rc2/Documentation/Configure.help
--- linux-2.4.27-rc1/Documentation/Configure.help	2004-06-25 20:48:23.709971451 -0700
+++ linux-2.4.27-rc2/Documentation/Configure.help	2004-06-25 20:48:25.559048582 -0700
@@ -569,17 +569,17 @@
   The umem driver has been allocated block major number 116.
   See Documentation/devices.txt for recommended device naming.
 
-Promise SATA SX8 (carmel) support
-CONFIG_BLK_DEV_CARMEL
+Promise SATA SX8 support
+CONFIG_BLK_DEV_SX8
    Saying Y or M here will enable support for the 
-   Promise SATA SX8 (carmel) controllers.
+   Promise SATA SX8 controllers.
 
   If you want to compile this driver as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want),
   say M here and read Documentation/modules.txt. The module will be
-  called carmel.o.
+  called sx8.o.
 
-  The carmel driver has been allocated block major numbers 160, 161.
+  The sx8 driver has been allocated block major numbers 160, 161.
   See Documentation/devices.txt for recommended device naming.
 
 Network block device support
@@ -7584,6 +7584,11 @@
   not have to supply an alternative one. They just say Y to "Use
   default SBA-200E firmware", above.
 
+CONFIG_ATM_FORE200E_USE_TASKLET
+  This defers work to be done by the interrupt handler to a
+  tasklet instead of handling everything at interrupt time.  This
+  may improve the responsiveness of the host.
+
 Maximum number of tx retries
 CONFIG_ATM_FORE200E_TX_RETRY
   Specifies the number of times the driver attempts to transmit
@@ -9265,6 +9270,48 @@
   say M here and read <file:Documentation/modules.txt>.  The module
   will be called megaraid2.o.
 
+CONFIG_SCSI_SATA
+  This driver family supports Serial ATA host controllers
+  and devices.
+
+  If unsure, say N.
+
+CONFIG_SCSI_SATA_SVW
+  This option enables support for Broadcom/Serverworks/Apple K2
+  SATA support.
+
+  If unsure, say N.
+
+CONFIG_SCSI_SATA_PROMISE
+  This option enables support for Promise Serial ATA TX2/TX4.
+
+  If unsure, say N.
+
+CONFIG_SCSI_SATA_SX4
+  This option enables support for Promise Serial ATA SX4.
+
+  If unsure, say N.
+
+CONFIG_SCSI_SATA_SIL
+  This option enables support for Silicon Image Serial ATA.
+
+  If unsure, say N.
+
+CONFIG_SCSI_SATA_SIS
+  This option enables support for SiS Serial ATA 964/180.
+
+  If unsure, say N.
+
+CONFIG_SCSI_SATA_VIA
+  This option enables support for VIA Serial ATA.
+
+  If unsure, say N.
+
+CONFIG_SCSI_SATA_VITESSE
+  This option enables support for Vitesse VSC7174 Serial ATA.
+
+  If unsure, say N.
+
 Intel/ICP (former GDT SCSI Disk Array) RAID Controller support
 CONFIG_SCSI_GDTH
   Formerly called GDT SCSI Disk Array Controller Support.
@@ -12164,6 +12211,20 @@
   module, say M here and read <file:Documentation/modules.txt> as well
   as <file:Documentation/networking/net-modules.txt>.
 
+CONFIG_E1000_NAPI
+  NAPI is a new driver API designed to reduce CPU and interrupt load
+  when the driver is receiving lots of packets from the card. It is
+  still somewhat experimental and thus not yet enabled by default.
+
+  If your estimated Rx load is 10kpps or more, or if the card will be
+  deployed on potentially unfriendly networks (e.g. in a firewall),
+  then say Y here.
+
+  See <file:Documentation/networking/NAPI_HOWTO.txt> for more
+  information.
+
+  If in doubt, say N.
+
 AMD LANCE and PCnet (AT1500 and NE2100) support
 CONFIG_LANCE
   If you have a network (Ethernet) card of this type, say Y and read
@@ -23071,22 +23132,20 @@
 
   Linux Bluetooth subsystem consist of several layers:
                BlueZ Core (HCI device and connection manager, scheduler)
-               HCI Device drivers (interface to the hardware)
-               L2CAP Module (L2CAP protocol)
-               SCO Module (SCO links)
-               RFCOMM Module (RFCOMM protocol)
-               BNEP Module (BNEP protocol)
-               CMTP Module (CMTP protocol)
+               HCI Device drivers (Interface to the hardware)
+               SCO Module (SCO audio links)
+               L2CAP Module (Logical Link Control and Adaptation Protocol)
+               RFCOMM Module (RFCOMM Protocol)
+               BNEP Module (Bluetooth Network Encapsulation Protocol)
+               CMTP Module (CAPI Message Transport Protocol)
 
-  Say Y here to enable Linux Bluetooth support and to build BlueZ Core
-  layer.
+  Say Y here to compile Bluetooth support into the kernel or say M to
+  compile it as module (bluez.o).
 
   To use Linux Bluetooth subsystem, you will need several user-space
   utilities like hciconfig and hcid.  These utilities and updates to
   Bluetooth kernel modules are provided in the BlueZ package.
-  For more information, see <http://bluez.sourceforge.net/>.
-
-  If you want to compile BlueZ Core as module (bluez.o) say M here.
+  For more information, see <http://www.bluez.org/>.
 
 L2CAP protocol support
 CONFIG_BLUEZ_L2CAP
@@ -23099,7 +23158,7 @@
 
 SCO links support
 CONFIG_BLUEZ_SCO
-  SCO link provides voice transport over Bluetooth. SCO support is
+  SCO link provides voice transport over Bluetooth.  SCO support is
   required for voice applications like Headset and Audio.
 
   Say Y here to compile SCO support into the kernel or say M to
@@ -23107,7 +23166,7 @@
 
 RFCOMM protocol support
 CONFIG_BLUEZ_RFCOMM
-  RFCOMM provides connection oriented stream transport. RFCOMM
+  RFCOMM provides connection oriented stream transport.  RFCOMM
   support is required for Dialup Networking, OBEX and other Bluetooth
   applications.
 
@@ -23121,25 +23180,12 @@
 BNEP protocol support
 CONFIG_BLUEZ_BNEP
   BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet
-  emulation layer on top of Bluetooth. BNEP is required for Bluetooth
-  PAN (Personal Area Network).
-
-  To use BNEP, you will need user-space utilities provided in the 
-  BlueZ-PAN package.
-  For more information, see <http://bluez.sourceforge.net>.
+  emulation layer on top of Bluetooth.  BNEP is required for
+  Bluetooth PAN (Personal Area Network).
 
   Say Y here to compile BNEP support into the kernel or say M to
   compile it as module (bnep.o).
 
-CMTP protocol support
-CONFIG_BLUEZ_CMTP
-  CMTP (CAPI Message Transport Protocol) is a transport layer
-  for CAPI messages. CMTP is required for the Bluetooth Common
-  ISDN Access Profile.
-
-  Say Y here to compile CMTP support into the kernel or say M to
-  compile it as module (cmtp.o).
-
 BNEP multicast filter support
 CONFIG_BLUEZ_BNEP_MC_FILTER
   This option enables the multicast filter support for BNEP.
@@ -23148,6 +23194,15 @@
 CONFIG_BLUEZ_BNEP_PROTO_FILTER
   This option enables the protocol filter support for BNEP.
 
+CMTP protocol support
+CONFIG_BLUEZ_CMTP
+  CMTP (CAPI Message Transport Protocol) is a transport layer
+  for CAPI messages.  CMTP is required for the Bluetooth Common
+  ISDN Access Profile.
+
+  Say Y here to compile CMTP support into the kernel or say M to
+  compile it as module (cmtp.o).
+
 HCI UART driver
 CONFIG_BLUEZ_HCIUART
   Bluetooth HCI UART driver.
diff -urN linux-2.4.27-rc1/Documentation/networking/ip-sysctl.txt linux-2.4.27-rc2/Documentation/networking/ip-sysctl.txt
--- linux-2.4.27-rc1/Documentation/networking/ip-sysctl.txt	2004-06-25 20:48:23.803975373 -0700
+++ linux-2.4.27-rc2/Documentation/networking/ip-sysctl.txt	2004-06-25 20:48:25.563048749 -0700
@@ -641,9 +641,11 @@
 			    disabled if local forwarding is enabled.
 
 autoconf - BOOLEAN
-	Configure link-local addresses using L2 hardware addresses.
+	Autoconfigure addresses using Prefix Information in Router 
+	Advertisements.
 
-	Default: TRUE
+	Functional default: enabled if accept_ra is enabled.
+			    disabled if accept_ra is disabled.
 
 dad_transmits - INTEGER
 	The amount of Duplicate Address Detection probes to send.
diff -urN linux-2.4.27-rc1/MAINTAINERS linux-2.4.27-rc2/MAINTAINERS
--- linux-2.4.27-rc1/MAINTAINERS	2004-06-25 20:48:23.814975831 -0700
+++ linux-2.4.27-rc2/MAINTAINERS	2004-06-25 20:48:25.566048874 -0700
@@ -334,6 +334,8 @@
 M:	maxk@qualcomm.com
 L:	bluez-devel@lists.sf.net
 W:	http://bluez.sf.net
+W:	http://www.bluez.org
+W:	http://www.holtmann.org/linux/bluetooth/
 S:	Maintained
 
 BLUETOOTH RFCOMM LAYER
@@ -341,7 +343,6 @@
 M:	marcel@holtmann.org
 P:	Maxim Krasnyansky
 M:	maxk@qualcomm.com
-W:	http://bluez.sf.net
 S:	Maintained
 
 BLUETOOTH BNEP LAYER
@@ -349,65 +350,60 @@
 M:	marcel@holtmann.org
 P:	Maxim Krasnyansky
 M:	maxk@qualcomm.com
-W:	http://bluez.sf.net
 S:	Maintained
 
 BLUETOOTH CMTP LAYER
 P:	Marcel Holtmann
 M:	marcel@holtmann.org
-W:	http://www.holtmann.org/linux/bluetooth/
 S:	Maintained
 
-BLUETOOTH HCI USB DRIVER
+BLUETOOTH HCI UART DRIVER
 P:	Marcel Holtmann
 M:	marcel@holtmann.org
 P:	Maxim Krasnyansky
 M:	maxk@qualcomm.com
-W:	http://bluez.sf.net
 S:	Maintained
 
-BLUETOOTH HCI UART DRIVER
+BLUETOOTH HCI USB DRIVER
 P:	Marcel Holtmann
 M:	marcel@holtmann.org
 P:	Maxim Krasnyansky
 M:	maxk@qualcomm.com
-W:	http://bluez.sf.net
+S:	Maintained
+
+BLUETOOTH HCI BCM203X DRIVER
+P:	Marcel Holtmann
+M:	marcel@holtmann.org
 S:	Maintained
 
 BLUETOOTH HCI BFUSB DRIVER
 P:	Marcel Holtmann
 M:	marcel@holtmann.org
-W:	http://www.holtmann.org/linux/bluetooth/
 S:	Maintained
 
 BLUETOOTH HCI DTL1 DRIVER
 P:	Marcel Holtmann
 M:	marcel@holtmann.org
-W:	http://www.holtmann.org/linux/bluetooth/
 S:	Maintained
 
 BLUETOOTH HCI BLUECARD DRIVER
 P:	Marcel Holtmann
 M:	marcel@holtmann.org
-W:	http://www.holtmann.org/linux/bluetooth/
 S:	Maintained
 
 BLUETOOTH HCI BT3C DRIVER
 P:	Marcel Holtmann
 M:	marcel@holtmann.org
-W:	http://www.holtmann.org/linux/bluetooth/
 S:	Maintained
 
 BLUETOOTH HCI BTUART DRIVER
 P:	Marcel Holtmann
 M:	marcel@holtmann.org
-W:	http://www.holtmann.org/linux/bluetooth/
 S:	Maintained
 
 BLUETOOTH HCI VHCI DRIVER
 P:	Maxim Krasnyansky
 M:	maxk@qualcomm.com
-W:	http://bluez.sf.net
 S:	Maintained
 
 BONDING DRIVER
diff -urN linux-2.4.27-rc1/Makefile linux-2.4.27-rc2/Makefile
--- linux-2.4.27-rc1/Makefile	2004-06-25 20:48:23.814975831 -0700
+++ linux-2.4.27-rc2/Makefile	2004-06-25 20:48:25.566048874 -0700
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 27
-EXTRAVERSION = -rc1
+EXTRAVERSION = -rc2
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
diff -urN linux-2.4.27-rc1/arch/i386/kernel/edd.c linux-2.4.27-rc2/arch/i386/kernel/edd.c
--- linux-2.4.27-rc1/arch/i386/kernel/edd.c	2004-02-18 05:36:30.000000000 -0800
+++ linux-2.4.27-rc2/arch/i386/kernel/edd.c	2004-06-25 20:48:25.568048957 -0700
@@ -46,9 +46,8 @@
 MODULE_DESCRIPTION("proc interface to BIOS EDD information");
 MODULE_LICENSE("GPL");
 
-#define EDD_VERSION "0.10 2003-Dec-05"
+#define EDD_VERSION "0.11 2004-Jun-21"
 #define EDD_DEVICE_NAME_SIZE 16
-#define REPORT_URL "http://domsch.com/linux/edd30/results.html"
 
 #define left (count - (p - page) - 1)
 
@@ -312,10 +311,6 @@
 	}
 
 out:
-	p += snprintf(p, left, "\nPlease check %s\n", REPORT_URL);
-	p += snprintf(p, left, "to see if this device has been reported.  If not,\n");
-	p += snprintf(p, left, "please send the information requested there.\n");
-
 	return proc_calc_metrics(page, start, off, count, eof, (p - page));
 }
 
@@ -405,7 +400,7 @@
 		return proc_calc_metrics(page, start, off, count, eof, 0);
 	}
 
-	p += snprintf(p, left, "0x%x\n", info->params.num_default_cylinders);
+	p += snprintf(p, left, "%u\n", info->params.num_default_cylinders);
 	return proc_calc_metrics(page, start, off, count, eof, (p - page));
 }
 
@@ -418,7 +413,7 @@
 		return proc_calc_metrics(page, start, off, count, eof, 0);
 	}
 
-	p += snprintf(p, left, "0x%x\n", info->params.num_default_heads);
+	p += snprintf(p, left, "%u\n", info->params.num_default_heads);
 	return proc_calc_metrics(page, start, off, count, eof, (p - page));
 }
 
@@ -431,7 +426,7 @@
 		return proc_calc_metrics(page, start, off, count, eof, 0);
 	}
 
-	p += snprintf(p, left, "0x%x\n", info->params.sectors_per_track);
+	p += snprintf(p, left, "%u\n", info->params.sectors_per_track);
 	return proc_calc_metrics(page, start, off, count, eof, (p - page));
 }
 
@@ -444,7 +439,7 @@
 		return proc_calc_metrics(page, start, off, count, eof, 0);
 	}
 
-	p += snprintf(p, left, "0x%llx\n", info->params.number_of_sectors);
+	p += snprintf(p, left, "%llu\n", info->params.number_of_sectors);
 	return proc_calc_metrics(page, start, off, count, eof, (p - page));
 }
 
diff -urN linux-2.4.27-rc1/arch/i386/kernel/mpparse.c linux-2.4.27-rc2/arch/i386/kernel/mpparse.c
--- linux-2.4.27-rc1/arch/i386/kernel/mpparse.c	2004-06-25 20:48:23.818975998 -0700
+++ linux-2.4.27-rc2/arch/i386/kernel/mpparse.c	2004-06-25 20:48:25.571049082 -0700
@@ -1211,7 +1211,7 @@
 
 		for (idx = 0; idx < mp_irq_entries; idx++)
 			if (mp_irqs[idx].mpc_srcbus == MP_ISA_BUS &&
-				(mp_irqs[idx].mpc_dstapic == ioapic) &&
+				(mp_irqs[idx].mpc_dstapic == mp_ioapics[ioapic].mpc_apicid) &&
 				(mp_irqs[idx].mpc_srcbusirq == i ||
 				mp_irqs[idx].mpc_dstirq == i))
 					break;
diff -urN linux-2.4.27-rc1/arch/i386/kernel/pci-pc.c linux-2.4.27-rc2/arch/i386/kernel/pci-pc.c
--- linux-2.4.27-rc1/arch/i386/kernel/pci-pc.c	2004-06-25 20:48:23.820976082 -0700
+++ linux-2.4.27-rc2/arch/i386/kernel/pci-pc.c	2004-06-25 20:48:25.573049166 -0700
@@ -1349,8 +1349,8 @@
 
 	/*
 	* Chip 	Old value   	New value
-	* C17  	0x1F01FF01  	0x1F0FFF01
-	* C18D 	0x9F01FF01  	0x9F0FFF01
+	* C17  	0x1F0FFF01  	0x1F01FF01
+	* C18D 	0x9F0FFF01  	0x9F01FF01
 	*/
 	fixed_val = rev < 0xC1 ? 0x1F01FF01 : 0x9F01FF01;
 
diff -urN linux-2.4.27-rc1/arch/m68k/mac/iop.c linux-2.4.27-rc2/arch/m68k/mac/iop.c
--- linux-2.4.27-rc1/arch/m68k/mac/iop.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.27-rc2/arch/m68k/mac/iop.c	2004-06-25 20:48:25.589049833 -0700
@@ -261,7 +261,7 @@
 		} else {
 			iop_base[IOP_NUM_ISM] = (struct mac_iop *) ISM_IOP_BASE_QUADRA;
 		}
-		iop_base[IOP_NUM_SCC]->status_ctrl = 0;
+		iop_base[IOP_NUM_ISM]->status_ctrl = 0;
 		iop_ism_present = 1;
 	} else {
 		iop_base[IOP_NUM_ISM] = NULL;
diff -urN linux-2.4.27-rc1/arch/x86_64/kernel/mpparse.c linux-2.4.27-rc2/arch/x86_64/kernel/mpparse.c
--- linux-2.4.27-rc1/arch/x86_64/kernel/mpparse.c	2004-06-25 20:48:23.891979043 -0700
+++ linux-2.4.27-rc2/arch/x86_64/kernel/mpparse.c	2004-06-25 20:48:25.638051877 -0700
@@ -866,7 +866,7 @@
 
 		for (idx = 0; idx < mp_irq_entries; idx++)
 			if (mp_irqs[idx].mpc_srcbus == MP_ISA_BUS &&
-				(mp_irqs[idx].mpc_dstapic == ioapic) &&
+				(mp_irqs[idx].mpc_dstapic == intsrc.mpc_dstapic) &&
 				(mp_irqs[idx].mpc_srcbusirq == i ||
 				mp_irqs[idx].mpc_dstirq == i))
 					break;
diff -urN linux-2.4.27-rc1/drivers/acpi/asus_acpi.c linux-2.4.27-rc2/drivers/acpi/asus_acpi.c
--- linux-2.4.27-rc1/drivers/acpi/asus_acpi.c	2004-06-25 20:48:23.898979335 -0700
+++ linux-2.4.27-rc2/drivers/acpi/asus_acpi.c	2004-06-25 20:48:25.646052211 -0700
@@ -40,6 +40,7 @@
 #include <linux/proc_fs.h>
 #include <acpi/acpi_drivers.h>
 #include <acpi/acpi_bus.h>
+#include <asm/uaccess.h>
 
 #define ASUS_ACPI_VERSION "0.28"
 
@@ -455,6 +456,21 @@
 
 	return len;
 }
+ 
+static int parse_arg(const char *buf, unsigned long count, int *val)
+{
+	char s[32];
+	if (!count)
+		return 0;
+	if (count > 31)
+		return -EINVAL;
+	if (copy_from_user(s, buf, count))
+		return -EFAULT;
+	s[count] = 0;
+	if (sscanf(s, "%i", val) != 1)
+		return -EINVAL;
+	return count;
+}
 
 
 /*
@@ -486,11 +502,14 @@
 write_led(const char *buffer, unsigned long count, struct asus_hotk *hotk, 
           char *ledname, int ledmask, int invert)
 {
-	int value;
+	int value, retval;
 	int led_out = 0;
 
-	if (sscanf(buffer, "%i", &value) == 1)
-		led_out = value ? 1 : 0;
+	retval = parse_arg(buffer, count, &value);
+	if (retval <= 0)
+		return retval;
+	count = retval;
+	led_out = value ? 1 : 0;
 
 	hotk->status =
 	    (led_out) ? (hotk->status | ledmask) : (hotk->status & ~ledmask);
@@ -643,12 +662,13 @@
 proc_write_lcd(struct file *file, const char *buffer,
 	       unsigned long count, void *data)
 {
-	int value;
+	int value, retval;
 	struct asus_hotk *hotk = (struct asus_hotk *) data;
 	
-	if (sscanf(buffer, "%i", &value) == 1)
+	retval = parse_arg(buffer, count, &value);
+	if (retval > 0)
 		set_lcd_state(hotk, value);
-	return count;
+	return retval;
 }
 
 
@@ -710,10 +730,11 @@
 proc_write_brn(struct file *file, const char *buffer,
 	       unsigned long count, void *data)
 {
-	int value;
+	int value, retval;
 	struct asus_hotk *hotk = (struct asus_hotk *) data;
 
-	if (sscanf(buffer, "%d", &value) == 1) {
+	retval = parse_arg(buffer, count, &value);
+	if (retval > 0) {
 		value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
 			/* 0 <= value <= 15 */
 		set_brightness(value, hotk);
@@ -721,7 +742,7 @@
 		printk(KERN_WARNING "Asus ACPI: Error reading user input\n");
 	}
 
-	return count;
+	return retval;
 }
 
 static void set_display(int value, struct asus_hotk *hotk)
@@ -759,16 +780,17 @@
 proc_write_disp(struct file *file, const char *buffer,
 	       unsigned long count, void *data)
 {
-	int value;
+	int value, retval;
 	struct asus_hotk *hotk = (struct asus_hotk *) data;
 
-	if (sscanf(buffer, "%d", &value) == 1)
+	retval = parse_arg(buffer, count, &value);
+	if (retval > 0)
 		set_display(value, hotk);
 	else {
 		printk(KERN_WARNING "Asus ACPI: Error reading user input\n");
 	}
 
-	return count;
+	return retval;
 }
 
 
diff -urN linux-2.4.27-rc1/drivers/block/Config.in linux-2.4.27-rc2/drivers/block/Config.in
--- linux-2.4.27-rc1/drivers/block/Config.in	2004-06-25 20:48:23.917980128 -0700
+++ linux-2.4.27-rc2/drivers/block/Config.in	2004-06-25 20:48:25.670053212 -0700
@@ -39,7 +39,7 @@
 dep_mbool '       Enable monitor thread' CONFIG_CISS_MONITOR_THREAD $CONFIG_BLK_CPQ_CISS_DA
 dep_tristate 'Mylex DAC960/DAC1100 PCI RAID Controller support' CONFIG_BLK_DEV_DAC960 $CONFIG_PCI
 dep_tristate 'Micro Memory MM5415 Battery Backed RAM support (EXPERIMENTAL)' CONFIG_BLK_DEV_UMEM $CONFIG_PCI $CONFIG_EXPERIMENTAL
-dep_tristate 'Promise SATA SX8 (carmel) support' CONFIG_BLK_DEV_CARMEL $CONFIG_PCI
+dep_tristate 'Promise SATA SX8 support' CONFIG_BLK_DEV_SX8 $CONFIG_PCI
 
 tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
 dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET
diff -urN linux-2.4.27-rc1/drivers/block/Makefile linux-2.4.27-rc2/drivers/block/Makefile
--- linux-2.4.27-rc1/drivers/block/Makefile	2004-06-25 20:48:23.917980128 -0700
+++ linux-2.4.27-rc2/drivers/block/Makefile	2004-06-25 20:48:25.674053379 -0700
@@ -31,7 +31,7 @@
 obj-$(CONFIG_BLK_DEV_DAC960)	+= DAC960.o
 obj-$(CONFIG_BLK_DEV_UMEM)	+= umem.o
 obj-$(CONFIG_BLK_DEV_NBD)	+= nbd.o
-obj-$(CONFIG_BLK_DEV_CARMEL)	+= carmel.o
+obj-$(CONFIG_BLK_DEV_SX8)	+= sx8.o
 
 subdir-$(CONFIG_PARIDE) += paride
 
diff -urN linux-2.4.27-rc1/drivers/block/carmel.c linux-2.4.27-rc2/drivers/block/carmel.c
--- linux-2.4.27-rc1/drivers/block/carmel.c	2004-06-25 20:48:23.920980253 -0700
+++ linux-2.4.27-rc2/drivers/block/carmel.c	1969-12-31 16:00:00.000000000 -0800
@@ -1,2083 +0,0 @@
-/*
- *  carmel.c: Driver for Promise SATA SX8 looks-like-I2O hardware
- *
- *  Copyright 2004 Red Hat, Inc.
- *
- *  Author/maintainer:  Jeff Garzik <jgarzik@pobox.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 this archive
- *  for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/blk.h>
-#include <linux/blkdev.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/compiler.h>
-#include <linux/bitops.h>
-#include <linux/delay.h>
-#include <linux/time.h>
-#include <linux/hdreg.h>
-#include <linux/fs.h>
-#include <linux/blkpg.h>
-#include <asm/io.h>
-#include <asm/semaphore.h>
-#include <asm/uaccess.h>
-
-MODULE_AUTHOR("Jeff Garzik");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Promise SATA SX8 (carmel) block driver");
-
-#if 0
-#define CARM_DEBUG
-#define CARM_VERBOSE_DEBUG
-#else
-#undef CARM_DEBUG
-#undef CARM_VERBOSE_DEBUG
-#endif
-#undef CARM_NDEBUG
-
-#define DRV_NAME "carmel"
-#define DRV_VERSION "0.8-24.1"
-#define PFX DRV_NAME ": "
-
-#define NEXT_RESP(idx)	((idx + 1) % RMSG_Q_LEN)
-
-/* 0xf is just arbitrary, non-zero noise; this is sorta like poisoning */
-#define TAG_ENCODE(tag)	(((tag) << 16) | 0xf)
-#define TAG_DECODE(tag)	(((tag) >> 16) & 0x1f)
-#define TAG_VALID(tag)	((((tag) & 0xf) == 0xf) && (TAG_DECODE(tag) < 32))
-
-/* note: prints function name for you */
-#ifdef CARM_DEBUG
-#define DPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args)
-#ifdef CARM_VERBOSE_DEBUG
-#define VPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args)
-#else
-#define VPRINTK(fmt, args...)
-#endif	/* CARM_VERBOSE_DEBUG */
-#else
-#define DPRINTK(fmt, args...)
-#define VPRINTK(fmt, args...)
-#endif	/* CARM_DEBUG */
-
-#ifdef CARM_NDEBUG
-#define assert(expr)
-#else
-#define assert(expr) \
-        if(unlikely(!(expr))) {                                   \
-        printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \
-        #expr,__FILE__,__FUNCTION__,__LINE__);          \
-        }
-#endif
-
-/* defines only for the constants which don't work well as enums */
-struct carm_host;
-
-enum {
-	/* adapter-wide limits */
-	CARM_MAX_PORTS		= 8,
-	CARM_SHM_SIZE		= (4096 << 7),
-	CARM_PART_SHIFT		= 5,
-	CARM_MINORS_PER_MAJOR	= (1 << CARM_PART_SHIFT),
-	CARM_MAX_WAIT_Q		= CARM_MAX_PORTS + 1,
-
-	/* command message queue limits */
-	CARM_MAX_REQ		= 64,	       /* max command msgs per host */
-	CARM_MAX_Q		= 1,		   /* one command at a time */
-	CARM_MSG_LOW_WATER	= (CARM_MAX_REQ / 4),	     /* refill mark */
-
-	/* S/G limits, host-wide and per-request */
-	CARM_MAX_REQ_SG		= 32,	     /* max s/g entries per request */
-	CARM_SG_BOUNDARY	= 0xffffUL,	    /* s/g segment boundary */
-	CARM_MAX_HOST_SG	= 600,		/* max s/g entries per host */
-	CARM_SG_LOW_WATER	= (CARM_MAX_HOST_SG / 4),   /* re-fill mark */
-
-	/* hardware registers */
-	CARM_IHQP		= 0x1c,
-	CARM_INT_STAT		= 0x10, /* interrupt status */
-	CARM_INT_MASK		= 0x14, /* interrupt mask */
-	CARM_HMUC		= 0x18, /* host message unit control */
-	RBUF_ADDR_LO		= 0x20, /* response msg DMA buf low 32 bits */
-	RBUF_ADDR_HI		= 0x24, /* response msg DMA buf high 32 bits */
-	RBUF_BYTE_SZ		= 0x28,
-	CARM_RESP_IDX		= 0x2c,
-	CARM_CMS0		= 0x30, /* command message size reg 0 */
-	CARM_LMUC		= 0x48,
-	CARM_HMPHA		= 0x6c,
-	CARM_INITC		= 0xb5,
-
-	/* bits in CARM_INT_{STAT,MASK} */
-	INT_RESERVED		= 0xfffffff0,
-	INT_WATCHDOG		= (1 << 3),	/* watchdog timer */
-	INT_Q_OVERFLOW		= (1 << 2),	/* cmd msg q overflow */
-	INT_Q_AVAILABLE		= (1 << 1),	/* cmd msg q has free space */
-	INT_RESPONSE		= (1 << 0),	/* response msg available */
-	INT_ACK_MASK		= INT_WATCHDOG | INT_Q_OVERFLOW,
-	INT_DEF_MASK		= INT_RESERVED | INT_Q_OVERFLOW |
-				  INT_RESPONSE,
-
-	/* command messages, and related register bits */
-	CARM_HAVE_RESP		= 0x01,
-	CARM_MSG_READ		= 1,
-	CARM_MSG_WRITE		= 2,
-	CARM_MSG_VERIFY		= 3,
-	CARM_MSG_GET_CAPACITY	= 4,
-	CARM_MSG_FLUSH		= 5,
-	CARM_MSG_IOCTL		= 6,
-	CARM_MSG_ARRAY		= 8,
-	CARM_MSG_MISC		= 9,
-	CARM_CME		= (1 << 2),
-	CARM_RME		= (1 << 1),
-	CARM_WZBC		= (1 << 0),
-	CARM_RMI		= (1 << 0),
-	CARM_Q_FULL		= (1 << 3),
-	CARM_MSG_SIZE		= 288,
-	CARM_Q_LEN		= 48,
-
-	/* CARM_MSG_IOCTL messages */
-	CARM_IOC_SCAN_CHAN	= 5,	/* scan channels for devices */
-	CARM_IOC_GET_TCQ	= 13,	/* get tcq/ncq depth */
-	CARM_IOC_SET_TCQ	= 14,	/* set tcq/ncq depth */
-
-	IOC_SCAN_CHAN_NODEV	= 0x1f,
-	IOC_SCAN_CHAN_OFFSET	= 0x40,
-
-	/* CARM_MSG_ARRAY messages */
-	CARM_ARRAY_INFO		= 0,
-
-	ARRAY_NO_EXIST		= (1 << 31),
-
-	/* response messages */
-	RMSG_SZ			= 8,	/* sizeof(struct carm_response) */
-	RMSG_Q_LEN		= 48,	/* resp. msg list length */
-	RMSG_OK			= 1,	/* bit indicating msg was successful */
-					/* length of entire resp. msg buffer */
-	RBUF_LEN		= RMSG_SZ * RMSG_Q_LEN,
-
-	PDC_SHM_SIZE		= (4096 << 7), /* length of entire h/w buffer */
-
-	/* CARM_MSG_MISC messages */
-	MISC_GET_FW_VER		= 2,
-	MISC_ALLOC_MEM		= 3,
-	MISC_SET_TIME		= 5,
-
-	/* MISC_GET_FW_VER feature bits */
-	FW_VER_4PORT		= (1 << 2), /* 1=4 ports, 0=8 ports */
-	FW_VER_NON_RAID		= (1 << 1), /* 1=non-RAID firmware, 0=RAID */
-	FW_VER_ZCR		= (1 << 0), /* zero channel RAID (whatever that is) */
-
-	/* carm_host flags */
-	FL_NON_RAID		= FW_VER_NON_RAID,
-	FL_4PORT		= FW_VER_4PORT,
-	FL_FW_VER_MASK		= (FW_VER_NON_RAID | FW_VER_4PORT),
-	FL_DAC			= (1 << 16),
-	FL_DYN_MAJOR		= (1 << 17),
-};
-
-enum carm_magic_numbers {
-	CARM_MAGIC_HOST		= 0xdeadbeefUL,
-	CARM_MAGIC_PORT		= 0xbedac0edUL,
-};
-
-enum scatter_gather_types {
-	SGT_32BIT		= 0,
-	SGT_64BIT		= 1,
-};
-
-enum host_states {
-	HST_INVALID,		/* invalid state; never used */
-	HST_ALLOC_BUF,		/* setting up master SHM area */
-	HST_ERROR,		/* we never leave here */
-	HST_PORT_SCAN,		/* start dev scan */
-	HST_DEV_SCAN_START,	/* start per-device probe */
-	HST_DEV_SCAN,		/* continue per-device probe */
-	HST_DEV_ACTIVATE,	/* activate devices we found */
-	HST_PROBE_FINISHED,	/* probe is complete */
-	HST_PROBE_START,	/* initiate probe */
-	HST_SYNC_TIME,		/* tell firmware what time it is */
-	HST_GET_FW_VER,		/* get firmware version, adapter port cnt */
-};
-
-#ifdef CARM_DEBUG
-static const char *state_name[] = {
-	"HST_INVALID",
-	"HST_ALLOC_BUF",
-	"HST_ERROR",
-	"HST_PORT_SCAN",
-	"HST_DEV_SCAN_START",
-	"HST_DEV_SCAN",
-	"HST_DEV_ACTIVATE",
-	"HST_PROBE_FINISHED",
-	"HST_PROBE_START",
-	"HST_SYNC_TIME",
-	"HST_GET_FW_VER",
-};
-#endif
-
-struct carm_port {
-	unsigned long			magic;
-	unsigned int			port_no;
-	unsigned int			n_queued;
-	struct carm_host		*host;
-	struct tasklet_struct		tasklet;
-	request_queue_t			q;
-
-	/* attached device characteristics */
-	u64				capacity;
-	char				name[41];
-	u16				dev_geom_head;
-	u16				dev_geom_sect;
-	u16				dev_geom_cyl;
-};
-
-struct carm_request {
-	unsigned int			tag;
-	int				n_elem;
-	unsigned int			msg_type;
-	unsigned int			msg_subtype;
-	unsigned int			msg_bucket;
-	struct request			*rq;
-	struct carm_port		*port;
-	struct request			special_rq;
-	struct scatterlist		sg[CARM_MAX_REQ_SG];
-};
-
-struct carm_host {
-	unsigned long			magic;
-	unsigned long			flags;
-	void				*mmio;
-	void				*shm;
-	dma_addr_t			shm_dma;
-
-	int				major;
-	int				id;
-	char				name[32];
-
-	struct pci_dev			*pdev;
-	unsigned int			state;
-	u32				fw_ver;
-
-	request_queue_t			oob_q;
-	unsigned int			n_oob;
-	struct tasklet_struct		oob_tasklet;
-
-	unsigned int			hw_sg_used;
-
-	unsigned int			resp_idx;
-
-	unsigned int			wait_q_prod;
-	unsigned int			wait_q_cons;
-	request_queue_t			*wait_q[CARM_MAX_WAIT_Q];
-
-	unsigned int			n_msgs;
-	u64				msg_alloc;
-	struct carm_request		req[CARM_MAX_REQ];
-	void				*msg_base;
-	dma_addr_t			msg_dma;
-
-	int				cur_scan_dev;
-	unsigned long			dev_active;
-	unsigned long			dev_present;
-	struct carm_port		port[CARM_MAX_PORTS];
-
-	struct tq_struct		fsm_task;
-
-	struct semaphore		probe_sem;
-
-	struct gendisk			gendisk;
-	struct hd_struct		gendisk_hd[256];
-	int				blk_sizes[256];
-	int				blk_block_sizes[256];
-	int				blk_sect_sizes[256];
-
-	struct list_head		host_list_node;
-};
-
-struct carm_response {
-	u32 ret_handle;
-	u32 status;
-}  __attribute__((packed));
-
-struct carm_msg_sg {
-	u32 start;
-	u32 len;
-}  __attribute__((packed));
-
-struct carm_msg_rw {
-	u8 type;
-	u8 id;
-	u8 sg_count;
-	u8 sg_type;
-	u32 handle;
-	u32 lba;
-	u16 lba_count;
-	u16 lba_high;
-	struct carm_msg_sg sg[32];
-}  __attribute__((packed));
-
-struct carm_msg_allocbuf {
-	u8 type;
-	u8 subtype;
-	u8 n_sg;
-	u8 sg_type;
-	u32 handle;
-	u32 addr;
-	u32 len;
-	u32 evt_pool;
-	u32 n_evt;
-	u32 rbuf_pool;
-	u32 n_rbuf;
-	u32 msg_pool;
-	u32 n_msg;
-	struct carm_msg_sg sg[8];
-}  __attribute__((packed));
-
-struct carm_msg_ioctl {
-	u8 type;
-	u8 subtype;
-	u8 array_id;
-	u8 reserved1;
-	u32 handle;
-	u32 data_addr;
-	u32 reserved2;
-}  __attribute__((packed));
-
-struct carm_msg_sync_time {
-	u8 type;
-	u8 subtype;
-	u16 reserved1;
-	u32 handle;
-	u32 reserved2;
-	u32 timestamp;
-}  __attribute__((packed));
-
-struct carm_msg_get_fw_ver {
-	u8 type;
-	u8 subtype;
-	u16 reserved1;
-	u32 handle;
-	u32 data_addr;
-	u32 reserved2;
-}  __attribute__((packed));
-
-struct carm_fw_ver {
-	u32 version;
-	u8 features;
-	u8 reserved1;
-	u16 reserved2;
-}  __attribute__((packed));
-
-struct carm_array_info {
-	u32 size;
-
-	u16 size_hi;
-	u16 stripe_size;
-
-	u32 mode;
-
-	u16 stripe_blk_sz;
-	u16 reserved1;
-
-	u16 cyl;
-	u16 head;
-
-	u16 sect;
-	u8 array_id;
-	u8 reserved2;
-
-	char name[40];
-
-	u32 array_status;
-
-	/* device list continues beyond this point? */
-}  __attribute__((packed));
-
-static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
-static void carm_remove_one (struct pci_dev *pdev);
-static int carm_bdev_ioctl(struct inode *ino, struct file *fil,
-			   unsigned int cmd, unsigned long arg);
-static request_queue_t *carm_find_queue(kdev_t device);
-static int carm_revalidate_disk(kdev_t dev);
-
-static struct pci_device_id carm_pci_tbl[] = {
-	{ PCI_VENDOR_ID_PROMISE, 0x8000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
-	{ PCI_VENDOR_ID_PROMISE, 0x8002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
-	{ }	/* terminate list */
-};
-MODULE_DEVICE_TABLE(pci, carm_pci_tbl);
-
-static struct pci_driver carm_driver = {
-	.name		= DRV_NAME,
-	.id_table	= carm_pci_tbl,
-	.probe		= carm_init_one,
-	.remove		= carm_remove_one,
-};
-
-static struct block_device_operations carm_bd_ops = {
-	.owner		= THIS_MODULE,
-	.ioctl		= carm_bdev_ioctl,
-};
-
-static unsigned int carm_host_id;
-static unsigned long carm_major_alloc;
-
-
-static struct carm_host *carm_from_dev(kdev_t dev, struct carm_port **port_out)
-{
-	struct carm_host *host;
-	struct carm_port *port;
-	request_queue_t *q;
-
-	q = carm_find_queue(dev);
-	if (!q || !q->queuedata) {
-		printk(KERN_ERR PFX "queue not found for major %d minor %d\n",
-			MAJOR(dev), MINOR(dev));
-		return NULL;
-	}
-
-	port = q->queuedata;
-	if (unlikely(port->magic != CARM_MAGIC_PORT)) {
-		printk(KERN_ERR PFX "bad port magic number for major %d minor %d\n",
-			MAJOR(dev), MINOR(dev));
-		return NULL;
-	}
-
-	host = port->host;
-	if (unlikely(host->magic != CARM_MAGIC_HOST)) {
-		printk(KERN_ERR PFX "bad host magic number for major %d minor %d\n",
-			MAJOR(dev), MINOR(dev));
-		return NULL;
-	}
-
-	if (port_out)
-		*port_out = port;
-	return host;
-}
-
-static int carm_bdev_ioctl(struct inode *ino, struct file *fil,
-			   unsigned int cmd, unsigned long arg)
-{
-	void *usermem = (void *) arg;
-	struct carm_port *port = NULL;
-	struct carm_host *host;
-
-	host = carm_from_dev(ino->i_rdev, &port);
-	if (!host)
-		return -EINVAL;
-
-	switch (cmd) {
-	case HDIO_GETGEO: {
-		struct hd_geometry geom;
-
-		if (!usermem)
-			return -EINVAL;
-
-		if (port->dev_geom_cyl) {
-			geom.heads = port->dev_geom_head;
-			geom.sectors = port->dev_geom_sect;
-			geom.cylinders = port->dev_geom_cyl;
-		} else {
-			u32 tmp = ((u32)port->capacity) / (0xff * 0x3f);
-			geom.heads = 0xff;
-			geom.sectors = 0x3f;
-			if (tmp > 65536)
-				geom.cylinders = 0xffff;
-			else
-				geom.cylinders = tmp;
-		}
-		geom.start = host->gendisk_hd[MINOR(ino->i_rdev)].start_sect;
-
-		if (copy_to_user(usermem, &geom, sizeof(geom)))
-			return -EFAULT;
-		return 0;
-	}
-
-	case HDIO_GETGEO_BIG: {
-		struct hd_big_geometry geom;
-
-		if (!usermem)
-			return -EINVAL;
-
-		if (port->dev_geom_cyl) {
-			geom.heads = port->dev_geom_head;
-			geom.sectors = port->dev_geom_sect;
-			geom.cylinders = port->dev_geom_cyl;
-		} else {
-			geom.heads = 0xff;
-			geom.sectors = 0x3f;
-			geom.cylinders = ((u32)port->capacity) / (0xff * 0x3f);
-		}
-		geom.start = host->gendisk_hd[MINOR(ino->i_rdev)].start_sect;
-
-		if (copy_to_user(usermem, &geom, sizeof(geom)))
-			return -EFAULT;
-		return 0;
-	}
-
-	case BLKRRPART:
-		if (!capable(CAP_SYS_ADMIN))
-			return -EPERM;
-		return carm_revalidate_disk(ino->i_rdev);
-
-	case BLKGETSIZE:
-	case BLKGETSIZE64:
-	case BLKFLSBUF:
-	case BLKBSZSET:
-	case BLKBSZGET:
-	case BLKROSET:
-	case BLKROGET:
-	case BLKRASET:
-	case BLKRAGET:
-	case BLKPG:
-	case BLKELVGET:
-	case BLKELVSET:
-		return blk_ioctl(ino->i_rdev, cmd, arg);
-
-	default:
-		break;
-	}
-
-	return -EOPNOTSUPP;
-}
-
-static inline unsigned long msecs_to_jiffies(unsigned long msecs)
-{
-	return ((HZ * msecs + 999) / 1000);
-}
-
-static void msleep(unsigned long msecs)
-{
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	schedule_timeout(msecs_to_jiffies(msecs) + 1);
-}
-
-static const u32 msg_sizes[] = { 32, 64, 128, CARM_MSG_SIZE };
-
-static inline int carm_lookup_bucket(u32 msg_size)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(msg_sizes); i++)
-		if (msg_size <= msg_sizes[i])
-			return i;
-	
-	return -ENOENT;
-}
-
-static void carm_init_buckets(void *mmio)
-{
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(msg_sizes); i++)
-		writel(msg_sizes[i], mmio + CARM_CMS0 + (4 * i));
-}
-
-static inline void *carm_ref_msg(struct carm_host *host,
-				 unsigned int msg_idx)
-{
-	return host->msg_base + (msg_idx * CARM_MSG_SIZE);
-}
-
-static inline dma_addr_t carm_ref_msg_dma(struct carm_host *host,
-					  unsigned int msg_idx)
-{
-	return host->msg_dma + (msg_idx * CARM_MSG_SIZE);
-}
-
-static int carm_send_msg(struct carm_host *host,
-			 struct carm_request *crq)
-{
-	void *mmio = host->mmio;
-	u32 msg = (u32) carm_ref_msg_dma(host, crq->tag);
-	u32 cm_bucket = crq->msg_bucket;
-	u32 tmp;
-	int rc = 0;
-
-	VPRINTK("ENTER\n");
-
-	tmp = readl(mmio + CARM_HMUC);
-	if (tmp & CARM_Q_FULL) {
-#if 0
-		tmp = readl(mmio + CARM_INT_MASK);
-		tmp |= INT_Q_AVAILABLE;
-		writel(tmp, mmio + CARM_INT_MASK);
-		readl(mmio + CARM_INT_MASK);	/* flush */
-#endif
-		DPRINTK("host msg queue full\n");
-		rc = -EBUSY;
-	} else {
-		writel(msg | (cm_bucket << 1), mmio + CARM_IHQP);
-		readl(mmio + CARM_IHQP);	/* flush */
-	}
-
-	return rc;
-}
-
-static struct carm_request *carm_get_request(struct carm_host *host)
-{
-	unsigned int i;
-
-	/* obey global hardware limit on S/G entries */
-	if (host->hw_sg_used >= (CARM_MAX_HOST_SG - CARM_MAX_REQ_SG))
-		return NULL;
-
-	for (i = 0; i < CARM_MAX_Q; i++)
-		if ((host->msg_alloc & (1ULL << i)) == 0) {
-			struct carm_request *crq = &host->req[i];
-			crq->port = NULL;
-			crq->n_elem = 0;
-
-			host->msg_alloc |= (1ULL << i);
-			host->n_msgs++;
-
-			assert(host->n_msgs <= CARM_MAX_REQ);
-			return crq;
-		}
-	
-	DPRINTK("no request available, returning NULL\n");
-	return NULL;
-}
-
-static int carm_put_request(struct carm_host *host, struct carm_request *crq)
-{
-	assert(crq->tag < CARM_MAX_Q);
-
-	if (unlikely((host->msg_alloc & (1ULL << crq->tag)) == 0))
-		return -EINVAL; /* tried to clear a tag that was not active */
-
-	assert(host->hw_sg_used >= crq->n_elem);
-
-	host->msg_alloc &= ~(1ULL << crq->tag);
-	host->hw_sg_used -= crq->n_elem;
-	host->n_msgs--;
-
-	return 0;
-}
-
-static void carm_insert_special(request_queue_t *q, struct request *rq,
-				void *data, int at_head)
-{
-	unsigned long flags;
-
-	rq->cmd = SPECIAL;
-	rq->special = data;
-	rq->q = NULL;
-	rq->nr_segments = 0;
-	rq->elevator_sequence = 0;
-
-	spin_lock_irqsave(&io_request_lock, flags);
-	if (at_head)
-		list_add(&rq->queue, &q->queue_head);
-	else
-		list_add_tail(&rq->queue, &q->queue_head);
-	q->request_fn(q);
-	spin_unlock_irqrestore(&io_request_lock, flags);
-}
-
-static struct carm_request *carm_get_special(struct carm_host *host)
-{
-	unsigned long flags;
-	struct carm_request *crq = NULL;
-	int tries = 5000;
-
-	while (tries-- > 0) {
-		spin_lock_irqsave(&io_request_lock, flags);
-		crq = carm_get_request(host);
-		spin_unlock_irqrestore(&io_request_lock, flags);
-
-		if (crq)
-			break;
-		msleep(10);
-	}
-
-	if (!crq)
-		return NULL;
-
-	crq->rq = &crq->special_rq;
-	return crq;
-}
-
-static int carm_array_info (struct carm_host *host, unsigned int array_idx)
-{
-	struct carm_msg_ioctl *ioc;
-	unsigned int idx;
-	u32 msg_data;
-	dma_addr_t msg_dma;
-	struct carm_request *crq;
-	int rc;
-	unsigned long flags;
-
-	crq = carm_get_special(host);
-	if (!crq) {
-		rc = -ENOMEM;
-		goto err_out;
-	}
-
-	idx = crq->tag;
-
-	ioc = carm_ref_msg(host, idx);
-	msg_dma = carm_ref_msg_dma(host, idx);
-	msg_data = (u32) (msg_dma + sizeof(struct carm_array_info));
-
-	crq->msg_type = CARM_MSG_ARRAY;
-	crq->msg_subtype = CARM_ARRAY_INFO;
-	rc = carm_lookup_bucket(sizeof(struct carm_msg_ioctl) +
-				sizeof(struct carm_array_info));
-	BUG_ON(rc < 0);
-	crq->msg_bucket = (u32) rc;
-
-	memset(ioc, 0, sizeof(*ioc));
-	ioc->type	= CARM_MSG_ARRAY;
-	ioc->subtype	= CARM_ARRAY_INFO;
-	ioc->array_id	= (u8) array_idx;
-	ioc->handle	= cpu_to_le32(TAG_ENCODE(idx));
-	ioc->data_addr	= cpu_to_le32(msg_data);
-
-	assert(host->state == HST_DEV_SCAN_START ||
-	       host->state == HST_DEV_SCAN);
-
-	DPRINTK("blk_insert_request, tag == %u\n", idx);
-	carm_insert_special(&host->oob_q, crq->rq, crq, 1);
-
-	return 0;
-
-err_out:
-	spin_lock_irqsave(&io_request_lock, flags);
-	host->state = HST_ERROR;
-	spin_unlock_irqrestore(&io_request_lock, flags);
-	return rc;
-}
-
-typedef unsigned int (*carm_sspc_t)(struct carm_host *, unsigned int, void *);
-
-static int carm_send_special (struct carm_host *host, carm_sspc_t func)
-{
-	struct carm_request *crq;
-	struct carm_msg_ioctl *ioc;
-	void *mem;
-	unsigned int idx, msg_size;
-	int rc;
-
-	crq = carm_get_special(host);
-	if (!crq)
-		return -ENOMEM;
-
-	idx = crq->tag;
-
-	mem = carm_ref_msg(host, idx);
-
-	msg_size = func(host, idx, mem);
-
-	ioc = mem;
-	crq->msg_type = ioc->type;
-	crq->msg_subtype = ioc->subtype;
-	rc = carm_lookup_bucket(msg_size);
-	BUG_ON(rc < 0);
-	crq->msg_bucket = (u32) rc;
-
-	DPRINTK("blk_insert_request, tag == %u\n", idx);
-	carm_insert_special(&host->oob_q, crq->rq, crq, 1);
-
-	return 0;
-}
-
-static unsigned int carm_fill_sync_time(struct carm_host *host,
-					unsigned int idx, void *mem)
-{
-	struct timeval tv;
-	struct carm_msg_sync_time *st = mem;
-
-	do_gettimeofday(&tv);
-
-	memset(st, 0, sizeof(*st));
-	st->type	= CARM_MSG_MISC;
-	st->subtype	= MISC_SET_TIME;
-	st->handle	= cpu_to_le32(TAG_ENCODE(idx));
-	st->timestamp	= cpu_to_le32(tv.tv_sec);
-
-	return sizeof(struct carm_msg_sync_time);
-}
-
-static unsigned int carm_fill_alloc_buf(struct carm_host *host,
-					unsigned int idx, void *mem)
-{
-	struct carm_msg_allocbuf *ab = mem;
-
-	memset(ab, 0, sizeof(*ab));
-	ab->type	= CARM_MSG_MISC;
-	ab->subtype	= MISC_ALLOC_MEM;
-	ab->handle	= cpu_to_le32(TAG_ENCODE(idx));
-	ab->n_sg	= 1;
-	ab->sg_type	= SGT_32BIT;
-	ab->addr	= cpu_to_le32(host->shm_dma + (PDC_SHM_SIZE >> 1));
-	ab->len		= cpu_to_le32(PDC_SHM_SIZE >> 1);
-	ab->evt_pool	= cpu_to_le32(host->shm_dma + (16 * 1024));
-	ab->n_evt	= cpu_to_le32(1024);
-	ab->rbuf_pool	= cpu_to_le32(host->shm_dma);
-	ab->n_rbuf	= cpu_to_le32(RMSG_Q_LEN);
-	ab->msg_pool	= cpu_to_le32(host->shm_dma + RBUF_LEN);
-	ab->n_msg	= cpu_to_le32(CARM_Q_LEN);
-	ab->sg[0].start	= cpu_to_le32(host->shm_dma + (PDC_SHM_SIZE >> 1));
-	ab->sg[0].len	= cpu_to_le32(65536);
-
-	return sizeof(struct carm_msg_allocbuf);
-}
-
-static unsigned int carm_fill_scan_channels(struct carm_host *host,
-					    unsigned int idx, void *mem)
-{
-	struct carm_msg_ioctl *ioc = mem;
-	u32 msg_data = (u32) (carm_ref_msg_dma(host, idx) +
-			      IOC_SCAN_CHAN_OFFSET);
-
-	memset(ioc, 0, sizeof(*ioc));
-	ioc->type	= CARM_MSG_IOCTL;
-	ioc->subtype	= CARM_IOC_SCAN_CHAN;
-	ioc->handle	= cpu_to_le32(TAG_ENCODE(idx));
-	ioc->data_addr	= cpu_to_le32(msg_data);
-
-	/* fill output data area with "no device" default values */
-	mem += IOC_SCAN_CHAN_OFFSET;
-	memset(mem, IOC_SCAN_CHAN_NODEV, CARM_MAX_PORTS);
-
-	return IOC_SCAN_CHAN_OFFSET + CARM_MAX_PORTS;
-}
-
-static unsigned int carm_fill_get_fw_ver(struct carm_host *host,
-					 unsigned int idx, void *mem)
-{
-	struct carm_msg_get_fw_ver *ioc = mem;
-	u32 msg_data = (u32) (carm_ref_msg_dma(host, idx) + sizeof(*ioc));
-
-	memset(ioc, 0, sizeof(*ioc));
-	ioc->type	= CARM_MSG_MISC;
-	ioc->subtype	= MISC_GET_FW_VER;
-	ioc->handle	= cpu_to_le32(TAG_ENCODE(idx));
-	ioc->data_addr	= cpu_to_le32(msg_data);
-
-	return sizeof(struct carm_msg_get_fw_ver) +
-	       sizeof(struct carm_fw_ver);
-}
-
-static void carm_activate_disk(struct carm_host *host,
-			       struct carm_port *port)
-{
-	int minor_start = port->port_no << CARM_PART_SHIFT;
-	int start, end, i;
-
-	host->gendisk_hd[minor_start].nr_sects = port->capacity;
-	host->blk_sizes[minor_start] = port->capacity;
-
-	start = minor_start;
-	end = minor_start + CARM_MINORS_PER_MAJOR;
-	for (i = start; i < end; i++) {
-		invalidate_device(MKDEV(host->major, i), 1);
-		host->gendisk.part[i].start_sect = 0;
-		host->gendisk.part[i].nr_sects = 0;
-		host->blk_block_sizes[i] = 512;
-		host->blk_sect_sizes[i] = 512;
-	}
-
-	grok_partitions(&host->gendisk, port->port_no, 
-			CARM_MINORS_PER_MAJOR,
-			port->capacity);
-}
-
-static int carm_revalidate_disk(kdev_t dev)
-{
-	struct carm_host *host;
-	struct carm_port *port = NULL;
-
-	host = carm_from_dev(dev, &port);
-	if (!host)
-		return -EINVAL;
-
-	carm_activate_disk(host, port);
-
-	return 0;
-}
-
-static inline void complete_buffers(struct buffer_head *bh, int status)
-{
-	struct buffer_head *xbh;
-	
-	while (bh) {
-		xbh = bh->b_reqnext; 
-		bh->b_reqnext = NULL; 
-		blk_finished_io(bh->b_size >> 9);
-		bh->b_end_io(bh, status);
-		bh = xbh;
-	}
-} 
-
-static inline void carm_end_request_queued(struct carm_host *host,
-					   struct carm_request *crq,
-					   int uptodate)
-{
-	struct request *req = crq->rq;
-	int rc;
-
-	complete_buffers(req->bh, uptodate);
-	end_that_request_last(req);
-
-	rc = carm_put_request(host, crq);
-	assert(rc == 0);
-}
-
-static inline void carm_push_q (struct carm_host *host, request_queue_t *q)
-{
-	unsigned int idx = host->wait_q_prod % CARM_MAX_WAIT_Q;
-
-	VPRINTK("STOPPED QUEUE %p\n", q);
-
-	host->wait_q[idx] = q;
-	host->wait_q_prod++;
-	BUG_ON(host->wait_q_prod == host->wait_q_cons); /* overrun */
-}
-
-static inline request_queue_t *carm_pop_q(struct carm_host *host)
-{
-	unsigned int idx;
-
-	if (host->wait_q_prod == host->wait_q_cons)
-		return NULL;
-
-	idx = host->wait_q_cons % CARM_MAX_WAIT_Q;
-	host->wait_q_cons++;
-
-	return host->wait_q[idx];
-}
-
-static inline void carm_round_robin(struct carm_host *host)
-{
-	request_queue_t *q = carm_pop_q(host);
-	if (q) {
-		struct tasklet_struct *tasklet;
-		if (q == &host->oob_q)
-			tasklet = &host->oob_tasklet;
-		else {
-			struct carm_port *port = q->queuedata;
-			tasklet = &port->tasklet;
-		}
-		tasklet_schedule(tasklet);
-		VPRINTK("STARTED QUEUE %p\n", q);
-	}
-}
-
-static inline void carm_end_rq(struct carm_host *host, struct carm_request *crq,
-			int is_ok)
-{
-	carm_end_request_queued(host, crq, is_ok);
-	if (CARM_MAX_Q == 1)
-		carm_round_robin(host);
-	else if ((host->n_msgs <= CARM_MSG_LOW_WATER) &&
-		 (host->hw_sg_used <= CARM_SG_LOW_WATER)) {
-		carm_round_robin(host);
-	}
-}
-
-static inline int carm_new_segment(request_queue_t *q, struct request *rq)
-{
-        if (rq->nr_segments < CARM_MAX_REQ_SG) {
-                rq->nr_segments++;
-                return 1;
-        }
-        return 0;
-}
-
-static int carm_back_merge_fn(request_queue_t *q, struct request *rq,
-                             struct buffer_head *bh, int max_segments)
-{
-	if (blk_seg_merge_ok(rq->bhtail, bh))	
-                return 1;
-        return carm_new_segment(q, rq);
-}
-
-static int carm_front_merge_fn(request_queue_t *q, struct request *rq,
-                             struct buffer_head *bh, int max_segments)
-{
-	if (blk_seg_merge_ok(bh, rq->bh))
-                return 1;
-        return carm_new_segment(q, rq);
-}
-
-static int carm_merge_requests_fn(request_queue_t *q, struct request *rq,
-                                 struct request *nxt, int max_segments)
-{
-        int total_segments = rq->nr_segments + nxt->nr_segments;
-
-	if (blk_seg_merge_ok(rq->bhtail, nxt->bh))
-                total_segments--;
-
-        if (total_segments > CARM_MAX_REQ_SG)
-                return 0;
-
-        rq->nr_segments = total_segments;
-        return 1;
-}
-
-static void carm_oob_rq_fn(request_queue_t *q)
-{
-	struct carm_host *host = q->queuedata;
-
-	tasklet_schedule(&host->oob_tasklet);
-}
-
-static void carm_rq_fn(request_queue_t *q)
-{
-	struct carm_port *port = q->queuedata;
-
-	tasklet_schedule(&port->tasklet);
-}
-
-static void carm_oob_tasklet(unsigned long _data)
-{
-	struct carm_host *host = (void *) _data;
-	request_queue_t *q = &host->oob_q;
-	struct carm_request *crq;
-	struct request *rq;
-	int rc, have_work = 1;
-	struct list_head *queue_head = &q->queue_head;
-	unsigned long flags;
-
-	spin_lock_irqsave(&io_request_lock, flags);
-	if (q->plugged || list_empty(queue_head))
-		have_work = 0;
-
-	if (!have_work)
-		goto out;
-
-	while (1) {
-		DPRINTK("get req\n");
-		if (list_empty(queue_head))
-			break;
-
-		rq = blkdev_entry_next_request(queue_head);
-
-		crq = rq->special;
-		assert(crq != NULL);
-		assert(crq->rq == rq);
-
-		crq->n_elem = 0;
-
-		DPRINTK("send req\n");
-		rc = carm_send_msg(host, crq);
-		if (rc) {
-			carm_push_q(host, q);
-			break;		/* call us again later, eventually */
-		} else
-			blkdev_dequeue_request(rq);
-	}
-
-out:
-	spin_unlock_irqrestore(&io_request_lock, flags);
-}
-
-static int blk_rq_map_sg(request_queue_t *q, struct request *rq,
-			 struct scatterlist *sg)
-{
-	int n_elem = 0;
-	struct buffer_head *bh = rq->bh;
-	u64 last_phys = ~0ULL;
-
-	while (bh) {
-		if (bh_phys(bh) == last_phys) {
-			sg[n_elem - 1].length += bh->b_size;
-			last_phys += bh->b_size;
-		} else {
-			if (unlikely(n_elem == CARM_MAX_REQ_SG))
-				BUG();
-			sg[n_elem].page = bh->b_page;
-			sg[n_elem].length = bh->b_size;
-			sg[n_elem].offset = bh_offset(bh);
-			last_phys = bh_phys(bh) + bh->b_size;
-			n_elem++;
-		}
-
-		bh = bh->b_reqnext;
-	}
-
-	return n_elem;
-}
-
-static void carm_rw_tasklet(unsigned long _data)
-{
-	struct carm_port *port = (void *) _data;
-	struct carm_host *host = port->host;
-	request_queue_t *q = &port->q;
-	struct carm_msg_rw *msg;
-	struct carm_request *crq;
-	struct request *rq;
-	struct scatterlist *sg;
-	int writing = 0, pci_dir, i, n_elem, rc, have_work = 1;
-	u32 tmp;
-	unsigned int msg_size;
-	unsigned long flags;
-	struct list_head *queue_head = &q->queue_head;
-	unsigned long start_sector;
-
-	spin_lock_irqsave(&io_request_lock, flags);
-	if (q->plugged || list_empty(queue_head))
-		have_work = 0;
-
-	if (!have_work)
-		goto out;
-
-queue_one_request:
-	VPRINTK("get req\n");
-	if (list_empty(queue_head))
-		goto out;
-
-	rq = blkdev_entry_next_request(queue_head);
-
-	crq = carm_get_request(host);
-	if (!crq) {
-		carm_push_q(host, q);
-		goto out;	/* call us again later, eventually */
-	}
-	crq->rq = rq;
-
-	if (rq_data_dir(rq) == WRITE) {
-		writing = 1;
-		pci_dir = PCI_DMA_TODEVICE;
-	} else {
-		pci_dir = PCI_DMA_FROMDEVICE;
-	}
-
-	/* get scatterlist from block layer */
-	sg = &crq->sg[0];
-	n_elem = blk_rq_map_sg(q, rq, sg);
-	if (n_elem <= 0) {
-		carm_end_rq(host, crq, 0);
-		goto out;	/* request with no s/g entries? */
-	}
-
-	/* map scatterlist to PCI bus addresses */
-	n_elem = pci_map_sg(host->pdev, sg, n_elem, pci_dir);
-	if (n_elem <= 0) {
-		carm_end_rq(host, crq, 0);
-		goto out;	/* request with no s/g entries? */
-	}
-	crq->n_elem = n_elem;
-	crq->port = port;
-	host->hw_sg_used += n_elem;
-
-	/*
-	 * build read/write message
-	 */
-
-	VPRINTK("build msg\n");
-	msg = (struct carm_msg_rw *) carm_ref_msg(host, crq->tag);
-
-	if (writing) {
-		msg->type = CARM_MSG_WRITE;
-		crq->msg_type = CARM_MSG_WRITE;
-	} else {
-		msg->type = CARM_MSG_READ;
-		crq->msg_type = CARM_MSG_READ;
-	}
-
-	start_sector = rq->sector;
-	start_sector += host->gendisk_hd[MINOR(rq->rq_dev)].start_sect;
-
-	msg->id		= port->port_no;
-	msg->sg_count	= n_elem;
-	msg->sg_type	= SGT_32BIT;
-	msg->handle	= cpu_to_le32(TAG_ENCODE(crq->tag));
-	msg->lba	= cpu_to_le32(start_sector & 0xffffffff);
-	tmp		= (start_sector >> 16) >> 16;
-	msg->lba_high	= cpu_to_le16( (u16) tmp );
-	msg->lba_count	= cpu_to_le16(rq->nr_sectors);
-
-	msg_size = sizeof(struct carm_msg_rw) - sizeof(msg->sg);
-	for (i = 0; i < n_elem; i++) {
-		struct carm_msg_sg *carm_sg = &msg->sg[i];
-		carm_sg->start = cpu_to_le32(sg_dma_address(&crq->sg[i]));
-		carm_sg->len = cpu_to_le32(sg_dma_len(&crq->sg[i]));
-		msg_size += sizeof(struct carm_msg_sg);
-	}
-
-	rc = carm_lookup_bucket(msg_size);
-	BUG_ON(rc < 0);
-	crq->msg_bucket = (u32) rc;
-
-	/*
-	 * queue read/write message to hardware
-	 */
-
-	VPRINTK("send msg, tag == %u\n", crq->tag);
-	rc = carm_send_msg(host, crq);
-	if (rc) {
-		carm_put_request(host, crq);
-		carm_push_q(host, q);
-		goto out;	/* call us again later, eventually */
-	} else
-		blkdev_dequeue_request(rq);
-
-	goto queue_one_request;
-
-out:
-	spin_unlock_irqrestore(&io_request_lock, flags);
-}
-
-static void carm_handle_array_info(struct carm_host *host,
-				   struct carm_request *crq, u8 *mem,
-				   int is_ok)
-{
-	struct carm_port *port;
-	u8 *msg_data = mem + sizeof(struct carm_array_info);
-	struct carm_array_info *desc = (struct carm_array_info *) msg_data;
-	u64 lo, hi;
-	int cur_port;
-	size_t slen;
-
-	DPRINTK("ENTER\n");
-
-	carm_end_rq(host, crq, is_ok);
-
-	if (!is_ok)
-		goto out;
-	if (le32_to_cpu(desc->array_status) & ARRAY_NO_EXIST)
-		goto out;
-
-	cur_port = host->cur_scan_dev;
-
-	/* should never occur */
-	if ((cur_port < 0) || (cur_port >= CARM_MAX_PORTS)) {
-		printk(KERN_ERR PFX "BUG: cur_scan_dev==%d, array_id==%d\n",
-		       cur_port, (int) desc->array_id);
-		goto out;
-	}
-
-	port = &host->port[cur_port];
-
-	lo = (u64) le32_to_cpu(desc->size);
-	hi = (u64) le32_to_cpu(desc->size_hi);
-
-	port->capacity = lo | (hi << 32);
-	port->dev_geom_head = le16_to_cpu(desc->head);
-	port->dev_geom_sect = le16_to_cpu(desc->sect);
-	port->dev_geom_cyl = le16_to_cpu(desc->cyl);
-
-	host->dev_active |= (1 << cur_port);
-
-	strncpy(port->name, desc->name, sizeof(port->name));
-	port->name[sizeof(port->name) - 1] = 0;
-	slen = strlen(port->name);
-	while (slen && (port->name[slen - 1] == ' ')) {
-		port->name[slen - 1] = 0;
-		slen--;
-	}
-
-	printk(KERN_INFO DRV_NAME "(%s): port %u device %Lu sectors\n",
-	       pci_name(host->pdev), port->port_no, port->capacity);
-	printk(KERN_INFO DRV_NAME "(%s): port %u device \"%s\"\n",
-	       pci_name(host->pdev), port->port_no, port->name);
-
-out:
-	assert(host->state == HST_DEV_SCAN);
-	schedule_task(&host->fsm_task);
-}
-
-static void carm_handle_scan_chan(struct carm_host *host,
-				  struct carm_request *crq, u8 *mem,
-				  int is_ok)
-{
-	u8 *msg_data = mem + IOC_SCAN_CHAN_OFFSET;
-	unsigned int i, dev_count = 0;
-	int new_state = HST_DEV_SCAN_START;
-
-	DPRINTK("ENTER\n");
-
-	carm_end_rq(host, crq, is_ok);
-
-	if (!is_ok) {
-		new_state = HST_ERROR;
-		goto out;
-	}
-
-	/* TODO: scan and support non-disk devices */
-	for (i = 0; i < 8; i++)
-		if (msg_data[i] == 0) { /* direct-access device (disk) */
-			host->dev_present |= (1 << i);
-			dev_count++;
-		}
-
-	printk(KERN_INFO DRV_NAME "(%s): found %u interesting devices\n",
-	       pci_name(host->pdev), dev_count);
-
-out:
-	assert(host->state == HST_PORT_SCAN);
-	host->state = new_state;
-	schedule_task(&host->fsm_task);
-}
-
-static void carm_handle_generic(struct carm_host *host,
-				struct carm_request *crq, int is_ok,
-				int cur_state, int next_state)
-{
-	DPRINTK("ENTER\n");
-
-	carm_end_rq(host, crq, is_ok);
-
-	assert(host->state == cur_state);
-	if (is_ok)
-		host->state = next_state;
-	else
-		host->state = HST_ERROR;
-	schedule_task(&host->fsm_task);
-}
-
-static inline void carm_handle_rw(struct carm_host *host,
-				  struct carm_request *crq, int is_ok)
-{
-	int pci_dir;
-
-	VPRINTK("ENTER\n");
-
-	if (rq_data_dir(crq->rq) == WRITE)
-		pci_dir = PCI_DMA_TODEVICE;
-	else
-		pci_dir = PCI_DMA_FROMDEVICE;
-
-	pci_unmap_sg(host->pdev, &crq->sg[0], crq->n_elem, pci_dir);
-
-	carm_end_rq(host, crq, is_ok);
-}
-
-static inline void carm_handle_resp(struct carm_host *host,
-				    u32 ret_handle_le, u32 status)
-{
-	u32 handle = le32_to_cpu(ret_handle_le);
-	unsigned int msg_idx;
-	struct carm_request *crq;
-	int is_ok = (status == RMSG_OK);
-	u8 *mem;
-
-	VPRINTK("ENTER, handle == 0x%x\n", handle);
-
-	if (unlikely(!TAG_VALID(handle))) {
-		printk(KERN_ERR DRV_NAME "(%s): BUG: invalid tag 0x%x\n",
-		       pci_name(host->pdev), handle);
-		return;
-	}
-
-	msg_idx = TAG_DECODE(handle);
-	VPRINTK("tag == %u\n", msg_idx);
-
-	crq = &host->req[msg_idx];
-
-	/* fast path */
-	if (likely(crq->msg_type == CARM_MSG_READ ||
-		   crq->msg_type == CARM_MSG_WRITE)) {
-		carm_handle_rw(host, crq, is_ok);
-		return;
-	}
-
-	mem = carm_ref_msg(host, msg_idx);
-
-	switch (crq->msg_type) {
-	case CARM_MSG_IOCTL: {
-		switch (crq->msg_subtype) {
-		case CARM_IOC_SCAN_CHAN:
-			carm_handle_scan_chan(host, crq, mem, is_ok);
-			break;
-		default:
-			/* unknown / invalid response */
-			goto err_out;
-		}
-		break;
-	}
-
-	case CARM_MSG_MISC: {
-		switch (crq->msg_subtype) {
-		case MISC_ALLOC_MEM:
-			carm_handle_generic(host, crq, is_ok,
-					    HST_ALLOC_BUF, HST_SYNC_TIME);
-			break;
-		case MISC_SET_TIME:
-			carm_handle_generic(host, crq, is_ok,
-					    HST_SYNC_TIME, HST_GET_FW_VER);
-			break;
-		case MISC_GET_FW_VER: {
-			struct carm_fw_ver *ver = (struct carm_fw_ver *)
-				mem + sizeof(struct carm_msg_get_fw_ver);
-			if (is_ok) {
-				host->fw_ver = le32_to_cpu(ver->version);
-				host->flags |= (ver->features & FL_FW_VER_MASK);
-			}
-			carm_handle_generic(host, crq, is_ok,
-					    HST_GET_FW_VER, HST_PORT_SCAN);
-			break;
-		}
-		default:
-			/* unknown / invalid response */
-			goto err_out;
-		}
-		break;
-	}
-
-	case CARM_MSG_ARRAY: {
-		switch (crq->msg_subtype) {
-		case CARM_ARRAY_INFO:
-			carm_handle_array_info(host, crq, mem, is_ok);
-			break;
-		default:
-			/* unknown / invalid response */
-			goto err_out;
-		}
-		break;
-	}
-
-	default:
-		/* unknown / invalid response */
-		goto err_out;
-	}
-
-	return;
-
-err_out:
-	printk(KERN_WARNING DRV_NAME "(%s): BUG: unhandled message type %d/%d\n",
-	       pci_name(host->pdev), crq->msg_type, crq->msg_subtype);
-	carm_end_rq(host, crq, 0);
-}
-
-static inline void carm_handle_responses(struct carm_host *host)
-{
-	void *mmio = host->mmio;
-	struct carm_response *resp = (struct carm_response *) host->shm;
-	unsigned int work = 0;
-	unsigned int idx = host->resp_idx % RMSG_Q_LEN;
-
-	while (1) {
-		u32 status = le32_to_cpu(resp[idx].status);
-
-		if (status == 0xffffffff) {
-			VPRINTK("ending response on index %u\n", idx);
-			writel(idx << 3, mmio + CARM_RESP_IDX);
-			break;
-		}
-
-		/* response to a message we sent */
-		else if ((status & (1 << 31)) == 0) {
-			VPRINTK("handling msg response on index %u\n", idx);
-			carm_handle_resp(host, resp[idx].ret_handle, status);
-			resp[idx].status = 0xffffffff;
-		}
-
-		/* asynchronous events the hardware throws our way */
-		else if ((status & 0xff000000) == (1 << 31)) {
-			u8 *evt_type_ptr = (u8 *) &resp[idx];
-			u8 evt_type = *evt_type_ptr;
-			printk(KERN_WARNING DRV_NAME "(%s): unhandled event type %d\n",
-			       pci_name(host->pdev), (int) evt_type);
-			resp[idx].status = 0xffffffff;
-		}
-
-		idx = NEXT_RESP(idx);
-		work++;
-	}
-
-	VPRINTK("EXIT, work==%u\n", work);
-	host->resp_idx += work;
-}
-
-static irqreturn_t carm_interrupt(int irq, void *__host, struct pt_regs *regs)
-{
-	struct carm_host *host = __host;
-	void *mmio;
-	u32 mask;
-	int handled = 0;
-	unsigned long flags;
-
-	if (!host) {
-		VPRINTK("no host\n");
-		return IRQ_NONE;
-	}
-
-	spin_lock_irqsave(&io_request_lock, flags);
-
-	mmio = host->mmio;
-
-	/* reading should also clear interrupts */
-	mask = readl(mmio + CARM_INT_STAT);
-
-	if (mask == 0 || mask == 0xffffffff) {
-		VPRINTK("no work, mask == 0x%x\n", mask);
-		goto out;
-	}
-
-	if (mask & INT_ACK_MASK)
-		writel(mask, mmio + CARM_INT_STAT);
-
-	if (unlikely(host->state == HST_INVALID)) {
-		VPRINTK("not initialized yet, mask = 0x%x\n", mask);
-		goto out;
-	}
-
-	if (mask & CARM_HAVE_RESP) {
-		handled = 1;
-		carm_handle_responses(host);
-	}
-
-out:
-	spin_unlock_irqrestore(&io_request_lock, flags);
-	VPRINTK("EXIT\n");
-	return IRQ_RETVAL(handled);
-}
-
-static void carm_fsm_task (void *_data)
-{
-	struct carm_host *host = _data;
-	unsigned long flags;
-	unsigned int state;
-	int rc, i, next_dev;
-	int reschedule = 0;
-	int new_state = HST_INVALID;
-
-	spin_lock_irqsave(&io_request_lock, flags);
-	state = host->state;
-	spin_unlock_irqrestore(&io_request_lock, flags);
-
-	DPRINTK("ENTER, state == %s\n", state_name[state]);
-
-	switch (state) {
-	case HST_PROBE_START:
-		new_state = HST_ALLOC_BUF;
-		reschedule = 1;
-		break;
-
-	case HST_ALLOC_BUF:
-		rc = carm_send_special(host, carm_fill_alloc_buf);
-		if (rc) {
-			new_state = HST_ERROR;
-			reschedule = 1;
-		}
-		break;
-
-	case HST_SYNC_TIME:
-		rc = carm_send_special(host, carm_fill_sync_time);
-		if (rc) {
-			new_state = HST_ERROR;
-			reschedule = 1;
-		}
-		break;
-
-	case HST_GET_FW_VER:
-		rc = carm_send_special(host, carm_fill_get_fw_ver);
-		if (rc) {
-			new_state = HST_ERROR;
-			reschedule = 1;
-		}
-		break;
-
-	case HST_PORT_SCAN:
-		rc = carm_send_special(host, carm_fill_scan_channels);
-		if (rc) {
-			new_state = HST_ERROR;
-			reschedule = 1;
-		}
-		break;
-
-	case HST_DEV_SCAN_START:
-		host->cur_scan_dev = -1;
-		new_state = HST_DEV_SCAN;
-		reschedule = 1;
-		break;
-
-	case HST_DEV_SCAN:
-		next_dev = -1;
-		for (i = host->cur_scan_dev + 1; i < CARM_MAX_PORTS; i++)
-			if (host->dev_present & (1 << i)) {
-				next_dev = i;
-				break;
-			}
-
-		if (next_dev >= 0) {
-			host->cur_scan_dev = next_dev;
-			rc = carm_array_info(host, next_dev);
-			if (rc) {
-				new_state = HST_ERROR;
-				reschedule = 1;
-			}
-		} else {
-			new_state = HST_DEV_ACTIVATE;
-			reschedule = 1;
-		}
-		break;
-
-	case HST_DEV_ACTIVATE: {
-		int activated = 0;
-		for (i = 0; i < CARM_MAX_PORTS; i++)
-			if (host->dev_active & (1 << i)) {
-				carm_activate_disk(host, &host->port[i]);
-				activated++;
-			}
-
-		printk(KERN_INFO DRV_NAME "(%s): %d ports activated\n",
-		       pci_name(host->pdev), activated);
-
-		new_state = HST_PROBE_FINISHED;
-		reschedule = 1;
-		break;
-	}
-
-	case HST_PROBE_FINISHED:
-		up(&host->probe_sem);
-		break;
-
-	case HST_ERROR:
-		/* FIXME: TODO */
-		break;
-
-	default:
-		/* should never occur */
-		printk(KERN_ERR PFX "BUG: unknown state %d\n", state);
-		assert(0);
-		break;
-	}
-
-	if (new_state != HST_INVALID) {
-		spin_lock_irqsave(&io_request_lock, flags);
-		host->state = new_state;
-		spin_unlock_irqrestore(&io_request_lock, flags);
-	}
-	if (reschedule)
-		schedule_task(&host->fsm_task);
-}
-
-static int carm_init_wait(void *mmio, u32 bits, unsigned int test_bit)
-{
-	unsigned int i;
-
-	for (i = 0; i < 50000; i++) {
-		u32 tmp = readl(mmio + CARM_LMUC);
-		udelay(100);
-
-		if (test_bit) {
-			if ((tmp & bits) == bits)
-				return 0;
-		} else {
-			if ((tmp & bits) == 0)
-				return 0;
-		}
-
-		cond_resched();
-	}
-
-	printk(KERN_ERR PFX "carm_init_wait timeout, bits == 0x%x, test_bit == %s\n",
-	       bits, test_bit ? "yes" : "no");
-	return -EBUSY;
-}
-
-static void carm_init_responses(struct carm_host *host)
-{
-	void *mmio = host->mmio;
-	unsigned int i;
-	struct carm_response *resp = (struct carm_response *) host->shm;
-
-	for (i = 0; i < RMSG_Q_LEN; i++)
-		resp[i].status = 0xffffffff;
-
-	writel(0, mmio + CARM_RESP_IDX);
-}
-
-static int carm_init_host(struct carm_host *host)
-{
-	void *mmio = host->mmio;
-	u32 tmp;
-	u8 tmp8;
-	int rc;
-	unsigned long flags;
-
-	DPRINTK("ENTER\n");
-
-	writel(0, mmio + CARM_INT_MASK);
-
-	tmp8 = readb(mmio + CARM_INITC);
-	if (tmp8 & 0x01) {
-		tmp8 &= ~0x01;
-		writeb(tmp8, CARM_INITC);
-		readb(mmio + CARM_INITC);	/* flush */
-
-		DPRINTK("snooze...\n");
-		msleep(5000);
-	}
-
-	tmp = readl(mmio + CARM_HMUC);
-	if (tmp & CARM_CME) {
-		DPRINTK("CME bit present, waiting\n");
-		rc = carm_init_wait(mmio, CARM_CME, 1);
-		if (rc) {
-			DPRINTK("EXIT, carm_init_wait 1 failed\n");
-			return rc;
-		}
-	}
-	if (tmp & CARM_RME) {
-		DPRINTK("RME bit present, waiting\n");
-		rc = carm_init_wait(mmio, CARM_RME, 1);
-		if (rc) {
-			DPRINTK("EXIT, carm_init_wait 2 failed\n");
-			return rc;
-		}
-	}
-
-	tmp &= ~(CARM_RME | CARM_CME);
-	writel(tmp, mmio + CARM_HMUC);
-	readl(mmio + CARM_HMUC);	/* flush */
-
-	rc = carm_init_wait(mmio, CARM_RME | CARM_CME, 0);
-	if (rc) {
-		DPRINTK("EXIT, carm_init_wait 3 failed\n");
-		return rc;
-	}
-
-	carm_init_buckets(mmio);
-
-	writel(host->shm_dma & 0xffffffff, mmio + RBUF_ADDR_LO);
-	writel((host->shm_dma >> 16) >> 16, mmio + RBUF_ADDR_HI);
-	writel(RBUF_LEN, mmio + RBUF_BYTE_SZ);
-
-	tmp = readl(mmio + CARM_HMUC);
-	tmp |= (CARM_RME | CARM_CME | CARM_WZBC);
-	writel(tmp, mmio + CARM_HMUC);
-	readl(mmio + CARM_HMUC);	/* flush */
-
-	rc = carm_init_wait(mmio, CARM_RME | CARM_CME, 1);
-	if (rc) {
-		DPRINTK("EXIT, carm_init_wait 4 failed\n");
-		return rc;
-	}
-
-	writel(0, mmio + CARM_HMPHA);
-	writel(INT_DEF_MASK, mmio + CARM_INT_MASK);
-
-	carm_init_responses(host);
-
-	/* start initialization, probing state machine */
-	spin_lock_irqsave(&io_request_lock, flags);
-	assert(host->state == HST_INVALID);
-	host->state = HST_PROBE_START;
-	spin_unlock_irqrestore(&io_request_lock, flags);
-
-	schedule_task(&host->fsm_task);
-
-	DPRINTK("EXIT\n");
-	return 0;
-}
-
-static void carm_init_ports (struct carm_host *host)
-{
-	struct carm_port *port;
-	request_queue_t *q;
-	unsigned int i;
-
-	for (i = 0; i < CARM_MAX_PORTS; i++) {
-		port = &host->port[i];
-		port->magic = CARM_MAGIC_PORT;
-		port->host = host;
-		port->port_no = i;
-		tasklet_init(&port->tasklet, carm_rw_tasklet,
-			     (unsigned long) port);
-
-		q = &port->q;
-
-		blk_init_queue(q, carm_rq_fn);
-		q->queuedata = port;
-		blk_queue_bounce_limit(q, host->pdev->dma_mask);
-		blk_queue_headactive(q, 0);
-
-		q->back_merge_fn = carm_back_merge_fn;
-		q->front_merge_fn = carm_front_merge_fn;
-		q->merge_requests_fn = carm_merge_requests_fn;
-	}
-}
-
-static request_queue_t *carm_find_queue(kdev_t device)
-{
-	struct carm_host *host;
-
-	host = blk_dev[MAJOR(device)].data;
-	if (!host)
-		return NULL;
-	if (host->magic != CARM_MAGIC_HOST)
-		return NULL;
-
-	DPRINTK("match: major %d, minor %d\n",
-		MAJOR(device), MINOR(device));
-	return &host->port[MINOR(device) >> CARM_PART_SHIFT].q;
-}
-
-static int carm_init_disks(struct carm_host *host)
-{
-	host->gendisk.major = host->major;
-	host->gendisk.major_name = host->name;
-	host->gendisk.minor_shift = CARM_PART_SHIFT;
-	host->gendisk.max_p = CARM_MINORS_PER_MAJOR;
-	host->gendisk.part = host->gendisk_hd;
-	host->gendisk.sizes = host->blk_sizes;
-	host->gendisk.nr_real = CARM_MAX_PORTS;
-	host->gendisk.fops = &carm_bd_ops;
-
-	blk_dev[host->major].queue = carm_find_queue;
-	blk_dev[host->major].data = host;
-	blk_size[host->major] = host->blk_sizes;
-	blksize_size[host->major] = host->blk_block_sizes;
-	hardsect_size[host->major] = host->blk_sect_sizes;
-
-	add_gendisk(&host->gendisk);
-
-	return 0;
-}
-
-static void carm_free_disks(struct carm_host *host)
-{
-	unsigned int i;
-
-	del_gendisk(&host->gendisk);
-
-	for (i = 0; i < CARM_MAX_PORTS; i++) {
-		struct carm_port *port = &host->port[i];
-
-		blk_cleanup_queue(&port->q);
-	}
-
-	blk_dev[host->major].queue = NULL;
-	blk_dev[host->major].data = NULL;
-	blk_size[host->major] = NULL;
-	blksize_size[host->major] = NULL;
-	hardsect_size[host->major] = NULL;
-}
-
-static void carm_stop_tasklets(struct carm_host *host)
-{
-	unsigned int i;
-
-	tasklet_kill(&host->oob_tasklet);
-
-	for (i = 0; i < CARM_MAX_PORTS; i++) {
-		struct carm_port *port = &host->port[i];
-		tasklet_kill(&port->tasklet);
-	}
-}
-
-static int carm_init_shm(struct carm_host *host)
-{
-	host->shm = pci_alloc_consistent(host->pdev, CARM_SHM_SIZE,
-					 &host->shm_dma);
-	if (!host->shm)
-		return -ENOMEM;
-
-	host->msg_base = host->shm + RBUF_LEN;
-	host->msg_dma = host->shm_dma + RBUF_LEN;
-
-	memset(host->shm, 0xff, RBUF_LEN);
-	memset(host->msg_base, 0, PDC_SHM_SIZE - RBUF_LEN);
-
-	return 0;
-}
-
-static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
-{
-	static unsigned int printed_version;
-	struct carm_host *host;
-	unsigned int pci_dac;
-	int rc;
-	unsigned int i;
-
-	if (!printed_version++)
-		printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
-
-	rc = pci_enable_device(pdev);
-	if (rc)
-		return rc;
-
-	rc = pci_request_regions(pdev, DRV_NAME);
-	if (rc)
-		goto err_out;
-
-#if IF_64BIT_DMA_IS_POSSIBLE /* grrrr... */
-	rc = pci_set_dma_mask(pdev, 0xffffffffffffffffULL);
-	if (!rc) {
-		rc = pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL);
-		if (rc) {
-			printk(KERN_ERR DRV_NAME "(%s): consistent DMA mask failure\n",
-				pci_name(pdev));
-			goto err_out_regions;
-		}
-		pci_dac = 1;
-	} else {
-#endif
-		rc = pci_set_dma_mask(pdev, 0xffffffffULL);
-		if (rc) {
-			printk(KERN_ERR DRV_NAME "(%s): DMA mask failure\n",
-				pci_name(pdev));
-			goto err_out_regions;
-		}
-		pci_dac = 0;
-#if IF_64BIT_DMA_IS_POSSIBLE /* grrrr... */
-	}
-#endif
-
-	host = kmalloc(sizeof(*host), GFP_KERNEL);
-	if (!host) {
-		printk(KERN_ERR DRV_NAME "(%s): memory alloc failure\n",
-		       pci_name(pdev));
-		rc = -ENOMEM;
-		goto err_out_regions;
-	}
-
-	memset(host, 0, sizeof(*host));
-	host->magic = CARM_MAGIC_HOST;
-	host->pdev = pdev;
-	host->flags = pci_dac ? FL_DAC : 0;
-	INIT_TQUEUE(&host->fsm_task, carm_fsm_task, host);
-	INIT_LIST_HEAD(&host->host_list_node);
-	init_MUTEX_LOCKED(&host->probe_sem);
-	tasklet_init(&host->oob_tasklet, carm_oob_tasklet,
-		     (unsigned long) host);
-	carm_init_ports(host);
-
-	for (i = 0; i < ARRAY_SIZE(host->req); i++)
-		host->req[i].tag = i;
-
-	host->mmio = ioremap(pci_resource_start(pdev, 0),
-			     pci_resource_len(pdev, 0));
-	if (!host->mmio) {
-		printk(KERN_ERR DRV_NAME "(%s): MMIO alloc failure\n",
-		       pci_name(pdev));
-		rc = -ENOMEM;
-		goto err_out_kfree;
-	}
-
-	rc = carm_init_shm(host);
-	if (rc) {
-		printk(KERN_ERR DRV_NAME "(%s): DMA SHM alloc failure\n",
-		       pci_name(pdev));
-		goto err_out_iounmap;
-	}
-
-	blk_init_queue(&host->oob_q, carm_oob_rq_fn);
-	host->oob_q.queuedata = host;
-	blk_queue_bounce_limit(&host->oob_q, pdev->dma_mask);
-	blk_queue_headactive(&host->oob_q, 0);
-
-	/*
-	 * Figure out which major to use: 160, 161, or dynamic
-	 */
-	if (!test_and_set_bit(0, &carm_major_alloc))
-		host->major = 160;
-	else if (!test_and_set_bit(1, &carm_major_alloc))
-		host->major = 161;
-	else
-		host->flags |= FL_DYN_MAJOR;
-
-	host->id = carm_host_id;
-	sprintf(host->name, DRV_NAME "%d", carm_host_id);
-
-	rc = register_blkdev(host->major, host->name, &carm_bd_ops);
-	if (rc < 0)
-		goto err_out_free_majors;
-	if (host->flags & FL_DYN_MAJOR)
-		host->major = rc;
-
-	rc = carm_init_disks(host);
-	if (rc)
-		goto err_out_blkdev_disks;
-
-	pci_set_master(pdev);
-
-	rc = request_irq(pdev->irq, carm_interrupt, SA_SHIRQ, DRV_NAME, host);
-	if (rc) {
-		printk(KERN_ERR DRV_NAME "(%s): irq alloc failure\n",
-		       pci_name(pdev));
-		goto err_out_blkdev_disks;
-	}
-
-	rc = carm_init_host(host);
-	if (rc)
-		goto err_out_free_irq;
-
-	DPRINTK("waiting for probe_sem\n");
-	down(&host->probe_sem);
-
-	printk(KERN_INFO "%s: pci %s, ports %d, io %lx, irq %u, major %d\n",
-	       host->name, pci_name(pdev), (int) CARM_MAX_PORTS,
-	       pci_resource_start(pdev, 0), pdev->irq, host->major);
-
-	carm_host_id++;
-	pci_set_drvdata(pdev, host);
-	return 0;
-
-err_out_free_irq:
-	free_irq(pdev->irq, host);
-err_out_blkdev_disks:
-	carm_free_disks(host);
-	unregister_blkdev(host->major, host->name);
-err_out_free_majors:
-	if (host->major == 160)
-		clear_bit(0, &carm_major_alloc);
-	else if (host->major == 161)
-		clear_bit(1, &carm_major_alloc);
-	blk_cleanup_queue(&host->oob_q);
-	pci_free_consistent(pdev, CARM_SHM_SIZE, host->shm, host->shm_dma);
-err_out_iounmap:
-	iounmap(host->mmio);
-err_out_kfree:
-	kfree(host);
-err_out_regions:
-	pci_release_regions(pdev);
-err_out:
-	pci_disable_device(pdev);
-	return rc;
-}
-
-static void carm_remove_one (struct pci_dev *pdev)
-{
-	struct carm_host *host = pci_get_drvdata(pdev);
-
-	if (!host) {
-		printk(KERN_ERR PFX "BUG: no host data for PCI(%s)\n",
-		       pci_name(pdev));
-		return;
-	}
-
-	free_irq(pdev->irq, host);
-	carm_stop_tasklets(host);
-	carm_free_disks(host);
-	unregister_blkdev(host->major, host->name);
-	if (host->major == 160)
-		clear_bit(0, &carm_major_alloc);
-	else if (host->major == 161)
-		clear_bit(1, &carm_major_alloc);
-	blk_cleanup_queue(&host->oob_q);
-	pci_free_consistent(pdev, CARM_SHM_SIZE, host->shm, host->shm_dma);
-	iounmap(host->mmio);
-	kfree(host);
-	pci_release_regions(pdev);
-	pci_disable_device(pdev);
-	pci_set_drvdata(pdev, NULL);
-}
-
-static int __init carm_init(void)
-{
-	return pci_module_init(&carm_driver);
-}
-
-static void __exit carm_exit(void)
-{
-	pci_unregister_driver(&carm_driver);
-}
-
-module_init(carm_init);
-module_exit(carm_exit);
-
-
diff -urN linux-2.4.27-rc1/drivers/block/sx8.c linux-2.4.27-rc2/drivers/block/sx8.c
--- linux-2.4.27-rc1/drivers/block/sx8.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.27-rc2/drivers/block/sx8.c	2004-06-25 20:48:25.709054839 -0700
@@ -0,0 +1,2083 @@
+/*
+ *  carmel.c: Driver for Promise SATA SX8 looks-like-I2O hardware
+ *
+ *  Copyright 2004 Red Hat, Inc.
+ *
+ *  Author/maintainer:  Jeff Garzik <jgarzik@pobox.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 this archive
+ *  for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/blk.h>
+#include <linux/blkdev.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/compiler.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <linux/hdreg.h>
+#include <linux/fs.h>
+#include <linux/blkpg.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+
+MODULE_AUTHOR("Jeff Garzik");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Promise SATA SX8 (carmel) block driver");
+
+#if 0
+#define CARM_DEBUG
+#define CARM_VERBOSE_DEBUG
+#else
+#undef CARM_DEBUG
+#undef CARM_VERBOSE_DEBUG
+#endif
+#undef CARM_NDEBUG
+
+#define DRV_NAME "carmel"
+#define DRV_VERSION "0.8-24.1"
+#define PFX DRV_NAME ": "
+
+#define NEXT_RESP(idx)	((idx + 1) % RMSG_Q_LEN)
+
+/* 0xf is just arbitrary, non-zero noise; this is sorta like poisoning */
+#define TAG_ENCODE(tag)	(((tag) << 16) | 0xf)
+#define TAG_DECODE(tag)	(((tag) >> 16) & 0x1f)
+#define TAG_VALID(tag)	((((tag) & 0xf) == 0xf) && (TAG_DECODE(tag) < 32))
+
+/* note: prints function name for you */
+#ifdef CARM_DEBUG
+#define DPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args)
+#ifdef CARM_VERBOSE_DEBUG
+#define VPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args)
+#else
+#define VPRINTK(fmt, args...)
+#endif	/* CARM_VERBOSE_DEBUG */
+#else
+#define DPRINTK(fmt, args...)
+#define VPRINTK(fmt, args...)
+#endif	/* CARM_DEBUG */
+
+#ifdef CARM_NDEBUG
+#define assert(expr)
+#else
+#define assert(expr) \
+        if(unlikely(!(expr))) {                                   \
+        printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \
+        #expr,__FILE__,__FUNCTION__,__LINE__);          \
+        }
+#endif
+
+/* defines only for the constants which don't work well as enums */
+struct carm_host;
+
+enum {
+	/* adapter-wide limits */
+	CARM_MAX_PORTS		= 8,
+	CARM_SHM_SIZE		= (4096 << 7),
+	CARM_PART_SHIFT		= 5,
+	CARM_MINORS_PER_MAJOR	= (1 << CARM_PART_SHIFT),
+	CARM_MAX_WAIT_Q		= CARM_MAX_PORTS + 1,
+
+	/* command message queue limits */
+	CARM_MAX_REQ		= 64,	       /* max command msgs per host */
+	CARM_MAX_Q		= 1,		   /* one command at a time */
+	CARM_MSG_LOW_WATER	= (CARM_MAX_REQ / 4),	     /* refill mark */
+
+	/* S/G limits, host-wide and per-request */
+	CARM_MAX_REQ_SG		= 32,	     /* max s/g entries per request */
+	CARM_SG_BOUNDARY	= 0xffffUL,	    /* s/g segment boundary */
+	CARM_MAX_HOST_SG	= 600,		/* max s/g entries per host */
+	CARM_SG_LOW_WATER	= (CARM_MAX_HOST_SG / 4),   /* re-fill mark */
+
+	/* hardware registers */
+	CARM_IHQP		= 0x1c,
+	CARM_INT_STAT		= 0x10, /* interrupt status */
+	CARM_INT_MASK		= 0x14, /* interrupt mask */
+	CARM_HMUC		= 0x18, /* host message unit control */
+	RBUF_ADDR_LO		= 0x20, /* response msg DMA buf low 32 bits */
+	RBUF_ADDR_HI		= 0x24, /* response msg DMA buf high 32 bits */
+	RBUF_BYTE_SZ		= 0x28,
+	CARM_RESP_IDX		= 0x2c,
+	CARM_CMS0		= 0x30, /* command message size reg 0 */
+	CARM_LMUC		= 0x48,
+	CARM_HMPHA		= 0x6c,
+	CARM_INITC		= 0xb5,
+
+	/* bits in CARM_INT_{STAT,MASK} */
+	INT_RESERVED		= 0xfffffff0,
+	INT_WATCHDOG		= (1 << 3),	/* watchdog timer */
+	INT_Q_OVERFLOW		= (1 << 2),	/* cmd msg q overflow */
+	INT_Q_AVAILABLE		= (1 << 1),	/* cmd msg q has free space */
+	INT_RESPONSE		= (1 << 0),	/* response msg available */
+	INT_ACK_MASK		= INT_WATCHDOG | INT_Q_OVERFLOW,
+	INT_DEF_MASK		= INT_RESERVED | INT_Q_OVERFLOW |
+				  INT_RESPONSE,
+
+	/* command messages, and related register bits */
+	CARM_HAVE_RESP		= 0x01,
+	CARM_MSG_READ		= 1,
+	CARM_MSG_WRITE		= 2,
+	CARM_MSG_VERIFY		= 3,
+	CARM_MSG_GET_CAPACITY	= 4,
+	CARM_MSG_FLUSH		= 5,
+	CARM_MSG_IOCTL		= 6,
+	CARM_MSG_ARRAY		= 8,
+	CARM_MSG_MISC		= 9,
+	CARM_CME		= (1 << 2),
+	CARM_RME		= (1 << 1),
+	CARM_WZBC		= (1 << 0),
+	CARM_RMI		= (1 << 0),
+	CARM_Q_FULL		= (1 << 3),
+	CARM_MSG_SIZE		= 288,
+	CARM_Q_LEN		= 48,
+
+	/* CARM_MSG_IOCTL messages */
+	CARM_IOC_SCAN_CHAN	= 5,	/* scan channels for devices */
+	CARM_IOC_GET_TCQ	= 13,	/* get tcq/ncq depth */
+	CARM_IOC_SET_TCQ	= 14,	/* set tcq/ncq depth */
+
+	IOC_SCAN_CHAN_NODEV	= 0x1f,
+	IOC_SCAN_CHAN_OFFSET	= 0x40,
+
+	/* CARM_MSG_ARRAY messages */
+	CARM_ARRAY_INFO		= 0,
+
+	ARRAY_NO_EXIST		= (1 << 31),
+
+	/* response messages */
+	RMSG_SZ			= 8,	/* sizeof(struct carm_response) */
+	RMSG_Q_LEN		= 48,	/* resp. msg list length */
+	RMSG_OK			= 1,	/* bit indicating msg was successful */
+					/* length of entire resp. msg buffer */
+	RBUF_LEN		= RMSG_SZ * RMSG_Q_LEN,
+
+	PDC_SHM_SIZE		= (4096 << 7), /* length of entire h/w buffer */
+
+	/* CARM_MSG_MISC messages */
+	MISC_GET_FW_VER		= 2,
+	MISC_ALLOC_MEM		= 3,
+	MISC_SET_TIME		= 5,
+
+	/* MISC_GET_FW_VER feature bits */
+	FW_VER_4PORT		= (1 << 2), /* 1=4 ports, 0=8 ports */
+	FW_VER_NON_RAID		= (1 << 1), /* 1=non-RAID firmware, 0=RAID */
+	FW_VER_ZCR		= (1 << 0), /* zero channel RAID (whatever that is) */
+
+	/* carm_host flags */
+	FL_NON_RAID		= FW_VER_NON_RAID,
+	FL_4PORT		= FW_VER_4PORT,
+	FL_FW_VER_MASK		= (FW_VER_NON_RAID | FW_VER_4PORT),
+	FL_DAC			= (1 << 16),
+	FL_DYN_MAJOR		= (1 << 17),
+};
+
+enum carm_magic_numbers {
+	CARM_MAGIC_HOST		= 0xdeadbeefUL,
+	CARM_MAGIC_PORT		= 0xbedac0edUL,
+};
+
+enum scatter_gather_types {
+	SGT_32BIT		= 0,
+	SGT_64BIT		= 1,
+};
+
+enum host_states {
+	HST_INVALID,		/* invalid state; never used */
+	HST_ALLOC_BUF,		/* setting up master SHM area */
+	HST_ERROR,		/* we never leave here */
+	HST_PORT_SCAN,		/* start dev scan */
+	HST_DEV_SCAN_START,	/* start per-device probe */
+	HST_DEV_SCAN,		/* continue per-device probe */
+	HST_DEV_ACTIVATE,	/* activate devices we found */
+	HST_PROBE_FINISHED,	/* probe is complete */
+	HST_PROBE_START,	/* initiate probe */
+	HST_SYNC_TIME,		/* tell firmware what time it is */
+	HST_GET_FW_VER,		/* get firmware version, adapter port cnt */
+};
+
+#ifdef CARM_DEBUG
+static const char *state_name[] = {
+	"HST_INVALID",
+	"HST_ALLOC_BUF",
+	"HST_ERROR",
+	"HST_PORT_SCAN",
+	"HST_DEV_SCAN_START",
+	"HST_DEV_SCAN",
+	"HST_DEV_ACTIVATE",
+	"HST_PROBE_FINISHED",
+	"HST_PROBE_START",
+	"HST_SYNC_TIME",
+	"HST_GET_FW_VER",
+};
+#endif
+
+struct carm_port {
+	unsigned long			magic;
+	unsigned int			port_no;
+	unsigned int			n_queued;
+	struct carm_host		*host;
+	struct tasklet_struct		tasklet;
+	request_queue_t			q;
+
+	/* attached device characteristics */
+	u64				capacity;
+	char				name[41];
+	u16				dev_geom_head;
+	u16				dev_geom_sect;
+	u16				dev_geom_cyl;
+};
+
+struct carm_request {
+	unsigned int			tag;
+	int				n_elem;
+	unsigned int			msg_type;
+	unsigned int			msg_subtype;
+	unsigned int			msg_bucket;
+	struct request			*rq;
+	struct carm_port		*port;
+	struct request			special_rq;
+	struct scatterlist		sg[CARM_MAX_REQ_SG];
+};
+
+struct carm_host {
+	unsigned long			magic;
+	unsigned long			flags;
+	void				*mmio;
+	void				*shm;
+	dma_addr_t			shm_dma;
+
+	int				major;
+	int				id;
+	char				name[32];
+
+	struct pci_dev			*pdev;
+	unsigned int			state;
+	u32				fw_ver;
+
+	request_queue_t			oob_q;
+	unsigned int			n_oob;
+	struct tasklet_struct		oob_tasklet;
+
+	unsigned int			hw_sg_used;
+
+	unsigned int			resp_idx;
+
+	unsigned int			wait_q_prod;
+	unsigned int			wait_q_cons;
+	request_queue_t			*wait_q[CARM_MAX_WAIT_Q];
+
+	unsigned int			n_msgs;
+	u64				msg_alloc;
+	struct carm_request		req[CARM_MAX_REQ];
+	void				*msg_base;
+	dma_addr_t			msg_dma;
+
+	int				cur_scan_dev;
+	unsigned long			dev_active;
+	unsigned long			dev_present;
+	struct carm_port		port[CARM_MAX_PORTS];
+
+	struct tq_struct		fsm_task;
+
+	struct semaphore		probe_sem;
+
+	struct gendisk			gendisk;
+	struct hd_struct		gendisk_hd[256];
+	int				blk_sizes[256];
+	int				blk_block_sizes[256];
+	int				blk_sect_sizes[256];
+
+	struct list_head		host_list_node;
+};
+
+struct carm_response {
+	u32 ret_handle;
+	u32 status;
+}  __attribute__((packed));
+
+struct carm_msg_sg {
+	u32 start;
+	u32 len;
+}  __attribute__((packed));
+
+struct carm_msg_rw {
+	u8 type;
+	u8 id;
+	u8 sg_count;
+	u8 sg_type;
+	u32 handle;
+	u32 lba;
+	u16 lba_count;
+	u16 lba_high;
+	struct carm_msg_sg sg[32];
+}  __attribute__((packed));
+
+struct carm_msg_allocbuf {
+	u8 type;
+	u8 subtype;
+	u8 n_sg;
+	u8 sg_type;
+	u32 handle;
+	u32 addr;
+	u32 len;
+	u32 evt_pool;
+	u32 n_evt;
+	u32 rbuf_pool;
+	u32 n_rbuf;
+	u32 msg_pool;
+	u32 n_msg;
+	struct carm_msg_sg sg[8];
+}  __attribute__((packed));
+
+struct carm_msg_ioctl {
+	u8 type;
+	u8 subtype;
+	u8 array_id;
+	u8 reserved1;
+	u32 handle;
+	u32 data_addr;
+	u32 reserved2;
+}  __attribute__((packed));
+
+struct carm_msg_sync_time {
+	u8 type;
+	u8 subtype;
+	u16 reserved1;
+	u32 handle;
+	u32 reserved2;
+	u32 timestamp;
+}  __attribute__((packed));
+
+struct carm_msg_get_fw_ver {
+	u8 type;
+	u8 subtype;
+	u16 reserved1;
+	u32 handle;
+	u32 data_addr;
+	u32 reserved2;
+}  __attribute__((packed));
+
+struct carm_fw_ver {
+	u32 version;
+	u8 features;
+	u8 reserved1;
+	u16 reserved2;
+}  __attribute__((packed));
+
+struct carm_array_info {
+	u32 size;
+
+	u16 size_hi;
+	u16 stripe_size;
+
+	u32 mode;
+
+	u16 stripe_blk_sz;
+	u16 reserved1;
+
+	u16 cyl;
+	u16 head;
+
+	u16 sect;
+	u8 array_id;
+	u8 reserved2;
+
+	char name[40];
+
+	u32 array_status;
+
+	/* device list continues beyond this point? */
+}  __attribute__((packed));
+
+static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+static void carm_remove_one (struct pci_dev *pdev);
+static int carm_bdev_ioctl(struct inode *ino, struct file *fil,
+			   unsigned int cmd, unsigned long arg);
+static request_queue_t *carm_find_queue(kdev_t device);
+static int carm_revalidate_disk(kdev_t dev);
+
+static struct pci_device_id carm_pci_tbl[] = {
+	{ PCI_VENDOR_ID_PROMISE, 0x8000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
+	{ PCI_VENDOR_ID_PROMISE, 0x8002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
+	{ }	/* terminate list */
+};
+MODULE_DEVICE_TABLE(pci, carm_pci_tbl);
+
+static struct pci_driver carm_driver = {
+	.name		= DRV_NAME,
+	.id_table	= carm_pci_tbl,
+	.probe		= carm_init_one,
+	.remove		= carm_remove_one,
+};
+
+static struct block_device_operations carm_bd_ops = {
+	.owner		= THIS_MODULE,
+	.ioctl		= carm_bdev_ioctl,
+};
+
+static unsigned int carm_host_id;
+static unsigned long carm_major_alloc;
+
+
+static struct carm_host *carm_from_dev(kdev_t dev, struct carm_port **port_out)
+{
+	struct carm_host *host;
+	struct carm_port *port;
+	request_queue_t *q;
+
+	q = carm_find_queue(dev);
+	if (!q || !q->queuedata) {
+		printk(KERN_ERR PFX "queue not found for major %d minor %d\n",
+			MAJOR(dev), MINOR(dev));
+		return NULL;
+	}
+
+	port = q->queuedata;
+	if (unlikely(port->magic != CARM_MAGIC_PORT)) {
+		printk(KERN_ERR PFX "bad port magic number for major %d minor %d\n",
+			MAJOR(dev), MINOR(dev));
+		return NULL;
+	}
+
+	host = port->host;
+	if (unlikely(host->magic != CARM_MAGIC_HOST)) {
+		printk(KERN_ERR PFX "bad host magic number for major %d minor %d\n",
+			MAJOR(dev), MINOR(dev));
+		return NULL;
+	}
+
+	if (port_out)
+		*port_out = port;
+	return host;
+}
+
+static int carm_bdev_ioctl(struct inode *ino, struct file *fil,
+			   unsigned int cmd, unsigned long arg)
+{
+	void *usermem = (void *) arg;
+	struct carm_port *port = NULL;
+	struct carm_host *host;
+
+	host = carm_from_dev(ino->i_rdev, &port);
+	if (!host)
+		return -EINVAL;
+
+	switch (cmd) {
+	case HDIO_GETGEO: {
+		struct hd_geometry geom;
+
+		if (!usermem)
+			return -EINVAL;
+
+		if (port->dev_geom_cyl) {
+			geom.heads = port->dev_geom_head;
+			geom.sectors = port->dev_geom_sect;
+			geom.cylinders = port->dev_geom_cyl;
+		} else {
+			u32 tmp = ((u32)port->capacity) / (0xff * 0x3f);
+			geom.heads = 0xff;
+			geom.sectors = 0x3f;
+			if (tmp > 65536)
+				geom.cylinders = 0xffff;
+			else
+				geom.cylinders = tmp;
+		}
+		geom.start = host->gendisk_hd[MINOR(ino->i_rdev)].start_sect;
+
+		if (copy_to_user(usermem, &geom, sizeof(geom)))
+			return -EFAULT;
+		return 0;
+	}
+
+	case HDIO_GETGEO_BIG: {
+		struct hd_big_geometry geom;
+
+		if (!usermem)
+			return -EINVAL;
+
+		if (port->dev_geom_cyl) {
+			geom.heads = port->dev_geom_head;
+			geom.sectors = port->dev_geom_sect;
+			geom.cylinders = port->dev_geom_cyl;
+		} else {
+			geom.heads = 0xff;
+			geom.sectors = 0x3f;
+			geom.cylinders = ((u32)port->capacity) / (0xff * 0x3f);
+		}
+		geom.start = host->gendisk_hd[MINOR(ino->i_rdev)].start_sect;
+
+		if (copy_to_user(usermem, &geom, sizeof(geom)))
+			return -EFAULT;
+		return 0;
+	}
+
+	case BLKRRPART:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		return carm_revalidate_disk(ino->i_rdev);
+
+	case BLKGETSIZE:
+	case BLKGETSIZE64:
+	case BLKFLSBUF:
+	case BLKBSZSET:
+	case BLKBSZGET:
+	case BLKROSET:
+	case BLKROGET:
+	case BLKRASET:
+	case BLKRAGET:
+	case BLKPG:
+	case BLKELVGET:
+	case BLKELVSET:
+		return blk_ioctl(ino->i_rdev, cmd, arg);
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static inline unsigned long msecs_to_jiffies(unsigned long msecs)
+{
+	return ((HZ * msecs + 999) / 1000);
+}
+
+static void msleep(unsigned long msecs)
+{
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(msecs_to_jiffies(msecs) + 1);
+}
+
+static const u32 msg_sizes[] = { 32, 64, 128, CARM_MSG_SIZE };
+
+static inline int carm_lookup_bucket(u32 msg_size)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(msg_sizes); i++)
+		if (msg_size <= msg_sizes[i])
+			return i;
+	
+	return -ENOENT;
+}
+
+static void carm_init_buckets(void *mmio)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(msg_sizes); i++)
+		writel(msg_sizes[i], mmio + CARM_CMS0 + (4 * i));
+}
+
+static inline void *carm_ref_msg(struct carm_host *host,
+				 unsigned int msg_idx)
+{
+	return host->msg_base + (msg_idx * CARM_MSG_SIZE);
+}
+
+static inline dma_addr_t carm_ref_msg_dma(struct carm_host *host,
+					  unsigned int msg_idx)
+{
+	return host->msg_dma + (msg_idx * CARM_MSG_SIZE);
+}
+
+static int carm_send_msg(struct carm_host *host,
+			 struct carm_request *crq)
+{
+	void *mmio = host->mmio;
+	u32 msg = (u32) carm_ref_msg_dma(host, crq->tag);
+	u32 cm_bucket = crq->msg_bucket;
+	u32 tmp;
+	int rc = 0;
+
+	VPRINTK("ENTER\n");
+
+	tmp = readl(mmio + CARM_HMUC);
+	if (tmp & CARM_Q_FULL) {
+#if 0
+		tmp = readl(mmio + CARM_INT_MASK);
+		tmp |= INT_Q_AVAILABLE;
+		writel(tmp, mmio + CARM_INT_MASK);
+		readl(mmio + CARM_INT_MASK);	/* flush */
+#endif
+		DPRINTK("host msg queue full\n");
+		rc = -EBUSY;
+	} else {
+		writel(msg | (cm_bucket << 1), mmio + CARM_IHQP);
+		readl(mmio + CARM_IHQP);	/* flush */
+	}
+
+	return rc;
+}
+
+static struct carm_request *carm_get_request(struct carm_host *host)
+{
+	unsigned int i;
+
+	/* obey global hardware limit on S/G entries */
+	if (host->hw_sg_used >= (CARM_MAX_HOST_SG - CARM_MAX_REQ_SG))
+		return NULL;
+
+	for (i = 0; i < CARM_MAX_Q; i++)
+		if ((host->msg_alloc & (1ULL << i)) == 0) {
+			struct carm_request *crq = &host->req[i];
+			crq->port = NULL;
+			crq->n_elem = 0;
+
+			host->msg_alloc |= (1ULL << i);
+			host->n_msgs++;
+
+			assert(host->n_msgs <= CARM_MAX_REQ);
+			return crq;
+		}
+	
+	DPRINTK("no request available, returning NULL\n");
+	return NULL;
+}
+
+static int carm_put_request(struct carm_host *host, struct carm_request *crq)
+{
+	assert(crq->tag < CARM_MAX_Q);
+
+	if (unlikely((host->msg_alloc & (1ULL << crq->tag)) == 0))
+		return -EINVAL; /* tried to clear a tag that was not active */
+
+	assert(host->hw_sg_used >= crq->n_elem);
+
+	host->msg_alloc &= ~(1ULL << crq->tag);
+	host->hw_sg_used -= crq->n_elem;
+	host->n_msgs--;
+
+	return 0;
+}
+
+static void carm_insert_special(request_queue_t *q, struct request *rq,
+				void *data, int at_head)
+{
+	unsigned long flags;
+
+	rq->cmd = SPECIAL;
+	rq->special = data;
+	rq->q = NULL;
+	rq->nr_segments = 0;
+	rq->elevator_sequence = 0;
+
+	spin_lock_irqsave(&io_request_lock, flags);
+	if (at_head)
+		list_add(&rq->queue, &q->queue_head);
+	else
+		list_add_tail(&rq->queue, &q->queue_head);
+	q->request_fn(q);
+	spin_unlock_irqrestore(&io_request_lock, flags);
+}
+
+static struct carm_request *carm_get_special(struct carm_host *host)
+{
+	unsigned long flags;
+	struct carm_request *crq = NULL;
+	int tries = 5000;
+
+	while (tries-- > 0) {
+		spin_lock_irqsave(&io_request_lock, flags);
+		crq = carm_get_request(host);
+		spin_unlock_irqrestore(&io_request_lock, flags);
+
+		if (crq)
+			break;
+		msleep(10);
+	}
+
+	if (!crq)
+		return NULL;
+
+	crq->rq = &crq->special_rq;
+	return crq;
+}
+
+static int carm_array_info (struct carm_host *host, unsigned int array_idx)
+{
+	struct carm_msg_ioctl *ioc;
+	unsigned int idx;
+	u32 msg_data;
+	dma_addr_t msg_dma;
+	struct carm_request *crq;
+	int rc;
+	unsigned long flags;
+
+	crq = carm_get_special(host);
+	if (!crq) {
+		rc = -ENOMEM;
+		goto err_out;
+	}
+
+	idx = crq->tag;
+
+	ioc = carm_ref_msg(host, idx);
+	msg_dma = carm_ref_msg_dma(host, idx);
+	msg_data = (u32) (msg_dma + sizeof(struct carm_array_info));
+
+	crq->msg_type = CARM_MSG_ARRAY;
+	crq->msg_subtype = CARM_ARRAY_INFO;
+	rc = carm_lookup_bucket(sizeof(struct carm_msg_ioctl) +
+				sizeof(struct carm_array_info));
+	BUG_ON(rc < 0);
+	crq->msg_bucket = (u32) rc;
+
+	memset(ioc, 0, sizeof(*ioc));
+	ioc->type	= CARM_MSG_ARRAY;
+	ioc->subtype	= CARM_ARRAY_INFO;
+	ioc->array_id	= (u8) array_idx;
+	ioc->handle	= cpu_to_le32(TAG_ENCODE(idx));
+	ioc->data_addr	= cpu_to_le32(msg_data);
+
+	assert(host->state == HST_DEV_SCAN_START ||
+	       host->state == HST_DEV_SCAN);
+
+	DPRINTK("blk_insert_request, tag == %u\n", idx);
+	carm_insert_special(&host->oob_q, crq->rq, crq, 1);
+
+	return 0;
+
+err_out:
+	spin_lock_irqsave(&io_request_lock, flags);
+	host->state = HST_ERROR;
+	spin_unlock_irqrestore(&io_request_lock, flags);
+	return rc;
+}
+
+typedef unsigned int (*carm_sspc_t)(struct carm_host *, unsigned int, void *);
+
+static int carm_send_special (struct carm_host *host, carm_sspc_t func)
+{
+	struct carm_request *crq;
+	struct carm_msg_ioctl *ioc;
+	void *mem;
+	unsigned int idx, msg_size;
+	int rc;
+
+	crq = carm_get_special(host);
+	if (!crq)
+		return -ENOMEM;
+
+	idx = crq->tag;
+
+	mem = carm_ref_msg(host, idx);
+
+	msg_size = func(host, idx, mem);
+
+	ioc = mem;
+	crq->msg_type = ioc->type;
+	crq->msg_subtype = ioc->subtype;
+	rc = carm_lookup_bucket(msg_size);
+	BUG_ON(rc < 0);
+	crq->msg_bucket = (u32) rc;
+
+	DPRINTK("blk_insert_request, tag == %u\n", idx);
+	carm_insert_special(&host->oob_q, crq->rq, crq, 1);
+
+	return 0;
+}
+
+static unsigned int carm_fill_sync_time(struct carm_host *host,
+					unsigned int idx, void *mem)
+{
+	struct timeval tv;
+	struct carm_msg_sync_time *st = mem;
+
+	do_gettimeofday(&tv);
+
+	memset(st, 0, sizeof(*st));
+	st->type	= CARM_MSG_MISC;
+	st->subtype	= MISC_SET_TIME;
+	st->handle	= cpu_to_le32(TAG_ENCODE(idx));
+	st->timestamp	= cpu_to_le32(tv.tv_sec);
+
+	return sizeof(struct carm_msg_sync_time);
+}
+
+static unsigned int carm_fill_alloc_buf(struct carm_host *host,
+					unsigned int idx, void *mem)
+{
+	struct carm_msg_allocbuf *ab = mem;
+
+	memset(ab, 0, sizeof(*ab));
+	ab->type	= CARM_MSG_MISC;
+	ab->subtype	= MISC_ALLOC_MEM;
+	ab->handle	= cpu_to_le32(TAG_ENCODE(idx));
+	ab->n_sg	= 1;
+	ab->sg_type	= SGT_32BIT;
+	ab->addr	= cpu_to_le32(host->shm_dma + (PDC_SHM_SIZE >> 1));
+	ab->len		= cpu_to_le32(PDC_SHM_SIZE >> 1);
+	ab->evt_pool	= cpu_to_le32(host->shm_dma + (16 * 1024));
+	ab->n_evt	= cpu_to_le32(1024);
+	ab->rbuf_pool	= cpu_to_le32(host->shm_dma);
+	ab->n_rbuf	= cpu_to_le32(RMSG_Q_LEN);
+	ab->msg_pool	= cpu_to_le32(host->shm_dma + RBUF_LEN);
+	ab->n_msg	= cpu_to_le32(CARM_Q_LEN);
+	ab->sg[0].start	= cpu_to_le32(host->shm_dma + (PDC_SHM_SIZE >> 1));
+	ab->sg[0].len	= cpu_to_le32(65536);
+
+	return sizeof(struct carm_msg_allocbuf);
+}
+
+static unsigned int carm_fill_scan_channels(struct carm_host *host,
+					    unsigned int idx, void *mem)
+{
+	struct carm_msg_ioctl *ioc = mem;
+	u32 msg_data = (u32) (carm_ref_msg_dma(host, idx) +
+			      IOC_SCAN_CHAN_OFFSET);
+
+	memset(ioc, 0, sizeof(*ioc));
+	ioc->type	= CARM_MSG_IOCTL;
+	ioc->subtype	= CARM_IOC_SCAN_CHAN;
+	ioc->handle	= cpu_to_le32(TAG_ENCODE(idx));
+	ioc->data_addr	= cpu_to_le32(msg_data);
+
+	/* fill output data area with "no device" default values */
+	mem += IOC_SCAN_CHAN_OFFSET;
+	memset(mem, IOC_SCAN_CHAN_NODEV, CARM_MAX_PORTS);
+
+	return IOC_SCAN_CHAN_OFFSET + CARM_MAX_PORTS;
+}
+
+static unsigned int carm_fill_get_fw_ver(struct carm_host *host,
+					 unsigned int idx, void *mem)
+{
+	struct carm_msg_get_fw_ver *ioc = mem;
+	u32 msg_data = (u32) (carm_ref_msg_dma(host, idx) + sizeof(*ioc));
+
+	memset(ioc, 0, sizeof(*ioc));
+	ioc->type	= CARM_MSG_MISC;
+	ioc->subtype	= MISC_GET_FW_VER;
+	ioc->handle	= cpu_to_le32(TAG_ENCODE(idx));
+	ioc->data_addr	= cpu_to_le32(msg_data);
+
+	return sizeof(struct carm_msg_get_fw_ver) +
+	       sizeof(struct carm_fw_ver);
+}
+
+static void carm_activate_disk(struct carm_host *host,
+			       struct carm_port *port)
+{
+	int minor_start = port->port_no << CARM_PART_SHIFT;
+	int start, end, i;
+
+	host->gendisk_hd[minor_start].nr_sects = port->capacity;
+	host->blk_sizes[minor_start] = port->capacity;
+
+	start = minor_start;
+	end = minor_start + CARM_MINORS_PER_MAJOR;
+	for (i = start; i < end; i++) {
+		invalidate_device(MKDEV(host->major, i), 1);
+		host->gendisk.part[i].start_sect = 0;
+		host->gendisk.part[i].nr_sects = 0;
+		host->blk_block_sizes[i] = 512;
+		host->blk_sect_sizes[i] = 512;
+	}
+
+	grok_partitions(&host->gendisk, port->port_no, 
+			CARM_MINORS_PER_MAJOR,
+			port->capacity);
+}
+
+static int carm_revalidate_disk(kdev_t dev)
+{
+	struct carm_host *host;
+	struct carm_port *port = NULL;
+
+	host = carm_from_dev(dev, &port);
+	if (!host)
+		return -EINVAL;
+
+	carm_activate_disk(host, port);
+
+	return 0;
+}
+
+static inline void complete_buffers(struct buffer_head *bh, int status)
+{
+	struct buffer_head *xbh;
+	
+	while (bh) {
+		xbh = bh->b_reqnext; 
+		bh->b_reqnext = NULL; 
+		blk_finished_io(bh->b_size >> 9);
+		bh->b_end_io(bh, status);
+		bh = xbh;
+	}
+} 
+
+static inline void carm_end_request_queued(struct carm_host *host,
+					   struct carm_request *crq,
+					   int uptodate)
+{
+	struct request *req = crq->rq;
+	int rc;
+
+	complete_buffers(req->bh, uptodate);
+	end_that_request_last(req);
+
+	rc = carm_put_request(host, crq);
+	assert(rc == 0);
+}
+
+static inline void carm_push_q (struct carm_host *host, request_queue_t *q)
+{
+	unsigned int idx = host->wait_q_prod % CARM_MAX_WAIT_Q;
+
+	VPRINTK("STOPPED QUEUE %p\n", q);
+
+	host->wait_q[idx] = q;
+	host->wait_q_prod++;
+	BUG_ON(host->wait_q_prod == host->wait_q_cons); /* overrun */
+}
+
+static inline request_queue_t *carm_pop_q(struct carm_host *host)
+{
+	unsigned int idx;
+
+	if (host->wait_q_prod == host->wait_q_cons)
+		return NULL;
+
+	idx = host->wait_q_cons % CARM_MAX_WAIT_Q;
+	host->wait_q_cons++;
+
+	return host->wait_q[idx];
+}
+
+static inline void carm_round_robin(struct carm_host *host)
+{
+	request_queue_t *q = carm_pop_q(host);
+	if (q) {
+		struct tasklet_struct *tasklet;
+		if (q == &host->oob_q)
+			tasklet = &host->oob_tasklet;
+		else {
+			struct carm_port *port = q->queuedata;
+			tasklet = &port->tasklet;
+		}
+		tasklet_schedule(tasklet);
+		VPRINTK("STARTED QUEUE %p\n", q);
+	}
+}
+
+static inline void carm_end_rq(struct carm_host *host, struct carm_request *crq,
+			int is_ok)
+{
+	carm_end_request_queued(host, crq, is_ok);
+	if (CARM_MAX_Q == 1)
+		carm_round_robin(host);
+	else if ((host->n_msgs <= CARM_MSG_LOW_WATER) &&
+		 (host->hw_sg_used <= CARM_SG_LOW_WATER)) {
+		carm_round_robin(host);
+	}
+}
+
+static inline int carm_new_segment(request_queue_t *q, struct request *rq)
+{
+        if (rq->nr_segments < CARM_MAX_REQ_SG) {
+                rq->nr_segments++;
+                return 1;
+        }
+        return 0;
+}
+
+static int carm_back_merge_fn(request_queue_t *q, struct request *rq,
+                             struct buffer_head *bh, int max_segments)
+{
+	if (blk_seg_merge_ok(rq->bhtail, bh))	
+                return 1;
+        return carm_new_segment(q, rq);
+}
+
+static int carm_front_merge_fn(request_queue_t *q, struct request *rq,
+                             struct buffer_head *bh, int max_segments)
+{
+	if (blk_seg_merge_ok(bh, rq->bh))
+                return 1;
+        return carm_new_segment(q, rq);
+}
+
+static int carm_merge_requests_fn(request_queue_t *q, struct request *rq,
+                                 struct request *nxt, int max_segments)
+{
+        int total_segments = rq->nr_segments + nxt->nr_segments;
+
+	if (blk_seg_merge_ok(rq->bhtail, nxt->bh))
+                total_segments--;
+
+        if (total_segments > CARM_MAX_REQ_SG)
+                return 0;
+
+        rq->nr_segments = total_segments;
+        return 1;
+}
+
+static void carm_oob_rq_fn(request_queue_t *q)
+{
+	struct carm_host *host = q->queuedata;
+
+	tasklet_schedule(&host->oob_tasklet);
+}
+
+static void carm_rq_fn(request_queue_t *q)
+{
+	struct carm_port *port = q->queuedata;
+
+	tasklet_schedule(&port->tasklet);
+}
+
+static void carm_oob_tasklet(unsigned long _data)
+{
+	struct carm_host *host = (void *) _data;
+	request_queue_t *q = &host->oob_q;
+	struct carm_request *crq;
+	struct request *rq;
+	int rc, have_work = 1;
+	struct list_head *queue_head = &q->queue_head;
+	unsigned long flags;
+
+	spin_lock_irqsave(&io_request_lock, flags);
+	if (q->plugged || list_empty(queue_head))
+		have_work = 0;
+
+	if (!have_work)
+		goto out;
+
+	while (1) {
+		DPRINTK("get req\n");
+		if (list_empty(queue_head))
+			break;
+
+		rq = blkdev_entry_next_request(queue_head);
+
+		crq = rq->special;
+		assert(crq != NULL);
+		assert(crq->rq == rq);
+
+		crq->n_elem = 0;
+
+		DPRINTK("send req\n");
+		rc = carm_send_msg(host, crq);
+		if (rc) {
+			carm_push_q(host, q);
+			break;		/* call us again later, eventually */
+		} else
+			blkdev_dequeue_request(rq);
+	}
+
+out:
+	spin_unlock_irqrestore(&io_request_lock, flags);
+}
+
+static int blk_rq_map_sg(request_queue_t *q, struct request *rq,
+			 struct scatterlist *sg)
+{
+	int n_elem = 0;
+	struct buffer_head *bh = rq->bh;
+	u64 last_phys = ~0ULL;
+
+	while (bh) {
+		if (bh_phys(bh) == last_phys) {
+			sg[n_elem - 1].length += bh->b_size;
+			last_phys += bh->b_size;
+		} else {
+			if (unlikely(n_elem == CARM_MAX_REQ_SG))
+				BUG();
+			sg[n_elem].page = bh->b_page;
+			sg[n_elem].length = bh->b_size;
+			sg[n_elem].offset = bh_offset(bh);
+			last_phys = bh_phys(bh) + bh->b_size;
+			n_elem++;
+		}
+
+		bh = bh->b_reqnext;
+	}
+
+	return n_elem;
+}
+
+static void carm_rw_tasklet(unsigned long _data)
+{
+	struct carm_port *port = (void *) _data;
+	struct carm_host *host = port->host;
+	request_queue_t *q = &port->q;
+	struct carm_msg_rw *msg;
+	struct carm_request *crq;
+	struct request *rq;
+	struct scatterlist *sg;
+	int writing = 0, pci_dir, i, n_elem, rc, have_work = 1;
+	u32 tmp;
+	unsigned int msg_size;
+	unsigned long flags;
+	struct list_head *queue_head = &q->queue_head;
+	unsigned long start_sector;
+
+	spin_lock_irqsave(&io_request_lock, flags);
+	if (q->plugged || list_empty(queue_head))
+		have_work = 0;
+
+	if (!have_work)
+		goto out;
+
+queue_one_request:
+	VPRINTK("get req\n");
+	if (list_empty(queue_head))
+		goto out;
+
+	rq = blkdev_entry_next_request(queue_head);
+
+	crq = carm_get_request(host);
+	if (!crq) {
+		carm_push_q(host, q);
+		goto out;	/* call us again later, eventually */
+	}
+	crq->rq = rq;
+
+	if (rq_data_dir(rq) == WRITE) {
+		writing = 1;
+		pci_dir = PCI_DMA_TODEVICE;
+	} else {
+		pci_dir = PCI_DMA_FROMDEVICE;
+	}
+
+	/* get scatterlist from block layer */
+	sg = &crq->sg[0];
+	n_elem = blk_rq_map_sg(q, rq, sg);
+	if (n_elem <= 0) {
+		carm_end_rq(host, crq, 0);
+		goto out;	/* request with no s/g entries? */
+	}
+
+	/* map scatterlist to PCI bus addresses */
+	n_elem = pci_map_sg(host->pdev, sg, n_elem, pci_dir);
+	if (n_elem <= 0) {
+		carm_end_rq(host, crq, 0);
+		goto out;	/* request with no s/g entries? */
+	}
+	crq->n_elem = n_elem;
+	crq->port = port;
+	host->hw_sg_used += n_elem;
+
+	/*
+	 * build read/write message
+	 */
+
+	VPRINTK("build msg\n");
+	msg = (struct carm_msg_rw *) carm_ref_msg(host, crq->tag);
+
+	if (writing) {
+		msg->type = CARM_MSG_WRITE;
+		crq->msg_type = CARM_MSG_WRITE;
+	} else {
+		msg->type = CARM_MSG_READ;
+		crq->msg_type = CARM_MSG_READ;
+	}
+
+	start_sector = rq->sector;
+	start_sector += host->gendisk_hd[MINOR(rq->rq_dev)].start_sect;
+
+	msg->id		= port->port_no;
+	msg->sg_count	= n_elem;
+	msg->sg_type	= SGT_32BIT;
+	msg->handle	= cpu_to_le32(TAG_ENCODE(crq->tag));
+	msg->lba	= cpu_to_le32(start_sector & 0xffffffff);
+	tmp		= (start_sector >> 16) >> 16;
+	msg->lba_high	= cpu_to_le16( (u16) tmp );
+	msg->lba_count	= cpu_to_le16(rq->nr_sectors);
+
+	msg_size = sizeof(struct carm_msg_rw) - sizeof(msg->sg);
+	for (i = 0; i < n_elem; i++) {
+		struct carm_msg_sg *carm_sg = &msg->sg[i];
+		carm_sg->start = cpu_to_le32(sg_dma_address(&crq->sg[i]));
+		carm_sg->len = cpu_to_le32(sg_dma_len(&crq->sg[i]));
+		msg_size += sizeof(struct carm_msg_sg);
+	}
+
+	rc = carm_lookup_bucket(msg_size);
+	BUG_ON(rc < 0);
+	crq->msg_bucket = (u32) rc;
+
+	/*
+	 * queue read/write message to hardware
+	 */
+
+	VPRINTK("send msg, tag == %u\n", crq->tag);
+	rc = carm_send_msg(host, crq);
+	if (rc) {
+		carm_put_request(host, crq);
+		carm_push_q(host, q);
+		goto out;	/* call us again later, eventually */
+	} else
+		blkdev_dequeue_request(rq);
+
+	goto queue_one_request;
+
+out:
+	spin_unlock_irqrestore(&io_request_lock, flags);
+}
+
+static void carm_handle_array_info(struct carm_host *host,
+				   struct carm_request *crq, u8 *mem,
+				   int is_ok)
+{
+	struct carm_port *port;
+	u8 *msg_data = mem + sizeof(struct carm_array_info);
+	struct carm_array_info *desc = (struct carm_array_info *) msg_data;
+	u64 lo, hi;
+	int cur_port;
+	size_t slen;
+
+	DPRINTK("ENTER\n");
+
+	carm_end_rq(host, crq, is_ok);
+
+	if (!is_ok)
+		goto out;
+	if (le32_to_cpu(desc->array_status) & ARRAY_NO_EXIST)
+		goto out;
+
+	cur_port = host->cur_scan_dev;
+
+	/* should never occur */
+	if ((cur_port < 0) || (cur_port >= CARM_MAX_PORTS)) {
+		printk(KERN_ERR PFX "BUG: cur_scan_dev==%d, array_id==%d\n",
+		       cur_port, (int) desc->array_id);
+		goto out;
+	}
+
+	port = &host->port[cur_port];
+
+	lo = (u64) le32_to_cpu(desc->size);
+	hi = (u64) le32_to_cpu(desc->size_hi);
+
+	port->capacity = lo | (hi << 32);
+	port->dev_geom_head = le16_to_cpu(desc->head);
+	port->dev_geom_sect = le16_to_cpu(desc->sect);
+	port->dev_geom_cyl = le16_to_cpu(desc->cyl);
+
+	host->dev_active |= (1 << cur_port);
+
+	strncpy(port->name, desc->name, sizeof(port->name));
+	port->name[sizeof(port->name) - 1] = 0;
+	slen = strlen(port->name);
+	while (slen && (port->name[slen - 1] == ' ')) {
+		port->name[slen - 1] = 0;
+		slen--;
+	}
+
+	printk(KERN_INFO DRV_NAME "(%s): port %u device %Lu sectors\n",
+	       pci_name(host->pdev), port->port_no, port->capacity);
+	printk(KERN_INFO DRV_NAME "(%s): port %u device \"%s\"\n",
+	       pci_name(host->pdev), port->port_no, port->name);
+
+out:
+	assert(host->state == HST_DEV_SCAN);
+	schedule_task(&host->fsm_task);
+}
+
+static void carm_handle_scan_chan(struct carm_host *host,
+				  struct carm_request *crq, u8 *mem,
+				  int is_ok)
+{
+	u8 *msg_data = mem + IOC_SCAN_CHAN_OFFSET;
+	unsigned int i, dev_count = 0;
+	int new_state = HST_DEV_SCAN_START;
+
+	DPRINTK("ENTER\n");
+
+	carm_end_rq(host, crq, is_ok);
+
+	if (!is_ok) {
+		new_state = HST_ERROR;
+		goto out;
+	}
+
+	/* TODO: scan and support non-disk devices */
+	for (i = 0; i < 8; i++)
+		if (msg_data[i] == 0) { /* direct-access device (disk) */
+			host->dev_present |= (1 << i);
+			dev_count++;
+		}
+
+	printk(KERN_INFO DRV_NAME "(%s): found %u interesting devices\n",
+	       pci_name(host->pdev), dev_count);
+
+out:
+	assert(host->state == HST_PORT_SCAN);
+	host->state = new_state;
+	schedule_task(&host->fsm_task);
+}
+
+static void carm_handle_generic(struct carm_host *host,
+				struct carm_request *crq, int is_ok,
+				int cur_state, int next_state)
+{
+	DPRINTK("ENTER\n");
+
+	carm_end_rq(host, crq, is_ok);
+
+	assert(host->state == cur_state);
+	if (is_ok)
+		host->state = next_state;
+	else
+		host->state = HST_ERROR;
+	schedule_task(&host->fsm_task);
+}
+
+static inline void carm_handle_rw(struct carm_host *host,
+				  struct carm_request *crq, int is_ok)
+{
+	int pci_dir;
+
+	VPRINTK("ENTER\n");
+
+	if (rq_data_dir(crq->rq) == WRITE)
+		pci_dir = PCI_DMA_TODEVICE;
+	else
+		pci_dir = PCI_DMA_FROMDEVICE;
+
+	pci_unmap_sg(host->pdev, &crq->sg[0], crq->n_elem, pci_dir);
+
+	carm_end_rq(host, crq, is_ok);
+}
+
+static inline void carm_handle_resp(struct carm_host *host,
+				    u32 ret_handle_le, u32 status)
+{
+	u32 handle = le32_to_cpu(ret_handle_le);
+	unsigned int msg_idx;
+	struct carm_request *crq;
+	int is_ok = (status == RMSG_OK);
+	u8 *mem;
+
+	VPRINTK("ENTER, handle == 0x%x\n", handle);
+
+	if (unlikely(!TAG_VALID(handle))) {
+		printk(KERN_ERR DRV_NAME "(%s): BUG: invalid tag 0x%x\n",
+		       pci_name(host->pdev), handle);
+		return;
+	}
+
+	msg_idx = TAG_DECODE(handle);
+	VPRINTK("tag == %u\n", msg_idx);
+
+	crq = &host->req[msg_idx];
+
+	/* fast path */
+	if (likely(crq->msg_type == CARM_MSG_READ ||
+		   crq->msg_type == CARM_MSG_WRITE)) {
+		carm_handle_rw(host, crq, is_ok);
+		return;
+	}
+
+	mem = carm_ref_msg(host, msg_idx);
+
+	switch (crq->msg_type) {
+	case CARM_MSG_IOCTL: {
+		switch (crq->msg_subtype) {
+		case CARM_IOC_SCAN_CHAN:
+			carm_handle_scan_chan(host, crq, mem, is_ok);
+			break;
+		default:
+			/* unknown / invalid response */
+			goto err_out;
+		}
+		break;
+	}
+
+	case CARM_MSG_MISC: {
+		switch (crq->msg_subtype) {
+		case MISC_ALLOC_MEM:
+			carm_handle_generic(host, crq, is_ok,
+					    HST_ALLOC_BUF, HST_SYNC_TIME);
+			break;
+		case MISC_SET_TIME:
+			carm_handle_generic(host, crq, is_ok,
+					    HST_SYNC_TIME, HST_GET_FW_VER);
+			break;
+		case MISC_GET_FW_VER: {
+			struct carm_fw_ver *ver = (struct carm_fw_ver *)
+				mem + sizeof(struct carm_msg_get_fw_ver);
+			if (is_ok) {
+				host->fw_ver = le32_to_cpu(ver->version);
+				host->flags |= (ver->features & FL_FW_VER_MASK);
+			}
+			carm_handle_generic(host, crq, is_ok,
+					    HST_GET_FW_VER, HST_PORT_SCAN);
+			break;
+		}
+		default:
+			/* unknown / invalid response */
+			goto err_out;
+		}
+		break;
+	}
+
+	case CARM_MSG_ARRAY: {
+		switch (crq->msg_subtype) {
+		case CARM_ARRAY_INFO:
+			carm_handle_array_info(host, crq, mem, is_ok);
+			break;
+		default:
+			/* unknown / invalid response */
+			goto err_out;
+		}
+		break;
+	}
+
+	default:
+		/* unknown / invalid response */
+		goto err_out;
+	}
+
+	return;
+
+err_out:
+	printk(KERN_WARNING DRV_NAME "(%s): BUG: unhandled message type %d/%d\n",
+	       pci_name(host->pdev), crq->msg_type, crq->msg_subtype);
+	carm_end_rq(host, crq, 0);
+}
+
+static inline void carm_handle_responses(struct carm_host *host)
+{
+	void *mmio = host->mmio;
+	struct carm_response *resp = (struct carm_response *) host->shm;
+	unsigned int work = 0;
+	unsigned int idx = host->resp_idx % RMSG_Q_LEN;
+
+	while (1) {
+		u32 status = le32_to_cpu(resp[idx].status);
+
+		if (status == 0xffffffff) {
+			VPRINTK("ending response on index %u\n", idx);
+			writel(idx << 3, mmio + CARM_RESP_IDX);
+			break;
+		}
+
+		/* response to a message we sent */
+		else if ((status & (1 << 31)) == 0) {
+			VPRINTK("handling msg response on index %u\n", idx);
+			carm_handle_resp(host, resp[idx].ret_handle, status);
+			resp[idx].status = 0xffffffff;
+		}
+
+		/* asynchronous events the hardware throws our way */
+		else if ((status & 0xff000000) == (1 << 31)) {
+			u8 *evt_type_ptr = (u8 *) &resp[idx];
+			u8 evt_type = *evt_type_ptr;
+			printk(KERN_WARNING DRV_NAME "(%s): unhandled event type %d\n",
+			       pci_name(host->pdev), (int) evt_type);
+			resp[idx].status = 0xffffffff;
+		}
+
+		idx = NEXT_RESP(idx);
+		work++;
+	}
+
+	VPRINTK("EXIT, work==%u\n", work);
+	host->resp_idx += work;
+}
+
+static irqreturn_t carm_interrupt(int irq, void *__host, struct pt_regs *regs)
+{
+	struct carm_host *host = __host;
+	void *mmio;
+	u32 mask;
+	int handled = 0;
+	unsigned long flags;
+
+	if (!host) {
+		VPRINTK("no host\n");
+		return IRQ_NONE;
+	}
+
+	spin_lock_irqsave(&io_request_lock, flags);
+
+	mmio = host->mmio;
+
+	/* reading should also clear interrupts */
+	mask = readl(mmio + CARM_INT_STAT);
+
+	if (mask == 0 || mask == 0xffffffff) {
+		VPRINTK("no work, mask == 0x%x\n", mask);
+		goto out;
+	}
+
+	if (mask & INT_ACK_MASK)
+		writel(mask, mmio + CARM_INT_STAT);
+
+	if (unlikely(host->state == HST_INVALID)) {
+		VPRINTK("not initialized yet, mask = 0x%x\n", mask);
+		goto out;
+	}
+
+	if (mask & CARM_HAVE_RESP) {
+		handled = 1;
+		carm_handle_responses(host);
+	}
+
+out:
+	spin_unlock_irqrestore(&io_request_lock, flags);
+	VPRINTK("EXIT\n");
+	return IRQ_RETVAL(handled);
+}
+
+static void carm_fsm_task (void *_data)
+{
+	struct carm_host *host = _data;
+	unsigned long flags;
+	unsigned int state;
+	int rc, i, next_dev;
+	int reschedule = 0;
+	int new_state = HST_INVALID;
+
+	spin_lock_irqsave(&io_request_lock, flags);
+	state = host->state;
+	spin_unlock_irqrestore(&io_request_lock, flags);
+
+	DPRINTK("ENTER, state == %s\n", state_name[state]);
+
+	switch (state) {
+	case HST_PROBE_START:
+		new_state = HST_ALLOC_BUF;
+		reschedule = 1;
+		break;
+
+	case HST_ALLOC_BUF:
+		rc = carm_send_special(host, carm_fill_alloc_buf);
+		if (rc) {
+			new_state = HST_ERROR;
+			reschedule = 1;
+		}
+		break;
+
+	case HST_SYNC_TIME:
+		rc = carm_send_special(host, carm_fill_sync_time);
+		if (rc) {
+			new_state = HST_ERROR;
+			reschedule = 1;
+		}
+		break;
+
+	case HST_GET_FW_VER:
+		rc = carm_send_special(host, carm_fill_get_fw_ver);
+		if (rc) {
+			new_state = HST_ERROR;
+			reschedule = 1;
+		}
+		break;
+
+	case HST_PORT_SCAN:
+		rc = carm_send_special(host, carm_fill_scan_channels);
+		if (rc) {
+			new_state = HST_ERROR;
+			reschedule = 1;
+		}
+		break;
+
+	case HST_DEV_SCAN_START:
+		host->cur_scan_dev = -1;
+		new_state = HST_DEV_SCAN;
+		reschedule = 1;
+		break;
+
+	case HST_DEV_SCAN:
+		next_dev = -1;
+		for (i = host->cur_scan_dev + 1; i < CARM_MAX_PORTS; i++)
+			if (host->dev_present & (1 << i)) {
+				next_dev = i;
+				break;
+			}
+
+		if (next_dev >= 0) {
+			host->cur_scan_dev = next_dev;
+			rc = carm_array_info(host, next_dev);
+			if (rc) {
+				new_state = HST_ERROR;
+				reschedule = 1;
+			}
+		} else {
+			new_state = HST_DEV_ACTIVATE;
+			reschedule = 1;
+		}
+		break;
+
+	case HST_DEV_ACTIVATE: {
+		int activated = 0;
+		for (i = 0; i < CARM_MAX_PORTS; i++)
+			if (host->dev_active & (1 << i)) {
+				carm_activate_disk(host, &host->port[i]);
+				activated++;
+			}
+
+		printk(KERN_INFO DRV_NAME "(%s): %d ports activated\n",
+		       pci_name(host->pdev), activated);
+
+		new_state = HST_PROBE_FINISHED;
+		reschedule = 1;
+		break;
+	}
+
+	case HST_PROBE_FINISHED:
+		up(&host->probe_sem);
+		break;
+
+	case HST_ERROR:
+		/* FIXME: TODO */
+		break;
+
+	default:
+		/* should never occur */
+		printk(KERN_ERR PFX "BUG: unknown state %d\n", state);
+		assert(0);
+		break;
+	}
+
+	if (new_state != HST_INVALID) {
+		spin_lock_irqsave(&io_request_lock, flags);
+		host->state = new_state;
+		spin_unlock_irqrestore(&io_request_lock, flags);
+	}
+	if (reschedule)
+		schedule_task(&host->fsm_task);
+}
+
+static int carm_init_wait(void *mmio, u32 bits, unsigned int test_bit)
+{
+	unsigned int i;
+
+	for (i = 0; i < 50000; i++) {
+		u32 tmp = readl(mmio + CARM_LMUC);
+		udelay(100);
+
+		if (test_bit) {
+			if ((tmp & bits) == bits)
+				return 0;
+		} else {
+			if ((tmp & bits) == 0)
+				return 0;
+		}
+
+		cond_resched();
+	}
+
+	printk(KERN_ERR PFX "carm_init_wait timeout, bits == 0x%x, test_bit == %s\n",
+	       bits, test_bit ? "yes" : "no");
+	return -EBUSY;
+}
+
+static void carm_init_responses(struct carm_host *host)
+{
+	void *mmio = host->mmio;
+	unsigned int i;
+	struct carm_response *resp = (struct carm_response *) host->shm;
+
+	for (i = 0; i < RMSG_Q_LEN; i++)
+		resp[i].status = 0xffffffff;
+
+	writel(0, mmio + CARM_RESP_IDX);
+}
+
+static int carm_init_host(struct carm_host *host)
+{
+	void *mmio = host->mmio;
+	u32 tmp;
+	u8 tmp8;
+	int rc;
+	unsigned long flags;
+
+	DPRINTK("ENTER\n");
+
+	writel(0, mmio + CARM_INT_MASK);
+
+	tmp8 = readb(mmio + CARM_INITC);
+	if (tmp8 & 0x01) {
+		tmp8 &= ~0x01;
+		writeb(tmp8, CARM_INITC);
+		readb(mmio + CARM_INITC);	/* flush */
+
+		DPRINTK("snooze...\n");
+		msleep(5000);
+	}
+
+	tmp = readl(mmio + CARM_HMUC);
+	if (tmp & CARM_CME) {
+		DPRINTK("CME bit present, waiting\n");
+		rc = carm_init_wait(mmio, CARM_CME, 1);
+		if (rc) {
+			DPRINTK("EXIT, carm_init_wait 1 failed\n");
+			return rc;
+		}
+	}
+	if (tmp & CARM_RME) {
+		DPRINTK("RME bit present, waiting\n");
+		rc = carm_init_wait(mmio, CARM_RME, 1);
+		if (rc) {
+			DPRINTK("EXIT, carm_init_wait 2 failed\n");
+			return rc;
+		}
+	}
+
+	tmp &= ~(CARM_RME | CARM_CME);
+	writel(tmp, mmio + CARM_HMUC);
+	readl(mmio + CARM_HMUC);	/* flush */
+
+	rc = carm_init_wait(mmio, CARM_RME | CARM_CME, 0);
+	if (rc) {
+		DPRINTK("EXIT, carm_init_wait 3 failed\n");
+		return rc;
+	}
+
+	carm_init_buckets(mmio);
+
+	writel(host->shm_dma & 0xffffffff, mmio + RBUF_ADDR_LO);
+	writel((host->shm_dma >> 16) >> 16, mmio + RBUF_ADDR_HI);
+	writel(RBUF_LEN, mmio + RBUF_BYTE_SZ);
+
+	tmp = readl(mmio + CARM_HMUC);
+	tmp |= (CARM_RME | CARM_CME | CARM_WZBC);
+	writel(tmp, mmio + CARM_HMUC);
+	readl(mmio + CARM_HMUC);	/* flush */
+
+	rc = carm_init_wait(mmio, CARM_RME | CARM_CME, 1);
+	if (rc) {
+		DPRINTK("EXIT, carm_init_wait 4 failed\n");
+		return rc;
+	}
+
+	writel(0, mmio + CARM_HMPHA);
+	writel(INT_DEF_MASK, mmio + CARM_INT_MASK);
+
+	carm_init_responses(host);
+
+	/* start initialization, probing state machine */
+	spin_lock_irqsave(&io_request_lock, flags);
+	assert(host->state == HST_INVALID);
+	host->state = HST_PROBE_START;
+	spin_unlock_irqrestore(&io_request_lock, flags);
+
+	schedule_task(&host->fsm_task);
+
+	DPRINTK("EXIT\n");
+	return 0;
+}
+
+static void carm_init_ports (struct carm_host *host)
+{
+	struct carm_port *port;
+	request_queue_t *q;
+	unsigned int i;
+
+	for (i = 0; i < CARM_MAX_PORTS; i++) {
+		port = &host->port[i];
+		port->magic = CARM_MAGIC_PORT;
+		port->host = host;
+		port->port_no = i;
+		tasklet_init(&port->tasklet, carm_rw_tasklet,
+			     (unsigned long) port);
+
+		q = &port->q;
+
+		blk_init_queue(q, carm_rq_fn);
+		q->queuedata = port;
+		blk_queue_bounce_limit(q, host->pdev->dma_mask);
+		blk_queue_headactive(q, 0);
+
+		q->back_merge_fn = carm_back_merge_fn;
+		q->front_merge_fn = carm_front_merge_fn;
+		q->merge_requests_fn = carm_merge_requests_fn;
+	}
+}
+
+static request_queue_t *carm_find_queue(kdev_t device)
+{
+	struct carm_host *host;
+
+	host = blk_dev[MAJOR(device)].data;
+	if (!host)
+		return NULL;
+	if (host->magic != CARM_MAGIC_HOST)
+		return NULL;
+
+	DPRINTK("match: major %d, minor %d\n",
+		MAJOR(device), MINOR(device));
+	return &host->port[MINOR(device) >> CARM_PART_SHIFT].q;
+}
+
+static int carm_init_disks(struct carm_host *host)
+{
+	host->gendisk.major = host->major;
+	host->gendisk.major_name = host->name;
+	host->gendisk.minor_shift = CARM_PART_SHIFT;
+	host->gendisk.max_p = CARM_MINORS_PER_MAJOR;
+	host->gendisk.part = host->gendisk_hd;
+	host->gendisk.sizes = host->blk_sizes;
+	host->gendisk.nr_real = CARM_MAX_PORTS;
+	host->gendisk.fops = &carm_bd_ops;
+
+	blk_dev[host->major].queue = carm_find_queue;
+	blk_dev[host->major].data = host;
+	blk_size[host->major] = host->blk_sizes;
+	blksize_size[host->major] = host->blk_block_sizes;
+	hardsect_size[host->major] = host->blk_sect_sizes;
+
+	add_gendisk(&host->gendisk);
+
+	return 0;
+}
+
+static void carm_free_disks(struct carm_host *host)
+{
+	unsigned int i;
+
+	del_gendisk(&host->gendisk);
+
+	for (i = 0; i < CARM_MAX_PORTS; i++) {
+		struct carm_port *port = &host->port[i];
+
+		blk_cleanup_queue(&port->q);
+	}
+
+	blk_dev[host->major].queue = NULL;
+	blk_dev[host->major].data = NULL;
+	blk_size[host->major] = NULL;
+	blksize_size[host->major] = NULL;
+	hardsect_size[host->major] = NULL;
+}
+
+static void carm_stop_tasklets(struct carm_host *host)
+{
+	unsigned int i;
+
+	tasklet_kill(&host->oob_tasklet);
+
+	for (i = 0; i < CARM_MAX_PORTS; i++) {
+		struct carm_port *port = &host->port[i];
+		tasklet_kill(&port->tasklet);
+	}
+}
+
+static int carm_init_shm(struct carm_host *host)
+{
+	host->shm = pci_alloc_consistent(host->pdev, CARM_SHM_SIZE,
+					 &host->shm_dma);
+	if (!host->shm)
+		return -ENOMEM;
+
+	host->msg_base = host->shm + RBUF_LEN;
+	host->msg_dma = host->shm_dma + RBUF_LEN;
+
+	memset(host->shm, 0xff, RBUF_LEN);
+	memset(host->msg_base, 0, PDC_SHM_SIZE - RBUF_LEN);
+
+	return 0;
+}
+
+static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	static unsigned int printed_version;
+	struct carm_host *host;
+	unsigned int pci_dac;
+	int rc;
+	unsigned int i;
+
+	if (!printed_version++)
+		printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+
+	rc = pci_enable_device(pdev);
+	if (rc)
+		return rc;
+
+	rc = pci_request_regions(pdev, DRV_NAME);
+	if (rc)
+		goto err_out;
+
+#if IF_64BIT_DMA_IS_POSSIBLE /* grrrr... */
+	rc = pci_set_dma_mask(pdev, 0xffffffffffffffffULL);
+	if (!rc) {
+		rc = pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL);
+		if (rc) {
+			printk(KERN_ERR DRV_NAME "(%s): consistent DMA mask failure\n",
+				pci_name(pdev));
+			goto err_out_regions;
+		}
+		pci_dac = 1;
+	} else {
+#endif
+		rc = pci_set_dma_mask(pdev, 0xffffffffULL);
+		if (rc) {
+			printk(KERN_ERR DRV_NAME "(%s): DMA mask failure\n",
+				pci_name(pdev));
+			goto err_out_regions;
+		}
+		pci_dac = 0;
+#if IF_64BIT_DMA_IS_POSSIBLE /* grrrr... */
+	}
+#endif
+
+	host = kmalloc(sizeof(*host), GFP_KERNEL);
+	if (!host) {
+		printk(KERN_ERR DRV_NAME "(%s): memory alloc failure\n",
+		       pci_name(pdev));
+		rc = -ENOMEM;
+		goto err_out_regions;
+	}
+
+	memset(host, 0, sizeof(*host));
+	host->magic = CARM_MAGIC_HOST;
+	host->pdev = pdev;
+	host->flags = pci_dac ? FL_DAC : 0;
+	INIT_TQUEUE(&host->fsm_task, carm_fsm_task, host);
+	INIT_LIST_HEAD(&host->host_list_node);
+	init_MUTEX_LOCKED(&host->probe_sem);
+	tasklet_init(&host->oob_tasklet, carm_oob_tasklet,
+		     (unsigned long) host);
+	carm_init_ports(host);
+
+	for (i = 0; i < ARRAY_SIZE(host->req); i++)
+		host->req[i].tag = i;
+
+	host->mmio = ioremap(pci_resource_start(pdev, 0),
+			     pci_resource_len(pdev, 0));
+	if (!host->mmio) {
+		printk(KERN_ERR DRV_NAME "(%s): MMIO alloc failure\n",
+		       pci_name(pdev));
+		rc = -ENOMEM;
+		goto err_out_kfree;
+	}
+
+	rc = carm_init_shm(host);
+	if (rc) {
+		printk(KERN_ERR DRV_NAME "(%s): DMA SHM alloc failure\n",
+		       pci_name(pdev));
+		goto err_out_iounmap;
+	}
+
+	blk_init_queue(&host->oob_q, carm_oob_rq_fn);
+	host->oob_q.queuedata = host;
+	blk_queue_bounce_limit(&host->oob_q, pdev->dma_mask);
+	blk_queue_headactive(&host->oob_q, 0);
+
+	/*
+	 * Figure out which major to use: 160, 161, or dynamic
+	 */
+	if (!test_and_set_bit(0, &carm_major_alloc))
+		host->major = 160;
+	else if (!test_and_set_bit(1, &carm_major_alloc))
+		host->major = 161;
+	else
+		host->flags |= FL_DYN_MAJOR;
+
+	host->id = carm_host_id;
+	sprintf(host->name, DRV_NAME "%d", carm_host_id);
+
+	rc = register_blkdev(host->major, host->name, &carm_bd_ops);
+	if (rc < 0)
+		goto err_out_free_majors;
+	if (host->flags & FL_DYN_MAJOR)
+		host->major = rc;
+
+	rc = carm_init_disks(host);
+	if (rc)
+		goto err_out_blkdev_disks;
+
+	pci_set_master(pdev);
+
+	rc = request_irq(pdev->irq, carm_interrupt, SA_SHIRQ, DRV_NAME, host);
+	if (rc) {
+		printk(KERN_ERR DRV_NAME "(%s): irq alloc failure\n",
+		       pci_name(pdev));
+		goto err_out_blkdev_disks;
+	}
+
+	rc = carm_init_host(host);
+	if (rc)
+		goto err_out_free_irq;
+
+	DPRINTK("waiting for probe_sem\n");
+	down(&host->probe_sem);
+
+	printk(KERN_INFO "%s: pci %s, ports %d, io %lx, irq %u, major %d\n",
+	       host->name, pci_name(pdev), (int) CARM_MAX_PORTS,
+	       pci_resource_start(pdev, 0), pdev->irq, host->major);
+
+	carm_host_id++;
+	pci_set_drvdata(pdev, host);
+	return 0;
+
+err_out_free_irq:
+	free_irq(pdev->irq, host);
+err_out_blkdev_disks:
+	carm_free_disks(host);
+	unregister_blkdev(host->major, host->name);
+err_out_free_majors:
+	if (host->major == 160)
+		clear_bit(0, &carm_major_alloc);
+	else if (host->major == 161)
+		clear_bit(1, &carm_major_alloc);
+	blk_cleanup_queue(&host->oob_q);
+	pci_free_consistent(pdev, CARM_SHM_SIZE, host->shm, host->shm_dma);
+err_out_iounmap:
+	iounmap(host->mmio);
+err_out_kfree:
+	kfree(host);
+err_out_regions:
+	pci_release_regions(pdev);
+err_out:
+	pci_disable_device(pdev);
+	return rc;
+}
+
+static void carm_remove_one (struct pci_dev *pdev)
+{
+	struct carm_host *host = pci_get_drvdata(pdev);
+
+	if (!host) {
+		printk(KERN_ERR PFX "BUG: no host data for PCI(%s)\n",
+		       pci_name(pdev));
+		return;
+	}
+
+	free_irq(pdev->irq, host);
+	carm_stop_tasklets(host);
+	carm_free_disks(host);
+	unregister_blkdev(host->major, host->name);
+	if (host->major == 160)
+		clear_bit(0, &carm_major_alloc);
+	else if (host->major == 161)
+		clear_bit(1, &carm_major_alloc);
+	blk_cleanup_queue(&host->oob_q);
+	pci_free_consistent(pdev, CARM_SHM_SIZE, host->shm, host->shm_dma);
+	iounmap(host->mmio);
+	kfree(host);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+}
+
+static int __init carm_init(void)
+{
+	return pci_module_init(&carm_driver);
+}
+
+static void __exit carm_exit(void)
+{
+	pci_unregister_driver(&carm_driver);
+}
+
+module_init(carm_init);
+module_exit(carm_exit);
+
+
diff -urN linux-2.4.27-rc1/drivers/bluetooth/hci_bcsp.c linux-2.4.27-rc2/drivers/bluetooth/hci_bcsp.c
--- linux-2.4.27-rc1/drivers/bluetooth/hci_bcsp.c	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.27-rc2/drivers/bluetooth/hci_bcsp.c	2004-06-25 20:48:25.711054922 -0700
@@ -34,7 +34,6 @@
 #include <linux/module.h>
 
 #include <linux/version.h>
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/sched.h>
diff -urN linux-2.4.27-rc1/drivers/bluetooth/hci_ldisc.c linux-2.4.27-rc2/drivers/bluetooth/hci_ldisc.c
--- linux-2.4.27-rc1/drivers/bluetooth/hci_ldisc.c	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.27-rc2/drivers/bluetooth/hci_ldisc.c	2004-06-25 20:48:25.712054964 -0700
@@ -33,7 +33,6 @@
 #include <linux/module.h>
 
 #include <linux/version.h>
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/sched.h>
diff -urN linux-2.4.27-rc1/drivers/bluetooth/hci_usb.c linux-2.4.27-rc2/drivers/bluetooth/hci_usb.c
--- linux-2.4.27-rc1/drivers/bluetooth/hci_usb.c	2004-06-25 20:48:23.926980503 -0700
+++ linux-2.4.27-rc2/drivers/bluetooth/hci_usb.c	2004-06-25 20:48:25.712054964 -0700
@@ -30,7 +30,7 @@
  *
  * $Id: hci_usb.c,v 1.8 2002/07/18 17:23:09 maxk Exp $    
  */
-#define VERSION "2.5"
+#define VERSION "2.6"
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -100,6 +100,9 @@
 	/* Digianswer device */
 	{ USB_DEVICE(0x08fd, 0x0001), driver_info: HCI_DIGIANSWER },
 
+	/* RTX Telecom based adapter with buggy SCO support */
+	{ USB_DEVICE(0x0400, 0x0807), driver_info: HCI_BROKEN_ISOC },
+
 	{ }	/* Terminating entry */
 };
 
@@ -878,7 +881,7 @@
 	}
 
 #ifdef CONFIG_BLUEZ_HCIUSB_SCO
-	if (!isoc_in_ep[1] || !isoc_out_ep[1]) {
+	if (id->driver_info & HCI_BROKEN_ISOC || !isoc_in_ep[1] || !isoc_out_ep[1]) {
 		BT_DBG("Isoc endpoints not found");
 		isoc_iface = NULL;
 	}
diff -urN linux-2.4.27-rc1/drivers/bluetooth/hci_usb.h linux-2.4.27-rc2/drivers/bluetooth/hci_usb.h
--- linux-2.4.27-rc1/drivers/bluetooth/hci_usb.h	2004-04-14 06:05:29.000000000 -0700
+++ linux-2.4.27-rc2/drivers/bluetooth/hci_usb.h	2004-06-25 20:48:25.713055006 -0700
@@ -40,6 +40,7 @@
 #define HCI_IGNORE           0x01
 #define HCI_RESET            0x02
 #define HCI_DIGIANSWER       0x04
+#define HCI_BROKEN_ISOC      0x08
 
 #define HCI_MAX_IFACE_NUM	3 
 
diff -urN linux-2.4.27-rc1/drivers/net/e1000/e1000.h linux-2.4.27-rc2/drivers/net/e1000/e1000.h
--- linux-2.4.27-rc1/drivers/net/e1000/e1000.h	2004-06-25 20:48:24.047985551 -0700
+++ linux-2.4.27-rc2/drivers/net/e1000/e1000.h	2004-06-25 20:48:26.090070732 -0700
@@ -197,6 +197,7 @@
 	uint32_t part_num;
 	uint32_t wol;
 	uint32_t smartspeed;
+	uint32_t en_mng_pt;
 	uint16_t link_speed;
 	uint16_t link_duplex;
 	spinlock_t stats_lock;
diff -urN linux-2.4.27-rc1/drivers/net/e1000/e1000_hw.c linux-2.4.27-rc2/drivers/net/e1000/e1000_hw.c
--- linux-2.4.27-rc1/drivers/net/e1000/e1000_hw.c	2004-06-25 20:48:24.057985968 -0700
+++ linux-2.4.27-rc2/drivers/net/e1000/e1000_hw.c	2004-06-25 20:48:26.100071150 -0700
@@ -265,6 +265,17 @@
         return -E1000_ERR_MAC_TYPE;
     }
 
+    switch(hw->mac_type) {
+    case e1000_82541:
+    case e1000_82547:
+    case e1000_82541_rev_2:
+    case e1000_82547_rev_2:
+        hw->asf_firmware_present = TRUE;
+        break;
+    default:
+        break;
+    }
+
     return E1000_SUCCESS;
 }
 
@@ -5189,3 +5200,27 @@
     return E1000_SUCCESS;
 }
 
+/******************************************************************************
+ * Verifies the hardware needs to allow ARPs to be processed by the host
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * returns: - TRUE/FALSE
+ *
+ *****************************************************************************/
+uint32_t
+e1000_enable_mng_pass_thru(struct e1000_hw *hw)
+{
+    uint32_t manc;
+
+    if (hw->asf_firmware_present) {
+        manc = E1000_READ_REG(hw, MANC);
+
+        if (!(manc & E1000_MANC_RCV_TCO_EN) ||
+            !(manc & E1000_MANC_EN_MAC_ADDR_FILTER))
+            return FALSE;
+        if ((manc & E1000_MANC_SMBUS_EN) && !(manc & E1000_MANC_ASF_EN))
+            return TRUE;
+    }
+    return FALSE;
+}
diff -urN linux-2.4.27-rc1/drivers/net/e1000/e1000_hw.h linux-2.4.27-rc2/drivers/net/e1000/e1000_hw.h
--- linux-2.4.27-rc1/drivers/net/e1000/e1000_hw.h	2004-06-25 20:48:24.059986051 -0700
+++ linux-2.4.27-rc2/drivers/net/e1000/e1000_hw.h	2004-06-25 20:48:26.102071233 -0700
@@ -307,6 +307,7 @@
 /* Adaptive IFS Functions */
 
 /* Everything else */
+uint32_t e1000_enable_mng_pass_thru(struct e1000_hw *hw);
 void e1000_clear_hw_cntrs(struct e1000_hw *hw);
 void e1000_reset_adaptive(struct e1000_hw *hw);
 void e1000_update_adaptive(struct e1000_hw *hw);
@@ -983,6 +984,7 @@
     e1000_ms_type master_slave;
     e1000_ms_type original_master_slave;
     e1000_ffe_config ffe_config_state;
+    uint32_t asf_firmware_present;
     unsigned long io_base;
     uint32_t phy_id;
     uint32_t phy_revision;
diff -urN linux-2.4.27-rc1/drivers/net/e1000/e1000_main.c linux-2.4.27-rc2/drivers/net/e1000/e1000_main.c
--- linux-2.4.27-rc1/drivers/net/e1000/e1000_main.c	2004-06-25 20:48:24.062986177 -0700
+++ linux-2.4.27-rc2/drivers/net/e1000/e1000_main.c	2004-06-25 20:48:26.105071358 -0700
@@ -52,7 +52,7 @@
 
 char e1000_driver_name[] = "e1000";
 char e1000_driver_string[] = "Intel(R) PRO/1000 Network Driver";
-char e1000_driver_version[] = "5.2.52-k1";
+char e1000_driver_version[] = "5.2.52-k3";
 char e1000_copyright[] = "Copyright (c) 1999-2004 Intel Corporation.";
 
 /* e1000_pci_tbl - PCI Device ID Table
@@ -299,7 +299,7 @@
 void
 e1000_reset(struct e1000_adapter *adapter)
 {
-	uint32_t pba;
+	uint32_t pba, manc;
 	/* Repartition Pba for greater than 9k mtu
 	 * To take effect CTRL.RST is required.
 	 */
@@ -341,6 +341,12 @@
 
 	e1000_reset_adaptive(&adapter->hw);
 	e1000_phy_get_info(&adapter->hw, &adapter->phy_info);
+
+	if(adapter->en_mng_pt) {
+		manc = E1000_READ_REG(&adapter->hw, MANC);
+		manc |= (E1000_MANC_ARP_EN | E1000_MANC_EN_MNG2HOST);
+		E1000_WRITE_REG(&adapter->hw, MANC, manc);
+	}
 }
 
 /**
@@ -483,6 +489,8 @@
 	if(pci_using_dac)
 		netdev->features |= NETIF_F_HIGHDMA;
 
+	adapter->en_mng_pt = e1000_enable_mng_pass_thru(&adapter->hw);
+
 	/* before reading the EEPROM, reset the controller to 
 	 * put the device in a known good starting state */
 	
@@ -2143,6 +2151,7 @@
 	if(work_done < work_to_do || !netif_running(netdev)) {
 		netif_rx_complete(netdev);
 		e1000_irq_enable(adapter);
+		return 0;
 	}
 
 	return (work_done >= work_to_do);
diff -urN linux-2.4.27-rc1/drivers/net/eql.c linux-2.4.27-rc2/drivers/net/eql.c
--- linux-2.4.27-rc1/drivers/net/eql.c	2001-09-30 12:26:06.000000000 -0700
+++ linux-2.4.27-rc2/drivers/net/eql.c	2004-06-25 20:48:26.107071442 -0700
@@ -409,6 +409,8 @@
 #endif  
 	master_dev = dev;		/* for "clarity" */
 	slave_dev  = __dev_get_by_name (srq.slave_name);
+	if (!slave_dev)
+		return -ENODEV;
 
 	if (master_dev != 0 && slave_dev != 0)
 	{
@@ -463,6 +465,8 @@
 #endif
 	master_dev = dev;		/* for "clarity" */
 	slave_dev  = __dev_get_by_name (srq.slave_name);
+	if (!slave_dev)
+		return -ENODEV;
 
 	if ( eql_is_slave (slave_dev) )	/* really is a slave */
 	{
@@ -491,6 +495,8 @@
 #endif
 	eql = (equalizer_t *) dev->priv;
 	slave_dev = __dev_get_by_name (sc.slave_name);
+	if (!slave_dev)
+		return -ENODEV;
 
 	if ( eql_is_slave (slave_dev) )
 	{
@@ -525,6 +531,8 @@
 
 	eql = (equalizer_t *) dev->priv;
 	slave_dev = __dev_get_by_name (sc.slave_name);
+	if (!slave_dev)
+		return -ENODEV;
 
 	if ( eql_is_slave (slave_dev) )
 	{
diff -urN linux-2.4.27-rc1/drivers/net/macsonic.c linux-2.4.27-rc2/drivers/net/macsonic.c
--- linux-2.4.27-rc1/drivers/net/macsonic.c	2003-08-25 04:44:42.000000000 -0700
+++ linux-2.4.27-rc2/drivers/net/macsonic.c	2004-06-25 20:48:26.134072568 -0700
@@ -199,6 +199,7 @@
 	if ((lp->rba = (char *)
 	     kmalloc(SONIC_NUM_RRS * SONIC_RBSIZE, GFP_KERNEL | GFP_DMA)) == NULL) {
 		printk(KERN_ERR "%s: couldn't allocate receive buffers\n", dev->name);
+		kfree(lp);
 		return -ENOMEM;
 	}
 
@@ -635,19 +636,14 @@
 }
 
 #ifdef MODULE
-static char namespace[16] = "";
 static struct net_device dev_macsonic;
 
 MODULE_PARM(sonic_debug, "i");
 MODULE_PARM_DESC(sonic_debug, "macsonic debug level (1-4)");
-MODULE_LICENSE("GPL");
-
-EXPORT_NO_SYMBOLS;
 
 int
 init_module(void)
 {
-        dev_macsonic.name = namespace;
         dev_macsonic.init = macsonic_probe;
 
         if (register_netdev(&dev_macsonic) != 0) {
diff -urN linux-2.4.27-rc1/drivers/net/wireless/airo.c linux-2.4.27-rc2/drivers/net/wireless/airo.c
--- linux-2.4.27-rc1/drivers/net/wireless/airo.c	2004-04-14 06:05:30.000000000 -0700
+++ linux-2.4.27-rc2/drivers/net/wireless/airo.c	2004-06-25 20:48:26.290079075 -0700
@@ -3666,19 +3666,22 @@
 			  size_t len,
 			  loff_t *offset )
 {
-	int i;
-	int pos;
+	loff_t pos = *offset;
 	struct proc_data *priv = (struct proc_data*)file->private_data;
 
-	if( !priv->rbuffer ) return -EINVAL;
+	if (!priv->rbuffer)
+		return -EINVAL;
 
-	pos = *offset;
-	for( i = 0; i+pos < priv->readlen && i < len; i++ ) {
-		if (put_user( priv->rbuffer[i+pos], buffer+i ))
-			return -EFAULT;
-	}
-	*offset += i;
-	return i;
+	if (pos < 0)
+		return -EINVAL;
+	if (pos >= priv->readlen)
+		return 0;
+	if (len > priv->readlen - pos)
+		len = priv->readlen - pos;
+	if (copy_to_user(buffer, priv->rbuffer + pos, len))
+		return -EFAULT;
+	*offset = pos + len;
+	return len;
 }
 
 /*
@@ -3690,24 +3693,24 @@
 			   size_t len,
 			   loff_t *offset )
 {
-	int i;
-	int pos;
+	loff_t pos = *offset;
 	struct proc_data *priv = (struct proc_data*)file->private_data;
 
-	if ( !priv->wbuffer ) {
+	if (!priv->wbuffer)
 		return -EINVAL;
-	}
-
-	pos = *offset;
 
-	for( i = 0; i + pos <  priv->maxwritelen &&
-		     i < len; i++ ) {
-		if (get_user( priv->wbuffer[i+pos], buffer + i ))
-			return -EFAULT;
-	}
-	if ( i+pos > priv->writelen ) priv->writelen = i+file->f_pos;
-	*offset += i;
-	return i;
+	if (pos < 0)
+		return -EINVAL;
+	if (pos >= priv->maxwritelen)
+		return 0;
+	if (len > priv->maxwritelen - pos)
+		len = priv->maxwritelen - pos;
+	if (copy_from_user(priv->wbuffer + pos, buffer, len))
+		return -EFAULT;
+	if (pos + len > priv->writelen)
+		priv->writelen = pos + len;
+	*offset = pos + len;
+	return len;
 }
 
 static int proc_status_open( struct inode *inode, struct file *file ) {
diff -urN linux-2.4.27-rc1/drivers/net/wireless/orinoco_pci.c linux-2.4.27-rc2/drivers/net/wireless/orinoco_pci.c
--- linux-2.4.27-rc1/drivers/net/wireless/orinoco_pci.c	2003-08-25 04:44:42.000000000 -0700
+++ linux-2.4.27-rc2/drivers/net/wireless/orinoco_pci.c	2004-06-25 20:48:26.291079117 -0700
@@ -360,6 +360,7 @@
 
 static struct pci_device_id orinoco_pci_pci_id_table[] __devinitdata = {
 	{0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID,},
+	{0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID,},
 	{0,},
 };
 
diff -urN linux-2.4.27-rc1/drivers/pcmcia/yenta.c linux-2.4.27-rc2/drivers/pcmcia/yenta.c
--- linux-2.4.27-rc1/drivers/pcmcia/yenta.c	2004-04-14 06:05:31.000000000 -0700
+++ linux-2.4.27-rc2/drivers/pcmcia/yenta.c	2004-06-25 20:48:26.351081620 -0700
@@ -35,6 +35,8 @@
 #define to_cycles(ns)	((ns)/120)
 #define to_ns(cycles)	((cycles)*120)
 
+static int override_bios;
+
 /*
  * Generate easy-to-use ways of reading a cardbus sockets
  * regular memory space ("cb_xxx"), configuration space
@@ -753,7 +755,7 @@
 
 	start = config_readl(socket, offset) & mask;
 	end = config_readl(socket, offset+4) | ~mask;
-	if (start && end > start) {
+	if (start && end > start && !override_bios) {
 		res->start = start;
 		res->end = end;
 		if (request_resource(root, res) == 0)
@@ -950,6 +952,9 @@
 
 	return 0;
 }
+  
+MODULE_PARM (override_bios, "i");
+MODULE_PARM_DESC (override_bios, "yenta ignore bios resource allocation");
 
 /*
  * Standard plain cardbus - no frills, no extensions
diff -urN linux-2.4.27-rc1/drivers/sound/mpu401.c linux-2.4.27-rc2/drivers/sound/mpu401.c
--- linux-2.4.27-rc1/drivers/sound/mpu401.c	2003-06-13 07:51:36.000000000 -0700
+++ linux-2.4.27-rc2/drivers/sound/mpu401.c	2004-06-25 20:48:26.774099265 -0700
@@ -1513,14 +1513,16 @@
 static int mpu_timer_ioctl(int dev, unsigned int command, caddr_t arg)
 {
 	int midi_dev = sound_timer_devs[dev]->devlink;
+	int *p = (int *)arg;
 
 	switch (command)
 	{
 		case SNDCTL_TMR_SOURCE:
 			{
 				int parm;
-	
-				parm = *(int *) arg;
+
+				if (get_user(parm, p))
+					return -EFAULT;
 				parm &= timer_caps;
 
 				if (parm != 0)
@@ -1532,7 +1534,9 @@
 					else if (timer_mode & TMR_MODE_SMPTE)
 						mpu_cmd(midi_dev, 0x3d, 0);		/* Use SMPTE sync */
 				}
-				return (*(int *) arg = timer_mode);
+				if (put_user(timer_mode, p))
+					return -EFAULT;
+				return timer_mode;
 			}
 			break;
 
@@ -1557,10 +1561,13 @@
 			{
 				int val;
 
-				val = *(int *) arg;
+				if (get_user(val, p))
+					return -EFAULT;
 				if (val)
 					set_timebase(midi_dev, val);
-				return (*(int *) arg = curr_timebase);
+				if (put_user(curr_timebase, p))
+					return -EFAULT;
+				return curr_timebase;
 			}
 			break;
 
@@ -1569,7 +1576,8 @@
 				int val;
 				int ret;
 
-				val = *(int *) arg;
+				if (get_user(val, p))
+					return -EFAULT;
 
 				if (val)
 				{
@@ -1584,7 +1592,9 @@
 					}
 					curr_tempo = val;
 				}
-				return (*(int *) arg = curr_tempo);
+				if (put_user(curr_tempo, p))
+					return -EFAULT;
+				return curr_tempo;
 			}
 			break;
 
@@ -1592,18 +1602,25 @@
 			{
 				int val;
 
-				val = *(int *) arg;
+				if (get_user(val, p))
+					return -EFAULT;
 				if (val != 0)		/* Can't change */
 					return -EINVAL;
-				return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60);
+				val = (curr_tempo * curr_timebase + 30) / 60;
+				if (put_user(val, p))
+					return -EFAULT;
+				return val;
 			}
 			break;
 
 		case SNDCTL_SEQ_GETTIME:
-			return (*(int *) arg = curr_ticks);
+			if (put_user(curr_ticks, p))
+				return -EFAULT;
+			return curr_ticks;
 
 		case SNDCTL_TMR_METRONOME:
-			metronome_mode = *(int *) arg;
+			if (get_user(metronome_mode, p))
+				return -EFAULT;
 			setup_metronome(midi_dev);
 			return 0;
 
diff -urN linux-2.4.27-rc1/drivers/sound/msnd.c linux-2.4.27-rc2/drivers/sound/msnd.c
--- linux-2.4.27-rc1/drivers/sound/msnd.c	2001-09-30 12:26:08.000000000 -0700
+++ linux-2.4.27-rc2/drivers/sound/msnd.c	2004-06-25 20:48:26.785099724 -0700
@@ -155,13 +155,10 @@
 	f->len = f->tail = f->head = 0;
 }
 
-int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len, int user)
+int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len)
 {
 	int count = 0;
 
-	if (f->len == f->n)
-		return 0;
-
 	while ((count < len) && (f->len != f->n)) {
 
 		int nwritten;
@@ -177,11 +174,7 @@
 				nwritten = len - count;
 		}
 
-		if (user) {
-			if (copy_from_user(f->data + f->tail, buf, nwritten))
-				return -EFAULT;
-		} else
-			isa_memcpy_fromio(f->data + f->tail, (unsigned long) buf, nwritten);
+		isa_memcpy_fromio(f->data + f->tail, (unsigned long) buf, nwritten);
 
 		count += nwritten;
 		buf += nwritten;
@@ -193,13 +186,10 @@
 	return count;
 }
 
-int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len, int user)
+int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len)
 {
 	int count = 0;
 
-	if (f->len == 0)
-		return f->len;
-
 	while ((count < len) && (f->len > 0)) {
 
 		int nread;
@@ -215,11 +205,7 @@
 				nread = len - count;
 		}
 
-		if (user) {
-			if (copy_to_user(buf, f->data + f->head, nread))
-				return -EFAULT;
-		} else
-			isa_memcpy_toio((unsigned long) buf, f->data + f->head, nread);
+		isa_memcpy_toio((unsigned long) buf, f->data + f->head, nread);
 
 		count += nread;
 		buf += nread;
diff -urN linux-2.4.27-rc1/drivers/sound/msnd.h linux-2.4.27-rc2/drivers/sound/msnd.h
--- linux-2.4.27-rc1/drivers/sound/msnd.h	2000-07-12 21:58:43.000000000 -0700
+++ linux-2.4.27-rc2/drivers/sound/msnd.h	2004-06-25 20:48:26.808100684 -0700
@@ -266,8 +266,8 @@
 void				msnd_fifo_free(msnd_fifo *f);
 int				msnd_fifo_alloc(msnd_fifo *f, size_t n);
 void				msnd_fifo_make_empty(msnd_fifo *f);
-int				msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len, int user);
-int				msnd_fifo_read(msnd_fifo *f, char *buf, size_t len, int user);
+int				msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len);
+int				msnd_fifo_read(msnd_fifo *f, char *buf, size_t len);
 
 int				msnd_wait_TXDE(multisound_dev_t *dev);
 int				msnd_wait_HC0(multisound_dev_t *dev);
diff -urN linux-2.4.27-rc1/drivers/sound/msnd_pinnacle.c linux-2.4.27-rc2/drivers/sound/msnd_pinnacle.c
--- linux-2.4.27-rc1/drivers/sound/msnd_pinnacle.c	2002-08-02 17:39:44.000000000 -0700
+++ linux-2.4.27-rc2/drivers/sound/msnd_pinnacle.c	2004-06-25 20:48:26.811100809 -0700
@@ -804,7 +804,7 @@
 
 static __inline__ int pack_DARQ_to_DARF(register int bank)
 {
-	register int size, n, timeout = 3;
+	register int size, timeout = 3;
 	register WORD wTmp;
 	LPDAQD DAQD;
 
@@ -825,13 +825,10 @@
 	/* Read data from the head (unprotected bank 1 access okay
            since this is only called inside an interrupt) */
 	outb(HPBLKSEL_1, dev.io + HP_BLKS);
-	if ((n = msnd_fifo_write(
+	msnd_fifo_write(
 		&dev.DARF,
 		(char *)(dev.base + bank * DAR_BUFF_SIZE),
-		size, 0)) <= 0) {
-		outb(HPBLKSEL_0, dev.io + HP_BLKS);
-		return n;
-	}
+		size);
 	outb(HPBLKSEL_0, dev.io + HP_BLKS);
 
 	return 1;
@@ -853,21 +850,16 @@
 		if (protect) {
 			/* Critical section: protect fifo in non-interrupt */
 			spin_lock_irqsave(&dev.lock, flags);
-			if ((n = msnd_fifo_read(
+			n = msnd_fifo_read(
 				&dev.DAPF,
 				(char *)(dev.base + bank_num * DAP_BUFF_SIZE),
-				DAP_BUFF_SIZE, 0)) < 0) {
-				spin_unlock_irqrestore(&dev.lock, flags);
-				return n;
-			}
+				DAP_BUFF_SIZE);
 			spin_unlock_irqrestore(&dev.lock, flags);
 		} else {
-			if ((n = msnd_fifo_read(
+			n = msnd_fifo_read(
 				&dev.DAPF,
 				(char *)(dev.base + bank_num * DAP_BUFF_SIZE),
-				DAP_BUFF_SIZE, 0)) < 0) {
-				return n;
-			}
+				DAP_BUFF_SIZE);
 		}
 		if (!n)
 			break;
@@ -894,30 +886,43 @@
 static int dsp_read(char *buf, size_t len)
 {
 	int count = len;
+	char *page = (char *)__get_free_page(PAGE_SIZE);
+
+	if (!page)
+		return -ENOMEM;
 
 	while (count > 0) {
-		int n;
+		int n, k;
 		unsigned long flags;
 
+		k = PAGE_SIZE;
+		if (k > count)
+			k = count;
+
 		/* Critical section: protect fifo in non-interrupt */
 		spin_lock_irqsave(&dev.lock, flags);
-		if ((n = msnd_fifo_read(&dev.DARF, buf, count, 1)) < 0) {
-			printk(KERN_WARNING LOGNAME ": FIFO read error\n");
-			spin_unlock_irqrestore(&dev.lock, flags);
-			return n;
-		}
+		n = msnd_fifo_read(&dev.DARF, page, k);
 		spin_unlock_irqrestore(&dev.lock, flags);
+		if (copy_to_user(buf, page, n)) {
+			free_page((unsigned long)page);
+			return -EFAULT;
+		}
 		buf += n;
 		count -= n;
 
+		if (n == k && count)
+			continue;
+
 		if (!test_bit(F_READING, &dev.flags) && dev.mode & FMODE_READ) {
 			dev.last_recbank = -1;
 			if (chk_send_dsp_cmd(&dev, HDEX_RECORD_START) == 0)
 				set_bit(F_READING, &dev.flags);
 		}
 
-		if (dev.rec_ndelay)
+		if (dev.rec_ndelay) {
+			free_page((unsigned long)page);
 			return count == len ? -EAGAIN : len - count;
+		}
 
 		if (count > 0) {
 			set_bit(F_READBLOCK, &dev.flags);
@@ -926,41 +931,57 @@
 				get_rec_delay_jiffies(DAR_BUFF_SIZE)))
 				clear_bit(F_READING, &dev.flags);
 			clear_bit(F_READBLOCK, &dev.flags);
-			if (signal_pending(current))
+			if (signal_pending(current)) {
+				free_page((unsigned long)page);
 				return -EINTR;
+			}
 		}
 	}
-
+	free_page((unsigned long)page);
 	return len - count;
 }
 
 static int dsp_write(const char *buf, size_t len)
 {
 	int count = len;
+	char *page = (char *)__get_free_page(GFP_KERNEL);
+
+	if (!page)
+		return -ENOMEM;
 
 	while (count > 0) {
-		int n;
+		int n, k;
 		unsigned long flags;
 
+		k = PAGE_SIZE;
+		if (k > count)
+			k = count;
+
+		if (copy_from_user(page, buf, k)) {
+			free_page((unsigned long)page);
+			return -EFAULT;
+		}
+
 		/* Critical section: protect fifo in non-interrupt */
 		spin_lock_irqsave(&dev.lock, flags);
-		if ((n = msnd_fifo_write(&dev.DAPF, buf, count, 1)) < 0) {
-			printk(KERN_WARNING LOGNAME ": FIFO write error\n");
-			spin_unlock_irqrestore(&dev.lock, flags);
-			return n;
-		}
+		n = msnd_fifo_write(&dev.DAPF, page, k);
 		spin_unlock_irqrestore(&dev.lock, flags);
 		buf += n;
 		count -= n;
 
+		if (count && n == k)
+			continue;
+
 		if (!test_bit(F_WRITING, &dev.flags) && (dev.mode & FMODE_WRITE)) {
 			dev.last_playbank = -1;
 			if (pack_DAPF_to_DAPQ(1) > 0)
 				set_bit(F_WRITING, &dev.flags);
 		}
 
-		if (dev.play_ndelay)
+		if (dev.play_ndelay) {
+			free_page((unsigned long)page);
 			return count == len ? -EAGAIN : len - count;
+		}
 
 		if (count > 0) {
 			set_bit(F_WRITEBLOCK, &dev.flags);
@@ -968,11 +989,14 @@
 				&dev.writeblock,
 				get_play_delay_jiffies(DAP_BUFF_SIZE));
 			clear_bit(F_WRITEBLOCK, &dev.flags);
-			if (signal_pending(current))
+			if (signal_pending(current)) {
+				free_page((unsigned long)page);
 				return -EINTR;
+			}
 		}
 	}
 
+	free_page((unsigned long)page);
 	return len - count;
 }
 
diff -urN linux-2.4.27-rc1/drivers/sound/pss.c linux-2.4.27-rc2/drivers/sound/pss.c
--- linux-2.4.27-rc1/drivers/sound/pss.c	2002-11-28 15:53:14.000000000 -0800
+++ linux-2.4.27-rc2/drivers/sound/pss.c	2004-06-25 20:48:26.812100851 -0700
@@ -450,20 +450,36 @@
 	}
 }
 
-static void arg_to_volume_mono(unsigned int volume, int *aleft)
+static int set_volume_mono(caddr_t p, int *aleft)
 {
 	int left;
+	unsigned volume;
+	if (get_user(volume, (unsigned *)p))
+		return -EFAULT;
 	
-	left = volume & 0x00ff;
+	left = volume & 0xff;
 	if (left > 100)
 		left = 100;
 	*aleft = left;
+	return 0;
 }
 
-static void arg_to_volume_stereo(unsigned int volume, int *aleft, int *aright)
+static int set_volume_stereo(caddr_t p, int *aleft, int *aright)
 {
-	arg_to_volume_mono(volume, aleft);
-	arg_to_volume_mono(volume >> 8, aright);
+	int left, right;
+	unsigned volume;
+	if (get_user(volume, (unsigned *)p))
+		return -EFAULT;
+
+	left = volume & 0xff;
+	if (left > 100)
+		left = 100;
+	right = (volume >> 8) & 0xff;
+	if (right > 100)
+		right = 100;
+	*aleft = left;
+	*aright = right;
+	return 0;
 }
 
 static int ret_vol_mono(int left)
@@ -510,33 +526,38 @@
 					return call_ad_mixer(devc, cmd, arg);
 				else
 				{
-					if (*(int *)arg != 0)
+					int v;
+					if (get_user(v, (int *)arg))
+						return -EFAULT;
+					if (v != 0)
 						return -EINVAL;
 					return 0;
 				}
 			case SOUND_MIXER_VOLUME:
-				arg_to_volume_stereo(*(unsigned int *)arg, &devc->mixer.volume_l,
-					&devc->mixer.volume_r); 
+				if (set_volume_stereo(arg,
+					&devc->mixer.volume_l,
+					&devc->mixer.volume_r))
+					return -EFAULT;
 				set_master_volume(devc, devc->mixer.volume_l,
 					devc->mixer.volume_r);
 				return ret_vol_stereo(devc->mixer.volume_l,
 					devc->mixer.volume_r);
 		  
 			case SOUND_MIXER_BASS:
-				arg_to_volume_mono(*(unsigned int *)arg,
-					&devc->mixer.bass);
+				if (set_volume_mono(arg, &devc->mixer.bass))
+					return -EFAULT;
 				set_bass(devc, devc->mixer.bass);
 				return ret_vol_mono(devc->mixer.bass);
 		  
 			case SOUND_MIXER_TREBLE:
-				arg_to_volume_mono(*(unsigned int *)arg,
-					&devc->mixer.treble);
+				if (set_volume_mono(arg, &devc->mixer.treble))
+					return -EFAULT;
 				set_treble(devc, devc->mixer.treble);
 				return ret_vol_mono(devc->mixer.treble);
 		  
 			case SOUND_MIXER_SYNTH:
-				arg_to_volume_mono(*(unsigned int *)arg,
-					&devc->mixer.synth);
+				if (set_volume_mono(arg, &devc->mixer.synth))
+					return -EFAULT;
 				set_synth_volume(devc, devc->mixer.synth);
 				return ret_vol_mono(devc->mixer.synth);
 		  
@@ -546,54 +567,67 @@
 	}
 	else			
 	{
+		int val, and_mask = 0, or_mask = 0;
 		/*
 		 * Return parameters
 		 */
 		switch (cmdf)
 		{
-
 			case SOUND_MIXER_DEVMASK:
 				if (call_ad_mixer(devc, cmd, arg) == -EINVAL)
-					*(int *)arg = 0; /* no mixer devices */
-				return (*(int *)arg |= SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_SYNTH);
+					break;
+				and_mask = ~0;
+				or_mask = SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_SYNTH;
+				break;
 		  
 			case SOUND_MIXER_STEREODEVS:
 				if (call_ad_mixer(devc, cmd, arg) == -EINVAL)
-					*(int *)arg = 0; /* no stereo devices */
-				return (*(int *)arg |= SOUND_MASK_VOLUME);
+					break;
+				and_mask = ~0;
+				or_mask = SOUND_MASK_VOLUME;
+				break;
 		  
 			case SOUND_MIXER_RECMASK:
 				if (devc->ad_mixer_dev != NO_WSS_MIXER)
 					return call_ad_mixer(devc, cmd, arg);
-				else
-					return (*(int *)arg = 0); /* no record devices */
+				break;
 
 			case SOUND_MIXER_CAPS:
 				if (devc->ad_mixer_dev != NO_WSS_MIXER)
 					return call_ad_mixer(devc, cmd, arg);
-				else
-					return (*(int *)arg = SOUND_CAP_EXCL_INPUT);
+				or_mask = SOUND_CAP_EXCL_INPUT;
+				break;
 
 			case SOUND_MIXER_RECSRC:
 				if (devc->ad_mixer_dev != NO_WSS_MIXER)
 					return call_ad_mixer(devc, cmd, arg);
-				else
-					return (*(int *)arg = 0); /* no record source */
+				break;
 
 			case SOUND_MIXER_VOLUME:
-				return (*(int *)arg = ret_vol_stereo(devc->mixer.volume_l, devc->mixer.volume_r));
+				or_mask =  ret_vol_stereo(devc->mixer.volume_l, devc->mixer.volume_r);
+				break;
 			  
 			case SOUND_MIXER_BASS:
-				return (*(int *)arg = ret_vol_mono(devc->mixer.bass));
+				or_mask =  ret_vol_mono(devc->mixer.bass);
+				break;
 			  
 			case SOUND_MIXER_TREBLE:
-				return (*(int *)arg = ret_vol_mono(devc->mixer.treble));
+				or_mask = ret_vol_mono(devc->mixer.treble);
+				break;
 			  
 			case SOUND_MIXER_SYNTH:
-				return (*(int *)arg = ret_vol_mono(devc->mixer.synth));
+				or_mask = ret_vol_mono(devc->mixer.synth);
+				break;
 			default:
 				return -EINVAL;
 		}
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		val &= and_mask;
+		val |= or_mask;
+		if (put_user(val, (int *)arg))
+			return -EFAULT;
+		return val;
 	}
 }
 
diff -urN linux-2.4.27-rc1/drivers/usb/gadget/Config.in linux-2.4.27-rc2/drivers/usb/gadget/Config.in
--- linux-2.4.27-rc1/drivers/usb/gadget/Config.in	2004-04-14 06:05:32.000000000 -0700
+++ linux-2.4.27-rc2/drivers/usb/gadget/Config.in	2004-06-25 20:48:26.817101059 -0700
@@ -53,6 +53,9 @@
 
   dep_tristate '  Gadget Zero (DEVELOPMENT)' CONFIG_USB_ZERO $CONFIG_USB_GADGET_CONTROLLER
   dep_tristate '  Ethernet Gadget (EXPERIMENTAL)' CONFIG_USB_ETH $CONFIG_USB_GADGET_CONTROLLER $CONFIG_NET
+  if [ "$CONFIG_USB_ETH" = "y" -o "$CONFIG_USB_ETH" = "m" ] ; then
+    bool       '    RNDIS support (EXPERIMENTAL)' CONFIG_USB_ETH_RNDIS
+  fi
   dep_tristate '  File-backed Storage Gadget (DEVELOPMENT)' CONFIG_USB_FILE_STORAGE $CONFIG_USB_GADGET_CONTROLLER
   dep_mbool '    File-backed Storage Gadget test mode' CONFIG_USB_FILE_STORAGE_TEST $CONFIG_USB_FILE_STORAGE
   
diff -urN linux-2.4.27-rc1/drivers/usb/gadget/Makefile linux-2.4.27-rc2/drivers/usb/gadget/Makefile
--- linux-2.4.27-rc1/drivers/usb/gadget/Makefile	2004-04-14 06:05:32.000000000 -0700
+++ linux-2.4.27-rc2/drivers/usb/gadget/Makefile	2004-06-25 20:48:26.822101268 -0700
@@ -16,13 +16,17 @@
 controller-$(CONFIG_USB_GOKU)		+= goku_udc.o
 
 # ... and only one of these, too; kbuild/kconfig don't help though.
-g_zero-objs			:= zero.o usbstring.o config.o
+g_zero-objs			:= zero.o usbstring.o config.o epautoconf.o
 obj-$(CONFIG_USB_ZERO)		+= g_zero.o
 
-g_ether-objs			:= ether.o usbstring.o config.o
+g_ether-objs			:= ether.o usbstring.o config.o epautoconf.o
+ifeq ($(CONFIG_USB_ETH_RNDIS),y)
+	g_ether-objs            += rndis.o
+endif
 obj-$(CONFIG_USB_ETH)		+= g_ether.o
 
-g_file_storage-objs		:= file_storage.o usbstring.o
+g_file_storage-objs		:= file_storage.o usbstring.o config.o \
+						epautoconf.o
 obj-$(CONFIG_USB_FILE_STORAGE)	+= g_file_storage.o
 
 export-objs :=			$(controller-y) $(controller-m)
diff -urN linux-2.4.27-rc1/drivers/usb/gadget/epautoconf.c linux-2.4.27-rc2/drivers/usb/gadget/epautoconf.c
--- linux-2.4.27-rc1/drivers/usb/gadget/epautoconf.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.27-rc2/drivers/usb/gadget/epautoconf.c	2004-06-25 20:48:26.823101310 -0700
@@ -0,0 +1,308 @@
+/*
+ * epautoconf.c -- endpoint autoconfiguration for usb gadget drivers
+ *
+ * Copyright (C) 2004 David Brownell
+ *
+ * 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/init.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+
+#include <asm/byteorder.h>
+
+#include "gadget_chips.h"
+
+
+/* we must assign addresses for configurable endpoints (like net2280) */
+static __initdata unsigned epnum;
+
+// #define MANY_ENDPOINTS
+#ifdef MANY_ENDPOINTS
+/* more than 15 configurable endpoints */
+static __initdata unsigned in_epnum;
+#endif
+
+
+/*
+ * This should work with endpoints from controller drivers sharing the
+ * same endpoint naming convention.  By example:
+ *
+ *	- ep1, ep2, ... address is fixed, not direction or type
+ *	- ep1in, ep2out, ... address and direction are fixed, not type
+ *	- ep1-bulk, ep2-bulk, ... address and type are fixed, not direction
+ *	- ep1in-bulk, ep2out-iso, ... all three are fixed
+ *	- ep-* ... no functionality restrictions
+ *
+ * Type suffixes are "-bulk", "-iso", or "-int".  Numbers are decimal.
+ * Less common restrictions are implied by gadget_is_*().
+ */
+static int __init
+ep_matches (
+	struct usb_gadget		*gadget,
+	struct usb_ep			*ep,
+	struct usb_endpoint_descriptor	*desc
+)
+{
+	u8		type;
+	const char	*tmp;
+	u16		max;
+
+	/* endpoint already claimed? */
+	if (0 != ep->driver_data)
+		return 0;
+		
+	/* only support ep0 for portable CONTROL traffic */
+	type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+	if (USB_ENDPOINT_XFER_CONTROL == type)
+		return 0;
+
+	/* some other naming convention */
+	if ('e' != ep->name[0])
+		return 0;
+
+	/* type-restriction:  "-iso", "-bulk", or "-int".
+	 * direction-restriction:  "in", "out".
+	 */
+	if ('-' != ep->name[2]) {
+		tmp = strrchr (ep->name, '-');
+		if (tmp) {
+			switch (type) {
+			case USB_ENDPOINT_XFER_INT:
+				/* bulk endpoints handle interrupt transfers,
+				 * except the toggle-quirky iso-synch kind
+				 */
+				if ('s' == tmp[2])	// == "-iso"
+					return 0;
+				/* for now, avoid PXA "interrupt-in";
+				 * it's documented as never using DATA1.
+				 */
+				if (gadget_is_pxa (gadget)
+						&& 'i' == tmp [1])
+					return 0;
+				break;
+			case USB_ENDPOINT_XFER_BULK:
+				if ('b' != tmp[1])	// != "-bulk"
+					return 0;
+				break;
+			case USB_ENDPOINT_XFER_ISOC:
+				if ('s' != tmp[2])	// != "-iso"
+					return 0;
+			}
+		} else {
+			tmp = ep->name + strlen (ep->name);
+		}
+
+		/* direction-restriction:  "..in-..", "out-.." */
+		tmp--;
+		if (!isdigit (*tmp)) {
+			if (desc->bEndpointAddress & USB_DIR_IN) {
+				if ('n' != *tmp)
+					return 0;
+			} else {
+				if ('t' != *tmp)
+					return 0;
+			}
+		}
+	}
+
+	/* endpoint maxpacket size is an input parameter, except for bulk
+	 * where it's an output parameter representing the full speed limit.
+	 * the usb spec fixes high speed bulk maxpacket at 512 bytes.
+	 */
+	max = 0x7ff & le16_to_cpup (&desc->wMaxPacketSize);
+	switch (type) {
+	case USB_ENDPOINT_XFER_INT:
+		/* INT:  limit 64 bytes full speed, 1024 high speed */
+		if (!gadget->is_dualspeed && max > 64)
+			return 0;
+		/* FALLTHROUGH */
+
+	case USB_ENDPOINT_XFER_ISOC:
+		/* ISO:  limit 1023 bytes full speed, 1024 high speed */
+		if (ep->maxpacket < max)
+			return 0;
+		if (!gadget->is_dualspeed && max > 1023)
+			return 0;
+
+		/* BOTH:  "high bandwidth" works only at high speed */
+		if ((desc->wMaxPacketSize & __constant_cpu_to_le16(3<<11))) {
+			if (!gadget->is_dualspeed)
+				return 0;
+			/* configure your hardware with enough buffering!! */
+		}
+		break;
+	}
+
+	/* MATCH!! */
+
+	/* report address */
+	if (isdigit (ep->name [2])) {
+		u8	num = simple_strtol (&ep->name [2], NULL, 10);
+		desc->bEndpointAddress |= num;
+#ifdef	MANY_ENDPOINTS
+	} else if (desc->bEndpointAddress & USB_DIR_IN) {
+		if (++in_epnum > 15)
+			return 0;
+		desc->bEndpointAddress = USB_DIR_IN | in_epnum;
+#endif
+	} else {
+		if (++epnum > 15)
+			return 0;
+		desc->bEndpointAddress |= epnum;
+	}
+
+	/* report (variable) full speed bulk maxpacket */
+	if (USB_ENDPOINT_XFER_BULK == type) {
+		int size = ep->maxpacket;
+
+		/* min() doesn't work on bitfields with gcc-3.5 */
+		if (size > 64)
+			size = 64;
+		desc->wMaxPacketSize = cpu_to_le16(size);
+	}
+	return 1;
+}
+
+static struct usb_ep * __init
+find_ep (struct usb_gadget *gadget, const char *name)
+{
+	struct usb_ep	*ep;
+
+	list_for_each_entry (ep, &gadget->ep_list, ep_list) {
+		if (0 == strcmp (ep->name, name))
+			return ep;
+	}
+	return 0;
+}
+
+/**
+ * usb_ep_autoconfig - choose an endpoint matching the descriptor
+ * @gadget: The device to which the endpoint must belong.
+ * @desc: Endpoint descriptor, with endpoint direction and transfer mode
+ *	initialized.  For periodic transfers, the maximum packet
+ *	size must also be initialized.  This is modified on success.
+ *
+ * By choosing an endpoint to use with the specified descriptor, this
+ * routine simplifies writing gadget drivers that work with multiple
+ * USB device controllers.  The endpoint would be passed later to
+ * usb_ep_enable(), along with some descriptor.
+ *
+ * That second descriptor won't always be the same as the first one.
+ * For example, isochronous endpoints can be autoconfigured for high
+ * bandwidth, and then used in several lower bandwidth altsettings.
+ * Also, high and full speed descriptors will be different.
+ *
+ * Be sure to examine and test the results of autoconfiguration on your
+ * hardware.  This code may not make the best choices about how to use the
+ * USB controller, and it can't know all the restrictions that may apply.
+ * Some combinations of driver and hardware won't be able to autoconfigure.
+ *
+ * On success, this returns an un-claimed usb_ep, and modifies the endpoint
+ * descriptor bEndpointAddress.  For bulk endpoints, the wMaxPacket value
+ * is initialized as if the endpoint were used at full speed.  To prevent
+ * the endpoint from being returned by a later autoconfig call, claim it
+ * by assigning ep->driver_data to some non-null value.
+ *
+ * On failure, this returns a null endpoint descriptor.
+ */
+struct usb_ep * __init usb_ep_autoconfig (
+	struct usb_gadget		*gadget,
+	struct usb_endpoint_descriptor	*desc
+)
+{
+	struct usb_ep	*ep;
+	u8		type;
+
+	type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+	/* First, apply chip-specific "best usage" knowledge.
+	 * This might make a good usb_gadget_ops hook ...
+	 */
+	if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) {
+		/* ep-e, ep-f are PIO with only 64 byte fifos */
+		ep = find_ep (gadget, "ep-e");
+		if (ep && ep_matches (gadget, ep, desc))
+			return ep;
+		ep = find_ep (gadget, "ep-f");
+		if (ep && ep_matches (gadget, ep, desc))
+			return ep;
+
+	} else if (gadget_is_goku (gadget)) {
+		if (USB_ENDPOINT_XFER_INT == type) {
+			/* single buffering is enough */
+			ep = find_ep (gadget, "ep3-bulk");
+			if (ep && ep_matches (gadget, ep, desc))
+				return ep;
+		} else if (USB_ENDPOINT_XFER_BULK == type
+				&& (USB_DIR_IN & desc->bEndpointAddress)) {
+			/* DMA may be available */
+			ep = find_ep (gadget, "ep2-bulk");
+			if (ep && ep_matches (gadget, ep, desc))
+				return ep;
+		}
+
+	} else if (gadget_is_sh (gadget) && USB_ENDPOINT_XFER_INT == type) {
+		/* single buffering is enough; maybe 8 byte fifo is too */
+		ep = find_ep (gadget, "ep3in-bulk");
+		if (ep && ep_matches (gadget, ep, desc))
+			return ep;
+
+	} else if (gadget_is_mq11xx (gadget) && USB_ENDPOINT_XFER_INT == type) {
+		ep = find_ep (gadget, "ep1-bulk");
+		if (ep && ep_matches (gadget, ep, desc))
+			return ep;
+	}
+
+	/* Second, look at endpoints until an unclaimed one looks usable */ 
+	list_for_each_entry (ep, &gadget->ep_list, ep_list) {
+		if (ep_matches (gadget, ep, desc))
+			return ep;
+	}
+
+	/* Fail */
+	return 0;
+}
+
+/**
+ * usb_ep_autoconfig_reset - reset endpoint autoconfig state
+ * @gadget: device for which autoconfig state will be reset
+ *
+ * Use this for devices where one configuration may need to assign
+ * endpoint resources very differently from the next one.  It clears
+ * state such as ep->driver_data and the record of assigned endpoints
+ * used by usb_ep_autoconfig().
+ */
+void __init usb_ep_autoconfig_reset (struct usb_gadget *gadget)
+{
+	struct usb_ep	*ep;
+
+	list_for_each_entry (ep, &gadget->ep_list, ep_list) {
+		ep->driver_data = 0;
+	}
+#ifdef	MANY_ENDPOINTS
+	in_epnum = 0;
+#endif
+	epnum = 0;
+}
+
diff -urN linux-2.4.27-rc1/drivers/usb/gadget/ether.c linux-2.4.27-rc2/drivers/usb/gadget/ether.c
--- linux-2.4.27-rc1/drivers/usb/gadget/ether.c	2004-04-14 06:05:32.000000000 -0700
+++ linux-2.4.27-rc2/drivers/usb/gadget/ether.c	2004-06-25 20:48:26.861102895 -0700
@@ -1,7 +1,8 @@
 /*
  * ether.c -- Ethernet gadget driver, with CDC and non-CDC options
  *
- * Copyright (C) 2003 David Brownell
+ * Copyright (C) 2003-2004 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
  *
  * 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
@@ -23,8 +24,8 @@
 // #define VERBOSE
 
 #include <linux/config.h>
-#include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/kernel.h>
 #include <linux/delay.h>
 #include <linux/ioport.h>
 #include <linux/sched.h>
@@ -37,6 +38,8 @@
 #include <linux/interrupt.h>
 #include <linux/uts.h>
 #include <linux/version.h>
+#include <linux/moduleparam.h>
+#include <linux/ctype.h>
 
 #include <asm/byteorder.h>
 #include <asm/io.h>
@@ -53,6 +56,8 @@
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
 
+#include "gadget_chips.h"
+
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -60,39 +65,54 @@
  *
  * CDC Ethernet is the standard USB solution for sending Ethernet frames
  * using USB.  Real hardware tends to use the same framing protocol but look
- * different for control features.  And Microsoft pushes their own approach
- * (RNDIS) instead of the standard.
+ * different for control features.  This driver strongly prefers to use
+ * this USB-IF standard as its open-systems interoperability solution;
+ * most host side USB stacks (except from Microsoft) support it.
  *
  * There's some hardware that can't talk CDC.  We make that hardware
  * implement a "minimalist" vendor-agnostic CDC core:  same framing, but
  * link-level setup only requires activating the configuration.
+ * Linux supports it, but other host operating systems may not.
+ * (This is a subset of CDC Ethernet.)
+ *
+ * A third option is also in use.  Rather than CDC Ethernet, or something
+ * simpler, Microsoft pushes their own approach: RNDIS.  The published
+ * RNDIS specs are ambiguous and appear to be incomplete, and are also
+ * needlessly complex.
  */
 
 #define DRIVER_DESC		"Ethernet Gadget"
-#define DRIVER_VERSION		"Bastille Day 2003"
+#define DRIVER_VERSION		"St Patrick's Day 2004"
 
 static const char shortname [] = "ether";
 static const char driver_desc [] = DRIVER_DESC;
 
-#define MIN_PACKET	sizeof(struct ethhdr)
-#define	MAX_PACKET	ETH_DATA_LEN	/* biggest packet we'll rx/tx */
 #define RX_EXTRA	20		/* guard against rx overflows */
 
-/* FIXME allow high speed jumbograms */
-
-/*-------------------------------------------------------------------------*/
+#ifdef	CONFIG_USB_ETH_RNDIS
+#include "rndis.h"
+#else
+#define rndis_init() 0
+#define rndis_exit() do{}while(0)
+#endif
 
+/* 2.6-compat */
 #ifndef container_of
-#define container_of		list_entry
+#define container_of	list_entry
 #endif
 
-/* 2.5 modified and renamed these */
-#ifndef	INIT_WORK
-#define work_struct		tq_struct
-#define INIT_WORK		INIT_TQUEUE
+#include <linux/tqueue.h>
+#define	work_struct		tq_struct
+#define	INIT_WORK		INIT_TQUEUE
 #define schedule_work		schedule_task
 #define	flush_scheduled_work	flush_scheduled_tasks
-#endif
+
+static void random_ether_addr (u8 *addr)
+{
+	get_random_bytes (addr, ETH_ALEN);
+	addr [0] &= 0xfe;	// clear multicast bit
+	addr [0] |= 0x02;	// set local assignment bit (IEEE802)
+}
 
 /*-------------------------------------------------------------------------*/
 
@@ -112,219 +132,147 @@
 	atomic_t		tx_qlen;
 
 	struct work_struct	work;
+	unsigned		zlp:1;
+	unsigned		cdc:1;
+	unsigned		rndis:1;
+	unsigned		suspended:1;
+	u16			cdc_filter;
 	unsigned long		todo;
 #define	WORK_RX_MEMORY		0
+	int			rndis_config;
+	u8			host_mac [ETH_ALEN];
 };
 
+/* This version autoconfigures as much as possible at run-time.
+ *
+ * It also ASSUMES a self-powered device, without remote wakeup,
+ * although remote wakeup support would make sense.
+ */
+static const char *EP_IN_NAME;
+static const char *EP_OUT_NAME;
+static const char *EP_STATUS_NAME;
+
 /*-------------------------------------------------------------------------*/
 
-/* Thanks to NetChip Technologies for donating this product ID.
- *
- * DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
+/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
  * Instead:  allocate your own, using normal USB-IF procedures.
  */
-#define DRIVER_VENDOR_NUM	0x0525		/* NetChip */
-#define DRIVER_PRODUCT_NUM	0xa4a1		/* Linux-USB Ethernet Gadget */
 
-/*-------------------------------------------------------------------------*/
+/* Thanks to NetChip Technologies for donating this product ID.
+ * It's for devices with only CDC Ethernet configurations.
+ */
+#define CDC_VENDOR_NUM	0x0525		/* NetChip */
+#define CDC_PRODUCT_NUM	0xa4a1		/* Linux-USB Ethernet Gadget */
 
-/*
- * hardware-specific configuration, controlled by which device
- * controller driver was configured.
- *
- * CHIP ... hardware identifier
- * DRIVER_VERSION_NUM ... alerts the host side driver to differences
- * EP_*_NAME ... which endpoints do we use for which purpose?
- * EP_*_NUM ... numbers for them (often limited by hardware)
- * WAKEUP ... if hardware supports remote wakeup AND we will issue the
- * 	usb_gadget_wakeup() call to initiate it, USB_CONFIG_ATT_WAKEUP
- *
- * hw_optimize(gadget) ... for any hardware tweaks we want to kick in
- * 	before we enable our endpoints
+/* For hardware that can't talk CDC, we use the same vendor ID that
+ * ARM Linux has used for ethernet-over-usb, both with sa1100 and
+ * with pxa250.  We're protocol-compatible, if the host-side drivers
+ * use the endpoint descriptors.  bcdDevice (version) is nonzero, so
+ * drivers that need to hard-wire endpoint numbers have a hook.
  *
- * add other defines for other portability issues, like hardware that
- * for some reason doesn't handle full speed bulk maxpacket of 64.
+ * The protocol is a minimal subset of CDC Ether, which works on any bulk
+ * hardware that's not deeply broken ... even on hardware that can't talk
+ * RNDIS (like SA-1100, with no interrupt endpoint, or anything that
+ * doesn't handle control-OUT).
  */
+#define	SIMPLE_VENDOR_NUM	0x049f
+#define	SIMPLE_PRODUCT_NUM	0x505a
 
-#define DEV_CONFIG_VALUE	3	/* some hardware cares */
-
-/* #undef on hardware that can't implement CDC */
-#define	DEV_CONFIG_CDC
+/* For hardware that can talk RNDIS and either of the above protocols,
+ * use this ID ... the windows INF files will know it.  Unless it's
+ * used with CDC Ethernet, Linux 2.4 hosts will need updates to choose
+ * the non-RNDIS configuration.
+ */
+#define RNDIS_VENDOR_NUM	0x0525	/* NetChip */
+#define RNDIS_PRODUCT_NUM	0xa4a2	/* Ethernet/RNDIS Gadget */
 
-/* undef on bus-powered hardware, and #define MAX_USB_POWER */
-#define SELFPOWER
 
-/*
- * NetChip 2280, PCI based.
- *
- * use DMA with fat fifos for all data traffic, PIO for the status channel
- * where its 64 byte maxpacket ceiling is no issue.
- *
- * performance note:  only PIO needs per-usb-packet IRQs (ep0, ep-e, ep-f)
- * otherwise IRQs are per-Ethernet-packet unless TX_DELAY and chaining help.
+/* Some systems will want different product identifers published in the
+ * device descriptor, either numbers or strings or both.  These string
+ * parameters are in UTF-8 (superset of ASCII's 7 bit characters).
  */
-#ifdef	CONFIG_USB_GADGET_NET2280
-#define CHIP			"net2280"
-#define DEFAULT_QLEN		4		/* has dma chaining */
-#define DRIVER_VERSION_NUM	0x0111
-static const char EP_OUT_NAME [] = "ep-a";
-#define EP_OUT_NUM	1
-static const char EP_IN_NAME [] = "ep-b";
-#define EP_IN_NUM	2
-static const char EP_STATUS_NAME [] = "ep-f";
-#define EP_STATUS_NUM	3
-/* supports remote wakeup, but this driver doesn't */
 
-extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode);
+static ushort __initdata idVendor;
+module_param(idVendor, ushort, 0);
+MODULE_PARM_DESC(idVendor, "USB Vendor ID");
 
-static inline void hw_optimize (struct usb_gadget *gadget)
-{
-	/* we can have bigger ep-a/ep-b fifos (2KB each, 4 USB packets
-	 * for highspeed bulk) because we're not using ep-c/ep-d.
-	 */
-	net2280_set_fifo_mode (gadget, 1);
-}
-#endif
+static ushort __initdata idProduct;
+module_param(idProduct, ushort, 0);
+MODULE_PARM_DESC(idProduct, "USB Product ID");
 
-/*
- * PXA-2xx UDC:  widely used in second gen Linux-capable ARM PDAs
- * and other products.  The IXP-42x UDC is register-compatible.
- *
- * multiple interfaces (or altsettings) aren't usable.  so this hardware
- * can't implement CDC, which needs both capabilities.
- */
-#ifdef	CONFIG_USB_GADGET_PXA2XX
-#undef	DEV_CONFIG_CDC
-#define CHIP			"pxa2xx"
-#define DRIVER_VERSION_NUM	0x0113
-static const char EP_OUT_NAME [] = "ep2out-bulk";
-#define EP_OUT_NUM	2
-static const char EP_IN_NAME [] = "ep1in-bulk";
-#define EP_IN_NUM	1
-/* supports remote wakeup, but this driver doesn't */
+static ushort __initdata bcdDevice;
+module_param(bcdDevice, ushort, 0);
+MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
 
-/* no hw optimizations to apply */
-#define hw_optimize(g) do {} while (0)
-#endif
+static char *__initdata iManufacturer;
+MODULE_PARM(iManufacturer, "s");
+MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
 
-/*
- * SA-1100 UDC:  widely used in first gen Linux-capable PDAs.
- *
- * can't have a notification endpoint, since there are only the two
- * bulk-capable ones.  the CDC spec allows that.
- */
-#ifdef	CONFIG_USB_GADGET_SA1100
-#define CHIP			"sa1100"
-#define DRIVER_VERSION_NUM	0x0115
-static const char EP_OUT_NAME [] = "ep1out-bulk";
-#define EP_OUT_NUM	1
-static const char EP_IN_NAME [] = "ep2in-bulk";
-#define EP_IN_NUM	2
-// EP_STATUS_NUM is undefined
-/* doesn't support remote wakeup? */
+static char *__initdata iProduct;
+MODULE_PARM(iProduct, "s");
+MODULE_PARM_DESC(iProduct, "USB Product string");
 
-/* no hw optimizations to apply */
-#define hw_optimize(g) do {} while (0)
-#endif
+/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */
+static char *__initdata dev_addr;
+MODULE_PARM(dev_addr, "s");
+MODULE_PARM_DESC(dev_addr, "Device Ethernet Address");
 
-/*
- * Toshiba TC86C001 ("Goku-S") UDC
- *
- * This has three semi-configurable full speed bulk/interrupt endpoints.
- */
-#ifdef	CONFIG_USB_GADGET_GOKU
-#define CHIP			"goku"
-#define DRIVER_VERSION_NUM	0x0116
-static const char EP_OUT_NAME [] = "ep1-bulk";
-#define EP_OUT_NUM	1
-static const char EP_IN_NAME [] = "ep2-bulk";
-#define EP_IN_NUM	2
-static const char EP_STATUS_NAME [] = "ep3-bulk";
-#define EP_STATUS_NUM	3
-/* doesn't support remote wakeup */
+/* this address is invisible to ifconfig */
+static char *__initdata host_addr;
+MODULE_PARM(host_addr, "s");
+MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
+
+
+/*-------------------------------------------------------------------------*/
 
-#define hw_optimize(g) do {} while (0)
+/* Include CDC support if we could run on CDC-capable hardware. */
+
+#ifdef CONFIG_USB_GADGET_NET2280
+#define	DEV_CONFIG_CDC
 #endif
 
-/*
- * SuperH UDC:  UDC built-in to some Renesas SH processors.
- *
- * This has three semi-configurable full speed bulk/interrupt endpoints.
- *
- * Only one configuration and interface is supported.  So this hardware
- * can't implement CDC.
- */
-#ifdef	CONFIG_USB_GADGET_SUPERH
-#undef	DEV_CONFIG_CDC
-#define CHIP			"superh"
-#define DRIVER_VERSION_NUM	0x0117
-static const char EP_OUT_NAME[] = "ep1out-bulk";
-#define EP_OUT_NUM		1
-static const char EP_IN_NAME[] = "ep2in-bulk";
-#define EP_IN_NUM		2
+#ifdef CONFIG_USB_GADGET_DUMMY_HCD
+#define	DEV_CONFIG_CDC
+#endif
 
-#define hw_optimize(g) do {} while (0)
+#ifdef CONFIG_USB_GADGET_GOKU
+#define	DEV_CONFIG_CDC
 #endif
 
-/*-------------------------------------------------------------------------*/
+#ifdef CONFIG_USB_GADGET_MQ11XX
+#define	DEV_CONFIG_CDC
+#endif
 
-#ifndef CHIP
-#	error Configure some USB peripheral controller driver!
+#ifdef CONFIG_USB_GADGET_OMAP
+#define	DEV_CONFIG_CDC
 #endif
 
-/* We normally expect hardware that can talk CDC.  That involves
- * using multiple interfaces and altsettings, and maybe a status
- * interrupt.  Driver binding to be done according to USB-IF class,
- * though you can use different VENDOR and PRODUCT numbers if you
- * want (and they're officially assigned).
- * 
- * For hardware that can't talk CDC, we use the same vendor ID that
- * ARM Linux has used for ethernet-over-usb, both with sa1100 and
- * with pxa250.  We're protocol-compatible, if the host-side drivers
- * use the endpoint descriptors.  DRIVER_VERSION_NUM is nonzero, so
- * drivers that need to hard-wire endpoint numbers have a hook.
+
+/* For CDC-incapable hardware, choose the simple cdc subset.
+ * Anything that talks bulk (without notable bugs) can do this.
  */
-#ifdef	DEV_CONFIG_CDC
-#define	DEV_CONFIG_CLASS	USB_CLASS_COMM
-#else	
-#define	DEV_CONFIG_CLASS	USB_CLASS_VENDOR_SPEC
-#undef	EP_STATUS_NUM
-#undef	DRIVER_VENDOR_NUM
-#undef	DRIVER_PRODUCT_NUM
-#define	DRIVER_VENDOR_NUM	0x049f
-#define	DRIVER_PRODUCT_NUM	0x505a
-#endif /* CONFIG_CDC_ETHER */
-
-/* power usage is config specific.
- * hardware that supports remote wakeup defaults to disabling it.
- */
-
-#ifndef	MAX_USB_POWER
-#ifdef	SELFPOWER
-/* some hosts are confused by 0mA  */
-#define MAX_USB_POWER	2	/* mA */
-#else
-/* bus powered */
-#error	Define your bus power consumption!
+#ifdef CONFIG_USB_GADGET_PXA2XX
+#define	DEV_CONFIG_SUBSET
+#endif
+
+#ifdef CONFIG_USB_GADGET_SH
+#define	DEV_CONFIG_SUBSET
 #endif
-#endif	/* MAX_USB_POWER */
 
-#ifndef	WAKEUP
-/* default: this driver won't do remote wakeup */
-#define WAKEUP		0
-/* else value must be USB_CONFIG_ATT_WAKEUP */
+#ifdef CONFIG_USB_GADGET_SA1100
+/* use non-CDC for backwards compatibility */
+#define	DEV_CONFIG_SUBSET
 #endif
 
+
 /*-------------------------------------------------------------------------*/
 
-#ifndef DEFAULT_QLEN
 #define DEFAULT_QLEN	2	/* double buffering by default */
-#endif
 
 #ifdef CONFIG_USB_GADGET_DUALSPEED
 
 static unsigned qmult = 5;
-MODULE_PARM (qmult, "i");
+module_param (qmult, uint, 0);
 
 
 /* for dual-speed hardware, use deeper queues at highspeed */
@@ -332,10 +280,14 @@
 	(DEFAULT_QLEN*((gadget->speed == USB_SPEED_HIGH) ? qmult : 1))
 
 /* also defer IRQs on highspeed TX */
-#define TX_DELAY	DEFAULT_QLEN
+#define TX_DELAY	qmult
+
+#define	BITRATE(g) ((g->speed == USB_SPEED_HIGH) ? 4800000 : 120000)
 
 #else	/* full speed (low speed doesn't do bulk) */
 #define qlen(gadget) DEFAULT_QLEN
+
+#define	BITRATE(g)	(12000)
 #endif
 
 
@@ -376,8 +328,13 @@
 
 /*
  * DESCRIPTORS ... most are static, but strings and (full) configuration
- * descriptors are built on demand.  Notice how most of the cdc descriptors
- * aren't needed in the "minimalist" mode.
+ * descriptors are built on demand.  For now we do either full CDC, or
+ * our simple subset, with RNDIS as an optional second configuration.
+ *
+ * RNDIS includes some CDC ACM descriptors ... like CDC Ethernet.  But
+ * the class descriptors match a modem (they're ignored; it's really just
+ * Ethernet functionality), they don't need the NOP altsetting, and the
+ * status transfer endpoint isn't optional.
  */
 
 #define STRING_MANUFACTURER		1
@@ -385,12 +342,24 @@
 #define STRING_ETHADDR			3
 #define STRING_DATA			4
 #define STRING_CONTROL			5
+#define STRING_RNDIS_CONTROL		6
+#define STRING_CDC			7
+#define STRING_SUBSET			8
+#define STRING_RNDIS			9
 
 #define USB_BUFSIZ	256		/* holds our biggest descriptor */
 
 /*
- * This device advertises one configuration.
+ * This device advertises one configuration, eth_config, unless RNDIS
+ * is enabled (rndis_config) on hardware supporting at least two configs.
+ *
+ * NOTE:  Controllers like superh_udc should probably be able to use
+ * an RNDIS-only configuration.
  */
+
+#define DEV_CONFIG_VALUE	1	/* cdc or subset */
+#define DEV_RNDIS_CONFIG_VALUE	2	/* rndis; optional */
+
 static struct usb_device_descriptor
 device_desc = {
 	.bLength =		sizeof device_desc,
@@ -398,13 +367,12 @@
 
 	.bcdUSB =		__constant_cpu_to_le16 (0x0200),
 
-	.bDeviceClass =		DEV_CONFIG_CLASS,
+	.bDeviceClass =		USB_CLASS_COMM,
 	.bDeviceSubClass =	0,
 	.bDeviceProtocol =	0,
 
-	.idVendor =		__constant_cpu_to_le16 (DRIVER_VENDOR_NUM),
-	.idProduct =		__constant_cpu_to_le16 (DRIVER_PRODUCT_NUM),
-	.bcdDevice =		__constant_cpu_to_le16 (DRIVER_VERSION_NUM),
+	.idVendor =		__constant_cpu_to_le16 (CDC_VENDOR_NUM),
+	.idProduct =		__constant_cpu_to_le16 (CDC_PRODUCT_NUM),
 	.iManufacturer =	STRING_MANUFACTURER,
 	.iProduct =		STRING_PRODUCT,
 	.bNumConfigurations =	1,
@@ -416,44 +384,70 @@
 	.bDescriptorType =	USB_DT_CONFIG,
 
 	/* compute wTotalLength on the fly */
-#ifdef	DEV_CONFIG_CDC
 	.bNumInterfaces =	2,
-#else
-	.bNumInterfaces =	1,
-#endif
 	.bConfigurationValue =	DEV_CONFIG_VALUE,
-	.iConfiguration =	STRING_PRODUCT,
-	.bmAttributes =		USB_CONFIG_ATT_ONE | WAKEUP,
-	.bMaxPower =		(MAX_USB_POWER + 1) / 2,
+	.iConfiguration =	STRING_CDC,
+	.bmAttributes =		USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+	.bMaxPower =		1,
 };
 
-#ifdef	DEV_CONFIG_CDC
+#ifdef	CONFIG_USB_ETH_RNDIS
+static const struct usb_config_descriptor 
+rndis_config = {
+	.bLength =              sizeof rndis_config,
+	.bDescriptorType =      USB_DT_CONFIG,
+
+	/* compute wTotalLength on the fly */
+	.bNumInterfaces =       2,
+	.bConfigurationValue =  DEV_RNDIS_CONFIG_VALUE,
+	.iConfiguration =       STRING_RNDIS,
+	.bmAttributes =		USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+	.bMaxPower =            1,
+};
+#endif
 
 /*
- * Compared to the "minimalist" non-CDC model, the CDC model adds
- * three class descriptors, two interface descrioptors, and a status
+ * Compared to the simple CDC subset, the full CDC Ethernet model adds
+ * three class descriptors, two interface descriptors, optional status
  * endpoint.  Both have a "data" interface and two bulk endpoints.
  * There are also differences in how control requests are handled.
+ *
+ * RNDIS shares a lot with CDC-Ethernet, since it's a variant of
+ * the CDC-ACM (modem) spec.
  */
 
-/* master comm interface optionally has a status notification endpoint */
-
-static const struct usb_interface_descriptor
+#ifdef	DEV_CONFIG_CDC
+static struct usb_interface_descriptor
 control_intf = {
 	.bLength =		sizeof control_intf,
 	.bDescriptorType =	USB_DT_INTERFACE,
 
 	.bInterfaceNumber =	0,
-#ifdef	EP_STATUS_NUM
+	/* status endpoint is optional; this may be patched later */
 	.bNumEndpoints =	1,
-#else
-	.bNumEndpoints =	0,
-#endif
 	.bInterfaceClass =	USB_CLASS_COMM,
 	.bInterfaceSubClass =	6,	/* ethernet control model */
 	.bInterfaceProtocol =	0,
 	.iInterface =		STRING_CONTROL,
 };
+#endif
+
+#ifdef	CONFIG_USB_ETH_RNDIS
+static const struct usb_interface_descriptor
+rndis_control_intf = {
+	.bLength =              sizeof rndis_control_intf,
+	.bDescriptorType =      USB_DT_INTERFACE,
+	  
+	.bInterfaceNumber =     0,
+	.bNumEndpoints =        1,
+	.bInterfaceClass =      USB_CLASS_COMM,
+	.bInterfaceSubClass =   2,	/* abstract control model */
+	.bInterfaceProtocol =   0xff,	/* vendor specific */
+	.iInterface =           STRING_RNDIS_CONTROL,
+};
+#endif
+
+#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
 
 /* "Header Functional Descriptor" from CDC spec  5.2.3.1 */
 struct header_desc {
@@ -466,13 +460,13 @@
 
 static const struct header_desc header_desc = {
 	.bLength =		sizeof header_desc,
-	.bDescriptorType =	0x24,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	0,
 
 	.bcdCDC =		__constant_cpu_to_le16 (0x0110),
 };
 
-/* "Union Functional Descriptor" from CDC spec 5.2.3.X */
+/* "Union Functional Descriptor" from CDC spec 5.2.3.8 */
 struct union_desc {
 	u8	bLength;
 	u8	bDescriptorType;
@@ -485,13 +479,58 @@
 
 static const struct union_desc union_desc = {
 	.bLength =		sizeof union_desc,
-	.bDescriptorType =	0x24,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	6,
 
 	.bMasterInterface0 =	0,	/* index of control interface */
 	.bSlaveInterface0 =	1,	/* index of DATA interface */
 };
 
+#endif	/* CDC || RNDIS */
+
+#ifdef	CONFIG_USB_ETH_RNDIS
+
+/* "Call Management Descriptor" from CDC spec  5.2.3.3 */
+struct call_mgmt_descriptor {
+	u8  bLength;
+	u8  bDescriptorType;
+	u8  bDescriptorSubType;
+
+	u8  bmCapabilities;
+	u8  bDataInterface;
+} __attribute__ ((packed));
+
+static const struct call_mgmt_descriptor call_mgmt_descriptor = {
+	.bLength =  		sizeof call_mgmt_descriptor,
+	.bDescriptorType = 	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType = 	0x01,
+
+	.bmCapabilities = 	0x00,
+	.bDataInterface = 	0x01,
+};
+
+
+/* "Abstract Control Management Descriptor" from CDC spec  5.2.3.4 */
+struct acm_descriptor {
+	u8  bLength;
+	u8  bDescriptorType;
+	u8  bDescriptorSubType;
+
+	u8  bmCapabilities;
+} __attribute__ ((packed));
+
+static struct acm_descriptor acm_descriptor = {
+	.bLength =  		sizeof acm_descriptor,
+	.bDescriptorType = 	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType = 	0x02,
+
+	.bmCapabilities = 	0X00,
+};
+
+#endif
+
+#ifdef	DEV_CONFIG_CDC
+
 /* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */
 struct ether_desc {
 	u8	bLength;
@@ -507,41 +546,53 @@
 
 static const struct ether_desc ether_desc = {
 	.bLength =		sizeof ether_desc,
-	.bDescriptorType =	0x24,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	0x0f,
 
 	/* this descriptor actually adds value, surprise! */
 	.iMACAddress =		STRING_ETHADDR,
 	.bmEthernetStatistics =	__constant_cpu_to_le32 (0), /* no statistics */
-	.wMaxSegmentSize =	__constant_cpu_to_le16 (MAX_PACKET + ETH_HLEN),
+	.wMaxSegmentSize =	__constant_cpu_to_le16 (ETH_FRAME_LEN),
 	.wNumberMCFilters =	__constant_cpu_to_le16 (0),
 	.bNumberPowerFilters =	0,
 };
 
-#ifdef	EP_STATUS_NUM
+#endif
 
-/* include the status endpoint if we can, even though it's optional.
+#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+
+/* include the status endpoint if we can, even where it's optional.
+ * use small wMaxPacketSize, since many "interrupt" endpoints have
+ * very small fifos and it's no big deal if CDC_NOTIFY_SPEED_CHANGE
+ * takes two packets.  also default to a big transfer interval, to
+ * waste less bandwidth.
  *
- * some drivers (like current Linux cdc-ether!) "need" it to exist even
+ * some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even
  * if they ignore the connect/disconnect notifications that real aether
  * can provide.  more advanced cdc configurations might want to support
  * encapsulated commands (vendor-specific, using control-OUT).
+ *
+ * RNDIS requires the status endpoint, since it uses that encapsulation
+ * mechanism for its funky RPC scheme.
  */
  
-#define LOG2_STATUS_INTERVAL_MSEC	6
-#define STATUS_BYTECOUNT		16	/* 8 byte header + data */
-static const struct usb_endpoint_descriptor
+#define LOG2_STATUS_INTERVAL_MSEC	5	/* 1 << 5 == 32 msec */
+#define STATUS_BYTECOUNT		8	/* 8 byte header + data */
+
+static struct usb_endpoint_descriptor
 fs_status_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
-	.bEndpointAddress =	EP_STATUS_NUM | USB_DIR_IN,
+	.bEndpointAddress =	USB_DIR_IN,
 	.bmAttributes =		USB_ENDPOINT_XFER_INT,
 	.wMaxPacketSize =	__constant_cpu_to_le16 (STATUS_BYTECOUNT),
 	.bInterval =		1 << LOG2_STATUS_INTERVAL_MSEC,
 };
 #endif
 
+#ifdef	DEV_CONFIG_CDC
+
 /* the default data interface has no endpoints ... */
 
 static const struct usb_interface_descriptor
@@ -555,10 +606,9 @@
 	.bInterfaceClass =	USB_CLASS_CDC_DATA,
 	.bInterfaceSubClass =	0,
 	.bInterfaceProtocol =	0,
-	.iInterface =		STRING_DATA,
 };
 
-/* ... but the "real" data interface has two full speed bulk endpoints */
+/* ... but the "real" data interface has two bulk endpoints */
 
 static const struct usb_interface_descriptor
 data_intf = {
@@ -573,16 +623,39 @@
 	.bInterfaceProtocol =	0,
 	.iInterface =		STRING_DATA,
 };
-#else
+
+#endif
+
+#ifdef	CONFIG_USB_ETH_RNDIS
+
+/* RNDIS doesn't activate by changing to the "real" altsetting */
+
+static const struct usb_interface_descriptor
+rndis_data_intf = {
+	.bLength =		sizeof rndis_data_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bInterfaceNumber =	1,
+	.bAlternateSetting =	0,
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_CDC_DATA,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0,
+	.iInterface =		STRING_DATA,
+};
+
+#endif
+
+#ifdef DEV_CONFIG_SUBSET
 
 /*
- * "Minimalist" non-CDC option is a simple vendor-neutral model that most
+ * "Simple" CDC-subset option is a simple vendor-neutral model that most
  * full speed controllers can handle:  one interface, two bulk endpoints.
  */
 
 static const struct usb_interface_descriptor
-data_intf = {
-	.bLength =		sizeof data_intf,
+subset_data_intf = {
+	.bLength =		sizeof subset_data_intf,
 	.bDescriptorType =	USB_DT_INTERFACE,
 
 	.bInterfaceNumber =	0,
@@ -594,47 +667,73 @@
 	.iInterface =		STRING_DATA,
 };
 
-#endif	/* DEV_CONFIG_CDC */
+#endif	/* SUBSET */
 
 
-static const struct usb_endpoint_descriptor
+static struct usb_endpoint_descriptor
 fs_source_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
-	.bEndpointAddress =	EP_IN_NUM | USB_DIR_IN,
+	.bEndpointAddress =	USB_DIR_IN,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
-	.wMaxPacketSize =	__constant_cpu_to_le16 (64),
 };
 
-static const struct usb_endpoint_descriptor
+static struct usb_endpoint_descriptor
 fs_sink_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
-	.bEndpointAddress =	EP_OUT_NUM,
+	.bEndpointAddress =	USB_DIR_OUT,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
-	.wMaxPacketSize =	__constant_cpu_to_le16 (64),
 };
 
-static const struct usb_descriptor_header *fs_function [] = {
+static const struct usb_descriptor_header *fs_eth_function [10] = {
 #ifdef DEV_CONFIG_CDC
 	/* "cdc" mode descriptors */
 	(struct usb_descriptor_header *) &control_intf,
 	(struct usb_descriptor_header *) &header_desc,
 	(struct usb_descriptor_header *) &union_desc,
 	(struct usb_descriptor_header *) &ether_desc,
-#ifdef	EP_STATUS_NUM
+	/* NOTE: status endpoint may need to be removed */
 	(struct usb_descriptor_header *) &fs_status_desc,
-#endif
+	/* data interface, with altsetting */
 	(struct usb_descriptor_header *) &data_nop_intf,
-#endif /* DEV_CONFIG_CDC */
-	/* minimalist core */
 	(struct usb_descriptor_header *) &data_intf,
 	(struct usb_descriptor_header *) &fs_source_desc,
 	(struct usb_descriptor_header *) &fs_sink_desc,
 	0,
+#endif /* DEV_CONFIG_CDC */
+};
+
+static inline void __init fs_subset_descriptors(void)
+{
+#ifdef DEV_CONFIG_SUBSET
+	fs_eth_function[0] = (struct usb_descriptor_header *) &subset_data_intf;
+	fs_eth_function[1] = (struct usb_descriptor_header *) &fs_source_desc;
+	fs_eth_function[2] = (struct usb_descriptor_header *) &fs_sink_desc;
+	fs_eth_function[3] = 0;
+#else
+	fs_eth_function[0] = 0;
+#endif
+}
+
+#ifdef	CONFIG_USB_ETH_RNDIS
+static const struct usb_descriptor_header *fs_rndis_function [] = {
+	/* control interface matches ACM, not Ethernet */
+	(struct usb_descriptor_header *) &rndis_control_intf,
+	(struct usb_descriptor_header *) &header_desc,
+	(struct usb_descriptor_header *) &call_mgmt_descriptor,
+	(struct usb_descriptor_header *) &acm_descriptor,
+	(struct usb_descriptor_header *) &union_desc,
+	(struct usb_descriptor_header *) &fs_status_desc,
+	/* data interface has no altsetting */
+	(struct usb_descriptor_header *) &rndis_data_intf,
+	(struct usb_descriptor_header *) &fs_source_desc,
+	(struct usb_descriptor_header *) &fs_sink_desc,
+	0,
 };
+#endif
 
 #ifdef	CONFIG_USB_GADGET_DUALSPEED
 
@@ -643,39 +742,34 @@
  * descriptors, unless they only run at full speed.
  */
 
-#ifdef	EP_STATUS_NUM
-static const struct usb_endpoint_descriptor
+#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+static struct usb_endpoint_descriptor
 hs_status_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
-	.bEndpointAddress =	EP_STATUS_NUM | USB_DIR_IN,
 	.bmAttributes =		USB_ENDPOINT_XFER_INT,
 	.wMaxPacketSize =	__constant_cpu_to_le16 (STATUS_BYTECOUNT),
-	.bInterval =		LOG2_STATUS_INTERVAL_MSEC + 3,
+	.bInterval =		LOG2_STATUS_INTERVAL_MSEC + 4,
 };
-#endif
+#endif /* DEV_CONFIG_CDC */
 
-static const struct usb_endpoint_descriptor
+static struct usb_endpoint_descriptor
 hs_source_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
-	.bEndpointAddress =	EP_IN_NUM | USB_DIR_IN,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 	.wMaxPacketSize =	__constant_cpu_to_le16 (512),
-	.bInterval =		1,
 };
 
-static const struct usb_endpoint_descriptor
+static struct usb_endpoint_descriptor
 hs_sink_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
-	.bEndpointAddress =	EP_OUT_NUM,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 	.wMaxPacketSize =	__constant_cpu_to_le16 (512),
-	.bInterval =		1,
 };
 
 static struct usb_qualifier_descriptor
@@ -684,29 +778,57 @@
 	.bDescriptorType =	USB_DT_DEVICE_QUALIFIER,
 
 	.bcdUSB =		__constant_cpu_to_le16 (0x0200),
-	.bDeviceClass =		DEV_CONFIG_CLASS,
+	.bDeviceClass =		USB_CLASS_COMM,
 
 	.bNumConfigurations =	1,
 };
 
-static const struct usb_descriptor_header *hs_function [] = {
+static const struct usb_descriptor_header *hs_eth_function [10] = {
 #ifdef DEV_CONFIG_CDC
 	/* "cdc" mode descriptors */
 	(struct usb_descriptor_header *) &control_intf,
 	(struct usb_descriptor_header *) &header_desc,
 	(struct usb_descriptor_header *) &union_desc,
 	(struct usb_descriptor_header *) &ether_desc,
-#ifdef	EP_STATUS_NUM
+	/* NOTE: status endpoint may need to be removed */
 	(struct usb_descriptor_header *) &hs_status_desc,
-#endif
+	/* data interface, with altsetting */
 	(struct usb_descriptor_header *) &data_nop_intf,
-#endif /* DEV_CONFIG_CDC */
-	/* minimalist core */
 	(struct usb_descriptor_header *) &data_intf,
 	(struct usb_descriptor_header *) &hs_source_desc,
 	(struct usb_descriptor_header *) &hs_sink_desc,
 	0,
+#endif /* DEV_CONFIG_CDC */
+};
+
+static inline void __init hs_subset_descriptors(void)
+{
+#ifdef DEV_CONFIG_SUBSET
+	hs_eth_function[0] = (struct usb_descriptor_header *) &subset_data_intf;
+	hs_eth_function[1] = (struct usb_descriptor_header *) &fs_source_desc;
+	hs_eth_function[2] = (struct usb_descriptor_header *) &fs_sink_desc;
+	hs_eth_function[3] = 0;
+#else
+	hs_eth_function[0] = 0;
+#endif
+}
+
+#ifdef	CONFIG_USB_ETH_RNDIS
+static const struct usb_descriptor_header *hs_rndis_function [] = {
+	/* control interface matches ACM, not Ethernet */
+	(struct usb_descriptor_header *) &rndis_control_intf,
+	(struct usb_descriptor_header *) &header_desc,
+	(struct usb_descriptor_header *) &call_mgmt_descriptor,
+	(struct usb_descriptor_header *) &acm_descriptor,
+	(struct usb_descriptor_header *) &union_desc,
+	(struct usb_descriptor_header *) &hs_status_desc,
+	/* data interface has no altsetting */
+	(struct usb_descriptor_header *) &rndis_data_intf,
+	(struct usb_descriptor_header *) &hs_source_desc,
+	(struct usb_descriptor_header *) &hs_sink_desc,
+	0,
 };
+#endif
 
 
 /* maxpacket and other transfer characteristics vary by speed. */
@@ -717,12 +839,19 @@
 /* if there's no high speed support, maxpacket doesn't change. */
 #define ep_desc(g,hs,fs) fs
 
+static inline void __init hs_subset_descriptors(void)
+{
+}
+
 #endif	/* !CONFIG_USB_GADGET_DUALSPEED */
 
 /*-------------------------------------------------------------------------*/
 
 /* descriptors that are built on-demand */
 
+static char				manufacturer [40];
+static char				product_desc [40] = DRIVER_DESC;
+
 #ifdef	DEV_CONFIG_CDC
 /* address that the host will use ... usually assigned at random */
 static char				ethaddr [2 * ETH_ALEN + 1];
@@ -730,13 +859,21 @@
 
 /* static strings, in iso 8859/1 */
 static struct usb_string		strings [] = {
-	{ STRING_MANUFACTURER,	UTS_SYSNAME " " UTS_RELEASE "/" CHIP, },
-	{ STRING_PRODUCT,	driver_desc, },
+	{ STRING_MANUFACTURER,	manufacturer, },
+	{ STRING_PRODUCT,	product_desc, },
+	{ STRING_DATA,		"Ethernet Data", },
 #ifdef	DEV_CONFIG_CDC
+	{ STRING_CDC,		"CDC Ethernet", },
 	{ STRING_ETHADDR,	ethaddr, },
 	{ STRING_CONTROL,	"CDC Communications Control", },
 #endif
-	{ STRING_DATA,		"Ethernet Data", },
+#ifdef	DEV_CONFIG_SUBSET
+	{ STRING_SUBSET,	"CDC Ethernet Subset", },
+#endif
+#ifdef	CONFIG_USB_ETH_RNDIS
+	{ STRING_RNDIS,		"RNDIS", },
+	{ STRING_RNDIS_CONTROL,	"RNDIS Communications Control", },
+#endif
 	{  }		/* end of list */
 };
 
@@ -753,20 +890,34 @@
 config_buf (enum usb_device_speed speed, u8 *buf, u8 type, unsigned index)
 {
 	int				len;
-	const struct usb_descriptor_header **function = fs_function;
 #ifdef CONFIG_USB_GADGET_DUALSPEED
 	int				hs = (speed == USB_SPEED_HIGH);
 
 	if (type == USB_DT_OTHER_SPEED_CONFIG)
 		hs = !hs;
-	if (hs)
-		function = hs_function;
+#define which_config(t)	(hs ? & t ## _config   : & t ## _config)
+#define which_fn(t)	(hs ? & hs_ ## t ## _function : & fs_ ## t ## _function)
+#else
+#define	which_config(t)	(& t ## _config)
+#define	which_fn(t)	(& fs_ ## t ## _function)
 #endif
 
-	/* a single configuration must always be index 0 */
-	if (index > 0)
+	if (index >= device_desc.bNumConfigurations)
 		return -EINVAL;
-	len = usb_gadget_config_buf (&eth_config, buf, USB_BUFSIZ, function);
+
+#ifdef	CONFIG_USB_ETH_RNDIS
+	/* list the RNDIS config first, to make Microsoft's drivers
+	 * happy. DOCSIS 1.0 needs this too.
+	 */
+	if (device_desc.bNumConfigurations == 2 && index == 0)
+		len = usb_gadget_config_buf (which_config (rndis), buf,
+			USB_BUFSIZ, (const struct usb_descriptor_header **)
+				which_fn (rndis));
+	else
+#endif
+		len = usb_gadget_config_buf (which_config (eth), buf,
+			USB_BUFSIZ, (const struct usb_descriptor_header **)
+				which_fn (eth));
 	if (len < 0)
 		return len;
 	((struct usb_config_descriptor *) buf)->bDescriptorType = type;
@@ -778,6 +929,91 @@
 static void eth_start (struct eth_dev *dev, int gfp_flags);
 static int alloc_requests (struct eth_dev *dev, unsigned n, int gfp_flags);
 
+#ifdef	DEV_CONFIG_CDC
+static inline int ether_alt_ep_setup (struct eth_dev *dev, struct usb_ep *ep)
+{
+	const struct usb_endpoint_descriptor	*d;
+
+	/* With CDC,  the host isn't allowed to use these two data
+	 * endpoints in the default altsetting for the interface.
+	 * so we don't activate them yet.  Reset from SET_INTERFACE.
+	 *
+	 * Strictly speaking RNDIS should work the same: activation is
+	 * a side effect of setting a packet filter.  Deactivation is
+	 * from REMOTE_NDIS_HALT_MSG, reset from REMOTE_NDIS_RESET_MSG.
+	 */
+
+	/* one endpoint writes data back IN to the host */
+	if (strcmp (ep->name, EP_IN_NAME) == 0) {
+		d = ep_desc (dev->gadget, &hs_source_desc, &fs_source_desc);
+		ep->driver_data = dev;
+		dev->in_ep = ep;
+		dev->in = d;
+
+	/* one endpoint just reads OUT packets */
+	} else if (strcmp (ep->name, EP_OUT_NAME) == 0) {
+		d = ep_desc (dev->gadget, &hs_sink_desc, &fs_sink_desc);
+		ep->driver_data = dev;
+		dev->out_ep = ep;
+		dev->out = d;
+
+	/* optional status/notification endpoint */
+	} else if (EP_STATUS_NAME &&
+			strcmp (ep->name, EP_STATUS_NAME) == 0) {
+		int			result;
+
+		d = ep_desc (dev->gadget, &hs_status_desc, &fs_status_desc);
+		result = usb_ep_enable (ep, d);
+		if (result < 0)
+			return result;
+
+		ep->driver_data = dev;
+		dev->status_ep = ep;
+		dev->status = d;
+	}
+	return 0;
+}
+#endif
+
+#if	defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS)
+static inline int ether_ep_setup (struct eth_dev *dev, struct usb_ep *ep)
+{
+	int					result;
+	const struct usb_endpoint_descriptor	*d;
+
+	/* CDC subset is simpler:  if the device is there,
+	 * it's live with rx and tx endpoints.
+	 *
+	 * Do this as a shortcut for RNDIS too.
+	 */
+
+	/* one endpoint writes data back IN to the host */
+	if (strcmp (ep->name, EP_IN_NAME) == 0) {
+		d = ep_desc (dev->gadget, &hs_source_desc, &fs_source_desc);
+		result = usb_ep_enable (ep, d);
+		if (result < 0)
+			return result;
+
+		ep->driver_data = dev;
+		dev->in_ep = ep;
+		dev->in = d;
+
+	/* one endpoint just reads OUT packets */
+	} else if (strcmp (ep->name, EP_OUT_NAME) == 0) {
+		d = ep_desc (dev->gadget, &hs_sink_desc, &fs_sink_desc);
+		result = usb_ep_enable (ep, d);
+		if (result < 0)
+			return result;
+
+		ep->driver_data = dev;
+		dev->out_ep = ep;
+		dev->out = d;
+	}
+
+	return 0;
+}
+#endif
+
 static int
 set_ether_config (struct eth_dev *dev, int gfp_flags)
 {
@@ -786,34 +1022,17 @@
 	struct usb_gadget	*gadget = dev->gadget;
 
 	gadget_for_each_ep (ep, gadget) {
-		const struct usb_endpoint_descriptor	*d;
-
 #ifdef	DEV_CONFIG_CDC
-		/* With CDC,  the host isn't allowed to use these two data
-		 * endpoints in the default altsetting for the interface.
-		 * so we don't activate them yet.
-		 */
+		if (!dev->rndis && dev->cdc) {
+			result = ether_alt_ep_setup (dev, ep);
+			if (result == 0)
+				continue;
+		}
+#endif
 
-		/* one endpoint writes data back IN to the host */
-		if (strcmp (ep->name, EP_IN_NAME) == 0) {
-			d = ep_desc (gadget, &hs_source_desc, &fs_source_desc);
-			ep->driver_data = dev;
-			dev->in_ep = ep;
-			dev->in = d;
-			continue;
-
-		/* one endpoint just reads OUT packets */
-		} else if (strcmp (ep->name, EP_OUT_NAME) == 0) {
-			d = ep_desc (gadget, &hs_sink_desc, &fs_sink_desc);
-			ep->driver_data = dev;
-			dev->out_ep = ep;
-			dev->out = d;
-			continue;
-		}
-
-#ifdef	EP_STATUS_NUM
-		/* optional status/notification endpoint */
-		else if (strcmp (ep->name, EP_STATUS_NAME) == 0) {
+#ifdef	CONFIG_USB_ETH_RNDIS
+		if (dev->rndis && strcmp (ep->name, EP_STATUS_NAME) == 0) {
+			const struct usb_endpoint_descriptor	*d;
 			d = ep_desc (gadget, &hs_status_desc, &fs_status_desc);
 			result = usb_ep_enable (ep, d);
 			if (result == 0) {
@@ -822,43 +1041,17 @@
 				dev->status = d;
 				continue;
 			}
-		}
+		} else
 #endif
 
-#else	/* !CONFIG_CDC_ETHER */
-
-		/* non-CDC is simpler:  if the device is there,
-		 * it's live with rx and tx endpoints.
-		 */
-		/* one endpoint writes data back IN to the host */
-		if (strcmp (ep->name, EP_IN_NAME) == 0) {
-			d = ep_desc (gadget, &hs_source_desc, &fs_source_desc);
-			result = usb_ep_enable (ep, d);
-			if (result == 0) {
-				ep->driver_data = dev;
-				dev->in_ep = ep;
-				dev->in = d;
+		{
+#if	defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS)
+			result = ether_ep_setup (dev, ep);
+			if (result == 0)
 				continue;
-			}
-
-		/* one endpoint just reads OUT packets */
-		} else if (strcmp (ep->name, EP_OUT_NAME) == 0) {
-			d = ep_desc (gadget, &hs_sink_desc, &fs_sink_desc);
-			result = usb_ep_enable (ep, d);
-			if (result == 0) {
-				ep->driver_data = dev;
-				dev->out_ep = ep;
-				dev->out = d;
-				continue;
-			}
+#endif
 		}
 
-#endif /* !CONFIG_CDC_ETHER */
-
-		/* ignore any other endpoints */
-		else
-			continue;
-
 		/* stop on error */
 		ERROR (dev, "can't enable %s, result %d\n", ep->name, result);
 		break;
@@ -869,23 +1062,41 @@
 	if (result == 0)
 		result = alloc_requests (dev, qlen (gadget), gfp_flags);
 
-#ifndef	DEV_CONFIG_CDC
-	if (result == 0) {
+	/* on error, disable any endpoints  */
+	if (result < 0) {
+#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+		if (dev->status_ep)
+			(void) usb_ep_disable (dev->status_ep);
+#endif
+		dev->status_ep = 0;
+		dev->status = 0;
+#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS)
+		if (dev->rndis || !dev->cdc) {
+			if (dev->in_ep)
+				(void) usb_ep_disable (dev->in_ep);
+			if (dev->out_ep)
+				(void) usb_ep_disable (dev->out_ep);
+		}
+#endif
+		dev->in_ep = 0;
+		dev->in = 0;
+		dev->out_ep = 0;
+		dev->out = 0;
+	} else
+
+	/* activate non-CDC configs right away
+	 * this isn't strictly according to the RNDIS spec
+	 */
+#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS)
+	if (dev->rndis || !dev->cdc) {
 		netif_carrier_on (dev->net);
 		if (netif_running (dev->net)) {
 			spin_unlock (&dev->lock);
 			eth_start (dev, GFP_ATOMIC);
 			spin_lock (&dev->lock);
 		}
-	} else {
-		(void) usb_ep_disable (dev->in_ep);
-		dev->in_ep = 0;
-		dev->in = 0;
-		(void) usb_ep_disable (dev->out_ep);
-		dev->out_ep = 0;
-		dev->out = 0;
 	}
-#endif /* !CONFIG_CDC_ETHER */
+#endif
 
 	if (result == 0)
 		DEBUG (dev, "qlen %d\n", qlen (gadget));
@@ -930,12 +1141,10 @@
 		dev->out_ep = 0;
 	}
 
-#ifdef	EP_STATUS_NUM
 	if (dev->status_ep) {
 		usb_ep_disable (dev->status_ep);
 		dev->status_ep = 0;
 	}
-#endif
 	dev->config = 0;
 }
 
@@ -951,20 +1160,29 @@
 	if (number == dev->config)
 		return 0;
 
-#ifdef CONFIG_USB_GADGET_SA1100
-	if (dev->config && atomic_read (&dev->tx_qlen) != 0) {
+	if (gadget_is_sa1100 (gadget)
+			&& dev->config
+			&& atomic_read (&dev->tx_qlen) != 0) {
 		/* tx fifo is full, but we can't clear it...*/
 		INFO (dev, "can't change configurations\n");
 		return -ESPIPE;
 	}
-#endif
 	eth_reset_config (dev);
-	hw_optimize (gadget);
+
+	/* default:  pass all packets, no multicast filtering */
+	dev->cdc_filter = 0x000f;
 
 	switch (number) {
 	case DEV_CONFIG_VALUE:
+		dev->rndis = 0;
+		result = set_ether_config (dev, gfp_flags);
+		break;
+#ifdef	CONFIG_USB_ETH_RNDIS
+	case DEV_RNDIS_CONFIG_VALUE:
+		dev->rndis = 1;
 		result = set_ether_config (dev, gfp_flags);
 		break;
+#endif
 	default:
 		result = -EINVAL;
 		/* FALL THROUGH */
@@ -986,21 +1204,29 @@
 		}
 
 		dev->config = number;
-		INFO (dev, "%s speed config #%d: %s\n", speed, number,
-				driver_desc);
+		INFO (dev, "%s speed config #%d: %s, using %s\n",
+				speed, number, driver_desc,
+				dev->rndis
+					? "RNDIS"
+					: (dev->cdc
+						? "CDC Ethernet"
+						: "CDC Ethernet Subset"));
 	}
 	return result;
 }
 
 /*-------------------------------------------------------------------------*/
 
-#ifdef	EP_STATUS_NUM
-
-/* section 3.8.2 table 11 of the CDC spec lists Ethernet notifications */
+/* section 3.8.2 table 11 of the CDC spec lists Ethernet notifications
+ * section 3.6.2.1 table 5 specifies ACM notifications, accepted by RNDIS
+ * and RNDIS also defines its own bit-incompatible notifications
+ */
 #define CDC_NOTIFY_NETWORK_CONNECTION	0x00	/* required; 6.3.1 */
 #define CDC_NOTIFY_RESPONSE_AVAILABLE	0x01	/* optional; 6.3.2 */
 #define CDC_NOTIFY_SPEED_CHANGE		0x2a	/* required; 6.3.8 */
 
+#ifdef	DEV_CONFIG_CDC
+
 struct cdc_notification {
 	u8	bmRequestType;
 	u8	bNotificationType;
@@ -1115,19 +1341,58 @@
 
 /* see section 3.8.2 table 10 of the CDC spec for more ethernet
  * requests, mostly for filters (multicast, pm) and statistics
+ * section 3.6.2.1 table 4 has ACM requests; RNDIS requires the
+ * encapsulated command mechanism.
  */
-#define CDC_SEND_ENCAPSULATED_REQUEST	0x00	/* optional */
-#define CDC_GET_ENCAPSULATED_RESPONSE	0x01	/* optional */
-#define CDC_SET_ETHERNET_PACKET_FILTER	0x43	/* required */
+#define CDC_SEND_ENCAPSULATED_COMMAND		0x00	/* optional */
+#define CDC_GET_ENCAPSULATED_RESPONSE		0x01	/* optional */
+#define CDC_SET_ETHERNET_MULTICAST_FILTERS	0x40	/* optional */
+#define CDC_SET_ETHERNET_PM_PATTERN_FILTER	0x41	/* optional */
+#define CDC_GET_ETHERNET_PM_PATTERN_FILTER	0x42	/* optional */
+#define CDC_SET_ETHERNET_PACKET_FILTER		0x43	/* required */
+#define CDC_GET_ETHERNET_STATISTIC		0x44	/* optional */
+
+/* table 62; bits in cdc_filter */
+#define	CDC_PACKET_TYPE_PROMISCUOUS		(1 << 0)
+#define	CDC_PACKET_TYPE_ALL_MULTICAST		(1 << 1) /* no filter */
+#define	CDC_PACKET_TYPE_DIRECTED		(1 << 2)
+#define	CDC_PACKET_TYPE_BROADCAST		(1 << 3)
+#define	CDC_PACKET_TYPE_MULTICAST		(1 << 4) /* filtered */
+
+#ifdef CONFIG_USB_ETH_RNDIS
+
+static void rndis_response_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	if (req->status || req->actual != req->length)
+		DEBUG (dev, "rndis response complete --> %d, %d/%d\n",
+		       req->status, req->actual, req->length);
+
+	/* done sending after CDC_GET_ENCAPSULATED_RESPONSE */
+}
+
+static void rndis_command_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	struct eth_dev          *dev = ep->driver_data;
+	int			status;
+	
+	/* received RNDIS command from CDC_SEND_ENCAPSULATED_COMMAND */
+	spin_lock(&dev->lock);
+	status = rndis_msg_parser (dev->rndis_config, (u8 *) req->buf);
+	if (status < 0)
+		ERROR(dev, "%s: rndis parse error %d\n", __FUNCTION__, status);
+	spin_unlock(&dev->lock);
+}
+
+#endif	/* RNDIS */
 
 /*
  * The setup() callback implements all the ep0 functionality that's not
  * handled lower down.  CDC has a number of less-common features:
  *
  *  - two interfaces:  control, and ethernet data
- *  - data interface has two altsettings:  default, and active
+ *  - Ethernet data interface has two altsettings:  default, and active
  *  - class-specific descriptors for the control interface
- *  - a mandatory class-specific control request
+ *  - class-specific control requests
  */
 static int
 eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
@@ -1187,17 +1452,6 @@
 		value = eth_set_config (dev, ctrl->wValue, GFP_ATOMIC);
 		spin_unlock (&dev->lock);
 		break;
-#ifdef	CONFIG_USB_GADGET_PXA2XX
-	/* PXA UDC prevents us from using SET_INTERFACE in normal ways.
-	 * And it hides GET_CONFIGURATION and GET_INTERFACE too.
-	 */
-	case USB_REQ_SET_INTERFACE:
-		spin_lock (&dev->lock);
-		value = eth_set_config (dev, DEV_CONFIG_VALUE, GFP_ATOMIC);
-		spin_unlock (&dev->lock);
-		break;
-
-#else	/* hardware that that stays out of our way */
 	case USB_REQ_GET_CONFIGURATION:
 		if (ctrl->bRequestType != USB_DIR_IN)
 			break;
@@ -1210,17 +1464,28 @@
 				|| !dev->config
 				|| ctrl->wIndex > 1)
 			break;
+		if (!dev->cdc && ctrl->wIndex != 0)
+			break;
 		spin_lock (&dev->lock);
+
+		/* PXA hardware partially handles SET_INTERFACE;
+		 * we need to kluge around that interference.
+		 */
+		if (gadget_is_pxa (gadget)) {
+			value = eth_set_config (dev, DEV_CONFIG_VALUE,
+						GFP_ATOMIC);
+			goto done_set_intf;
+		}
+
+#ifdef DEV_CONFIG_CDC
 		switch (ctrl->wIndex) {
 		case 0:		/* control/master intf */
 			if (ctrl->wValue != 0)
 				break;
-#ifdef	EP_STATUS_NUM
 			if (dev->status_ep) {
 				usb_ep_disable (dev->status_ep);
 				usb_ep_enable (dev->status_ep, dev->status);
 			}
-#endif
 			value = 0;
 			break;
 		case 1:		/* data intf */
@@ -1237,9 +1502,8 @@
 				usb_ep_enable (dev->in_ep, dev->in);
 				usb_ep_enable (dev->out_ep, dev->out);
 				netif_carrier_on (dev->net);
-#ifdef	EP_STATUS_NUM
-				issue_start_status (dev);
-#endif
+				if (dev->status_ep)
+					issue_start_status (dev);
 				if (netif_running (dev->net)) {
 					spin_unlock (&dev->lock);
 					eth_start (dev, GFP_ATOMIC);
@@ -1252,6 +1516,14 @@
 			value = 0;
 			break;
 		}
+#else
+		/* FIXME this is wrong, as is the assumption that
+		 * all non-PXA hardware talks real CDC ...
+		 */
+		dev_warn (&gadget->dev, "set_interface ignored!\n");
+#endif /* DEV_CONFIG_CDC */
+
+done_set_intf:
 		spin_unlock (&dev->lock);
 		break;
 	case USB_REQ_GET_INTERFACE:
@@ -1259,15 +1531,16 @@
 				|| !dev->config
 				|| ctrl->wIndex > 1)
 			break;
+		if (!(dev->cdc || dev->rndis) && ctrl->wIndex != 0)
+			break;
 
-		/* if carrier is on, data interface is active. */
-		*(u8 *)req->buf =
-			((ctrl->wIndex == 1) && netif_carrier_ok (dev->net))
-				? 1
-				: 0,
+		/* for CDC, iff carrier is on, data interface is active. */
+		if (dev->rndis || ctrl->wIndex != 1)
+			*(u8 *)req->buf = 0;
+		else
+			*(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0;
 		value = min (ctrl->wLength, (u16) 1);
 		break;
-#endif
 
 #ifdef DEV_CONFIG_CDC
 	case CDC_SET_ETHERNET_PACKET_FILTER:
@@ -1275,17 +1548,62 @@
 		 * wValue = packet filter bitmap
 		 */
 		if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE)
+				|| !dev->cdc
+				|| dev->rndis
 				|| ctrl->wLength != 0
 				|| ctrl->wIndex > 1)
+			break;
 		DEBUG (dev, "NOP packet filter %04x\n", ctrl->wValue);
 		/* NOTE: table 62 has 5 filter bits to reduce traffic,
 		 * and we "must" support multicast and promiscuous.
-		 * this NOP implements a bad filter...
+		 * this NOP implements a bad filter (always promisc)
 		 */
+		dev->cdc_filter = ctrl->wValue;
 		value = 0;
 		break;
 #endif /* DEV_CONFIG_CDC */
 
+#ifdef CONFIG_USB_ETH_RNDIS		
+	/* RNDIS uses the CDC command encapsulation mechanism to implement
+	 * an RPC scheme, with much getting/setting of attributes by OID.
+	 */
+	case CDC_SEND_ENCAPSULATED_COMMAND:
+		if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE)
+				|| !dev->rndis
+				|| ctrl->wLength > USB_BUFSIZ
+				|| ctrl->wValue
+				|| rndis_control_intf.bInterfaceNumber
+					!= ctrl->wIndex)
+			break;
+		/* read the request, then process it */
+		value = ctrl->wLength;
+		req->complete = rndis_command_complete;
+		/* later, rndis_control_ack () sends a notification */
+		break;
+		
+	case CDC_GET_ENCAPSULATED_RESPONSE:
+		if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)
+					== ctrl->bRequestType
+				&& dev->rndis
+				// && ctrl->wLength >= 0x0400
+				&& !ctrl->wValue
+				&& rndis_control_intf.bInterfaceNumber
+					== ctrl->wIndex) {
+			u8 *buf;
+
+			/* return the result */
+			buf = rndis_get_next_response (dev->rndis_config,
+						       &value);
+			if (buf) {
+				memcpy (req->buf, buf, value);
+				req->complete = rndis_response_complete;
+				rndis_free_response(dev->rndis_config, buf);
+			}
+			/* else stalls ... spec says to avoid that */
+		}
+		break;
+#endif	/* RNDIS */
+
 	default:
 		VDEBUG (dev,
 			"unknown control req%02x.%02x v%04x i%04x l%d\n",
@@ -1320,6 +1638,8 @@
 	eth_reset_config (dev);
 	spin_unlock_irqrestore (&dev->lock, flags);
 
+	/* FIXME RNDIS should enter RNDIS_UNINITIALIZED */
+
 	/* next we may get setup() calls to enumerate new connections;
 	 * or an unbind() during shutdown (including removing module).
 	 */
@@ -1333,7 +1653,9 @@
 {
 	struct eth_dev	*dev = (struct eth_dev *) net->priv;
 
-	if (new_mtu <= MIN_PACKET || new_mtu > MAX_PACKET)
+	// FIXME if rndis, don't change while link's live
+
+	if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN)
 		return -ERANGE;
 	/* no zero-length packet read wanted after mtu-sized packets */
 	if (((new_mtu + sizeof (struct ethhdr)) % dev->in_ep->maxpacket) == 0)
@@ -1347,12 +1669,12 @@
 	return &((struct eth_dev *) net->priv)->stats;
 }
 
-static int eth_ethtool_ioctl (struct net_device *net, void *useraddr)
+static int eth_ethtool_ioctl (struct net_device *net, void __user *useraddr)
 {
 	struct eth_dev	*dev = (struct eth_dev *) net->priv;
 	u32		cmd;
 
-	if (get_user (cmd, (u32 *)useraddr))
+	if (get_user (cmd, (u32 __user *)useraddr))
 		return -EFAULT;
 	switch (cmd) {
 
@@ -1363,7 +1685,8 @@
 		info.cmd = ETHTOOL_GDRVINFO;
 		strncpy (info.driver, shortname, sizeof info.driver);
 		strncpy (info.version, DRIVER_VERSION, sizeof info.version);
-		strncpy (info.fw_version, CHIP, sizeof info.fw_version);
+		strncpy (info.fw_version, dev->gadget->name,
+			sizeof info.fw_version);
 		strncpy (info.bus_info, dev->gadget->dev.bus_id,
 			sizeof info.bus_info);
 		if (copy_to_user (useraddr, &info, sizeof (info)))
@@ -1389,7 +1712,7 @@
 {
 	switch (cmd) {
 	case SIOCETHTOOL:
-		return eth_ethtool_ioctl (net, (void *)rq->ifr_data);
+		return eth_ethtool_ioctl(net, rq->ifr_data);
 	default:
 		return -EOPNOTSUPP;
 	}
@@ -1414,7 +1737,27 @@
 	int			retval = -ENOMEM;
 	size_t			size;
 
+	/* Padding up to RX_EXTRA handles minor disagreements with host.
+	 * Normally we use the USB "terminate on short read" convention;
+	 * so allow up to (N*maxpacket)-1, since that memory is normally
+	 * already allocated.  Major loss of synch means -EOVERFLOW; any
+	 * obviously corrupted packets will automatically be discarded. 
+	 *
+	 * RNDIS uses internal framing, and explicitly allows senders to
+	 * pad to end-of-packet.  That's potentially nice for speed,
+	 * but means receivers can't recover synch on their own.
+	 */
 	size = (sizeof (struct ethhdr) + dev->net->mtu + RX_EXTRA);
+	size += dev->out_ep->maxpacket - 1;
+#ifdef CONFIG_USB_ETH_RNDIS
+	if (dev->rndis)
+		size += sizeof (struct rndis_packet_msg_type);
+#endif	
+	size -= size % dev->out_ep->maxpacket;
+#ifdef CONFIG_USB_ETH_RNDIS
+	if (!dev->rndis)
+#endif	
+		size--;
 
 	if ((skb = alloc_skb (size, gfp_flags)) == 0) {
 		DEBUG (dev, "no rx skb\n");
@@ -1451,8 +1794,12 @@
 	/* normal completion */
 	case 0:
 		skb_put (skb, req->actual);
-		if (MIN_PACKET > skb->len
-				|| skb->len > (MAX_PACKET + ETH_HLEN)) {
+#ifdef CONFIG_USB_ETH_RNDIS
+		/* we know MaxPacketsPerTransfer == 1 here */
+		if (dev->rndis)
+			rndis_rm_hdr (req->buf, &(skb->len));
+#endif
+		if (ETH_HLEN > skb->len || skb->len > ETH_FRAME_LEN) {
 			dev->stats.rx_errors++;
 			dev->stats.rx_length_errors++;
 			DEBUG (dev, "rx length %d\n", skb->len);
@@ -1640,6 +1987,11 @@
 	struct usb_request	*req = 0;
 	unsigned long		flags;
 
+	/* FIXME check dev->cdc_filter to decide whether to send this,
+	 * instead of acting as if CDC_PACKET_TYPE_PROMISCUOUS were
+	 * always set.  RNDIS has the same kind of outgoing filter.
+	 */
+
 	spin_lock_irqsave (&dev->lock, flags);
 	req = container_of (dev->tx_reqs.next, struct usb_request, list);
 	list_del (&req->list);
@@ -1649,21 +2001,35 @@
 
 	/* no buffer copies needed, unless the network stack did it
 	 * or the hardware can't use skb buffers.
+	 * or there's not enough space for any RNDIS headers we need
 	 */
+#ifdef CONFIG_USB_ETH_RNDIS
+	if (dev->rndis) {
+		struct sk_buff	*skb_rndis;
+
+		skb_rndis = skb_realloc_headroom (skb,
+				sizeof (struct rndis_packet_msg_type));
+		if (!skb_rndis)
+			goto drop;
+	
+		dev_kfree_skb_any (skb);
+		skb = skb_rndis;
+		rndis_add_hdr (skb);
+		length = skb->len;
+	}
+#endif
 	req->buf = skb->data;
 	req->context = skb;
 	req->complete = tx_complete;
 
-#ifdef	CONFIG_USB_GADGET_SA1100
-	/* don't demand zlp (req->zero) support from all hardware */
-	if ((length % dev->in_ep->maxpacket) == 0)
-		length++;
-#else
 	/* use zlp framing on tx for strict CDC-Ether conformance,
 	 * though any robust network rx path ignores extra padding.
+	 * and some hardware doesn't like to write zlps.
 	 */
 	req->zero = 1;
-#endif
+	if (!dev->zlp && (length % dev->in_ep->maxpacket) == 0)
+		length++;
+
 	req->length = length;
 
 #ifdef	CONFIG_USB_GADGET_DUALSPEED
@@ -1684,6 +2050,9 @@
 	}
 
 	if (retval) {
+#ifdef CONFIG_USB_ETH_RNDIS
+drop:
+#endif
 		dev->stats.tx_dropped++;
 		dev_kfree_skb_any (skb);
 		spin_lock_irqsave (&dev->lock, flags);
@@ -1695,6 +2064,81 @@
 	return 0;
 }
 
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_USB_ETH_RNDIS
+
+static void rndis_send_media_state (struct eth_dev *dev, int connect)
+{
+	if (!dev)
+		return;
+	
+	if (connect) {
+		if (rndis_signal_connect (dev->rndis_config))
+			return;
+	} else {
+		if (rndis_signal_disconnect (dev->rndis_config))
+			return;
+	}
+}
+
+static void rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	if (req->status || req->actual != req->length)
+		DEBUG (dev, "rndis control ack complete --> %d, %d/%d\n",
+		       req->status, req->actual, req->length);
+
+	usb_ep_free_buffer(ep, req->buf, req->dma, 8);
+	usb_ep_free_request(ep, req);
+}
+
+static int rndis_control_ack (struct net_device *net)
+{
+	struct eth_dev          *dev = (struct eth_dev *) net->priv;
+	u32                     length;
+	struct usb_request      *resp;
+	
+	/* in case RNDIS calls this after disconnect */
+	if (!dev->status_ep) {
+		DEBUG (dev, "status ENODEV\n");
+		return -ENODEV;
+	}
+
+	/* Allocate memory for notification ie. ACK */
+	resp = usb_ep_alloc_request (dev->status_ep, GFP_ATOMIC);
+	if (!resp) {
+		DEBUG (dev, "status ENOMEM\n");
+		return -ENOMEM;
+	}
+	
+	resp->buf = usb_ep_alloc_buffer (dev->status_ep, 8,
+					 &resp->dma, GFP_ATOMIC);
+	if (!resp->buf) {
+		DEBUG (dev, "status buf ENOMEM\n");
+		usb_ep_free_request (dev->status_ep, resp);
+		return -ENOMEM;
+	}
+	
+	/* Send RNDIS RESPONSE_AVAILABLE notification;
+	 * CDC_NOTIFY_RESPONSE_AVAILABLE should work too
+	 */
+	resp->length = 8;
+	resp->complete = rndis_control_ack_complete;
+	
+	*((u32 *) resp->buf) = __constant_cpu_to_le32 (1);
+	*((u32 *) resp->buf + 1) = __constant_cpu_to_le32 (0);
+	
+	length = usb_ep_queue (dev->status_ep, resp, GFP_ATOMIC);
+	if (length < 0) {
+		resp->status = 0;
+		rndis_control_ack_complete (dev->status_ep, resp);
+	}
+	
+	return 0;
+}
+
+#endif	/* RNDIS */
+
 static void eth_start (struct eth_dev *dev, int gfp_flags)
 {
 	DEBUG (dev, "%s\n", __FUNCTION__);
@@ -1705,6 +2149,14 @@
 	/* and open the tx floodgates */ 
 	atomic_set (&dev->tx_qlen, 0);
 	netif_wake_queue (dev->net);
+#ifdef CONFIG_USB_ETH_RNDIS
+	if (dev->rndis) {
+		rndis_set_param_medium (dev->rndis_config,
+					NDIS_MEDIUM_802_3,
+					BITRATE(dev->gadget));
+		rndis_send_media_state (dev, 1);
+	}
+#endif	
 }
 
 static int eth_open (struct net_device *net)
@@ -1739,11 +2191,19 @@
 			usb_ep_enable (dev->in_ep, dev->in);
 			usb_ep_enable (dev->out_ep, dev->out);
 		}
-#ifdef	EP_STATUS_NUM
-		usb_ep_disable (dev->status_ep);
-		usb_ep_enable (dev->status_ep, dev->status);
-#endif
+		if (dev->status_ep) {
+			usb_ep_disable (dev->status_ep);
+			usb_ep_enable (dev->status_ep, dev->status);
+		}
+	}
+	
+#ifdef	CONFIG_USB_ETH_RNDIS
+	if (dev->rndis) {
+		rndis_set_param_medium (dev->rndis_config,
+					NDIS_MEDIUM_802_3, 0);
+		rndis_send_media_state (dev, 0);
 	}
+#endif
 
 	return 0;
 }
@@ -1756,6 +2216,10 @@
 	struct eth_dev		*dev = get_gadget_data (gadget);
 
 	DEBUG (dev, "unbind\n");
+#ifdef CONFIG_USB_ETH_RNDIS
+	rndis_deregister (dev->rndis_config);
+	rndis_exit ();
+#endif
 
 	/* we've already been disconnected ... no i/o is active */
 	if (dev->req) {
@@ -1767,37 +2231,218 @@
 	}
 
 	unregister_netdev (dev->net);
-	dev_put (dev->net);
+	free_netdev(dev->net);
 
 	/* assuming we used keventd, it must quiesce too */
 	flush_scheduled_work ();
 	set_gadget_data (gadget, 0);
 }
 
-static int
+static u8 __init nibble (unsigned char c)
+{
+	if (likely (isdigit (c)))
+		return c - '0';
+	c = toupper (c);
+	if (likely (isxdigit (c)))
+		return 10 + c - 'A';
+	return 0;
+}
+
+static void __init get_ether_addr (const char *str, u8 *dev_addr)
+{
+	if (str) {
+		unsigned	i;
+
+		for (i = 0; i < 6; i++) {
+			unsigned char num;
+
+			if((*str == '.') || (*str == ':'))
+				str++;
+			num = nibble(*str++) << 4;
+			num |= (nibble(*str++));
+			dev_addr [i] = num;
+		}
+		if (is_valid_ether_addr (dev_addr))
+			return;
+	}
+	random_ether_addr(dev_addr);
+}
+
+static int __init
 eth_bind (struct usb_gadget *gadget)
 {
 	struct eth_dev		*dev;
 	struct net_device	*net;
+	u8			cdc = 1, zlp = 1, rndis = 1;
+	struct usb_ep		*ep;
 	int			status = -ENOMEM;
-#ifdef	DEV_CONFIG_CDC
-	u8			node_id [ETH_ALEN];
 
-	/* just one upstream link at a time */
-	if (ethaddr [0] != 0)
+	/* these flags are only ever cleared; compiler take note */
+#ifndef	DEV_CONFIG_CDC
+	cdc = 0;
+#endif
+#ifndef	CONFIG_USB_ETH_RNDIS
+	rndis = 0;
+#endif
+
+	/* Because most host side USB stacks handle CDC Ethernet, that
+	 * standard protocol is _strongly_ preferred for interop purposes.
+	 * (By everyone except Microsoft.)
+	 */
+	if (gadget_is_net2280 (gadget)) {
+		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0201);
+	} else if (gadget_is_dummy (gadget)) {
+		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0202);
+	} else if (gadget_is_pxa (gadget)) {
+		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0203);
+		/* pxa doesn't support altsettings */
+		cdc = 0;
+	} else if (gadget_is_sh(gadget)) {
+		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0204);
+		/* sh doesn't support multiple interfaces or configs */
+		cdc = 0;
+		rndis = 0;
+	} else if (gadget_is_sa1100 (gadget)) {
+		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0205);
+		/* hardware can't write zlps */
+		zlp = 0;
+		/* sa1100 CAN do CDC, without status endpoint ... we use
+		 * non-CDC to be compatible with ARM Linux-2.4 "usb-eth".
+		 */
+		cdc = 0;
+	} else if (gadget_is_goku (gadget)) {
+		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0206);
+	} else if (gadget_is_mq11xx (gadget)) {
+		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0207);
+	} else if (gadget_is_omap (gadget)) {
+		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208);
+	} else {
+		/* can't assume CDC works.  don't want to default to
+		 * anything less functional on CDC-capable hardware,
+		 * so we fail in this case.
+		 */
+		printk (KERN_ERR "%s: controller '%s' not recognized\n",
+			shortname, gadget->name);
+		return -ENODEV;
+	}
+	snprintf (manufacturer, sizeof manufacturer,
+		UTS_SYSNAME " " UTS_RELEASE "/%s",
+		gadget->name);
+
+	/* If there's an RNDIS configuration, that's what Windows wants to
+	 * be using ... so use these product IDs here and in the "linux.inf"
+	 * needed to install MSFT drivers.  Current Linux kernels will use
+	 * the second configuration if it's CDC Ethernet, and need some help
+	 * to choose the right configuration otherwise.
+	 */
+	if (rndis) {
+		device_desc.idVendor =
+			__constant_cpu_to_le16(RNDIS_VENDOR_NUM);
+		device_desc.idProduct =
+			__constant_cpu_to_le16(RNDIS_PRODUCT_NUM);
+		snprintf (product_desc, sizeof product_desc,
+			"RNDIS/%s", driver_desc);
+
+	/* CDC subset ... recognized by Linux since 2.4.10, but Windows
+	 * drivers aren't widely available.
+	 */
+	} else if (!cdc) {
+		device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
+		device_desc.idVendor =
+			__constant_cpu_to_le16(SIMPLE_VENDOR_NUM);
+		device_desc.idProduct =
+			__constant_cpu_to_le16(SIMPLE_PRODUCT_NUM);
+	}
+
+
+	/* support optional vendor/distro customization */
+	if (idVendor) {
+		if (!idProduct) {
+			printk (KERN_ERR "%s: idVendor needs idProduct!\n",
+				shortname);
+			return -ENODEV;
+		}
+		device_desc.idVendor = cpu_to_le16(idVendor);
+		device_desc.idProduct = cpu_to_le16(idProduct);
+		if (bcdDevice)
+			device_desc.bcdDevice = cpu_to_le16(bcdDevice);
+	}
+	if (iManufacturer)
+		strncpy (manufacturer, iManufacturer, sizeof manufacturer);
+	if (iProduct)
+		strncpy (product_desc, iProduct, sizeof product_desc);
+
+	/* all we really need is bulk IN/OUT */
+	usb_ep_autoconfig_reset (gadget);
+	ep = usb_ep_autoconfig (gadget, &fs_source_desc);
+	if (!ep) {
+autoconf_fail:
+		printk (KERN_ERR "%s: can't autoconfigure on %s\n",
+			shortname, gadget->name);
 		return -ENODEV;
+	}
+	EP_IN_NAME = ep->name;
+	ep->driver_data = ep;	/* claim */
+	
+	ep = usb_ep_autoconfig (gadget, &fs_sink_desc);
+	if (!ep)
+		goto autoconf_fail;
+	EP_OUT_NAME = ep->name;
+	ep->driver_data = ep;	/* claim */
+
+#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+	/* CDC Ethernet control interface doesn't require a status endpoint.
+	 * Since some hosts expect one, try to allocate one anyway.
+	 */
+	if (cdc || rndis) {
+		ep = usb_ep_autoconfig (gadget, &fs_status_desc);
+		if (ep) {
+			EP_STATUS_NAME = ep->name;
+			ep->driver_data = ep;	/* claim */
+		} else if (rndis) {
+			printk (KERN_ERR "%s: can't run RNDIS on %s\n",
+				shortname, gadget->name);
+			return -ENODEV;
+		} else if (cdc) {
+			control_intf.bNumEndpoints = 0;
+			/* FIXME remove endpoint from descriptor list */
+		}
+	}
 #endif
 
-	device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
+	/* one config:  cdc, else minimal subset */
+	if (!cdc) {
+		eth_config.bNumInterfaces = 1;
+		eth_config.iConfiguration = STRING_SUBSET;
+		fs_subset_descriptors();
+		hs_subset_descriptors();
+	}
+
+	/* For now RNDIS is always a second config */
+	if (rndis)
+		device_desc.bNumConfigurations = 2;
+
 #ifdef	CONFIG_USB_GADGET_DUALSPEED
+	if (rndis)
+		dev_qualifier.bNumConfigurations = 2;
+	else if (!cdc)
+		dev_qualifier.bDeviceClass = USB_CLASS_VENDOR_SPEC;
+
 	/* assumes ep0 uses the same value for both speeds ... */
 	dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0;
+
+	/* and that all endpoints are dual-speed */
+	hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
+	hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
+#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+	if (EP_STATUS_NAME)
+		hs_status_desc.bEndpointAddress =
+				fs_status_desc.bEndpointAddress;
 #endif
+#endif	/* DUALSPEED */
 
-#ifdef	SELFPOWERED
-	eth_config.bmAttributes |= USB_CONFIG_ATT_SELFPOWERED;
+	device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
 	usb_gadget_set_selfpowered (gadget);
-#endif
 
  	net = alloc_etherdev (sizeof *dev);
  	if (!net)
@@ -1812,25 +2457,32 @@
 	dev->net = net;
 	SET_MODULE_OWNER (net);
 	strcpy (net->name, "usb%d");
+	dev->cdc = cdc;
+	dev->zlp = zlp;
 
-	/* one random address for the gadget device ... both of these could
-	 * reasonably come from an id prom or a module parameter.
+	/* Module params for these addresses should come from ID proms.
+	 * The host side address is used with CDC and RNDIS, and commonly
+	 * ends up in a persistent config database.
 	 */
-	get_random_bytes (net->dev_addr, ETH_ALEN);
-	net->dev_addr [0] &= 0xfe;	// clear multicast bit
-	net->dev_addr [0] |= 0x02;	// set local assignment bit (IEEE802)
-
+	get_ether_addr(dev_addr, net->dev_addr);
+	if (cdc || rndis) {
+		get_ether_addr(host_addr, dev->host_mac);
 #ifdef	DEV_CONFIG_CDC
-	/* ... another address for the host, on the other end of the
-	 * link, gets exported through CDC (see CDC spec table 41)
-	 */
-	get_random_bytes (node_id, sizeof node_id);
-	node_id [0] &= 0xfe;	// clear multicast bit
-	node_id [0] |= 0x02;    // set local assignment bit (IEEE802)
-	snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X",
-		node_id [0], node_id [1], node_id [2],
-		node_id [3], node_id [4], node_id [5]);
+		snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X",
+			dev->host_mac [0], dev->host_mac [1],
+			dev->host_mac [2], dev->host_mac [3],
+			dev->host_mac [4], dev->host_mac [5]);
 #endif
+	}
+
+	if (rndis) {
+		status = rndis_init();
+		if (status < 0) {
+			printk (KERN_ERR "%s: can't init RNDIS, %d\n",
+				shortname, status);
+			goto fail;
+		}
+	}
 
 	net->change_mtu = eth_change_mtu;
 	net->get_stats = eth_get_stats;
@@ -1867,16 +2519,60 @@
 
  	// SET_NETDEV_DEV (dev->net, &gadget->dev);
  	status = register_netdev (dev->net);
- 	if (status == 0) {
+	if (status < 0)
+		goto fail1;
 
-		INFO (dev, "%s, " CHIP ", version: " DRIVER_VERSION "\n",
-				driver_desc);
-#ifdef	DEV_CONFIG_CDC
-		INFO (dev, "CDC host enet %s\n", ethaddr);
-#endif
- 		return status;
+	INFO (dev, "%s, version: " DRIVER_VERSION "\n", driver_desc);
+	INFO (dev, "using %s, OUT %s IN %s%s%s\n", gadget->name,
+		EP_OUT_NAME, EP_IN_NAME,
+		EP_STATUS_NAME ? " STATUS " : "",
+		EP_STATUS_NAME ? EP_STATUS_NAME : ""
+		);
+	INFO (dev, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+		net->dev_addr [0], net->dev_addr [1],
+		net->dev_addr [2], net->dev_addr [3],
+		net->dev_addr [4], net->dev_addr [5]);
+
+	if (cdc || rndis)
+		INFO (dev, "HOST MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+			dev->host_mac [0], dev->host_mac [1],
+			dev->host_mac [2], dev->host_mac [3],
+			dev->host_mac [4], dev->host_mac [5]);
+
+#ifdef	CONFIG_USB_ETH_RNDIS
+	if (rndis) {
+		u32	vendorID = 0;
+
+		/* FIXME RNDIS vendor id == "vendor NIC code" == ? */
+		
+		dev->rndis_config = rndis_register (rndis_control_ack);
+		if (dev->rndis_config < 0) {
+fail0:
+			unregister_netdev (dev->net);
+			status = -ENODEV;
+			goto fail;
+		}
+		
+		/* these set up a lot of the OIDs that RNDIS needs */
+		rndis_set_host_mac (dev->rndis_config, dev->host_mac);
+		if (rndis_set_param_dev (dev->rndis_config, dev->net,
+					 &dev->stats))
+			goto fail0;
+		if (rndis_set_param_vendor (dev->rndis_config, vendorID,
+					    manufacturer))
+			goto fail0;
+		if (rndis_set_param_medium (dev->rndis_config,
+					    NDIS_MEDIUM_802_3,
+					    0))
+			goto fail0;
+		INFO (dev, "RNDIS ready\n");
 	}
-	pr_debug("%s: register_netdev failed, %d\n", shortname, status);
+#endif	
+
+	return status;
+
+fail1:
+	pr_debug ("%s: register_netdev failed, %d\n", shortname, status);
 fail:
 	eth_unbind (gadget);
 	return status;
@@ -1884,6 +2580,26 @@
 
 /*-------------------------------------------------------------------------*/
 
+static void
+eth_suspend (struct usb_gadget *gadget)
+{
+	struct eth_dev		*dev = get_gadget_data (gadget);
+
+	DEBUG (dev, "suspend\n");
+	dev->suspended = 1;
+}
+
+static void
+eth_resume (struct usb_gadget *gadget)
+{
+	struct eth_dev		*dev = get_gadget_data (gadget);
+
+	DEBUG (dev, "resume\n");
+	dev->suspended = 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
 static struct usb_gadget_driver eth_driver = {
 #ifdef CONFIG_USB_GADGET_DUALSPEED
 	.speed		= USB_SPEED_HIGH,
@@ -1897,6 +2613,9 @@
 	.setup		= eth_setup,
 	.disconnect	= eth_disconnect,
 
+	.suspend	= eth_suspend,
+	.resume		= eth_resume,
+
 	.driver 	= {
 		.name		= (char *) shortname,
 		// .shutdown = ...
@@ -1906,7 +2625,7 @@
 };
 
 MODULE_DESCRIPTION (DRIVER_DESC);
-MODULE_AUTHOR ("David Brownell");
+MODULE_AUTHOR ("David Brownell, Benedikt Spanger");
 MODULE_LICENSE ("GPL");
 
 
diff -urN linux-2.4.27-rc1/drivers/usb/gadget/file_storage.c linux-2.4.27-rc2/drivers/usb/gadget/file_storage.c
--- linux-2.4.27-rc1/drivers/usb/gadget/file_storage.c	2004-02-18 05:36:31.000000000 -0800
+++ linux-2.4.27-rc2/drivers/usb/gadget/file_storage.c	2004-06-25 20:48:26.867103145 -0700
@@ -1,7 +1,7 @@
 /*
  * file_storage.c -- File-backed USB Storage Gadget, for USB development
  *
- * Copyright (C) 2003 Alan Stern
+ * Copyright (C) 2003, 2004 Alan Stern
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -229,12 +229,14 @@
 #include <linux/usb_ch9.h>
 #include <linux/usb_gadget.h>
 
+#include "gadget_chips.h"
+
 
 /*-------------------------------------------------------------------------*/
 
 #define DRIVER_DESC		"File-backed Storage Gadget"
 #define DRIVER_NAME		"g_file_storage"
-#define DRIVER_VERSION		"14 January 2004"
+#define DRIVER_VERSION		"05 June 2004"
 
 static const char longname[] = DRIVER_DESC;
 static const char shortname[] = DRIVER_NAME;
@@ -251,165 +253,11 @@
 #define DRIVER_PRODUCT_ID	0xa4a5	// Linux-USB File-backed Storage Gadget
 
 
-/*-------------------------------------------------------------------------*/
-
-/*
- * Hardware-specific configuration, controlled by which device
- * controller driver was configured.
- *
- * CHIP ... hardware identifier
- * DRIVER_VERSION_NUM ... alerts the host side driver to differences
- * EP_*_NAME ... which endpoints do we use for which purpose?
- * EP_*_NUM ... numbers for them (often limited by hardware)
- * FS_BULK_IN_MAXPACKET ... maxpacket value for full-speed bulk-in ep
- * FS_BULK_OUT_MAXPACKET ... maxpacket value for full-speed bulk-out ep
- * HIGHSPEED ... define if ep0 and descriptors need high speed support
- * MAX_USB_POWER ... define if we use other than 100 mA bus current
- * SELFPOWER ... if we can run on bus power, zero
- * NO_BULK_STALL ... bulk endpoint halts don't work well so avoid them
- */
-
-
-/*
- * NetChip 2280, PCI based.
- *
- * This has half a dozen configurable endpoints, four with dedicated
- * DMA channels to manage their FIFOs.  It supports high speed.
- * Those endpoints can be arranged in any desired configuration.
- */
-#ifdef	CONFIG_USB_GADGET_NET2280
-#define CHIP			"net2280"
-#define DRIVER_VERSION_NUM	0x0211
-static const char EP_BULK_IN_NAME[] = "ep-a";
-#define EP_BULK_IN_NUM		1
-#define FS_BULK_IN_MAXPACKET	64
-static const char EP_BULK_OUT_NAME[] = "ep-b";
-#define EP_BULK_OUT_NUM		2
-#define FS_BULK_OUT_MAXPACKET	64
-static const char EP_INTR_IN_NAME[] = "ep-e";
-#define EP_INTR_IN_NUM		5
-#define HIGHSPEED
-#endif
-
-
-/*
- * Dummy_hcd, software-based loopback controller.
- *
- * This imitates the abilities of the NetChip 2280, so we will use
- * the same configuration.
- */
-#ifdef	CONFIG_USB_GADGET_DUMMY
-#define CHIP			"dummy"
-#define DRIVER_VERSION_NUM	0x0212
-static const char EP_BULK_IN_NAME[] = "ep-a";
-#define EP_BULK_IN_NUM		1
-#define FS_BULK_IN_MAXPACKET	64
-static const char EP_BULK_OUT_NAME[] = "ep-b";
-#define EP_BULK_OUT_NUM		2
-#define FS_BULK_OUT_MAXPACKET	64
-static const char EP_INTR_IN_NAME[] = "ep-e";
-#define EP_INTR_IN_NUM		5
-#define HIGHSPEED
-#endif
-
-
-/*
- * PXA-2xx UDC:  widely used in second gen Linux-capable PDAs.
- *
- * This has fifteen fixed-function full speed endpoints, and it
- * can support all USB transfer types.
- *
- * These supports three or four configurations, with fixed numbers.
- * The hardware interprets SET_INTERFACE, net effect is that you
- * can't use altsettings or reset the interfaces independently.
- * So stick to a single interface.
- */
-#ifdef	CONFIG_USB_GADGET_PXA2XX
-#define CHIP			"pxa2xx"
-#define DRIVER_VERSION_NUM	0x0213
-static const char EP_BULK_IN_NAME[] = "ep1in-bulk";
-#define EP_BULK_IN_NUM		1
-#define FS_BULK_IN_MAXPACKET	64
-static const char EP_BULK_OUT_NAME[] = "ep2out-bulk";
-#define EP_BULK_OUT_NUM		2
-#define FS_BULK_OUT_MAXPACKET	64
-static const char EP_INTR_IN_NAME[] = "ep6in-bulk";
-#define EP_INTR_IN_NUM		6
-#endif
-
-
 /*
- * SuperH UDC:  UDC built-in to some Renesas SH processors.
- *
- * This has three fixed-function full speed bulk/interrupt endpoints.
- *
- * Only one configuration and interface is supported (SET_CONFIGURATION
- * and SET_INTERFACE are handled completely by the hardware).
- */
-#ifdef	CONFIG_USB_GADGET_SUPERH
-#define CHIP			"superh"
-#define DRIVER_VERSION_NUM	0x0215
-static const char EP_BULK_IN_NAME[] = "ep2in-bulk";
-#define EP_BULK_IN_NUM		2
-#define FS_BULK_IN_MAXPACKET	64
-static const char EP_BULK_OUT_NAME[] = "ep1out-bulk";
-#define EP_BULK_OUT_NUM		1
-#define FS_BULK_OUT_MAXPACKET	64
-static const char EP_INTR_IN_NAME[] = "ep3in-bulk";
-#define EP_INTR_IN_NUM		3
-#define NO_BULK_STALL
-#endif
-
-
-/*
- * Toshiba TC86C001 ("Goku-S") UDC
- *
- * This has three semi-configurable full speed bulk/interrupt endpoints.
- */
-#ifdef	CONFIG_USB_GADGET_GOKU
-#define CHIP			"goku"
-#define DRIVER_VERSION_NUM	0x0216
-static const char EP_BULK_OUT_NAME [] = "ep1-bulk";
-#define EP_BULK_OUT_NUM		1
-#define FS_BULK_IN_MAXPACKET	64
-static const char EP_BULK_IN_NAME [] = "ep2-bulk";
-#define EP_BULK_IN_NUM		2
-#define FS_BULK_OUT_MAXPACKET	64
-static const char EP_INTR_IN_NAME [] = "ep3-bulk";
-#define EP_INTR_IN_NUM		3
-#endif
-
-
-/*-------------------------------------------------------------------------*/
-
-#ifndef CHIP
-#	error Configure some USB peripheral controller driver!
-#endif
-
-/* Power usage is config specific.
- * Hardware that supports remote wakeup defaults to disabling it.
+ * This driver assumes self-powered hardware and has no way for users to
+ * trigger remote wakeup.  It uses autoconfiguration to select endpoints
+ * and endpoint addresses.
  */
-#ifndef	SELFPOWER
-/* default: say we're self-powered */
-#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
-/* else:
- * - SELFPOWER value must be zero
- * - MAX_USB_POWER may be nonzero.
- */
-#endif
-
-#ifndef	MAX_USB_POWER
-/* Any hub supports this steady state bus power consumption */
-#define MAX_USB_POWER	100	/* mA */
-#endif
-
-/* We don't support remote wake-up */
-
-#ifdef NO_BULK_STALL
-#define CAN_STALL	0
-#else
-#define CAN_STALL	1
-#endif
 
 
 /*-------------------------------------------------------------------------*/
@@ -478,14 +326,15 @@
 static char		*file[MAX_LUNS] = {NULL, };
 static int		ro[MAX_LUNS] = {0, };
 static unsigned int	luns = 0;
+						// Default values
 static char 		*transport = "BBB";
 static char 		*protocol = "SCSI";
 static int 		removable = 0;
 static unsigned short	vendor = DRIVER_VENDOR_ID;
 static unsigned short	product = DRIVER_PRODUCT_ID;
-static unsigned short	release = DRIVER_VERSION_NUM;
+static unsigned short	release = 0xffff;	// Use controller chip type
 static unsigned int	buflen = 16384;
-static int		stall = CAN_STALL;
+static int		stall = 1;
 
 static struct {
 	unsigned int	nluns;
@@ -990,7 +839,7 @@
 	/* The next three values can be overridden by module parameters */
 	.idVendor =		__constant_cpu_to_le16(DRIVER_VENDOR_ID),
 	.idProduct =		__constant_cpu_to_le16(DRIVER_PRODUCT_ID),
-	.bcdDevice =		__constant_cpu_to_le16(DRIVER_VERSION_NUM),
+	.bcdDevice =		__constant_cpu_to_le16(0xffff),
 
 	.iManufacturer =	STRING_MANUFACTURER,
 	.iProduct =		STRING_PRODUCT,
@@ -1003,11 +852,11 @@
 	.bLength =		sizeof config_desc,
 	.bDescriptorType =	USB_DT_CONFIG,
 
-	/* wTotalLength adjusted during bind() */
+	/* wTotalLength computed by usb_gadget_config_buf() */
 	.bNumInterfaces =	1,
 	.bConfigurationValue =	CONFIG_VALUE,
-	.bmAttributes =		USB_CONFIG_ATT_ONE | SELFPOWER,
-	.bMaxPower =		(MAX_USB_POWER + 1) / 2,
+	.bmAttributes =		USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+	.bMaxPower =		1,	// self-powered
 };
 
 /* There is only one interface. */
@@ -1017,47 +866,56 @@
 	.bLength =		sizeof intf_desc,
 	.bDescriptorType =	USB_DT_INTERFACE,
 
-	.bNumEndpoints =	2,		// Adjusted during bind()
+	.bNumEndpoints =	2,		// Adjusted during fsg_bind()
 	.bInterfaceClass =	USB_CLASS_MASS_STORAGE,
-	.bInterfaceSubClass =	USB_SC_SCSI,	// Adjusted during bind()
-	.bInterfaceProtocol =	USB_PR_BULK,	// Adjusted during bind()
+	.bInterfaceSubClass =	USB_SC_SCSI,	// Adjusted during fsg_bind()
+	.bInterfaceProtocol =	USB_PR_BULK,	// Adjusted during fsg_bind()
 };
 
 /* Three full-speed endpoint descriptors: bulk-in, bulk-out,
  * and interrupt-in. */
 
-static const struct usb_endpoint_descriptor
+static struct usb_endpoint_descriptor
 fs_bulk_in_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
-	.bEndpointAddress =	EP_BULK_IN_NUM | USB_DIR_IN,
+	.bEndpointAddress =	USB_DIR_IN,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
-	.wMaxPacketSize =	__constant_cpu_to_le16(FS_BULK_IN_MAXPACKET),
+	/* wMaxPacketSize set by autoconfiguration */
 };
 
-static const struct usb_endpoint_descriptor
+static struct usb_endpoint_descriptor
 fs_bulk_out_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
-	.bEndpointAddress =	EP_BULK_OUT_NUM,
+	.bEndpointAddress =	USB_DIR_OUT,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
-	.wMaxPacketSize =	__constant_cpu_to_le16(FS_BULK_OUT_MAXPACKET),
+	/* wMaxPacketSize set by autoconfiguration */
 };
 
-static const struct usb_endpoint_descriptor
+static struct usb_endpoint_descriptor
 fs_intr_in_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
-	.bEndpointAddress =	EP_INTR_IN_NUM | USB_DIR_IN,
+	.bEndpointAddress =	USB_DIR_IN,
 	.bmAttributes =		USB_ENDPOINT_XFER_INT,
 	.wMaxPacketSize =	__constant_cpu_to_le16(2),
 	.bInterval =		32,	// frames -> 32 ms
 };
 
-#ifdef	HIGHSPEED
+static const struct usb_descriptor_header *fs_function[] = {
+	(struct usb_descriptor_header *) &intf_desc,
+	(struct usb_descriptor_header *) &fs_bulk_in_desc,
+	(struct usb_descriptor_header *) &fs_bulk_out_desc,
+	(struct usb_descriptor_header *) &fs_intr_in_desc,
+	NULL,
+};
+
+
+#ifdef	CONFIG_USB_GADGET_DUALSPEED
 
 /*
  * USB 2.0 devices need to expose both high speed and full speed
@@ -1067,47 +925,55 @@
  * and a "device qualifier" ... plus more construction options
  * for the config descriptor.
  */
-static const struct usb_endpoint_descriptor
+static struct usb_qualifier_descriptor
+dev_qualifier = {
+	.bLength =		sizeof dev_qualifier,
+	.bDescriptorType =	USB_DT_DEVICE_QUALIFIER,
+
+	.bcdUSB =		__constant_cpu_to_le16(0x0200),
+	.bDeviceClass =		USB_CLASS_PER_INTERFACE,
+
+	.bNumConfigurations =	1,
+};
+
+static struct usb_endpoint_descriptor
 hs_bulk_in_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
-	.bEndpointAddress =	EP_BULK_IN_NUM | USB_DIR_IN,
+	/* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 	.wMaxPacketSize =	__constant_cpu_to_le16(512),
 };
 
-static const struct usb_endpoint_descriptor
+static struct usb_endpoint_descriptor
 hs_bulk_out_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
-	.bEndpointAddress =	EP_BULK_OUT_NUM,
+	/* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 	.wMaxPacketSize =	__constant_cpu_to_le16(512),
 	.bInterval =		1,	// NAK every 1 uframe
 };
 
-static const struct usb_endpoint_descriptor
+static struct usb_endpoint_descriptor
 hs_intr_in_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
-	.bEndpointAddress =	EP_INTR_IN_NUM | USB_DIR_IN,
+	/* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */
 	.bmAttributes =		USB_ENDPOINT_XFER_INT,
 	.wMaxPacketSize =	__constant_cpu_to_le16(2),
 	.bInterval =		9,	// 2**(9-1) = 256 uframes -> 32 ms
 };
 
-static struct usb_qualifier_descriptor
-dev_qualifier = {
-	.bLength =		sizeof dev_qualifier,
-	.bDescriptorType =	USB_DT_DEVICE_QUALIFIER,
-
-	.bcdUSB =		__constant_cpu_to_le16(0x0200),
-	.bDeviceClass =		USB_CLASS_PER_INTERFACE,
-
-	.bNumConfigurations =	1,
+static const struct usb_descriptor_header *hs_function[] = {
+	(struct usb_descriptor_header *) &intf_desc,
+	(struct usb_descriptor_header *) &hs_bulk_in_desc,
+	(struct usb_descriptor_header *) &hs_bulk_out_desc,
+	(struct usb_descriptor_header *) &hs_intr_in_desc,
+	NULL,
 };
 
 /* Maxpacket and other transfer characteristics vary by speed. */
@@ -1115,22 +981,23 @@
 
 #else
 
-/* If there's no high speed support, maxpacket doesn't change. */
+/* If there's no high speed support, always use the full-speed descriptor. */
 #define ep_desc(g,fs,hs)	fs
 
-#endif	/* !HIGHSPEED */
+#endif	/* !CONFIG_USB_GADGET_DUALSPEED */
 
 
 /* The CBI specification limits the serial string to 12 uppercase hexadecimal
  * characters. */
+static char				manufacturer[40];
 static char				serial[13];
 
-/* Static strings, in ISO 8859/1 */
+/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */
 static struct usb_string		strings[] = {
-	{ STRING_MANUFACTURER, UTS_SYSNAME " " UTS_RELEASE " with " CHIP, },
-	{ STRING_PRODUCT, longname, },
-	{ STRING_SERIAL, serial, },
-	{ }			// end of list
+	{STRING_MANUFACTURER,	manufacturer},
+	{STRING_PRODUCT,	longname},
+	{STRING_SERIAL,		serial},
+	{}
 };
 
 static struct usb_gadget_strings	stringtab = {
@@ -1140,61 +1007,33 @@
 
 
 /*
- * Config descriptors are handcrafted.  They must agree with the code
- * that sets configurations and with code managing interfaces and their
- * altsettings.  They must also handle different speeds and other-speed
- * requests.
+ * Config descriptors must agree with the code that sets configurations
+ * and with code managing interfaces and their altsettings.  They must
+ * also handle different speeds and other-speed requests.
  */
 static int populate_config_buf(enum usb_device_speed speed,
-		u8 *buf0, u8 type, unsigned index)
+		u8 *buf, u8 type, unsigned index)
 {
-	u8	*buf = buf0;
-#ifdef HIGHSPEED
-	int	hs;
-#endif
+	int					len;
+	const struct usb_descriptor_header	**function;
 
 	if (index > 0)
 		return -EINVAL;
-	if (config_desc.wTotalLength  > EP0_BUFSIZE)
-		return -EDOM;
 
-	/* Config (or other speed config) */
-	memcpy(buf, &config_desc, USB_DT_CONFIG_SIZE);
-	buf[1] = type;
-	buf += USB_DT_CONFIG_SIZE;
-
-	/* Interface */
-	memcpy(buf, &intf_desc, USB_DT_INTERFACE_SIZE);
-	buf += USB_DT_INTERFACE_SIZE;
-
-	/* The endpoints in the interface (at that speed) */
-#ifdef HIGHSPEED
-	hs = (speed == USB_SPEED_HIGH);
+#ifdef CONFIG_USB_GADGET_DUALSPEED
 	if (type == USB_DT_OTHER_SPEED_CONFIG)
-		hs = !hs;
-	if (hs) {
-		memcpy(buf, &hs_bulk_in_desc, USB_DT_ENDPOINT_SIZE);
-		buf += USB_DT_ENDPOINT_SIZE;
-		memcpy(buf, &hs_bulk_out_desc, USB_DT_ENDPOINT_SIZE);
-		buf += USB_DT_ENDPOINT_SIZE;
-		if (transport_is_cbi()) {
-			memcpy(buf, &hs_intr_in_desc, USB_DT_ENDPOINT_SIZE);
-			buf += USB_DT_ENDPOINT_SIZE;
-		}
-	} else
+		speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed;
+	if (speed == USB_SPEED_HIGH)
+		function = hs_function;
+	else
 #endif
-	{
-		memcpy(buf, &fs_bulk_in_desc, USB_DT_ENDPOINT_SIZE);
-		buf += USB_DT_ENDPOINT_SIZE;
-		memcpy(buf, &fs_bulk_out_desc, USB_DT_ENDPOINT_SIZE);
-		buf += USB_DT_ENDPOINT_SIZE;
-		if (transport_is_cbi()) {
-			memcpy(buf, &fs_intr_in_desc, USB_DT_ENDPOINT_SIZE);
-			buf += USB_DT_ENDPOINT_SIZE;
-		}
-	}
+		function = fs_function;
 
-	return buf - buf0;
+	len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function);
+	if (len < 0)
+		return len;
+	((struct usb_config_descriptor *) buf)->bDescriptorType = type;
+	return len;
 }
 
 
@@ -1493,22 +1332,26 @@
 			value = min(ctrl->wLength, (u16) sizeof device_desc);
 			memcpy(req->buf, &device_desc, value);
 			break;
-#ifdef HIGHSPEED
+#ifdef CONFIG_USB_GADGET_DUALSPEED
 		case USB_DT_DEVICE_QUALIFIER:
 			VDBG(fsg, "get device qualifier\n");
+			if (!fsg->gadget->is_dualspeed)
+				break;
 			value = min(ctrl->wLength, (u16) sizeof dev_qualifier);
 			memcpy(req->buf, &dev_qualifier, value);
 			break;
 
 		case USB_DT_OTHER_SPEED_CONFIG:
 			VDBG(fsg, "get other-speed config descriptor\n");
+			if (!fsg->gadget->is_dualspeed)
+				break;
 			goto get_config;
-#endif /* HIGHSPEED */
+#endif
 		case USB_DT_CONFIG:
 			VDBG(fsg, "get configuration descriptor\n");
-#ifdef HIGHSPEED
+#ifdef CONFIG_USB_GADGET_DUALSPEED
 		get_config:
-#endif /* HIGHSPEED */
+#endif
 			value = populate_config_buf(fsg->gadget->speed,
 					req->buf,
 					ctrl->wValue >> 8,
@@ -2148,7 +1991,7 @@
 	buf[4] = 31;		// Additional length
 				// No special options
 	sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, product_id,
-			DRIVER_VERSION_NUM);
+			mod_data.release);
 	return 36;
 }
 
@@ -2196,7 +2039,7 @@
 	buf[0] = 0x80 | 0x70;			// Valid, current error
 	buf[2] = SK(sd);
 	put_be32(&buf[3], sdinfo);		// Sense information
-	buf[7] = 18 - 7;			// Additional sense length
+	buf[7] = 18 - 8;			// Additional sense length
 	buf[12] = ASC(sd);
 	buf[13] = ASCQ(sd);
 	return 18;
@@ -2643,7 +2486,7 @@
 		/* Store and send the Bulk-only CSW */
 		csw->Signature = __constant_cpu_to_le32(USB_BULK_CS_SIG);
 		csw->Tag = fsg->tag;
-		csw->Residue = fsg->residue;
+		csw->Residue = cpu_to_le32(fsg->residue);
 		csw->Status = status;
 
 		bh->inreq->length = USB_BULK_CS_WRAP_LEN;
@@ -3089,7 +2932,7 @@
 		fsg->data_dir = DATA_DIR_TO_HOST;
 	else
 		fsg->data_dir = DATA_DIR_FROM_HOST;
-	fsg->data_size = cbw->DataTransferLength;
+	fsg->data_size = le32_to_cpu(cbw->DataTransferLength);
 	if (fsg->data_size == 0)
 		fsg->data_dir = DATA_DIR_NONE;
 	fsg->lun = cbw->Lun;
@@ -3242,6 +3085,7 @@
 	if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0)
 		goto reset;
 	fsg->bulk_out_enabled = 1;
+	fsg->bulk_out_maxpacket = d->wMaxPacketSize;
 
 	if (transport_is_cbi()) {
 		d = ep_desc(fsg->gadget, &fs_intr_in_desc, &hs_intr_in_desc);
@@ -3717,6 +3561,34 @@
 	mod_data.protocol_type = USB_SC_SCSI;
 	mod_data.protocol_name = "Transparent SCSI";
 
+	if (gadget_is_sh(fsg->gadget))
+		mod_data.can_stall = 0;
+
+	if (mod_data.release == 0xffff) {	// Parameter wasn't set
+		if (gadget_is_net2280(fsg->gadget))
+			mod_data.release = __constant_cpu_to_le16(0x0221);
+		else if (gadget_is_dummy(fsg->gadget))
+			mod_data.release = __constant_cpu_to_le16(0x0222);
+		else if (gadget_is_pxa(fsg->gadget))
+			mod_data.release = __constant_cpu_to_le16(0x0223);
+		else if (gadget_is_sh(fsg->gadget))
+			mod_data.release = __constant_cpu_to_le16(0x0224);
+
+		/* The sa1100 controller is not supported */
+
+		else if (gadget_is_goku(fsg->gadget))
+			mod_data.release = __constant_cpu_to_le16(0x0226);
+		else if (gadget_is_mq11xx(fsg->gadget))
+			mod_data.release = __constant_cpu_to_le16(0x0227);
+		else if (gadget_is_omap(fsg->gadget))
+			mod_data.release = __constant_cpu_to_le16(0x0228);
+		else {
+			WARN(fsg, "controller '%s' not recognized\n",
+				fsg->gadget->name);
+			mod_data.release = __constant_cpu_to_le16(0x0299);
+		}
+	}
+
 	prot = simple_strtol(mod_data.protocol_parm, NULL, 0);
 
 #ifdef CONFIG_USB_FILE_STORAGE_TEST
@@ -3729,7 +3601,7 @@
 		mod_data.transport_type = USB_PR_CBI;
 		mod_data.transport_name = "Control-Bulk-Interrupt";
 	} else {
-		INFO(fsg, "invalid transport: %s\n", mod_data.transport_parm);
+		ERROR(fsg, "invalid transport: %s\n", mod_data.transport_parm);
 		return -EINVAL;
 	}
 
@@ -3758,13 +3630,13 @@
 		mod_data.protocol_type = USB_SC_8070;
 		mod_data.protocol_name = "8070i";
 	} else {
-		INFO(fsg, "invalid protocol: %s\n", mod_data.protocol_parm);
+		ERROR(fsg, "invalid protocol: %s\n", mod_data.protocol_parm);
 		return -EINVAL;
 	}
 
 	mod_data.buflen &= PAGE_CACHE_MASK;
 	if (mod_data.buflen <= 0) {
-		INFO(fsg, "invalid buflen\n");
+		ERROR(fsg, "invalid buflen\n");
 		return -ETOOSMALL;
 	}
 #endif /* CONFIG_USB_FILE_STORAGE_TEST */
@@ -3800,7 +3672,7 @@
 		}
 	}
 	if (i > MAX_LUNS) {
-		INFO(fsg, "invalid number of LUNs: %d\n", i);
+		ERROR(fsg, "invalid number of LUNs: %d\n", i);
 		rc = -EINVAL;
 		goto out;
 	}
@@ -3827,45 +3699,57 @@
 			if ((rc = open_backing_file(curlun, file[i])) != 0)
 				goto out;
 		} else if (!mod_data.removable) {
-			INFO(fsg, "no file given for LUN%d\n", i);
+			ERROR(fsg, "no file given for LUN%d\n", i);
 			rc = -EINVAL;
 			goto out;
 		}
 	}
 
+	/* Find all the endpoints we will use */
+	usb_ep_autoconfig_reset(gadget);
+	ep = usb_ep_autoconfig(gadget, &fs_bulk_in_desc);
+	if (!ep)
+		goto autoconf_fail;
+	ep->driver_data = fsg;		// claim the endpoint
+	fsg->bulk_in = ep;
+
+	ep = usb_ep_autoconfig(gadget, &fs_bulk_out_desc);
+	if (!ep)
+		goto autoconf_fail;
+	ep->driver_data = fsg;		// claim the endpoint
+	fsg->bulk_out = ep;
+
+	if (transport_is_cbi()) {
+		ep = usb_ep_autoconfig(gadget, &fs_intr_in_desc);
+		if (!ep)
+			goto autoconf_fail;
+		ep->driver_data = fsg;		// claim the endpoint
+		fsg->intr_in = ep;
+	}
+
 	/* Fix up the descriptors */
 	device_desc.bMaxPacketSize0 = fsg->ep0->maxpacket;
-#ifdef HIGHSPEED
-	dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket;		// ???
-#endif
 	device_desc.idVendor = cpu_to_le16(mod_data.vendor);
 	device_desc.idProduct = cpu_to_le16(mod_data.product);
 	device_desc.bcdDevice = cpu_to_le16(mod_data.release);
 
 	i = (transport_is_cbi() ? 3 : 2);	// Number of endpoints
-	config_desc.wTotalLength = USB_DT_CONFIG_SIZE + USB_DT_INTERFACE_SIZE
-			+ USB_DT_ENDPOINT_SIZE * i;
 	intf_desc.bNumEndpoints = i;
 	intf_desc.bInterfaceSubClass = mod_data.protocol_type;
 	intf_desc.bInterfaceProtocol = mod_data.transport_type;
+	fs_function[i+1] = NULL;
 
-	/* Find all the endpoints we will use */
-	gadget_for_each_ep(ep, gadget) {
-		if (strcmp(ep->name, EP_BULK_IN_NAME) == 0)
-			fsg->bulk_in = ep;
-		else if (strcmp(ep->name, EP_BULK_OUT_NAME) == 0)
-			fsg->bulk_out = ep;
-		else if (strcmp(ep->name, EP_INTR_IN_NAME) == 0)
-			fsg->intr_in = ep;
-	}
-	if (!fsg->bulk_in || !fsg->bulk_out ||
-			(transport_is_cbi() && !fsg->intr_in)) {
-		DBG(fsg, "unable to find all endpoints\n");
-		rc = -ENOTSUPP;
-		goto out;
-	}
-	fsg->bulk_out_maxpacket = (gadget->speed == USB_SPEED_HIGH ? 512 :
-			FS_BULK_OUT_MAXPACKET);
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+	hs_function[i+1] = NULL;
+
+	/* Assume ep0 uses the same maxpacket value for both speeds */
+	dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket;
+
+	/* Assume that all endpoint addresses are the same for both speeds */
+	hs_bulk_in_desc.bEndpointAddress = fs_bulk_in_desc.bEndpointAddress;
+	hs_bulk_out_desc.bEndpointAddress = fs_bulk_out_desc.bEndpointAddress;
+	hs_intr_in_desc.bEndpointAddress = fs_intr_in_desc.bEndpointAddress;
+#endif
 
 	rc = -ENOMEM;
 
@@ -3894,6 +3778,10 @@
 	/* This should reflect the actual gadget power source */
 	usb_gadget_set_selfpowered(gadget);
 
+	snprintf(manufacturer, sizeof manufacturer,
+			UTS_SYSNAME " " UTS_RELEASE " with %s",
+			gadget->name);
+
 	/* On a real device, serial[] would be loaded from permanent
 	 * storage.  We just encode it from the driver version string. */
 	for (i = 0; i < sizeof(serial) - 2; i += 2) {
@@ -3942,6 +3830,10 @@
 	DBG(fsg, "I/O thread pid: %d\n", fsg->thread_pid);
 	return 0;
 
+autoconf_fail:
+	ERROR(fsg, "unable to autoconfigure all endpoints\n");
+	rc = -ENOTSUPP;
+
 out:
 	fsg->state = FSG_STATE_TERMINATED;	// The thread is dead
 	fsg_unbind(gadget);
@@ -3953,7 +3845,7 @@
 /*-------------------------------------------------------------------------*/
 
 static struct usb_gadget_driver		fsg_driver = {
-#ifdef HIGHSPEED
+#ifdef CONFIG_USB_GADGET_DUALSPEED
 	.speed		= USB_SPEED_HIGH,
 #else
 	.speed		= USB_SPEED_FULL,
diff -urN linux-2.4.27-rc1/drivers/usb/gadget/gadget_chips.h linux-2.4.27-rc2/drivers/usb/gadget/gadget_chips.h
--- linux-2.4.27-rc1/drivers/usb/gadget/gadget_chips.h	2004-04-14 06:05:32.000000000 -0700
+++ linux-2.4.27-rc2/drivers/usb/gadget/gadget_chips.h	2004-06-25 20:48:26.867103145 -0700
@@ -14,7 +14,13 @@
 #define	gadget_is_net2280(g)	0
 #endif
 
-#ifdef CONFIG_USB_GADGET_PXA
+#ifdef CONFIG_USB_GADGET_DUMMY_HCD
+#define	gadget_is_dummy(g)	!strcmp("dummy_udc", (g)->name)
+#else
+#define	gadget_is_dummy(g)	0
+#endif
+
+#ifdef CONFIG_USB_GADGET_PXA2XX
 #define	gadget_is_pxa(g)	!strcmp("pxa2xx_udc", (g)->name)
 #else
 #define	gadget_is_pxa(g)	0
diff -urN linux-2.4.27-rc1/drivers/usb/gadget/ndis.h linux-2.4.27-rc2/drivers/usb/gadget/ndis.h
--- linux-2.4.27-rc1/drivers/usb/gadget/ndis.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.27-rc2/drivers/usb/gadget/ndis.h	2004-06-25 20:48:26.868103187 -0700
@@ -0,0 +1,217 @@
+/*
+ * ndis.h 
+ * 
+ * ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de>
+ * 
+ * Thanks to the cygwin development team, 
+ * espacially to Casper S. Hornstrup <chorns@users.sourceforge.net>
+ * 
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef _LINUX_NDIS_H
+#define _LINUX_NDIS_H
+
+
+#define NDIS_STATUS_MULTICAST_FULL	  0xC0010009
+#define NDIS_STATUS_MULTICAST_EXISTS      0xC001000A
+#define NDIS_STATUS_MULTICAST_NOT_FOUND   0xC001000B
+
+enum NDIS_DEVICE_POWER_STATE {
+	NdisDeviceStateUnspecified = 0,
+	NdisDeviceStateD0,
+	NdisDeviceStateD1,
+	NdisDeviceStateD2,
+	NdisDeviceStateD3,
+	NdisDeviceStateMaximum
+};
+
+struct NDIS_PM_WAKE_UP_CAPABILITIES {
+	enum NDIS_DEVICE_POWER_STATE  MinMagicPacketWakeUp;
+	enum NDIS_DEVICE_POWER_STATE  MinPatternWakeUp;
+	enum NDIS_DEVICE_POWER_STATE  MinLinkChangeWakeUp;
+};
+
+/* NDIS_PNP_CAPABILITIES.Flags constants */
+#define NDIS_DEVICE_WAKE_UP_ENABLE                0x00000001
+#define NDIS_DEVICE_WAKE_ON_PATTERN_MATCH_ENABLE  0x00000002
+#define NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE   0x00000004
+
+struct NDIS_PNP_CAPABILITIES {
+	u32					Flags;
+	struct NDIS_PM_WAKE_UP_CAPABILITIES	WakeUpCapabilities;
+};
+
+struct NDIS_PM_PACKET_PATTERN {
+	u32	Priority;
+	u32	Reserved;
+	u32	MaskSize;
+	u32	PatternOffset;
+	u32	PatternSize;
+	u32	PatternFlags;
+};
+
+
+/* Required Object IDs (OIDs) */
+#define OID_GEN_SUPPORTED_LIST            0x00010101
+#define OID_GEN_HARDWARE_STATUS           0x00010102
+#define OID_GEN_MEDIA_SUPPORTED           0x00010103
+#define OID_GEN_MEDIA_IN_USE              0x00010104
+#define OID_GEN_MAXIMUM_LOOKAHEAD         0x00010105
+#define OID_GEN_MAXIMUM_FRAME_SIZE        0x00010106
+#define OID_GEN_LINK_SPEED                0x00010107
+#define OID_GEN_TRANSMIT_BUFFER_SPACE     0x00010108
+#define OID_GEN_RECEIVE_BUFFER_SPACE      0x00010109
+#define OID_GEN_TRANSMIT_BLOCK_SIZE       0x0001010A
+#define OID_GEN_RECEIVE_BLOCK_SIZE        0x0001010B
+#define OID_GEN_VENDOR_ID                 0x0001010C
+#define OID_GEN_VENDOR_DESCRIPTION        0x0001010D
+#define OID_GEN_CURRENT_PACKET_FILTER     0x0001010E
+#define OID_GEN_CURRENT_LOOKAHEAD         0x0001010F
+#define OID_GEN_DRIVER_VERSION            0x00010110
+#define OID_GEN_MAXIMUM_TOTAL_SIZE        0x00010111
+#define OID_GEN_PROTOCOL_OPTIONS          0x00010112
+#define OID_GEN_MAC_OPTIONS               0x00010113
+#define OID_GEN_MEDIA_CONNECT_STATUS      0x00010114
+#define OID_GEN_MAXIMUM_SEND_PACKETS      0x00010115
+#define OID_GEN_VENDOR_DRIVER_VERSION     0x00010116
+#define OID_GEN_SUPPORTED_GUIDS           0x00010117
+#define OID_GEN_NETWORK_LAYER_ADDRESSES   0x00010118
+#define OID_GEN_TRANSPORT_HEADER_OFFSET   0x00010119
+#define OID_GEN_MACHINE_NAME              0x0001021A
+#define OID_GEN_RNDIS_CONFIG_PARAMETER    0x0001021B
+#define OID_GEN_VLAN_ID                   0x0001021C
+
+/* Optional OIDs */
+#define OID_GEN_MEDIA_CAPABILITIES        0x00010201
+#define OID_GEN_PHYSICAL_MEDIUM           0x00010202
+
+/* Required statistics OIDs */
+#define OID_GEN_XMIT_OK                   0x00020101
+#define OID_GEN_RCV_OK                    0x00020102
+#define OID_GEN_XMIT_ERROR                0x00020103
+#define OID_GEN_RCV_ERROR                 0x00020104
+#define OID_GEN_RCV_NO_BUFFER             0x00020105
+
+/* Optional statistics OIDs */
+#define OID_GEN_DIRECTED_BYTES_XMIT       0x00020201
+#define OID_GEN_DIRECTED_FRAMES_XMIT      0x00020202
+#define OID_GEN_MULTICAST_BYTES_XMIT      0x00020203
+#define OID_GEN_MULTICAST_FRAMES_XMIT     0x00020204
+#define OID_GEN_BROADCAST_BYTES_XMIT      0x00020205
+#define OID_GEN_BROADCAST_FRAMES_XMIT     0x00020206
+#define OID_GEN_DIRECTED_BYTES_RCV        0x00020207
+#define OID_GEN_DIRECTED_FRAMES_RCV       0x00020208
+#define OID_GEN_MULTICAST_BYTES_RCV       0x00020209
+#define OID_GEN_MULTICAST_FRAMES_RCV      0x0002020A
+#define OID_GEN_BROADCAST_BYTES_RCV       0x0002020B
+#define OID_GEN_BROADCAST_FRAMES_RCV      0x0002020C
+#define OID_GEN_RCV_CRC_ERROR             0x0002020D
+#define OID_GEN_TRANSMIT_QUEUE_LENGTH     0x0002020E
+#define OID_GEN_GET_TIME_CAPS             0x0002020F
+#define OID_GEN_GET_NETCARD_TIME          0x00020210
+#define OID_GEN_NETCARD_LOAD              0x00020211
+#define OID_GEN_DEVICE_PROFILE            0x00020212
+#define OID_GEN_INIT_TIME_MS              0x00020213
+#define OID_GEN_RESET_COUNTS              0x00020214
+#define OID_GEN_MEDIA_SENSE_COUNTS        0x00020215
+#define OID_GEN_FRIENDLY_NAME             0x00020216
+#define OID_GEN_MINIPORT_INFO             0x00020217
+#define OID_GEN_RESET_VERIFY_PARAMETERS   0x00020218
+
+/* IEEE 802.3 (Ethernet) OIDs */
+#define NDIS_802_3_MAC_OPTION_PRIORITY    0x00000001
+
+#define OID_802_3_PERMANENT_ADDRESS       0x01010101
+#define OID_802_3_CURRENT_ADDRESS         0x01010102
+#define OID_802_3_MULTICAST_LIST          0x01010103
+#define OID_802_3_MAXIMUM_LIST_SIZE       0x01010104
+#define OID_802_3_MAC_OPTIONS             0x01010105
+#define OID_802_3_RCV_ERROR_ALIGNMENT     0x01020101
+#define OID_802_3_XMIT_ONE_COLLISION      0x01020102
+#define OID_802_3_XMIT_MORE_COLLISIONS    0x01020103
+#define OID_802_3_XMIT_DEFERRED           0x01020201
+#define OID_802_3_XMIT_MAX_COLLISIONS     0x01020202
+#define OID_802_3_RCV_OVERRUN             0x01020203
+#define OID_802_3_XMIT_UNDERRUN           0x01020204
+#define OID_802_3_XMIT_HEARTBEAT_FAILURE  0x01020205
+#define OID_802_3_XMIT_TIMES_CRS_LOST     0x01020206
+#define OID_802_3_XMIT_LATE_COLLISIONS    0x01020207
+
+/* OID_GEN_MINIPORT_INFO constants */
+#define NDIS_MINIPORT_BUS_MASTER                      0x00000001
+#define NDIS_MINIPORT_WDM_DRIVER                      0x00000002
+#define NDIS_MINIPORT_SG_LIST                         0x00000004
+#define NDIS_MINIPORT_SUPPORTS_MEDIA_QUERY            0x00000008
+#define NDIS_MINIPORT_INDICATES_PACKETS               0x00000010
+#define NDIS_MINIPORT_IGNORE_PACKET_QUEUE             0x00000020
+#define NDIS_MINIPORT_IGNORE_REQUEST_QUEUE            0x00000040
+#define NDIS_MINIPORT_IGNORE_TOKEN_RING_ERRORS        0x00000080
+#define NDIS_MINIPORT_INTERMEDIATE_DRIVER             0x00000100
+#define NDIS_MINIPORT_IS_NDIS_5                       0x00000200
+#define NDIS_MINIPORT_IS_CO                           0x00000400
+#define NDIS_MINIPORT_DESERIALIZE                     0x00000800
+#define NDIS_MINIPORT_REQUIRES_MEDIA_POLLING          0x00001000
+#define NDIS_MINIPORT_SUPPORTS_MEDIA_SENSE            0x00002000
+#define NDIS_MINIPORT_NETBOOT_CARD                    0x00004000
+#define NDIS_MINIPORT_PM_SUPPORTED                    0x00008000
+#define NDIS_MINIPORT_SUPPORTS_MAC_ADDRESS_OVERWRITE  0x00010000
+#define NDIS_MINIPORT_USES_SAFE_BUFFER_APIS           0x00020000
+#define NDIS_MINIPORT_HIDDEN                          0x00040000
+#define NDIS_MINIPORT_SWENUM                          0x00080000
+#define NDIS_MINIPORT_SURPRISE_REMOVE_OK              0x00100000
+#define NDIS_MINIPORT_NO_HALT_ON_SUSPEND              0x00200000
+#define NDIS_MINIPORT_HARDWARE_DEVICE                 0x00400000
+#define NDIS_MINIPORT_SUPPORTS_CANCEL_SEND_PACKETS    0x00800000
+#define NDIS_MINIPORT_64BITS_DMA                      0x01000000
+
+#define NDIS_MEDIUM_802_3		0x00000000
+#define NDIS_MEDIUM_802_5		0x00000001
+#define NDIS_MEDIUM_FDDI		0x00000002
+#define NDIS_MEDIUM_WAN			0x00000003
+#define NDIS_MEDIUM_LOCAL_TALK		0x00000004
+#define NDIS_MEDIUM_DIX			0x00000005
+#define NDIS_MEDIUM_ARCENT_RAW		0x00000006
+#define NDIS_MEDIUM_ARCENT_878_2	0x00000007
+#define NDIS_MEDIUM_ATM			0x00000008
+#define NDIS_MEDIUM_WIRELESS_LAN	0x00000009
+#define NDIS_MEDIUM_IRDA		0x0000000A
+#define NDIS_MEDIUM_BPC			0x0000000B
+#define NDIS_MEDIUM_CO_WAN		0x0000000C
+#define NDIS_MEDIUM_1394		0x0000000D
+
+#define NDIS_PACKET_TYPE_DIRECTED	0x00000001
+#define NDIS_PACKET_TYPE_MULTICAST	0x00000002
+#define NDIS_PACKET_TYPE_ALL_MULTICAST	0x00000004
+#define NDIS_PACKET_TYPE_BROADCAST	0x00000008
+#define NDIS_PACKET_TYPE_SOURCE_ROUTING	0x00000010
+#define NDIS_PACKET_TYPE_PROMISCUOUS	0x00000020
+#define NDIS_PACKET_TYPE_SMT		0x00000040
+#define NDIS_PACKET_TYPE_ALL_LOCAL	0x00000080
+#define NDIS_PACKET_TYPE_GROUP		0x00000100
+#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL	0x00000200
+#define NDIS_PACKET_TYPE_FUNCTIONAL	0x00000400
+#define NDIS_PACKET_TYPE_MAC_FRAME	0x00000800
+
+#define NDIS_MEDIA_STATE_CONNECTED	0x00000000
+#define NDIS_MEDIA_STATE_DISCONNECTED	0x00000001
+
+#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA     0x00000001
+#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED      0x00000002
+#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND      0x00000004
+#define NDIS_MAC_OPTION_NO_LOOPBACK             0x00000008
+#define NDIS_MAC_OPTION_FULL_DUPLEX             0x00000010
+#define NDIS_MAC_OPTION_EOTX_INDICATION         0x00000020
+#define NDIS_MAC_OPTION_8021P_PRIORITY          0x00000040
+#define NDIS_MAC_OPTION_RESERVED                0x80000000
+
+#endif /* _LINUX_NDIS_H */
diff -urN linux-2.4.27-rc1/drivers/usb/gadget/rndis.c linux-2.4.27-rc2/drivers/usb/gadget/rndis.c
--- linux-2.4.27-rc1/drivers/usb/gadget/rndis.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.27-rc2/drivers/usb/gadget/rndis.c	2004-06-25 20:48:26.870103270 -0700
@@ -0,0 +1,1426 @@
+/* 
+ * RNDIS MSG parser
+ * 
+ * Version:     $Id: rndis.c,v 1.19 2004/03/25 21:33:46 robert Exp $
+ * 
+ * Authors:	Benedikt Spranger, Pengutronix
+ * 		Robert Schwebel, Pengutronix
+ * 
+ *              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 software was originally developed in conformance with
+ *		Microsoft's Remote NDIS Specification License Agreement.
+ *              
+ * 03/12/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
+ *		Fixed message length bug in init_response
+ * 
+ * 03/25/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
+ * 		Fixed rndis_rm_hdr length bug.
+ *
+ * Copyright (C) 2004 by David Brownell
+ *		updates to merge with Linux 2.6, better match RNDIS spec
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/system.h>
+
+
+#undef	RNDIS_PM
+#undef	VERBOSE
+
+#include "rndis.h"
+
+
+/* The driver for your USB chip needs to support ep0 OUT to work with
+ * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional).
+ *
+ * Windows hosts need an INF file like Documentation/usb/linux.inf
+ * and will be happier if you provide the host_addr module parameter.
+ */
+
+#if 0
+#define DEBUG(str,args...) do { \
+	if (rndis_debug) \
+		printk(KERN_DEBUG str , ## args ); \
+	} while (0)
+static int rndis_debug = 0;
+
+module_param (rndis_debug, bool, 0);
+MODULE_PARM_DESC (rndis_debug, "enable debugging");
+
+#else
+
+#define rndis_debug		0
+#define DEBUG(str,args...)	do{}while(0)
+#endif
+
+#define RNDIS_MAX_CONFIGS	1
+
+static struct proc_dir_entry *rndis_connect_dir;
+static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];
+
+static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS];
+
+/* Driver Version */
+static const u32 rndis_driver_version = __constant_cpu_to_le32 (1);
+
+/* Function Prototypes */
+static int rndis_init_response (int configNr, rndis_init_msg_type *buf);
+static int rndis_query_response (int configNr, rndis_query_msg_type *buf);
+static int rndis_set_response (int configNr, rndis_set_msg_type *buf);
+static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf);
+static int rndis_keepalive_response (int configNr, 
+				     rndis_keepalive_msg_type *buf);
+
+static rndis_resp_t *rndis_add_response (int configNr, u32 length);
+
+
+/* NDIS Functions */
+static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
+{
+	int 			retval = -ENOTSUPP;
+	u32 			length = 0;
+	u32			*tmp;
+	int			i, count;
+	rndis_query_cmplt_type	*resp;
+
+	if (!r) return -ENOMEM;
+	resp = (rndis_query_cmplt_type *) r->buf;
+
+	if (!resp) return -ENOMEM;
+	
+	switch (OID) {
+
+	/* general oids (table 4-1) */
+
+	/* mandatory */
+	case OID_GEN_SUPPORTED_LIST:
+		DEBUG ("%s: OID_GEN_SUPPORTED_LIST\n", __FUNCTION__);
+		length = sizeof (oid_supported_list);
+		count  = length / sizeof (u32);
+		tmp = (u32 *) ((u8 *)resp + 24);
+		for (i = 0; i < count; i++)
+			tmp[i] = cpu_to_le32 (oid_supported_list[i]);
+		retval = 0;
+		break;
+		
+	/* mandatory */
+	case OID_GEN_HARDWARE_STATUS:
+		DEBUG("%s: OID_GEN_HARDWARE_STATUS\n", __FUNCTION__);
+		length = 4;
+		/* Bogus question! 
+		 * Hardware must be ready to recieve high level protocols.
+		 * BTW: 
+		 * reddite ergo quae sunt Caesaris Caesari
+		 * et quae sunt Dei Deo!
+		 */
+		*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+		retval = 0;
+		break;
+		
+	/* mandatory */
+	case OID_GEN_MEDIA_SUPPORTED:
+		DEBUG("%s: OID_GEN_MEDIA_SUPPORTED\n", __FUNCTION__);
+		length = 4;
+		*((u32 *) resp + 6) = cpu_to_le32 (
+					rndis_per_dev_params [configNr].medium);
+		retval = 0;
+		break;
+		
+	/* mandatory */
+	case OID_GEN_MEDIA_IN_USE:
+		DEBUG("%s: OID_GEN_MEDIA_IN_USE\n", __FUNCTION__);
+		length = 4;
+		/* one medium, one transport... (maybe you do it better) */
+		*((u32 *) resp + 6) = cpu_to_le32 (
+					rndis_per_dev_params [configNr].medium);
+		retval = 0;
+		break;
+		
+	/* mandatory */
+	case OID_GEN_MAXIMUM_FRAME_SIZE:
+		DEBUG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].dev) {
+			length = 4;
+			*((u32 *) resp + 6) = cpu_to_le32 (
+				rndis_per_dev_params [configNr].dev->mtu);
+			retval = 0;
+		} else {
+			*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+			retval = 0;
+		}
+		break;
+		
+	/* mandatory */
+	case OID_GEN_LINK_SPEED:
+		DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__);
+		length = 4;
+		if (rndis_per_dev_params [configNr].media_state
+			== NDIS_MEDIA_STATE_DISCONNECTED)
+		    *((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+		else
+		    *((u32 *) resp + 6) = cpu_to_le32 (
+				rndis_per_dev_params [configNr].speed);
+		retval = 0;
+		break;
+
+	/* mandatory */
+	case OID_GEN_TRANSMIT_BLOCK_SIZE:
+		DEBUG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].dev) {
+			length = 4;
+			*((u32 *) resp + 6) = cpu_to_le32 (
+				rndis_per_dev_params [configNr].dev->mtu);
+			retval = 0;
+		}
+		break;
+		
+	/* mandatory */
+	case OID_GEN_RECEIVE_BLOCK_SIZE:
+		DEBUG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].dev) {
+			length = 4;
+			*((u32 *) resp + 6) = cpu_to_le32 (
+				rndis_per_dev_params [configNr].dev->mtu);
+			retval = 0;
+		}
+		break;
+		
+	/* mandatory */
+	case OID_GEN_VENDOR_ID:
+		DEBUG("%s: OID_GEN_VENDOR_ID\n", __FUNCTION__);
+		length = 4;
+		*((u32 *) resp + 6) = cpu_to_le32 (
+			rndis_per_dev_params [configNr].vendorID);
+		retval = 0;
+		break;
+		
+	/* mandatory */
+	case OID_GEN_VENDOR_DESCRIPTION:
+		DEBUG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __FUNCTION__);
+		length = strlen (rndis_per_dev_params [configNr].vendorDescr);
+		memcpy ((u8 *) resp + 24, 
+			rndis_per_dev_params [configNr].vendorDescr, length);
+		retval = 0;
+		break;
+
+	case OID_GEN_VENDOR_DRIVER_VERSION:
+		DEBUG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __FUNCTION__);
+		length = 4;
+		/* Created as LE */
+		*((u32 *) resp + 6) = rndis_driver_version;
+		retval = 0;
+		break;
+
+	/* mandatory */
+	case OID_GEN_CURRENT_PACKET_FILTER:
+		DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__);
+		length = 4;
+		*((u32 *) resp + 6) = cpu_to_le32 (
+					rndis_per_dev_params[configNr].filter);
+		retval = 0;
+		break;
+
+	/* mandatory */
+	case OID_GEN_MAXIMUM_TOTAL_SIZE:
+		DEBUG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __FUNCTION__);
+		length = 4;
+		*((u32 *) resp + 6) = __constant_cpu_to_le32(
+					RNDIS_MAX_TOTAL_SIZE);
+		retval = 0;
+		break;
+
+	/* mandatory */
+	case OID_GEN_MEDIA_CONNECT_STATUS:
+		DEBUG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__);
+		length = 4;
+		*((u32 *) resp + 6) = cpu_to_le32 (
+					rndis_per_dev_params [configNr]
+						.media_state);
+		retval = 0;
+		break;
+
+	case OID_GEN_PHYSICAL_MEDIUM:
+		DEBUG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __FUNCTION__);
+		length = 4;
+		*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+		retval = 0;
+		break;
+
+	/* The RNDIS specification is incomplete/wrong.   Some versions
+	 * of MS-Windows expect OIDs that aren't specified there.  Other
+	 * versions emit undefined RNDIS messages. DOCUMENT ALL THESE!
+	 */
+	case OID_GEN_MAC_OPTIONS:		/* from WinME */
+		DEBUG("%s: OID_GEN_MAC_OPTIONS\n", __FUNCTION__);
+		length = 4;
+		*((u32 *) resp + 6) = __constant_cpu_to_le32(
+			  NDIS_MAC_OPTION_RECEIVE_SERIALIZED
+			| NDIS_MAC_OPTION_FULL_DUPLEX);
+		retval = 0;
+		break;
+
+	/* statistics OIDs (table 4-2) */
+
+	/* mandatory */
+	case OID_GEN_XMIT_OK:
+		DEBUG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].stats) {
+			length = 4;
+			*((u32 *) resp + 6) = cpu_to_le32 (
+			    rndis_per_dev_params [configNr].stats->tx_packets - 
+			    rndis_per_dev_params [configNr].stats->tx_errors -
+			    rndis_per_dev_params [configNr].stats->tx_dropped);
+			retval = 0;
+		} else {
+			*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+			retval = 0;
+		}
+		break;
+
+	/* mandatory */
+	case OID_GEN_RCV_OK:
+		DEBUG("%s: OID_GEN_RCV_OK\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].stats) {
+			length = 4;
+			*((u32 *) resp + 6) = cpu_to_le32 (
+			    rndis_per_dev_params [configNr].stats->rx_packets - 
+			    rndis_per_dev_params [configNr].stats->rx_errors -
+			    rndis_per_dev_params [configNr].stats->rx_dropped);
+			retval = 0;
+		} else {
+			*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+			retval = 0;
+		}
+		break;
+		
+	/* mandatory */
+	case OID_GEN_XMIT_ERROR:
+		DEBUG("%s: OID_GEN_XMIT_ERROR\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].stats) {
+			length = 4;
+			*((u32 *) resp + 6) = cpu_to_le32 (
+				rndis_per_dev_params [configNr]
+					.stats->tx_errors);
+			retval = 0;
+		} else {
+			*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+			retval = 0;
+		}
+		break;
+		
+	/* mandatory */
+	case OID_GEN_RCV_ERROR:
+		DEBUG("%s: OID_GEN_RCV_ERROR\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].stats) {
+			*((u32 *) resp + 6) = cpu_to_le32 (
+				rndis_per_dev_params [configNr]
+					.stats->rx_errors);
+			retval = 0;
+		} else {
+			*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+			retval = 0;
+		}
+		break;
+		
+	/* mandatory */
+	case OID_GEN_RCV_NO_BUFFER:
+		DEBUG("%s: OID_GEN_RCV_NO_BUFFER\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].stats) {
+			*((u32 *) resp + 6) = cpu_to_le32 (
+				rndis_per_dev_params [configNr]
+					.stats->rx_dropped);
+			retval = 0;
+		} else {
+			*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+			retval = 0;
+		}
+		break;
+
+#ifdef	RNDIS_OPTIONAL_STATS
+	case OID_GEN_DIRECTED_BYTES_XMIT:
+		DEBUG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __FUNCTION__);
+		/* 
+		 * Aunt Tilly's size of shoes
+		 * minus antarctica count of penguins
+		 * divided by weight of Alpha Centauri
+		 */
+		if (rndis_per_dev_params [configNr].stats) {
+			length = 4;
+			*((u32 *) resp + 6) = cpu_to_le32 (
+				(rndis_per_dev_params [configNr]
+					.stats->tx_packets - 
+				 rndis_per_dev_params [configNr]
+					 .stats->tx_errors -
+				 rndis_per_dev_params [configNr]
+					 .stats->tx_dropped)
+				* 123);
+			retval = 0;
+		} else {
+			*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+			retval = 0;
+		}
+		break;
+		
+	case OID_GEN_DIRECTED_FRAMES_XMIT:
+		DEBUG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __FUNCTION__);
+		/* dito */
+		if (rndis_per_dev_params [configNr].stats) {
+			length = 4;
+			*((u32 *) resp + 6) = cpu_to_le32 (
+				(rndis_per_dev_params [configNr]
+					.stats->tx_packets - 
+				 rndis_per_dev_params [configNr]
+					 .stats->tx_errors -
+				 rndis_per_dev_params [configNr]
+					 .stats->tx_dropped)
+				/ 123);
+			retval = 0;
+		} else {
+			*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+			retval = 0;
+		}
+		break;
+		
+	case OID_GEN_MULTICAST_BYTES_XMIT:
+		DEBUG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].stats) {
+			*((u32 *) resp + 6) = cpu_to_le32 (
+				rndis_per_dev_params [configNr]
+					.stats->multicast*1234);
+			retval = 0;
+		} else {
+			*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+			retval = 0;
+		}
+		break;
+		
+	case OID_GEN_MULTICAST_FRAMES_XMIT:
+		DEBUG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].stats) {
+			*((u32 *) resp + 6) = cpu_to_le32 (
+				rndis_per_dev_params [configNr]
+					.stats->multicast);
+			retval = 0;
+		} else {
+			*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+			retval = 0;
+		}
+		break;
+		
+	case OID_GEN_BROADCAST_BYTES_XMIT:
+		DEBUG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].stats) {
+			*((u32 *) resp + 6) = cpu_to_le32 (
+				rndis_per_dev_params [configNr]
+					.stats->tx_packets/42*255);
+			retval = 0;
+		} else {
+			*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+			retval = 0;
+		}
+		break;
+		
+	case OID_GEN_BROADCAST_FRAMES_XMIT:
+		DEBUG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].stats) {
+			*((u32 *) resp + 6) = cpu_to_le32 (
+				rndis_per_dev_params [configNr]
+					.stats->tx_packets/42);
+			retval = 0;
+		} else {
+			*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+			retval = 0;
+		}
+		break;
+		
+	case OID_GEN_DIRECTED_BYTES_RCV:
+		DEBUG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __FUNCTION__);
+		*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+		retval = 0;
+		break;
+		
+	case OID_GEN_DIRECTED_FRAMES_RCV:
+		DEBUG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __FUNCTION__);
+		*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+		retval = 0;
+		break;
+		
+	case OID_GEN_MULTICAST_BYTES_RCV:
+		DEBUG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].stats) {
+			*((u32 *) resp + 6) = cpu_to_le32 (
+				rndis_per_dev_params [configNr]
+					.stats->multicast * 1111);
+			retval = 0;
+		} else {
+			*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+			retval = 0;
+		}
+		break;
+		
+	case OID_GEN_MULTICAST_FRAMES_RCV:
+		DEBUG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].stats) {
+			*((u32 *) resp + 6) = cpu_to_le32 (
+				rndis_per_dev_params [configNr]
+					.stats->multicast);
+			retval = 0;
+		} else {
+			*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+			retval = 0;
+		}
+		break;
+		
+	case OID_GEN_BROADCAST_BYTES_RCV:
+		DEBUG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].stats) {
+			*((u32 *) resp + 6) = cpu_to_le32 (
+				rndis_per_dev_params [configNr]
+					.stats->rx_packets/42*255);
+			retval = 0;
+		} else {
+			*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+			retval = 0;
+		}
+		break;
+		
+	case OID_GEN_BROADCAST_FRAMES_RCV:
+		DEBUG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].stats) {
+			*((u32 *) resp + 6) = cpu_to_le32 (
+				rndis_per_dev_params [configNr]
+					.stats->rx_packets/42);
+			retval = 0;
+		} else {
+			*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+			retval = 0;
+		}
+		break;
+		
+	case OID_GEN_RCV_CRC_ERROR:
+		DEBUG("%s: OID_GEN_RCV_CRC_ERROR\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].stats) {
+			*((u32 *) resp + 6) = cpu_to_le32 (
+				rndis_per_dev_params [configNr]
+					.stats->rx_crc_errors);
+			retval = 0;
+		} else {
+			*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+			retval = 0;
+		}
+		break;
+		
+	case OID_GEN_TRANSMIT_QUEUE_LENGTH:
+		DEBUG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __FUNCTION__);
+		*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+		retval = 0;
+		break;
+#endif	/* RNDIS_OPTIONAL_STATS */
+
+	/* ieee802.3 OIDs (table 4-3) */
+
+	/* mandatory */
+	case OID_802_3_PERMANENT_ADDRESS:
+		DEBUG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].dev) {
+			length = ETH_ALEN;
+			memcpy ((u8 *) resp + 24,
+				rndis_per_dev_params [configNr].host_mac,
+				length);
+			retval = 0;
+		} else {
+			*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+			retval = 0;
+		}
+		break;
+		
+	/* mandatory */
+	case OID_802_3_CURRENT_ADDRESS:
+		DEBUG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].dev) {
+			length = ETH_ALEN;
+			memcpy ((u8 *) resp + 24,
+				rndis_per_dev_params [configNr].host_mac,
+				length);
+			retval = 0;
+		}
+		break;
+		
+	/* mandatory */
+	case OID_802_3_MULTICAST_LIST:
+		DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__);
+		length = 4;
+		/* Multicast base address only */
+		*((u32 *) resp + 6) = __constant_cpu_to_le32 (0xE0000000);
+		retval = 0;
+		break;
+		
+	/* mandatory */
+	case OID_802_3_MAXIMUM_LIST_SIZE:
+		DEBUG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __FUNCTION__);
+		 length = 4;
+		/* Multicast base address only */
+		*((u32 *) resp + 6) = __constant_cpu_to_le32 (1);
+		retval = 0;
+		break;
+		
+	case OID_802_3_MAC_OPTIONS:
+		DEBUG("%s: OID_802_3_MAC_OPTIONS\n", __FUNCTION__);
+		break;
+
+	/* ieee802.3 statistics OIDs (table 4-4) */
+
+	/* mandatory */
+	case OID_802_3_RCV_ERROR_ALIGNMENT:
+		DEBUG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __FUNCTION__);
+		if (rndis_per_dev_params [configNr].stats)
+		{
+			length = 4;
+			*((u32 *) resp + 6) = cpu_to_le32 (
+				rndis_per_dev_params [configNr]
+					.stats->rx_frame_errors);
+			retval = 0;
+		}
+		break;
+		
+	/* mandatory */
+	case OID_802_3_XMIT_ONE_COLLISION:
+		DEBUG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __FUNCTION__);
+		length = 4;
+		*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+		retval = 0;
+		break;
+		
+	/* mandatory */
+	case OID_802_3_XMIT_MORE_COLLISIONS:
+		DEBUG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __FUNCTION__);
+		length = 4;
+		*((u32 *) resp + 6) = __constant_cpu_to_le32 (0);
+		retval = 0;
+		break;
+		
+#ifdef	RNDIS_OPTIONAL_STATS
+	case OID_802_3_XMIT_DEFERRED:
+		DEBUG("%s: OID_802_3_XMIT_DEFERRED\n", __FUNCTION__);
+		/* TODO */
+		break;
+		
+	case OID_802_3_XMIT_MAX_COLLISIONS:
+		DEBUG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __FUNCTION__);
+		/* TODO */
+		break;
+		
+	case OID_802_3_RCV_OVERRUN:
+		DEBUG("%s: OID_802_3_RCV_OVERRUN\n", __FUNCTION__);
+		/* TODO */
+		break;
+		
+	case OID_802_3_XMIT_UNDERRUN:
+		DEBUG("%s: OID_802_3_XMIT_UNDERRUN\n", __FUNCTION__);
+		/* TODO */
+		break;
+		
+	case OID_802_3_XMIT_HEARTBEAT_FAILURE:
+		DEBUG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __FUNCTION__);
+		/* TODO */
+		break;
+		
+	case OID_802_3_XMIT_TIMES_CRS_LOST:
+		DEBUG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __FUNCTION__);
+		/* TODO */
+		break;
+		
+	case OID_802_3_XMIT_LATE_COLLISIONS:
+		DEBUG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __FUNCTION__);
+		/* TODO */
+		break;		
+#endif	/* RNDIS_OPTIONAL_STATS */
+
+#ifdef	RNDIS_PM
+	/* power management OIDs (table 4-5) */
+	case OID_PNP_CAPABILITIES:
+		DEBUG("%s: OID_PNP_CAPABILITIES\n", __FUNCTION__);
+
+		/* just PM, and remote wakeup on link status change
+		 * (not magic packet or pattern match)
+		 */
+		length = sizeof (struct NDIS_PNP_CAPABILITIES);
+		memset (resp, 0, length);
+		{
+			struct NDIS_PNP_CAPABILITIES *caps = (void *) resp;
+
+			caps->Flags = NDIS_DEVICE_WAKE_UP_ENABLE;
+			caps->WakeUpCapabilities.MinLinkChangeWakeUp 
+				 = NdisDeviceStateD3;
+
+			/* FIXME then use usb_gadget_wakeup(), and
+			 * set USB_CONFIG_ATT_WAKEUP in config desc
+			 */
+		}
+		retval = 0;
+		break;
+	case OID_PNP_QUERY_POWER:
+		DEBUG("%s: OID_PNP_QUERY_POWER\n", __FUNCTION__);
+		/* sure, handle any power state that maps to USB suspend */
+		retval = 0;
+		break;
+#endif
+
+	default:
+		printk (KERN_WARNING "%s: query unknown OID 0x%08X\n", 
+			 __FUNCTION__, OID);
+	}
+	
+	resp->InformationBufferOffset = __constant_cpu_to_le32 (16);
+	resp->InformationBufferLength = cpu_to_le32 (length);
+	resp->MessageLength = cpu_to_le32 (24 + length);
+	r->length = 24 + length;
+	return retval;
+}
+
+static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, 
+			      rndis_resp_t *r)
+{
+	rndis_set_cmplt_type		*resp;
+	int 				i, retval = -ENOTSUPP;
+	struct rndis_params		*params;
+
+	if (!r)
+		return -ENOMEM;
+	resp = (rndis_set_cmplt_type *) r->buf;
+	if (!resp)
+		return -ENOMEM;
+
+	DEBUG("set OID %08x value, len %d:\n", OID, buf_len);
+	for (i = 0; i < buf_len; i += 16) {
+		DEBUG ("%03d: "
+			" %02x %02x %02x %02x"
+			" %02x %02x %02x %02x"
+			" %02x %02x %02x %02x"
+			" %02x %02x %02x %02x"
+			"\n",
+			i,
+			buf[i], buf [i+1],
+				buf[i+2], buf[i+3],
+			buf[i+4], buf [i+5],
+				buf[i+6], buf[i+7],
+			buf[i+8], buf [i+9],
+				buf[i+10], buf[i+11],
+			buf[i+12], buf [i+13],
+				buf[i+14], buf[i+15]);
+	}
+
+	switch (OID) {
+	case OID_GEN_CURRENT_PACKET_FILTER:
+		params = &rndis_per_dev_params [configNr];
+		retval = 0;
+
+		/* FIXME use these NDIS_PACKET_TYPE_* bitflags to
+		 * filter packets in hard_start_xmit()
+		 * NDIS_PACKET_TYPE_x == CDC_PACKET_TYPE_x for x in:
+		 *	PROMISCUOUS, DIRECTED,
+		 *	MULTICAST, ALL_MULTICAST, BROADCAST
+		 */
+		params->filter = cpu_to_le32p((u32 *)buf);
+		DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n",
+			__FUNCTION__, params->filter);
+
+		/* this call has a significant side effect:  it's
+		 * what makes the packet flow start and stop, like
+		 * activating the CDC Ethernet altsetting.
+		 */
+		if (params->filter) {
+			params->state = RNDIS_DATA_INITIALIZED;
+			netif_carrier_on(params->dev);
+			if (netif_running(params->dev))
+				netif_wake_queue (params->dev);
+		} else {
+			params->state = RNDIS_INITIALIZED;
+			netif_carrier_off (params->dev);
+			netif_stop_queue (params->dev);
+		}
+		break;
+		
+	case OID_802_3_MULTICAST_LIST:
+		/* I think we can ignore this */		
+		DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__);
+		retval = 0;
+		break;
+#if 0
+	case OID_GEN_RNDIS_CONFIG_PARAMETER:
+		{
+		struct rndis_config_parameter	*param;
+		param = (struct rndis_config_parameter *) buf;
+		DEBUG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n",
+			__FUNCTION__,
+			min(cpu_to_le32(param->ParameterNameLength),80),
+			buf + param->ParameterNameOffset);
+		retval = 0;
+		}
+		break;
+#endif
+
+#ifdef	RNDIS_PM
+	case OID_PNP_SET_POWER:
+		DEBUG ("OID_PNP_SET_POWER\n");
+		/* sure, handle any power state that maps to USB suspend */
+		retval = 0;
+		break;
+
+	case OID_PNP_ENABLE_WAKE_UP:
+		/* always-connected ... */
+		DEBUG ("OID_PNP_ENABLE_WAKE_UP\n");
+		retval = 0;
+		break;
+
+	// no PM resume patterns supported (specified where?)
+	// so OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN always fails
+#endif
+
+	default:
+		printk (KERN_WARNING "%s: set unknown OID 0x%08X, size %d\n", 
+			 __FUNCTION__, OID, buf_len);
+	}
+	
+	return retval;
+}
+
+/* 
+ * Response Functions 
+ */
+
+static int rndis_init_response (int configNr, rndis_init_msg_type *buf)
+{
+	rndis_init_cmplt_type	*resp; 
+	rndis_resp_t            *r;
+	
+	if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP;
+	
+	r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type));
+	
+	if (!r) return -ENOMEM;
+	
+	resp = (rndis_init_cmplt_type *) r->buf;
+	
+	if (!resp) return -ENOMEM;
+	
+	resp->MessageType = __constant_cpu_to_le32 (
+			REMOTE_NDIS_INITIALIZE_CMPLT);
+	resp->MessageLength = __constant_cpu_to_le32 (52);
+	resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+	resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
+	resp->MajorVersion = __constant_cpu_to_le32 (RNDIS_MAJOR_VERSION);
+	resp->MinorVersion = __constant_cpu_to_le32 (RNDIS_MINOR_VERSION);
+	resp->DeviceFlags = __constant_cpu_to_le32 (RNDIS_DF_CONNECTIONLESS);
+	resp->Medium = __constant_cpu_to_le32 (RNDIS_MEDIUM_802_3);
+	resp->MaxPacketsPerTransfer = __constant_cpu_to_le32 (1);
+	resp->MaxTransferSize = cpu_to_le32 (
+		  rndis_per_dev_params [configNr].dev->mtu
+		+ sizeof (struct ethhdr)
+		+ sizeof (struct rndis_packet_msg_type)
+		+ 22);
+	resp->PacketAlignmentFactor = __constant_cpu_to_le32 (0);
+	resp->AFListOffset = __constant_cpu_to_le32 (0);
+	resp->AFListSize = __constant_cpu_to_le32 (0);
+	
+	if (rndis_per_dev_params [configNr].ack)
+	    rndis_per_dev_params [configNr].ack (
+	    		rndis_per_dev_params [configNr].dev);
+	
+	return 0;
+}
+
+static int rndis_query_response (int configNr, rndis_query_msg_type *buf)
+{
+	rndis_query_cmplt_type *resp;
+	rndis_resp_t            *r;
+	
+	// DEBUG("%s: OID = %08X\n", __FUNCTION__, cpu_to_le32(buf->OID));
+	if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP;
+	
+	/* 
+	 * we need more memory: 
+	 * oid_supported_list is the largest answer 
+	 */
+	r = rndis_add_response (configNr, sizeof (oid_supported_list));
+	
+	if (!r) return -ENOMEM;
+	resp = (rndis_query_cmplt_type *) r->buf;
+	
+	if (!resp) return -ENOMEM;
+	
+	resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_QUERY_CMPLT);
+	resp->MessageLength = __constant_cpu_to_le32 (24);
+	resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+	
+	if (gen_ndis_query_resp (configNr, cpu_to_le32 (buf->OID), r)) {
+		/* OID not supported */
+		resp->Status = __constant_cpu_to_le32 (
+				RNDIS_STATUS_NOT_SUPPORTED);
+		resp->InformationBufferLength = __constant_cpu_to_le32 (0);
+		resp->InformationBufferOffset = __constant_cpu_to_le32 (0);
+	} else
+		resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
+	
+	if (rndis_per_dev_params [configNr].ack)
+	    rndis_per_dev_params [configNr].ack (
+	    		rndis_per_dev_params [configNr].dev);
+	return 0;
+}
+
+static int rndis_set_response (int configNr, rndis_set_msg_type *buf)
+{
+	u32			BufLength, BufOffset;
+	rndis_set_cmplt_type	*resp;
+	rndis_resp_t		*r;
+	
+	r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type));
+	
+	if (!r) return -ENOMEM;
+	resp = (rndis_set_cmplt_type *) r->buf;
+	if (!resp) return -ENOMEM;
+
+	BufLength = cpu_to_le32 (buf->InformationBufferLength);
+	BufOffset = cpu_to_le32 (buf->InformationBufferOffset);
+
+#ifdef	VERBOSE
+	DEBUG("%s: Length: %d\n", __FUNCTION__, BufLength);
+	DEBUG("%s: Offset: %d\n", __FUNCTION__, BufOffset);
+	DEBUG("%s: InfoBuffer: ", __FUNCTION__);
+	
+	for (i = 0; i < BufLength; i++) {
+		DEBUG ("%02x ", *(((u8 *) buf) + i + 8 + BufOffset));
+	}
+	
+	DEBUG ("\n");
+#endif
+	
+	resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_SET_CMPLT);
+	resp->MessageLength = __constant_cpu_to_le32 (16);
+	resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+	if (gen_ndis_set_resp (configNr, cpu_to_le32 (buf->OID), 
+			       ((u8 *) buf) + 8 + BufOffset, BufLength, r))
+	    resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_NOT_SUPPORTED);
+	else resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
+	
+	if (rndis_per_dev_params [configNr].ack)
+	    rndis_per_dev_params [configNr].ack (
+	    		rndis_per_dev_params [configNr].dev);
+	
+	return 0;
+}
+
+static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf)
+{
+	rndis_reset_cmplt_type	*resp;
+	rndis_resp_t		*r;
+	
+	r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type));
+	
+	if (!r) return -ENOMEM;
+	resp = (rndis_reset_cmplt_type *) r->buf;
+	if (!resp) return -ENOMEM;
+	
+	resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_RESET_CMPLT);
+	resp->MessageLength = __constant_cpu_to_le32 (16);
+	resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
+	/* resent information */
+	resp->AddressingReset = __constant_cpu_to_le32 (1);
+	
+	if (rndis_per_dev_params [configNr].ack)
+	    rndis_per_dev_params [configNr].ack (
+	    		rndis_per_dev_params [configNr].dev);
+
+	return 0;
+}
+
+static int rndis_keepalive_response (int configNr,
+				     rndis_keepalive_msg_type *buf)
+{
+	rndis_keepalive_cmplt_type	*resp;
+	rndis_resp_t			*r;
+
+	/* host "should" check only in RNDIS_DATA_INITIALIZED state */
+
+	r = rndis_add_response (configNr, sizeof (rndis_keepalive_cmplt_type));
+	resp = (rndis_keepalive_cmplt_type *) r->buf;
+	if (!resp) return -ENOMEM;
+		
+	resp->MessageType = __constant_cpu_to_le32 (
+			REMOTE_NDIS_KEEPALIVE_CMPLT);
+	resp->MessageLength = __constant_cpu_to_le32 (16);
+	resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+	resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
+	
+	if (rndis_per_dev_params [configNr].ack)
+	    rndis_per_dev_params [configNr].ack (
+	    		rndis_per_dev_params [configNr].dev);
+	
+	return 0;
+}
+
+
+/* 
+ * Device to Host Comunication 
+ */
+static int rndis_indicate_status_msg (int configNr, u32 status)
+{
+	rndis_indicate_status_msg_type	*resp;	
+	rndis_resp_t			*r;
+	
+	if (rndis_per_dev_params [configNr].state == RNDIS_UNINITIALIZED)
+	    return -ENOTSUPP;
+	
+	r = rndis_add_response (configNr, 
+				sizeof (rndis_indicate_status_msg_type));
+	if (!r) return -ENOMEM;
+	
+	resp = (rndis_indicate_status_msg_type *) r->buf;
+	if (!resp) return -ENOMEM;
+	
+	resp->MessageType = __constant_cpu_to_le32 (
+			REMOTE_NDIS_INDICATE_STATUS_MSG);
+	resp->MessageLength = __constant_cpu_to_le32 (20);
+	resp->Status = cpu_to_le32 (status);
+	resp->StatusBufferLength = __constant_cpu_to_le32 (0);
+	resp->StatusBufferOffset = __constant_cpu_to_le32 (0);
+	
+	if (rndis_per_dev_params [configNr].ack) 
+	    rndis_per_dev_params [configNr].ack (
+	    		rndis_per_dev_params [configNr].dev);
+	return 0;
+}
+
+int rndis_signal_connect (int configNr)
+{
+	rndis_per_dev_params [configNr].media_state
+			= NDIS_MEDIA_STATE_CONNECTED;
+	return rndis_indicate_status_msg (configNr, 
+					  RNDIS_STATUS_MEDIA_CONNECT);
+}
+
+int rndis_signal_disconnect (int configNr)
+{
+	rndis_per_dev_params [configNr].media_state
+			= NDIS_MEDIA_STATE_DISCONNECTED;
+	return rndis_indicate_status_msg (configNr,
+					  RNDIS_STATUS_MEDIA_DISCONNECT);
+}
+
+void rndis_set_host_mac (int configNr, const u8 *addr)
+{
+	rndis_per_dev_params [configNr].host_mac = addr;
+}
+
+/* 
+ * Message Parser 
+ */
+int rndis_msg_parser (u8 configNr, u8 *buf)
+{
+	u32 MsgType, MsgLength, *tmp;
+	struct rndis_params		*params;
+	
+	if (!buf)
+		return -ENOMEM;
+	
+	tmp = (u32 *) buf; 
+	MsgType   = cpu_to_le32p(tmp++);
+	MsgLength = cpu_to_le32p(tmp++);
+	
+	if (configNr >= RNDIS_MAX_CONFIGS)
+		return -ENOTSUPP;
+	params = &rndis_per_dev_params [configNr];
+	
+	/* For USB: responses may take up to 10 seconds */
+	switch (MsgType)
+	{
+	case REMOTE_NDIS_INITIALIZE_MSG:
+		DEBUG("%s: REMOTE_NDIS_INITIALIZE_MSG\n", 
+			__FUNCTION__ );
+		params->state = RNDIS_INITIALIZED;
+		return  rndis_init_response (configNr,
+					     (rndis_init_msg_type *) buf);
+		
+	case REMOTE_NDIS_HALT_MSG:
+		DEBUG("%s: REMOTE_NDIS_HALT_MSG\n",
+			__FUNCTION__ );
+		params->state = RNDIS_UNINITIALIZED;
+		if (params->dev) {
+			netif_carrier_off (params->dev);
+			netif_stop_queue (params->dev);
+		}
+		return 0;
+		
+	case REMOTE_NDIS_QUERY_MSG:
+		return rndis_query_response (configNr, 
+					     (rndis_query_msg_type *) buf);
+		
+	case REMOTE_NDIS_SET_MSG:
+		return rndis_set_response (configNr, 
+					   (rndis_set_msg_type *) buf);
+		
+	case REMOTE_NDIS_RESET_MSG:
+		DEBUG("%s: REMOTE_NDIS_RESET_MSG\n", 
+			__FUNCTION__ );
+		return rndis_reset_response (configNr,
+					     (rndis_reset_msg_type *) buf);
+
+	case REMOTE_NDIS_KEEPALIVE_MSG:
+		/* For USB: host does this every 5 seconds */
+#ifdef	VERBOSE
+		DEBUG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", 
+			__FUNCTION__ );
+#endif
+		return rndis_keepalive_response (configNr,
+						 (rndis_keepalive_msg_type *) 
+						 buf);
+		
+	default: 
+		/* At least Windows XP emits some undefined RNDIS messages.
+		 * In one case those messages seemed to relate to the host
+		 * suspending itself.
+		 */
+		printk (KERN_WARNING
+			"%s: unknown RNDIS message 0x%08X len %d\n", 
+			__FUNCTION__ , MsgType, MsgLength);
+		{
+			unsigned i;
+			for (i = 0; i < MsgLength; i += 16) {
+				DEBUG ("%03d: "
+					" %02x %02x %02x %02x"
+					" %02x %02x %02x %02x"
+					" %02x %02x %02x %02x"
+					" %02x %02x %02x %02x"
+					"\n",
+					i,
+					buf[i], buf [i+1],
+						buf[i+2], buf[i+3],
+					buf[i+4], buf [i+5],
+						buf[i+6], buf[i+7],
+					buf[i+8], buf [i+9],
+						buf[i+10], buf[i+11],
+					buf[i+12], buf [i+13],
+						buf[i+14], buf[i+15]);
+			}
+		}
+		break;
+	}
+	
+	return -ENOTSUPP;
+}
+
+int rndis_register (int (* rndis_control_ack) (struct net_device *))
+{
+	u8 i;
+	
+	for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
+		if (!rndis_per_dev_params [i].used) {
+			rndis_per_dev_params [i].used = 1;
+			rndis_per_dev_params [i].ack = rndis_control_ack;
+			DEBUG("%s: configNr = %d\n", __FUNCTION__, i);
+			return i;
+		}
+	}
+	DEBUG("failed\n");
+	
+	return -1;
+}
+
+void rndis_deregister (int configNr)
+{
+	DEBUG("%s: \n", __FUNCTION__ );
+	
+	if (configNr >= RNDIS_MAX_CONFIGS) return;
+	rndis_per_dev_params [configNr].used = 0;
+	
+	return;
+}
+
+int rndis_set_param_dev (u8 configNr, struct net_device *dev, 
+			 struct net_device_stats *stats)
+{
+	DEBUG("%s:\n", __FUNCTION__ );
+	if (!dev || !stats) return -1;
+	if (configNr >= RNDIS_MAX_CONFIGS) return -1;
+	
+	rndis_per_dev_params [configNr].dev = dev;
+	rndis_per_dev_params [configNr].stats = stats;
+	
+	return 0;
+}
+
+int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr)
+{
+	DEBUG("%s:\n", __FUNCTION__ );
+	if (!vendorDescr) return -1;
+	if (configNr >= RNDIS_MAX_CONFIGS) return -1;
+	
+	rndis_per_dev_params [configNr].vendorID = vendorID;
+	rndis_per_dev_params [configNr].vendorDescr = vendorDescr;
+	
+	return 0;
+}
+
+int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed)
+{
+	DEBUG("%s:\n", __FUNCTION__ );
+	if (configNr >= RNDIS_MAX_CONFIGS) return -1;
+	
+	rndis_per_dev_params [configNr].medium = medium;
+	rndis_per_dev_params [configNr].speed = speed;
+	
+	return 0;
+}
+
+void rndis_add_hdr (struct sk_buff *skb)
+{
+	if (!skb) return;
+	skb_push (skb, sizeof (struct rndis_packet_msg_type));
+	memset (skb->data, 0, sizeof (struct rndis_packet_msg_type));
+	*((u32 *) skb->data) = __constant_cpu_to_le32 (1);
+	*((u32 *) skb->data + 1) = cpu_to_le32(skb->len);
+	*((u32 *) skb->data + 2) = __constant_cpu_to_le32 (36);
+	*((u32 *) skb->data + 3) = cpu_to_le32(skb->len - 44);
+	
+	return;
+}
+
+void rndis_free_response (int configNr, u8 *buf)
+{
+	rndis_resp_t		*r;
+	struct list_head	*act, *tmp;
+	
+	list_for_each_safe (act, tmp, 
+			    &(rndis_per_dev_params [configNr].resp_queue))
+	{
+		r = list_entry (act, rndis_resp_t, list);
+		if (r && r->buf == buf) {
+			list_del (&r->list);
+			kfree (r);
+		}
+	}
+}
+
+u8 *rndis_get_next_response (int configNr, u32 *length)
+{
+	rndis_resp_t		*r;
+	struct list_head 	*act, *tmp;
+	
+	if (!length) return NULL;
+	
+	list_for_each_safe (act, tmp, 
+			    &(rndis_per_dev_params [configNr].resp_queue))
+	{
+		r = list_entry (act, rndis_resp_t, list);
+		if (!r->send) {
+			r->send = 1;
+			*length = r->length;
+			return r->buf;
+		}
+	}
+	
+	return NULL;
+}
+
+static rndis_resp_t *rndis_add_response (int configNr, u32 length)
+{
+	rndis_resp_t	*r;
+	
+	r = kmalloc (sizeof (rndis_resp_t) + length, GFP_ATOMIC);
+	if (!r) return NULL;
+	
+	r->buf = (u8 *) (r + 1);
+	r->length = length;
+	r->send = 0;
+	
+	list_add_tail (&r->list, 
+		       &(rndis_per_dev_params [configNr].resp_queue));
+	return r;
+}
+
+int rndis_rm_hdr (u8 *buf, u32 *length)
+{
+	u32 i, messageLen, dataOffset, *tmp;
+	
+	tmp = (u32 *) buf; 
+
+	if (!buf || !length) return -1;
+	if (cpu_to_le32p(tmp++) != 1) return -1;
+	
+	messageLen = cpu_to_le32p(tmp++);
+	dataOffset = cpu_to_le32p(tmp++) + 8;
+
+	if (messageLen < dataOffset || messageLen > *length) return -1;
+	
+	for (i = dataOffset; i < messageLen; i++)
+		buf [i - dataOffset] = buf [i];
+		
+	*length = messageLen - dataOffset;
+	
+	return 0;
+}
+
+int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof, 
+		     void *data)
+{
+	char *out = page;
+	int len;
+	rndis_params *param = (rndis_params *) data;
+	
+	out += snprintf (out, count, 
+			 "Config Nr. %d\n"
+			 "used      : %s\n"
+			 "state     : %s\n"
+			 "medium    : 0x%08X\n"
+			 "speed     : %d\n"
+			 "cable     : %s\n"
+			 "vendor ID : 0x%08X\n"
+			 "vendor    : %s\n", 
+			 param->confignr, (param->used) ? "y" : "n", 
+			 ({ char *s = "?";
+			 switch (param->state) {
+			 case RNDIS_UNINITIALIZED:
+				s = "RNDIS_UNINITIALIZED"; break;
+			 case RNDIS_INITIALIZED:
+				s = "RNDIS_INITIALIZED"; break;
+			 case RNDIS_DATA_INITIALIZED:
+				s = "RNDIS_DATA_INITIALIZED"; break;
+			}; s; }),
+			 param->medium, 
+			 (param->media_state) ? 0 : param->speed*100, 
+			 (param->media_state) ? "disconnected" : "connected",
+			 param->vendorID, param->vendorDescr);      
+	
+	len = out - page;
+	len -= off;
+	
+	if (len < count) {
+		*eof = 1;
+		if (len <= 0)
+			return 0;
+	} else
+		len = count;
+	
+	*start = page + off;
+	return len;
+}
+
+int rndis_proc_write (struct file *file, const char *buffer, 
+		      unsigned long count, void *data)
+{
+	u32 speed = 0;
+	int i, fl_speed = 0;
+	
+	for (i = 0; i < count; i++) {
+		switch (*buffer) {
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+		case '8':
+		case '9':
+			fl_speed = 1;
+			speed = speed*10 + *buffer - '0';
+			break;
+		case 'C':
+		case 'c':
+			rndis_signal_connect (((rndis_params *) data)
+				->confignr);
+			break;
+		case 'D':
+		case 'd':
+			rndis_signal_disconnect (((rndis_params *) data)
+				->confignr);
+			break;
+		default: 
+			if (fl_speed) ((rndis_params *) data)->speed = speed;
+			else DEBUG ("%c is not valid\n", *buffer);
+			break;
+		}
+		
+		buffer++;
+	}
+	
+	return count;
+}
+
+int __init rndis_init (void)
+{
+	u8 i;
+	char name [4];
+
+	/* FIXME this should probably be /proc/driver/rndis,
+	 * and only if debugging is enabled
+	 */
+	
+	if (!(rndis_connect_dir =  proc_mkdir ("rndis", NULL))) {
+		printk (KERN_ERR "%s: couldn't create /proc/rndis entry", 
+			__FUNCTION__);
+		return -EIO;
+	}
+	
+	for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
+		sprintf (name, "%03d", i);
+		if (!(rndis_connect_state [i]
+				= create_proc_entry (name, 0660,
+						rndis_connect_dir))) 
+		{
+			DEBUG ("%s :remove entries", __FUNCTION__);
+			for (i--; i > 0; i--) {
+				sprintf (name, "%03d", i);
+				remove_proc_entry (name, rndis_connect_dir);
+			}
+			DEBUG ("\n");
+			
+			remove_proc_entry ("000", rndis_connect_dir);
+			remove_proc_entry ("rndis", NULL);
+			return -EIO;
+		}
+		rndis_connect_state [i]->nlink = 1;
+		rndis_connect_state [i]->write_proc = rndis_proc_write;
+		rndis_connect_state [i]->read_proc = rndis_proc_read;
+		rndis_connect_state [i]->data = (void *)
+				(rndis_per_dev_params + i);
+		rndis_per_dev_params [i].confignr = i;
+		rndis_per_dev_params [i].used = 0;
+		rndis_per_dev_params [i].state = RNDIS_UNINITIALIZED;
+		rndis_per_dev_params [i].media_state
+				= NDIS_MEDIA_STATE_DISCONNECTED;
+		INIT_LIST_HEAD (&(rndis_per_dev_params [i].resp_queue));
+	}
+	
+	return 0;
+}
+
+void rndis_exit (void)
+{
+	u8 i;
+	char name [4];
+	
+	for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
+		sprintf (name, "%03d", i);
+		remove_proc_entry (name, rndis_connect_dir);
+	}
+	remove_proc_entry ("rndis", NULL);
+	return;
+}
+
diff -urN linux-2.4.27-rc1/drivers/usb/gadget/rndis.h linux-2.4.27-rc2/drivers/usb/gadget/rndis.h
--- linux-2.4.27-rc1/drivers/usb/gadget/rndis.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.27-rc2/drivers/usb/gadget/rndis.h	2004-06-25 20:48:26.871103312 -0700
@@ -0,0 +1,348 @@
+/* 
+ * RNDIS	Definitions for Remote NDIS
+ * 
+ * Version:	$Id: rndis.h,v 1.15 2004/03/25 21:33:46 robert Exp $
+ * 
+ * Authors:	Benedikt Spranger, Pengutronix
+ * 		Robert Schwebel, Pengutronix
+ * 
+ * 		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 software was originally developed in conformance with
+ *		Microsoft's Remote NDIS Specification License Agreement.
+ */
+
+#ifndef _LINUX_RNDIS_H
+#define _LINUX_RNDIS_H
+
+#include "ndis.h"
+
+#define RNDIS_MAXIMUM_FRAME_SIZE	1518
+#define RNDIS_MAX_TOTAL_SIZE		1558
+
+/* Remote NDIS Versions */
+#define RNDIS_MAJOR_VERSION		1
+#define RNDIS_MINOR_VERSION		0
+
+/* Status Values */
+#define RNDIS_STATUS_SUCCESS		0x00000000U	/* Success           */
+#define RNDIS_STATUS_FAILURE		0xC0000001U	/* Unspecified error */
+#define RNDIS_STATUS_INVALID_DATA	0xC0010015U	/* Invalid data      */
+#define RNDIS_STATUS_NOT_SUPPORTED	0xC00000BBU	/* Unsupported request */
+#define RNDIS_STATUS_MEDIA_CONNECT	0x4001000BU	/* Device connected  */
+#define RNDIS_STATUS_MEDIA_DISCONNECT	0x4001000CU	/* Device disconnected */
+/* For all not specified status messages:
+ * RNDIS_STATUS_Xxx -> NDIS_STATUS_Xxx 
+ */
+
+/* Message Set for Connectionless (802.3) Devices */
+#define REMOTE_NDIS_INITIALIZE_MSG	0x00000002U	/* Initialize device */
+#define REMOTE_NDIS_HALT_MSG		0x00000003U
+#define REMOTE_NDIS_QUERY_MSG		0x00000004U
+#define REMOTE_NDIS_SET_MSG		0x00000005U
+#define REMOTE_NDIS_RESET_MSG		0x00000006U
+#define REMOTE_NDIS_INDICATE_STATUS_MSG	0x00000007U
+#define REMOTE_NDIS_KEEPALIVE_MSG	0x00000008U
+
+/* Message completion */
+#define REMOTE_NDIS_INITIALIZE_CMPLT	0x80000002U
+#define REMOTE_NDIS_QUERY_CMPLT		0x80000004U
+#define REMOTE_NDIS_SET_CMPLT		0x80000005U
+#define REMOTE_NDIS_RESET_CMPLT		0x80000006U
+#define REMOTE_NDIS_KEEPALIVE_CMPLT	0x80000008U
+
+/* Device Flags */
+#define RNDIS_DF_CONNECTIONLESS		0x00000001U
+#define RNDIS_DF_CONNECTION_ORIENTED	0x00000002U
+
+#define RNDIS_MEDIUM_802_3		0x00000000U
+
+/* from drivers/net/sk98lin/h/skgepnmi.h */
+#define OID_PNP_CAPABILITIES			0xFD010100
+#define OID_PNP_SET_POWER			0xFD010101
+#define OID_PNP_QUERY_POWER			0xFD010102
+#define OID_PNP_ADD_WAKE_UP_PATTERN		0xFD010103
+#define OID_PNP_REMOVE_WAKE_UP_PATTERN		0xFD010104
+#define OID_PNP_ENABLE_WAKE_UP			0xFD010106
+
+
+/* supported OIDs */
+static const u32 oid_supported_list [] = 
+{
+	/* the general stuff */
+	OID_GEN_SUPPORTED_LIST,
+	OID_GEN_HARDWARE_STATUS,
+	OID_GEN_MEDIA_SUPPORTED,
+	OID_GEN_MEDIA_IN_USE,
+	OID_GEN_MAXIMUM_FRAME_SIZE,
+	OID_GEN_LINK_SPEED,
+	OID_GEN_TRANSMIT_BLOCK_SIZE,
+	OID_GEN_RECEIVE_BLOCK_SIZE,
+	OID_GEN_VENDOR_ID,
+	OID_GEN_VENDOR_DESCRIPTION,
+	OID_GEN_VENDOR_DRIVER_VERSION,
+	OID_GEN_CURRENT_PACKET_FILTER,
+	OID_GEN_MAXIMUM_TOTAL_SIZE,
+	OID_GEN_MEDIA_CONNECT_STATUS,
+	OID_GEN_PHYSICAL_MEDIUM,
+#if 0
+	OID_GEN_RNDIS_CONFIG_PARAMETER,
+#endif
+	
+	/* the statistical stuff */
+	OID_GEN_XMIT_OK,
+	OID_GEN_RCV_OK,
+	OID_GEN_XMIT_ERROR,
+	OID_GEN_RCV_ERROR,
+	OID_GEN_RCV_NO_BUFFER,
+#ifdef	RNDIS_OPTIONAL_STATS
+	OID_GEN_DIRECTED_BYTES_XMIT,
+	OID_GEN_DIRECTED_FRAMES_XMIT,
+	OID_GEN_MULTICAST_BYTES_XMIT,
+	OID_GEN_MULTICAST_FRAMES_XMIT,
+	OID_GEN_BROADCAST_BYTES_XMIT,
+	OID_GEN_BROADCAST_FRAMES_XMIT,
+	OID_GEN_DIRECTED_BYTES_RCV,
+	OID_GEN_DIRECTED_FRAMES_RCV,
+	OID_GEN_MULTICAST_BYTES_RCV,
+	OID_GEN_MULTICAST_FRAMES_RCV,
+	OID_GEN_BROADCAST_BYTES_RCV,
+	OID_GEN_BROADCAST_FRAMES_RCV,
+	OID_GEN_RCV_CRC_ERROR,
+	OID_GEN_TRANSMIT_QUEUE_LENGTH,
+#endif	/* RNDIS_OPTIONAL_STATS */
+
+    	/* mandatory 802.3 */
+	/* the general stuff */
+	OID_802_3_PERMANENT_ADDRESS,
+	OID_802_3_CURRENT_ADDRESS,
+	OID_802_3_MULTICAST_LIST,
+	OID_802_3_MAC_OPTIONS,
+	OID_802_3_MAXIMUM_LIST_SIZE,
+	
+	/* the statistical stuff */
+	OID_802_3_RCV_ERROR_ALIGNMENT,
+	OID_802_3_XMIT_ONE_COLLISION,
+	OID_802_3_XMIT_MORE_COLLISIONS,
+#ifdef	RNDIS_OPTIONAL_STATS
+	OID_802_3_XMIT_DEFERRED,
+	OID_802_3_XMIT_MAX_COLLISIONS,
+	OID_802_3_RCV_OVERRUN,
+	OID_802_3_XMIT_UNDERRUN,
+	OID_802_3_XMIT_HEARTBEAT_FAILURE,
+	OID_802_3_XMIT_TIMES_CRS_LOST,
+	OID_802_3_XMIT_LATE_COLLISIONS,
+#endif	/* RNDIS_OPTIONAL_STATS */
+
+#ifdef	RNDIS_PM
+	/* PM and wakeup are mandatory for USB: */
+
+	/* power management */
+	OID_PNP_CAPABILITIES,
+	OID_PNP_QUERY_POWER,
+	OID_PNP_SET_POWER,
+
+	/* wake up host */
+	OID_PNP_ENABLE_WAKE_UP,
+	OID_PNP_ADD_WAKE_UP_PATTERN,
+	OID_PNP_REMOVE_WAKE_UP_PATTERN,
+#endif
+};
+
+
+typedef struct rndis_init_msg_type 
+{
+	u32	MessageType;
+	u32	MessageLength;
+	u32	RequestID;
+	u32	MajorVersion;
+	u32	MinorVersion;
+	u32	MaxTransferSize;
+} rndis_init_msg_type;
+
+typedef struct rndis_init_cmplt_type
+{
+	u32	MessageType;
+	u32	MessageLength;
+	u32	RequestID;
+	u32	Status;
+	u32	MajorVersion;
+	u32	MinorVersion;
+	u32	DeviceFlags;
+	u32	Medium;
+	u32	MaxPacketsPerTransfer;
+	u32	MaxTransferSize;
+	u32	PacketAlignmentFactor;
+	u32	AFListOffset;
+	u32	AFListSize;
+} rndis_init_cmplt_type;
+
+typedef struct rndis_halt_msg_type
+{
+	u32	MessageType;
+	u32	MessageLength;
+	u32	RequestID;
+} rndis_halt_msg_type;
+
+typedef struct rndis_query_msg_type
+{
+	u32	MessageType;
+	u32	MessageLength;
+	u32	RequestID;
+	u32	OID;
+	u32	InformationBufferLength;
+	u32	InformationBufferOffset;
+	u32	DeviceVcHandle;
+} rndis_query_msg_type;
+
+typedef struct rndis_query_cmplt_type
+{
+	u32	MessageType;
+	u32	MessageLength;
+	u32	RequestID;
+	u32	Status;
+	u32	InformationBufferLength;
+	u32	InformationBufferOffset;
+} rndis_query_cmplt_type;
+
+typedef struct rndis_set_msg_type
+{
+	u32	MessageType;
+	u32	MessageLength;
+	u32	RequestID;
+	u32	OID;
+	u32	InformationBufferLength;
+	u32	InformationBufferOffset;
+	u32	DeviceVcHandle;
+} rndis_set_msg_type;
+
+typedef struct rndis_set_cmplt_type
+{
+	u32	MessageType;
+	u32	MessageLength;
+	u32	RequestID;
+	u32	Status;
+} rndis_set_cmplt_type;
+
+typedef struct rndis_reset_msg_type
+{
+	u32	MessageType;
+	u32	MessageLength;
+	u32	Reserved;
+} rndis_reset_msg_type;
+
+typedef struct rndis_reset_cmplt_type
+{
+	u32	MessageType;
+	u32	MessageLength;
+	u32	Status;
+	u32	AddressingReset;
+} rndis_reset_cmplt_type;
+
+typedef struct rndis_indicate_status_msg_type
+{
+	u32	MessageType;
+	u32	MessageLength;
+	u32	Status;
+	u32	StatusBufferLength;
+	u32	StatusBufferOffset;
+} rndis_indicate_status_msg_type;
+
+typedef struct rndis_keepalive_msg_type
+{
+	u32	MessageType;
+	u32	MessageLength;
+	u32	RequestID;
+} rndis_keepalive_msg_type;
+
+typedef struct rndis_keepalive_cmplt_type
+{
+	u32	MessageType;
+	u32	MessageLength;
+	u32	RequestID;
+	u32	Status;
+} rndis_keepalive_cmplt_type;
+
+struct rndis_packet_msg_type
+{
+	u32	MessageType;
+	u32	MessageLength;
+	u32	DataOffset;
+	u32	DataLength;
+	u32	OOBDataOffset;
+	u32	OOBDataLength;
+	u32	NumOOBDataElements;
+	u32	PerPacketInfoOffset;
+	u32	PerPacketInfoLength;
+	u32	VcHandle;
+	u32	Reserved;
+};
+
+struct rndis_config_parameter
+{
+	u32	ParameterNameOffset;
+	u32	ParameterNameLength;
+	u32	ParameterType;
+	u32	ParameterValueOffset;
+	u32	ParameterValueLength;
+};
+
+/* implementation specific */
+enum rndis_state
+{
+	RNDIS_UNINITIALIZED,
+	RNDIS_INITIALIZED,
+	RNDIS_DATA_INITIALIZED,
+};
+
+typedef struct rndis_resp_t
+{
+	struct list_head	list;
+	u8			*buf;
+	u32			length;
+	int			send;
+} rndis_resp_t;
+
+typedef struct rndis_params
+{
+	u8			confignr;
+	int			used;
+	enum rndis_state	state;
+	u32			filter;
+	u32			medium;
+	u32			speed;
+	u32			media_state;
+	const u8		*host_mac;
+	struct net_device 	*dev;
+	struct net_device_stats *stats;
+	u32			vendorID;
+	const char		*vendorDescr;
+	int 			(*ack) (struct net_device *);
+	struct list_head	resp_queue;
+} rndis_params;
+
+/* RNDIS Message parser and other useless functions */
+int  rndis_msg_parser (u8 configNr, u8 *buf);
+int  rndis_register (int (*rndis_control_ack) (struct net_device *));
+void rndis_deregister (int configNr);
+int  rndis_set_param_dev (u8 configNr, struct net_device *dev,
+			 struct net_device_stats *stats);
+int  rndis_set_param_vendor (u8 configNr, u32 vendorID, 
+			    const char *vendorDescr);
+int  rndis_set_param_medium (u8 configNr, u32 medium, u32 speed);
+void rndis_add_hdr (struct sk_buff *skb);
+int  rndis_rm_hdr (u8 *buf, u32 *length);
+u8   *rndis_get_next_response (int configNr, u32 *length);
+void rndis_free_response (int configNr, u8 *buf);
+
+int  rndis_signal_connect (int configNr);
+int  rndis_signal_disconnect (int configNr);
+int  rndis_state (int configNr);
+extern void rndis_set_host_mac (int configNr, const u8 *addr);
+
+int __init rndis_init (void);
+void rndis_exit (void);
+
+#endif  /* _LINUX_RNDIS_H */
diff -urN linux-2.4.27-rc1/drivers/usb/gadget/usbstring.c linux-2.4.27-rc2/drivers/usb/gadget/usbstring.c
--- linux-2.4.27-rc1/drivers/usb/gadget/usbstring.c	2004-04-14 06:05:32.000000000 -0700
+++ linux-2.4.27-rc2/drivers/usb/gadget/usbstring.c	2004-06-25 20:48:26.876103520 -0700
@@ -11,11 +11,12 @@
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/string.h>
-#include <linux/version.h>
+#include <linux/init.h>
 
 #include <linux/usb_ch9.h>
 #include <linux/usb_gadget.h>
 
+#include <asm/byteorder.h>
 #include <asm/unaligned.h>
 
 
diff -urN linux-2.4.27-rc1/drivers/usb/gadget/zero.c linux-2.4.27-rc2/drivers/usb/gadget/zero.c
--- linux-2.4.27-rc1/drivers/usb/gadget/zero.c	2004-04-14 06:05:32.000000000 -0700
+++ linux-2.4.27-rc2/drivers/usb/gadget/zero.c	2004-06-25 20:48:26.888104021 -0700
@@ -87,10 +87,12 @@
 #include <linux/usb_ch9.h>
 #include <linux/usb_gadget.h>
 
+#include "gadget_chips.h"
+
 
 /*-------------------------------------------------------------------------*/
 
-#define DRIVER_VERSION		"Bastille Day 2003"
+#define DRIVER_VERSION		"St Patrick's Day 2004"
 
 static const char shortname [] = "zero";
 static const char longname [] = "Gadget Zero";
@@ -103,100 +105,12 @@
 /*
  * driver assumes self-powered hardware, and
  * has no way for users to trigger remote wakeup.
- */
-
-/*
- * hardware-specific configuration, controlled by which device
- * controller driver was configured.
- *
- * CHIP ... hardware identifier
- * DRIVER_VERSION_NUM ... alerts the host side driver to differences
- * EP_*_NAME ... which endpoints do we use for which purpose?
- * EP_*_NUM ... numbers for them (often limited by hardware)
  *
- * add other defines for other portability issues, like hardware that
- * for some reason doesn't handle full speed bulk maxpacket of 64.
+ * this version autoconfigures as much as possible,
+ * which is reasonable for most "bulk-only" drivers.
  */
-
-/*
- * DRIVER_VERSION_NUM 0x0000 (?):  Martin Diehl's ezusb an21/fx code
- */
-
-/*
- * NetChip 2280, PCI based.
- *
- * This has half a dozen configurable endpoints, four with dedicated
- * DMA channels to manage their FIFOs.  It supports high speed.
- * Those endpoints can be arranged in any desired configuration.
- */
-#ifdef	CONFIG_USB_GADGET_NET2280
-#define CHIP			"net2280"
-#define DRIVER_VERSION_NUM	0x0111
-static const char EP_OUT_NAME [] = "ep-a";
-#define EP_OUT_NUM	2
-static const char EP_IN_NAME [] = "ep-b";
-#define EP_IN_NUM	2
-#endif
-
-/*
- * PXA-2xx UDC:  widely used in second gen Linux-capable PDAs.
- *
- * This has fifteen fixed-function full speed endpoints, and it
- * can support all USB transfer types.
- *
- * These supports three or four configurations, with fixed numbers.
- * The hardware interprets SET_INTERFACE, net effect is that you
- * can't use altsettings or reset the interfaces independently.
- * So stick to a single interface.
- */
-#ifdef	CONFIG_USB_GADGET_PXA2XX
-#define CHIP			"pxa2xx"
-#define DRIVER_VERSION_NUM	0x0113
-static const char EP_OUT_NAME [] = "ep12out-bulk";
-#define EP_OUT_NUM	12
-static const char EP_IN_NAME [] = "ep11in-bulk";
-#define EP_IN_NUM	11
-#endif
-
-/*
- * SA-1100 UDC:  widely used in first gen Linux-capable PDAs.
- *
- * This has only two fixed function endpoints, which can only
- * be used for bulk (or interrupt) transfers.  (Plus control.)
- *
- * Since it can't flush its TX fifos without disabling the UDC,
- * the current configuration or altsettings can't change except
- * in special situations.  So this is a case of "choose it right
- * during enumeration" ...
- */
-#ifdef	CONFIG_USB_GADGET_SA1100
-#define CHIP			"sa1100"
-#define DRIVER_VERSION_NUM	0x0115
-static const char EP_OUT_NAME [] = "ep1out-bulk";
-#define EP_OUT_NUM	1
-static const char EP_IN_NAME [] = "ep2in-bulk";
-#define EP_IN_NUM	2
-#endif
-
-/*
- * Toshiba TC86C001 ("Goku-S") UDC
- *
- * This has three semi-configurable full speed bulk/interrupt endpoints.
- */
-#ifdef	CONFIG_USB_GADGET_GOKU
-#define CHIP			"goku"
-#define DRIVER_VERSION_NUM	0x0116
-static const char EP_OUT_NAME [] = "ep1-bulk";
-#define EP_OUT_NUM	1
-static const char EP_IN_NAME [] = "ep2-bulk";
-#define EP_IN_NUM	2
-#endif
-
-/*-------------------------------------------------------------------------*/
-
-#ifndef EP_OUT_NUM
-#	error Configure some USB peripheral controller driver!
-#endif
+static const char *EP_IN_NAME;		/* source */
+static const char *EP_OUT_NAME;		/* sink */
 
 /*-------------------------------------------------------------------------*/
 
@@ -214,6 +128,9 @@
 	 */
 	u8			config;
 	struct usb_ep		*in_ep, *out_ep;
+
+	/* autoresume timer */
+	struct timer_list	resume;
 };
 
 #define xprintk(d,level,fmt,args...) \
@@ -221,20 +138,19 @@
 		## args)
 
 #ifdef DEBUG
-#undef DEBUG
-#define DEBUG(dev,fmt,args...) \
+#define DBG(dev,fmt,args...) \
 	xprintk(dev , KERN_DEBUG , fmt , ## args)
 #else
-#define DEBUG(dev,fmt,args...) \
+#define DBG(dev,fmt,args...) \
 	do { } while (0)
 #endif /* DEBUG */
 
 #ifdef VERBOSE
-#define VDEBUG	DEBUG
+#define VDBG	DBG
 #else
-#define VDEBUG(dev,fmt,args...) \
+#define VDBG(dev,fmt,args...) \
 	do { } while (0)
-#endif /* DEBUG */
+#endif /* VERBOSE */
 
 #define ERROR(dev,fmt,args...) \
 	xprintk(dev , KERN_ERR , fmt , ## args)
@@ -270,6 +186,13 @@
 MODULE_PARM (loopdefault, "b");
 MODULE_PARM_DESC (loopdefault, "true to have default config be loopback");
 
+/*
+ * if it's nonzero, autoresume says how many seconds to wait
+ * before trying to wake up the host after suspend.
+ */
+static unsigned autoresume = 0;
+MODULE_PARM (autoresume, "i");
+
 /*-------------------------------------------------------------------------*/
 
 /* Thanks to NetChip Technologies for donating this product ID.
@@ -310,14 +233,13 @@
 
 	.idVendor =		__constant_cpu_to_le16 (DRIVER_VENDOR_NUM),
 	.idProduct =		__constant_cpu_to_le16 (DRIVER_PRODUCT_NUM),
-	.bcdDevice =		__constant_cpu_to_le16 (DRIVER_VERSION_NUM),
 	.iManufacturer =	STRING_MANUFACTURER,
 	.iProduct =		STRING_PRODUCT,
 	.iSerialNumber =	STRING_SERIAL,
 	.bNumConfigurations =	2,
 };
 
-static const struct usb_config_descriptor
+static struct usb_config_descriptor
 source_sink_config = {
 	.bLength =		sizeof source_sink_config,
 	.bDescriptorType =	USB_DT_CONFIG,
@@ -330,7 +252,7 @@
 	.bMaxPower =		1,	/* self-powered */
 };
 
-static const struct usb_config_descriptor
+static struct usb_config_descriptor
 loopback_config = {
 	.bLength =		sizeof loopback_config,
 	.bDescriptorType =	USB_DT_CONFIG,
@@ -367,24 +289,22 @@
 
 /* two full speed bulk endpoints; their use is config-dependent */
 
-static const struct usb_endpoint_descriptor
+static struct usb_endpoint_descriptor
 fs_source_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
-	.bEndpointAddress =	EP_IN_NUM | USB_DIR_IN,
+	.bEndpointAddress =	USB_DIR_IN,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
-	.wMaxPacketSize =	__constant_cpu_to_le16 (64),
 };
 
-static const struct usb_endpoint_descriptor
+static struct usb_endpoint_descriptor
 fs_sink_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 
-	.bEndpointAddress =	EP_OUT_NUM,
+	.bEndpointAddress =	USB_DIR_OUT,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
-	.wMaxPacketSize =	__constant_cpu_to_le16 (64),
 };
 
 static const struct usb_descriptor_header *fs_source_sink_function [] = {
@@ -648,7 +568,7 @@
 	case -ECONNABORTED: 		/* hardware forced ep reset */
 	case -ECONNRESET:		/* request dequeued */
 	case -ESHUTDOWN:		/* disconnect from host */
-		VDEBUG (dev, "%s gone (%d), %d/%d\n", ep->name, status,
+		VDBG (dev, "%s gone (%d), %d/%d\n", ep->name, status,
 				req->actual, req->length);
 		if (ep == dev->out_ep)
 			check_read_data (dev, ep, req);
@@ -661,7 +581,7 @@
 					 */
 	default:
 #if 1
-		DEBUG (dev, "%s complete --> %d, %d/%d\n", ep->name,
+		DBG (dev, "%s complete --> %d, %d/%d\n", ep->name,
 				status, req->actual, req->length);
 #endif
 	case -EREMOTEIO:		/* short read */
@@ -752,7 +672,7 @@
 		break;
 	}
 	if (result == 0)
-		DEBUG (dev, "buflen %d\n", buflen);
+		DBG (dev, "buflen %d\n", buflen);
 
 	/* caller is responsible for cleanup on error */
 	return result;
@@ -863,14 +783,14 @@
 				req->complete = loopback_complete;
 				result = usb_ep_queue (ep, req, GFP_ATOMIC);
 				if (result)
-					DEBUG (dev, "%s queue req --> %d\n",
+					DBG (dev, "%s queue req --> %d\n",
 							ep->name, result);
 			} else
 				result = -ENOMEM;
 		}
 	}
 	if (result == 0)
-		DEBUG (dev, "qlen %d, buflen %d\n", qlen, buflen);
+		DBG (dev, "qlen %d, buflen %d\n", qlen, buflen);
 
 	/* caller is responsible for cleanup on error */
 	return result;
@@ -883,7 +803,7 @@
 	if (dev->config == 0)
 		return;
 
-	DEBUG (dev, "reset config\n");
+	DBG (dev, "reset config\n");
 
 	/* just disable endpoints, forcing completion of pending i/o.
 	 * all our completion handlers free their requests in this case.
@@ -918,13 +838,11 @@
 	if (number == dev->config)
 		return 0;
 
-#ifdef CONFIG_USB_GADGET_SA1100
-	if (dev->config) {
+	if (gadget_is_sa1100 (gadget) && dev->config) {
 		/* tx fifo is full, but we can't clear it...*/
 		INFO (dev, "can't change configurations\n");
 		return -ESPIPE;
 	}
-#endif
 	zero_reset_config (dev);
 
 	switch (number) {
@@ -968,7 +886,7 @@
 static void zero_setup_complete (struct usb_ep *ep, struct usb_request *req)
 {
 	if (req->status || req->actual != req->length)
-		DEBUG ((struct zero_dev *) ep->driver_data,
+		DBG ((struct zero_dev *) ep->driver_data,
 				"setup complete --> %d, %d/%d\n",
 				req->status, req->actual, req->length);
 }
@@ -1116,7 +1034,7 @@
 
 	default:
 unknown:
-		VDEBUG (dev,
+		VDBG (dev,
 			"unknown control req%02x.%02x v%04x i%04x l%d\n",
 			ctrl->bRequestType, ctrl->bRequest,
 			ctrl->wValue, ctrl->wIndex, ctrl->wLength);
@@ -1127,7 +1045,7 @@
 		req->length = value;
 		value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
 		if (value < 0) {
-			DEBUG (dev, "ep_queue --> %d\n", value);
+			DBG (dev, "ep_queue --> %d\n", value);
 			req->status = 0;
 			zero_setup_complete (gadget->ep0, req);
 		}
@@ -1157,6 +1075,19 @@
 	 */
 }
 
+static void
+zero_autoresume (unsigned long _dev)
+{
+	struct zero_dev	*dev = (struct zero_dev *) _dev;
+	int		status;
+
+	/* normally the host would be woken up for something
+	 * more significant than just a timer firing...
+	 */
+	status = usb_gadget_wakeup (dev->gadget);
+	DBG (dev, "wakeup --> %d\n", status);
+}
+
 /*-------------------------------------------------------------------------*/
 
 static void
@@ -1164,11 +1095,12 @@
 {
 	struct zero_dev		*dev = get_gadget_data (gadget);
 
-	DEBUG (dev, "unbind\n");
+	DBG (dev, "unbind\n");
 
 	/* we've already been disconnected ... no i/o is active */
 	if (dev->req)
 		free_ep_req (gadget->ep0, dev->req);
+	del_timer_sync (&dev->resume);
 	kfree (dev);
 	set_gadget_data (gadget, 0);
 }
@@ -1177,7 +1109,70 @@
 zero_bind (struct usb_gadget *gadget)
 {
 	struct zero_dev		*dev;
+	struct usb_ep		*ep;
+
+	/* Bulk-only drivers like this one SHOULD be able to
+	 * autoconfigure on any sane usb controller driver,
+	 * but there may also be important quirks to address.
+	 */
+	usb_ep_autoconfig_reset (gadget);
+	ep = usb_ep_autoconfig (gadget, &fs_source_desc);
+	if (!ep) {
+autoconf_fail:
+		printk (KERN_ERR "%s: can't autoconfigure on %s\n",
+			shortname, gadget->name);
+		return -ENODEV;
+	}
+	EP_IN_NAME = ep->name;
+	ep->driver_data = ep;	/* claim */
+	
+	ep = usb_ep_autoconfig (gadget, &fs_sink_desc);
+	if (!ep)
+		goto autoconf_fail;
+	EP_OUT_NAME = ep->name;
+	ep->driver_data = ep;	/* claim */
+
+
+	/*
+	 * DRIVER POLICY CHOICE:  you may want to do this differently.
+	 * One thing to avoid is reusing a bcdDevice revision code
+	 * with different host-visible configurations or behavior
+	 * restrictions -- using ep1in/ep2out vs ep1out/ep3in, etc
+	 */
+	if (gadget_is_net2280 (gadget)) {
+		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0201);
+	} else if (gadget_is_pxa (gadget)) {
+		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0203);
+#if 0
+	} else if (gadget_is_sh(gadget)) {
+		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0204);
+		/* SH has only one configuration; see "loopdefault" */
+		device_desc.bNumConfigurations = 1;
+		/* FIXME make 1 == default.bConfigurationValue */
+#endif
+	} else if (gadget_is_sa1100 (gadget)) {
+		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0205);
+	} else if (gadget_is_goku (gadget)) {
+		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0206);
+	} else if (gadget_is_mq11xx (gadget)) {
+		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0207);
+	} else if (gadget_is_omap (gadget)) {
+		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208);
+	} else {
+		/* gadget zero is so simple (for now, no altsettings) that
+		 * it SHOULD NOT have problems with bulk-capable hardware.
+		 * so warn about unrcognized controllers, don't panic.
+		 *
+		 * things like configuration and altsetting numbering
+		 * can need hardware-specific attention though.
+		 */
+		printk (KERN_WARNING "%s: controller '%s' not recognized\n",
+			shortname, gadget->name);
+		device_desc.bcdDevice = __constant_cpu_to_le16 (0x9999);
+	}
 
+
+	/* ok, we made sense of the hardware ... */
 	dev = kmalloc (sizeof *dev, SLAB_KERNEL);
 	if (!dev)
 		return -ENOMEM;
@@ -1208,6 +1203,16 @@
 	hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
 #endif
 
+	usb_gadget_set_selfpowered (gadget);
+
+	init_timer (&dev->resume);
+	dev->resume.function = zero_autoresume;
+	dev->resume.data = (unsigned long) dev;
+	if (autoresume) {
+		source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+		loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	}
+
 	gadget->ep0->driver_data = dev;
 
 	INFO (dev, "%s, version: " DRIVER_VERSION "\n", longname);
@@ -1227,6 +1232,33 @@
 
 /*-------------------------------------------------------------------------*/
 
+static void
+zero_suspend (struct usb_gadget *gadget)
+{
+	struct zero_dev		*dev = get_gadget_data (gadget);
+
+	if (gadget->speed == USB_SPEED_UNKNOWN)
+		return;
+
+	if (autoresume) {
+		mod_timer (&dev->resume, jiffies + (HZ * autoresume));
+		DBG (dev, "suspend, wakeup in %d seconds\n", autoresume);
+	} else
+		DBG (dev, "suspend\n");
+}
+
+static void
+zero_resume (struct usb_gadget *gadget)
+{
+	struct zero_dev		*dev = get_gadget_data (gadget);
+
+	DBG (dev, "resume\n");
+	del_timer (&dev->resume);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
 static struct usb_gadget_driver zero_driver = {
 #ifdef CONFIG_USB_GADGET_DUALSPEED
 	.speed		= USB_SPEED_HIGH,
@@ -1240,6 +1272,9 @@
 	.setup		= zero_setup,
 	.disconnect	= zero_disconnect,
 
+	.suspend	= zero_suspend,
+	.resume		= zero_resume,
+
 	.driver 	= {
 		.name		= (char *) shortname,
 		// .shutdown = ...
diff -urN linux-2.4.27-rc1/drivers/usb/serial/usbserial.c linux-2.4.27-rc2/drivers/usb/serial/usbserial.c
--- linux-2.4.27-rc1/drivers/usb/serial/usbserial.c	2004-06-25 20:48:24.185991307 -0700
+++ linux-2.4.27-rc2/drivers/usb/serial/usbserial.c	2004-06-25 20:48:26.952106691 -0700
@@ -297,7 +297,6 @@
 #include <linux/spinlock.h>
 #include <linux/list.h>
 #include <linux/smp_lock.h>
-#include <linux/spinlock.h>
 #include <linux/usb.h>
 
 #ifdef CONFIG_USB_SERIAL_DEBUG
diff -urN linux-2.4.27-rc1/fs/affs/amigaffs.c linux-2.4.27-rc2/fs/affs/amigaffs.c
--- linux-2.4.27-rc1/fs/affs/amigaffs.c	2001-09-11 08:19:35.000000000 -0700
+++ linux-2.4.27-rc2/fs/affs/amigaffs.c	2004-06-25 20:48:27.136114366 -0700
@@ -458,7 +458,6 @@
 	if (!(sb->s_flags & MS_RDONLY))
 		printk(KERN_WARNING "AFFS: Remounting filesystem read-only\n");
 	sb->s_flags |= MS_RDONLY;
-	AFFS_SB->s_flags |= SF_READONLY;	/* Don't allow to remount rw */
 }
 
 void
diff -urN linux-2.4.27-rc1/fs/affs/bitmap.c linux-2.4.27-rc2/fs/affs/bitmap.c
--- linux-2.4.27-rc1/fs/affs/bitmap.c	2002-02-25 11:38:07.000000000 -0800
+++ linux-2.4.27-rc2/fs/affs/bitmap.c	2004-06-25 20:48:27.136114366 -0700
@@ -268,8 +268,7 @@
 	return 0;
 }
 
-int
-affs_init_bitmap(struct super_block *sb)
+int affs_init_bitmap(struct super_block *sb, int *flags)
 {
 	struct affs_bm_info *bm;
 	struct buffer_head *bmap_bh = NULL, *bh = NULL;
@@ -277,13 +276,13 @@
 	u32 size, blk, end, offset, mask;
 	int i, res = 0;
 
-	if (sb->s_flags & MS_RDONLY)
+	if (*flags & MS_RDONLY)
 		return 0;
 
 	if (!AFFS_ROOT_TAIL(sb, AFFS_SB->s_root_bh)->bm_flag) {
 		printk(KERN_NOTICE "AFFS: Bitmap invalid - mounting %s read only\n",
 			kdevname(sb->s_dev));
-		sb->s_flags |= MS_RDONLY;
+		*flags |= MS_RDONLY;
 		return 0;
 	}
 
@@ -296,7 +295,7 @@
 	bm = AFFS_SB->s_bitmap = kmalloc(size, GFP_KERNEL);
 	if (!AFFS_SB->s_bitmap) {
 		printk(KERN_ERR "AFFS: Bitmap allocation failed\n");
-		return 1;
+		return -ENOMEM;
 	}
 	memset(AFFS_SB->s_bitmap, 0, size);
 
@@ -311,13 +310,13 @@
 		bh = affs_bread(sb, bm->bm_key);
 		if (!bh) {
 			printk(KERN_ERR "AFFS: Cannot read bitmap\n");
-			res = 1;
+			res = -EIO;
 			goto out;
 		}
 		if (affs_checksum_block(sb, bh)) {
 			printk(KERN_WARNING "AFFS: Bitmap %u invalid - mounting %s read only.\n",
 			       bm->bm_key, kdevname(sb->s_dev));
-			sb->s_flags |= MS_RDONLY;
+			*flags |= MS_RDONLY;
 			goto out;
 		}
 		pr_debug("AFFS: read bitmap block %d: %d\n", blk, bm->bm_key);
@@ -333,7 +332,7 @@
 		bmap_bh = affs_bread(sb, be32_to_cpu(bmap_blk[blk]));
 		if (!bmap_bh) {
 			printk(KERN_ERR "AFFS: Cannot read bitmap extension\n");
-			res = 1;
+			res = -EIO;
 			goto out;
 		}
 		bmap_blk = (u32 *)bmap_bh->b_data;
@@ -378,3 +377,15 @@
 	affs_brelse(bmap_bh);
 	return res;
 }
+
+void affs_free_bitmap(struct super_block *sb)
+{
+	if (!AFFS_SB->s_bitmap)
+		return;
+
+	affs_brelse(AFFS_SB->s_bmap_bh);
+	AFFS_SB->s_bmap_bh = NULL;
+	AFFS_SB->s_last_bmap = ~0;
+	kfree(AFFS_SB->s_bitmap);
+	AFFS_SB->s_bitmap = NULL;
+}
diff -urN linux-2.4.27-rc1/fs/affs/super.c linux-2.4.27-rc2/fs/affs/super.c
--- linux-2.4.27-rc1/fs/affs/super.c	2002-02-25 11:38:07.000000000 -0800
+++ linux-2.4.27-rc2/fs/affs/super.c	2004-06-25 20:48:27.137114408 -0700
@@ -48,10 +48,9 @@
 		mark_buffer_dirty(AFFS_SB->s_root_bh);
 	}
 
-	affs_brelse(AFFS_SB->s_bmap_bh);
 	if (AFFS_SB->s_prefix)
 		kfree(AFFS_SB->s_prefix);
-	kfree(AFFS_SB->s_bitmap);
+	affs_free_bitmap(sb);
 	affs_brelse(AFFS_SB->s_root_bh);
 
 	return;
@@ -235,6 +234,7 @@
 	gid_t			 gid;
 	int			 reserved;
 	unsigned long		 mount_flags;
+	int			 tmp_flags;	/* fix remount prototype... */
 
 	pr_debug("AFFS: read_super(%s)\n",data ? (const char *)data : "no options");
 
@@ -349,7 +349,6 @@
 		printk(KERN_NOTICE "AFFS: Dircache FS - mounting %s read only\n",
 			kdevname(dev));
 		sb->s_flags |= MS_RDONLY;
-		AFFS_SB->s_flags |= SF_READONLY;
 	}
 	switch (chksum) {
 		case MUFS_FS:
@@ -405,8 +404,10 @@
 	AFFS_SB->s_root_bh = root_bh;
 	/* N.B. after this point s_root_bh must be released */
 
-	if (affs_init_bitmap(sb))
+	tmp_flags = sb->s_flags;
+	if (affs_init_bitmap(sb, &tmp_flags))
 		goto out_error;
+	sb->s_flags = tmp_flags;
 
 	/* set up enough so that it can read an inode */
 
@@ -445,14 +446,14 @@
 	int			 reserved;
 	int			 root_block;
 	unsigned long		 mount_flags;
-	unsigned long		 read_only = AFFS_SB->s_flags & SF_READONLY;
+	int			 res = 0;
 
 	pr_debug("AFFS: remount(flags=0x%x,opts=\"%s\")\n",*flags,data);
 
 	if (!parse_options(data,&uid,&gid,&mode,&reserved,&root_block,
 	    &blocksize,&AFFS_SB->s_prefix,AFFS_SB->s_volume,&mount_flags))
 		return -EINVAL;
-	AFFS_SB->s_flags = mount_flags | read_only;
+	AFFS_SB->s_flags = mount_flags;
 	AFFS_SB->s_mode  = mode;
 	AFFS_SB->s_uid   = uid;
 	AFFS_SB->s_gid   = gid;
@@ -463,14 +464,11 @@
 		sb->s_dirt = 1;
 		while (sb->s_dirt)
 			affs_write_super(sb);
-		sb->s_flags |= MS_RDONLY;
-	} else if (!(AFFS_SB->s_flags & SF_READONLY)) {
-		sb->s_flags &= ~MS_RDONLY;
-	} else {
-		affs_warning(sb,"remount","Cannot remount fs read/write because of errors");
-		return -EINVAL;
-	}
-	return 0;
+		affs_free_bitmap(sb);
+	} else
+		res = affs_init_bitmap(sb, flags);
+
+	return res;
 }
 
 static int
diff -urN linux-2.4.27-rc1/fs/jbd/transaction.c linux-2.4.27-rc2/fs/jbd/transaction.c
--- linux-2.4.27-rc1/fs/jbd/transaction.c	2004-06-25 20:48:24.259994394 -0700
+++ linux-2.4.27-rc2/fs/jbd/transaction.c	2004-06-25 20:48:27.150114950 -0700
@@ -1700,11 +1700,11 @@
 
 	bh = p->buffers;
 
-	printk(KERN_ERR "%s: page index:%u count:%d flags:%x\n", __FUNCTION__,
+	printk(KERN_ERR "%s: page index:%lu count:%d flags:%lx\n", __FUNCTION__,
 		 p->index, atomic_read(&p->count), p->flags);
 
 	while (bh) {
-		printk(KERN_ERR "%s: bh b_next:%p blocknr:%u b_list:%u state:%x\n",
+		printk(KERN_ERR "%s: bh b_next:%p blocknr:%lu b_list:%u state:%lx\n",
 			__FUNCTION__, bh->b_next, bh->b_blocknr, bh->b_list,
 				bh->b_state);
 		bh = bh->b_this_page;
@@ -1769,7 +1769,7 @@
 	do {
 		struct buffer_head *p = tmp;
 
-		if (!unlikely(tmp)) {
+		if (unlikely(!tmp)) {
 			debug_page(page);
 			BUG();
 		}
diff -urN linux-2.4.27-rc1/fs/jfs/jfs_btree.h linux-2.4.27-rc2/fs/jfs/jfs_btree.h
--- linux-2.4.27-rc1/fs/jfs/jfs_btree.h	2004-06-25 20:48:24.260994436 -0700
+++ linux-2.4.27-rc2/fs/jfs/jfs_btree.h	2004-06-25 20:48:27.150114950 -0700
@@ -147,7 +147,7 @@
 	printk("btstack dump:\n");
 	for (i = 0; i < MAXTREEHEIGHT; i++)
 		printk(KERN_ERR "bn = %Lx, index = %d\n",
-		       btstack->stack[i].bn,
+		       (long long)btstack->stack[i].bn,
 		       btstack->stack[i].index);
 }
 
diff -urN linux-2.4.27-rc1/fs/xfs/linux-2.4/kmem.c linux-2.4.27-rc2/fs/xfs/linux-2.4/kmem.c
--- linux-2.4.27-rc1/fs/xfs/linux-2.4/kmem.c	2004-06-25 20:48:24.309996480 -0700
+++ linux-2.4.27-rc2/fs/xfs/linux-2.4/kmem.c	2004-06-25 20:48:27.198116953 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2002 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -33,18 +33,23 @@
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/vmalloc.h>
-#include <linux/slab.h>
+#include <linux/swap.h>
 
 #include "time.h"
 #include "kmem.h"
 
-#define DEF_PRIORITY	(6)
+#define MAX_VMALLOCS	6
 #define MAX_SLAB_SIZE	0x20000
 #define MAX_SHAKE	8
 
 static kmem_shake_func_t	shake_list[MAX_SHAKE];
 static DECLARE_MUTEX(shake_sem);
 
+/*
+ * Old-school memory shaking for vanilla 2.4 kernels (which
+ * have no VM shake callback/registration infrastructure).
+ */
+
 kmem_shaker_t
 kmem_shake_register(kmem_shake_func_t sfunc)
 {
@@ -97,37 +102,21 @@
 void *
 kmem_alloc(size_t size, int flags)
 {
-	int	shrink  = DEF_PRIORITY;	/* # times to try to shrink cache */
-        int     lflags  = kmem_flags_convert(flags);
-        int     nosleep = flags & KM_NOSLEEP;
-	void	*rval;
-
-repeat:
-	if (MAX_SLAB_SIZE < size) {
-		/* Avoid doing filesystem sensitive stuff to get this */
-		rval = __vmalloc(size, lflags, PAGE_KERNEL);
-	} else {
-		rval = kmalloc(size, lflags);
-	}
-
-	if (rval || nosleep)
-		return rval;
+	int	retries = 0, lflags = kmem_flags_convert(flags);
+	void	*ptr;
 
-	/*
-	 * KM_SLEEP callers don't expect a failure
-	 */
-	if (shrink) {
+	do {
+		if (size < MAX_SLAB_SIZE || retries > MAX_VMALLOCS)
+			ptr = kmalloc(size, lflags);
+		else
+			ptr = __vmalloc(size, lflags, PAGE_KERNEL);
+		if (ptr || (flags & (KM_MAYFAIL|KM_NOSLEEP)))
+			return ptr;
 		kmem_shake();
-
-		shrink--;
-		goto repeat;
-	}
-
-	rval = __vmalloc(size, lflags, PAGE_KERNEL);
-	if (!rval && !(flags & KM_MAYFAIL))
-		panic("kmem_alloc: NULL memory on KM_SLEEP request!");
-
-	return rval;
+		if (!(++retries % 100))
+			printk(KERN_ERR "possible deadlock in %s (mode:0x%x)\n",
+					__FUNCTION__, lflags);
+	} while (1);
 }
 
 void *
@@ -136,11 +125,9 @@
 	void	*ptr;
 
 	ptr = kmem_alloc(size, flags);
-
 	if (ptr)
 		memset((char *)ptr, 0, (int)size);
-
-	return (ptr);
+	return ptr;
 }
 
 void
@@ -157,7 +144,7 @@
 void *
 kmem_realloc(void *ptr, size_t newsize, size_t oldsize, int flags)
 {
-	void *new;
+	void	*new;
 
 	new = kmem_alloc(newsize, flags);
 	if (ptr) {
@@ -166,79 +153,33 @@
 				((oldsize < newsize) ? oldsize : newsize));
 		kmem_free(ptr, oldsize);
 	}
-
 	return new;
 }
 
-kmem_zone_t *
-kmem_zone_init(int size, char *zone_name)
-{
-	return kmem_cache_create(zone_name, size, 0, 0, NULL, NULL);
-}
-
 void *
 kmem_zone_alloc(kmem_zone_t *zone, int flags)
 {
-	int	shrink = DEF_PRIORITY;	/* # times to try to shrink cache */
-	void	*ptr = NULL;
-
-repeat:
-	ptr = kmem_cache_alloc(zone, kmem_flags_convert(flags));
-
-	if (ptr || (flags & KM_NOSLEEP))
-		return ptr;
+	int	retries = 0, lflags = kmem_flags_convert(flags);
+	void	*ptr;
 
-	/*
-	 * KM_SLEEP callers don't expect a failure
-	 */
-	if (shrink) {
+	do {
+		ptr = kmem_cache_alloc(zone, lflags);
+		if (ptr || (flags & (KM_MAYFAIL|KM_NOSLEEP)))
+			return ptr;
 		kmem_shake();
-
-		shrink--;
-		goto repeat;
-	}
-
-	if (flags & KM_SLEEP)
-		panic("kmem_zone_alloc: NULL memory on KM_SLEEP request!");
-
-	return NULL;
+		if (!(++retries % 100))
+			printk(KERN_ERR "possible deadlock in %s (mode:0x%x)\n",
+					__FUNCTION__, lflags);
+	} while (1);
 }
 
 void *
 kmem_zone_zalloc(kmem_zone_t *zone, int flags)
 {
-	int	shrink = DEF_PRIORITY;	/* # times to try to shrink cache */
-	void	*ptr = NULL;
-
-repeat:
-	ptr = kmem_cache_alloc(zone, kmem_flags_convert(flags));
-
-	if (ptr) {
-		memset(ptr, 0, kmem_cache_size(zone));
-		return ptr;
-	}
-
-	if (flags & KM_NOSLEEP)
-		return ptr;
-
-	/*
-	 * KM_SLEEP callers don't expect a failure
-	 */
-	if (shrink) {
-		kmem_shake();
-
-		shrink--;
-		goto repeat;
-	}
-
-	if (flags & KM_SLEEP)
-		panic("kmem_zone_zalloc: NULL memory on KM_SLEEP request!");
-
-	return NULL;
-}
+	void	*ptr;
 
-void
-kmem_zone_free(kmem_zone_t *zone, void *ptr)
-{
-	kmem_cache_free(zone, ptr);
+	ptr = kmem_zone_alloc(zone, flags);
+	if (ptr)
+		memset((char *)ptr, 0, kmem_cache_size(zone));
+	return ptr;
 }
diff -urN linux-2.4.27-rc1/fs/xfs/linux-2.4/kmem.h linux-2.4.27-rc2/fs/xfs/linux-2.4/kmem.h
--- linux-2.4.27-rc1/fs/xfs/linux-2.4/kmem.h	2004-06-25 20:48:24.309996480 -0700
+++ linux-2.4.27-rc2/fs/xfs/linux-2.4/kmem.h	2004-06-25 20:48:27.199116994 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -83,7 +83,7 @@
 {
         int lflags;
         
-#if DEBUG
+#ifdef DEBUG
 	if (unlikely(flags & ~(KM_SLEEP|KM_NOSLEEP|KM_NOFS|KM_MAYFAIL))) {
 		printk(KERN_WARNING
 		    "XFS: memory allocation with wrong flags (%x)\n", flags);
@@ -104,10 +104,33 @@
         return lflags;
 }
 
-extern kmem_zone_t  *kmem_zone_init(int, char *);
+static __inline kmem_zone_t *
+kmem_zone_init(int size, char *zone_name)
+{
+	return kmem_cache_create(zone_name, size, 0, 0, NULL, NULL);
+}
+
+static __inline void
+kmem_zone_free(kmem_zone_t *zone, void *ptr)
+{
+	kmem_cache_free(zone, ptr);
+}
+
+static __inline void
+kmem_zone_destroy(kmem_zone_t *zone)
+{
+	if (zone && kmem_cache_destroy(zone))
+		BUG();
+}
+
+static __inline int
+kmem_zone_shrink(kmem_zone_t *zone)
+{
+	return kmem_cache_shrink(zone);
+}
+
 extern void	    *kmem_zone_zalloc(kmem_zone_t *, int);
 extern void	    *kmem_zone_alloc(kmem_zone_t *, int);
-extern void         kmem_zone_free(kmem_zone_t *, void *);
 
 extern void	    *kmem_alloc(size_t, int);
 extern void	    *kmem_realloc(void *, size_t, size_t, int);
@@ -119,6 +142,11 @@
 
 extern kmem_shaker_t	kmem_shake_register(kmem_shake_func_t);
 extern void	    kmem_shake_deregister(kmem_shaker_t);
-static __inline int	kmem_shake_allow(unsigned int mask) { return 1; }
+
+static __inline int
+kmem_shake_allow(unsigned int gfp_mask)
+{
+	return (gfp_mask & __GFP_WAIT);
+}
 
 #endif /* __XFS_SUPPORT_KMEM_H__ */
diff -urN linux-2.4.27-rc1/fs/xfs/linux-2.4/xfs_aops.c linux-2.4.27-rc2/fs/xfs/linux-2.4/xfs_aops.c
--- linux-2.4.27-rc1/fs/xfs/linux-2.4/xfs_aops.c	2004-06-25 20:48:24.314996689 -0700
+++ linux-2.4.27-rc2/fs/xfs/linux-2.4/xfs_aops.c	2004-06-25 20:48:27.203117161 -0700
@@ -153,22 +153,9 @@
 	vnode_t			*vp = LINVFS_GET_VP(inode);
 	int			error, niomaps = 1;
 
-	if (((flags & (BMAPI_DIRECT|BMAPI_SYNC)) == BMAPI_DIRECT) &&
-	    (offset >= i_size_read(inode)))
-		count = max_t(ssize_t, count, XFS_WRITE_IO_LOG);
-retry:
 	VOP_BMAP(vp, offset, count, flags, iomapp, &niomaps, error);
-	if ((error == EAGAIN) || (error == EIO))
-		return -error;
-	if (unlikely((flags & (BMAPI_WRITE|BMAPI_DIRECT)) ==
-					(BMAPI_WRITE|BMAPI_DIRECT) && niomaps &&
-					(iomapp->iomap_flags & IOMAP_DELAY))) {
-		flags = BMAPI_ALLOCATE;
-		goto retry;
-	}
-	if (flags & (BMAPI_WRITE|BMAPI_ALLOCATE)) {
+	if (!error && (flags & (BMAPI_WRITE|BMAPI_ALLOCATE)))
 		VMODIFY(vp);
-	}
 	return -error;
 }
 
@@ -192,7 +179,7 @@
 
 	if (full_offset < iomapp->iomap_offset)
 		return NULL;
-	if (iomapp->iomap_offset + iomapp->iomap_bsize > full_offset)
+	if (iomapp->iomap_offset + (iomapp->iomap_bsize -1) >= full_offset)
 		return iomapp;
 	return NULL;
 }
@@ -261,7 +248,7 @@
 		*fsbs = 0;
 		bh = head = page_buffers(page);
 		do {
-			if (!buffer_unwritten(bh))
+			if (!buffer_unwritten(bh) || !buffer_uptodate(bh))
 				break;
 			if (!xfs_offset_to_map(page, iomapp, p_offset))
 				break;
@@ -639,13 +626,12 @@
 	pgoff_t			tindex,
 	xfs_iomap_t		*iomapp,
 	int			startio,
-	int			all_bh)
+	int			all_bh,
+	pgoff_t			tlast)
 {
-	pgoff_t			tlast;
 	struct page		*page;
 
-	tlast = (iomapp->iomap_offset + iomapp->iomap_bsize) >> PAGE_CACHE_SHIFT;
-	for (; tindex < tlast; tindex++) {
+	for (; tindex <= tlast; tindex++) {
 		page = xfs_probe_delalloc_page(inode, tindex);
 		if (!page)
 			break;
@@ -681,16 +667,20 @@
 {
 	struct buffer_head	*bh_arr[MAX_BUF_PER_PAGE], *bh, *head;
 	xfs_iomap_t		*iomp, iomap;
-	unsigned long		p_offset = 0, end_index;
 	loff_t			offset;
-	unsigned long long	end_offset;
+	unsigned long           p_offset = 0;
+	__uint64_t              end_offset;
+	pgoff_t                 end_index, last_index, tlast;
 	int			len, err, i, cnt = 0, uptodate = 1;
 	int			flags = startio ? 0 : BMAPI_TRYLOCK;
 	int			page_dirty = 1;
+	int                     delalloc = 0;
 
 
 	/* Are we off the end of the file ? */
-	end_index = i_size_read(inode) >> PAGE_CACHE_SHIFT;
+	offset = i_size_read(inode);
+	end_index = offset >> PAGE_CACHE_SHIFT;
+	last_index = (offset - 1) >> PAGE_CACHE_SHIFT;
 	if (page->index >= end_index) {
 		if ((page->index >= end_index + 1) ||
 		    !(i_size_read(inode) & (PAGE_CACHE_SIZE - 1))) {
@@ -724,6 +714,8 @@
 		 * extent state conversion transaction on completion.
 		 */
 		if (buffer_unwritten(bh)) {
+			if (!startio)
+				continue;
 			if (!iomp) {
 				err = xfs_map_blocks(inode, offset, len, &iomap,
 						BMAPI_READ|BMAPI_IGNSTATE);
@@ -733,7 +725,7 @@
 				iomp = xfs_offset_to_map(page, &iomap,
 								p_offset);
 			}
-			if (iomp && startio) {
+			if (iomp) {
 				if (!bh->b_end_io) {
 					err = xfs_map_unwritten(inode, page,
 							head, bh, p_offset,
@@ -742,7 +734,10 @@
 					if (err) {
 						goto error;
 					}
+				} else {
+					set_bit(BH_Lock, &bh->b_state);
 				}
+				BUG_ON(!buffer_locked(bh));
 				bh_arr[cnt++] = bh;
 				page_dirty = 0;
 			}
@@ -752,6 +747,7 @@
 		 */
 		} else if (buffer_delay(bh)) {
 			if (!iomp) {
+				delalloc = 1;
 				err = xfs_map_blocks(inode, offset, len, &iomap,
 						BMAPI_ALLOCATE | flags);
 				if (err) {
@@ -823,8 +819,14 @@
 	if (startio)
 		xfs_submit_page(page, bh_arr, cnt);
 
-	if (iomp)
-		xfs_cluster_write(inode, page->index + 1, iomp, startio, unmapped);
+	if (iomp) {
+		tlast = (iomp->iomap_offset + iomp->iomap_bsize - 1) >>
+					PAGE_CACHE_SHIFT;
+		if (delalloc && (tlast > last_index))
+			tlast = last_index;
+		xfs_cluster_write(inode, page->index + 1, iomp,
+					startio, unmapped, tlast);
+	}
 
 	return page_dirty;
 
@@ -1177,7 +1179,7 @@
 	size_t			page_offset;
 	xfs_buf_t		*pb;
 	xfs_iomap_t		iomap;
-	int			error = 0;
+	int			niomap, error = 0;
 	int			pb_flags, map_flags, pg_index = 0;
 	size_t			length, total;
 	loff_t			offset, map_size;
@@ -1198,9 +1200,26 @@
 	map_flags = (rw ? BMAPI_WRITE : BMAPI_READ) | BMAPI_DIRECT;
 	pb_flags = (rw ? PBF_WRITE : PBF_READ) | PBF_FORCEIO | PBF_DIRECTIO;
 	while (length) {
-		error = xfs_map_blocks(inode, offset, length, &iomap, map_flags);
+		niomap = 1;
+		VOP_BMAP(vp, offset, length, map_flags, &iomap, &niomap, error);
+		if (unlikely(!error && niomap &&
+				(iomap.iomap_flags & IOMAP_DELAY))) {
+#ifdef DEBUG
+			printk(
+	"XFS: %s - direct IO (%lld:%ld) into a delayed allocate extent?\n",
+				__FUNCTION__, (long long)offset, (long)length);
+			xfs_stack_trace();
+#endif
+			VOP_BMAP(vp, offset, length, BMAPI_ALLOCATE,
+					&iomap, &niomap, error);
+		}
 		if (error)
 			break;
+
+		if (rw == WRITE)
+			VMODIFY(vp);
+
+		BUG_ON(niomap != 1);
 		BUG_ON(iomap.iomap_flags & IOMAP_DELAY);
 
 		map_size = iomap.iomap_bsize - iomap.iomap_delta;
@@ -1244,7 +1263,7 @@
 					>> PAGE_CACHE_SHIFT;
 			if ((pb = pagebuf_lookup(iomap.iomap_target, offset,
 						size, pb_flags)) == NULL) {
-				error = -ENOMEM;
+				error = ENOMEM;
 				break;
 			}
 			/* Need to hook up pagebuf to kiobuf pages */
@@ -1262,11 +1281,8 @@
 
 			pagebuf_rele(pb);
 
-			if (error) {
-				if (error > 0)
-					error = -error;
+			if (error)
 				break;
-			}
 
 			page_offset = (page_offset + size) & ~PAGE_CACHE_MASK;
 			if (page_offset)
@@ -1278,7 +1294,9 @@
 		length -= size;
 	}
 
-	return (error ? error : (int)(total - length));
+	if (error)
+		return -error;
+	return (int)(total - length);
 }
 
 
diff -urN linux-2.4.27-rc1/fs/xfs/linux-2.4/xfs_buf.c linux-2.4.27-rc2/fs/xfs/linux-2.4/xfs_buf.c
--- linux-2.4.27-rc1/fs/xfs/linux-2.4/xfs_buf.c	2004-06-25 20:48:24.317996814 -0700
+++ linux-2.4.27-rc2/fs/xfs/linux-2.4/xfs_buf.c	2004-06-25 20:48:27.206117286 -0700
@@ -98,6 +98,7 @@
  */
 
 STATIC kmem_cache_t *pagebuf_cache;
+STATIC kmem_shaker_t pagebuf_shake;
 
 #define MAX_IO_DAEMONS		NR_CPUS
 #define CPU_TO_DAEMON(cpu)	(cpu)
@@ -118,8 +119,8 @@
 struct buffer_head		*pb_resv_bh = NULL;	/* list of bh */
 int				pb_resv_bh_cnt = 0;	/* # of bh available */
 
-STATIC void pagebuf_daemon_wakeup(void);
 STATIC void _pagebuf_ioapply(xfs_buf_t *);
+STATIC int pagebuf_daemon_wakeup(int, unsigned int);
 STATIC void pagebuf_delwri_queue(xfs_buf_t *, int);
 STATIC void pagebuf_runall_queues(struct list_head[]);
 
@@ -409,6 +410,7 @@
 	error = _pagebuf_get_pages(bp, page_count, flags);
 	if (unlikely(error))
 		return error;
+	bp->pb_flags |= _PBF_PAGE_CACHE;
 
 	first = bp->pb_file_offset >> PAGE_CACHE_SHIFT;
 
@@ -419,8 +421,12 @@
 	      retry:
 		page = find_or_create_page(mapping, first + i, gfp_mask);
 		if (unlikely(page == NULL)) {
-			if (flags & PBF_READ_AHEAD)
+			if (flags & PBF_READ_AHEAD) {
+				bp->pb_page_count = i;
+				for (i = 0; i < bp->pb_page_count; i++)
+					unlock_page(bp->pb_pages[i]);
 				return -ENOMEM;
+			}
 
 			/*
 			 * This could deadlock.
@@ -428,13 +434,13 @@
 			 * But until all the XFS lowlevel code is revamped to
 			 * handle buffer allocation failures we can't do much.
 			 */
-			if (!(++retries % 100)) {
-				printk(KERN_ERR "possibly deadlocking in %s\n",
-						__FUNCTION__);
-			}
+			if (!(++retries % 100))
+				printk(KERN_ERR
+					"possible deadlock in %s (mode:0x%x)\n",
+					__FUNCTION__, gfp_mask);
 
 			XFS_STATS_INC(pb_page_retries);
-			pagebuf_daemon_wakeup();
+			pagebuf_daemon_wakeup(0, gfp_mask);
 			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout(10);
 			goto retry;
@@ -457,8 +463,6 @@
 			unlock_page(bp->pb_pages[i]);
 	}
 
-	bp->pb_flags |= _PBF_PAGE_CACHE;
-
 	if (page_count) {
 		/* if we have any uptodate pages, mark that in the buffer */
 		bp->pb_flags &= ~PBF_NONE;
@@ -550,7 +554,7 @@
 		do {
 			set_current_state(TASK_UNINTERRUPTIBLE);
 			spin_unlock_irqrestore(&pb_resv_bh_lock, flags);
-			blk_run_queues();
+			run_task_queue(&tq_disk);
 			schedule();
 			spin_lock_irqsave(&pb_resv_bh_lock, flags);
 		} while (pb_resv_bh_cnt < 1);
@@ -1103,7 +1107,7 @@
 {
 	PB_TRACE(pb, "lock", 0);
 	if (atomic_read(&pb->pb_io_remaining))
-		blk_run_queues();
+		run_task_queue(&tq_disk);
 	down(&pb->pb_sema);
 	PB_SET_OWNER(pb);
 	PB_TRACE(pb, "locked", 0);
@@ -1199,7 +1203,7 @@
 		if (atomic_read(&pb->pb_pin_count) == 0)
 			break;
 		if (atomic_read(&pb->pb_io_remaining))
-			blk_run_queues();
+			run_task_queue(&tq_disk);
 		schedule();
 	}
 	remove_wait_queue(&pb->pb_waiters, &wait);
@@ -1746,7 +1750,7 @@
 {
 	PB_TRACE(pb, "iowait", 0);
 	if (atomic_read(&pb->pb_io_remaining))
-		blk_run_queues();
+		run_task_queue(&tq_disk);
 	if ((pb->pb_flags & PBF_FS_DATAIOD))
 		pagebuf_runall_queues(pagebuf_dataiodone_tq);
 	down(&pb->pb_iodonesema);
@@ -1863,7 +1867,7 @@
 	if (pb->pb_flags & _PBF_RUN_QUEUES) {
 		pb->pb_flags &= ~_PBF_RUN_QUEUES;
 		if (atomic_read(&pb->pb_io_remaining) > 1)
-			blk_run_queues();
+			run_task_queue(&tq_disk);
 	}
 }
 
@@ -1903,13 +1907,20 @@
 pagebuf_delwri_dequeue(
 	xfs_buf_t		*pb)
 {
-	PB_TRACE(pb, "delwri_uq", 0);
-	ASSERT(pb->pb_flags & PBF_DELWRI);
+	int			dequeued = 0;
 
 	spin_lock(&pbd_delwrite_lock);
-	list_del_init(&pb->pb_list);
+	if ((pb->pb_flags & PBF_DELWRI) && !list_empty(&pb->pb_list)) {
+		list_del_init(&pb->pb_list);
+		dequeued = 1;
+	}
 	pb->pb_flags &= ~PBF_DELWRI;
 	spin_unlock(&pbd_delwrite_lock);
+
+	if (dequeued)
+		pagebuf_rele(pb);
+
+	PB_TRACE(pb, "delwri_dq", (long)dequeued);
 }
 
 
@@ -2012,12 +2023,16 @@
 STATIC int pagebuf_daemon_active;
 STATIC int force_flush;
 
-STATIC void
-pagebuf_daemon_wakeup(void)
+
+STATIC int
+pagebuf_daemon_wakeup(
+	int			priority,
+	unsigned int		mask)
 {
 	force_flush = 1;
 	barrier();
 	wake_up_process(pagebuf_daemon_task);
+	return 0;
 }
 
 STATIC int
@@ -2083,7 +2098,7 @@
 		if (as_list_len > 0)
 			purge_addresses();
 		if (count)
-			blk_run_queues();
+			run_task_queue(&tq_disk);
 
 		force_flush = 0;
 	} while (pagebuf_daemon_active);
@@ -2143,12 +2158,12 @@
 		pagebuf_iostrategy(pb);
 
 		if (++flush_cnt > 32) {
-			blk_run_queues();
+			run_task_queue(&tq_disk);
 			flush_cnt = 0;
 		}
 	}
 
-	blk_run_queues();
+	run_task_queue(&tq_disk);
 
 	/*
 	 * Remaining list items must be flushed before returning
@@ -2228,15 +2243,6 @@
 	}
 }
 
-
-STATIC int
-pagebuf_shaker(int number, unsigned int mask)
-{
-	pagebuf_daemon_wakeup();
-	return 0;
-}
-
-
 /*
  *	Initialization and Termination
  */
@@ -2249,31 +2255,35 @@
 	pagebuf_cache = kmem_cache_create("xfs_buf_t", sizeof(xfs_buf_t), 0,
 			SLAB_HWCACHE_ALIGN, NULL, NULL);
 	if (pagebuf_cache == NULL) {
-		printk("pagebuf: couldn't init pagebuf cache\n");
-		pagebuf_terminate();
+		printk("XFS: couldn't init xfs_buf_t cache\n");
 		return -ENOMEM;
 	}
 
 	if (_pagebuf_prealloc_bh(NR_RESERVED_BH) < NR_RESERVED_BH) {
-		printk("pagebuf: couldn't pre-allocate %d buffer heads\n",
+		printk("XFS: couldn't allocate %d reserved buffers\n",
 			NR_RESERVED_BH);
-		pagebuf_terminate();
+		kmem_zone_destroy(pagebuf_cache);
 		return -ENOMEM;
 	}
-
 	init_waitqueue_head(&pb_resv_bh_wait);
 
-	for (i = 0; i < NHASH; i++) {
-		spin_lock_init(&pbhash[i].pb_hash_lock);
-		INIT_LIST_HEAD(&pbhash[i].pb_hash);
-	}
-
 #ifdef PAGEBUF_TRACE
 	pagebuf_trace_buf = ktrace_alloc(PAGEBUF_TRACE_SIZE, KM_SLEEP);
 #endif
 
 	pagebuf_daemon_start();
-	kmem_shake_register(pagebuf_shaker);
+
+	pagebuf_shake = kmem_shake_register(pagebuf_daemon_wakeup);
+	if (pagebuf_shake == NULL) {
+		pagebuf_terminate();
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < NHASH; i++) {
+		spin_lock_init(&pbhash[i].pb_hash_lock);
+		INIT_LIST_HEAD(&pbhash[i].pb_hash);
+	}
+
 	return 0;
 }
 
@@ -2291,6 +2301,6 @@
 	ktrace_free(pagebuf_trace_buf);
 #endif
 
-	kmem_cache_destroy(pagebuf_cache);
-	kmem_shake_deregister(pagebuf_shaker);
+	kmem_zone_destroy(pagebuf_cache);
+	kmem_shake_deregister(pagebuf_shake);
 }
diff -urN linux-2.4.27-rc1/fs/xfs/linux-2.4/xfs_buf.h linux-2.4.27-rc2/fs/xfs/linux-2.4/xfs_buf.h
--- linux-2.4.27-rc1/fs/xfs/linux-2.4/xfs_buf.h	2004-06-25 20:48:24.318996856 -0700
+++ linux-2.4.27-rc2/fs/xfs/linux-2.4/xfs_buf.h	2004-06-25 20:48:27.208117370 -0700
@@ -371,7 +371,6 @@
 BUFFER_FNS(Delay, delay)
 
 #define get_seconds()		CURRENT_TIME
-#define blk_run_queues()	run_task_queue(&tq_disk)
 #define i_size_read(inode)	((inode)->i_size)
 #define i_size_write(inode, sz)	((inode)->i_size = (sz))
 
@@ -400,27 +399,15 @@
 #define XFS_BUF_ISSTALE(x)	((x)->pb_flags & XFS_B_STALE)
 #define XFS_BUF_SUPER_STALE(x)	do {				\
 					XFS_BUF_STALE(x);	\
-					xfs_buf_undelay(x);	\
+					pagebuf_delwri_dequeue(x);	\
 					XFS_BUF_DONE(x);	\
 				} while (0)
 
 #define XFS_BUF_MANAGE		PBF_FS_MANAGED
 #define XFS_BUF_UNMANAGE(x)	((x)->pb_flags &= ~PBF_FS_MANAGED)
 
-static inline void xfs_buf_undelay(xfs_buf_t *pb)
-{
-	if (pb->pb_flags & PBF_DELWRI) {
-		if (pb->pb_list.next != &pb->pb_list) {
-			pagebuf_delwri_dequeue(pb);
-			pagebuf_rele(pb);
-		} else {
-			pb->pb_flags &= ~PBF_DELWRI;
-		}
-	}
-}
-
 #define XFS_BUF_DELAYWRITE(x)	 ((x)->pb_flags |= PBF_DELWRI)
-#define XFS_BUF_UNDELAYWRITE(x)	 xfs_buf_undelay(x)
+#define XFS_BUF_UNDELAYWRITE(x)	 pagebuf_delwri_dequeue(x)
 #define XFS_BUF_ISDELAYWRITE(x)	 ((x)->pb_flags & PBF_DELWRI)
 
 #define XFS_BUF_ERROR(x,no)	 pagebuf_ioerror(x,no)
@@ -553,7 +540,7 @@
 {
 	bp->pb_fspriv3 = mp;
 	bp->pb_strat = xfs_bdstrat_cb;
-	xfs_buf_undelay(bp);
+	pagebuf_delwri_dequeue(bp);
 	return pagebuf_iostart(bp, PBF_WRITE | PBF_ASYNC | _PBF_RUN_QUEUES);
 }
 
@@ -593,7 +580,7 @@
 	if (!iowait)
 		pb->pb_flags |= _PBF_RUN_QUEUES;
 
-	xfs_buf_undelay(pb);
+	pagebuf_delwri_dequeue(pb);
 	pagebuf_iostrategy(pb);
 	if (iowait) {
 		error = pagebuf_iowait(pb);
diff -urN linux-2.4.27-rc1/fs/xfs/linux-2.4/xfs_file.c linux-2.4.27-rc2/fs/xfs/linux-2.4/xfs_file.c
--- linux-2.4.27-rc1/fs/xfs/linux-2.4/xfs_file.c	2004-06-25 20:48:24.319996897 -0700
+++ linux-2.4.27-rc2/fs/xfs/linux-2.4/xfs_file.c	2004-06-25 20:48:27.209117412 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -54,6 +54,7 @@
 #include <linux/iobuf.h>
 
 #include <linux/dcache.h>
+#include <linux/smp_lock.h>
 #include <linux/mman.h> /* for PROT_WRITE */
 
 static struct vm_operations_struct linvfs_file_vm_ops;
@@ -346,9 +347,10 @@
 	int		error;
 	vnode_t		*vp = LINVFS_GET_VP(inode);
 
-	ASSERT(vp);
+	unlock_kernel();
 	VOP_IOCTL(vp, inode, filp, 0, cmd, arg, error);
 	VMODIFY(vp);
+	lock_kernel();
 
 	/* NOTE:  some of the ioctl's return positive #'s as a
 	 *	  byte count indicating success, such as
@@ -369,9 +371,10 @@
 	int		error;
 	vnode_t		*vp = LINVFS_GET_VP(inode);
 
-	ASSERT(vp);
+	unlock_kernel();
 	VOP_IOCTL(vp, inode, filp, IO_INVIS, cmd, arg, error);
 	VMODIFY(vp);
+	lock_kernel();
 
 	/* NOTE:  some of the ioctl's return positive #'s as a
 	 *	  byte count indicating success, such as
diff -urN linux-2.4.27-rc1/fs/xfs/linux-2.4/xfs_fs_subr.c linux-2.4.27-rc2/fs/xfs/linux-2.4/xfs_fs_subr.c
--- linux-2.4.27-rc1/fs/xfs/linux-2.4/xfs_fs_subr.c	2004-06-25 20:48:24.320996939 -0700
+++ linux-2.4.27-rc2/fs/xfs/linux-2.4/xfs_fs_subr.c	2004-06-25 20:48:27.209117412 -0700
@@ -36,7 +36,7 @@
  * Stub for no-op vnode operations that return error status.
  */
 int
-fs_noerr()
+fs_noerr(void)
 {
 	return 0;
 }
@@ -45,7 +45,7 @@
  * Operation unsupported under this file system.
  */
 int
-fs_nosys()
+fs_nosys(void)
 {
 	return ENOSYS;
 }
@@ -55,7 +55,7 @@
  */
 /* ARGSUSED */
 void
-fs_noval()
+fs_noval(void)
 {
 }
 
diff -urN linux-2.4.27-rc1/fs/xfs/linux-2.4/xfs_super.c linux-2.4.27-rc2/fs/xfs/linux-2.4/xfs_super.c
--- linux-2.4.27-rc1/fs/xfs/linux-2.4/xfs_super.c	2004-06-25 20:48:24.329997314 -0700
+++ linux-2.4.27-rc2/fs/xfs/linux-2.4/xfs_super.c	2004-06-25 20:48:27.218117787 -0700
@@ -71,7 +71,8 @@
 
 STATIC struct quotactl_ops linvfs_qops;
 STATIC struct super_operations linvfs_sops;
-STATIC kmem_cache_t * linvfs_inode_cachep;
+STATIC kmem_zone_t *linvfs_inode_zone;
+STATIC kmem_shaker_t xfs_inode_shaker;
 
 STATIC struct xfs_mount_args *
 xfs_args_allocate(
@@ -229,6 +230,18 @@
 	}
 }
 
+int
+xfs_inode_shake(
+	int		priority,
+	unsigned int	gfp_mask)
+{
+	int		pages;
+
+	pages = kmem_zone_shrink(linvfs_inode_zone);
+	pages += kmem_zone_shrink(xfs_inode_zone);
+	return pages;
+}
+
 struct inode *
 xfs_get_inode(
 	bhv_desc_t	*bdp,
@@ -324,7 +337,7 @@
 {
 	vnode_t			*vp;
 
-	vp = (vnode_t *)kmem_cache_alloc(linvfs_inode_cachep, 
+	vp = (vnode_t *)kmem_cache_alloc(linvfs_inode_zone, 
                 kmem_flags_convert(KM_SLEEP));
 	if (!vp)
 		return NULL;
@@ -335,7 +348,7 @@
 linvfs_destroy_inode(
 	struct inode		*inode)
 {
-	kmem_cache_free(linvfs_inode_cachep, LINVFS_GET_VP(inode));
+	kmem_cache_free(linvfs_inode_zone, LINVFS_GET_VP(inode));
 }
 
 #define VNODE_SIZE	\
@@ -360,11 +373,10 @@
 STATIC int
 init_inodecache( void )
 {
-	linvfs_inode_cachep = kmem_cache_create("linvfs_icache",
+	linvfs_inode_zone = kmem_cache_create("linvfs_icache",
 				VNODE_SIZE, 0, SLAB_HWCACHE_ALIGN,
 				init_once, NULL);
-
-	if (linvfs_inode_cachep == NULL)
+	if (linvfs_inode_zone == NULL)
 		return -ENOMEM;
 	return 0;
 }
@@ -372,7 +384,7 @@
 STATIC void
 destroy_inodecache( void )
 {
-	if (kmem_cache_destroy(linvfs_inode_cachep))
+	if (kmem_cache_destroy(linvfs_inode_zone))
 		printk(KERN_WARNING "%s: cache still in use!\n", __FUNCTION__);
 }
 
@@ -871,12 +883,22 @@
 	vfs_initdmapi();
 	vfs_initquota();
 
+	xfs_inode_shaker = kmem_shake_register(xfs_inode_shake);
+	if (!xfs_inode_shaker) {
+		error = -ENOMEM;
+		goto undo_shaker;
+	}
+
 	error = register_filesystem(&xfs_fs_type);
 	if (error)
 		goto undo_register;
+	XFS_DM_INIT(&xfs_fs_type);
 	return 0;
 
 undo_register:
+	kmem_shake_deregister(xfs_inode_shaker);
+
+undo_shaker:
 	pagebuf_terminate();
 
 undo_pagebuf:
@@ -889,7 +911,9 @@
 STATIC void __exit
 exit_xfs_fs( void )
 {
+	XFS_DM_EXIT(&xfs_fs_type);
 	unregister_filesystem(&xfs_fs_type);
+	kmem_shake_deregister(xfs_inode_shaker);
 	xfs_cleanup();
 	vfs_exitquota();
 	vfs_exitdmapi();
diff -urN linux-2.4.27-rc1/fs/xfs/linux-2.4/xfs_super.h linux-2.4.27-rc2/fs/xfs/linux-2.4/xfs_super.h
--- linux-2.4.27-rc1/fs/xfs/linux-2.4/xfs_super.h	2004-06-25 20:48:24.329997314 -0700
+++ linux-2.4.27-rc2/fs/xfs/linux-2.4/xfs_super.h	2004-06-25 20:48:27.219117829 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -92,6 +92,12 @@
 # define XFS_TRACE_STRING
 #endif
 
+#ifdef CONFIG_XFS_DMAPI
+# define XFS_DMAPI_STRING	"dmapi support, "
+#else
+# define XFS_DMAPI_STRING
+#endif
+
 #ifdef DEBUG
 # define XFS_DBG_STRING		"debug"
 #else
@@ -103,6 +109,7 @@
 				XFS_REALTIME_STRING \
 				XFS_BIGFS_STRING \
 				XFS_TRACE_STRING \
+				XFS_DMAPI_STRING \
 				XFS_DBG_STRING /* DBG must be last */
 
 #define LINVFS_GET_VFS(s) \
diff -urN linux-2.4.27-rc1/fs/xfs/xfs.h linux-2.4.27-rc2/fs/xfs/xfs.h
--- linux-2.4.27-rc1/fs/xfs/xfs.h	2004-06-25 20:48:24.337997648 -0700
+++ linux-2.4.27-rc2/fs/xfs/xfs.h	2004-06-25 20:48:27.227118162 -0700
@@ -35,5 +35,6 @@
 #include <linux-2.4/xfs_linux.h>
 
 #include <xfs_fs.h> 
+#include <xfs_macros.h>
 
 #endif	/* __XFS_H__ */
diff -urN linux-2.4.27-rc1/fs/xfs/xfs_acl.c linux-2.4.27-rc2/fs/xfs/xfs_acl.c
--- linux-2.4.27-rc1/fs/xfs/xfs_acl.c	2004-06-25 20:48:24.338997690 -0700
+++ linux-2.4.27-rc2/fs/xfs/xfs_acl.c	2004-06-25 20:48:27.228118204 -0700
@@ -231,8 +231,6 @@
 	int			flags = 0;
 
 	VN_HOLD(vp);
-	if ((error = _MAC_VACCESS(vp, NULL, VREAD)))
-		goto out;
 	if(size) {
 		if (!(_ACL_ALLOC(xfs_acl))) {
 			error = ENOMEM;
@@ -395,8 +393,6 @@
 		return ENOTDIR;
 	if (vp->v_vfsp->vfs_flag & VFS_RDONLY)
 		return EROFS;
-	if ((error = _MAC_VACCESS(vp, NULL, VWRITE)))
-		return error;
 	va.va_mask = XFS_AT_UID;
 	VOP_GETATTR(vp, &va, 0, NULL, error);
 	if (error)
diff -urN linux-2.4.27-rc1/fs/xfs/xfs_acl.h linux-2.4.27-rc2/fs/xfs/xfs_acl.h
--- linux-2.4.27-rc1/fs/xfs/xfs_acl.h	2004-02-18 05:36:32.000000000 -0800
+++ linux-2.4.27-rc2/fs/xfs/xfs_acl.h	2004-06-25 20:48:27.228118204 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001-2003 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2001-2004 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -61,14 +61,17 @@
 #define SGI_ACL_DEFAULT_SIZE	(sizeof(SGI_ACL_DEFAULT)-1)
 
 
-#ifdef __KERNEL__
-
 #ifdef CONFIG_XFS_POSIX_ACL
 
 struct vattr;
 struct vnode;
 struct xfs_inode;
 
+extern struct kmem_zone *xfs_acl_zone;
+#define xfs_acl_zone_init(zone, name)	\
+		(zone) = kmem_zone_init(sizeof(xfs_acl_t), name)
+#define xfs_acl_zone_destroy(zone)	kmem_cache_destroy(zone)
+
 extern int xfs_acl_inherit(struct vnode *, struct vattr *, xfs_acl_t *);
 extern int xfs_acl_iaccess(struct xfs_inode *, mode_t, cred_t *);
 extern int xfs_acl_get(struct vnode *, xfs_acl_t *, xfs_acl_t *);
@@ -80,17 +83,10 @@
 extern int xfs_acl_vget(struct vnode *, void *, size_t, int);
 extern int xfs_acl_vremove(struct vnode *vp, int);
 
-extern struct kmem_zone *xfs_acl_zone;
-
 #define _ACL_TYPE_ACCESS	1
 #define _ACL_TYPE_DEFAULT	2
 #define _ACL_PERM_INVALID(perm)	((perm) & ~(ACL_READ|ACL_WRITE|ACL_EXECUTE))
 
-#define _ACL_DECL(a)		xfs_acl_t *(a) = NULL
-#define _ACL_ALLOC(a)		((a) = kmem_zone_alloc(xfs_acl_zone, KM_SLEEP))
-#define _ACL_FREE(a)		((a)? kmem_zone_free(xfs_acl_zone, (a)) : 0)
-#define _ACL_ZONE_INIT(z,name)	((z) = kmem_zone_init(sizeof(xfs_acl_t), name))
-#define _ACL_ZONE_DESTROY(z)	(kmem_cache_destroy(z))
 #define _ACL_INHERIT(c,v,d)	(xfs_acl_inherit(c,v,d))
 #define _ACL_GET_ACCESS(pv,pa)	(xfs_acl_vtoacl(pv,pa,NULL) == 0)
 #define _ACL_GET_DEFAULT(pv,pd)	(xfs_acl_vtoacl(pv,NULL,pd) == 0)
@@ -98,17 +94,19 @@
 #define _ACL_DEFAULT_EXISTS	xfs_acl_vhasacl_default
 #define _ACL_XFS_IACCESS(i,m,c) (XFS_IFORK_Q(i) ? xfs_acl_iaccess(i,m,c) : -1)
 
+#define _ACL_ALLOC(a)		((a) = kmem_zone_alloc(xfs_acl_zone, KM_SLEEP))
+#define _ACL_FREE(a)		((a)? kmem_zone_free(xfs_acl_zone, (a)) : 0)
+
 #else
+#define xfs_acl_zone_init(zone,name)
+#define xfs_acl_zone_destroy(zone)
 #define xfs_acl_vset(v,p,sz,t)	(-EOPNOTSUPP)
 #define xfs_acl_vget(v,p,sz,t)	(-EOPNOTSUPP)
 #define xfs_acl_vremove(v,t)	(-EOPNOTSUPP)
 #define xfs_acl_vhasacl_access(v)	(0)
 #define xfs_acl_vhasacl_default(v)	(0)
-#define _ACL_DECL(a)		((void)0)
 #define _ACL_ALLOC(a)		(1)	/* successfully allocate nothing */
 #define _ACL_FREE(a)		((void)0)
-#define _ACL_ZONE_INIT(z,name)	((void)0)
-#define _ACL_ZONE_DESTROY(z)	((void)0)
 #define _ACL_INHERIT(c,v,d)	(0)
 #define _ACL_GET_ACCESS(pv,pa)	(0)
 #define _ACL_GET_DEFAULT(pv,pd)	(0)
@@ -117,6 +115,4 @@
 #define _ACL_XFS_IACCESS(i,m,c) (-1)
 #endif
 
-#endif	/* __KERNEL__ */
-
 #endif	/* __XFS_ACL_H__ */
diff -urN linux-2.4.27-rc1/fs/xfs/xfs_arch.h linux-2.4.27-rc2/fs/xfs/xfs_arch.h
--- linux-2.4.27-rc1/fs/xfs/xfs_arch.h	2004-02-18 05:36:32.000000000 -0800
+++ linux-2.4.27-rc2/fs/xfs/xfs_arch.h	2004-06-25 20:48:27.228118204 -0700
@@ -157,11 +157,11 @@
 
 /* does not return a value */
 #define INT_MOD_EXPR(reference,arch,code) \
-    (void)(((arch) == ARCH_NOCONVERT) \
+    (((arch) == ARCH_NOCONVERT) \
 	? \
-	    ((reference) code) \
+	    (void)((reference) code) \
 	: \
-	    ( \
+	    (void)( \
 		(reference) = INT_GET((reference),arch) , \
 		((reference) code), \
 		INT_SET(reference, arch, reference) \
@@ -187,10 +187,10 @@
 
 /* does not return a value */
 #define INT_COPY(dst,src,arch) \
-    (void)( \
+    ( \
 	((sizeof(dst) == sizeof(src)) || ((arch) == ARCH_NOCONVERT)) \
 	    ? \
-		((dst) = (src)) \
+		(void)((dst) = (src)) \
 	    : \
 		INT_SET(dst, arch, INT_GET(src, arch)) \
     )
diff -urN linux-2.4.27-rc1/fs/xfs/xfs_attr.c linux-2.4.27-rc2/fs/xfs/xfs_attr.c
--- linux-2.4.27-rc1/fs/xfs/xfs_attr.c	2004-06-25 20:48:24.339997732 -0700
+++ linux-2.4.27-rc2/fs/xfs/xfs_attr.c	2004-06-25 20:48:27.230118288 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -2149,8 +2149,8 @@
 		/*
 		 * If the "remote" value is in the cache, remove it.
 		 */
-		/* bp = incore(mp->m_dev, dblkno, blkcnt, 1); */
-		bp = xfs_incore(mp->m_ddev_targp, dblkno, blkcnt, 1);
+		bp = xfs_incore(mp->m_ddev_targp, dblkno, blkcnt,
+				XFS_INCORE_TRYLOCK);
 		if (bp) {
 			XFS_BUF_STALE(bp);
 			XFS_BUF_UNDELAYWRITE(bp);
diff -urN linux-2.4.27-rc1/fs/xfs/xfs_bit.c linux-2.4.27-rc2/fs/xfs/xfs_bit.c
--- linux-2.4.27-rc1/fs/xfs/xfs_bit.c	2004-06-25 20:48:24.340997773 -0700
+++ linux-2.4.27-rc2/fs/xfs/xfs_bit.c	2004-06-25 20:48:27.231118329 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2002 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -154,14 +154,17 @@
 xfs_lowbit64(
 	__uint64_t	v)
 {
-	int n;
-	n = ffs((unsigned)v);
-	if (n <= 0) {
-		n = ffs(v >> 32);
-		if (n >= 0)
-			n+=32;
+	__uint32_t	w = (__uint32_t)v;
+	int		n = 0;
+
+	if (w) {	/* lower bits */
+		n = ffs(w);
+	} else {	/* upper bits */
+		w = (__uint32_t)(v >> 32);
+		if (w && (n = ffs(w)))
+			n += 32;
 	}
-	return (n <= 0) ? n : n-1;
+	return n - 1;
 }
 
 /*
@@ -171,10 +174,11 @@
 xfs_highbit64(
 	__uint64_t	v)
 {
-	__uint32_t h = v >> 32;
+	__uint32_t	h = (__uint32_t)(v >> 32);
+
 	if (h)
 		return xfs_highbit32(h) + 32;
-	return xfs_highbit32((__u32)v);
+	return xfs_highbit32((__uint32_t)v);
 }
 
 
diff -urN linux-2.4.27-rc1/fs/xfs/xfs_dmapi.h linux-2.4.27-rc2/fs/xfs/xfs_dmapi.h
--- linux-2.4.27-rc1/fs/xfs/xfs_dmapi.h	2004-04-14 06:05:40.000000000 -0700
+++ linux-2.4.27-rc2/fs/xfs/xfs_dmapi.h	2004-06-25 20:48:27.242118788 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -199,7 +199,14 @@
 
 extern struct bhv_vfsops xfs_dmops;
 
-extern int dmapi_init(void);
-extern void dmapi_uninit(void);
+#ifdef CONFIG_XFS_DMAPI
+void xfs_dm_init(struct file_system_type *);
+void xfs_dm_exit(struct file_system_type *);
+#define XFS_DM_INIT(fstype)	xfs_dm_init(fstype)
+#define XFS_DM_EXIT(fstype)	xfs_dm_exit(fstype)
+#else
+#define XFS_DM_INIT(fstype)
+#define XFS_DM_EXIT(fstype)
+#endif
 
 #endif  /* __XFS_DMAPI_H__ */
diff -urN linux-2.4.27-rc1/fs/xfs/xfs_inode.c linux-2.4.27-rc2/fs/xfs/xfs_inode.c
--- linux-2.4.27-rc1/fs/xfs/xfs_inode.c	2004-06-25 20:48:24.354998357 -0700
+++ linux-2.4.27-rc2/fs/xfs/xfs_inode.c	2004-06-25 20:48:27.245118913 -0700
@@ -1140,8 +1140,7 @@
 	 * Call the space management code to pick
 	 * the on-disk inode to be allocated.
 	 */
-	ASSERT(pip != NULL);
-	error = xfs_dialloc(tp, pip ? pip->i_ino : 0, mode, okalloc,
+	error = xfs_dialloc(tp, pip->i_ino, mode, okalloc,
 			    ialloc_context, call_again, &ino);
 	if (error != 0) {
 		return error;
@@ -3696,12 +3695,6 @@
 	mode_t		orgmode = mode;
 	struct inode	*inode = LINVFS_GET_IP(XFS_ITOV(ip));
 
-	/*
-	 * Verify that the MAC policy allows the requested access.
-	 */
-	if ((error = _MAC_XFS_IACCESS(ip, mode, cr)))
-		return XFS_ERROR(error);
-
 	if (mode & S_IWUSR) {
 		umode_t		imode = inode->i_mode;
 
diff -urN linux-2.4.27-rc1/fs/xfs/xfs_inode.h linux-2.4.27-rc2/fs/xfs/xfs_inode.h
--- linux-2.4.27-rc1/fs/xfs/xfs_inode.h	2004-06-25 20:48:24.354998357 -0700
+++ linux-2.4.27-rc2/fs/xfs/xfs_inode.h	2004-06-25 20:48:27.245118913 -0700
@@ -459,8 +459,8 @@
  * directory, group of new file is set to that of the parent, and
  * new subdirectory gets S_ISGID bit from parent.
  */
-#define XFS_INHERIT_GID(pip, vfsp)	((pip) != NULL && \
-	(((vfsp)->vfs_flag & VFS_GRPID) || ((pip)->i_d.di_mode & S_ISGID)))
+#define XFS_INHERIT_GID(pip, vfsp)	\
+	(((vfsp)->vfs_flag & VFS_GRPID) || ((pip)->i_d.di_mode & S_ISGID))
 
 /*
  * xfs_iget.c prototypes.
diff -urN linux-2.4.27-rc1/fs/xfs/xfs_mount.h linux-2.4.27-rc2/fs/xfs/xfs_mount.h
--- linux-2.4.27-rc1/fs/xfs/xfs_mount.h	2004-06-25 20:48:24.364998774 -0700
+++ linux-2.4.27-rc2/fs/xfs/xfs_mount.h	2004-06-25 20:48:27.256119372 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -428,10 +428,6 @@
  */
 #define XFS_READIO_LOG_LARGE	16
 #define XFS_WRITEIO_LOG_LARGE	16
-/*
- * Default allocation size
- */
-#define XFS_WRITE_IO_LOG	16
 
 /*
  * Max and min values for UIO and mount-option defined I/O sizes;
diff -urN linux-2.4.27-rc1/fs/xfs/xfs_rtalloc.c linux-2.4.27-rc2/fs/xfs/xfs_rtalloc.c
--- linux-2.4.27-rc1/fs/xfs/xfs_rtalloc.c	2004-02-18 05:36:32.000000000 -0800
+++ linux-2.4.27-rc2/fs/xfs/xfs_rtalloc.c	2004-06-25 20:48:27.257119414 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2002 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -100,7 +100,9 @@
 xfs_lowbit32(
 	__uint32_t	v)
 {
-	return ffs(v)-1;
+	if (v)
+		return ffs(v) - 1;
+	return -1;
 }
 
 /*
diff -urN linux-2.4.27-rc1/fs/xfs/xfs_vfsops.c linux-2.4.27-rc2/fs/xfs/xfs_vfsops.c
--- linux-2.4.27-rc1/fs/xfs/xfs_vfsops.c	2004-06-25 20:48:24.370999025 -0700
+++ linux-2.4.27-rc2/fs/xfs/xfs_vfsops.c	2004-06-25 20:48:27.263119664 -0700
@@ -118,7 +118,7 @@
 	xfs_ili_zone = kmem_zone_init(sizeof(xfs_inode_log_item_t), "xfs_ili");
 	xfs_chashlist_zone = kmem_zone_init(sizeof(xfs_chashlist_t),
 					    "xfs_chashlist");
-	_ACL_ZONE_INIT(xfs_acl_zone, "xfs_acl");
+	xfs_acl_zone_init(xfs_acl_zone, "xfs_acl");
 
 	/*
 	 * Allocate global trace buffers.
@@ -170,6 +170,7 @@
 	xfs_cleanup_procfs();
 	xfs_sysctl_unregister();
 	xfs_refcache_destroy();
+	xfs_acl_zone_destroy(xfs_acl_zone);
 
 #ifdef XFS_DIR2_TRACE
 	ktrace_free(xfs_dir2_trace_buf);
@@ -202,7 +203,6 @@
 	kmem_cache_destroy(xfs_ifork_zone);
 	kmem_cache_destroy(xfs_ili_zone);
 	kmem_cache_destroy(xfs_chashlist_zone);
-	_ACL_ZONE_DESTROY(xfs_acl_zone);
 }
 
 /*
diff -urN linux-2.4.27-rc1/fs/xfs/xfs_vnodeops.c linux-2.4.27-rc2/fs/xfs/xfs_vnodeops.c
--- linux-2.4.27-rc1/fs/xfs/xfs_vnodeops.c	2004-06-25 20:48:24.372999108 -0700
+++ linux-2.4.27-rc2/fs/xfs/xfs_vnodeops.c	2004-06-25 20:48:27.265119748 -0700
@@ -411,11 +411,6 @@
 
 	xfs_ilock(ip, lock_flags);
 
-	if (_MAC_XFS_IACCESS(ip, MACWRITE, credp)) {
-		code = XFS_ERROR(EACCES);
-		goto error_return;
-	}
-
 	/* boolean: are we the file owner? */
 	file_owner = (current_fsuid(credp) == ip->i_d.di_uid);
 
@@ -2446,11 +2441,6 @@
 		xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
 	}
 
-	if ((error = _MAC_XFS_IACCESS(ip, MACWRITE, credp))) {
-		REMOVE_DEBUG_TRACE(__LINE__);
-		goto error_return;
-	}
-
 	/*
 	 * Entry must exist since we did a lookup in xfs_lock_dir_and_entry.
 	 */
@@ -2536,8 +2526,6 @@
  error1:
 	xfs_bmap_cancel(&free_list);
 	cancel_flags |= XFS_TRANS_ABORT;
-
- error_return:
 	xfs_trans_cancel(tp, cancel_flags);
 	goto std_return;
 
@@ -3105,10 +3093,6 @@
 	ITRACE(cdp);
 	xfs_trans_ijoin(tp, cdp, XFS_ILOCK_EXCL);
 
-	if ((error = _MAC_XFS_IACCESS(cdp, MACWRITE, credp))) {
-		goto error_return;
-	}
-
 	ASSERT(cdp->i_d.di_nlink >= 2);
 	if (cdp->i_d.di_nlink != 2) {
 		error = XFS_ERROR(ENOTEMPTY);
diff -urN linux-2.4.27-rc1/include/linux/affs_fs.h linux-2.4.27-rc2/include/linux/affs_fs.h
--- linux-2.4.27-rc1/include/linux/affs_fs.h	2001-04-19 22:57:06.000000000 -0700
+++ linux-2.4.27-rc2/include/linux/affs_fs.h	2004-06-25 20:48:27.274120123 -0700
@@ -33,7 +33,8 @@
 extern u32	affs_count_free_blocks(struct super_block *s);
 extern void	affs_free_block(struct super_block *sb, u32 block);
 extern u32	affs_alloc_block(struct inode *inode, u32 goal);
-extern int	affs_init_bitmap(struct super_block *sb);
+extern int	affs_init_bitmap(struct super_block *sb, int *flags);
+extern void	affs_free_bitmap(struct super_block *sb);
 
 /* namei.c */
 
diff -urN linux-2.4.27-rc1/include/linux/affs_fs_sb.h linux-2.4.27-rc2/include/linux/affs_fs_sb.h
--- linux-2.4.27-rc1/include/linux/affs_fs_sb.h	2001-04-19 22:57:06.000000000 -0700
+++ linux-2.4.27-rc2/include/linux/affs_fs_sb.h	2004-06-25 20:48:27.274120123 -0700
@@ -47,7 +47,6 @@
 #define SF_OFS		0x0200		/* Old filesystem */
 #define SF_PREFIX	0x0400		/* Buffer for prefix is allocated */
 #define SF_VERBOSE	0x0800		/* Talk about fs when mounting */
-#define SF_READONLY	0x1000		/* Don't allow to remount rw */
 
 /* short cut to get to the affs specific sb data */
 #define AFFS_SB		(&sb->u.affs_sb)
diff -urN linux-2.4.27-rc1/include/linux/ethtool.h linux-2.4.27-rc2/include/linux/ethtool.h
--- linux-2.4.27-rc1/include/linux/ethtool.h	2003-11-28 10:26:21.000000000 -0800
+++ linux-2.4.27-rc2/include/linux/ethtool.h	2004-06-25 20:48:27.278120290 -0700
@@ -345,6 +345,8 @@
 	int	(*phys_id)(struct net_device *, u32);
 	int	(*get_stats_count)(struct net_device *);
 	void	(*get_ethtool_stats)(struct net_device *, struct ethtool_stats *, u64 *);
+	int	(*begin)(struct net_device *);
+	void	(*complete)(struct net_device *);
 };
 
 /* CMDs currently supported */
diff -urN linux-2.4.27-rc1/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.4.27-rc2/include/linux/netfilter_ipv4/ip_conntrack.h
--- linux-2.4.27-rc1/include/linux/netfilter_ipv4/ip_conntrack.h	2004-04-14 06:05:40.000000000 -0700
+++ linux-2.4.27-rc2/include/linux/netfilter_ipv4/ip_conntrack.h	2004-06-25 20:48:27.283120498 -0700
@@ -103,7 +103,7 @@
 #include <linux/types.h>
 #include <linux/skbuff.h>
 
-#ifdef CONFIG_NF_DEBUG
+#ifdef CONFIG_NETFILTER_DEBUG
 #define IP_NF_ASSERT(x)							\
 do {									\
 	if (!(x))							\
diff -urN linux-2.4.27-rc1/include/linux/pkt_sched.h linux-2.4.27-rc2/include/linux/pkt_sched.h
--- linux-2.4.27-rc1/include/linux/pkt_sched.h	2004-04-14 06:05:40.000000000 -0700
+++ linux-2.4.27-rc2/include/linux/pkt_sched.h	2004-06-25 20:48:27.288120707 -0700
@@ -437,6 +437,7 @@
 {
 	__u32	latency;
 	__u32   limit;
-};	
+	__u32	loss;
+};
 
 #endif
diff -urN linux-2.4.27-rc1/include/linux/skbuff.h linux-2.4.27-rc2/include/linux/skbuff.h
--- linux-2.4.27-rc1/include/linux/skbuff.h	2004-04-14 06:05:40.000000000 -0700
+++ linux-2.4.27-rc2/include/linux/skbuff.h	2004-06-25 20:48:27.291120832 -0700
@@ -1166,7 +1166,18 @@
 	if (nfct)
 		atomic_inc(&nfct->master->use);
 }
+static inline void
+nf_reset(struct sk_buff *skb)
+{
+	nf_conntrack_put(skb->nfct);
+	skb->nfct = NULL;
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug = 0;
 #endif
+}
+#else /* CONFIG_NETFILTER */
+static inline void nf_reset(struct sk_buff *skb) {}
+#endif /* CONFIG_NETFILTER */
 
 #endif	/* __KERNEL__ */
 #endif	/* _LINUX_SKBUFF_H */
diff -urN linux-2.4.27-rc1/include/linux/usb_ch9.h linux-2.4.27-rc2/include/linux/usb_ch9.h
--- linux-2.4.27-rc1/include/linux/usb_ch9.h	2003-11-28 10:26:21.000000000 -0800
+++ linux-2.4.27-rc2/include/linux/usb_ch9.h	2004-06-25 20:48:27.292120874 -0700
@@ -68,6 +68,20 @@
 #define USB_REQ_SET_INTERFACE		0x0B
 #define USB_REQ_SYNCH_FRAME		0x0C
 
+/*
+ * USB feature flags are written using USB_REQ_{CLEAR,SET}_FEATURE, and
+ * are read as a bit array returned by USB_REQ_GET_STATUS.  (So there
+ * are at most sixteen features of each type.)
+ */
+#define USB_DEVICE_SELF_POWERED		0	/* (read only) */
+#define USB_DEVICE_REMOTE_WAKEUP	1	/* dev may initiate wakeup */
+#define USB_DEVICE_TEST_MODE		2	/* (high speed only) */
+#define USB_DEVICE_B_HNP_ENABLE		3	/* dev may initiate HNP */
+#define USB_DEVICE_A_HNP_SUPPORT	4	/* RH port supports HNP */
+#define USB_DEVICE_A_ALT_HNP_SUPPORT	5	/* other RH port does */
+
+#define USB_ENDPOINT_HALT		0	/* IN/OUT will STALL */
+
 
 /**
  * struct usb_ctrlrequest - SETUP data for a USB device control request
diff -urN linux-2.4.27-rc1/include/linux/usb_gadget.h linux-2.4.27-rc2/include/linux/usb_gadget.h
--- linux-2.4.27-rc1/include/linux/usb_gadget.h	2004-04-14 06:05:40.000000000 -0700
+++ linux-2.4.27-rc2/include/linux/usb_gadget.h	2004-06-25 20:48:27.293120916 -0700
@@ -748,6 +748,15 @@
 int usb_gadget_config_buf(const struct usb_config_descriptor *config,
 	void *buf, unsigned buflen, const struct usb_descriptor_header **desc);
 
+/*-------------------------------------------------------------------------*/
+
+/* utility wrapping a simple endpoint selection policy */
+
+extern struct usb_ep *usb_ep_autoconfig (struct usb_gadget *,
+			struct usb_endpoint_descriptor *) __init;
+
+extern void usb_ep_autoconfig_reset (struct usb_gadget *) __init;
+
 #endif  /* __KERNEL__ */
 
 #endif	/* __LINUX_USB_GADGET_H */
diff -urN linux-2.4.27-rc1/include/net/bluetooth/bluetooth.h linux-2.4.27-rc2/include/net/bluetooth/bluetooth.h
--- linux-2.4.27-rc1/include/net/bluetooth/bluetooth.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.27-rc2/include/net/bluetooth/bluetooth.h	2004-06-25 20:48:27.293120916 -0700
@@ -52,6 +52,7 @@
 #define BTPROTO_RFCOMM	3
 #define BTPROTO_BNEP	4
 #define BTPROTO_CMTP	5
+#define BTPROTO_HIDP	6
 
 #define SOL_HCI     0
 #define SOL_L2CAP   6
diff -urN linux-2.4.27-rc1/net/bluetooth/af_bluetooth.c linux-2.4.27-rc2/net/bluetooth/af_bluetooth.c
--- linux-2.4.27-rc1/net/bluetooth/af_bluetooth.c	2004-04-14 06:05:41.000000000 -0700
+++ linux-2.4.27-rc2/net/bluetooth/af_bluetooth.c	2004-06-25 20:48:27.306121458 -0700
@@ -27,7 +27,7 @@
  *
  * $Id: af_bluetooth.c,v 1.8 2002/07/22 20:32:54 maxk Exp $
  */
-#define VERSION "2.3"
+#define VERSION "2.4"
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -57,7 +57,7 @@
 #endif
 
 /* Bluetooth sockets */
-#define BLUEZ_MAX_PROTO	6
+#define BLUEZ_MAX_PROTO	7
 static struct net_proto_family *bluez_proto[BLUEZ_MAX_PROTO];
 
 int bluez_sock_register(int proto, struct net_proto_family *ops)
diff -urN linux-2.4.27-rc1/net/bluetooth/hci_event.c linux-2.4.27-rc2/net/bluetooth/hci_event.c
--- linux-2.4.27-rc1/net/bluetooth/hci_event.c	2004-02-18 05:36:32.000000000 -0800
+++ linux-2.4.27-rc2/net/bluetooth/hci_event.c	2004-06-25 20:48:27.307121500 -0700
@@ -320,7 +320,7 @@
 			status, batostr(&cc->bdaddr), conn);
 
 	if (status) {
-		if (conn) {
+		if (conn && conn->state == BT_CONNECT) {
 			conn->state = BT_CLOSED;
 			hci_proto_connect_cfm(conn, status);
 			hci_conn_del(conn);
diff -urN linux-2.4.27-rc1/net/bridge/br.c linux-2.4.27-rc2/net/bridge/br.c
--- linux-2.4.27-rc1/net/bridge/br.c	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.27-rc2/net/bridge/br.c	2004-06-25 20:48:27.308121541 -0700
@@ -22,6 +22,7 @@
 #include <linux/init.h>
 #include <linux/if_bridge.h>
 #include <linux/brlock.h>
+#include <linux/rtnetlink.h>
 #include <asm/uaccess.h>
 #include "br_private.h"
 
@@ -54,15 +55,13 @@
 	return 0;
 }
 
-static void __br_clear_ioctl_hook(void)
-{
-	br_ioctl_hook = NULL;
-}
-
 static void __exit br_deinit(void)
 {
 	unregister_netdevice_notifier(&br_device_notifier);
-	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+
+	rtnl_lock();
+	br_ioctl_hook = NULL;
+	rtnl_unlock();
 
 	br_write_lock_bh(BR_NETPROTO_LOCK);
 	br_handle_frame_hook = NULL;
diff -urN linux-2.4.27-rc1/net/bridge/br_if.c linux-2.4.27-rc2/net/bridge/br_if.c
--- linux-2.4.27-rc1/net/bridge/br_if.c	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.27-rc2/net/bridge/br_if.c	2004-06-25 20:48:27.308121541 -0700
@@ -19,6 +19,7 @@
 #include <linux/inetdevice.h>
 #include <linux/rtnetlink.h>
 #include <linux/brlock.h>
+#include <linux/etherdevice.h>
 #include <asm/uaccess.h>
 #include "br_private.h"
 
@@ -44,7 +45,7 @@
 	struct net_bridge_port *p;
 	struct net_bridge_port **pptr;
 
-	if ((p = dev->br_port) == NULL)
+	if ((p = dev->br_port) == NULL || p->br != br)
 		return -EINVAL;
 
 	br_stp_disable_port(p);
@@ -232,6 +233,9 @@
 	if (dev->hard_start_xmit == br_dev_xmit)
 		return -ELOOP;
 
+	if (!is_valid_ether_addr(dev->dev_addr))
+		return -EADDRNOTAVAIL;
+
 	dev_hold(dev);
 	write_lock_bh(&br->lock);
 	if ((p = new_nbp(br, dev)) == NULL) {
diff -urN linux-2.4.27-rc1/net/bridge/br_ioctl.c linux-2.4.27-rc2/net/bridge/br_ioctl.c
--- linux-2.4.27-rc1/net/bridge/br_ioctl.c	2003-06-13 07:51:39.000000000 -0700
+++ linux-2.4.27-rc2/net/bridge/br_ioctl.c	2004-06-25 20:48:27.308121541 -0700
@@ -16,6 +16,7 @@
 #include <linux/kernel.h>
 #include <linux/if_bridge.h>
 #include <linux/inetdevice.h>
+#include <linux/rtnetlink.h>
 #include <asm/uaccess.h>
 #include "br_private.h"
 
@@ -230,11 +231,8 @@
 	return -EOPNOTSUPP;
 }
 
-static DECLARE_MUTEX(ioctl_mutex);
-
 int br_ioctl_deviceless_stub(unsigned long arg)
 {
-	int err;
 	unsigned long i[3];
 
 	if (!capable(CAP_NET_ADMIN))
@@ -243,11 +241,8 @@
 	if (copy_from_user(i, (void *)arg, 3*sizeof(unsigned long)))
 		return -EFAULT;
 
-	down(&ioctl_mutex);
-	err = br_ioctl_deviceless(i[0], i[1], i[2]);
-	up(&ioctl_mutex);
-
-	return err;
+	ASSERT_RTNL();
+	return  br_ioctl_deviceless(i[0], i[1], i[2]);
 }
 
 int br_ioctl(struct net_bridge *br, unsigned int cmd, unsigned long arg0, unsigned long arg1, unsigned long arg2)
@@ -257,18 +252,9 @@
 	if (!capable(CAP_NET_ADMIN))
 		return -EPERM;
 
-	down(&ioctl_mutex);
+	ASSERT_RTNL();
 	err = br_ioctl_deviceless(cmd, arg0, arg1);
 	if (err == -EOPNOTSUPP)
 		err = br_ioctl_device(br, cmd, arg0, arg1, arg2);
-	up(&ioctl_mutex);
-
 	return err;
 }
-
-void br_call_ioctl_atomic(void (*fn)(void))
-{
-	down(&ioctl_mutex);
-	fn();
-	up(&ioctl_mutex);
-}
diff -urN linux-2.4.27-rc1/net/bridge/br_private.h linux-2.4.27-rc2/net/bridge/br_private.h
--- linux-2.4.27-rc1/net/bridge/br_private.h	2003-08-25 04:44:44.000000000 -0700
+++ linux-2.4.27-rc2/net/bridge/br_private.h	2004-06-25 20:48:27.309121583 -0700
@@ -169,7 +169,6 @@
 extern void br_handle_frame(struct sk_buff *skb);
 
 /* br_ioctl.c */
-extern void br_call_ioctl_atomic(void (*fn)(void));
 extern int br_ioctl(struct net_bridge *br,
 	     unsigned int cmd,
 	     unsigned long arg0,
diff -urN linux-2.4.27-rc1/net/core/Makefile linux-2.4.27-rc2/net/core/Makefile
--- linux-2.4.27-rc1/net/core/Makefile	2003-11-28 10:26:21.000000000 -0800
+++ linux-2.4.27-rc2/net/core/Makefile	2004-06-25 20:48:27.309121583 -0700
@@ -9,7 +9,7 @@
 
 O_TARGET := core.o
 
-export-objs := netfilter.o profile.o
+export-objs := netfilter.o profile.o ethtool.o
 
 obj-y := sock.o skbuff.o iovec.o datagram.o scm.o
 
diff -urN linux-2.4.27-rc1/net/core/ethtool.c linux-2.4.27-rc2/net/core/ethtool.c
--- linux-2.4.27-rc1/net/core/ethtool.c	2004-02-18 05:36:32.000000000 -0800
+++ linux-2.4.27-rc2/net/core/ethtool.c	2004-06-25 20:48:27.310121625 -0700
@@ -9,6 +9,7 @@
  * It's GPL, stupid.
  */
 
+#include <linux/module.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/ethtool.h>
@@ -56,9 +57,26 @@
 	return 0;
 }
 
+#ifdef NETIF_F_TSO
+u32 ethtool_op_get_tso(struct net_device *dev)
+{
+	return (dev->features & NETIF_F_TSO) != 0;
+}
+
+int ethtool_op_set_tso(struct net_device *dev, u32 data)
+{
+	if (data)
+		dev->features |= NETIF_F_TSO;
+	else
+		dev->features &= ~NETIF_F_TSO;
+
+	return 0;
+}
+#endif /* NETIF_F_TSO */
+
 /* Handlers for each ethtool command */
 
-static int ethtool_get_settings(struct net_device *dev, void *useraddr)
+static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
 {
 	struct ethtool_cmd cmd = { ETHTOOL_GSET };
 	int err;
@@ -75,7 +93,7 @@
 	return 0;
 }
 
-static int ethtool_set_settings(struct net_device *dev, void *useraddr)
+static int ethtool_set_settings(struct net_device *dev, void __user *useraddr)
 {
 	struct ethtool_cmd cmd;
 
@@ -88,7 +106,7 @@
 	return dev->ethtool_ops->set_settings(dev, &cmd);
 }
 
-static int ethtool_get_drvinfo(struct net_device *dev, void *useraddr)
+static int ethtool_get_drvinfo(struct net_device *dev, void __user *useraddr)
 {
 	struct ethtool_drvinfo info;
 	struct ethtool_ops *ops = dev->ethtool_ops;
@@ -114,7 +132,7 @@
 	return 0;
 }
 
-static int ethtool_get_regs(struct net_device *dev, char *useraddr)
+static int ethtool_get_regs(struct net_device *dev, char __user *useraddr)
 {
 	struct ethtool_regs regs;
 	struct ethtool_ops *ops = dev->ethtool_ops;
@@ -150,7 +168,7 @@
 	return ret;
 }
 
-static int ethtool_get_wol(struct net_device *dev, char *useraddr)
+static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
 {
 	struct ethtool_wolinfo wol = { ETHTOOL_GWOL };
 
@@ -164,7 +182,7 @@
 	return 0;
 }
 
-static int ethtool_set_wol(struct net_device *dev, char *useraddr)
+static int ethtool_set_wol(struct net_device *dev, char __user *useraddr)
 {
 	struct ethtool_wolinfo wol;
 
@@ -177,7 +195,7 @@
 	return dev->ethtool_ops->set_wol(dev, &wol);
 }
 
-static int ethtool_get_msglevel(struct net_device *dev, char *useraddr)
+static int ethtool_get_msglevel(struct net_device *dev, char __user *useraddr)
 {
 	struct ethtool_value edata = { ETHTOOL_GMSGLVL };
 
@@ -191,7 +209,7 @@
 	return 0;
 }
 
-static int ethtool_set_msglevel(struct net_device *dev, char *useraddr)
+static int ethtool_set_msglevel(struct net_device *dev, char __user *useraddr)
 {
 	struct ethtool_value edata;
 
@@ -213,7 +231,7 @@
 	return dev->ethtool_ops->nway_reset(dev);
 }
 
-static int ethtool_get_link(struct net_device *dev, void *useraddr)
+static int ethtool_get_link(struct net_device *dev, void __user *useraddr)
 {
 	struct ethtool_value edata = { ETHTOOL_GLINK };
 
@@ -227,7 +245,7 @@
 	return 0;
 }
 
-static int ethtool_get_eeprom(struct net_device *dev, void *useraddr)
+static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr)
 {
 	struct ethtool_eeprom eeprom;
 	struct ethtool_ops *ops = dev->ethtool_ops;
@@ -272,7 +290,7 @@
 	return ret;
 }
 
-static int ethtool_set_eeprom(struct net_device *dev, void *useraddr)
+static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr)
 {
 	struct ethtool_eeprom eeprom;
 	struct ethtool_ops *ops = dev->ethtool_ops;
@@ -313,7 +331,7 @@
 	return ret;
 }
 
-static int ethtool_get_coalesce(struct net_device *dev, void *useraddr)
+static int ethtool_get_coalesce(struct net_device *dev, void __user *useraddr)
 {
 	struct ethtool_coalesce coalesce = { ETHTOOL_GCOALESCE };
 
@@ -327,7 +345,7 @@
 	return 0;
 }
 
-static int ethtool_set_coalesce(struct net_device *dev, void *useraddr)
+static int ethtool_set_coalesce(struct net_device *dev, void __user *useraddr)
 {
 	struct ethtool_coalesce coalesce;
 
@@ -340,7 +358,7 @@
 	return dev->ethtool_ops->set_coalesce(dev, &coalesce);
 }
 
-static int ethtool_get_ringparam(struct net_device *dev, void *useraddr)
+static int ethtool_get_ringparam(struct net_device *dev, void __user *useraddr)
 {
 	struct ethtool_ringparam ringparam = { ETHTOOL_GRINGPARAM };
 
@@ -354,7 +372,7 @@
 	return 0;
 }
 
-static int ethtool_set_ringparam(struct net_device *dev, void *useraddr)
+static int ethtool_set_ringparam(struct net_device *dev, void __user *useraddr)
 {
 	struct ethtool_ringparam ringparam;
 
@@ -367,7 +385,7 @@
 	return dev->ethtool_ops->set_ringparam(dev, &ringparam);
 }
 
-static int ethtool_get_pauseparam(struct net_device *dev, void *useraddr)
+static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr)
 {
 	struct ethtool_pauseparam pauseparam = { ETHTOOL_GPAUSEPARAM };
 
@@ -381,7 +399,7 @@
 	return 0;
 }
 
-static int ethtool_set_pauseparam(struct net_device *dev, void *useraddr)
+static int ethtool_set_pauseparam(struct net_device *dev, void __user *useraddr)
 {
 	struct ethtool_pauseparam pauseparam;
 
@@ -394,7 +412,7 @@
 	return dev->ethtool_ops->set_pauseparam(dev, &pauseparam);
 }
 
-static int ethtool_get_rx_csum(struct net_device *dev, char *useraddr)
+static int ethtool_get_rx_csum(struct net_device *dev, char __user *useraddr)
 {
 	struct ethtool_value edata = { ETHTOOL_GRXCSUM };
 
@@ -408,7 +426,7 @@
 	return 0;
 }
 
-static int ethtool_set_rx_csum(struct net_device *dev, char *useraddr)
+static int ethtool_set_rx_csum(struct net_device *dev, char __user *useraddr)
 {
 	struct ethtool_value edata;
 
@@ -422,7 +440,7 @@
 	return 0;
 }
 
-static int ethtool_get_tx_csum(struct net_device *dev, char *useraddr)
+static int ethtool_get_tx_csum(struct net_device *dev, char __user *useraddr)
 {
 	struct ethtool_value edata = { ETHTOOL_GTXCSUM };
 
@@ -436,7 +454,7 @@
 	return 0;
 }
 
-static int ethtool_set_tx_csum(struct net_device *dev, char *useraddr)
+static int ethtool_set_tx_csum(struct net_device *dev, char __user *useraddr)
 {
 	struct ethtool_value edata;
 
@@ -449,7 +467,7 @@
 	return dev->ethtool_ops->set_tx_csum(dev, edata.data);
 }
 
-static int ethtool_get_sg(struct net_device *dev, char *useraddr)
+static int ethtool_get_sg(struct net_device *dev, char __user *useraddr)
 {
 	struct ethtool_value edata = { ETHTOOL_GSG };
 
@@ -463,7 +481,7 @@
 	return 0;
 }
 
-static int ethtool_set_sg(struct net_device *dev, char *useraddr)
+static int ethtool_set_sg(struct net_device *dev, char __user *useraddr)
 {
 	struct ethtool_value edata;
 
@@ -476,7 +494,39 @@
 	return dev->ethtool_ops->set_sg(dev, edata.data);
 }
 
-static int ethtool_self_test(struct net_device *dev, char *useraddr)
+#ifdef NETIF_F_TSO
+static int ethtool_get_tso(struct net_device *dev, char __user *useraddr)
+{
+	struct ethtool_value edata = { ETHTOOL_GTSO };
+
+	if (!dev->ethtool_ops->get_tso)
+		return -EOPNOTSUPP;
+
+	edata.data = dev->ethtool_ops->get_tso(dev);
+
+	if (copy_to_user(useraddr, &edata, sizeof(edata)))
+		return -EFAULT;
+	return 0;
+}
+
+static int ethtool_set_tso(struct net_device *dev, char __user *useraddr)
+{
+	struct ethtool_value edata;
+
+	if (!dev->ethtool_ops->set_tso)
+		return -EOPNOTSUPP;
+
+	if (copy_from_user(&edata, useraddr, sizeof(edata)))
+		return -EFAULT;
+
+	return dev->ethtool_ops->set_tso(dev, edata.data);
+}
+
+EXPORT_SYMBOL(ethtool_op_get_tso);
+EXPORT_SYMBOL(ethtool_op_set_tso);
+#endif /* NETIF_F_TSO */
+
+static int ethtool_self_test(struct net_device *dev, char __user *useraddr)
 {
 	struct ethtool_test test;
 	struct ethtool_ops *ops = dev->ethtool_ops;
@@ -509,7 +559,7 @@
 	return ret;
 }
 
-static int ethtool_get_strings(struct net_device *dev, void *useraddr)
+static int ethtool_get_strings(struct net_device *dev, void __user *useraddr)
 {
 	struct ethtool_gstrings gstrings;
 	struct ethtool_ops *ops = dev->ethtool_ops;
@@ -556,7 +606,7 @@
 	return ret;
 }
 
-static int ethtool_phys_id(struct net_device *dev, void *useraddr)
+static int ethtool_phys_id(struct net_device *dev, void __user *useraddr)
 {
 	struct ethtool_value id;
 
@@ -569,7 +619,7 @@
 	return dev->ethtool_ops->phys_id(dev, id.data);
 }
 
-static int ethtool_get_stats(struct net_device *dev, void *useraddr)
+static int ethtool_get_stats(struct net_device *dev, void __user *useraddr)
 {
 	struct ethtool_stats stats;
 	struct ethtool_ops *ops = dev->ethtool_ops;
@@ -607,8 +657,9 @@
 int dev_ethtool(struct ifreq *ifr)
 {
 	struct net_device *dev = __dev_get_by_name(ifr->ifr_name);
-	void *useraddr = (void *) ifr->ifr_data;
+	void __user *useraddr = ifr->ifr_data;
 	u32 ethcmd;
+	int rc;
 
 	/*
 	 * XXX: This can be pushed down into the ethtool_* handlers that
@@ -626,69 +677,121 @@
 	if (copy_from_user(&ethcmd, useraddr, sizeof (ethcmd)))
 		return -EFAULT;
 
+	if(dev->ethtool_ops->begin)
+		if ((rc = dev->ethtool_ops->begin(dev)) < 0)
+			return rc;
+
 	switch (ethcmd) {
 	case ETHTOOL_GSET:
-		return ethtool_get_settings(dev, useraddr);
+		rc = ethtool_get_settings(dev, useraddr);
+		break;
 	case ETHTOOL_SSET:
-		return ethtool_set_settings(dev, useraddr);
+		rc = ethtool_set_settings(dev, useraddr);
+		break;
 	case ETHTOOL_GDRVINFO:
-		return ethtool_get_drvinfo(dev, useraddr);
+		rc = ethtool_get_drvinfo(dev, useraddr);
+
+		break;
 	case ETHTOOL_GREGS:
-		return ethtool_get_regs(dev, useraddr);
+		rc = ethtool_get_regs(dev, useraddr);
+		break;
 	case ETHTOOL_GWOL:
-		return ethtool_get_wol(dev, useraddr);
+		rc = ethtool_get_wol(dev, useraddr);
+		break;
 	case ETHTOOL_SWOL:
-		return ethtool_set_wol(dev, useraddr);
+		rc = ethtool_set_wol(dev, useraddr);
+		break;
 	case ETHTOOL_GMSGLVL:
-		return ethtool_get_msglevel(dev, useraddr);
+		rc = ethtool_get_msglevel(dev, useraddr);
+		break;
 	case ETHTOOL_SMSGLVL:
-		return ethtool_set_msglevel(dev, useraddr);
+		rc = ethtool_set_msglevel(dev, useraddr);
+		break;
 	case ETHTOOL_NWAY_RST:
-		return ethtool_nway_reset(dev);
+		rc = ethtool_nway_reset(dev);
+		break;
 	case ETHTOOL_GLINK:
-		return ethtool_get_link(dev, useraddr);
+		rc = ethtool_get_link(dev, useraddr);
+		break;
 	case ETHTOOL_GEEPROM:
-		return ethtool_get_eeprom(dev, useraddr);
+		rc = ethtool_get_eeprom(dev, useraddr);
+		break;
 	case ETHTOOL_SEEPROM:
-		return ethtool_set_eeprom(dev, useraddr);
+		rc = ethtool_set_eeprom(dev, useraddr);
+		break;
 	case ETHTOOL_GCOALESCE:
-		return ethtool_get_coalesce(dev, useraddr);
+		rc = ethtool_get_coalesce(dev, useraddr);
+		break;
 	case ETHTOOL_SCOALESCE:
-		return ethtool_set_coalesce(dev, useraddr);
+		rc = ethtool_set_coalesce(dev, useraddr);
+		break;
 	case ETHTOOL_GRINGPARAM:
-		return ethtool_get_ringparam(dev, useraddr);
+		rc = ethtool_get_ringparam(dev, useraddr);
+		break;
 	case ETHTOOL_SRINGPARAM:
-		return ethtool_set_ringparam(dev, useraddr);
+		rc = ethtool_set_ringparam(dev, useraddr);
+		break;
 	case ETHTOOL_GPAUSEPARAM:
-		return ethtool_get_pauseparam(dev, useraddr);
+		rc = ethtool_get_pauseparam(dev, useraddr);
+		break;
 	case ETHTOOL_SPAUSEPARAM:
-		return ethtool_set_pauseparam(dev, useraddr);
+		rc = ethtool_set_pauseparam(dev, useraddr);
+		break;
 	case ETHTOOL_GRXCSUM:
-		return ethtool_get_rx_csum(dev, useraddr);
+		rc = ethtool_get_rx_csum(dev, useraddr);
+		break;
 	case ETHTOOL_SRXCSUM:
-		return ethtool_set_rx_csum(dev, useraddr);
+		rc = ethtool_set_rx_csum(dev, useraddr);
+		break;
 	case ETHTOOL_GTXCSUM:
-		return ethtool_get_tx_csum(dev, useraddr);
+		rc = ethtool_get_tx_csum(dev, useraddr);
+		break;
 	case ETHTOOL_STXCSUM:
-		return ethtool_set_tx_csum(dev, useraddr);
+		rc = ethtool_set_tx_csum(dev, useraddr);
+		break;
 	case ETHTOOL_GSG:
-		return ethtool_get_sg(dev, useraddr);
+		rc = ethtool_get_sg(dev, useraddr);
+		break;
 	case ETHTOOL_SSG:
-		return ethtool_set_sg(dev, useraddr);
+		rc = ethtool_set_sg(dev, useraddr);
+		break;
+#ifdef NETIF_F_TSO
+	case ETHTOOL_GTSO:
+		rc = ethtool_get_tso(dev, useraddr);
+		break;
+	case ETHTOOL_STSO:
+		rc = ethtool_set_tso(dev, useraddr);
+		break;
+#endif /* NETIF_F_TSO */
 	case ETHTOOL_TEST:
-		return ethtool_self_test(dev, useraddr);
+		rc = ethtool_self_test(dev, useraddr);
+		break;
 	case ETHTOOL_GSTRINGS:
-		return ethtool_get_strings(dev, useraddr);
+		rc = ethtool_get_strings(dev, useraddr);
+		break;
 	case ETHTOOL_PHYS_ID:
-		return ethtool_phys_id(dev, useraddr);
+		rc = ethtool_phys_id(dev, useraddr);
+		break;
 	case ETHTOOL_GSTATS:
-		return ethtool_get_stats(dev, useraddr);
+		rc = ethtool_get_stats(dev, useraddr);
+		break;
 	default:
-		return -EOPNOTSUPP;
+		rc =  -EOPNOTSUPP;
 	}
+	
+	if(dev->ethtool_ops->complete)
+		dev->ethtool_ops->complete(dev);
+	return rc;
 
  ioctl:
 	if (dev->do_ioctl)
 		return dev->do_ioctl(dev, ifr, SIOCETHTOOL);
 	return -EOPNOTSUPP;
 }
+
+EXPORT_SYMBOL(dev_ethtool);
+EXPORT_SYMBOL(ethtool_op_get_link);
+EXPORT_SYMBOL(ethtool_op_get_sg);
+EXPORT_SYMBOL(ethtool_op_get_tx_csum);
+EXPORT_SYMBOL(ethtool_op_set_sg);
+EXPORT_SYMBOL(ethtool_op_set_tx_csum);
diff -urN linux-2.4.27-rc1/net/decnet/dn_dev.c linux-2.4.27-rc2/net/decnet/dn_dev.c
--- linux-2.4.27-rc1/net/decnet/dn_dev.c	2001-12-21 09:42:05.000000000 -0800
+++ linux-2.4.27-rc2/net/decnet/dn_dev.c	2004-06-25 20:48:27.311121666 -0700
@@ -1061,31 +1061,39 @@
 {
 	struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
 	struct dn_ifaddr *ifa;
-	struct ifreq *ifr = (struct ifreq *)buf;
+	char buffer[DN_IFREQ_SIZE];
+	struct ifreq *ifr = (struct ifreq *)buffer;
+	struct sockaddr_dn *addr = (struct sockaddr_dn *)&ifr->ifr_addr;
 	int done = 0;
 
 	if ((dn_db == NULL) || ((ifa = dn_db->ifa_list) == NULL))
 		return 0;
 
 	for(; ifa; ifa = ifa->ifa_next) {
-		if (!ifr) {
+		if (!buf) {
 			done += sizeof(DN_IFREQ_SIZE);
 			continue;
 		}
 		if (len < DN_IFREQ_SIZE)
 			return done;
-		memset(ifr, 0, DN_IFREQ_SIZE);
+		memset(buffer, 0, DN_IFREQ_SIZE);
 
 		if (ifa->ifa_label)
 			strcpy(ifr->ifr_name, ifa->ifa_label);
 		else
 			strcpy(ifr->ifr_name, dev->name);
 
-		(*(struct sockaddr_dn *) &ifr->ifr_addr).sdn_family = AF_DECnet;
-		(*(struct sockaddr_dn *) &ifr->ifr_addr).sdn_add.a_len = 2;
-		(*(dn_address *)(*(struct sockaddr_dn *) &ifr->ifr_addr).sdn_add.a_addr) = ifa->ifa_local;
+		addr->sdn_family = AF_DECnet;
+		addr->sdn_add.a_len = 2;
+		memcpy(addr->sdn_add.a_addr, &ifa->ifa_local,
+			sizeof(dn_address));
+
+		if (copy_to_user(buf, buffer, DN_IFREQ_SIZE)) {
+			done = -EFAULT;
+			break;
+		}
 
-		ifr = (struct ifreq *)((char *)ifr + DN_IFREQ_SIZE);
+		buf  += DN_IFREQ_SIZE;
 		len  -= DN_IFREQ_SIZE;
 		done += DN_IFREQ_SIZE;
 	}
diff -urN linux-2.4.27-rc1/net/ipv4/ip_gre.c linux-2.4.27-rc2/net/ipv4/ip_gre.c
--- linux-2.4.27-rc1/net/ipv4/ip_gre.c	2003-11-28 10:26:21.000000000 -0800
+++ linux-2.4.27-rc2/net/ipv4/ip_gre.c	2004-06-25 20:48:27.314121792 -0700
@@ -644,13 +644,7 @@
 		skb->dev = tunnel->dev;
 		dst_release(skb->dst);
 		skb->dst = NULL;
-#ifdef CONFIG_NETFILTER
-		nf_conntrack_put(skb->nfct);
-		skb->nfct = NULL;
-#ifdef CONFIG_NETFILTER_DEBUG
-		skb->nf_debug = 0;
-#endif
-#endif
+		nf_reset(skb);
 		ipgre_ecn_decapsulate(iph, skb);
 		netif_rx(skb);
 		read_unlock(&ipgre_lock);
@@ -876,13 +870,7 @@
 		}
 	}
 
-#ifdef CONFIG_NETFILTER
-	nf_conntrack_put(skb->nfct);
-	skb->nfct = NULL;
-#ifdef CONFIG_NETFILTER_DEBUG
-	skb->nf_debug = 0;
-#endif
-#endif
+	nf_reset(skb);
 
 	IPTUNNEL_XMIT();
 	tunnel->recursion--;
diff -urN linux-2.4.27-rc1/net/ipv4/ip_input.c linux-2.4.27-rc2/net/ipv4/ip_input.c
--- linux-2.4.27-rc1/net/ipv4/ip_input.c	2002-08-02 17:39:46.000000000 -0700
+++ linux-2.4.27-rc2/net/ipv4/ip_input.c	2004-06-25 20:48:27.314121792 -0700
@@ -226,12 +226,9 @@
 
 	__skb_pull(skb, ihl);
 
-#ifdef CONFIG_NETFILTER
 	/* Free reference early: we don't need it any more, and it may
            hold ip_conntrack module loaded indefinitely. */
-	nf_conntrack_put(skb->nfct);
-	skb->nfct = NULL;
-#endif /*CONFIG_NETFILTER*/
+	nf_reset(skb);
 
         /* Point into the IP datagram, just past the header. */
         skb->h.raw = skb->data;
diff -urN linux-2.4.27-rc1/net/ipv4/ipip.c linux-2.4.27-rc2/net/ipv4/ipip.c
--- linux-2.4.27-rc1/net/ipv4/ipip.c	2003-11-28 10:26:21.000000000 -0800
+++ linux-2.4.27-rc2/net/ipv4/ipip.c	2004-06-25 20:48:27.315121833 -0700
@@ -495,13 +495,7 @@
 		skb->dev = tunnel->dev;
 		dst_release(skb->dst);
 		skb->dst = NULL;
-#ifdef CONFIG_NETFILTER
-		nf_conntrack_put(skb->nfct);
-		skb->nfct = NULL;
-#ifdef CONFIG_NETFILTER_DEBUG
-		skb->nf_debug = 0;
-#endif
-#endif
+		nf_reset(skb);
 		ipip_ecn_decapsulate(iph, skb);
 		netif_rx(skb);
 		read_unlock(&ipip_lock);
@@ -646,13 +640,7 @@
 	if ((iph->ttl = tiph->ttl) == 0)
 		iph->ttl	=	old_iph->ttl;
 
-#ifdef CONFIG_NETFILTER
-	nf_conntrack_put(skb->nfct);
-	skb->nfct = NULL;
-#ifdef CONFIG_NETFILTER_DEBUG
-	skb->nf_debug = 0;
-#endif
-#endif
+	nf_reset(skb);
 
 	IPTUNNEL_XMIT();
 	tunnel->recursion--;
diff -urN linux-2.4.27-rc1/net/ipv4/ipmr.c linux-2.4.27-rc2/net/ipv4/ipmr.c
--- linux-2.4.27-rc1/net/ipv4/ipmr.c	2003-11-28 10:26:21.000000000 -0800
+++ linux-2.4.27-rc2/net/ipv4/ipmr.c	2004-06-25 20:48:27.316121875 -0700
@@ -1097,10 +1097,7 @@
 	skb->h.ipiph = skb->nh.iph;
 	skb->nh.iph = iph;
 	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
-#ifdef CONFIG_NETFILTER
-	nf_conntrack_put(skb->nfct);
-	skb->nfct = NULL;
-#endif
+	nf_reset(skb);
 }
 
 static inline int ipmr_forward_finish(struct sk_buff *skb)
@@ -1446,10 +1443,7 @@
 	skb->dst = NULL;
 	((struct net_device_stats*)reg_dev->priv)->rx_bytes += skb->len;
 	((struct net_device_stats*)reg_dev->priv)->rx_packets++;
-#ifdef CONFIG_NETFILTER
-	nf_conntrack_put(skb->nfct);
-	skb->nfct = NULL;
-#endif
+	nf_reset(skb);
 	netif_rx(skb);
 	dev_put(reg_dev);
 	return 0;
@@ -1513,10 +1507,7 @@
 	((struct net_device_stats*)reg_dev->priv)->rx_bytes += skb->len;
 	((struct net_device_stats*)reg_dev->priv)->rx_packets++;
 	skb->dst = NULL;
-#ifdef CONFIG_NETFILTER
-	nf_conntrack_put(skb->nfct);
-	skb->nfct = NULL;
-#endif
+	nf_reset(skb);
 	netif_rx(skb);
 	dev_put(reg_dev);
 	return 0;
diff -urN linux-2.4.27-rc1/net/ipv4/netfilter/ip_conntrack_core.c linux-2.4.27-rc2/net/ipv4/netfilter/ip_conntrack_core.c
--- linux-2.4.27-rc1/net/ipv4/netfilter/ip_conntrack_core.c	2004-06-25 20:48:24.415000860 -0700
+++ linux-2.4.27-rc2/net/ipv4/netfilter/ip_conntrack_core.c	2004-06-25 20:48:27.318121958 -0700
@@ -175,13 +175,12 @@
 destroy_expect(struct ip_conntrack_expect *exp)
 {
 	DEBUGP("destroy_expect(%p) use=%d\n", exp, atomic_read(&exp->use));
-	IP_NF_ASSERT(atomic_read(&exp->use));
+	IP_NF_ASSERT(atomic_read(&exp->use) == 0);
 	IP_NF_ASSERT(!timer_pending(&exp->timeout));
 
 	kfree(exp);
 }
 
-
 inline void ip_conntrack_expect_put(struct ip_conntrack_expect *exp)
 {
 	IP_NF_ASSERT(exp);
@@ -504,7 +503,7 @@
 		 enum ip_conntrack_info *ctinfo,
 		 unsigned int hooknum)
 {
-	const struct iphdr *iph;
+	const struct iphdr *iph = skb->nh.iph;
 	struct icmphdr *hdr;
 	struct ip_conntrack_tuple innertuple, origtuple;
 	struct iphdr *inner;
@@ -515,7 +514,6 @@
 	IP_NF_ASSERT(iph->protocol == IPPROTO_ICMP);
 	IP_NF_ASSERT(skb->nfct == NULL);
 
-	iph = skb->nh.iph;
 	hdr = (struct icmphdr *)((u_int32_t *)iph + iph->ihl);
 	inner = (struct iphdr *)(hdr + 1);
 	datalen = skb->len - iph->ihl*4 - sizeof(*hdr);
@@ -731,7 +729,6 @@
 		DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n",
 			conntrack, expected);
 		/* Welcome, Mr. Bond.  We've been expecting you... */
-		IP_NF_ASSERT(master_ct(conntrack));
 		__set_bit(IPS_EXPECTED_BIT, &conntrack->status);
 		conntrack->master = expected;
 		expected->sibling = conntrack;
diff -urN linux-2.4.27-rc1/net/ipv4/netfilter/ip_conntrack_standalone.c linux-2.4.27-rc2/net/ipv4/netfilter/ip_conntrack_standalone.c
--- linux-2.4.27-rc1/net/ipv4/netfilter/ip_conntrack_standalone.c	2004-02-18 05:36:32.000000000 -0800
+++ linux-2.4.27-rc2/net/ipv4/netfilter/ip_conntrack_standalone.c	2004-06-25 20:48:27.319122000 -0700
@@ -349,7 +349,7 @@
 	if (ret < 0)
 		goto cleanup_nothing;
 
-	proc = proc_net_create("ip_conntrack",0,list_conntracks);
+	proc = proc_net_create("ip_conntrack", 0440, list_conntracks);
 	if (!proc) goto cleanup_init;
 	proc->owner = THIS_MODULE;
 
diff -urN linux-2.4.27-rc1/net/ipv4/netfilter/ip_nat_core.c linux-2.4.27-rc2/net/ipv4/netfilter/ip_nat_core.c
--- linux-2.4.27-rc1/net/ipv4/netfilter/ip_nat_core.c	2004-02-18 05:36:32.000000000 -0800
+++ linux-2.4.27-rc2/net/ipv4/netfilter/ip_nat_core.c	2004-06-25 20:48:27.320122042 -0700
@@ -520,6 +520,7 @@
 	MUST_BE_WRITE_LOCKED(&ip_nat_lock);
 	IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING
 		     || hooknum == NF_IP_POST_ROUTING
+		     || hooknum == NF_IP_LOCAL_IN
 		     || hooknum == NF_IP_LOCAL_OUT);
 	IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS);
 	IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
@@ -882,9 +883,11 @@
 
 	IP_NF_ASSERT(skb->len >= iph->ihl*4 + sizeof(struct icmphdr));
 	/* Must be RELATED */
-	IP_NF_ASSERT(skb->nfct - (struct ip_conntrack *)skb->nfct->master
+	IP_NF_ASSERT(skb->nfct
+		     - ((struct ip_conntrack *)skb->nfct->master)->infos
 		     == IP_CT_RELATED
-		     || skb->nfct - (struct ip_conntrack *)skb->nfct->master
+		     || skb->nfct
+		     - ((struct ip_conntrack *)skb->nfct->master)->infos
 		     == IP_CT_RELATED+IP_CT_IS_REPLY);
 
 	/* Redirects on non-null nats must be dropped, else they'll
diff -urN linux-2.4.27-rc1/net/ipv4/netfilter/ip_tables.c linux-2.4.27-rc2/net/ipv4/netfilter/ip_tables.c
--- linux-2.4.27-rc1/net/ipv4/netfilter/ip_tables.c	2004-06-25 20:48:24.416000902 -0700
+++ linux-2.4.27-rc2/net/ipv4/netfilter/ip_tables.c	2004-06-25 20:48:27.321122084 -0700
@@ -1714,6 +1714,15 @@
 	return 0;
 }
 
+static inline int print_target(const struct ipt_target *t,
+                               off_t start_offset, char *buffer, int length,
+                               off_t *pos, unsigned int *count)
+{
+	if (t == &ipt_standard_target || t == &ipt_error_target)
+		return 0;
+	return print_name((char *)t, start_offset, buffer, length, pos, count);
+}
+
 static int ipt_get_tables(char *buffer, char **start, off_t offset, int length)
 {
 	off_t pos = 0;
@@ -1740,7 +1749,7 @@
 	if (down_interruptible(&ipt_mutex) != 0)
 		return 0;
 
-	LIST_FIND(&ipt_target, print_name, void *,
+	LIST_FIND(&ipt_target, print_target, struct ipt_target *,
 		  offset, buffer, length, &pos, &count);
 	
 	up(&ipt_mutex);
diff -urN linux-2.4.27-rc1/net/ipv4/netfilter/ipt_REJECT.c linux-2.4.27-rc2/net/ipv4/netfilter/ipt_REJECT.c
--- linux-2.4.27-rc1/net/ipv4/netfilter/ipt_REJECT.c	2003-11-28 10:26:21.000000000 -0800
+++ linux-2.4.27-rc2/net/ipv4/netfilter/ipt_REJECT.c	2004-06-25 20:48:27.322122125 -0700
@@ -126,12 +126,8 @@
 	nskb->dst = &rt->u.dst;
 
 	/* This packet will not be the same as the other: clear nf fields */
-	nf_conntrack_put(nskb->nfct);
-	nskb->nfct = NULL;
+	nf_reset(nskb);
 	nskb->nfcache = 0;
-#ifdef CONFIG_NETFILTER_DEBUG
-	nskb->nf_debug = 0;
-#endif
 	nskb->nfmark = 0;
 
 	tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl);
diff -urN linux-2.4.27-rc1/net/ipv4/netfilter/ipt_helper.c linux-2.4.27-rc2/net/ipv4/netfilter/ipt_helper.c
--- linux-2.4.27-rc1/net/ipv4/netfilter/ipt_helper.c	2004-02-18 05:36:32.000000000 -0800
+++ linux-2.4.27-rc2/net/ipv4/netfilter/ipt_helper.c	2004-06-25 20:48:27.322122125 -0700
@@ -37,17 +37,17 @@
 	struct ip_conntrack_expect *exp;
 	struct ip_conntrack *ct;
 	enum ip_conntrack_info ctinfo;
-	int ret = 0;
+	int ret = info->invert;
 	
 	ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo);
 	if (!ct) {
 		DEBUGP("ipt_helper: Eek! invalid conntrack?\n");
-		return 0;
+		return ret;
 	}
 
 	if (!ct->master) {
 		DEBUGP("ipt_helper: conntrack %p has no master\n", ct);
-		return 0;
+		return ret;
 	}
 
 	exp = ct->master;
@@ -67,8 +67,11 @@
 	DEBUGP("master's name = %s , info->name = %s\n", 
 		exp->expectant->helper->name, info->name);
 
-	ret = !strncmp(exp->expectant->helper->name, info->name, 
-	               strlen(exp->expectant->helper->name)) ^ info->invert;
+	if (info->name[0] == '\0')
+		ret ^= 1;
+	else
+		ret ^= !strncmp(exp->expectant->helper->name, info->name, 
+		                strlen(exp->expectant->helper->name));
 out_unlock:
 	READ_UNLOCK(&ip_conntrack_lock);
 	return ret;
@@ -88,10 +91,6 @@
 	if (matchsize != IPT_ALIGN(sizeof(struct ipt_helper_info)))
 		return 0;
 
-	/* verify that we actually should match anything */
-	if ( strlen(info->name) == 0 )
-		return 0;
-	
 	return 1;
 }
 
diff -urN linux-2.4.27-rc1/net/ipv4/netfilter/ipt_owner.c linux-2.4.27-rc2/net/ipv4/netfilter/ipt_owner.c
--- linux-2.4.27-rc1/net/ipv4/netfilter/ipt_owner.c	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.27-rc2/net/ipv4/netfilter/ipt_owner.c	2004-06-25 20:48:27.322122125 -0700
@@ -171,7 +171,15 @@
 
 	if (matchsize != IPT_ALIGN(sizeof(struct ipt_owner_info)))
 		return 0;
-
+#ifdef CONFIG_SMP
+	/* files->file_lock can not be used in a BH */
+	if (((struct ipt_owner_info *)matchinfo)->match
+	    & (IPT_OWNER_PID|IPT_OWNER_SID|IPT_OWNER_COMM)) {
+		printk("ipt_owner: pid, sid and command matching is broken "
+		       "on SMP.\n");
+		return 0;
+	}
+#endif
 	return 1;
 }
 
diff -urN linux-2.4.27-rc1/net/ipv4/netfilter/ipt_unclean.c linux-2.4.27-rc2/net/ipv4/netfilter/ipt_unclean.c
--- linux-2.4.27-rc1/net/ipv4/netfilter/ipt_unclean.c	2003-11-28 10:26:21.000000000 -0800
+++ linux-2.4.27-rc2/net/ipv4/netfilter/ipt_unclean.c	2004-06-25 20:48:27.323122167 -0700
@@ -467,9 +467,8 @@
 	/* Fragment checks. */
 
 	/* CHECK: More fragments, but doesn't fill 8-byte boundary. */
-	if ((ntohs(iph->frag_off) & IP_MF)
-	    && (ntohs(iph->tot_len) % 8) != 0) {
-		limpk("Truncated fragment %u long.\n", ntohs(iph->tot_len));
+	if ((ntohs(iph->frag_off) & IP_MF) && datalen % 8 != 0) {
+		limpk("Truncated fragment %u long.\n", datalen);
 		return 0;
 	}
 
@@ -479,14 +478,6 @@
 		return 0;
 	}
 
-	/* CHECK: DF set and offset or MF set. */
-	if ((ntohs(iph->frag_off) & IP_DF)
-	    && (offset || (ntohs(iph->frag_off) & IP_MF))) {
-		limpk("DF set and offset=%u, MF=%u.\n",
-		      offset, ntohs(iph->frag_off) & IP_MF);
-		return 0;
-	}
-
 	/* CHECK: Zero-sized fragments. */
 	if ((offset || (ntohs(iph->frag_off) & IP_MF))
 	    && datalen == 0) {
diff -urN linux-2.4.27-rc1/net/ipv4/netfilter/iptable_mangle.c linux-2.4.27-rc2/net/ipv4/netfilter/iptable_mangle.c
--- linux-2.4.27-rc1/net/ipv4/netfilter/iptable_mangle.c	2003-06-13 07:51:39.000000000 -0700
+++ linux-2.4.27-rc2/net/ipv4/netfilter/iptable_mangle.c	2004-06-25 20:48:27.323122167 -0700
@@ -160,7 +160,9 @@
 	if (ret != NF_DROP && ret != NF_STOLEN && ret != NF_QUEUE
 	    && ((*pskb)->nh.iph->saddr != saddr
 		|| (*pskb)->nh.iph->daddr != daddr
+#ifdef CONFIG_IP_ROUTE_FWMARK
 		|| (*pskb)->nfmark != nfmark
+#endif
 		|| (*pskb)->nh.iph->tos != tos))
 		return ip_route_me_harder(pskb) == 0 ? ret : NF_DROP;
 
diff -urN linux-2.4.27-rc1/net/ipv6/mcast.c linux-2.4.27-rc2/net/ipv6/mcast.c
--- linux-2.4.27-rc1/net/ipv6/mcast.c	2004-04-14 06:05:41.000000000 -0700
+++ linux-2.4.27-rc2/net/ipv6/mcast.c	2004-06-25 20:48:27.333122584 -0700
@@ -362,7 +362,7 @@
 	err = -EADDRNOTAVAIL;
 
 	for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) {
-		if (pmc->ifindex != pgsr->gsr_interface)
+		if (pgsr->gsr_interface && pmc->ifindex != pgsr->gsr_interface)
 			continue;
 		if (ipv6_addr_cmp(&pmc->addr, group) == 0)
 			break;
diff -urN linux-2.4.27-rc1/net/ipv6/netfilter/ip6_tables.c linux-2.4.27-rc2/net/ipv6/netfilter/ip6_tables.c
--- linux-2.4.27-rc1/net/ipv6/netfilter/ip6_tables.c	2004-06-25 20:48:24.427001361 -0700
+++ linux-2.4.27-rc2/net/ipv6/netfilter/ip6_tables.c	2004-06-25 20:48:27.335122668 -0700
@@ -1822,6 +1822,15 @@
 	return 0;
 }
 
+static inline int print_target(const struct ip6t_target *t,
+                               off_t start_offset, char *buffer, int length,
+                               off_t *pos, unsigned int *count)
+{
+	if (t == &ip6t_standard_target || t == &ip6t_error_target)
+		return 0;
+	return print_name((char *)t, start_offset, buffer, length, pos, count);
+}
+
 static int ip6t_get_tables(char *buffer, char **start, off_t offset, int length)
 {
 	off_t pos = 0;
@@ -1848,7 +1857,7 @@
 	if (down_interruptible(&ip6t_mutex) != 0)
 		return 0;
 
-	LIST_FIND(&ip6t_target, print_name, char *,
+	LIST_FIND(&ip6t_target, print_target, struct ip6t_target *,
 		  offset, buffer, length, &pos, &count);
 
 	up(&ip6t_mutex);
diff -urN linux-2.4.27-rc1/net/ipv6/netfilter/ip6t_owner.c linux-2.4.27-rc2/net/ipv6/netfilter/ip6t_owner.c
--- linux-2.4.27-rc1/net/ipv6/netfilter/ip6t_owner.c	2001-10-30 15:08:12.000000000 -0800
+++ linux-2.4.27-rc2/net/ipv6/netfilter/ip6t_owner.c	2004-06-25 20:48:27.335122668 -0700
@@ -137,7 +137,14 @@
 
 	if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_owner_info)))
 		return 0;
-
+#ifdef CONFIG_SMP
+	/* files->file_lock can not be used in a BH */
+	if (((struct ip6t_owner_info *)matchinfo)->match
+	    & (IP6T_OWNER_PID|IP6T_OWNER_SID)) {
+		printk("ip6t_owner: pid and sid matching is broken on SMP.\n");
+		return 0;
+	}
+#endif
 	return 1;
 }
 
diff -urN linux-2.4.27-rc1/net/ipv6/sit.c linux-2.4.27-rc2/net/ipv6/sit.c
--- linux-2.4.27-rc1/net/ipv6/sit.c	2003-11-28 10:26:21.000000000 -0800
+++ linux-2.4.27-rc2/net/ipv6/sit.c	2004-06-25 20:48:27.336122709 -0700
@@ -402,13 +402,7 @@
 		skb->dev = tunnel->dev;
 		dst_release(skb->dst);
 		skb->dst = NULL;
-#ifdef CONFIG_NETFILTER
-		nf_conntrack_put(skb->nfct);
-		skb->nfct = NULL;
-#ifdef CONFIG_NETFILTER_DEBUG
-		skb->nf_debug = 0;
-#endif
-#endif
+		nf_reset(skb);
 		ipip6_ecn_decapsulate(iph, skb);
 		netif_rx(skb);
 		read_unlock(&ipip6_lock);
@@ -599,13 +593,7 @@
 	if ((iph->ttl = tiph->ttl) == 0)
 		iph->ttl	=	iph6->hop_limit;
 
-#ifdef CONFIG_NETFILTER
-	nf_conntrack_put(skb->nfct);
-	skb->nfct = NULL;
-#ifdef CONFIG_NETFILTER_DEBUG
-	skb->nf_debug = 0;
-#endif
-#endif
+	nf_reset(skb);
 
 	IPTUNNEL_XMIT();
 	tunnel->recursion--;
diff -urN linux-2.4.27-rc1/net/netsyms.c linux-2.4.27-rc2/net/netsyms.c
--- linux-2.4.27-rc1/net/netsyms.c	2004-04-14 06:05:41.000000000 -0700
+++ linux-2.4.27-rc2/net/netsyms.c	2004-06-25 20:48:27.339122835 -0700
@@ -18,7 +18,6 @@
 #include <linux/fcdevice.h>
 #include <linux/ioport.h>
 #include <linux/tty.h>
-#include <linux/ethtool.h>
 #include <net/neighbour.h>
 #include <net/snmp.h>
 #include <net/dst.h>
@@ -622,11 +621,4 @@
 EXPORT_SYMBOL(wireless_spy_update);
 #endif /* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */
 
-/* ethtool.c */
-EXPORT_SYMBOL(ethtool_op_get_link);
-EXPORT_SYMBOL(ethtool_op_get_tx_csum);
-EXPORT_SYMBOL(ethtool_op_set_tx_csum);
-EXPORT_SYMBOL(ethtool_op_get_sg);
-EXPORT_SYMBOL(ethtool_op_set_sg);
-
 #endif  /* CONFIG_NET */
diff -urN linux-2.4.27-rc1/net/sched/sch_cbq.c linux-2.4.27-rc2/net/sched/sch_cbq.c
--- linux-2.4.27-rc1/net/sched/sch_cbq.c	2004-02-18 05:36:32.000000000 -0800
+++ linux-2.4.27-rc2/net/sched/sch_cbq.c	2004-06-25 20:48:27.340122876 -0700
@@ -1054,7 +1054,7 @@
 
 	if (sch->q.qlen) {
 		sch->stats.overlimits++;
-		if (q->wd_expires && !netif_queue_stopped(sch->dev)) {
+		if (q->wd_expires) {
 			long delay = PSCHED_US2JIFFIE(q->wd_expires);
 			if (delay <= 0)
 				delay = 1;
diff -urN linux-2.4.27-rc1/net/sched/sch_delay.c linux-2.4.27-rc2/net/sched/sch_delay.c
--- linux-2.4.27-rc1/net/sched/sch_delay.c	2004-06-25 20:48:24.430001486 -0700
+++ linux-2.4.27-rc2/net/sched/sch_delay.c	2004-06-25 20:48:27.340122876 -0700
@@ -40,6 +40,7 @@
 struct dly_sched_data {
 	u32	latency;
 	u32	limit;
+	u32	loss;
 	struct timer_list timer;
 	struct Qdisc *qdisc;
 };
@@ -58,6 +59,12 @@
 	struct dly_skb_cb *cb = (struct dly_skb_cb *)skb->cb;
 	int ret;
 
+	/* Random packet drop 0 => none, ~0 => all */
+	if (q->loss >= net_random()) {
+		sch->stats.drops++;
+		return 0;	/* lie about loss so TCP doesn't know */
+	}
+
 	PSCHED_GET_TIME(cb->queuetime);
 
 	/* Queue to underlying scheduler */
@@ -69,7 +76,7 @@
 		sch->stats.bytes += skb->len;
 		sch->stats.packets++;
 	}
-	return 0;
+	return ret;
 }
 
 /* Requeue packets but don't change time stamp */
@@ -104,12 +111,14 @@
 static struct sk_buff *dly_dequeue(struct Qdisc *sch)
 {
 	struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
-	struct sk_buff *skb = q->qdisc->dequeue(q->qdisc);
+	struct sk_buff *skb;
 
+ retry:
+	skb = q->qdisc->dequeue(q->qdisc);
 	if (skb) {
 		struct dly_skb_cb *cb = (struct dly_skb_cb *)skb->cb;
 		psched_time_t now;
-		long diff;
+		long diff, delay;
 
 		PSCHED_GET_TIME(now);
 		diff = q->latency - PSCHED_TDIFF(now, cb->queuetime);
@@ -120,17 +129,17 @@
 			return skb;
 		}
 
-		if (!netif_queue_stopped(sch->dev)) {
-			long delay = PSCHED_US2JIFFIE(diff);
-			if (delay <= 0)
-				delay = 1;
-			mod_timer(&q->timer, jiffies+delay);
-		}
-
 		if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) {
 			sch->q.qlen--;
 			sch->stats.drops++;
+			goto retry;
 		}
+
+		delay = PSCHED_US2JIFFIE(diff);
+		if (delay <= 0)
+		  delay = 1;
+		mod_timer(&q->timer, jiffies+delay);
+
 		sch->flags |= TCQ_F_THROTTLED;
 	}
 	return NULL;
@@ -195,6 +204,7 @@
 	} else {
 		q->latency = qopt->latency;
 		q->limit = qopt->limit;
+		q->loss = qopt->loss;
 	}
 	return err;
 }
@@ -240,6 +250,7 @@
 
 	qopt.latency = q->latency;
 	qopt.limit = q->limit;
+	qopt.loss = q->loss;
 
 	RTA_PUT(skb, TCA_OPTIONS, sizeof(qopt), &qopt);
 
diff -urN linux-2.4.27-rc1/net/sched/sch_hfsc.c linux-2.4.27-rc2/net/sched/sch_hfsc.c
--- linux-2.4.27-rc1/net/sched/sch_hfsc.c	2004-04-14 06:05:41.000000000 -0700
+++ linux-2.4.27-rc2/net/sched/sch_hfsc.c	2004-06-25 20:48:27.341122918 -0700
@@ -1722,8 +1722,7 @@
 		cl = actlist_get_minvt(&q->root, cur_time);
 		if (cl == NULL) {
 			sch->stats.overlimits++;
-			if (!netif_queue_stopped(sch->dev))
-				hfsc_schedule_watchdog(sch, cur_time);
+			hfsc_schedule_watchdog(sch, cur_time);
 			return NULL;
 		}
 	}
diff -urN linux-2.4.27-rc1/net/sched/sch_htb.c linux-2.4.27-rc2/net/sched/sch_htb.c
--- linux-2.4.27-rc1/net/sched/sch_htb.c	2004-04-14 06:05:41.000000000 -0700
+++ linux-2.4.27-rc2/net/sched/sch_htb.c	2004-06-25 20:48:27.342122960 -0700
@@ -1017,7 +1017,6 @@
 static void htb_delay_by(struct Qdisc *sch,long delay)
 {
 	struct htb_sched *q = (struct htb_sched *)sch->data;
-	if (netif_queue_stopped(sch->dev)) return;
 	if (delay <= 0) delay = 1;
 	if (unlikely(delay > 5*HZ)) {
 		if (net_ratelimit())
diff -urN linux-2.4.27-rc1/net/sched/sch_tbf.c linux-2.4.27-rc2/net/sched/sch_tbf.c
--- linux-2.4.27-rc1/net/sched/sch_tbf.c	2004-06-25 20:48:24.430001486 -0700
+++ linux-2.4.27-rc2/net/sched/sch_tbf.c	2004-06-25 20:48:27.343123001 -0700
@@ -201,7 +201,7 @@
 
 	if (skb) {
 		psched_time_t now;
-		long toks;
+		long toks, delay;
 		long ptoks = 0;
 		unsigned int len = skb->len;
 
@@ -229,14 +229,12 @@
 			return skb;
 		}
 
-		if (!netif_queue_stopped(sch->dev)) {
-			long delay = PSCHED_US2JIFFIE(max_t(long, -toks, -ptoks));
+		delay = PSCHED_US2JIFFIE(max_t(long, -toks, -ptoks));
 
-			if (delay == 0)
-				delay = 1;
+		if (delay == 0)
+			delay = 1;
 
-			mod_timer(&q->wd_timer, jiffies+delay);
-		}
+		mod_timer(&q->wd_timer, jiffies+delay);
 
 		/* Maybe we have a shorter packet in the queue,
 		   which can be sent now. It sounds cool,
