diff -Nru a/Documentation/filesystems/devfs/ChangeLog b/Documentation/filesystems/devfs/ChangeLog
--- a/Documentation/filesystems/devfs/ChangeLog	Sat May 18 00:46:08 2002
+++ b/Documentation/filesystems/devfs/ChangeLog	Sat May 18 00:46:08 2002
@@ -1912,3 +1912,22 @@
   Thanks to Anton Blanchard <anton@samba.org>
 
 - Updated README from master HTML file
+===============================================================================
+Changes for patch v211
+
+- Do not put miscellaneous character devices in /dev/misc if they
+  specify their own directory (i.e. contain a '/' character)
+
+- Copied macro for error messages from fs/devfs/base.c to
+  fs/devfs/util.c and made use of this macro
+
+- Removed 2.4.x compatibility code from fs/devfs/base.c
+===============================================================================
+Changes for patch v212
+
+- Added BKL to <devfs_open> because drivers still need it
+===============================================================================
+Changes for patch v213
+
+- Protected <scan_dir_for_removable> and <get_removable_partition>
+  from changing directory contents
diff -Nru a/Documentation/filesystems/porting b/Documentation/filesystems/porting
--- a/Documentation/filesystems/porting	Sat May 18 00:46:08 2002
+++ b/Documentation/filesystems/porting	Sat May 18 00:46:08 2002
@@ -146,3 +146,9 @@
 
 It is planned that this will be required for exporting once the code
 settles down a bit.
+
+[mandatory]
+
+s_export_op is now required for exporting a filesystem.
+isofs, ext2, ext3, resierfs, fat
+can be used as examples of very different filesystems.
diff -Nru a/Documentation/networking/8139too.txt b/Documentation/networking/8139too.txt
--- a/Documentation/networking/8139too.txt	Sat May 18 00:46:14 2002
+++ b/Documentation/networking/8139too.txt	Sat May 18 00:46:14 2002
@@ -98,6 +98,7 @@
 Lantech FastNet TX
 Ovislink Fast Ethernet
 Planet ENW-9504 (V.4) 10/100
+SDT Jeoun Fast PCI-TX
 SMC EZNET 10/100
 UNEX NexNIC ND012C
 
diff -Nru a/Documentation/networking/cs89x0.txt b/Documentation/networking/cs89x0.txt
--- a/Documentation/networking/cs89x0.txt	Sat May 18 00:46:08 2002
+++ b/Documentation/networking/cs89x0.txt	Sat May 18 00:46:08 2002
@@ -51,7 +51,7 @@
     6.2 Information Required Before Contacting Technical Support
     6.3 Obtaining the Latest Driver Version
     6.4 Current maintainer
-
+    6.5 Kernel boot parameters
 
 
 1.0 CIRRUS LOGIC LAN CS8900/CS8920 ETHERNET ADAPTERS
@@ -690,4 +690,14 @@
 6.4 Current maintainer
 
 In February 2000 the maintenance of this driver was assumed by Andrew
-Morton <andrewm@uow.edu.au>
+Morton <akpm@zip.com.au>
+
+6.5 Kernel module parameters
+
+For use in embedded environments with no cs89x0 EEPROM, the kernel boot
+parameter `cs89x0_media=' has been implemented.  Usage is:
+
+	cs89x0_media=rj45    or
+	cs89x0_media=aui     or
+	cs89x0_media=bnc
+
diff -Nru a/MAINTAINERS b/MAINTAINERS
--- a/MAINTAINERS	Sat May 18 00:46:08 2002
+++ b/MAINTAINERS	Sat May 18 00:46:08 2002
@@ -389,8 +389,14 @@
 S:	Maintained
 
 CYCLADES ASYNC MUX DRIVER
-P:	Ivan Passos
-M:	ivan@cyclades.com
+P:	Henrique Gobbi
+M:	henrique@cyclades.com
+W:	http://www.cyclades.com/
+S:	Supported
+
+CYCLADES PC300 DRIVER
+P:	Henrique Gobbi
+M:	henrique@cyclades.com
 W:	http://www.cyclades.com/
 S:	Supported
 
diff -Nru a/Makefile b/Makefile
--- a/Makefile	Sat May 18 00:46:08 2002
+++ b/Makefile	Sat May 18 00:46:08 2002
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 5
-SUBLEVEL = 15
+SUBLEVEL = 16
 EXTRAVERSION =
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
diff -Nru a/arch/alpha/boot/Makefile b/arch/alpha/boot/Makefile
--- a/arch/alpha/boot/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/alpha/boot/Makefile	Sat May 18 00:46:08 2002
@@ -8,12 +8,11 @@
 # Copyright (C) 1994 by Linus Torvalds
 #
 
-LINKFLAGS = -static -T bootloader.lds #-N -relax
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := -traditional
 
-.S.s:
-	$(CPP) $(AFLAGS) -traditional -o $*.o $<
-.S.o:
-	$(CC) $(AFLAGS) -traditional -c -o $*.o $<
+LINKFLAGS = -static -T bootloader.lds #-N -relax
 
 OBJECTS = head.o main.o
 BPOBJECTS = head.o bootp.o
diff -Nru a/arch/alpha/defconfig b/arch/alpha/defconfig
--- a/arch/alpha/defconfig	Sat May 18 00:46:10 2002
+++ b/arch/alpha/defconfig	Sat May 18 00:46:10 2002
@@ -266,7 +266,6 @@
 # CONFIG_BLK_DEV_HPT366 is not set
 # CONFIG_BLK_DEV_NS87415 is not set
 # CONFIG_BLK_DEV_OPTI621 is not set
-# CONFIG_BLK_DEV_PDC_ADMA is not set
 # CONFIG_BLK_DEV_PDC202XX is not set
 # CONFIG_PDC202XX_BURST is not set
 # CONFIG_PDC202XX_FORCE is not set
diff -Nru a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile
--- a/arch/alpha/kernel/Makefile	Sat May 18 00:46:09 2002
+++ b/arch/alpha/kernel/Makefile	Sat May 18 00:46:09 2002
@@ -7,12 +7,11 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-.S.s:
-	$(CPP) $(CFLAGS) $(AFLAGS) -o $*.s $<
-.S.o:
-	$(CC) $(CFLAGS) $(AFLAGS) -c -o $*.o $<
+USE_STANDARD_AS_RULE := true
 
 O_TARGET := kernel.o
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 export-objs := alpha_ksyms.o
 
diff -Nru a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c
--- a/arch/alpha/kernel/process.c	Sat May 18 00:46:09 2002
+++ b/arch/alpha/kernel/process.c	Sat May 18 00:46:09 2002
@@ -260,16 +260,22 @@
 alpha_clone(unsigned long clone_flags, unsigned long usp,
 	    struct switch_stack * swstack)
 {
+	struct task_struct *p;
 	if (!usp)
 		usp = rdusp();
-	return do_fork(clone_flags, usp, (struct pt_regs *) (swstack+1), 0);
+
+	p = do_fork(clone_flags & ~CLONE_IDLETASK,
+		    usp, (struct pt_regs *) (swstack+1), 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 int
 alpha_vfork(struct switch_stack * swstack)
 {
-	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(),
-			(struct pt_regs *) (swstack+1), 0);
+	struct task_struct *p;
+	p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(),
+		    (struct pt_regs *) (swstack+1), 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /*
diff -Nru a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c
--- a/arch/alpha/kernel/smp.c	Sat May 18 00:46:08 2002
+++ b/arch/alpha/kernel/smp.c	Sat May 18 00:46:08 2002
@@ -433,13 +433,13 @@
 	return 0;
 }
 
-static int __init
+static struct task_struct * __init
 fork_by_hand(void)
 {
 	/* Don't care about the contents of regs since we'll never
 	   reschedule the forked task. */
 	struct pt_regs regs;
-	return do_fork(CLONE_VM|CLONE_PID, 0, &regs, 0);
+	return do_fork(CLONE_VM|CLONE_IDLETASK, 0, &regs, 0);
 }
 
 /*
@@ -457,12 +457,9 @@
 	   the other task-y sort of data structures set up like we
 	   wish.  We can't use kernel_thread since we must avoid
 	   rescheduling the child.  */
-	if (fork_by_hand() < 0)
+	idle = fork_by_hand();
+	if (IS_ERR(idle))
 		panic("failed fork for CPU %d", cpuid);
-
-	idle = prev_task(&init_task);
-	if (!idle)
-		panic("No idle process for CPU %d", cpuid);
 
 	init_idle(idle, cpuid);
 	unhash_process(idle);
diff -Nru a/arch/alpha/lib/Makefile b/arch/alpha/lib/Makefile
--- a/arch/alpha/lib/Makefile	Sat May 18 00:46:15 2002
+++ b/arch/alpha/lib/Makefile	Sat May 18 00:46:15 2002
@@ -2,10 +2,9 @@
 # Makefile for alpha-specific library files..
 #
 
-.S.s:
-	$(CPP) -D__ASSEMBLY__ $(CFLAGS) -o $*.s $<
-.S.o:
-	$(CC) -D__ASSEMBLY__ $(CFLAGS) -c -o $*.o $<
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 # Many of these routines have implementations tuned for ev6.
 # Choose them iff we're targeting ev6 specifically.
diff -Nru a/arch/alpha/vmlinux.lds.in b/arch/alpha/vmlinux.lds.in
--- a/arch/alpha/vmlinux.lds.in	Sat May 18 00:46:08 2002
+++ b/arch/alpha/vmlinux.lds.in	Sat May 18 00:46:08 2002
@@ -3,6 +3,7 @@
 OUTPUT_FORMAT("elf64-alpha")
 ENTRY(__start)
 PHDRS { kernel PT_LOAD ; }
+jiffies = jiffies_64;
 SECTIONS
 {
 #ifdef CONFIG_ALPHA_LEGACY_START_ADDRESS
diff -Nru a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile
--- a/arch/arm/boot/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/arm/boot/Makefile	Sat May 18 00:46:08 2002
@@ -14,14 +14,12 @@
 ZRELADDR	 = 0x02080000
 PARAMS_PHYS	 = 0x0207c000
 INITRD_PHYS	 = 0x02180000
-INITRD_VIRT	 = 0x02180000
 endif
 
 ifeq ($(CONFIG_ARCH_RPC),y)
 ZRELADDR	 = 0x10008000
 PARAMS_PHYS	 = 0x10000100
 INITRD_PHYS	 = 0x18000000
-INITRD_VIRT	 = 0xc8000000
 endif
 
 ifeq ($(CONFIG_ARCH_CLPS7500),y)
@@ -32,7 +30,6 @@
 ZRELADDR	 = 0x00008000
 PARAMS_PHYS	 = 0x00000400
 INITRD_PHYS	 = 0x00800000
-INITRD_VIRT	 = 0xc0800000
 endif
 
 ifeq ($(CONFIG_ARCH_SHARK),y)
@@ -44,14 +41,12 @@
 ZRELADDR	 = 0x00008000
 PARAMS_PHYS	 = 0x00000100
 INITRD_PHYS	 = 0x00800000
-INITRD_VIRT	 = 0xc0800000
 endif
 
 ifeq ($(CONFIG_ARCH_INTEGRATOR),y)
 ZRELADDR	 = 0x00008000
 PARAMS_PHYS	 = 0x00000100
 INITRD_PHYS	 = 0x00800000
-INITRD_VIRT	 = 0xc0800000
 endif
 
 ifeq ($(CONFIG_ARCH_CAMELOT),y)
@@ -75,11 +70,9 @@
 # Should probably have some agreement on these...
 ifeq ($(CONFIG_ARCH_P720T),y)
 INITRD_PHYS	 = 0xc0400000
-INITRD_VIRT	 = 0xc0400000
 endif
 ifeq ($(CONFIG_ARCH_CDB89712),y)
 INITRD_PHYS	 = 0x00700000
-INITRD_VIRT	 = 0xc0300000
 endif
 
 ifeq ($(CONFIG_ARCH_SA1100),y)
@@ -119,7 +112,7 @@
 ZBSSADDR	=ALIGN(4)
 endif
 
-export	SYSTEM ZTEXTADDR ZBSSADDR ZRELADDR INITRD_PHYS INITRD_VIRT PARAMS_PHYS
+export	SYSTEM ZTEXTADDR ZBSSADDR ZRELADDR INITRD_PHYS PARAMS_PHYS
 
 Image:	$(CONFIGURE) $(SYSTEM)
 	$(OBJCOPY) -O binary -R .note -R .comment -S $(SYSTEM) $@
@@ -139,7 +132,7 @@
 	@$(MAKE) -C bootp bootp
 
 initrd:
-	@test "$(INITRD_VIRT)" != "" || (echo This architecture does not support INITRD; exit -1)
+	@test "$(INITRD_PHYS)" != "" || (echo This architecture does not support INITRD; exit -1)
 	@test "$(INITRD)" != "" || (echo You must specify INITRD; exit -1)
 
 install: $(CONFIGURE) Image
diff -Nru a/arch/arm/boot/bootp/Makefile b/arch/arm/boot/bootp/Makefile
--- a/arch/arm/boot/bootp/Makefile	Sat May 18 00:46:14 2002
+++ b/arch/arm/boot/bootp/Makefile	Sat May 18 00:46:14 2002
@@ -5,7 +5,6 @@
 ZSYSTEM		=$(TOPDIR)/arch/arm/boot/zImage
 ZLDFLAGS	=-p -X -T bootp.lds \
 		 --defsym initrd_addr=$(INITRD_PHYS) \
-		 --defsym initrd_virt=$(INITRD_VIRT) \
 		 --defsym params=$(PARAMS_PHYS) \
 		 --defsym kernel_addr=$(ZTEXTADDR)
 
diff -Nru a/arch/arm/boot/bootp/init.S b/arch/arm/boot/bootp/init.S
--- a/arch/arm/boot/bootp/init.S	Sat May 18 00:46:08 2002
+++ b/arch/arm/boot/bootp/init.S	Sat May 18 00:46:08 2002
@@ -102,8 +102,8 @@
 		.word	kernel_len
 
 		.word	0x54410001		@ r4 = ATAG_CORE
-		.word	0x54410005		@ r5 = ATAG_INITRD
-		.word	initrd_virt		@ r6
+		.word	0x54420005		@ r5 = ATAG_INITRD
+		.word	initrd_addr		@ r6
 		.word	initrd_len		@ r7
 		.word	params			@ r8
 
diff -Nru a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
--- a/arch/arm/boot/compressed/head.S	Sat May 18 00:46:14 2002
+++ b/arch/arm/boot/compressed/head.S	Sat May 18 00:46:14 2002
@@ -151,7 +151,7 @@
 		add	r1, r1, r0		@ table.  This fixes up the
 		str	r1, [r6], #4		@ C references.
 		cmp	r6, ip
-		blt	1b
+		blo	1b
 
 not_relocated:	mov	r0, #0
 1:		str	r0, [r2], #4		@ clear bss
@@ -159,7 +159,7 @@
 		str	r0, [r2], #4
 		str	r0, [r2], #4
 		cmp	r2, r3
-		blt	1b
+		blo	1b
 
 		mrc	p15, 0, r6, c0, c0	@ get processor ID
 		bl	cache_on
@@ -206,7 +206,7 @@
 		ldmia	r2!, {r8 - r13}
 		stmia	r1!, {r8 - r13}
 		cmp	r2, r3
-		blt	1b
+		blo	1b
 
 		bl	cache_clean_flush
 		add	pc, r5, r0		@ call relocation code
@@ -270,9 +270,9 @@
 		orr	r1, r1, #3 << 10
 		add	r2, r3, #16384
 1:		cmp	r1, r8			@ if virt > start of RAM
-		orrge	r1, r1, #0x0c		@ set cacheable, bufferable
+		orrhs	r1, r1, #0x0c		@ set cacheable, bufferable
 		cmp	r1, r9			@ if virt > end of RAM
-		bicge	r1, r1, #0x0c		@ clear cacheable, bufferable
+		bichs	r1, r1, #0x0c		@ clear cacheable, bufferable
 		str	r1, [r0], #4		@ 1:1 mapping
 		add	r1, r1, #1048576
 		teq	r0, r2
@@ -330,7 +330,7 @@
 		.endr
 
 		cmp	r5, r8
-		blt	1b
+		blo	1b
 		debug_reloc_end
 
 call_kernel:	bl	cache_clean_flush
@@ -395,6 +395,12 @@
 		b	__cache_on
 		b	__armv4_cache_off
 		mov	pc, lr
+
+		.word	0x41129200		@ ARM920T
+		.word	0xff00fff0
+		b	__cache_on
+		b	__armv4_cache_off
+		b	__armv4_cache_flush
 
 		.word	0x4401a100		@ sa110 / sa1100
 		.word	0xffffffe0
diff -Nru a/arch/arm/config.in b/arch/arm/config.in
--- a/arch/arm/config.in	Sat May 18 00:46:08 2002
+++ b/arch/arm/config.in	Sat May 18 00:46:08 2002
@@ -464,20 +464,8 @@
 dep_bool 'Preemptible Kernel (experimental)' CONFIG_PREEMPT $CONFIG_CPU_32 $CONFIG_EXPERIMENTAL
 dep_tristate 'Advanced Power Management Emulation' CONFIG_APM $CONFIG_PM
 dep_tristate 'RISC OS personality' CONFIG_ARTHUR $CONFIG_CPU_32
+string 'Default kernel command string' CONFIG_CMDLINE ""
 
-if [ "$CONFIG_ARCH_EBSA110" = "y" -o         \
-     "$CONFIG_ARCH_SA1100" = "y" -o          \
-     "$CONFIG_ARCH_CLPS7500" = "y" -o        \
-     "$CONFIG_ARCH_PERSONAL_SERVER" = "y" -o \
-     "$CONFIG_ARCH_CATS" = "y" -o            \
-     "$CONFIG_ARCH_P720T" = "y" -o	     \
-     "$CONFIG_ARCH_CDB89712" = "y" -o	     \
-     "$CONFIG_ARCH_CAMELOT" = "y" -o	     \
-     "$CONFIG_ARCH_ANAKIN" = "y" -o          \
-     "$CONFIG_ARCH_IOP310" = "y" -o 	     \
-     "$CONFIG_ARCH_ADIFCC" = "y" ]; then
-   string 'Default kernel command string' CONFIG_CMDLINE ""
-fi
 if [ "$CONFIG_ARCH_NETWINDER" = "y" -o  \
      "$CONFIG_ARCH_EBSA110" = "y" -o    \
      "$CONFIG_ARCH_EBSA285" = "y" -o    \
diff -Nru a/arch/arm/def-configs/neponset b/arch/arm/def-configs/neponset
--- a/arch/arm/def-configs/neponset	Sat May 18 00:46:09 2002
+++ b/arch/arm/def-configs/neponset	Sat May 18 00:46:09 2002
@@ -849,7 +849,7 @@
 #
 # CONFIG_USB_UHCI is not set
 # CONFIG_USB_UHCI_ALT is not set
-CONFIG_USB_OHCI=y
+# CONFIG_USB_OHCI is not set
 CONFIG_USB_OHCI_SA1111=y
 
 #
diff -Nru a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
--- a/arch/arm/kernel/armksyms.c	Sat May 18 00:46:09 2002
+++ b/arch/arm/kernel/armksyms.c	Sat May 18 00:46:09 2002
@@ -182,7 +182,6 @@
 EXPORT_SYMBOL_NOVERS(strlen);
 EXPORT_SYMBOL_NOVERS(strnlen);
 EXPORT_SYMBOL_NOVERS(strpbrk);
-EXPORT_SYMBOL_NOVERS(strsep);
 EXPORT_SYMBOL_NOVERS(strrchr);
 EXPORT_SYMBOL_NOVERS(strstr);
 EXPORT_SYMBOL_NOVERS(memset);
diff -Nru a/arch/arm/kernel/compat.c b/arch/arm/kernel/compat.c
--- a/arch/arm/kernel/compat.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/kernel/compat.c	Sat May 18 00:46:08 2002
@@ -9,6 +9,11 @@
  *
  * We keep the old params compatibility cruft in one place (here)
  * so we don't end up with lots of mess around other places.
+ *
+ * NOTE:
+ *  The old struct param_struct is deprecated, but it will be kept in
+ *  the kernel for 5 years from now (2001). This will allow boot loaders
+ *  to convert to the new struct tag way.
  */
 #include <linux/config.h>
 #include <linux/types.h>
@@ -22,6 +27,59 @@
 
 #include <asm/mach/arch.h>
 
+/*
+ * Usage:
+ *  - do not go blindly adding fields, add them at the end
+ *  - when adding fields, don't rely on the address until
+ *    a patch from me has been released
+ *  - unused fields should be zero (for future expansion)
+ *  - this structure is relatively short-lived - only
+ *    guaranteed to contain useful data in setup_arch()
+ *
+ * This is the old deprecated way to pass parameters to the kernel
+ */
+struct param_struct {
+    union {
+	struct {
+	    unsigned long page_size;		/*  0 */
+	    unsigned long nr_pages;		/*  4 */
+	    unsigned long ramdisk_size;		/*  8 */
+	    unsigned long flags;		/* 12 */
+#define FLAG_READONLY	1
+#define FLAG_RDLOAD	4
+#define FLAG_RDPROMPT	8
+	    unsigned long rootdev;		/* 16 */
+	    unsigned long video_num_cols;	/* 20 */
+	    unsigned long video_num_rows;	/* 24 */
+	    unsigned long video_x;		/* 28 */
+	    unsigned long video_y;		/* 32 */
+	    unsigned long memc_control_reg;	/* 36 */
+	    unsigned char sounddefault;		/* 40 */
+	    unsigned char adfsdrives;		/* 41 */
+	    unsigned char bytes_per_char_h;	/* 42 */
+	    unsigned char bytes_per_char_v;	/* 43 */
+	    unsigned long pages_in_bank[4];	/* 44 */
+	    unsigned long pages_in_vram;	/* 60 */
+	    unsigned long initrd_start;		/* 64 */
+	    unsigned long initrd_size;		/* 68 */
+	    unsigned long rd_start;		/* 72 */
+	    unsigned long system_rev;		/* 76 */
+	    unsigned long system_serial_low;	/* 80 */
+	    unsigned long system_serial_high;	/* 84 */
+	    unsigned long mem_fclk_21285;       /* 88 */
+	} s;
+	char unused[256];
+    } u1;
+    union {
+	char paths[8][128];
+	struct {
+	    unsigned long magic;
+	    char n[1024 - sizeof(unsigned long)];
+	} s;
+    } u2;
+    char commandline[COMMAND_LINE_SIZE];
+};
+
 static struct tag * __init memtag(struct tag *tag, unsigned long start, unsigned long size)
 {
 	tag = tag_next(tag);
@@ -33,7 +91,7 @@
 	return tag;
 }
 
-static void __init build_tag_list(struct param_struct *params, void *taglist, int mem_init)
+static void __init build_tag_list(struct param_struct *params, void *taglist)
 {
 	struct tag *tag = taglist;
 
@@ -44,6 +102,22 @@
 		       "trying to continue\n");
 		return;
 	}
+#ifdef CONFIG_ARCH_NETWINDER
+	if (params->u1.s.nr_pages != 0x02000 &&
+	    params->u1.s.nr_pages != 0x04000 &&
+	    params->u1.s.nr_pages != 0x08000 &&
+	    params->u1.s.nr_pages != 0x10000) {
+		printk(KERN_WARNING "Warning: bad NeTTrom parameters "
+		       "detected, using defaults\n");
+
+		params->u1.s.nr_pages = 0x1000;	/* 16MB */
+		params->u1.s.ramdisk_size = 0;
+		params->u1.s.flags = FLAG_READONLY;
+		params->u1.s.initrd_start = 0;
+		params->u1.s.initrd_size = 0;
+		params->u1.s.rd_start = 0;
+	}
+#endif
 
 	tag->hdr.tag  = ATAG_CORE;
 	tag->hdr.size = tag_size(tag_core);
@@ -76,17 +150,15 @@
 	tag->hdr.size = tag_size(tag_revision);
 	tag->u.revision.rev = params->u1.s.system_rev;
 
-	if (mem_init) {
 #ifdef CONFIG_ARCH_ACORN
-		if (machine_is_riscpc()) {
-			int i;
-			for (i = 0; i < 4; i++)
-				tag = memtag(tag, PHYS_OFFSET + (i << 26),
-					 params->u1.s.pages_in_bank[i] * PAGE_SIZE);
-		} else
+	if (machine_is_riscpc()) {
+		int i;
+		for (i = 0; i < 4; i++)
+			tag = memtag(tag, PHYS_OFFSET + (i << 26),
+				 params->u1.s.pages_in_bank[i] * PAGE_SIZE);
+	} else
 #endif
-		tag = memtag(tag, PHYS_OFFSET, params->u1.s.nr_pages * PAGE_SIZE);
-	}
+	tag = memtag(tag, PHYS_OFFSET, params->u1.s.nr_pages * PAGE_SIZE);
 
 #ifdef CONFIG_FOOTBRIDGE
 	if (params->u1.s.mem_fclk_21285) {
@@ -97,6 +169,23 @@
 	}
 #endif
 
+#ifdef CONFIG_ARCH_EBSA285
+	if (machine_is_ebsa285()) {
+		tag = tag_next(tag);
+		tag->hdr.tag = ATAG_VIDEOTEXT;
+		tag->hdr.size = tag_size(tag_videotext);
+		tag->u.videotext.x            = params->u1.s.video_x;
+		tag->u.videotext.y            = params->u1.s.video_y;
+		tag->u.videotext.video_page   = 0;
+		tag->u.videotext.video_mode   = 0;
+		tag->u.videotext.video_cols   = params->u1.s.video_num_cols;
+		tag->u.videotext.video_ega_bx = 0;
+		tag->u.videotext.video_lines  = params->u1.s.video_num_rows;
+		tag->u.videotext.video_isvga  = 1;
+		tag->u.videotext.video_points = 8;
+	}
+#endif
+
 #ifdef CONFIG_ARCH_ACORN
 	tag = tag_next(tag);
 	tag->hdr.tag = ATAG_ACORN;
@@ -114,14 +203,22 @@
 	strcpy(tag->u.cmdline.cmdline, params->commandline);
 
 	tag = tag_next(tag);
-	tag->hdr.tag = 0;
+	tag->hdr.tag = ATAG_NONE;
 	tag->hdr.size = 0;
 
 	memmove(params, taglist, ((int)tag) - ((int)taglist) +
 				 sizeof(struct tag_header));
 }
 
-void __init convert_to_tag_list(struct param_struct *params, int mem_init)
+void __init convert_to_tag_list(struct tag *tags)
+{
+	struct param_struct *params = (struct param_struct *)tags;
+	build_tag_list(params, &params->u2);
+}
+
+void __init squash_mem_tags(struct tag *tag)
 {
-	build_tag_list(params, &params->u2, mem_init);
+	for (; tag->hdr.size; tag = tag_next(tag))
+		if (tag->hdr.tag == ATAG_MEM)
+			tag->hdr.tag = ATAG_NONE;
 }
diff -Nru a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
--- a/arch/arm/kernel/entry-armv.S	Sat May 18 00:46:09 2002
+++ b/arch/arm/kernel/entry-armv.S	Sat May 18 00:46:09 2002
@@ -799,8 +799,7 @@
 		adrsvc	al, r9, 1f			@ r9  = normal FP return
 		bl	call_fpe			@ lr  = undefined instr return
 
-		mov	r0, r5				@ unsigned long pc
-		mov	r1, sp				@ struct pt_regs *regs
+		mov	r0, sp				@ struct pt_regs *regs
 		bl	do_undefinstr
 
 1:		set_cpsr_c r0, #PSR_I_BIT | MODE_SVC
@@ -926,8 +925,7 @@
 		ldr	pc, [r4]			@ Call FP module USR entry point
 
 fpundefinstr:	set_cpsr_c r0, #MODE_SVC		@ Enable interrupts
-		mov	r0, lr
-		mov	r1, sp
+		mov	r0, sp
 		adrsvc	al, lr, ret_from_exception
 		b	do_undefinstr
 
diff -Nru a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
--- a/arch/arm/kernel/ptrace.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/kernel/ptrace.c	Sat May 18 00:46:08 2002
@@ -45,7 +45,7 @@
 get_user_regs(struct task_struct *task)
 {
 	return (struct pt_regs *)
-		((unsigned long)task + 8192 - sizeof(struct pt_regs));
+		((unsigned long)task->thread_info + 8192 - sizeof(struct pt_regs));
 }
 
 /*
diff -Nru a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
--- a/arch/arm/kernel/setup.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/kernel/setup.c	Sat May 18 00:46:08 2002
@@ -35,10 +35,6 @@
 #define MEM_SIZE	(16*1024*1024)
 #endif
 
-#ifndef CONFIG_CMDLINE
-#define CONFIG_CMDLINE ""
-#endif
-
 #ifdef CONFIG_PREEMPT
 spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 #endif
@@ -57,7 +53,8 @@
 
 extern unsigned int mem_fclk_21285;
 extern void paging_init(struct meminfo *, struct machine_desc *desc);
-extern void convert_to_tag_list(struct param_struct *params, int mem_init);
+extern void convert_to_tag_list(struct tag *tags);
+extern void squash_mem_tags(struct tag *tag);
 extern void bootmem_init(struct meminfo *);
 extern void reboot_setup(char *str);
 extern int root_mountflags;
@@ -517,6 +514,20 @@
 
 __tagtable(ATAG_INITRD, parse_tag_initrd);
 
+static int __init parse_tag_initrd2(const struct tag *tag)
+{
+	unsigned long start = 0;
+
+	if (tag->u.initrd.size) {
+		start = (unsigned long)phys_to_virt(tag->u.initrd.start);
+
+		setup_initrd(start, tag->u.initrd.size);
+	}
+	return 0;
+}
+
+__tagtable(ATAG_INITRD2, parse_tag_initrd2);
+
 static int __init parse_tag_serialnr(const struct tag *tag)
 {
 	system_serial_low = tag->u.serialnr.low;
@@ -575,14 +586,29 @@
 				t->hdr.tag);
 }
 
+/*
+ * This holds our defaults.
+ */
+static struct init_tags {
+	struct tag_header hdr1;
+	struct tag_core   core;
+	struct tag_header hdr2;
+	struct tag_mem32  mem;
+	struct tag_header hdr3;
+} init_tags __initdata = {
+	{ tag_size(tag_core), ATAG_CORE },
+	{ 1, PAGE_SIZE, 0xff },
+	{ tag_size(tag_mem32), ATAG_MEM },
+	{ MEM_SIZE, PHYS_OFFSET },
+	{ 0, ATAG_NONE }
+};
+
 void __init setup_arch(char **cmdline_p)
 {
-	struct tag *tags = NULL;
+	struct tag *tags = (struct tag *)&init_tags;
 	struct machine_desc *mdesc;
 	char *from = default_command_line;
 
-	ROOT_DEV = mk_kdev(0, 255);
-
 	setup_processor();
 	mdesc = setup_machine(machine_arch_type);
 	machine_name = mdesc->name;
@@ -594,28 +620,21 @@
 		tags = phys_to_virt(mdesc->param_offset);
 
 	/*
-	 * Do the machine-specific fixups before we parse the
-	 * parameters or tags.
-	 */
-	if (mdesc->fixup)
-		mdesc->fixup(mdesc, (struct param_struct *)tags,
-			     &from, &meminfo);
-
-	/*
 	 * If we have the old style parameters, convert them to
-	 * a tag list before.
+	 * a tag list.
 	 */
-	if (tags && tags->hdr.tag != ATAG_CORE)
-		convert_to_tag_list((struct param_struct *)tags,
-				    meminfo.nr_banks == 0);
+	if (tags->hdr.tag != ATAG_CORE)
+		convert_to_tag_list(tags);
+	if (tags->hdr.tag != ATAG_CORE)
+		tags = (struct tag *)&init_tags;
 
-	if (tags && tags->hdr.tag == ATAG_CORE)
-		parse_tags(tags);
+	if (mdesc->fixup)
+		mdesc->fixup(mdesc, tags, &from, &meminfo);
 
-	if (meminfo.nr_banks == 0) {
-		meminfo.nr_banks      = 1;
-		meminfo.bank[0].start = PHYS_OFFSET;
-		meminfo.bank[0].size  = MEM_SIZE;
+	if (tags->hdr.tag == ATAG_CORE) {
+		if (meminfo.nr_banks != 0)
+			squash_mem_tags(tags);
+		parse_tags(tags);
 	}
 
 	init_mm.start_code = (unsigned long) &_text;
diff -Nru a/arch/arm/kernel/sys_arm.c b/arch/arm/kernel/sys_arm.c
--- a/arch/arm/kernel/sys_arm.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/kernel/sys_arm.c	Sat May 18 00:46:08 2002
@@ -238,7 +238,9 @@
  */
 asmlinkage int sys_fork(struct pt_regs *regs)
 {
-	return do_fork(SIGCHLD, regs->ARM_sp, regs, 0);
+	struct task_struct *p;
+	p = do_fork(SIGCHLD, regs->ARM_sp, regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /* Clone a task - this clones the calling program thread.
@@ -246,14 +248,18 @@
  */
 asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, struct pt_regs *regs)
 {
+	struct task_struct *p;
 	if (!newsp)
 		newsp = regs->ARM_sp;
-	return do_fork(clone_flags, newsp, regs, 0);
+	p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 asmlinkage int sys_vfork(struct pt_regs *regs)
 {
-	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs, 0);
+	struct task_struct *p;
+	p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /* sys_execve() executes a new program.
diff -Nru a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
--- a/arch/arm/kernel/traps.c	Sat May 18 00:46:09 2002
+++ b/arch/arm/kernel/traps.c	Sat May 18 00:46:09 2002
@@ -116,7 +116,7 @@
 
 static void dump_stack(struct task_struct *tsk, unsigned long sp)
 {
-	dump_mem("Stack: ", sp - 16, 8192+(unsigned long)tsk->thread_info);
+	dump_mem("Stack: ", sp, 8192+(unsigned long)tsk->thread_info);
 }
 
 static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
@@ -132,7 +132,7 @@
 	} else if (verify_stack(fp)) {
 		printk("invalid frame pointer 0x%08x", fp);
 		ok = 0;
-	} else if (fp < 4096+(unsigned long)tsk->thread_info)
+	} else if (fp < (unsigned long)(tsk->thread_info + 1))
 		printk("frame pointer underflow");
 	printk("\n");
 
@@ -167,8 +167,8 @@
 	printk("Internal error: %s: %x\n", str, err);
 	printk("CPU: %d\n", smp_processor_id());
 	show_regs(regs);
-	printk("Process %s (pid: %d, stackpage=%08lx)\n",
-		current->comm, current->pid, 4096+(unsigned long)tsk);
+	printk("Process %s (pid: %d, stack limit = 0x%p)\n",
+		current->comm, current->pid, tsk->thread_info + 1);
 
 	if (!user_mode(regs) || in_interrupt()) {
 		mm_segment_t fs;
@@ -201,7 +201,7 @@
     	die(str, regs, err);
 }
 
-asmlinkage void do_undefinstr(int address, struct pt_regs *regs, int mode)
+asmlinkage void do_undefinstr(struct pt_regs *regs)
 {
 	unsigned long *pc;
 	siginfo_t info;
@@ -229,7 +229,7 @@
 
 	force_sig_info(SIGILL, &info, current);
 
-	die_if_kernel("Oops - undefined instruction", regs, mode);
+	die_if_kernel("Oops - undefined instruction", regs, 0);
 }
 
 #ifdef CONFIG_CPU_26
diff -Nru a/arch/arm/lib/csumpartialcopygeneric.S b/arch/arm/lib/csumpartialcopygeneric.S
--- a/arch/arm/lib/csumpartialcopygeneric.S	Sat May 18 00:46:08 2002
+++ b/arch/arm/lib/csumpartialcopygeneric.S	Sat May 18 00:46:08 2002
@@ -137,19 +137,19 @@
 
 4:		ands	len, len, #3
 		beq	.done
-		load1l	r5
+		load1l	r4
 		tst	len, #2
-		mov	r4, r5, lsr #byte(0)
+		mov	r5, r4, lsr #byte(0)
 		beq	.exit
-		adcs	sum, sum, r5, push #16
-		strb	r4, [dst], #1
-		mov	r4, r5, lsr #byte(1)
-		strb	r4, [dst], #1
-		mov	r4, r5, lsr #byte(2)
+		adcs	sum, sum, r4, push #16
+		strb	r5, [dst], #1
+		mov	r5, r4, lsr #byte(1)
+		strb	r5, [dst], #1
+		mov	r5, r4, lsr #byte(2)
 .exit:		tst	len, #1
-		strneb	r4, [dst], #1
-		andne	r4, r4, #255
-		adcnes	sum, sum, r4, lsl #byte(0)
+		strneb	r5, [dst], #1
+		andne	r5, r5, #255
+		adcnes	sum, sum, r5, lsl #byte(0)
 
 		/*
 		 * If the dst pointer was not 16-bit aligned, we
@@ -210,17 +210,17 @@
 		orr	r4, r4, r5, push #24
 		str	r4, [dst], #4
 		adcs	sum, sum, r4
+		mov	r4, r5, pull #8
 4:		ands	len, len, #3
 		beq	.done
-		mov	r4, r5, lsr #byte(1)
+		mov	r5, r4, lsr #byte(0)
 		tst	len, #2
 		beq	.exit
-		bic	r5, r5, #0xff << byte(0)
-		adcs	sum, sum, r5, push #8
-		strb	r4, [dst], #1
-		mov	r4, r5, lsr #byte(2)
-		strb	r4, [dst], #1
-		mov	r4, r5, lsr #byte(3)
+		adcs	sum, sum, r4, push #16
+		strb	r5, [dst], #1
+		mov	r5, r4, lsr #byte(1)
+		strb	r5, [dst], #1
+		mov	r5, r4, lsr #byte(2)
 		b	.exit
 
 .src2_aligned:	mov	r4, r5, pull #16
@@ -262,18 +262,19 @@
 		orr	r4, r4, r5, push #16
 		str	r4, [dst], #4
 		adcs	sum, sum, r4
+		mov	r4, r5, pull #16
 4:		ands	len, len, #3
 		beq	.done
-		mov	r4, r5, lsr #byte(2)
+		mov	r5, r4, lsr #byte(0)
 		tst	len, #2
 		beq	.exit
-		adcs	sum, sum, r5, pull #16
-		strb	r4, [dst], #1
-		mov	r4, r5, lsr #byte(3)
-		strb	r4, [dst], #1
+		adcs	sum, sum, r4
+		strb	r5, [dst], #1
+		mov	r5, r4, lsr #byte(1)
+		strb	r5, [dst], #1
 		tst	len, #1
 		beq	.done
-		load1b	r4
+		load1b	r5
 		b	.exit
 
 .src3_aligned:	mov	r4, r5, pull #24
@@ -315,16 +316,17 @@
 		orr	r4, r4, r5, push #8
 		str	r4, [dst], #4
 		adcs	sum, sum, r4
+		mov	r4, r5, pull #24
 4:		ands	len, len, #3
 		beq	.done
-		mov	r4, r5, lsr #byte(3)
+		mov	r5, r4, lsr #byte(0)
 		tst	len, #2
 		beq	.exit
-		adcs	sum, sum, r5, pull #24
-		strb	r4, [dst], #1
-		load1l	r5
-		mov	r4, r5, lsr #byte(0)
-		strb	r4, [dst], #1
+		strb	r5, [dst], #1
+		adcs	sum, sum, r4
+		load1l	r4
+		mov	r5, r4, lsr #byte(0)
+		strb	r5, [dst], #1
 		adcs	sum, sum, r4, push #24
-		mov	r4, r5, lsr #byte(1)
+		mov	r5, r4, lsr #byte(1)
 		b	.exit
diff -Nru a/arch/arm/mach-adifcc/arch.c b/arch/arm/mach-adifcc/arch.c
--- a/arch/arm/mach-adifcc/arch.c	Sat May 18 00:46:14 2002
+++ b/arch/arm/mach-adifcc/arch.c	Sat May 18 00:46:14 2002
@@ -22,7 +22,7 @@
 extern void adifcc_init_irq(void);
 
 static void __init
-fixup_adifcc(struct machine_desc *desc, struct param_struct *params,
+fixup_adifcc(struct machine_desc *desc, struct tag *tags,
 	      char **cmdline, struct meminfo *mi)
 {
 #ifdef CONFIG_ARCH_ADI_EVB
diff -Nru a/arch/arm/mach-anakin/arch.c b/arch/arm/mach-anakin/arch.c
--- a/arch/arm/mach-anakin/arch.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-anakin/arch.c	Sat May 18 00:46:08 2002
@@ -27,7 +27,7 @@
 extern void genarch_init_irq(void);
 
 static void __init
-fixup_anakin(struct machine_desc *desc, struct param_struct *unused,
+fixup_anakin(struct machine_desc *desc, struct tag *tag,
              char **cmdline, struct meminfo *mi)
 {
 	ROOT_DEV = mk_kdev(RAMDISK_MAJOR, 0);
diff -Nru a/arch/arm/mach-clps711x/cdb89712.c b/arch/arm/mach-clps711x/cdb89712.c
--- a/arch/arm/mach-clps711x/cdb89712.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-clps711x/cdb89712.c	Sat May 18 00:46:08 2002
@@ -45,7 +45,7 @@
 };
 
 static void __init
-fixup_cdb89712(struct machine_desc *desc, struct param_struct *params,
+fixup_cdb89712(struct machine_desc *desc, struct tag *tags,
 	    char **cmdline, struct meminfo *mi)
 {
 }
diff -Nru a/arch/arm/mach-clps711x/clep7312.c b/arch/arm/mach-clps711x/clep7312.c
--- a/arch/arm/mach-clps711x/clep7312.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-clps711x/clep7312.c	Sat May 18 00:46:08 2002
@@ -27,7 +27,7 @@
 extern void clps711x_map_io(void); 
 
 static void __init
-fixup_clep7312(struct machine_desc *desc, struct param_struct *params,
+fixup_clep7312(struct machine_desc *desc, struct tag *tags,
 	    char **cmdline, struct meminfo *mi)
 {
 	mi->nr_banks=1;
diff -Nru a/arch/arm/mach-clps711x/edb7211-arch.c b/arch/arm/mach-clps711x/edb7211-arch.c
--- a/arch/arm/mach-clps711x/edb7211-arch.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-clps711x/edb7211-arch.c	Sat May 18 00:46:08 2002
@@ -29,7 +29,7 @@
 extern void edb7211_map_io(void);
 
 static void __init
-fixup_edb7211(struct machine_desc *desc, struct param_struct *params,
+fixup_edb7211(struct machine_desc *desc, struct tag *tags,
 	      char **cmdline, struct meminfo *mi)
 {
 	/*
diff -Nru a/arch/arm/mach-clps711x/fortunet.c b/arch/arm/mach-clps711x/fortunet.c
--- a/arch/arm/mach-clps711x/fortunet.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-clps711x/fortunet.c	Sat May 18 00:46:08 2002
@@ -53,7 +53,7 @@
 #define IMAGE_PARAMS_PHYS	0xC01F0000
 
 static void __init
-fortunet_fixup(struct machine_desc *desc, struct param_struct *params,
+fortunet_fixup(struct machine_desc *desc, struct tag *tags,
 		 char **cmdline, struct meminfo *mi)
 {
 	IMAGE_PARAMS *ip;
diff -Nru a/arch/arm/mach-clps711x/p720t.c b/arch/arm/mach-clps711x/p720t.c
--- a/arch/arm/mach-clps711x/p720t.c	Sat May 18 00:46:10 2002
+++ b/arch/arm/mach-clps711x/p720t.c	Sat May 18 00:46:10 2002
@@ -49,11 +49,9 @@
 };
 
 static void __init
-fixup_p720t(struct machine_desc *desc, struct param_struct *params,
+fixup_p720t(struct machine_desc *desc, struct tag *tag,
 	    char **cmdline, struct meminfo *mi)
 {
-	struct tag *tag = (struct tag *)params;
-
 	/*
 	 * Our bootloader doesn't setup any tags (yet).
 	 */
diff -Nru a/arch/arm/mach-epxa10db/arch.c b/arch/arm/mach-epxa10db/arch.c
--- a/arch/arm/mach-epxa10db/arch.c	Sat May 18 00:46:13 2002
+++ b/arch/arm/mach-epxa10db/arch.c	Sat May 18 00:46:13 2002
@@ -35,7 +35,7 @@
 
 
 static void __init
-epxa10db_fixup(struct machine_desc *desc, struct param_struct *params,
+epxa10db_fixup(struct machine_desc *desc, struct tag *tags,
 		 char **cmdline, struct meminfo *mi)
 {
 
diff -Nru a/arch/arm/mach-footbridge/arch.c b/arch/arm/mach-footbridge/arch.c
--- a/arch/arm/mach-footbridge/arch.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-footbridge/arch.c	Sat May 18 00:46:08 2002
@@ -36,25 +36,11 @@
 __tagtable(ATAG_MEMCLK, parse_tag_memclk);
 
 #ifdef CONFIG_ARCH_EBSA285
-
-static void __init
-fixup_ebsa285(struct machine_desc *desc, struct param_struct *params,
-	      char **cmdline, struct meminfo *mi)
-{
-#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
-	ORIG_X		 = params->u1.s.video_x;
-	ORIG_Y		 = params->u1.s.video_y;
-	ORIG_VIDEO_COLS  = params->u1.s.video_num_cols;
-	ORIG_VIDEO_LINES = params->u1.s.video_num_rows;
-#endif
-}
-
 MACHINE_START(EBSA285, "EBSA285")
 	MAINTAINER("Russell King")
 	BOOT_MEM(0x00000000, DC21285_ARMCSR_BASE, 0xfe000000)
 	BOOT_PARAMS(0x00000100)
 	VIDEO(0x000a0000, 0x000bffff)
-	FIXUP(fixup_ebsa285)
 	MAPIO(footbridge_map_io)
 	INITIRQ(footbridge_init_irq)
 MACHINE_END
@@ -67,7 +53,7 @@
  * the parameter page.
  */
 static void __init
-fixup_netwinder(struct machine_desc *desc, struct param_struct *params,
+fixup_netwinder(struct machine_desc *desc, struct tag *tags,
 		char **cmdline, struct meminfo *mi)
 {
 #ifdef CONFIG_ISAPNP
@@ -80,21 +66,6 @@
 	 */
 	isapnp_disable = 1;
 #endif
-
-	if (params->u1.s.nr_pages != 0x02000 &&
-	    params->u1.s.nr_pages != 0x04000 &&
-	    params->u1.s.nr_pages != 0x08000 &&
-	    params->u1.s.nr_pages != 0x10000) {
-		printk(KERN_WARNING "Warning: bad NeTTrom parameters "
-		       "detected, using defaults\n");
-
-		params->u1.s.nr_pages = 0x1000;	/* 16MB */
-		params->u1.s.ramdisk_size = 0;
-		params->u1.s.flags = FLAG_READONLY;
-		params->u1.s.initrd_start = 0;
-		params->u1.s.initrd_size = 0;
-		params->u1.s.rd_start = 0;
-	}
 }
 
 MACHINE_START(NETWINDER, "Rebel-NetWinder")
@@ -116,7 +87,7 @@
  * hard reboots fail on early boards.
  */
 static void __init
-fixup_cats(struct machine_desc *desc, struct param_struct *unused,
+fixup_cats(struct machine_desc *desc, struct tag *tags,
 	   char **cmdline, struct meminfo *mi)
 {
 	ORIG_VIDEO_LINES  = 25;
@@ -138,7 +109,7 @@
 #ifdef CONFIG_ARCH_CO285
 
 static void __init
-fixup_coebsa285(struct machine_desc *desc, struct param_struct *unused,
+fixup_coebsa285(struct machine_desc *desc, struct tag *tags,
 		char **cmdline, struct meminfo *mi)
 {
 	extern unsigned long boot_memory_end;
diff -Nru a/arch/arm/mach-integrator/arch.c b/arch/arm/mach-integrator/arch.c
--- a/arch/arm/mach-integrator/arch.c	Sat May 18 00:46:14 2002
+++ b/arch/arm/mach-integrator/arch.c	Sat May 18 00:46:14 2002
@@ -51,7 +51,7 @@
 #endif
 
 static void __init
-integrator_fixup(struct machine_desc *desc, struct param_struct *unused,
+integrator_fixup(struct machine_desc *desc, struct tag *tags,
 		 char **cmdline, struct meminfo *mi)
 {
 #ifdef CONFIG_KMI_KEYB
diff -Nru a/arch/arm/mach-integrator/mm.c b/arch/arm/mach-integrator/mm.c
--- a/arch/arm/mach-integrator/mm.c	Sat May 18 00:46:10 2002
+++ b/arch/arm/mach-integrator/mm.c	Sat May 18 00:46:10 2002
@@ -33,10 +33,10 @@
 
 /*
  * Logical      Physical
- * e8000000	40000000	PCI memory
- * ec000000	62000000	PCI config space
- * ed000000	61000000	PCI V3 regs
- * ee000000	60000000	PCI IO
+ * e8000000	40000000	PCI memory		PHYS_PCI_MEM_BASE	(max 512M)
+ * ec000000	61000000	PCI config space	PHYS_PCI_CONFIG_BASE	(max 16M)
+ * ed000000	62000000	PCI V3 regs		PHYS_PCI_V3_BASE	(max 64k)
+ * ee000000	60000000	PCI IO			PHYS_PCI_IO_BASE	(max 16M)
  * ef000000			Cache flush
  * f1000000	10000000	Core module registers
  * f1100000	11000000	System controller registers
@@ -67,7 +67,7 @@
  { IO_ADDRESS(INTEGRATOR_GPIO_BASE),  INTEGRATOR_GPIO_BASE,  SZ_4K     , DOMAIN_IO, 0, 1},
  { PCI_MEMORY_VADDR,                  PHYS_PCI_MEM_BASE,     SZ_16M    , DOMAIN_IO, 0, 1},
  { PCI_CONFIG_VADDR,                  PHYS_PCI_CONFIG_BASE,  SZ_16M    , DOMAIN_IO, 0, 1},
- { PCI_V3_VADDR,                      PHYS_PCI_V3_BASE,      SZ_512K   , DOMAIN_IO, 0, 1},
+ { PCI_V3_VADDR,                      PHYS_PCI_V3_BASE,      SZ_64K   , DOMAIN_IO, 0, 1},
  { PCI_IO_VADDR,                      PHYS_PCI_IO_BASE,      SZ_64K    , DOMAIN_IO, 0, 1},
  LAST_DESC
 };
diff -Nru a/arch/arm/mach-integrator/pci_v3.c b/arch/arm/mach-integrator/pci_v3.c
--- a/arch/arm/mach-integrator/pci_v3.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-integrator/pci_v3.c	Sat May 18 00:46:08 2002
@@ -50,7 +50,7 @@
  * 40000000 - 4FFFFFFF      PCI memory.  256M non-prefetchable
  * 50000000 - 5FFFFFFF      PCI memory.  256M prefetchable
  * 60000000 - 60FFFFFF      PCI IO.  16M
- * 68000000 - 68FFFFFF      PCI Configuration. 16M
+ * 61000000 - 61FFFFFF      PCI Configuration. 16M
  * 
  * There are three V3 windows, each described by a pair of V3 registers.
  * These are LB_BASE0/LB_MAP0, LB_BASE1/LB_MAP1 and LB_BASE2/LB_MAP2.
@@ -58,14 +58,6 @@
  * can be used either for PCI I/O or for I20 accesses.  By default, uHAL
  * uses this only for PCI IO space.
  * 
- * PCI Memory is mapped so that assigned addresses in PCI Memory match
- * local bus memory addresses.  In other words, if a PCI device is assigned
- * address 80200000 then that address is a valid local bus address as well
- * as a valid PCI Memory address.  PCI IO addresses are mapped to start
- * at zero.  This means that local bus address 60000000 maps to PCI IO address
- * 00000000 and so on.   Device driver writers need to be aware of this 
- * distinction.
- * 
  * Normally these spaces are mapped using the following base registers:
  * 
  * Usage Local Bus Memory         Base/Map registers used
@@ -73,7 +65,7 @@
  * Mem   40000000 - 4FFFFFFF      LB_BASE0/LB_MAP0
  * Mem   50000000 - 5FFFFFFF      LB_BASE1/LB_MAP1
  * IO    60000000 - 60FFFFFF      LB_BASE2/LB_MAP2
- * Cfg   68000000 - 68FFFFFF      
+ * Cfg   61000000 - 61FFFFFF
  * 
  * This means that I20 and PCI configuration space accesses will fail.
  * When PCI configuration accesses are needed (via the uHAL PCI 
@@ -84,7 +76,7 @@
  * Mem   40000000 - 4FFFFFFF      LB_BASE0/LB_MAP0
  * Mem   50000000 - 5FFFFFFF      LB_BASE0/LB_MAP0
  * IO    60000000 - 60FFFFFF      LB_BASE2/LB_MAP2
- * Cfg   68000000 - 68FFFFFF      LB_BASE1/LB_MAP1
+ * Cfg   61000000 - 61FFFFFF      LB_BASE1/LB_MAP1
  * 
  * To make this work, the code depends on overlapping windows working.
  * The V3 chip translates an address by checking its range within 
@@ -174,10 +166,10 @@
 static spinlock_t v3_lock = SPIN_LOCK_UNLOCKED;
 
 #define PCI_BUS_NONMEM_START	0x00000000
-#define PCI_BUS_NONMEM_SIZE	0x10000000
+#define PCI_BUS_NONMEM_SIZE	SZ_256M
 
-#define PCI_BUS_PREMEM_START	0x10000000
-#define PCI_BUS_PREMEM_SIZE	0x10000000
+#define PCI_BUS_PREMEM_START	PCI_BUS_NONMEM_START + PCI_BUS_NONMEM_SIZE
+#define PCI_BUS_PREMEM_SIZE	SZ_256M
 
 #if PCI_BUS_NONMEM_START & 0x000fffff
 #error PCI_BUS_NONMEM_START must be megabyte aligned
@@ -400,15 +392,15 @@
 
 static struct resource non_mem = {
 	name:	"PCI non-prefetchable",
-	start:	0x40000000 + PCI_BUS_NONMEM_START,
-	end:	0x40000000 + PCI_BUS_NONMEM_START + PCI_BUS_NONMEM_SIZE - 1,
+	start:	PHYS_PCI_MEM_BASE + PCI_BUS_NONMEM_START,
+	end:	PHYS_PCI_MEM_BASE + PCI_BUS_NONMEM_START + PCI_BUS_NONMEM_SIZE - 1,
 	flags:	IORESOURCE_MEM,
 };
 
 static struct resource pre_mem = {
 	name:	"PCI prefetchable",
-	start:	0x40000000 + PCI_BUS_PREMEM_START,
-	end:	0x40000000 + PCI_BUS_PREMEM_START + PCI_BUS_PREMEM_SIZE - 1,
+	start:	PHYS_PCI_MEM_BASE + PCI_BUS_PREMEM_START,
+	end:	PHYS_PCI_MEM_BASE + PCI_BUS_PREMEM_START + PCI_BUS_PREMEM_SIZE - 1,
 	flags:	IORESOURCE_MEM | IORESOURCE_PREFETCH,
 };
 
@@ -433,7 +425,7 @@
 	 */
 	resource[0] = &ioport_resource;
 	resource[1] = &non_mem;
-	resource[2] = &pre_mem;
+//	resource[2] = &pre_mem;
 
 	return 1;
 }
@@ -530,7 +522,7 @@
 	int ret = 0;
 
 	if (nr == 0) {
-		sys->mem_offset = 0x40000000;
+		sys->mem_offset = PHYS_PCI_MEM_BASE;
 		ret = pci_v3_setup_resources(sys->resource);
 	}
 
diff -Nru a/arch/arm/mach-iop310/arch.c b/arch/arm/mach-iop310/arch.c
--- a/arch/arm/mach-iop310/arch.c	Sat May 18 00:46:14 2002
+++ b/arch/arm/mach-iop310/arch.c	Sat May 18 00:46:14 2002
@@ -26,7 +26,7 @@
 extern void iq80310_init_irq(void);
 
 static void __init
-fixup_iq80310(struct machine_desc *desc, struct param_struct *params,
+fixup_iq80310(struct machine_desc *desc, struct tag *tags,
 	      char **cmdline, struct meminfo *mi)
 {
 	system_rev = (*(volatile unsigned int*)0xfe830000) & 0x0f;
diff -Nru a/arch/arm/mach-l7200/core.c b/arch/arm/mach-l7200/core.c
--- a/arch/arm/mach-l7200/core.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-l7200/core.c	Sat May 18 00:46:08 2002
@@ -83,7 +83,7 @@
 }
 
 static void __init
-fixup_l7200(struct machine_desc *desc, struct param_struct *unused,
+fixup_l7200(struct machine_desc *desc, struct tag *tags,
              char **cmdline, struct meminfo *mi)
 {
         mi->nr_banks      = 1;
diff -Nru a/arch/arm/mach-pxa/idp.c b/arch/arm/mach-pxa/idp.c
--- a/arch/arm/mach-pxa/idp.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-pxa/idp.c	Sat May 18 00:46:08 2002
@@ -69,7 +69,7 @@
 }
 
 static void __init
-fixup_idp(struct machine_desc *desc, struct param_struct *params,
+fixup_idp(struct machine_desc *desc, struct tag *tags,
 		char **cmdline, struct meminfo *mi)
 {
 #ifdef PXA_IDP_REV02
diff -Nru a/arch/arm/mach-pxa/lubbock.c b/arch/arm/mach-pxa/lubbock.c
--- a/arch/arm/mach-pxa/lubbock.c	Sat May 18 00:46:09 2002
+++ b/arch/arm/mach-pxa/lubbock.c	Sat May 18 00:46:09 2002
@@ -120,7 +120,7 @@
 __initcall(lubbock_init);
 
 static void __init
-fixup_lubbock(struct machine_desc *desc, struct param_struct *params,
+fixup_lubbock(struct machine_desc *desc, struct tag *tags,
 		char **cmdline, struct meminfo *mi)
 {
 	SET_BANK (0, 0xa0000000, 64*1024*1024);
diff -Nru a/arch/arm/mach-rpc/riscpc.c b/arch/arm/mach-rpc/riscpc.c
--- a/arch/arm/mach-rpc/riscpc.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-rpc/riscpc.c	Sat May 18 00:46:08 2002
@@ -61,7 +61,7 @@
 #endif
 
 static void __init
-fixup_riscpc(struct machine_desc *desc, struct param_struct *unusd,
+fixup_riscpc(struct machine_desc *desc, struct tag *tags,
 	    char **cmdline, struct meminfo *mi)
 {
 	/*
diff -Nru a/arch/arm/mach-sa1100/adsbitsy.c b/arch/arm/mach-sa1100/adsbitsy.c
--- a/arch/arm/mach-sa1100/adsbitsy.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-sa1100/adsbitsy.c	Sat May 18 00:46:08 2002
@@ -82,7 +82,7 @@
  */
 
 static void __init
-fixup_adsbitsy(struct machine_desc *desc, struct param_struct *params,
+fixup_adsbitsy(struct machine_desc *desc, struct tag *tags,
 		     char **cmdline, struct meminfo *mi)
 {
 	SET_BANK( 0, 0xc0000000, 32*1024*1024 );
diff -Nru a/arch/arm/mach-sa1100/assabet.c b/arch/arm/mach-sa1100/assabet.c
--- a/arch/arm/mach-sa1100/assabet.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-sa1100/assabet.c	Sat May 18 00:46:08 2002
@@ -103,7 +103,6 @@
 		 * or BCR_clear().
 		 */
 		ASSABET_BCR = BCR_value = ASSABET_BCR_DB1111;
-		NCR_0 = 0;
 
 #ifndef CONFIG_ASSABET_NEPONSET
 		printk( "Warning: Neponset detected but full support "
@@ -159,13 +158,11 @@
 	SCR_value = scr;
 }
 
-extern void convert_to_tag_list(struct param_struct *params, int mem_init);
-
 static void __init
-fixup_assabet(struct machine_desc *desc, struct param_struct *params,
+fixup_assabet(struct machine_desc *desc, struct tag *tags,
 	      char **cmdline, struct meminfo *mi)
 {
-	struct tag *t = (struct tag *)params;
+	struct tag *t = tags;
 
 	/* This must be done before any call to machine_has_neponset() */
 	map_sa1100_gpio_regs();
@@ -174,12 +171,6 @@
 	if (machine_has_neponset())
 		printk("Neponset expansion board detected\n");
 
-	/*
-	 * Apparantly bootldr uses a param_struct.  Groan.
-	 */
-	if (t->hdr.tag != ATAG_CORE)
-		convert_to_tag_list(params, 1);
-
 	if (t->hdr.tag != ATAG_CORE) {
 		t->hdr.tag = ATAG_CORE;
 		t->hdr.size = tag_size(tag_core);
@@ -319,17 +310,16 @@
 	sa1100_map_io();
 	iotable_init(assabet_io_desc);
 
+	if (machine_has_neponset()) {
 #ifdef CONFIG_ASSABET_NEPONSET
-	/*
-	 * We map Neponset registers even if it isn't present since
-	 * many drivers will try to probe their stuff (and fail).
-	 * This is still more friendly than a kernel paging request
-	 * crash.
-	 */
-	neponset_map_io();
+		/*
+		 * We map Neponset registers even if it isn't present since
+		 * many drivers will try to probe their stuff (and fail).
+		 * This is still more friendly than a kernel paging request
+		 * crash.
+		 */
+		neponset_map_io();
 #endif
-
-	if (machine_has_neponset()) {
 		/*
 		 * When Neponset is attached, the first UART should be
 		 * UART3.  That's what Angel is doing and many documents
diff -Nru a/arch/arm/mach-sa1100/badge4.c b/arch/arm/mach-sa1100/badge4.c
--- a/arch/arm/mach-sa1100/badge4.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-sa1100/badge4.c	Sat May 18 00:46:08 2002
@@ -143,13 +143,6 @@
 EXPORT_SYMBOL(badge4_set_5V);
 
 
-static void __init
-fixup_badge4(struct machine_desc *desc, struct param_struct *params,
-	    char **cmdline, struct meminfo *mi)
-{
-	/* nothing needed here */
-}
-
 static struct map_desc badge4_io_desc[] __initdata = {
   /*  virtual    physical    length      domain     r w c b		   */
   {0xf1000000, 0x08000000, 0x00100000, DOMAIN_IO, 0,1,0,0},/* SRAM  bank 1 */
@@ -170,7 +163,6 @@
 MACHINE_START(BADGE4, "Hewlett-Packard Laboratories BadgePAD 4")
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	BOOT_PARAMS(0xc0000100)
-	FIXUP(fixup_badge4)
 	MAPIO(badge4_map_io)
 	INITIRQ(sa1100_init_irq)
 MACHINE_END
diff -Nru a/arch/arm/mach-sa1100/brutus.c b/arch/arm/mach-sa1100/brutus.c
--- a/arch/arm/mach-sa1100/brutus.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-sa1100/brutus.c	Sat May 18 00:46:08 2002
@@ -23,7 +23,7 @@
 
 
 static void __init
-fixup_brutus(struct machine_desc *desc, struct param_struct *params,
+fixup_brutus(struct machine_desc *desc, struct tag *tags,
 	     char **cmdline, struct meminfo *mi)
 {
 	SET_BANK( 0, 0xc0000000, 4*1024*1024 );
diff -Nru a/arch/arm/mach-sa1100/cerf.c b/arch/arm/mach-sa1100/cerf.c
--- a/arch/arm/mach-sa1100/cerf.c	Sat May 18 00:46:09 2002
+++ b/arch/arm/mach-sa1100/cerf.c	Sat May 18 00:46:09 2002
@@ -36,7 +36,7 @@
 }
 
 static void __init
-fixup_cerf(struct machine_desc *desc, struct param_struct *params,
+fixup_cerf(struct machine_desc *desc, struct tag *tags,
 	   char **cmdline, struct meminfo *mi)
 {
 #if defined(CONFIG_SA1100_CERF_64MB)
diff -Nru a/arch/arm/mach-sa1100/empeg.c b/arch/arm/mach-sa1100/empeg.c
--- a/arch/arm/mach-sa1100/empeg.c	Sat May 18 00:46:14 2002
+++ b/arch/arm/mach-sa1100/empeg.c	Sat May 18 00:46:14 2002
@@ -17,7 +17,7 @@
 
 
 static void __init
-fixup_empeg(struct machine_desc *desc, struct param_struct *params,
+fixup_empeg(struct machine_desc *desc, struct tag *tags,
 	    char **cmdline, struct meminfo *mi)
 {
 	SET_BANK( 0, 0xc0000000, 4*1024*1024 );
diff -Nru a/arch/arm/mach-sa1100/flexanet.c b/arch/arm/mach-sa1100/flexanet.c
--- a/arch/arm/mach-sa1100/flexanet.c	Sat May 18 00:46:09 2002
+++ b/arch/arm/mach-sa1100/flexanet.c	Sat May 18 00:46:09 2002
@@ -152,13 +152,9 @@
 
 
 static void __init
-fixup_flexanet(struct machine_desc *desc, struct param_struct *params,
+fixup_flexanet(struct machine_desc *desc, struct tag *tags,
 	      char **cmdline, struct meminfo *mi)
 {
-	int				status;
-	unsigned long	now;
-
-
 	/* fixed RAM size, by now (64MB) */
 	SET_BANK( 0, 0xc0000000, 64*1024*1024 );
 	mi->nr_banks = 1;
diff -Nru a/arch/arm/mach-sa1100/freebird.c b/arch/arm/mach-sa1100/freebird.c
--- a/arch/arm/mach-sa1100/freebird.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-sa1100/freebird.c	Sat May 18 00:46:08 2002
@@ -52,7 +52,7 @@
 __initcall(freebird_init);
 
 static void __init
-fixup_freebird(struct machine_desc *desc, struct param_struct *params,
+fixup_freebird(struct machine_desc *desc, struct tag *tags,
 	       char **cmdline, struct meminfo *mi)
 {
 #ifdef CONFIG_SA1100_FREEBIRD_OLD
diff -Nru a/arch/arm/mach-sa1100/graphicsclient.c b/arch/arm/mach-sa1100/graphicsclient.c
--- a/arch/arm/mach-sa1100/graphicsclient.c	Sat May 18 00:46:15 2002
+++ b/arch/arm/mach-sa1100/graphicsclient.c	Sat May 18 00:46:15 2002
@@ -125,7 +125,7 @@
  */
 
 static void __init
-fixup_graphicsclient(struct machine_desc *desc, struct param_struct *params,
+fixup_graphicsclient(struct machine_desc *desc, struct tag *tags,
 		     char **cmdline, struct meminfo *mi)
 {
 	SET_BANK( 0, 0xc0000000, 16*1024*1024 );
diff -Nru a/arch/arm/mach-sa1100/graphicsmaster.c b/arch/arm/mach-sa1100/graphicsmaster.c
--- a/arch/arm/mach-sa1100/graphicsmaster.c	Sat May 18 00:46:09 2002
+++ b/arch/arm/mach-sa1100/graphicsmaster.c	Sat May 18 00:46:09 2002
@@ -157,7 +157,7 @@
  */
 
 static void __init
-fixup_graphicsmaster(struct machine_desc *desc, struct param_struct *params,
+fixup_graphicsmaster(struct machine_desc *desc, struct tag *tags,
 		     char **cmdline, struct meminfo *mi)
 {
 	SET_BANK( 0, 0xc0000000, 16*1024*1024 );
diff -Nru a/arch/arm/mach-sa1100/huw_webpanel.c b/arch/arm/mach-sa1100/huw_webpanel.c
--- a/arch/arm/mach-sa1100/huw_webpanel.c	Sat May 18 00:46:09 2002
+++ b/arch/arm/mach-sa1100/huw_webpanel.c	Sat May 18 00:46:09 2002
@@ -55,7 +55,7 @@
 
 
 static void __init
-fixup_huw_webpanel(struct machine_desc *desc, struct param_struct *params,
+fixup_huw_webpanel(struct machine_desc *desc, struct tag *tags,
 		   char **cmdline, struct meminfo *mi)
 {
 	/**
diff -Nru a/arch/arm/mach-sa1100/irq.c b/arch/arm/mach-sa1100/irq.c
--- a/arch/arm/mach-sa1100/irq.c	Sat May 18 00:46:15 2002
+++ b/arch/arm/mach-sa1100/irq.c	Sat May 18 00:46:15 2002
@@ -40,31 +40,29 @@
 	irq_desc[irq].handle(irq, &irq_desc[irq], &regs);
 }
 
+/*
+ * To get the GPIO number from an IRQ number
+ */
+#define GPIO_11_27_IRQ(i)	((i) - 21)
 #define GPIO11_27_MASK(irq)	(1 << GPIO_11_27_IRQ(irq))
 
 static int sa1100_gpio_type(unsigned int irq, unsigned int type)
 {
 	unsigned int mask;
 
-	printk(KERN_DEBUG "IRQ%d: ", irq);
-
 	if (irq <= 10)
 		mask = 1 << irq;
 	else
 		mask = GPIO11_27_MASK(irq);
 
 	if (type & __IRQT_RISEDGE) {
-		printk("rising ");
 		GPIO_IRQ_rising_edge |= mask;
 	} else
 		GPIO_IRQ_rising_edge &= ~mask;
 	if (type & __IRQT_FALEDGE) {
-		printk("falling ");
 		GPIO_IRQ_falling_edge |= mask;
 	} else
 		GPIO_IRQ_falling_edge &= ~mask;
-
-	printk("edges\n");
 
 	GRER = GPIO_IRQ_rising_edge & GPIO_IRQ_mask;
 	GFER = GPIO_IRQ_falling_edge & GPIO_IRQ_mask;
diff -Nru a/arch/arm/mach-sa1100/itsy.c b/arch/arm/mach-sa1100/itsy.c
--- a/arch/arm/mach-sa1100/itsy.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-sa1100/itsy.c	Sat May 18 00:46:08 2002
@@ -17,7 +17,7 @@
 
 
 static void __init
-fixup_itsy(struct machine_desc *desc, struct param_struct *params,
+fixup_itsy(struct machine_desc *desc, struct tag *tags,
 	   char **cmdline, struct meminfo *mi)
 {
 	SET_BANK( 0, 0xc0000000, 16*1024*1024 );
diff -Nru a/arch/arm/mach-sa1100/jornada720.c b/arch/arm/mach-sa1100/jornada720.c
--- a/arch/arm/mach-sa1100/jornada720.c	Sat May 18 00:46:10 2002
+++ b/arch/arm/mach-sa1100/jornada720.c	Sat May 18 00:46:10 2002
@@ -57,7 +57,7 @@
 
 
 static void __init
-fixup_jornada720(struct machine_desc *desc, struct param_struct *params,
+fixup_jornada720(struct machine_desc *desc, struct tag *tags,
 		 char **cmdline, struct meminfo *mi)
 {
 	SET_BANK( 0, 0xc0000000, 32*1024*1024 );
diff -Nru a/arch/arm/mach-sa1100/nanoengine.c b/arch/arm/mach-sa1100/nanoengine.c
--- a/arch/arm/mach-sa1100/nanoengine.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-sa1100/nanoengine.c	Sat May 18 00:46:08 2002
@@ -17,7 +17,7 @@
 
 
 static void __init
-fixup_nanoengine(struct machine_desc *desc, struct param_struct *params,
+fixup_nanoengine(struct machine_desc *desc, struct tag *tags,
 		 char **cmdline, struct meminfo *mi)
 {
 	SET_BANK( 0, 0xc0000000, 32*1024*1024 );
diff -Nru a/arch/arm/mach-sa1100/neponset.c b/arch/arm/mach-sa1100/neponset.c
--- a/arch/arm/mach-sa1100/neponset.c	Sat May 18 00:46:09 2002
+++ b/arch/arm/mach-sa1100/neponset.c	Sat May 18 00:46:09 2002
@@ -84,7 +84,7 @@
 	}
 }
 
-static void __init neponset_init_irq(void)
+static inline void __init neponset_init_irq(void)
 {
 	/*
 	 * Install handler for GPIO25.
@@ -102,6 +102,64 @@
 	set_irq_flags(IRQ_NEPONSET_USAR, IRQF_VALID | IRQF_PROBE);
 }
 
+static void neponset_set_mctrl(struct uart_port *port, u_int mctrl)
+{
+	u_int mdm_ctl0 = MDM_CTL_0;
+
+	if (port->mapbase == _Ser1UTCR0) {
+		if (mctrl & TIOCM_RTS)
+			mdm_ctl0 &= ~MDM_CTL0_RTS2;
+		else
+			mdm_ctl0 |= MDM_CTL0_RTS2;
+
+		if (mctrl & TIOCM_DTR)
+			mdm_ctl0 &= ~MDM_CTL0_DTR2;
+		else
+			mdm_ctl0 |= MDM_CTL0_DTR2;
+	} else if (port->mapbase == _Ser3UTCR0) {
+		if (mctrl & TIOCM_RTS)
+			mdm_ctl0 &= ~MDM_CTL0_RTS1;
+		else
+			mdm_ctl0 |= MDM_CTL0_RTS1;
+
+		if (mctrl & TIOCM_DTR)
+			mdm_ctl0 &= ~MDM_CTL0_DTR1;
+		else
+			mdm_ctl0 |= MDM_CTL0_DTR1;
+	}
+
+	MDM_CTL_0 = mdm_ctl0;
+}
+
+static u_int neponset_get_mctrl(struct uart_port *port)
+{
+	u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR;
+	u_int mdm_ctl1 = MDM_CTL_1;
+
+	if (port->mapbase == _Ser1UTCR0) {
+		if (mdm_ctl1 & MDM_CTL1_DCD2)
+			ret &= ~TIOCM_CD;
+		if (mdm_ctl1 & MDM_CTL1_CTS2)
+			ret &= ~TIOCM_CTS;
+		if (mdm_ctl1 & MDM_CTL1_DSR2)
+			ret &= ~TIOCM_DSR;
+	} else if (port->mapbase == _Ser3UTCR0) {
+		if (mdm_ctl1 & MDM_CTL1_DCD1)
+			ret &= ~TIOCM_CD;
+		if (mdm_ctl1 & MDM_CTL1_CTS1)
+			ret &= ~TIOCM_CTS;
+		if (mdm_ctl1 & MDM_CTL1_DSR1)
+			ret &= ~TIOCM_DSR;
+	}
+
+	return ret;
+}
+
+static struct sa1100_port_fns neponset_port_fns __initdata = {
+	set_mctrl:	neponset_set_mctrl,
+	get_mctrl:	neponset_get_mctrl,
+};
+
 static int __init neponset_init(void)
 {
 	int ret;
@@ -134,12 +192,14 @@
 	if (ret)
 		return ret;
 
+	sa1100_register_uart_fns(&neponset_port_fns);
+
 	neponset_init_irq();
 
 	/*
 	 * Disable GPIO 0/1 drivers so the buttons work on the module.
 	 */
-	NCR_0 |= NCR_GP01_OFF;
+	NCR_0 = NCR_GP01_OFF;
 
 	/*
 	 * Neponset has SA1111 connected to CS4.  We know that after
@@ -162,67 +222,7 @@
   LAST_DESC
 };
 
-static void neponset_set_mctrl(struct uart_port *port, u_int mctrl)
-{
-	u_int mdm_ctl0 = MDM_CTL_0;
-
-	if (port->mapbase == _Ser1UTCR0) {
-		if (mctrl & TIOCM_RTS)
-			mdm_ctl0 &= ~MDM_CTL0_RTS2;
-		else
-			mdm_ctl0 |= MDM_CTL0_RTS2;
-
-		if (mctrl & TIOCM_DTR)
-			mdm_ctl0 &= ~MDM_CTL0_DTR2;
-		else
-			mdm_ctl0 |= MDM_CTL0_DTR2;
-	} else if (port->mapbase == _Ser3UTCR0) {
-		if (mctrl & TIOCM_RTS)
-			mdm_ctl0 &= ~MDM_CTL0_RTS1;
-		else
-			mdm_ctl0 |= MDM_CTL0_RTS1;
-
-		if (mctrl & TIOCM_DTR)
-			mdm_ctl0 &= ~MDM_CTL0_DTR1;
-		else
-			mdm_ctl0 |= MDM_CTL0_DTR1;
-	}
-
-	MDM_CTL_0 = mdm_ctl0;
-}
-
-static u_int neponset_get_mctrl(struct uart_port *port)
-{
-	u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR;
-	u_int mdm_ctl1 = MDM_CTL_1;
-
-	if (port->mapbase == _Ser1UTCR0) {
-		if (mdm_ctl1 & MDM_CTL1_DCD2)
-			ret &= ~TIOCM_CD;
-		if (mdm_ctl1 & MDM_CTL1_CTS2)
-			ret &= ~TIOCM_CTS;
-		if (mdm_ctl1 & MDM_CTL1_DSR2)
-			ret &= ~TIOCM_DSR;
-	} else if (port->mapbase == _Ser3UTCR0) {
-		if (mdm_ctl1 & MDM_CTL1_DCD1)
-			ret &= ~TIOCM_CD;
-		if (mdm_ctl1 & MDM_CTL1_CTS1)
-			ret &= ~TIOCM_CTS;
-		if (mdm_ctl1 & MDM_CTL1_DSR1)
-			ret &= ~TIOCM_DSR;
-	}
-
-	return ret;
-}
-
-static struct sa1100_port_fns neponset_port_fns __initdata = {
-	set_mctrl:	neponset_set_mctrl,
-	get_mctrl:	neponset_get_mctrl,
-};
-
 void __init neponset_map_io(void)
 {
 	iotable_init(neponset_io_desc);
-	if (machine_has_neponset())
-		sa1100_register_uart_fns(&neponset_port_fns);
 }
diff -Nru a/arch/arm/mach-sa1100/omnimeter.c b/arch/arm/mach-sa1100/omnimeter.c
--- a/arch/arm/mach-sa1100/omnimeter.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-sa1100/omnimeter.c	Sat May 18 00:46:08 2002
@@ -41,7 +41,7 @@
 __initcall(omnimeter_init);
 
 static void __init
-fixup_omnimeter(struct machine_desc *desc, struct param_struct *params,
+fixup_omnimeter(struct machine_desc *desc, struct tag *tags,
 		char **cmdline, struct meminfo *mi)
 {
 	SET_BANK( 0, 0xc0000000, 16*1024*1024 );
diff -Nru a/arch/arm/mach-sa1100/pangolin.c b/arch/arm/mach-sa1100/pangolin.c
--- a/arch/arm/mach-sa1100/pangolin.c	Sat May 18 00:46:15 2002
+++ b/arch/arm/mach-sa1100/pangolin.c	Sat May 18 00:46:15 2002
@@ -17,7 +17,7 @@
 
 
 static void __init
-fixup_pangolin(struct machine_desc *desc, struct param_struct *params,
+fixup_pangolin(struct machine_desc *desc, struct tag *tags,
 	       char **cmdline, struct meminfo *mi)
 {
 	SET_BANK( 0, 0xc0000000, 128*1024*1024 );
diff -Nru a/arch/arm/mach-sa1100/pfs168.c b/arch/arm/mach-sa1100/pfs168.c
--- a/arch/arm/mach-sa1100/pfs168.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-sa1100/pfs168.c	Sat May 18 00:46:08 2002
@@ -57,7 +57,7 @@
 
 
 static void __init
-fixup_pfs168(struct machine_desc *desc, struct param_struct *params,
+fixup_pfs168(struct machine_desc *desc, struct tag *tags,
 	     char **cmdline, struct meminfo *mi)
 {
 	SET_BANK( 0, 0xc0000000, 16*1024*1024 );
diff -Nru a/arch/arm/mach-sa1100/pleb.c b/arch/arm/mach-sa1100/pleb.c
--- a/arch/arm/mach-sa1100/pleb.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-sa1100/pleb.c	Sat May 18 00:46:08 2002
@@ -16,7 +16,7 @@
 #include "generic.h"
 
 static void __init
-fixup_pleb(struct machine_desc *desc, struct param_struct *params,
+fixup_pleb(struct machine_desc *desc, struct tag *tags,
            char **cmdline, struct meminfo *mi)
 {
 	SET_BANK(0, 0xc0000000, 16*1024*1024);
diff -Nru a/arch/arm/mach-sa1100/sherman.c b/arch/arm/mach-sa1100/sherman.c
--- a/arch/arm/mach-sa1100/sherman.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-sa1100/sherman.c	Sat May 18 00:46:08 2002
@@ -17,7 +17,7 @@
 
 
 static void __init
-fixup_sherman(struct machine_desc *desc, struct param_struct *params,
+fixup_sherman(struct machine_desc *desc, struct tag *tags,
 	      char **cmdline, struct meminfo *mi)
 {
 	SET_BANK( 0, 0xc0000000, 64*1024*1024 );
diff -Nru a/arch/arm/mach-sa1100/simpad.c b/arch/arm/mach-sa1100/simpad.c
--- a/arch/arm/mach-sa1100/simpad.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-sa1100/simpad.c	Sat May 18 00:46:08 2002
@@ -41,7 +41,7 @@
 }
 
 static void __init
-fixup_simpad(struct machine_desc *desc, struct param_struct *params,
+fixup_simpad(struct machine_desc *desc, struct tag *tags,
 		   char **cmdline, struct meminfo *mi)
 {
 #ifdef CONFIG_SA1100_SIMPAD_DRAM_64MB /* DRAM */
diff -Nru a/arch/arm/mach-sa1100/system3.c b/arch/arm/mach-sa1100/system3.c
--- a/arch/arm/mach-sa1100/system3.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-sa1100/system3.c	Sat May 18 00:46:08 2002
@@ -74,7 +74,7 @@
 
 /* init funcs */
 static void __init fixup_system3(struct machine_desc *desc,
-		struct param_struct *params, char **cmdline, struct meminfo *mi);
+		struct tag *tags, char **cmdline, struct meminfo *mi);
 static int __init system3_init(void);
 static void __init system3_init_irq(void);
 static void __init system3_map_io(void);
@@ -87,8 +87,6 @@
 static void system3_lcd_power(int on);
 static void system3_backlight_power(int on);
 
-extern void convert_to_tag_list(struct param_struct *params, int mem_init);
-
 
 /**********************************************************************
  *  global data
@@ -239,7 +237,7 @@
  *
  */
 static void __init fixup_system3(struct machine_desc *desc,
-		struct param_struct *params, char **cmdline, struct meminfo *mi)
+		struct tag *tags, char **cmdline, struct meminfo *mi)
 {
 	DPRINTK( "%s\n", "START" );
 
diff -Nru a/arch/arm/mach-sa1100/victor.c b/arch/arm/mach-sa1100/victor.c
--- a/arch/arm/mach-sa1100/victor.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mach-sa1100/victor.c	Sat May 18 00:46:08 2002
@@ -42,7 +42,7 @@
 
 
 static void __init
-fixup_victor(struct machine_desc *desc, struct param_struct *params,
+fixup_victor(struct machine_desc *desc, struct tag *tags,
 	     char **cmdline, struct meminfo *mi)
 {
 	SET_BANK( 0, 0xc0000000, 4*1024*1024 );
diff -Nru a/arch/arm/mach-sa1100/xp860.c b/arch/arm/mach-sa1100/xp860.c
--- a/arch/arm/mach-sa1100/xp860.c	Sat May 18 00:46:09 2002
+++ b/arch/arm/mach-sa1100/xp860.c	Sat May 18 00:46:09 2002
@@ -58,7 +58,7 @@
 
 
 static void __init
-fixup_xp860(struct machine_desc *desc, struct param_struct *params,
+fixup_xp860(struct machine_desc *desc, struct tag *tags,
 	    char **cmdline, struct meminfo *mi)
 {
 	SET_BANK( 0, 0xc0000000, 32*1024*1024 );
diff -Nru a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile
--- a/arch/arm/mm/Makefile	Sat May 18 00:46:10 2002
+++ b/arch/arm/mm/Makefile	Sat May 18 00:46:10 2002
@@ -29,15 +29,21 @@
 
 # Select the processor-specific files
 p-$(CONFIG_CPU_26)	+= proc-arm2,3.o
+
+# ARMv3
 p-$(CONFIG_CPU_ARM610)	+= proc-arm6,7.o  tlb-v3.o   copypage-v3.o
 p-$(CONFIG_CPU_ARM710)	+= proc-arm6,7.o  tlb-v3.o   copypage-v3.o
-p-$(CONFIG_CPU_ARM720T)	+= proc-arm720.o  tlb-v4.o   copypage-v4.o   abort-lv4t.o
-p-$(CONFIG_CPU_ARM920T)	+= proc-arm920.o  tlb-v4wb.o copypage-v4.o   abort-ev4t.o
-p-$(CONFIG_CPU_ARM922T)	+= proc-arm922.o  tlb-v4wb.o copypage-v4.o   abort-ev4t.o
-p-$(CONFIG_CPU_ARM926T)	+= proc-arm926.o  tlb-v4wb.o copypage-v4.o   abort-ev5ej.o
-p-$(CONFIG_CPU_ARM1020)	+= proc-arm1020.o tlb-v4wb.o copypage-v4.o   abort-ev4t.o
-p-$(CONFIG_CPU_SA110)	+= proc-sa110.o   tlb-v4wb.o copypage-v4.o   copypage-v4mc.o abort-ev4.o   minicache.o
-p-$(CONFIG_CPU_SA1100)	+= proc-sa110.o   tlb-v4wb.o copypage-v4.o   copypage-v4mc.o abort-ev4.o   minicache.o
+
+# ARMv4
+p-$(CONFIG_CPU_ARM720T)	+= proc-arm720.o  tlb-v4.o   copypage-v4wt.o abort-lv4t.o
+p-$(CONFIG_CPU_ARM920T)	+= proc-arm920.o  tlb-v4wb.o copypage-v4wb.o abort-ev4t.o
+p-$(CONFIG_CPU_ARM922T)	+= proc-arm922.o  tlb-v4wb.o copypage-v4wb.o abort-ev4t.o
+p-$(CONFIG_CPU_ARM1020)	+= proc-arm1020.o tlb-v4wb.o copypage-v4wb.o abort-ev4t.o
+p-$(CONFIG_CPU_SA110)	+= proc-sa110.o   tlb-v4wb.o copypage-v4wb.o abort-ev4.o   minicache.o
+p-$(CONFIG_CPU_SA1100)	+= proc-sa110.o   tlb-v4wb.o copypage-v4mc.o abort-ev4.o   minicache.o
+
+# ARMv5
+p-$(CONFIG_CPU_ARM926T)	+= proc-arm926.o  tlb-v4wb.o copypage-v4wb.o abort-ev5ej.o
 p-$(CONFIG_CPU_XSCALE)	+= proc-xscale.o  tlb-v4wb.o copypage-v5te.o abort-ev4t.o  minicache.o
 
 obj-y		+= $(sort $(p-y))
diff -Nru a/arch/arm/mm/consistent.c b/arch/arm/mm/consistent.c
--- a/arch/arm/mm/consistent.c	Sat May 18 00:46:08 2002
+++ b/arch/arm/mm/consistent.c	Sat May 18 00:46:08 2002
@@ -52,7 +52,8 @@
 		goto no_page;
 
 	*dma_handle = page_to_bus(page);
-	ret = __ioremap(page_to_phys(page), size, 0);
+	ret = __ioremap(page_to_pfn(page) << PAGE_SHIFT, size, 0,
+			PAGE_SIZE << order);
 	if (!ret)
 		goto no_remap;
 
diff -Nru a/arch/arm/mm/copypage-v4.S b/arch/arm/mm/copypage-v4.S
--- a/arch/arm/mm/copypage-v4.S	Sat May 18 00:46:08 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,77 +0,0 @@
-/*
- *  linux/arch/arm/lib/copypage.S
- *
- *  Copyright (C) 1995-1999 Russell King
- *
- * 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.
- *
- *  ASM optimised string functions
- */
-#include <linux/linkage.h>
-#include <asm/constants.h>
-
-	.text
-	.align	5
-/*
- * ARMv4 optimised copy_user_page
- *
- * We flush the destination cache lines just before we write the data into the
- * corresponding address.  Since the Dcache is read-allocate, this removes the
- * Dcache aliasing issue.  The writes will be forwarded to the write buffer,
- * and merged as appropriate.
- *
- * Note: We rely on all ARMv4 processors implementing the "invalidate D line"
- * instruction.  If your processor does not supply this, you have to write your
- * own copy_user_page that does the right thing.
- */
-ENTRY(v4_copy_user_page)
-	stmfd	sp!, {r4, lr}			@ 2
-	mov	r2, #PAGE_SZ/64			@ 1
-	ldmia	r1!, {r3, r4, ip, lr}		@ 4
-1:	mcr	p15, 0, r0, c7, c6, 1		@ 1   invalidate D line
-	stmia	r0!, {r3, r4, ip, lr}		@ 4
-	ldmia	r1!, {r3, r4, ip, lr}		@ 4+1
-	stmia	r0!, {r3, r4, ip, lr}		@ 4
-	ldmia	r1!, {r3, r4, ip, lr}		@ 4
-	mcr	p15, 0, r0, c7, c6, 1		@ 1   invalidate D line
-	stmia	r0!, {r3, r4, ip, lr}		@ 4
-	ldmia	r1!, {r3, r4, ip, lr}		@ 4
-	subs	r2, r2, #1			@ 1
-	stmia	r0!, {r3, r4, ip, lr}		@ 4
-	ldmneia	r1!, {r3, r4, ip, lr}		@ 4
-	bne	1b				@ 1
-	mcr	p15, 0, r1, c7, c10, 4		@ 1   drain WB
-	ldmfd	 sp!, {r4, pc}			@ 3
-
-	.align	5
-/*
- * ARMv4 optimised clear_user_page
- *
- * Same story as above.
- */
-ENTRY(v4_clear_user_page)
-	str	lr, [sp, #-4]!
-	mov	r1, #PAGE_SZ/64			@ 1
-	mov	r2, #0				@ 1
-	mov	r3, #0				@ 1
-	mov	ip, #0				@ 1
-	mov	lr, #0				@ 1
-1:	mcr	p15, 0, r0, c7, c6, 1		@ 1   invalidate D line
-	stmia	r0!, {r2, r3, ip, lr}		@ 4
-	stmia	r0!, {r2, r3, ip, lr}		@ 4
-	mcr	p15, 0, r0, c7, c6, 1		@ 1   invalidate D line
-	stmia	r0!, {r2, r3, ip, lr}		@ 4
-	stmia	r0!, {r2, r3, ip, lr}		@ 4
-	subs	r1, r1, #1			@ 1
-	bne	1b				@ 1
-	mcr	p15, 0, r1, c7, c10, 4		@ 1   drain WB
-	ldr	pc, [sp], #4
-
-	.section ".text.init", #alloc, #execinstr
-
-ENTRY(v4_user_fns)
-	.long	v4_clear_user_page
-	.long	v4_copy_user_page
-
diff -Nru a/arch/arm/mm/copypage-v4wb.S b/arch/arm/mm/copypage-v4wb.S
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/arm/mm/copypage-v4wb.S	Sat May 18 00:46:08 2002
@@ -0,0 +1,77 @@
+/*
+ *  linux/arch/arm/lib/copypage.S
+ *
+ *  Copyright (C) 1995-1999 Russell King
+ *
+ * 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.
+ *
+ *  ASM optimised string functions
+ */
+#include <linux/linkage.h>
+#include <asm/constants.h>
+
+	.text
+	.align	5
+/*
+ * ARMv4 optimised copy_user_page
+ *
+ * We flush the destination cache lines just before we write the data into the
+ * corresponding address.  Since the Dcache is read-allocate, this removes the
+ * Dcache aliasing issue.  The writes will be forwarded to the write buffer,
+ * and merged as appropriate.
+ *
+ * Note: We rely on all ARMv4 processors implementing the "invalidate D line"
+ * instruction.  If your processor does not supply this, you have to write your
+ * own copy_user_page that does the right thing.
+ */
+ENTRY(v4wb_copy_user_page)
+	stmfd	sp!, {r4, lr}			@ 2
+	mov	r2, #PAGE_SZ/64			@ 1
+	ldmia	r1!, {r3, r4, ip, lr}		@ 4
+1:	mcr	p15, 0, r0, c7, c6, 1		@ 1   invalidate D line
+	stmia	r0!, {r3, r4, ip, lr}		@ 4
+	ldmia	r1!, {r3, r4, ip, lr}		@ 4+1
+	stmia	r0!, {r3, r4, ip, lr}		@ 4
+	ldmia	r1!, {r3, r4, ip, lr}		@ 4
+	mcr	p15, 0, r0, c7, c6, 1		@ 1   invalidate D line
+	stmia	r0!, {r3, r4, ip, lr}		@ 4
+	ldmia	r1!, {r3, r4, ip, lr}		@ 4
+	subs	r2, r2, #1			@ 1
+	stmia	r0!, {r3, r4, ip, lr}		@ 4
+	ldmneia	r1!, {r3, r4, ip, lr}		@ 4
+	bne	1b				@ 1
+	mcr	p15, 0, r1, c7, c10, 4		@ 1   drain WB
+	ldmfd	 sp!, {r4, pc}			@ 3
+
+	.align	5
+/*
+ * ARMv4 optimised clear_user_page
+ *
+ * Same story as above.
+ */
+ENTRY(v4wb_clear_user_page)
+	str	lr, [sp, #-4]!
+	mov	r1, #PAGE_SZ/64			@ 1
+	mov	r2, #0				@ 1
+	mov	r3, #0				@ 1
+	mov	ip, #0				@ 1
+	mov	lr, #0				@ 1
+1:	mcr	p15, 0, r0, c7, c6, 1		@ 1   invalidate D line
+	stmia	r0!, {r2, r3, ip, lr}		@ 4
+	stmia	r0!, {r2, r3, ip, lr}		@ 4
+	mcr	p15, 0, r0, c7, c6, 1		@ 1   invalidate D line
+	stmia	r0!, {r2, r3, ip, lr}		@ 4
+	stmia	r0!, {r2, r3, ip, lr}		@ 4
+	subs	r1, r1, #1			@ 1
+	bne	1b				@ 1
+	mcr	p15, 0, r1, c7, c10, 4		@ 1   drain WB
+	ldr	pc, [sp], #4
+
+	.section ".text.init", #alloc, #execinstr
+
+ENTRY(v4wb_user_fns)
+	.long	v4wb_clear_user_page
+	.long	v4wb_copy_user_page
+
diff -Nru a/arch/arm/mm/copypage-v4wt.S b/arch/arm/mm/copypage-v4wt.S
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/arm/mm/copypage-v4wt.S	Sat May 18 00:46:15 2002
@@ -0,0 +1,71 @@
+/*
+ *  linux/arch/arm/lib/copypage-v4.S
+ *
+ *  Copyright (C) 1995-1999 Russell King
+ *
+ * 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.
+ *
+ *  ASM optimised string functions
+ *
+ *  This is for CPUs with a writethrough cache and 'flush ID cache' is
+ *  the only supported cache operation.
+ */
+#include <linux/linkage.h>
+#include <asm/constants.h>
+
+	.text
+	.align	5
+/*
+ * ARMv4 optimised copy_user_page
+ *
+ * Since we have writethrough caches, we don't have to worry about
+ * dirty data in the cache.  However, we do have to ensure that
+ * subsequent reads are up to date.
+ */
+ENTRY(v4wt_copy_user_page)
+	stmfd	sp!, {r4, lr}			@ 2
+	mov	r2, #PAGE_SZ/64			@ 1
+	ldmia	r1!, {r3, r4, ip, lr}		@ 4
+1:	stmia	r0!, {r3, r4, ip, lr}		@ 4
+	ldmia	r1!, {r3, r4, ip, lr}		@ 4+1
+	stmia	r0!, {r3, r4, ip, lr}		@ 4
+	ldmia	r1!, {r3, r4, ip, lr}		@ 4
+	stmia	r0!, {r3, r4, ip, lr}		@ 4
+	ldmia	r1!, {r3, r4, ip, lr}		@ 4
+	subs	r2, r2, #1			@ 1
+	stmia	r0!, {r3, r4, ip, lr}		@ 4
+	ldmneia	r1!, {r3, r4, ip, lr}		@ 4
+	bne	1b				@ 1
+	mcr	p15, 0, r2, c7, c7, 0		@ flush ID cache
+	ldmfd	sp!, {r4, pc}			@ 3
+
+	.align	5
+/*
+ * ARMv4 optimised clear_user_page
+ *
+ * Same story as above.
+ */
+ENTRY(v4wt_clear_user_page)
+	str	lr, [sp, #-4]!
+	mov	r1, #PAGE_SZ/64			@ 1
+	mov	r2, #0				@ 1
+	mov	r3, #0				@ 1
+	mov	ip, #0				@ 1
+	mov	lr, #0				@ 1
+1:	stmia	r0!, {r2, r3, ip, lr}		@ 4
+	stmia	r0!, {r2, r3, ip, lr}		@ 4
+	stmia	r0!, {r2, r3, ip, lr}		@ 4
+	stmia	r0!, {r2, r3, ip, lr}		@ 4
+	subs	r1, r1, #1			@ 1
+	bne	1b				@ 1
+	mcr	p15, 0, r2, c7, c7, 0		@ flush ID cache
+	ldr	pc, [sp], #4
+
+	.section ".text.init", #alloc, #execinstr
+
+ENTRY(v4wt_user_fns)
+	.long	v4wt_clear_user_page
+	.long	v4wt_copy_user_page
+
diff -Nru a/arch/arm/mm/proc-arm1020.S b/arch/arm/mm/proc-arm1020.S
--- a/arch/arm/mm/proc-arm1020.S	Sat May 18 00:46:08 2002
+++ b/arch/arm/mm/proc-arm1020.S	Sat May 18 00:46:08 2002
@@ -557,5 +557,5 @@
 	.long	cpu_arm1020_info
 	.long	arm1020_processor_functions
 	.long	v4wbi_tlb_fns
-	.long	v4_user_fns
+	.long	v4wb_user_fns
 	.size	__arm1020_proc_info, . - __arm1020_proc_info
diff -Nru a/arch/arm/mm/proc-arm720.S b/arch/arm/mm/proc-arm720.S
--- a/arch/arm/mm/proc-arm720.S	Sat May 18 00:46:08 2002
+++ b/arch/arm/mm/proc-arm720.S	Sat May 18 00:46:08 2002
@@ -261,5 +261,5 @@
 		.long	cpu_arm720_info				@ info
 		.long	arm720_processor_functions
 		.long	v4_tlb_fns
-		.long	v4_user_fns
+		.long	v4wt_user_fns
 		.size	__arm720_proc_info, . - __arm720_proc_info
diff -Nru a/arch/arm/mm/proc-arm920.S b/arch/arm/mm/proc-arm920.S
--- a/arch/arm/mm/proc-arm920.S	Sat May 18 00:46:08 2002
+++ b/arch/arm/mm/proc-arm920.S	Sat May 18 00:46:08 2002
@@ -543,5 +543,5 @@
 	.long	cpu_arm920_info
 	.long	arm920_processor_functions
 	.long	v4wbi_tlb_fns
-	.long	v4_user_fns
+	.long	v4wb_user_fns
 	.size	__arm920_proc_info, . - __arm920_proc_info
diff -Nru a/arch/arm/mm/proc-arm922.S b/arch/arm/mm/proc-arm922.S
--- a/arch/arm/mm/proc-arm922.S	Sat May 18 00:46:09 2002
+++ b/arch/arm/mm/proc-arm922.S	Sat May 18 00:46:09 2002
@@ -544,5 +544,5 @@
 	.long	cpu_arm922_info
 	.long	arm922_processor_functions
 	.long	v4wbi_tlb_fns
-	.long	v4_user_fns
+	.long	v4wb_user_fns
 	.size	__arm922_proc_info, . - __arm922_proc_info
diff -Nru a/arch/arm/mm/proc-arm926.S b/arch/arm/mm/proc-arm926.S
--- a/arch/arm/mm/proc-arm926.S	Sat May 18 00:46:08 2002
+++ b/arch/arm/mm/proc-arm926.S	Sat May 18 00:46:08 2002
@@ -542,5 +542,5 @@
 	.long	cpu_arm926_info
 	.long	arm926_processor_functions
 	.long	v4wbi_tlb_fns
-	.long	v4_user_fns
+	.long	v4wb_user_fns
 	.size	__arm926_proc_info, . - __arm926_proc_info
diff -Nru a/arch/arm/mm/proc-sa110.S b/arch/arm/mm/proc-sa110.S
--- a/arch/arm/mm/proc-sa110.S	Sat May 18 00:46:09 2002
+++ b/arch/arm/mm/proc-sa110.S	Sat May 18 00:46:09 2002
@@ -611,6 +611,7 @@
 
 	.section ".proc.info", #alloc, #execinstr
 
+#ifdef CONFIG_CPU_SA110
 	.type	__sa110_proc_info,#object
 __sa110_proc_info:
 	.long	0x4401a100
@@ -623,9 +624,11 @@
 	.long	cpu_sa110_info
 	.long	sa110_processor_functions
 	.long	v4wb_tlb_fns
-	.long	v4_user_fns
+	.long	v4wb_user_fns
 	.size	__sa110_proc_info, . - __sa110_proc_info
+#endif
 
+#ifdef CONFIG_CPU_SA1100
 	.type	__sa1100_proc_info,#object
 __sa1100_proc_info:
 	.long	0x4401a110
@@ -655,3 +658,4 @@
 	.long	v4wb_tlb_fns
 	.long	v4_mc_user_fns
 	.size	__sa1110_proc_info, . - __sa1110_proc_info
+#endif
diff -Nru a/arch/arm/mm/tlb-v4.S b/arch/arm/mm/tlb-v4.S
--- a/arch/arm/mm/tlb-v4.S	Sat May 18 00:46:09 2002
+++ b/arch/arm/mm/tlb-v4.S	Sat May 18 00:46:09 2002
@@ -55,14 +55,13 @@
 	eors	r3, ip, r3				@ == mm ?
 	movne	pc, lr				@ no, we dont do anything
 	vma_vm_flags ip, r2
+.v4_flush_kern_tlb_range:
 	bic	r0, r0, #0x0ff
 	bic	r0, r0, #0xf00
-1:	mcr	p15, 0, r0, c8, c6, 1		@ invalidate D TLB entry
+1:	mcr	p15, 0, r0, c8, c7, 1		@ invalidate TLB entry
 	add	r0, r0, #PAGE_SZ
 	cmp	r0, r1
 	blo	1b
-	tst	ip, #VM_EXEC
-	mcrne	p15, 0, r3, c8, c5, 0		@ invalidate I TLB
 	mov	pc, lr
 
 /*
@@ -80,9 +79,8 @@
 	teq	r2, r3				@ equal
 	movne	pc, lr				@ no
 	vma_vm_flags r2, r1
-	mcr	p15, 0, r0, c8, c6, 1		@ invalidate D TLB entry
-	tst	r2, #VM_EXEC
-	mcrne	p15, 0, r3, c8, c5, 0		@ invalidate I TLB
+.v4_flush_kern_tlb_page:
+	mcr	p15, 0, r0, c8, c7, 1		@ invalidate TLB entry
 	mov	pc, lr
 
 /*
@@ -94,16 +92,8 @@
  *	- start - virtual address (may not be aligned)
  *	- end   - virtual address (may not be aligned)
  */
-	.align	5
-ENTRY(v4_flush_kern_tlb_range)
-	bic	r0, r0, #0x0ff
-	bic	r0, r0, #0xf00
-1:	mcr	p15, 0, r0, c8, c6, 1		@ invalidate D TLB entry
-	add	r0, r0, #PAGE_SZ
-	cmp	r0, r1
-	blo	1b
-	mcr	p15, 0, r3, c8, c5, 0		@ invalidate I TLB
-	mov	pc, lr
+.globl v4_flush_kern_tlb_range
+.equ v4_flush_kern_tlb_range, .v4_flush_kern_tlb_range
 
 
 /*
@@ -115,9 +105,8 @@
  *
  *	- kaddr - Kernel virtual memory address
  */
-ENTRY(v4_flush_kern_tlb_page)
-	mcr	p15, 0, r0, c8, c6, 1		@ invalidate D TLB entry
-	mov	pc, lr
+.globl v4_flush_kern_tlb_page
+.equ v4_flush_kern_tlb_page, .v4_flush_kern_tlb_page
 
 	.section ".text.init", #alloc, #execinstr
 
diff -Nru a/arch/arm/vmlinux-armo.lds.in b/arch/arm/vmlinux-armo.lds.in
--- a/arch/arm/vmlinux-armo.lds.in	Sat May 18 00:46:15 2002
+++ b/arch/arm/vmlinux-armo.lds.in	Sat May 18 00:46:15 2002
@@ -4,6 +4,7 @@
  */
 OUTPUT_ARCH(arm)
 ENTRY(stext)
+jiffies = jiffies_64;
 SECTIONS
 {
 	. = TEXTADDR;
diff -Nru a/arch/arm/vmlinux-armv.lds.in b/arch/arm/vmlinux-armv.lds.in
--- a/arch/arm/vmlinux-armv.lds.in	Sat May 18 00:46:09 2002
+++ b/arch/arm/vmlinux-armv.lds.in	Sat May 18 00:46:09 2002
@@ -4,6 +4,7 @@
  */
 OUTPUT_ARCH(arm)
 ENTRY(stext)
+jiffies = jiffies_64;
 SECTIONS
 {
 	. = TEXTADDR;
diff -Nru a/arch/cris/cris.ld b/arch/cris/cris.ld
--- a/arch/cris/cris.ld	Sat May 18 00:46:09 2002
+++ b/arch/cris/cris.ld	Sat May 18 00:46:09 2002
@@ -8,6 +8,7 @@
  * the kernel has booted. 
  */	
 
+jiffies = jiffies_64;
 SECTIONS
 {
 	. = @CONFIG_ETRAX_DRAM_VIRTUAL_BASE@;
diff -Nru a/arch/cris/kernel/Makefile b/arch/cris/kernel/Makefile
--- a/arch/cris/kernel/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/cris/kernel/Makefile	Sat May 18 00:46:08 2002
@@ -8,10 +8,7 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-# These assembly files can't be assembld with -traditional, so we
-# need another build rule than the one in the toplevel Makefile.
-.S.o:
-	$(CC) $(AFLAGS) -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
 
 all: kernel.o head.o
 
diff -Nru a/arch/cris/kernel/process.c b/arch/cris/kernel/process.c
--- a/arch/cris/kernel/process.c	Sat May 18 00:46:08 2002
+++ b/arch/cris/kernel/process.c	Sat May 18 00:46:08 2002
@@ -299,7 +299,9 @@
 asmlinkage int sys_fork(long r10, long r11, long r12, long r13, long mof, long srp,
 			struct pt_regs *regs)
 {
-	return do_fork(SIGCHLD, rdusp(), regs, 0);
+	struct task_struct *p;
+	p = do_fork(SIGCHLD, rdusp(), regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /* if newusp is 0, we just grab the old usp */
@@ -308,9 +310,11 @@
 			 long r12, long r13, long mof, long srp,
 			 struct pt_regs *regs)
 {
+	struct task_struct *p;
 	if (!newusp)
 		newusp = rdusp();
-	return do_fork(flags, newusp, regs, 0);
+	p = do_fork(flags & ~CLONE_IDLETASK, newusp, regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /* vfork is a system call in i386 because of register-pressure - maybe
@@ -320,7 +324,9 @@
 asmlinkage int sys_vfork(long r10, long r11, long r12, long r13, long mof, long srp,
 			 struct pt_regs *regs)
 {
-        return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0);
+	struct task_struct *p;
+        p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /*
diff -Nru a/arch/cris/lib/Makefile b/arch/cris/lib/Makefile
--- a/arch/cris/lib/Makefile	Sat May 18 00:46:14 2002
+++ b/arch/cris/lib/Makefile	Sat May 18 00:46:14 2002
@@ -2,10 +2,12 @@
 # Makefile for Etrax-specific library files..
 #
 
-.S.o:
-	$(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
 
 L_TARGET = lib.a
+
+EXTRA_AFLAGS := -traditional
+
 obj-y  = checksum.o checksumcopy.o string.o usercopy.o memset.o csumcpfruser.o
 
 include $(TOPDIR)/Rules.make
diff -Nru a/arch/i386/defconfig b/arch/i386/defconfig
--- a/arch/i386/defconfig	Sat May 18 00:46:08 2002
+++ b/arch/i386/defconfig	Sat May 18 00:46:08 2002
@@ -275,7 +275,6 @@
 CONFIG_BLK_DEV_PIIX=y
 # CONFIG_BLK_DEV_NS87415 is not set
 # CONFIG_BLK_DEV_OPTI621 is not set
-# CONFIG_BLK_DEV_PDC_ADMA is not set
 # CONFIG_BLK_DEV_PDC202XX is not set
 # CONFIG_PDC202XX_BURST is not set
 # CONFIG_PDC202XX_FORCE is not set
diff -Nru a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c
--- a/arch/i386/kernel/process.c	Sat May 18 00:46:08 2002
+++ b/arch/i386/kernel/process.c	Sat May 18 00:46:08 2002
@@ -711,11 +711,15 @@
 
 asmlinkage int sys_fork(struct pt_regs regs)
 {
-	return do_fork(SIGCHLD, regs.esp, &regs, 0);
+	struct task_struct *p;
+
+	p = do_fork(SIGCHLD, regs.esp, &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 asmlinkage int sys_clone(struct pt_regs regs)
 {
+	struct task_struct *p;
 	unsigned long clone_flags;
 	unsigned long newsp;
 
@@ -723,7 +727,8 @@
 	newsp = regs.ecx;
 	if (!newsp)
 		newsp = regs.esp;
-	return do_fork(clone_flags, newsp, &regs, 0);
+	p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /*
@@ -738,7 +743,10 @@
  */
 asmlinkage int sys_vfork(struct pt_regs regs)
 {
-	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, &regs, 0);
+	struct task_struct *p;
+
+	p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /*
diff -Nru a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c
--- a/arch/i386/kernel/setup.c	Sat May 18 00:46:09 2002
+++ b/arch/i386/kernel/setup.c	Sat May 18 00:46:09 2002
@@ -2153,11 +2153,6 @@
 		strcpy(c->x86_model_id, p);
 	
 #ifdef CONFIG_SMP
-	/* PGE CPUID bug: Pentium4 supports PGE, but seems to have SMP bugs.. */
-	if ( c->x86 == 15 )
-		clear_bit(X86_FEATURE_PGE, c->x86_capability);
-
-
 	if (test_bit(X86_FEATURE_HT, c->x86_capability)) {
 		extern	int phys_proc_id[NR_CPUS];
 		
diff -Nru a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c
--- a/arch/i386/kernel/smpboot.c	Sat May 18 00:46:09 2002
+++ b/arch/i386/kernel/smpboot.c	Sat May 18 00:46:09 2002
@@ -529,14 +529,14 @@
 	unsigned short ss;
 } stack_start;
 
-static int __init fork_by_hand(void)
+static struct task_struct * __init fork_by_hand(void)
 {
 	struct pt_regs regs;
 	/*
 	 * don't care about the eip and regs settings since
 	 * we'll never reschedule the forked task.
 	 */
-	return do_fork(CLONE_VM|CLONE_PID, 0, &regs, 0);
+	return do_fork(CLONE_VM|CLONE_IDLETASK, 0, &regs, 0);
 }
 
 /* which physical APIC ID maps to which logical CPU number */
@@ -822,17 +822,14 @@
 	 * We can't use kernel_thread since we must avoid to
 	 * reschedule the child.
 	 */
-	if (fork_by_hand() < 0)
+	idle = fork_by_hand();
+	if (IS_ERR(idle))
 		panic("failed fork for CPU %d", cpu);
 
 	/*
 	 * We remove it from the pidhash and the runqueue
 	 * once we got the process:
 	 */
-	idle = prev_task(&init_task);
-	if (!idle)
-		panic("No idle process for CPU %d", cpu);
-
 	init_idle(idle, cpu);
 
 	map_cpu_to_boot_apicid(cpu, apicid);
diff -Nru a/arch/i386/pci/Makefile b/arch/i386/pci/Makefile
--- a/arch/i386/pci/Makefile	Sat May 18 00:46:13 2002
+++ b/arch/i386/pci/Makefile	Sat May 18 00:46:13 2002
@@ -24,6 +24,4 @@
 obj-y		+= irq.o common.o
 endif		# CONFIG_VISWS
 
-export-objs     +=      $(obj-y)
-
 include $(TOPDIR)/Rules.make
diff -Nru a/arch/i386/vmlinux.lds b/arch/i386/vmlinux.lds
--- a/arch/i386/vmlinux.lds	Sat May 18 00:46:08 2002
+++ b/arch/i386/vmlinux.lds	Sat May 18 00:46:08 2002
@@ -4,6 +4,7 @@
 OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
 OUTPUT_ARCH(i386)
 ENTRY(_start)
+jiffies = jiffies_64;
 SECTIONS
 {
   . = 0xC0000000 + 0x100000;
diff -Nru a/arch/ia64/defconfig b/arch/ia64/defconfig
--- a/arch/ia64/defconfig	Sat May 18 00:46:08 2002
+++ b/arch/ia64/defconfig	Sat May 18 00:46:08 2002
@@ -247,7 +247,6 @@
 # CONFIG_BLK_DEV_PIIX is not set
 # CONFIG_BLK_DEV_NS87415 is not set
 # CONFIG_BLK_DEV_OPTI621 is not set
-# CONFIG_BLK_DEV_PDC_ADMA is not set
 # CONFIG_BLK_DEV_PDC202XX is not set
 # CONFIG_PDC202XX_BURST is not set
 # CONFIG_PDC202XX_FORCE is not set
diff -Nru a/arch/ia64/ia32/ia32_entry.S b/arch/ia64/ia32/ia32_entry.S
--- a/arch/ia64/ia32/ia32_entry.S	Sat May 18 00:46:13 2002
+++ b/arch/ia64/ia32/ia32_entry.S	Sat May 18 00:46:13 2002
@@ -41,7 +41,7 @@
 	mov out3=16				// stacksize (compensates for 16-byte scratch area)
 	adds out2=IA64_SWITCH_STACK_SIZE+16,sp	// out2 = &regs
 	zxt4 out0=in0				// out0 = clone_flags
-	br.call.sptk.many rp=do_fork
+	br.call.sptk.many rp=do_fork_WITHOUT_CLONE_IDLETASK		// FIXME: mask out CLONE_IDLETASK from flags, and return value now task_struct *.
 .ret0:	.restore sp
 	adds sp=IA64_SWITCH_STACK_SIZE,sp	// pop the switch stack
 	mov ar.pfs=loc1
@@ -167,7 +167,7 @@
 	mov out1=0
 	mov out3=0
 	adds out2=IA64_SWITCH_STACK_SIZE+16,sp	// out2 = &regs
-	br.call.sptk.few rp=do_fork
+	br.call.sptk.few rp=do_fork_FIXME_RETURNS_TASK_STRUCT
 .ret5:	mov ar.pfs=loc1
 	.restore sp
 	adds sp=IA64_SWITCH_STACK_SIZE,sp	// pop the switch stack
diff -Nru a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S
--- a/arch/ia64/kernel/entry.S	Sat May 18 00:46:08 2002
+++ b/arch/ia64/kernel/entry.S	Sat May 18 00:46:08 2002
@@ -101,7 +101,7 @@
 	mov out3=in2
 	adds out2=IA64_SWITCH_STACK_SIZE+16,sp	// out2 = &regs
 	mov out0=in0				// out0 = clone_flags
-	br.call.sptk.many rp=do_fork
+	br.call.sptk.many rp=do_fork_WITHOUT_CLONE_IDLETASK // FIXME: mask out CLONE_IDLETASK from flags, and now returns task_struct *.
 .ret1:	.restore sp
 	adds sp=IA64_SWITCH_STACK_SIZE,sp	// pop the switch stack
 	mov ar.pfs=loc1
@@ -120,7 +120,7 @@
 	mov out3=16				// stacksize (compensates for 16-byte scratch area)
 	adds out2=IA64_SWITCH_STACK_SIZE+16,sp	// out2 = &regs
 	mov out0=in0				// out0 = clone_flags
-	br.call.sptk.many rp=do_fork
+	br.call.sptk.many rp=do_fork_WITHOUT_CLONE_IDLETASK // FIXME: mask out CLONE_IDLETASK from flags, and now return task_struct *.
 .ret2:	.restore sp
 	adds sp=IA64_SWITCH_STACK_SIZE,sp	// pop the switch stack
 	mov ar.pfs=loc1
diff -Nru a/arch/ia64/kernel/smpboot.c b/arch/ia64/kernel/smpboot.c
--- a/arch/ia64/kernel/smpboot.c	Sat May 18 00:46:08 2002
+++ b/arch/ia64/kernel/smpboot.c	Sat May 18 00:46:08 2002
@@ -391,14 +391,14 @@
 	return cpu_idle();
 }
 
-static int __init
+static struct task_struct * __init
 fork_by_hand (void)
 {
 	/*
 	 * don't care about the eip and regs settings since
 	 * we'll never reschedule the forked task.
 	 */
-	return do_fork(CLONE_VM|CLONE_PID, 0, 0, 0);
+	return do_fork(CLONE_VM|CLONE_IDLETASK, 0, 0, 0);
 }
 
 static void __init
@@ -412,17 +412,14 @@
 	 * We can't use kernel_thread since we must avoid to
 	 * reschedule the child.
 	 */
-	if (fork_by_hand() < 0)
+	idle = fork_by_hand();
+	if (IS_ERR(idle))
 		panic("failed fork for CPU %d", cpu);
 
 	/*
 	 * We remove it from the pidhash and the runqueue
 	 * once we got the process:
 	 */
-	idle = prev_task(&init_task);
-	if (!idle)
-		panic("No idle process for CPU %d", cpu);
-
 	init_idle(idle, cpu);
 
 	ia64_cpu_to_sapicid[cpu] = sapicid;
diff -Nru a/arch/ia64/vmlinux.lds.S b/arch/ia64/vmlinux.lds.S
--- a/arch/ia64/vmlinux.lds.S	Sat May 18 00:46:08 2002
+++ b/arch/ia64/vmlinux.lds.S	Sat May 18 00:46:08 2002
@@ -7,6 +7,7 @@
 OUTPUT_FORMAT("elf64-ia64-little")
 OUTPUT_ARCH(ia64)
 ENTRY(phys_start)
+jiffies = jiffies_64; 
 SECTIONS
 {
   /* Sections to be discarded */
diff -Nru a/arch/m68k/fpsp040/Makefile b/arch/m68k/fpsp040/Makefile
--- a/arch/m68k/fpsp040/Makefile	Sat May 18 00:46:15 2002
+++ b/arch/m68k/fpsp040/Makefile	Sat May 18 00:46:15 2002
@@ -7,8 +7,9 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-.S.o:
-	$(CC) $(AFLAGS) -traditional -c -o $*.o $<
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := -traditional
 
 OS_TARGET := fpsp.o
 
diff -Nru a/arch/m68k/ifpsp060/Makefile b/arch/m68k/ifpsp060/Makefile
--- a/arch/m68k/ifpsp060/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/m68k/ifpsp060/Makefile	Sat May 18 00:46:08 2002
@@ -4,8 +4,9 @@
 # License.  See the file "README.legal" in the main directory of this archive
 # for more details.
 
-.S.o:
-	$(CC) $(AFLAGS) -traditional -c -o $*.o $<
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := -traditional
 
 OS_TARGET := ifpsp.o
 
diff -Nru a/arch/m68k/kernel/Makefile b/arch/m68k/kernel/Makefile
--- a/arch/m68k/kernel/Makefile	Sat May 18 00:46:14 2002
+++ b/arch/m68k/kernel/Makefile	Sat May 18 00:46:14 2002
@@ -7,8 +7,9 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-.S.o:
-	$(CC) $(AFLAGS) -traditional -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := -traditional
 
 ifndef CONFIG_SUN3
 all: head.o kernel.o
diff -Nru a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c
--- a/arch/m68k/kernel/process.c	Sat May 18 00:46:10 2002
+++ b/arch/m68k/kernel/process.c	Sat May 18 00:46:10 2002
@@ -177,25 +177,31 @@
 
 asmlinkage int m68k_fork(struct pt_regs *regs)
 {
-	return do_fork(SIGCHLD, rdusp(), regs, 0);
+	struct task_struct *p;
+	p = do_fork(SIGCHLD, rdusp(), regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 asmlinkage int m68k_vfork(struct pt_regs *regs)
 {
-	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0);
+	struct task_struct *p;
+	p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 asmlinkage int m68k_clone(struct pt_regs *regs)
 {
 	unsigned long clone_flags;
 	unsigned long newsp;
+	struct task_struct *p;
 
 	/* syscall2 puts clone_flags in d1 and usp in d2 */
 	clone_flags = regs->d1;
 	newsp = regs->d2;
 	if (!newsp)
 		newsp = rdusp();
-	return do_fork(clone_flags, newsp, regs, 0);
+	p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
diff -Nru a/arch/m68k/lib/Makefile b/arch/m68k/lib/Makefile
--- a/arch/m68k/lib/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/m68k/lib/Makefile	Sat May 18 00:46:08 2002
@@ -2,10 +2,11 @@
 # Makefile for m68k-specific library files..
 #
 
-.S.o:
-	$(CC) $(AFLAGS) -traditional -c $< -o $@
+USE_STANDARD_AS_RULE := true
 
 L_TARGET = lib.a
+
+EXTRA_AFLAGS := -traditional
 
 obj-y		:= ashldi3.o ashrdi3.o lshrdi3.o muldi3.o \
 			checksum.o memcmp.o memcpy.o memset.o semaphore.o
diff -Nru a/arch/m68k/math-emu/Makefile b/arch/m68k/math-emu/Makefile
--- a/arch/m68k/math-emu/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/m68k/math-emu/Makefile	Sat May 18 00:46:08 2002
@@ -7,10 +7,12 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-.S.o:
-	$(CC) $(EXTRA_CFLAGS) $(AFLAGS) -traditional -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
 
-#EXTRA_CFLAGS=-DFPU_EMU_DEBUG
+EXTRA_AFLAGS := -traditional
+
+#EXTRA_AFLAGS += -DFPU_EMU_DEBUG
+#EXTRA_CFLAGS += -DFPU_EMU_DEBUG
 
 O_TARGET := mathemu.o
 
diff -Nru a/arch/m68k/sun3/Makefile b/arch/m68k/sun3/Makefile
--- a/arch/m68k/sun3/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/m68k/sun3/Makefile	Sat May 18 00:46:08 2002
@@ -1,4 +1,4 @@
-#
+q#
 # Makefile for Linux arch/m68k/sun3 source directory
 #
 # Note! Dependencies are done automagically by 'make dep', which also
@@ -6,9 +6,6 @@
 # unless it's something special (ie not a .c file).
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
-
-.S.o:
-	$(CC) $(AFLAGS) -traditional -Wa,-m68020 -c $< -o $*.o
 
 O_TARGET := sun3.o 
 
diff -Nru a/arch/m68k/vmlinux-sun3.lds b/arch/m68k/vmlinux-sun3.lds
--- a/arch/m68k/vmlinux-sun3.lds	Sat May 18 00:46:08 2002
+++ b/arch/m68k/vmlinux-sun3.lds	Sat May 18 00:46:08 2002
@@ -2,6 +2,7 @@
 OUTPUT_FORMAT("elf32-m68k", "elf32-m68k", "elf32-m68k")
 OUTPUT_ARCH(m68k)
 ENTRY(_start)
+jiffies = jiffies_64 + 4;
 SECTIONS
 {
   . = 0xE004000;
diff -Nru a/arch/m68k/vmlinux.lds b/arch/m68k/vmlinux.lds
--- a/arch/m68k/vmlinux.lds	Sat May 18 00:46:14 2002
+++ b/arch/m68k/vmlinux.lds	Sat May 18 00:46:14 2002
@@ -2,6 +2,7 @@
 OUTPUT_FORMAT("elf32-m68k", "elf32-m68k", "elf32-m68k")
 OUTPUT_ARCH(m68k)
 ENTRY(_start)
+jiffies = jiffies_64 + 4;
 SECTIONS
 {
   . = 0x1000;
diff -Nru a/arch/mips/au1000/common/Makefile b/arch/mips/au1000/common/Makefile
--- a/arch/mips/au1000/common/Makefile	Sat May 18 00:46:15 2002
+++ b/arch/mips/au1000/common/Makefile	Sat May 18 00:46:15 2002
@@ -10,10 +10,9 @@
 # unless it's something special (ie not a .c file).
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 all: au1000.o
 
diff -Nru a/arch/mips/au1000/pb1000/Makefile b/arch/mips/au1000/pb1000/Makefile
--- a/arch/mips/au1000/pb1000/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/mips/au1000/pb1000/Makefile	Sat May 18 00:46:08 2002
@@ -10,11 +10,6 @@
 # unless it's something special (ie not a .c file).
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
-
 all: pb1000.o
 
 O_TARGET := pb1000.o
diff -Nru a/arch/mips/boot/Makefile b/arch/mips/boot/Makefile
--- a/arch/mips/boot/Makefile	Sat May 18 00:46:14 2002
+++ b/arch/mips/boot/Makefile	Sat May 18 00:46:14 2002
@@ -6,11 +6,6 @@
 # Copyright (C) 1995, 1998, 2001 by Ralf Baechle
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
-
 OBJS  = milo.o a.out.o
 
 #
diff -Nru a/arch/mips/ddb5074/Makefile b/arch/mips/ddb5074/Makefile
--- a/arch/mips/ddb5074/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/mips/ddb5074/Makefile	Sat May 18 00:46:08 2002
@@ -9,10 +9,9 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 O_TARGET = ddb5074.a
 
diff -Nru a/arch/mips/ddb5476/Makefile b/arch/mips/ddb5476/Makefile
--- a/arch/mips/ddb5476/Makefile	Sat May 18 00:46:14 2002
+++ b/arch/mips/ddb5476/Makefile	Sat May 18 00:46:14 2002
@@ -9,10 +9,9 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 O_TARGET = ddb5476.a
 
diff -Nru a/arch/mips/ddb5xxx/common/Makefile b/arch/mips/ddb5xxx/common/Makefile
--- a/arch/mips/ddb5xxx/common/Makefile	Sat May 18 00:46:09 2002
+++ b/arch/mips/ddb5xxx/common/Makefile	Sat May 18 00:46:09 2002
@@ -6,11 +6,6 @@
 # unless it's something special (ie not a .c file).
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
-
 O_TARGET:= ddb5xxx.o
 
 obj-y	 += irq.o irq_cpu.o nile4.o prom.o pci.o pci_auto.o rtc_ds1386.o
diff -Nru a/arch/mips/ddb5xxx/ddb5477/Makefile b/arch/mips/ddb5xxx/ddb5477/Makefile
--- a/arch/mips/ddb5xxx/ddb5477/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/mips/ddb5xxx/ddb5477/Makefile	Sat May 18 00:46:08 2002
@@ -6,10 +6,9 @@
 # unless it's something special (ie not a .c file).
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 O_TARGET:= ddb5477.o
 
diff -Nru a/arch/mips/dec/Makefile b/arch/mips/dec/Makefile
--- a/arch/mips/dec/Makefile	Sat May 18 00:46:09 2002
+++ b/arch/mips/dec/Makefile	Sat May 18 00:46:09 2002
@@ -6,10 +6,7 @@
 # unless it's something special (ie not a .c file).
 #
 
-.S.s:
-	$(CPP) $(AFLAGS) $< -o $@
-.S.o:
-	$(CC) $(AFLAGS) -c $< -o $@
+USE_STANDARD_AS_RULE := true
 
 O_TARGET := dec.o
 
diff -Nru a/arch/mips/dec/boot/Makefile b/arch/mips/dec/boot/Makefile
--- a/arch/mips/dec/boot/Makefile	Sat May 18 00:46:13 2002
+++ b/arch/mips/dec/boot/Makefile	Sat May 18 00:46:13 2002
@@ -6,11 +6,6 @@
 # unless it's something special (ie not a .c file).
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
-
 netboot: all
 	mipsel-linux-ld -N -G 0 -T ld.ecoff ../../boot/zImage \
 		dec_boot.o ramdisk.img -o nbImage
diff -Nru a/arch/mips/dec/prom/Makefile b/arch/mips/dec/prom/Makefile
--- a/arch/mips/dec/prom/Makefile	Sat May 18 00:46:09 2002
+++ b/arch/mips/dec/prom/Makefile	Sat May 18 00:46:09 2002
@@ -8,10 +8,9 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 OBJS  = init.o memory.o cmdline.o identify.o locore.o
 
diff -Nru a/arch/mips/gt64120/common/Makefile b/arch/mips/gt64120/common/Makefile
--- a/arch/mips/gt64120/common/Makefile	Sat May 18 00:46:09 2002
+++ b/arch/mips/gt64120/common/Makefile	Sat May 18 00:46:09 2002
@@ -6,11 +6,6 @@
 # unless it's something special (ie not a .c file).
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
-
 O_TARGET:= gt64120.o
 
 obj-y	 := gt_irq.o pci.o
diff -Nru a/arch/mips/gt64120/momenco_ocelot/Makefile b/arch/mips/gt64120/momenco_ocelot/Makefile
--- a/arch/mips/gt64120/momenco_ocelot/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/mips/gt64120/momenco_ocelot/Makefile	Sat May 18 00:46:08 2002
@@ -6,10 +6,9 @@
 # unless it's something special (ie not a .c file).
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 O_TARGET:= momenco_ocelot.o
 
diff -Nru a/arch/mips/ite-boards/generic/Makefile b/arch/mips/ite-boards/generic/Makefile
--- a/arch/mips/ite-boards/generic/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/mips/ite-boards/generic/Makefile	Sat May 18 00:46:08 2002
@@ -10,10 +10,9 @@
 # unless it's something special (ie not a .c file).
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 all: it8172.o
 
diff -Nru a/arch/mips/ite-boards/ivr/Makefile b/arch/mips/ite-boards/ivr/Makefile
--- a/arch/mips/ite-boards/ivr/Makefile	Sat May 18 00:46:09 2002
+++ b/arch/mips/ite-boards/ivr/Makefile	Sat May 18 00:46:09 2002
@@ -11,11 +11,6 @@
 # unless it's something special (ie not a .c file).
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
-
 all: ivr.o
 
 O_TARGET := ivr.o
diff -Nru a/arch/mips/ite-boards/qed-4n-s01b/Makefile b/arch/mips/ite-boards/qed-4n-s01b/Makefile
--- a/arch/mips/ite-boards/qed-4n-s01b/Makefile	Sat May 18 00:46:14 2002
+++ b/arch/mips/ite-boards/qed-4n-s01b/Makefile	Sat May 18 00:46:14 2002
@@ -11,11 +11,6 @@
 # unless it's something special (ie not a .c file).
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
-
 all: ite.o
 
 O_TARGET := ite.o
diff -Nru a/arch/mips/jazz/Makefile b/arch/mips/jazz/Makefile
--- a/arch/mips/jazz/Makefile	Sat May 18 00:46:09 2002
+++ b/arch/mips/jazz/Makefile	Sat May 18 00:46:09 2002
@@ -6,10 +6,9 @@
 # unless it's something special (ie not a .c file).
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $@
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $@
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 all: jazz.o
 
diff -Nru a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
--- a/arch/mips/kernel/Makefile	Sat May 18 00:46:09 2002
+++ b/arch/mips/kernel/Makefile	Sat May 18 00:46:09 2002
@@ -6,12 +6,9 @@
 # unless it's something special (ie not a .c file).
 #
 
-.S.s:
-	$(CPP) $(AFLAGS) $< -o $@
-.S.o:
-	$(CC) $(AFLAGS) -c $< -o $@
+USE_STANDARD_AS_RULE := true
 
-EXTRA_AFLAGS = -mips3 -mcpu=r4000
+# EXTRA_AFLAGS = -mips3 -mcpu=r4000 # not used?
 
 all:	kernel.o head.o init_task.o
 
diff -Nru a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c
--- a/arch/mips/kernel/smp.c	Sat May 18 00:46:08 2002
+++ b/arch/mips/kernel/smp.c	Sat May 18 00:46:08 2002
@@ -122,8 +122,7 @@
 
 		/* Spawn a new process normally.  Grab a pointer to
 		   its task struct so we can mess with it */
-		do_fork(CLONE_VM|CLONE_PID, 0, &regs, 0);
-		p = prev_task(&init_task);
+		p = do_fork(CLONE_VM|CLONE_IDLETASK, 0, &regs, 0);
 
 		/* Schedule the first task manually */
 		p->processor = i;
@@ -151,7 +150,7 @@
 		 * The following code is purely to make sure
 		 * Linux can schedule processes on this slave.
 		 */
-		kernel_thread(0, NULL, CLONE_PID);
+		kernel_thread(0, NULL, CLONE_IDLETASK);
 		p = prev_task(&init_task);
 		sprintf(p->comm, "%s%d", "Idle", i);
 		init_tasks[i] = p;
diff -Nru a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c
--- a/arch/mips/kernel/syscall.c	Sat May 18 00:46:08 2002
+++ b/arch/mips/kernel/syscall.c	Sat May 18 00:46:08 2002
@@ -95,10 +95,10 @@
 save_static_function(sys_fork);
 static_unused int _sys_fork(struct pt_regs regs)
 {
-	int res;
+	struct task_struct *p;
 
-	res = do_fork(SIGCHLD, regs.regs[29], &regs, 0);
-	return res;
+	p = do_fork(SIGCHLD, regs.regs[29], &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 
@@ -107,14 +107,14 @@
 {
 	unsigned long clone_flags;
 	unsigned long newsp;
-	int res;
+	struct task_struct *p;
 
 	clone_flags = regs.regs[4];
 	newsp = regs.regs[5];
 	if (!newsp)
 		newsp = regs.regs[29];
-	res = do_fork(clone_flags, newsp, &regs, 0);
-	return res;
+	p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /*
diff -Nru a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile
--- a/arch/mips/lib/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/mips/lib/Makefile	Sat May 18 00:46:08 2002
@@ -2,12 +2,11 @@
 # Makefile for MIPS-specific library files..
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
+USE_STANDARD_AS_RULE := true;
 
 L_TARGET = lib.a
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 obj-y				+= csum_partial.o csum_partial_copy.o \
 				   rtc-std.o rtc-no.o memcpy.o memset.o \
diff -Nru a/arch/mips/math-emu/Makefile b/arch/mips/math-emu/Makefile
--- a/arch/mips/math-emu/Makefile	Sat May 18 00:46:14 2002
+++ b/arch/mips/math-emu/Makefile	Sat May 18 00:46:14 2002
@@ -6,11 +6,6 @@
 # unless it's something special (ie not a .c file).
 #
 
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
-
-EXTRA_ASFLAGS = -mips2 -mcpu=r4000
-
 O_TARGET:= fpu_emulator.o
 
 obj-y	:= cp1emu.o ieee754m.o ieee754d.o ieee754dp.o ieee754sp.o ieee754.o \
diff -Nru a/arch/mips/mips-boards/atlas/Makefile b/arch/mips/mips-boards/atlas/Makefile
--- a/arch/mips/mips-boards/atlas/Makefile	Sat May 18 00:46:15 2002
+++ b/arch/mips/mips-boards/atlas/Makefile	Sat May 18 00:46:15 2002
@@ -28,11 +28,6 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
-
 all: atlas.o
 
 O_TARGET := atlas.o
diff -Nru a/arch/mips/mips-boards/generic/Makefile b/arch/mips/mips-boards/generic/Makefile
--- a/arch/mips/mips-boards/generic/Makefile	Sat May 18 00:46:14 2002
+++ b/arch/mips/mips-boards/generic/Makefile	Sat May 18 00:46:14 2002
@@ -27,10 +27,9 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 O_TARGET := mipsboards.o
 
diff -Nru a/arch/mips/mips-boards/malta/Makefile b/arch/mips/mips-boards/malta/Makefile
--- a/arch/mips/mips-boards/malta/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/mips/mips-boards/malta/Makefile	Sat May 18 00:46:08 2002
@@ -28,11 +28,6 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
-
 O_TARGET := malta.o
 
 obj-y := malta_int.o malta_rtc.o malta_setup.o
diff -Nru a/arch/mips/philips/nino/Makefile b/arch/mips/philips/nino/Makefile
--- a/arch/mips/philips/nino/Makefile	Sat May 18 00:46:09 2002
+++ b/arch/mips/philips/nino/Makefile	Sat May 18 00:46:09 2002
@@ -6,10 +6,7 @@
 # unless it's something special (ie not a .c file).
 #
 
-.S.s:
-	$(CPP) $(AFLAGS) $< -o $@
-.S.o:
-	$(CC) $(AFLAGS) -c $< -o $@
+USE_STANDARD_AS_RULE := true
 
 O_TARGET := nino.o
 
diff -Nru a/arch/mips/sgi/kernel/Makefile b/arch/mips/sgi/kernel/Makefile
--- a/arch/mips/sgi/kernel/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/mips/sgi/kernel/Makefile	Sat May 18 00:46:08 2002
@@ -8,10 +8,9 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 O_TARGET := ip22-kern.o
 
diff -Nru a/arch/mips/sni/Makefile b/arch/mips/sni/Makefile
--- a/arch/mips/sni/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/mips/sni/Makefile	Sat May 18 00:46:08 2002
@@ -6,10 +6,9 @@
 # unless it's something special (ie not a .c file).
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 all:	sni.o int-handler.o
 
diff -Nru a/arch/mips/tools/Makefile b/arch/mips/tools/Makefile
--- a/arch/mips/tools/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/mips/tools/Makefile	Sat May 18 00:46:08 2002
@@ -7,11 +7,6 @@
 #
 TARGET	:= $(TOPDIR)/include/asm-$(ARCH)/offset.h
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
-
 $(TARGET): offset.h
 	cmp -s $^ $@ || (cp $^ $(TARGET).new && mv $(TARGET).new $(TARGET))
 
@@ -22,7 +17,7 @@
 
 clean:
 	rm -f offset.[hs] $(TARGET).new
-	
+
 mrproper:	
 	rm -f offset.[hs] $(TARGET).new
 	rm -f $(TARGET)
diff -Nru a/arch/mips64/boot/Makefile b/arch/mips64/boot/Makefile
--- a/arch/mips64/boot/Makefile	Sat May 18 00:46:15 2002
+++ b/arch/mips64/boot/Makefile	Sat May 18 00:46:15 2002
@@ -6,11 +6,6 @@
 # Copyright (C) 1995, 1998, 1999 by Ralf Baechle
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
-
 #
 # Some DECstations need all possible sections of an ECOFF executable
 #
diff -Nru a/arch/mips64/kernel/Makefile b/arch/mips64/kernel/Makefile
--- a/arch/mips64/kernel/Makefile	Sat May 18 00:46:09 2002
+++ b/arch/mips64/kernel/Makefile	Sat May 18 00:46:09 2002
@@ -6,8 +6,9 @@
 # unless it's something special (ie not a .c file).
 #
 
-.S.o:
-	$(CC) $(CFLAGS) $(CFLAGS_$@) -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 all:	kernel.o head.o init_task.o
 
@@ -25,7 +26,7 @@
 obj-$(CONFIG_BINFMT_ELF32)	+= binfmt_elf32.o
 obj-$(CONFIG_SMP)		+= smp.o
 
-CFLAGS_r4k_genex.o := -P
-CFLAGS_r4k_tlb_glue.o := -P
+AFLAGS_r4k_genex.o := -P
+AFLAGS_r4k_tlb_glue.o := -P
 
 include $(TOPDIR)/Rules.make
diff -Nru a/arch/mips64/kernel/syscall.c b/arch/mips64/kernel/syscall.c
--- a/arch/mips64/kernel/syscall.c	Sat May 18 00:46:14 2002
+++ b/arch/mips64/kernel/syscall.c	Sat May 18 00:46:14 2002
@@ -77,26 +77,26 @@
 
 asmlinkage int sys_fork(abi64_no_regargs, struct pt_regs regs)
 {
-	int res;
+	struct task_struct *p;
 
 	save_static(&regs);
-	res = do_fork(SIGCHLD, regs.regs[29], &regs, 0);
-	return res;
+	p = do_fork(SIGCHLD, regs.regs[29], &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 asmlinkage int sys_clone(abi64_no_regargs, struct pt_regs regs)
 {
 	unsigned long clone_flags;
 	unsigned long newsp;
-	int res;
+	struct task_struct *p;
 
 	save_static(&regs);
 	clone_flags = regs.regs[4];
 	newsp = regs.regs[5];
 	if (!newsp)
 		newsp = regs.regs[29];
-	res = do_fork(clone_flags, newsp, &regs, 0);
-	return res;
+	p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /*
diff -Nru a/arch/mips64/ld.script.elf64 b/arch/mips64/ld.script.elf64
--- a/arch/mips64/ld.script.elf64	Sat May 18 00:46:08 2002
+++ b/arch/mips64/ld.script.elf64	Sat May 18 00:46:08 2002
@@ -1,5 +1,6 @@
 OUTPUT_ARCH(mips)
 ENTRY(kernel_entry)
+jiffies = jiffies_64;
 SECTIONS
 {
   /* Read-only sections, merged into text segment: */
diff -Nru a/arch/mips64/lib/Makefile b/arch/mips64/lib/Makefile
--- a/arch/mips64/lib/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/mips64/lib/Makefile	Sat May 18 00:46:08 2002
@@ -2,10 +2,9 @@
 # Makefile for MIPS-specific library files..
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 L_TARGET = lib.a
 
diff -Nru a/arch/mips64/math-emu/Makefile b/arch/mips64/math-emu/Makefile
--- a/arch/mips64/math-emu/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/mips64/math-emu/Makefile	Sat May 18 00:46:08 2002
@@ -6,11 +6,6 @@
 # unless it's something special (ie not a .c file).
 #
 
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
-
-EXTRA_ASFLAGS = -mips2 -mcpu=r4000
-
 O_TARGET:= fpu_emulator.o
 
 obj-y	:= cp1emu.o ieee754m.o ieee754d.o ieee754dp.o ieee754sp.o ieee754.o \
diff -Nru a/arch/mips64/mips-boards/atlas/Makefile b/arch/mips64/mips-boards/atlas/Makefile
--- a/arch/mips64/mips-boards/atlas/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/mips64/mips-boards/atlas/Makefile	Sat May 18 00:46:08 2002
@@ -28,11 +28,6 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
-
 all: atlas.o
 
 O_TARGET := atlas.o
diff -Nru a/arch/mips64/mips-boards/generic/Makefile b/arch/mips64/mips-boards/generic/Makefile
--- a/arch/mips64/mips-boards/generic/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/mips64/mips-boards/generic/Makefile	Sat May 18 00:46:08 2002
@@ -27,10 +27,9 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 all: mipsboards.o
 
diff -Nru a/arch/mips64/mips-boards/malta/Makefile b/arch/mips64/mips-boards/malta/Makefile
--- a/arch/mips64/mips-boards/malta/Makefile	Sat May 18 00:46:09 2002
+++ b/arch/mips64/mips-boards/malta/Makefile	Sat May 18 00:46:09 2002
@@ -28,11 +28,6 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
-
 all: malta.o
 
 O_TARGET := malta.o
diff -Nru a/arch/mips64/sgi-ip22/Makefile b/arch/mips64/sgi-ip22/Makefile
--- a/arch/mips64/sgi-ip22/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/mips64/sgi-ip22/Makefile	Sat May 18 00:46:08 2002
@@ -3,10 +3,9 @@
 # under Linux.
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 L_TARGET = ip22.a
 
diff -Nru a/arch/mips64/sgi-ip27/Makefile b/arch/mips64/sgi-ip27/Makefile
--- a/arch/mips64/sgi-ip27/Makefile	Sat May 18 00:46:09 2002
+++ b/arch/mips64/sgi-ip27/Makefile	Sat May 18 00:46:09 2002
@@ -2,10 +2,9 @@
 # Makefile for the IP27 specific kernel interface routines under Linux.
 #
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 O_TARGET = ip27.o
 
diff -Nru a/arch/mips64/sgi-ip27/ip27-init.c b/arch/mips64/sgi-ip27/ip27-init.c
--- a/arch/mips64/sgi-ip27/ip27-init.c	Sat May 18 00:46:15 2002
+++ b/arch/mips64/sgi-ip27/ip27-init.c	Sat May 18 00:46:15 2002
@@ -490,7 +490,7 @@
 			 * The following code is purely to make sure
 			 * Linux can schedule processes on this slave.
 			 */
-			kernel_thread(0, NULL, CLONE_PID);
+			kernel_thread(0, NULL, CLONE_IDLETASK);
 			p = prev_task(&init_task);
 			sprintf(p->comm, "%s%d", "Idle", num_cpus);
 			init_tasks[num_cpus] = p;
diff -Nru a/arch/mips64/sgi-ip32/Makefile b/arch/mips64/sgi-ip32/Makefile
--- a/arch/mips64/sgi-ip32/Makefile	Sat May 18 00:46:14 2002
+++ b/arch/mips64/sgi-ip32/Makefile	Sat May 18 00:46:14 2002
@@ -8,10 +8,9 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
+
+EXTRA_AFLAGS := $(CFLAGS)
 
 O_TARGET := ip32-kern.a
 
diff -Nru a/arch/mips64/tools/Makefile b/arch/mips64/tools/Makefile
--- a/arch/mips64/tools/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/mips64/tools/Makefile	Sat May 18 00:46:08 2002
@@ -7,11 +7,6 @@
 #
 TARGET	:= $(TOPDIR)/include/asm-$(ARCH)/offset.h
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
-
 $(TARGET): offset.h
 	cmp -s $^ $@ || (cp $^ $(TARGET).new && mv $(TARGET).new $(TARGET))
 
@@ -22,7 +17,7 @@
 
 clean:
 	rm -f offset.[hs] $(TARGET).new
-	
+
 mrproper:	
 	rm -f offset.[hs] $(TARGET).new
 	rm -f $(TARGET)
diff -Nru a/arch/parisc/kernel/entry.S b/arch/parisc/kernel/entry.S
--- a/arch/parisc/kernel/entry.S	Sat May 18 00:46:08 2002
+++ b/arch/parisc/kernel/entry.S	Sat May 18 00:46:08 2002
@@ -500,7 +500,7 @@
 	ldo	CLONE_VM(%r0), %r26   /* Force CLONE_VM since only init_mm */
 	or	%r26, %r24, %r26      /* will have kernel mappings.	 */
 	copy	%r0, %r25
-	bl	do_fork, %r2
+	bl	do_fork_FIXME_NOW_RETURNS_TASK_STRUCT, %r2
 	copy	%r1, %r24
 
 	/* Parent Returns here */
diff -Nru a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c
--- a/arch/parisc/kernel/process.c	Sat May 18 00:46:15 2002
+++ b/arch/parisc/kernel/process.c	Sat May 18 00:46:15 2002
@@ -159,14 +159,17 @@
 sys_clone(unsigned long clone_flags, unsigned long usp,
 	  struct pt_regs *regs)
 {
-	return do_fork(clone_flags, usp, regs, 0);
+	struct task_struct *p;
+	p = do_fork(clone_flags & ~CLONE_IDLETASK, usp, regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 int
 sys_vfork(struct pt_regs *regs)
 {
-	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD,
-		       regs->gr[30], regs, 0);
+	struct task_struct *p;
+	p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gr[30], regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 int
diff -Nru a/arch/parisc/lib/Makefile b/arch/parisc/lib/Makefile
--- a/arch/parisc/lib/Makefile	Sat May 18 00:46:13 2002
+++ b/arch/parisc/lib/Makefile	Sat May 18 00:46:13 2002
@@ -2,12 +2,12 @@
 # Makefile for parisc-specific library files..
 #
 
+USE_STANDARD_AS_RULE := true
 
 L_TARGET = lib.a
-L_OBJS	= lusercopy.o bitops.o checksum.o
 
+obj-y := lusercopy.o bitops.o checksum.o
 
-.S.o:
-	$(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o
+EXTRA_AFLAGS := -traditional
 
 include $(TOPDIR)/Rules.make
diff -Nru a/arch/parisc/tools/Makefile b/arch/parisc/tools/Makefile
--- a/arch/parisc/tools/Makefile	Sat May 18 00:46:15 2002
+++ b/arch/parisc/tools/Makefile	Sat May 18 00:46:15 2002
@@ -7,11 +7,6 @@
 #
 TARGET	:= $(TOPDIR)/include/asm-$(ARCH)/offset.h
 
-.S.s:
-	$(CPP) $(CFLAGS) $< -o $*.s
-.S.o:
-	$(CC) $(CFLAGS) -c $< -o $*.o
-
 all: $(TARGET)
 
 $(TARGET): offset.h
diff -Nru a/arch/parisc/vmlinux.lds b/arch/parisc/vmlinux.lds
--- a/arch/parisc/vmlinux.lds	Sat May 18 00:46:08 2002
+++ b/arch/parisc/vmlinux.lds	Sat May 18 00:46:08 2002
@@ -2,6 +2,7 @@
 OUTPUT_FORMAT("elf32-hppa")
 OUTPUT_ARCH(hppa)
 ENTRY(_stext)
+jiffies = jiffies_64 + 4;
 SECTIONS
 {
 
diff -Nru a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c
--- a/arch/ppc/kernel/process.c	Sat May 18 00:46:09 2002
+++ b/arch/ppc/kernel/process.c	Sat May 18 00:46:09 2002
@@ -437,22 +437,28 @@
 int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6,
 	      struct pt_regs *regs)
 {
+ 	struct task_struct *p;
 	CHECK_FULL_REGS(regs);
-	return do_fork(p1, regs->gpr[1], regs, 0);
+ 	p = do_fork(p1 & ~CLONE_IDLETASK, regs->gpr[1], regs, 0);
+ 	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6,
 	     struct pt_regs *regs)
 {
+	struct task_struct *p;
 	CHECK_FULL_REGS(regs);
-	return do_fork(SIGCHLD, regs->gpr[1], regs, 0);
+	p = do_fork(SIGCHLD, regs->gpr[1], regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 int sys_vfork(int p1, int p2, int p3, int p4, int p5, int p6,
 	      struct pt_regs *regs)
 {
+	struct task_struct *p;
 	CHECK_FULL_REGS(regs);
-	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gpr[1], regs, 0);
+	p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gpr[1], regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2,
diff -Nru a/arch/ppc/kernel/smp.c b/arch/ppc/kernel/smp.c
--- a/arch/ppc/kernel/smp.c	Sat May 18 00:46:15 2002
+++ b/arch/ppc/kernel/smp.c	Sat May 18 00:46:15 2002
@@ -343,11 +343,9 @@
 		/* create a process for the processor */
 		/* only regs.msr is actually used, and 0 is OK for it */
 		memset(&regs, 0, sizeof(struct pt_regs));
-		if (do_fork(CLONE_VM|CLONE_PID, 0, &regs, 0) < 0)
+		p = do_fork(CLONE_VM|CLONE_IDLETASK, 0, &regs, 0);
+		if (IS_ERR(p))
 			panic("failed fork for CPU %d", i);
-		p = prev_task(&init_task);
-		if (!p)
-			panic("No idle task for CPU %d", i);
 		init_idle(p, i);
 		unhash_process(p);
 
diff -Nru a/arch/ppc/vmlinux.lds b/arch/ppc/vmlinux.lds
--- a/arch/ppc/vmlinux.lds	Sat May 18 00:46:10 2002
+++ b/arch/ppc/vmlinux.lds	Sat May 18 00:46:10 2002
@@ -2,6 +2,7 @@
 SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/local/powerpc-any-elf/lib);
 /* Do we need any of these for elf?
    __DYNAMIC = 0;    */
+jiffies = jiffies_64 + 4;
 SECTIONS
 {
   /* Read-only sections, merged into text segment: */
diff -Nru a/arch/ppc64/kernel/process.c b/arch/ppc64/kernel/process.c
--- a/arch/ppc64/kernel/process.c	Sat May 18 00:46:08 2002
+++ b/arch/ppc64/kernel/process.c	Sat May 18 00:46:08 2002
@@ -256,19 +256,25 @@
 int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6,
 	      struct pt_regs *regs)
 {
-	return do_fork(p1, regs->gpr[1], regs, 0);
+	struct task_struct *p;
+	p = do_fork(p1 & ~CLONE_IDLETASK, regs->gpr[1], regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6,
 	     struct pt_regs *regs)
 {
-	return do_fork(SIGCHLD, regs->gpr[1], regs, 0);
+	struct task_struct *p;
+	p = do_fork(SIGCHLD, regs->gpr[1], regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 int sys_vfork(int p1, int p2, int p3, int p4, int p5, int p6,
 			 struct pt_regs *regs)
 {
-	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gpr[1], regs, 0);
+	struct task_struct *p;
+	p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gpr[1], regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2,
diff -Nru a/arch/ppc64/kernel/smp.c b/arch/ppc64/kernel/smp.c
--- a/arch/ppc64/kernel/smp.c	Sat May 18 00:46:14 2002
+++ b/arch/ppc64/kernel/smp.c	Sat May 18 00:46:14 2002
@@ -640,11 +640,9 @@
 
 		memset(&regs, 0, sizeof(struct pt_regs));
 
-		if (do_fork(CLONE_VM|CLONE_PID, 0, &regs, 0) < 0)
+		p = do_fork(CLONE_VM|CLONE_IDLETASK, 0, &regs, 0);
+		if (IS_ERR(p))
 			panic("failed fork for CPU %d", i);
-		p = prev_task(&init_task);
-		if (!p)
-			panic("No idle task for CPU %d", i);
 
 		init_idle(p, i);
 
diff -Nru a/arch/ppc64/vmlinux.lds b/arch/ppc64/vmlinux.lds
--- a/arch/ppc64/vmlinux.lds	Sat May 18 00:46:08 2002
+++ b/arch/ppc64/vmlinux.lds	Sat May 18 00:46:08 2002
@@ -2,6 +2,7 @@
 SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/local/powerpc-any-elf/lib);
 /* Do we need any of these for elf?
    __DYNAMIC = 0;    */
+jiffies = jiffies_64;
 SECTIONS
 {
   /* Read-only sections, merged into text segment: */
diff -Nru a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile
--- a/arch/s390/boot/Makefile	Sat May 18 00:46:10 2002
+++ b/arch/s390/boot/Makefile	Sat May 18 00:46:10 2002
@@ -7,14 +7,15 @@
 #
 # Note 2! The CFLAGS definition is now in the main makefile...
 
+USE_STANDARD_AS_RULE := true
+
 OBJCOPY = $(CROSS_COMPILE)objcopy
 
 O_TARGET := 
 
-include $(TOPDIR)/Rules.make
+EXTRA_AFLAGS := -traditional
 
-.S.o:
-	$(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o
+include $(TOPDIR)/Rules.make
 
 %.lnk: %.o
 	$(LD) -Ttext 0x0 -o $@ $<
diff -Nru a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile
--- a/arch/s390/kernel/Makefile	Sat May 18 00:46:15 2002
+++ b/arch/s390/kernel/Makefile	Sat May 18 00:46:15 2002
@@ -7,12 +7,13 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-.S.o:
-	$(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
 
 all: kernel.o head.o init_task.o
 
 O_TARGET := kernel.o
+
+EXTRA_AFLAGS := -traditional
 
 export-objs	:= debug.o ebcdic.o irq.o s390_ext.o smp.o s390_ksyms.o
 obj-y	:= entry.o bitmap.o traps.o time.o process.o irq.o \
diff -Nru a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c
--- a/arch/s390/kernel/process.c	Sat May 18 00:46:14 2002
+++ b/arch/s390/kernel/process.c	Sat May 18 00:46:14 2002
@@ -332,19 +332,23 @@
 
 asmlinkage int sys_fork(struct pt_regs regs)
 {
-        return do_fork(SIGCHLD, regs.gprs[15], &regs, 0);
+	struct task_struct *p;
+        p = do_fork(SIGCHLD, regs.gprs[15], &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 asmlinkage int sys_clone(struct pt_regs regs)
 {
         unsigned long clone_flags;
         unsigned long newsp;
+	struct task_struct *p;
 
         clone_flags = regs.gprs[3];
         newsp = regs.orig_gpr2;
         if (!newsp)
                 newsp = regs.gprs[15];
-        return do_fork(clone_flags, newsp, &regs, 0);
+        p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /*
@@ -359,8 +363,9 @@
  */
 asmlinkage int sys_vfork(struct pt_regs regs)
 {
-	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD,
-                       regs.gprs[15], &regs, 0);
+	struct task_struct *p;
+	p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.gprs[15], &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /*
diff -Nru a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
--- a/arch/s390/kernel/smp.c	Sat May 18 00:46:08 2002
+++ b/arch/s390/kernel/smp.c	Sat May 18 00:46:08 2002
@@ -505,13 +505,13 @@
 {
 }
 
-static int __init fork_by_hand(void)
+static struct task_struct *__init fork_by_hand(void)
 {
        struct pt_regs regs;
        /* don't care about the psw and regs settings since we'll never
           reschedule the forked task. */
        memset(&regs,0,sizeof(struct pt_regs));
-       return do_fork(CLONE_VM|CLONE_PID, 0, &regs, 0);
+       return do_fork(CLONE_VM|CLONE_IDLETASK, 0, &regs, 0);
 }
 
 static void __init do_boot_cpu(int cpu)
@@ -521,16 +521,14 @@
 
         /* We can't use kernel_thread since we must _avoid_ to reschedule
            the child. */
-        if (fork_by_hand() < 0)
+        idle = fork_by_hand();
+	if (IS_ERR(idle))
                 panic("failed fork for CPU %d", cpu);
 
         /*
          * We remove it from the pidhash and the runqueue
          * once we got the process:
          */
-        idle = prev_task(&init_task);
-        if (!idle)
-                panic("No idle process for CPU %d",cpu);
         idle->processor = cpu;
 	idle->cpus_runnable = 1 << cpu; /* we schedule the first task manually */
 
diff -Nru a/arch/s390/lib/Makefile b/arch/s390/lib/Makefile
--- a/arch/s390/lib/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/s390/lib/Makefile	Sat May 18 00:46:08 2002
@@ -2,15 +2,11 @@
 # Makefile for s390-specific library files..
 #
 
-ifdef SMP
-.S.o:
-	$(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o
-else
-.S.o:
-	$(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
-endif
+USE_STANDARD_AS_RULE := true
 
 L_TARGET = lib.a
+
+EXTRA_AFLAGS := -traditional
 
 obj-y = checksum.o delay.o memset.o misaligned.o strcmp.o strncpy.o uaccess.o
 export-objs += misaligned.o
diff -Nru a/arch/s390/vmlinux-shared.lds b/arch/s390/vmlinux-shared.lds
--- a/arch/s390/vmlinux-shared.lds	Sat May 18 00:46:15 2002
+++ b/arch/s390/vmlinux-shared.lds	Sat May 18 00:46:15 2002
@@ -4,6 +4,7 @@
 OUTPUT_FORMAT("elf32-s390", "elf32-s390", "elf32-s390")
 OUTPUT_ARCH(s390)
 ENTRY(_start)
+jiffies = jiffies_64 + 4;
 SECTIONS
 {
   . = 0x00000000;
diff -Nru a/arch/s390/vmlinux.lds b/arch/s390/vmlinux.lds
--- a/arch/s390/vmlinux.lds	Sat May 18 00:46:08 2002
+++ b/arch/s390/vmlinux.lds	Sat May 18 00:46:08 2002
@@ -4,6 +4,7 @@
 OUTPUT_FORMAT("elf32-s390", "elf32-s390", "elf32-s390")
 OUTPUT_ARCH(s390)
 ENTRY(_start)
+jiffies = jiffies_64 + 4;
 SECTIONS
 {
   . = 0x00000000;
diff -Nru a/arch/s390x/boot/Makefile b/arch/s390x/boot/Makefile
--- a/arch/s390x/boot/Makefile	Sat May 18 00:46:10 2002
+++ b/arch/s390x/boot/Makefile	Sat May 18 00:46:10 2002
@@ -7,14 +7,15 @@
 #
 # Note 2! The CFLAGS definition is now in the main makefile...
 
+USE_STANDARD_AS_RULE := true
+
 OBJCOPY = $(CROSS_COMPILE)objcopy
 
 O_TARGET := 
 
 include $(TOPDIR)/Rules.make
 
-.S.o:
-	$(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o
+EXTRA_AFLAGS := -traditional
 
 %.lnk: %.o
 	$(LD) -Ttext 0x0 -o $@ $<
diff -Nru a/arch/s390x/kernel/Makefile b/arch/s390x/kernel/Makefile
--- a/arch/s390x/kernel/Makefile	Sat May 18 00:46:09 2002
+++ b/arch/s390x/kernel/Makefile	Sat May 18 00:46:09 2002
@@ -7,12 +7,13 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-.S.o:
-	$(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
 
 all: kernel.o head.o init_task.o
 
 O_TARGET	:= kernel.o
+
+EXTRA_AFLAGS := -traditional
 
 export-objs	:= debug.o ebcdic.o irq.o s390_ext.o smp.o s390_ksyms.o \
 		   exec32.o
diff -Nru a/arch/s390x/kernel/process.c b/arch/s390x/kernel/process.c
--- a/arch/s390x/kernel/process.c	Sat May 18 00:46:08 2002
+++ b/arch/s390x/kernel/process.c	Sat May 18 00:46:08 2002
@@ -331,19 +331,23 @@
 
 asmlinkage int sys_fork(struct pt_regs regs)
 {
-        return do_fork(SIGCHLD, regs.gprs[15], &regs, 0);
+	struct task_struct *p;
+        p = do_fork(SIGCHLD, regs.gprs[15], &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 asmlinkage int sys_clone(struct pt_regs regs)
 {
         unsigned long clone_flags;
         unsigned long newsp;
+	struct task_struct *p;
 
         clone_flags = regs.gprs[3];
         newsp = regs.orig_gpr2;
         if (!newsp)
                 newsp = regs.gprs[15];
-        return do_fork(clone_flags, newsp, &regs, 0);
+        p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /*
@@ -358,8 +362,9 @@
  */
 asmlinkage int sys_vfork(struct pt_regs regs)
 {
-	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD,
-                       regs.gprs[15], &regs, 0);
+	struct task_struct *p;
+	p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.gprs[15], &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /*
diff -Nru a/arch/s390x/kernel/smp.c b/arch/s390x/kernel/smp.c
--- a/arch/s390x/kernel/smp.c	Sat May 18 00:46:13 2002
+++ b/arch/s390x/kernel/smp.c	Sat May 18 00:46:13 2002
@@ -484,13 +484,13 @@
 {
 }
 
-static int __init fork_by_hand(void)
+static struct task_struct * __init fork_by_hand(void)
 {
        struct pt_regs regs;
        /* don't care about the psw and regs settings since we'll never
           reschedule the forked task. */
        memset(&regs,0,sizeof(struct pt_regs));
-       return do_fork(CLONE_VM|CLONE_PID, 0, &regs, 0);
+       return do_fork(CLONE_VM|CLONE_IDLETASK, 0, &regs, 0);
 }
 
 static void __init do_boot_cpu(int cpu)
@@ -500,16 +500,14 @@
 
         /* We can't use kernel_thread since we must _avoid_ to reschedule
            the child. */
-        if (fork_by_hand() < 0)
+        idle = fork_by_hand();
+	if (IS_ERR(idle))
                 panic("failed fork for CPU %d", cpu);
 
         /*
          * We remove it from the pidhash and the runqueue
          * once we got the process:
          */
-        idle = prev_task(&init_task);
-        if (!idle)
-                panic("No idle process for CPU %d",cpu);
         idle->processor = cpu;
 	idle->cpus_runnable = 1 << cpu; /* we schedule the first task manually */
 
diff -Nru a/arch/s390x/lib/Makefile b/arch/s390x/lib/Makefile
--- a/arch/s390x/lib/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/s390x/lib/Makefile	Sat May 18 00:46:08 2002
@@ -2,15 +2,11 @@
 # Makefile for s390-specific library files..
 #
 
-ifdef SMP
-.S.o:
-	$(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o
-else
-.S.o:
-	$(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
-endif
+USE_STANDARD_AS_RULE := true
 
 L_TARGET = lib.a
+
+EXTRA_AFLAGS := -traditional
 
 obj-y = checksum.o delay.o memset.o misaligned.o strcmp.o strncpy.o uaccess.o
 export-objs += misaligned.o
diff -Nru a/arch/s390x/vmlinux-shared.lds b/arch/s390x/vmlinux-shared.lds
--- a/arch/s390x/vmlinux-shared.lds	Sat May 18 00:46:14 2002
+++ b/arch/s390x/vmlinux-shared.lds	Sat May 18 00:46:14 2002
@@ -4,6 +4,7 @@
 OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390")
 OUTPUT_ARCH(s390)
 ENTRY(_start)
+jiffies = jiffies_64;
 SECTIONS
 {
   . = 0x00000000;
diff -Nru a/arch/s390x/vmlinux.lds b/arch/s390x/vmlinux.lds
--- a/arch/s390x/vmlinux.lds	Sat May 18 00:46:09 2002
+++ b/arch/s390x/vmlinux.lds	Sat May 18 00:46:09 2002
@@ -4,6 +4,7 @@
 OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390")
 OUTPUT_ARCH(s390)
 ENTRY(_start)
+jiffies = jiffies_64;
 SECTIONS
 {
   . = 0x00000000;
diff -Nru a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c
--- a/arch/sh/kernel/process.c	Sat May 18 00:46:14 2002
+++ b/arch/sh/kernel/process.c	Sat May 18 00:46:14 2002
@@ -276,16 +276,20 @@
 			unsigned long r6, unsigned long r7,
 			struct pt_regs regs)
 {
-	return do_fork(SIGCHLD, regs.regs[15], &regs, 0);
+	struct task_struct *p;
+	p = do_fork(SIGCHLD, regs.regs[15], &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
 			 unsigned long r6, unsigned long r7,
 			 struct pt_regs regs)
 {
+	struct task_struct *p;
 	if (!newsp)
 		newsp = regs.regs[15];
-	return do_fork(clone_flags, newsp, &regs, 0);
+	p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /*
@@ -302,7 +306,9 @@
 			 unsigned long r6, unsigned long r7,
 			 struct pt_regs regs)
 {
-	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.regs[15], &regs, 0);
+	struct task_struct *p;
+	p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.regs[15], &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /*
diff -Nru a/arch/sh/vmlinux.lds.S b/arch/sh/vmlinux.lds.S
--- a/arch/sh/vmlinux.lds.S	Sat May 18 00:46:08 2002
+++ b/arch/sh/vmlinux.lds.S	Sat May 18 00:46:08 2002
@@ -5,8 +5,10 @@
 #include <linux/config.h>
 #ifdef CONFIG_CPU_LITTLE_ENDIAN
 OUTPUT_FORMAT("elf32-sh-linux", "elf32-sh-linux", "elf32-sh-linux")
+jiffies = jiffies_64;        
 #else
 OUTPUT_FORMAT("elf32-shbig-linux", "elf32-shbig-linux", "elf32-shbig-linux")
+jiffies = jiffies_64 + 4;
 #endif
 OUTPUT_ARCH(sh)
 ENTRY(_start)
diff -Nru a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
--- a/arch/sparc/kernel/Makefile	Sat May 18 00:46:09 2002
+++ b/arch/sparc/kernel/Makefile	Sat May 18 00:46:09 2002
@@ -7,17 +7,15 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-SH = $(CONFIG_SHELL)
-
-.S.s:
-	$(CPP) $(AFLAGS) -ansi $< -o $*.s
+USE_STANDARD_AS_RULE := true
 
-.S.o:
-	$(CC) $(AFLAGS) -ansi -c $< -o $*.o
+SH = $(CONFIG_SHELL)
 
 all: kernel.o head.o init_task.o
 
 O_TARGET := kernel.o
+
+EXTRA_AFLAGS := -ansi
 
 export-objs := sparc_ksyms.o
 IRQ_OBJS := irq.o sun4m_irq.o sun4c_irq.o sun4d_irq.o
diff -Nru a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S
--- a/arch/sparc/kernel/entry.S	Sat May 18 00:46:09 2002
+++ b/arch/sparc/kernel/entry.S	Sat May 18 00:46:09 2002
@@ -1393,7 +1393,7 @@
 	std	%g4, [%curptr + AOFF_task_thread + AOFF_thread_fork_kpsr]
 	add	%sp, REGWIN_SZ, %o2		! arg2:	pt_regs ptr
 	mov	0, %o3
-	call	C_LABEL(do_fork)
+	call	C_LABEL(do_fork_FIXME_NOW_RETURNS_TASK_STRUCT)
 	 mov	%l5, %o7
 	
 	/* Whee, kernel threads! */
@@ -1416,7 +1416,8 @@
 	std	%g4, [%curptr + AOFF_task_thread + AOFF_thread_fork_kpsr]
 	add	%sp, REGWIN_SZ, %o2		! arg2:	pt_regs ptr
 	mov	0, %o3
-	call	C_LABEL(do_fork)
+	/* FIXME:	remove CLONE_IDLETASK from flags first */
+	call	C_LABEL(do_fork_WITHOUT_CLONE_IDLETASK)
 	 mov	%l5, %o7
 
 	/* Whee, real vfork! */
@@ -1432,9 +1433,9 @@
 	sethi	%hi(0x4000 | 0x0100 | SIGCHLD), %o0
 	mov	%fp, %o1
 	or	%o0, %lo(0x4000 | 0x0100 | SIGCHLD), %o0
-	sethi	%hi(C_LABEL(do_fork)), %l1
+	sethi	%hi(C_LABEL(do_fork_FIXME_NOW_RETURNS_TASK_STRUCT)), %l1
 	mov	0, %o3
-	jmpl	%l1 + %lo(C_LABEL(do_fork)), %g0
+	jmpl	%l1 + %lo(C_LABEL(do_fork_FIXME_NOW_RETURNS_TASK_STRUCT)), %g0
 	 add	%sp, REGWIN_SZ, %o2
 
         .align  4
diff -Nru a/arch/sparc/kernel/ptrace.c b/arch/sparc/kernel/ptrace.c
--- a/arch/sparc/kernel/ptrace.c	Sat May 18 00:46:09 2002
+++ b/arch/sparc/kernel/ptrace.c	Sat May 18 00:46:09 2002
@@ -234,29 +234,13 @@
 
 #ifdef DEBUG_PTRACE
 char *pt_rq [] = {
-"TRACEME",
-"PEEKTEXT",
-"PEEKDATA",
-"PEEKUSR",
-"POKETEXT",
-"POKEDATA",
-"POKEUSR",
-"CONT",
-"KILL",
-"SINGLESTEP",
-"SUNATTACH",
-"SUNDETACH",
-"GETREGS",
-"SETREGS",
-"GETFPREGS",
-"SETFPREGS",
-"READDATA",
-"WRITEDATA",
-"READTEXT",
-"WRITETEXT",
-"GETFPAREGS",
-"SETFPAREGS",
-""
+	/* 0  */ "TRACEME", "PEEKTEXT", "PEEKDATA", "PEEKUSR",
+	/* 4  */ "POKETEXT", "POKEDATA", "POKEUSR", "CONT",
+	/* 8  */ "KILL", "SINGLESTEP", "SUNATTACH", "SUNDETACH",
+	/* 12 */ "GETREGS", "SETREGS", "GETFPREGS", "SETFPREGS",
+	/* 16 */ "READDATA", "WRITEDATA", "READTEXT", "WRITETEXT",
+	/* 20 */ "GETFPAREGS", "SETFPAREGS", "unknown", "unknown",
+	/* 24 */ "SYSCALL", ""
 };
 #endif
 
@@ -285,7 +269,7 @@
 	{
 		char *s;
 
-		if ((request > 0) && (request < 21))
+		if ((request >= 0) && (request <= 24))
 			s = pt_rq [request];
 		else
 			s = "unknown";
diff -Nru a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c
--- a/arch/sparc/kernel/sun4d_smp.c	Sat May 18 00:46:08 2002
+++ b/arch/sparc/kernel/sun4d_smp.c	Sat May 18 00:46:08 2002
@@ -214,7 +214,7 @@
 			int no;
 
 			/* Cook up an idler for this guy. */
-			kernel_thread(start_secondary, NULL, CLONE_PID);
+			kernel_thread(start_secondary, NULL, CLONE_IDLETASK);
 
 			cpucount++;
 
diff -Nru a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c
--- a/arch/sparc/kernel/sun4m_smp.c	Sat May 18 00:46:08 2002
+++ b/arch/sparc/kernel/sun4m_smp.c	Sat May 18 00:46:08 2002
@@ -187,7 +187,7 @@
 			int timeout;
 
 			/* Cook up an idler for this guy. */
-			kernel_thread(start_secondary, NULL, CLONE_PID);
+			kernel_thread(start_secondary, NULL, CLONE_IDLETASK);
 
 			cpucount++;
 
diff -Nru a/arch/sparc/kernel/sys_sunos.c b/arch/sparc/kernel/sys_sunos.c
--- a/arch/sparc/kernel/sys_sunos.c	Sat May 18 00:46:09 2002
+++ b/arch/sparc/kernel/sys_sunos.c	Sat May 18 00:46:09 2002
@@ -1048,8 +1048,8 @@
 	return ret;
 }
 
-extern asmlinkage int sys_read(unsigned int fd,char *buf,int count);
-extern asmlinkage int sys_write(unsigned int fd,char *buf,int count);
+extern asmlinkage ssize_t sys_read(unsigned int fd,char *buf,int count);
+extern asmlinkage ssize_t sys_write(unsigned int fd,char *buf,int count);
 extern asmlinkage int sys_recv(int fd, void * ubuf, int size, unsigned flags);
 extern asmlinkage int sys_send(int fd, void * buff, int len, unsigned flags);
 extern asmlinkage int sys_accept(int fd, struct sockaddr *sa, int *addrlen);
diff -Nru a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile
--- a/arch/sparc/lib/Makefile	Sat May 18 00:46:15 2002
+++ b/arch/sparc/lib/Makefile	Sat May 18 00:46:15 2002
@@ -2,13 +2,11 @@
 # Makefile for Sparc library files..
 #
 
-.S.s:
-	$(CPP) $(AFLAGS) -ansi -DST_DIV0=0x2 $< -o $*.s
-
-.S.o:
-	$(CC) $(AFLAGS) -ansi -DST_DIV0=0x2 -c $< -o $*.o
+USE_STANDARD_AS_RULE := true
 
 L_TARGET = lib.a
+
+EXTRA_AFLAGS := -ansi -DST_DIV0=0x02
 
 obj-y := mul.o rem.o sdiv.o udiv.o umul.o urem.o ashrdi3.o memcpy.o memset.o \
          strlen.o checksum.o blockops.o memscan.o memcmp.o strncmp.o \
diff -Nru a/arch/sparc/math-emu/Makefile b/arch/sparc/math-emu/Makefile
--- a/arch/sparc/math-emu/Makefile	Sat May 18 00:46:08 2002
+++ b/arch/sparc/math-emu/Makefile	Sat May 18 00:46:08 2002
@@ -7,15 +7,12 @@
 #
 # Note 2! The CFLAGS definition is now in the main makefile...
 
+USE_STANDARD_AS_RULE := true
+
 O_TARGET := math-emu.o
 obj-y    := math.o ashldi3.o
 
-.S.s:
-	$(CPP) $(AFLAGS) -ansi $< -o $*.s
-
-.S.o:
-	$(CC) $(AFLAGS) -ansi -c $< -o $*.o
-
+EXTRA_AFLAGS := -ansi
 EXTRA_CFLAGS = -I. -I$(TOPDIR)/include/math-emu -w
 
 include $(TOPDIR)/Rules.make
diff -Nru a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile
--- a/arch/sparc/mm/Makefile	Sat May 18 00:46:14 2002
+++ b/arch/sparc/mm/Makefile	Sat May 18 00:46:14 2002
@@ -7,8 +7,8 @@
 #
 # Note 2! The CFLAGS definition is now in the main makefile...
 
-.S.o:
-	$(CC) $(AFLAGS) -ansi -c -o $*.o $<
+USE_STANDARD_AS_RULE := true
+EXTRA_AFLAGS := -ansi
 
 O_TARGET := mm.o
 obj-y    := fault.o init.o loadmmu.o generic.o extable.o btfixup.o
diff -Nru a/arch/sparc/vmlinux.lds b/arch/sparc/vmlinux.lds
--- a/arch/sparc/vmlinux.lds	Sat May 18 00:46:08 2002
+++ b/arch/sparc/vmlinux.lds	Sat May 18 00:46:08 2002
@@ -2,6 +2,7 @@
 OUTPUT_FORMAT("elf32-sparc", "elf32-sparc", "elf32-sparc")
 OUTPUT_ARCH(sparc)
 ENTRY(_start)
+jiffies = jiffies_64 + 4;
 SECTIONS
 {
   . = 0x10000 + SIZEOF_HEADERS;
diff -Nru a/arch/sparc64/defconfig b/arch/sparc64/defconfig
--- a/arch/sparc64/defconfig	Sat May 18 00:46:09 2002
+++ b/arch/sparc64/defconfig	Sat May 18 00:46:09 2002
@@ -310,7 +310,6 @@
 # CONFIG_BLK_DEV_PIIX is not set
 CONFIG_BLK_DEV_NS87415=y
 # CONFIG_BLK_DEV_OPTI621 is not set
-# CONFIG_BLK_DEV_PDC_ADMA is not set
 # CONFIG_BLK_DEV_PDC202XX is not set
 # CONFIG_PDC202XX_BURST is not set
 # CONFIG_PDC202XX_FORCE is not set
diff -Nru a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S
--- a/arch/sparc64/kernel/entry.S	Sat May 18 00:46:08 2002
+++ b/arch/sparc64/kernel/entry.S	Sat May 18 00:46:08 2002
@@ -1429,7 +1429,7 @@
 sys_clone:	flushw
 		movrz		%o1, %fp, %o1
 		mov		0, %o3
-		ba,pt		%xcc, do_fork
+		ba,pt		%xcc, do_fork_FIXME_NOW_RETURNS_TASK_STRUCT
 		 add		%sp, STACK_BIAS + REGWIN_SZ, %o2
 ret_from_syscall:
 		/* Clear SPARC_FLAG_NEWCHILD, switch_to leaves thread.flags in
diff -Nru a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c
--- a/arch/sparc64/kernel/pci.c	Sat May 18 00:46:08 2002
+++ b/arch/sparc64/kernel/pci.c	Sat May 18 00:46:08 2002
@@ -181,11 +181,11 @@
 extern void clock_probe(void);
 extern void power_init(void);
 
-static void __init pcibios_init(void)
+static int __init pcibios_init(void)
 {
 	pci_controller_probe();
 	if (pci_controller_root == NULL)
-		return;
+		return 0;
 
 	pci_scan_each_controller_bus();
 
@@ -197,6 +197,8 @@
 	rs_init();
 	clock_probe();
 	power_init();
+
+	return 0;
 }
 
 subsys_initcall(pcibios_init);
diff -Nru a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c
--- a/arch/sparc64/kernel/process.c	Sat May 18 00:46:09 2002
+++ b/arch/sparc64/kernel/process.c	Sat May 18 00:46:09 2002
@@ -586,13 +586,6 @@
  * under SunOS are nothing short of bletcherous:
  * Parent -->  %o0 == childs  pid, %o1 == 0
  * Child  -->  %o0 == parents pid, %o1 == 1
- *
- * NOTE: We have a separate fork kpsr/kwim because
- *       the parent could change these values between
- *       sys_fork invocation and when we reach here
- *       if the parent should sleep while trying to
- *       allocate the task_struct and kernel stack in
- *       do_fork().
  */
 int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
 		unsigned long unused,
diff -Nru a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c
--- a/arch/sparc64/kernel/ptrace.c	Sat May 18 00:46:09 2002
+++ b/arch/sparc64/kernel/ptrace.c	Sat May 18 00:46:09 2002
@@ -78,29 +78,13 @@
 
 #ifdef DEBUG_PTRACE
 char *pt_rq [] = {
-"TRACEME",
-"PEEKTEXT",
-"PEEKDATA",
-"PEEKUSR",
-"POKETEXT",
-"POKEDATA",
-"POKEUSR",
-"CONT",
-"KILL",
-"SINGLESTEP",
-"SUNATTACH",
-"SUNDETACH",
-"GETREGS",
-"SETREGS",
-"GETFPREGS",
-"SETFPREGS",
-"READDATA",
-"WRITEDATA",
-"READTEXT",
-"WRITETEXT",
-"GETFPAREGS",
-"SETFPAREGS",
-""
+	/* 0  */ "TRACEME", "PEEKTEXT", "PEEKDATA", "PEEKUSR",
+	/* 4  */ "POKETEXT", "POKEDATA", "POKEUSR", "CONT",
+	/* 8  */ "KILL", "SINGLESTEP", "SUNATTACH", "SUNDETACH",
+	/* 12 */ "GETREGS", "SETREGS", "GETFPREGS", "SETFPREGS",
+	/* 16 */ "READDATA", "WRITEDATA", "READTEXT", "WRITETEXT",
+	/* 20 */ "GETFPAREGS", "SETFPAREGS", "unknown", "unknown",
+	/* 24 */ "SYSCALL", ""
 };
 #endif
 
@@ -134,7 +118,7 @@
 	{
 		char *s;
 
-		if ((request > 0) && (request < 21))
+		if ((request >= 0) && (request <= 24))
 			s = pt_rq [request];
 		else
 			s = "unknown";
diff -Nru a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c
--- a/arch/sparc64/kernel/smp.c	Sat May 18 00:46:08 2002
+++ b/arch/sparc64/kernel/smp.c	Sat May 18 00:46:08 2002
@@ -268,7 +268,7 @@
 			int no;
 
 			prom_printf("Starting CPU %d... ", i);
-			kernel_thread(NULL, NULL, CLONE_PID);
+			kernel_thread(NULL, NULL, CLONE_IDLETASK);
 			cpucount++;
 
 			p = prev_task(&init_task);
diff -Nru a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c
--- a/arch/sparc64/kernel/sparc64_ksyms.c	Sat May 18 00:46:08 2002
+++ b/arch/sparc64/kernel/sparc64_ksyms.c	Sat May 18 00:46:08 2002
@@ -372,6 +372,10 @@
 /* RAID code needs this */
 EXPORT_SYMBOL_NOVERS(VISenter);
 
+extern void batten_down_hatches(void);
+/* for input/keybdev */
+EXPORT_SYMBOL(batten_down_hatches);
+
 #ifdef CONFIG_DEBUG_BUGVERBOSE
 EXPORT_SYMBOL(do_BUG);
 #endif
diff -Nru a/arch/sparc64/kernel/sys_sunos32.c b/arch/sparc64/kernel/sys_sunos32.c
--- a/arch/sparc64/kernel/sys_sunos32.c	Sat May 18 00:46:14 2002
+++ b/arch/sparc64/kernel/sys_sunos32.c	Sat May 18 00:46:14 2002
@@ -1198,8 +1198,8 @@
 	return ret;
 }
 
-extern asmlinkage int sys_read(unsigned int fd, char *buf, unsigned long count);
-extern asmlinkage int sys_write(unsigned int fd, char *buf, unsigned long count);
+extern asmlinkage ssize_t sys_read(unsigned int fd, char *buf, unsigned long count);
+extern asmlinkage ssize_t sys_write(unsigned int fd, char *buf, unsigned long count);
 extern asmlinkage int sys_recv(int fd, void *ubuf, size_t size, unsigned flags);
 extern asmlinkage int sys_send(int fd, void *buff, size_t len, unsigned flags);
 extern asmlinkage int sys_accept(int fd, struct sockaddr *sa, int *addrlen);
diff -Nru a/arch/sparc64/vmlinux.lds b/arch/sparc64/vmlinux.lds
--- a/arch/sparc64/vmlinux.lds	Sat May 18 00:46:08 2002
+++ b/arch/sparc64/vmlinux.lds	Sat May 18 00:46:08 2002
@@ -3,6 +3,7 @@
 OUTPUT_ARCH(sparc:v9a)
 ENTRY(_start)
 
+jiffies = jiffies_64;
 SECTIONS
 {
   swapper_pmd_dir = 0x0000000000402000;
diff -Nru a/arch/x86_64/defconfig b/arch/x86_64/defconfig
--- a/arch/x86_64/defconfig	Sat May 18 00:46:14 2002
+++ b/arch/x86_64/defconfig	Sat May 18 00:46:14 2002
@@ -228,7 +228,6 @@
 # CONFIG_BLK_DEV_PIIX is not set
 # CONFIG_BLK_DEV_NS87415 is not set
 # CONFIG_BLK_DEV_OPTI621 is not set
-# CONFIG_BLK_DEV_PDC_ADMA is not set
 # CONFIG_BLK_DEV_PDC202XX is not set
 # CONFIG_PDC202XX_BURST is not set
 # CONFIG_PDC202XX_FORCE is not set
diff -Nru a/arch/x86_64/ia32/sys_ia32.c b/arch/x86_64/ia32/sys_ia32.c
--- a/arch/x86_64/ia32/sys_ia32.c	Sat May 18 00:46:09 2002
+++ b/arch/x86_64/ia32/sys_ia32.c	Sat May 18 00:46:09 2002
@@ -2683,14 +2683,18 @@
 
 asmlinkage int sys32_fork(struct pt_regs regs)
 {
-	return do_fork(SIGCHLD, regs.rsp, &regs, 0);
+	struct task_struct *p;
+	p = do_fork(SIGCHLD, regs.rsp, &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 asmlinkage int sys32_clone(unsigned int clone_flags, unsigned int newsp, struct pt_regs regs)
 {
+	struct task_struct *p;
 	if (!newsp)
 		newsp = regs.rsp;
-	return do_fork(clone_flags, newsp, &regs, 0);
+	p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /*
@@ -2705,7 +2709,9 @@
  */
 asmlinkage int sys32_vfork(struct pt_regs regs)
 {
-	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.rsp, &regs, 0);
+	struct task_struct *p;
+	p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.rsp, &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /*
diff -Nru a/arch/x86_64/kernel/entry.S b/arch/x86_64/kernel/entry.S
--- a/arch/x86_64/kernel/entry.S	Sat May 18 00:46:09 2002
+++ b/arch/x86_64/kernel/entry.S	Sat May 18 00:46:09 2002
@@ -570,7 +570,7 @@
 	movq %rsp, %rdx
 
 	# clone now
-	call do_fork
+	call do_fork_FIXME_NOW_RETURNS_TASK_STRUCT
 	# save retval on the stack so it's popped before `ret`
 	movq %rax, RAX(%rsp)
 
diff -Nru a/arch/x86_64/kernel/process.c b/arch/x86_64/kernel/process.c
--- a/arch/x86_64/kernel/process.c	Sat May 18 00:46:08 2002
+++ b/arch/x86_64/kernel/process.c	Sat May 18 00:46:08 2002
@@ -608,14 +608,18 @@
 
 asmlinkage long sys_fork(struct pt_regs regs)
 {
-	return do_fork(SIGCHLD, regs.rsp, &regs, 0);
+	struct task_struct *p;
+	p = do_fork(SIGCHLD, regs.rsp, &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 asmlinkage long sys_clone(unsigned long clone_flags, unsigned long newsp, struct pt_regs regs)
 {
+	struct task_struct *p;
 	if (!newsp)
 		newsp = regs.rsp;
-	return do_fork(clone_flags, newsp, &regs, 0);
+	p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /*
@@ -630,7 +634,9 @@
  */
 asmlinkage long sys_vfork(struct pt_regs regs)
 {
-	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.rsp, &regs, 0);
+	struct task_struct *p;
+	p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.rsp, &regs, 0);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /*
diff -Nru a/arch/x86_64/kernel/smpboot.c b/arch/x86_64/kernel/smpboot.c
--- a/arch/x86_64/kernel/smpboot.c	Sat May 18 00:46:15 2002
+++ b/arch/x86_64/kernel/smpboot.c	Sat May 18 00:46:15 2002
@@ -476,14 +476,14 @@
 extern volatile unsigned long init_rsp; 
 extern void (*initial_code)(void);
 
-static int __init fork_by_hand(void)
+static struct task_struct * __init fork_by_hand(void)
 {
 	struct pt_regs regs;
 	/*
 	 * don't care about the rip and regs settings since
 	 * we'll never reschedule the forked task.
 	 */
-	return do_fork(CLONE_VM|CLONE_PID, 0, &regs, 0);
+	return do_fork(CLONE_VM|CLONE_IDLETASK, 0, &regs, 0);
 }
 
 #if APIC_DEBUG
@@ -538,17 +538,14 @@
 	 * We can't use kernel_thread since we must avoid to
 	 * reschedule the child.
 	 */
-	if (fork_by_hand() < 0)
+	idle = fork_by_hand();
+	if (IS_ERR(idle))
 		panic("failed fork for CPU %d", cpu);
 
 	/*
 	 * We remove it from the pidhash and the runqueue
 	 * once we got the process:
 	 */
-	idle = prev_task(&init_task);
-	if (!idle)
-		panic("No idle process for CPU %d", cpu);
-
 	init_idle(idle,cpu);
 
 	x86_cpu_to_apicid[cpu] = apicid;
diff -Nru a/arch/x86_64/vmlinux.lds b/arch/x86_64/vmlinux.lds
--- a/arch/x86_64/vmlinux.lds	Sat May 18 00:46:08 2002
+++ b/arch/x86_64/vmlinux.lds	Sat May 18 00:46:08 2002
@@ -3,6 +3,7 @@
  */
 OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
 OUTPUT_ARCH(i386:x86-64)
+jiffies = jiffies_64;
 ENTRY(_start)
 SECTIONS
 {
diff -Nru a/drivers/base/Makefile b/drivers/base/Makefile
--- a/drivers/base/Makefile	Sat May 18 00:46:08 2002
+++ b/drivers/base/Makefile	Sat May 18 00:46:08 2002
@@ -2,6 +2,6 @@
 
 obj-y		:= core.o sys.o interface.o fs.o power.o
 
-export-objs	:= $(obj-y)
+export-objs	:= core.o fs.o power.o sys.o
 
 include $(TOPDIR)/Rules.make
diff -Nru a/drivers/block/Config.help b/drivers/block/Config.help
--- a/drivers/block/Config.help	Sat May 18 00:46:08 2002
+++ b/drivers/block/Config.help	Sat May 18 00:46:08 2002
@@ -90,6 +90,24 @@
 
   Most users will answer N here.
 
+Micro Memory MM5415 Battery Backed RAM support (EXPERIMENTAL)
+CONFIG_BLK_DEV_UMEM
+  Saying Y here will include support for the MM5415 family of
+  battery backed (Non-volatile) RAM cards.
+  http://www.umem.com/
+
+  The cards appear as block devices that can be partitioned into
+  as many as 15 partitions.
+
+  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 umem.o.
+
+  The umem driver has not yet been allocated a MAJOR number, so
+  one is chosen dynamically.  Use "devfs" or look in /proc/devices
+  for the device number
+
 CONFIG_BLK_DEV_NBD
   Saying Y here will allow your computer to be a client for network
   block devices, i.e. it will be able to use block devices exported by
diff -Nru a/drivers/block/Config.in b/drivers/block/Config.in
--- a/drivers/block/Config.in	Sat May 18 00:46:08 2002
+++ b/drivers/block/Config.in	Sat May 18 00:46:08 2002
@@ -37,6 +37,7 @@
 dep_tristate 'Compaq Smart Array 5xxx support' CONFIG_BLK_CPQ_CISS_DA $CONFIG_PCI 
 dep_mbool '       SCSI tape drive support for Smart Array 5xxx' CONFIG_CISS_SCSI_TAPE $CONFIG_BLK_CPQ_CISS_DA $CONFIG_SCSI
 dep_tristate 'Mylex DAC960/DAC1100 PCI RAID Controller support' CONFIG_BLK_DEV_DAC960 $CONFIG_PCI
+dep_tristate 'Micro Memory MM5415 Battery Backed RAM support' CONFIG_BLK_DEV_UMEM $CONFIG_PCI $CONFIG_EXPERIMENTAL
 
 tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
 dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET
diff -Nru a/drivers/block/Makefile b/drivers/block/Makefile
--- a/drivers/block/Makefile	Sat May 18 00:46:14 2002
+++ b/drivers/block/Makefile	Sat May 18 00:46:14 2002
@@ -30,6 +30,7 @@
 obj-$(CONFIG_BLK_CPQ_CISS_DA)  += cciss.o
 obj-$(CONFIG_BLK_DEV_DAC960)	+= DAC960.o
 
+obj-$(CONFIG_BLK_DEV_UMEM)	+= umem.o
 obj-$(CONFIG_BLK_DEV_NBD)	+= nbd.o
 
 subdir-$(CONFIG_PARIDE) += paride
diff -Nru a/drivers/block/swim3.c b/drivers/block/swim3.c
--- a/drivers/block/swim3.c	Sat May 18 00:46:09 2002
+++ b/drivers/block/swim3.c	Sat May 18 00:46:09 2002
@@ -29,13 +29,15 @@
 #include <asm/prom.h>
 #include <asm/uaccess.h>
 #include <asm/mediabay.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 
 #define MAJOR_NR	FLOPPY_MAJOR
 #include <linux/blk.h>
 #include <linux/devfs_fs_kernel.h>
 
-static int floppy_sizes[2] = {2880,2880};
+static int floppy_blocksizes[2] = {512,512};
+static int floppy_sizes[2] = {1440,1440};
 
 #define MAX_FLOPPIES	2
 
@@ -312,10 +314,10 @@
 		return;
 	}
 	while (!QUEUE_EMPTY && fs->state == idle) {
-		if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
+		if (major(CURRENT->rq_dev) != MAJOR_NR)
 			panic(DEVICE_NAME ": request list destroyed");
-		if (CURRENT->bh && !buffer_locked(CURRENT->bh))
-			panic(DEVICE_NAME ": block not locked");
+//		if (CURRENT->bh && !buffer_locked(CURRENT->bh))
+//			panic(DEVICE_NAME ": block not locked");
 #if 0
 		printk("do_fd_req: dev=%x cmd=%d sec=%ld nr_sec=%ld buf=%p\n",
 		       kdev_t_to_nr(CURRENT->rq_dev), CURRENT->cmd,
@@ -337,7 +339,7 @@
 			continue;
 		}
 
-		if (CURRENT->cmd == WRITE) {
+		if (rq_data_dir(CURRENT) == WRITE) {
 			if (fs->write_prot < 0)
 				fs->write_prot = swim3_readbit(fs, WRITE_PROT);
 			if (fs->write_prot) {
@@ -425,7 +427,7 @@
 		printk(KERN_ERR "swim3: transfer 0 sectors?\n");
 		return;
 	}
-	if (CURRENT->cmd == WRITE)
+	if (rq_data_dir(CURRENT) == WRITE)
 		n = 1;
 	else {
 		n = fs->secpertrack - fs->req_sector + 1;
@@ -438,21 +440,21 @@
 	out_8(&sw->nsect, n);
 	out_8(&sw->gap3, 0);
 	st_le32(&dr->cmdptr, virt_to_bus(cp));
-	if (CURRENT->cmd == WRITE) {
+	if (rq_data_dir(CURRENT) == WRITE) {
 		/* Set up 3 dma commands: write preamble, data, postamble */
 		init_dma(cp, OUTPUT_MORE, write_preamble, sizeof(write_preamble));
 		++cp;
 		init_dma(cp, OUTPUT_MORE, CURRENT->buffer, 512);
 		++cp;
-		init_dma(cp, OUTPUT_MORE, write_postamble, sizeof(write_postamble));
+		init_dma(cp, OUTPUT_LAST, write_postamble, sizeof(write_postamble));
 	} else {
-		init_dma(cp, INPUT_MORE, CURRENT->buffer, n * 512);
+		init_dma(cp, INPUT_LAST, CURRENT->buffer, n * 512);
 	}
 	++cp;
 	out_le16(&cp->command, DBDMA_STOP);
 	out_le32(&dr->control, (RUN << 16) | RUN);
 	out_8(&sw->control_bis,
-	      (CURRENT->cmd == WRITE? WRITE_SECTORS: 0) | DO_ACTION);
+	      (rq_data_dir(CURRENT) == WRITE? WRITE_SECTORS: 0) | DO_ACTION);
 	/* enable intr when transfer complete */
 	out_8(&sw->intr_enable, ERROR_INTR | TRANSFER_DONE);
 	set_timeout(fs, 2*HZ, xfer_timeout);	/* enable timeout */
@@ -591,7 +593,7 @@
 	out_8(&sw->intr_enable, 0);
 	out_8(&sw->control_bic, WRITE_SECTORS | DO_ACTION);
 	out_8(&sw->select, RELAX);
-	if (CURRENT->cmd == WRITE)
+	if (rq_data_dir(CURRENT) == WRITE)
 		++cp;
 	if (ld_le16(&cp->xfer_status) != 0)
 		s = fs->scount - ((ld_le16(&cp->res_count) + 511) >> 9);
@@ -600,7 +602,7 @@
 	CURRENT->sector += s;
 	CURRENT->current_nr_sectors -= s;
 	printk(KERN_ERR "swim3: timeout %sing sector %ld\n",
-	       (CURRENT->cmd==WRITE? "writ": "read"), CURRENT->sector);
+	       (rq_data_dir(CURRENT)==WRITE? "writ": "read"), CURRENT->sector);
 	end_request(0);
 	fs->state = idle;
 	start_request(fs);
@@ -621,8 +623,8 @@
 	printk("swim3 intr state=%d intr=%x err=%x\n", fs->state, intr, err);
 #endif
 	if ((intr & ERROR_INTR) && fs->state != do_transfer)
-		printk(KERN_ERR "swim3_interrupt, state=%d, cmd=%x, intr=%x, err=%x\n",
-		       fs->state, CURRENT->cmd, intr, err);
+		printk(KERN_ERR "swim3_interrupt, state=%d, dir=%lx, intr=%x, err=%x\n",
+		       fs->state, rq_data_dir(CURRENT), intr, err);
 	switch (fs->state) {
 	case locating:
 		if (intr & SEEN_SECTOR) {
@@ -678,13 +680,16 @@
 			break;
 		dr = fs->dma;
 		cp = fs->dma_cmd;
-		st_le32(&dr->control, RUN << 16);
+		/* We must wait a bit for dbdma to complete */
+		for (n=0; (in_le32(&dr->status) & ACTIVE) && n < 1000; n++)
+			udelay(10);
+		DBDMA_DO_STOP(dr);
 		out_8(&sw->intr_enable, 0);
 		out_8(&sw->control_bic, WRITE_SECTORS | DO_ACTION);
 		out_8(&sw->select, RELAX);
 		del_timer(&fs->timeout);
 		fs->timeout_pending = 0;
-		if (CURRENT->cmd == WRITE)
+		if (rq_data_dir(CURRENT) == WRITE)
 			++cp;
 		stat = ld_le16(&cp->xfer_status);
 		resid = ld_le16(&cp->res_count);
@@ -701,7 +706,7 @@
 				act(fs);
 			} else {
 				printk("swim3: error %sing block %ld (err=%x)\n",
-				       CURRENT->cmd == WRITE? "writ": "read",
+				       rq_data_dir(CURRENT) == WRITE? "writ": "read",
 				       CURRENT->sector, err);
 				end_request(0);
 				fs->state = idle;
@@ -710,8 +715,8 @@
 			if ((stat & ACTIVE) == 0 || resid != 0) {
 				/* musta been an error */
 				printk(KERN_ERR "swim3: fd dma: stat=%x resid=%d\n", stat, resid);
-				printk(KERN_ERR "  state=%d, cmd=%x, intr=%x, err=%x\n",
-				       fs->state, CURRENT->cmd, intr, err);
+				printk(KERN_ERR "  state=%d, dir=%lx, intr=%x, err=%x\n",
+				       fs->state, rq_data_dir(CURRENT), intr, err);
 				end_request(0);
 				fs->state = idle;
 				start_request(fs);
@@ -815,7 +820,7 @@
 {
 	struct floppy_state *fs;
 	int err;
-	int devnum = MINOR(inode->i_rdev);
+	int devnum = minor(inode->i_rdev);
 
 	if (devnum >= floppy_count)
 		return -ENODEV;
@@ -847,7 +852,7 @@
 	struct floppy_state *fs;
 	volatile struct swim3 *sw;
 	int n, err;
-	int devnum = MINOR(inode->i_rdev);
+	int devnum = minor(inode->i_rdev);
 
 	if (devnum >= floppy_count)
 		return -ENODEV;
@@ -921,7 +926,7 @@
 {
 	struct floppy_state *fs;
 	volatile struct swim3 *sw;
-	int devnum = MINOR(inode->i_rdev);
+	int devnum = minor(inode->i_rdev);
 
 	if (devnum >= floppy_count)
 		return -ENODEV;
@@ -938,9 +943,9 @@
 static int floppy_check_change(kdev_t dev)
 {
 	struct floppy_state *fs;
-	int devnum = MINOR(dev);
+	int devnum = minor(dev);
 
-	if (MAJOR(dev) != MAJOR_NR || (devnum >= floppy_count))
+	if (major(dev) != MAJOR_NR || (devnum >= floppy_count))
 		return 0;
 		
 	fs = &floppy_states[devnum];
@@ -952,9 +957,9 @@
 	struct floppy_state *fs;
 	volatile struct swim3 *sw;
 	int ret, n;
-	int devnum = MINOR(dev);
+	int devnum = minor(dev);
 
-	if (MAJOR(dev) != MAJOR_NR || (devnum >= floppy_count))
+	if (major(dev) != MAJOR_NR || (devnum >= floppy_count))
 		return 0;
 
 	fs = &floppy_states[devnum];
@@ -1031,8 +1036,8 @@
 			       MAJOR_NR);
 			return -EBUSY;
 		}
-		blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_fd_request,
-				&swim3_lock);
+		blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST,&swim3_lock);
+		blksize_size[MAJOR_NR] = floppy_blocksizes;
 		blk_size[MAJOR_NR] = floppy_sizes;
 	}
 
@@ -1060,9 +1065,14 @@
 		return -EINVAL;
 	}
 
+	if (!request_OF_resource(swim, 0, NULL)) {
+		printk(KERN_INFO "swim3: can't request IO resource !\n");
+		return -EINVAL;
+	}
+
 	mediabay = (strcasecmp(swim->parent->type, "media-bay") == 0) ? swim->parent : NULL;
 	if (mediabay == NULL)
-		feature_set(swim, FEATURE_SWIM3_enable);
+		pmac_call_feature(PMAC_FTR_SWIM3_ENABLE, swim, 0, 1);
 	
 	memset(fs, 0, sizeof(*fs));
 	fs->state = idle;
@@ -1084,14 +1094,14 @@
 
 	if (request_irq(fs->swim3_intr, swim3_interrupt, 0, "SWIM3", fs)) {
 		printk(KERN_ERR "Couldn't get irq %d for SWIM3\n", fs->swim3_intr);
-		feature_clear(swim, FEATURE_SWIM3_enable);
+		pmac_call_feature(PMAC_FTR_SWIM3_ENABLE, swim, 0, 0);
 		return -EBUSY;
 	}
 /*
 	if (request_irq(fs->dma_intr, fd_dma_interrupt, 0, "SWIM3-dma", fs)) {
 		printk(KERN_ERR "Couldn't get irq %d for SWIM3 DMA",
 		       fs->dma_intr);
-		feature_clear(swim, FEATURE_SWIM3_enable);
+		pmac_call_feature(PMAC_FTR_SWIM3_ENABLE, swim, 0, 0);
 		return -EBUSY;
 	}
 */
diff -Nru a/drivers/block/umem.c b/drivers/block/umem.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/block/umem.c	Sat May 18 00:46:15 2002
@@ -0,0 +1,1331 @@
+/*
+ * mm.c - Micro Memory(tm) PCI memory board block device driver - v2.3
+ *
+ * (C) 2001 San Mehat <nettwerk@valinux.com>
+ * (C) 2001 Johannes Erdfelt <jerdfelt@valinux.com>
+ * (C) 2001 NeilBrown <neilb@cse.unsw.edu.au>
+ *
+ * This driver for the Micro Memory PCI Memory Module with Battery Backup
+ * is Copyright Micro Memory Inc 2001-2002.  All rights reserved.
+ *
+ * This driver is released to the public under the terms of the
+ *  GNU GENERAL PUBLIC LICENSE version 2
+ * See the file COPYING for details.
+ *
+ * This driver provides a standard block device interface for Micro Memory(tm)
+ * PCI based RAM boards.
+ * 10/05/01: Phap Nguyen - Rebuilt the driver
+ * 10/22/01: Phap Nguyen - v2.1 Added disk partitioning
+ * 29oct2001:NeilBrown   - Use make_request_fn instead of request_fn
+ *                       - use stand disk partitioning (so fdisk works).
+ * 08nov2001:NeilBrown	 - change driver name from "mm" to "umem"
+ *			 - incorporate into main kernel
+ * 08apr2002:NeilBrown   - Move some of interrupt handle to tasklet
+ *			 - use spin_lock_bh instead of _irq
+ *			 - Never block on make_request.  queue
+ *			   bh's instead.
+ *			 - unregister umem from devfs at mod unload
+ *			 - Change version to 2.3
+ * 07Nov2001:Phap Nguyen - Select pci read command: 06, 12, 15 (Decimal)
+ * 07Jan2002: P. Nguyen  - Used PCI Memory Write & Invalidate for DMA
+ * 15May2002:NeilBrown   - convert to bio for 2.5
+ * 17May2002:NeilBrown   - remove init_mem initialisation.  Instead detect
+ *			 - a sequence of writes that cover the card, and
+ *			 - set initialised bit then.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioctl.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/smp_lock.h>
+#include <linux/timer.h>
+#include <linux/pci.h>
+
+#include <linux/fcntl.h>        /* O_ACCMODE */
+#include <linux/hdreg.h>  /* HDIO_GETGEO */
+
+#include <linux/umem.h>
+
+#include <asm/uaccess.h>
+
+#define PRINTK(x...) do {} while (0)
+#define dprintk(x...) do {} while (0)
+/*#define dprintk(x...) printk(x) */
+
+#define MM_MAXCARDS 4
+#define MM_RAHEAD 2      /* two sectors */
+#define MM_BLKSIZE 1024  /* 1k blocks */
+#define MM_HARDSECT 512  /* 512-byte hardware sectors */
+#define MM_SHIFT 6       /* max 64 partitions on 4 cards  */
+#define DEVICE_NR(device) (MINOR(device)>>MM_SHIFT)
+
+/*
+ * Version Information
+ */
+
+#define DRIVER_VERSION "v2.3"
+#define DRIVER_AUTHOR "San Mehat, Johannes Erdfelt, NeilBrown"
+#define DRIVER_DESC "Micro Memory(tm) PCI memory board block driver"
+
+static int debug;
+/* #define HW_TRACE(x)     writeb(x,cards[0].csr_remap + MEMCTRLSTATUS_MAGIC) */
+#define HW_TRACE(x)
+
+#define DEBUG_LED_ON_TRANSFER	0x01
+#define DEBUG_BATTERY_POLLING	0x02
+
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug bitmask");
+
+static int pci_read_cmd = 0x0C;		/* Read Multiple */
+MODULE_PARM(pci_read_cmd, "i");
+MODULE_PARM_DESC(pci_read_cmd, "PCI read command");
+
+static int pci_write_cmd = 0x0F;	/* Write and Invalidate */
+MODULE_PARM(pci_write_cmd, "i");
+MODULE_PARM_DESC(pci_write_cmd, "PCI write command");
+
+static int pci_cmds;
+
+static int major_nr;
+#define MAJOR_NR	(major_nr)
+
+#include <linux/blk.h>
+#include <linux/blkpg.h>
+
+
+static devfs_handle_t devfs_handle;      /*  For the directory */
+
+
+struct cardinfo {
+	int		card_number;
+	struct pci_dev	*dev;
+
+	int		irq;
+
+	unsigned long	csr_base;
+	unsigned char	*csr_remap;
+	unsigned long	csr_len;
+#ifdef CONFIG_MM_MAP_MEMORY
+	unsigned long	mem_base;
+	unsigned char	*mem_remap;
+	unsigned long	mem_len;
+#endif
+
+	unsigned int	win_size; /* PCI window size */
+	unsigned int	mm_size;  /* size in kbytes */
+
+	unsigned int	init_size; /* initial segment, in sectors,
+				    * that we know to
+				    * have been written
+				    */
+	struct bio	*bio, *currentbio, **biotail;
+
+	struct mm_page {
+		dma_addr_t		page_dma;
+		struct mm_dma_desc	*desc;
+		int	 		cnt, headcnt;
+		struct bio		*bio, **biotail;
+	} mm_pages[2];
+#define DESC_PER_PAGE ((PAGE_SIZE*2)/sizeof(struct mm_dma_desc))
+
+	int  Active, Ready;
+
+	struct tasklet_struct	tasklet;
+	unsigned int dma_status;
+
+	struct tq_struct plug_tq;
+
+	struct {
+		int		good;
+		int		warned;
+		unsigned long	last_change;
+	} battery[2];
+
+	atomic_t	 usage;
+	spinlock_t 	lock;
+	int		check_batteries;
+
+};
+
+static struct cardinfo cards[MM_MAXCARDS];
+static struct block_device_operations mm_fops;
+static struct timer_list battery_timer;
+
+
+static int              mm_sizes[MM_MAXCARDS << MM_SHIFT];
+static struct hd_struct mm_partitions[MM_MAXCARDS << MM_SHIFT];
+
+static int num_cards = 0;
+
+struct gendisk mm_gendisk = {
+    major:         0,			/* Major number assigned later */
+    major_name:    "umem",		/* Name of the major device */
+    minor_shift:   MM_SHIFT,		/* Shift to get device number */
+    fops:          &mm_fops,   		/* Block dev operations */
+/* everything else is dynamic */
+};
+
+
+static void check_batteries(struct cardinfo *card);
+
+/*
+-----------------------------------------------------------------------------------
+--                           get_userbit
+-----------------------------------------------------------------------------------
+*/
+static int get_userbit(struct cardinfo *card, int bit)
+{
+	unsigned char led;
+
+	led = readb(card->csr_remap + MEMCTRLCMD_LEDCTRL);
+	return led & bit;
+}
+/*
+-----------------------------------------------------------------------------------
+--                            set_userbit
+-----------------------------------------------------------------------------------
+*/
+static int set_userbit(struct cardinfo *card, int bit, unsigned char state)
+{
+	unsigned char led;
+
+	led = readb(card->csr_remap + MEMCTRLCMD_LEDCTRL);
+	if (state)
+		led |= bit;
+	else
+		led &= ~bit;
+	writeb(led, card->csr_remap + MEMCTRLCMD_LEDCTRL);
+
+	return 0;
+}
+/*
+-----------------------------------------------------------------------------------
+--                             set_led
+-----------------------------------------------------------------------------------
+*/
+/*
+ * NOTE: For the power LED, use the LED_POWER_* macros since they differ
+ */
+static void set_led(struct cardinfo *card, int shift, unsigned char state)
+{
+	unsigned char led;
+
+	led = readb(card->csr_remap + MEMCTRLCMD_LEDCTRL);
+	if (state == LED_FLIP)
+		led ^= (1<<shift);
+	else {
+		led &= ~(0x03 << shift);
+		led |= (state << shift);
+	}
+	writeb(led, card->csr_remap + MEMCTRLCMD_LEDCTRL);
+
+}
+
+#ifdef MM_DIAG
+/*
+-----------------------------------------------------------------------------------
+--                              dump_regs
+-----------------------------------------------------------------------------------
+*/
+static void dump_regs(struct cardinfo *card)
+{
+	unsigned char *p;
+	int i, i1;
+
+	p = card->csr_remap;
+	for (i = 0; i < 8; i++) {
+		printk(KERN_DEBUG "%p   ", p);
+
+		for (i1 = 0; i1 < 16; i1++)
+			printk("%02x ", *p++);
+
+		printk("\n");
+	}
+}
+#endif
+/*
+-----------------------------------------------------------------------------------
+--                            dump_dmastat
+-----------------------------------------------------------------------------------
+*/
+static void dump_dmastat(struct cardinfo *card, unsigned int dmastat)
+{
+	printk(KERN_DEBUG "MM%d*: DMAstat - ", card->card_number);
+	if (dmastat & DMASCR_ANY_ERR)
+		printk("ANY_ERR ");
+	if (dmastat & DMASCR_MBE_ERR)
+		printk("MBE_ERR ");
+	if (dmastat & DMASCR_PARITY_ERR_REP)
+		printk("PARITY_ERR_REP ");
+	if (dmastat & DMASCR_PARITY_ERR_DET)
+		printk("PARITY_ERR_DET ");
+	if (dmastat & DMASCR_SYSTEM_ERR_SIG)
+		printk("SYSTEM_ERR_SIG ");
+	if (dmastat & DMASCR_TARGET_ABT)
+		printk("TARGET_ABT ");
+	if (dmastat & DMASCR_MASTER_ABT)
+		printk("MASTER_ABT ");
+	if (dmastat & DMASCR_CHAIN_COMPLETE)
+		printk("CHAIN_COMPLETE ");
+	if (dmastat & DMASCR_DMA_COMPLETE)
+		printk("DMA_COMPLETE ");
+	printk("\n");
+}
+
+/*
+ * Theory of request handling
+ *
+ * Each bio is assigned to one mm_dma_desc - which may not be enough FIXME
+ * We have two pages of mm_dma_desc, holding about 64 descriptors
+ * each.  These are allocated at init time.
+ * One page is "Ready" and is either full, or can have request added.
+ * The other page might be "Active", which DMA is happening on it.
+ *
+ * Whenever IO on the active page completes, the Ready page is activated
+ * and the ex-Active page is clean out and made Ready.
+ * Otherwise the Ready page is only activated when it becomes full, or
+ * when mm_unplug_device is called via run_task_queue(&tq_disk).
+ *
+ * If a request arrives while both pages a full, it is queued, and b_rdev is
+ * overloaded to record whether it was a read or a write.
+ *
+ * The interrupt handler only polls the device to clear the interrupt.
+ * The processing of the result is done in a tasklet.
+ */
+
+static void mm_start_io(struct cardinfo *card)
+{
+	/* we have the lock, we know there is
+	 * no IO active, and we know that card->Active
+	 * is set
+	 */
+	struct mm_dma_desc *desc;
+	struct mm_page *page;
+	int offset;
+
+	/* make the last descriptor end the chain */
+	page = &card->mm_pages[card->Active];
+	PRINTK("start_io: %d %d->%d\n", card->Active, page->headcnt, page->cnt-1);
+	desc = &page->desc[page->cnt-1];
+
+	desc->control_bits |= cpu_to_le32(DMASCR_CHAIN_COMP_EN);
+	desc->control_bits &= ~cpu_to_le32(DMASCR_CHAIN_EN);
+	desc->sem_control_bits = desc->control_bits;
+
+			       
+	if (debug & DEBUG_LED_ON_TRANSFER)
+		set_led(card, LED_REMOVE, LED_ON);
+
+	desc = &page->desc[page->headcnt];
+	writel(0, card->csr_remap + DMA_PCI_ADDR);
+	writel(0, card->csr_remap + DMA_PCI_ADDR + 4);
+
+	writel(0, card->csr_remap + DMA_LOCAL_ADDR);
+	writel(0, card->csr_remap + DMA_LOCAL_ADDR + 4);
+
+	writel(0, card->csr_remap + DMA_TRANSFER_SIZE);
+	writel(0, card->csr_remap + DMA_TRANSFER_SIZE + 4);
+
+	writel(0, card->csr_remap + DMA_SEMAPHORE_ADDR);
+	writel(0, card->csr_remap + DMA_SEMAPHORE_ADDR + 4);
+
+	offset = ((char*)desc) - ((char*)page->desc);
+	writel(cpu_to_le32((page->page_dma+offset)&0xffffffff),
+	       card->csr_remap + DMA_DESCRIPTOR_ADDR);
+	/* if sizeof(dma_addr_t) == 32, this will generate a warning, sorry */
+	writel(cpu_to_le32((page->page_dma)>>32),
+	       card->csr_remap + DMA_DESCRIPTOR_ADDR + 4);
+
+	/* Go, go, go */
+	writel(cpu_to_le32(DMASCR_GO | DMASCR_CHAIN_EN | pci_cmds),
+	       card->csr_remap + DMA_STATUS_CTRL);
+}
+
+static int add_bio(struct cardinfo *card);
+
+static void activate(struct cardinfo *card)
+{
+	/* if No page is Active, and Ready is 
+	 * not empty, then switch Ready page
+	 * to active and start IO.
+	 * Then add any bh's that are available to Ready
+	 */
+
+	do {
+		while (add_bio(card))
+			;
+
+		if (card->Active == -1 &&
+		    card->mm_pages[card->Ready].cnt > 0) {
+			card->Active = card->Ready;
+			card->Ready = 1-card->Ready;
+			mm_start_io(card);
+		}
+
+	} while (card->Active == -1 && add_bio(card));
+}
+
+static inline void reset_page(struct mm_page *page)
+{
+	page->cnt = 0;
+	page->headcnt = 0;
+	page->bio = NULL;
+	page->biotail = & page->bio;
+}
+
+static void mm_unplug_device(void *data)
+{
+	struct cardinfo *card = data;
+
+	spin_lock_bh(&card->lock);
+	activate(card);
+	spin_unlock_bh(&card->lock);
+}
+
+/* 
+ * If there is room on Ready page, take
+ * one bh off list and add it.
+ * return 1 if there was room, else 0.
+ */
+static int add_bio(struct cardinfo *card)
+{
+	struct mm_page *p;
+	struct mm_dma_desc *desc;
+	dma_addr_t dma_handle;
+	int offset;
+	struct bio *bio;
+	int rw;
+	int len;
+
+	bio = card->currentbio;
+	if (!bio && card->bio) {
+		card->currentbio = card->bio;
+		card->bio = card->bio->bi_next;
+		if (card->bio == NULL)
+			card->biotail = &card->bio;
+		card->currentbio->bi_next = NULL;
+		return 1;
+	}
+	if (!bio)
+		return 0;
+
+	rw = bio_rw(bio);
+	if (card->mm_pages[card->Ready].cnt >= DESC_PER_PAGE)
+		return 0;
+
+	len = bio_iovec(bio)->bv_len;
+	dma_handle = pci_map_page(card->dev, 
+				  bio_page(bio),
+				  bio_offset(bio),
+				  len,
+				  (rw==READ) ?
+				  PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
+
+	p = &card->mm_pages[card->Ready];
+	desc = &p->desc[p->cnt];
+	p->cnt++;
+	if ((p->biotail) != &bio->bi_next) {
+		*(p->biotail) = bio;
+		p->biotail = &(bio->bi_next);
+		bio->bi_next = NULL;
+	}
+
+	desc->data_dma_handle = dma_handle;
+
+	desc->pci_addr = cpu_to_le64((u64)desc->data_dma_handle);
+	desc->local_addr= cpu_to_le64(bio->bi_sector << 9);
+	desc->transfer_size = cpu_to_le32(len);
+	offset = ( ((char*)&desc->sem_control_bits) - ((char*)p->desc));
+	desc->sem_addr = cpu_to_le64((u64)(p->page_dma+offset));
+	desc->zero1 = desc->zero2 = 0;
+	offset = ( ((char*)(desc+1)) - ((char*)p->desc));
+	desc->next_desc_addr = cpu_to_le64(p->page_dma+offset);
+	desc->control_bits = cpu_to_le32(DMASCR_GO|DMASCR_ERR_INT_EN|
+					 DMASCR_PARITY_INT_EN|
+					 DMASCR_CHAIN_EN |
+					 DMASCR_SEM_EN |
+					 pci_cmds);
+	if (rw == WRITE)
+		desc->control_bits |= cpu_to_le32(DMASCR_TRANSFER_READ);
+	desc->sem_control_bits = desc->control_bits;
+
+	bio->bi_sector += (len>>9);
+	bio->bi_size -= len;
+	bio->bi_idx++;
+	if (bio->bi_idx >= bio->bi_vcnt) 
+		card->currentbio = NULL;
+
+	return 1;
+}
+
+static void process_page(unsigned long data)
+{
+	/* check if any of the requests in the page are DMA_COMPLETE,
+	 * and deal with them appropriately.
+	 * If we find a descriptor without DMA_COMPLETE in the semaphore, then
+	 * dma must have hit an error on that descriptor, so use dma_status instead
+	 * and assume that all following descriptors must be re-tried.
+	 */
+	struct mm_page *page;
+	struct bio *return_bio=NULL;
+	struct cardinfo *card = (struct cardinfo *)data;
+	unsigned int dma_status = card->dma_status;
+
+	spin_lock_bh(&card->lock);
+	if (card->Active < 0)
+		goto out_unlock;
+	page = &card->mm_pages[card->Active];
+	
+	while (page->headcnt < page->cnt) {
+		struct bio *bio = page->bio;
+		struct mm_dma_desc *desc = &page->desc[page->headcnt];
+		int control = le32_to_cpu(desc->sem_control_bits);
+		int last=0;
+		int idx;
+
+		if (!(control & DMASCR_DMA_COMPLETE)) {
+			control = dma_status;
+			last=1; 
+		}
+		page->headcnt++;
+		idx = bio->bi_phys_segments;
+		bio->bi_phys_segments++;
+		if (bio->bi_phys_segments >= bio->bi_vcnt)
+			page->bio = bio->bi_next;
+
+		pci_unmap_page(card->dev, desc->data_dma_handle, 
+			       bio_iovec_idx(bio,idx)->bv_len,
+				 (control& DMASCR_TRANSFER_READ) ?
+				PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+		if (control & DMASCR_HARD_ERROR) {
+			/* error */
+			clear_bit(BIO_UPTODATE, &bio->bi_flags);
+			printk(KERN_WARNING "MM%d: I/O error on sector %d/%d\n",
+			       card->card_number, 
+			       le32_to_cpu(desc->local_addr)>>9,
+			       le32_to_cpu(desc->transfer_size));
+			dump_dmastat(card, control);
+		} else if (test_bit(BIO_RW, &bio->bi_rw) &&
+			   le32_to_cpu(desc->local_addr)>>9 == card->init_size) {
+			card->init_size += le32_to_cpu(desc->transfer_size)>>9;
+			if (card->init_size>>1 >= card->mm_size) {
+				printk(KERN_INFO "MM%d: memory now initialised\n",
+				       card->card_number);
+				set_userbit(card, MEMORY_INITIALIZED, 1);
+			}
+		}
+		if (bio != page->bio) {
+			bio->bi_next = return_bio;
+			return_bio = bio;
+		}
+
+		if (last) break;
+	}
+
+	if (debug & DEBUG_LED_ON_TRANSFER)
+		set_led(card, LED_REMOVE, LED_OFF);
+
+	if (card->check_batteries) {
+		card->check_batteries = 0;
+		check_batteries(card);
+	}
+	if (page->headcnt >= page->cnt) {
+		reset_page(page);
+		card->Active = -1;
+		activate(card);
+	} else {
+		/* haven't finished with this one yet */
+		PRINTK("do some more\n");
+		mm_start_io(card);
+	}
+ out_unlock:
+	spin_unlock_bh(&card->lock);
+
+	while(return_bio) {
+		struct bio *bio = return_bio;
+		return_bio = bio->bi_next;
+		bio->bi_next = NULL;
+		bio->bi_end_io(bio);
+	}
+}
+
+/*
+-----------------------------------------------------------------------------------
+--                              mm_make_request
+-----------------------------------------------------------------------------------
+*/
+static int mm_make_request(request_queue_t *q, struct bio *bio)
+{
+	struct cardinfo *card = &cards[DEVICE_NR(
+		bio->bi_bdev->bd_dev)];
+	PRINTK("mm_make_request %ld %d\n", bh->b_rsector, bh->b_size);
+
+	/* set uptodate now, and clear it if there are any errors */
+	set_bit(BIO_UPTODATE, &bio->bi_flags);
+	bio->bi_phys_segments = bio->bi_idx; /* count of completed segments*/
+	spin_lock_bh(&card->lock);
+	*card->biotail = bio;
+	bio->bi_next = NULL;
+	card->biotail = &bio->bi_next;
+	spin_unlock_bh(&card->lock);
+
+	queue_task(&card->plug_tq, &tq_disk);
+	return 0;
+}
+
+/*
+-----------------------------------------------------------------------------------
+--                              mm_interrupt
+-----------------------------------------------------------------------------------
+*/
+static void mm_interrupt(int irq, void *__card, struct pt_regs *regs)
+{
+	struct cardinfo *card = (struct cardinfo *) __card;
+	unsigned int dma_status;
+	unsigned short cfg_status;
+
+HW_TRACE(0x30);
+
+	dma_status = le32_to_cpu(readl(card->csr_remap + DMA_STATUS_CTRL));
+
+	if (!(dma_status & (DMASCR_ERROR_MASK | DMASCR_CHAIN_COMPLETE))) {
+		/* interrupt wasn't for me ... */
+		return;
+        }
+
+	/* clear COMPLETION interrupts */
+	writel(cpu_to_le32(DMASCR_DMA_COMPLETE|DMASCR_CHAIN_COMPLETE),
+	       card->csr_remap+ DMA_STATUS_CTRL);
+
+	
+	/* log errors and clear interrupt status */
+	if (dma_status & DMASCR_ANY_ERR) {
+		unsigned int	data_log1, data_log2;
+		unsigned int	addr_log1, addr_log2;
+		unsigned char	stat, count, syndrome, check;
+
+		stat = readb(card->csr_remap + MEMCTRLCMD_ERRSTATUS);
+
+		data_log1 = le32_to_cpu(readl(card->csr_remap + ERROR_DATA_LOG));
+		data_log2 = le32_to_cpu(readl(card->csr_remap + ERROR_DATA_LOG + 4));
+		addr_log1 = le32_to_cpu(readl(card->csr_remap + ERROR_ADDR_LOG));
+		addr_log2 = readb(card->csr_remap + ERROR_ADDR_LOG + 4);
+
+		count = readb(card->csr_remap + ERROR_COUNT);
+		syndrome = readb(card->csr_remap + ERROR_SYNDROME);
+		check = readb(card->csr_remap + ERROR_CHECK);
+
+		dump_dmastat(card, dma_status);
+
+		if (stat & 0x01)
+			printk(KERN_ERR "MM%d*: Memory access error detected (err count %d)\n",
+				card->card_number, count);
+		if (stat & 0x02)
+			printk(KERN_ERR "MM%d*: Multi-bit EDC error\n",
+				card->card_number);
+
+		printk(KERN_ERR "MM%d*: Fault Address 0x%02x%08x, Fault Data 0x%08x%08x\n",
+			card->card_number, addr_log2, addr_log1, data_log2, data_log1);
+		printk(KERN_ERR "MM%d*: Fault Check 0x%02x, Fault Syndrome 0x%02x\n",
+			card->card_number, check, syndrome);
+
+		writeb(0, card->csr_remap + ERROR_COUNT);
+	}
+
+	if (dma_status & DMASCR_PARITY_ERR_REP) {
+		printk(KERN_ERR "MM%d*: PARITY ERROR REPORTED\n", card->card_number);
+		pci_read_config_word(card->dev, PCI_STATUS, &cfg_status);
+		pci_write_config_word(card->dev, PCI_STATUS, cfg_status);
+	}
+
+	if (dma_status & DMASCR_PARITY_ERR_DET) {
+		printk(KERN_ERR "MM%d*: PARITY ERROR DETECTED\n", card->card_number); 
+		pci_read_config_word(card->dev, PCI_STATUS, &cfg_status);
+		pci_write_config_word(card->dev, PCI_STATUS, cfg_status);
+	}
+
+	if (dma_status & DMASCR_SYSTEM_ERR_SIG) {
+		printk(KERN_ERR "MM%d*: SYSTEM ERROR\n", card->card_number); 
+		pci_read_config_word(card->dev, PCI_STATUS, &cfg_status);
+		pci_write_config_word(card->dev, PCI_STATUS, cfg_status);
+	}
+
+	if (dma_status & DMASCR_TARGET_ABT) {
+		printk(KERN_ERR "MM%d*: TARGET ABORT\n", card->card_number); 
+		pci_read_config_word(card->dev, PCI_STATUS, &cfg_status);
+		pci_write_config_word(card->dev, PCI_STATUS, cfg_status);
+	}
+
+	if (dma_status & DMASCR_MASTER_ABT) {
+		printk(KERN_ERR "MM%d*: MASTER ABORT\n", card->card_number); 
+		pci_read_config_word(card->dev, PCI_STATUS, &cfg_status);
+		pci_write_config_word(card->dev, PCI_STATUS, cfg_status);
+	}
+
+	/* and process the DMA descriptors */
+	card->dma_status = dma_status;
+	tasklet_schedule(&card->tasklet);
+
+HW_TRACE(0x36);
+
+}
+/*
+-----------------------------------------------------------------------------------
+--                         set_fault_to_battery_status
+-----------------------------------------------------------------------------------
+*/
+/*
+ * If both batteries are good, no LED
+ * If either battery has been warned, solid LED
+ * If both batteries are bad, flash the LED quickly
+ * If either battery is bad, flash the LED semi quickly
+ */
+static void set_fault_to_battery_status(struct cardinfo *card)
+{
+	if (card->battery[0].good && card->battery[1].good)
+		set_led(card, LED_FAULT, LED_OFF);
+	else if (card->battery[0].warned || card->battery[1].warned)
+		set_led(card, LED_FAULT, LED_ON);
+	else if (!card->battery[0].good && !card->battery[1].good)
+		set_led(card, LED_FAULT, LED_FLASH_7_0);
+	else
+		set_led(card, LED_FAULT, LED_FLASH_3_5);
+}
+
+static void init_battery_timer(void);
+
+
+/*
+-----------------------------------------------------------------------------------
+--                            check_battery
+-----------------------------------------------------------------------------------
+*/
+static int check_battery(struct cardinfo *card, int battery, int status)
+{
+	if (status != card->battery[battery].good) {
+		card->battery[battery].good = !card->battery[battery].good;
+		card->battery[battery].last_change = jiffies;
+
+		if (card->battery[battery].good) {
+			printk(KERN_ERR "MM%d: Battery %d now good\n",
+				card->card_number, battery + 1);
+			card->battery[battery].warned = 0;
+		} else
+			printk(KERN_ERR "MM%d: Battery %d now FAILED\n",
+				card->card_number, battery + 1);
+
+		return 1;
+	} else if (!card->battery[battery].good &&
+		   !card->battery[battery].warned &&
+		   time_after_eq(jiffies, card->battery[battery].last_change +
+				 (HZ * 60 * 60 * 5))) {
+		printk(KERN_ERR "MM%d: Battery %d still FAILED after 5 hours\n",
+			card->card_number, battery + 1);
+		card->battery[battery].warned = 1;
+
+		return 1;
+	}
+
+	return 0;
+}
+/*
+-----------------------------------------------------------------------------------
+--                              check_batteries
+-----------------------------------------------------------------------------------
+*/
+static void check_batteries(struct cardinfo *card)
+{
+	/* NOTE: this must *never* be called while the card
+	 * is doing (bus-to-card) DMA, or you will need the
+	 * reset switch
+	 */
+	unsigned char status;
+	int ret1, ret2;
+
+	status = readb(card->csr_remap + MEMCTRLSTATUS_BATTERY);
+	if (debug & DEBUG_BATTERY_POLLING)
+		printk(KERN_DEBUG "MM%d: checking battery status, 1 = %s, 2 = %s\n",
+		       card->card_number,
+		       (status & BATTERY_1_FAILURE) ? "FAILURE" : "OK",
+		       (status & BATTERY_2_FAILURE) ? "FAILURE" : "OK");
+
+	ret1 = check_battery(card, 0, !(status & BATTERY_1_FAILURE));
+	ret2 = check_battery(card, 1, !(status & BATTERY_2_FAILURE));
+
+	if (ret1 || ret2)
+		set_fault_to_battery_status(card);
+}
+
+static void check_all_batteries(unsigned long ptr)
+{
+	int i;
+
+	for (i = 0; i < num_cards; i++) {
+		struct cardinfo *card = &cards[i];
+		spin_lock_bh(&card->lock);
+		if (card->Active >= 0)
+			card->check_batteries = 1;
+		else
+			check_batteries(card);
+		spin_unlock_bh(&card->lock);
+	}
+
+	init_battery_timer();
+}
+/*
+-----------------------------------------------------------------------------------
+--                            init_battery_timer
+-----------------------------------------------------------------------------------
+*/
+static void init_battery_timer(void)
+{
+	init_timer(&battery_timer);
+	battery_timer.function = check_all_batteries;
+	battery_timer.expires = jiffies + (HZ * 60);
+	add_timer(&battery_timer);
+}
+/*
+-----------------------------------------------------------------------------------
+--                              del_battery_timer
+-----------------------------------------------------------------------------------
+*/
+static void del_battery_timer(void)
+{
+	del_timer(&battery_timer);
+}
+/*
+-----------------------------------------------------------------------------------
+--                                mm_revalidate
+-----------------------------------------------------------------------------------
+*/
+/*
+ * Note no locks taken out here.  In a worst case scenario, we could drop
+ * a chunk of system memory.  But that should never happen, since validation
+ * happens at open or mount time, when locks are held.
+ */
+static int mm_revalidate(kdev_t i_rdev)
+{
+	int i;
+
+	int card_number = DEVICE_NR(kdev_val(i_rdev));
+	/* first partition, # of partitions */
+	int part1 = (card_number << MM_SHIFT) + 1;
+	int npart = (1 << MM_SHIFT) -1;
+
+	/* first clear old partition information */
+	for (i=0; i<npart ;i++) {
+		mm_gendisk.sizes[part1+i]=0;
+		mm_gendisk.part[part1+i].start_sect = 0;
+		mm_gendisk.part[part1+i].nr_sects = 0;
+	}
+
+	mm_gendisk.part[card_number << MM_SHIFT].nr_sects =
+		cards[card_number].mm_size << 1;
+
+
+	/* then fill new info */
+	printk(KERN_INFO "mm partition check: (%d)\n", card_number);
+	grok_partitions(mk_kdev(major_nr,part1-1),
+			mm_gendisk.sizes[card_number<<MM_SHIFT]);
+	return 0;
+}
+/*
+-----------------------------------------------------------------------------------
+--                            mm_ioctl
+-----------------------------------------------------------------------------------
+*/
+static int mm_ioctl(struct inode *i, struct file *f, unsigned int cmd, unsigned long arg)
+{
+	int err, size, card_number;
+	struct hd_geometry geo;
+	unsigned int minor;
+
+	if (!i)
+		return -EINVAL;
+
+	minor       = minor(i->i_rdev);
+	card_number = (minor >> MM_SHIFT);
+
+
+	switch(cmd) {
+
+	case BLKGETSIZE:
+		/* Return the device size, expressed in sectors */
+		err = ! access_ok (VERIFY_WRITE, arg, sizeof(long));
+		if (err) return -EFAULT;
+		size = mm_gendisk.part[minor].nr_sects;
+		if (copy_to_user((long *) arg, &size, sizeof (long)))
+			return -EFAULT;
+		return 0;
+
+	case BLKRRPART:
+		return (mm_revalidate(i->i_rdev));
+
+	case HDIO_GETGEO:
+		/*
+		 * get geometry: we have to fake one...  trim the size to a
+		 * multiple of 2048 (1M): tell we have 32 sectors, 64 heads,
+		 * whatever cylinders.
+		 */
+		err = ! access_ok(VERIFY_WRITE, arg, sizeof(geo));
+		if (err) return -EFAULT;
+		size = cards[card_number].mm_size * (1024 / MM_HARDSECT);
+		geo.heads     = 64;
+		geo.sectors   = 32;
+		geo.start     = mm_gendisk.part[minor].start_sect;
+		geo.cylinders = size / (geo.heads * geo.sectors);
+
+		if (copy_to_user((void *) arg, &geo, sizeof(geo)))
+			return -EFAULT;
+		return 0;
+
+
+	default:
+		return blk_ioctl(i->i_bdev, cmd, arg);
+	}
+
+	return -ENOTTY; /* unknown command */
+}
+/*
+-----------------------------------------------------------------------------------
+--                                mm_check_change
+-----------------------------------------------------------------------------------
+  Future support for removable devices
+*/
+static int mm_check_change(kdev_t i_rdev)
+{
+	int card_number = DEVICE_NR(kdev_val(i_rdev));
+/*  struct cardinfo *dev = cards + card_number; */
+	if (card_number >= num_cards) /* paranoid */
+		return 0;
+
+	return 0;
+}
+
+/*
+-----------------------------------------------------------------------------------
+--                            mm_open
+-----------------------------------------------------------------------------------
+*/
+static int mm_open(struct inode *i, struct file *filp)
+{
+	int num;
+	struct cardinfo *card;
+
+	num = DEVICE_NR(kdev_val(i->i_rdev));
+	if (num >= num_cards)
+		return -ENXIO;
+
+	card = cards + num;
+
+	atomic_inc(&card->usage);
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+/*
+-----------------------------------------------------------------------------------
+--                              mm_do_release
+-----------------------------------------------------------------------------------
+*/
+static int mm_do_release(struct inode *i, struct file *filp)
+{
+	int num;
+	struct cardinfo *card;
+
+	num = DEVICE_NR(kdev_val(i->i_rdev));
+
+	card = cards + num;
+
+	if (atomic_dec_and_test(&card->usage))
+		invalidate_device(i->i_rdev, 1);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+/*
+-----------------------------------------------------------------------------------
+--                             mm_fops
+-----------------------------------------------------------------------------------
+*/
+static struct block_device_operations mm_fops = {
+	owner:		THIS_MODULE,
+	open:		mm_open,
+	release:	mm_do_release,
+	ioctl:		mm_ioctl,
+	revalidate:	mm_revalidate,
+	check_media_change: mm_check_change,
+};
+/*
+-----------------------------------------------------------------------------------
+--                                mm_pci_probe
+-----------------------------------------------------------------------------------
+*/
+static int __devinit mm_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	int ret = -ENODEV;
+	struct cardinfo *card = &cards[num_cards];
+	unsigned char	mem_present;
+	unsigned char	batt_status;
+	unsigned int	saved_bar, data;
+
+	if (pci_enable_device(dev) < 0)
+		return -ENODEV;
+
+	pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0xF8);
+	pci_set_master(dev);
+
+	card->dev         = dev;
+	card->card_number = num_cards;
+
+	card->csr_base = pci_resource_start(dev, 0);
+	card->csr_len  = pci_resource_len(dev, 0);
+#ifdef CONFIG_MM_MAP_MEMORY
+	card->mem_base = pci_resource_start(dev, 1);
+	card->mem_len  = pci_resource_len(dev, 1);
+#endif
+
+	printk(KERN_INFO "Micro Memory(tm) controller #%d found at %02x:%02x (PCI Mem Module (Battery Backup))\n",
+	       card->card_number, dev->bus->number, dev->devfn);
+
+	if (pci_set_dma_mask(dev, 0xffffffffffffffffLL) &&
+	    !pci_set_dma_mask(dev, 0xffffffffLL)) {
+		printk(KERN_WARNING "MM%d: NO suitable DMA found\n",num_cards);
+		return  -ENOMEM;
+	}
+	if (!request_mem_region(card->csr_base, card->csr_len, "Micro Memory")) {
+		printk(KERN_ERR "MM%d: Unable to request memory region\n", card->card_number);
+		ret = -ENOMEM;
+
+		goto failed_req_csr;
+	}
+
+	card->csr_remap = ioremap_nocache(card->csr_base, card->csr_len);
+	if (!card->csr_remap) {
+		printk(KERN_ERR "MM%d: Unable to remap memory region\n", card->card_number);
+		ret = -ENOMEM;
+
+		goto failed_remap_csr;
+	}
+
+	printk(KERN_INFO "MM%d: CSR 0x%08lx -> 0x%p (0x%lx)\n", card->card_number,
+	       card->csr_base, card->csr_remap, card->csr_len);
+
+#ifdef CONFIG_MM_MAP_MEMORY
+	if (!request_mem_region(card->mem_base, card->mem_len, "Micro Memory")) {
+		printk(KERN_ERR "MM%d: Unable to request memory region\n", card->card_number);
+		ret = -ENOMEM;
+
+		goto failed_req_mem;
+	}
+
+	if (!(card->mem_remap = (unsigned char *)ioremap(card->mem_base, cards->mem_len))) {
+		printk(KERN_ERR "MM%d: Unable to remap memory region\n", card->card_number);
+		ret = -ENOMEM;
+
+		goto failed_remap_mem;
+	}
+
+	printk(KERN_INFO "MM%d: MEM 0x%8lx -> 0x%8lx (0x%lx)\n", card->card_number,
+	       card->mem_base, card->mem_remap, card->mem_len);
+#else
+	printk(KERN_INFO "MM%d: MEM area not remapped (CONFIG_MM_MAP_MEMORY not set)\n",
+	       card->card_number);
+#endif
+	if (readb(card->csr_remap + MEMCTRLSTATUS_MAGIC) != MM_MAGIC_VALUE) {
+		printk(KERN_ERR "MM%d: Magic number invalid\n", card->card_number);
+		ret = -ENOMEM;
+
+		goto failed_magic;
+	}
+	card->mm_pages[0].desc = pci_alloc_consistent(card->dev,
+						      PAGE_SIZE*2,
+						      &card->mm_pages[0].page_dma);
+	card->mm_pages[1].desc = pci_alloc_consistent(card->dev,
+						      PAGE_SIZE*2,
+						      &card->mm_pages[1].page_dma);
+	if (card->mm_pages[0].desc == NULL ||
+	    card->mm_pages[1].desc == NULL) {
+		printk(KERN_ERR "MM%d: alloc failed\n", card->card_number);
+		goto failed_alloc;
+	}
+	reset_page(&card->mm_pages[0]);
+	reset_page(&card->mm_pages[1]);
+	card->Ready = 0;	/* page 0 is ready */
+	card->Active = -1;	/* no page is active */
+	card->bio = NULL;
+	card->biotail = &card->bio;
+
+	tasklet_init(&card->tasklet, process_page, (unsigned long)card);
+
+	card->plug_tq.sync = 0;
+	card->plug_tq.routine = &mm_unplug_device;
+	card->plug_tq.data = card;
+	card->check_batteries = 0;
+	
+	mem_present = readb(card->csr_remap + MEMCTRLSTATUS_MEMORY);
+	switch (mem_present) {
+	case MEM_128_MB:
+		card->mm_size = 1024 * 128;
+		break;
+	case MEM_256_MB:
+		card->mm_size = 1024 * 256;
+		break;
+	case MEM_512_MB:
+		card->mm_size = 1024 * 512;
+		break;
+	case MEM_1_GB:
+		card->mm_size = 1024 * 1024;
+		break;
+	case MEM_2_GB:
+		card->mm_size = 1024 * 2048;
+		break;
+	default:
+		card->mm_size = 0;
+		break;
+	}
+
+	/* Clear the LED's we control */
+	set_led(card, LED_REMOVE, LED_OFF);
+	set_led(card, LED_FAULT, LED_OFF);
+
+	batt_status = readb(card->csr_remap + MEMCTRLSTATUS_BATTERY);
+
+	card->battery[0].good = !(batt_status & BATTERY_1_FAILURE);
+	card->battery[1].good = !(batt_status & BATTERY_2_FAILURE);
+	card->battery[0].last_change = card->battery[1].last_change = jiffies;
+
+	printk(KERN_INFO "MM%d: Size %d KB, Battery 1 %s (%s), Battery 2 %s (%s)\n",
+	       card->card_number, card->mm_size,
+	       (batt_status & BATTERY_1_DISABLED ? "Disabled" : "Enabled"),
+	       card->battery[0].good ? "OK" : "FAILURE",
+	       (batt_status & BATTERY_2_DISABLED ? "Disabled" : "Enabled"),
+	       card->battery[1].good ? "OK" : "FAILURE");
+
+	set_fault_to_battery_status(card);
+
+	pci_read_config_dword(dev, PCI_BASE_ADDRESS_1, &saved_bar);
+	data = 0xffffffff;
+	pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, data);
+	pci_read_config_dword(dev, PCI_BASE_ADDRESS_1, &data);
+	pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, saved_bar);
+	data &= 0xfffffff0;
+	data = ~data;
+	data += 1;
+
+	card->win_size = data;
+
+
+	if (request_irq(dev->irq, mm_interrupt, SA_SHIRQ, "pci-umem", card)) {
+		printk(KERN_ERR "MM%d: Unable to allocate IRQ\n", card->card_number);
+		ret = -ENODEV;
+
+		goto failed_req_irq;
+	}
+
+	card->irq = dev->irq;
+	printk(KERN_INFO "MM%d: Window size %d bytes, IRQ %d\n", card->card_number,
+	       card->win_size, card->irq);
+
+        spin_lock_init(&card->lock);
+
+	dev->driver_data = card;
+
+	if (pci_write_cmd != 0x0F) 	/* If not Memory Write & Invalidate */
+		pci_write_cmd = 0x07;	/* then Memory Write command */
+
+	if (pci_write_cmd & 0x08) { /* use Memory Write and Invalidate */
+		unsigned short cfg_command;
+		pci_read_config_word(dev, PCI_COMMAND, &cfg_command);
+		cfg_command |= 0x10; /* Memory Write & Invalidate Enable */
+		pci_write_config_word(dev, PCI_COMMAND, cfg_command);
+	}
+	pci_cmds = (pci_read_cmd << 28) | (pci_write_cmd << 24);
+
+	num_cards++;
+
+	if (!get_userbit(card, MEMORY_INITIALIZED)) {
+		printk(KERN_INFO "MM%d: memory NOT initialized. Consider over-writing whole device.\n", card->card_number);
+		card->init_size = 0;
+	} else {
+		printk(KERN_INFO "MM%d: memory already initialized\n", card->card_number);
+		card->init_size = card->mm_size;
+	}
+
+	/* Enable ECC */
+	writeb(EDC_STORE_CORRECT, card->csr_remap + MEMCTRLCMD_ERRCTRL);
+
+	return 0;
+
+ failed_req_irq:
+ failed_alloc:
+	if (card->mm_pages[0].desc)
+		pci_free_consistent(card->dev, PAGE_SIZE*2,
+				    card->mm_pages[0].desc,
+				    card->mm_pages[0].page_dma);
+	if (card->mm_pages[1].desc)
+		pci_free_consistent(card->dev, PAGE_SIZE*2,
+				    card->mm_pages[1].desc,
+				    card->mm_pages[1].page_dma);
+ failed_magic:
+#ifdef CONFIG_MM_MAP_MEMORY
+	iounmap((void *) card->mem_remap);
+ failed_remap_mem:
+	release_mem_region(card->mem_base, card->mem_len);
+ failed_req_mem:
+#endif
+	iounmap((void *) card->csr_base);
+ failed_remap_csr:
+	release_mem_region(card->csr_base, card->csr_len);
+ failed_req_csr:
+
+	return ret;
+}
+/*
+-----------------------------------------------------------------------------------
+--                              mm_pci_remove
+-----------------------------------------------------------------------------------
+*/
+static void mm_pci_remove(struct pci_dev *dev)
+{
+	struct cardinfo *card = dev->driver_data;
+
+	tasklet_kill(&card->tasklet);
+	iounmap(card->csr_remap);
+	release_mem_region(card->csr_base, card->csr_len);
+#ifdef CONFIG_MM_MAP_MEMORY
+	iounmap(card->mem_remap);
+	release_mem_region(card->mem_base, card->mem_len);
+#endif
+	free_irq(card->irq, card);
+
+	if (card->mm_pages[0].desc)
+		pci_free_consistent(card->dev, PAGE_SIZE*2,
+				    card->mm_pages[0].desc,
+				    card->mm_pages[0].page_dma);
+	if (card->mm_pages[1].desc)
+		pci_free_consistent(card->dev, PAGE_SIZE*2,
+				    card->mm_pages[1].desc,
+				    card->mm_pages[1].page_dma);
+}
+
+static const struct pci_device_id __devinitdata mm_pci_ids[] = { {
+	vendor:		PCI_VENDOR_ID_MICRO_MEMORY,
+	device:		PCI_DEVICE_ID_MICRO_MEMORY_5415CN,
+	}, { /* end: all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE(pci, mm_pci_ids);
+
+static struct pci_driver mm_pci_driver = {
+	name:		"umem",
+	id_table:	mm_pci_ids,
+	probe:		mm_pci_probe,
+	remove:		mm_pci_remove,
+};
+/*
+-----------------------------------------------------------------------------------
+--                               mm_init
+-----------------------------------------------------------------------------------
+*/
+int __init mm_init(void)
+{
+	int retval, i;
+	int err;
+
+	printk(KERN_INFO DRIVER_VERSION " : " DRIVER_DESC "\n");
+
+	memset (cards,    0, MM_MAXCARDS * sizeof(struct cardinfo));
+	memset (mm_sizes, 0, (MM_MAXCARDS << MM_SHIFT) * sizeof (int));
+	memset (mm_partitions, 0,
+		(MM_MAXCARDS << MM_SHIFT) * sizeof(struct hd_struct));
+
+	retval = pci_module_init(&mm_pci_driver);
+	if (retval)
+		return -ENOMEM;
+
+	err = major_nr = register_blkdev(0, "umem", &mm_fops);
+	if (err < 0) {
+		printk(KERN_ERR "MM: Could not register block device\n");
+		return -EIO;
+	}
+	devfs_handle = devfs_mk_dir(NULL, "umem", NULL);
+
+
+	/* Initialize partition size: partion 0 of each card is the entire card */
+	for (i = 0; i < num_cards; i++) {
+		mm_sizes[i << MM_SHIFT] = cards[i].mm_size;
+	}
+        mm_gendisk.sizes = mm_sizes;
+
+	for (i = 0; i < num_cards; i++) {
+		spin_lock_init(&cards[i].lock);
+		mm_partitions[i << MM_SHIFT].nr_sects =
+			cards[i].mm_size * (1024 / MM_HARDSECT);
+	}
+
+	mm_gendisk.part      = mm_partitions;
+	mm_gendisk.nr_real   = num_cards;
+
+	add_gendisk(&mm_gendisk);
+
+	blk_queue_make_request(BLK_DEFAULT_QUEUE(MAJOR_NR),
+			       mm_make_request);
+
+        blk_size[MAJOR_NR]      = mm_gendisk.sizes;
+        for (i = 0; i < num_cards; i++) {
+		register_disk(&mm_gendisk, mk_kdev(MAJOR_NR, i<<MM_SHIFT), MM_SHIFT,
+			      &mm_fops, cards[i].mm_size << 1);
+	}
+
+	init_battery_timer();
+	printk("MM: desc_per_page = %ld\n", DESC_PER_PAGE);
+/* printk("mm_init: Done. 10-19-01 9:00\n"); */
+	return 0;
+}
+/*
+-----------------------------------------------------------------------------------
+--                             mm_cleanup
+-----------------------------------------------------------------------------------
+*/
+void __exit mm_cleanup(void)
+{
+	int i;
+
+	del_battery_timer();
+
+	for (i=0; i < num_cards ; i++)
+		devfs_register_partitions(&mm_gendisk, i<<MM_SHIFT, 1);
+	if (devfs_handle)
+		devfs_unregister(devfs_handle);
+	devfs_handle = NULL;
+
+	pci_unregister_driver(&mm_pci_driver);
+
+	unregister_blkdev(MAJOR_NR, "umem");
+
+        for (i = 0; i < (num_cards << MM_SHIFT); i++)
+		invalidate_device (mk_kdev(MAJOR_NR,i), 1);
+
+	blk_size     [MAJOR_NR] = NULL;
+
+	/*
+	 * Get our gendisk structure off the list.
+	 */
+	del_gendisk(&mm_gendisk);
+}
+
+module_init(mm_init);
+module_exit(mm_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff -Nru a/drivers/char/misc.c b/drivers/char/misc.c
--- a/drivers/char/misc.c	Sat May 18 00:46:09 2002
+++ b/drivers/char/misc.c	Sat May 18 00:46:09 2002
@@ -170,7 +170,7 @@
  
 int misc_register(struct miscdevice * misc)
 {
-	static devfs_handle_t devfs_handle;
+	static devfs_handle_t devfs_handle, dir;
 	struct miscdevice *c;
 	
 	if (misc->next || misc->prev)
@@ -201,11 +201,12 @@
 		misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);
 	if (!devfs_handle)
 		devfs_handle = devfs_mk_dir (NULL, "misc", NULL);
+	dir = strchr (misc->name, '/') ? NULL : devfs_handle;
 	misc->devfs_handle =
-	    devfs_register (devfs_handle, misc->name, DEVFS_FL_NONE,
-			    MISC_MAJOR, misc->minor,
-			    S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
-			    misc->fops, NULL);
+		devfs_register (dir, misc->name, DEVFS_FL_NONE,
+				MISC_MAJOR, misc->minor,
+				S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
+				misc->fops, NULL);
 
 	/*
 	 * Add it to the front, so that later devices can "override"
diff -Nru a/drivers/char/rtc.c b/drivers/char/rtc.c
--- a/drivers/char/rtc.c	Sat May 18 00:46:09 2002
+++ b/drivers/char/rtc.c	Sat May 18 00:46:09 2002
@@ -815,7 +815,7 @@
 	}
 no_irq:
 #else
-	if (check_region (RTC_PORT (0), RTC_IO_EXTENT))
+	if (!request_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc"))
 	{
 		printk(KERN_ERR "rtc: I/O port %d is not free.\n", RTC_PORT (0));
 		return -EIO;
@@ -826,11 +826,11 @@
 	{
 		/* Yeah right, seeing as irq 8 doesn't even hit the bus. */
 		printk(KERN_ERR "rtc: IRQ %d is not free.\n", RTC_IRQ);
+		release_region(RTC_PORT(0), RTC_IO_EXTENT);
 		return -EIO;
 	}
 #endif
 
-	request_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc");
 #endif /* __sparc__ vs. others */
 
 	misc_register(&rtc_dev);
diff -Nru a/drivers/ide/Config.help b/drivers/ide/Config.help
--- a/drivers/ide/Config.help	Sat May 18 00:46:14 2002
+++ b/drivers/ide/Config.help	Sat May 18 00:46:14 2002
@@ -294,9 +294,6 @@
 
   It is normally safe to answer Y; however, the default is N.
 
-CONFIG_BLK_DEV_PDC_ADMA
-  Please read the comments at the top of <file:drivers/ide/ide-pci.c>.
-
 CONFIG_BLK_DEV_AEC62XX
   This driver adds up to 4 more EIDE devices sharing a single
   interrupt. This add-on card is a bootable PCI UDMA controller. In
diff -Nru a/drivers/ide/Config.in b/drivers/ide/Config.in
--- a/drivers/ide/Config.in	Sat May 18 00:46:15 2002
+++ b/drivers/ide/Config.in	Sat May 18 00:46:15 2002
@@ -73,7 +73,6 @@
       fi
       dep_bool '    NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415 $CONFIG_BLK_DEV_IDEDMA_PCI
       dep_mbool '    OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621 $CONFIG_PCI $CONFIG_EXPERIMENTAL
-      dep_mbool '    Pacific Digital A-DMA support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC_ADMA $CONFIG_EXPERIMENTAL
       dep_bool '    PROMISE PDC202{46|62|65|67|68|69|70} support' CONFIG_BLK_DEV_PDC202XX $CONFIG_BLK_DEV_IDEDMA_PCI
       dep_bool '      Special UDMA Feature' CONFIG_PDC202XX_BURST $CONFIG_BLK_DEV_PDC202XX
       dep_bool '      Special FastTrak Feature' CONFIG_PDC202XX_FORCE $CONFIG_BLK_DEV_PDC202XX
diff -Nru a/drivers/ide/Makefile b/drivers/ide/Makefile
--- a/drivers/ide/Makefile	Sat May 18 00:46:08 2002
+++ b/drivers/ide/Makefile	Sat May 18 00:46:08 2002
@@ -10,7 +10,7 @@
 
 O_TARGET := idedriver.o
 
-export-objs	:= ide-taskfile.o ide.o ide-features.o ide-probe.o ide-dma.o ataraid.o
+export-objs	:= ide-taskfile.o ide.o ide-features.o ide-probe.o quirks.o pcidma.o ataraid.o
 
 obj-y		:=
 obj-m		:=
@@ -43,7 +43,8 @@
 ide-obj-$(CONFIG_BLK_DEV_HPT366)	+= hpt366.o
 ide-obj-$(CONFIG_BLK_DEV_HT6560B)	+= ht6560b.o
 ide-obj-$(CONFIG_BLK_DEV_IDE_ICSIDE)	+= icside.o
-ide-obj-$(CONFIG_BLK_DEV_IDEDMA_PCI)	+= ide-dma.o
+ide-obj-$(CONFIG_BLK_DEV_IDEDMA)	+= quirks.o
+ide-obj-$(CONFIG_BLK_DEV_IDEDMA_PCI)	+= pcidma.o
 ide-obj-$(CONFIG_BLK_DEV_IDE_TCQ)	+= tcq.o
 ide-obj-$(CONFIG_PCI)			+= ide-pci.o
 ide-obj-$(CONFIG_BLK_DEV_ISAPNP)	+= ide-pnp.o
@@ -54,7 +55,6 @@
 ide-obj-$(CONFIG_BLK_DEV_SVWKS)		+= serverworks.o
 ide-obj-$(CONFIG_BLK_DEV_PDC202XX)	+= pdc202xx.o
 ide-obj-$(CONFIG_BLK_DEV_PDC4030)	+= pdc4030.o
-ide-obj-$(CONFIG_BLK_DEV_PDC_ADMA)	+= pdcadma.o
 ide-obj-$(CONFIG_BLK_DEV_PIIX)		+= piix.o
 ide-obj-$(CONFIG_BLK_DEV_QD65XX)	+= qd65xx.o
 ide-obj-$(CONFIG_BLK_DEV_IDE_RAPIDE)	+= rapide.o
@@ -72,6 +72,6 @@
 obj-$(CONFIG_BLK_DEV_ATARAID_PDC)	+= pdcraid.o
 obj-$(CONFIG_BLK_DEV_ATARAID_HPT)	+= hptraid.o
 
-ide-mod-objs		:= ide-taskfile.o ide.o ide-probe.o ide-geometry.o ide-features.o ata-timing.o $(ide-obj-y)
+ide-mod-objs	:= ide-taskfile.o ide.o ide-probe.o ide-geometry.o ide-features.o ata-timing.o $(ide-obj-y)
 
 include $(TOPDIR)/Rules.make
diff -Nru a/drivers/ide/ata-timing.c b/drivers/ide/ata-timing.c
--- a/drivers/ide/ata-timing.c	Sat May 18 00:46:09 2002
+++ b/drivers/ide/ata-timing.c	Sat May 18 00:46:09 2002
@@ -70,7 +70,7 @@
  * then to be matched agains in esp. other drives no the same channel or even
  * the whole particular host chip.
  */
-short ata_timing_mode(ide_drive_t *drive, int map)
+short ata_timing_mode(struct ata_device *drive, int map)
 {
 	struct hd_driveid *id = drive->id;
 	short best = 0;
@@ -192,7 +192,7 @@
 	return t;
 }
 
-int ata_timing_compute(ide_drive_t *drive, short speed, struct ata_timing *t,
+int ata_timing_compute(struct ata_device *drive, short speed, struct ata_timing *t,
 		int T, int UT)
 {
 	struct hd_driveid *id = drive->id;
diff -Nru a/drivers/ide/cmd640.c b/drivers/ide/cmd640.c
--- a/drivers/ide/cmd640.c	Sat May 18 00:46:09 2002
+++ b/drivers/ide/cmd640.c	Sat May 18 00:46:09 2002
@@ -155,7 +155,7 @@
 #define	CMDTIM		0x52
 #define	ARTTIM0		0x53
 #define	DRWTIM0		0x54
-#define ARTTIM1 	0x55
+#define ARTTIM1		0x55
 #define DRWTIM1		0x56
 #define ARTTIM23	0x57
 #define   ARTTIM23_DIS_RA2	0x04
@@ -166,36 +166,42 @@
 /*
  * Registers and masks for easy access by drive index:
  */
-static byte prefetch_regs[4]  = {CNTRL, CNTRL, ARTTIM23, ARTTIM23};
-static byte prefetch_masks[4] = {CNTRL_DIS_RA0, CNTRL_DIS_RA1, ARTTIM23_DIS_RA2, ARTTIM23_DIS_RA3};
+static u8 prefetch_regs[4]  = {CNTRL, CNTRL, ARTTIM23, ARTTIM23};
+static u8 prefetch_masks[4] = {CNTRL_DIS_RA0, CNTRL_DIS_RA1, ARTTIM23_DIS_RA2, ARTTIM23_DIS_RA3};
 
 #ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
 
-static byte arttim_regs[4] = {ARTTIM0, ARTTIM1, ARTTIM23, ARTTIM23};
-static byte drwtim_regs[4] = {DRWTIM0, DRWTIM1, DRWTIM23, DRWTIM23};
+/*
+ * Protects register file access from overlapping on primary and secondary
+ * channel, since those share hardware resources.
+ */
+static spinlock_t cmd640_lock __cacheline_aligned = SPIN_LOCK_UNLOCKED;
+
+static u8 arttim_regs[4] = {ARTTIM0, ARTTIM1, ARTTIM23, ARTTIM23};
+static u8 drwtim_regs[4] = {DRWTIM0, DRWTIM1, DRWTIM23, DRWTIM23};
 
 /*
  * Current cmd640 timing values for each drive.
  * The defaults for each are the slowest possible timings.
  */
-static byte setup_counts[4]    = {4, 4, 4, 4};     /* Address setup count (in clocks) */
-static byte active_counts[4]   = {16, 16, 16, 16}; /* Active count   (encoded) */
-static byte recovery_counts[4] = {16, 16, 16, 16}; /* Recovery count (encoded) */
+static u8 setup_counts[4]    = {4, 4, 4, 4};     /* Address setup count (in clocks) */
+static u8 active_counts[4]   = {16, 16, 16, 16}; /* Active count   (encoded) */
+static u8 recovery_counts[4] = {16, 16, 16, 16}; /* Recovery count (encoded) */
 
-#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+#endif
 
 /*
  * These are initialized to point at the devices we control
  */
 static struct ata_channel *cmd_hwif0, *cmd_hwif1;
-static ide_drive_t *cmd_drives[4];
+static struct ata_device *cmd_drives[4];
 
 /*
  * Interface to access cmd640x registers
  */
 static unsigned int cmd640_key;
 static void (*put_cmd640_reg)(unsigned short reg, byte val);
-static byte (*get_cmd640_reg)(unsigned short reg);
+static u8 (*get_cmd640_reg)(unsigned short reg);
 
 /*
  * This is read from the CFR reg, and is used in several places.
@@ -221,9 +227,9 @@
 	restore_flags(flags);
 }
 
-static byte get_cmd640_reg_pci1 (unsigned short reg)
+static u8 get_cmd640_reg_pci1 (unsigned short reg)
 {
-	byte b;
+	u8 b;
 	unsigned long flags;
 
 	save_flags(flags);
@@ -236,7 +242,7 @@
 
 /* PCI method 2 access (from CMD datasheet) */
 
-static void put_cmd640_reg_pci2 (unsigned short reg, byte val)
+static void put_cmd640_reg_pci2 (unsigned short reg, u8 val)
 {
 	unsigned long flags;
 
@@ -248,9 +254,9 @@
 	restore_flags(flags);
 }
 
-static byte get_cmd640_reg_pci2 (unsigned short reg)
+static u8 get_cmd640_reg_pci2 (unsigned short reg)
 {
-	byte b;
+	u8 b;
 	unsigned long flags;
 
 	save_flags(flags);
@@ -264,7 +270,7 @@
 
 /* VLB access */
 
-static void put_cmd640_reg_vlb (unsigned short reg, byte val)
+static void put_cmd640_reg_vlb (unsigned short reg, u8 val)
 {
 	unsigned long flags;
 
@@ -275,9 +281,9 @@
 	restore_flags(flags);
 }
 
-static byte get_cmd640_reg_vlb (unsigned short reg)
+static u8 get_cmd640_reg_vlb (unsigned short reg)
 {
-	byte b;
+	u8 b;
 	unsigned long flags;
 
 	save_flags(flags);
@@ -290,7 +296,7 @@
 
 static int __init match_pci_cmd640_device (void)
 {
-	const byte ven_dev[4] = {0x95, 0x10, 0x40, 0x06};
+	const u8 ven_dev[4] = {0x95, 0x10, 0x40, 0x06};
 	unsigned int i;
 	for (i = 0; i < 4; i++) {
 		if (get_cmd640_reg(i) != ven_dev[i])
@@ -338,7 +344,7 @@
  */
 static int __init probe_for_cmd640_vlb (void)
 {
-	byte b;
+	u8 b;
 
 	get_cmd640_reg = get_cmd640_reg_vlb;
 	put_cmd640_reg = put_cmd640_reg_vlb;
@@ -404,7 +410,7 @@
 static void __init check_prefetch (unsigned int index)
 {
 	struct ata_device *drive = cmd_drives[index];
-	byte b = get_cmd640_reg(prefetch_regs[index]);
+	u8 b = get_cmd640_reg(prefetch_regs[index]);
 
 	if (b & prefetch_masks[index]) {	/* is prefetch off? */
 		drive->channel->no_unmask = 0;
@@ -450,19 +456,19 @@
  */
 static void set_prefetch_mode (unsigned int index, int mode)
 {
-	ide_drive_t *drive = cmd_drives[index];
+	struct ata_device *drive = cmd_drives[index];
 	int reg = prefetch_regs[index];
-	byte b;
+	u8 b;
 	unsigned long flags;
 
 	save_flags(flags);
 	cli();
 	b = get_cmd640_reg(reg);
 	if (mode) {	/* want prefetch on? */
-#if CMD640_PREFETCH_MASKS
+# if CMD640_PREFETCH_MASKS
 		drive->channel->no_unmask = 1;
 		drive->channel->unmask = 0;
-#endif
+# endif
 		drive->channel->no_io_32bit = 0;
 		b &= ~prefetch_masks[index];	/* enable prefetch */
 	} else {
@@ -480,7 +486,7 @@
  */
 static void display_clocks (unsigned int index)
 {
-	byte active_count, recovery_count;
+	u8 active_count, recovery_count;
 
 	active_count = active_counts[index];
 	if (active_count == 1)
@@ -497,7 +503,7 @@
  * Pack active and recovery counts into single byte representation
  * used by controller
  */
-inline static byte pack_nibbles (byte upper, byte lower)
+static inline u8 pack_nibbles (u8 upper, u8 lower)
 {
 	return ((upper & 0x0f) << 4) | (lower & 0x0f);
 }
@@ -507,7 +513,7 @@
  */
 static void __init retrieve_drive_counts (unsigned int index)
 {
-	byte b;
+	u8 b;
 
 	/*
 	 * Get the internal setup timing, and convert to clock count
@@ -537,9 +543,9 @@
 static void program_drive_counts (unsigned int index)
 {
 	unsigned long flags;
-	byte setup_count    = setup_counts[index];
-	byte active_count   = active_counts[index];
-	byte recovery_count = recovery_counts[index];
+	u8 setup_count    = setup_counts[index];
+	u8 active_count   = active_counts[index];
+	u8 recovery_count = recovery_counts[index];
 
 	/*
 	 * Set up address setup count and drive read/write timing registers.
@@ -589,10 +595,12 @@
 /*
  * Set a specific pio_mode for a drive
  */
-static void cmd640_set_mode (unsigned int index, byte pio_mode, unsigned int cycle_time, unsigned int active_time, unsigned int setup_time)
+static void cmd640_set_mode (unsigned int index, u8 pio_mode, unsigned int cycle_time, unsigned int active_time, unsigned int setup_time)
 {
 	int recovery_time, clock_time;
-	byte setup_count, active_count, recovery_count, recovery_count2, cycle_count;
+	u8 setup_count, active_count;
+	u8 recovery_count, recovery_count2;
+	u8 cycle_count;
 
 	recovery_time = cycle_time - (setup_time + active_time);
 	clock_time = 1000 / system_bus_speed;
@@ -639,16 +647,19 @@
 /*
  * Drive PIO mode selection:
  */
-static void cmd640_tune_drive (ide_drive_t *drive, byte mode_wanted)
+static void cmd640_tune_drive(struct ata_device *drive, byte mode_wanted)
 {
-	byte b;
+	u8 b;
 	struct ata_timing *t;
 	unsigned int index = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cmd640_lock, flags);
 
 	while (drive != cmd_drives[index]) {
 		if (++index > 3) {
-			printk("%s: bad news in cmd640_tune_drive\n", drive->name);
-			return;
+			printk(KERN_ERR "%s: bad news in cmd640_tune_drive\n", drive->name);
+			goto out_lock;
 		}
 	}
 	switch (mode_wanted) {
@@ -659,21 +670,21 @@
 			if (mode_wanted)
 				b |= 0x27;
 			put_cmd640_reg(CNTRL, b);
-			printk("%s: %sabled cmd640 fast host timing (devsel)\n", drive->name, mode_wanted ? "en" : "dis");
-			return;
+			printk(KERN_INFO "%s: %sabled cmd640 fast host timing (devsel)\n", drive->name, mode_wanted ? "en" : "dis");
+			goto out_lock;
 
 		case 8: /* set prefetch off */
 		case 9: /* set prefetch on */
 			mode_wanted &= 1;
 			set_prefetch_mode(index, mode_wanted);
 			printk("%s: %sabled cmd640 prefetch\n", drive->name, mode_wanted ? "en" : "dis");
-			return;
+			goto out_lock;
 	}
 
 	if (mode_wanted == 255)
 		t = ata_timing_data(ata_timing_mode(drive, XFER_PIO | XFER_EPIO));
 	else
-		t = ata_timing_data(XFER_PIO_0 + min_t(byte, mode_wanted, 4));
+		t = ata_timing_data(XFER_PIO_0 + min_t(u8, mode_wanted, 4));
 
 	cmd640_set_mode(index, t->mode - XFER_PIO_0, t->cycle, t->active, t->setup);
 
@@ -681,10 +692,14 @@
 		drive->name, t->mode, t->cycle);
 
 	display_clocks(index);
+
+out_lock:
+	spin_unlock_irqrestore(&cmd640_lock, flags);
+
 	return;
 }
 
-#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+#endif
 
 /*
  * Probe for a cmd640 chipset, and initialize it if found.  Called from ide.c
@@ -697,7 +712,7 @@
 	int second_port_cmd640 = 0;
 	const char *bus_type, *port2;
 	unsigned int index;
-	byte b, cfr;
+	u8 b, cfr;
 
 	if (cmd640_vlb && probe_for_cmd640_vlb()) {
 		bus_type = "VLB";
@@ -743,7 +758,7 @@
 	cmd_hwif0->chipset = ide_cmd640;
 #ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
 	cmd_hwif0->tuneproc = &cmd640_tune_drive;
-#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+#endif
 
 	/*
 	 * Ensure compatibility by always using the slowest timings
@@ -777,7 +792,7 @@
 				second_port_cmd640 = 1;
 #ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
 				second_port_toggled = 1;
-#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+#endif
 				port2 = "enabled";
 			} else {
 				put_cmd640_reg(CNTRL, b); /* restore original setting */
@@ -796,7 +811,7 @@
 		cmd_hwif1->unit = ATA_SECONDARY;
 #ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
 		cmd_hwif1->tuneproc = &cmd640_tune_drive;
-#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+#endif
 	}
 	printk("%s: %sserialized, secondary interface %s\n", cmd_hwif1->name,
 		cmd_hwif0->serialized ? "" : "not ", port2);
@@ -806,7 +821,7 @@
 	 * Do not unnecessarily disturb any prior BIOS setup of these.
 	 */
 	for (index = 0; index < (2 + (second_port_cmd640 << 1)); index++) {
-		ide_drive_t *drive = cmd_drives[index];
+		struct ata_device *drive = cmd_drives[index];
 #ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
 		if (drive->autotune || ((index > 1) && second_port_toggled)) {
 			/*
@@ -837,7 +852,7 @@
 		check_prefetch (index);
 		printk("cmd640: drive%d timings/prefetch(%s) preserved\n",
 			index, drive->channel->no_io_32bit ? "off" : "on");
-#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+#endif
 	}
 
 #ifdef CMD640_DUMP_REGS
diff -Nru a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c
--- a/drivers/ide/hpt366.c	Sat May 18 00:46:08 2002
+++ b/drivers/ide/hpt366.c	Sat May 18 00:46:08 2002
@@ -670,8 +670,8 @@
 	 * Disable the "fast interrupt" prediction.
 	 */
 	pci_read_config_byte(dev, regfast, &drive_fast);
-	if (drive_fast & 0x02)
-		pci_write_config_byte(dev, regfast, drive_fast & ~0x20);
+	if (drive_fast & 0x80)
+		pci_write_config_byte(dev, regfast, drive_fast & ~0x80);
 
 	pci_read_config_dword(dev, regtime, &reg1);
 	reg2 = pci_bus_clock_list(speed,
diff -Nru a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
--- a/drivers/ide/ide-cd.c	Sat May 18 00:46:09 2002
+++ b/drivers/ide/ide-cd.c	Sat May 18 00:46:09 2002
@@ -594,7 +594,7 @@
 		pc = (struct packet_command *) rq->special;
 		pc->stat = 1;
 		cdrom_end_request(drive, rq, 1);
-		*startstop = ide_error (drive, "request sense failure", stat);
+		*startstop = ide_error (drive, rq, "request sense failure", stat);
 
 		return 1;
 	} else if (rq->flags & (REQ_PC | REQ_BLOCK_PC)) {
@@ -614,7 +614,7 @@
 			return 0;
 		} else if (!pc->quiet) {
 			/* Otherwise, print an error. */
-			ide_dump_status(drive, "packet command error", stat);
+			ide_dump_status(drive, rq, "packet command error", stat);
 		}
 
 		/* Set the error flag and complete the request.
@@ -662,18 +662,18 @@
 			   sense_key == DATA_PROTECT) {
 			/* No point in retrying after an illegal
 			   request or data protect error.*/
-			ide_dump_status (drive, "command error", stat);
+			ide_dump_status(drive, rq, "command error", stat);
 			cdrom_end_request(drive, rq,  0);
 		} else if (sense_key == MEDIUM_ERROR) {
 			/* No point in re-trying a zillion times on a bad
 			 * sector.  The error is not correctable at all.
 			 */
-			ide_dump_status (drive, "media error (bad sector)", stat);
+			ide_dump_status(drive, rq, "media error (bad sector)", stat);
 			cdrom_end_request(drive, rq, 0);
 		} else if ((err & ~ABRT_ERR) != 0) {
 			/* Go to the default handler
 			   for other errors. */
-			*startstop = ide_error (drive, __FUNCTION__, stat);
+			*startstop = ide_error(drive, rq, __FUNCTION__, stat);
 			return 1;
 		} else if ((++rq->errors > ERROR_MAX)) {
 			/* We've racked up too many retries.  Abort. */
@@ -732,7 +732,7 @@
 	struct cdrom_info *info = drive->driver_data;
 
 	/* Wait for the controller to be idle. */
-	if (ide_wait_stat(&startstop, drive, 0, BUSY_STAT, WAIT_READY))
+	if (ide_wait_stat(&startstop, drive, rq, 0, BUSY_STAT, WAIT_READY))
 		return startstop;
 
 	if (info->dma) {
@@ -789,7 +789,7 @@
 			return startstop;
 	} else {
 		/* Otherwise, we must wait for DRQ to get set. */
-		if (ide_wait_stat(&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY))
+		if (ide_wait_stat(&startstop, drive, rq, DRQ_STAT, BUSY_STAT, WAIT_READY))
 			return startstop;
 	}
 
@@ -917,7 +917,7 @@
 			__ide_end_request(drive, rq, 1, rq->nr_sectors);
 			return ide_stopped;
 		} else
-			return ide_error (drive, "dma error", stat);
+			return ide_error (drive, rq, "dma error", stat);
 	}
 
 	/* Read the interrupt reason and the transfer length. */
@@ -1496,7 +1496,7 @@
 	 */
 	if (dma) {
 		if (dma_error)
-			return ide_error(drive, "dma error", stat);
+			return ide_error(drive, rq, "dma error", stat);
 
 		__ide_end_request(drive, rq, 1, rq->nr_sectors);
 		return ide_stopped;
@@ -2659,12 +2659,6 @@
 	return nslots;
 }
 
-static void ide_cdrom_add_settings(ide_drive_t *drive)
-{
-	ide_add_setting(drive, "dsc_overlap",
-			SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1,	1, &drive->dsc_overlap, NULL);
-}
-
 static
 int ide_cdrom_setup(ide_drive_t *drive)
 {
@@ -2798,7 +2792,7 @@
 		info->devinfo.handle = NULL;
 		return 1;
 	}
-	ide_cdrom_add_settings(drive);
+
 	return 0;
 }
 
diff -Nru a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c
--- a/drivers/ide/ide-disk.c	Sat May 18 00:46:08 2002
+++ b/drivers/ide/ide-disk.c	Sat May 18 00:46:08 2002
@@ -316,7 +316,7 @@
 		unsigned long flags;
 		int ret;
 
-		spin_lock_irqsave(&ide_lock, flags);
+		spin_lock_irqsave(drive->channel->lock, flags);
 
 		ret = blk_queue_start_tag(&drive->queue, rq);
 
@@ -325,7 +325,7 @@
 		if (ata_pending_commands(drive) > drive->max_last_depth)
 			drive->max_last_depth = ata_pending_commands(drive);
 
-		spin_unlock_irqrestore(&ide_lock, flags);
+		spin_unlock_irqrestore(drive->channel->lock, flags);
 
 		if (ret) {
 			BUG_ON(!ata_pending_commands(drive));
@@ -438,13 +438,6 @@
 	if (!drive->id)
 		return -EIO;
 
-	/* FIXME: Hmm... just bailing out my be problematic, since there *is*
-	 * activity during boot. For now the same problem persists in
-	 * set_pio_mode() we will have to do something about it soon.
-	 */
-	if (HWGROUP(drive)->handler)
-		return -EBUSY;
-
 	if (arg > drive->id->max_multsect)
 		arg = drive->id->max_multsect;
 
@@ -466,18 +459,9 @@
 
 static int set_nowerr(struct ata_device *drive, int arg)
 {
-	if (HWGROUP(drive)->handler)
-		return -EBUSY;
-
 	drive->nowerr = arg;
 	drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT;
 
-	/* FIXME: I'm less then sure that we are under the global request lock here!
-	 */
-#if 0
-	spin_unlock_irq(&ide_lock);
-#endif
-
 	return 0;
 }
 
@@ -531,8 +515,10 @@
 {
 	if (!drive->driver)
 		return -EPERM;
+
 	if (!drive->channel->XXX_udma)
 		return -EPERM;
+
 	if (arg == drive->queue_depth && drive->using_tcq)
 		return 0;
 
@@ -568,20 +554,6 @@
 	return (probe_lba_addressing(drive, arg));
 }
 
-static void idedisk_add_settings(struct ata_device *drive)
-{
-	struct hd_driveid *id = drive->id;
-
-	ide_add_setting(drive,	"address",		SETTING_RW,					HDIO_GET_ADDRESS,	HDIO_SET_ADDRESS,	TYPE_INTA,	0,	2,				1,	1,	&drive->addressing,	set_lba_addressing);
-	ide_add_setting(drive,	"multcount",		id ? SETTING_RW : SETTING_READ,			HDIO_GET_MULTCOUNT,	HDIO_SET_MULTCOUNT,	TYPE_BYTE,	0,	id ? id->max_multsect : 0,	1,	1,	&drive->mult_count,		set_multcount);
-	ide_add_setting(drive,	"nowerr",		SETTING_RW,					HDIO_GET_NOWERR,	HDIO_SET_NOWERR,	TYPE_BYTE,	0,	1,				1,	1,	&drive->nowerr,			set_nowerr);
-	ide_add_setting(drive,	"wcache",		SETTING_RW,					HDIO_GET_WCACHE,	HDIO_SET_WCACHE,	TYPE_BYTE,	0,	1,				1,	1,	&drive->wcache,			write_cache);
-	ide_add_setting(drive,	"acoustic",		SETTING_RW,					HDIO_GET_ACOUSTIC,	HDIO_SET_ACOUSTIC,	TYPE_BYTE,	0,	254,				1,	1,	&drive->acoustic,		set_acoustic);
-#ifdef CONFIG_BLK_DEV_IDE_TCQ
-	ide_add_setting(drive,	"using_tcq",		SETTING_RW,					HDIO_GET_QDMA,		HDIO_SET_QDMA,		TYPE_BYTE,	0,	IDE_MAX_TAG,			1,		1,		&drive->using_tcq,		set_using_tcq);
-#endif
-}
-
 static int idedisk_suspend(struct device *dev, u32 state, u32 level)
 {
 	struct ata_device *drive = dev->driver_data;
@@ -594,8 +566,8 @@
 		return 0;
 
 	/* wait until all commands are finished */
-	printk("ide_disk_suspend()\n");
-	while (HWGROUP(drive)->handler)
+	/* FIXME: waiting for spinlocks should be done instead. */
+	while (drive->channel->handler)
 		yield();
 
 	/* set the drive to standby */
@@ -624,9 +596,6 @@
 
 
 /* This is just a hook for the overall driver tree.
- *
- * FIXME: This is soon goig to replace the custom linked list games played up
- * to great extend between the different components of the IDE drivers.
  */
 
 static struct device_driver idedisk_devdrv = {
@@ -783,8 +752,6 @@
 	sector_t set_max;
 	int drvid = -1;
 
-	idedisk_add_settings(drive);
-
 	if (id == NULL)
 		return;
 
@@ -1022,8 +989,164 @@
 	return ide_unregister_subdriver(drive);
 }
 
+static int idedisk_ioctl(struct ata_device *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct hd_driveid *id = drive->id;
+
+	switch (cmd) {
+		case HDIO_GET_ADDRESS: {
+			unsigned long val = drive->addressing;
+
+			if (put_user(val, (unsigned long *) arg))
+				return -EFAULT;
+			return 0;
+		}
+
+		case HDIO_SET_ADDRESS: {
+			int val;
+
+			if (arg < 0 || arg > 2)
+				return -EINVAL;
+
+			if (ide_spin_wait_hwgroup(drive))
+				return -EBUSY;
+
+			val = set_lba_addressing(drive, arg);
+			spin_unlock_irq(drive->channel->lock);
+
+			return val;
+		}
+
+		case HDIO_GET_MULTCOUNT: {
+			unsigned long val = drive->mult_count & 0xFF;
+
+			if (put_user(val, (unsigned long *) arg))
+				return -EFAULT;
+			return 0;
+		}
+
+		case HDIO_SET_MULTCOUNT: {
+			int val;
+
+			if (!id)
+				return -EBUSY;
+
+			if (arg < 0 || arg > (id ? id->max_multsect : 0))
+				return -EINVAL;
+
+			if (ide_spin_wait_hwgroup(drive))
+				return -EBUSY;
+
+			val = set_multcount(drive, arg);
+			spin_unlock_irq(drive->channel->lock);
+
+			return val;
+		}
+
+		case HDIO_GET_NOWERR: {
+			unsigned long val = drive->nowerr;
+
+			if (put_user(val, (unsigned long *) arg))
+				return -EFAULT;
+
+			return 0;
+		}
+
+		case HDIO_SET_NOWERR: {
+			int val;
+
+			if (arg < 0 || arg > 1)
+				return -EINVAL;
+
+			if (ide_spin_wait_hwgroup(drive))
+				return -EBUSY;
+
+			val = set_nowerr(drive, arg);
+			spin_unlock_irq(drive->channel->lock);
+
+			return val;
+		}
+
+		case HDIO_GET_WCACHE: {
+			unsigned long val = drive->wcache;
+
+			if (put_user(val, (unsigned long *) arg))
+				return -EFAULT;
+
+			return 0;
+		}
+
+		case HDIO_SET_WCACHE: {
+			int val;
+
+			if (arg < 0 || arg > 1)
+				return -EINVAL;
+
+			if (ide_spin_wait_hwgroup(drive))
+				return -EBUSY;
+
+			val = write_cache(drive, arg);
+			spin_unlock_irq(drive->channel->lock);
+
+			return val;
+		}
+
+		case HDIO_GET_ACOUSTIC: {
+			unsigned long val = drive->acoustic;
+
+			if (put_user(val, (u8 *) arg))
+				return -EFAULT;
+			return 0;
+		}
+
+		case HDIO_SET_ACOUSTIC: {
+			int val;
+
+			if (arg < 0 || arg > 254)
+				return -EINVAL;
+
+			if (ide_spin_wait_hwgroup(drive))
+				return -EBUSY;
+
+			val = set_acoustic(drive, arg);
+			spin_unlock_irq(drive->channel->lock);
+
+			return val;
+		}
+
+#ifdef CONFIG_BLK_DEV_IDE_TCQ
+		case HDIO_GET_QDMA: {
+			u8 val = drive->using_tcq;
+
+			if (put_user(val, (u8 *) arg))
+				return -EFAULT;
+
+			return 0;
+		}
+
+		case HDIO_SET_QDMA: {
+			int val;
+
+			if (arg < 0 || arg > IDE_MAX_TAG)
+				return -EINVAL;
+
+			if (ide_spin_wait_hwgroup(drive))
+				return -EBUSY;
+
+			val = set_using_tcq(drive, arg);
+			spin_unlock_irq(drive->channel->lock);
+
+			return val;
+		}
+#endif
+		default:
+			return -EINVAL;
+	}
+}
+
+
 /*
- *      IDE subdriver functions, registered with ide.c
+ * Subdriver functions.
  */
 static struct ata_operations idedisk_driver = {
 	owner:			THIS_MODULE,
@@ -1031,7 +1154,7 @@
 	standby:		idedisk_standby,
 	do_request:		idedisk_do_request,
 	end_request:		NULL,
-	ioctl:			NULL,
+	ioctl:			idedisk_ioctl,
 	open:			idedisk_open,
 	release:		idedisk_release,
 	check_media_change:	idedisk_check_media_change,
@@ -1048,11 +1171,9 @@
 
 	while ((drive = ide_scan_devices(ATA_DISK, "ide-disk", &idedisk_driver, failed)) != NULL) {
 		if (idedisk_cleanup (drive)) {
-			printk (KERN_ERR "%s: cleanup_module() called while still busy\n", drive->name);
-			failed++;
+			printk(KERN_ERR "%s: cleanup_module() called while still busy\n", drive->name);
+			++failed;
 		}
-		/* We must remove proc entries defined in this module.
-		   Otherwise we oops while accessing these entries */
 	}
 }
 
@@ -1073,10 +1194,11 @@
 			idedisk_cleanup(drive);
 			continue;
 		}
-		failed--;
+		--failed;
 	}
 	revalidate_drives();
 	MOD_DEC_USE_COUNT;
+
 	return 0;
 }
 
diff -Nru a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c
--- a/drivers/ide/ide-dma.c	Sat May 18 00:46:08 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,866 +0,0 @@
-/**** vi:set ts=8 sts=8 sw=8:************************************************
- *
- *  Copyright (c) 1999-2000  Andre Hedrick <andre@linux-ide.org>
- *  Copyright (c) 1995-1998  Mark Lord
- *
- *  May be copied or modified under the terms of the GNU General Public License
- *
- *  Special Thanks to Mark for his Six years of work.
- *
- * This module provides support for the bus-master IDE DMA functions
- * of various PCI chipsets, including the Intel PIIX (i82371FB for
- * the 430 FX chipset), the PIIX3 (i82371SB for the 430 HX/VX and
- * 440 chipsets), and the PIIX4 (i82371AB for the 430 TX chipset)
- * ("PIIX" stands for "PCI ISA IDE Xcellerator").
- *
- * Pretty much the same code works for other IDE PCI bus-mastering chipsets.
- *
- * DMA is supported for all IDE devices (disk drives, cdroms, tapes, floppies).
- *
- * By default, DMA support is prepared for use, but is currently enabled only
- * for drives which already have DMA enabled (UltraDMA or mode 2 multi/single),
- * or which are recognized as "good" (see table below).  Drives with only mode0
- * or mode1 (multi/single) DMA should also work with this chipset/driver
- * (eg. MC2112A) but are not enabled by default.
- *
- * Use "hdparm -i" to view modes supported by a given drive.
- *
- * The hdparm-3.5 (or later) utility can be used for manually enabling/disabling
- * DMA support, but must be (re-)compiled against this kernel version or later.
- *
- * To enable DMA, use "hdparm -d1 /dev/hd?" on a per-drive basis after booting.
- * If problems arise, ide.c will disable DMA operation after a few retries.
- * This error recovery mechanism works and has been extremely well exercised.
- *
- * IDE drives, depending on their vintage, may support several different modes
- * of DMA operation.  The boot-time modes are indicated with a "*" in
- * the "hdparm -i" listing, and can be changed with *knowledgeable* use of
- * the "hdparm -X" feature.  There is seldom a need to do this, as drives
- * normally power-up with their "best" PIO/DMA modes enabled.
- *
- * Testing has been done with a rather extensive number of drives,
- * with Quantum & Western Digital models generally outperforming the pack,
- * and Fujitsu & Conner (and some Seagate which are really Conner) drives
- * showing more lackluster throughput.
- *
- * Keep an eye on /var/adm/messages for "DMA disabled" messages.
- *
- * Some people have reported trouble with Intel Zappa motherboards.
- * This can be fixed by upgrading the AMI BIOS to version 1.00.04.BS0,
- * available from ftp://ftp.intel.com/pub/bios/10004bs0.exe
- * (thanks to Glen Morrell <glen@spin.Stanford.edu> for researching this).
- *
- * Thanks to "Christopher J. Reimer" <reimer@doe.carleton.ca> for
- * fixing the problem with the BIOS on some Acer motherboards.
- *
- * Thanks to "Benoit Poulot-Cazajous" <poulot@chorus.fr> for testing
- * "TX" chipset compatibility and for providing patches for the "TX" chipset.
- *
- * Thanks to Christian Brunner <chb@muc.de> for taking a good first crack
- * at generic DMA -- his patches were referred to when preparing this code.
- *
- * Most importantly, thanks to Robert Bringman <rob@mars.trion.com>
- * for supplying a Promise UDMA board & WD UDMA drive for this work!
- *
- * And, yes, Intel Zappa boards really *do* use both PIIX IDE ports.
- *
- * ATA-66/100 and recovery functions, I forgot the rest......
- */
-
-#include <linux/config.h>
-#define __NO_VERSION__
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/ide.h>
-#include <linux/delay.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-
-/*
- * Long lost data from 2.0.34 that is now in 2.0.39
- *
- * This was used in ./drivers/block/triton.c to do DMA Base address setup
- * when PnP failed.  Oh the things we forget.  I believe this was part
- * of SFF-8038i that has been withdrawn from public access... :-((
- */
-#define DEFAULT_BMIBA	0xe800	/* in case BIOS did not init it */
-#define DEFAULT_BMCRBA	0xcc00	/* VIA's default value */
-#define DEFAULT_BMALIBA	0xd400	/* ALI's default value */
-
-#ifdef CONFIG_IDEDMA_NEW_DRIVE_LISTINGS
-
-struct drive_list_entry {
-	char * id_model;
-	char * id_firmware;
-};
-
-struct drive_list_entry drive_whitelist[] = {
-	{ "Micropolis 2112A", NULL },
-	{ "CONNER CTMA 4000", NULL },
-	{ "CONNER CTT8000-A", NULL },
-	{ "ST34342A", NULL },
-	{ NULL, NULL }
-};
-
-struct drive_list_entry drive_blacklist[] = {
-
-	{ "WDC AC11000H", NULL },
-	{ "WDC AC22100H", NULL },
-	{ "WDC AC32500H", NULL },
-	{ "WDC AC33100H", NULL },
-	{ "WDC AC31600H", NULL },
-	{ "WDC AC32100H", "24.09P07" },
-	{ "WDC AC23200L", "21.10N21" },
-	{ "Compaq CRD-8241B", NULL },
-	{ "CRD-8400B", NULL },
-	{ "CRD-8480B", NULL },
-	{ "CRD-8480C", NULL },
-	{ "CRD-8482B", NULL },
-	{ "CRD-84", NULL },
-	{ "SanDisk SDP3B", NULL },
-	{ "SanDisk SDP3B-64", NULL },
-	{ "SANYO CD-ROM CRD", NULL },
-	{ "HITACHI CDR-8", NULL },
-	{ "HITACHI CDR-8335", NULL },
-	{ "HITACHI CDR-8435", NULL },
-	{ "Toshiba CD-ROM XM-6202B", NULL },
-	{ "CD-532E-A", NULL },
-	{ "E-IDE CD-ROM CR-840", NULL },
-	{ "CD-ROM Drive/F5A", NULL },
-	{ "RICOH CD-R/RW MP7083A", NULL },
-	{ "WPI CDD-820", NULL },
-	{ "SAMSUNG CD-ROM SC-148C", NULL },
-	{ "SAMSUNG CD-ROM SC-148F", NULL },
-	{ "SAMSUNG CD-ROM SC", NULL },
-	{ "SanDisk SDP3B-64", NULL },
-	{ "SAMSUNG CD-ROM SN-124", NULL },
-	{ "PLEXTOR CD-R PX-W8432T", NULL },
-	{ "ATAPI CD-ROM DRIVE 40X MAXIMUM", NULL },
-	{ "_NEC DV5800A", NULL },
-	{ NULL,	NULL }
-
-};
-
-static int in_drive_list(struct hd_driveid *id, struct drive_list_entry * drive_table)
-{
-	for ( ; drive_table->id_model ; drive_table++)
-		if ((!strcmp(drive_table->id_model, id->model)) &&
-		    ((drive_table->id_firmware && !strstr(drive_table->id_firmware, id->fw_rev)) ||
-		     (!drive_table->id_firmware)))
-			return 1;
-	return 0;
-}
-
-#else
-
-/*
- * good_dma_drives() lists the model names (from "hdparm -i")
- * of drives which do not support mode2 DMA but which are
- * known to work fine with this interface under Linux.
- */
-const char *good_dma_drives[] = {"Micropolis 2112A",
-				 "CONNER CTMA 4000",
-				 "CONNER CTT8000-A",
-				 "ST34342A",	/* for Sun Ultra */
-				 NULL};
-
-/*
- * bad_dma_drives() lists the model names (from "hdparm -i")
- * of drives which supposedly support (U)DMA but which are
- * known to corrupt data with this interface under Linux.
- *
- * This is an empirical list. Its generated from bug reports. That means
- * while it reflects actual problem distributions it doesn't answer whether
- * the drive or the controller, or cabling, or software, or some combination
- * thereof is the fault. If you don't happen to agree with the kernel's
- * opinion of your drive - use hdparm to turn DMA on.
- */
-const char *bad_dma_drives[] = {"WDC AC11000H",
-				"WDC AC22100H",
-				"WDC AC32100H",
-				"WDC AC32500H",
-				"WDC AC33100H",
-				"WDC AC31600H",
-				NULL};
-
-#endif
-
-/*
- * This is the handler for disk read/write DMA interrupts.
- */
-ide_startstop_t ide_dma_intr(struct ata_device *drive, struct request *rq)
-{
-	u8 stat, dma_stat;
-
-	dma_stat = udma_stop(drive);
-	if (OK_STAT(stat = GET_STAT(),DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
-		if (!dma_stat) {
-			__ide_end_request(drive, rq, 1, rq->nr_sectors);
-			return ide_stopped;
-		}
-		printk(KERN_ERR "%s: dma_intr: bad DMA status (dma_stat=%x)\n",
-		       drive->name, dma_stat);
-	}
-	return ide_error(drive, "dma_intr", stat);
-}
-
-/*
- * FIXME: taskfiles should be a map of pages, not a long virt address... /jens
- * FIXME: I agree with Jens --mdcki!
- */
-static int build_sglist(struct ata_channel *ch, struct request *rq)
-{
-	struct scatterlist *sg = ch->sg_table;
-	int nents = 0;
-
-	if (rq->flags & REQ_DRIVE_ACB) {
-		struct ata_taskfile *args = rq->special;
-#if 1
-		unsigned char *virt_addr = rq->buffer;
-		int sector_count = rq->nr_sectors;
-#else
-		nents = blk_rq_map_sg(rq->q, rq, ch->sg_table);
-
-		if (nents > rq->nr_segments)
-			printk("ide-dma: received %d segments, build %d\n", rq->nr_segments, nents);
-#endif
-
-		if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE)
-			ch->sg_dma_direction = PCI_DMA_TODEVICE;
-		else
-			ch->sg_dma_direction = PCI_DMA_FROMDEVICE;
-
-		/*
-		 * FIXME: This depends upon a hard coded page size!
-		 */
-		if (sector_count > 128) {
-			memset(&sg[nents], 0, sizeof(*sg));
-
-			sg[nents].page = virt_to_page(virt_addr);
-			sg[nents].offset = (unsigned long) virt_addr & ~PAGE_MASK;
-			sg[nents].length = 128  * SECTOR_SIZE;
-			++nents;
-			virt_addr = virt_addr + (128 * SECTOR_SIZE);
-			sector_count -= 128;
-		}
-		memset(&sg[nents], 0, sizeof(*sg));
-		sg[nents].page = virt_to_page(virt_addr);
-		sg[nents].offset = (unsigned long) virt_addr & ~PAGE_MASK;
-		sg[nents].length =  sector_count  * SECTOR_SIZE;
-		++nents;
-	} else {
-		nents = blk_rq_map_sg(rq->q, rq, ch->sg_table);
-
-		if (rq->q && nents > rq->nr_phys_segments)
-			printk("ide-dma: received %d phys segments, build %d\n", rq->nr_phys_segments, nents);
-
-		if (rq_data_dir(rq) == READ)
-			ch->sg_dma_direction = PCI_DMA_FROMDEVICE;
-		else
-			ch->sg_dma_direction = PCI_DMA_TODEVICE;
-
-	}
-	return pci_map_sg(ch->pci_dev, sg, nents, ch->sg_dma_direction);
-}
-
-/*
- *  For both Blacklisted and Whitelisted drives.
- *  This is setup to be called as an extern for future support
- *  to other special driver code.
- */
-int check_drive_lists(struct ata_device *drive, int good_bad)
-{
-	struct hd_driveid *id = drive->id;
-
-#ifdef CONFIG_IDEDMA_NEW_DRIVE_LISTINGS
-	if (good_bad) {
-		return in_drive_list(id, drive_whitelist);
-	} else {
-		int blacklist = in_drive_list(id, drive_blacklist);
-		if (blacklist)
-			printk("%s: Disabling (U)DMA for %s\n", drive->name, id->model);
-		return(blacklist);
-	}
-#else
-	const char **list;
-
-	if (good_bad) {
-		/* Consult the list of known "good" drives */
-		list = good_dma_drives;
-		while (*list) {
-			if (!strcmp(*list++,id->model))
-				return 1;
-		}
-	} else {
-		/* Consult the list of known "bad" drives */
-		list = bad_dma_drives;
-		while (*list) {
-			if (!strcmp(*list++,id->model)) {
-				printk("%s: Disabling (U)DMA for %s\n",
-					drive->name, id->model);
-				return 1;
-			}
-		}
-	}
-#endif
-	return 0;
-}
-
-static int config_drive_for_dma(struct ata_device *drive)
-{
-	int config_allows_dma = 1;
-	struct hd_driveid *id = drive->id;
-	struct ata_channel *ch = drive->channel;
-
-#ifdef CONFIG_IDEDMA_ONLYDISK
-	if (drive->type != ATA_DISK)
-		config_allows_dma = 0;
-#endif
-
-	if (id && (id->capability & 1) && ch->autodma && config_allows_dma) {
-		/* Consult the list of known "bad" drives */
-		if (udma_black_list(drive)) {
-			udma_enable(drive, 0, 1);
-
-			return 0;
-		}
-
-		/* Enable DMA on any drive that has UltraDMA (mode 6/7/?) enabled */
-		if ((id->field_valid & 4) && (eighty_ninty_three(drive)))
-			if ((id->dma_ultra & (id->dma_ultra >> 14) & 2)) {
-				udma_enable(drive, 1, 1);
-
-				return 0;
-			}
-		/* Enable DMA on any drive that has UltraDMA (mode 3/4/5) enabled */
-		if ((id->field_valid & 4) && (eighty_ninty_three(drive)))
-			if ((id->dma_ultra & (id->dma_ultra >> 11) & 7)) {
-				udma_enable(drive, 1, 1);
-
-				return 0;
-			}
-		/* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */
-		if (id->field_valid & 4)	/* UltraDMA */
-			if ((id->dma_ultra & (id->dma_ultra >> 8) & 7)) {
-				udma_enable(drive, 1, 1);
-
-				return 0;
-			}
-		/* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */
-		if (id->field_valid & 2)	/* regular DMA */
-			if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404) {
-				udma_enable(drive, 1, 1);
-
-				return 0;
-			}
-		/* Consult the list of known "good" drives */
-		if (udma_white_list(drive)) {
-			udma_enable(drive, 1, 1);
-
-			return 0;
-		}
-	}
-	udma_enable(drive, 0, 0);
-
-	return 0;
-}
-
-/*
- * 1 dma-ing, 2 error, 4 intr
- */
-static int dma_timer_expiry(struct ata_device *drive, struct request *__rq)
-{
-	/* FIXME: What's that? */
-	u8 dma_stat = inb(drive->channel->dma_base+2);
-
-#ifdef DEBUG
-	printk("%s: dma_timer_expiry: dma status == 0x%02x\n", drive->name, dma_stat);
-#endif /* DEBUG */
-
-#if 0
-	HWGROUP(drive)->expiry = NULL;	/* one free ride for now */
-#endif
-
-	if (dma_stat & 2) {	/* ERROR */
-		u8 stat = GET_STAT();
-		return ide_error(drive, "dma_timer_expiry", stat);
-	}
-	if (dma_stat & 1)	/* DMAing */
-		return WAIT_CMD;
-	return 0;
-}
-
-int ata_start_dma(struct ata_device *drive, struct request *rq)
-{
-	struct ata_channel *ch = drive->channel;
-	unsigned long dma_base = ch->dma_base;
-	unsigned int reading = 0;
-
-	if (rq_data_dir(rq) == READ)
-		reading = 1 << 3;
-
-	/* try PIO instead of DMA */
-	if (!udma_new_table(ch, rq))
-		return 1;
-
-	outl(ch->dmatable_dma, dma_base + 4); /* PRD table */
-	outb(reading, dma_base);		/* specify r/w */
-	outb(inb(dma_base+2)|6, dma_base+2);	/* clear INTR & ERROR flags */
-	drive->waiting_for_dma = 1;
-	return 0;
-}
-
-/*
- * This initiates/aborts DMA read/write operations on a drive.
- *
- * The caller is assumed to have selected the drive and programmed the drive's
- * sector address using CHS or LBA.  All that remains is to prepare for DMA
- * and then issue the actual read/write DMA/PIO command to the drive.
- *
- * For ATAPI devices, we just prepare for DMA and return. The caller should
- * then issue the packet command to the drive and call us again with
- * udma_start afterwards.
- *
- * Returns 0 if all went well.
- * Returns 1 if DMA read/write could not be started, in which case
- * the caller should revert to PIO for the current request.
- * May also be invoked from trm290.c
- */
-int XXX_ide_dmaproc(struct ata_device *drive)
-{
-	return config_drive_for_dma(drive);
-}
-
-/*
- * Needed for allowing full modular support of ide-driver
- */
-void ide_release_dma(struct ata_channel *ch)
-{
-	if (!ch->dma_base)
-		return;
-
-	if (ch->dmatable_cpu) {
-		pci_free_consistent(ch->pci_dev,
-				    PRD_ENTRIES * PRD_BYTES,
-				    ch->dmatable_cpu,
-				    ch->dmatable_dma);
-		ch->dmatable_cpu = NULL;
-	}
-	if (ch->sg_table) {
-		kfree(ch->sg_table);
-		ch->sg_table = NULL;
-	}
-	if ((ch->dma_extra) && (ch->unit == 0))
-		release_region((ch->dma_base + 16), ch->dma_extra);
-	release_region(ch->dma_base, 8);
-	ch->dma_base = 0;
-}
-
-/*
- * This can be called for a dynamically installed interface. Don't __init it
- */
-void ata_init_dma(struct ata_channel *ch, unsigned long dma_base)
-{
-	if (!request_region(dma_base, 8, ch->name)) {
-		printk(KERN_ERR "ATA: ERROR: BM DMA portst already in use!\n");
-
-		return;
-	}
-	printk(KERN_INFO"    %s: BM-DMA at 0x%04lx-0x%04lx", ch->name, dma_base, dma_base + 7);
-	ch->dma_base = dma_base;
-	ch->dmatable_cpu = pci_alloc_consistent(ch->pci_dev,
-						  PRD_ENTRIES * PRD_BYTES,
-						  &ch->dmatable_dma);
-	if (ch->dmatable_cpu == NULL)
-		goto dma_alloc_failure;
-
-	ch->sg_table = kmalloc(sizeof(struct scatterlist) * PRD_ENTRIES,
-				 GFP_KERNEL);
-	if (ch->sg_table == NULL) {
-		pci_free_consistent(ch->pci_dev, PRD_ENTRIES * PRD_BYTES,
-				    ch->dmatable_cpu, ch->dmatable_dma);
-		goto dma_alloc_failure;
-	}
-
-	if (!ch->XXX_udma)
-		ch->XXX_udma = XXX_ide_dmaproc;
-
-	if (ch->chipset != ide_trm290) {
-		u8 dma_stat = inb(dma_base+2);
-		printk(", BIOS settings: %s:%s, %s:%s",
-		       ch->drives[0].name, (dma_stat & 0x20) ? "DMA" : "pio",
-		       ch->drives[1].name, (dma_stat & 0x40) ? "DMA" : "pio");
-	}
-	printk("\n");
-	return;
-
-dma_alloc_failure:
-	printk(" -- ERROR, UNABLE TO ALLOCATE DMA TABLES\n");
-}
-
-/****************************************************************************
- * UDMA function which should have architecture specific counterparts where
- * neccessary.
- *
- * The intention is that at some point in time we will move this whole to
- * architecture specific kernel sections. For now I would love the architecture
- * maintainers to just #ifdef #endif this stuff directly here. I have for now
- * tryed to update as much as I could in the architecture specific code.  But
- * of course I may have done mistakes, so please bear with me and update it
- * here the proper way.
- *
- * Thank you a lot in advance!
- *
- * Sat May  4 20:29:46 CEST 2002 Marcin Dalecki.
- */
-
-/*
- * This is the generic part of the DMA setup used by the host chipset drivers
- * in the corresponding DMA setup method.
- *
- * FIXME: there are some places where this gets used driectly for "error
- * recovery" in the ATAPI drivers. This was just plain wrong before, in esp.
- * not portable, and just got uncovered now.
- */
-void udma_enable(struct ata_device *drive, int on, int verbose)
-{
-	struct ata_channel *ch = drive->channel;
-	int set_high = 1;
-	u8 unit;
-	u64 addr;
-
-
-	/* Method overloaded by host chip specific code. */
-	if (ch->udma_enable) {
-		ch->udma_enable(drive, on, verbose);
-
-		return;
-	}
-
-	/* Fall back to the default implementation. */
-	unit = (drive->select.b.unit & 0x01);
-	addr = BLK_BOUNCE_HIGH;
-
-	if (!on) {
-		if (verbose)
-			printk("%s: DMA disabled\n", drive->name);
-		set_high = 0;
-		outb(inb(ch->dma_base + 2) & ~(1 << (5 + unit)), ch->dma_base + 2);
-#ifdef CONFIG_BLK_DEV_IDE_TCQ
-		udma_tcq_enable(drive, 0);
-#endif
-	}
-
-	/* toggle bounce buffers */
-
-	if (on && drive->type == ATA_DISK && drive->channel->highmem) {
-		if (!PCI_DMA_BUS_IS_PHYS)
-			addr = BLK_BOUNCE_ANY;
-		else
-			addr = drive->channel->pci_dev->dma_mask;
-	}
-
-	blk_queue_bounce_limit(&drive->queue, addr);
-
-	drive->using_dma = on;
-
-	if (on) {
-		outb(inb(ch->dma_base + 2) | (1 << (5 + unit)), ch->dma_base + 2);
-#ifdef CONFIG_BLK_DEV_IDE_TCQ_DEFAULT
-		udma_tcq_enable(drive, 1);
-#endif
-	}
-}
-
-/*
- * This prepares a dma request.  Returns 0 if all went okay, returns 1
- * otherwise.  May also be invoked from trm290.c
- */
-int udma_new_table(struct ata_channel *ch, struct request *rq)
-{
-	unsigned int *table = ch->dmatable_cpu;
-#ifdef CONFIG_BLK_DEV_TRM290
-	unsigned int is_trm290_chipset = (ch->chipset == ide_trm290);
-#else
-	const int is_trm290_chipset = 0;
-#endif
-	unsigned int count = 0;
-	int i;
-	struct scatterlist *sg;
-
-	ch->sg_nents = i = build_sglist(ch, rq);
-	if (!i)
-		return 0;
-
-	sg = ch->sg_table;
-	while (i) {
-		u32 cur_addr;
-		u32 cur_len;
-
-		cur_addr = sg_dma_address(sg);
-		cur_len = sg_dma_len(sg);
-
-		/*
-		 * Fill in the dma table, without crossing any 64kB boundaries.
-		 * Most hardware requires 16-bit alignment of all blocks,
-		 * but the trm290 requires 32-bit alignment.
-		 */
-
-		while (cur_len) {
-			u32 xcount, bcount = 0x10000 - (cur_addr & 0xffff);
-
-			if (count++ >= PRD_ENTRIES) {
-				printk("ide-dma: count %d, sg_nents %d, cur_len %d, cur_addr %u\n",
-						count, ch->sg_nents, cur_len, cur_addr);
-				BUG();
-			}
-
-			if (bcount > cur_len)
-				bcount = cur_len;
-			*table++ = cpu_to_le32(cur_addr);
-			xcount = bcount & 0xffff;
-			if (is_trm290_chipset)
-				xcount = ((xcount >> 2) - 1) << 16;
-			if (xcount == 0x0000) {
-		        /*
-			 * Most chipsets correctly interpret a length of
-			 * 0x0000 as 64KB, but at least one (e.g. CS5530)
-			 * misinterprets it as zero (!). So here we break
-			 * the 64KB entry into two 32KB entries instead.
-			 */
-				if (count++ >= PRD_ENTRIES) {
-					pci_unmap_sg(ch->pci_dev, sg,
-						     ch->sg_nents,
-						     ch->sg_dma_direction);
-					return 0;
-				}
-
-				*table++ = cpu_to_le32(0x8000);
-				*table++ = cpu_to_le32(cur_addr + 0x8000);
-				xcount = 0x8000;
-			}
-			*table++ = cpu_to_le32(xcount);
-			cur_addr += bcount;
-			cur_len -= bcount;
-		}
-
-		sg++;
-		i--;
-	}
-
-	if (!count)
-		printk(KERN_ERR "%s: empty DMA table?\n", ch->name);
-	else if (!is_trm290_chipset)
-		*--table |= cpu_to_le32(0x80000000);
-
-	return count;
-}
-
-/* Teardown mappings after DMA has completed.  */
-void udma_destroy_table(struct ata_channel *ch)
-{
-	pci_unmap_sg(ch->pci_dev, ch->sg_table, ch->sg_nents, ch->sg_dma_direction);
-}
-
-void udma_print(struct ata_device *drive)
-{
-#ifdef CONFIG_ARCH_ACORN
-	printk(", DMA");
-#else
-	struct hd_driveid *id = drive->id;
-	char *str = NULL;
-
-	if ((id->field_valid & 4) && (eighty_ninty_three(drive)) &&
-	    (id->dma_ultra & (id->dma_ultra >> 14) & 3)) {
-		if ((id->dma_ultra >> 15) & 1)
-			str = ", UDMA(mode 7)";	/* UDMA BIOS-enabled! */
-		else
-			str = ", UDMA(133)";	/* UDMA BIOS-enabled! */
-	} else if ((id->field_valid & 4) && (eighty_ninty_three(drive)) &&
-		  (id->dma_ultra & (id->dma_ultra >> 11) & 7)) {
-		if ((id->dma_ultra >> 13) & 1) {
-			str = ", UDMA(100)";	/* UDMA BIOS-enabled! */
-		} else if ((id->dma_ultra >> 12) & 1) {
-			str = ", UDMA(66)";	/* UDMA BIOS-enabled! */
-		} else {
-			str = ", UDMA(44)";	/* UDMA BIOS-enabled! */
-		}
-	} else if ((id->field_valid & 4) &&
-		   (id->dma_ultra & (id->dma_ultra >> 8) & 7)) {
-		if ((id->dma_ultra >> 10) & 1) {
-			str = ", UDMA(33)";	/* UDMA BIOS-enabled! */
-		} else if ((id->dma_ultra >> 9) & 1) {
-			str = ", UDMA(25)";	/* UDMA BIOS-enabled! */
-		} else {
-			str = ", UDMA(16)";	/* UDMA BIOS-enabled! */
-		}
-	} else if (id->field_valid & 4)
-		str = ", (U)DMA";	/* Can be BIOS-enabled! */
-	else
-		str = ", DMA";
-
-	printk(str);
-#endif
-}
-
-/*
- * Drive back/white list handling for UDMA capability:
- */
-
-int udma_black_list(struct ata_device *drive)
-{
-	return check_drive_lists(drive, 0);
-}
-
-int udma_white_list(struct ata_device *drive)
-{
-	return check_drive_lists(drive, 1);
-}
-
-/*
- * Generic entry points for functions provided possibly by the host chip set
- * drivers.
- */
-
-/*
- * Prepare the channel for a DMA startfer. Please note that only the broken
- * Pacific Digital host chip needs the reques to be passed there to decide
- * about addressing modes.
- */
-
-int udma_start(struct ata_device *drive, struct request *rq)
-{
-	struct ata_channel *ch = drive->channel;
-	unsigned long dma_base = ch->dma_base;
-
-	if (ch->udma_start)
-		return ch->udma_start(drive, rq);
-
-	/* Note that this is done *after* the cmd has
-	 * been issued to the drive, as per the BM-IDE spec.
-	 * The Promise Ultra33 doesn't work correctly when
-	 * we do this part before issuing the drive cmd.
-	 */
-	outb(inb(dma_base)|1, dma_base);		/* start DMA */
-	return 0;
-}
-
-int udma_stop(struct ata_device *drive)
-{
-	struct ata_channel *ch = drive->channel;
-	unsigned long dma_base = ch->dma_base;
-	u8 dma_stat;
-
-	if (ch->udma_stop)
-		return ch->udma_stop(drive);
-
-	drive->waiting_for_dma = 0;
-	outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
-	dma_stat = inb(dma_base+2);		/* get DMA status */
-	outb(dma_stat|6, dma_base+2);		/* clear the INTR & ERROR bits */
-	udma_destroy_table(ch);			/* purge DMA mappings */
-
-	return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0;	/* verify good DMA status */
-}
-
-/*
- * This is the default read write function.
- *
- * It's exported only for host chips which use it for fallback or (too) late
- * capability checking.
- */
-
-int ata_do_udma(unsigned int reading, struct ata_device *drive, struct request *rq)
-{
-	if (ata_start_dma(drive, rq))
-		return 1;
-
-	if (drive->type != ATA_DISK)
-		return 0;
-
-	reading <<= 3;
-
-	ide_set_handler(drive, ide_dma_intr, WAIT_CMD, dma_timer_expiry);	/* issue cmd to drive */
-	if ((rq->flags & REQ_DRIVE_ACB) && (drive->addressing == 1)) {
-		struct ata_taskfile *args = rq->special;
-
-		OUT_BYTE(args->taskfile.command, IDE_COMMAND_REG);
-	} else if (drive->addressing) {
-		OUT_BYTE(reading ? WIN_READDMA_EXT : WIN_WRITEDMA_EXT, IDE_COMMAND_REG);
-	} else {
-		OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
-	}
-
-	return udma_start(drive, rq);
-}
-
-int udma_read(struct ata_device *drive, struct request *rq)
-{
-	struct ata_channel *ch = drive->channel;
-
-	if (ch->udma_read)
-		return ch->udma_read(drive, rq);
-
-	return ata_do_udma(1, drive, rq);
-}
-
-int udma_write(struct ata_device *drive, struct request *rq)
-{
-	struct ata_channel *ch = drive->channel;
-
-	if (ch->udma_write)
-		return ch->udma_write(drive, rq);
-
-	return ata_do_udma(0, drive, rq);
-}
-
-/*
- * FIXME: This should be attached to a channel as we can see now!
- */
-int udma_irq_status(struct ata_device *drive)
-{
-	struct ata_channel *ch = drive->channel;
-	u8 dma_stat;
-
-	if (ch->udma_irq_status)
-		return ch->udma_irq_status(drive);
-
-	/* default action */
-	dma_stat = inb(ch->dma_base + 2);
-
-	return (dma_stat & 4) == 4;	/* return 1 if INTR asserted */
-}
-
-void udma_timeout(struct ata_device *drive)
-{
-	printk(KERN_ERR "ATA: UDMA timeout occured %s!\n", drive->name);
-
-	/* Invoke the chipset specific handler now. */
-	if (drive->channel->udma_timeout)
-		drive->channel->udma_timeout(drive);
-
-}
-
-void udma_irq_lost(struct ata_device *drive)
-{
-	if (drive->channel->udma_irq_lost)
-		drive->channel->udma_irq_lost(drive);
-}
-
-EXPORT_SYMBOL(udma_enable);
-EXPORT_SYMBOL(udma_start);
-EXPORT_SYMBOL(udma_stop);
-EXPORT_SYMBOL(udma_read);
-EXPORT_SYMBOL(udma_write);
-EXPORT_SYMBOL(ata_do_udma);
-EXPORT_SYMBOL(udma_irq_status);
-EXPORT_SYMBOL(udma_print);
-EXPORT_SYMBOL(udma_black_list);
-EXPORT_SYMBOL(udma_white_list);
diff -Nru a/drivers/ide/ide-features.c b/drivers/ide/ide-features.c
--- a/drivers/ide/ide-features.c	Sat May 18 00:46:14 2002
+++ b/drivers/ide/ide-features.c	Sat May 18 00:46:14 2002
@@ -78,31 +78,6 @@
 	return "XFER ERROR";
 }
 
-byte ide_auto_reduce_xfer (ide_drive_t *drive)
-{
-	if (!drive->crc_count)
-		return drive->current_speed;
-	drive->crc_count = 0;
-
-	switch(drive->current_speed) {
-		case XFER_UDMA_7:	return XFER_UDMA_6;
-		case XFER_UDMA_6:	return XFER_UDMA_5;
-		case XFER_UDMA_5:	return XFER_UDMA_4;
-		case XFER_UDMA_4:	return XFER_UDMA_3;
-		case XFER_UDMA_3:	return XFER_UDMA_2;
-		case XFER_UDMA_2:	return XFER_UDMA_1;
-		case XFER_UDMA_1:	return XFER_UDMA_0;
-			/*
-			 * OOPS we do not goto non Ultra DMA modes
-			 * without iCRC's available we force
-			 * the system to PIO and make the user
-			 * invoke the ATA-1 ATA-2 DMA modes.
-			 */
-		case XFER_UDMA_0:
-		default:		return XFER_PIO_4;
-	}
-}
-
 /*
  * hd_driveid data come as little endian,
  * they need to be converted on big endian machines
@@ -110,7 +85,7 @@
 void ide_fix_driveid(struct hd_driveid *id)
 {
 #ifndef __LITTLE_ENDIAN
-#ifdef __BIG_ENDIAN
+# ifdef __BIG_ENDIAN
 	int i;
 	unsigned short *stringcast;
 
@@ -196,13 +171,13 @@
 	for (i = 0; i < 48; i++)
 		id->words206_254[i] = __le16_to_cpu(id->words206_254[i]);
 	id->integrity_word  = __le16_to_cpu(id->integrity_word);
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif /* __BIG_ENDIAN */
-#endif /* __LITTLE_ENDIAN */
+# else
+#  error "Please fix <asm/byteorder.h>"
+# endif
+#endif
 }
 
-int ide_driveid_update (ide_drive_t *drive)
+int ide_driveid_update(struct ata_device *drive)
 {
 	/*
 	 * Re-read drive->id for possible DMA mode
@@ -255,56 +230,9 @@
 }
 
 /*
- * Verify that we are doing an approved SETFEATURES_XFER with respect
- * to the hardware being able to support request.  Since some hardware
- * can improperly report capabilties, we check to see if the host adapter
- * in combination with the device (usually a disk) properly detect
- * and acknowledge each end of the ribbon.
- */
-int ide_ata66_check (ide_drive_t *drive, struct ata_taskfile *args)
-{
-	if ((args->taskfile.command == WIN_SETFEATURES) &&
-	    (args->taskfile.sector_number > XFER_UDMA_2) &&
-	    (args->taskfile.feature == SETFEATURES_XFER)) {
-		if (!drive->channel->udma_four) {
-			printk("%s: Speed warnings UDMA 3/4/5 is not functional.\n", drive->channel->name);
-			return 1;
-		}
-#ifndef CONFIG_IDEDMA_IVB
-		if ((drive->id->hw_config & 0x6000) == 0) {
-#else
-		if (((drive->id->hw_config & 0x2000) == 0) ||
-		    ((drive->id->hw_config & 0x4000) == 0)) {
-#endif
-			printk("%s: Speed warnings UDMA 3/4/5 is not functional.\n", drive->name);
-			return 1;
-		}
-	}
-	return 0;
-}
-
-/*
- * Backside of HDIO_DRIVE_CMD call of SETFEATURES_XFER.
- * 1 : Safe to update drive->id DMA registers.
- * 0 : OOPs not allowed.
- */
-int set_transfer (ide_drive_t *drive, struct ata_taskfile *args)
-{
-	if ((args->taskfile.command == WIN_SETFEATURES) &&
-	    (args->taskfile.sector_number >= XFER_SW_DMA_0) &&
-	    (args->taskfile.feature == SETFEATURES_XFER) &&
-	    (drive->id->dma_ultra ||
-	     drive->id->dma_mword ||
-	     drive->id->dma_1word))
-		return 1;
-
-	return 0;
-}
-
-/*
- *  All hosts that use the 80c ribbon mus use!
+ *  All hosts that use the 80c ribbon must use this!
  */
-byte eighty_ninty_three (ide_drive_t *drive)
+byte eighty_ninty_three(struct ata_device *drive)
 {
 	return ((byte) ((drive->channel->udma_four) &&
 #ifndef CONFIG_IDEDMA_IVB
@@ -324,7 +252,7 @@
  *
  * const char *msg == consider adding for verbose errors.
  */
-int ide_config_drive_speed (ide_drive_t *drive, byte speed)
+int ide_config_drive_speed(struct ata_device *drive, byte speed)
 {
 	struct ata_channel *hwif = drive->channel;
 	int i;
@@ -393,7 +321,7 @@
 	enable_irq(hwif->irq);
 
 	if (error) {
-		ide_dump_status(drive, "set_drive_speed_status", stat);
+		ide_dump_status(drive, NULL, "set_drive_speed_status", stat);
 		return error;
 	}
 
@@ -407,7 +335,7 @@
 	} else {
 		outb(inb(hwif->dma_base+2) & ~(1<<(5+unit)), hwif->dma_base+2);
 	}
-#endif /* (CONFIG_BLK_DEV_IDEDMA) && !(CONFIG_DMA_NONPCI) */
+#endif
 
 	switch(speed) {
 		case XFER_UDMA_7:   drive->id->dma_ultra |= 0x8080; break;
@@ -429,11 +357,7 @@
 	return error;
 }
 
-EXPORT_SYMBOL(ide_auto_reduce_xfer);
 EXPORT_SYMBOL(ide_fix_driveid);
 EXPORT_SYMBOL(ide_driveid_update);
-EXPORT_SYMBOL(ide_ata66_check);
-EXPORT_SYMBOL(set_transfer);
 EXPORT_SYMBOL(eighty_ninty_three);
 EXPORT_SYMBOL(ide_config_drive_speed);
-
diff -Nru a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c
--- a/drivers/ide/ide-floppy.c	Sat May 18 00:46:08 2002
+++ b/drivers/ide/ide-floppy.c	Sat May 18 00:46:08 2002
@@ -692,7 +692,7 @@
 		return 0;
 	}
 	rq->errors = error;
-	ide_end_drive_cmd (drive, 0, 0);
+	ide_end_drive_cmd (drive, rq, 0, 0);
 
 	return 0;
 }
@@ -879,7 +879,7 @@
 	pc = idefloppy_next_pc_storage (drive);
 	rq = idefloppy_next_rq_storage (drive);
 	idefloppy_create_request_sense_cmd (pc);
-	idefloppy_queue_pc_head (drive, pc, rq);
+	idefloppy_queue_pc_head(drive, pc, rq);
 }
 
 /*
@@ -1006,7 +1006,7 @@
 	idefloppy_floppy_t *floppy = drive->driver_data;
 	idefloppy_ireason_reg_t ireason;
 
-	if (ide_wait_stat (&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+	if (ide_wait_stat (&startstop, drive, rq, DRQ_STAT, BUSY_STAT, WAIT_READY)) {
 		printk (KERN_ERR "ide-floppy: Strange, packet command initiated yet DRQ isn't asserted\n");
 		return startstop;
 	}
@@ -1043,13 +1043,13 @@
 	return IDEFLOPPY_WAIT_CMD;		/* Timeout for the packet command */
 }
 
-static ide_startstop_t idefloppy_transfer_pc1(struct ata_device *drive, struct request *__rq)
+static ide_startstop_t idefloppy_transfer_pc1(struct ata_device *drive, struct request *rq)
 {
 	idefloppy_floppy_t *floppy = drive->driver_data;
 	ide_startstop_t startstop;
 	idefloppy_ireason_reg_t ireason;
 
-	if (ide_wait_stat (&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+	if (ide_wait_stat(&startstop, drive, rq, DRQ_STAT, BUSY_STAT, WAIT_READY)) {
 		printk (KERN_ERR "ide-floppy: Strange, packet command initiated yet DRQ isn't asserted\n");
 		return startstop;
 	}
@@ -1960,14 +1960,6 @@
 	return 0;
 }
 
-static void idefloppy_add_settings(ide_drive_t *drive)
-{
-	ide_add_setting(drive,	"bios_cyl",		SETTING_RW,					-1,			-1,			TYPE_INT,	0,	1023,				1,	1,	&drive->bios_cyl,		NULL);
-	ide_add_setting(drive,	"bios_head",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	255,				1,	1,	&drive->bios_head,		NULL);
-	ide_add_setting(drive,	"bios_sect",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	63,				1,	1,	&drive->bios_sect,		NULL);
-
-}
-
 /*
  *	Driver initialization.
  */
@@ -2009,7 +2001,7 @@
 	}
 
 	(void) idefloppy_get_capacity (drive);
-	idefloppy_add_settings(drive);
+
 	for (i = 0; i < MAX_DRIVES; ++i) {
 		struct ata_channel *hwif = drive->channel;
 
diff -Nru a/drivers/ide/ide-geometry.c b/drivers/ide/ide-geometry.c
--- a/drivers/ide/ide-geometry.c	Sat May 18 00:46:08 2002
+++ b/drivers/ide/ide-geometry.c	Sat May 18 00:46:08 2002
@@ -1,6 +1,4 @@
 /*
- * linux/drivers/ide/ide-geometry.c
- *
  * Sun Feb 24 23:13:03 CET 2002: Patch by Andries Brouwer to remove the
  * confused CMOS probe applied. This is solving more problems than it may
  * (unexpectedly) introduce.
@@ -14,17 +12,17 @@
 
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
 
-extern ide_drive_t * get_info_ptr(kdev_t);
+extern struct ata_device * get_info_ptr(kdev_t);
 
 /*
  * If heads is nonzero: find a translation with this many heads and S=63.
  * Otherwise: find out how OnTrack Disk Manager would translate the disk.
  */
 static void
-ontrack(ide_drive_t *drive, int heads, unsigned int *c, int *h, int *s)
+ontrack(struct ata_device *drive, int heads, unsigned int *c, int *h, int *s)
 {
-	static const byte dm_head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0};
-	const byte *headp = dm_head_vals;
+	static const u8 dm_head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0};
+	const u8 *headp = dm_head_vals;
 	unsigned long total;
 
 	/*
@@ -72,13 +70,13 @@
  *	-1 = similar to "0", plus redirect sector 0 to sector 1.
  *	 2 = convert to a CHS geometry with "ptheads" heads.
  *
- * Returns 0 if the translation was not possible, if the device was not 
+ * Returns 0 if the translation was not possible, if the device was not
  * an IDE disk drive, or if a geometry was "forced" on the commandline.
  * Returns 1 if the geometry translation was successful.
  */
-int ide_xlate_1024 (kdev_t i_rdev, int xparm, int ptheads, const char *msg)
+int ide_xlate_1024(kdev_t i_rdev, int xparm, int ptheads, const char *msg)
 {
-	ide_drive_t *drive;
+	struct ata_device *drive;
 	const char *msg1 = "";
 	int heads = 0;
 	int c, h, s;
@@ -144,4 +142,4 @@
 		       drive->bios_cyl, drive->bios_head, drive->bios_sect);
 	return ret;
 }
-#endif /* defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) */
+#endif
diff -Nru a/drivers/ide/ide-pci.c b/drivers/ide/ide-pci.c
--- a/drivers/ide/ide-pci.c	Sat May 18 00:46:14 2002
+++ b/drivers/ide/ide-pci.c	Sat May 18 00:46:14 2002
@@ -122,7 +122,7 @@
 	 * Unless there is a bootable card that does not use the standard
 	 * ports 1f0/170 (the ide0/ide1 defaults). The (bootable) flag.
 	 */
-	if (bootable) {
+	if (bootable == ON_BOARD) {
 		for (h = 0; h < MAX_HWIFS; ++h) {
 			hwif = &ide_hwifs[h];
 			if (hwif->chipset == ide_unknown)
@@ -553,15 +553,14 @@
 			}
 		}
 	}
-
-	printk("ATA: %s: controller on PCI bus %02x dev %02x\n",
-			dev->name, dev->bus->number, dev->devfn);
+	printk(KERN_INFO "ATA: %s: controller on PCI slot %s dev %02x\n",
+			dev->name, dev->slot_name, dev->devfn);
 	setup_pci_device(dev, d);
 	if (!dev2)
 		return;
 	d2 = d;
-	printk("ATA: %s: controller on PCI bus %02x dev %02x\n",
-			dev2->name, dev2->bus->number, dev2->devfn);
+	printk(KERN_INFO "ATA: %s: controller on PCI slot %s dev %02x\n",
+			dev2->name, dev2->slot_name, dev2->devfn);
 	setup_pci_device(dev2, d2);
 }
 
@@ -584,8 +583,8 @@
 		}
 	}
 
-	printk("%s: IDE controller on PCI bus %02x dev %02x\n",
-		dev->name, dev->bus->number, dev->devfn);
+	printk(KERN_INFO "ATA: %s: controller on PCI slot %s dev %02x\n",
+		dev->name, dev->slot_name, dev->devfn);
 	setup_pci_device(dev, d);
 	if (!dev2) {
 		return;
@@ -601,8 +600,8 @@
 		}
 	}
 	d2 = d;
-	printk("%s: IDE controller on PCI bus %02x dev %02x\n",
-		dev2->name, dev2->bus->number, dev2->devfn);
+	printk(KERN_INFO "ATA: %s: controller on PCI slot %s dev %02x\n",
+		dev2->name, dev2->slot_name, dev2->devfn);
 	setup_pci_device(dev2, d2);
 
 }
@@ -623,7 +622,7 @@
 	switch(class_rev) {
 		case 5:
 		case 4:
-		case 3:	printk("%s: IDE controller on PCI slot %s\n", dev->name, dev->slot_name);
+		case 3:	printk(KERN_INFO "ATA: %s: controller on PCI slot %s\n", dev->name, dev->slot_name);
 			setup_pci_device(dev, d);
 			return;
 		default:	break;
@@ -639,17 +638,17 @@
 			pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin2);
 			if ((pin1 != pin2) && (dev->irq == dev2->irq)) {
 				d->bootable = ON_BOARD;
-				printk("%s: onboard version of chipset, pin1=%d pin2=%d\n", dev->name, pin1, pin2);
+				printk(KERN_INFO "ATAL: %s: onboard version of chipset, pin1=%d pin2=%d\n", dev->name, pin1, pin2);
 			}
 			break;
 		}
 	}
-	printk("%s: IDE controller on PCI slot %s\n", dev->name, dev->slot_name);
+	printk(KERN_INFO "ATA: %s: controller on PCI slot %s\n", dev->name, dev->slot_name);
 	setup_pci_device(dev, d);
 	if (!dev2)
 		return;
 	d2 = d;
-	printk("%s: IDE controller on PCI slot %s\n", dev2->name, dev2->slot_name);
+	printk(KERN_INFO "ATA: %s: controller on PCI slot %s\n", dev2->name, dev2->slot_name);
 	setup_pci_device(dev2, d2);
 }
 
@@ -679,6 +678,10 @@
 	}
 
 	if (!d) {
+		/* Only check the device calls, if it wasn't listed, since
+		 * there are in esp. some pdc202xx chips which "work around"
+		 * beeing grabbed by generic drivers.
+		 */
 		if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
 			printk(KERN_INFO "ATA: unknown interface: %s, on PCI slot %s\n",
 					dev->name, dev->slot_name);
@@ -703,7 +706,7 @@
 			hpt374_device_order_fixup(dev, d);
 	} else if (d->vendor == PCI_VENDOR_ID_PROMISE && d->device == PCI_DEVICE_ID_PROMISE_20268R)
 		pdc20270_device_order_fixup(dev, d);
-	else if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
+	else {
 		printk(KERN_INFO "ATA: %s (%04x:%04x) on PCI slot %s\n",
 				dev->name, vendor, device, dev->slot_name);
 		setup_pci_device(dev, d);
diff -Nru a/drivers/ide/ide-pmac.c b/drivers/ide/ide-pmac.c
--- a/drivers/ide/ide-pmac.c	Sat May 18 00:46:14 2002
+++ b/drivers/ide/ide-pmac.c	Sat May 18 00:46:14 2002
@@ -15,12 +15,14 @@
  * Some code taken from drivers/ide/ide-dma.c:
  *
  *  Copyright (c) 1995-1998  Mark Lord
- *  
+ *
  * TODO:
- * 
+ *
  *  - Find a way to duplicate less code with ide-dma and use the
  *    dma fileds in the hwif structure instead of our own
+ *
  *  - Fix check_disk_change() call
+ *
  *  - Make module-able (includes setting ppc_md. hooks from within
  *    this file and not from arch code, and handling module deps with
  *    mediabay (by having both modules do dynamic lookup of each other
@@ -53,8 +55,6 @@
 #endif
 #include "ata-timing.h"
 
-extern spinlock_t ide_lock;
-
 #undef IDE_PMAC_DEBUG
 
 #define DMA_WAIT_TIMEOUT	500
@@ -111,7 +111,7 @@
 /* 66Mhz cell, found in KeyLargo. Can do ultra mode 0 to 2 on
  * 40 connector cable and to 4 on 80 connector one.
  * Clock unit is 15ns (66Mhz)
- * 
+ *
  * 3 Values can be programmed:
  *  - Write data setup, which appears to match the cycle time. They
  *    also call it DIOW setup.
@@ -146,7 +146,7 @@
 
 /* 33Mhz cell, found in OHare, Heathrow (& Paddington) and KeyLargo
  * Can do pio & mdma modes, clock unit is 30ns (33Mhz)
- * 
+ *
  * The access time and recovery time can be programmed. Some older
  * Darwin code base limit OHare to 150ns cycle time. I decided to do
  * the same here fore safety against broken old hardware ;)
@@ -180,7 +180,7 @@
 # define GOOD_DMA_DRIVE		1
 
 /* Rounded Multiword DMA timings
- * 
+ *
  * I gave up finding a generic formula for all controller
  * types and instead, built tables based on timing values
  * used by Apple in Darwin's implementation.
@@ -280,7 +280,7 @@
 #endif /* CONFIG_PMAC_PBOOK */
 
 static int __pmac
-pmac_ide_find(ide_drive_t *drive)
+pmac_ide_find(struct ata_device *drive)
 {
 	struct ata_channel *hwif = drive->channel;
 	ide_ioreg_t base;
@@ -352,7 +352,7 @@
  * device for yaboot configuration
  */
 struct device_node*
-pmac_ide_get_devnode(ide_drive_t *drive)
+pmac_ide_get_devnode(struct ata_device *drive)
 {
 	int i = pmac_ide_find(drive);
 	if (i < 0)
@@ -365,7 +365,7 @@
  * is enough, I beleive selectproc will be called whenever an IDE command is started,
  * but... */
 static void __pmac
-pmac_ide_selectproc(ide_drive_t *drive)
+pmac_ide_selectproc(struct ata_device *drive)
 {
 	int i = pmac_ide_find(drive);
 	if (i < 0)
@@ -387,11 +387,11 @@
  * almost identical to the generic one and works, I've not yet
  * managed to figure out what bit is causing the lockup in the
  * generic code, possibly a timing issue...
- * 
+ *
  * --BenH
  */
 static int __pmac
-wait_for_ready(ide_drive_t *drive)
+wait_for_ready(struct ata_device *drive)
 {
 	/* Timeout bumped for some powerbooks */
 	int timeout = 2000;
@@ -417,12 +417,12 @@
 }
 
 static int __pmac
-pmac_ide_do_setfeature(ide_drive_t *drive, byte command)
+pmac_ide_do_setfeature(struct ata_device *drive, byte command)
 {
 	int result = 1;
 	unsigned long flags;
 	struct ata_channel *hwif = drive->channel;
-	
+
 	disable_irq(hwif->irq);	/* disable_irq_nosync ?? */
 	udelay(1);
 	SELECT_DRIVE(drive->channel, drive);
@@ -477,7 +477,7 @@
 
 /* Calculate PIO timings */
 static void __pmac
-pmac_ide_tuneproc(ide_drive_t *drive, byte pio)
+pmac_ide_tuneproc(struct ata_device *drive, byte pio)
 {
 	struct ata_timing *t;
 	int i;
@@ -536,8 +536,8 @@
 #ifdef IDE_PMAC_DEBUG
 	printk(KERN_ERR "ide_pmac: Set PIO timing for mode %d, reg: 0x%08x\n",
 		pio,  *timings);
-#endif	
-		
+#endif
+
 	if (drive->select.all == IN_BYTE(IDE_SELECT_REG))
 		pmac_ide_selectproc(drive);
 }
@@ -553,14 +553,14 @@
 	addrTicks = SYSCLK_TICKS_66(udma_timings[speed & 0xf].addrSetup);
 
 	*timings = ((*timings) & ~(TR_66_UDMA_MASK | TR_66_MDMA_MASK)) |
-			(wrDataSetupTicks << TR_66_UDMA_WRDATASETUP_SHIFT) | 
+			(wrDataSetupTicks << TR_66_UDMA_WRDATASETUP_SHIFT) |
 			(rdyToPauseTicks << TR_66_UDMA_RDY2PAUS_SHIFT) |
 			(addrTicks <<TR_66_UDMA_ADDRSETUP_SHIFT) |
 			TR_66_UDMA_EN;
 #ifdef IDE_PMAC_DEBUG
 	printk(KERN_ERR "ide_pmac: Set UDMA timing for mode %d, reg: 0x%08x\n",
 		speed & 0xf,  *timings);
-#endif	
+#endif
 
 	return 0;
 }
@@ -584,7 +584,7 @@
 	/* Adjust for drive */
 	if (drive_cycle_time && drive_cycle_time > cycleTime)
 		cycleTime = drive_cycle_time;
-	/* OHare limits according to some old Apple sources */	
+	/* OHare limits according to some old Apple sources */
 	if ((intf_type == controller_ohare) && (cycleTime < 150))
 		cycleTime = 150;
 	/* Get the proper timing array for this controller */
@@ -616,7 +616,7 @@
 #ifdef IDE_PMAC_DEBUG
 	printk(KERN_ERR "ide_pmac: MDMA, cycleTime: %d, accessTime: %d, recTime: %d\n",
 		cycleTime, accessTime, recTime);
-#endif	
+#endif
 	if (intf_type == controller_kl_ata4 || intf_type == controller_kl_ata4_80) {
 		/* 66Mhz cell */
 		accessTicks = SYSCLK_TICKS_66(accessTime);
@@ -646,7 +646,7 @@
 		int halfTick = 0;
 		int origAccessTime = accessTime;
 		int origRecTime = recTime;
-		
+
 		accessTicks = SYSCLK_TICKS(accessTime);
 		accessTicks = max(accessTicks, 1U);
 		accessTicks = min(accessTicks, 0x1fU);
@@ -658,7 +658,7 @@
 		if ((accessTicks > 1) &&
 		    ((accessTime - IDE_SYSCLK_NS/2) >= origAccessTime) &&
 		    ((recTime - IDE_SYSCLK_NS/2) >= origRecTime)) {
-            		halfTick = 1;
+			halfTick = 1;
 			accessTicks--;
 		}
 		*timings = ((*timings) & ~TR_33_MDMA_MASK) |
@@ -667,19 +667,19 @@
 		if (halfTick)
 			*timings |= TR_33_MDMA_HALFTICK;
 	}
-#ifdef IDE_PMAC_DEBUG
+# ifdef IDE_PMAC_DEBUG
 	printk(KERN_ERR "ide_pmac: Set MDMA timing for mode %d, reg: 0x%08x\n",
 		speed & 0xf,  *timings);
-#endif	
+# endif
 	return 0;
 }
-#endif /* #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC */
+#endif
 
 /* You may notice we don't use this function on normal operation,
  * our, normal mdma function is supposed to be more precise
  */
 static int __pmac
-pmac_ide_tune_chipset (ide_drive_t *drive, byte speed)
+pmac_ide_tune_chipset (struct ata_device *drive, byte speed)
 {
 	int intf		= pmac_ide_find(drive);
 	int unit		= (drive->select.b.unit & 0x01);
@@ -688,21 +688,21 @@
 
 	if (intf < 0)
 		return 1;
-		
+
 	timings = &pmac_ide[intf].timings[unit];
-	
+
 	switch(speed) {
 #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
 		case XFER_UDMA_4:
 		case XFER_UDMA_3:
 			if (pmac_ide[intf].kind != controller_kl_ata4_80)
-				return 1;		
+				return 1;
 		case XFER_UDMA_2:
 		case XFER_UDMA_1:
 		case XFER_UDMA_0:
 			if (pmac_ide[intf].kind != controller_kl_ata4 &&
 				pmac_ide[intf].kind != controller_kl_ata4_80)
-				return 1;		
+				return 1;
 			ret = set_timings_udma(timings, speed);
 			break;
 		case XFER_MW_DMA_2:
@@ -714,7 +714,7 @@
 		case XFER_SW_DMA_1:
 		case XFER_SW_DMA_0:
 			return 1;
-#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
+#endif
 		case XFER_PIO_4:
 		case XFER_PIO_3:
 		case XFER_PIO_2:
@@ -731,8 +731,8 @@
 	ret = pmac_ide_do_setfeature(drive, speed);
 	if (ret)
 		return ret;
-		
-	pmac_ide_selectproc(drive);	
+
+	pmac_ide_selectproc(drive);
 	drive->current_speed = speed;
 
 	return 0;
@@ -742,7 +742,7 @@
 sanitize_timings(int i)
 {
 	unsigned value;
-	
+
 	switch(pmac_ide[i].kind) {
 		case controller_kl_ata4:
 		case controller_kl_ata4_80:
@@ -770,8 +770,8 @@
 pmac_ide_check_base(ide_ioreg_t base)
 {
 	int ix;
-	
- 	for (ix = 0; ix < MAX_HWIFS; ++ix)
+
+	for (ix = 0; ix < MAX_HWIFS; ++ix)
 		if (base == pmac_ide[ix].regbase)
 			return ix;
 	return -1;
@@ -794,7 +794,7 @@
 pmac_find_ide_boot(char *bootdevice, int n)
 {
 	int i;
-	
+
 	/*
 	 * Look through the list of IDE interfaces for this one.
 	 */
@@ -857,7 +857,7 @@
 		int in_bay = 0;
 		u8 pbus, pid;
 		struct pci_dev *pdev = NULL;
-		
+
 		/*
 		 * If this node is not under a mac-io or dbdma node,
 		 * leave it to the generic PCI driver.
@@ -883,7 +883,7 @@
 		if (pdev == NULL)
 			printk(KERN_WARNING "ide: no PCI host for device %s, DMA disabled\n",
 			       np->full_name);
-		
+
 		/*
 		 * If this slot is taken (e.g. by ide-pci.c) try the next one.
 		 */
@@ -950,7 +950,7 @@
 		    && strcasecmp(np->parent->name, "media-bay") == 0) {
 #ifdef CONFIG_PMAC_PBOOK
 			media_bay_set_ide_infos(np->parent,base,irq,i);
-#endif /* CONFIG_PMAC_PBOOK */
+#endif
 			in_bay = 1;
 			if (!bidp)
 				pmif->aapl_bus_id = 1;
@@ -961,7 +961,7 @@
 			 */
 			ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, 0, 1);
 		} else {
- 			/* This is necessary to enable IDE when net-booting */
+			/* This is necessary to enable IDE when net-booting */
 			printk(KERN_INFO "pmac_ide: enabling IDE bus ID %d\n",
 				pmif->aapl_bus_id);
 			ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmif->aapl_bus_id, 1);
@@ -981,14 +981,14 @@
 #ifdef CONFIG_PMAC_PBOOK
 		if (in_bay && check_media_bay_by_base(base, MB_CD) == 0)
 			hwif->noprobe = 0;
-#endif /* CONFIG_PMAC_PBOOK */
+#endif
 
 #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
 		if (pdev && np->n_addrs >= 2) {
 			/* has a DBDMA controller channel */
 			pmac_ide_setup_dma(np, i);
 		}
-#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
+#endif
 
 		++i;
 	}
@@ -998,12 +998,12 @@
 
 #ifdef CONFIG_PMAC_PBOOK
 	pmu_register_sleep_notifier(&idepmac_sleep_notifier);
-#endif /* CONFIG_PMAC_PBOOK */
+#endif
 }
 
 #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
 
-static void __init 
+static void __init
 pmac_ide_setup_dma(struct device_node *np, int ix)
 {
 	struct pmac_ide_hwif *pmif = &pmac_ide[ix];
@@ -1037,7 +1037,7 @@
 	if (pmif->sg_table == NULL) {
 		pci_free_consistent(	ide_hwifs[ix].pci_dev,
 					(MAX_DCMDS + 2) * sizeof(struct dbdma_cmd),
-				    	pmif->dma_table_cpu, pmif->dma_table_dma);
+					pmif->dma_table_cpu, pmif->dma_table_dma);
 		return;
 	}
 	ide_hwifs[ix].udma_enable = pmac_udma_enable;
@@ -1230,7 +1230,7 @@
 
 /* Calculate MultiWord DMA timings */
 static int __pmac
-pmac_ide_mdma_enable(ide_drive_t *drive, int idx)
+pmac_ide_mdma_enable(struct ata_device *drive, int idx)
 {
 	byte bits = drive->id->dma_mword & 0x07;
 	byte feature = dma_bits_to_command(bits);
@@ -1240,16 +1240,16 @@
 	int ret;
 
 	/* Set feature on drive */
-    	printk(KERN_INFO "%s: Enabling MultiWord DMA %d\n", drive->name, feature & 0xf);
+	printk(KERN_INFO "%s: Enabling MultiWord DMA %d\n", drive->name, feature & 0xf);
 	ret = pmac_ide_do_setfeature(drive, feature);
 	if (ret) {
-	    	printk(KERN_WARNING "%s: Failed !\n", drive->name);
-	    	return 0;
+		printk(KERN_WARNING "%s: Failed !\n", drive->name);
+		return 0;
 	}
 
 	if (!drive->init_speed)
 		drive->init_speed = feature;
-	
+
 	/* which drive is it ? */
 	if (drive->select.b.unit & 0x01)
 		timings = &pmac_ide[idx].timings[1];
@@ -1265,13 +1265,13 @@
 	/* Calculate controller timings */
 	set_timings_mdma(pmac_ide[idx].kind, timings, feature, drive_cycle_time);
 
-	drive->current_speed = feature;	
+	drive->current_speed = feature;
 	return 1;
 }
 
 /* Calculate Ultra DMA timings */
 static int __pmac
-pmac_ide_udma_enable(ide_drive_t *drive, int idx, int high_speed)
+pmac_ide_udma_enable(struct ata_device *drive, int idx, int high_speed)
 {
 	byte bits = drive->id->dma_ultra & 0x1f;
 	byte feature = udma_bits_to_command(bits, high_speed);
@@ -1279,7 +1279,7 @@
 	int ret;
 
 	/* Set feature on drive */
-    	printk(KERN_INFO "%s: Enabling Ultra DMA %d\n", drive->name, feature & 0xf);
+	printk(KERN_INFO "%s: Enabling Ultra DMA %d\n", drive->name, feature & 0xf);
 	ret = pmac_ide_do_setfeature(drive, feature);
 	if (ret) {
 		printk(KERN_WARNING "%s: Failed !\n", drive->name);
@@ -1297,12 +1297,12 @@
 
 	set_timings_udma(timings, feature);
 
-	drive->current_speed = feature;	
+	drive->current_speed = feature;
 	return 1;
 }
 
 static int __pmac
-pmac_ide_check_dma(ide_drive_t *drive)
+pmac_ide_check_dma(struct ata_device *drive)
 {
 	int ata4, udma, idx;
 	struct hd_driveid *id = drive->id;
@@ -1343,7 +1343,7 @@
 	return 0;
 }
 
-static void ide_toggle_bounce(ide_drive_t *drive, int on)
+static void ide_toggle_bounce(struct ata_device *drive, int on)
 {
 	dma64_addr_t addr = BLK_BOUNCE_HIGH;
 
@@ -1432,7 +1432,7 @@
 	/* Apple adds 60ns to wrDataSetup on reads */
 	if (ata4 && (pmac_ide[ix].timings[unit] & TR_66_UDMA_EN)) {
 		out_le32((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG + _IO_BASE),
-			pmac_ide[ix].timings[unit] + 
+			pmac_ide[ix].timings[unit] +
 			((reading) ? 0x00800000UL : 0));
 		(void)in_le32((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG + _IO_BASE));
 	}
@@ -1530,7 +1530,7 @@
 }
 #endif
 
-static void idepmac_sleep_device(ide_drive_t *drive, int i, unsigned base)
+static void idepmac_sleep_device(struct ata_device *drive, int i, unsigned base)
 {
 	int j;
 
@@ -1568,7 +1568,7 @@
 
 #ifdef CONFIG_PMAC_PBOOK
 static void __pmac
-idepmac_wake_device(ide_drive_t *drive, int used_dma)
+idepmac_wake_device(struct ata_device *drive, int used_dma)
 {
 	/* We force the IDE subdriver to check for a media change
 	 * This must be done first or we may lost the condition
@@ -1581,7 +1581,7 @@
 
 	/* We kick the VFS too (see fix in ide.c revalidate) */
 	check_disk_change(mk_kdev(drive->channel->major, (drive->select.b.unit) << PARTN_BITS));
-	
+
 #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
 	/* We re-enable DMA on the drive if it was active. */
 	/* This doesn't work with the CD-ROM in the media-bay, probably
@@ -1590,12 +1590,12 @@
 	 */
 	if (used_dma && !ide_spin_wait_hwgroup(drive)) {
 		/* Lock HW group */
-		set_bit(IDE_BUSY, &HWGROUP(drive)->flags);
+		set_bit(IDE_BUSY, &drive->channel->active);
 		pmac_ide_check_dma(drive);
-		clear_bit(IDE_BUSY, &HWGROUP(drive)->flags);
-		spin_unlock_irq(&ide_lock);
+		clear_bit(IDE_BUSY, &drive->channel->active);
+		spin_unlock_irq(drive->channel->lock);
 	}
-#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
+#endif
 }
 
 static void __pmac
@@ -1606,11 +1606,11 @@
 	/* We clear the timings */
 	pmac_ide[i].timings[0] = 0;
 	pmac_ide[i].timings[1] = 0;
-	
+
 	/* The media bay will handle itself just fine */
 	if (mediabay)
 		return;
-	
+
 	/* Disable the bus */
 	ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, pmac_ide[i].aapl_bus_id, 0);
 }
@@ -1630,7 +1630,7 @@
 }
 
 static void
-idepmac_sleep_drive(ide_drive_t *drive, int idx, unsigned long base)
+idepmac_sleep_drive(struct ata_device *drive, int idx, unsigned long base)
 {
 	/* Wait for HW group to complete operations */
 	if (ide_spin_wait_hwgroup(drive))
@@ -1639,22 +1639,22 @@
 		return;
 	else {
 		/* Lock HW group */
-		set_bit(IDE_BUSY, &HWGROUP(drive)->flags);
+		set_bit(IDE_BUSY, &drive->channel->active);
 		/* Stop the device */
 		idepmac_sleep_device(drive, idx, base);
-		spin_unlock_irq(&ide_lock);
+		spin_unlock_irq(drive->channel->lock);
 	}
 }
 
 static void
-idepmac_wake_drive(ide_drive_t *drive, unsigned long base)
+idepmac_wake_drive(struct ata_device *drive, unsigned long base)
 {
 	int j;
-	
+
 	/* Reset timings */
 	pmac_ide_selectproc(drive);
 	mdelay(10);
-	
+
 	/* Wait up to 20 seconds for the drive to be ready */
 	for (j = 0; j < 200; j++) {
 		int status;
@@ -1667,12 +1667,12 @@
 			break;
 	}
 
-	/* We resume processing on the HW group */
-	spin_lock_irq(&ide_lock);
-	clear_bit(IDE_BUSY, &HWGROUP(drive)->flags);
+	/* We resume processing on the lock group */
+	spin_lock_irq(drive->channel->lock);
+	clear_bit(IDE_BUSY, &drive->channel->active);
 	if (!list_empty(&drive->queue.queue_head))
 		do_ide_request(&drive->queue);
-	spin_unlock_irq(&ide_lock);
+	spin_unlock_irq(drive->channel->lock);
 }
 
 /* Note: We support only master drives for now. This will have to be
@@ -1685,7 +1685,7 @@
 	int i, ret;
 	unsigned long base;
 	int big_delay;
- 
+
 	switch (when) {
 	case PBOOK_SLEEP_REQUEST:
 		break;
@@ -1707,7 +1707,7 @@
 			}
 			/* Disable irq during sleep */
 			disable_irq(pmac_ide[i].irq);
-			
+
 			/* Check if this is a media bay with an IDE device or not
 			 * a media bay.
 			 */
@@ -1722,8 +1722,8 @@
 
 			if ((base = pmac_ide[i].regbase) == 0)
 				continue;
-				
-			/* Make sure we have sane timings */		
+
+			/* Make sure we have sane timings */
 			sanitize_timings(i);
 
 			/* Check if this is a media bay with an IDE device or not
@@ -1731,7 +1731,7 @@
 			 */
 			ret = check_media_bay_by_base(base, MB_CD);
 			if ((ret == 0) || (ret == -ENODEV)) {
-				idepmac_wake_interface(i, base, (ret == 0));				
+				idepmac_wake_interface(i, base, (ret == 0));
 				big_delay = 1;
 			}
 
@@ -1739,18 +1739,18 @@
 		/* Let hardware get up to speed */
 		if (big_delay)
 			mdelay(IDE_WAKEUP_DELAY_MS);
-	
+
 		for (i = 0; i < pmac_ide_count; ++i) {
 			struct ata_channel *hwif;
 			int used_dma, dn;
 			int irq_on = 0;
-			
+
 			if ((base = pmac_ide[i].regbase) == 0)
 				continue;
-				
+
 			hwif = &ide_hwifs[i];
 			for (dn=0; dn<MAX_DRIVES; dn++) {
-				ide_drive_t *drive = &hwif->drives[dn];
+				struct ata_device *drive = &hwif->drives[dn];
 				if (!drive->present)
 					continue;
 				/* We don't have re-configured DMA yet */
diff -Nru a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
--- a/drivers/ide/ide-probe.c	Sat May 18 00:46:08 2002
+++ b/drivers/ide/ide-probe.c	Sat May 18 00:46:08 2002
@@ -578,15 +578,15 @@
 {
 	unsigned long flags;
 	int i;
-	ide_hwgroup_t *hwgroup;
-	ide_hwgroup_t *new_hwgroup;
+	spinlock_t *lock;
+	spinlock_t *new_lock;
 	struct ata_channel *match = NULL;
 
 	/* Spare allocation before sleep. */
-	new_hwgroup = kmalloc(sizeof(*hwgroup), GFP_KERNEL);
+	new_lock = kmalloc(sizeof(*lock), GFP_KERNEL);
 
 	spin_lock_irqsave(&ide_lock, flags);
-	ch->hwgroup = NULL;
+	ch->lock = NULL;
 
 #if MAX_HWIFS > 1
 	/*
@@ -596,7 +596,7 @@
 		struct ata_channel *h = &ide_hwifs[i];
 
 		/* scan only initialized channels */
-		if (!h->hwgroup)
+		if (!h->lock)
 			continue;
 
 		if (ch->irq != h->irq)
@@ -606,7 +606,7 @@
 
 		if (ch->chipset != ide_pci || h->chipset != ide_pci ||
 		     ch->serialized || h->serialized) {
-			if (match && match->hwgroup && match->hwgroup != h->hwgroup)
+			if (match && match->lock && match->lock != h->lock)
 				printk("%s: potential irq problem with %s and %s\n", ch->name, h->name, match->name);
 			/* don't undo a prior perfect match */
 			if (!match || match->irq != ch->irq)
@@ -615,22 +615,20 @@
 	}
 #endif
 	/*
-	 * If we are still without a hwgroup, then form a new one
+	 * If we are still without a lock group, then form a new one
 	 */
-	if (match) {
-		hwgroup = match->hwgroup;
-		if(new_hwgroup)
-			kfree(new_hwgroup);
-	} else {
-		hwgroup = new_hwgroup;
-		if (!hwgroup) {
+	if (!match) {
+		lock = new_lock;
+		if (!lock) {
 			spin_unlock_irqrestore(&ide_lock, flags);
+
 			return 1;
 		}
-		memset(hwgroup, 0, sizeof(*hwgroup));
-		init_timer(&hwgroup->timer);
-		hwgroup->timer.function = &ide_timer_expiry;
-		hwgroup->timer.data = (unsigned long) hwgroup;
+		spin_lock_init(lock);
+	} else {
+		lock = match->lock;
+		if(new_lock)
+			kfree(new_lock);
 	}
 
 	/*
@@ -648,7 +646,8 @@
 
 		if (request_irq(ch->irq, &ata_irq_request, sa, ch->name, ch)) {
 			if (!match)
-				kfree(hwgroup);
+				kfree(lock);
+
 			spin_unlock_irqrestore(&ide_lock, flags);
 
 			return 1;
@@ -656,9 +655,14 @@
 	}
 
 	/*
-	 * Everything is okay. Tag us as member of this hardware group.
+	 * Everything is okay. Tag us as member of this lock group.
 	 */
-	ch->hwgroup = hwgroup;
+	ch->lock = lock;
+
+	init_timer(&ch->timer);
+	ch->timer.function = &ide_timer_expiry;
+	ch->timer.data = (unsigned long) ch;
+
 	for (i = 0; i < MAX_DRIVES; ++i) {
 		struct ata_device *drive = &ch->drives[i];
 		request_queue_t *q;
@@ -667,8 +671,8 @@
 		if (!drive->present)
 			continue;
 
-		if (!hwgroup->XXX_drive)
-			hwgroup->XXX_drive = drive;
+		if (!ch->drive)
+			ch->drive = drive;
 
 		/*
 		 * Init the per device request queue
@@ -676,7 +680,7 @@
 
 		q = &drive->queue;
 		q->queuedata = drive->channel;
-		blk_init_queue(q, do_ide_request, &ide_lock);
+		blk_init_queue(q, do_ide_request, drive->channel->lock);
 		blk_queue_segment_boundary(q, 0xffff);
 
 		/* ATA can do up to 128K per request, pdc4030 needs smaller limit */
@@ -842,7 +846,6 @@
 	for (unit = 0; unit < MAX_DRIVES; ++unit) {
 		char name[80];
 
-		ide_add_generic_settings(ch->drives + unit);
 		ch->drives[unit].dn = ((ch->unit ? 2 : 0) + unit);
 		sprintf(name, "host%d/bus%d/target%d/lun%d",
 			ch->index, ch->unit, unit, ch->drives[unit].lun);
diff -Nru a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c
--- a/drivers/ide/ide-tape.c	Sat May 18 00:46:08 2002
+++ b/drivers/ide/ide-tape.c	Sat May 18 00:46:08 2002
@@ -1917,15 +1917,15 @@
 			idetape_active_next_stage (drive);
 
 			/*
-			 *	Insert the next request into the request queue.
+			 * Insert the next request into the request queue.
 			 */
-			(void) ide_do_drive_cmd (drive, tape->active_data_request, ide_end);
+			ide_do_drive_cmd(drive, tape->active_data_request, ide_end);
 		} else if (!error) {
 			if (!tape->onstream)
 				idetape_increase_max_pipeline_stages (drive);
 		}
 	}
-	ide_end_drive_cmd (drive, 0, 0);
+	ide_end_drive_cmd(drive, rq, 0, 0);
 	if (remove_stage)
 		idetape_remove_stage_head (drive);
 	if (tape->active_data_request == NULL)
@@ -1986,7 +1986,7 @@
 	ide_init_drive_cmd (rq);
 	rq->buffer = (char *) pc;
 	rq->flags = IDETAPE_PC_RQ1;
-	(void) ide_do_drive_cmd (drive, rq, ide_preempt);
+	ide_do_drive_cmd(drive, rq, ide_preempt);
 }
 
 /*
@@ -2228,7 +2228,7 @@
  *		we will handle the next request.
  *
  */
-static ide_startstop_t idetape_transfer_pc(struct ata_device *drive, struct request *__rq)
+static ide_startstop_t idetape_transfer_pc(struct ata_device *drive, struct request *rq)
 {
 	idetape_tape_t *tape = drive->driver_data;
 	idetape_pc_t *pc = tape->pc;
@@ -2236,7 +2236,7 @@
 	int retries = 100;
 	ide_startstop_t startstop;
 
-	if (ide_wait_stat (&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+	if (ide_wait_stat(&startstop, drive, rq, DRQ_STAT, BUSY_STAT, WAIT_READY)) {
 		printk (KERN_ERR "ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n");
 		return startstop;
 	}
@@ -3197,7 +3197,7 @@
 	ide_init_drive_cmd (&rq);
 	rq.buffer = (char *) pc;
 	rq.flags = IDETAPE_PC_RQ1;
-	return ide_do_drive_cmd (drive, &rq, ide_wait);
+	return ide_do_drive_cmd(drive, &rq, ide_wait);
 }
 
 static void idetape_create_load_unload_cmd (ide_drive_t *drive, idetape_pc_t *pc,int cmd)
@@ -5926,41 +5926,7 @@
 	tape->tape_block_size =( block_descrp->length[0]<<16) + (block_descrp->length[1]<<8) + block_descrp->length[2];
 #if IDETAPE_DEBUG_INFO
 	printk (KERN_INFO "ide-tape: Adjusted block size - %d\n", tape->tape_block_size);
-#endif /* IDETAPE_DEBUG_INFO */
-}
-static void idetape_add_settings (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-
-/*
- *			drive	setting name	read/write	ioctl	ioctl		data type	min			max			mul_factor			div_factor			data pointer				set function
- */
-	ide_add_setting(drive,	"buffer",	SETTING_READ,	-1,	-1,		TYPE_SHORT,	0,			0xffff,			1,				2,				&tape->capabilities.buffer_size,	NULL);
-	ide_add_setting(drive,	"pipeline_min",	SETTING_RW,	-1,	-1,		TYPE_INT,	2,			0xffff,			tape->stage_size / 1024,	1,				&tape->min_pipeline,			NULL);
-	ide_add_setting(drive,	"pipeline",	SETTING_RW,	-1,	-1,		TYPE_INT,	2,			0xffff,			tape->stage_size / 1024,	1,				&tape->max_stages,			NULL);
-	ide_add_setting(drive,	"pipeline_max",	SETTING_RW,	-1,	-1,		TYPE_INT,	2,			0xffff,			tape->stage_size / 1024,	1,				&tape->max_pipeline,			NULL);
-	ide_add_setting(drive,	"pipeline_used",SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			tape->stage_size / 1024,	1,				&tape->nr_stages,			NULL);
-	ide_add_setting(drive,	"pipeline_pending",SETTING_READ,-1,	-1,		TYPE_INT,	0,			0xffff,			tape->stage_size / 1024,	1,				&tape->nr_pending_stages,		NULL);
-	ide_add_setting(drive,	"speed",	SETTING_READ,	-1,	-1,		TYPE_SHORT,	0,			0xffff,			1,				1,				&tape->capabilities.speed,		NULL);
-	ide_add_setting(drive,	"stage",	SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1024,				&tape->stage_size,			NULL);
-	ide_add_setting(drive,	"tdsc",		SETTING_RW,	-1,	-1,		TYPE_INT,	IDETAPE_DSC_RW_MIN,	IDETAPE_DSC_RW_MAX,	1000,				HZ,				&tape->best_dsc_rw_frequency,		NULL);
-	ide_add_setting(drive,	"dsc_overlap",	SETTING_RW,	-1,	-1,		TYPE_BYTE,	0,			1,			1,				1,				&drive->dsc_overlap,			NULL);
-	ide_add_setting(drive,	"pipeline_head_speed_c",SETTING_READ,	-1,	-1,	TYPE_INT,	0,			0xffff,			1,				1,				&tape->controlled_pipeline_head_speed,	NULL);
-	ide_add_setting(drive,	"pipeline_head_speed_u",SETTING_READ,	-1,	-1,	TYPE_INT,	0,			0xffff,			1,				1,				&tape->uncontrolled_pipeline_head_speed,	NULL);
-	ide_add_setting(drive,	"avg_speed",	SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->avg_speed,		NULL);
-	ide_add_setting(drive,	"debug_level",SETTING_RW,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->debug_level,		NULL);
-	if (tape->onstream) {
-		ide_add_setting(drive,	"cur_frames",	SETTING_READ,	-1,	-1,		TYPE_SHORT,	0,			0xffff,			1,				1,				&tape->cur_frames,		NULL);
-		ide_add_setting(drive,	"max_frames",	SETTING_READ,	-1,	-1,		TYPE_SHORT,	0,			0xffff,			1,				1,				&tape->max_frames,		NULL);
-		ide_add_setting(drive,	"insert_speed",	SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->insert_speed,		NULL);
-		ide_add_setting(drive,	"speed_control",SETTING_RW,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->speed_control,		NULL);
-		ide_add_setting(drive,	"tape_still_time",SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->tape_still_time,		NULL);
-		ide_add_setting(drive,	"max_insert_speed",SETTING_RW,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->max_insert_speed,	NULL);
-		ide_add_setting(drive,	"insert_size",	SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->insert_size,		NULL);
-		ide_add_setting(drive,	"capacity",	SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->capacity,		NULL);
-		ide_add_setting(drive,	"first_frame",	SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->first_frame_position,		NULL);
-		ide_add_setting(drive,	"logical_blk",	SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->logical_blk_num,		NULL);
-	}
+#endif
 }
 
 /*
@@ -6074,8 +6040,6 @@
 		drive->name, tape->name, tape->capabilities.speed, (tape->capabilities.buffer_size * 512) / tape->stage_size,
 		tape->stage_size / 1024, tape->max_stages * tape->stage_size / 1024,
 		tape->best_dsc_rw_frequency * 1000 / HZ, drive->using_dma ? ", DMA":"");
-
-	idetape_add_settings(drive);
 }
 
 static int idetape_cleanup (ide_drive_t *drive)
diff -Nru a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c
--- a/drivers/ide/ide-taskfile.c	Sat May 18 00:46:10 2002
+++ b/drivers/ide/ide-taskfile.c	Sat May 18 00:46:10 2002
@@ -19,6 +19,7 @@
 #include <linux/errno.h>
 #include <linux/genhd.h>
 #include <linux/blkpg.h>
+#include <linux/completion.h>
 #include <linux/slab.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
@@ -61,41 +62,6 @@
  * Data transfer functions for polled IO.
  */
 
-#if SUPPORT_VLB_SYNC
-/*
- * Some localbus EIDE interfaces require a special access sequence
- * when using 32-bit I/O instructions to transfer data.  We call this
- * the "vlb_sync" sequence, which consists of three successive reads
- * of the sector count register location, with interrupts disabled
- * to ensure that the reads all happen together.
- */
-static void ata_read_vlb(struct ata_device *drive, void *buffer, unsigned int wcount)
-{
-	unsigned long flags;
-
-	__save_flags(flags);	/* local CPU only */
-	__cli();		/* local CPU only */
-	IN_BYTE(IDE_NSECTOR_REG);
-	IN_BYTE(IDE_NSECTOR_REG);
-	IN_BYTE(IDE_NSECTOR_REG);
-	insl(IDE_DATA_REG, buffer, wcount);
-	__restore_flags(flags);	/* local CPU only */
-}
-
-static void ata_write_vlb(struct ata_device *drive, void *buffer, unsigned int wcount)
-{
-	unsigned long flags;
-
-	__save_flags(flags);	/* local CPU only */
-	__cli();		/* local CPU only */
-	IN_BYTE(IDE_NSECTOR_REG);
-	IN_BYTE(IDE_NSECTOR_REG);
-	IN_BYTE(IDE_NSECTOR_REG);
-	outsl(IDE_DATA_REG, buffer, wcount);
-	__restore_flags(flags);	/* local CPU only */
-}
-#endif
-
 static void ata_read_32(struct ata_device *drive, void *buffer, unsigned int wcount)
 {
 	insl(IDE_DATA_REG, buffer, wcount);
@@ -157,12 +123,7 @@
 	io_32bit = drive->channel->io_32bit;
 
 	if (io_32bit) {
-#if SUPPORT_VLB_SYNC
-		if (io_32bit & 2)
-			ata_read_vlb(drive, buffer, wcount);
-		else
-#endif
-			ata_read_32(drive, buffer, wcount);
+		ata_read_32(drive, buffer, wcount);
 	} else {
 #if SUPPORT_SLOW_DATA_PORTS
 		if (drive->channel->slow)
@@ -188,12 +149,7 @@
 	io_32bit = drive->channel->io_32bit;
 
 	if (io_32bit) {
-#if SUPPORT_VLB_SYNC
-		if (io_32bit & 2)
-			ata_write_vlb(drive, buffer, wcount);
-		else
-#endif
-			ata_write_32(drive, buffer, wcount);
+		ata_write_32(drive, buffer, wcount);
 	} else {
 #if SUPPORT_SLOW_DATA_PORTS
 		if (drive->channel->slow)
@@ -279,6 +235,7 @@
 
 	if (stat & BUSY_STAT)
 		return 0;	/* drive busy:  definitely not interrupting */
+
 	return 1;		/* drive ready: *might* be interrupting */
 }
 
@@ -308,7 +265,7 @@
 	struct ata_taskfile *args = rq->special;
 	ide_startstop_t startstop;
 
-	if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ))
+	if (ide_wait_stat(&startstop, drive, rq, DATA_READY, drive->bad_wstat, WAIT_DRQ))
 		return startstop;
 
 	ata_poll_drive_ready(drive);
@@ -319,7 +276,6 @@
 static ide_startstop_t task_mulout_intr(struct ata_device *drive, struct request *rq)
 {
 	u8 stat = GET_STAT();
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 	int mcount = drive->mult_count;
 	ide_startstop_t startstop;
 
@@ -329,7 +285,7 @@
 	 */
 	if (!rq->nr_sectors) {
 		if (stat & (ERR_STAT|DRQ_STAT)) {
-			startstop = ide_error(drive, "task_mulout_intr", stat);
+			startstop = ide_error(drive, rq, "task_mulout_intr", stat);
 
 			return startstop;
 		}
@@ -342,13 +298,13 @@
 
 	if (!OK_STAT(stat, DATA_READY, BAD_R_STAT)) {
 		if (stat & (ERR_STAT | DRQ_STAT)) {
-			startstop = ide_error(drive, "task_mulout_intr", stat);
+			startstop = ide_error(drive, rq, "task_mulout_intr", stat);
 
 			return startstop;
 		}
 
 		/* no data yet, so wait for another interrupt */
-		if (hwgroup->handler == NULL)
+		if (!drive->channel->handler)
 			ide_set_handler(drive, task_mulout_intr, WAIT_CMD, NULL);
 
 		return ide_started;
@@ -391,7 +347,7 @@
 	} while (mcount);
 
 	rq->errors = 0;
-	if (hwgroup->handler == NULL)
+	if (!drive->channel->handler)
 		ide_set_handler(drive, task_mulout_intr, WAIT_CMD, NULL);
 
 	return ide_started;
@@ -489,12 +445,12 @@
 /*
  * This is invoked on completion of a WIN_RESTORE (recalibrate) cmd.
  */
-ide_startstop_t recal_intr(struct ata_device *drive, struct request *__rq)
+ide_startstop_t recal_intr(struct ata_device *drive, struct request *rq)
 {
 	u8 stat;
 
 	if (!OK_STAT(stat = GET_STAT(),READY_STAT,BAD_STAT))
-		return ide_error(drive, "recal_intr", stat);
+		return ide_error(drive, rq, "recal_intr", stat);
 	return ide_stopped;
 }
 
@@ -511,11 +467,11 @@
 	if (!OK_STAT(stat = GET_STAT(), READY_STAT, BAD_STAT)) {
 		/* Keep quiet for NOP because it is expected to fail. */
 		if (args && args->taskfile.command != WIN_NOP)
-			return ide_error(drive, "task_no_data_intr", stat);
+			return ide_error(drive, rq, "task_no_data_intr", stat);
 	}
 
 	if (args)
-		ide_end_drive_cmd (drive, stat, GET_ERR());
+		ide_end_drive_cmd(drive, rq, stat, GET_ERR());
 
 	return ide_stopped;
 }
@@ -523,7 +479,7 @@
 /*
  * Handler for command with PIO data-in phase
  */
-static ide_startstop_t task_in_intr (struct ata_device *drive, struct request *rq)
+static ide_startstop_t task_in_intr(struct ata_device *drive, struct request *rq)
 {
 	u8 stat	= GET_STAT();
 	char *pBuf = NULL;
@@ -531,7 +487,7 @@
 
 	if (!OK_STAT(stat,DATA_READY,BAD_R_STAT)) {
 		if (stat & (ERR_STAT|DRQ_STAT)) {
-			return ide_error(drive, "task_in_intr", stat);
+			return ide_error(drive, rq, "task_in_intr", stat);
 		}
 		if (!(stat & BUSY_STAT)) {
 			DTF("task_in_intr to Soon wait for next interrupt\n");
@@ -569,7 +525,7 @@
 	struct ata_taskfile *args = rq->special;
 	ide_startstop_t startstop;
 
-	if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
+	if (ide_wait_stat(&startstop, drive, rq, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
 		printk(KERN_ERR "%s: no DRQ after issuing %s\n", drive->name, drive->mult_count ? "MULTWRITE" : "WRITE");
 		return startstop;
 	}
@@ -600,13 +556,13 @@
 	unsigned long flags;
 
 	if (!OK_STAT(stat,DRIVE_READY,drive->bad_wstat))
-		return ide_error(drive, "task_out_intr", stat);
+		return ide_error(drive, rq, "task_out_intr", stat);
 
 	if (!rq->current_nr_sectors)
 		if (!ide_end_request(drive, rq, 1))
 			return ide_stopped;
 
-	if ((rq->current_nr_sectors==1) ^ (stat & DRQ_STAT)) {
+	if ((rq->nr_sectors == 1) != (stat & DRQ_STAT)) {
 		pBuf = ide_map_rq(rq, &flags);
 		DTF("write: %p, rq->current_nr_sectors: %d\n", pBuf, (int) rq->current_nr_sectors);
 
@@ -632,7 +588,7 @@
 
 	if (!OK_STAT(stat = GET_STAT(),DATA_READY,BAD_R_STAT)) {
 		if (stat & (ERR_STAT|DRQ_STAT)) {
-			return ide_error(drive, "task_mulin_intr", stat);
+			return ide_error(drive, rq, "task_mulin_intr", stat);
 		}
 		/* no data yet, so wait for another interrupt */
 		ide_set_handler(drive, task_mulin_intr, WAIT_CMD, NULL);
@@ -858,6 +814,77 @@
 	}
 }
 
+/*
+ * This function is intended to be used prior to invoking ide_do_drive_cmd().
+ */
+void ide_init_drive_cmd(struct request *rq)
+{
+	memset(rq, 0, sizeof(*rq));
+	rq->flags = REQ_DRIVE_CMD;
+}
+
+/*
+ * This function issues a special IDE device request onto the request queue.
+ *
+ * If action is ide_wait, then the rq is queued at the end of the request
+ * queue, and the function sleeps until it has been processed.  This is for use
+ * when invoked from an ioctl handler.
+ *
+ * If action is ide_preempt, then the rq is queued at the head of the request
+ * queue, displacing the currently-being-processed request and this function
+ * returns immediately without waiting for the new rq to be completed.  This is
+ * VERY DANGEROUS, and is intended for careful use by the ATAPI tape/cdrom
+ * driver code.
+ *
+ * If action is ide_end, then the rq is queued at the end of the request queue,
+ * and the function returns immediately without waiting for the new rq to be
+ * completed. This is again intended for careful use by the ATAPI tape/cdrom
+ * driver code.
+ */
+int ide_do_drive_cmd(struct ata_device *drive, struct request *rq, ide_action_t action)
+{
+	unsigned long flags;
+	unsigned int major = drive->channel->major;
+	request_queue_t *q = &drive->queue;
+	struct list_head *queue_head = &q->queue_head;
+	DECLARE_COMPLETION(wait);
+
+#ifdef CONFIG_BLK_DEV_PDC4030
+	if (drive->channel->chipset == ide_pdc4030 && rq->buffer != NULL)
+		return -ENOSYS;  /* special drive cmds not supported */
+#endif
+	rq->errors = 0;
+	rq->rq_status = RQ_ACTIVE;
+	rq->rq_dev = mk_kdev(major,(drive->select.b.unit)<<PARTN_BITS);
+	if (action == ide_wait)
+		rq->waiting = &wait;
+
+	spin_lock_irqsave(drive->channel->lock, flags);
+
+	if (blk_queue_empty(&drive->queue) || action == ide_preempt) {
+		if (action == ide_preempt)
+			drive->rq = NULL;
+	} else {
+		if (action == ide_wait)
+			queue_head = queue_head->prev;
+		else
+			queue_head = queue_head->next;
+	}
+	q->elevator.elevator_add_req_fn(q, rq, queue_head);
+
+	do_ide_request(q);
+
+	spin_unlock_irqrestore(drive->channel->lock, flags);
+
+	if (action == ide_wait) {
+		wait_for_completion(&wait);	/* wait for it to be serviced */
+		return rq->errors ? -EIO : 0;	/* return -EIO if errors */
+	}
+
+	return 0;
+
+}
+
 int ide_raw_taskfile(struct ata_device *drive, struct ata_taskfile *args)
 {
 	struct request rq;
@@ -884,12 +911,59 @@
  * interface.
  */
 
+/*
+ * Backside of HDIO_DRIVE_CMD call of SETFEATURES_XFER.
+ * 1 : Safe to update drive->id DMA registers.
+ * 0 : OOPs not allowed.
+ */
+static int set_transfer(struct ata_device *drive, struct ata_taskfile *args)
+{
+	if ((args->taskfile.command == WIN_SETFEATURES) &&
+	    (args->taskfile.sector_number >= XFER_SW_DMA_0) &&
+	    (args->taskfile.feature == SETFEATURES_XFER) &&
+	    (drive->id->dma_ultra ||
+	     drive->id->dma_mword ||
+	     drive->id->dma_1word))
+		return 1;
+
+	return 0;
+}
+
+/*
+ * Verify that we are doing an approved SETFEATURES_XFER with respect
+ * to the hardware being able to support request.  Since some hardware
+ * can improperly report capabilties, we check to see if the host adapter
+ * in combination with the device (usually a disk) properly detect
+ * and acknowledge each end of the ribbon.
+ */
+static int ata66_check(struct ata_device *drive, struct ata_taskfile *args)
+{
+	if ((args->taskfile.command == WIN_SETFEATURES) &&
+	    (args->taskfile.sector_number > XFER_UDMA_2) &&
+	    (args->taskfile.feature == SETFEATURES_XFER)) {
+		if (!drive->channel->udma_four) {
+			printk("%s: Speed warnings UDMA 3/4/5 is not functional.\n", drive->channel->name);
+			return 1;
+		}
+#ifndef CONFIG_IDEDMA_IVB
+		if ((drive->id->hw_config & 0x6000) == 0) {
+#else
+		if (((drive->id->hw_config & 0x2000) == 0) ||
+		    ((drive->id->hw_config & 0x4000) == 0)) {
+#endif
+			printk("%s: Speed warnings UDMA 3/4/5 is not functional.\n", drive->name);
+			return 1;
+		}
+	}
+	return 0;
+}
+
 int ide_cmd_ioctl(struct ata_device *drive, unsigned long arg)
 {
 	int err = 0;
 	u8 vals[4];
 	u8 *argbuf = vals;
-	u8 xfer_rate = 0;
+	u8 pio = 0;
 	int argsize = 4;
 	struct ata_taskfile args;
 	struct request rq;
@@ -924,10 +998,11 @@
 	}
 
 	/* Always make sure the transfer reate has been setup.
+	 * FIXME: what about setting up the drive with ->tuneproc?
 	 */
 	if (set_transfer(drive, &args)) {
-		xfer_rate = vals[1];
-		if (ide_ata66_check(drive, &args))
+		pio = vals[1];
+		if (ata66_check(drive, &args))
 			goto abort;
 	}
 
@@ -936,10 +1011,11 @@
 	rq.buffer = argbuf;
 	err = ide_do_drive_cmd(drive, &rq, ide_wait);
 
-	if (!err && xfer_rate) {
+	if (!err && pio) {
 		/* active-retuning-calls future */
-		if ((drive->channel->speedproc) != NULL)
-			drive->channel->speedproc(drive, xfer_rate);
+		/* FIXME: what about the setup for the drive?! */
+		if (drive->channel->speedproc)
+			drive->channel->speedproc(drive, pio);
 		ide_driveid_update(drive);
 	}
 
@@ -961,6 +1037,8 @@
 EXPORT_SYMBOL(ata_taskfile);
 EXPORT_SYMBOL(recal_intr);
 EXPORT_SYMBOL(task_no_data_intr);
+EXPORT_SYMBOL(ide_init_drive_cmd);
+EXPORT_SYMBOL(ide_do_drive_cmd);
 EXPORT_SYMBOL(ide_raw_taskfile);
 EXPORT_SYMBOL(ide_cmd_type_parser);
 EXPORT_SYMBOL(ide_cmd_ioctl);
diff -Nru a/drivers/ide/ide.c b/drivers/ide/ide.c
--- a/drivers/ide/ide.c	Sat May 18 00:46:09 2002
+++ b/drivers/ide/ide.c	Sat May 18 00:46:09 2002
@@ -54,7 +54,6 @@
 #include <linux/delay.h>
 #include <linux/ide.h>
 #include <linux/devfs_fs_kernel.h>
-#include <linux/completion.h>
 #include <linux/reboot.h>
 #include <linux/cdrom.h>
 #include <linux/device.h>
@@ -290,7 +289,7 @@
 	unsigned long flags;
 	int ret = 1;
 
-	spin_lock_irqsave(&ide_lock, flags);
+	spin_lock_irqsave(drive->channel->lock, flags);
 
 	BUG_ON(!(rq->flags & REQ_STARTED));
 
@@ -317,12 +316,13 @@
 			blkdev_dequeue_request(rq);
 		else
 			blk_queue_end_tag(&drive->queue, rq);
-		HWGROUP(drive)->rq = NULL;
+		drive->rq = NULL;
 		end_that_request_last(rq);
 		ret = 0;
 	}
 
-	spin_unlock_irqrestore(&ide_lock, flags);
+	spin_unlock_irqrestore(drive->channel->lock, flags);
+
 	return ret;
 }
 
@@ -337,18 +337,45 @@
 		      unsigned long timeout, ata_expiry_t expiry)
 {
 	unsigned long flags;
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	struct ata_channel *ch = drive->channel;
 
-	spin_lock_irqsave(&ide_lock, flags);
-	if (hwgroup->handler != NULL) {
+	spin_lock_irqsave(ch->lock, flags);
+
+	if (ch->handler != NULL) {
 		printk("%s: ide_set_handler: handler not null; old=%p, new=%p, from %p\n",
-			drive->name, hwgroup->handler, handler, __builtin_return_address(0));
+			drive->name, ch->handler, handler, __builtin_return_address(0));
+	}
+	ch->handler = handler;
+	ch->expiry = expiry;
+	ch->timer.expires = jiffies + timeout;
+	add_timer(&ch->timer);
+
+	spin_unlock_irqrestore(ch->lock, flags);
+}
+
+static u8 auto_reduce_xfer(struct ata_device *drive)
+{
+	if (!drive->crc_count)
+		return drive->current_speed;
+	drive->crc_count = 0;
+
+	switch(drive->current_speed) {
+		case XFER_UDMA_7:	return XFER_UDMA_6;
+		case XFER_UDMA_6:	return XFER_UDMA_5;
+		case XFER_UDMA_5:	return XFER_UDMA_4;
+		case XFER_UDMA_4:	return XFER_UDMA_3;
+		case XFER_UDMA_3:	return XFER_UDMA_2;
+		case XFER_UDMA_2:	return XFER_UDMA_1;
+		case XFER_UDMA_1:	return XFER_UDMA_0;
+			/*
+			 * OOPS we do not goto non Ultra DMA modes
+			 * without iCRC's available we force
+			 * the system to PIO and make the user
+			 * invoke the ATA-1 ATA-2 DMA modes.
+			 */
+		case XFER_UDMA_0:
+		default:		return XFER_PIO_4;
 	}
-	hwgroup->handler	= handler;
-	hwgroup->expiry		= expiry;
-	hwgroup->timer.expires	= jiffies + timeout;
-	add_timer(&hwgroup->timer);
-	spin_unlock_irqrestore(&ide_lock, flags);
 }
 
 static void check_crc_errors(struct ata_device *drive)
@@ -359,8 +386,10 @@
 	/* check the DMA crc count */
 	if (drive->crc_count) {
 		udma_enable(drive, 0, 0);
-		if ((drive->channel->speedproc) != NULL)
-		        drive->channel->speedproc(drive, ide_auto_reduce_xfer(drive));
+		if (drive->channel->speedproc) {
+			u8 pio = auto_reduce_xfer(drive);
+		        drive->channel->speedproc(drive, pio);
+		}
 		if (drive->current_speed >= XFER_SW_DMA_0)
 			udma_enable(drive, 1, 1);
 	} else
@@ -592,15 +621,11 @@
 
 /*
  * Clean up after success/failure of an explicit drive cmd
+ *
+ * Should be called under lock held.
  */
-void ide_end_drive_cmd(struct ata_device *drive, byte stat, byte err)
+void ide_end_drive_cmd(struct ata_device *drive, struct request *rq, u8 stat, u8 err)
 {
-	unsigned long flags;
-	struct request *rq;
-
-	spin_lock_irqsave(&ide_lock, flags);
-	rq = HWGROUP(drive)->rq;
-
 	if (rq->flags & REQ_DRIVE_CMD) {
 		u8 *args = rq->buffer;
 		rq->errors = !OK_STAT(stat, READY_STAT, BAD_STAT);
@@ -638,16 +663,14 @@
 	}
 
 	blkdev_dequeue_request(rq);
-	HWGROUP(drive)->rq = NULL;
+	drive->rq = NULL;
 	end_that_request_last(rq);
-
-	spin_unlock_irqrestore(&ide_lock, flags);
 }
 
 /*
  * Error reporting, in human readable form (luxurious, but a memory hog).
  */
-byte ide_dump_status(struct ata_device *drive, const char *msg, byte stat)
+u8 ide_dump_status(struct ata_device *drive, struct request * rq, const char *msg, u8 stat)
 {
 	unsigned long flags;
 	byte err = 0;
@@ -722,8 +745,8 @@
 						  IN_BYTE(IDE_SECTOR_REG));
 					}
 				}
-				if (HWGROUP(drive) && HWGROUP(drive)->rq)
-					printk(", sector=%ld", HWGROUP(drive)->rq->sector);
+				if (rq)
+					printk(", sector=%ld", rq->sector);
 			}
 		}
 #endif
@@ -786,18 +809,17 @@
 /*
  * Take action based on the error returned by the drive.
  */
-ide_startstop_t ide_error(struct ata_device *drive, const char *msg, byte stat)
+ide_startstop_t ide_error(struct ata_device *drive, struct request *rq, const char *msg, byte stat)
 {
-	struct request *rq;
 	byte err;
 
-	err = ide_dump_status(drive, msg, stat);
-	if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL)
+	err = ide_dump_status(drive, rq, msg, stat);
+	if (!drive || !rq)
 		return ide_stopped;
 	/* retry only "normal" I/O: */
 	if (!(rq->flags & REQ_CMD)) {
 		rq->errors = 1;
-		ide_end_drive_cmd(drive, stat, err);
+		ide_end_drive_cmd(drive, rq, stat, err);
 		return ide_stopped;
 	}
 
@@ -839,19 +861,6 @@
 }
 
 /*
- * Issue a simple drive command.  The drive must be selected beforehand.
- */
-void ide_cmd(struct ata_device *drive, byte cmd, byte nsect, ata_handler_t handler)
-{
-	ide_set_handler (drive, handler, WAIT_CMD, NULL);
-	if (IDE_CONTROL_REG)
-		OUT_BYTE(drive->ctl,IDE_CONTROL_REG);	/* clear nIEN */
-	SELECT_MASK(drive->channel, drive, 0);
-	OUT_BYTE(nsect,IDE_NSECTOR_REG);
-	OUT_BYTE(cmd,IDE_COMMAND_REG);
-}
-
-/*
  * Invoked on completion of a special DRIVE_CMD.
  */
 static ide_startstop_t drive_cmd_intr(struct ata_device *drive, struct request *rq)
@@ -869,13 +878,27 @@
 	}
 
 	if (!OK_STAT(stat, READY_STAT, BAD_STAT))
-		return ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
-	ide_end_drive_cmd (drive, stat, GET_ERR());
+		return ide_error(drive, rq, "drive_cmd", stat); /* already calls ide_end_drive_cmd */
+	ide_end_drive_cmd(drive, rq, stat, GET_ERR());
 
 	return ide_stopped;
 }
 
 /*
+ * Issue a simple drive command.  The drive must be selected beforehand.
+ */
+static void drive_cmd(struct ata_device *drive, u8 cmd, u8 nsect)
+{
+	ide_set_handler(drive, drive_cmd_intr, WAIT_CMD, NULL);
+	if (IDE_CONTROL_REG)
+		OUT_BYTE(drive->ctl, IDE_CONTROL_REG);	/* clear nIEN */
+	SELECT_MASK(drive->channel, drive, 0);
+	OUT_BYTE(nsect, IDE_NSECTOR_REG);
+	OUT_BYTE(cmd, IDE_COMMAND_REG);
+}
+
+
+/*
  * Busy-wait for the drive status to be not "busy".  Check then the status for
  * all of the "good" bits and none of the "bad" bits, and if all is okay it
  * returns 0.  All other cases return 1 after invoking ide_error() -- caller
@@ -886,10 +909,12 @@
  * setting a timer to wake up at half second intervals thereafter, until
  * timeout is achieved, before timing out.
  */
-int ide_wait_stat(ide_startstop_t *startstop, struct ata_device *drive, byte good, byte bad, unsigned long timeout) {
-	byte stat;
+int ide_wait_stat(ide_startstop_t *startstop,
+		struct ata_device *drive, struct request *rq,
+		byte good, byte bad, unsigned long timeout)
+{
+	u8 stat;
 	int i;
-	unsigned long flags;
 
 	/* bail early if we've exceeded max_failures */
 	if (drive->max_failures && (drive->failures > drive->max_failures)) {
@@ -899,31 +924,28 @@
 
 	udelay(1);	/* spec allows drive 400ns to assert "BUSY" */
 	if ((stat = GET_STAT()) & BUSY_STAT) {
-		__save_flags(flags);	/* local CPU only */
-		ide__sti();		/* local CPU only */
 		timeout += jiffies;
 		while ((stat = GET_STAT()) & BUSY_STAT) {
-			if (0 < (signed long)(jiffies - timeout)) {
-				__restore_flags(flags);	/* local CPU only */
-				*startstop = ide_error(drive, "status timeout", stat);
+			if (time_after(jiffies, timeout)) {
+				*startstop = ide_error(drive, rq, "status timeout", stat);
 				return 1;
 			}
 		}
-		__restore_flags(flags);	/* local CPU only */
 	}
+
 	/*
-	 * Allow status to settle, then read it again.
-	 * A few rare drives vastly violate the 400ns spec here,
-	 * so we'll wait up to 10usec for a "good" status
-	 * rather than expensively fail things immediately.
-	 * This fix courtesy of Matthew Faupel & Niccolo Rigacci.
+	 * Allow status to settle, then read it again.  A few rare drives
+	 * vastly violate the 400ns spec here, so we'll wait up to 10usec for a
+	 * "good" status rather than expensively fail things immediately.  This
+	 * fix courtesy of Matthew Faupel & Niccolo Rigacci.
 	 */
 	for (i = 0; i < 10; i++) {
 		udelay(1);
 		if (OK_STAT((stat = GET_STAT()), good, bad))
 			return 0;
 	}
-	*startstop = ide_error(drive, "status error", stat);
+	*startstop = ide_error(drive, rq, "status error", stat);
+
 	return 1;
 }
 
@@ -975,7 +997,7 @@
 		ide_startstop_t res;
 
 		SELECT_DRIVE(ch, drive);
-		if (ide_wait_stat(&res, drive, drive->ready_stat,
+		if (ide_wait_stat(&res, drive, rq, drive->ready_stat,
 					BUSY_STAT|DRQ_STAT, WAIT_READY)) {
 			printk(KERN_WARNING "%s: drive not ready for command\n", drive->name);
 
@@ -1019,12 +1041,12 @@
 			OUT_BYTE(0xc2, IDE_HCYL_REG);
 			OUT_BYTE(args[2],IDE_FEATURE_REG);
 			OUT_BYTE(args[1],IDE_SECTOR_REG);
-			ide_cmd(drive, args[0], args[3], &drive_cmd_intr);
+			drive_cmd(drive, args[0], args[3]);
 
 			return ide_started;
 		}
 		OUT_BYTE(args[2],IDE_FEATURE_REG);
-		ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
+		drive_cmd(drive, args[0], args[1]);
 
 		return ide_started;
 	}
@@ -1066,29 +1088,29 @@
 #ifdef DEBUG
 	printk("%s: DRIVE_CMD (null)\n", drive->name);
 #endif
-	ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
+	ide_end_drive_cmd(drive, rq, GET_STAT(), GET_ERR());
 
 	return ide_stopped;
 }
 
 ide_startstop_t restart_request(struct ata_device *drive)
 {
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	struct ata_channel *ch = drive->channel;
 	unsigned long flags;
-	struct request *rq;
 
-	spin_lock_irqsave(&ide_lock, flags);
-	hwgroup->handler = NULL;
-	del_timer(&hwgroup->timer);
-	rq = hwgroup->rq;
-	spin_unlock_irqrestore(&ide_lock, flags);
+	spin_lock_irqsave(ch->lock, flags);
+
+	ch->handler = NULL;
+	del_timer(&ch->timer);
+
+	spin_unlock_irqrestore(ch->lock, flags);
 
-	return start_request(drive, rq);
+	return start_request(drive, drive->rq);
 }
 
 /*
- * This is used by a drive to give excess bandwidth back to the hwgroup by
- * sleeping for timeout jiffies.
+ * This is used by a drive to give excess bandwidth back by sleeping for
+ * timeout jiffies.
  */
 void ide_stall_queue(struct ata_device *drive, unsigned long timeout)
 {
@@ -1104,84 +1126,63 @@
 static unsigned long longest_sleep(struct ata_channel *channel)
 {
 	unsigned long sleep = 0;
-	int i;
-
-	for (i = 0; i < MAX_HWIFS; ++i) {
-		int unit;
-		struct ata_channel *ch = &ide_hwifs[i];
+	int unit;
 
-		if (!ch->present)
-			continue;
+	for (unit = 0; unit < MAX_DRIVES; ++unit) {
+		struct ata_device *drive = &channel->drives[unit];
 
-		if (ch->hwgroup != channel->hwgroup)
+		if (!drive->present)
 			continue;
 
-		for (unit = 0; unit < MAX_DRIVES; ++unit) {
-			struct ata_device *drive = &ch->drives[unit];
-
-			if (!drive->present)
-				continue;
-
-			/* This device is sleeping and waiting to be serviced
-			 * later than any other device we checked thus far.
-			 */
-			if (drive->sleep && (!sleep || time_after(sleep, drive->sleep)))
-				sleep = drive->sleep;
-		}
+		/* This device is sleeping and waiting to be serviced
+		 * later than any other device we checked thus far.
+		 */
+		if (drive->sleep && (!sleep || time_after(sleep, drive->sleep)))
+			sleep = drive->sleep;
 	}
 
 	return sleep;
 }
 
 /*
- * Select the next device which will be serviced.
+ * Select the next device which will be serviced.  This selects only between
+ * devices on the same channel, since everything else will be scheduled on the
+ * queue level.
  */
 static struct ata_device *choose_urgent_device(struct ata_channel *channel)
 {
 	struct ata_device *choice = NULL;
 	unsigned long sleep = 0;
-	int i;
+	int unit;
 
-	for (i = 0; i < MAX_HWIFS; ++i) {
-		int unit;
-		struct ata_channel *ch = &ide_hwifs[i];
+	for (unit = 0; unit < MAX_DRIVES; ++unit) {
+		struct ata_device *drive = &channel->drives[unit];
 
-		if (!ch->present)
+		if (!drive->present)
 			continue;
 
-		if (ch->hwgroup != channel->hwgroup)
+		/* There are no request pending for this device.
+		 */
+		if (list_empty(&drive->queue.queue_head))
 			continue;
 
-		for (unit = 0; unit < MAX_DRIVES; ++unit) {
-			struct ata_device *drive = &ch->drives[unit];
-
-			if (!drive->present)
-				continue;
-
-			/* There are no request pending for this device.
-			 */
-			if (list_empty(&drive->queue.queue_head))
-				continue;
-
-			/* This device still wants to remain idle.
-			 */
-			if (drive->sleep && time_after(drive->sleep, jiffies))
-				continue;
+		/* This device still wants to remain idle.
+		 */
+		if (drive->sleep && time_after(drive->sleep, jiffies))
+			continue;
 
-			/* Take this device, if there is no device choosen thus far or
-			 * it's more urgent.
-			 */
-			if (!choice || (drive->sleep && (!choice->sleep || time_after(choice->sleep, drive->sleep)))) {
-				if (!blk_queue_plugged(&drive->queue))
-					choice = drive;
-			}
+		/* Take this device, if there is no device choosen thus far or
+		 * it's more urgent.
+		 */
+		if (!choice || (drive->sleep && (!choice->sleep || time_after(choice->sleep, drive->sleep)))) {
+			if (!blk_queue_plugged(&drive->queue))
+				choice = drive;
 		}
 	}
 
 	if (choice)
 		return choice;
 
-	channel->hwgroup->rq = NULL;
 	sleep = longest_sleep(channel);
 
 	if (sleep) {
@@ -1195,17 +1196,17 @@
 		if (time_after(jiffies, sleep - WAIT_MIN_SLEEP))
 			sleep = jiffies + WAIT_MIN_SLEEP;
 #if 1
-		if (timer_pending(&channel->hwgroup->timer))
+		if (timer_pending(&channel->timer))
 			printk(KERN_ERR "ide_set_handler: timer already active\n");
 #endif
-		set_bit(IDE_SLEEP, &channel->hwgroup->flags);
-		mod_timer(&channel->hwgroup->timer, sleep);
+		set_bit(IDE_SLEEP, &channel->active);
+		mod_timer(&channel->timer, sleep);
 		/* we purposely leave hwgroup busy while sleeping */
 	} else {
 		/* Ugly, but how can we sleep for the lock otherwise? perhaps
 		 * from tq_disk? */
 		ide_release_lock(&irq_lock);/* for atari only */
-		clear_bit(IDE_BUSY, &channel->hwgroup->flags);
+		clear_bit(IDE_BUSY, &channel->active);
 	}
 
 	return NULL;
@@ -1213,19 +1214,19 @@
 
 
 /*
- * Feed commands to a drive until it barfs.  Called with ide_lock/DRIVE_LOCK
- * held and busy channel.
+ * Feed commands to a drive until it barfs.  Called with queue lock held and
+ * busy channel.
  */
-static void queue_commands(struct ata_device *drive, int masked_irq)
+static void queue_commands(struct ata_device *drive)
 {
-	ide_hwgroup_t *hwgroup = drive->channel->hwgroup;
+	struct ata_channel *ch = drive->channel;
 	ide_startstop_t startstop = -1;
 
 	for (;;) {
 		struct request *rq = NULL;
 
-		if (!test_bit(IDE_BUSY, &hwgroup->flags))
-			printk(KERN_ERR"%s: error: not busy while queueing!\n", drive->name);
+		if (!test_bit(IDE_BUSY, &ch->active))
+			printk(KERN_ERR "%s: error: not busy while queueing!\n", drive->name);
 
 		/* Abort early if we can't queue another command. for non
 		 * tcq, ata_can_queue is always 1 since we never get here
@@ -1233,14 +1234,14 @@
 		 */
 		if (!ata_can_queue(drive)) {
 			if (!ata_pending_commands(drive))
-				clear_bit(IDE_BUSY, &hwgroup->flags);
+				clear_bit(IDE_BUSY, &ch->active);
 			break;
 		}
 
 		drive->sleep = 0;
 
-		if (test_bit(IDE_DMA, &hwgroup->flags)) {
-			printk("ide_do_request: DMA in progress...\n");
+		if (test_bit(IDE_DMA, &ch->active)) {
+			printk(KERN_ERR "%s: error: DMA in progress...\n", drive->name);
 			break;
 		}
 
@@ -1257,8 +1258,8 @@
 
 		if (!(rq = elv_next_request(&drive->queue))) {
 			if (!ata_pending_commands(drive))
-				clear_bit(IDE_BUSY, &hwgroup->flags);
-			hwgroup->rq = NULL;
+				clear_bit(IDE_BUSY, &ch->active);
+			drive->rq = NULL;
 			break;
 		}
 
@@ -1269,27 +1270,14 @@
 		if (!(rq->flags & REQ_CMD) && ata_pending_commands(drive))
 			break;
 
-		hwgroup->rq = rq;
-
-		/* Some systems have trouble with IDE IRQs arriving while the
-		 * driver is still setting things up.  So, here we disable the
-		 * IRQ used by this interface while the request is being
-		 * started.  This may look bad at first, but pretty much the
-		 * same thing happens anyway when any interrupt comes in, IDE
-		 * or otherwise -- the kernel masks the IRQ while it is being
-		 * handled.
-		 */
+		drive->rq = rq;
 
-		if (masked_irq && drive->channel->irq != masked_irq)
-			disable_irq_nosync(drive->channel->irq);
+		spin_unlock(drive->channel->lock);
 
-		spin_unlock(&ide_lock);
 		ide__sti();	/* allow other IRQs while we start this request */
 		startstop = start_request(drive, rq);
 
-		spin_lock_irq(&ide_lock);
-		if (masked_irq && drive->channel->irq != masked_irq)
-			enable_irq(drive->channel->irq);
+		spin_lock_irq(drive->channel->lock);
 
 		/* command started, we are busy */
 		if (startstop == ide_started)
@@ -1310,42 +1298,14 @@
 
 /*
  * Issue a new request.
- * Caller must have already done spin_lock_irqsave(&ide_lock, ...)
- *
- * A hwgroup is a serialized group of IDE interfaces.  Usually there is
- * exactly one hwif (interface) per hwgroup, but buggy controllers (eg. CMD640)
- * may have both interfaces in a single hwgroup to "serialize" access.
- * Or possibly multiple ISA interfaces can share a common IRQ by being grouped
- * together into one hwgroup for serialized access.
- *
- * Note also that several hwgroups can end up sharing a single IRQ,
- * possibly along with many other devices.  This is especially common in
- * PCI-based systems with off-board IDE controller cards.
- *
- * The IDE driver uses the queue spinlock to protect access to the request
- * queues.
- *
- * The first thread into the driver for a particular hwgroup sets the
- * hwgroup->flags IDE_BUSY flag to indicate that this hwgroup is now active,
- * and then initiates processing of the top request from the request queue.
- *
- * Other threads attempting entry notice the busy setting, and will simply
- * queue their new requests and exit immediately.  Note that hwgroup->flags
- * remains busy even when the driver is merely awaiting the next interrupt.
- * Thus, the meaning is "this hwgroup is busy processing a request".
- *
- * When processing of a request completes, the completing thread or IRQ-handler
- * will start the next request from the queue.  If no more work remains,
- * the driver will clear the hwgroup->flags IDE_BUSY flag and exit.
+ * Caller must have already done spin_lock_irqsave(channel->lock, ...)
  */
-static void ide_do_request(struct ata_channel *channel, int masked_irq)
+static void do_request(struct ata_channel *channel)
 {
-	ide_hwgroup_t *hwgroup = channel->hwgroup;
 	ide_get_lock(&irq_lock, ata_irq_request, hwgroup);/* for atari only: POSSIBLY BROKEN HERE(?) */
+//	__cli();	/* necessary paranoia: ensure IRQs are masked on local CPU */
 
-	__cli();	/* necessary paranoia: ensure IRQs are masked on local CPU */
-
-	while (!test_and_set_bit(IDE_BUSY, &hwgroup->flags)) {
+	while (!test_and_set_bit(IDE_BUSY, &channel->active)) {
 		struct ata_channel *ch;
 		struct ata_device *drive;
 
@@ -1357,7 +1317,7 @@
 
 		ch = drive->channel;
 
-		if (hwgroup->XXX_drive->channel->sharing_irq && ch != hwgroup->XXX_drive->channel && ch->io_ports[IDE_CONTROL_OFFSET]) {
+		if (channel->sharing_irq && ch != channel && ch->io_ports[IDE_CONTROL_OFFSET]) {
 			/* set nIEN for previous channel */
 			/* FIXME: check this! It appears to act on the current channel! */
 
@@ -1369,15 +1329,16 @@
 
 		/* Remember the last drive we where acting on.
 		 */
-		hwgroup->XXX_drive = drive;
+		ch->drive = drive;
 
-		queue_commands(drive, masked_irq);
+		queue_commands(drive);
 	}
+
 }
 
 void do_ide_request(request_queue_t *q)
 {
-	ide_do_request(q->queuedata, 0);
+	do_request(q->queuedata);
 }
 
 /*
@@ -1406,7 +1367,7 @@
 	 * un-busy drive etc (hwgroup->busy is cleared on return) and
 	 * make sure request is sane
 	 */
-	HWGROUP(drive)->rq = NULL;
+	drive->rq = NULL;
 
 	rq->errors = 0;
 	if (rq->bio) {
@@ -1419,11 +1380,11 @@
 /*
  * This is our timeout function for all drive operations.  But note that it can
  * also be invoked as a result of a "sleep" operation triggered by the
- * mod_timer() call in ide_do_request.
+ * mod_timer() call in do_request.
  */
 void ide_timer_expiry(unsigned long data)
 {
-	ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data;
+	struct ata_channel *ch = (struct ata_channel *) data;
 	ata_handler_t *handler;
 	ata_expiry_t *expiry;
 	unsigned long flags;
@@ -1434,10 +1395,11 @@
 	 * worth mentioning.
 	 */
 
-	spin_lock_irqsave(&ide_lock, flags);
-	del_timer(&hwgroup->timer);
+	spin_lock_irqsave(ch->lock, flags);
+	del_timer(&ch->timer);
 
-	if ((handler = hwgroup->handler) == NULL) {
+	handler = ch->handler;
+	if (!handler) {
 
 		/*
 		 * Either a marginal timeout occurred (got the interrupt just
@@ -1446,37 +1408,40 @@
 		 * complain about anything.
 		 */
 
-		if (test_and_clear_bit(IDE_SLEEP, &hwgroup->flags))
-			clear_bit(IDE_BUSY, &hwgroup->flags);
+		if (test_and_clear_bit(IDE_SLEEP, &ch->active))
+			clear_bit(IDE_BUSY, &ch->active);
 	} else {
-		struct ata_device *drive = hwgroup->XXX_drive;
+		struct ata_device *drive = ch->drive;
 		if (!drive) {
-			printk("ide_timer_expiry: hwgroup->drive was NULL\n");
-			hwgroup->handler = NULL;
+			printk(KERN_ERR "ide_timer_expiry: IRQ handler was NULL\n");
+			ch->handler = NULL;
 		} else {
-			struct ata_channel *ch;
 			ide_startstop_t startstop;
 
 			/* paranoia */
-			if (!test_and_set_bit(IDE_BUSY, &hwgroup->flags))
-				printk("%s: ide_timer_expiry: hwgroup was not busy??\n", drive->name);
-			if ((expiry = hwgroup->expiry) != NULL) {
+			if (!test_and_set_bit(IDE_BUSY, &ch->active))
+				printk(KERN_ERR "%s: ide_timer_expiry: IRQ handler was not busy??\n", drive->name);
+			if ((expiry = ch->expiry) != NULL) {
 				/* continue */
-				if ((wait = expiry(drive, HWGROUP(drive)->rq)) != 0) {
+				if ((wait = expiry(drive, drive->rq)) != 0) {
 					/* reengage timer */
-					hwgroup->timer.expires  = jiffies + wait;
-					add_timer(&hwgroup->timer);
-					spin_unlock_irqrestore(&ide_lock, flags);
+					ch->timer.expires  = jiffies + wait;
+					add_timer(&ch->timer);
+
+					spin_unlock_irqrestore(ch->lock, flags);
+
 					return;
 				}
 			}
-			hwgroup->handler = NULL;
+			ch->handler = NULL;
 			/*
 			 * We need to simulate a real interrupt when invoking
 			 * the handler() function, which means we need to globally
 			 * mask the specific IRQ:
 			 */
-			spin_unlock(&ide_lock);
+
+			spin_unlock(ch->lock);
+
 			ch = drive->channel;
 #if DISABLE_IRQ_NOSYNC
 			disable_irq_nosync(ch->irq);
@@ -1485,31 +1450,33 @@
 #endif
 			__cli();	/* local CPU only, as if we were handling an interrupt */
 			if (ch->poll_timeout != 0) {
-				startstop = handler(drive, ch->hwgroup->rq);
+				startstop = handler(drive, drive->rq);
 			} else if (drive_is_ready(drive)) {
 				if (drive->waiting_for_dma)
 					udma_irq_lost(drive);
 				(void) ide_ack_intr(ch);
 				printk("%s: lost interrupt\n", drive->name);
-				startstop = handler(drive, ch->hwgroup->rq);
+				startstop = handler(drive, drive->rq);
 			} else {
 				if (drive->waiting_for_dma) {
 					startstop = ide_stopped;
-					dma_timeout_retry(drive, ch->hwgroup->rq);
+					dma_timeout_retry(drive, drive->rq);
 				} else
-					startstop = ide_error(drive, "irq timeout", GET_STAT());
+					startstop = ide_error(drive, drive->rq, "irq timeout", GET_STAT());
 			}
 			set_recovery_timer(ch);
 			enable_irq(ch->irq);
-			spin_lock_irq(&ide_lock);
+
+			spin_lock_irq(ch->lock);
+
 			if (startstop == ide_stopped)
-				clear_bit(IDE_BUSY, &hwgroup->flags);
+				clear_bit(IDE_BUSY, &ch->active);
 		}
 	}
 
-	ide_do_request(hwgroup->XXX_drive->channel, 0);
+	do_request(ch->drive->channel);
 
-	spin_unlock_irqrestore(&ide_lock, flags);
+	spin_unlock_irqrestore(ch->lock, flags);
 }
 
 /*
@@ -1571,14 +1538,12 @@
 void ata_irq_request(int irq, void *data, struct pt_regs *regs)
 {
 	struct ata_channel *ch = data;
-	ide_hwgroup_t *hwgroup = ch->hwgroup;
-
 	unsigned long flags;
 	struct ata_device *drive;
-	ata_handler_t *handler = hwgroup->handler;
+	ata_handler_t *handler = ch->handler;
 	ide_startstop_t startstop;
 
-	spin_lock_irqsave(&ide_lock, flags);
+	spin_lock_irqsave(ch->lock, flags);
 
 	if (!ide_ack_intr(ch))
 		goto out_lock;
@@ -1617,7 +1582,7 @@
 		}
 		goto out_lock;
 	}
-	drive = hwgroup->XXX_drive;
+	drive = ch->drive;
 	if (!drive_is_ready(drive)) {
 		/*
 		 * This happens regularly when we share a PCI IRQ with another device.
@@ -1628,18 +1593,19 @@
 		goto out_lock;
 	}
 	/* paranoia */
-	if (!test_and_set_bit(IDE_BUSY, &hwgroup->flags))
+	if (!test_and_set_bit(IDE_BUSY, &ch->active))
 		printk(KERN_ERR "%s: %s: hwgroup was not busy!?\n", drive->name, __FUNCTION__);
-	hwgroup->handler = NULL;
-	del_timer(&hwgroup->timer);
-	spin_unlock(&ide_lock);
+	ch->handler = NULL;
+	del_timer(&ch->timer);
+
+	spin_unlock(ch->lock);
 
 	if (ch->unmask)
 		ide__sti();	/* local CPU only */
 
 	/* service this interrupt, may set handler for next interrupt */
-	startstop = handler(drive, hwgroup->rq);
-	spin_lock_irq(&ide_lock);
+	startstop = handler(drive, drive->rq);
+	spin_lock_irq(ch->lock);
 
 	/*
 	 * Note that handler() may have set things up for another
@@ -1650,17 +1616,17 @@
 	 */
 	set_recovery_timer(drive->channel);
 	if (startstop == ide_stopped) {
-		if (hwgroup->handler == NULL) {	/* paranoia */
-			clear_bit(IDE_BUSY, &hwgroup->flags);
-			ide_do_request(ch, ch->irq);
+		if (!ch->handler) {	/* paranoia */
+			clear_bit(IDE_BUSY, &ch->active);
+			do_request(ch);
 		} else {
 			printk("%s: %s: huh? expected NULL handler on exit\n", drive->name, __FUNCTION__);
 		}
 	} else if (startstop == ide_released)
-		queue_commands(drive, ch->irq);
+		queue_commands(drive);
 
 out_lock:
-	spin_unlock_irqrestore(&ide_lock, flags);
+	spin_unlock_irqrestore(ch->lock, flags);
 }
 
 /*
@@ -1688,77 +1654,6 @@
 }
 
 /*
- * This function is intended to be used prior to invoking ide_do_drive_cmd().
- */
-void ide_init_drive_cmd(struct request *rq)
-{
-	memset(rq, 0, sizeof(*rq));
-	rq->flags = REQ_DRIVE_CMD;
-}
-
-/*
- * This function issues a special IDE device request onto the request queue.
- *
- * If action is ide_wait, then the rq is queued at the end of the request
- * queue, and the function sleeps until it has been processed.  This is for use
- * when invoked from an ioctl handler.
- *
- * If action is ide_preempt, then the rq is queued at the head of the request
- * queue, displacing the currently-being-processed request and this function
- * returns immediately without waiting for the new rq to be completed.  This is
- * VERY DANGEROUS, and is intended for careful use by the ATAPI tape/cdrom
- * driver code.
- *
- * If action is ide_next, then the rq is queued immediately after the
- * currently-being-processed-request (if any), and the function returns without
- * waiting for the new rq to be completed.  As above, This is VERY DANGEROUS,
- * and is intended for careful use by the ATAPI tape/cdrom driver code.
- *
- * If action is ide_end, then the rq is queued at the end of the request queue,
- * and the function returns immediately without waiting for the new rq to be
- * completed. This is again intended for careful use by the ATAPI tape/cdrom
- * driver code.
- */
-int ide_do_drive_cmd(struct ata_device *drive, struct request *rq, ide_action_t action)
-{
-	unsigned long flags;
-	unsigned int major = drive->channel->major;
-	request_queue_t *q = &drive->queue;
-	struct list_head *queue_head = &q->queue_head;
-	DECLARE_COMPLETION(wait);
-
-#ifdef CONFIG_BLK_DEV_PDC4030
-	if (drive->channel->chipset == ide_pdc4030 && rq->buffer != NULL)
-		return -ENOSYS;  /* special drive cmds not supported */
-#endif
-	rq->errors = 0;
-	rq->rq_status = RQ_ACTIVE;
-	rq->rq_dev = mk_kdev(major,(drive->select.b.unit)<<PARTN_BITS);
-	if (action == ide_wait)
-		rq->waiting = &wait;
-	spin_lock_irqsave(&ide_lock, flags);
-	if (blk_queue_empty(&drive->queue) || action == ide_preempt) {
-		if (action == ide_preempt)
-			HWGROUP(drive)->rq = NULL;
-	} else {
-		if (action == ide_wait || action == ide_end)
-			queue_head = queue_head->prev;
-		else
-			queue_head = queue_head->next;
-	}
-	q->elevator.elevator_add_req_fn(q, rq, queue_head);
-	ide_do_request(drive->channel, 0);
-	spin_unlock_irqrestore(&ide_lock, flags);
-	if (action == ide_wait) {
-		wait_for_completion(&wait);	/* wait for it to be serviced */
-		return rq->errors ? -EIO : 0;	/* return -EIO if errors */
-	}
-
-	return 0;
-
-}
-
-/*
  * This routine is called to flush all partitions and partition tables
  * for a changed disk, and then re-read the new partition table.
  * If we are revalidating a disk because of a media change, then we
@@ -1775,15 +1670,19 @@
 	if ((drive = get_info_ptr(i_rdev)) == NULL)
 		return -ENODEV;
 
+	/* FIXME: The locking here doesn't make the slightest sense! */
 	spin_lock_irqsave(&ide_lock, flags);
 
 	if (drive->busy || (drive->usage > 1)) {
+
 		spin_unlock_irqrestore(&ide_lock, flags);
+
 		return -EBUSY;
 	}
 
 	drive->busy = 1;
 	MOD_INC_USE_COUNT;
+
 	spin_unlock_irqrestore(&ide_lock, flags);
 
 	res = wipe_partitions(i_rdev);
@@ -1800,6 +1699,7 @@
 	drive->busy = 0;
 	wake_up(&drive->wqueue);
 	MOD_DEC_USE_COUNT;
+
 	return res;
 }
 
@@ -1923,7 +1823,7 @@
 {
 	struct gendisk *gd;
 	struct ata_device *d;
-	ide_hwgroup_t *hwgroup;
+	spinlock_t *lock;
 	int unit;
 	int i;
 	unsigned long flags;
@@ -1961,6 +1861,7 @@
 	 * All clear?  Then blow away the buffer cache
 	 */
 	spin_unlock_irqrestore(&ide_lock, flags);
+
 	for (unit = 0; unit < MAX_DRIVES; ++unit) {
 		struct ata_device * drive = &ch->drives[unit];
 
@@ -1975,6 +1876,7 @@
 			}
 		}
 	}
+
 	spin_lock_irqsave(&ide_lock, flags);
 
 	/*
@@ -1998,11 +1900,11 @@
 #endif
 
 	/*
-	 * Remove us from the hwgroup.
+	 * Remove us from the lock group.
 	 */
 
-	hwgroup = ch->hwgroup;
-	d = hwgroup->XXX_drive;
+	lock = ch->lock;
+	d = ch->drive;
 	for (i = 0; i < MAX_DRIVES; ++i) {
 		struct ata_device *drive = &ch->drives[i];
 
@@ -2013,8 +1915,9 @@
 		if (!drive->present)
 			continue;
 
-		if (hwgroup->XXX_drive == drive)
-			hwgroup->XXX_drive = NULL;
+		/* FIXME: possibly unneccessary */
+		if (ch->drive == drive)
+			ch->drive = NULL;
 
 		if (drive->id != NULL) {
 			kfree(drive->id);
@@ -2024,13 +1927,13 @@
 		blk_cleanup_queue(&drive->queue);
 	}
 	if (d->present)
-		hwgroup->XXX_drive = d;
+		ch->drive = d;
 
 
 	/*
 	 * Free the irq if we were the only channel using it.
 	 *
-	 * Free the hwgroup if we were the only member.
+	 * Free the lock group if we were the only member.
 	 */
 	n_irq = n_ch = 0;
 	for (i = 0; i < MAX_HWIFS; ++i) {
@@ -2041,14 +1944,14 @@
 
 		if (tmp->irq == ch->irq)
 			++n_irq;
-		if (tmp->hwgroup == ch->hwgroup)
+		if (tmp->lock == ch->lock)
 			++n_ch;
 	}
 	if (n_irq == 1)
-		free_irq(ch->irq, ch->hwgroup);
+		free_irq(ch->irq, ch);
 	if (n_ch == 1) {
-		kfree(ch->hwgroup);
-		ch->hwgroup = NULL;
+		kfree(ch->lock);
+		ch->lock = NULL;
 	}
 
 #if defined(CONFIG_BLK_DEV_IDEDMA) && !defined(CONFIG_DMA_NONPCI)
@@ -2082,7 +1985,7 @@
 
 	old = *ch;
 	init_hwif_data(ch, ch->index);
-	ch->hwgroup = old.hwgroup;
+	ch->lock = old.lock;
 	ch->tuneproc = old.tuneproc;
 	ch->speedproc = old.speedproc;
 	ch->selectproc = old.selectproc;
@@ -2220,145 +2123,28 @@
 	return ide_register_hw(&hw, NULL);
 }
 
-void ide_add_setting(struct ata_device *drive, const char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set)
-{
-	ide_settings_t **p = &drive->settings;
-	ide_settings_t *setting = NULL;
-
-	while ((*p) && strcmp((*p)->name, name) < 0)
-		p = &((*p)->next);
-	if ((setting = kmalloc(sizeof(*setting), GFP_KERNEL)) == NULL)
-		goto abort;
-	memset(setting, 0, sizeof(*setting));
-	if ((setting->name = kmalloc(strlen(name) + 1, GFP_KERNEL)) == NULL)
-		goto abort;
-	strcpy(setting->name, name);		setting->rw = rw;
-	setting->read_ioctl = read_ioctl;	setting->write_ioctl = write_ioctl;
-	setting->data_type = data_type;		setting->min = min;
-	setting->max = max;			setting->mul_factor = mul_factor;
-	setting->div_factor = div_factor;	setting->data = data;
-	setting->set = set;			setting->next = *p;
-	if (drive->driver)
-		setting->auto_remove = 1;
-	*p = setting;
-	return;
-abort:
-	if (setting)
-		kfree(setting);
-}
-
-void ide_remove_setting(struct ata_device *drive, char *name)
-{
-	ide_settings_t **p = &drive->settings, *setting;
-
-	while ((*p) && strcmp((*p)->name, name))
-		p = &((*p)->next);
-	if ((setting = (*p)) == NULL)
-		return;
-	(*p) = setting->next;
-	kfree(setting->name);
-	kfree(setting);
-}
-
-static void auto_remove_settings(struct ata_device *drive)
-{
-	ide_settings_t *setting;
-repeat:
-	setting = drive->settings;
-	while (setting) {
-		if (setting->auto_remove) {
-			ide_remove_setting(drive, setting->name);
-			goto repeat;
-		}
-		setting = setting->next;
-	}
-}
-
-int ide_read_setting(struct ata_device *drive, ide_settings_t *setting)
-{
-	int val = -EINVAL;
-	unsigned long flags;
-
-	if ((setting->rw & SETTING_READ)) {
-		spin_lock_irqsave(&ide_lock, flags);
-		switch(setting->data_type) {
-			case TYPE_BYTE:
-				val = *((u8 *) setting->data);
-				break;
-			case TYPE_SHORT:
-				val = *((u16 *) setting->data);
-				break;
-			case TYPE_INT:
-			case TYPE_INTA:
-				val = *((u32 *) setting->data);
-				break;
-		}
-		spin_unlock_irqrestore(&ide_lock, flags);
-	}
-	return val;
-}
-
 int ide_spin_wait_hwgroup(struct ata_device *drive)
 {
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
-
 	/* FIXME: Wait on a proper timer. Instead of playing games on the
 	 * spin_lock().
 	 */
 
 	unsigned long timeout = jiffies + (10 * HZ);
 
-	spin_lock_irq(&ide_lock);
+	spin_lock_irq(drive->channel->lock);
+
+	while (test_bit(IDE_BUSY, &drive->channel->active)) {
+
+		spin_unlock_irq(drive->channel->lock);
 
-	while (test_bit(IDE_BUSY, &hwgroup->flags)) {
-		spin_unlock_irq(&ide_lock);
 		if (time_after(jiffies, timeout)) {
 			printk("%s: channel busy\n", drive->name);
 			return -EBUSY;
 		}
-		spin_lock_irq(&ide_lock);
-	}
 
-	return 0;
-}
-
-/*
- * FIXME:  This should be changed to enqueue a special request
- * to the driver to change settings, and then wait on a semaphore for completion.
- * The current scheme of polling is kludgey, though safe enough.
- */
-int ide_write_setting(struct ata_device *drive, ide_settings_t *setting, int val)
-{
-	int i;
-	u32 *p;
-
-	if (!capable(CAP_SYS_ADMIN))
-		return -EACCES;
-	if (!(setting->rw & SETTING_WRITE))
-		return -EPERM;
-	if (val < setting->min || val > setting->max)
-		return -EINVAL;
-	if (setting->set)
-		return setting->set(drive, val);
-	if (ide_spin_wait_hwgroup(drive))
-		return -EBUSY;
-	switch (setting->data_type) {
-		case TYPE_BYTE:
-			*((u8 *) setting->data) = val;
-			break;
-		case TYPE_SHORT:
-			*((u16 *) setting->data) = val;
-			break;
-		case TYPE_INT:
-			*((u32 *) setting->data) = val;
-			break;
-		case TYPE_INTA:
-			p = (u32 *) setting->data;
-			for (i = 0; i < 1 << PARTN_BITS; i++, p++)
-				*p = val;
-			break;
+		spin_lock_irq(drive->channel->lock);
 	}
-	spin_unlock_irq(&ide_lock);
+
 	return 0;
 }
 
@@ -2381,6 +2167,7 @@
 		return -EPERM;
 
 	udma_enable(drive, arg, 1);
+
 	return 0;
 }
 
@@ -2389,32 +2176,12 @@
 	if (!drive->channel->tuneproc)
 		return -ENOSYS;
 
-	/* FIXME: This is very much the same kind of problem as we have with
-	 * set_mutlmode() see for a edscription there.
-	 */
-	if (HWGROUP(drive)->handler)
-		return -EBUSY;
-
 	if (drive->channel->tuneproc != NULL)
 		drive->channel->tuneproc(drive, (u8) arg);
 
 	return 0;
 }
 
-void ide_add_generic_settings(struct ata_device *drive)
-{
-/*			drive	setting name		read/write access				read ioctl		write ioctl		data type	min	max				mul_factor	div_factor	data pointer			set function */
-	ide_add_setting(drive,	"io_32bit",		drive->channel->no_io_32bit ? SETTING_READ : SETTING_RW,	HDIO_GET_32BIT,		HDIO_SET_32BIT,		TYPE_BYTE,	0,	1 + (SUPPORT_VLB_SYNC << 1),	1,		1,		&drive->channel->io_32bit,		set_io_32bit);
-	ide_add_setting(drive,	"pio_mode",		SETTING_WRITE,					-1,			HDIO_SET_PIO_MODE,	TYPE_BYTE,	0,	255,				1,		1,		NULL,				set_pio_mode);
-	ide_add_setting(drive,	"slow",			SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	1,				1,		1,		&drive->channel->slow,			NULL);
-	ide_add_setting(drive,	"unmaskirq",		drive->channel->no_unmask ? SETTING_READ : SETTING_RW,	HDIO_GET_UNMASKINTR,	HDIO_SET_UNMASKINTR,	TYPE_BYTE,	0,	1,				1,		1,		&drive->channel->unmask,			NULL);
-	ide_add_setting(drive,	"using_dma",		SETTING_RW,					HDIO_GET_DMA,		HDIO_SET_DMA,		TYPE_BYTE,	0,	1,				1,		1,		&drive->using_dma,		set_using_dma);
-	ide_add_setting(drive,	"ide_scsi",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	1,				1,		1,		&drive->scsi,			NULL);
-	ide_add_setting(drive,	"init_speed",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	69,				1,		1,		&drive->init_speed,		NULL);
-	ide_add_setting(drive,	"current_speed",	SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	69,				1,		1,		&drive->current_speed,		NULL);
-	ide_add_setting(drive,	"number",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	3,				1,		1,		&drive->dn,			NULL);
-}
-
 /*
  * Delay for *at least* 50ms.  As we don't know how much time is left
  * until the next tick occurs, we wait an extra tick to be safe.
@@ -2433,105 +2200,211 @@
 #endif /* CONFIG_BLK_DEV_IDECS */
 }
 
+/*
+ * Handle ioctls.
+ *
+ * NOTE: Due to ridiculous coding habbits in the hdparm utility we have to
+ * always return unsigned long in case we are returning simple values.
+ */
 static int ide_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
-	int err = 0, major, minor;
+	unsigned int major, minor;
 	struct ata_device *drive;
 	struct request rq;
 	kdev_t dev;
-	ide_settings_t *setting;
 
 	dev = inode->i_rdev;
-	major = major(dev); minor = minor(dev);
+	major = major(dev);
+	minor = minor(dev);
+
 	if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
 		return -ENODEV;
 
-	/* Find setting by ioctl */
 
-	setting = drive->settings;
+	/* Contrary to popular beleve we disallow even the reading of the ioctl
+	 * values for users which don't have permission too. We do this becouse
+	 * such information could be used by an attacker to deply a simple-user
+	 * attack, which triggers bugs present only on a particular
+	 * configuration.
+	 */
 
-	while (setting) {
-		if (setting->read_ioctl == cmd || setting->write_ioctl == cmd)
-			break;
-		setting = setting->next;
-	}
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
 
-	if (setting != NULL) {
-		if (cmd == setting->read_ioctl) {
-			err = ide_read_setting(drive, setting);
-			return err >= 0 ? put_user(err, (long *) arg) : err;
-		} else {
-			if ((minor(inode->i_rdev) & PARTN_MASK))
+	ide_init_drive_cmd(&rq);
+	switch (cmd) {
+		case HDIO_GET_32BIT: {
+			unsigned long val = drive->channel->io_32bit;
+
+			if (put_user(val, (unsigned long *) arg))
+				return -EFAULT;
+			return 0;
+		}
+
+		case HDIO_SET_32BIT: {
+			int val;
+
+			if (arg < 0 || arg > 1)
 				return -EINVAL;
-			return ide_write_setting(drive, setting, arg);
+
+			if (ide_spin_wait_hwgroup(drive))
+				return -EBUSY;
+
+			val = set_io_32bit(drive, arg);
+			spin_unlock_irq(drive->channel->lock);
+
+			return val;
 		}
-	}
 
-	ide_init_drive_cmd(&rq);
-	switch (cmd) {
-		case HDIO_GETGEO:
-		{
+		case HDIO_SET_PIO_MODE: {
+			int val;
+
+			if (arg < 0 || arg > 255)
+				return -EINVAL;
+
+			if (ide_spin_wait_hwgroup(drive))
+				return -EBUSY;
+
+			val = set_pio_mode(drive, arg);
+			spin_unlock_irq(drive->channel->lock);
+
+			return val;
+		}
+
+		case HDIO_GET_UNMASKINTR: {
+			unsigned long val = drive->channel->unmask;
+
+			if (put_user(val, (unsigned long *) arg))
+				return -EFAULT;
+
+			return 0;
+		}
+
+
+		case HDIO_SET_UNMASKINTR: {
+			if (arg < 0 || arg > 1)
+				return -EINVAL;
+
+			if (drive->channel->no_unmask)
+				return -EIO;
+
+			if (ide_spin_wait_hwgroup(drive))
+				return -EBUSY;
+
+			drive->channel->unmask = arg;
+			spin_unlock_irq(drive->channel->lock);
+
+			return 0;
+		}
+
+		case HDIO_GET_DMA: {
+			unsigned long val = drive->using_dma;
+
+			if (put_user(val, (unsigned long *) arg))
+				return -EFAULT;
+
+			return 0;
+		}
+
+		case HDIO_SET_DMA: {
+			int val;
+
+			if (arg < 0 || arg > 1)
+				return -EINVAL;
+
+			if (ide_spin_wait_hwgroup(drive))
+				return -EBUSY;
+
+			val = set_using_dma(drive, arg);
+			spin_unlock_irq(drive->channel->lock);
+
+			return val;
+		}
+
+		case HDIO_GETGEO: {
 			struct hd_geometry *loc = (struct hd_geometry *) arg;
 			unsigned short bios_cyl = drive->bios_cyl; /* truncate */
 
 			if (!loc || (drive->type != ATA_DISK && drive->type != ATA_FLOPPY))
 				return -EINVAL;
-			if (put_user(drive->bios_head, (byte *) &loc->heads)) return -EFAULT;
-			if (put_user(drive->bios_sect, (byte *) &loc->sectors)) return -EFAULT;
-			if (put_user(bios_cyl, (unsigned short *) &loc->cylinders)) return -EFAULT;
+
+			if (put_user(drive->bios_head, (byte *) &loc->heads))
+				return -EFAULT;
+
+			if (put_user(drive->bios_sect, (byte *) &loc->sectors))
+				return -EFAULT;
+
+			if (put_user(bios_cyl, (unsigned short *) &loc->cylinders))
+				return -EFAULT;
+
 			if (put_user((unsigned)drive->part[minor(inode->i_rdev)&PARTN_MASK].start_sect,
-				(unsigned long *) &loc->start)) return -EFAULT;
+				(unsigned long *) &loc->start))
+				return -EFAULT;
+
 			return 0;
 		}
 
-		case HDIO_GETGEO_BIG_RAW:
-		{
+		case HDIO_GETGEO_BIG_RAW: {
 			struct hd_big_geometry *loc = (struct hd_big_geometry *) arg;
+
 			if (!loc || (drive->type != ATA_DISK && drive->type != ATA_FLOPPY))
 				return -EINVAL;
-			if (put_user(drive->head, (u8 *) &loc->heads)) return -EFAULT;
-			if (put_user(drive->sect, (u8 *) &loc->sectors)) return -EFAULT;
-			if (put_user(drive->cyl, (unsigned int *) &loc->cylinders)) return -EFAULT;
+
+			if (put_user(drive->head, (u8 *) &loc->heads))
+				return -EFAULT;
+
+			if (put_user(drive->sect, (u8 *) &loc->sectors))
+				return -EFAULT;
+
+			if (put_user(drive->cyl, (unsigned int *) &loc->cylinders))
+				return -EFAULT;
+
 			if (put_user((unsigned)drive->part[minor(inode->i_rdev)&PARTN_MASK].start_sect,
-				(unsigned long *) &loc->start)) return -EFAULT;
+				(unsigned long *) &loc->start))
+				return -EFAULT;
+
 			return 0;
 		}
 
 		case BLKRRPART: /* Re-read partition tables */
-			if (!capable(CAP_SYS_ADMIN))
-				return -EACCES;
 			return ide_revalidate_disk(inode->i_rdev);
 
 		case HDIO_GET_IDENTITY:
 			if (minor(inode->i_rdev) & PARTN_MASK)
 				return -EINVAL;
+
 			if (drive->id == NULL)
 				return -ENOMSG;
+
 			if (copy_to_user((char *)arg, (char *)drive->id, sizeof(*drive->id)))
 				return -EFAULT;
+
 			return 0;
 
 		case HDIO_GET_NICE:
-			return put_user(drive->dsc_overlap	<<	IDE_NICE_DSC_OVERLAP	|
-					drive->atapi_overlap	<<	IDE_NICE_ATAPI_OVERLAP,
+			return put_user(drive->dsc_overlap << IDE_NICE_DSC_OVERLAP |
+					drive->atapi_overlap << IDE_NICE_ATAPI_OVERLAP,
 					(long *) arg);
 
 		case HDIO_DRIVE_CMD:
-			if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+			if (!capable(CAP_SYS_RAWIO))
 				return -EACCES;
+
 			return ide_cmd_ioctl(drive, arg);
 
 		case HDIO_SET_NICE:
-			if (!capable(CAP_SYS_ADMIN)) return -EACCES;
 			if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP))))
 				return -EPERM;
+
 			drive->dsc_overlap = (arg >> IDE_NICE_DSC_OVERLAP) & 1;
 			/* Only CD-ROM's and tapes support DSC overlap. */
 			if (drive->dsc_overlap && !(drive->type == ATA_ROM || drive->type == ATA_TAPE)) {
 				drive->dsc_overlap = 0;
 				return -EPERM;
 			}
+
 			return 0;
+
 		case BLKGETSIZE:
 		case BLKGETSIZE64:
 		case BLKROSET:
@@ -2553,27 +2426,29 @@
 			return block_ioctl(inode->i_bdev, cmd, arg);
 
 		case HDIO_GET_BUSSTATE:
-			if (!capable(CAP_SYS_ADMIN))
-				return -EACCES;
 			if (put_user(drive->channel->bus_state, (long *)arg))
 				return -EFAULT;
+
 			return 0;
 
 		case HDIO_SET_BUSSTATE:
-			if (!capable(CAP_SYS_ADMIN))
-				return -EACCES;
 			if (drive->channel->busproc)
 				drive->channel->busproc(drive, (int)arg);
+
 			return 0;
 
+		/* Now check whatever this particular ioctl has a device type
+		 * specific implementation.
+		 */
 		default:
 			if (ata_ops(drive) && ata_ops(drive)->ioctl)
 				return ata_ops(drive)->ioctl(drive, inode, file, cmd, arg);
+
 			return -EINVAL;
 	}
 }
 
-static int ide_check_media_change (kdev_t i_rdev)
+static int ide_check_media_change(kdev_t i_rdev)
 {
 	struct ata_device *drive;
 	int res = 0; /* not changed */
@@ -2590,6 +2465,7 @@
 			res = 1; /* assume it was changed */
 		ata_put(ata_ops(drive));
 	}
+
 	return res;
 }
 
@@ -3201,7 +3077,6 @@
 #if defined(CONFIG_BLK_DEV_ISAPNP) && defined(CONFIG_ISAPNP) && defined(MODULE)
 	pnpide_init(0);
 #endif
-	auto_remove_settings(drive);
 	drive->driver = NULL;
 	drive->present = 0;
 
@@ -3255,7 +3130,6 @@
 EXPORT_SYMBOL(ide_lock);
 EXPORT_SYMBOL(drive_is_flashcard);
 EXPORT_SYMBOL(ide_timer_expiry);
-EXPORT_SYMBOL(ide_add_generic_settings);
 EXPORT_SYMBOL(do_ide_request);
 /*
  * Driver module
@@ -3269,17 +3143,12 @@
 EXPORT_SYMBOL(ide_fixstring);
 EXPORT_SYMBOL(ide_wait_stat);
 EXPORT_SYMBOL(restart_request);
-EXPORT_SYMBOL(ide_init_drive_cmd);
-EXPORT_SYMBOL(ide_do_drive_cmd);
 EXPORT_SYMBOL(ide_end_drive_cmd);
 EXPORT_SYMBOL(__ide_end_request);
 EXPORT_SYMBOL(ide_end_request);
 EXPORT_SYMBOL(ide_revalidate_disk);
-EXPORT_SYMBOL(ide_cmd);
 EXPORT_SYMBOL(ide_delay_50ms);
 EXPORT_SYMBOL(ide_stall_queue);
-EXPORT_SYMBOL(ide_add_setting);
-EXPORT_SYMBOL(ide_remove_setting);
 
 EXPORT_SYMBOL(ide_register_hw);
 EXPORT_SYMBOL(ide_register);
@@ -3427,9 +3296,6 @@
 # ifdef CONFIG_BLK_DEV_AMD74XX
 	init_amd74xx();
 # endif
-# ifdef CONFIG_BLK_DEV_PDC_ADMA
-	init_pdcadma();
-# endif
 # ifdef CONFIG_BLK_DEV_SVWKS
 	init_svwks();
 # endif
@@ -3493,7 +3359,7 @@
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
 # if defined(__mc68000__) || defined(CONFIG_APUS)
 	if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) {
-		ide_get_lock(&irq_lock, NULL, NULL);/* for atari only */
+		// ide_get_lock(&irq_lock, NULL, NULL);/* for atari only */
 		disable_irq(ide_hwifs[0].irq);	/* disable_irq_nosync ?? */
 //		disable_irq_nosync(ide_hwifs[0].irq);
 	}
diff -Nru a/drivers/ide/pcidma.c b/drivers/ide/pcidma.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/ide/pcidma.c	Sat May 18 00:46:08 2002
@@ -0,0 +1,555 @@
+/**** vi:set ts=8 sts=8 sw=8:************************************************
+ *
+ *  Copyright (C) 2002	     Marcin Dalecki <martin@dalecki.de>
+ *
+ *  Based on previous work by:
+ *
+ *  Copyright (c) 1999-2000  Andre Hedrick <andre@linux-ide.org>
+ *  Copyright (c) 1995-1998  Mark Lord
+ *
+ *  May be copied or modified under the terms of the GNU General Public License
+ */
+
+/*
+ * Those are the generic BM DMA support functions for PCI bus based systems.
+ */
+
+#include <linux/config.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#define DEFAULT_BMIBA	0xe800	/* in case BIOS did not init it */
+#define DEFAULT_BMCRBA	0xcc00	/* VIA's default value */
+#define DEFAULT_BMALIBA	0xd400	/* ALI's default value */
+
+/*
+ * This is the handler for disk read/write DMA interrupts.
+ */
+ide_startstop_t ide_dma_intr(struct ata_device *drive, struct request *rq)
+{
+	u8 stat, dma_stat;
+
+	dma_stat = udma_stop(drive);
+	if (OK_STAT(stat = GET_STAT(),DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
+		if (!dma_stat) {
+			__ide_end_request(drive, rq, 1, rq->nr_sectors);
+			return ide_stopped;
+		}
+		printk(KERN_ERR "%s: dma_intr: bad DMA status (dma_stat=%x)\n",
+		       drive->name, dma_stat);
+	}
+	return ide_error(drive, rq, "dma_intr", stat);
+}
+
+/*
+ * FIXME: taskfiles should be a map of pages, not a long virt address... /jens
+ * FIXME: I agree with Jens --mdcki!
+ */
+static int build_sglist(struct ata_channel *ch, struct request *rq)
+{
+	struct scatterlist *sg = ch->sg_table;
+	int nents = 0;
+
+	if (rq->flags & REQ_DRIVE_ACB) {
+		struct ata_taskfile *args = rq->special;
+#if 1
+		unsigned char *virt_addr = rq->buffer;
+		int sector_count = rq->nr_sectors;
+#else
+		nents = blk_rq_map_sg(rq->q, rq, ch->sg_table);
+
+		if (nents > rq->nr_segments)
+			printk("ide-dma: received %d segments, build %d\n", rq->nr_segments, nents);
+#endif
+
+		if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE)
+			ch->sg_dma_direction = PCI_DMA_TODEVICE;
+		else
+			ch->sg_dma_direction = PCI_DMA_FROMDEVICE;
+
+		/*
+		 * FIXME: This depends upon a hard coded page size!
+		 */
+		if (sector_count > 128) {
+			memset(&sg[nents], 0, sizeof(*sg));
+
+			sg[nents].page = virt_to_page(virt_addr);
+			sg[nents].offset = (unsigned long) virt_addr & ~PAGE_MASK;
+			sg[nents].length = 128  * SECTOR_SIZE;
+			++nents;
+			virt_addr = virt_addr + (128 * SECTOR_SIZE);
+			sector_count -= 128;
+		}
+		memset(&sg[nents], 0, sizeof(*sg));
+		sg[nents].page = virt_to_page(virt_addr);
+		sg[nents].offset = (unsigned long) virt_addr & ~PAGE_MASK;
+		sg[nents].length =  sector_count  * SECTOR_SIZE;
+		++nents;
+	} else {
+		nents = blk_rq_map_sg(rq->q, rq, ch->sg_table);
+
+		if (rq->q && nents > rq->nr_phys_segments)
+			printk("ide-dma: received %d phys segments, build %d\n", rq->nr_phys_segments, nents);
+
+		if (rq_data_dir(rq) == READ)
+			ch->sg_dma_direction = PCI_DMA_FROMDEVICE;
+		else
+			ch->sg_dma_direction = PCI_DMA_TODEVICE;
+
+	}
+
+	return pci_map_sg(ch->pci_dev, sg, nents, ch->sg_dma_direction);
+}
+
+/*
+ * 1 dma-ing, 2 error, 4 intr
+ */
+static int dma_timer_expiry(struct ata_device *drive, struct request *rq)
+{
+	/* FIXME: What's that? */
+	u8 dma_stat = inb(drive->channel->dma_base+2);
+
+#ifdef DEBUG
+	printk("%s: dma_timer_expiry: dma status == 0x%02x\n", drive->name, dma_stat);
+#endif
+
+#if 0
+	drive->expiry = NULL;	/* one free ride for now */
+#endif
+
+	if (dma_stat & 2) {	/* ERROR */
+		u8 stat = GET_STAT();
+		return ide_error(drive, rq, "dma_timer_expiry", stat);
+	}
+	if (dma_stat & 1)	/* DMAing */
+		return WAIT_CMD;
+	return 0;
+}
+
+int ata_start_dma(struct ata_device *drive, struct request *rq)
+{
+	struct ata_channel *ch = drive->channel;
+	unsigned long dma_base = ch->dma_base;
+	unsigned int reading = 0;
+
+	if (rq_data_dir(rq) == READ)
+		reading = 1 << 3;
+
+	/* try PIO instead of DMA */
+	if (!udma_new_table(ch, rq))
+		return 1;
+
+	outl(ch->dmatable_dma, dma_base + 4); /* PRD table */
+	outb(reading, dma_base);		/* specify r/w */
+	outb(inb(dma_base+2)|6, dma_base+2);	/* clear INTR & ERROR flags */
+	drive->waiting_for_dma = 1;
+
+	return 0;
+}
+
+/*
+ * Configure a device for DMA operation.
+ */
+int XXX_ide_dmaproc(struct ata_device *drive)
+{
+	int config_allows_dma = 1;
+	struct hd_driveid *id = drive->id;
+	struct ata_channel *ch = drive->channel;
+
+#ifdef CONFIG_IDEDMA_ONLYDISK
+	if (drive->type != ATA_DISK)
+		config_allows_dma = 0;
+#endif
+
+	if (id && (id->capability & 1) && ch->autodma && config_allows_dma) {
+		/* Consult the list of known "bad" drives */
+		if (udma_black_list(drive)) {
+			udma_enable(drive, 0, 1);
+
+			return 0;
+		}
+
+		/* Enable DMA on any drive that has UltraDMA (mode 6/7/?) enabled */
+		if ((id->field_valid & 4) && (eighty_ninty_three(drive)))
+			if ((id->dma_ultra & (id->dma_ultra >> 14) & 2)) {
+				udma_enable(drive, 1, 1);
+
+				return 0;
+			}
+		/* Enable DMA on any drive that has UltraDMA (mode 3/4/5) enabled */
+		if ((id->field_valid & 4) && (eighty_ninty_three(drive)))
+			if ((id->dma_ultra & (id->dma_ultra >> 11) & 7)) {
+				udma_enable(drive, 1, 1);
+
+				return 0;
+			}
+		/* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */
+		if (id->field_valid & 4)	/* UltraDMA */
+			if ((id->dma_ultra & (id->dma_ultra >> 8) & 7)) {
+				udma_enable(drive, 1, 1);
+
+				return 0;
+			}
+		/* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */
+		if (id->field_valid & 2)	/* regular DMA */
+			if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404) {
+				udma_enable(drive, 1, 1);
+
+				return 0;
+			}
+		/* Consult the list of known "good" drives */
+		if (udma_white_list(drive)) {
+			udma_enable(drive, 1, 1);
+
+			return 0;
+		}
+	}
+	udma_enable(drive, 0, 0);
+
+	return 0;
+}
+
+/*
+ * Needed for allowing full modular support of ide-driver
+ */
+void ide_release_dma(struct ata_channel *ch)
+{
+	if (!ch->dma_base)
+		return;
+
+	if (ch->dmatable_cpu) {
+		pci_free_consistent(ch->pci_dev,
+				    PRD_ENTRIES * PRD_BYTES,
+				    ch->dmatable_cpu,
+				    ch->dmatable_dma);
+		ch->dmatable_cpu = NULL;
+	}
+	if (ch->sg_table) {
+		kfree(ch->sg_table);
+		ch->sg_table = NULL;
+	}
+	if ((ch->dma_extra) && (ch->unit == 0))
+		release_region((ch->dma_base + 16), ch->dma_extra);
+	release_region(ch->dma_base, 8);
+	ch->dma_base = 0;
+}
+
+/****************************************************************************
+ * PCI specific UDMA channel method implementations.
+ */
+
+/*
+ * This is the generic part of the DMA setup used by the host chipset drivers
+ * in the corresponding DMA setup method.
+ *
+ * FIXME: there are some places where this gets used driectly for "error
+ * recovery" in the ATAPI drivers. This was just plain wrong before, in esp.
+ * not portable, and just got uncovered now.
+ */
+static void udma_pci_enable(struct ata_device *drive, int on, int verbose)
+{
+	struct ata_channel *ch = drive->channel;
+	int set_high = 1;
+	u8 unit;
+	u64 addr;
+
+	/* Fall back to the default implementation. */
+	unit = (drive->select.b.unit & 0x01);
+	addr = BLK_BOUNCE_HIGH;
+
+	if (!on) {
+		if (verbose)
+			printk("%s: DMA disabled\n", drive->name);
+		set_high = 0;
+		outb(inb(ch->dma_base + 2) & ~(1 << (5 + unit)), ch->dma_base + 2);
+#ifdef CONFIG_BLK_DEV_IDE_TCQ
+		udma_tcq_enable(drive, 0);
+#endif
+	}
+
+	/* toggle bounce buffers */
+
+	if (on && drive->type == ATA_DISK && drive->channel->highmem) {
+		if (!PCI_DMA_BUS_IS_PHYS)
+			addr = BLK_BOUNCE_ANY;
+		else
+			addr = drive->channel->pci_dev->dma_mask;
+	}
+
+	blk_queue_bounce_limit(&drive->queue, addr);
+
+	drive->using_dma = on;
+
+	if (on) {
+		outb(inb(ch->dma_base + 2) | (1 << (5 + unit)), ch->dma_base + 2);
+#ifdef CONFIG_BLK_DEV_IDE_TCQ_DEFAULT
+		udma_tcq_enable(drive, 1);
+#endif
+	}
+}
+
+/*
+ * This prepares a dma request.  Returns 0 if all went okay, returns 1
+ * otherwise.  May also be invoked from trm290.c
+ */
+int udma_new_table(struct ata_channel *ch, struct request *rq)
+{
+	unsigned int *table = ch->dmatable_cpu;
+#ifdef CONFIG_BLK_DEV_TRM290
+	unsigned int is_trm290_chipset = (ch->chipset == ide_trm290);
+#else
+	const int is_trm290_chipset = 0;
+#endif
+	unsigned int count = 0;
+	int i;
+	struct scatterlist *sg;
+
+	ch->sg_nents = i = build_sglist(ch, rq);
+	if (!i)
+		return 0;
+
+	sg = ch->sg_table;
+	while (i) {
+		u32 cur_addr;
+		u32 cur_len;
+
+		cur_addr = sg_dma_address(sg);
+		cur_len = sg_dma_len(sg);
+
+		/*
+		 * Fill in the dma table, without crossing any 64kB boundaries.
+		 * Most hardware requires 16-bit alignment of all blocks,
+		 * but the trm290 requires 32-bit alignment.
+		 */
+
+		while (cur_len) {
+			u32 xcount, bcount = 0x10000 - (cur_addr & 0xffff);
+
+			if (count++ >= PRD_ENTRIES) {
+				printk("ide-dma: count %d, sg_nents %d, cur_len %d, cur_addr %u\n",
+						count, ch->sg_nents, cur_len, cur_addr);
+				BUG();
+			}
+
+			if (bcount > cur_len)
+				bcount = cur_len;
+			*table++ = cpu_to_le32(cur_addr);
+			xcount = bcount & 0xffff;
+			if (is_trm290_chipset)
+				xcount = ((xcount >> 2) - 1) << 16;
+			if (xcount == 0x0000) {
+		        /*
+			 * Most chipsets correctly interpret a length of
+			 * 0x0000 as 64KB, but at least one (e.g. CS5530)
+			 * misinterprets it as zero (!). So here we break
+			 * the 64KB entry into two 32KB entries instead.
+			 */
+				if (count++ >= PRD_ENTRIES) {
+					pci_unmap_sg(ch->pci_dev, sg,
+						     ch->sg_nents,
+						     ch->sg_dma_direction);
+					return 0;
+				}
+
+				*table++ = cpu_to_le32(0x8000);
+				*table++ = cpu_to_le32(cur_addr + 0x8000);
+				xcount = 0x8000;
+			}
+			*table++ = cpu_to_le32(xcount);
+			cur_addr += bcount;
+			cur_len -= bcount;
+		}
+
+		sg++;
+		i--;
+	}
+
+	if (!count)
+		printk(KERN_ERR "%s: empty DMA table?\n", ch->name);
+	else if (!is_trm290_chipset)
+		*--table |= cpu_to_le32(0x80000000);
+
+	return count;
+}
+
+/* Teardown mappings after DMA has completed.  */
+void udma_destroy_table(struct ata_channel *ch)
+{
+	pci_unmap_sg(ch->pci_dev, ch->sg_table, ch->sg_nents, ch->sg_dma_direction);
+}
+
+/*
+ * Prepare the channel for a DMA startfer. Please note that only the broken
+ * Pacific Digital host chip needs the reques to be passed there to decide
+ * about addressing modes.
+ */
+
+static int udma_pci_start(struct ata_device *drive, struct request *rq)
+{
+	struct ata_channel *ch = drive->channel;
+	unsigned long dma_base = ch->dma_base;
+
+	/* Note that this is done *after* the cmd has
+	 * been issued to the drive, as per the BM-IDE spec.
+	 * The Promise Ultra33 doesn't work correctly when
+	 * we do this part before issuing the drive cmd.
+	 */
+	outb(inb(dma_base)|1, dma_base);		/* start DMA */
+	return 0;
+}
+
+static int udma_pci_stop(struct ata_device *drive)
+{
+	struct ata_channel *ch = drive->channel;
+	unsigned long dma_base = ch->dma_base;
+	u8 dma_stat;
+
+	drive->waiting_for_dma = 0;
+	outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
+	dma_stat = inb(dma_base+2);		/* get DMA status */
+	outb(dma_stat|6, dma_base+2);		/* clear the INTR & ERROR bits */
+	udma_destroy_table(ch);			/* purge DMA mappings */
+
+	return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0;	/* verify good DMA status */
+}
+
+static int udma_pci_read(struct ata_device *drive, struct request *rq)
+{
+	return ata_do_udma(1, drive, rq);
+}
+
+static int udma_pci_write(struct ata_device *drive, struct request *rq)
+{
+	return ata_do_udma(0, drive, rq);
+}
+
+/*
+ * FIXME: This should be attached to a channel as we can see now!
+ */
+static int udma_pci_irq_status(struct ata_device *drive)
+{
+	struct ata_channel *ch = drive->channel;
+	u8 dma_stat;
+
+	/* default action */
+	dma_stat = inb(ch->dma_base + 2);
+
+	return (dma_stat & 4) == 4;	/* return 1 if INTR asserted */
+}
+
+static void udma_pci_timeout(struct ata_device *drive)
+{
+	printk(KERN_ERR "ATA: UDMA timeout occured %s!\n", drive->name);
+}
+
+static void udma_pci_irq_lost(struct ata_device *drive)
+{
+}
+
+/*
+ * This can be called for a dynamically installed interface. Don't __init it
+ */
+void ata_init_dma(struct ata_channel *ch, unsigned long dma_base)
+{
+	if (!request_region(dma_base, 8, ch->name)) {
+		printk(KERN_ERR "ATA: ERROR: BM DMA portst already in use!\n");
+
+		return;
+	}
+	printk(KERN_INFO"    %s: BM-DMA at 0x%04lx-0x%04lx", ch->name, dma_base, dma_base + 7);
+	ch->dma_base = dma_base;
+	ch->dmatable_cpu = pci_alloc_consistent(ch->pci_dev,
+						  PRD_ENTRIES * PRD_BYTES,
+						  &ch->dmatable_dma);
+	if (ch->dmatable_cpu == NULL)
+		goto dma_alloc_failure;
+
+	ch->sg_table = kmalloc(sizeof(struct scatterlist) * PRD_ENTRIES,
+				 GFP_KERNEL);
+	if (ch->sg_table == NULL) {
+		pci_free_consistent(ch->pci_dev, PRD_ENTRIES * PRD_BYTES,
+				    ch->dmatable_cpu, ch->dmatable_dma);
+		goto dma_alloc_failure;
+	}
+
+	/*
+	 * We could just assign them, and then leave it up to the chipset
+	 * specific code to override these after they've called this function.
+	 */
+	if (!ch->XXX_udma)
+		ch->XXX_udma = XXX_ide_dmaproc;
+	if (!ch->udma_enable)
+		ch->udma_enable = udma_pci_enable;
+	if (!ch->udma_start)
+		ch->udma_start = udma_pci_start;
+	if (!ch->udma_stop)
+		ch->udma_stop = udma_pci_stop;
+	if (!ch->udma_read)
+		ch->udma_read = udma_pci_read;
+	if (!ch->udma_write)
+		ch->udma_write = udma_pci_write;
+	if (!ch->udma_irq_status)
+		ch->udma_irq_status = udma_pci_irq_status;
+	if (!ch->udma_timeout)
+		ch->udma_timeout = udma_pci_timeout;
+	if (!ch->udma_irq_lost)
+		ch->udma_irq_lost = udma_pci_irq_lost;
+
+	if (ch->chipset != ide_trm290) {
+		u8 dma_stat = inb(dma_base+2);
+		printk(", BIOS settings: %s:%s, %s:%s",
+		       ch->drives[0].name, (dma_stat & 0x20) ? "DMA" : "pio",
+		       ch->drives[1].name, (dma_stat & 0x40) ? "DMA" : "pio");
+	}
+	printk("\n");
+	return;
+
+dma_alloc_failure:
+	printk(" -- ERROR, UNABLE TO ALLOCATE DMA TABLES\n");
+}
+
+/*
+ * This is the default read write function.
+ *
+ * It's exported only for host chips which use it for fallback or (too) late
+ * capability checking.
+ */
+
+int ata_do_udma(unsigned int reading, struct ata_device *drive, struct request *rq)
+{
+	if (ata_start_dma(drive, rq))
+		return 1;
+
+	if (drive->type != ATA_DISK)
+		return 0;
+
+	reading <<= 3;
+
+	ide_set_handler(drive, ide_dma_intr, WAIT_CMD, dma_timer_expiry);	/* issue cmd to drive */
+	if ((rq->flags & REQ_DRIVE_ACB) && (drive->addressing == 1)) {
+		struct ata_taskfile *args = rq->special;
+
+		outb(args->taskfile.command, IDE_COMMAND_REG);
+	} else if (drive->addressing) {
+		outb(reading ? WIN_READDMA_EXT : WIN_WRITEDMA_EXT, IDE_COMMAND_REG);
+	} else {
+		outb(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
+	}
+
+	return udma_start(drive, rq);
+}
+
+EXPORT_SYMBOL(ata_do_udma);
+EXPORT_SYMBOL(ide_dma_intr);
diff -Nru a/drivers/ide/pcihost.h b/drivers/ide/pcihost.h
--- a/drivers/ide/pcihost.h	Sat May 18 00:46:08 2002
+++ b/drivers/ide/pcihost.h	Sat May 18 00:46:08 2002
@@ -10,10 +10,6 @@
  * 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
  */
 
 /*
@@ -71,9 +67,6 @@
 #endif
 #ifdef CONFIG_BLK_DEV_AMD74XX
 extern int init_amd74xx(void);
-#endif
-#ifdef CONFIG_BLK_DEV_PDC_ADMA
-extern int init_pdcadma(void);
 #endif
 #ifdef CONFIG_BLK_DEV_SVWKS
 extern int init_svwks(void);
diff -Nru a/drivers/ide/pdc202xx.c b/drivers/ide/pdc202xx.c
--- a/drivers/ide/pdc202xx.c	Sat May 18 00:46:15 2002
+++ b/drivers/ide/pdc202xx.c	Sat May 18 00:46:15 2002
@@ -53,220 +53,10 @@
 #define PDC202XX_DEBUG_DRIVE_INFO		0
 #define PDC202XX_DECODE_REGISTER_INFO		0
 
-#undef DISPLAY_PDC202XX_TIMINGS
-
 #ifndef SPLIT_BYTE
 #define SPLIT_BYTE(B,H,L)	((H)=(B>>4), (L)=(B-((B>>4)<<4)))
 #endif
 
-#if defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS)
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-
-static int pdc202xx_get_info(char *, char **, off_t, int);
-extern int (*pdc202xx_display_info)(char *, char **, off_t, int); /* ide-proc.c */
-static struct pci_dev *bmide_dev;
-
-char *pdc202xx_pio_verbose (u32 drive_pci)
-{
-	if ((drive_pci & 0x000ff000) == 0x000ff000) return("NOTSET");
-	if ((drive_pci & 0x00000401) == 0x00000401) return("PIO 4");
-	if ((drive_pci & 0x00000602) == 0x00000602) return("PIO 3");
-	if ((drive_pci & 0x00000803) == 0x00000803) return("PIO 2");
-	if ((drive_pci & 0x00000C05) == 0x00000C05) return("PIO 1");
-	if ((drive_pci & 0x00001309) == 0x00001309) return("PIO 0");
-	return("PIO ?");
-}
-
-char *pdc202xx_dma_verbose (u32 drive_pci)
-{
-	if ((drive_pci & 0x00036000) == 0x00036000) return("MWDMA 2");
-	if ((drive_pci & 0x00046000) == 0x00046000) return("MWDMA 1");
-	if ((drive_pci & 0x00056000) == 0x00056000) return("MWDMA 0");
-	if ((drive_pci & 0x00056000) == 0x00056000) return("SWDMA 2");
-	if ((drive_pci & 0x00068000) == 0x00068000) return("SWDMA 1");
-	if ((drive_pci & 0x000BC000) == 0x000BC000) return("SWDMA 0");
-	return("PIO---");
-}
-
-char *pdc202xx_ultra_verbose (u32 drive_pci, u16 slow_cable)
-{
-	if ((drive_pci & 0x000ff000) == 0x000ff000)
-		return("NOTSET");
-	if ((drive_pci & 0x00012000) == 0x00012000)
-		return((slow_cable) ? "UDMA 2" : "UDMA 4");
-	if ((drive_pci & 0x00024000) == 0x00024000)
-		return((slow_cable) ? "UDMA 1" : "UDMA 3");
-	if ((drive_pci & 0x00036000) == 0x00036000)
-		return("UDMA 0");
-	return(pdc202xx_dma_verbose(drive_pci));
-}
-
-static char * pdc202xx_info (char *buf, struct pci_dev *dev)
-{
-	char *p = buf;
-
-	u32 bibma  = pci_resource_start(dev, 4);
-	u32 reg60h = 0, reg64h = 0, reg68h = 0, reg6ch = 0;
-	u16 reg50h = 0, pmask = (1<<10), smask = (1<<11);
-	u8 hi = 0, lo = 0;
-
-        /*
-         * at that point bibma+0x2 et bibma+0xa are byte registers
-         * to investigate:
-         */
-	u8 c0	= inb_p((unsigned short)bibma + 0x02);
-	u8 c1	= inb_p((unsigned short)bibma + 0x0a);
-
-	u8 sc11	= inb_p((unsigned short)bibma + 0x11);
-	u8 sc1a	= inb_p((unsigned short)bibma + 0x1a);
-	u8 sc1b	= inb_p((unsigned short)bibma + 0x1b);
-	u8 sc1c	= inb_p((unsigned short)bibma + 0x1c); 
-	u8 sc1d	= inb_p((unsigned short)bibma + 0x1d);
-	u8 sc1e	= inb_p((unsigned short)bibma + 0x1e);
-	u8 sc1f	= inb_p((unsigned short)bibma + 0x1f);
-
-	pci_read_config_word(dev, 0x50, &reg50h);
-	pci_read_config_dword(dev, 0x60, &reg60h);
-	pci_read_config_dword(dev, 0x64, &reg64h);
-	pci_read_config_dword(dev, 0x68, &reg68h);
-	pci_read_config_dword(dev, 0x6c, &reg6ch);
-
-	switch(dev->device) {
-		case PCI_DEVICE_ID_PROMISE_20267:
-			p += sprintf(p, "\n                                PDC20267 Chipset.\n");
-			break;
-		case PCI_DEVICE_ID_PROMISE_20265:
-			p += sprintf(p, "\n                                PDC20265 Chipset.\n");
-			break;
-		case PCI_DEVICE_ID_PROMISE_20262:
-			p += sprintf(p, "\n                                PDC20262 Chipset.\n");
-			break;
-		case PCI_DEVICE_ID_PROMISE_20246:
-			p += sprintf(p, "\n                                PDC20246 Chipset.\n");
-			reg50h |= 0x0c00;
-			break;
-		default:
-			p += sprintf(p, "\n                                PDC202XX Chipset.\n");
-			break;
-	}
-
-	p += sprintf(p, "------------------------------- General Status ---------------------------------\n");
-	p += sprintf(p, "Burst Mode                           : %sabled\n", (sc1f & 0x01) ? "en" : "dis");
-	p += sprintf(p, "Host Mode                            : %s\n", (sc1f & 0x08) ? "Tri-Stated" : "Normal");
-	p += sprintf(p, "Bus Clocking                         : %s\n",
-		((sc1f & 0xC0) == 0xC0) ? "100 External" :
-		((sc1f & 0x80) == 0x80) ? "66 External" :
-		((sc1f & 0x40) == 0x40) ? "33 External" : "33 PCI Internal");
-	p += sprintf(p, "IO pad select                        : %s mA\n",
-		((sc1c & 0x03) == 0x03) ? "10" :
-		((sc1c & 0x02) == 0x02) ? "8" :
-		((sc1c & 0x01) == 0x01) ? "6" :
-		((sc1c & 0x00) == 0x00) ? "4" : "??");
-	SPLIT_BYTE(sc1e, hi, lo);
-	p += sprintf(p, "Status Polling Period                : %d\n", hi);
-	p += sprintf(p, "Interrupt Check Status Polling Delay : %d\n", lo);
-	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
-	p += sprintf(p, "                %s                         %s\n",
-		(c0&0x80)?"disabled":"enabled ",
-		(c1&0x80)?"disabled":"enabled ");
-	p += sprintf(p, "66 Clocking     %s                         %s\n",
-		(sc11&0x02)?"enabled ":"disabled",
-		(sc11&0x08)?"enabled ":"disabled");
-	p += sprintf(p, "           Mode %s                      Mode %s\n",
-		(sc1a & 0x01) ? "MASTER" : "PCI   ",
-		(sc1b & 0x01) ? "MASTER" : "PCI   ");
-	p += sprintf(p, "                %s                     %s\n",
-		(sc1d & 0x08) ? "Error       " :
-		((sc1d & 0x05) == 0x05) ? "Not My INTR " :
-		(sc1d & 0x04) ? "Interrupting" :
-		(sc1d & 0x02) ? "FIFO Full   " :
-		(sc1d & 0x01) ? "FIFO Empty  " : "????????????",
-		(sc1d & 0x80) ? "Error       " :
-		((sc1d & 0x50) == 0x50) ? "Not My INTR " :
-		(sc1d & 0x40) ? "Interrupting" :
-		(sc1d & 0x20) ? "FIFO Full   " :
-		(sc1d & 0x10) ? "FIFO Empty  " : "????????????");
-	p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
-	p += sprintf(p, "DMA enabled:    %s              %s             %s               %s\n",
-		(c0&0x20)?"yes":"no ",(c0&0x40)?"yes":"no ",(c1&0x20)?"yes":"no ",(c1&0x40)?"yes":"no ");
-	p += sprintf(p, "DMA Mode:       %s           %s          %s            %s\n",
-		pdc202xx_ultra_verbose(reg60h, (reg50h & pmask)),
-		pdc202xx_ultra_verbose(reg64h, (reg50h & pmask)),
-		pdc202xx_ultra_verbose(reg68h, (reg50h & smask)),
-		pdc202xx_ultra_verbose(reg6ch, (reg50h & smask)));
-	p += sprintf(p, "PIO Mode:       %s            %s           %s            %s\n",
-		pdc202xx_pio_verbose(reg60h),
-		pdc202xx_pio_verbose(reg64h),
-		pdc202xx_pio_verbose(reg68h),
-		pdc202xx_pio_verbose(reg6ch));
-#if 0
-	p += sprintf(p, "--------------- Can ATAPI DMA ---------------\n");
-#endif
-	return (char *)p;
-}
-
-static char * pdc202xx_info_new (char *buf, struct pci_dev *dev)
-{
-	char *p = buf;
-//	u32 bibma = pci_resource_start(dev, 4);
-
-//	u32 reg60h = 0, reg64h = 0, reg68h = 0, reg6ch = 0;
-//	u16 reg50h = 0, word88 = 0;
-//	int udmasel[4]={0,0,0,0}, piosel[4]={0,0,0,0}, i=0, hd=0;
-
-	switch(dev->device) {
-		case PCI_DEVICE_ID_PROMISE_20275:
-			p += sprintf(p, "\n                                PDC20275 Chipset.\n");
-			break;
-		case PCI_DEVICE_ID_PROMISE_20276:
-			p += sprintf(p, "\n                                PDC20276 Chipset.\n");
-			break;
-		case PCI_DEVICE_ID_PROMISE_20269:
-			p += sprintf(p, "\n                                PDC20269 TX2 Chipset.\n");
-			break;
-		case PCI_DEVICE_ID_PROMISE_20268:
-		case PCI_DEVICE_ID_PROMISE_20268R:
-			p += sprintf(p, "\n                                PDC20268 TX2 Chipset.\n");
-			break;
-default:
-			p += sprintf(p, "\n                                PDC202XX Chipset.\n");
-			break;
-	}
-	return (char *)p;
-}
-
-static int pdc202xx_get_info (char *buffer, char **addr, off_t offset, int count)
-{
-	char *p = buffer;
-	switch(bmide_dev->device) {
-		case PCI_DEVICE_ID_PROMISE_20275:
-		case PCI_DEVICE_ID_PROMISE_20276:
-		case PCI_DEVICE_ID_PROMISE_20269:
-		case PCI_DEVICE_ID_PROMISE_20268:
-		case PCI_DEVICE_ID_PROMISE_20268R:
-			p = pdc202xx_info_new(buffer, bmide_dev);
-			break;
-		default:
-			p = pdc202xx_info(buffer, bmide_dev);
-			break;
-	}
-	return p-buffer;	/* => must be less than 4k! */
-}
-#endif  /* defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS) */
-
-byte pdc202xx_proc = 0;
-
-const char *pdc_quirk_drives[] = {
-	"QUANTUM FIREBALLlct08 08",
-	"QUANTUM FIREBALLP KA6.4",
-	"QUANTUM FIREBALLP LM20.4",
-	"QUANTUM FIREBALLP KX20.5",
-	"QUANTUM FIREBALLP KX27.3",
-	"QUANTUM FIREBALLP LM20.5",
-	NULL
-};
-
 extern char *ide_xfer_verbose (byte xfer_rate);
 
 /* A Register */
@@ -322,7 +112,6 @@
 
 	switch(registers) {
 		case REG_A:
-			bit2 = 0;
 			printk("A Register ");
 			if (value & 0x80) printk("SYNC_IN ");
 			if (value & 0x40) printk("ERRDY_EN ");
@@ -335,7 +124,6 @@
 			printk("PIO(A) = %d ", bit2);
 			break;
 		case REG_B:
-			bit1 = 0;bit2 = 0;
 			printk("B Register ");
 			if (value & 0x80) { printk("MB2 ");bit1 |= 0x80; }
 			if (value & 0x40) { printk("MB1 ");bit1 |= 0x40; }
@@ -349,7 +137,6 @@
 			printk("PIO(B) = %d ", bit2);
 			break;
 		case REG_C:
-			bit2 = 0;
 			printk("C Register ");
 			if (value & 0x80) printk("DMARQp ");
 			if (value & 0x40) printk("IORDYp ");
@@ -379,23 +166,22 @@
 
 #endif /* PDC202XX_DECODE_REGISTER_INFO */
 
-static int check_in_drive_lists(struct ata_device *drive, const char **list)
-{
+int check_in_drive_lists(struct ata_device *drive) {
+	const char *pdc_quirk_drives[] = {
+		"QUANTUM FIREBALLlct08 08",
+		"QUANTUM FIREBALLP KA6.4",
+		"QUANTUM FIREBALLP LM20.4",
+		"QUANTUM FIREBALLP KX20.5",
+		"QUANTUM FIREBALLP KX27.3",
+		"QUANTUM FIREBALLP LM20.5",
+		NULL
+	};
+     const char**list = pdc_quirk_drives;
 	struct hd_driveid *id = drive->id;
 
-	if (pdc_quirk_drives == list) {
-		while (*list) {
-			if (strstr(id->model, *list++)) {
-				return 2;
-			}
-		}
-	} else {
-		while (*list) {
-			if (!strcmp(*list++,id->model)) {
-				return 1;
-			}
-		}
-	}
+	while (*list)
+		if (strstr(id->model, *list++))
+			return 2;
 	return 0;
 }
 
@@ -523,6 +309,15 @@
 	return err;
 }
 
+#define set_2regs(a, b) \
+        OUT_BYTE((a + adj), indexreg); \
+	OUT_BYTE(b, datareg);
+
+#define set_reg_and_wait(value, reg, delay) \
+	OUT_BYTE(value, reg); \
+        mdelay(delay);
+
+
 static int pdc202xx_new_tune_chipset(struct ata_device *drive, byte speed)
 {
 	struct ata_channel *hwif = drive->channel;
@@ -549,121 +344,79 @@
 		case XFER_UDMA_7:
 			speed = XFER_UDMA_6;
 		case XFER_UDMA_6:
-			OUT_BYTE((0x10 + adj), indexreg);
-			OUT_BYTE(0x1a, datareg);
-			OUT_BYTE((0x11 + adj), indexreg);
-			OUT_BYTE(0x01, datareg);
-			OUT_BYTE((0x12 + adj), indexreg);
-			OUT_BYTE(0xcb, datareg);
+			set_2regs(0x10, 0x1a);
+			set_2regs(0x11, 0x01);
+			set_2regs(0x12, 0xcb);
 			break;
 		case XFER_UDMA_5:
-			OUT_BYTE((0x10 + adj), indexreg);
-			OUT_BYTE(0x1a, datareg);
-			OUT_BYTE((0x11 + adj), indexreg);
-			OUT_BYTE(0x02, datareg);
-			OUT_BYTE((0x12 + adj), indexreg);
-			OUT_BYTE(0xcb, datareg);
+			set_2regs(0x10, 0x1a);
+			set_2regs(0x11, 0x02);
+			set_2regs(0x12, 0xcb);
 			break;
 		case XFER_UDMA_4:
-			OUT_BYTE((0x10 + adj), indexreg);
-			OUT_BYTE(0x1a, datareg);
-			OUT_BYTE((0x11 + adj), indexreg);
-			OUT_BYTE(0x03, datareg);
-			OUT_BYTE((0x12 + adj), indexreg);
-			OUT_BYTE(0xcd, datareg);
+			set_2regs(0x10, 0x1a);
+			set_2regs(0x11, 0x03);
+			set_2regs(0x12, 0xcd);
 			break;
 		case XFER_UDMA_3:
-			OUT_BYTE((0x10 + adj), indexreg);
-			OUT_BYTE(0x1a, datareg);
-			OUT_BYTE((0x11 + adj), indexreg);
-			OUT_BYTE(0x05, datareg);
-			OUT_BYTE((0x12 + adj), indexreg);
-			OUT_BYTE(0xcd, datareg);
+			set_2regs(0x10, 0x1a);
+			set_2regs(0x11, 0x05);
+			set_2regs(0x12, 0xcd);
 			break;
 		case XFER_UDMA_2:
-			OUT_BYTE((0x10 + adj), indexreg);
-			OUT_BYTE(0x2a, datareg);
-			OUT_BYTE((0x11 + adj), indexreg);
-			OUT_BYTE(0x07, datareg);
-			OUT_BYTE((0x12 + adj), indexreg);
-			OUT_BYTE(0xcd, datareg);
+			set_2regs(0x10, 0x2a);
+			set_2regs(0x11, 0x07);
+			set_2regs(0x12, 0xcd);
 			break;
 		case XFER_UDMA_1:
-			OUT_BYTE((0x10 + adj), indexreg);
-			OUT_BYTE(0x3a, datareg);
-			OUT_BYTE((0x11 + adj), indexreg);
-			OUT_BYTE(0x0a, datareg);
-			OUT_BYTE((0x12 + adj), indexreg);
-			OUT_BYTE(0xd0, datareg);
+			set_2regs(0x10, 0x3a);
+			set_2regs(0x11, 0x0a);
+			set_2regs(0x12, 0xd0);
 			break;
 		case XFER_UDMA_0:
-			OUT_BYTE((0x10 + adj), indexreg);
-			OUT_BYTE(0x4a, datareg);
-			OUT_BYTE((0x11 + adj), indexreg);
-			OUT_BYTE(0x0f, datareg);
-			OUT_BYTE((0x12 + adj), indexreg);
-			OUT_BYTE(0xd5, datareg);
+			set_2regs(0x10, 0x4a);
+			set_2regs(0x11, 0x0f);
+			set_2regs(0x12, 0xd5);
 			break;
 		case XFER_MW_DMA_2:
-			OUT_BYTE((0x0e + adj), indexreg);
-			OUT_BYTE(0x69, datareg);
-			OUT_BYTE((0x0f + adj), indexreg);
-			OUT_BYTE(0x25, datareg);
+			set_2regs(0x0e, 0x69);
+			set_2regs(0x0f, 0x25);
 			break;
 		case XFER_MW_DMA_1:
-			OUT_BYTE((0x0e + adj), indexreg);
-			OUT_BYTE(0x6b, datareg);
-			OUT_BYTE((0x0f+ adj), indexreg);
-			OUT_BYTE(0x27, datareg);
+			set_2regs(0x0e, 0x6b);
+			set_2regs(0x0f, 0x27);
 			break;
 		case XFER_MW_DMA_0:
-			OUT_BYTE((0x0e + adj), indexreg);
-			OUT_BYTE(0xdf, datareg);
-			OUT_BYTE((0x0f + adj), indexreg);
-			OUT_BYTE(0x5f, datareg);
+			set_2regs(0x0e, 0xdf);
+			set_2regs(0x0f, 0x5f);
 			break;
 #else
 	switch (speed) {
 #endif /* CONFIG_BLK_DEV_IDEDMA */
 		case XFER_PIO_4:
-			OUT_BYTE((0x0c + adj), indexreg);
-			OUT_BYTE(0x23, datareg);
-			OUT_BYTE((0x0d + adj), indexreg);
-			OUT_BYTE(0x09, datareg);
-			OUT_BYTE((0x13 + adj), indexreg);
-			OUT_BYTE(0x25, datareg);
+			set_2regs(0x0c, 0x23);
+			set_2regs(0x0d, 0x09);
+			set_2regs(0x13, 0x25);
 			break;
 		case XFER_PIO_3:
-			OUT_BYTE((0x0c + adj), indexreg);
-			OUT_BYTE(0x27, datareg);
-			OUT_BYTE((0x0d + adj), indexreg);
-			OUT_BYTE(0x0d, datareg);
-			OUT_BYTE((0x13 + adj), indexreg);
-			OUT_BYTE(0x35, datareg);
+			set_2regs(0x0c, 0x27);
+			set_2regs(0x0d, 0x0d);
+			set_2regs(0x13, 0x35);
 			break;
 		case XFER_PIO_2:
-			OUT_BYTE((0x0c + adj), indexreg);
-			OUT_BYTE(0x23, datareg);
-			OUT_BYTE((0x0d + adj), indexreg);
-			OUT_BYTE(0x26, datareg);
-			OUT_BYTE((0x13 + adj), indexreg);
-			OUT_BYTE(0x64, datareg);
+			set_2regs(0x0c, 0x23);
+			set_2regs(0x0d, 0x26);
+			set_2regs(0x13, 0x64);
 			break;
 		case XFER_PIO_1:
-			OUT_BYTE((0x0c + adj), indexreg);
-			OUT_BYTE(0x46, datareg);
-			OUT_BYTE((0x0d + adj), indexreg);
-			OUT_BYTE(0x29, datareg);
-			OUT_BYTE((0x13 + adj), indexreg);
-			OUT_BYTE(0xa4, datareg);
+			set_2regs(0x0c, 0x46);
+			set_2regs(0x0d, 0x29);
+			set_2regs(0x13, 0xa4);
 			break;
 		case XFER_PIO_0:
-			OUT_BYTE((0x0c + adj), indexreg);
-			OUT_BYTE(0xfb, datareg);
-			OUT_BYTE((0x0d + adj), indexreg);
-			OUT_BYTE(0x2b, datareg);
-			OUT_BYTE((0x13 + adj), indexreg);
-			OUT_BYTE(0xac, datareg);
+			set_2regs(0x0c, 0xfb);
+			set_2regs(0x0d, 0x2b);
+			set_2regs(0x13, 0xac);
 			break;
 		default:
 			;
@@ -684,21 +437,15 @@
  * 180, 120,  90,  90,  90,  60,  30
  *  11,   5,   4,   3,   2,   1,   0
  */
-static int config_chipset_for_pio(struct ata_device *drive, byte pio)
+static void config_chipset_for_pio(struct ata_device *drive, byte pio)
 {
-	byte speed = 0x00;
+	byte speed;
 
 	if (pio == 255)
 		speed = ata_timing_mode(drive, XFER_PIO | XFER_EPIO);
-	else
-		speed = XFER_PIO_0 + min_t(byte, pio, 4);
+	else	speed = XFER_PIO_0 + min_t(byte, pio, 4);
 
-	return ((int) pdc202xx_tune_chipset(drive, speed));
-}
-
-static void pdc202xx_tune_drive(struct ata_device *drive, byte pio)
-{
-	(void) config_chipset_for_pio(drive, pio);
+	pdc202xx_tune_chipset(drive, speed);
 }
 
 #ifdef CONFIG_BLK_DEV_IDEDMA
@@ -833,8 +580,7 @@
 		if (drive->type != ATA_DISK)
 			return 0;
 		if (id->capability & 4) {	/* IORDY_EN & PREFETCH_EN */
-			OUT_BYTE((iordy + adj), indexreg);
-			OUT_BYTE((IN_BYTE(datareg)|0x03), datareg);
+			set_2regs(iordy, (IN_BYTE(datareg)|0x03));
 		}
 		goto jumpbit_is_set;
 	}
@@ -971,7 +717,7 @@
 		on = 0;
 		verbose = 0;
 no_dma_set:
-		(void) config_chipset_for_pio(drive, 5);
+		config_chipset_for_pio(drive, 5);
 	}
 
 	udma_enable(drive, on, verbose);
@@ -979,11 +725,6 @@
 	return 0;
 }
 
-int pdc202xx_quirkproc(struct ata_device *drive)
-{
-	return ((int) check_in_drive_lists(drive, pdc_quirk_drives));
-}
-
 static int pdc202xx_udma_start(struct ata_device *drive, struct request *rq)
 {
 	u8 clock		= 0;
@@ -1119,15 +860,7 @@
 	return (dma_stat & 4) == 4;	/* return 1 if INTR asserted */
 }
 
-static void pdc202xx_udma_timeout(struct ata_device *drive)
-{
-	if (!drive->channel->resetproc)
-		return;
-	/* Assume naively that resetting the drive may help. */
-	drive->channel->resetproc(drive);
-}
-
-static void pdc202xx_udma_irq_lost(struct ata_device *drive)
+static void pdc202xx_bug (struct ata_device *drive)
 {
 	if (!drive->channel->resetproc)
 		return;
@@ -1143,10 +876,8 @@
 
 void pdc202xx_new_reset(struct ata_device *drive)
 {
-	OUT_BYTE(0x04,IDE_CONTROL_REG);
-	mdelay(1000);
-	OUT_BYTE(0x00,IDE_CONTROL_REG);
-	mdelay(1000);
+	set_reg_and_wait(0x04,IDE_CONTROL_REG, 1000);
+	set_reg_and_wait(0x00,IDE_CONTROL_REG, 1000);
 	printk("PDC202XX: %s channel reset.\n",
 		drive->channel->unit ? "Secondary" : "Primary");
 }
@@ -1156,40 +887,12 @@
 	unsigned long high_16	= pci_resource_start(drive->channel->pci_dev, 4);
 	byte udma_speed_flag	= IN_BYTE(high_16 + 0x001f);
 
-	OUT_BYTE(udma_speed_flag | 0x10, high_16 + 0x001f);
-	mdelay(100);
-	OUT_BYTE(udma_speed_flag & ~0x10, high_16 + 0x001f);
-	mdelay(2000);		/* 2 seconds ?! */
+	set_reg_and_wait(udma_speed_flag | 0x10, high_16 + 0x001f, 100);
+	set_reg_and_wait(udma_speed_flag & ~0x10, high_16 + 0x001f, 2000);		/* 2 seconds ?! */
 	printk("PDC202XX: %s channel reset.\n",
 		drive->channel->unit ? "Secondary" : "Primary");
 }
 
-/*
- * Since SUN Cobalt is attempting to do this operation, I should disclose
- * this has been a long time ago Thu Jul 27 16:40:57 2000 was the patch date
- * HOTSWAP ATA Infrastructure.
- */
-static int pdc202xx_tristate(struct ata_device * drive, int state)
-{
-#if 0
-	struct ata_channel *hwif = drive->channel;
-	unsigned long high_16	= pci_resource_start(hwif->pci_dev, 4);
-	byte sc1f		= inb(high_16 + 0x001f);
-
-	if (!hwif)
-		return -EINVAL;
-
-//	hwif->bus_state = state;
-
-	if (state) {
-		outb(sc1f | 0x08, high_16 + 0x001f);
-	} else {
-		outb(sc1f & ~0x08, high_16 + 0x001f);
-	}
-#endif
-	return 0;
-}
-
 static unsigned int __init pdc202xx_init_chipset(struct pci_dev *dev)
 {
 	unsigned long high_16	= pci_resource_start(dev, 4);
@@ -1213,10 +916,8 @@
 			break;
 		case PCI_DEVICE_ID_PROMISE_20267:
 		case PCI_DEVICE_ID_PROMISE_20265:
-			OUT_BYTE(udma_speed_flag | 0x10, high_16 + 0x001f);
-			mdelay(100);
-			OUT_BYTE(udma_speed_flag & ~0x10, high_16 + 0x001f);
-			mdelay(2000);   /* 2 seconds ?! */
+			set_reg_and_wait(udma_speed_flag | 0x10, high_16 + 0x001f, 100);
+			set_reg_and_wait(udma_speed_flag & ~0x10, high_16 + 0x001f, 2000);   /* 2 seconds ?! */
 			break;
 		case PCI_DEVICE_ID_PROMISE_20262:
 			/*
@@ -1229,10 +930,8 @@
 			 * reset leaves the timing registers intact,
 			 * but resets the drives.
 			 */
-			OUT_BYTE(udma_speed_flag | 0x10, high_16 + 0x001f);
-			mdelay(100);
-			OUT_BYTE(udma_speed_flag & ~0x10, high_16 + 0x001f);
-			mdelay(2000);	/* 2 seconds ?! */
+			set_reg_and_wait(udma_speed_flag | 0x10, high_16 + 0x001f, 100);
+			set_reg_and_wait(udma_speed_flag & ~0x10, high_16 + 0x001f, 2000);	/* 2 seconds ?! */
 		default:
 			if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE) {
 				byte irq = 0, irq2 = 0;
@@ -1265,31 +964,7 @@
 	}
 #endif /* CONFIG_PDC202XX_BURST */
 
-#ifdef CONFIG_PDC202XX_MASTER
-	if (!(primary_mode & 1)) {
-		printk("%s: FORCING PRIMARY MODE BIT 0x%02x -> 0x%02x ",
-			dev->name, primary_mode, (primary_mode|1));
-		OUT_BYTE(primary_mode|1, high_16 + 0x001a);
-		printk("%s\n", (IN_BYTE(high_16 + 0x001a) & 1) ? "MASTER" : "PCI");
-	}
-
-	if (!(secondary_mode & 1)) {
-		printk("%s: FORCING SECONDARY MODE BIT 0x%02x -> 0x%02x ",
-			dev->name, secondary_mode, (secondary_mode|1));
-		OUT_BYTE(secondary_mode|1, high_16 + 0x001b);
-		printk("%s\n", (IN_BYTE(high_16 + 0x001b) & 1) ? "MASTER" : "PCI");
-	}
-#endif
-
 fttk_tx_series:
-
-#if defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS)
-	if (!pdc202xx_proc) {
-		pdc202xx_proc = 1;
-		bmide_dev = dev;
-		pdc202xx_display_info = &pdc202xx_get_info;
-	}
-#endif
 	return dev->irq;
 }
 
@@ -1314,8 +989,8 @@
 
 static void __init ide_init_pdc202xx(struct ata_channel *hwif)
 {
-	hwif->tuneproc  = &pdc202xx_tune_drive;
-	hwif->quirkproc = &pdc202xx_quirkproc;
+	hwif->tuneproc  = &config_chipset_for_pio;
+	hwif->quirkproc = &check_in_drive_lists;
 
         switch(hwif->pci_dev->device) {
 		case PCI_DEVICE_ID_PROMISE_20275:
@@ -1329,7 +1004,6 @@
 		case PCI_DEVICE_ID_PROMISE_20267:
 		case PCI_DEVICE_ID_PROMISE_20265:
 		case PCI_DEVICE_ID_PROMISE_20262:
-			hwif->busproc   = &pdc202xx_tristate;
 			hwif->resetproc	= &pdc202xx_reset;
 		case PCI_DEVICE_ID_PROMISE_20246:
 			hwif->speedproc = &pdc202xx_tune_chipset;
@@ -1337,19 +1011,13 @@
 			break;
 	}
 
-#undef CONFIG_PDC202XX_32_UNMASK
-#ifdef CONFIG_PDC202XX_32_UNMASK
-	hwif->io_32bit = 1;
-	hwif->unmask = 1;
-#endif
-
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	if (hwif->dma_base) {
 		hwif->udma_start = pdc202xx_udma_start;
 		hwif->udma_stop = pdc202xx_udma_stop;
 		hwif->udma_irq_status = pdc202xx_udma_irq_status;
-		hwif->udma_irq_lost = pdc202xx_udma_irq_lost;
-		hwif->udma_timeout = pdc202xx_udma_timeout;
+		hwif->udma_irq_lost = pdc202xx_bug;
+		hwif->udma_timeout = pdc202xx_bug;
 		hwif->XXX_udma = pdc202xx_dmaproc;
 		hwif->highmem = 1;
 		if (!noautodma)
@@ -1450,7 +1118,7 @@
 		init_chipset: pdc202xx_init_chipset,
 		ata66_check: ata66_pdc202xx,
 		init_channel: ide_init_pdc202xx,
-		exnablebits: {{0x50,0x02,0x02}, {0x50,0x04,0x04}},
+		enablebits: {{0x50,0x02,0x02}, {0x50,0x04,0x04}},
 		bootable: OFF_BOARD,
 		extra: 48,
 		flags: ATA_F_IRQ  | ATA_F_DMA
@@ -1509,7 +1177,7 @@
 
 int __init init_pdc202xx(void)
 {
-	int i;
+	unsigned int i;
 
 	for (i = 0; i < ARRAY_SIZE(chipsets); ++i) {
 		ata_register_chipset(&chipsets[i]);
diff -Nru a/drivers/ide/pdc4030.c b/drivers/ide/pdc4030.c
--- a/drivers/ide/pdc4030.c	Sat May 18 00:46:08 2002
+++ b/drivers/ide/pdc4030.c	Sat May 18 00:46:08 2002
@@ -1,14 +1,11 @@
 /*  -*- linux-c -*-
- *  linux/drivers/ide/pdc4030.c		Version 0.92  Jan 15, 2002
  *
  *  Copyright (C) 1995-2002  Linus Torvalds & authors (see below)
- */
-
-/*
+ *
  *  Principal Author/Maintainer:  peterd@pnd-pc.demon.co.uk
  *
  *  This file provides support for the second port and cache of Promise
- *  IDE interfaces, e.g. DC4030VL, DC4030VL-1 and DC4030VL-2.
+ *  VLB based IDE interfaces, e.g. DC4030VL, DC4030VL-1 and DC4030VL-2.
  *
  *  Thanks are due to Mark Lord for advice and patiently answering stupid
  *  questions, and all those mugs^H^H^H^Hbrave souls who've tested this,
@@ -44,14 +41,14 @@
 
 /*
  * Once you've compiled it in, you'll have to also enable the interface
- * setup routine from the kernel command line, as in 
+ * setup routine from the kernel command line, as in
  *
  *	'linux ide0=dc4030' or 'linux ide1=dc4030'
  *
  * It should now work as a second controller also ('ide1=dc4030') but only
  * if you DON'T have BIOS V4.44, which has a bug. If you have this version
  * and EPROM programming facilities, you need to fix 4 bytes:
- * 	2496:	81	81
+ *	2496:	81	81
  *	2497:	3E	3E
  *	2498:	22	98	*
  *	2499:	06	05	*
@@ -67,7 +64,7 @@
  *
  * As of January 1999, Promise Technology Inc. have finally supplied me with
  * some technical information which has shed a glimmer of light on some of the
- * problems I was having, especially with writes. 
+ * problems I was having, especially with writes.
  *
  * There are still potential problems with the robustness and efficiency of
  * this driver because I still don't understand what the card is doing with
@@ -94,20 +91,85 @@
 
 #include "pdc4030.h"
 
-#if SUPPORT_VLB_SYNC != 1
-#error This driver will not work unless SUPPORT_VLB_SYNC is 1
-#endif
+/*
+ * Data transfer functions for polled IO.
+ */
+
+/*
+ * Some localbus EIDE interfaces require a special access sequence
+ * when using 32-bit I/O instructions to transfer data.  We call this
+ * the "vlb_sync" sequence, which consists of three successive reads
+ * of the sector count register location, with interrupts disabled
+ * to ensure that the reads all happen together.
+ */
+static void read_vlb(struct ata_device *drive, void *buffer, unsigned int wcount)
+{
+	unsigned long flags;
+
+	__save_flags(flags);	/* local CPU only */
+	__cli();		/* local CPU only */
+	inb(IDE_NSECTOR_REG);
+	inb(IDE_NSECTOR_REG);
+	inb(IDE_NSECTOR_REG);
+	insl(IDE_DATA_REG, buffer, wcount);
+	__restore_flags(flags);	/* local CPU only */
+}
+
+static void write_vlb(struct ata_device *drive, void *buffer, unsigned int wcount)
+{
+	unsigned long flags;
+
+	__save_flags(flags);	/* local CPU only */
+	__cli();		/* local CPU only */
+	inb(IDE_NSECTOR_REG);
+	inb(IDE_NSECTOR_REG);
+	inb(IDE_NSECTOR_REG);
+	outsl(IDE_DATA_REG, buffer, wcount);
+	__restore_flags(flags);	/* local CPU only */
+}
+
+static void read_16(struct ata_device *drive, void *buffer, unsigned int wcount)
+{
+	insw(IDE_DATA_REG, buffer, wcount<<1);
+}
+
+static void write_16(struct ata_device *drive, void *buffer, unsigned int wcount)
+{
+	outsw(IDE_DATA_REG, buffer, wcount<<1);
+}
+
+/*
+ * This is used for most PIO data transfers *from* the device.
+ */
+static void promise_read(struct ata_device *drive, void *buffer, unsigned int wcount)
+{
+	if (drive->channel->io_32bit)
+		read_vlb(drive, buffer, wcount);
+	else
+		read_16(drive, buffer, wcount);
+}
+
+/*
+ * This is used for most PIO data transfers *to* the device interface.
+ */
+static void promise_write(struct ata_device *drive, void *buffer, unsigned int wcount)
+{
+	if (drive->channel->io_32bit)
+		write_vlb(drive, buffer, wcount);
+	else
+		write_16(drive, buffer, wcount);
+}
 
 /*
  * promise_selectproc() is invoked by ide.c
  * in preparation for access to the specified drive.
  */
-static void promise_selectproc (ide_drive_t *drive)
+static void promise_selectproc(struct ata_device *drive)
 {
-	unsigned int number;
+	u8 number;
 
 	number = (drive->channel->unit << 1) + drive->select.b.unit;
-	OUT_BYTE(number,IDE_FEATURE_REG);
+	outb(number, IDE_FEATURE_REG);
 }
 
 /*
@@ -115,15 +177,15 @@
  * by command F0. They all have the same success/failure notification -
  * 'P' (=0x50) on success, 'p' (=0x70) on failure.
  */
-int pdc4030_cmd(ide_drive_t *drive, byte cmd)
+int pdc4030_cmd(struct ata_device *drive, byte cmd)
 {
 	unsigned long timeout, timer;
 	byte status_val;
 
 	promise_selectproc(drive);	/* redundant? */
-	OUT_BYTE(0xF3,IDE_SECTOR_REG);
-	OUT_BYTE(cmd,IDE_SELECT_REG);
-	OUT_BYTE(PROMISE_EXTENDED_COMMAND,IDE_COMMAND_REG);
+	outb(0xF3, IDE_SECTOR_REG);
+	outb(cmd, IDE_SELECT_REG);
+	outb(PROMISE_EXTENDED_COMMAND, IDE_COMMAND_REG);
 	timeout = HZ * 10;
 	timeout += jiffies;
 	do {
@@ -134,7 +196,7 @@
 		/* Delays at least 10ms to give interface a chance */
 		timer = jiffies + (HZ + 99)/100 + 1;
 		while (time_after(timer, jiffies));
-		status_val = IN_BYTE(IDE_SECTOR_REG);
+		status_val = inb(IDE_SECTOR_REG);
 	} while (status_val != 0x50 && status_val != 0x70);
 
 	if(status_val == 0x50)
@@ -146,7 +208,7 @@
 /*
  * pdc4030_identify sends a vendor-specific IDENTIFY command to the drive
  */
-int pdc4030_identify(ide_drive_t *drive)
+int pdc4030_identify(struct ata_device *drive)
 {
 	return pdc4030_cmd(drive, PROMISE_IDENTIFY);
 }
@@ -164,33 +226,34 @@
  */
 int __init setup_pdc4030(struct ata_channel *hwif)
 {
-        ide_drive_t *drive;
+        struct ata_device *drive;
 	struct ata_channel *hwif2;
 	struct dc_ident ident;
 	int i;
 	ide_startstop_t startstop;
 
-	if (!hwif) return 0;
+	if (!hwif)
+		return 0;
 
 	drive = &hwif->drives[0];
 	hwif2 = &ide_hwifs[hwif->index+1];
 	if (hwif->chipset == ide_pdc4030) /* we've already been found ! */
 		return 1;
 
-	if (IN_BYTE(IDE_NSECTOR_REG) == 0xFF || IN_BYTE(IDE_SECTOR_REG) == 0xFF) {
+	if (inb(IDE_NSECTOR_REG) == 0xFF || inb(IDE_SECTOR_REG) == 0xFF) {
 		return 0;
 	}
 	if (IDE_CONTROL_REG)
-		OUT_BYTE(0x08,IDE_CONTROL_REG);
+		outb(0x08, IDE_CONTROL_REG);
 	if (pdc4030_cmd(drive,PROMISE_GET_CONFIG)) {
 		return 0;
 	}
-	if (ide_wait_stat(&startstop, drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) {
+	if (ide_wait_stat(&startstop, drive, NULL, DATA_READY,BAD_W_STAT,WAIT_DRQ)) {
 		printk(KERN_INFO
 			"%s: Failed Promise read config!\n",hwif->name);
 		return 0;
 	}
-	ata_read(drive, &ident, SECTOR_WORDS);
+	promise_read(drive, &ident, SECTOR_WORDS);
 	if (ident.id[1] != 'P' || ident.id[0] != 'T') {
 		return 0;
 	}
@@ -233,7 +296,9 @@
 	hwif->chipset	= hwif2->chipset = ide_pdc4030;
 	hwif->unit	= ATA_PRIMARY;
 	hwif2->unit	= ATA_SECONDARY;
-	hwif->selectproc = hwif2->selectproc = &promise_selectproc;
+	hwif->ata_read  = hwif2->ata_read = promise_read;
+	hwif->ata_write = hwif2->ata_write = promise_write;
+	hwif->selectproc = hwif2->selectproc = promise_selectproc;
 	hwif->serialized = hwif2->serialized = 1;
 
 /* Shift the remaining interfaces up by one */
@@ -251,8 +316,7 @@
 	memcpy(hwif2->io_ports, hwif->hw.io_ports, sizeof(hwif2->io_ports));
 	hwif2->irq = hwif->irq;
 	hwif2->hw.irq = hwif->hw.irq = hwif->irq;
-	hwif->io_32bit = 3;
-	hwif2->io_32bit = 3;
+	hwif->io_32bit = hwif2->io_32bit = 1;
 	for (i=0; i<2 ; i++) {
 		if (!ident.current_tm[i].cyl)
 			hwif->drives[i].noprobe = 1;
@@ -269,20 +333,20 @@
  */
 int __init detect_pdc4030(struct ata_channel *hwif)
 {
-	ide_drive_t *drive = &hwif->drives[0];
+	struct ata_device *drive = &hwif->drives[0];
 
 	if (IDE_DATA_REG == 0) { /* Skip test for non-existent interface */
 		return 0;
 	}
-	OUT_BYTE(0xF3, IDE_SECTOR_REG);
-	OUT_BYTE(0x14, IDE_SELECT_REG);
-	OUT_BYTE(PROMISE_EXTENDED_COMMAND, IDE_COMMAND_REG);
-	
+	outb(0xF3, IDE_SECTOR_REG);
+	outb(0x14, IDE_SELECT_REG);
+	outb(PROMISE_EXTENDED_COMMAND, IDE_COMMAND_REG);
+
 	ide_delay_50ms();
 
-	if (IN_BYTE(IDE_ERROR_REG) == 'P' &&
-	    IN_BYTE(IDE_NSECTOR_REG) == 'T' &&
-	    IN_BYTE(IDE_SECTOR_REG) == 'I') {
+	if (inb(IDE_ERROR_REG) == 'P' &&
+	    inb(IDE_NSECTOR_REG) == 'T' &&
+	    inb(IDE_SECTOR_REG) == 'I') {
 		return 1;
 	} else {
 		return 0;
@@ -309,21 +373,21 @@
  */
 static ide_startstop_t promise_read_intr(struct ata_device *drive, struct request *rq)
 {
-	byte stat;
+	u8 stat;
 	int total_remaining;
 	unsigned int sectors_left, sectors_avail, nsect;
 	unsigned long flags;
 	char *to;
 
 	if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
-		return ide_error(drive, "promise_read_intr", stat);
+		return ide_error(drive, rq, "promise_read_intr", stat);
 	}
 
 read_again:
 	do {
-		sectors_left = IN_BYTE(IDE_NSECTOR_REG);
-		IN_BYTE(IDE_SECTOR_REG);
-	} while (IN_BYTE(IDE_NSECTOR_REG) != sectors_left);
+		sectors_left = inb(IDE_NSECTOR_REG);
+		inb(IDE_SECTOR_REG);
+	} while (inb(IDE_NSECTOR_REG) != sectors_left);
 	sectors_avail = rq->nr_sectors - sectors_left;
 	if (!sectors_avail)
 		goto read_again;
@@ -334,7 +398,7 @@
 		nsect = sectors_avail;
 	sectors_avail -= nsect;
 	to = bio_kmap_irq(rq->bio, &flags) + ide_rq_offset(rq);
-	ata_read(drive, to, nsect * SECTOR_WORDS);
+	promise_read(drive, to, nsect * SECTOR_WORDS);
 #ifdef DEBUG_READ
 	printk(KERN_DEBUG "%s:  promise_read: sectors(%ld-%ld), "
 	       "buf=0x%08lx, rem=%ld\n", drive->name, rq->sector,
@@ -348,17 +412,18 @@
 	if ((rq->current_nr_sectors -= nsect) <= 0) {
 		ide_end_request(drive, rq, 1);
 	}
-/*
- * Now the data has been read in, do the following:
- * 
- * if there are still sectors left in the request, 
- *   if we know there are still sectors available from the interface,
- *     go back and read the next bit of the request.
- *   else if DRQ is asserted, there are more sectors available, so
- *     go back and find out how many, then read them in.
- *   else if BUSY is asserted, we are going to get an interrupt, so
- *     set the handler for the interrupt and just return
- */
+
+	/*
+	 * Now the data has been read in, do the following:
+	 *
+	 * if there are still sectors left in the request, if we know there are
+	 * still sectors available from the interface, go back and read the
+	 * next bit of the request.  else if DRQ is asserted, there are more
+	 * sectors available, so go back and find out how many, then read them
+	 * in.  else if BUSY is asserted, we are going to get an interrupt, so
+	 * set the handler for the interrupt and just return
+	 */
+
 	if (total_remaining > 0) {
 		if (sectors_avail)
 			goto read_next;
@@ -375,7 +440,7 @@
 		}
 		printk(KERN_ERR "%s: Eeek! promise_read_intr: sectors left "
 		       "!DRQ !BUSY\n", drive->name);
-		return ide_error(drive, "promise read intr", stat);
+		return ide_error(drive, rq, "promise read intr", stat);
 	}
 	return ide_stopped;
 }
@@ -400,7 +465,7 @@
 		ch->poll_timeout = 0;
 		printk(KERN_ERR "%s: completion timeout - still busy!\n",
 		       drive->name);
-		return ide_error(drive, "busy timeout", GET_STAT());
+		return ide_error(drive, rq, "busy timeout", GET_STAT());
 	}
 
 	ch->poll_timeout = 0;
@@ -457,7 +522,7 @@
 		 * Ok, we're all setup for the interrupt
 		 * re-entering us on the last transfer.
 		 */
-		ata_write(drive, buffer, nsect << 7);
+		promise_write(drive, buffer, nsect << 7);
 		bio_kunmap_irq(buffer, &flags);
 	} while (mcount);
 
@@ -471,14 +536,14 @@
 {
 	struct ata_channel *ch = drive->channel;
 
-	if (IN_BYTE(IDE_NSECTOR_REG) != 0) {
+	if (inb(IDE_NSECTOR_REG) != 0) {
 		if (time_before(jiffies, ch->poll_timeout)) {
 			ide_set_handler(drive, promise_write_pollfunc, HZ/100, NULL);
 			return ide_started; /* continue polling... */
 		}
 		ch->poll_timeout = 0;
 		printk(KERN_ERR "%s: write timed out!\n",drive->name);
-		return ide_error(drive, "write timeout", GET_STAT());
+		return ide_error(drive, rq, "write timeout", GET_STAT());
 	}
 
 	/*
@@ -495,13 +560,13 @@
 }
 
 /*
- * promise_write() transfers a block of one or more sectors of data to a
- * drive as part of a disk write operation. All but 4 sectors are transferred
- * in the first attempt, then the interface is polled (nicely!) for completion
- * before the final 4 sectors are transferred. There is no interrupt generated
- * on writes (at least on the DC4030VL-2), we just have to poll for NOT BUSY.
+ * This transfers a block of one or more sectors of data to a drive as part of
+ * a disk write operation. All but 4 sectors are transferred in the first
+ * attempt, then the interface is polled (nicely!) for completion before the
+ * final 4 sectors are transferred. There is no interrupt generated on writes
+ * (at least on the DC4030VL-2), we just have to poll for NOT BUSY.
  */
-static ide_startstop_t promise_write(struct ata_device *drive, struct request *rq)
+static ide_startstop_t promise_do_write(struct ata_device *drive, struct request *rq)
 {
 	struct ata_channel *ch = drive->channel;
 
@@ -557,18 +622,18 @@
 	}
 
 	if (IDE_CONTROL_REG)
-		OUT_BYTE(drive->ctl, IDE_CONTROL_REG);  /* clear nIEN */
+		outb(drive->ctl, IDE_CONTROL_REG);  /* clear nIEN */
 	SELECT_MASK(drive->channel, drive, 0);
 
-	OUT_BYTE(taskfile->feature, IDE_FEATURE_REG);
-	OUT_BYTE(taskfile->sector_count, IDE_NSECTOR_REG);
+	outb(taskfile->feature, IDE_FEATURE_REG);
+	outb(taskfile->sector_count, IDE_NSECTOR_REG);
 	/* refers to number of sectors to transfer */
-	OUT_BYTE(taskfile->sector_number, IDE_SECTOR_REG);
+	outb(taskfile->sector_number, IDE_SECTOR_REG);
 	/* refers to sector offset or start sector */
-	OUT_BYTE(taskfile->low_cylinder, IDE_LCYL_REG);
-	OUT_BYTE(taskfile->high_cylinder, IDE_HCYL_REG);
-	OUT_BYTE(taskfile->device_head, IDE_SELECT_REG);
-	OUT_BYTE(taskfile->command, IDE_COMMAND_REG);
+	outb(taskfile->low_cylinder, IDE_LCYL_REG);
+	outb(taskfile->high_cylinder, IDE_HCYL_REG);
+	outb(taskfile->device_head, IDE_SELECT_REG);
+	outb(taskfile->command, IDE_COMMAND_REG);
 
 	switch (rq_data_dir(rq)) {
 	case READ:
@@ -589,7 +654,7 @@
 				udelay(1);
 				return promise_read_intr(drive, rq);
 			}
-			if (IN_BYTE(IDE_SELECT_REG) & 0x01) {
+			if (inb(IDE_SELECT_REG) & 0x01) {
 #ifdef DEBUG_READ
 				printk(KERN_DEBUG "%s: read: waiting for "
 				                  "interrupt\n", drive->name);
@@ -613,14 +678,14 @@
  *	call the promise_write function to deal with writing the data out
  * NOTE: No interrupts are generated on writes. Write completion must be polled
  */
-		if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
+		if (ide_wait_stat(&startstop, drive, rq, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
 			printk(KERN_ERR "%s: no DRQ after issuing "
 			       "PROMISE_WRITE\n", drive->name);
 			return startstop;
 		}
 		if (!drive->channel->unmask)
 			__cli();	/* local CPU only */
-		return promise_write(drive, rq);
+		return promise_do_write(drive, rq);
 	}
 
 	default:
diff -Nru a/drivers/ide/pdcadma.c b/drivers/ide/pdcadma.c
--- a/drivers/ide/pdcadma.c	Sat May 18 00:46:08 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,128 +0,0 @@
-/*
- * linux/drivers/ide/pdcadma.c		Version 0.01	June 21, 2001
- *
- * Copyright (C) 1999-2000		Andre Hedrick <andre@linux-ide.org>
- * May be copied or modified under the terms of the GNU General Public License
- *
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#include "ata-timing.h"
-#include "pcihost.h"
-
-#undef DISPLAY_PDCADMA_TIMINGS
-
-#if defined(DISPLAY_PDCADMA_TIMINGS) && defined(CONFIG_PROC_FS)
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-
-static int pdcadma_get_info(char *, char **, off_t, int);
-extern int (*pdcadma_display_info)(char *, char **, off_t, int); /* ide-proc.c */
-static struct pci_dev *bmide_dev;
-
-static int pdcadma_get_info (char *buffer, char **addr, off_t offset, int count)
-{
-	char *p = buffer;
-	u32 bibma = pci_resource_start(bmide_dev, 4);
-
-	p += sprintf(p, "\n                                PDC ADMA %04X Chipset.\n", bmide_dev->device);
-	p += sprintf(p, "UDMA\n");
-	p += sprintf(p, "PIO\n");
-
-	return p-buffer;	/* => must be less than 4k! */
-}
-#endif
-
-byte pdcadma_proc = 0;
-
-extern char *ide_xfer_verbose (byte xfer_rate);
-
-#ifdef CONFIG_BLK_DEV_IDEDMA
-
-/*
- * This initiates/aborts (U)DMA read/write operations on a drive.
- */
-static int pdcadma_dmaproc(struct ata_device *drive)
-{
-	udma_enable(drive, 0, 0);
-
-	return 0;
-}
-#endif
-
-static unsigned int __init pci_init_pdcadma(struct pci_dev *dev)
-{
-#if defined(DISPLAY_PDCADMA_TIMINGS) && defined(CONFIG_PROC_FS)
-	if (!pdcadma_proc) {
-		pdcadma_proc = 1;
-		bmide_dev = dev;
-		pdcadma_display_info = pdcadma_get_info;
-	}
-#endif
-	return 0;
-}
-
-static unsigned int __init ata66_pdcadma(struct ata_channel *channel)
-{
-	return 1;
-}
-
-static void __init ide_init_pdcadma(struct ata_channel *hwif)
-{
-	hwif->autodma = 0;
-	hwif->dma_base = 0;
-
-//	hwif->tuneproc = &pdcadma_tune_drive;
-//	hwif->speedproc = &pdcadma_tune_chipset;
-
-//	if (hwif->dma_base) {
-//		hwif->XXX_dmaproc = &pdcadma_dmaproc;
-//		hwif->autodma = 1;
-//	}
-}
-
-static void __init ide_dmacapable_pdcadma(struct ata_channel *hwif, unsigned long dmabase)
-{
-//	ide_setup_dma(hwif, dmabase, 8);
-}
-
-
-/* module data table */
-static struct ata_pci_device chipset __initdata = {
-	PCI_VENDOR_ID_PDC, PCI_DEVICE_ID_PDC_1841,
-	pci_init_pdcadma,
-	ata66_pdcadma,
-	ide_init_pdcadma,
-	ide_dmacapable_pdcadma,
-	{
-		{0x00,0x00,0x00},
-		{0x00,0x00,0x00}
-	},
-	OFF_BOARD,
-	0,
-	ATA_F_NODMA
-};
-
-int __init init_pdcadma(void)
-{
-	ata_register_chipset(&chipset);
-
-        return 0;
-}
diff -Nru a/drivers/ide/quirks.c b/drivers/ide/quirks.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/ide/quirks.c	Sat May 18 00:46:15 2002
@@ -0,0 +1,231 @@
+/**** vi:set ts=8 sts=8 sw=8:************************************************
+ *
+ *  Copyright (C) 2002 Marcin Dalecki <martin@dalecki.de>
+ *
+ *  Copyright (c) 1999-2000  Andre Hedrick <andre@linux-ide.org>
+ *  Copyright (c) 1995-1998  Mark Lord
+ *
+ * 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.
+ */
+
+/*
+ *  Just the black and white list handling for BM-DMA operation.
+ */
+
+#include <linux/config.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#ifdef CONFIG_IDEDMA_NEW_DRIVE_LISTINGS
+
+struct drive_list_entry {
+	char * id_model;
+	char * id_firmware;
+};
+
+struct drive_list_entry drive_whitelist[] = {
+	{ "Micropolis 2112A", NULL },
+	{ "CONNER CTMA 4000", NULL },
+	{ "CONNER CTT8000-A", NULL },
+	{ "ST34342A", NULL },
+	{ NULL, NULL }
+};
+
+struct drive_list_entry drive_blacklist[] = {
+
+	{ "WDC AC11000H", NULL },
+	{ "WDC AC22100H", NULL },
+	{ "WDC AC32500H", NULL },
+	{ "WDC AC33100H", NULL },
+	{ "WDC AC31600H", NULL },
+	{ "WDC AC32100H", "24.09P07" },
+	{ "WDC AC23200L", "21.10N21" },
+	{ "Compaq CRD-8241B", NULL },
+	{ "CRD-8400B", NULL },
+	{ "CRD-8480B", NULL },
+	{ "CRD-8480C", NULL },
+	{ "CRD-8482B", NULL },
+	{ "CRD-84", NULL },
+	{ "SanDisk SDP3B", NULL },
+	{ "SanDisk SDP3B-64", NULL },
+	{ "SANYO CD-ROM CRD", NULL },
+	{ "HITACHI CDR-8", NULL },
+	{ "HITACHI CDR-8335", NULL },
+	{ "HITACHI CDR-8435", NULL },
+	{ "Toshiba CD-ROM XM-6202B", NULL },
+	{ "CD-532E-A", NULL },
+	{ "E-IDE CD-ROM CR-840", NULL },
+	{ "CD-ROM Drive/F5A", NULL },
+	{ "RICOH CD-R/RW MP7083A", NULL },
+	{ "WPI CDD-820", NULL },
+	{ "SAMSUNG CD-ROM SC-148C", NULL },
+	{ "SAMSUNG CD-ROM SC-148F", NULL },
+	{ "SAMSUNG CD-ROM SC", NULL },
+	{ "SanDisk SDP3B-64", NULL },
+	{ "SAMSUNG CD-ROM SN-124", NULL },
+	{ "PLEXTOR CD-R PX-W8432T", NULL },
+	{ "ATAPI CD-ROM DRIVE 40X MAXIMUM", NULL },
+	{ "_NEC DV5800A", NULL },
+	{ NULL,	NULL }
+
+};
+
+static int in_drive_list(struct hd_driveid *id, struct drive_list_entry * drive_table)
+{
+	for ( ; drive_table->id_model ; drive_table++)
+		if ((!strcmp(drive_table->id_model, id->model)) &&
+		    ((drive_table->id_firmware && !strstr(drive_table->id_firmware, id->fw_rev)) ||
+		     (!drive_table->id_firmware)))
+			return 1;
+	return 0;
+}
+
+#else
+
+/*
+ * good_dma_drives() lists the model names (from "hdparm -i")
+ * of drives which do not support mode2 DMA but which are
+ * known to work fine with this interface under Linux.
+ */
+const char *good_dma_drives[] = {"Micropolis 2112A",
+				 "CONNER CTMA 4000",
+				 "CONNER CTT8000-A",
+				 "ST34342A",	/* for Sun Ultra */
+				 NULL};
+
+/*
+ * bad_dma_drives() lists the model names (from "hdparm -i")
+ * of drives which supposedly support (U)DMA but which are
+ * known to corrupt data with this interface under Linux.
+ *
+ * This is an empirical list. Its generated from bug reports. That means
+ * while it reflects actual problem distributions it doesn't answer whether
+ * the drive or the controller, or cabling, or software, or some combination
+ * thereof is the fault. If you don't happen to agree with the kernel's
+ * opinion of your drive - use hdparm to turn DMA on.
+ */
+const char *bad_dma_drives[] = {"WDC AC11000H",
+				"WDC AC22100H",
+				"WDC AC32100H",
+				"WDC AC32500H",
+				"WDC AC33100H",
+				"WDC AC31600H",
+				NULL};
+
+#endif
+
+/*
+ *  For both Blacklisted and Whitelisted drives.
+ *  This is setup to be called as an extern for future support
+ *  to other special driver code.
+ */
+int check_drive_lists(struct ata_device *drive, int good_bad)
+{
+	struct hd_driveid *id = drive->id;
+
+#ifdef CONFIG_IDEDMA_NEW_DRIVE_LISTINGS
+	if (good_bad) {
+		return in_drive_list(id, drive_whitelist);
+	} else {
+		int blacklist = in_drive_list(id, drive_blacklist);
+		if (blacklist)
+			printk("%s: Disabling (U)DMA for %s\n", drive->name, id->model);
+		return(blacklist);
+	}
+#else
+	const char **list;
+
+	if (good_bad) {
+		/* Consult the list of known "good" drives */
+		list = good_dma_drives;
+		while (*list) {
+			if (!strcmp(*list++,id->model))
+				return 1;
+		}
+	} else {
+		/* Consult the list of known "bad" drives */
+		list = bad_dma_drives;
+		while (*list) {
+			if (!strcmp(*list++,id->model)) {
+				printk("%s: Disabling (U)DMA for %s\n",
+					drive->name, id->model);
+				return 1;
+			}
+		}
+	}
+#endif
+	return 0;
+}
+
+void udma_print(struct ata_device *drive)
+{
+#ifdef CONFIG_ARCH_ACORN
+	printk(", DMA");
+#else
+	struct hd_driveid *id = drive->id;
+	char *str = NULL;
+
+	if ((id->field_valid & 4) && (eighty_ninty_three(drive)) &&
+	    (id->dma_ultra & (id->dma_ultra >> 14) & 3)) {
+		if ((id->dma_ultra >> 15) & 1)
+			str = ", UDMA(mode 7)";	/* UDMA BIOS-enabled! */
+		else
+			str = ", UDMA(133)";	/* UDMA BIOS-enabled! */
+	} else if ((id->field_valid & 4) && (eighty_ninty_three(drive)) &&
+		  (id->dma_ultra & (id->dma_ultra >> 11) & 7)) {
+		if ((id->dma_ultra >> 13) & 1) {
+			str = ", UDMA(100)";	/* UDMA BIOS-enabled! */
+		} else if ((id->dma_ultra >> 12) & 1) {
+			str = ", UDMA(66)";	/* UDMA BIOS-enabled! */
+		} else {
+			str = ", UDMA(44)";	/* UDMA BIOS-enabled! */
+		}
+	} else if ((id->field_valid & 4) &&
+		   (id->dma_ultra & (id->dma_ultra >> 8) & 7)) {
+		if ((id->dma_ultra >> 10) & 1) {
+			str = ", UDMA(33)";	/* UDMA BIOS-enabled! */
+		} else if ((id->dma_ultra >> 9) & 1) {
+			str = ", UDMA(25)";	/* UDMA BIOS-enabled! */
+		} else {
+			str = ", UDMA(16)";	/* UDMA BIOS-enabled! */
+		}
+	} else if (id->field_valid & 4)
+		str = ", (U)DMA";	/* Can be BIOS-enabled! */
+	else
+		str = ", DMA";
+
+	printk(str);
+#endif
+}
+
+/*
+ * Drive back/white list handling for UDMA capability:
+ */
+
+int udma_black_list(struct ata_device *drive)
+{
+	return check_drive_lists(drive, 0);
+}
+
+int udma_white_list(struct ata_device *drive)
+{
+	return check_drive_lists(drive, 1);
+}
+
+EXPORT_SYMBOL(udma_print);
+EXPORT_SYMBOL(udma_black_list);
+EXPORT_SYMBOL(udma_white_list);
diff -Nru a/drivers/ide/sl82c105.c b/drivers/ide/sl82c105.c
--- a/drivers/ide/sl82c105.c	Sat May 18 00:46:08 2002
+++ b/drivers/ide/sl82c105.c	Sat May 18 00:46:08 2002
@@ -76,7 +76,7 @@
 	if (ide_config_drive_speed(drive, xfer_mode) == 0)
 		drv_ctrl = get_timing_sl82c105(t);
 
-	if (drive->using_dma == 0) {
+	if (!drive->using_dma) {
 		/*
 		 * If we are actually using MW DMA, then we can not
 		 * reprogram the interface drive control register.
diff -Nru a/drivers/ide/tcq.c b/drivers/ide/tcq.c
--- a/drivers/ide/tcq.c	Sat May 18 00:46:14 2002
+++ b/drivers/ide/tcq.c	Sat May 18 00:46:14 2002
@@ -52,7 +52,7 @@
 #undef IDE_TCQ_FIDDLE_SI
 
 static ide_startstop_t ide_dmaq_intr(struct ata_device *drive, struct request *rq);
-static ide_startstop_t service(struct ata_device *drive);
+static ide_startstop_t service(struct ata_device *drive, struct request *rq);
 
 static inline void drive_ctl_nien(struct ata_device *drive, int set)
 {
@@ -70,7 +70,7 @@
 	struct ata_taskfile *args = rq->special;
 
 	ide__sti();
-	ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
+	ide_end_drive_cmd(drive, rq, GET_STAT(), GET_ERR());
 	kfree(args);
 	return ide_stopped;
 }
@@ -82,7 +82,7 @@
  */
 static void tcq_invalidate_queue(struct ata_device *drive)
 {
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	struct ata_channel *ch = drive->channel;
 	request_queue_t *q = &drive->queue;
 	struct ata_taskfile *args;
 	struct request *rq;
@@ -90,20 +90,20 @@
 
 	printk(KERN_INFO "ATA: %s: invalidating pending queue (%d)\n", drive->name, ata_pending_commands(drive));
 
-	spin_lock_irqsave(&ide_lock, flags);
+	spin_lock_irqsave(ch->lock, flags);
 
-	del_timer(&hwgroup->timer);
+	del_timer(&ch->timer);
 
-	if (test_bit(IDE_DMA, &hwgroup->flags))
+	if (test_bit(IDE_DMA, &ch->active))
 		udma_stop(drive);
 
 	blk_queue_invalidate_tags(q);
 
 	drive->using_tcq = 0;
 	drive->queue_depth = 1;
-	clear_bit(IDE_BUSY, &hwgroup->flags);
-	clear_bit(IDE_DMA, &hwgroup->flags);
-	hwgroup->handler = NULL;
+	clear_bit(IDE_BUSY, &ch->active);
+	clear_bit(IDE_DMA, &ch->active);
+	ch->handler = NULL;
 
 	/*
 	 * Do some internal stuff -- we really need this command to be
@@ -144,32 +144,32 @@
 	 * start doing stuff again
 	 */
 	q->request_fn(q);
-	spin_unlock_irqrestore(&ide_lock, flags);
+	spin_unlock_irqrestore(ch->lock, flags);
 	printk(KERN_DEBUG "ATA: tcq_invalidate_queue: done\n");
 }
 
 static void ata_tcq_irq_timeout(unsigned long data)
 {
 	struct ata_device *drive = (struct ata_device *) data;
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	struct ata_channel *ch = drive->channel;
 	unsigned long flags;
 
 	printk(KERN_ERR "ATA: %s: timeout waiting for interrupt...\n", __FUNCTION__);
 
-	spin_lock_irqsave(&ide_lock, flags);
+	spin_lock_irqsave(ch->lock, flags);
 
-	if (test_and_set_bit(IDE_BUSY, &hwgroup->flags))
-		printk(KERN_ERR "ATA: %s: hwgroup not busy\n", __FUNCTION__);
-	if (hwgroup->handler == NULL)
-		printk(KERN_ERR "ATA: %s: missing isr!\n", __FUNCTION__);
+	if (test_and_set_bit(IDE_BUSY, &ch->active))
+		printk(KERN_ERR "ATA: %s: IRQ handler not busy\n", __FUNCTION__);
+	if (!ch->handler)
+		printk(KERN_ERR "ATA: %s: missing ISR!\n", __FUNCTION__);
 
-	spin_unlock_irqrestore(&ide_lock, flags);
+	spin_unlock_irqrestore(ch->lock, flags);
 
 	/*
 	 * if pending commands, try service before giving up
 	 */
 	if (ata_pending_commands(drive) && (GET_STAT() & SERVICE_STAT))
-		if (service(drive) == ide_started)
+		if (service(drive, drive->rq) == ide_started)
 			return;
 
 	if (drive)
@@ -178,21 +178,25 @@
 
 static void set_irq(struct ata_device *drive, ata_handler_t *handler)
 {
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	struct ata_channel *ch = drive->channel;
 	unsigned long flags;
 
-	spin_lock_irqsave(&ide_lock, flags);
+	spin_lock_irqsave(ch->lock, flags);
 
 	/*
 	 * always just bump the timer for now, the timeout handling will
 	 * have to be changed to be per-command
+	 *
+	 * FIXME: Jens - this is broken it will interfere with
+	 * the normal timer function on serialized drives!
 	 */
-	hwgroup->timer.function = ata_tcq_irq_timeout;
-	hwgroup->timer.data = (unsigned long) hwgroup->XXX_drive;
-	mod_timer(&hwgroup->timer, jiffies + 5 * HZ);
 
-	hwgroup->handler = handler;
-	spin_unlock_irqrestore(&ide_lock, flags);
+	ch->timer.function = ata_tcq_irq_timeout;
+	ch->timer.data = (unsigned long) ch->drive;
+	mod_timer(&ch->timer, jiffies + 5 * HZ);
+	ch->handler = handler;
+
+	spin_unlock_irqrestore(ch->lock, flags);
 }
 
 /*
@@ -223,9 +227,8 @@
  *
  * Also, nIEN must be set as not to need protection against ide_dmaq_intr
  */
-static ide_startstop_t service(struct ata_device *drive)
+static ide_startstop_t service(struct ata_device *drive, struct request *rq)
 {
-	struct request *rq;
 	u8 feat;
 	u8 stat;
 	int tag;
@@ -236,13 +239,13 @@
 	 * Could be called with IDE_DMA in-progress from invalidate
 	 * handler, refuse to do anything.
 	 */
-	if (test_bit(IDE_DMA, &HWGROUP(drive)->flags))
+	if (test_bit(IDE_DMA, &drive->channel->active))
 		return ide_stopped;
 
 	/*
 	 * need to select the right drive first...
 	 */
-	if (drive != HWGROUP(drive)->XXX_drive) {
+	if (drive != drive->channel->drive) {
 		SELECT_DRIVE(drive->channel, drive);
 		udelay(10);
 	}
@@ -256,8 +259,9 @@
 
 	if (wait_altstat(drive, &stat, BUSY_STAT)) {
 		printk(KERN_ERR"%s: BUSY clear took too long\n", __FUNCTION__);
-		ide_dump_status(drive, __FUNCTION__, stat);
+		ide_dump_status(drive, rq, __FUNCTION__, stat);
 		tcq_invalidate_queue(drive);
+
 		return ide_stopped;
 	}
 
@@ -267,8 +271,9 @@
 	 * FIXME, invalidate queue
 	 */
 	if (stat & ERR_STAT) {
-		ide_dump_status(drive, __FUNCTION__, stat);
+		ide_dump_status(drive, rq, __FUNCTION__, stat);
 		tcq_invalidate_queue(drive);
+
 		return ide_stopped;
 	}
 
@@ -276,7 +281,7 @@
 	 * should not happen, a buggy device could introduce loop
 	 */
 	if ((feat = GET_FEAT()) & NSEC_REL) {
-		HWGROUP(drive)->rq = NULL;
+		drive->rq = NULL;
 		printk("%s: release in service\n", drive->name);
 		return ide_stopped;
 	}
@@ -291,7 +296,7 @@
 		return ide_stopped;
 	}
 
-	HWGROUP(drive)->rq = rq;
+	drive->rq = rq;
 
 	/*
 	 * we'll start a dma read or write, device will trigger
@@ -301,7 +306,7 @@
 	return udma_tcq_start(drive, rq);
 }
 
-static ide_startstop_t check_service(struct ata_device *drive)
+static ide_startstop_t check_service(struct ata_device *drive, struct request *rq)
 {
 	u8 stat;
 
@@ -311,7 +316,7 @@
 		return ide_stopped;
 
 	if ((stat = GET_STAT()) & SERVICE_STAT)
-		return service(drive);
+		return service(drive, rq);
 
 	/*
 	 * we have pending commands, wait for interrupt
@@ -335,8 +340,9 @@
 	 */
 	if (unlikely(!OK_STAT(stat, READY_STAT, drive->bad_wstat | DRQ_STAT))) {
 		printk(KERN_ERR "%s: %s: error status %x\n", __FUNCTION__, drive->name,stat);
-		ide_dump_status(drive, __FUNCTION__, stat);
+		ide_dump_status(drive, rq, __FUNCTION__, stat);
 		tcq_invalidate_queue(drive);
+
 		return ide_stopped;
 	}
 
@@ -349,7 +355,7 @@
 	/*
 	 * we completed this command, check if we can service a new command
 	 */
-	return check_service(drive);
+	return check_service(drive, rq);
 }
 
 /*
@@ -380,11 +386,11 @@
 	 */
 	if (stat & SERVICE_STAT) {
 		TCQ_PRINTK("%s: SERV (stat=%x)\n", __FUNCTION__, stat);
-		return service(drive);
+		return service(drive, rq);
 	}
 
 	printk("%s: stat=%x, not expected\n", __FUNCTION__, stat);
-	return check_service(drive);
+	return check_service(drive, rq);
 }
 
 /*
@@ -521,7 +527,7 @@
 	struct ata_channel *ch = drive->channel;
 
 	TCQ_PRINTK("%s: setting up queued %d\n", __FUNCTION__, rq->tag);
-	if (!test_bit(IDE_BUSY, &ch->hwgroup->flags))
+	if (!test_bit(IDE_BUSY, &ch->active))
 		printk("queued_rw: IDE_BUSY not set\n");
 
 	if (tcq_wait_dataphase(drive))
@@ -558,7 +564,7 @@
 	OUT_BYTE(args->taskfile.command, IDE_COMMAND_REG);
 
 	if (wait_altstat(drive, &stat, BUSY_STAT)) {
-		ide_dump_status(drive, "queued start", stat);
+		ide_dump_status(drive, rq, "queued start", stat);
 		tcq_invalidate_queue(drive);
 		return ide_stopped;
 	}
@@ -566,7 +572,7 @@
 	drive_ctl_nien(drive, 0);
 
 	if (stat & ERR_STAT) {
-		ide_dump_status(drive, "tcq_start", stat);
+		ide_dump_status(drive, rq, "tcq_start", stat);
 		return ide_stopped;
 	}
 
@@ -576,13 +582,13 @@
 	 */
 	if ((feat = GET_FEAT()) & NSEC_REL) {
 		drive->immed_rel++;
-		HWGROUP(drive)->rq = NULL;
+		drive->rq = NULL;
 		set_irq(drive, ide_dmaq_intr);
 
 		TCQ_PRINTK("REL in queued_start\n");
 
 		if ((stat = GET_STAT()) & SERVICE_STAT)
-			return service(drive);
+			return service(drive, rq);
 
 		return ide_released;
 	}
diff -Nru a/drivers/ide/umc8672.c b/drivers/ide/umc8672.c
--- a/drivers/ide/umc8672.c	Sat May 18 00:46:09 2002
+++ b/drivers/ide/umc8672.c	Sat May 18 00:46:09 2002
@@ -21,7 +21,7 @@
  */
 
 /*
- * VLB Controller Support from 
+ * VLB Controller Support from
  * Wolfram Podien
  * Rohoefe 3
  * D28832 Achim
@@ -34,7 +34,7 @@
  * #define UMC_DRIVE0 11
  * in the beginning of the driver, which sets the speed of drive 0 to 11 (there
  * are some lines present). 0 - 11 are allowed speed values. These values are
- * the results from the DOS speed test program supplied from UMC. 11 is the 
+ * the results from the DOS speed test program supplied from UMC. 11 is the
  * highest speed (about PIO mode 3)
  */
 #define REALLY_SLOW_IO		/* some systems can safely undef this */
@@ -92,13 +92,11 @@
 	out_umc (0xd7,(speedtab[0][speeds[2]] | (speedtab[0][speeds[3]]<<4)));
 	out_umc (0xd6,(speedtab[0][speeds[0]] | (speedtab[0][speeds[1]]<<4)));
 	tmp = 0;
-	for (i = 3; i >= 0; i--)
-	{
+	for (i = 3; i >= 0; i--) {
 		tmp = (tmp << 2) | speedtab[1][speeds[i]];
 	}
 	out_umc (0xdc,tmp);
-	for (i = 0;i < 4; i++)
-	{
+	for (i = 0;i < 4; i++) {
 		out_umc (0xd0+i,speedtab[2][speeds[i]]);
 		out_umc (0xd8+i,speedtab[2][speeds[i]]);
 	}
@@ -108,10 +106,9 @@
 		speeds[0], speeds[1], speeds[2], speeds[3]);
 }
 
-static void tune_umc (ide_drive_t *drive, byte pio)
+static void tune_umc(struct ata_device *drive, byte pio)
 {
 	unsigned long flags;
-	ide_hwgroup_t *hwgroup = ide_hwifs[drive->channel->index ^ 1].hwgroup;
 
 	if (pio == 255)
 		pio = ata_timing_mode(drive, XFER_PIO | XFER_EPIO) - XFER_PIO_0;
@@ -121,16 +118,12 @@
 	printk("%s: setting umc8672 to PIO mode%d (speed %d)\n", drive->name, pio, pio_to_umc[pio]);
 	save_flags(flags);	/* all CPUs */
 	cli();			/* all CPUs */
-	if (hwgroup && hwgroup->handler != NULL) {
-		printk("umc8672: other interface is busy: exiting tune_umc()\n");
-	} else {
-		current_speeds[drive->name[2] - 'a'] = pio_to_umc[pio];
-		umc_set_speeds (current_speeds);
-	}
+	current_speeds[drive->name[2] - 'a'] = pio_to_umc[pio];
+	umc_set_speeds (current_speeds);
 	restore_flags(flags);	/* all CPUs */
 }
 
-void __init init_umc8672 (void)	/* called from ide.c */
+void __init init_umc8672(void)	/* called from ide.c */
 {
 	unsigned long flags;
 
diff -Nru a/drivers/isdn/capi/Makefile b/drivers/isdn/capi/Makefile
--- a/drivers/isdn/capi/Makefile	Sat May 18 00:46:09 2002
+++ b/drivers/isdn/capi/Makefile	Sat May 18 00:46:09 2002
@@ -6,11 +6,11 @@
 
 # Objects that export symbols.
 
-export-objs	:= kcapi.o capiutil.o capifs.o
+export-objs	:= kcapi.o capiutil.o capilib.o capifs.o
 
 # Multipart objects.
 
-kernelcapi-objs	:= kcapi.o capiutil.o
+kernelcapi-objs	:= kcapi.o capiutil.o capilib.o
 
 # Ordering constraints: kernelcapi.o first
 
diff -Nru a/drivers/isdn/capi/capilib.c b/drivers/isdn/capi/capilib.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/capi/capilib.c	Sat May 18 00:46:15 2002
@@ -0,0 +1,200 @@
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/isdn/capilli.h>
+
+#define DBG(format, arg...) do { \
+printk(KERN_DEBUG __FUNCTION__ ": " format "\n" , ## arg); \
+} while (0)
+
+struct capilib_msgidqueue {
+	struct capilib_msgidqueue *next;
+	u16 msgid;
+};
+
+struct capilib_ncci {
+	struct list_head list;
+	u16 applid;
+	u32 ncci;
+	u32 winsize;
+	int   nmsg;
+	struct capilib_msgidqueue *msgidqueue;
+	struct capilib_msgidqueue *msgidlast;
+	struct capilib_msgidqueue *msgidfree;
+	struct capilib_msgidqueue msgidpool[CAPI_MAXDATAWINDOW];
+};
+
+// ---------------------------------------------------------------------------
+// NCCI Handling
+
+static inline void mq_init(struct capilib_ncci * np)
+{
+	int i;
+	np->msgidqueue = 0;
+	np->msgidlast = 0;
+	np->nmsg = 0;
+	memset(np->msgidpool, 0, sizeof(np->msgidpool));
+	np->msgidfree = &np->msgidpool[0];
+	for (i = 1; i < np->winsize; i++) {
+		np->msgidpool[i].next = np->msgidfree;
+		np->msgidfree = &np->msgidpool[i];
+	}
+}
+
+static inline int mq_enqueue(struct capilib_ncci * np, u16 msgid)
+{
+	struct capilib_msgidqueue *mq;
+	if ((mq = np->msgidfree) == 0)
+		return 0;
+	np->msgidfree = mq->next;
+	mq->msgid = msgid;
+	mq->next = 0;
+	if (np->msgidlast)
+		np->msgidlast->next = mq;
+	np->msgidlast = mq;
+	if (!np->msgidqueue)
+		np->msgidqueue = mq;
+	np->nmsg++;
+	return 1;
+}
+
+static inline int mq_dequeue(struct capilib_ncci * np, u16 msgid)
+{
+	struct capilib_msgidqueue **pp;
+	for (pp = &np->msgidqueue; *pp; pp = &(*pp)->next) {
+		if ((*pp)->msgid == msgid) {
+			struct capilib_msgidqueue *mq = *pp;
+			*pp = mq->next;
+			if (mq == np->msgidlast)
+				np->msgidlast = 0;
+			mq->next = np->msgidfree;
+			np->msgidfree = mq;
+			np->nmsg--;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+void capilib_new_ncci(struct list_head *head, u16 applid, u32 ncci, u32 winsize)
+{
+	struct capilib_ncci *np;
+
+	np = kmalloc(sizeof(*np), GFP_ATOMIC);
+	if (!np) {
+		printk(KERN_WARNING "capilib_new_ncci: no memory.\n");
+		return;
+	}
+	if (winsize > CAPI_MAXDATAWINDOW) {
+		printk(KERN_ERR "capi_new_ncci: winsize %d too big\n",
+		       winsize);
+		winsize = CAPI_MAXDATAWINDOW;
+	}
+	np->applid = applid;
+	np->ncci = ncci;
+	np->winsize = winsize;
+	mq_init(np);
+	list_add_tail(&np->list, head);
+	DBG("kcapi: appl %d ncci 0x%x up", applid, ncci);
+}
+
+EXPORT_SYMBOL(capilib_new_ncci);
+
+void capilib_free_ncci(struct list_head *head, u16 applid, u32 ncci)
+{
+	struct list_head *l;
+	struct capilib_ncci *np;
+
+	list_for_each(l, head) {
+		np = list_entry(l, struct capilib_ncci, list);
+		if (np->applid != applid)
+			continue;
+		if (np->ncci != ncci)
+			continue;
+		printk(KERN_INFO "kcapi: appl %d ncci 0x%x down\n", applid, ncci);
+		list_del(&np->list);
+		kfree(np);
+		return;
+	}
+	printk(KERN_ERR "capilib_free_ncci: ncci 0x%x not found\n", ncci);
+}
+
+EXPORT_SYMBOL(capilib_free_ncci);
+
+void capilib_release_appl(struct list_head *head, u16 applid)
+{
+	struct list_head *l, *n;
+	struct capilib_ncci *np;
+
+	list_for_each_safe(l, n, head) {
+		np = list_entry(l, struct capilib_ncci, list);
+		if (np->applid != applid)
+			continue;
+		printk(KERN_INFO "kcapi: appl %d ncci 0x%x forced down\n", applid, np->ncci);
+		list_del(&np->list);
+		kfree(np);
+	}
+}
+
+EXPORT_SYMBOL(capilib_release_appl);
+
+void capilib_release(struct list_head *head)
+{
+	struct list_head *l, *n;
+	struct capilib_ncci *np;
+
+	list_for_each_safe(l, n, head) {
+		np = list_entry(l, struct capilib_ncci, list);
+		printk(KERN_INFO "kcapi: appl %d ncci 0x%x forced down\n", np->applid, np->ncci);
+		list_del(&np->list);
+		kfree(np);
+	}
+}
+
+EXPORT_SYMBOL(capilib_release);
+
+u16 capilib_data_b3_req(struct list_head *head, u16 applid, u32 ncci, u16 msgid)
+{
+	struct list_head *l;
+	struct capilib_ncci *np;
+
+	list_for_each(l, head) {
+		np = list_entry(l, struct capilib_ncci, list);
+		if (np->applid != applid)
+			continue;
+		if (np->ncci != ncci)
+			continue;
+		
+		if (mq_enqueue(np, msgid) == 0)
+			return CAPI_SENDQUEUEFULL;
+
+		return CAPI_NOERROR;
+	}
+	printk(KERN_ERR "capilib_data_b3_req: ncci 0x%x not found\n", ncci);
+	return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capilib_data_b3_req);
+
+void capilib_data_b3_conf(struct list_head *head, u16 applid, u32 ncci, u16 msgid)
+{
+	struct list_head *l;
+	struct capilib_ncci *np;
+
+	list_for_each(l, head) {
+		np = list_entry(l, struct capilib_ncci, list);
+		if (np->applid != applid)
+			continue;
+		if (np->ncci != ncci)
+			continue;
+		
+		if (mq_dequeue(np, msgid) == 0) {
+			printk(KERN_ERR "kcapi: msgid %hu ncci 0x%x not on queue\n",
+			       msgid, ncci);
+		}
+		return;
+	}
+	printk(KERN_ERR "capilib_data_b3_conf: ncci 0x%x not found\n", ncci);
+}
+
+EXPORT_SYMBOL(capilib_data_b3_conf);
diff -Nru a/drivers/isdn/capi/kcapi.c b/drivers/isdn/capi/kcapi.c
--- a/drivers/isdn/capi/kcapi.c	Sat May 18 00:46:14 2002
+++ b/drivers/isdn/capi/kcapi.c	Sat May 18 00:46:14 2002
@@ -55,23 +55,6 @@
 
 /* ------------------------------------------------------------- */
 
-struct msgidqueue {
-	struct msgidqueue *next;
-	u16 msgid;
-};
-
-struct capi_ncci {
-	struct capi_ncci *next;
-	u16 applid;
-	u32 ncci;
-	u32 winsize;
-	int   nmsg;
-	struct msgidqueue *msgidqueue;
-	struct msgidqueue *msgidlast;
-	struct msgidqueue *msgidfree;
-	struct msgidqueue msgidpool[CAPI_MAXDATAWINDOW];
-};
-
 struct capi_appl {
 	u16 applid;
 	capi_register_params rparam;
@@ -246,45 +229,6 @@
 }
 
 /*
- * /proc/capi/ncci:
- *	applid ncci winsize nblk
- */
-static int proc_ncci_read_proc(char *page, char **start, off_t off,
-                                       int count, int *eof, void *data)
-{
-	struct capi_appl *ap;
-	struct capi_ncci *np;
-	int i;
-	int len = 0;
-
-	for (i=1; i <= CAPI_MAXAPPL; i++) {
-		ap = get_capi_appl_by_nr(i);
-		if (!ap) continue;
-		for (np = ap->nccilist; np; np = np->next) {
-			len += sprintf(page+len, "%d 0x%x %d %d\n",
-				np->applid,
-				np->ncci,
-				np->winsize,
-				np->nmsg);
-			if (len <= off) {
-				off -= len;
-				len = 0;
-			} else {
-				if (len-off > count)
-					goto endloop;
-			}
-		}
-	}
-endloop:
-	*start = page+off;
-	if (len < count)
-		*eof = 1;
-	if (len>count) len = count;
-	if (len<0) len = 0;
-	return len;
-}
-
-/*
  * /proc/capi/driver:
  *	driver ncontroller
  */
@@ -477,7 +421,6 @@
 } procfsentries[] = {
    { "capi",		  S_IFDIR, 0 },
    { "capi/applications", 0	 , proc_applications_read_proc },
-   { "capi/ncci", 	  0	 , proc_ncci_read_proc },
    { "capi/driver",       0	 , proc_driver_read_proc },
    { "capi/users", 	  0	 , proc_users_read_proc },
    { "capi/controller",   0	 , proc_controller_read_proc },
@@ -525,26 +468,9 @@
 
 static void release_appl(struct capi_ctr *card, u16 applid)
 {
-	struct capi_appl *ap = get_capi_appl_by_nr(applid);
-	struct capi_ncci **pp, **nextpp;
-	
-	DBG("");
+	DBG("applid %#x", applid);
 	
-	for (pp = &ap->nccilist; *pp; pp = nextpp) {
-		if (NCCI2CTRL((*pp)->ncci) == card->cnr) {
-			struct capi_ncci *np = *pp;
-			*pp = np->next;
-			printk(KERN_INFO "kcapi: appl %d ncci 0x%x down!\n", applid, np->ncci);
-			kfree(np);
-			ap->nncci--;
-			nextpp = pp;
-		} else {
-			nextpp = &(*pp)->next;
-		}
-	}
-
 	card->driver->release_appl(card, applid);
-
 	capi_ctr_put(card);
 }
 
@@ -679,130 +605,12 @@
 	MOD_DEC_USE_COUNT;
 }
 	
-/* -------- NCCI Handling ------------------------------------- */
-
-static inline void mq_init(struct capi_ncci * np)
-{
-	int i;
-	np->msgidqueue = 0;
-	np->msgidlast = 0;
-	np->nmsg = 0;
-	memset(np->msgidpool, 0, sizeof(np->msgidpool));
-	np->msgidfree = &np->msgidpool[0];
-	for (i = 1; i < np->winsize; i++) {
-		np->msgidpool[i].next = np->msgidfree;
-		np->msgidfree = &np->msgidpool[i];
-	}
-}
-
-static inline int mq_enqueue(struct capi_ncci * np, u16 msgid)
-{
-	struct msgidqueue *mq;
-	if ((mq = np->msgidfree) == 0)
-		return 0;
-	np->msgidfree = mq->next;
-	mq->msgid = msgid;
-	mq->next = 0;
-	if (np->msgidlast)
-		np->msgidlast->next = mq;
-	np->msgidlast = mq;
-	if (!np->msgidqueue)
-		np->msgidqueue = mq;
-	np->nmsg++;
-	return 1;
-}
-
-static inline int mq_dequeue(struct capi_ncci * np, u16 msgid)
-{
-	struct msgidqueue **pp;
-	for (pp = &np->msgidqueue; *pp; pp = &(*pp)->next) {
-		if ((*pp)->msgid == msgid) {
-			struct msgidqueue *mq = *pp;
-			*pp = mq->next;
-			if (mq == np->msgidlast)
-				np->msgidlast = 0;
-			mq->next = np->msgidfree;
-			np->msgidfree = mq;
-			np->nmsg--;
-			return 1;
-		}
-	}
-	return 0;
-}
-
-/*
- * ncci management
- */
-
-static void controllercb_new_ncci(struct capi_ctr * card,
-					u16 appl, u32 ncci, u32 winsize)
-{
-	struct capi_ncci *np;
-	struct capi_appl *ap = get_capi_appl_by_nr(appl);
-
-	if (!ap) {
-		printk(KERN_ERR "avmb1_handle_new_ncci: illegal appl %d\n", appl);
-		return;
-	}
-	if ((np = (struct capi_ncci *) kmalloc(sizeof(struct capi_ncci), GFP_ATOMIC)) == 0) {
-		printk(KERN_ERR "capi_new_ncci: alloc failed ncci 0x%x\n", ncci);
-		return;
-	}
-	if (winsize > CAPI_MAXDATAWINDOW) {
-		printk(KERN_ERR "capi_new_ncci: winsize %d too big, set to %d\n",
-		       winsize, CAPI_MAXDATAWINDOW);
-		winsize = CAPI_MAXDATAWINDOW;
-	}
-	np->applid = appl;
-	np->ncci = ncci;
-	np->winsize = winsize;
-	mq_init(np);
-	np->next = ap->nccilist;
-	ap->nccilist = np;
-	ap->nncci++;
-	printk(KERN_INFO "kcapi: appl %d ncci 0x%x up\n", appl, ncci);
-}
-
-static void controllercb_free_ncci(struct capi_ctr * card,
-				u16 appl, u32 ncci)
-{
-	struct capi_ncci **pp;
-	struct capi_appl *ap = get_capi_appl_by_nr(appl);
-	if (!ap) {
-		printk(KERN_ERR "free_ncci: illegal appl %d\n", appl);
-		return;
-	}
-	for (pp = &ap->nccilist; *pp; pp = &(*pp)->next) {
-		if ((*pp)->ncci == ncci) {
-			struct capi_ncci *np = *pp;
-			*pp = np->next;
-			kfree(np);
-			ap->nncci--;
-			printk(KERN_INFO "kcapi: appl %d ncci 0x%x down\n", appl, ncci);
-			return;
-		}
-	}
-	printk(KERN_ERR "free_ncci: ncci 0x%x not found\n", ncci);
-}
-
-
-static struct capi_ncci *find_ncci(struct capi_appl * app, u32 ncci)
-{
-	struct capi_ncci *np;
-	for (np = app->nccilist; np; np = np->next) {
-		if (np->ncci == ncci)
-			return np;
-	}
-	return 0;
-}
-
 /* -------- Receiver ------------------------------------------ */
 
 static void recv_handler(void *dummy)
 {
 	struct sk_buff *skb;
 	struct capi_appl *ap;
-	struct capi_ncci *np;
 
 	while ((skb = skb_dequeue(&recv_queue)) != 0) {
 		ap = get_capi_appl_by_nr(CAPIMSG_APPID(skb->data));
@@ -819,13 +627,6 @@
 			continue;
 		}
 		if (   CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3
-		    && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF
-	            && (np = find_ncci(ap, CAPIMSG_NCCI(skb->data))) != 0
-		    && mq_dequeue(np, CAPIMSG_MSGID(skb->data)) == 0) {
-			printk(KERN_ERR "kcapi: msgid %hu ncci 0x%x not on queue\n",
-				CAPIMSG_MSGID(skb->data), np->ncci);
-		}
-		if (   CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3
 		    && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) {
 			ap->nrecvdatapkt++;
 		} else {
@@ -917,22 +718,9 @@
 
 	for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
 		struct capi_appl *ap = get_capi_appl_by_nr(appl);
-		struct capi_ncci **pp, **nextpp;
-
 		if (!ap)
 			continue;
 
-		for (pp = &ap->nccilist; *pp; pp = nextpp) {
-			if (NCCI2CTRL((*pp)->ncci) == card->cnr) {
-				struct capi_ncci *np = *pp;
-				*pp = np->next;
-				printk(KERN_INFO "kcapi: appl %d ncci 0x%x forced down!\n", appl, np->ncci);
-				kfree(np);
-				nextpp = pp;
-			} else {
-				nextpp = &(*pp)->next;
-			}
-		}
 		capi_ctr_put(card);
 	}
 
@@ -993,8 +781,6 @@
         card->suspend_output = controllercb_suspend_output;
         card->resume_output = controllercb_resume_output;
         card->handle_capimsg = controllercb_handle_capimsg;
-        card->new_ncci = controllercb_new_ncci;
-        card->free_ncci = controllercb_free_ncci;
 
 	list_add_tail(&card->driver_list, &driver->contr_head);
 	driver->ncontroller++;
@@ -1185,7 +971,6 @@
 {
 	struct capi_ctr *card;
 	struct capi_appl *ap;
-	struct capi_ncci *np;
 	int showctl = 0;
 	u8 cmd, subcmd;
 
@@ -1213,9 +998,6 @@
         subcmd = CAPIMSG_SUBCOMMAND(skb->data);
 
 	if (cmd == CAPI_DATA_B3 && subcmd== CAPI_REQ) {
-	    	if ((np = find_ncci(ap, CAPIMSG_NCCI(skb->data))) != 0
-	            && mq_enqueue(np, CAPIMSG_MSGID(skb->data)) == 0)
-			return CAPI_SENDQUEUEFULL;
 		card->nsentdatapkt++;
 		ap->nsentdatapkt++;
 	        if (card->traceflag > 2) showctl |= 2;
@@ -1239,8 +1021,7 @@
 		}
 
 	}
-	card->driver->send_message(card, skb);
-	return CAPI_NOERROR;
+	return card->driver->send_message(card, skb);
 }
 
 static u16 capi_get_message(u16 applid, struct sk_buff **msgp)
@@ -1354,54 +1135,13 @@
 static int old_capi_manufacturer(unsigned int cmd, void *data)
 {
 	avmb1_loadandconfigdef ldef;
-	avmb1_extcarddef cdef;
 	avmb1_resetdef rdef;
 	avmb1_getdef gdef;
-	struct capi_driver *driver;
 	struct capi_ctr *card;
-	capicardparams cparams;
 	capiloaddata ldata;
 	int retval;
 
 	switch (cmd) {
-	case AVMB1_ADDCARD:
-	case AVMB1_ADDCARD_WITH_TYPE:
-		if (cmd == AVMB1_ADDCARD) {
-		   if ((retval = copy_from_user((void *) &cdef, data,
-					    sizeof(avmb1_carddef))))
-			   return retval;
-		   cdef.cardtype = AVM_CARDTYPE_B1;
-		} else {
-		   if ((retval = copy_from_user((void *) &cdef, data,
-					    sizeof(avmb1_extcarddef))))
-			   return retval;
-		}
-		cparams.port = cdef.port;
-		cparams.irq = cdef.irq;
-		cparams.cardnr = cdef.cardnr;
-
-                switch (cdef.cardtype) {
-			case AVM_CARDTYPE_B1:
-				driver = find_driver("b1isa");
-				break;
-			case AVM_CARDTYPE_T1:
-				driver = find_driver("t1isa");
-				break;
-			default:
-				driver = 0;
-				break;
-		}
-		if (!driver) {
-			printk(KERN_ERR "kcapi: driver not loaded.\n");
-			return -EIO;
-		}
-		if (!driver->add_card) {
-			printk(KERN_ERR "kcapi: driver has no add card function.\n");
-			return -EIO;
-		}
-
-		return driver->add_card(driver, &cparams);
-
 	case AVMB1_LOAD:
 	case AVMB1_LOAD_AND_CONFIG:
 
@@ -1417,6 +1157,7 @@
 				return retval;
 		}
 		card = get_capi_ctr_by_nr(ldef.contr);
+		card = capi_ctr_get(card);
 		if (!card)
 			return -ESRCH;
 		if (card->driver->load_firmware == 0) {
@@ -1450,6 +1191,7 @@
 
 		if (retval) {
 			card->cardstate = CARD_DETECTED;
+			capi_ctr_put(card);
 			return retval;
 		}
 
@@ -1458,9 +1200,12 @@
 			set_current_state(TASK_INTERRUPTIBLE);
 			schedule_timeout(HZ/10);	/* 0.1 sec */
 
-			if (signal_pending(current))
+			if (signal_pending(current)) {
+				capi_ctr_put(card);
 				return -EINTR;
+			}
 		}
+		capi_ctr_put(card);
 		return 0;
 
 	case AVMB1_RESETCARD:
@@ -1505,30 +1250,6 @@
 			return retval;
 
 		return 0;
-
-	case AVMB1_REMOVECARD:
-		if ((retval = copy_from_user((void *) &rdef, data,
-					 sizeof(avmb1_resetdef))))
-			return retval;
-
-		card = get_capi_ctr_by_nr(rdef.contr);
-		if (!card)
-			return -ESRCH;
-
-		if (card->cardstate != CARD_DETECTED)
-			return -EBUSY;
-
-		card->driver->remove_ctr(card);
-
-		while (cards[rdef.contr]) {
-
-			set_current_state(TASK_INTERRUPTIBLE);
-			schedule_timeout(HZ/10);	/* 0.1 sec */
-
-			if (signal_pending(current))
-				return -EINTR;
-		}
-		return 0;
 	}
 	return -EINVAL;
 }
@@ -1541,8 +1262,6 @@
 
 	switch (cmd) {
 #ifdef CONFIG_AVMB1_COMPAT
-	case AVMB1_ADDCARD:
-	case AVMB1_ADDCARD_WITH_TYPE:
 	case AVMB1_LOAD:
 	case AVMB1_LOAD_AND_CONFIG:
 	case AVMB1_RESETCARD:
@@ -1566,37 +1285,6 @@
 		printk(KERN_INFO "kcapi: contr %d set trace=%d\n",
 			card->cnr, card->traceflag);
 		return 0;
-	}
-
-	case KCAPI_CMD_ADDCARD:
-	{
-		struct capi_driver *driver;
-		capicardparams cparams;
-		kcapi_carddef cdef;
-
-		if ((retval = copy_from_user((void *) &cdef, data,
-							sizeof(cdef))))
-			return retval;
-
-		cparams.port = cdef.port;
-		cparams.irq = cdef.irq;
-		cparams.membase = cdef.membase;
-		cparams.cardnr = cdef.cardnr;
-		cparams.cardtype = 0;
-		cdef.driver[sizeof(cdef.driver)-1] = 0;
-
-		if ((driver = find_driver(cdef.driver)) == 0) {
-			printk(KERN_ERR "kcapi: driver \"%s\" not loaded.\n",
-					cdef.driver);
-			return -ESRCH;
-		}
-
-		if (!driver->add_card) {
-			printk(KERN_ERR "kcapi: driver \"%s\" has no add card function.\n", cdef.driver);
-			return -EIO;
-		}
-
-		return driver->add_card(driver, &cparams);
 	}
 
 	default:
diff -Nru a/drivers/isdn/hardware/avm/avmcard.h b/drivers/isdn/hardware/avm/avmcard.h
--- a/drivers/isdn/hardware/avm/avmcard.h	Sat May 18 00:46:09 2002
+++ b/drivers/isdn/hardware/avm/avmcard.h	Sat May 18 00:46:09 2002
@@ -69,6 +69,7 @@
 	struct avmcard  *card;
 	struct capi_ctr *capi_ctrl;
 	
+	struct list_head ncci_head;
 } avmctrl_info;
 
 typedef struct avmcard {
@@ -536,6 +537,7 @@
 }
 
 /* b1.c */
+void b1_set_revision(struct capi_driver *driver, char *rev);
 avmcard *b1_alloc_card(int nr_controllers);
 void b1_free_card(avmcard *card);
 int b1_detect(unsigned int base, enum avmcardtype cardtype);
@@ -549,7 +551,7 @@
 void b1_register_appl(struct capi_ctr *ctrl, u16 appl,
 				capi_register_params *rp);
 void b1_release_appl(struct capi_ctr *ctrl, u16 appl);
-void b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
+u16  b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
 void b1_parse_version(avmctrl_info *card);
 void b1_interrupt(int interrupt, void *devptr, struct pt_regs *regs);
 
@@ -560,7 +562,6 @@
 				   long rsize, long ssize);
 void avmcard_dma_free(avmcard_dmainfo *);
 
-
 /* b1dma.c */
 int b1pciv4_detect(avmcard *card);
 int t1pci_detect(avmcard *card);
@@ -574,7 +575,7 @@
 				u16 appl,
 				capi_register_params *rp);
 void b1dma_release_appl(struct capi_ctr *ctrl, u16 appl);
-void b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
+u16  b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
 int b1dmactl_read_proc(char *page, char **start, off_t off,
         		int count, int *eof, struct capi_ctr *ctrl);
 
diff -Nru a/drivers/isdn/hardware/avm/b1.c b/drivers/isdn/hardware/avm/b1.c
--- a/drivers/isdn/hardware/avm/b1.c	Sat May 18 00:46:09 2002
+++ b/drivers/isdn/hardware/avm/b1.c	Sat May 18 00:46:09 2002
@@ -59,6 +59,21 @@
 
 /* ------------------------------------------------------------- */	
 
+void b1_set_revision(struct capi_driver *driver, char *rev)
+{
+	char *p;
+
+	if ((p = strchr(rev, ':')) != 0 && p[1]) {
+		strncpy(driver->revision, p + 2, sizeof(driver->revision));
+		driver->revision[sizeof(driver->revision)-1] = 0;
+		if ((p = strchr(driver->revision, '$')) != 0 && p > driver->revision)
+			*(p-1) = 0;
+	}
+	printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision);
+}
+
+/* ------------------------------------------------------------- */	
+
 avmcard *b1_alloc_card(int nr_controllers)
 {
 	avmcard *card;
@@ -80,6 +95,7 @@
 
 	card->ctrlinfo = cinfo;
 	for (i = 0; i < nr_controllers; i++) {
+		INIT_LIST_HEAD(&cinfo[i].ncci_head);
 		cinfo[i].card = card;
 	}
 	spin_lock_init(&card->lock);
@@ -331,6 +347,7 @@
 	b1_reset(port);
 
 	memset(cinfo->version, 0, sizeof(cinfo->version));
+	capilib_release(&cinfo->ncci_head);
 	ctrl->reseted(ctrl);
 }
 
@@ -366,6 +383,8 @@
 	unsigned int port = card->port;
 	unsigned long flags;
 
+	capilib_release_appl(&cinfo->ncci_head, appl);
+
 	save_flags(flags);
 	cli();
 	b1_put_byte(port, SEND_RELEASE);
@@ -373,7 +392,7 @@
 	restore_flags(flags);
 }
 
-void b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+u16 b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
 {
 	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
 	avmcard *card = cinfo->card;
@@ -382,20 +401,36 @@
 	u16 len = CAPIMSG_LEN(skb->data);
 	u8 cmd = CAPIMSG_COMMAND(skb->data);
 	u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+	u16 dlen, retval;
 
-	save_flags(flags);
-	cli();
 	if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
-		u16 dlen = CAPIMSG_DATALEN(skb->data);
+		retval = capilib_data_b3_req(&cinfo->ncci_head,
+					     CAPIMSG_APPID(skb->data),
+					     CAPIMSG_NCCI(skb->data),
+					     CAPIMSG_MSGID(skb->data));
+		if (retval != CAPI_NOERROR) 
+			goto out;
+
+		dlen = CAPIMSG_DATALEN(skb->data);
+
+		save_flags(flags);
+		cli();
 		b1_put_byte(port, SEND_DATA_B3_REQ);
 		b1_put_slice(port, skb->data, len);
 		b1_put_slice(port, skb->data + len, dlen);
+		restore_flags(flags);
 	} else {
+		retval = CAPI_NOERROR;
+
+		save_flags(flags);
+		cli();
 		b1_put_byte(port, SEND_MESSAGE);
 		b1_put_slice(port, skb->data, len);
+		restore_flags(flags);
 	}
-	restore_flags(flags);
+ out:
 	dev_kfree_skb_any(skb);
+	return retval;
 }
 
 /* ------------------------------------------------------------- */
@@ -525,6 +560,11 @@
 					card->name);
 		} else {
 			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+			if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF)
+				capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+						     CAPIMSG_NCCI(skb->data),
+						     CAPIMSG_MSGID(skb->data));
+
 			ctrl->handle_capimsg(ctrl, ApplId, skb);
 		}
 		break;
@@ -535,7 +575,7 @@
 		NCCI = b1_get_word(card->port);
 		WindowSize = b1_get_word(card->port);
 
-		ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize);
+		capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize);
 
 		break;
 
@@ -545,8 +585,8 @@
 		NCCI = b1_get_word(card->port);
 
 		if (NCCI != 0xffffffff)
-			ctrl->free_ncci(ctrl, ApplId, NCCI);
-
+			capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI);
+	       
 		break;
 
 	case RECEIVE_START:
@@ -738,6 +778,7 @@
 
 EXPORT_SYMBOL(b1_irq_table);
 
+EXPORT_SYMBOL(b1_set_revision);
 EXPORT_SYMBOL(b1_alloc_card);
 EXPORT_SYMBOL(b1_free_card);
 EXPORT_SYMBOL(b1_detect);
diff -Nru a/drivers/isdn/hardware/avm/b1dma.c b/drivers/isdn/hardware/avm/b1dma.c
--- a/drivers/isdn/hardware/avm/b1dma.c	Sat May 18 00:46:14 2002
+++ b/drivers/isdn/hardware/avm/b1dma.c	Sat May 18 00:46:14 2002
@@ -488,6 +488,11 @@
 					card->name);
 		} else {
 			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+			if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF)
+				capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+						     CAPIMSG_NCCI(skb->data),
+						     CAPIMSG_MSGID(skb->data));
+
 			ctrl->handle_capimsg(ctrl, ApplId, skb);
 		}
 		break;
@@ -498,7 +503,7 @@
 		NCCI = _get_word(&p);
 		WindowSize = _get_word(&p);
 
-		ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize);
+		capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize);
 
 		break;
 
@@ -508,7 +513,7 @@
 		NCCI = _get_word(&p);
 
 		if (NCCI != 0xffffffff)
-			ctrl->free_ncci(ctrl, ApplId, NCCI);
+			capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI);
 
 		break;
 
@@ -734,6 +739,7 @@
  	b1dma_reset(card);
 
 	memset(cinfo->version, 0, sizeof(cinfo->version));
+	capilib_release(&cinfo->ncci_head);
 	ctrl->reseted(ctrl);
 }
 
@@ -785,6 +791,8 @@
 	struct sk_buff *skb;
 	void *p;
 
+	capilib_release_appl(&cinfo->ncci_head, appl);
+
 	skb = alloc_skb(7, GFP_ATOMIC);
 	if (!skb) {
 		printk(KERN_CRIT "%s: no memory, lost release appl.\n",
@@ -804,12 +812,24 @@
 
 /* ------------------------------------------------------------- */
 
-void b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+u16 b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
 {
 	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
 	avmcard *card = cinfo->card;
+	u16 retval = CAPI_NOERROR;
 
-	b1dma_queue_tx(card, skb);
+ 	if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) {
+		retval = capilib_data_b3_req(&cinfo->ncci_head,
+					     CAPIMSG_APPID(skb->data),
+					     CAPIMSG_NCCI(skb->data),
+					     CAPIMSG_MSGID(skb->data));
+	}
+	if (retval == CAPI_NOERROR) 
+		b1dma_queue_tx(card, skb);
+	else
+		dev_kfree_skb_any(skb);
+
+	return retval;
 }
 
 /* ------------------------------------------------------------- */
diff -Nru a/drivers/isdn/hardware/avm/b1isa.c b/drivers/isdn/hardware/avm/b1isa.c
--- a/drivers/isdn/hardware/avm/b1isa.c	Sat May 18 00:46:09 2002
+++ b/drivers/isdn/hardware/avm/b1isa.c	Sat May 18 00:46:09 2002
@@ -18,6 +18,7 @@
 #include <linux/ioport.h>
 #include <linux/capi.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <asm/io.h>
 #include <linux/isdn/capicmd.h>
 #include <linux/isdn/capiutil.h>
@@ -34,33 +35,31 @@
 
 /* ------------------------------------------------------------- */
 
-static void b1isa_remove_ctr(struct capi_ctr *ctrl)
+static struct capi_driver b1isa_driver;
+
+static void b1isa_remove(struct pci_dev *pdev)
 {
-	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmctrl_info *cinfo = pci_get_drvdata(pdev);
 	avmcard *card = cinfo->card;
-	unsigned int port = card->port;
+	unsigned int port = cinfo->card->port;
 
 	b1_reset(port);
 	b1_reset(port);
 
-	detach_capi_ctr(ctrl);
+	detach_capi_ctr(cinfo->capi_ctrl);
 	free_irq(card->irq, card);
 	release_region(card->port, AVMB1_PORTLEN);
 	b1_free_card(card);
-
-	MOD_DEC_USE_COUNT;
 }
 
 /* ------------------------------------------------------------- */
 
-static int b1isa_add_card(struct capi_driver *driver, struct capicardparams *p)
+static int __init b1isa_probe(struct pci_dev *pdev)
 {
 	avmctrl_info *cinfo;
 	avmcard *card;
 	int retval;
 
-	MOD_INC_USE_COUNT;
-
 	card = b1_alloc_card(1);
 	if (!card) {
 		printk(KERN_WARNING "b1isa: no memory.\n");
@@ -70,10 +69,10 @@
 
 	cinfo = card->ctrlinfo;
 
-	sprintf(card->name, "b1isa-%x", p->port);
-	card->port = p->port;
-	card->irq = p->irq;
+	card->port = pci_resource_start(pdev, 0);
+	card->irq = pdev->irq;
 	card->cardtype = avm_b1isa;
+	sprintf(card->name, "b1isa-%x", card->port);
 
 	if (   card->port != 0x150 && card->port != 0x250
 	    && card->port != 0x300 && card->port != 0x340) {
@@ -107,7 +106,7 @@
 	b1_reset(card->port);
 	b1_getrevision(card);
 
-	cinfo->capi_ctrl = attach_capi_ctr(driver, card->name, cinfo);
+	cinfo->capi_ctrl = attach_capi_ctr(&b1isa_driver, card->name, cinfo);
 	if (!cinfo->capi_ctrl) {
 		printk(KERN_ERR "b1isa: attach controller failed.\n");
 		retval = -EBUSY;
@@ -116,8 +115,9 @@
 
 	printk(KERN_INFO
 		"%s: AVM B1 ISA at i/o %#x, irq %d, revision %d\n",
-		driver->name, card->port, card->irq, card->revision);
+		b1isa_driver.name, card->port, card->irq, card->revision);
 
+	pci_set_drvdata(pdev, cinfo);
 	return 0;
 
  err_free_irq:
@@ -127,7 +127,6 @@
  err_free:
 	b1_free_card(card);
  err:
-	MOD_DEC_USE_COUNT;
 	return retval;
 }
 
@@ -155,7 +154,6 @@
 	revision: "0.0",
 	load_firmware: b1_load_firmware,
 	reset_ctr: b1_reset_ctr,
-	remove_ctr: b1isa_remove_ctr,
 	register_appl: b1_register_appl,
 	release_appl: b1_release_appl,
 	send_message: b1_send_message,
@@ -163,34 +161,63 @@
 	procinfo: b1isa_procinfo,
 	ctr_read_proc: b1ctl_read_proc,
 	driver_read_proc: 0,	/* use standard driver_read_proc */
-	
-	add_card: b1isa_add_card,
 };
 
+#define MAX_CARDS 4
+static struct pci_dev isa_dev[MAX_CARDS];
+static int io[MAX_CARDS];
+static int irq[MAX_CARDS];
+
+MODULE_PARM(io, "1-" __MODULE_STRING(MAX_CARDS) "i");
+MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_CARDS) "i");
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
+
 static int __init b1isa_init(void)
 {
-	struct capi_driver *driver = &b1isa_driver;
-	char *p;
+	int i, retval;
+	int found = 0;
 
 	MOD_INC_USE_COUNT;
 
-	if ((p = strchr(revision, ':')) != 0 && p[1]) {
-		strncpy(driver->revision, p + 2, sizeof(driver->revision));
-		driver->revision[sizeof(driver->revision)-1] = 0;
-		if ((p = strchr(driver->revision, '$')) != 0 && p > driver->revision)
-			*(p-1) = 0;
-	}
+	b1_set_revision(&b1isa_driver, revision);
+        attach_capi_driver(&b1isa_driver);
+
+	for (i = 0; i < MAX_CARDS; i++) {
+		if (!io[i])
+			break;
 
-	printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision);
+		isa_dev[i].resource[0].start = io[i];
+		isa_dev[i].irq_resource[0].start = irq[i];
 
-        attach_capi_driver(driver);
+		if (b1isa_probe(&isa_dev[i]) == 0)
+			found++;
+	}
+	if (found == 0) {
+		retval = -ENODEV;
+		goto err;
+	}
+	retval = 0;
+	goto out;
+
+ err:
+	detach_capi_driver(&b1isa_driver);
+ out:
 	MOD_DEC_USE_COUNT;
-	return 0;
+	return retval;
 }
 
 static void __exit b1isa_exit(void)
 {
-    detach_capi_driver(&b1isa_driver);
+	int i;
+
+	for (i = 0; i < MAX_CARDS; i++) {
+		if (!io[i])
+			break;
+
+		b1isa_remove(&isa_dev[i]);
+	}
+	detach_capi_driver(&b1isa_driver);
 }
 
 module_init(b1isa_init);
diff -Nru a/drivers/isdn/hardware/avm/b1pci.c b/drivers/isdn/hardware/avm/b1pci.c
--- a/drivers/isdn/hardware/avm/b1pci.c	Sat May 18 00:46:08 2002
+++ b/drivers/isdn/hardware/avm/b1pci.c	Sat May 18 00:46:08 2002
@@ -60,16 +60,14 @@
 
 /* ------------------------------------------------------------- */
 
-static int b1pci_add_card(struct capi_driver *driver,
-                          struct capicardparams *p,
-			  struct pci_dev *dev)
+static int b1pci_probe(struct capi_driver *driver,
+		       struct capicardparams *p,
+		       struct pci_dev *pdev)
 {
 	avmcard *card;
 	avmctrl_info *cinfo;
 	int retval;
 
-	MOD_INC_USE_COUNT;
-
 	card = b1_alloc_card(1);
 	if (!card) {
 		printk(KERN_WARNING "%s: no memory.\n", driver->name);
@@ -127,6 +125,7 @@
 			driver->name, card->port, card->irq, card->revision);
 	}
 
+	pci_set_drvdata(pdev, card);
 	return 0;
 
  err_free_irq:
@@ -136,26 +135,22 @@
  err_free:
 	b1_free_card(card);
  err:
-	MOD_DEC_USE_COUNT;
 	return retval;
 }
 
-static void b1pci_remove_ctr(struct capi_ctr *ctrl)
+static void b1pci_remove(struct pci_dev *pdev)
 {
-	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-	avmcard *card = cinfo->card;
+	avmcard *card = pci_get_drvdata(pdev);
+	avmctrl_info *cinfo = card->ctrlinfo;
 	unsigned int port = card->port;
 
 	b1_reset(port);
 	b1_reset(port);
 
-	detach_capi_ctr(ctrl);
+	detach_capi_ctr(cinfo->capi_ctrl);
 	free_irq(card->irq, card);
 	release_region(card->port, AVMB1_PORTLEN);
-	ctrl->driverdata = 0;
 	b1_free_card(card);
-
-	MOD_DEC_USE_COUNT;
 }
 
 /* ------------------------------------------------------------- */
@@ -166,7 +161,6 @@
 	revision: "0.0",
 	load_firmware: b1_load_firmware,
 	reset_ctr: b1_reset_ctr,
-	remove_ctr: b1pci_remove_ctr,
 	register_appl: b1_register_appl,
 	release_appl: b1_release_appl,
 	send_message: b1_send_message,
@@ -174,8 +168,6 @@
 	procinfo: b1pci_procinfo,
 	ctr_read_proc: b1ctl_read_proc,
 	driver_read_proc: 0,	/* use standard driver_read_proc */
-	
-	add_card: 0, /* no add_card function */
 };
 
 #ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
@@ -200,16 +192,14 @@
 
 /* ------------------------------------------------------------- */
 
-static int b1pciv4_add_card(struct capi_driver *driver,
-                            struct capicardparams *p,
-			    struct pci_dev *dev)
+static int b1pciv4_probe(struct capi_driver *driver,
+			 struct capicardparams *p,
+			 struct pci_dev *pdev)
 {
 	avmcard *card;
 	avmctrl_info *cinfo;
 	int retval;
 
-	MOD_INC_USE_COUNT;
-
 	card = b1_alloc_card(1);
 	if (!card) {
 		printk(KERN_WARNING "%s: no memory.\n", driver->name);
@@ -217,7 +207,7 @@
 		goto err;
 	}
 
-        card->dma = avmcard_dma_alloc(driver->name, dev, 2048+128, 2048+128);
+        card->dma = avmcard_dma_alloc(driver->name, pdev, 2048+128, 2048+128);
 	if (!card->dma) {
 		printk(KERN_WARNING "%s: dma alloc.\n", driver->name);
 		retval = -ENOMEM;
@@ -280,6 +270,7 @@
 		driver->name, card->port, card->irq,
 		card->membase, card->revision);
 
+	pci_set_drvdata(pdev, card);
 	return 0;
 
  err_free_irq:
@@ -293,27 +284,23 @@
  err_free:
 	b1_free_card(card);
  err:
-	MOD_DEC_USE_COUNT;
 	return retval;
 
 }
 
-static void b1pciv4_remove_ctr(struct capi_ctr *ctrl)
+static void b1pciv4_remove(struct pci_dev *pdev)
 {
-	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-	avmcard *card = cinfo->card;
+	avmcard *card = pci_get_drvdata(pdev);
+	avmctrl_info *cinfo = card->ctrlinfo;
 
  	b1dma_reset(card);
 
-	detach_capi_ctr(ctrl);
+	detach_capi_ctr(cinfo->capi_ctrl);
 	free_irq(card->irq, card);
 	iounmap(card->mbase);
 	release_region(card->port, AVMB1_PORTLEN);
-	ctrl->driverdata = 0;
         avmcard_dma_free(card->dma);
 	b1_free_card(card);
-
-	MOD_DEC_USE_COUNT;
 }
 
 /* ------------------------------------------------------------- */
@@ -325,7 +312,6 @@
 	revision: "0.0",
 	load_firmware: b1dma_load_firmware,
 	reset_ctr: b1dma_reset_ctr,
-	remove_ctr: b1pciv4_remove_ctr,
 	register_appl: b1dma_register_appl,
 	release_appl: b1dma_release_appl,
 	send_message: b1dma_send_message,
@@ -333,42 +319,40 @@
 	procinfo: b1pciv4_procinfo,
 	ctr_read_proc: b1dmactl_read_proc,
 	driver_read_proc: 0,	/* use standard driver_read_proc */
-	
-	add_card: 0, /* no add_card function */
 };
 
 #endif /* CONFIG_ISDN_DRV_AVMB1_B1PCIV4 */
 
-static int __devinit b1pci_probe(struct pci_dev *dev,
-				 const struct pci_device_id *ent)
+static int __devinit b1pci_pci_probe(struct pci_dev *pdev,
+				     const struct pci_device_id *ent)
 {
 	struct capi_driver *driver = &b1pci_driver;
 	struct capicardparams param;
 	int retval;
 
-	if (pci_enable_device(dev) < 0) {
+	if (pci_enable_device(pdev) < 0) {
 		printk(KERN_ERR "%s: failed to enable AVM-B1\n",
 		       driver->name);
 		return -ENODEV;
 	}
-	param.irq = dev->irq;
+	param.irq = pdev->irq;
 
-	if (pci_resource_start(dev, 2)) { /* B1 PCI V4 */
+	if (pci_resource_start(pdev, 2)) { /* B1 PCI V4 */
 #ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
 		driver = &b1pciv4_driver;
 
-		pci_set_master(dev);
+		pci_set_master(pdev);
 #endif
-		param.membase = pci_resource_start(dev, 0);
-		param.port = pci_resource_start(dev, 2);
+		param.membase = pci_resource_start(pdev, 0);
+		param.port = pci_resource_start(pdev, 2);
 
 		printk(KERN_INFO
 		"%s: PCI BIOS reports AVM-B1 V4 at i/o %#x, irq %d, mem %#x\n",
 		driver->name, param.port, param.irq, param.membase);
 #ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
-		retval = b1pciv4_add_card(driver, &param, dev);
+		retval = b1pciv4_probe(driver, &param, pdev);
 #else
-		retval = b1pci_add_card(driver, &param, dev);
+		retval = b1pci_probe(driver, &param, pdev);
 #endif
 		if (retval != 0) {
 		        printk(KERN_ERR
@@ -377,12 +361,12 @@
 		}
 	} else {
 		param.membase = 0;
-		param.port = pci_resource_start(dev, 1);
+		param.port = pci_resource_start(pdev, 1);
 
 		printk(KERN_INFO
 		"%s: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n",
 		driver->name, param.port, param.irq);
-		retval = b1pci_add_card(driver, &param, dev);
+		retval = b1pci_probe(driver, &param, pdev);
 		if (retval != 0) {
 		        printk(KERN_ERR
 			"%s: no AVM-B1 at i/o %#x, irq %d detected\n",
@@ -392,68 +376,60 @@
 	return retval;
 }
 
+static void __devexit b1pci_pci_remove(struct pci_dev *pdev)
+{
+	avmcard *card = pci_get_drvdata(pdev);
+
+	if (card->dma)
+		b1pciv4_remove(pdev);
+	else
+		b1pci_remove(pdev);
+}
+
 static struct pci_driver b1pci_pci_driver = {
 	name:		"b1pci",
 	id_table:	b1pci_pci_tbl,
-	probe:		b1pci_probe,
+	probe:		b1pci_pci_probe,
+	remove:		__devexit_p(b1pci_pci_remove),
 };
 
 static int __init b1pci_init(void)
 {
-	struct capi_driver *driver = &b1pci_driver;
-#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
-	struct capi_driver *driverv4 = &b1pciv4_driver;
-#endif
-	char *p;
-	int ncards;
+	int retval;
 
 	MOD_INC_USE_COUNT;
 
-	if ((p = strchr(revision, ':')) != 0 && p[1]) {
-		strncpy(driver->revision, p + 2, sizeof(driver->revision));
-		driver->revision[sizeof(driver->revision)-1] = 0;
-		if ((p = strchr(driver->revision, '$')) != 0 && p > driver->revision)
-			*(p-1) = 0;
-	}
+	b1_set_revision(&b1pci_driver, revision);
+        attach_capi_driver(&b1pci_driver);
+
 #ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
-	if ((p = strchr(revision, ':')) != 0 && p[1]) {
-		strncpy(driverv4->revision, p + 2, sizeof(driverv4->revision));
-		driverv4->revision[sizeof(driverv4->revision)-1] = 0;
-		if ((p = strchr(driverv4->revision, '$')) != 0 && p > driverv4->revision)
-			*(p-1) = 0;
-	}
+	b1_set_revision(&b1pciv4_driver, revision);
+        attach_capi_driver(&b1pciv4_driver);
 #endif
 
-	printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision);
-
-        attach_capi_driver(driver);
-
-#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
-	printk(KERN_INFO "%s: revision %s\n", driverv4->name, driverv4->revision);
+	retval = pci_module_init(&b1pci_pci_driver);
+	if (retval < 0) 
+		goto err;
 
-        attach_capi_driver(driverv4);
-#endif
+	printk(KERN_INFO "%s: %d B1-PCI card(s) detected\n",
+	       b1pci_driver.name, retval);
+	retval = 0;
+	goto out;
 
-	ncards = pci_register_driver(&b1pci_pci_driver);
-	if (ncards) {
-		printk(KERN_INFO "%s: %d B1-PCI card(s) detected\n",
-				driver->name, ncards);
-		MOD_DEC_USE_COUNT;
-		return 0;
-	}
-	printk(KERN_ERR "%s: NO B1-PCI card detected\n", driver->name);
-	pci_unregister_driver(&b1pci_pci_driver);
-	detach_capi_driver(driver);
+ err:
+	detach_capi_driver(&b1pci_driver);
 #ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
-	detach_capi_driver(driverv4);
+	detach_capi_driver(&b1pciv4_driver);
 #endif
+ out:
 	MOD_DEC_USE_COUNT;
-	return -ENODEV;
+	return retval;
 }
 
 static void __exit b1pci_exit(void)
 {
 	pci_unregister_driver(&b1pci_pci_driver);
+
 	detach_capi_driver(&b1pci_driver);
 #ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
 	detach_capi_driver(&b1pciv4_driver);
diff -Nru a/drivers/isdn/hardware/avm/b1pcmcia.c b/drivers/isdn/hardware/avm/b1pcmcia.c
--- a/drivers/isdn/hardware/avm/b1pcmcia.c	Sat May 18 00:46:08 2002
+++ b/drivers/isdn/hardware/avm/b1pcmcia.c	Sat May 18 00:46:08 2002
@@ -47,8 +47,6 @@
 	detach_capi_ctr(ctrl);
 	free_irq(card->irq, card);
 	b1_free_card(card);
-
-	MOD_DEC_USE_COUNT;
 }
 
 /* ------------------------------------------------------------- */
@@ -63,8 +61,6 @@
 	char *cardname;
 	int retval;
 
-	MOD_INC_USE_COUNT;
-
 	card = b1_alloc_card(1);
 	if (!card) {
 		printk(KERN_WARNING "%s: no memory.\n", driver->name);
@@ -123,7 +119,6 @@
  err_free:
 	b1_free_card(card);
  err:
-	MOD_DEC_USE_COUNT;
 	return retval;
 }
 
@@ -153,7 +148,6 @@
 	revision: "0.0",
 	load_firmware: b1_load_firmware,
 	reset_ctr: b1_reset_ctr,
-	remove_ctr: b1pcmcia_remove_ctr,
 	register_appl: b1_register_appl,
 	release_appl: b1_release_appl,
 	send_message: b1_send_message,
@@ -161,8 +155,6 @@
 	procinfo: b1pcmcia_procinfo,
 	ctr_read_proc: b1ctl_read_proc,
 	driver_read_proc: 0,	/* use standard driver_read_proc */
-
-	add_card: 0,
 };
 
 /* ------------------------------------------------------------- */
@@ -208,30 +200,18 @@
 
 static int __init b1pcmcia_init(void)
 {
-	struct capi_driver *driver = &b1pcmcia_driver;
-	char *p;
-	int retval = 0;
-
 	MOD_INC_USE_COUNT;
 
-	if ((p = strchr(revision, ':')) != 0 && p[1]) {
-		strncpy(driver->revision, p + 2, sizeof(driver->revision));
-		driver->revision[sizeof(driver->revision)-1] = 0;
-		if ((p = strchr(driver->revision, '$')) != 0 && p > driver->revision)
-			*(p-1) = 0;
-	}
-
-	printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision);
-
-        attach_capi_driver(driver);
+	b1_set_revision(&b1pcmcia_driver, revision);
+        attach_capi_driver(&b1pcmcia_driver);
 
 	MOD_DEC_USE_COUNT;
-	return retval;
+	return 0;
 }
 
 static void __exit b1pcmcia_exit(void)
 {
-    detach_capi_driver(&b1pcmcia_driver);
+	detach_capi_driver(&b1pcmcia_driver);
 }
 
 module_init(b1pcmcia_init);
diff -Nru a/drivers/isdn/hardware/avm/c4.c b/drivers/isdn/hardware/avm/c4.c
--- a/drivers/isdn/hardware/avm/c4.c	Sat May 18 00:46:08 2002
+++ b/drivers/isdn/hardware/avm/c4.c	Sat May 18 00:46:08 2002
@@ -548,6 +548,7 @@
 		MsgLen = _get_slice(&p, card->msgbuf);
 		cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr;
 		if (cidx >= card->nlogcontr) cidx = 0;
+		cinfo = &card->ctrlinfo[cidx];
 		ctrl = card->ctrlinfo[cidx].capi_ctrl;
 
 		if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
@@ -555,6 +556,11 @@
 					card->name);
 		} else {
 			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+			if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF)
+				capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+						     CAPIMSG_NCCI(skb->data),
+						     CAPIMSG_MSGID(skb->data));
+
 			ctrl->handle_capimsg(ctrl, ApplId, skb);
 		}
 		break;
@@ -566,9 +572,8 @@
 		WindowSize = _get_word(&p);
 		cidx = (NCCI&0x7f) - card->cardnr;
 		if (cidx >= card->nlogcontr) cidx = 0;
-		ctrl = card->ctrlinfo[cidx].capi_ctrl;
 
-		ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize);
+		capilib_new_ncci(&card->ctrlinfo[cidx].ncci_head, ApplId, NCCI, WindowSize);
 
 		break;
 
@@ -580,9 +585,7 @@
 		if (NCCI != 0xffffffff) {
 			cidx = (NCCI&0x7f) - card->cardnr;
 			if (cidx >= card->nlogcontr) cidx = 0;
-			ctrl = card->ctrlinfo[cidx].capi_ctrl;
-			if (ctrl)
-				ctrl->free_ncci(ctrl, ApplId, NCCI);
+			capilib_free_ncci(&card->ctrlinfo[cidx].ncci_head, ApplId, NCCI);
 		}
 		break;
 
@@ -675,6 +678,7 @@
                 for (i=0; i < 4; i++) {
 			avmctrl_info *cinfo = &card->ctrlinfo[i];
 			memset(cinfo->version, 0, sizeof(cinfo->version));
+			capilib_release(&cinfo->ncci_head);
 			if (cinfo->capi_ctrl)
 				cinfo->capi_ctrl->reseted(cinfo->capi_ctrl);
 		}
@@ -897,9 +901,9 @@
 	card->nlogcontr = 0;
 }
 
-static void c4_remove_ctr(struct capi_ctr *ctrl)
+static void c4_remove(struct pci_dev *pdev)
 {
-	avmcard *card = ((avmctrl_info *)(ctrl->driverdata))->card;
+	avmcard *card = pci_get_drvdata(pdev);
 	avmctrl_info *cinfo;
 	int i;
 
@@ -916,11 +920,8 @@
 	free_irq(card->irq, card);
 	iounmap(card->mbase);
 	release_region(card->port, AVMB1_PORTLEN);
-	ctrl->driverdata = 0;
         avmcard_dma_free(card->dma);
 	b1_free_card(card);
-
-	MOD_DEC_USE_COUNT;
 }
 
 /* ------------------------------------------------------------- */
@@ -974,6 +975,8 @@
 	struct sk_buff *skb;
 	void *p;
 
+	capilib_release_appl(&cinfo->ncci_head, appl);
+
 	if (ctrl->cnr == card->cardnr) {
 		skb = alloc_skb(7, GFP_ATOMIC);
 		if (!skb) {
@@ -996,12 +999,25 @@
 /* ------------------------------------------------------------- */
 
 
-static void c4_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+static u16 c4_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
 {
 	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
 	avmcard *card = cinfo->card;
-	skb_queue_tail(&card->dma->send_queue, skb);
-	c4_dispatch_tx(card);
+	u16 retval = CAPI_NOERROR;
+
+ 	if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) {
+		retval = capilib_data_b3_req(&cinfo->ncci_head,
+					     CAPIMSG_APPID(skb->data),
+					     CAPIMSG_NCCI(skb->data),
+					     CAPIMSG_MSGID(skb->data));
+	}
+	if (retval == CAPI_NOERROR) {
+		skb_queue_tail(&card->dma->send_queue, skb);
+		c4_dispatch_tx(card);
+	} else {
+		dev_kfree_skb_any(skb);
+	}
+	return retval;
 }
 
 /* ------------------------------------------------------------- */
@@ -1102,8 +1118,6 @@
 	int retval;
 	int i;
 
-	MOD_INC_USE_COUNT;
-
 	card = b1_alloc_card(nr_controllers);
 	if (!card) {
 		printk(KERN_WARNING "%s: no memory.\n", driver->name);
@@ -1189,7 +1203,6 @@
  err_free:
 	b1_free_card(card);
  err:
-	MOD_DEC_USE_COUNT;
 	return retval;
 }
 
@@ -1201,7 +1214,6 @@
 	revision: "0.0",
 	load_firmware: c4_load_firmware,
 	reset_ctr: c4_reset_ctr,
-	remove_ctr: c4_remove_ctr,
 	register_appl: c4_register_appl,
 	release_appl: c4_release_appl,
 	send_message: c4_send_message,
@@ -1209,8 +1221,6 @@
 	procinfo: c4_procinfo,
 	ctr_read_proc: c4_read_proc,
 	driver_read_proc: 0,	/* use standard driver_read_proc */
-	
-	add_card: 0, /* no add_card function */
 };
 
 static struct capi_driver c4_driver = {
@@ -1219,7 +1229,6 @@
 	revision: "0.0",
 	load_firmware: c4_load_firmware,
 	reset_ctr: c4_reset_ctr,
-	remove_ctr: c4_remove_ctr,
 	register_appl: c4_register_appl,
 	release_appl: c4_release_appl,
 	send_message: c4_send_message,
@@ -1227,26 +1236,8 @@
 	procinfo: c4_procinfo,
 	ctr_read_proc: c4_read_proc,
 	driver_read_proc: 0,	/* use standard driver_read_proc */
-	
-	add_card: 0, /* no add_card function */
 };
 
-static int c4_attach_driver (struct capi_driver * driver)
-{
-	char *p;
-	if ((p = strchr(revision, ':')) != 0 && p[1]) {
-		strncpy(driver->revision, p + 2, sizeof(driver->revision));
-		driver->revision[sizeof(driver->revision)-1] = 0;
-		if ((p = strchr(driver->revision, '$')) != 0 && p > driver->revision)
-			*(p-1) = 0;
-	}
-
-	printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision);
-
-        attach_capi_driver(driver);
-	return 0;
-}
-
 static int __devinit c4_probe(struct pci_dev *dev,
 			      const struct pci_device_id *ent)
 {
@@ -1284,40 +1275,37 @@
        name:           "c4",
        id_table:       c4_pci_tbl,
        probe:          c4_probe,
+       remove:         c4_remove,
 };
 
 static int __init c4_init(void)
 {
 	int retval;
-	int ncards;
 
 	MOD_INC_USE_COUNT;
 
-	retval = c4_attach_driver (&c4_driver);
-	if (retval) {
-		MOD_DEC_USE_COUNT;
-		return retval;
-	}
+	b1_set_revision(&c2_driver, revision);
+        attach_capi_driver(&c2_driver);
 
-	retval = c4_attach_driver (&c2_driver);
-	if (retval) {
-		MOD_DEC_USE_COUNT;
-		return retval;
-	}
+	b1_set_revision(&c4_driver, revision);
+        attach_capi_driver(&c4_driver);
 
-	ncards = pci_register_driver(&c4_pci_driver);
-	if (ncards) {
-		printk(KERN_INFO "%s: %d C4/C2 card(s) detected\n",
-				c4_driver.name, ncards);
-		MOD_DEC_USE_COUNT;
-		return 0;
-	}
-	printk(KERN_ERR "%s: NO C4/C2 card detected\n", c4_driver.name);
-	pci_unregister_driver(&c4_pci_driver);
-	detach_capi_driver(&c4_driver);
+	retval = pci_module_init(&c4_pci_driver);
+	if (retval < 0)
+		goto err;
+
+	printk(KERN_INFO "%s: %d C4/C2 card(s) detected\n",
+	       c4_driver.name, retval);
+
+	retval = 0;
+	goto out;
+
+ err:
 	detach_capi_driver(&c2_driver);
+	detach_capi_driver(&c4_driver);
+ out:
 	MOD_DEC_USE_COUNT;
-	return -ENODEV;
+	return retval;
 }
 
 static void __exit c4_exit(void)
diff -Nru a/drivers/isdn/hardware/avm/t1isa.c b/drivers/isdn/hardware/avm/t1isa.c
--- a/drivers/isdn/hardware/avm/t1isa.c	Sat May 18 00:46:14 2002
+++ b/drivers/isdn/hardware/avm/t1isa.c	Sat May 18 00:46:14 2002
@@ -17,8 +17,10 @@
 #include <linux/interrupt.h>
 #include <linux/ioport.h>
 #include <linux/capi.h>
+#include <linux/netdevice.h>
 #include <linux/kernelcapi.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <asm/io.h>
 #include <linux/isdn/capicmd.h>
 #include <linux/isdn/capiutil.h>
@@ -35,6 +37,8 @@
 
 /* ------------------------------------------------------------- */
 
+static struct capi_driver t1isa_driver;
+
 static int hema_irq_table[16] =
 {0,
  0,
@@ -174,6 +178,11 @@
 						card->name);
 			} else {
 				memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+				if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3)
+					capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+							     CAPIMSG_NCCI(skb->data),
+							     CAPIMSG_MSGID(skb->data));
+
 				ctrl->handle_capimsg(ctrl, ApplId, skb);
 			}
 			break;
@@ -184,7 +193,7 @@
 			NCCI = b1_get_word(card->port);
 			WindowSize = b1_get_word(card->port);
 
-			ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize);
+			capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize);
 
 			break;
 
@@ -194,7 +203,7 @@
 			NCCI = b1_get_word(card->port);
 
 			if (NCCI != 0xffffffff)
-				ctrl->free_ncci(ctrl, ApplId, NCCI);
+				capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI);
 
 			break;
 
@@ -313,12 +322,13 @@
 	b1_reset(port);
 
 	memset(cinfo->version, 0, sizeof(cinfo->version));
+	capilib_release(&cinfo->ncci_head);
 	ctrl->reseted(ctrl);
 }
 
-static void t1isa_remove_ctr(struct capi_ctr *ctrl)
+static void t1isa_remove(struct pci_dev *pdev)
 {
-	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmctrl_info *cinfo = pci_get_drvdata(pdev);
 	avmcard *card = cinfo->card;
 	unsigned int port = card->port;
 
@@ -327,99 +337,82 @@
 	b1_reset(port);
 	t1_reset(port);
 
-	detach_capi_ctr(ctrl);
+	detach_capi_ctr(cinfo->capi_ctrl);
 	free_irq(card->irq, card);
 	release_region(card->port, AVMB1_PORTLEN);
 	b1_free_card(card);
-
-	MOD_DEC_USE_COUNT;
 }
 
 /* ------------------------------------------------------------- */
 
-static int t1isa_add_card(struct capi_driver *driver, struct capicardparams *p)
+static int __init t1isa_probe(struct pci_dev *pdev)
 {
-	struct capi_ctr *ctrl;
-	struct list_head *l;
 	avmctrl_info *cinfo;
 	avmcard *card;
 	int retval;
-
-	MOD_INC_USE_COUNT;
+	static int cardnr = 1;
 
 	card = b1_alloc_card(1);
 	if (!card) {
-		printk(KERN_WARNING "%s: no memory.\n", driver->name);
+		printk(KERN_WARNING "%s: no memory.\n", t1isa_driver.name);
 		retval = -ENOMEM;
 		goto err;
 	}
 
 	cinfo = card->ctrlinfo;
-	sprintf(card->name, "t1isa-%x", p->port);
-	card->port = p->port;
-	card->irq = p->irq;
+	card->port = pci_resource_start(pdev, 0);
+	card->irq = pdev->irq;
 	card->cardtype = avm_t1isa;
-	card->cardnr = p->cardnr;
+	card->cardnr = cardnr++;
+	sprintf(card->name, "t1isa-%x", card->port);
 
 	if (!(((card->port & 0x7) == 0) && ((card->port & 0x30) != 0x30))) {
 		printk(KERN_WARNING "%s: illegal port 0x%x.\n",
-				driver->name, card->port);
+		       t1isa_driver.name, card->port);
 		retval = -EINVAL;
 		goto err_free;
         }
 	if (hema_irq_table[card->irq & 0xf] == 0) {
 		printk(KERN_WARNING "%s: irq %d not valid.\n",
-		       driver->name, card->irq);
+		       t1isa_driver.name, card->irq);
 		retval = -EINVAL;
 		goto err_free;
 	}
-	list_for_each(l, &driver->contr_head) {
-	        avmcard *cardp;
-
-		ctrl = list_entry(l, struct capi_ctr, driver_list);
-		cardp = ((avmctrl_info *)(ctrl->driverdata))->card;
-		if (cardp->cardnr == card->cardnr) {
-			printk(KERN_WARNING "%s: card with number %d already installed at 0x%x.\n",
-					driver->name, card->cardnr, cardp->port);
-			retval = -EINVAL;
-			goto err_free;
-		}
-	}
 	if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
-		printk(KERN_WARNING "%s: ports 0x%03x-0x%03x in use.\n",
-		       driver->name, card->port, card->port + AVMB1_PORTLEN);
+		printk(KERN_INFO "%s: ports 0x%03x-0x%03x in use.\n",
+		       t1isa_driver.name, card->port, card->port + AVMB1_PORTLEN);
 		retval = -EBUSY;
 		goto err_free;
 	}
 	retval = request_irq(card->irq, t1isa_interrupt, 0, card->name, card);
 	if (retval) {
-		printk(KERN_ERR "%s: unable to get IRQ %d.\n",
-		       driver->name, card->irq);
+		printk(KERN_INFO "%s: unable to get IRQ %d.\n",
+		       t1isa_driver.name, card->irq);
 		retval = -EBUSY;
 		goto err_release_region;
 	}
 
         if ((retval = t1_detectandinit(card->port, card->irq, card->cardnr)) != 0) {
-		printk(KERN_NOTICE "%s: NO card at 0x%x (%d)\n",
-		       driver->name, card->port, retval);
+		printk(KERN_INFO "%s: NO card at 0x%x (%d)\n",
+		       t1isa_driver.name, card->port, retval);
 		retval = -ENODEV;
 		goto err_free_irq;
 	}
 	t1_disable_irq(card->port);
 	b1_reset(card->port);
 
-	cinfo->capi_ctrl = attach_capi_ctr(driver, card->name, cinfo);
+	cinfo->capi_ctrl = attach_capi_ctr(&t1isa_driver, card->name, cinfo);
 	if (!cinfo->capi_ctrl) {
-		printk(KERN_ERR "%s: attach controller failed.\n",
-				driver->name);
+		printk(KERN_INFO "%s: attach controller failed.\n",
+		       t1isa_driver.name);
 		retval = -EBUSY;
 		goto err_free_irq;
 	}
 
-	printk(KERN_INFO
-		"%s: AVM T1 ISA at i/o %#x, irq %d, card %d\n",
-		driver->name, card->port, card->irq, card->cardnr);
+	printk(KERN_INFO "%s: AVM T1 ISA at i/o %#x, irq %d, card %d\n",
+		t1isa_driver.name, card->port, card->irq, card->cardnr);
 
+	pci_set_drvdata(pdev, cinfo);
 	return 0;
 
  err_free_irq:
@@ -429,11 +422,10 @@
  err_free:
 	b1_free_card(card);
  err:
-	MOD_DEC_USE_COUNT;
 	return retval;
 }
 
-static void t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
 {
 	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
 	avmcard *card = cinfo->card;
@@ -442,20 +434,36 @@
 	u16 len = CAPIMSG_LEN(skb->data);
 	u8 cmd = CAPIMSG_COMMAND(skb->data);
 	u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+	u16 dlen, retval;
 
-	save_flags(flags);
-	cli();
 	if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
-		u16 dlen = CAPIMSG_DATALEN(skb->data);
+		retval = capilib_data_b3_req(&cinfo->ncci_head,
+					     CAPIMSG_APPID(skb->data),
+					     CAPIMSG_NCCI(skb->data),
+					     CAPIMSG_MSGID(skb->data));
+		if (retval != CAPI_NOERROR) 
+			goto out;
+
+		dlen = CAPIMSG_DATALEN(skb->data);
+
+		save_flags(flags);
+		cli();
 		b1_put_byte(port, SEND_DATA_B3_REQ);
 		t1_put_slice(port, skb->data, len);
 		t1_put_slice(port, skb->data + len, dlen);
+		restore_flags(flags);
 	} else {
+		retval = CAPI_NOERROR;
+
+		save_flags(flags);
+		cli();
 		b1_put_byte(port, SEND_MESSAGE);
 		t1_put_slice(port, skb->data, len);
+		restore_flags(flags);
 	}
-	restore_flags(flags);
-	dev_kfree_skb(skb);
+ out:
+	dev_kfree_skb_any(skb);
+	return retval;
 }
 /* ------------------------------------------------------------- */
 
@@ -484,7 +492,6 @@
 	revision: "0.0",
 	load_firmware: t1isa_load_firmware,
 	reset_ctr: t1isa_reset_ctr,
-	remove_ctr: t1isa_remove_ctr,
 	register_appl: b1_register_appl,
 	release_appl: b1_release_appl,
 	send_message: t1isa_send_message,
@@ -492,36 +499,63 @@
 	procinfo: t1isa_procinfo,
 	ctr_read_proc: b1ctl_read_proc,
 	driver_read_proc: 0,	/* use standard driver_read_proc */
-	
-	add_card: t1isa_add_card,
 };
 
+#define MAX_CARDS 4
+static struct pci_dev isa_dev[MAX_CARDS];
+static int io[MAX_CARDS];
+static int irq[MAX_CARDS];
+
+MODULE_PARM(io, "1-" __MODULE_STRING(MAX_CARDS) "i");
+MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_CARDS) "i");
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
+
 static int __init t1isa_init(void)
 {
-	struct capi_driver *driver = &t1isa_driver;
-	char *p;
-	int retval = 0;
+	int i, retval;
+	int found = 0;
 
 	MOD_INC_USE_COUNT;
 
-	if ((p = strchr(revision, ':')) != 0 && p[1]) {
-		strncpy(driver->revision, p + 2, sizeof(driver->revision));
-		driver->revision[sizeof(driver->revision)-1] = 0;
-		if ((p = strchr(driver->revision, '$')) != 0 && p > driver->revision)
-			*(p-1) = 0;
-	}
+	b1_set_revision(&t1isa_driver, revision);
+        attach_capi_driver(&t1isa_driver);
 
-	printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision);
+	for (i = 0; i < MAX_CARDS; i++) {
+		if (!io[i])
+			break;
+
+		isa_dev[i].resource[0].start = io[i];
+		isa_dev[i].irq_resource[0].start = irq[i];
 
-        attach_capi_driver(driver);
+		if (t1isa_probe(&isa_dev[i]) == 0)
+			found++;
+	}
+	if (found == 0) {
+		retval = -ENODEV;
+		goto err;
+	}
+	retval = 0;
+	goto out;
 
+ err:
+	detach_capi_driver(&t1isa_driver);
+ out:
 	MOD_DEC_USE_COUNT;
 	return retval;
 }
 
 static void __exit t1isa_exit(void)
 {
-    detach_capi_driver(&t1isa_driver);
+	int i;
+
+	for (i = 0; i < MAX_CARDS; i++) {
+		if (!io[i])
+			break;
+
+		t1isa_remove(&isa_dev[i]);
+	}
+	detach_capi_driver(&t1isa_driver);
 }
 
 module_init(t1isa_init);
diff -Nru a/drivers/isdn/hardware/avm/t1pci.c b/drivers/isdn/hardware/avm/t1pci.c
--- a/drivers/isdn/hardware/avm/t1pci.c	Sat May 18 00:46:08 2002
+++ b/drivers/isdn/hardware/avm/t1pci.c	Sat May 18 00:46:08 2002
@@ -47,14 +47,12 @@
 
 static int t1pci_add_card(struct capi_driver *driver,
                           struct capicardparams *p,
-	                  struct pci_dev *dev)
+	                  struct pci_dev *pdev)
 {
 	avmcard *card;
 	avmctrl_info *cinfo;
 	int retval;
 
-	MOD_INC_USE_COUNT;
-
 	card = b1_alloc_card(1);
 	if (!card) {
 		printk(KERN_WARNING "%s: no memory.\n", driver->name);
@@ -62,7 +60,7 @@
 		goto err;
 	}
 
-        card->dma = avmcard_dma_alloc(driver->name, dev, 2048+128, 2048+128);
+        card->dma = avmcard_dma_alloc(driver->name, pdev, 2048+128, 2048+128);
 	if (!card->dma) {
 		printk(KERN_WARNING "%s: no memory.\n", driver->name);
 		retval = -ENOMEM;
@@ -127,6 +125,7 @@
 		"%s: AVM T1 PCI at i/o %#x, irq %d, mem %#lx\n",
 		driver->name, card->port, card->irq, card->membase);
 
+	pci_set_drvdata(pdev, card);
 	return 0;
 
  err_free_irq:
@@ -140,28 +139,24 @@
  err_free:
 	b1_free_card(card);
  err:
-	MOD_DEC_USE_COUNT;
 	return retval;
 }
 
 /* ------------------------------------------------------------- */
 
-static void t1pci_remove_ctr(struct capi_ctr *ctrl)
+static void t1pci_remove(struct pci_dev *pdev)
 {
-	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
-	avmcard *card = cinfo->card;
+	avmcard *card = pci_get_drvdata(pdev);
+	avmctrl_info *cinfo = card->ctrlinfo;
 
  	b1dma_reset(card);
 
-	detach_capi_ctr(ctrl);
+	detach_capi_ctr(cinfo->capi_ctrl);
 	free_irq(card->irq, card);
 	iounmap(card->mbase);
 	release_region(card->port, AVMB1_PORTLEN);
-	ctrl->driverdata = 0;
 	avmcard_dma_free(card->dma);
 	b1_free_card(card);
-
-	MOD_DEC_USE_COUNT;
 }
 
 /* ------------------------------------------------------------- */
@@ -190,7 +185,6 @@
 	revision: "0.0",
 	load_firmware: b1dma_load_firmware,
 	reset_ctr: b1dma_reset_ctr,
-	remove_ctr: t1pci_remove_ctr,
 	register_appl: b1dma_register_appl,
 	release_appl: b1dma_release_appl,
 	send_message: b1dma_send_message,
@@ -198,8 +192,6 @@
 	procinfo: t1pci_procinfo,
 	ctr_read_proc: b1dmactl_read_proc,
 	driver_read_proc: 0,	/* use standard driver_read_proc */
-	
-	add_card: 0, /* no add_card function */
 };
 
 /* ------------------------------------------------------------- */
@@ -240,38 +232,32 @@
        name:           "t1pci",
        id_table:       t1pci_pci_tbl,
        probe:          t1pci_probe,
+       remove:         t1pci_remove,
 };
 
 static int __init t1pci_init(void)
 {
-	struct capi_driver *driver = &t1pci_driver;
-	char *p;
-	int ncards;
+	int retval;
 
 	MOD_INC_USE_COUNT;
 
-	if ((p = strchr(revision, ':')) != 0 && p[1]) {
-		strncpy(driver->revision, p + 2, sizeof(driver->revision) - 1);
-		if ((p = strchr(driver->revision, '$')) != 0 && p > driver->revision)
-			*(p-1) = 0;
-	}
+	b1_set_revision(&t1pci_driver, revision);
+        attach_capi_driver(&t1pci_driver);
 
-	printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision);
+	retval = pci_register_driver(&t1pci_pci_driver);
+	if (retval < 0)
+		goto err;
 
-        attach_capi_driver(&t1pci_driver);
+	printk(KERN_INFO "%s: %d T1-PCI card(s) detected\n",
+	       t1pci_driver.name, retval);
+	retval = 0;
+	goto out;
 
-	ncards = pci_register_driver(&t1pci_pci_driver);
-	if (ncards) {
-		printk(KERN_INFO "%s: %d T1-PCI card(s) detected\n",
-				driver->name, ncards);
-		MOD_DEC_USE_COUNT;
-		return 0;
-	}
-	printk(KERN_ERR "%s: NO T1-PCI card detected\n", driver->name);
-	pci_unregister_driver(&t1pci_pci_driver);
+ err:
 	detach_capi_driver(&t1pci_driver);
+ out:
 	MOD_DEC_USE_COUNT;
-	return -ENODEV;
+	return retval;
 }
 
 static void __exit t1pci_exit(void)
diff -Nru a/drivers/isdn/hysdn/hycapi.c b/drivers/isdn/hysdn/hycapi.c
--- a/drivers/isdn/hysdn/hycapi.c	Sat May 18 00:46:08 2002
+++ b/drivers/isdn/hysdn/hycapi.c	Sat May 18 00:46:08 2002
@@ -15,7 +15,7 @@
 #include <linux/signal.h>
 #include <linux/kernel.h>
 #include <linux/skbuff.h>
-
+#include <linux/netdevice.h>
 
 #define	VER_DRIVER	0
 #define	VER_CARDTYPE	1
@@ -62,9 +62,12 @@
 void 
 hycapi_reset_ctr(struct capi_ctr *ctrl)
 {
+	hycapictrl_info *cinfo = ctrl->driverdata;
+
 #ifdef HYCAPI_PRINTFNAMES
 	printk(KERN_NOTICE "HYCAPI hycapi_reset_ctr\n");
 #endif
+	capilib_release(&cinfo->ncci_head);
 	ctrl->reseted(ctrl);
 }
 
@@ -269,6 +272,9 @@
 	__u16 len;
 	__u8 _command = 0xa1, _subcommand = 0x80;
 	__u16 MessageNumber = 0x0000;
+
+	capilib_release_appl(&cinfo->ncci_head, appl);
+
 #ifdef HYCAPI_PRINTFNAMES
 	printk(KERN_NOTICE "hycapi_release_appl\n");
 #endif
@@ -373,12 +379,14 @@
 
 ***************************************************************/
 
-void hycapi_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+u16 hycapi_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
 {
 	__u16 appl_id;
 	int _len, _len2;
 	__u8 msghead[64];
-	
+	hycapictrl_info *cinfo = ctrl->driverdata;
+	u16 retval = CAPI_NOERROR;
+
 	appl_id = CAPIMSG_APPID(skb->data);
 	switch(_hycapi_appCheck(appl_id, ctrl->cnr))
 	{
@@ -392,12 +400,13 @@
 			break;
 		default:
 			printk(KERN_ERR "HYCAPI: Controller mixup!\n");
-			return;
+			retval = CAPI_ILLAPPNR;
+			goto out;
 	}
 	switch(CAPIMSG_CMD(skb->data)) {		
 		case CAPI_DISCONNECT_B3_RESP:
-			ctrl->free_ncci(ctrl, appl_id, 
-					CAPIMSG_NCCI(skb->data));
+			capilib_free_ncci(&cinfo->ncci_head, appl_id, 
+					  CAPIMSG_NCCI(skb->data));
 			break;
 		case CAPI_DATA_B3_REQ:
 			_len = CAPIMSG_LEN(skb->data);
@@ -407,6 +416,10 @@
 				memcpy(skb->data + _len2, msghead, 22);
 				skb_pull(skb, _len2);
 				CAPIMSG_SETLEN(skb->data, 22);
+				retval = capilib_data_b3_req(&cinfo->ncci_head,
+							     CAPIMSG_APPID(skb->data),
+							     CAPIMSG_NCCI(skb->data),
+							     CAPIMSG_MSGID(skb->data));
 			}
 			break;
 		case CAPI_LISTEN_REQ:
@@ -423,7 +436,13 @@
 		default:
 			break;
 	}
-	hycapi_sendmsg_internal(ctrl, skb);
+ out:
+	if (retval == CAPI_NOERROR)
+		hycapi_sendmsg_internal(ctrl, skb);
+	else 
+		dev_kfree_skb_any(skb);
+
+	return retval;
 }
 
 /*********************************************************************
@@ -575,8 +594,8 @@
 			switch(info)
 			{
 				case 0:
-					ctrl->new_ncci(ctrl, ApplId, CAPIMSG_NCCI(skb->data), 
-						       hycapi_applications[ApplId-1].rp.datablkcnt); 
+					capilib_new_ncci(&cinfo->ncci_head, ApplId, CAPIMSG_NCCI(skb->data), 
+							 hycapi_applications[ApplId-1].rp.datablkcnt); 
 					
 					break;
 				case 0x0001:
@@ -604,9 +623,14 @@
 			}
 			break;
 		case CAPI_CONNECT_B3_IND:
-			ctrl->new_ncci(ctrl, ApplId, 
-				       CAPIMSG_NCCI(skb->data), 
-				       hycapi_applications[ApplId-1].rp.datablkcnt);
+			capilib_new_ncci(&cinfo->ncci_head, ApplId, 
+					 CAPIMSG_NCCI(skb->data), 
+					 hycapi_applications[ApplId-1].rp.datablkcnt);
+			break;
+	        case CAPI_DATA_B3_CONF:
+			capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+					     CAPIMSG_NCCI(skb->data),
+					     CAPIMSG_MSGID(skb->data));
 			break;
 		default:
 			break;
@@ -669,7 +693,6 @@
 	revision: "0.0",
 	load_firmware: hycapi_load_firmware, 
 	reset_ctr: hycapi_reset_ctr,
-	remove_ctr: hycapi_remove_ctr,
 	register_appl: hycapi_register_appl,
 	release_appl: hycapi_release_appl,
 	send_message: hycapi_send_message,
@@ -677,8 +700,6 @@
 	procinfo: hycapi_procinfo,
 	ctr_read_proc: hycapi_read_proc,
 	driver_read_proc: 0,	/* use standard driver_read_proc */
-
-	add_card: 0, /* no add_card function */
 };
 
 
@@ -786,6 +807,7 @@
 		card->hyctrlinfo = cinfo;
 		cinfo->card = card;
 		spin_lock_init(&cinfo->lock);
+		INIT_LIST_HEAD(&cinfo->ncci_head);
 
 		switch (card->brdtype) {
 			case BD_PCCARD:  strcpy(cinfo->cardname,"HYSDN Hycard"); break;
diff -Nru a/drivers/isdn/hysdn/hysdn_defs.h b/drivers/isdn/hysdn/hysdn_defs.h
--- a/drivers/isdn/hysdn/hysdn_defs.h	Sat May 18 00:46:08 2002
+++ b/drivers/isdn/hysdn/hysdn_defs.h	Sat May 18 00:46:08 2002
@@ -213,6 +213,8 @@
 		int in_idx, out_idx;	/* indexes to buffer ring */
 		int sk_count;		/* number of buffers currently in ring */
 		struct sk_buff *tx_skb;	/* buffer for tx operation */
+	  
+		struct list_head ncci_head;
 	} *hyctrlinfo;
 #endif /* CONFIG_HYSDN_CAPI */
 } hysdn_card;
@@ -284,7 +286,7 @@
 extern void hycapi_register_appl(struct capi_ctr *, __u16 appl,
 				 capi_register_params *);
 extern void hycapi_release_appl(struct capi_ctr *, __u16 appl);
-extern void hycapi_send_message(struct capi_ctr *, struct sk_buff *skb);
+extern u16  hycapi_send_message(struct capi_ctr *, struct sk_buff *skb);
 extern char *hycapi_procinfo(struct capi_ctr *);
 extern int hycapi_read_proc(char *page, char **start, off_t off,
 			    int count, int *eof, struct capi_ctr *card);
diff -Nru a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile
--- a/drivers/macintosh/Makefile	Sat May 18 00:46:09 2002
+++ b/drivers/macintosh/Makefile	Sat May 18 00:46:09 2002
@@ -32,9 +32,11 @@
 obj-$(CONFIG_MAC_HID)		+= mac_hid.o
 obj-$(CONFIG_INPUT_ADBHID)	+= adbhid.o
 obj-$(CONFIG_PPC_RTC)		+= rtc.o
+obj-$(CONFIG_ANSLCD)		+= ans-lcd.o
 
 obj-$(CONFIG_ADB_PMU)		+= via-pmu.o
 obj-$(CONFIG_ADB_CUDA)		+= via-cuda.o
+obj-$(CONFIG_PMAC_APM_EMU)	+= apm_emu.o
 
 obj-$(CONFIG_ADB)		+= adb.o
 obj-$(CONFIG_ADB_KEYBOARD)	+= mac_keyb.o
diff -Nru a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c
--- a/drivers/macintosh/adb.c	Sat May 18 00:46:09 2002
+++ b/drivers/macintosh/adb.c	Sat May 18 00:46:09 2002
@@ -34,6 +34,7 @@
 #include <linux/wait.h>
 #include <linux/init.h>
 #include <linux/delay.h>
+#include <linux/completion.h>
 #include <asm/uaccess.h>
 #ifdef CONFIG_PPC
 #include <asm/prom.h>
@@ -77,8 +78,8 @@
 static int adb_got_sleep = 0;
 static int adb_inited = 0;
 static pid_t adb_probe_task_pid;
-static unsigned long adb_probe_task_flag;
-static wait_queue_head_t adb_probe_task_wq;
+static DECLARE_MUTEX(adb_probe_mutex);
+static struct completion adb_probe_task_comp;
 static int sleepy_trackpad;
 int __adb_probe_sync;
 
@@ -241,7 +242,8 @@
 	printk(KERN_INFO "adb: finished probe task...\n");
 	
 	adb_probe_task_pid = 0;
-	clear_bit(0, &adb_probe_task_flag);
+	up(&adb_probe_mutex);
+	
 	return 0;
 }
 
@@ -263,14 +265,8 @@
 		do_adb_reset_bus();
 		return 0;
 	}
-		
-	/* We need to get a lock on the probe thread */
-	while (test_and_set_bit(0, &adb_probe_task_flag))
-		schedule();
 
-	/* Just wait for PID to be 0 just in case (possible race) */
-	while (adb_probe_task_pid != 0)
-		schedule();
+	down(&adb_probe_mutex);
 
 	/* Create probe thread as a child of keventd */
 	if (current_is_keventd())
@@ -318,7 +314,7 @@
 		if (machine_is_compatible("AAPL,PowerBook1998") ||
 			machine_is_compatible("PowerBook1,1"))
 			sleepy_trackpad = 1;
-		init_waitqueue_head(&adb_probe_task_wq);
+		init_completion(&adb_probe_task_comp);
 		adbdev_init();
 		adb_reset_bus();
 	}
@@ -340,21 +336,20 @@
 	case PBOOK_SLEEP_REQUEST:
 		adb_got_sleep = 1;
 		/* We need to get a lock on the probe thread */
-		while (test_and_set_bit(0, &adb_probe_task_flag))
-			schedule();
-		/* Just wait for PID to be 0 just in case (possible race) */
-		while (adb_probe_task_pid != 0)
-			schedule();
+		down(&adb_probe_mutex);
+		/* Stop autopoll */
 		if (adb_controller->autopoll)
 			adb_controller->autopoll(0);
 		ret = notifier_call_chain(&adb_client_list, ADB_MSG_POWERDOWN, NULL);
-		if (ret & NOTIFY_STOP_MASK)
+		if (ret & NOTIFY_STOP_MASK) {
+			up(&adb_probe_mutex);
 			return PBOOK_SLEEP_REFUSE;
+		}
 		break;
 	case PBOOK_SLEEP_REJECT:
 		if (adb_got_sleep) {
 			adb_got_sleep = 0;
-			clear_bit(0, &adb_probe_task_flag);
+			up(&adb_probe_mutex);
 			adb_reset_bus();
 		}
 		break;
@@ -363,7 +358,7 @@
 		break;
 	case PBOOK_WAKE:
 		adb_got_sleep = 0;
-		clear_bit(0, &adb_probe_task_flag);
+		up(&adb_probe_mutex);
 		adb_reset_bus();
 		break;
 	}
@@ -435,9 +430,10 @@
 static void
 adb_probe_wakeup(struct adb_request *req)
 {
-	wake_up(&adb_probe_task_wq);
+	complete(&adb_probe_task_comp);
 }
 
+/* Static request used during probe */
 static struct adb_request adb_sreq;
 static unsigned long adb_sreq_lock; // Use semaphore ! */ 
 
@@ -484,20 +480,11 @@
 	if ((flags & ADBREQ_SYNC) &&
 	    (current->pid && adb_probe_task_pid &&
 	    adb_probe_task_pid == current->pid)) {
-		DECLARE_WAITQUEUE(wait, current);
 		req->done = adb_probe_wakeup;
-		add_wait_queue(&adb_probe_task_wq, &wait);
 		rc = adb_controller->send_request(req, 0);
 		if (rc || req->complete)
 			goto bail;
-		for (;;) {
-			set_current_state(TASK_UNINTERRUPTIBLE);
-			if (req->complete)
-				break;
-			schedule();
-		}
-		current->state = TASK_RUNNING;
-		remove_wait_queue(&adb_probe_task_wq, &wait);
+		wait_for_completion(&adb_probe_task_comp);
 		rc = 0;
 		goto bail;
 	}
@@ -652,7 +639,7 @@
 {
 	struct adbdev_state *state;
 
-	if (MINOR(inode->i_rdev) > 0 || adb_controller == NULL)
+	if (minor(inode->i_rdev) > 0 || adb_controller == NULL)
 		return -ENXIO;
 	state = kmalloc(sizeof(struct adbdev_state), GFP_KERNEL);
 	if (state == 0)
@@ -672,6 +659,7 @@
 	struct adbdev_state *state = file->private_data;
 	unsigned long flags;
 
+	lock_kernel();
 	if (state) {
 		file->private_data = NULL;
 		spin_lock_irqsave(&state->lock, flags);
@@ -684,6 +672,7 @@
 			spin_unlock_irqrestore(&state->lock, flags);
 		}
 	}
+	unlock_kernel();
 	return 0;
 }
 
@@ -705,17 +694,16 @@
 		return ret;
 
 	req = NULL;
+	spin_lock_irqsave(&state->lock, flags);
 	add_wait_queue(&state->wait_queue, &wait);
 	current->state = TASK_INTERRUPTIBLE;
 
 	for (;;) {
-		spin_lock_irqsave(&state->lock, flags);
 		req = state->completed;
 		if (req != NULL)
 			state->completed = req->next;
 		else if (atomic_read(&state->n_pending) == 0)
 			ret = -EIO;
-		spin_unlock_irqrestore(&state->lock, flags);
 		if (req != NULL || ret != 0)
 			break;
 		
@@ -727,12 +715,15 @@
 			ret = -ERESTARTSYS;
 			break;
 		}
+		spin_unlock_irqrestore(&state->lock, flags);
 		schedule();
+		spin_lock_irqsave(&state->lock, flags);
 	}
 
 	current->state = TASK_RUNNING;
 	remove_wait_queue(&state->wait_queue, &wait);
-
+	spin_unlock_irqrestore(&state->lock, flags);
+	
 	if (ret)
 		return ret;
 
@@ -755,6 +746,8 @@
 
 	if (count < 2 || count > sizeof(req->data))
 		return -EINVAL;
+	if (adb_controller == NULL)
+		return -ENXIO;
 	ret = verify_area(VERIFY_READ, buf, count);
 	if (ret)
 		return ret;
@@ -774,7 +767,10 @@
 		goto out;
 
 	atomic_inc(&state->n_pending);
-	if (adb_controller == NULL) return -ENXIO;
+
+	/* If a probe is in progress or we are sleeping, wait for it to complete */
+	down(&adb_probe_mutex);
+	up(&adb_probe_mutex);
 
 	/* Special case for ADB_BUSRESET request, all others are sent to
 	   the controller */
@@ -782,6 +778,8 @@
 		&&(req->data[1] == ADB_BUSRESET)) {
 		ret = do_adb_reset_bus();
 		atomic_dec(&state->n_pending);
+		if (ret == 0)
+			ret = count;
 		goto out;
 	} else {	
 		req->reply_expected = ((req->data[1] & 0xc) == 0xc);
diff -Nru a/drivers/macintosh/adbhid.c b/drivers/macintosh/adbhid.c
--- a/drivers/macintosh/adbhid.c	Sat May 18 00:46:10 2002
+++ b/drivers/macintosh/adbhid.c	Sat May 18 00:46:10 2002
@@ -96,6 +96,11 @@
 static struct adb_ids mouse_ids;
 static struct adb_ids buttons_ids;
 
+#ifdef CONFIG_PMAC_BACKLIGHT
+/* Exported to via-pmu.c */
+int disable_kernel_backlight = 0;
+#endif /* CONFIG_PMAC_BACKLIGHT */
+
 /* Kind of keyboard, see Apple technote 1152  */
 #define ADB_KEYBOARD_UNKNOWN	0
 #define ADB_KEYBOARD_ANSI	0x0100
@@ -273,43 +278,60 @@
 		break;
 	case 0x1f: /* Powerbook button device */
 	  {
+	  	int down = (data[1] == (data[1] & 0xf));
 #ifdef CONFIG_PMAC_BACKLIGHT
 		int backlight = get_backlight_level();
-
+#endif
 		/*
 		 * XXX: Where is the contrast control for the passive?
 		 *  -- Cort
 		 */
 
-		switch (data[1]) {
+		switch (data[1] & 0x0f) {
 		case 0x8:	/* mute */
+			input_report_key(&adbhid[id]->input, KEY_MUTE, down);
 			break;
 
-		case 0x7:	/* contrast decrease */
+		case 0x7:	/* volume decrease */
+			input_report_key(&adbhid[id]->input, KEY_VOLUMEDOWN, down);
 			break;
 
-		case 0x6:	/* contrast increase */
-			break;
+		case 0x6:	/* volume increase */
+			input_report_key(&adbhid[id]->input, KEY_VOLUMEUP, down);
+ 			break;
 
+		case 0xb:	/* eject */
+			input_report_key(&adbhid[id]->input, KEY_EJECTCD, down);
+			break;
 		case 0xa:	/* brightness decrease */
-			if (backlight < 0)
+#ifdef CONFIG_PMAC_BACKLIGHT
+			if (!disable_kernel_backlight) {
+				if (!down || backlight < 0)
+					break;
+				if (backlight > BACKLIGHT_OFF)
+					set_backlight_level(backlight-1);
+				else
+					set_backlight_level(BACKLIGHT_OFF);
 				break;
-			if (backlight > BACKLIGHT_OFF)
-				set_backlight_level(backlight-1);
-			else
-				set_backlight_level(BACKLIGHT_OFF);
+			}
+#endif /* CONFIG_PMAC_BACKLIGHT */
+			input_report_key(&adbhid[id]->input, KEY_BRIGHTNESSDOWN, down);
 			break;
-
 		case 0x9:	/* brightness increase */
-			if (backlight < 0)
+#ifdef CONFIG_PMAC_BACKLIGHT
+			if (!disable_kernel_backlight) {
+				if (!down || backlight < 0)
+					break;
+				if (backlight < BACKLIGHT_MAX)
+					set_backlight_level(backlight+1);
+				else 
+					set_backlight_level(BACKLIGHT_MAX);
 				break;
-			if (backlight < BACKLIGHT_MAX)
-				set_backlight_level(backlight+1);
-			else 
-				set_backlight_level(BACKLIGHT_MAX);
+			}
+#endif /* CONFIG_PMAC_BACKLIGHT */
+			input_report_key(&adbhid[id]->input, KEY_BRIGHTNESSUP, down);
 			break;
 		}
-#endif /* CONFIG_PMAC_BACKLIGHT */
 	  }
 	  break;
 	}
@@ -504,6 +526,13 @@
 		case 0x1f: /* Powerbook button device */
 			sprintf(adbhid[id]->name, "ADB Powerbook buttons on ID %d:%d.%02x",
 				id, default_id, original_handler_id);
+			adbhid[id]->input.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+			set_bit(KEY_MUTE, adbhid[id]->input.keybit);
+			set_bit(KEY_VOLUMEUP, adbhid[id]->input.keybit);
+			set_bit(KEY_VOLUMEDOWN, adbhid[id]->input.keybit);
+			set_bit(KEY_BRIGHTNESSUP, adbhid[id]->input.keybit);
+			set_bit(KEY_BRIGHTNESSDOWN, adbhid[id]->input.keybit);
+			set_bit(KEY_EJECTCD, adbhid[id]->input.keybit);
 			break;
 		}
 		if (adbhid[id]->name[0])
@@ -542,16 +571,38 @@
 }
 
 
+static u16
+adbhid_input_reregister(int id, int default_id, int org_handler_id,
+			int cur_handler_id, int mk)
+{
+	if (adbhid[id]) {
+		if (adbhid[id]->input.idproduct !=
+		    ((id << 12)|(default_id << 8)|org_handler_id)) {
+			adbhid_input_unregister(id);
+			adbhid_input_register(id, default_id, org_handler_id,
+				cur_handler_id, mk);
+		}
+	} else
+		adbhid_input_register(id, default_id, org_handler_id,
+			cur_handler_id, mk);
+	return 1<<id;
+}
+
+static void
+adbhid_input_devcleanup(u16 exist)
+{
+	int i;
+	for(i=1; i<16; i++)
+	    if (adbhid[i] && !(exist&(1<<i)))
+		adbhid_input_unregister(i);
+}
+ 
 static void
 adbhid_probe(void)
 {
 	struct adb_request req;
 	int i, default_id, org_handler_id, cur_handler_id;
-
-	for (i = 1; i < 16; i++) {
-		if (adbhid[i])
-			adbhid_input_unregister(i);
-	}
+	u16 reg = 0;
 
 	adb_register(ADB_MOUSE, 0, &mouse_ids, adbhid_mouse_input);
 	adb_register(ADB_KEYBOARD, 0, &keyboard_ids, adbhid_keyboard_input);
@@ -580,14 +631,14 @@
 			printk("ADB keyboard at %d, handler 1\n", id);
 
 		adb_get_infos(id, &default_id, &cur_handler_id);
-		adbhid_input_register(id, default_id, org_handler_id, cur_handler_id, 0);
+		reg |= adbhid_input_reregister(id, default_id, org_handler_id, cur_handler_id, 0);
 	}
 
 	for (i = 0; i < buttons_ids.nids; i++) {
 		int id = buttons_ids.id[i];
 
 		adb_get_infos(id, &default_id, &org_handler_id);
-		adbhid_input_register(id, default_id, org_handler_id, org_handler_id, 0);
+		reg |= adbhid_input_reregister(id, default_id, org_handler_id, org_handler_id, 0);
 	}
 
 	/* Try to switch all mice to handler 4, or 2 for three-button
@@ -676,9 +727,10 @@
 		printk("\n");
 
 		adb_get_infos(id, &default_id, &cur_handler_id);
-		adbhid_input_register(id, default_id, org_handler_id,
+		reg |= adbhid_input_reregister(id, default_id, org_handler_id,
 				      cur_handler_id, mouse_kind);
         }
+	adbhid_input_devcleanup(reg);
 }
 
 static void 
diff -Nru a/drivers/macintosh/ans-lcd.c b/drivers/macintosh/ans-lcd.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/macintosh/ans-lcd.c	Sat May 18 00:46:15 2002
@@ -0,0 +1,171 @@
+/*
+ * /dev/lcd driver for Apple Network Servers.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <asm/sections.h>
+#include <asm/prom.h>
+#include <asm/ans-lcd.h>
+#include <asm/io.h>
+
+#define ANSLCD_ADDR		0xf301c000
+#define ANSLCD_CTRL_IX 0x00
+#define ANSLCD_DATA_IX 0x10
+
+static unsigned long anslcd_short_delay = 80;
+static unsigned long anslcd_long_delay = 3280;
+static volatile unsigned char* anslcd_ptr;
+
+#undef DEBUG
+
+static void __pmac
+anslcd_write_byte_ctrl ( unsigned char c )
+{
+#ifdef DEBUG
+	printk(KERN_DEBUG "LCD: CTRL byte: %02x\n",c);
+#endif
+	out_8(anslcd_ptr + ANSLCD_CTRL_IX, c);
+	switch(c) {
+		case 1:
+		case 2:
+		case 3:
+			udelay(anslcd_long_delay); break;
+		default: udelay(anslcd_short_delay);
+	}
+}
+
+static void __pmac
+anslcd_write_byte_data ( unsigned char c )
+{
+	out_8(anslcd_ptr + ANSLCD_DATA_IX, c);
+	udelay(anslcd_short_delay);
+}
+
+static ssize_t __pmac
+anslcd_write( struct file * file, const char * buf, 
+				size_t count, loff_t *ppos )
+{
+	const char * p = buf;
+	int i;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "LCD: write\n");
+#endif
+
+	if ( verify_area(VERIFY_READ, buf, count) )
+		return -EFAULT;
+	for ( i = *ppos; count > 0; ++i, ++p, --count ) 
+	{
+		char c;
+		__get_user(c, p);
+		anslcd_write_byte_data( c );
+	}
+	*ppos = i;
+	return p - buf;
+}
+
+static int __pmac
+anslcd_ioctl( struct inode * inode, struct file * file,
+				unsigned int cmd, unsigned long arg )
+{
+	char ch, *temp;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "LCD: ioctl(%d,%d)\n",cmd,arg);
+#endif
+
+	switch ( cmd )
+	{
+	case ANSLCD_CLEAR:
+		anslcd_write_byte_ctrl ( 0x38 );
+		anslcd_write_byte_ctrl ( 0x0f );
+		anslcd_write_byte_ctrl ( 0x06 );
+		anslcd_write_byte_ctrl ( 0x01 );
+		anslcd_write_byte_ctrl ( 0x02 );
+		return 0;
+	case ANSLCD_SENDCTRL:
+		temp = (char *) arg;
+		__get_user(ch, temp);
+		for (; ch; temp++) { /* FIXME: This is ugly, but should work, as a \0 byte is not a valid command code */
+			anslcd_write_byte_ctrl ( ch );
+			__get_user(ch, temp);
+		}
+		return 0;
+	case ANSLCD_SETSHORTDELAY:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
+		anslcd_short_delay=arg;
+		return 0;
+	case ANSLCD_SETLONGDELAY:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
+		anslcd_long_delay=arg;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int __pmac
+anslcd_open( struct inode * inode, struct file * file )
+{
+	return 0;
+}
+
+struct file_operations anslcd_fops = {
+	write:	anslcd_write,
+	ioctl:	anslcd_ioctl,
+	open:	anslcd_open,
+};
+
+static struct miscdevice anslcd_dev = {
+	ANSLCD_MINOR,
+	"anslcd",
+	&anslcd_fops
+};
+
+const char anslcd_logo[] =	"********************"  /* Line #1 */
+				"*      LINUX!      *"  /* Line #3 */
+				"*    Welcome to    *"  /* Line #2 */
+				"********************"; /* Line #4 */
+
+int __init
+anslcd_init(void)
+{
+	int a;
+	struct device_node* node;
+
+	node = find_devices("lcd");
+	if (!node || !node->parent)
+		return -ENODEV;
+	if (strcmp(node->parent->name, "gc"))
+		return -ENODEV;
+
+	anslcd_ptr = (volatile unsigned char*)ioremap(ANSLCD_ADDR, 0x20);
+	
+	misc_register(&anslcd_dev);
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "LCD: init\n");
+#endif
+
+	anslcd_write_byte_ctrl ( 0x38 );
+	anslcd_write_byte_ctrl ( 0x0c );
+	anslcd_write_byte_ctrl ( 0x06 );
+	anslcd_write_byte_ctrl ( 0x01 );
+	anslcd_write_byte_ctrl ( 0x02 );
+	for(a=0;a<80;a++) {
+		anslcd_write_byte_data(anslcd_logo[a]);
+	}
+	return 0;
+}
+
+__initcall(anslcd_init);
+
diff -Nru a/drivers/macintosh/apm_emu.c b/drivers/macintosh/apm_emu.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/macintosh/apm_emu.c	Sat May 18 00:46:15 2002
@@ -0,0 +1,554 @@
+/* APM emulation layer for PowerMac
+ * 
+ * Copyright 2001 Benjamin Herrenschmidt (benh@kernel.crashing.org)
+ *
+ * Lots of code inherited from apm.c, see appropriate notice in
+ *  arch/i386/kernel/apm.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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.
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/poll.h>
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include <linux/fcntl.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+#include <linux/miscdevice.h>
+#include <linux/apm_bios.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/pm.h>
+#include <linux/kernel.h>
+#include <linux/smp_lock.h>
+
+#include <linux/adb.h>
+#include <linux/pmu.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/machdep.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(args...) printk(KERN_DEBUG args)
+//#define DBG(args...) xmon_printf(args)
+#else
+#define DBG(args...) do { } while (0)
+#endif
+
+/*
+ * The apm_bios device is one of the misc char devices.
+ * This is its minor number.
+ */
+#define	APM_MINOR_DEV	134
+
+/*
+ * Maximum number of events stored
+ */
+#define APM_MAX_EVENTS		20
+
+#define FAKE_APM_BIOS_VERSION	0x0101
+
+#define APM_USER_NOTIFY_TIMEOUT	(5*HZ)
+
+/*
+ * The per-file APM data
+ */
+struct apm_user {
+	int		magic;
+	struct apm_user *	next;
+	int		suser: 1;
+	int		suspend_waiting: 1;
+	int		suspends_pending;
+	int		suspends_read;
+	int		event_head;
+	int		event_tail;
+	apm_event_t	events[APM_MAX_EVENTS];
+};
+
+/*
+ * The magic number in apm_user
+ */
+#define APM_BIOS_MAGIC		0x4101
+
+/*
+ * Local variables
+ */
+static int			suspends_pending;
+
+static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
+static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
+static struct apm_user *	user_list;
+
+static int apm_notify_sleep(struct pmu_sleep_notifier *self, int when);
+static struct pmu_sleep_notifier apm_sleep_notifier = {
+	apm_notify_sleep,
+	SLEEP_LEVEL_USERLAND,
+};
+
+static char			driver_version[] = "0.5";	/* no spaces */
+
+#ifdef DEBUG
+static char *	apm_event_name[] = {
+	"system standby",
+	"system suspend",
+	"normal resume",
+	"critical resume",
+	"low battery",
+	"power status change",
+	"update time",
+	"critical suspend",
+	"user standby",
+	"user suspend",
+	"system standby resume",
+	"capabilities change"
+};
+#define NR_APM_EVENT_NAME	\
+		(sizeof(apm_event_name) / sizeof(apm_event_name[0]))
+
+#endif
+
+static int queue_empty(struct apm_user *as)
+{
+	return as->event_head == as->event_tail;
+}
+
+static apm_event_t get_queued_event(struct apm_user *as)
+{
+	as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
+	return as->events[as->event_tail];
+}
+
+static void queue_event(apm_event_t event, struct apm_user *sender)
+{
+	struct apm_user *	as;
+
+	DBG("apm_emu: queue_event(%s)\n", apm_event_name[event-1]);
+	if (user_list == NULL)
+		return;
+	for (as = user_list; as != NULL; as = as->next) {
+		if (as == sender)
+			continue;
+		as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
+		if (as->event_head == as->event_tail) {
+			static int notified;
+
+			if (notified++ == 0)
+			    printk(KERN_ERR "apm_emu: an event queue overflowed\n");
+			as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
+		}
+		as->events[as->event_head] = event;
+		if (!as->suser)
+			continue;
+		switch (event) {
+		case APM_SYS_SUSPEND:
+		case APM_USER_SUSPEND:
+			as->suspends_pending++;
+			suspends_pending++;
+			break;
+		case APM_NORMAL_RESUME:
+			as->suspend_waiting = 0;
+			break;
+		}
+	}
+	wake_up_interruptible(&apm_waitqueue);
+}
+
+static int check_apm_user(struct apm_user *as, const char *func)
+{
+	if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
+		printk(KERN_ERR "apm_emu: %s passed bad filp\n", func);
+		return 1;
+	}
+	return 0;
+}
+
+static ssize_t do_read(struct file *fp, char *buf, size_t count, loff_t *ppos)
+{
+	struct apm_user *	as;
+	int			i;
+	apm_event_t		event;
+	DECLARE_WAITQUEUE(wait, current);
+
+	as = fp->private_data;
+	if (check_apm_user(as, "read"))
+		return -EIO;
+	if (count < sizeof(apm_event_t))
+		return -EINVAL;
+	if (queue_empty(as)) {
+		if (fp->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		add_wait_queue(&apm_waitqueue, &wait);
+repeat:
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (queue_empty(as) && !signal_pending(current)) {
+			schedule();
+			goto repeat;
+		}
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&apm_waitqueue, &wait);
+	}
+	i = count;
+	while ((i >= sizeof(event)) && !queue_empty(as)) {
+		event = get_queued_event(as);
+		DBG("apm_emu: do_read, returning: %s\n", apm_event_name[event-1]);
+		if (copy_to_user(buf, &event, sizeof(event))) {
+			if (i < count)
+				break;
+			return -EFAULT;
+		}
+		switch (event) {
+		case APM_SYS_SUSPEND:
+		case APM_USER_SUSPEND:
+			as->suspends_read++;
+			break;
+		}
+		buf += sizeof(event);
+		i -= sizeof(event);
+	}
+	if (i < count)
+		return count - i;
+	if (signal_pending(current))
+		return -ERESTARTSYS;
+	return 0;
+}
+
+static unsigned int do_poll(struct file *fp, poll_table * wait)
+{
+	struct apm_user * as;
+
+	as = fp->private_data;
+	if (check_apm_user(as, "poll"))
+		return 0;
+	poll_wait(fp, &apm_waitqueue, wait);
+	if (!queue_empty(as))
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static int do_ioctl(struct inode * inode, struct file *filp,
+		    u_int cmd, u_long arg)
+{
+	struct apm_user *	as;
+	DECLARE_WAITQUEUE(wait, current);
+
+	as = filp->private_data;
+	if (check_apm_user(as, "ioctl"))
+		return -EIO;
+	if (!as->suser)
+		return -EPERM;
+	switch (cmd) {
+	case APM_IOC_SUSPEND:
+		/* If a suspend message was sent to userland, we
+		 * consider this as a confirmation message
+		 */
+		if (as->suspends_read > 0) {
+			as->suspends_read--;
+			as->suspends_pending--;
+			suspends_pending--;
+		} else {
+			// Route to PMU suspend ?
+			break;
+		}
+		as->suspend_waiting = 1;
+		add_wait_queue(&apm_waitqueue, &wait);
+		DBG("apm_emu: ioctl waking up sleep waiter !\n");
+		wake_up(&apm_suspend_waitqueue);
+		mb();
+		while(as->suspend_waiting && !signal_pending(current)) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+		}
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&apm_waitqueue, &wait);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int do_release(struct inode * inode, struct file * filp)
+{
+	struct apm_user *	as;
+
+	as = filp->private_data;
+	if (check_apm_user(as, "release"))
+		return 0;
+	filp->private_data = NULL;
+	lock_kernel();
+	if (as->suspends_pending > 0) {
+		suspends_pending -= as->suspends_pending;
+		if (suspends_pending <= 0)
+			wake_up(&apm_suspend_waitqueue);
+	}
+	if (user_list == as)
+		user_list = as->next;
+	else {
+		struct apm_user *	as1;
+
+		for (as1 = user_list;
+		     (as1 != NULL) && (as1->next != as);
+		     as1 = as1->next)
+			;
+		if (as1 == NULL)
+			printk(KERN_ERR "apm: filp not in user list\n");
+		else
+			as1->next = as->next;
+	}
+	unlock_kernel();
+	kfree(as);
+	return 0;
+}
+
+static int do_open(struct inode * inode, struct file * filp)
+{
+	struct apm_user *	as;
+
+	as = (struct apm_user *)kmalloc(sizeof(*as), GFP_KERNEL);
+	if (as == NULL) {
+		printk(KERN_ERR "apm: cannot allocate struct of size %d bytes\n",
+		       sizeof(*as));
+		return -ENOMEM;
+	}
+	as->magic = APM_BIOS_MAGIC;
+	as->event_tail = as->event_head = 0;
+	as->suspends_pending = 0;
+	as->suspends_read = 0;
+	/*
+	 * XXX - this is a tiny bit broken, when we consider BSD
+         * process accounting. If the device is opened by root, we
+	 * instantly flag that we used superuser privs. Who knows,
+	 * we might close the device immediately without doing a
+	 * privileged operation -- cevans
+	 */
+	as->suser = capable(CAP_SYS_ADMIN);
+	as->next = user_list;
+	user_list = as;
+	filp->private_data = as;
+
+	DBG("apm_emu: opened by %s, suser: %d\n", current->comm, (int)as->suser);
+
+	return 0;
+}
+
+/* Wait for all clients to ack the suspend request. APM API
+ * doesn't provide a way to NAK, but this could be added
+ * here.
+ */
+static int wait_all_suspend(void)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	add_wait_queue(&apm_suspend_waitqueue, &wait);
+	DBG("apm_emu: wait_all_suspend(), suspends_pending: %d\n", suspends_pending);
+	while(suspends_pending > 0) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&apm_suspend_waitqueue, &wait);
+
+	DBG("apm_emu: wait_all_suspend() - complete !\n");
+	
+	return 1;
+}
+
+static int apm_notify_sleep(struct pmu_sleep_notifier *self, int when)
+{
+	switch(when) {
+		case PBOOK_SLEEP_REQUEST:
+			queue_event(APM_SYS_SUSPEND, NULL);
+			if (!wait_all_suspend())
+				return PBOOK_SLEEP_REFUSE;
+			break;
+		case PBOOK_SLEEP_REJECT:
+		case PBOOK_WAKE:
+			queue_event(APM_NORMAL_RESUME, NULL);
+			break;
+	}
+	return PBOOK_SLEEP_OK;
+}
+
+#define APM_CRITICAL		10
+#define APM_LOW			30
+
+static int apm_emu_get_info(char *buf, char **start, off_t fpos, int length)
+{
+	/* Arguments, with symbols from linux/apm_bios.h.  Information is
+	   from the Get Power Status (0x0a) call unless otherwise noted.
+
+	   0) Linux driver version (this will change if format changes)
+	   1) APM BIOS Version.  Usually 1.0, 1.1 or 1.2.
+	   2) APM flags from APM Installation Check (0x00):
+	      bit 0: APM_16_BIT_SUPPORT
+	      bit 1: APM_32_BIT_SUPPORT
+	      bit 2: APM_IDLE_SLOWS_CLOCK
+	      bit 3: APM_BIOS_DISABLED
+	      bit 4: APM_BIOS_DISENGAGED
+	   3) AC line status
+	      0x00: Off-line
+	      0x01: On-line
+	      0x02: On backup power (BIOS >= 1.1 only)
+	      0xff: Unknown
+	   4) Battery status
+	      0x00: High
+	      0x01: Low
+	      0x02: Critical
+	      0x03: Charging
+	      0x04: Selected battery not present (BIOS >= 1.2 only)
+	      0xff: Unknown
+	   5) Battery flag
+	      bit 0: High
+	      bit 1: Low
+	      bit 2: Critical
+	      bit 3: Charging
+	      bit 7: No system battery
+	      0xff: Unknown
+	   6) Remaining battery life (percentage of charge):
+	      0-100: valid
+	      -1: Unknown
+	   7) Remaining battery life (time units):
+	      Number of remaining minutes or seconds
+	      -1: Unknown
+	   8) min = minutes; sec = seconds */
+
+	unsigned short  ac_line_status = 0xff;
+	unsigned short  battery_status = 0xff;
+	unsigned short  battery_flag   = 0xff;
+	int		percentage     = -1;
+	int             time_units     = -1;
+	int		real_count     = 0;
+	int		charge         = -1;
+	int		current        = 0;
+	int		i;
+	char *		p = buf;
+	char		charging       = 0;
+
+	ac_line_status = ((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0);
+	for (i=0; i<pmu_battery_count; i++) {
+		if (percentage < 0)
+			percentage = 0;
+		if (charge < 0)
+			charge = 0;
+		if (pmu_batteries[i].flags & PMU_BATT_PRESENT) {
+			percentage += (pmu_batteries[i].charge * 100) /
+				pmu_batteries[i].max_charge;
+			/* hrm... should we provide the remaining charge
+			 * time when AC is plugged ? If yes, just remove
+			 * that test --BenH
+			 */
+			if (!ac_line_status) {
+				charge += pmu_batteries[i].charge;
+				current += pmu_batteries[i].current;
+			}
+			real_count++;
+			if ((pmu_batteries[i].flags & PMU_BATT_CHARGING))
+				charging++;
+		}
+	}
+	if (real_count) {
+		time_units = (charge * 59) / (current * -1);
+		if(!charging)
+			battery_flag &= ~0x08;
+		percentage /= real_count;
+		if (battery_flag & 0x08) {
+			battery_status = 0x03;
+			battery_flag = 0x08;
+		} else if (percentage <= APM_CRITICAL) {
+			battery_status = 0x02;
+			battery_flag = 0x04;
+		} else if (percentage <= APM_LOW) {
+			battery_status = 0x01;
+			battery_flag = 0x02;
+		} else {
+			battery_status = 0x00;
+			battery_flag = 0x01;
+		}
+	}
+	p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
+		     driver_version,
+		     (FAKE_APM_BIOS_VERSION >> 8) & 0xff,
+		     FAKE_APM_BIOS_VERSION & 0xff,
+		     0,
+		     ac_line_status,
+		     battery_status,
+		     battery_flag,
+		     percentage,
+		     time_units,
+		     "min");
+
+	return p - buf;
+}
+
+static struct file_operations apm_bios_fops = {
+	owner:		THIS_MODULE,
+	read:		do_read,
+	poll:		do_poll,
+	ioctl:		do_ioctl,
+	open:		do_open,
+	release:	do_release,
+};
+
+static struct miscdevice apm_device = {
+	APM_MINOR_DEV,
+	"apm_bios",
+	&apm_bios_fops
+};
+
+static int __init apm_emu_init(void)
+{
+	struct proc_dir_entry *apm_proc;
+
+	if (sys_ctrler != SYS_CTRLER_PMU) {
+		printk(KERN_INFO "apm_emu: Requires a machine with a PMU.\n");
+		return -ENODEV;
+	}
+		
+	apm_proc = create_proc_info_entry("apm", 0, NULL, apm_emu_get_info);
+	if (apm_proc)
+		SET_MODULE_OWNER(apm_proc);
+
+	misc_register(&apm_device);
+
+	pmu_register_sleep_notifier(&apm_sleep_notifier);
+
+	printk(KERN_INFO "apm_emu: APM Emulation %s initialized.\n", driver_version);
+
+	return 0;
+}
+
+static void __exit apm_emu_exit(void)
+{
+	pmu_unregister_sleep_notifier(&apm_sleep_notifier);
+	misc_deregister(&apm_device);
+	remove_proc_entry("apm", NULL);
+
+	printk(KERN_INFO "apm_emu: APM Emulation removed.\n");
+}
+
+module_init(apm_emu_init);
+module_exit(apm_emu_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt");
+MODULE_DESCRIPTION("APM emulation layer for PowerMac");
+MODULE_LICENSE("GPL");
+EXPORT_NO_SYMBOLS;
+
diff -Nru a/drivers/macintosh/mac_hid.c b/drivers/macintosh/mac_hid.c
--- a/drivers/macintosh/mac_hid.c	Sat May 18 00:46:08 2002
+++ b/drivers/macintosh/mac_hid.c	Sat May 18 00:46:08 2002
@@ -200,15 +200,16 @@
 	0, 0, 0, KEY_KPCOMMA, 0, KEY_INTL3, 0, 0,		/* 0x00-0x07 */
 	0, 0, 0, 0, KEY_LANG1, KEY_LANG2, 0, 0,			/* 0x08-0x0f */
 	0, 0, 0, 0, 0, 0, 0, 0,					/* 0x10-0x17 */
-	0, 0, 0, 0, KEY_KPENTER, KEY_RIGHTCTRL, 0, 0,		/* 0x18-0x1f */
-	0, 0, 0, 0, 0, 0, 0, 0,					/* 0x20-0x27 */
+	0, 0, 0, 0, KEY_KPENTER, KEY_RIGHTCTRL, KEY_VOLUMEUP, 0,/* 0x18-0x1f */
+	0, 0, 0, 0, 0, KEY_VOLUMEDOWN, KEY_MUTE, 0,		/* 0x20-0x27 */
 	0, 0, 0, 0, 0, 0, 0, 0,					/* 0x28-0x2f */
 	0, 0, 0, 0, 0, KEY_KPSLASH, 0, KEY_SYSRQ,		/* 0x30-0x37 */
-	KEY_RIGHTALT, 0, 0, 0, 0, 0, 0, 0,			/* 0x38-0x3f */
+	KEY_RIGHTALT, KEY_BRIGHTNESSUP, KEY_BRIGHTNESSDOWN, 
+		KEY_EJECTCD, 0, 0, 0, 0,			/* 0x38-0x3f */
 	0, 0, 0, 0, 0, 0, 0, KEY_HOME,				/* 0x40-0x47 */
 	KEY_UP, KEY_PAGEUP, 0, KEY_LEFT, 0, KEY_RIGHT, 0, KEY_END, /* 0x48-0x4f */
 	KEY_DOWN, KEY_PAGEDOWN, KEY_INSERT, KEY_DELETE, 0, 0, 0, 0, /* 0x50-0x57 */
-	0, 0, 0, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_COMPOSE, 0, 0, /* 0x58-0x5f */
+	0, 0, 0, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_COMPOSE, KEY_POWER, 0, /* 0x58-0x5f */
 	0, 0, 0, 0, 0, 0, 0, 0,					/* 0x60-0x67 */
 	0, 0, 0, 0, 0, 0, 0, KEY_MACRO,				/* 0x68-0x6f */
 	0, 0, 0, 0, 0, 0, 0, 0,					/* 0x70-0x77 */
diff -Nru a/drivers/macintosh/macserial.c b/drivers/macintosh/macserial.c
--- a/drivers/macintosh/macserial.c	Sat May 18 00:46:09 2002
+++ b/drivers/macintosh/macserial.c	Sat May 18 00:46:09 2002
@@ -38,8 +38,10 @@
 #include <asm/irq.h>
 #include <asm/prom.h>
 #include <asm/system.h>
+#include <asm/segment.h>
 #include <asm/bitops.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #include <linux/adb.h>
 #include <linux/pmu.h>
 #ifdef CONFIG_KGDB
@@ -58,6 +60,7 @@
 #endif
 
 #define SUPPORT_SERIAL_DMA
+#define MACSERIAL_VERSION	"2.0"
 
 /*
  * It would be nice to dynamically allocate everything that
@@ -181,7 +184,7 @@
 
 static inline int __pmac
 serial_paranoia_check(struct mac_serial *info,
-		      dev_t device, const char *routine)
+		      kdev_t device, const char *routine)
 {
 #ifdef SERIAL_PARANOIA_CHECK
 	static const char badmagic[] = KERN_WARNING
@@ -190,11 +193,11 @@
 		"Warning: null mac_serial for (%d, %d) in %s\n";
 
 	if (!info) {
-		printk(badinfo, MAJOR(device), MINOR(device), routine);
+		printk(badinfo, major(device), minor(device), routine);
 		return 1;
 	}
 	if (info->magic != SERIAL_MAGIC) {
-		printk(badmagic, MAJOR(device), MINOR(device), routine);
+		printk(badmagic, major(device), minor(device), routine);
 		return 1;
 	}
 #endif
@@ -1030,7 +1033,7 @@
 {
 	unsigned long flags;
 
-	OPNDBG("setting up ttys%d SCC...\n", info->line);
+	OPNDBG("setting up ttyS%d SCC...\n", info->line);
 
 	save_flags(flags); cli(); /* Disable interrupts */
 
@@ -1178,7 +1181,7 @@
 	}
 
 	memset(info->curregs, 0, sizeof(info->curregs));
-	memset(info->curregs, 0, sizeof(info->pendregs));
+	memset(info->pendregs, 0, sizeof(info->pendregs));
 
 	info->flags &= ~ZILOG_INITIALIZED;
 }
@@ -1193,69 +1196,34 @@
 {
 	int delay = 0;
 
-	if (feature_test(info->dev_node, FEATURE_Serial_enable) < 0)
-		return 0;	/* don't have serial power control */
-
-	/* The timings looks strange but that's the ones MacOS seems
-	   to use for the internal modem. I think we can use a lot faster
-	   ones, at least whe not using the modem, this should be tested.
-	 */
 	if (state) {
-		PWRDBG("ttyS%02d: powering up hardware\n", info->line);
-		if (feature_test(info->dev_node, FEATURE_Serial_enable) == 0) {
-			feature_set(info->dev_node, FEATURE_Serial_enable);
-			mdelay(10);
-			feature_set(info->dev_node, FEATURE_Serial_reset);
-			mdelay(15);
-			feature_clear(info->dev_node, FEATURE_Serial_reset);
-			mdelay(10);
-		}
-		if (info->zs_chan_a == info->zs_channel)
-			feature_set(info->dev_node, FEATURE_Serial_IO_A);
-		else
-			feature_set(info->dev_node, FEATURE_Serial_IO_B);
-		delay = 10;
-		if (info->is_cobalt_modem) {
-			feature_set_modem_power(info->dev_node, 1);
+		PWRDBG("ttyS%d: powering up hardware\n", info->line);
+		pmac_call_feature(
+			PMAC_FTR_SCC_ENABLE,
+			info->dev_node, info->port_type, 1);
+		if (info->is_internal_modem) {
+			pmac_call_feature(
+				PMAC_FTR_MODEM_ENABLE,
+				info->dev_node, 0, 1);
 			delay = 2500;	/* wait for 2.5s before using */
-		}
-#ifdef CONFIG_PMAC_PBOOK
-		if (info->is_irda)
-			pmu_enable_irled(1);
-#endif /* CONFIG_PMAC_PBOOK */
+		} else if (info->is_irda)
+			mdelay(50);	/* Do better here once the problems
+			                 * with blocking have been ironed out
+			                 */
 	} else {
 		/* TODO: Make that depend on a timer, don't power down
 		 * immediately
 		 */
-		PWRDBG("ttyS%02d: shutting down hardware\n", info->line);
-		if (info->is_cobalt_modem) {
-			PWRDBG("ttyS%02d: shutting down modem\n", info->line);
-			feature_set_modem_power(info->dev_node, 0);
-		}
-#ifdef CONFIG_PMAC_PBOOK
-		if (info->is_irda)
-			pmu_enable_irled(0);
-#endif /* CONFIG_PMAC_PBOOK */
-
-		if (info->zs_chan_a == info->zs_channel && !info->is_irda) {
-			PWRDBG("ttyS%02d: shutting down SCC channel A\n", info->line);
-			feature_clear(info->dev_node, FEATURE_Serial_IO_A);
-		} else if (!info->is_irda) {
-			PWRDBG("ttyS%02d: shutting down SCC channel B\n", info->line);
-			feature_clear(info->dev_node, FEATURE_Serial_IO_B);
-		}
-		/* XXX for now, shut down SCC core only on powerbooks */
-		if (is_powerbook
-		    && !(feature_test(info->dev_node, FEATURE_Serial_IO_A) ||
-			 feature_test(info->dev_node, FEATURE_Serial_IO_B))) {
-			PWRDBG("ttyS%02d: shutting down SCC core\n", info->line);
-			feature_set(info->dev_node, FEATURE_Serial_reset);
-			mdelay(15);
-			feature_clear(info->dev_node, FEATURE_Serial_reset);
-			mdelay(25);
-			feature_clear(info->dev_node, FEATURE_Serial_enable);
-			mdelay(5);
-		}
+		PWRDBG("ttyS%d: shutting down hardware\n", info->line);
+		if (info->is_internal_modem) {
+			PWRDBG("ttyS%d: shutting down modem\n", info->line);
+			pmac_call_feature(
+				PMAC_FTR_MODEM_ENABLE,
+				info->dev_node, 0, 0);
+		}
+		pmac_call_feature(
+			PMAC_FTR_SCC_ENABLE,
+			info->dev_node, info->port_type, 0);
 	}
 	return delay;
 }
@@ -1988,7 +1956,7 @@
 		return;
 	}
 
-	OPNDBG("rs_close ttys%d, count = %d\n", info->line, info->count);
+	OPNDBG("rs_close ttyS%d, count = %d\n", info->line, info->count);
 	if ((tty->count == 1) && (info->count != 1)) {
 		/*
 		 * Uh, oh.  tty->count is 1, which means that the tty
@@ -2003,7 +1971,7 @@
 	}
 	if (--info->count < 0) {
 		printk(KERN_ERR "rs_close: bad serial port count for "
-				"ttys%d: %d\n", info->line, info->count);
+				"ttyS%d: %d\n", info->line, info->count);
 		info->count = 0;
 	}
 	if (info->count) {
@@ -2220,7 +2188,7 @@
 	 */
 	retval = 0;
 	add_wait_queue(&info->open_wait, &wait);
-	OPNDBG("block_til_ready before block: ttys%d, count = %d\n",
+	OPNDBG("block_til_ready before block: ttyS%d, count = %d\n",
 	       info->line, info->count);
 	cli();
 	if (!tty_hung_up_p(filp)) 
@@ -2255,7 +2223,7 @@
 			retval = -ERESTARTSYS;
 			break;
 		}
-		OPNDBG("block_til_ready blocking: ttys%d, count = %d\n",
+		OPNDBG("block_til_ready blocking: ttyS%d, count = %d\n",
 		       info->line, info->count);
 		schedule();
 	}
@@ -2264,7 +2232,7 @@
 	if (!tty_hung_up_p(filp))
 		info->count++;
 	info->blocked_open--;
-	OPNDBG("block_til_ready after blocking: ttys%d, count = %d\n",
+	OPNDBG("block_til_ready after blocking: ttyS%d, count = %d\n",
 	       info->line, info->count);
 	if (retval)
 		return retval;
@@ -2285,7 +2253,7 @@
 	unsigned long		page;
 
 	MOD_INC_USE_COUNT;
-	line = MINOR(tty->device) - tty->driver.minor_start;
+	line = minor(tty->device) - tty->driver.minor_start;
 	if ((line < 0) || (line >= zs_channels_found)) {
 		MOD_DEC_USE_COUNT;
 		return -ENODEV;
@@ -2365,7 +2333,7 @@
 	info->session = current->session;
 	info->pgrp = current->pgrp;
 
-	OPNDBG("rs_open ttys%d successful...\n", info->line);
+	OPNDBG("rs_open ttyS%d successful...\n", info->line);
 	return 0;
 }
 
@@ -2373,14 +2341,14 @@
 
 static void show_serial_version(void)
 {
-	printk(KERN_INFO "PowerMac Z8530 serial driver version 2.0\n");
+	printk(KERN_INFO "PowerMac Z8530 serial driver version " MACSERIAL_VERSION "\n");
 }
 
 /*
  * Initialize one channel, both the mac_serial and mac_zschannel
  * structs.  We use the dev_node field of the mac_serial struct.
  */
-static void
+static int
 chan_init(struct mac_serial *zss, struct mac_zschannel *zs_chan,
 	  struct mac_zschannel *zs_chan_a)
 {
@@ -2410,12 +2378,15 @@
 
 	/* setup misc varariables */
 	zss->kgdb_channel = 0;
-	zss->is_cobalt_modem = device_is_compatible(ch, "cobalt");
 
-	/* XXX tested only with wallstreet PowerBook,
-	   should do no harm anyway */
+	/* For now, we assume you either have a slot-names property
+	 * with "Modem" in it, or your channel is compatible with
+	 * "cobalt". Might need additional fixups
+	 */
+	zss->is_internal_modem = device_is_compatible(ch, "cobalt");
 	conn = get_property(ch, "AAPL,connector", &len);
 	zss->is_irda = conn && (strcmp(conn, "infrared") == 0);
+	zss->port_type = PMAC_SCC_ASYNC;
 	/* 1999 Powerbook G3 has slot-names property instead */
 	slots = (struct slot_names_prop *)get_property(ch, "slot-names", &len);
 	if (slots && slots->count > 0) {
@@ -2424,8 +2395,29 @@
 		else if (strcmp(slots->name, "Modem") == 0)
 			zss->is_internal_modem = 1;
 	}
+	if (zss->is_irda)
+		zss->port_type = PMAC_SCC_IRDA;
+	if (zss->is_internal_modem) {
+		struct device_node* i2c_modem = find_devices("i2c-modem");
+		if (i2c_modem) {
+			char* mid = get_property(i2c_modem, "modem-id", NULL);
+			if (mid) switch(*mid) {
+			case 0x04 :
+			case 0x05 :
+			case 0x07 :
+			case 0x08 :
+			case 0x0b :
+			case 0x0c :
+				zss->port_type = PMAC_SCC_I2S1;
+			}
+			printk(KERN_INFO "macserial: i2c-modem detected, id: %d\n",
+				mid ? (*mid) : 0);
+		} else {
+			printk(KERN_INFO "macserial: serial modem detected\n");
+		}
+	}
 
-	if (zss->has_dma) {
+	while (zss->has_dma) {
 		zss->dma_priv = NULL;
 		/* it seems that the last two addresses are the
 		   DMA controllers */
@@ -2436,11 +2428,69 @@
 		zss->tx_dma_irq = ch->intrs[1].line;
 		zss->rx_dma_irq = ch->intrs[2].line;
 		spin_lock_init(&zss->rx_dma_lock);
+		break;
 	}
 
 	init_timer(&zss->powerup_timer);
 	zss->powerup_timer.function = powerup_done;
 	zss->powerup_timer.data = (unsigned long) zss;
+	return 0;
+}
+
+/*
+ * /proc fs routines. TODO: Add status lines & error stats
+ */
+static inline int
+line_info(char *buf, struct mac_serial *info)
+{
+	int		ret=0;
+	unsigned char* connector;
+	int lenp;
+
+	ret += sprintf(buf, "%d: port:0x%X irq:%d", info->line, info->port, info->irq);
+
+	connector = get_property(info->dev_node, "AAPL,connector", &lenp);
+	if (connector)
+		ret+=sprintf(buf+ret," con:%s ", connector);
+	if (info->is_internal_modem) {
+		if (!connector)
+			ret+=sprintf(buf+ret," con:");
+		ret+=sprintf(buf+ret,"%s", " (internal modem)");
+	}
+	if (info->is_irda) {
+		if (!connector)
+			ret+=sprintf(buf+ret," con:");
+		ret+=sprintf(buf+ret,"%s", " (IrDA)");
+	}
+	ret+=sprintf(buf+ret,"\n");
+
+	return ret;
+}
+
+int macserial_read_proc(char *page, char **start, off_t off, int count,
+		 int *eof, void *data)
+{
+	int l, len = 0;
+	off_t	begin = 0;
+	struct mac_serial *info;
+
+	len += sprintf(page, "serinfo:1.0 driver:" MACSERIAL_VERSION "\n");
+	for (info = zs_chain; info && len < 4000; info = info->zs_next) {
+		l = line_info(page + len, info);
+		len += l;
+		if (len+begin > off+count)
+			goto done;
+		if (len+begin < off) {
+			begin += len;
+			len = 0;
+		}
+	}
+	*eof = 1;
+done:
+	if (off >= len+begin)
+		return 0;
+	*start = page + (off-begin);
+	return ((count < begin+len-off) ? count : begin+len-off);
 }
 
 /* Ask the PROM how many Z8530s we have and initialize their zs_channels */
@@ -2495,13 +2545,15 @@
 			continue;
 
 		/* set up A side */
-		chan_init(&zs_soft[chip + chan_a_index], zs_chan, zs_chan);
+		if (chan_init(&zs_soft[chip + chan_a_index], zs_chan, zs_chan))
+			continue;
 		++zs_chan;
 
 		/* set up B side, if it exists */
 		if (nchan > 1)
-			chan_init(&zs_soft[chip + 1 - chan_a_index],
-				  zs_chan, zs_chan - 1);
+			if (chan_init(&zs_soft[chip + 1 - chan_a_index],
+				  zs_chan, zs_chan - 1))
+				continue;
 		++zs_chan;
 	}
 	*pp = 0;
@@ -2527,13 +2579,35 @@
 	if (zs_chain == 0)
 		probe_sccs();
 
-	/* XXX assume it's a powerbook if we have a via-pmu */
+	/* XXX assume it's a powerbook if we have a via-pmu
+	 * 
+	 * This is OK for core99 machines as well.
+	 */
 	is_powerbook = find_devices("via-pmu") != 0;
 
-	/* Register the interrupt handler for each one */
+	/* Register the interrupt handler for each one
+	 * We also request the OF resources here as probe_sccs()
+	 * might be called too early for that
+	 */
 	save_flags(flags); cli();
 	for (i = 0; i < zs_channels_found; ++i) {
+		struct device_node* ch = zs_soft[i].dev_node;
+		if (!request_OF_resource(ch, 0, NULL)) {
+			printk(KERN_ERR "macserial: can't request IO resource !\n");
+			return -ENODEV;
+		}
 		if (zs_soft[i].has_dma) {
+			if (!request_OF_resource(ch, ch->n_addrs - 2, " (tx dma)")) {
+				printk(KERN_ERR "macserial: can't request TX DMA resource !\n");
+				zs_soft[i].has_dma = 0;
+				goto no_dma;
+			}
+			if (!request_OF_resource(ch, ch->n_addrs - 1, " (rx dma)")) {
+				release_OF_resource(ch, ch->n_addrs - 2);
+				printk(KERN_ERR "macserial: can't request RX DMA resource !\n");
+				zs_soft[i].has_dma = 0;
+				goto no_dma;
+			}
 			if (request_irq(zs_soft[i].tx_dma_irq, rs_txdma_irq, 0,
 					"SCC-txdma", &zs_soft[i]))
 				printk(KERN_ERR "macserial: can't get irq %d\n",
@@ -2545,6 +2619,7 @@
 				       zs_soft[i].rx_dma_irq);
 			disable_irq(zs_soft[i].rx_dma_irq);
 		}
+no_dma:		
 		if (request_irq(zs_soft[i].irq, rs_interrupt, 0,
 				"SCC", &zs_soft[i]))
 			printk(KERN_ERR "macserial: can't get irq %d\n",
@@ -2560,6 +2635,7 @@
 
 	memset(&serial_driver, 0, sizeof(struct tty_driver));
 	serial_driver.magic = TTY_DRIVER_MAGIC;
+	serial_driver.driver_name = "macserial";
 #ifdef CONFIG_DEVFS_FS
 	serial_driver.name = "tts/%d";
 #else
@@ -2596,6 +2672,7 @@
 	serial_driver.hangup = rs_hangup;
 	serial_driver.break_ctl = rs_break;
 	serial_driver.wait_until_sent = rs_wait_until_sent;
+	serial_driver.read_proc = macserial_read_proc;
 
 	/*
 	 * The callout device is just like normal device except for
@@ -2609,6 +2686,8 @@
 #endif /* CONFIG_DEVFS_FS */
 	callout_driver.major = TTYAUX_MAJOR;
 	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+	callout_driver.read_proc = 0;
+	callout_driver.proc_entry = 0;
 
 	if (tty_register_driver(&serial_driver))
 		panic("Couldn't register serial driver\n");
@@ -2675,21 +2754,11 @@
 		connector = get_property(info->dev_node, "AAPL,connector", &lenp);
 		if (connector)
 			printk(", port = %s", connector);
-		if (info->is_cobalt_modem)
-			printk(" (cobalt modem)");
-		else if (info->is_internal_modem)
+		if (info->is_internal_modem)
 			printk(" (internal modem)");
 		if (info->is_irda)
 			printk(" (IrDA)");
 		printk("\n");
-
-#ifndef CONFIG_XMON
-#ifdef CONFIG_KGDB
-		if (!info->kgdb_channel)
-#endif /* CONFIG_KGDB */
-			/* By default, disable the port */
-			set_scc_power(info, 0);
-#endif /* CONFIG_XMON */
  	}
 	tmp_buf = 0;
 
@@ -2711,6 +2780,12 @@
 			free_irq(zs_soft[i].tx_dma_irq, &zs_soft[i]);
 			free_irq(zs_soft[i].rx_dma_irq, &zs_soft[i]);
 		}
+		release_OF_resource(zs_soft[i].dev_node, 0);
+		if (zs_soft[i].has_dma) {
+			struct device_node* ch = zs_soft[i].dev_node;
+			release_OF_resource(ch, ch->n_addrs - 2);
+			release_OF_resource(ch, ch->n_addrs - 1);
+		}
 	}
 	restore_flags(flags);
 	tty_unregister_driver(&callout_driver);
@@ -2791,6 +2866,30 @@
 	/* Don't disable the transmitter. */
 }
 
+/*
+ *	Receive character from the serial port
+ */
+static int serial_console_wait_key(struct console *co)
+{
+	struct mac_serial *info = zs_soft + co->index;
+	int           val;
+
+	/* Turn of interrupts and enable the transmitter. */
+	write_zsreg(info->zs_channel, R1, info->curregs[1] & ~INT_ALL_Rx);
+	write_zsreg(info->zs_channel, R3, info->curregs[3] | RxENABLE);
+
+	/* Wait for something in the receive buffer. */
+	while((read_zsreg(info->zs_channel, 0) & Rx_CH_AV) == 0)
+		eieio();
+	val = read_zsdata(info->zs_channel);
+
+	/* Restore the values in the registers. */
+	write_zsreg(info->zs_channel, R1, info->curregs[1]);
+	write_zsreg(info->zs_channel, R3, info->curregs[3]);
+
+	return val;
+}
+
 static kdev_t serial_console_device(struct console *c)
 {
 	return MKDEV(TTY_MAJOR, 64 + c->index);
@@ -2981,6 +3080,7 @@
 	name:		"ttyS",
 	write:		serial_console_write,
 	device:		serial_console_device,
+	wait_key:	serial_console_wait_key,
 	setup:		serial_console_setup,
 	flags:		CON_PRINTBUFFER,
 	index:		-1,
diff -Nru a/drivers/macintosh/macserial.h b/drivers/macintosh/macserial.h
--- a/drivers/macintosh/macserial.h	Sat May 18 00:46:09 2002
+++ b/drivers/macintosh/macserial.h	Sat May 18 00:46:09 2002
@@ -111,8 +111,8 @@
 	char kgdb_channel;  /* Kgdb is running on this channel */
 	char is_cons;       /* Is this our console. */
 	char is_internal_modem; /* is connected to an internal modem */
-	char is_cobalt_modem;	/* is a gatwick-based cobalt modem */
 	char is_irda;		/* is connected to an IrDA codec */
+	int port_type;		/* Port type for pmac_feature */
 	unsigned char tx_active; /* character is being xmitted */
 	unsigned char tx_stopped; /* output is suspended */
 	unsigned char power_wait; /* waiting for power-up delay to expire */
diff -Nru a/drivers/macintosh/mediabay.c b/drivers/macintosh/mediabay.c
--- a/drivers/macintosh/mediabay.c	Sat May 18 00:46:08 2002
+++ b/drivers/macintosh/mediabay.c	Sat May 18 00:46:08 2002
@@ -22,12 +22,17 @@
 #include <linux/hdreg.h>
 #include <linux/stddef.h>
 #include <linux/unistd.h>
+#include <linux/init.h>
 #include <asm/prom.h>
 #include <asm/pgtable.h>
 #include <asm/io.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #include <asm/mediabay.h>
 #include <asm/sections.h>
+#include <asm/ohare.h>
+#include <asm/heathrow.h>
+#include <asm/keylargo.h>
 #include <linux/adb.h>
 #include <linux/pmu.h>
 
@@ -40,7 +45,7 @@
 #endif
 
 #undef MB_USE_INTERRUPTS
-#undef MB_DEBUG
+#define MB_DEBUG
 #define MB_IGNORE_SIGNALS
 
 #ifdef MB_DEBUG
@@ -49,24 +54,46 @@
 #define MBDBG(fmt, arg...)	do { } while (0)
 #endif
 
-struct media_bay_hw {
-	unsigned char	b0;
-	unsigned char	contents;
-	unsigned char	b2;
-	unsigned char	b3;
+/* Type of media bay */
+enum {
+	mb_ohare,
+	mb_heathrow,
+	mb_keylargo
+};
+
+#define MB_FCR32(bay, r)	((bay)->base + ((r) >> 2))
+#define MB_FCR8(bay, r)		(((volatile u8*)((bay)->base)) + (r))
+
+#define MB_IN32(bay,r)		(in_le32(MB_FCR32(bay,r)))
+#define MB_OUT32(bay,r,v)	(out_le32(MB_FCR32(bay,r), (v)))
+#define MB_BIS(bay,r,v)		(MB_OUT32((bay), (r), MB_IN32((bay), r) | (v)))
+#define MB_BIC(bay,r,v)		(MB_OUT32((bay), (r), MB_IN32((bay), r) & ~(v)))
+#define MB_IN8(bay,r)		(in_8(MB_FCR8(bay,r)))
+#define MB_OUT8(bay,r,v)	(out_8(MB_FCR8(bay,r), (v)))
+
+struct media_bay_info;
+
+struct mb_ops {
+	char*	name;
+	u8	(*content)(struct media_bay_info* bay);
+	void	(*power)(struct media_bay_info* bay, int on_off);
+	int	(*setup_bus)(struct media_bay_info* bay, u8 device_id);
+	void	(*un_reset)(struct media_bay_info* bay);
+	void	(*un_reset_ide)(struct media_bay_info* bay);
 };
 
 struct media_bay_info {
-	volatile struct media_bay_hw*	addr;
-	volatile u8*			extint_gpio;
+	volatile u32*			base;
 	int				content_id;
 	int				state;
 	int				last_value;
 	int				value_count;
 	int				timer;
 	struct device_node*		dev_node;
-	int				pismo;	/* New PowerBook3,1 */
-	int				gpio_cache;
+	int				mb_type;
+	struct mb_ops*			ops;
+	int				index;
+	int				cached_gpio;
 #ifdef CONFIG_BLK_DEV_IDE
 	unsigned long			cd_base;
 	int 				cd_index;
@@ -77,33 +104,12 @@
 
 #define MAX_BAYS	2
 
-static volatile struct media_bay_info media_bays[MAX_BAYS];
+static struct media_bay_info media_bays[MAX_BAYS];
 int media_bay_count = 0;
 
-inline int mb_content(volatile struct media_bay_info *bay)
-{
-        if (bay->pismo) {
-                unsigned char new_gpio = in_8(bay->extint_gpio + 0xe) & 2;
-                if (new_gpio) {
-                	bay->gpio_cache = new_gpio;
-                        return MB_NO;
-                } else if (bay->gpio_cache != new_gpio) {
-                        /* make sure content bits are set */
-                        feature_set(bay->dev_node, FEATURE_Mediabay_content);
-                        udelay(5);
-                        bay->gpio_cache = new_gpio;
-                }
-                return (in_le32((unsigned*)bay->addr) >> 4) & 0xf;
-        } else {
-                int cont = (in_8(&bay->addr->contents) >> 4) & 7;
-                return (cont == 7) ? MB_NO : cont;
-        }
-}
-
 #ifdef CONFIG_BLK_DEV_IDE
 /* check the busy bit in the media-bay ide interface
    (assumes the media-bay contains an ide device) */
-//#define MB_IDE_READY(i)	((inb(media_bays[i].cd_base + 0x70) & 0xc0) == 0x40)
 #define MB_IDE_READY(i)	((inb(media_bays[i].cd_base + 0x70) & 0x80) == 0)
 #endif
 
@@ -116,7 +122,7 @@
  * Consider the media-bay ID value stable if it is the same for
  * this number of milliseconds
  */
-#define MB_STABLE_DELAY	40
+#define MB_STABLE_DELAY	100
 
 /* Wait after powering up the media bay this delay in ms
  * timeout bumped for some powerbooks
@@ -166,175 +172,259 @@
 	mb_powering_down	/* Powering down (avoid too fast down/up) */
 };
 
-static void poll_media_bay(int which);
-static void set_media_bay(int which, int id);
-static void set_mb_power(int which, int onoff);
-static void media_bay_step(int i);
-static int media_bay_task(void *);
+#define MB_POWER_SOUND		0x08
+#define MB_POWER_FLOPPY		0x04
+#define MB_POWER_ATA		0x02
+#define MB_POWER_PCI		0x01
+#define MB_POWER_OFF		0x00
 
-#ifdef MB_USE_INTERRUPTS
-static void media_bay_intr(int irq, void *devid, struct pt_regs *regs);
-#endif
+/*
+ * Functions for polling content of media bay
+ */
+ 
+static u8 __pmac
+ohare_mb_content(struct media_bay_info *bay)
+{
+	return (MB_IN32(bay, OHARE_MBCR) >> 12) & 7;
+}
+
+static u8 __pmac
+heathrow_mb_content(struct media_bay_info *bay)
+{
+	return (MB_IN32(bay, HEATHROW_MBCR) >> 12) & 7;
+}
+
+static u8 __pmac
+keylargo_mb_content(struct media_bay_info *bay)
+{
+	int new_gpio;
+
+	new_gpio = MB_IN8(bay, KL_GPIO_MEDIABAY_IRQ) & KEYLARGO_GPIO_INPUT_DATA;
+	if (new_gpio) {
+		bay->cached_gpio = new_gpio;
+		return MB_NO;
+	} else if (bay->cached_gpio != new_gpio) {
+		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_ENABLE);
+		(void)MB_IN32(bay, KEYLARGO_MBCR);
+		udelay(5);
+		MB_BIC(bay, KEYLARGO_MBCR, 0x0000000F);
+		(void)MB_IN32(bay, KEYLARGO_MBCR);
+		udelay(5);
+		bay->cached_gpio = new_gpio;
+	}
+	return (MB_IN32(bay, KEYLARGO_MBCR) >> 4) & 7;
+}
 
 /*
- * It seems that the bit for the media-bay interrupt in the IRQ_LEVEL
- * register is always set when there is something in the media bay.
- * This causes problems for the interrupt code if we attach an interrupt
- * handler to the media-bay interrupt, because it tends to go into
- * an infinite loop calling the media bay interrupt handler.
- * Therefore we do it all by polling the media bay once each tick.
+ * Functions for powering up/down the bay, puts the bay device
+ * into reset state as well
  */
 
-void __pmac
-media_bay_init(void)
+static void __pmac
+ohare_mb_power(struct media_bay_info* bay, int on_off)
 {
-	struct device_node *np;
-	int		n,i;
-	
-	for (i=0; i<MAX_BAYS; i++) {
-		memset((char *)&media_bays[i], 0, sizeof(struct media_bay_info));
-		media_bays[i].content_id	= -1;
-#ifdef CONFIG_BLK_DEV_IDE
-		media_bays[i].cd_index		= -1;
-#endif
+	if (on_off) {
+		/* Power up device, assert it's reset line */
+		MB_BIC(bay, OHARE_FCR, OH_BAY_RESET_N);
+		MB_BIC(bay, OHARE_FCR, OH_BAY_POWER_N);
+	} else {
+		/* Disable all devices */
+		MB_BIC(bay, OHARE_FCR, OH_BAY_DEV_MASK);
+		MB_BIC(bay, OHARE_FCR, OH_FLOPPY_ENABLE);
+		/* Cut power from bay, release reset line */
+		MB_BIS(bay, OHARE_FCR, OH_BAY_POWER_N);
+		MB_BIS(bay, OHARE_FCR, OH_BAY_RESET_N);
+		MB_BIS(bay, OHARE_FCR, OH_IDE1_RESET_N);
 	}
-	
-	np = find_devices("media-bay");
-	n = 0;
-	while(np && (n<MAX_BAYS)) {
-		if (np->n_addrs == 0)
-			continue;
-		media_bays[n].addr = (volatile struct media_bay_hw *)
-			ioremap(np->addrs[0].address, sizeof(struct media_bay_hw));
+	MB_BIC(bay, OHARE_MBCR, 0x00000F00);
+}
 
-		media_bays[n].pismo = device_is_compatible(np, "keylargo-media-bay");
-		if (media_bays[n].pismo) {
-			if (!np->parent || strcmp(np->parent->name, "mac-io")) {
-				printk(KERN_ERR "Pismo media-bay has no mac-io parent !\n");
-				continue;
-			}
-			media_bays[n].extint_gpio = ioremap(np->parent->addrs[0].address
-				+ 0x58, 0x10);
-		}
+static void __pmac
+heathrow_mb_power(struct media_bay_info* bay, int on_off)
+{
+	if (on_off) {
+		/* Power up device, assert it's reset line */
+		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
+		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_POWER_N);
+	} else {
+		/* Disable all devices */
+		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_DEV_MASK);
+		MB_BIC(bay, HEATHROW_FCR, HRW_SWIM_ENABLE);
+		/* Cut power from bay, release reset line */
+		MB_BIS(bay, HEATHROW_FCR, HRW_BAY_POWER_N);
+		MB_BIS(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
+		MB_BIS(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
+	}
+	MB_BIC(bay, HEATHROW_MBCR, 0x00000F00);
+}
 
-#ifdef MB_USE_INTERRUPTS
-		if (np->n_intrs == 0) {
-			printk(KERN_ERR "media bay %d has no irq\n",n);
-			continue;
-		}
+static void __pmac
+keylargo_mb_power(struct media_bay_info* bay, int on_off)
+{
+	if (on_off) {
+		/* Power up device, assert it's reset line */
+            	MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
+            	MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_POWER);
+	} else {
+		/* Disable all devices */
+		MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_MASK);
+		MB_BIC(bay, KEYLARGO_FCR1, KL1_EIDE0_ENABLE);
+		/* Cut power from bay, release reset line */
+		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_POWER);
+		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
+		MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
+	}
+	MB_BIC(bay, KEYLARGO_MBCR, 0x0000000F);
+}
 
-		if (request_irq(np->intrs[0].line, media_bay_intr, 0, "Media bay", (void *)n)) {
-			printk(KERN_ERR "Couldn't get IRQ %d for media bay %d\n",
-				np->intrs[0].line, n);
-			continue;
-		}
-#endif	
-		media_bay_count++;
-	
-		media_bays[n].dev_node		= np;
+/*
+ * Functions for configuring the media bay for a given type of device,
+ * enable the related busses
+ */
 
-		/* Force an immediate detect */
-		set_mb_power(n,0);
-		mdelay(MB_POWER_DELAY);
-		if(!media_bays[n].pismo)
-			out_8(&media_bays[n].addr->contents, 0x70);
-		mdelay(MB_STABLE_DELAY);
-		media_bays[n].content_id = MB_NO;
-		media_bays[n].last_value = mb_content(&media_bays[n]);
-		media_bays[n].value_count = MS_TO_HZ(MB_STABLE_DELAY);
-		media_bays[n].state = mb_empty;
-		do {
-			mdelay(1000/HZ);
-			media_bay_step(n);
-		} while((media_bays[n].state != mb_empty) &&
-			(media_bays[n].state != mb_up));
+static int __pmac
+ohare_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
+{
+	switch(device_id) {
+		case MB_FD:
+		case MB_FD1:
+			MB_BIS(bay, OHARE_FCR, OH_BAY_FLOPPY_ENABLE);
+			MB_BIS(bay, OHARE_FCR, OH_FLOPPY_ENABLE);
+			return 0;
+		case MB_CD:
+			MB_BIC(bay, OHARE_FCR, OH_IDE1_RESET_N);
+			MB_BIS(bay, OHARE_FCR, OH_BAY_IDE_ENABLE);
+			return 0;
+		case MB_PCI:
+			MB_BIS(bay, OHARE_FCR, OH_BAY_PCI_ENABLE);
+			return 0;
+	}
+	return -ENODEV;
+}
 
-		n++;
-		np=np->next;
+static int __pmac
+heathrow_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
+{
+	switch(device_id) {
+		case MB_FD:
+		case MB_FD1:
+			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_FLOPPY_ENABLE);
+			MB_BIS(bay, HEATHROW_FCR, HRW_SWIM_ENABLE);
+			return 0;
+		case MB_CD:
+			MB_BIC(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
+			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_IDE_ENABLE);
+			return 0;
+		case MB_PCI:
+			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_PCI_ENABLE);
+			return 0;
 	}
+	return -ENODEV;
+}
 
-	if (media_bay_count)
-	{
-		printk(KERN_INFO "Registered %d media-bay(s)\n", media_bay_count);
+static int __pmac
+keylargo_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
+{
+	switch(device_id) {
+		case MB_CD:
+			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_IDE_ENABLE);
+			MB_BIC(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
+			MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_ENABLE);
+			return 0;
+		case MB_PCI:
+			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_PCI_ENABLE);
+			return 0;
+		case MB_SOUND:
+			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_SOUND_ENABLE);
+			return 0;
+	}
+	return -ENODEV;
+}
 
-#ifdef CONFIG_PMAC_PBOOK
-		pmu_register_sleep_notifier(&mb_sleep_notifier);
-#endif /* CONFIG_PMAC_PBOOK */
+/*
+ * Functions for tweaking resets
+ */
 
-		kernel_thread(media_bay_task, NULL,
-			      CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
-	}
+static void __pmac
+ohare_mb_un_reset(struct media_bay_info* bay)
+{
+	MB_BIS(bay, OHARE_FCR, OH_BAY_RESET_N);
 }
 
-#ifdef MB_USE_INTERRUPTS
 static void __pmac
-media_bay_intr(int irq, void *devid, struct pt_regs *regs)
+heathrow_mb_un_reset(struct media_bay_info* bay)
 {
+	MB_BIS(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
+}
+
+static void __pmac
+keylargo_mb_un_reset(struct media_bay_info* bay)
+{
+	MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
+}
+
+static void __pmac
+ohare_mb_un_reset_ide(struct media_bay_info* bay)
+{
+	MB_BIS(bay, OHARE_FCR, OH_IDE1_RESET_N);
+}
+
+static void __pmac
+heathrow_mb_un_reset_ide(struct media_bay_info* bay)
+{
+	MB_BIS(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
 }
-#endif
 
 static void __pmac
-set_mb_power(int which, int onoff)
+keylargo_mb_un_reset_ide(struct media_bay_info* bay)
 {
-	volatile struct media_bay_info*	mb = &media_bays[which];
+	MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
+}
 
+static inline void __pmac
+set_mb_power(struct media_bay_info* bay, int onoff)
+{
+	/* Power up up and assert the bay reset line */
 	if (onoff) {
-		feature_set(mb->dev_node, FEATURE_Mediabay_power);
-		udelay(10);
-		feature_set(mb->dev_node, FEATURE_Mediabay_reset);
-		udelay(10);
-		mb->state = mb_powering_up;
-		MBDBG("mediabay%d: powering up\n", which);
-	} else {
-		feature_clear(mb->dev_node, FEATURE_Mediabay_floppy_enable);
-		if (mb->pismo)
-			feature_clear(mb->dev_node, FEATURE_IDE0_enable);
-		else
-			feature_clear(mb->dev_node, FEATURE_IDE1_enable);
-		feature_clear(mb->dev_node, FEATURE_Mediabay_IDE_switch);
-		feature_clear(mb->dev_node, FEATURE_Mediabay_PCI_enable);
-		feature_clear(mb->dev_node, FEATURE_SWIM3_enable);
-		feature_clear(mb->dev_node, FEATURE_Mediabay_power);
-		mb->state = mb_powering_down;
-		MBDBG("mediabay%d: powering down\n", which);
+		bay->ops->power(bay, 1);
+		bay->state = mb_powering_up;
+		MBDBG("mediabay%d: powering up\n", bay->index);
+	} else { 
+		/* Make sure everything is powered down & disabled */
+		bay->ops->power(bay, 0);
+		bay->state = mb_powering_down;
+		MBDBG("mediabay%d: powering down\n", bay->index);
 	}
-	mb->timer = MS_TO_HZ(MB_POWER_DELAY);
+	bay->timer = MS_TO_HZ(MB_POWER_DELAY);
 }
 
 static void __pmac
-set_media_bay(int which, int id)
+poll_media_bay(struct media_bay_info* bay)
 {
-	volatile struct media_bay_info* bay;
+	int id = bay->ops->content(bay);
 
-	bay = &media_bays[which];
-	
-	switch (id) {
-	case MB_CD:
-		if (bay->pismo) {
-			feature_set(bay->dev_node, FEATURE_Mediabay_IDE_switch);
-			udelay(10);
-			feature_set(bay->dev_node, FEATURE_IDE0_enable);
-			udelay(10);
-			feature_set(bay->dev_node, FEATURE_IDE0_reset);
-		} else {
-			feature_set(bay->dev_node, FEATURE_IDE1_enable);
-			udelay(10);
-			feature_set(bay->dev_node, FEATURE_IDE1_reset);
+	if (id == bay->last_value) {
+	    if (id != bay->content_id
+	        && ++bay->value_count >= MS_TO_HZ(MB_STABLE_DELAY)) {
+	        /* If the device type changes without going thru "MB_NO", we force
+	           a pass by "MB_NO" to make sure things are properly reset */
+	        if ((id != MB_NO) && (bay->content_id != MB_NO)) {
+	            id = MB_NO;
+		    MBDBG("mediabay%d: forcing MB_NO\n", bay->index);
 		}
-		printk(KERN_INFO "media bay %d contains a CD-ROM drive\n", which);
-		break;
-	case MB_FD:
-	case MB_FD1:
-		feature_set(bay->dev_node, FEATURE_Mediabay_floppy_enable);
-		feature_set(bay->dev_node, FEATURE_SWIM3_enable);
-		printk(KERN_INFO "media bay %d contains a floppy disk drive\n", which);
-		break;
-	case MB_NO:
-		break;
-	default:
-		printk(KERN_INFO "media bay %d contains an unknown device (%d)\n",
-		       which, id);
-		break;
+		MBDBG("mediabay%d: switching to %d\n", bay->index, id);
+		set_mb_power(bay, id != MB_NO);
+		bay->content_id = id;
+		if (id == MB_NO) {
+#ifdef CONFIG_BLK_DEV_IDE
+		    bay->cd_retry = 0;
+#endif
+		    printk(KERN_INFO "media bay %d is empty\n", bay->index);
+		}
+ 	    }
+	} else {
+		bay->last_value = id;
+		bay->value_count = 0;
 	}
 }
 
@@ -412,11 +502,11 @@
 static void __pmac
 media_bay_step(int i)
 {
-	volatile struct media_bay_info* bay = &media_bays[i];
+	struct media_bay_info* bay = &media_bays[i];
 
 	/* We don't poll when powering down */
 	if (bay->state != mb_powering_down)
-	    poll_media_bay(i);
+	    poll_media_bay(bay);
 
 	/* If timer expired or polling IDE busy, run state machine */
 	if ((bay->state != mb_ide_waiting) && (bay->timer != 0) && ((--bay->timer) != 0))
@@ -424,13 +514,17 @@
 
 	switch(bay->state) {
 	case mb_powering_up:
-	    	set_media_bay(i, bay->last_value);
+	    	if (bay->ops->setup_bus(bay, bay->last_value) < 0) {
+			MBDBG("mediabay%d: device not supported (kind:%d)\n", i, bay->content_id);
+	    		set_mb_power(bay, 0);
+	    		break;
+	    	}
 	    	bay->timer = MS_TO_HZ(MB_RESET_DELAY);
 	    	bay->state = mb_enabling_bay;
 		MBDBG("mediabay%d: enabling (kind:%d)\n", i, bay->content_id);
 		break;
 	case mb_enabling_bay:
-	    	feature_clear(bay->dev_node, FEATURE_Mediabay_reset);
+		bay->ops->un_reset(bay);
 	    	bay->timer = MS_TO_HZ(MB_SETUP_DELAY);
 	    	bay->state = mb_resetting;
 		MBDBG("mediabay%d: waiting reset (kind:%d)\n", i, bay->content_id);
@@ -444,16 +538,13 @@
 	    	}
 #ifdef CONFIG_BLK_DEV_IDE
 		MBDBG("mediabay%d: waiting IDE reset (kind:%d)\n", i, bay->content_id);
-		if (bay->pismo)
-	    		feature_clear(bay->dev_node, FEATURE_IDE0_reset);
-		else
-	    		feature_clear(bay->dev_node, FEATURE_IDE1_reset);
+		bay->ops->un_reset_ide(bay);
 	    	bay->timer = MS_TO_HZ(MB_IDE_WAIT);
 	    	bay->state = mb_ide_resetting;
 #else
 		printk(KERN_DEBUG "media-bay %d is ide (not compiled in kernel)\n", i);
-		set_mb_power(i, 0);
-#endif // #ifdef CONFIG_BLK_DEV_IDE
+		set_mb_power(bay, 0);
+#endif /* CONFIG_BLK_DEV_IDE */
 	    	break;
 	    
 #ifdef CONFIG_BLK_DEV_IDE
@@ -481,7 +572,7 @@
 				/* We eventually do a retry */
 				bay->cd_retry++;
 				printk("IDE register error\n");
-				set_mb_power(i, 0);
+				set_mb_power(bay, 0);
 			} else {
 				printk(KERN_DEBUG "media-bay %d is ide %d\n", i, bay->cd_index);
 				MBDBG("mediabay %d IDE ready\n", i);
@@ -491,10 +582,10 @@
 	    	if (bay->timer == 0) {
 			printk("\nIDE Timeout in bay %d !\n", i);
 			MBDBG("mediabay%d: nIDE Timeout !\n", i);
-			set_mb_power(i, 0);
+			set_mb_power(bay, 0);
 	    	}
 		break;
-#endif // #ifdef CONFIG_BLK_DEV_IDE
+#endif /* CONFIG_BLK_DEV_IDE */
 
 	case mb_powering_down:
 	    	bay->state = mb_empty;
@@ -514,7 +605,7 @@
 				bay->content_id = MB_NO;
 			}
 	    	}
-#endif	    
+#endif /* CONFIG_BLK_DEV_IDE */    
 		MBDBG("mediabay%d: end of power down\n", i);
 	    	break;
 	}
@@ -526,7 +617,7 @@
  * with the IDE driver.  It needs to be a thread because
  * ide_register can't be called from interrupt context.
  */
-int __pmac
+static int __pmac
 media_bay_task(void *x)
 {
 	int	i;
@@ -547,37 +638,12 @@
 	}
 }
 
-void __pmac
-poll_media_bay(int which)
+#ifdef MB_USE_INTERRUPTS
+static void __pmac
+media_bay_intr(int irq, void *devid, struct pt_regs *regs)
 {
-	volatile struct media_bay_info* bay = &media_bays[which];
-	int id = mb_content(bay);
-
-	if (id == bay->last_value) {
-	    if (id != bay->content_id
-	        && ++bay->value_count >= MS_TO_HZ(MB_STABLE_DELAY)) {
-	        /* If the device type changes without going thru "MB_NO", we force
-	           a pass by "MB_NO" to make sure things are properly reset */
-	        if ((id != MB_NO) && (bay->content_id != MB_NO)) {
-	            id = MB_NO;
-		    MBDBG("mediabay%d: forcing MB_NO\n", which);
-		}
-		MBDBG("mediabay%d: switching to %d\n", which, id);
-		set_mb_power(which, id != MB_NO);
-		bay->content_id = id;
-		if (id == MB_NO) {
-#ifdef CONFIG_BLK_DEV_IDE
-		    bay->cd_retry = 0;
-#endif
-		    printk(KERN_INFO "media bay %d is empty\n", which);
-		}
- 	    }
-	} else {
-		bay->last_value = id;
-		bay->value_count = 0;
-	}
 }
-
+#endif
 
 #ifdef CONFIG_PMAC_PBOOK
 /*
@@ -586,7 +652,7 @@
 int __pmac
 mb_notify_sleep(struct pmu_sleep_notifier *self, int when)
 {
-	volatile struct media_bay_info* bay;
+	struct media_bay_info* bay;
 	int i;
 	
 	switch (when) {
@@ -597,7 +663,7 @@
 	case PBOOK_SLEEP_NOW:
 		for (i=0; i<media_bay_count; i++) {
 			bay = &media_bays[i];
-			set_mb_power(i, 0);
+			set_mb_power(bay, 0);
 			mdelay(10);
 		}
 		break;
@@ -609,14 +675,11 @@
 			   they seem to help the 3400 get it right.
 			 */
 			/* Force MB power to 0 */
-			set_mb_power(i, 0);
+			set_mb_power(bay, 0);
 			mdelay(MB_POWER_DELAY);
-			if (!bay->pismo)
-				out_8(&bay->addr->contents, 0x70);
-			mdelay(MB_STABLE_DELAY);
-			if (mb_content(bay) != bay->content_id)
+			if (bay->ops->content(bay) != bay->content_id)
 				continue;
-			set_mb_power(i, 1);
+			set_mb_power(bay, 1);
 			bay->last_value = bay->content_id;
 			bay->value_count = MS_TO_HZ(MB_STABLE_DELAY);
 			bay->timer = MS_TO_HZ(MB_POWER_DELAY);
@@ -635,3 +698,136 @@
 }
 #endif /* CONFIG_PMAC_PBOOK */
 
+
+/* Definitions of "ops" structures.
+ */
+static struct mb_ops ohare_mb_ops __pmacdata = {
+	name:		"Ohare",
+	content:	ohare_mb_content,
+	power:		ohare_mb_power,
+	setup_bus:	ohare_mb_setup_bus,
+	un_reset:	ohare_mb_un_reset,
+	un_reset_ide:	ohare_mb_un_reset_ide,
+};
+
+static struct mb_ops heathrow_mb_ops __pmacdata = {
+	name:		"Heathrow",
+	content:	heathrow_mb_content,
+	power:		heathrow_mb_power,
+	setup_bus:	heathrow_mb_setup_bus,
+	un_reset:	heathrow_mb_un_reset,
+	un_reset_ide:	heathrow_mb_un_reset_ide,
+};
+
+static struct mb_ops keylargo_mb_ops __pmacdata = {
+	name:		"KeyLargo",
+	content:	keylargo_mb_content,
+	power:		keylargo_mb_power,
+	setup_bus:	keylargo_mb_setup_bus,
+	un_reset:	keylargo_mb_un_reset,
+	un_reset_ide:	keylargo_mb_un_reset_ide,
+};
+
+/*
+ * It seems that the bit for the media-bay interrupt in the IRQ_LEVEL
+ * register is always set when there is something in the media bay.
+ * This causes problems for the interrupt code if we attach an interrupt
+ * handler to the media-bay interrupt, because it tends to go into
+ * an infinite loop calling the media bay interrupt handler.
+ * Therefore we do it all by polling the media bay once each tick.
+ */
+
+static int __init
+media_bay_init(void)
+{
+	struct device_node *np;
+	int		n,i;
+	
+	for (i=0; i<MAX_BAYS; i++) {
+		memset((char *)&media_bays[i], 0, sizeof(struct media_bay_info));
+		media_bays[i].content_id	= -1;
+#ifdef CONFIG_BLK_DEV_IDE
+		media_bays[i].cd_index		= -1;
+#endif
+	}
+	
+	np = find_devices("media-bay");
+	n = 0;
+	while(np && (n<MAX_BAYS)) {
+		struct media_bay_info* bay = &media_bays[n];
+		if (!np->parent || np->n_addrs == 0 || !request_OF_resource(np, 0, NULL)) {
+			np = np->next;
+			printk(KERN_ERR "media-bay: Can't request IO resource !\n");
+			continue;
+		}
+		bay->mb_type = mb_ohare;
+
+		if (device_is_compatible(np, "keylargo-media-bay")) {
+			bay->mb_type = mb_keylargo;
+			bay->ops = &keylargo_mb_ops;
+		} else if (device_is_compatible(np, "heathrow-media-bay")) {
+			bay->mb_type = mb_heathrow;
+			bay->ops = &heathrow_mb_ops;
+		} else if (device_is_compatible(np, "ohare-media-bay")) {
+			bay->mb_type = mb_ohare;
+			bay->ops = &ohare_mb_ops;
+		} else {
+			printk(KERN_ERR "mediabay: Unknown bay type !\n");
+			np = np->next;
+			continue;
+		}
+		bay->base = (volatile u32*)ioremap(np->parent->addrs[0].address, 0x1000);
+
+		/* Enable probe logic on keylargo */
+		if (bay->mb_type == mb_keylargo)
+			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_ENABLE);
+#ifdef MB_USE_INTERRUPTS
+		if (np->n_intrs == 0) {
+			printk(KERN_ERR "media bay %d has no irq\n",n);
+			np = np->next;
+			continue;
+		}
+
+		if (request_irq(np->intrs[0].line, media_bay_intr, 0, "Media bay", (void *)n)) {
+			printk(KERN_ERR "Couldn't get IRQ %d for media bay %d\n",
+				np->intrs[0].line, n);
+			np = np->next;
+			continue;
+		}
+#endif	
+		media_bay_count++;
+	
+		printk(KERN_INFO "mediabay%d: Registered %s media-bay\n", n, bay->ops->name);
+		bay->dev_node = np;
+		bay->index = n;
+
+		/* Force an immediate detect */
+		set_mb_power(bay, 0);
+		mdelay(MB_POWER_DELAY);
+		bay->content_id = MB_NO;
+		bay->last_value = bay->ops->content(bay);
+		bay->value_count = MS_TO_HZ(MB_STABLE_DELAY);
+		bay->state = mb_empty;
+		do {
+			mdelay(1000/HZ);
+			media_bay_step(n);
+		} while((bay->state != mb_empty) &&
+			(bay->state != mb_up));
+
+		n++;
+		np=np->next;
+	}
+
+	if (media_bay_count)
+	{
+#ifdef CONFIG_PMAC_PBOOK
+		pmu_register_sleep_notifier(&mb_sleep_notifier);
+#endif /* CONFIG_PMAC_PBOOK */
+
+		kernel_thread(media_bay_task, NULL,
+			      CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+	}
+	return 0;
+}
+
+device_initcall(media_bay_init);
diff -Nru a/drivers/macintosh/rtc.c b/drivers/macintosh/rtc.c
--- a/drivers/macintosh/rtc.c	Sat May 18 00:46:09 2002
+++ b/drivers/macintosh/rtc.c	Sat May 18 00:46:09 2002
@@ -34,14 +34,13 @@
 void get_rtc_time(struct rtc_time *t)
 {
 	unsigned long nowtime;
-    
+
 	nowtime = (ppc_md.get_rtc_time)();
 
 	to_tm(nowtime, t);
 
 	t->tm_year -= 1900;
-	t->tm_mon -= 1;
-	t->tm_wday -= 1;
+	t->tm_mon -= 1; /* Make sure userland has a 0-based month */
 }
 
 /* Set the current date and time in the real time clock. */
@@ -49,7 +48,8 @@
 {
 	unsigned long nowtime;
 
-	nowtime = mktime(t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+	nowtime = mktime(t->tm_year+1900, t->tm_mon+1, t->tm_mday,
+			t->tm_hour, t->tm_min, t->tm_sec);
 
 	(ppc_md.set_rtc_time)(nowtime);
 }
diff -Nru a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c
--- a/drivers/macintosh/via-cuda.c	Sat May 18 00:46:08 2002
+++ b/drivers/macintosh/via-cuda.c	Sat May 18 00:46:08 2002
@@ -125,7 +125,7 @@
 #endif /* CONFIG_ADB */
 
 #ifdef CONFIG_PPC
-int
+int __init
 find_via_cuda(void)
 {
     int err;
@@ -186,11 +186,13 @@
 }
 #endif /* CONFIG_PPC */
 
-int via_cuda_start(void)
+static int __init via_cuda_start(void)
 {
     if (via == NULL)
 	return -ENODEV;
 
+    request_OF_resource(vias, 0, NULL);
+
     if (request_irq(CUDA_IRQ, cuda_interrupt, 0, "ADB", cuda_interrupt)) {
 	printk(KERN_ERR "cuda_init: can't get irq %d\n", CUDA_IRQ);
 	return -EAGAIN;
@@ -202,6 +204,8 @@
     return 0;
 }
 
+device_initcall(via_cuda_start);
+
 #ifdef CONFIG_ADB
 static int
 cuda_probe()
@@ -217,7 +221,7 @@
     return 0;
 }
 
-static int
+static int __init
 cuda_init(void)
 {
     if (via == NULL)
diff -Nru a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
--- a/drivers/macintosh/via-pmu.c	Sat May 18 00:46:09 2002
+++ b/drivers/macintosh/via-pmu.c	Sat May 18 00:46:09 2002
@@ -9,9 +9,9 @@
  * and the RTC (real time clock) chip.
  *
  * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi.
+ * Copyright (C) 2001 Benjamin Herrenschmidt
  * 
- * todo: - Check this driver for smp safety (new Core99 motherboards).
- *       - Cleanup synchro between VIA interrupt and GPIO-based PMU
+ * todo: - Cleanup synchro between VIA interrupt and GPIO-based PMU
  *         interrupt.
  *
  *
@@ -45,10 +45,12 @@
 #include <asm/sections.h>
 #include <asm/irq.h>
 #include <asm/hardirq.h>
-#include <asm/feature.h>
+#include <asm/pmac_feature.h>
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
+#include <asm/sections.h>
 #include <asm/cputable.h>
+#include <asm/time.h>
 #ifdef CONFIG_PMAC_BACKLIGHT
 #include <asm/backlight.h>
 #endif
@@ -56,9 +58,14 @@
 /* Some compile options */
 #undef SUSPEND_USES_PMU
 #define DEBUG_SLEEP
+#undef HACKED_PCI_SAVE
+#define NEW_OHARE_CODE
 
 /* Misc minor number allocated for /dev/pmu */
-#define PMU_MINOR	154
+#define PMU_MINOR		154
+
+/* How many iterations between battery polls */
+#define BATTERY_POLLING_COUNT	2
 
 static volatile unsigned char *via;
 
@@ -108,7 +115,7 @@
 static struct adb_request *current_req;
 static struct adb_request *last_req;
 static struct adb_request *req_awaiting_reply;
-static unsigned char interrupt_data[32];
+static unsigned char interrupt_data[256]; /* Made bigger: I've been told that might happen */
 static unsigned char *reply_ptr;
 static int data_index;
 static int data_len;
@@ -126,9 +133,30 @@
 static int pmu_version;
 static int drop_interrupts;
 #ifdef CONFIG_PMAC_PBOOK
+static int option_lid_wakeup = 1;
 static int sleep_in_progress;
+static int can_sleep;
+#endif /* CONFIG_PMAC_PBOOK */
+
+static struct proc_dir_entry *proc_pmu_root;
+static struct proc_dir_entry *proc_pmu_info;
+static struct proc_dir_entry *proc_pmu_options;
+
+#ifdef CONFIG_PMAC_PBOOK
+int pmu_battery_count;
+int pmu_cur_battery;
+unsigned int pmu_power_flags;
+struct pmu_battery_info pmu_batteries[PMU_MAX_BATTERIES];
+static int query_batt_timer = BATTERY_POLLING_COUNT;
+static struct adb_request batt_req;
+static struct proc_dir_entry *proc_pmu_batt[PMU_MAX_BATTERIES];
 #endif /* CONFIG_PMAC_PBOOK */
 
+#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT)
+extern int disable_kernel_backlight;
+#endif /* defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) */
+
+int __fake_sleep;
 int asleep;
 struct notifier_block *sleep_notifier_list;
 
@@ -153,15 +181,22 @@
 static void pmu_done(struct adb_request *req);
 static void pmu_handle_data(unsigned char *data, int len,
 			    struct pt_regs *regs);
-static void set_volume(int level);
 static void gpio1_interrupt(int irq, void *arg, struct pt_regs *regs);
+static int proc_get_info(char *page, char **start, off_t off,
+			  int count, int *eof, void *data);
 #ifdef CONFIG_PMAC_BACKLIGHT
 static int pmu_set_backlight_level(int level, void* data);
 static int pmu_set_backlight_enable(int on, int level, void* data);
 #endif /* CONFIG_PMAC_BACKLIGHT */
 #ifdef CONFIG_PMAC_PBOOK
 static void pmu_pass_intr(unsigned char *data, int len);
-#endif
+static int proc_get_batt(char *page, char **start, off_t off,
+			int count, int *eof, void *data);
+#endif /* CONFIG_PMAC_PBOOK */
+static int proc_read_options(char *page, char **start, off_t off,
+			int count, int *eof, void *data);
+static int proc_write_options(struct file *file, const char *buffer,
+			unsigned long count, void *data);
 
 #ifdef CONFIG_ADB
 struct adb_driver via_pmu_driver = {
@@ -309,8 +344,12 @@
 	} else
 		pmu_kind = PMU_UNKNOWN;
 
+#ifdef CONFIG_PMAC_PBOOK
+	if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0)
+		can_sleep = 1;
+#endif /* CONFIG_PMAC_PBOOK */
 	via = (volatile unsigned char *) ioremap(vias->addrs->address, 0x2000);
-
+	
 	out_8(&via[IER], IER_CLR | 0x7f);	/* disable all intrs */
 	out_8(&via[IFR], 0x7f);			/* clear IFR */
 
@@ -341,7 +380,7 @@
 	return vias == NULL? -ENODEV: 0;
 }
 
-static int __openfirmware
+static int __init
 pmu_init(void)
 {
 	if (vias == NULL)
@@ -355,9 +394,9 @@
  * It happens after IDE and SCSI initialization, which can take a few
  * seconds, and by that time the PMU could have given up on us and
  * turned us off.
- * This is called from arch/ppc/kernel/pmac_setup.c:pmac_init2().
+ * Thus this is called with arch_initcall rather than device_initcall.
  */
-int via_pmu_start(void)
+static int __init via_pmu_start(void)
 {
 	if (vias == NULL)
 		return -ENODEV;
@@ -365,6 +404,9 @@
 	bright_req_1.complete = 1;
 	bright_req_2.complete = 1;
 	bright_req_3.complete = 1;
+#ifdef CONFIG_PMAC_PBOOK
+	batt_req.complete = 1;
+#endif
 
 	if (request_irq(vias->intrs[0].line, via_pmu_interrupt, 0, "VIA-PMU",
 			(void *)0)) {
@@ -383,11 +425,6 @@
 
 	pmu_fully_inited = 1;
 
-#ifdef CONFIG_PMAC_BACKLIGHT
-	/* Enable backlight */
-	register_backlight_controller(&pmu_backlight_controller, NULL, "pmu");
-#endif /* CONFIG_PMAC_BACKLIGHT */
-
 	/* Make sure PMU settle down before continuing. This is _very_ important
 	 * since the IDE probe may shut interrupts down for quite a bit of time. If
 	 * a PMU communication is pending while this happens, the PMU may timeout
@@ -403,6 +440,66 @@
 	return 0;
 }
 
+arch_initcall(via_pmu_start);
+
+/*
+ * This has to be done after pci_init, which is a subsys_initcall.
+ */
+static int __init via_pmu_dev_init(void)
+{
+	if (vias == NULL)
+		return -ENODEV;
+
+	request_OF_resource(vias, 0, NULL);
+#ifdef CONFIG_PMAC_BACKLIGHT
+	/* Enable backlight */
+	register_backlight_controller(&pmu_backlight_controller, NULL, "pmu");
+#endif /* CONFIG_PMAC_BACKLIGHT */
+
+#ifdef CONFIG_PMAC_PBOOK
+  	if (machine_is_compatible("AAPL,3400/2400") ||
+  		machine_is_compatible("AAPL,3500"))
+		pmu_battery_count = 1;
+	else if (machine_is_compatible("AAPL,PowerBook1998") ||
+		machine_is_compatible("PowerBook1,1"))
+		pmu_battery_count = 2;
+	else {
+		struct device_node* prim = find_devices("power-mgt");
+		u32 *prim_info = NULL;
+		if (prim)
+			prim_info = (u32 *)get_property(prim, "prim-info", NULL);
+		if (prim_info) {
+			/* Other stuffs here yet unknown */
+			pmu_battery_count = (prim_info[6] >> 16) & 0xff;
+		}
+	}
+#endif /* CONFIG_PMAC_PBOOK */
+	/* Create /proc/pmu */
+	proc_pmu_root = proc_mkdir("pmu", 0);
+	if (proc_pmu_root) {
+		int i;
+		proc_pmu_info = create_proc_read_entry("info", 0, proc_pmu_root,
+					proc_get_info, NULL);
+#ifdef CONFIG_PMAC_PBOOK
+		for (i=0; i<pmu_battery_count; i++) {
+			char title[16];
+			sprintf(title, "battery_%d", i);
+			proc_pmu_batt[i] = create_proc_read_entry(title, 0, proc_pmu_root,
+						proc_get_batt, (void *)i);
+		}
+#endif /* CONFIG_PMAC_PBOOK */
+		proc_pmu_options = create_proc_entry("options", 0600, proc_pmu_root);
+		if (proc_pmu_options) {
+			proc_pmu_options->nlink = 1;
+			proc_pmu_options->read_proc = proc_read_options;
+			proc_pmu_options->write_proc = proc_write_options;
+		}
+	}
+	return 0;
+}
+
+device_initcall(via_pmu_dev_init);
+
 static int __openfirmware
 init_pmu()
 {
@@ -448,8 +545,8 @@
 	pmu_request(&req, NULL, 1, PMU_GET_VERSION);
 	while (!req.complete)
 		pmu_poll();
-	if (req.reply_len > 1)
-		pmu_version = req.reply[1];
+	if (req.reply_len > 0)
+		pmu_version = req.reply[0];
 
 	return 1;
 }
@@ -460,6 +557,420 @@
 	return pmu_kind;
 }
 
+#ifdef CONFIG_PMAC_PBOOK
+
+/* 
+ * WARNING ! This code probably needs some debugging... -- BenH.
+ */
+#ifdef NEW_OHARE_CODE
+static void __pmac
+done_battery_state_ohare(struct adb_request* req)
+{
+	unsigned int bat_flags = 0;
+	int current = 0;
+	unsigned int capa, max, voltage, time;
+	int lrange[] = { 0, 275, 850, 1680, 2325, 
+				2765, 3160, 3500, 3830, 4115, 
+				4360, 4585, 4795, 4990, 5170, 
+				5340, 5510, 5710, 5930, 6150, 
+				6370, 6500
+				};
+	
+	if (req->reply[0] & 0x01)
+		pmu_power_flags |= PMU_PWR_AC_PRESENT;
+	else
+		pmu_power_flags &= ~PMU_PWR_AC_PRESENT;
+
+	if (req->reply[0] & 0x04) {
+		int vb, i, j, k, charge, pcharge;
+		bat_flags |= PMU_BATT_PRESENT;
+		vb = (req->reply[1] << 8) | req->reply[2];
+		voltage = ((vb * 2650) + 726650)/100;
+		vb *= 100;
+		current = req->reply[5];
+		if ((req->reply[0] & 0x01) == 0 && (current > 200))
+			vb += (current - 200) * 15;
+		else if (req->reply[0] & 0x02)
+			vb = (vb - 2000);
+	  	i = (33000 - vb) / 10;
+	  	j = i - (i % 100);
+		k = j/100;
+		if (k <= 0)
+	       		charge = 0;
+		else if (k >= 21)
+	       		charge = 650000;
+	  	else
+			charge = (lrange[k + 1] - lrange[k]) * (i - j) + (lrange[k] * 100);
+		charge = (1000 - charge / 650) / 10;
+		if (req->reply[0] & 0x40) {
+			pcharge = (req->reply[6] << 8) + req->reply[7];
+			if (pcharge > 6500)
+				pcharge = 6500;
+			pcharge *= 100;
+			pcharge = (1000 - pcharge / 650) / 10;
+			if (pcharge < charge)
+				charge = pcharge;
+		}
+		capa = charge;
+		max = 100;
+		time = (charge * 16440) / current;
+		current = -current;
+		
+	} else
+		capa = max = current = voltage = time = 0;
+
+	if (req->reply[0] & 0x02)
+		bat_flags |= PMU_BATT_CHARGING;
+
+	pmu_batteries[pmu_cur_battery].flags = bat_flags;
+	pmu_batteries[pmu_cur_battery].charge = capa;
+	pmu_batteries[pmu_cur_battery].max_charge = max;
+	pmu_batteries[pmu_cur_battery].current = current;
+	pmu_batteries[pmu_cur_battery].voltage = voltage;
+	pmu_batteries[pmu_cur_battery].time_remaining = time;
+}
+#else /* NEW_OHARE_CODE */
+static void __pmac
+done_battery_state_ohare(struct adb_request* req)
+{
+	unsigned int bat_flags = 0;
+	int current = 0;
+	unsigned int capa, max, voltage, time;
+	int lrange[] = { 0, 275, 850, 1680, 2325, 
+				2765, 3160, 3500, 3830, 4115, 
+				4360, 4585, 4795, 4990, 5170, 
+				5340, 5510, 5710, 5930, 6150, 
+				6370, 6500
+				};
+	
+	if (req->reply[0] & 0x01)
+		pmu_power_flags |= PMU_PWR_AC_PRESENT;
+	else
+		pmu_power_flags &= ~PMU_PWR_AC_PRESENT;
+
+	if (req->reply[0] & 0x04) {
+		int vb, i, j, charge, pcharge;
+		bat_flags |= PMU_BATT_PRESENT;
+		vb = (req->reply[1] << 8) | req->reply[2];
+		voltage = ((vb * 2650) + 726650)/100;
+		current = *((signed char *)&req->reply[5]);
+		if ((req->reply[0] & 0x01) == 0 && (current > 200))
+			vb += (current - 200) * 15;
+		else if (req->reply[0] & 0x02)
+			vb = (vb - 10) * 100;
+	  	i = (33000 - vb) / 10;
+	  	j = i - (i % 100);
+	  	if (j <= 0)
+	       		charge = 0;
+	  	else if (j >= 21)
+	       		charge = 650000;
+	  	else
+			charge = (lrange[j + 1] - lrange[j]) * (i - j) + (lrange[j] * 100);
+		charge = (1000 - charge / 650) / 10;
+		if (req->reply[0] & 0x40) {
+			pcharge = (req->reply[6] << 8) + req->reply[7];
+			if (pcharge > 6500)
+				pcharge = 6500;
+			pcharge *= 100;
+			pcharge = (1000 - pcharge / 650) / 10;
+			if (pcharge < charge)
+				charge = pcharge;
+		}
+		capa = charge;
+		max = 100;
+		time = (charge * 274) / current;
+		current = -current;
+		
+	} else
+		capa = max = current = voltage = time = 0;
+
+	if ((req->reply[0] & 0x02) && (current > 0))
+		bat_flags |= PMU_BATT_CHARGING;
+	if (req->reply[0] & 0x04) /* CHECK THIS ONE */
+		bat_flags |= PMU_BATT_PRESENT;
+
+	pmu_batteries[pmu_cur_battery].flags = bat_flags;
+	pmu_batteries[pmu_cur_battery].charge = capa;
+	pmu_batteries[pmu_cur_battery].max_charge = max;
+	pmu_batteries[pmu_cur_battery].current = current;
+	pmu_batteries[pmu_cur_battery].voltage = voltage;
+	pmu_batteries[pmu_cur_battery].time_remaining = time;
+}
+#endif /* NEW_OHARE_CODE */
+
+ static void __pmac
+done_battery_state_comet(struct adb_request* req)
+{
+	/* format:
+	 *  [0]    :  flags
+	 *    0x01 :  AC indicator
+	 *    0x02 :  charging
+	 *    0x04 :  battery exist
+	 *    0x08 :  
+	 *    0x10 :  
+	 *    0x20 :  full charged
+	 *    0x40 :  pcharge reset
+	 *    0x80 :  battery exist
+	 *
+	 *  [1][2] :  battery voltage
+	 *  [3]    :  CPU temperature
+	 *  [4]    :  battery temperature
+	 *  [5]    :  current
+	 *  [6][7] :  pcharge
+	 *              --tkoba
+	 */
+
+	unsigned int bat_flags = 0;
+	int current = 0;
+	unsigned int max = 100;
+	unsigned int charge, voltage, time;
+	int lrange[] = { 0, 600, 750, 900, 1000, 1080, 
+				1180, 1250, 1300, 1340, 1360, 
+				1390, 1420, 1440, 1470, 1490, 
+				1520, 1550, 1580, 1610, 1650, 
+				1700
+				};
+	
+	if (req->reply[0] & 0x01)
+		pmu_power_flags |= PMU_PWR_AC_PRESENT;
+	else
+		pmu_power_flags &= ~PMU_PWR_AC_PRESENT;
+
+	if (req->reply[0] & 0x04) {	/* battery exist */
+		int vb, i;
+		bat_flags |= PMU_BATT_PRESENT;
+		vb = (req->reply[1] << 8) | req->reply[2];
+		voltage = ((vb * 2650) + 726650)/100;
+		vb *= 10;
+		current = req->reply[5];
+		if ((req->reply[0] & 0x01) == 0 && (current > 200))
+			vb += ((current - 200) * 3);	/* vb = 500<->1800 */
+		else if (req->reply[0] & 0x02)
+			vb = ((vb - 800) * 1700/13)/100;	/*  in charging vb = 1300<->2130 */
+
+		if (req->reply[0] & 0x20) {	/* full charged */
+			charge = max;
+		} else {
+			if (lrange[21] < vb)
+				charge = max;
+			else {
+				if (vb < lrange[1])
+					charge = 0;
+				else {
+					for (i=21; vb < lrange[i]; --i);
+					charge = (i * 100)/21;
+				}
+			}
+			if (charge > max) charge = max;
+		}
+		time = (charge * 72);
+		current = -current;
+	} else
+		max = current = voltage = time = 0;
+
+	if (req->reply[0] & 0x02)
+		bat_flags |= PMU_BATT_CHARGING;
+
+	pmu_batteries[pmu_cur_battery].flags = bat_flags;
+	pmu_batteries[pmu_cur_battery].charge = charge;
+	pmu_batteries[pmu_cur_battery].max_charge = max;
+	pmu_batteries[pmu_cur_battery].current = current;
+	pmu_batteries[pmu_cur_battery].voltage = voltage;
+	pmu_batteries[pmu_cur_battery].time_remaining = time;
+}
+
+static void __pmac
+done_battery_state_smart(struct adb_request* req)
+{
+	/* format:
+	 *  [0] : format of this structure (known: 3,4,5)
+	 *  [1] : flags
+	 *  
+	 *  format 3 & 4:
+	 *  
+	 *  [2] : charge
+	 *  [3] : max charge
+	 *  [4] : current
+	 *  [5] : voltage
+	 *  
+	 *  format 5:
+	 *  
+	 *  [2][3] : charge
+	 *  [4][5] : max charge
+	 *  [6][7] : current
+	 *  [8][9] : voltage
+	 */
+	 
+	unsigned int bat_flags = 0;
+	int current;
+	unsigned int capa, max, voltage;
+	
+	if (req->reply[1] & 0x01)
+		pmu_power_flags |= PMU_PWR_AC_PRESENT;
+	else
+		pmu_power_flags &= ~PMU_PWR_AC_PRESENT;
+
+
+	if (req->reply[1] & 0x04) {
+		bat_flags |= PMU_BATT_PRESENT;
+		switch(req->reply[0]) {
+			case 3:
+			case 4: capa = req->reply[2];
+				max = req->reply[3];
+				current = *((signed char *)&req->reply[4]);
+				voltage = req->reply[5];
+				break;
+			case 5: capa = (req->reply[2] << 8) | req->reply[3];
+				max = (req->reply[4] << 8) | req->reply[5];
+				current = *((signed short *)&req->reply[6]);
+				voltage = (req->reply[8] << 8) | req->reply[9];
+				break;
+			default:
+				printk(KERN_WARNING "pmu.c : unrecognized battery info, len: %d, %02x %02x %02x %02x\n",
+					req->reply_len, req->reply[0], req->reply[1], req->reply[2], req->reply[3]);
+				break;
+		}
+	} else
+		capa = max = current = voltage = 0;
+
+	if ((req->reply[1] & 0x01) && (current > 0))
+		bat_flags |= PMU_BATT_CHARGING;
+
+	pmu_batteries[pmu_cur_battery].flags = bat_flags;
+	pmu_batteries[pmu_cur_battery].charge = capa;
+	pmu_batteries[pmu_cur_battery].max_charge = max;
+	pmu_batteries[pmu_cur_battery].current = current;
+	pmu_batteries[pmu_cur_battery].voltage = voltage;
+	if (current) {
+		if ((req->reply[1] & 0x01) && (current > 0))
+			pmu_batteries[pmu_cur_battery].time_remaining
+				= ((max-capa) * 3600) / current;
+		else
+			pmu_batteries[pmu_cur_battery].time_remaining
+				= (capa * 3600) / (-current);
+	} else
+		pmu_batteries[pmu_cur_battery].time_remaining = 0;
+
+	pmu_cur_battery = (pmu_cur_battery + 1) % pmu_battery_count;
+}
+
+static void __pmac
+query_battery_state(void)
+{
+	if (!batt_req.complete)
+		return;
+	if (pmu_kind == PMU_OHARE_BASED) {
+		int mb = pmac_call_feature(PMAC_FTR_GET_MB_INFO,
+			NULL, PMAC_MB_INFO_MODEL, 0);
+
+		if (mb == PMAC_TYPE_COMET)
+			pmu_request(&batt_req, done_battery_state_comet,
+				1, PMU_BATTERY_STATE);
+		else
+			pmu_request(&batt_req, done_battery_state_ohare,
+				1, PMU_BATTERY_STATE);
+	} else
+		pmu_request(&batt_req, done_battery_state_smart,
+			2, PMU_SMART_BATTERY_STATE, pmu_cur_battery+1);
+}
+
+#endif /* CONFIG_PMAC_PBOOK */
+
+static int
+proc_get_info(char *page, char **start, off_t off,
+		int count, int *eof, void *data)
+{
+	char* p = page;
+
+	p += sprintf(p, "PMU driver version     : %d\n", PMU_DRIVER_VERSION);
+	p += sprintf(p, "PMU firmware version   : %02x\n", pmu_version);
+#ifdef CONFIG_PMAC_PBOOK
+	p += sprintf(p, "AC Power               : %d\n",
+		((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0));
+	p += sprintf(p, "Battery count          : %d\n", pmu_battery_count);
+#endif /* CONFIG_PMAC_PBOOK */
+
+	return p - page;
+}
+
+#ifdef CONFIG_PMAC_PBOOK
+static int
+proc_get_batt(char *page, char **start, off_t off,
+		int count, int *eof, void *data)
+{
+	int batnum = (int)data;
+	char *p = page;
+	
+	p += sprintf(p, "\n");
+	p += sprintf(p, "flags      : %08x\n",
+		pmu_batteries[batnum].flags);
+	p += sprintf(p, "charge     : %d\n",
+		pmu_batteries[batnum].charge);
+	p += sprintf(p, "max_charge : %d\n",
+		pmu_batteries[batnum].max_charge);
+	p += sprintf(p, "current    : %d\n",
+		pmu_batteries[batnum].current);
+	p += sprintf(p, "voltage    : %d\n",
+		pmu_batteries[batnum].voltage);
+	p += sprintf(p, "time rem.  : %d\n",
+		pmu_batteries[batnum].time_remaining);
+
+	return p - page;
+}
+#endif /* CONFIG_PMAC_PBOOK */
+
+static int
+proc_read_options(char *page, char **start, off_t off,
+			int count, int *eof, void *data)
+{
+	char *p = page;
+
+#ifdef CONFIG_PMAC_PBOOK
+	if (pmu_kind == PMU_KEYLARGO_BASED && can_sleep)
+		p += sprintf(p, "lid_wakeup=%d\n", option_lid_wakeup);
+#endif /* CONFIG_PMAC_PBOOK */
+
+	return p - page;
+}
+			
+static int
+proc_write_options(struct file *file, const char *buffer,
+			unsigned long count, void *data)
+{
+	char tmp[33];
+	char *label, *val;
+	unsigned long fcount = count;
+	
+	if (!count)
+		return -EINVAL;
+	if (count > 32)
+		count = 32;
+	if (copy_from_user(tmp, buffer, count))
+		return -EFAULT;
+	tmp[count] = 0;
+
+	label = tmp;
+	while(*label == ' ')
+		label++;
+	val = label;
+	while(*val && (*val != '=')) {
+		if (*val == ' ')
+			*val = 0;
+		val++;
+	}
+	if ((*val) == 0)
+		return -EINVAL;
+	*(val++) = 0;
+	while(*val == ' ')
+		val++;
+#ifdef CONFIG_PMAC_PBOOK
+	if (pmu_kind == PMU_KEYLARGO_BASED && can_sleep)
+		if (!strcmp(label, "lid_wakeup"))
+			option_lid_wakeup = ((*val) == '1');
+#endif /* CONFIG_PMAC_PBOOK */
+	return fcount;
+}
+
 #ifdef CONFIG_ADB
 /* Send an ADB command */
 static int __openfirmware
@@ -622,11 +1133,7 @@
 	for (i = 0; i < nbytes; ++i)
 		req->data[i] = va_arg(list, int);
 	va_end(list);
-	if (pmu_data_len[req->data[0]][1] != 0) {
-		req->reply[0] = ADB_RET_OK;
-		req->reply_len = 1;
-	} else
-		req->reply_len = 0;
+	req->reply_len = 0;
 	req->reply_expected = 0;
 	return pmu_queue_request(req);
 }
@@ -834,28 +1341,23 @@
 	/* This is a bit brutal, we can probably do better */
 	spin_lock_irqsave(&pmu_lock, flags);
 	++disable_poll;
-		
-	while ((intr = in_8(&via[IFR])) != 0) {
+	
+	for (;;) {
+		intr = in_8(&via[IFR]) & (SR_INT | CB1_INT);
+		if (intr == 0)
+			break;
 		if (++nloop > 1000) {
 			printk(KERN_DEBUG "PMU: stuck in intr loop, "
-			       "intr=%x pmu_state=%d\n", intr, pmu_state);
+			       "intr=%x, ier=%x pmu_state=%d\n",
+			       intr, in_8(&via[IER]), pmu_state);
 			break;
 		}
 		out_8(&via[IFR], intr);
 		if (intr & SR_INT)
 			pmu_sr_intr(regs);
-		else if (intr & CB1_INT)
+		if (intr & CB1_INT)
 			adb_int_pending = 1;
 	}
-	/* This is not necessary except if synchronous ADB requests are done
-	 * with interrupts off, which should not happen. Since I'm not sure
-	 * this "wiring" will remain, I'm commenting it out for now. Please do
-	 * not remove. -- BenH.
-	 */
-#if 0
-	if (gpio_reg && !pmu_suspended && (in_8(gpio_reg + 0x9) & 0x02) == 0)
-		adb_int_pending = 1;
-#endif
 
 	if (pmu_state == idle) {
 		if (adb_int_pending) {
@@ -866,9 +1368,8 @@
 			wait_for_ack();
 			send_byte(PMU_INT_ACK);
 			adb_int_pending = 0;
-		} else if (current_req) {
+		} else if (current_req)
 			pmu_start();
-		}
 	}
 	
 	--disable_poll;
@@ -878,8 +1379,10 @@
 static void __openfirmware
 gpio1_interrupt(int irq, void *arg, struct pt_regs *regs)
 {
-	adb_int_pending = 1;
-	via_pmu_interrupt(0, 0, 0);
+	if ((in_8(gpio_reg + 0x9) & 0x02) == 0) {
+		adb_int_pending = 1;
+		via_pmu_interrupt(0, 0, 0);
+	}
 }
 
 static void __openfirmware
@@ -1040,16 +1543,28 @@
 				adb_input(data+1, len-1, regs, 1);
 #endif /* CONFIG_ADB */		
 		}
-	} else if (data[0] == 0x08 && len == 3) {
-		/* sound/brightness buttons pressed */
-#ifdef CONFIG_PMAC_BACKLIGHT
-		set_backlight_level(data[1] >> 4);
-#endif
-		set_volume(data[2]);
 	} else {
+		/* Sound/brightness button pressed */
+		if ((data[0] & PMU_INT_SNDBRT) && len == 3) {
+#ifdef CONFIG_PMAC_BACKLIGHT
+#ifdef CONFIG_INPUT_ADBHID
+			if (!disable_kernel_backlight)
+#endif /* CONFIG_INPUT_ADBHID */
+				set_backlight_level(data[1] >> 4);
+#endif /* CONFIG_PMAC_BACKLIGHT */
+		}
 #ifdef CONFIG_PMAC_PBOOK
-		pmu_pass_intr(data, len);
-#endif
+		/* Environement or tick interrupt, query batteries */
+		if (pmu_battery_count && (data[0] & PMU_INT_TICK)) {
+			if ((--query_batt_timer) == 0) {
+				query_battery_state();
+				query_batt_timer = BATTERY_POLLING_COUNT;
+			}
+		} else if (pmu_battery_count && (data[0] & PMU_INT_ENVIRONMENT))
+			query_battery_state();
+ 		if (data[0])
+			pmu_pass_intr(data, len);
+#endif /* CONFIG_PMAC_PBOOK */
 	}
 }
 
@@ -1116,11 +1631,6 @@
 		pmu_poll();
 }
 
-static void __openfirmware
-set_volume(int level)
-{
-}
-
 void __openfirmware
 pmu_restart(void)
 {
@@ -1266,50 +1776,96 @@
  * PCI devices which may get powered off when we sleep.
  */
 static struct pci_save {
+#ifndef HACKED_PCI_SAVE
 	u16	command;
 	u16	cache_lat;
 	u16	intr;
 	u32	rom_address;
+#else
+	u32	config[16];
+#endif	
 } *pbook_pci_saves;
-static int n_pbook_pci_saves;
+static int pbook_npci_saves;
 
 static void __openfirmware
-pbook_pci_save(void)
+pbook_alloc_pci_save(void)
 {
 	int npci;
 	struct pci_dev *pd;
-	struct pci_save *ps;
 
 	npci = 0;
 	pci_for_each_dev(pd) {
 		++npci;
 	}
-	n_pbook_pci_saves = npci;
 	if (npci == 0)
 		return;
-	ps = (struct pci_save *) kmalloc(npci * sizeof(*ps), GFP_KERNEL);
-	pbook_pci_saves = ps;
+	pbook_pci_saves = (struct pci_save *)
+		kmalloc(npci * sizeof(struct pci_save), GFP_KERNEL);
+	pbook_npci_saves = npci;
+}
+
+static void __openfirmware
+pbook_free_pci_save(void)
+{
+	if (pbook_pci_saves == NULL)
+		return;
+	kfree(pbook_pci_saves);
+	pbook_pci_saves = NULL;
+	pbook_npci_saves = 0;
+}
+
+static void __openfirmware
+pbook_pci_save(void)
+{
+	struct pci_save *ps = pbook_pci_saves;
+	struct pci_dev *pd;
+	int npci = pbook_npci_saves;
+	
 	if (ps == NULL)
 		return;
 
 	pci_for_each_dev(pd) {
+		if (npci-- == 0)
+			return;
+#ifndef HACKED_PCI_SAVE
 		pci_read_config_word(pd, PCI_COMMAND, &ps->command);
 		pci_read_config_word(pd, PCI_CACHE_LINE_SIZE, &ps->cache_lat);
 		pci_read_config_word(pd, PCI_INTERRUPT_LINE, &ps->intr);
 		pci_read_config_dword(pd, PCI_ROM_ADDRESS, &ps->rom_address);
+#else
+		int i;
+		for (i=1;i<16;i++)
+			pci_read_config_dword(pd, i<<4, &ps->config[i]);
+#endif
 		++ps;
 	}
 }
 
+/* For this to work, we must take care of a few things: If gmac was enabled
+ * during boot, it will be in the pci dev list. If it's disabled at this point
+ * (and it will probably be), then you can't access it's config space.
+ */
 static void __openfirmware
 pbook_pci_restore(void)
 {
 	u16 cmd;
 	struct pci_save *ps = pbook_pci_saves - 1;
 	struct pci_dev *pd;
+	int npci = pbook_npci_saves;
 	int j;
 
 	pci_for_each_dev(pd) {
+#ifdef HACKED_PCI_SAVE
+		int i;
+		if (npci-- == 0)
+			return;
+		ps++;
+		for (i=2;i<16;i++)
+			pci_write_config_dword(pd, i<<4, ps->config[i]);
+		pci_write_config_dword(pd, 4, ps->config[1]);
+#else
+		if (npci-- == 0)
+			return;
 		ps++;
 		if (ps->command == 0)
 			continue;
@@ -1330,8 +1886,8 @@
 				ps->intr);
 			pci_write_config_word(pd, PCI_COMMAND, ps->command);
 			break;
-			/* other header types not restored at present */
 		}
+#endif	
 	}
 }
 
@@ -1370,7 +1926,7 @@
 	}
 	mdelay(50);
 }
-#endif /* DEBUG_SLEEP */
+#endif
 
 /*
  * Put the powerbook to sleep.
@@ -1403,6 +1959,17 @@
 	out_8(&via[IER], IER_SET | SR_INT | CB1_INT);
 }
 
+static inline void wakeup_decrementer(void)
+{
+	set_dec(tb_ticks_per_jiffy);
+	/* No currently-supported powerbook has a 601,
+	 * so use get_tbl, not native
+	 */
+	last_jiffy_stamp(0) = tb_last_stamp = get_tbl();
+}
+
+extern int sys_sync(void);
+
 #define	GRACKLE_PM	(1<<7)
 #define GRACKLE_DOZE	(1<<5)
 #define	GRACKLE_NAP	(1<<4)
@@ -1411,7 +1978,6 @@
 int __openfirmware powerbook_sleep_G3(void)
 {
 	unsigned long save_l2cr;
-	unsigned long wait;
 	unsigned short pmcr1;
 	struct adb_request req;
 	int ret, timeout;
@@ -1443,13 +2009,9 @@
 		return -EBUSY;
 	}
 
-	/* Give the disks a little time to actually finish writing */
-	for (wait = jiffies + (HZ/2); time_before(jiffies, wait); )
-		mb();
-
 	/* Wait for completion of async backlight requests */
 	while (!bright_req_1.complete || !bright_req_2.complete ||
-		!bright_req_3.complete)
+		!bright_req_3.complete || !batt_req.complete)
 		pmu_poll();
 	
 	/* Turn off various things. Darwin does some retry tests here... */
@@ -1495,7 +2057,7 @@
 	/* The VIA is supposed not to be restored correctly*/
 	save_via_state();
 	/* We shut down some HW */
-	feature_prepare_for_sleep();
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1);
 
 	pci_read_config_word(grackle, 0x70, &pmcr1);
 	/* Apparently, MacOS uses NAP mode for Grackle ??? */
@@ -1512,7 +2074,7 @@
 	pci_write_config_word(grackle, 0x70, pmcr1);
 
 	/* Make sure the PMU is idle */
-	feature_wake_up();
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
 	restore_via_state();
 	
 	/* Restore L2 cache */
@@ -1522,11 +2084,6 @@
 	/* Restore userland MMU context */
 	set_context(current->active_mm->context, current->active_mm->pgd);
 
-	/* Re-enable DEC interrupts and kick DEC */
-	asm volatile("mtdec %0" : : "r" (0x7fffffff));
-	sti();
-	asm volatile("mtdec %0" : : "r" (0x10000000));
-
 	/* Power things up */
 	pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0xfc);
 	while (!req.complete)
@@ -1558,6 +2115,10 @@
 	/* Leave some time for HW to settle down */
 	mdelay(100);
 
+	/* Restart jiffies & scheduling */
+	wakeup_decrementer();
+	sti();
+
 	/* Notify drivers */
 	broadcast_wake();
 
@@ -1567,11 +2128,10 @@
 int __openfirmware powerbook_sleep_Core99(void)
 {
 	unsigned long save_l2cr;
-	unsigned long wait;
 	struct adb_request req;
 	int ret, timeout;
 	
-	if (!feature_can_sleep()) {
+	if (!can_sleep) {
 		printk(KERN_ERR "Sleep mode not supported on this machine\n");
 		return -ENOSYS;
 	}
@@ -1597,14 +2157,9 @@
 		printk("pmu: sleep failed\n");
 		return -EBUSY;
 	}
-
-	/* Give the disks a little time to actually finish writing */
-	for (wait = jiffies + HZ; time_before(jiffies, wait); )
-		mb();
-
 	/* Wait for completion of async backlight requests */
 	while (!bright_req_1.complete || !bright_req_2.complete ||
-		!bright_req_3.complete)
+		!bright_req_3.complete || !batt_req.complete)
 		pmu_poll();
 
 	/* Tell PMU what events will wake us up */
@@ -1614,7 +2169,8 @@
 		pmu_poll();
 
 	pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_SET_WAKEUP_EVENTS,
-		0, PMU_PWR_WAKEUP_KEY | PMU_PWR_WAKEUP_LID_OPEN);
+		0, PMU_PWR_WAKEUP_KEY |
+		(option_lid_wakeup ? PMU_PWR_WAKEUP_LID_OPEN : 0));
 	while (!req.complete)
 		pmu_poll();
 		
@@ -1651,10 +2207,12 @@
 	/* Save the state of PCI config space for some slots */
 	//pbook_pci_save();
 
-	/* Ask the PMU to put us to sleep */
-	pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
-	while (!req.complete && pmu_state != idle)
-		pmu_poll();
+	if (!__fake_sleep) {
+		/* Ask the PMU to put us to sleep */
+		pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
+		while (!req.complete && pmu_state != idle)
+			pmu_poll();
+	}
 
 	out_8(&via[B], in_8(&via[B]) | TREQ);
 	wait_for_ack();
@@ -1666,13 +2224,16 @@
 	 * talk to the PMU after this, so I moved it to _after_ sending the
 	 * sleep command to it. Still need to be checked.
 	 */
-	feature_prepare_for_sleep();
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1);
 
 	/* Call low-level ASM sleep handler */
-	low_sleep_handler();
+	if (__fake_sleep)
+		mdelay(5000);
+	else
+		low_sleep_handler();
 
 	/* Restore Apple core ASICs state */
-	feature_wake_up();
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
 
 	/* Restore VIA */
 	restore_via_state();
@@ -1694,11 +2255,6 @@
 	/* Restore userland MMU context */
 	set_context(current->active_mm->context, current->active_mm->pgd);
 
-	/* Re-enable DEC interrupts and kick DEC */
-	asm volatile("mtdec %0" : : "r" (0x7fffffff));
-	sti();
-	asm volatile("mtdec %0" : : "r" (0x10000000));
-
 	/* Tell PMU we are ready */
 	pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2);
 	while (!req.complete)
@@ -1725,6 +2281,10 @@
 	/* Leave some time for HW to settle down */
 	mdelay(100);
 
+	/* Restart jiffies & scheduling */
+	wakeup_decrementer();
+	sti();
+
 	/* Notify drivers */
 	broadcast_wake();
 
@@ -1738,7 +2298,7 @@
 {
 	int ret, i, x;
 	unsigned int hid0;
-	unsigned long p, wait;
+	unsigned long p;
 	struct adb_request sleep_req;
 	char *mem_ctrl;
 	unsigned int *mem_ctrl_sleep;
@@ -1751,9 +2311,13 @@
 	}
 	mem_ctrl_sleep = (unsigned int *) (mem_ctrl + PB3400_MEM_CTRL_SLEEP);
 
+	/* Allocate room for PCI save */
+	pbook_alloc_pci_save();
+
 	/* Notify device drivers */
 	ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, PBOOK_SLEEP_REJECT);
 	if (ret != PBOOK_SLEEP_OK) {
+		pbook_free_pci_save();
 		printk("pmu: sleep rejected\n");
 		return -EBUSY;
 	}
@@ -1770,16 +2334,13 @@
 	ret = broadcast_sleep(PBOOK_SLEEP_NOW, PBOOK_WAKE);
 	if (ret != PBOOK_SLEEP_OK) {
 		printk("pmu: sleep failed\n");
+		pbook_free_pci_save();
 		return -EBUSY;
 	}
 
-	/* Give the disks a little time to actually finish writing */
-	for (wait = jiffies + (HZ/4); time_before(jiffies, wait); )
-		mb();
-
 	/* Wait for completion of async backlight requests */
 	while (!bright_req_1.complete || !bright_req_2.complete ||
-		!bright_req_3.complete)
+		!bright_req_3.complete || !batt_req.complete)
 		pmu_poll();
 
 	/* Disable all interrupts except pmu */
@@ -1811,6 +2372,8 @@
 	while (!sleep_req.complete)
 		mb();
 
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1);
+
 	/* displacement-flush the L2 cache - necessary? */
 	for (p = KERNELBASE; p < KERNELBASE + 0x100000; p += 0x1000)
 		i = *(volatile int *)p;
@@ -1825,23 +2388,27 @@
 
 	/* OK, we're awake again, start restoring things */
 	out_be32(mem_ctrl_sleep, 0x3f);
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
 	pbook_pci_restore();
 
 	/* wait for the PMU interrupt sequence to complete */
 	while (asleep)
 		mb();
 
-	/* Re-enable DEC interrupts and kick DEC */
-	asm volatile("mtdec %0" : : "r" (0x7fffffff));
-	sti();
-	asm volatile("mtdec %0" : : "r" (0x10000000));
-
 	/* reenable interrupts */
 	pmac_sleep_restore_intrs();
 
+	/* Leave some time for HW to settle down */
+	mdelay(100);
+
+	/* Restart jiffies & scheduling */
+	wakeup_decrementer();
+	sti();
+
 	/* Notify drivers */
 	broadcast_wake();
 
+	pbook_free_pci_save();
 	iounmap(mem_ctrl);
 	return 0;
 }
@@ -1860,6 +2427,9 @@
 	}	rb_buf[RB_SIZE];
 	wait_queue_head_t wait;
 	spinlock_t lock;
+#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT)
+	int	backlight_locker;
+#endif /* defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) */	
 };
 
 static LIST_HEAD(all_pmu_pvt);
@@ -1877,6 +2447,7 @@
 	spin_lock_irqsave(&all_pvt_lock, flags);
 	for (list = &all_pmu_pvt; (list = list->next) != &all_pmu_pvt; ) {
 		pp = list_entry(list, struct pmu_private, list);
+		spin_lock(&pp->lock);
 		i = pp->rb_put + 1;
 		if (i >= RB_SIZE)
 			i = 0;
@@ -1887,6 +2458,7 @@
 			pp->rb_put = i;
 			wake_up_interruptible(&pp->wait);
 		}
+		spin_unlock(&pp->lock);
 	}
 	spin_unlock_irqrestore(&all_pvt_lock, flags);
 }
@@ -1903,6 +2475,9 @@
 	spin_lock_init(&pp->lock);
 	init_waitqueue_head(&pp->wait);
 	spin_lock_irqsave(&all_pvt_lock, flags);
+#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT)
+	pp->backlight_locker = 0;
+#endif /* defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) */	
 	list_add(&pp->list, &all_pmu_pvt);
 	spin_unlock_irqrestore(&all_pvt_lock, flags);
 	file->private_data = pp;
@@ -1914,6 +2489,7 @@
 {
 	struct pmu_private *pp = file->private_data;
 	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
 	int ret;
 
 	if (count < 1 || pp == 0)
@@ -1922,38 +2498,41 @@
 	if (ret)
 		return ret;
 
+	spin_lock_irqsave(&pp->lock, flags);
 	add_wait_queue(&pp->wait, &wait);
 	current->state = TASK_INTERRUPTIBLE;
 
 	for (;;) {
 		ret = -EAGAIN;
-		spin_lock(&pp->lock);
 		if (pp->rb_get != pp->rb_put) {
 			int i = pp->rb_get;
 			struct rb_entry *rp = &pp->rb_buf[i];
 			ret = rp->len;
+			spin_unlock_irqrestore(&pp->lock, flags);
 			if (ret > count)
 				ret = count;
 			if (ret > 0 && copy_to_user(buf, rp->data, ret))
 				ret = -EFAULT;
 			if (++i >= RB_SIZE)
 				i = 0;
+			spin_lock_irqsave(&pp->lock, flags);
 			pp->rb_get = i;
 		}
-		spin_unlock(&pp->lock);
 		if (ret >= 0)
 			break;
-
 		if (file->f_flags & O_NONBLOCK)
 			break;
 		ret = -ERESTARTSYS;
 		if (signal_pending(current))
 			break;
+		spin_unlock_irqrestore(&pp->lock, flags);
 		schedule();
+		spin_lock_irqsave(&pp->lock, flags);
 	}
 	current->state = TASK_RUNNING;
 	remove_wait_queue(&pp->wait, &wait);
-
+	spin_unlock_irqrestore(&pp->lock, flags);
+	
 	return ret;
 }
 
@@ -1967,14 +2546,15 @@
 {
 	struct pmu_private *pp = filp->private_data;
 	unsigned int mask = 0;
-
+	unsigned long flags;
+	
 	if (pp == 0)
 		return 0;
 	poll_wait(filp, &pp->wait, wait);
-	spin_lock(&pp->lock);
+	spin_lock_irqsave(&pp->lock, flags);
 	if (pp->rb_get != pp->rb_put)
 		mask |= POLLIN;
-	spin_unlock(&pp->lock);
+	spin_unlock_irqrestore(&pp->lock, flags);
 	return mask;
 }
 
@@ -1983,13 +2563,22 @@
 	struct pmu_private *pp = file->private_data;
 	unsigned long flags;
 
+	lock_kernel();
 	if (pp != 0) {
 		file->private_data = 0;
 		spin_lock_irqsave(&all_pvt_lock, flags);
 		list_del(&pp->list);
 		spin_unlock_irqrestore(&all_pvt_lock, flags);
+#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT)
+		if (pp->backlight_locker) {
+			spin_lock_irqsave(&pmu_lock, flags);
+			disable_kernel_backlight--;
+			spin_unlock_irqrestore(&pmu_lock, flags);
+		}
+#endif defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT)
 		kfree(pp);
 	}
+	unlock_kernel();
 	return 0;
 }
 
@@ -1997,6 +2586,7 @@
 static int pmu_ioctl(struct inode * inode, struct file *filp,
 		     u_int cmd, u_long arg)
 {
+	struct pmu_private *pp = filp->private_data;
 	int error;
 
 	switch (cmd) {
@@ -2023,7 +2613,7 @@
 		sleep_in_progress = 0;
 		return error;
 	case PMU_IOC_CAN_SLEEP:
-		return put_user(feature_can_sleep(), (__u32 *)arg);
+		return put_user((u32)can_sleep, (__u32 *)arg);
 
 #ifdef CONFIG_PMAC_BACKLIGHT
 	/* Backlight should have its own device or go via
@@ -2046,6 +2636,18 @@
 			error = set_backlight_level(value);
 		return error;
 	}
+#ifdef CONFIG_INPUT_ADBHID
+	case PMU_IOC_GRAB_BACKLIGHT: {
+		unsigned long flags;
+		if (pp->backlight_locker)
+			return 0;
+		pp->backlight_locker = 1;
+		spin_lock_irqsave(&pmu_lock, flags);
+		disable_kernel_backlight++;
+		spin_unlock_irqrestore(&pmu_lock, flags);
+		return 0;
+	}
+#endif /* CONFIG_INPUT_ADBHID */
 #endif /* CONFIG_PMAC_BACKLIGHT */
 	case PMU_IOC_GET_MODEL:
 	    	return put_user(pmu_kind, (__u32 *)arg);
@@ -2153,5 +2755,8 @@
 EXPORT_SYMBOL(pmu_register_sleep_notifier);
 EXPORT_SYMBOL(pmu_unregister_sleep_notifier);
 EXPORT_SYMBOL(pmu_enable_irled);
+EXPORT_SYMBOL(pmu_battery_count);
+EXPORT_SYMBOL(pmu_batteries);
+EXPORT_SYMBOL(pmu_power_flags);
 #endif /* CONFIG_PMAC_PBOOK */
 
diff -Nru a/drivers/media/video/planb.c b/drivers/media/video/planb.c
--- a/drivers/media/video/planb.c	Sat May 18 00:46:08 2002
+++ b/drivers/media/video/planb.c	Sat May 18 00:46:08 2002
@@ -2079,7 +2079,6 @@
 #endif
 	pb->tab_size = PLANB_MAXLINES + 40;
 	pb->suspend = 0;
-	pb->lock = 0;
 	init_MUTEX(&pb->lock);
 	pb->ch1_cmd = 0;
 	pb->ch2_cmd = 0;
diff -Nru a/drivers/net/3c509.c b/drivers/net/3c509.c
--- a/drivers/net/3c509.c	Sat May 18 00:46:08 2002
+++ b/drivers/net/3c509.c	Sat May 18 00:46:08 2002
@@ -47,10 +47,12 @@
 			- ethtool support
 		v1.18b 1Mar2002 Zwane Mwaikambo <zwane@commfireservices.com>
 			- Power Management support
+                v1.18c 1Mar2002 David Ruggiero <jdr@farfalle.com>
+                        - Full duplex support
 */
 
 #define DRV_NAME	"3c509"
-#define DRV_VERSION	"1.18b"
+#define DRV_VERSION	"1.18c"
 #define DRV_RELDATE	"1Mar2002"
 
 /* A few values that may be tweaked. */
@@ -141,6 +143,8 @@
 #define WN0_IRQ		0x08		/* Window 0: Set IRQ line in bits 12-15. */
 #define WN4_MEDIA	0x0A		/* Window 4: Various transcvr/media bits. */
 #define  MEDIA_TP	0x00C0		/* Enable link beat and jabber for 10baseT. */
+#define WN4_NETDIAG	0x06		/* Window 4: Net diagnostic */
+#define FD_ENABLE	0x8000		/* Enable full-duplex ("external loopback") */  
 
 /*
  * Must be a power of two (we use a binary and in the
@@ -500,12 +504,18 @@
 	memcpy(dev->dev_addr, phys_addr, sizeof(phys_addr));
 	dev->base_addr = ioaddr;
 	dev->irq = irq;
-	dev->if_port = (dev->mem_start & 0x1f) ? dev->mem_start & 3 : if_port;
+        
+	if (dev->mem_start & 0x05) { /* xcvr codes 1/3/4/12 */
+		dev->if_port = (dev->mem_start & 0x0f);
+        } else { /* xcvr codes 0/8 */
+		/* use eeprom value, but save user's full-duplex selection */
+		dev->if_port = (if_port | (dev->mem_start & 0x08) );
+	}
 
 	{
 		const char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"};
 		printk("%s: 3c5x9 at %#3.3lx, %s port, address ",
-			   dev->name, dev->base_addr, if_names[dev->if_port]);
+			dev->name, dev->base_addr, if_names[(dev->if_port & 0x03)]);
 	}
 
 	/* Read in the station address. */
@@ -557,7 +567,7 @@
 /* Read a word from the EEPROM using the regular EEPROM access register.
    Assume that we are in register window zero.
  */
-static ushort __init read_eeprom(int ioaddr, int index)
+static ushort read_eeprom(int ioaddr, int index)
 {
 	outw(EEPROM_READ + index, ioaddr + 10);
 	/* Pause for at least 162 us. for the read to take place. */
@@ -1083,7 +1093,7 @@
 
 static void el3_up(struct net_device *dev)
 {
-	int i;
+	int i, sw_info, net_diag;
 	int ioaddr = dev->base_addr;
 	
 	/* Activating the board required and does no harm otherwise */
@@ -1098,12 +1108,44 @@
 	for (i = 0; i < 6; i++)
 		outb(dev->dev_addr[i], ioaddr + i);
 
-	if (dev->if_port == 3)
+	if ((dev->if_port & 0x03) == 3) /* BNC interface */
 		/* Start the thinnet transceiver. We should really wait 50ms...*/
 		outw(StartCoax, ioaddr + EL3_CMD);
-	else if (dev->if_port == 0) {
-		/* 10baseT interface, enabled link beat and jabber check. */
+	else if ((dev->if_port & 0x03) == 0) { /* 10baseT interface */
+		/* Combine secondary sw_info word (the adapter level) and primary
+			sw_info word (duplex setting plus other useless bits) */
+		EL3WINDOW(0);
+		sw_info = (read_eeprom(ioaddr, 0x14) & 0x400f) | 
+			(read_eeprom(ioaddr, 0x0d) & 0xBff0);
+
 		EL3WINDOW(4);
+		net_diag = inw(ioaddr + WN4_NETDIAG);
+		net_diag = (net_diag | FD_ENABLE); /* temporarily assume full-duplex will be set */
+		printk("%s: ", dev->name);
+		switch (dev->if_port & 0x0c) {
+			case 12:
+				/* force full-duplex mode if 3c5x9b */
+				if (sw_info & 0x000f) {
+					printk("Forcing 3c5x9b full-duplex mode");
+					break;
+				}
+			case 8:
+				/* set full-duplex mode based on eeprom config setting */
+				if ((sw_info & 0x000f) && (sw_info & 0x8000)) {
+					printk("Setting 3c5x9b full-duplex mode (from EEPROM configuration bit)");
+					break;
+				}
+			default:
+				/* xcvr=(0 || 4) OR user has an old 3c5x9 non "B" model */
+				printk("Setting 3c5x9/3c5x9B half-duplex mode");
+				net_diag = (net_diag & ~FD_ENABLE); /* disable full duplex */
+		}
+
+		outw(net_diag, ioaddr + WN4_NETDIAG);
+		printk(" if_port: %d, sw_info: %4.4x\n", dev->if_port, sw_info);
+		if (el3_debug > 3)
+			printk("%s: 3c5x9 net diag word is now: %4.4x.\n", dev->name, net_diag);
+		/* Enable link beat and jabber check. */
 		outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
 	}
 
@@ -1208,11 +1250,11 @@
 /* Parameters that may be passed into the module. */
 static int debug = -1;
 static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-static int xcvr[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int xcvr[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
 
 MODULE_PARM(debug,"i");
 MODULE_PARM(irq,"1-8i");
-MODULE_PARM(xcvr,"1-8i");
+MODULE_PARM(xcvr,"1-12i");
 MODULE_PARM(max_interrupt_work, "i");
 MODULE_PARM_DESC(debug, "debug level (0-6)");
 MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
diff -Nru a/drivers/net/8139too.c b/drivers/net/8139too.c
--- a/drivers/net/8139too.c	Sat May 18 00:46:08 2002
+++ b/drivers/net/8139too.c	Sat May 18 00:46:08 2002
@@ -2329,7 +2329,7 @@
 
 	if (cmd != SIOCETHTOOL) {
 		/* With SIOCETHTOOL, this would corrupt the pointer.  */
-		data->phy_id &= 0x1f;
+		data->phy_id &= 0x3f;
 		data->reg_num &= 0x1f;
 	}
 
diff -Nru a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c
--- a/drivers/net/cs89x0.c	Sat May 18 00:46:14 2002
+++ b/drivers/net/cs89x0.c	Sat May 18 00:46:14 2002
@@ -81,6 +81,9 @@
                     : Make `version[]' __initdata
                     : Uninlined the read/write reg/word functions.
 
+  Oskar Schirmer    : oskar@scara.com
+                    : HiCO.SH4 (superh) support added (irq#1, cs89x0_media=)
+
 */
 
 /* Always include 'config.h' first in case the user wants to turn on
@@ -156,6 +159,10 @@
 static unsigned int netcard_portlist[] __initdata =
    { 0x80090303, 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0};
 static unsigned int cs8900_irq_map[] = {12,0,0,0};
+#elif defined(CONFIG_SH_HICOSH4)
+static unsigned int netcard_portlist[] __initdata =
+   { 0x0300, 0};
+static unsigned int cs8900_irq_map[] = {1,0,0,0};
 #else
 static unsigned int netcard_portlist[] __initdata =
    { 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0};
@@ -247,6 +254,20 @@
 __setup("cs89x0_dma=", dma_fn);
 #endif	/* !defined(MODULE) && (ALLOW_DMA != 0) */
 
+#ifndef MODULE
+static int g_cs89x0_media__force;
+
+static int __init media_fn(char *str)
+{
+	if (!strcmp(str, "rj45")) g_cs89x0_media__force = FORCE_RJ45;
+	else if (!strcmp(str, "aui")) g_cs89x0_media__force = FORCE_AUI;
+	else if (!strcmp(str, "bnc")) g_cs89x0_media__force = FORCE_BNC;
+	return 1;
+}
+
+__setup("cs89x0_media=", media_fn);
+#endif
+
 
 /* Check for a network adaptor of this type, and return '0' iff one exists.
    If dev->base_addr == 0, probe all likely locations.
@@ -383,6 +404,9 @@
 			lp->dmasize = 16;	/* Could make this an option... */
 		}
 #endif
+#ifndef MODULE
+		lp->force = g_cs89x0_media__force;
+#endif
         }
 	lp = (struct net_local *)dev->priv;
 
@@ -394,6 +418,12 @@
 		goto out1;
 	}
 
+#ifdef CONFIG_SH_HICOSH4
+	/* truely reset the chip */
+	outw(0x0114, ioaddr + ADD_PORT);
+	outw(0x0040, ioaddr + DATA_PORT);
+#endif
+
 	/* if they give us an odd I/O address, then do ONE write to
            the address port, to get it back to address zero, where we
            expect to find the EISA signature word. An IO with a base of 0x3
@@ -413,7 +443,7 @@
 	}
 printk("PP_addr=0x%x\n", inw(ioaddr + ADD_PORT));
 
-        if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG) {
+	if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG) {
 		printk(KERN_ERR "%s: incorrect signature 0x%x\n",
 			dev->name, inw(ioaddr + DATA_PORT));
   		retval = -ENODEV;
@@ -454,6 +484,39 @@
 	   EEPROM read on reset. So, if the chip says it read the EEPROM
 	   the driver will always do *something* instead of complain that
 	   adapter_cnf is 0. */
+
+#ifdef CONFIG_SH_HICOSH4
+	if (1) {
+		/* For the HiCO.SH4 board, things are different: we don't
+		   have EEPROM, but there is some data in flash, so we go
+		   get it there directly (MAC). */
+		__u16 *confd;
+		short cnt;
+		if (((* (volatile __u32 *) 0xa0013ff0) & 0x00ffffff)
+			== 0x006c3000) {
+			confd = (__u16*) 0xa0013fc0;
+		} else {
+			confd = (__u16*) 0xa001ffc0;
+		}
+		cnt = (*confd++ & 0x00ff) >> 1;
+		while (--cnt > 0) {
+			__u16 j = *confd++;
+			
+			switch (j & 0x0fff) {
+			case PP_IA:
+				for (i = 0; i < ETH_ALEN/2; i++) {
+					dev->dev_addr[i*2] = confd[i] & 0xFF;
+					dev->dev_addr[i*2+1] = confd[i] >> 8;
+				}
+				break;
+			}
+			j = (j >> 12) + 1;
+			confd += j;
+			cnt -= j;
+		}
+	} else
+#endif
+
         if ((readreg(dev, PP_SelfST) & (EEPROM_OK | EEPROM_PRESENT)) == 
 	      (EEPROM_OK|EEPROM_PRESENT)) {
 	        /* Load the MAC. */
@@ -507,6 +570,11 @@
         printk("\n");
    
 	/* First check to see if an EEPROM is attached. */
+#ifdef CONFIG_SH_HICOSH4 /* no EEPROM on HiCO, don't hazzle with it here */
+	if (1) {
+		printk(KERN_NOTICE "cs89x0: No EEPROM on HiCO.SH4\n");
+	} else
+#endif
 	if ((readreg(dev, PP_SelfST) & EEPROM_PRESENT) == 0)
 		printk(KERN_WARNING "cs89x0: No EEPROM, relying on command line....\n");
 	else if (get_eeprom_data(dev, START_EEPROM_DATA,CHKSUM_LEN,eeprom_buff) < 0) {
@@ -616,10 +684,10 @@
 	}
 
 	/* print the ethernet address. */
-	printk(", MAC ");
+	printk(", MAC");
 	for (i = 0; i < ETH_ALEN; i++)
 	{
-		printk("%s%02x", i ? ":" : "", dev->dev_addr[i]);
+		printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]);
 	}
 
 	dev->open		= net_open;
@@ -1046,6 +1114,7 @@
 	int i;
 	int ret;
 
+#ifndef CONFIG_SH_HICOSH4 /* uses irq#1, so this wont work */
 	if (dev->irq < 2) {
 		/* Allow interrupts to be generated by the chip */
 /* Cirrus' release had this: */
@@ -1056,7 +1125,7 @@
 		writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
 
 		for (i = 2; i < CS8920_NO_INTS; i++) {
-			if ((1 << dev->irq) & lp->irq_map) {
+			if ((1 << i) & lp->irq_map) {
 				if (request_irq(i, net_interrupt, 0, dev->name, dev) == 0) {
 					dev->irq = i;
 					write_irq(dev, lp->chip_type, i);
@@ -1072,7 +1141,10 @@
 			ret = -EAGAIN;
 			goto bad_out;
 		}
-	} else {
+	}
+	else
+#endif
+	{
 		if (((1 << dev->irq) & lp->irq_map) == 0) {
 			printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",
                                dev->name, dev->irq, lp->irq_map);
diff -Nru a/drivers/net/cs89x0.h b/drivers/net/cs89x0.h
--- a/drivers/net/cs89x0.h	Sat May 18 00:46:08 2002
+++ b/drivers/net/cs89x0.h	Sat May 18 00:46:08 2002
@@ -437,7 +437,11 @@
 #define IRQ_MAP_EEPROM_DATA 0x0046 /*  Offset into eeprom for the IRQ map */
 #define IRQ_MAP_LEN 0x0004 /*  No of bytes to read for the IRQ map */
 #define PNP_IRQ_FRMT 0x0022 /*  PNP small item IRQ format */
+#ifdef CONFIG_SH_HICOSH4
+#define CS8900_IRQ_MAP 0x0002 /* HiCO-SH4 board has its IRQ on #1 */
+#else
 #define CS8900_IRQ_MAP 0x1c20 /*  This IRQ map is fixed */
+#endif
 
 #define CS8920_NO_INTS 0x0F   /*  Max CS8920 interrupt select # */
 
diff -Nru a/drivers/net/irda/smc-ircc.c b/drivers/net/irda/smc-ircc.c
--- a/drivers/net/irda/smc-ircc.c	Sat May 18 00:46:08 2002
+++ b/drivers/net/irda/smc-ircc.c	Sat May 18 00:46:08 2002
@@ -10,6 +10,8 @@
  * Modified by:   Dag Brattli <dag@brattli.net>
  * Modified at:   Tue Jun 26 2001
  * Modified by:   Stefani Seibold <stefani@seibold.net>
+ * Modified at:   Thur Apr 18 2002
+ * Modified by:   Jeff Snyder <je4d@pobox.com>
  * 
  *     Copyright (c) 2001      Stefani Seibold
  *     Copyright (c) 1999-2001 Dag Brattli
@@ -539,7 +541,7 @@
 	if (ircc_irq < 255) {
 		if (ircc_irq!=irq)
 			MESSAGE("%s, Overriding IRQ - chip says %d, using %d\n",
-				driver_name, self->io->irq, ircc_irq);
+				driver_name, irq, ircc_irq);
 		self->io->irq = ircc_irq;
 	}
 	else
@@ -547,13 +549,13 @@
 	if (ircc_dma < 255) {
 		if (ircc_dma!=dma)
 			MESSAGE("%s, Overriding DMA - chip says %d, using %d\n",
-				driver_name, self->io->dma, ircc_dma);
+				driver_name, dma, ircc_dma);
 		self->io->dma = ircc_dma;
 	}
 	else
 		self->io->dma = dma;
 
-	request_region(fir_base, CHIP_IO_EXTENT, driver_name);
+	request_region(self->io->fir_base, CHIP_IO_EXTENT, driver_name);
 
 	/* Initialize QoS for this device */
 	irda_init_max_qos_capabilies(&irport->qos);
@@ -1191,10 +1193,9 @@
         outb(IRCC_CFGB_IR, iobase+IRCC_SCE_CFGB);
 #endif
 	/* Release the PORT that this driver is using */
-	IRDA_DEBUG(0, __FUNCTION__ "(), releasing 0x%03x\n", 
-		   self->io->fir_base);
+	IRDA_DEBUG(0, __FUNCTION__ "(), releasing 0x%03x\n", iobase);
 
-	release_region(self->io->fir_base, self->io->fir_ext);
+	release_region(iobase, CHIP_IO_EXTENT);
 
 	if (self->tx_buff.head)
 		kfree(self->tx_buff.head);
diff -Nru a/drivers/net/mii.c b/drivers/net/mii.c
--- a/drivers/net/mii.c	Sat May 18 00:46:09 2002
+++ b/drivers/net/mii.c	Sat May 18 00:46:09 2002
@@ -146,6 +146,8 @@
 
 int mii_link_ok (struct mii_if_info *mii)
 {
+	/* first, a dummy read, needed to latch some MII phys */
+	mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
 	if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
 		return 1;
 	return 0;
diff -Nru a/drivers/net/tulip/21142.c b/drivers/net/tulip/21142.c
--- a/drivers/net/tulip/21142.c	Sat May 18 00:46:08 2002
+++ b/drivers/net/tulip/21142.c	Sat May 18 00:46:08 2002
@@ -172,8 +172,9 @@
 			int i;
 			for (i = 0; i < tp->mtable->leafcount; i++)
 				if (tp->mtable->mleaf[i].media == dev->if_port) {
+					int startup = ! ((tp->chip_id == DC21143 && tp->revision == 65));
 					tp->cur_index = i;
-					tulip_select_media(dev, 1);
+					tulip_select_media(dev, startup);
 					setup_done = 1;
 					break;
 				}
diff -Nru a/drivers/net/tulip/ChangeLog b/drivers/net/tulip/ChangeLog
--- a/drivers/net/tulip/ChangeLog	Sat May 18 00:46:08 2002
+++ b/drivers/net/tulip/ChangeLog	Sat May 18 00:46:08 2002
@@ -1,3 +1,13 @@
+2002-05-11  Juan Quintela  <quintela@mandrakesoft.com>
+
+	* 21142.c (t21142_lnk_change): Revert earlier patch
+	to always reset phy; only conditionally do so now.
+
+2002-05-03  Jeff Garzik  <jgarzik@mandrakesoft.com>
+
+	* tulip_core (tulip_pci_tbl): Add new "comet"
+	pci id.  Contributed by Ohta Kyuma.
+
 2002-03-07  Jeff Garzik  <jgarzik@mandrakesoft.com>
 
 	* tulip_core (tulip_mwi_config): Use new PCI API functions
diff -Nru a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c
--- a/drivers/net/tulip/tulip_core.c	Sat May 18 00:46:14 2002
+++ b/drivers/net/tulip/tulip_core.c	Sat May 18 00:46:14 2002
@@ -15,8 +15,8 @@
 */
 
 #define DRV_NAME	"tulip"
-#define DRV_VERSION	"1.1.12"
-#define DRV_RELDATE	"Mar 07, 2002"
+#define DRV_VERSION	"1.1.13"
+#define DRV_RELDATE	"May 11, 2002"
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -204,6 +204,7 @@
 	{ 0x1317, 0x0981, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
 	{ 0x1317, 0x0985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
 	{ 0x1317, 0x1985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+	{ 0x1317, 0x9511, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
 	{ 0x13D1, 0xAB02, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
 	{ 0x13D1, 0xAB03, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
 	{ 0x13D1, 0xAB08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
diff -Nru a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c
--- a/drivers/net/via-rhine.c	Sat May 18 00:46:09 2002
+++ b/drivers/net/via-rhine.c	Sat May 18 00:46:09 2002
@@ -9,8 +9,8 @@
 	a complete program and may only be used when the entire operating
 	system is licensed under the GPL.
 
-	This driver is designed for the VIA VT86c100A Rhine-II PCI Fast Ethernet
-	controller.  It also works with the older 3043 Rhine-I chip.
+	This driver is designed for the VIA VT86C100A Rhine-I. 
+	It also works with the 6102 Rhine-II, and 6105/6105M Rhine-III.   
 
 	The author may be reached as becker@scyld.com, or C/O
 	Scyld Computing Corporation
@@ -81,14 +81,26 @@
 	- Add ethtool support
 	- Replace some MII-related magic numbers with constants
 	
-	LK1.1.14 (jgarzik):
-	- Merge new PCI id from 'linuxfet' driver.
+	LK1.1.14 (Ivan G.):
+ 	- fixes comments for Rhine-III
+	- removes W_MAX_TIMEOUT (unused)
+	- adds HasDavicomPhy for Rhine-I (basis: linuxfet driver; my card
+	  is R-I and has Davicom chip, flag is referenced in kernel driver)
+	- sends chip_id as a parameter to wait_for_reset since np is not
+	  initialized on first call
+	- changes mmio "else if (chip_id==VT6102)" to "else" so it will work
+	  for Rhine-III's (documentation says same bit is correct)		
+	- transmit frame queue message is off by one - fixed
+	- adds IntrNormalSummary to "Something Wicked" exclusion list
+	  so normal interrupts will not trigger the message (src: Donald Becker)
+	(Roger Lahti)
+	- cosmetic cleanups, remove 3 unused members of struct netdev_private
 
 */
 
 #define DRV_NAME	"via-rhine"
 #define DRV_VERSION	"1.1.14"
-#define DRV_RELDATE	"Feb-12-2002"
+#define DRV_RELDATE	"May-3-2002"
 
 
 /* A few user-configurable values.
@@ -139,9 +151,6 @@
 
 #define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
 
-/* max time out delay time */
-#define W_MAX_TIMEOUT	0x0FFFU
-
 #if !defined(__OPTIMIZE__)  ||  !defined(__KERNEL__)
 #warning  You must compile this file with the correct options!
 #warning  See the last lines of the source file.
@@ -176,7 +185,7 @@
 KERN_INFO DRV_NAME ".c:v1.10-LK" DRV_VERSION "  " DRV_RELDATE "  Written by Donald Becker\n"
 KERN_INFO "  http://www.scyld.com/network/via-rhine.html\n";
 
-static char shortname[] __devinitdata = DRV_NAME;
+static char shortname[] = DRV_NAME;
 
 
 /* This driver was written to use PCI memory space, however most versions
@@ -320,8 +329,8 @@
 enum via_rhine_chips {
 	VT86C100A = 0,
 	VT6102,
-	VT3043,
 	VT6105,
+	VT6105M
 };
 
 struct via_rhine_chip_info {
@@ -346,21 +355,21 @@
 static struct via_rhine_chip_info via_rhine_chip_info[] __devinitdata =
 {
 	{ "VIA VT86C100A Rhine", RHINE_IOTYPE, 128,
-	  CanHaveMII | ReqTxAlign },
+	  CanHaveMII | ReqTxAlign | HasDavicomPhy },
 	{ "VIA VT6102 Rhine-II", RHINE_IOTYPE, 256,
 	  CanHaveMII | HasWOL },
-	{ "VIA VT3043 Rhine",    RHINE_IOTYPE, 128,
-	  CanHaveMII | ReqTxAlign },
 	{ "VIA VT6105 Rhine-III", RHINE_IOTYPE, 256,
-	  CanHaveMII | HasWOL },
+	  CanHaveMII | HasWOL },	  
+	{ "VIA VT6105M Rhine-III", RHINE_IOTYPE, 256,
+	  CanHaveMII | HasWOL },	  	  	 
 };
 
 static struct pci_device_id via_rhine_pci_tbl[] __devinitdata =
 {
-	{0x1106, 0x6100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VT86C100A},
+	{0x1106, 0x3043, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VT86C100A},
 	{0x1106, 0x3065, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VT6102},
-	{0x1106, 0x3043, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VT3043},
 	{0x1106, 0x3106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VT6105},
+	{0x1106, 0x3053, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VT6105M},	
 	{0,}			/* terminate list */
 };
 MODULE_DEVICE_TABLE(pci, via_rhine_pci_tbl);
@@ -379,6 +388,11 @@
 	StickyHW=0x83, WOLcrClr=0xA4, WOLcgClr=0xA7, PwrcsrClr=0xAC,
 };
 
+/* Bits in ConfigD (select backoff algorithm (Ethernet capture effect)) */
+enum backoff_bits {
+	BackOpt=0x01, BackAMD=0x02, BackDEC=0x04, BackRandom=0x08
+};
+
 #ifdef USE_MEM
 /* Registers we check that mmio and reg are the same. */
 int mmio_verify_registers[] = {
@@ -427,11 +441,11 @@
 	u32 next_desc;
 };
 
-/* Bits in *_desc.status */
 enum rx_status_bits {
 	RxOK=0x8000, RxWholePkt=0x0300, RxErr=0x008F
 };
 
+/* Bits in *_desc.status */
 enum desc_status_bits {
 	DescOwn=0x80000000, DescEndPacket=0x4000, DescIntr=0x1000,
 };
@@ -479,13 +493,10 @@
 	u16 chip_cmd;						/* Current setting for ChipCmd */
 
 	/* These values are keep track of the transceiver/media in use. */
-	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
-	unsigned int duplex_lock:1;
 	unsigned int default_port:4;		/* Last dev->if_port value. */
 	u8 tx_thresh, rx_thresh;
 
 	/* MII transceiver section. */
-	u16 advertising;					/* NWay media advertisement */
 	unsigned char phys[MAX_MII_CNT];			/* MII device addresses. */
 	unsigned int mii_cnt;			/* number of MIIs found, but only the first one is used */
 	u16 mii_status;						/* last read MII status */
@@ -509,15 +520,13 @@
 static int  via_rhine_close(struct net_device *dev);
 static inline void clear_tally_counters(long ioaddr);
 
-static void wait_for_reset(struct net_device *dev, char *name)
+static void wait_for_reset(struct net_device *dev, int chip_id, char *name)
 {
-	struct netdev_private *np = dev->priv;
 	long ioaddr = dev->base_addr;
-	int chip_id = np->chip_id;
 	int i;
 
-	/* 3043 may need long delay after reset (dlink) */
-	if (chip_id == VT3043 || chip_id == VT86C100A)
+	/* VT86C100A may need long delay after reset (dlink) */
+	if (chip_id == VT86C100A)
 		udelay(100);
 
 	i = 0;
@@ -538,11 +547,11 @@
 static void __devinit enable_mmio(long ioaddr, int chip_id)
 {
 	int n;
-	if (chip_id == VT3043 || chip_id == VT86C100A) {
+	if (chip_id == VT86C100A) {
 		/* More recent docs say that this bit is reserved ... */
 		n = inb(ioaddr + ConfigA) | 0x20;
 		outb(n, ioaddr + ConfigA);
-	} else if (chip_id == VT6102) {
+	} else {
 		n = inb(ioaddr + ConfigD) | 0x80;
 		outb(n, ioaddr + ConfigD);
 	}
@@ -666,7 +675,7 @@
 	writew(CmdReset, ioaddr + ChipCmd);
 
 	dev->base_addr = ioaddr;
-	wait_for_reset(dev, shortname);
+	wait_for_reset(dev, chip_id, shortname);
 
 	/* Reload the station address from the EEPROM. */
 #ifdef USE_IO
@@ -787,7 +796,7 @@
 				   (option & 0x300 ? 100 : 10),
 				   (option & 0x220 ? "full" : "half"));
 			if (np->mii_cnt)
-				mdio_write(dev, np->phys[0], 0,
+				mdio_write(dev, np->phys[0], MII_BMCR,
 						   ((option & 0x300) ? 0x2000 : 0) |  /* 100mbps? */
 						   ((option & 0x220) ? 0x0100 : 0));  /* Full duplex? */
 		}
@@ -971,9 +980,9 @@
 		writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
 
 	/* Initialize other registers. */
-	writew(0x0006, ioaddr + PCIBusConfig);	/* Tune configuration??? */
-	/* Configure the FIFO thresholds. */
-	writeb(0x20, ioaddr + TxConfig);	/* Initial threshold 32 bytes */
+	writew(0x0006, ioaddr + PCIBusConfig);	/* Store & forward */
+	/* Configure initial FIFO thresholds. */
+	writeb(0x20, ioaddr + TxConfig);
 	np->tx_thresh = 0x20;
 	np->rx_thresh = 0x60;			/* Written in via_rhine_set_rx_mode(). */
 
@@ -1032,13 +1041,13 @@
 
 	if (phy_id == np->phys[0]) {
 		switch (regnum) {
-		case 0:							/* Is user forcing speed/duplex? */
+		case MII_BMCR:					/* Is user forcing speed/duplex? */
 			if (value & 0x9000)			/* Autonegotiation. */
 				np->mii_if.duplex_lock = 0;
 			else
 				np->mii_if.full_duplex = (value & 0x0100) ? 1 : 0;
 			break;
-		case 4:
+		case MII_ADVERTISE:
 			np->mii_if.advertising = value;
 			break;
 		}
@@ -1077,7 +1086,7 @@
 		return i;
 	alloc_rbufs(dev);
 	alloc_tbufs(dev);
-	wait_for_reset(dev, dev->name);
+	wait_for_reset(dev, np->chip_id, dev->name);
 	init_registers(dev);
 	if (debug > 2)
 		printk(KERN_DEBUG "%s: Done via_rhine_open(), status %4.4x "
@@ -1184,7 +1193,7 @@
 	alloc_rbufs(dev);
 
 	/* Reinitialize the hardware. */
-	wait_for_reset(dev, dev->name);
+	wait_for_reset(dev, np->chip_id, dev->name);
 	init_registers(dev);
 	
 	spin_unlock(&np->lock);
@@ -1254,7 +1263,7 @@
 
 	if (debug > 4) {
 		printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
-			   dev->name, np->cur_tx, entry);
+			   dev->name, np->cur_tx-1, entry);
 	}
 	return 0;
 }
@@ -1505,8 +1514,8 @@
 			printk(KERN_INFO "%s: Transmitter underrun, increasing Tx "
 				   "threshold setting to %2.2x.\n", dev->name, np->tx_thresh);
 	}
-	if ((intr_status & ~( IntrLinkChange | IntrStatsMax |
-						  IntrTxAbort | IntrTxAborted))) {
+	if (intr_status & ~( IntrLinkChange | IntrStatsMax |
+ 						 IntrTxAbort | IntrTxAborted | IntrNormalSummary)) {
 		if (debug > 1)
 			printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n",
 			   dev->name, intr_status);
diff -Nru a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c
--- a/drivers/net/wan/pc300_drv.c	Sat May 18 00:46:15 2002
+++ b/drivers/net/wan/pc300_drv.c	Sat May 18 00:46:15 2002
@@ -6,7 +6,7 @@
  * pc300.c	Cyclades-PC300(tm) Driver.
  *
  * Author:	Ivan Passos <ivan@cyclades.com>
- * Maintainer:	Daniela Squassoni <daniela@cyclades.com>
+ * Maintainer:	Henrique Gobbi <henrique@cyclades.com>
  *
  * Copyright:	(c) 1999-2002 Cyclades Corp.
  *
diff -Nru a/drivers/net/wan/pc300_tty.c b/drivers/net/wan/pc300_tty.c
--- a/drivers/net/wan/pc300_tty.c	Sat May 18 00:46:09 2002
+++ b/drivers/net/wan/pc300_tty.c	Sat May 18 00:46:09 2002
@@ -46,7 +46,7 @@
 #include <linux/init.h>
 #include <linux/netdevice.h>
 #include <linux/spinlock.h>
-#include <linux/malloc.h>
+#include <linux/slab.h>
 #include <linux/if.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
diff -Nru a/drivers/net/wireless/Config.help b/drivers/net/wireless/Config.help
--- a/drivers/net/wireless/Config.help	Sat May 18 00:46:08 2002
+++ b/drivers/net/wireless/Config.help	Sat May 18 00:46:08 2002
@@ -55,7 +55,6 @@
   Support for these adaptors is so far still incomplete and buggy.
   You have been warned.
 
-Prism 2.5 PCI 802.11b adaptor support
 CONFIG_PCI_HERMES
   Enable support for PCI and mini-PCI 802.11b wireless NICs based on
   the Prism 2.5 chipset.  These are true PCI cards, not the 802.11b
diff -Nru a/drivers/net/wireless/airport.c b/drivers/net/wireless/airport.c
--- a/drivers/net/wireless/airport.c	Sat May 18 00:46:09 2002
+++ b/drivers/net/wireless/airport.c	Sat May 18 00:46:09 2002
@@ -1,4 +1,4 @@
-/* airport.c 0.11a
+/* airport.c 0.11b
  *
  * A driver for "Hermes" chipset based Apple Airport wireless
  * card.
@@ -42,7 +42,7 @@
 #include "hermes.h"
 #include "orinoco.h"
 
-static char version[] __initdata = "airport.c 0.11a (Benjamin Herrenschmidt <benh@kernel.crashing.org>)";
+static char version[] __initdata = "airport.c 0.11b (Benjamin Herrenschmidt <benh@kernel.crashing.org>)";
 MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
 MODULE_DESCRIPTION("Driver for the Apple Airport wireless card.");
 MODULE_LICENSE("Dual MPL/GPL");
@@ -270,8 +270,8 @@
 static void
 airport_detach(struct net_device *dev)
 {
-	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
-	struct airport *card = (struct airport *)priv->card;
+	struct orinoco_private *priv = dev->priv;
+	struct airport *card = priv->card;
 
 	/* Unregister proc entry */
 	orinoco_proc_dev_cleanup(priv);
@@ -299,7 +299,7 @@
 	current->state = TASK_UNINTERRUPTIBLE;
 	schedule_timeout(HZ);
 	
-	kfree(card);
+	kfree(dev);
 }				/* airport_detach */
 
 static int __init
diff -Nru a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
--- a/drivers/net/wireless/orinoco.c	Sat May 18 00:46:10 2002
+++ b/drivers/net/wireless/orinoco.c	Sat May 18 00:46:10 2002
@@ -1,4 +1,4 @@
-/* orinoco.c 0.11a	- (formerly known as dldwd_cs.c and orinoco_cs.c)
+/* orinoco.c 0.11b	- (formerly known as dldwd_cs.c and orinoco_cs.c)
  *
  * A driver for Hermes or Prism 2 chipset based PCMCIA wireless
  * adaptors, with Lucent/Agere, Intersil or Symbol firmware.
@@ -256,7 +256,7 @@
  *	o Fixes for recent Symbol firmwares which lack AP density
  *	  (Pavel Roskin).
  *
- * v0.11 -> v0.11a - 29 Apr 2002 - David Gibson
+ * v0.11 -> v0.11b - 29 Apr 2002 - David Gibson
  *	o Handle different register spacing, necessary for Prism 2.5
  *	  PCI adaptors (Steve Hill).
  *	o Cleaned up initialization of card structures in orinoco_cs
@@ -274,15 +274,21 @@
  *	o Makefile changes for better integration into David Hinds
  *	  pcmcia-cs package.
  *
+ * v0.11a -> v0.11b - 1 May 2002 - David Gibson
+ *	o Better error reporting in orinoco_plx_init_one()
+ *	o Fixed multiple bad kfree() bugs introduced by the
+ *	  alloc_orinocodev() changes.
+ *
  * TODO
- *	o Re-assess our encapsulation detection strategy
+ *	o New wireless extensions API
  *	o Handle de-encapsulation within network layer, provide 802.11
  *	  headers
  *	o Fix possible races in SPY handling.
  *	o Disconnect wireless extensions from fundamental configuration.
  *
  *	o Convert /proc debugging stuff to seqfile
- *	o Use multiple Tx buffers */
+ *	o Use multiple Tx buffers
+ */
 /* Notes on locking:
  *
  * The basic principle of operation is that everything except the
@@ -351,7 +357,7 @@
 #define SPY_NUMBER(priv)	0
 #endif /* WIRELESS_SPY */
 
-static char version[] __initdata = "orinoco.c 0.11a (David Gibson <hermes@gibson.dropbear.id.au> and others)";
+static char version[] __initdata = "orinoco.c 0.11b (David Gibson <hermes@gibson.dropbear.id.au> and others)";
 MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>");
 MODULE_DESCRIPTION("Driver for Lucent Orinoco, Prism II based and similar wireless cards");
 #ifdef MODULE_LICENSE
diff -Nru a/drivers/net/wireless/orinoco_cs.c b/drivers/net/wireless/orinoco_cs.c
--- a/drivers/net/wireless/orinoco_cs.c	Sat May 18 00:46:14 2002
+++ b/drivers/net/wireless/orinoco_cs.c	Sat May 18 00:46:14 2002
@@ -1,4 +1,4 @@
-/* orinoco_cs.c 0.11a	- (formerly known as dldwd_cs.c)
+/* orinoco_cs.c 0.11b	- (formerly known as dldwd_cs.c)
  *
  * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
  * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
@@ -47,7 +47,7 @@
 
 /*====================================================================*/
 
-static char version[] __initdata = "orinoco_cs.c 0.11a (David Gibson <hermes@gibson.dropbear.id.au> and others)";
+static char version[] __initdata = "orinoco_cs.c 0.11b (David Gibson <hermes@gibson.dropbear.id.au> and others)";
 
 MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>");
 MODULE_DESCRIPTION("Driver for PCMCIA Lucent Orinoco, Prism II based and similar wireless cards");
@@ -373,6 +373,7 @@
 {
 	dev_link_t **linkp;
 	struct orinoco_private *priv = link->priv;
+	struct net_device *dev = priv->ndev;
 
 	TRACE_ENTER("orinoco");
 
@@ -408,9 +409,9 @@
 	if (link->dev) {
 		DEBUG(0, "orinoco_cs: About to unregister net device %p\n",
 		      priv->ndev);
-		unregister_netdev(priv->ndev);
+		unregister_netdev(dev);
 	}
-	kfree(priv->card);
+	kfree(dev);
 
  out:
 	TRACE_EXIT("orinoco");
diff -Nru a/drivers/net/wireless/orinoco_plx.c b/drivers/net/wireless/orinoco_plx.c
--- a/drivers/net/wireless/orinoco_plx.c	Sat May 18 00:46:15 2002
+++ b/drivers/net/wireless/orinoco_plx.c	Sat May 18 00:46:15 2002
@@ -1,4 +1,4 @@
-/* orinoco_plx.c 0.11a
+/* orinoco_plx.c 0.11b
  * 
  * Driver for Prism II devices which would usually be driven by orinoco_cs,
  * but are connected to the PCI bus by a PLX9052. 
@@ -134,7 +134,7 @@
 #include "hermes.h"
 #include "orinoco.h"
 
-static char version[] __initdata = "orinoco_plx.c 0.11a (Daniel Barlow <dan@telent.net>)";
+static char version[] __initdata = "orinoco_plx.c 0.11b (Daniel Barlow <dan@telent.net>)";
 MODULE_AUTHOR("Daniel Barlow <dan@telent.net>");
 MODULE_DESCRIPTION("Driver for wireless LAN cards using the PLX9052 PCI bridge");
 #ifdef MODULE_LICENSE
@@ -284,7 +284,7 @@
 
 	hermes_struct_init(&(priv->hw), dev->base_addr,
 			HERMES_IO, HERMES_16BIT_REGSPACING);
-	pci_set_drvdata(pdev, priv);
+	pci_set_drvdata(pdev, dev);
 
 	err = request_irq(pdev->irq, orinoco_plx_interrupt, SA_SHIRQ, dev->name, priv);
 	if (err) {
@@ -337,12 +337,12 @@
 
 static void __devexit orinoco_plx_remove_one(struct pci_dev *pdev)
 {
-	struct orinoco_private *priv = pci_get_drvdata(pdev);
-	struct net_device *dev = priv->ndev;
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct orinoco_private *priv = dev->priv;
 
 	TRACE_ENTER("orinoco_plx");
 
-	if (!priv)
+	if (! dev)
 		BUG();
 
 	orinoco_proc_dev_cleanup(priv);
@@ -352,7 +352,7 @@
 	if (dev->irq)
 		free_irq(dev->irq, priv);
 		
-	kfree(priv);
+	kfree(dev);
 
 	release_region(pci_resource_start(pdev, 3), pci_resource_len(pdev, 3));
 
diff -Nru a/drivers/pci/Makefile b/drivers/pci/Makefile
--- a/drivers/pci/Makefile	Sat May 18 00:46:14 2002
+++ b/drivers/pci/Makefile	Sat May 18 00:46:14 2002
@@ -11,6 +11,8 @@
 
 O_TARGET := driver.o
 
+export-objs := access.o hotplug.o pci-driver.o pci.o pool.o probe.o proc.o search.o
+
 obj-y		+= access.o probe.o pci.o pool.o quirks.o \
 			compat.o names.o pci-driver.o search.o
 obj-$(CONFIG_PM)  += power.o
@@ -36,8 +38,6 @@
 ifndef CONFIG_X86
 obj-y += syscall.o
 endif
-
-export-objs := $(obj-y)
 
 include $(TOPDIR)/Rules.make
 
diff -Nru a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c
--- a/drivers/scsi/ide-scsi.c	Sat May 18 00:46:08 2002
+++ b/drivers/scsi/ide-scsi.c	Sat May 18 00:46:08 2002
@@ -271,7 +271,7 @@
 		ide_end_request(drive, rq, uptodate);
 		return 0;
 	}
-	ide_end_drive_cmd (drive, 0, 0);
+	ide_end_drive_cmd(drive, rq, 0, 0);
 	if (rq->errors >= ERROR_MAX) {
 		pc->scsi_cmd->result = DID_ERROR << 16;
 		if (log)
@@ -401,7 +401,7 @@
 	byte ireason;
 	ide_startstop_t startstop;
 
-	if (ide_wait_stat (&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+	if (ide_wait_stat(&startstop, drive, rq, DRQ_STAT, BUSY_STAT, WAIT_READY)) {
 		printk (KERN_ERR "ide-scsi: Strange, packet command initiated yet DRQ isn't asserted\n");
 		return startstop;
 	}
@@ -489,20 +489,6 @@
 static ide_drive_t *idescsi_drives[MAX_HWIFS * MAX_DRIVES];
 static int idescsi_initialized = 0;
 
-static void idescsi_add_settings(ide_drive_t *drive)
-{
-	idescsi_scsi_t *scsi = drive->driver_data;
-
-/*
- *			drive	setting name	read/write	ioctl	ioctl		data type	min	max	mul_factor	div_factor	data pointer		set function
- */
-	ide_add_setting(drive,	"bios_cyl",	SETTING_RW,	-1,	-1,		TYPE_INT,	0,	1023,	1,		1,		&drive->bios_cyl,	NULL);
-	ide_add_setting(drive,	"bios_head",	SETTING_RW,	-1,	-1,		TYPE_BYTE,	0,	255,	1,		1,		&drive->bios_head,	NULL);
-	ide_add_setting(drive,	"bios_sect",	SETTING_RW,	-1,	-1,		TYPE_BYTE,	0,	63,	1,		1,		&drive->bios_sect,	NULL);
-	ide_add_setting(drive,	"transform",	SETTING_RW,	-1,	-1,		TYPE_INT,	0,	3,	1,		1,		&scsi->transform,	NULL);
-	ide_add_setting(drive,	"log",		SETTING_RW,	-1,	-1,		TYPE_INT,	0,	1,	1,		1,		&scsi->log,		NULL);
-}
-
 /*
  *	Driver initialization.
  */
@@ -521,8 +507,7 @@
 	clear_bit(IDESCSI_SG_TRANSFORM, &scsi->transform);
 #if IDESCSI_DEBUG_LOG
 	set_bit(IDESCSI_LOG_CMD, &scsi->log);
-#endif /* IDESCSI_DEBUG_LOG */
-	idescsi_add_settings(drive);
+#endif
 }
 
 static int idescsi_cleanup (ide_drive_t *drive)
@@ -807,18 +792,6 @@
  */
 int idescsi_device_reset (Scsi_Cmnd *cmd)
 {
-#if 0
-	ide_drive_t *drive = idescsi_drives[cmd->target];
-        struct request req;
-
-        ide_init_drive_cmd(&req);
-        req.flags = REQ_SPECIAL;
-
-	/* FIX ME, the next executable line causes on oops in lk 2.5.10-dj1
-	 * [code copied from ide-cd's ide_cdrom_reset(), does it work?]
-	 */
-        ide_do_drive_cmd(drive, &req, ide_wait);
-#endif
 	return SUCCESS;
 }
 
diff -Nru a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c
--- a/drivers/scsi/mesh.c	Sat May 18 00:46:14 2002
+++ b/drivers/scsi/mesh.c	Sat May 18 00:46:14 2002
@@ -6,8 +6,16 @@
  *
  * Paul Mackerras, August 1996.
  * Copyright (C) 1996 Paul Mackerras.
+ *
+ * Apr. 21 2002  - BenH		Rework bus reset code for new error handler
+ *                              Add delay after initial bus reset
+ *                              Add module parameters
+ * To do:
+ * - handle aborts correctly
+ * - retry arbitration if lost (unless higher levels do this for us)
  */
 #include <linux/config.h>
+#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
 #include <linux/types.h>
@@ -28,7 +36,9 @@
 #include <asm/irq.h>
 #include <asm/hydra.h>
 #include <asm/processor.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/pci-bridge.h>
 #ifdef CONFIG_PMAC_PBOOK
 #include <linux/adb.h>
 #include <linux/pmu.h>
@@ -38,35 +48,39 @@
 #include "hosts.h"
 #include "mesh.h"
 
-/*
- * To do:
- * - handle aborts correctly
- * - retry arbitration if lost (unless higher levels do this for us)
- */
-
-#define MESH_NEW_STYLE_EH
-
 #if 1
 #undef KERN_DEBUG
 #define KERN_DEBUG KERN_WARNING
 #endif
 
-#if CONFIG_SCSI_MESH_SYNC_RATE == 0
-int mesh_sync_period = 100;
-int mesh_sync_offset = 0;
-#else
-int mesh_sync_period = 1000 / CONFIG_SCSI_MESH_SYNC_RATE;	/* ns */
-int mesh_sync_offset = 15;
-#endif
-
-int mesh_sync_targets = 0xff;	/* targets to set synchronous (bitmap) */
-int mesh_resel_targets = 0xff;	/* targets that we let disconnect (bitmap) */
-int mesh_debug_targets = 0;	/* print debug for these targets */
-unsigned char use_active_neg = 0;  /* bit mask for SEQ_ACTIVE_NEG if used */
-
-#define ALLOW_SYNC(tgt)		((mesh_sync_targets >> (tgt)) & 1)
-#define ALLOW_RESEL(tgt)	((mesh_resel_targets >> (tgt)) & 1)
-#define ALLOW_DEBUG(tgt)	((mesh_debug_targets >> (tgt)) & 1)
+MODULE_AUTHOR("Paul Mackerras (paulus@samba.org)");
+MODULE_DESCRIPTION("PowerMac MESH SCSI driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(sync_rate, "i");
+MODULE_PARM_DESC(sync_rate, "Synchronous rate (0..10, 0=async)");
+MODULE_PARM(sync_targets, "i");
+MODULE_PARM_DESC(sync_targets, "Bitmask of targets allowed to set synchronous");
+MODULE_PARM(resel_targets, "i");
+MODULE_PARM_DESC(resel_targets, "Bitmask of targets allowed to set disconnect");
+MODULE_PARM(debug_targets, "i");
+MODULE_PARM_DESC(debug_targets, "Bitmask of debugged targets");
+MODULE_PARM(init_reset_delay, "i");
+MODULE_PARM_DESC(init_reset_delay, "Initial bus reset delay (0=no reset)");
+
+static int sync_rate = CONFIG_SCSI_MESH_SYNC_RATE;
+static int sync_targets = 0xff;
+static int resel_targets = 0xff;
+static int debug_targets = 0;	/* print debug for these targets */
+static int init_reset_delay = CONFIG_SCSI_MESH_RESET_DELAY_MS;
+
+static int mesh_sync_period = 100;
+static int mesh_sync_offset = 0;
+static unsigned char use_active_neg = 0;  /* bit mask for SEQ_ACTIVE_NEG if used */
+
+#define ALLOW_SYNC(tgt)		((sync_targets >> (tgt)) & 1)
+#define ALLOW_RESEL(tgt)	((resel_targets >> (tgt)) & 1)
+#define ALLOW_DEBUG(tgt)	((debug_targets >> (tgt)) & 1)
 #define DEBUG_TARGET(cmd)	((cmd) && ALLOW_DEBUG((cmd)->target))
 
 #undef MESH_DBG
@@ -94,7 +108,8 @@
 	statusing,
 	busfreeing,
 	disconnecting,
-	reselecting
+	reselecting,
+	sleeping
 };
 
 enum msg_phase {
@@ -118,7 +133,6 @@
 	int	data_goes_out;		/* guess as to data direction */
 	Scsi_Cmnd *current_req;
 	u32	saved_ptr;
-	int	want_abort;
 #ifdef MESH_DBG
 	int	log_ix;
 	int	n_log;
@@ -155,12 +169,7 @@
 	struct mesh_target tgts[8];
 	void	*dma_cmd_space;
 	struct device_node *ofnode;
-	u8*	mio_base;
-#ifndef MESH_NEW_STYLE_EH
-	Scsi_Cmnd *completed_q;
-	Scsi_Cmnd *completed_qtail;
-	struct tq_struct tqueue;
-#endif
+	struct pci_dev* pdev;
 #ifdef MESH_DBG
 	int	log_ix;
 	int	n_log;
@@ -192,9 +201,6 @@
 static void mesh_dump_regs(struct mesh_state *);
 static void mesh_start(struct mesh_state *);
 static void mesh_start_cmd(struct mesh_state *, Scsi_Cmnd *);
-#ifndef MESH_NEW_STYLE_EH
-static void finish_cmds(void *);
-#endif
 static void add_sdtr_msg(struct mesh_state *);
 static void set_sdtr(struct mesh_state *, int, int);
 static void start_phase(struct mesh_state *);
@@ -247,6 +253,16 @@
 	    use_active_neg = SEQ_ACTIVE_NEG;
 	}
 
+	/* Calculate sync rate from module parameters */
+	if (sync_rate > 10)
+		sync_rate = 10;
+	if (sync_rate > 0) {
+		printk(KERN_INFO "mesh: configured for synchronous %d MB/s\n", sync_rate);
+		mesh_sync_period = 1000 / sync_rate;	/* ns */
+		mesh_sync_offset = 15;
+	} else
+		printk(KERN_INFO "mesh: configured for asynchronous\n");
+
 	nmeshes = 0;
 	prev_statep = &all_meshes;
 	/*
@@ -258,13 +274,23 @@
 	if (mesh == 0)
 		mesh = find_compatible_devices("scsi", "chrp,mesh0");
 	for (; mesh != 0; mesh = mesh->next) {
-		struct device_node *mio;
+		u8 pci_bus, pci_devfn;
+		struct pci_dev* pdev = NULL;
 		
 		if (mesh->n_addrs != 2 || mesh->n_intrs != 2) {
 			printk(KERN_ERR "mesh: expected 2 addrs and 2 intrs"
-			       " (got %d,%d)", mesh->n_addrs, mesh->n_intrs);
+			       " (got %d,%d)\n", mesh->n_addrs, mesh->n_intrs);
 			continue;
 		}
+		if (mesh->parent != NULL
+		    && pci_device_from_OF_node(mesh->parent, &pci_bus,
+					       &pci_devfn) == 0)
+			pdev = pci_find_slot(pci_bus, pci_devfn);
+		if (pdev == NULL) {
+			printk(KERN_ERR "mesh: Can't locate PCI entry\n");
+			continue;
+		}
+
 		mesh_host = scsi_register(tp, sizeof(struct mesh_state));
 		if (mesh_host == 0) {
 			printk(KERN_ERR "mesh: couldn't register host");
@@ -281,6 +307,7 @@
 		memset(ms, 0, sizeof(*ms));
 		ms->host = mesh_host;
 		ms->ofnode = mesh;
+		ms->pdev = pdev;
 		ms->mesh = (volatile struct mesh_regs *)
 			ioremap(mesh->addrs[0].address, 0x1000);
 		ms->dma = (volatile struct dbdma_regs *)
@@ -305,10 +332,6 @@
 			ms->tgts[tgt].sync_params = ASYNC_PARAMS;
 			ms->tgts[tgt].current_req = 0;
 		}
-#ifndef MESH_NEW_STYLE_EH
-		ms->tqueue.routine = finish_cmds;
-		ms->tqueue.data = ms;
-#endif
 		*prev_statep = ms;
 		prev_statep = &ms->next;
 
@@ -325,12 +348,6 @@
 		if (mesh_sync_period < minper)
 			mesh_sync_period = minper;
 
-		ms->mio_base = 0;
-		for (mio = ms->ofnode->parent; mio; mio = mio->parent)
-			if (strcmp(mio->name, "mac-io") == 0 && mio->n_addrs > 0)
-				break;
-		if (mio)
-			ms->mio_base = (u8 *) ioremap(mio->addrs[0].address, 0x40);
 		set_mesh_power(ms, 1);
 
 		mesh_init(ms);
@@ -363,11 +380,9 @@
 		iounmap((void *) ms->mesh);
 	if (ms->dma)
 		iounmap((void *) ms->dma);
-	if (ms->mio_base)
-		iounmap((void *) ms->mio_base);
 	kfree(ms->dma_cmd_space);
 	free_irq(ms->meshintr, ms);
-	feature_clear(ms->ofnode, FEATURE_MESH_enable);
+	pmac_call_feature(PMAC_FTR_MESH_ENABLE, ms->ofnode, 0, 0);
 	return 0;
 }
 
@@ -377,16 +392,10 @@
 	if (_machine != _MACH_Pmac)
 		return;
 	if (state) {
-		feature_set(ms->ofnode, FEATURE_MESH_enable);
-		/* This seems to enable the termination power. strangely
-		   this doesn't fully agree with OF, but with MacOS */
-		if (ms->mio_base)
-			out_8(ms->mio_base + 0x36, 0x70);
+		pmac_call_feature(PMAC_FTR_MESH_ENABLE, ms->ofnode, 0, 1);
 		mdelay(200);
 	} else {
-		feature_clear(ms->ofnode, FEATURE_MESH_enable);
-		if (ms->mio_base)
-			out_8(ms->mio_base + 0x36, 0x34);
+		pmac_call_feature(PMAC_FTR_MESH_ENABLE, ms->ofnode, 0, 0);
 		mdelay(10);
 	}
 }			
@@ -411,15 +420,33 @@
 		
 	case PBOOK_SLEEP_NOW:
 		for (ms = all_meshes; ms != 0; ms = ms->next) {
+			unsigned long flags;
+
+			scsi_block_requests(ms->host);
+			spin_lock_irqsave(ms->host->host_lock, flags);
+			while(ms->phase != idle) {
+				spin_unlock_irqrestore(ms->host->host_lock, flags);
+				current->state = TASK_UNINTERRUPTIBLE;
+				schedule_timeout(1);
+				spin_lock_irqsave(ms->host->host_lock, flags);
+			}
+			ms->phase = sleeping;
+			spin_unlock_irqrestore(ms->host->host_lock, flags);
 			disable_irq(ms->meshintr);
 			set_mesh_power(ms, 0);
 		}
 		break;
 	case PBOOK_WAKE:
 		for (ms = all_meshes; ms != 0; ms = ms->next) {
+			unsigned long flags;
+			
 			set_mesh_power(ms, 1);
 			mesh_init(ms);
+			spin_lock_irqsave(ms->host->host_lock, flags);
+			mesh_start(ms);
+			spin_unlock_irqrestore(ms->host->host_lock, flags);
 			enable_irq(ms->meshintr);
+			scsi_unblock_requests(ms->host);
 		}
 		break;
 	}
@@ -427,10 +454,13 @@
 }
 #endif /* CONFIG_PMAC_PBOOK */
 
+/*
+ * Called by midlayer with host locked to queue a new
+ * request
+ */
 int
 mesh_queue(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
 {
-	unsigned long flags;
 	struct mesh_state *ms;
 
 	cmd->scsi_done = done;
@@ -438,8 +468,6 @@
 
 	ms = (struct mesh_state *) cmd->host->hostdata;
 
-	save_flags(flags);
-	cli();
 	if (ms->request_q == NULL)
 		ms->request_q = cmd;
 	else
@@ -449,19 +477,25 @@
 	if (ms->phase == idle)
 		mesh_start(ms);
 
-	restore_flags(flags);
 	return 0;
 }
 
+/* Todo: here we can at least try to remove the command from the
+ * queue if it isn't connected yet, and for pending command, assert
+ * ATN until the bus gets freed.
+ */
 int
 mesh_abort(Scsi_Cmnd *cmd)
 {
 	struct mesh_state *ms = (struct mesh_state *) cmd->host->hostdata;
+	unsigned long flags;
 
 	printk(KERN_DEBUG "mesh_abort(%p)\n", cmd);
+	spin_lock_irqsave(ms->host->host_lock, flags);
 	mesh_dump_regs(ms);
 	dumplog(ms, cmd->target);
 	dumpslog(ms);
+	spin_unlock_irqrestore(ms->host->host_lock, flags);
 	return SCSI_ABORT_SNOOZE;
 }
 
@@ -498,49 +532,41 @@
 	}
 }
 
+/*
+ * Called by the midlayer with the lock held to reset the
+ * SCSI host and bus.
+ * The midlayer will wait for devices to come back, we don't need
+ * to do that ourselves
+ */
 int
-mesh_reset(Scsi_Cmnd *cmd, unsigned how)
+mesh_host_reset(Scsi_Cmnd *cmd)
 {
 	struct mesh_state *ms = (struct mesh_state *) cmd->host->hostdata;
 	volatile struct mesh_regs *mr = ms->mesh;
 	volatile struct dbdma_regs *md = ms->dma;
-	unsigned long flags;
-	int ret;
 
-	printk(KERN_DEBUG "mesh_reset %x\n", how);
-	ret = SCSI_RESET_BUS_RESET;
-	save_flags(flags);
-	cli();
+	printk(KERN_DEBUG "mesh_host_reset\n");
+
+	/* Reset the controller & dbdma channel */
 	out_le32(&md->control, (RUN|PAUSE|FLUSH|WAKE) << 16);	/* stop dma */
 	out_8(&mr->exception, 0xff);	/* clear all exception bits */
 	out_8(&mr->error, 0xff);	/* clear all error bits */
-	if (how & SCSI_RESET_SUGGEST_HOST_RESET) {
-		out_8(&mr->sequence, SEQ_RESETMESH);
-		ret |= SCSI_RESET_HOST_RESET;
-		udelay(1);
-		out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
-		out_8(&mr->source_id, ms->host->this_id);
-		out_8(&mr->sel_timeout, 25);	/* 250ms */
-		out_8(&mr->sync_params, ASYNC_PARAMS);
-	}
+	out_8(&mr->sequence, SEQ_RESETMESH);
+	udelay(1);
+	out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+	out_8(&mr->source_id, ms->host->this_id);
+	out_8(&mr->sel_timeout, 25);	/* 250ms */
+	out_8(&mr->sync_params, ASYNC_PARAMS);
+
+	/* Reset the bus */
 	out_8(&mr->bus_status1, BS1_RST);	/* assert RST */
 	udelay(30);			/* leave it on for >= 25us */
 	out_8(&mr->bus_status1, 0);	/* negate RST */
-#ifdef DO_ASYNC_RESET
-	if (how & SCSI_RESET_ASYNCHRONOUS) {
-		restore_flags(flags);
-		ret |= SCSI_RESET_PENDING;
-	} else
-#endif
-	{
-		handle_reset(ms);
-		restore_flags(flags);
-#ifndef MESH_NEW_STYLE_EH
-		finish_cmds(ms);
-#endif
-		ret |= SCSI_RESET_SUCCESS;
-	}
-	return ret;
+
+	/* Complete pending commands */
+	handle_reset(ms);
+	
+	return SUCCESS;
 }
 
 /*
@@ -569,13 +595,10 @@
 	return NOTIFY_DONE;
 }
 
-int
-mesh_command(Scsi_Cmnd *cmd)
-{
-	printk(KERN_WARNING "whoops... mesh_command called\n");
-	return -1;
-}
-
+/* Called with  meshinterrupt disabled, initialize the chipset
+ * and eventually do the initial bus reset. The lock must not be
+ * held since we can schedule.
+ */
 static void
 mesh_init(struct mesh_state *ms)
 {
@@ -584,6 +607,7 @@
 
 	udelay(100);
 
+	/* Reset controller */
 	out_le32(&md->control, (RUN|PAUSE|FLUSH|WAKE) << 16);	/* stop dma */
 	out_8(&mr->exception, 0xff);	/* clear all exception bits */
 	out_8(&mr->error, 0xff);	/* clear all error bits */
@@ -594,15 +618,28 @@
 	out_8(&mr->sel_timeout, 25);	/* 250ms */
 	out_8(&mr->sync_params, ASYNC_PARAMS);
 
-	out_8(&mr->bus_status1, BS1_RST);	/* assert RST */
-	udelay(30);			/* leave it on for >= 25us */
-	out_8(&mr->bus_status1, 0);	/* negate RST */
-
+	if (init_reset_delay) {
+		printk(KERN_INFO "mesh: performing initial bus reset...\n");
+		
+		/* Reset bus */
+		out_8(&mr->bus_status1, BS1_RST);	/* assert RST */
+		udelay(30);			/* leave it on for >= 25us */
+		out_8(&mr->bus_status1, 0);	/* negate RST */
+
+		/* Wait for bus to come back */
+		current->state = TASK_UNINTERRUPTIBLE;
+		schedule_timeout((init_reset_delay * HZ) / 1000);
+	}
+	
+	/* Reconfigure controller */
+	out_8(&mr->interrupt, 0xff);	/* clear all interrupt bits */
 	out_8(&mr->sequence, SEQ_FLUSHFIFO);
 	udelay(1);
 	out_8(&mr->sync_params, ASYNC_PARAMS);
 	out_8(&mr->sequence, SEQ_ENBRESEL);
-	out_8(&mr->interrupt, 0xff);	/* clear all interrupt bits */
+
+	ms->phase = idle;
+	ms->msgphase = msg_none;
 }
 
 /*
@@ -662,6 +699,8 @@
 		       cmd->use_sg, cmd->request_buffer, cmd->request_bufflen);
 	}
 #endif
+	if (ms->dma_started)
+		panic("mesh: double DMA start !\n");
 
 	ms->phase = arbitrating;
 	ms->msgphase = msg_none;
@@ -786,28 +825,6 @@
 	}
 }
 
-#ifndef MESH_NEW_STYLE_EH
-static void
-finish_cmds(void *data)
-{
-	struct mesh_state *ms = data;
-	Scsi_Cmnd *cmd;
-	unsigned long flags;
-
-	for (;;) {
-		spin_lock_irqsave(ms->host->host_lock, flags);
-		cmd = ms->completed_q;
-		if (cmd == NULL) {
-			spin_unlock_irqrestore(ms->host->host_lock, flags);
-			break;
-		}
-		ms->completed_q = (Scsi_Cmnd *) cmd->host_scribble;
-		(*cmd->scsi_done)(cmd);
-		spin_unlock_irqrestore(ms->host->host_lock, flags);
-	}
-}
-#endif /* MESH_NEW_STYLE_EH */
-
 static inline void
 add_sdtr_msg(struct mesh_state *ms)
 {
@@ -1332,10 +1349,13 @@
 		dumpslog(ms);
 	}
 
+	if (ms->dma_started) {
+		printk(KERN_ERR "mesh: reselected with DMA started !\n");
+		halt_dma(ms);
+	}
 	ms->current_req = NULL;
 	ms->phase = dataing;
 	ms->msgphase = msg_in;
-	ms->dma_started = 0;
 	ms->n_msgout = 0;
 	ms->last_n_msgout = 0;
 	prev = ms->conn_tgt;
@@ -1458,7 +1478,7 @@
 do_mesh_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
 {
 	unsigned long flags;
-	struct Scsi_Host *dev = ((struct mech_state *)dev_id)->host;
+	struct Scsi_Host *dev = ((struct mesh_state *)dev_id)->host;
 	
 	spin_lock_irqsave(dev->host_lock, flags);
 	mesh_interrupt(irq, dev_id, ptregs);
@@ -1755,18 +1775,7 @@
 static void
 mesh_completed(struct mesh_state *ms, Scsi_Cmnd *cmd)
 {
-#ifdef MESH_NEW_STYLE_EH
 	(*cmd->scsi_done)(cmd);
-#else
-	if (ms->completed_q == NULL)
-		ms->completed_q = cmd;
-	else
-		ms->completed_qtail->host_scribble = (void *) cmd;
-	ms->completed_qtail = cmd;
-	cmd->host_scribble = NULL;
-	queue_task(&ms->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
-#endif /* MESH_NEW_STYLE_EH */
 }
 
 /*
@@ -1786,24 +1795,29 @@
 	if (cmd) {
 		cmd->SCp.this_residual = cmd->request_bufflen;
 		if (cmd->use_sg > 0) {
+			int nseg;
 			total = 0;
 			scl = (struct scatterlist *) cmd->buffer;
 			off = ms->data_ptr;
-			for (i = 0; i < cmd->use_sg; ++i, ++scl) {
+			nseg = pci_map_sg(ms->pdev, scl, cmd->use_sg,
+				 scsi_to_pci_dma_dir(cmd ->sc_data_direction));
+			for (i = 0; i <nseg; ++i, ++scl) {
+				u32 dma_addr = sg_dma_address(scl);
+				u32 dma_len = sg_dma_len(scl);
+				
 				total += scl->length;
-				if (off >= scl->length) {
-					off -= scl->length;
+				if (off >= dma_len) {
+					off -= dma_len;
 					continue;
 				}
-				if (scl->length > 0xffff)
+				if (dma_len > 0xffff)
 					panic("mesh: scatterlist element >= 64k");
-				st_le16(&dcmds->req_count, scl->length - off);
+				st_le16(&dcmds->req_count, dma_len - off);
 				st_le16(&dcmds->command, dma_cmd);
-				st_le32(&dcmds->phy_addr,
-					virt_to_phys(scl->address) + off);
+				st_le32(&dcmds->phy_addr, dma_addr + off);
 				dcmds->xfer_status = 0;
 				++dcmds;
-				dtot += scl->length - off;
+				dtot += dma_len - off;
 				off = 0;
 			}
 		} else if (ms->data_ptr < cmd->request_bufflen) {
@@ -1811,6 +1825,7 @@
 			if (dtot > 0xffff)
 				panic("mesh: transfer size >= 64k");
 			st_le16(&dcmds->req_count, dtot);
+			/* XXX Use pci DMA API here ... */
 			st_le32(&dcmds->phy_addr,
 				virt_to_phys(cmd->request_buffer) + ms->data_ptr);
 			dcmds->xfer_status = 0;
@@ -1876,6 +1891,12 @@
 		       "data_ptr=%x total=%x goes_out=%d\n",
 		       ms->conn_tgt, ms->data_ptr, cmd->request_bufflen,
 		       ms->tgts[ms->conn_tgt].data_goes_out);
+	}
+	if (cmd->use_sg != 0) {
+		struct scatterlist *sg;
+		sg = (struct scatterlist *)cmd->request_buffer;
+		pci_unmap_sg(ms->pdev, sg, cmd->use_sg,
+			     scsi_to_pci_dma_dir(cmd->sc_data_direction));
 	}
 	ms->dma_started = 0;
 }
diff -Nru a/drivers/scsi/mesh.h b/drivers/scsi/mesh.h
--- a/drivers/scsi/mesh.h	Sat May 18 00:46:08 2002
+++ b/drivers/scsi/mesh.h	Sat May 18 00:46:08 2002
@@ -9,25 +9,26 @@
 
 int mesh_detect(Scsi_Host_Template *);
 int mesh_release(struct Scsi_Host *);
-int mesh_command(Scsi_Cmnd *);
 int mesh_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int mesh_abort(Scsi_Cmnd *);
-int mesh_reset(Scsi_Cmnd *, unsigned int);
+int mesh_host_reset(Scsi_Cmnd *);
 
-#define SCSI_MESH {					\
-	proc_name:	"mesh",				\
-	name:		"MESH",				\
-	detect:		mesh_detect,			\
-	release:	mesh_release,			\
-	command:	mesh_command,			\
-	queuecommand:	mesh_queue,			\
-	abort:		mesh_abort,			\
-	reset:		mesh_reset,			\
-	can_queue:	20,				\
-	this_id:	7,				\
-	sg_tablesize:	SG_ALL,				\
-	cmd_per_lun:	2,				\
-	use_clustering:	DISABLE_CLUSTERING,		\
+#define SCSI_MESH {						\
+	proc_name:			"mesh",			\
+	name:				"MESH",			\
+	detect:				mesh_detect,		\
+	release:			mesh_release,		\
+	command:			NULL,			\
+	queuecommand:			mesh_queue,		\
+	eh_abort_handler:		mesh_abort,		\
+	eh_device_reset_handler:	NULL,			\
+	eh_bus_reset_handler:		NULL,			\
+	eh_host_reset_handler:		mesh_host_reset,	\
+	can_queue:			20,			\
+	this_id:			7,			\
+	sg_tablesize:			SG_ALL,			\
+	cmd_per_lun:			2,			\
+	use_clustering:			DISABLE_CLUSTERING,	\
 }
 
 /*
diff -Nru a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
--- a/drivers/scsi/scsi_lib.c	Sat May 18 00:46:10 2002
+++ b/drivers/scsi/scsi_lib.c	Sat May 18 00:46:10 2002
@@ -963,21 +963,22 @@
 
 			/* 
 			 * This sets up the scatter-gather table (allocating if
-			 * required).  Hosts that need bounce buffers will also
-			 * get those allocated here.  
+			 * required).
 			 */
 			if (!scsi_init_io(SCpnt)) {
-				SCpnt = __scsi_end_request(SCpnt, 0, 
-							   SCpnt->request.nr_sectors, 0, 0);
-				if( SCpnt != NULL )
-				{
-					panic("Should not have leftover blocks\n");
-				}
 				spin_lock_irq(q->queue_lock);
 				SHpnt->host_busy--;
 				SDpnt->device_busy--;
-				continue;
+				if (SDpnt->device_busy == 0) {
+					SDpnt->starved = 1;
+					SHpnt->some_device_starved = 1;
+				}
+				SCpnt->request.special = SCpnt;
+				SCpnt->request.flags |= REQ_SPECIAL;
+				_elv_add_request(q, &SCpnt->request, 0, 0);
+				break;
 			}
+
 			/*
 			 * Initialize the actual SCSI command for this request.
 			 */
diff -Nru a/drivers/scsi/scsi_merge.c b/drivers/scsi/scsi_merge.c
--- a/drivers/scsi/scsi_merge.c	Sat May 18 00:46:15 2002
+++ b/drivers/scsi/scsi_merge.c	Sat May 18 00:46:15 2002
@@ -78,8 +78,12 @@
 	if (in_interrupt())
 		gfp_mask &= ~__GFP_WAIT;
 
+	/*
+	 * if sg table allocation fails, requeue request later.
+	 */
 	sgpnt = scsi_alloc_sgtable(SCpnt, gfp_mask);
-	BUG_ON(!sgpnt);
+	if (!sgpnt)
+		return 0;
 
 	SCpnt->request_buffer = (char *) sgpnt;
 	SCpnt->request_bufflen = req->nr_sectors << 9;
@@ -102,8 +106,13 @@
 	printk("Incorrect number of segments after building list\n");
 	printk("counted %d, received %d\n", count, SCpnt->use_sg);
 	printk("req nr_sec %lu, cur_nr_sec %u\n", req->nr_sectors, req->current_nr_sectors);
-	BUG();
-	return 0; /* ahem */
+
+	/*
+	 * kill it. there should be no leftover blocks in this request
+	 */
+	SCpnt = scsi_end_request(SCpnt, 0, req->nr_sectors);
+	BUG_ON(SCpnt);
+	return 0;
 }
 
 /*
diff -Nru a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
--- a/drivers/usb/core/hcd.c	Sat May 18 00:46:14 2002
+++ b/drivers/usb/core/hcd.c	Sat May 18 00:46:14 2002
@@ -1320,7 +1320,7 @@
 	list_del_init (&urb->urb_list);
 	dev = urb->dev;
 	urb->dev = NULL;
-	usb_dec_dev_use (dev);
+	usb_put_dev (dev);
 	spin_unlock_irqrestore (&hcd_data_lock, flags);
 }
 
@@ -1516,7 +1516,7 @@
 
 	spin_lock_irqsave (&hcd_data_lock, flags);
 	if (HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_QUIESCING) {
-		usb_inc_dev_use (urb->dev);
+		usb_get_dev (urb->dev);
 		list_add (&urb->urb_list, &dev->urb_list);
 		status = 0;
 	} else {
@@ -1732,7 +1732,8 @@
 		return -EINVAL;
 	}
 
-	hcd->driver->free_config (hcd, udev);
+	if (hcd->driver->free_config)
+		hcd->driver->free_config (hcd, udev);
 
 	spin_lock_irqsave (&hcd_data_lock, flags);
 	list_del (&dev->dev_list);
diff -Nru a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
--- a/drivers/usb/core/hcd.h	Sat May 18 00:46:09 2002
+++ b/drivers/usb/core/hcd.h	Sat May 18 00:46:09 2002
@@ -182,9 +182,6 @@
 /* -------------------------------------------------------------------------- */
 
 /* Enumeration is only for the hub driver, or HCD virtual root hubs */
-extern struct usb_device *usb_alloc_dev(struct usb_device *parent,
-	struct usb_bus *);
-extern void usb_free_dev(struct usb_device *);
 extern int usb_new_device(struct usb_device *dev);
 extern void usb_connect(struct usb_device *dev);
 extern void usb_disconnect(struct usb_device **);
diff -Nru a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
--- a/drivers/usb/core/hub.c	Sat May 18 00:46:09 2002
+++ b/drivers/usb/core/hub.c	Sat May 18 00:46:09 2002
@@ -149,6 +149,98 @@
 	spin_unlock_irqrestore(&hub_event_lock, flags);
 }
 
+/* USB 2.0 spec Section 11.24.2.3 */
+static inline int
+hub_clear_tt_buffer (struct usb_device *hub, u16 devinfo, u16 tt)
+{
+	return usb_control_msg (hub, usb_rcvctrlpipe (hub, 0),
+		HUB_CLEAR_TT_BUFFER, USB_DIR_IN | USB_RECIP_OTHER,
+		devinfo, tt, 0, 0, HZ);
+}
+
+/*
+ * enumeration blocks khubd for a long time. we use keventd instead, since
+ * long blocking there is the exception, not the rule.  accordingly, HCDs
+ * talking to TTs must queue control transfers (not just bulk and iso), so
+ * both can talk to the same hub concurrently.
+ */
+static void hub_tt_kevent (void *arg)
+{
+	struct usb_hub		*hub = arg;
+	unsigned long		flags;
+
+	spin_lock_irqsave (&hub->tt.lock, flags);
+	while (!list_empty (&hub->tt.clear_list)) {
+		struct list_head	*temp;
+		struct usb_tt_clear	*clear;
+		int			status;
+
+		temp = hub->tt.clear_list.next;
+		clear = list_entry (temp, struct usb_tt_clear, clear_list);
+		list_del (&clear->clear_list);
+
+		/* drop lock so HCD can concurrently report other TT errors */
+		spin_unlock_irqrestore (&hub->tt.lock, flags);
+		status = hub_clear_tt_buffer (hub->dev,
+				clear->devinfo, clear->tt);
+		spin_lock_irqsave (&hub->tt.lock, flags);
+
+		if (status)
+			err ("usb-%s-%s clear tt %d (%04x) error %d",
+				hub->dev->bus->bus_name, hub->dev->devpath,
+				clear->tt, clear->devinfo, status);
+		kfree (clear);
+	}
+	spin_unlock_irqrestore (&hub->tt.lock, flags);
+}
+
+/**
+ * usb_hub_tt_clear_buffer - clear control/bulk TT state in high speed hub
+ * @dev: the device whose split transaction failed
+ * @pipe: identifies the endpoint of the failed transaction
+ *
+ * High speed HCDs use this to tell the hub driver that some split control or
+ * bulk transaction failed in a way that requires clearing internal state of
+ * a transaction translator.  This is normally detected (and reported) from
+ * interrupt context.
+ *
+ * It may not be possible for that hub to handle additional full (or low)
+ * speed transactions until that state is fully cleared out.
+ */
+void usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe)
+{
+	struct usb_tt		*tt = dev->tt;
+	unsigned long		flags;
+	struct usb_tt_clear	*clear;
+
+	/* we've got to cope with an arbitrary number of pending TT clears,
+	 * since each TT has "at least two" buffers that can need it (and
+	 * there can be many TTs per hub).  even if they're uncommon.
+	 */
+	if ((clear = kmalloc (sizeof *clear, SLAB_ATOMIC)) == 0) {
+		err ("can't save CLEAR_TT_BUFFER state for hub at usb-%s-%s",
+			dev->bus->bus_name, tt->hub->devpath);
+		/* FIXME recover somehow ... RESET_TT? */
+		return;
+	}
+
+	/* info that CLEAR_TT_BUFFER needs */
+	clear->tt = tt->multi ? dev->ttport : 1;
+	clear->devinfo = usb_pipeendpoint (pipe);
+	clear->devinfo |= dev->devnum << 4;
+	clear->devinfo |= usb_pipecontrol (pipe)
+			? (USB_ENDPOINT_XFER_CONTROL << 11)
+			: (USB_ENDPOINT_XFER_BULK << 11);
+	if (usb_pipein (pipe))
+		clear->devinfo |= 1 << 15;
+	
+	/* tell keventd to clear state for this TT */
+	spin_lock_irqsave (&tt->lock, flags);
+	list_add_tail (&clear->clear_list, &tt->clear_list);
+	schedule_task (&tt->kevent);
+	spin_unlock_irqrestore (&tt->lock, flags);
+}
+
 static void usb_hub_power_on(struct usb_hub *hub)
 {
 	int i;
@@ -231,6 +323,9 @@
                         break;
 	}
 
+	spin_lock_init (&hub->tt.lock);
+	INIT_LIST_HEAD (&hub->tt.clear_list);
+	INIT_TQUEUE (&hub->tt.kevent, hub_tt_kevent, hub);
 	switch (dev->descriptor.bDeviceProtocol) {
 		case 0:
 			break;
@@ -431,6 +526,10 @@
 
 	down(&hub->khubd_sem); /* Wait for khubd to leave this hub alone. */
 	up(&hub->khubd_sem);
+
+	/* assuming we used keventd, it must quiesce too */
+	if (hub->tt.hub)
+		flush_scheduled_tasks ();
 
 	if (hub->urb) {
 		usb_unlink_urb(hub->urb);
diff -Nru a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
--- a/drivers/usb/core/hub.h	Sat May 18 00:46:08 2002
+++ b/drivers/usb/core/hub.h	Sat May 18 00:46:08 2002
@@ -136,6 +136,34 @@
 
 struct usb_device;
 
+/*
+ * As of USB 2.0, full/low speed devices are segregated into trees.
+ * One type grows from USB 1.1 host controllers (OHCI, UHCI etc).
+ * The other type grows from high speed hubs when they connect to
+ * full/low speed devices using "Transaction Translators" (TTs).
+ *
+ * TTs should only be known to the hub driver, and high speed bus
+ * drivers (only EHCI for now).  They affect periodic scheduling and
+ * sometimes control/bulk error recovery.
+ */
+struct usb_tt {
+	struct usb_device	*hub;	/* upstream highspeed hub */
+	int			multi;	/* true means one TT per port */
+
+	/* for control/bulk error recovery (CLEAR_TT_BUFFER) */
+	spinlock_t		lock;
+	struct list_head	clear_list;	/* of usb_tt_clear */
+	struct tq_struct	kevent;
+};
+
+struct usb_tt_clear {
+	struct list_head	clear_list;
+	unsigned		tt;
+	u16			devinfo;
+};
+
+extern void usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe);
+
 struct usb_hub {
 	struct usb_device	*dev;		/* the "real" device */
 	struct urb		*urb;		/* for interrupt polling pipe */
diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
--- a/drivers/usb/core/usb.c	Sat May 18 00:46:10 2002
+++ b/drivers/usb/core/usb.c	Sat May 18 00:46:10 2002
@@ -962,35 +962,54 @@
 
 	init_MUTEX(&dev->serialize);
 
-	dev->bus->op->allocate(dev);
+	if (dev->bus->op->allocate)
+		dev->bus->op->allocate(dev);
 
 	return dev;
 }
 
 /**
- * usb_free_dev - free a usb device structure (usbcore-internal)
+ * usb_get_dev - increments the reference count of the device
+ * @dev: the device being referenced
+ *
+ * Each live reference to a device should be refcounted.
+ *
+ * Drivers for USB interfaces should normally record such references in
+ * their probe() methods, when they bind to an interface, and release
+ * them by calling usb_put_dev(), in their disconnect() methods.
+ *
+ * A pointer to the device with the incremented reference counter is returned.
+ */
+struct usb_device *usb_get_dev (struct usb_device *dev)
+{
+	if (dev) {
+		atomic_inc (&dev->refcnt);
+		return dev;
+	}
+	return NULL;
+}
+
+/**
+ * usb_free_dev - free a usb device structure when all users of it are finished.
  * @dev: device that's been disconnected
  * Context: !in_interrupt ()
  *
+ * Must be called when a user of a device is finished with it.  When the last
+ * user of the device calls this function, the memory of the device is freed.
+ *
  * Used by hub and virtual root hub drivers.  The device is completely
  * gone, everything is cleaned up, so it's time to get rid of these last
  * records of this device.
  */
 void usb_free_dev(struct usb_device *dev)
 {
-	if (in_interrupt ())
-		BUG ();
-	if (!atomic_dec_and_test (&dev->refcnt)) {
-		/* MUST go to zero here, else someone's hanging on to
-		 * a device that's supposed to have been cleaned up!!
-		 */
-		BUG ();
+	if (atomic_dec_and_test(&dev->refcnt)) {
+		if (dev->bus->op->deallocate)
+			dev->bus->op->deallocate(dev);
+		usb_destroy_configuration (dev);
+		usb_bus_put (dev->bus);
+		kfree (dev);
 	}
-
-	dev->bus->op->deallocate (dev);
-	usb_destroy_configuration (dev);
-	usb_bus_put (dev->bus);
-	kfree (dev);
 }
 
 /**
@@ -1046,7 +1065,7 @@
 }
 
 /**
- * usb_get_urb - incrementes the reference count of the urb
+ * usb_get_urb - increments the reference count of the urb
  * @urb: pointer to the urb to modify
  *
  * This must be  called whenever a urb is transfered from a device driver to a
@@ -1162,10 +1181,15 @@
  */
 int usb_submit_urb(struct urb *urb, int mem_flags)
 {
-	if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op)
+
+	if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op) {
+		if (usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)) <= 0) {
+			err("%s: pipe %x has invalid size (<= 0)", __FUNCTION__, urb->pipe);
+			return -EMSGSIZE;
+		}
 		return urb->dev->bus->op->submit_urb(urb, mem_flags);
-	else
-		return -ENODEV;
+	}
+	return -ENODEV;
 }
 
 /*-------------------------------------------------------------------*/
@@ -1928,8 +1952,9 @@
 		put_device(&dev->dev);
 	}
 
-	/* Free up the device itself */
-	usb_free_dev(dev);
+	/* Decrement the reference count, it'll auto free everything when */
+	/* it hits 0 which could very well be now */
+	usb_put_dev(dev);
 }
 
 /**
@@ -2558,9 +2583,13 @@
  * Only hub drivers (including virtual root hub drivers for host
  * controllers) should ever call this.
  */
+#define NEW_DEVICE_RETRYS	2
+#define SET_ADDRESS_RETRYS	2
 int usb_new_device(struct usb_device *dev)
 {
-	int err;
+	int err = 0;
+	int i;
+	int j;
 
 	/* USB v1.1 5.5.3 */
 	/* We read the first 8 bytes from the device descriptor to get to */
@@ -2569,18 +2598,30 @@
 	dev->epmaxpacketin [0] = 8;
 	dev->epmaxpacketout[0] = 8;
 
-	err = usb_set_address(dev);
-	if (err < 0) {
-		err("USB device not accepting new address=%d (error=%d)",
-			dev->devnum, err);
-		clear_bit(dev->devnum, dev->bus->devmap.devicemap);
-		dev->devnum = -1;
-		return 1;
-	}
+	for (i = 0; i < NEW_DEVICE_RETRYS; ++i) {
 
-	wait_ms(10);	/* Let the SET_ADDRESS settle */
+		for (j = 0; j < SET_ADDRESS_RETRYS; ++j) {
+			err = usb_set_address(dev);
+			if (err >= 0)
+				break;
+			wait_ms(200);
+		}
+		if (err < 0) {
+			err("USB device not accepting new address=%d (error=%d)",
+				dev->devnum, err);
+			clear_bit(dev->devnum, dev->bus->devmap.devicemap);
+			dev->devnum = -1;
+			return 1;
+		}
+
+		wait_ms(10);	/* Let the SET_ADDRESS settle */
+
+		err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8);
+		if (err >= 8)
+			break;
+		wait_ms(100);
+	}
 
-	err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8);
 	if (err < 8) {
 		if (err < 0)
 			err("USB device not responding, giving up (error=%d)", err);
@@ -2743,9 +2784,8 @@
 
 /*
  * USB may be built into the kernel or be built as modules.
- * If the USB core [and maybe a host controller driver] is built
- * into the kernel, and other device drivers are built as modules,
- * then these symbols need to be exported for the modules to use.
+ * These symbols are exported for device (or host controller)
+ * driver modules to use.
  */
 EXPORT_SYMBOL(usb_ifnum_to_ifpos);
 EXPORT_SYMBOL(usb_ifnum_to_if);
@@ -2762,6 +2802,8 @@
 
 EXPORT_SYMBOL(usb_alloc_dev);
 EXPORT_SYMBOL(usb_free_dev);
+EXPORT_SYMBOL(usb_get_dev);
+EXPORT_SYMBOL(usb_hub_tt_clear_buffer);
 
 EXPORT_SYMBOL(usb_find_interface_driver_for_ifnum);
 EXPORT_SYMBOL(usb_driver_claim_interface);
@@ -2799,6 +2841,5 @@
 EXPORT_SYMBOL(usb_set_configuration);
 EXPORT_SYMBOL(usb_set_interface);
 
-EXPORT_SYMBOL(usb_make_path);
 EXPORT_SYMBOL(usb_devfs_handle);
 MODULE_LICENSE("GPL");
diff -Nru a/drivers/usb/host/Config.in b/drivers/usb/host/Config.in
--- a/drivers/usb/host/Config.in	Sat May 18 00:46:08 2002
+++ b/drivers/usb/host/Config.in	Sat May 18 00:46:08 2002
@@ -4,7 +4,12 @@
 comment 'USB Host Controller Drivers'
 dep_tristate '  EHCI HCD (USB 2.0) support (EXPERIMENTAL)' CONFIG_USB_EHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
 dep_tristate '  OHCI HCD support (EXPERIMENTAL)' CONFIG_USB_OHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
-# dep_tristate '  UHCI HCD (most Intel and VIA) support (EXPERIMENTAL)' CONFIG_USB_UHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
+if [ "$CONFIG_USB_UHCI_HCD_ALT" != "y" ]; then
+   dep_tristate '  UHCI HCD (most Intel and VIA) support (EXPERIMENTAL)' CONFIG_USB_UHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
+fi
+if [ "$CONFIG_USB_UHCI_HCD" != "y" ]; then
+   dep_tristate '  UHCI HCD Alternate (most Intel and VIA) support (EXPERIMENTAL)' CONFIG_USB_UHCI_HCD_ALT $CONFIG_USB $CONFIG_EXPERIMENTAL
+fi
 if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then
    dep_tristate '  UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB
 fi
@@ -13,4 +18,4 @@
 else
    define_bool CONFIG_USB_UHCI_ALT n
 fi
-dep_tristate '  OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB
+#dep_tristate '  OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB
diff -Nru a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
--- a/drivers/usb/host/Makefile	Sat May 18 00:46:15 2002
+++ b/drivers/usb/host/Makefile	Sat May 18 00:46:15 2002
@@ -7,7 +7,8 @@
 
 obj-$(CONFIG_USB_EHCI_HCD)	+= ehci-hcd.o
 obj-$(CONFIG_USB_OHCI_HCD)	+= ohci-hcd.o
-# obj-$(CONFIG_USB_UHCI_HCD)	+= uhci-hcd.o
+obj-$(CONFIG_USB_UHCI_HCD)	+= usb-uhci-hcd.o
+obj-$(CONFIG_USB_UHCI_HCD_ALT)	+= uhci-hcd.o
 
 obj-$(CONFIG_USB_UHCI)		+= usb-uhci.o
 obj-$(CONFIG_USB_UHCI_ALT)	+= uhci.o
diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
--- a/drivers/usb/host/ehci-hcd.c	Sat May 18 00:46:14 2002
+++ b/drivers/usb/host/ehci-hcd.c	Sat May 18 00:46:14 2002
@@ -46,8 +46,6 @@
 #include <asm/system.h>
 #include <asm/unaligned.h>
 
-//#undef KERN_DEBUG
-//#define KERN_DEBUG ""
 
 /*-------------------------------------------------------------------------*/
 
@@ -55,21 +53,20 @@
  * EHCI hc_driver implementation ... experimental, incomplete.
  * Based on the final 1.0 register interface specification.
  *
- * There are lots of things to help out with here ... notably
- * everything "periodic", and of course testing with all sorts
- * of usb 2.0 devices and configurations.
- *
  * USB 2.0 shows up in upcoming www.pcmcia.org technology.
  * First was PCMCIA, like ISA; then CardBus, which is PCI.
  * Next comes "CardBay", using USB 2.0 signals.
  *
- * Contains additional contributions by:
- *	Brad Hards
- *	Rory Bolt
- *	...
+ * Contains additional contributions by: Brad Hards, Rory Bolt, ...
+ *
+ * Special thanks to Intel and VIA for providing host controllers to
+ * test this driver on, and Cypress (including In-System Design) for
+ * providing early devices for those host controllers to talk to!
  *
  * HISTORY:
  *
+ * 2002-05-11	Clear TT errors for FS/LS ctrl/bulk.  Fill in some other
+ *	missing pieces:  enabling 64bit dma, handoff from BIOS/SMM.
  * 2002-05-07	Some error path cleanups to report better errors; wmb();
  *	use non-CVS version id; better iso bandwidth claim.
  * 2002-04-19	Control/bulk/interrupt submit no longer uses giveback() on
@@ -85,7 +82,7 @@
  * 2001-June	Works with usb-storage and NEC EHCI on 2.4
  */
 
-#define DRIVER_VERSION "2002-May-07"
+#define DRIVER_VERSION "2002-May-11"
 #define DRIVER_AUTHOR "David Brownell"
 #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
 
@@ -165,6 +162,34 @@
 
 static void ehci_tasklet (unsigned long param);
 
+/* EHCI 0.96 (and later) section 5.1 says how to kick BIOS/SMM/...
+ * off the controller (maybe it can boot from highspeed USB disks).
+ */
+static int bios_handoff (struct ehci_hcd *ehci, int where, u32 cap)
+{
+	if (cap & (1 << 16)) {
+		int msec = 500;
+
+		/* request handoff to OS */
+		cap &= 1 << 24;
+		pci_write_config_dword (ehci->hcd.pdev, where, cap);
+
+		/* and wait a while for it to happen */
+		do {
+			wait_ms (10);
+			msec -= 10;
+			pci_read_config_dword (ehci->hcd.pdev, where, &cap);
+		} while ((cap & (1 << 16)) && msec);
+		if (cap & (1 << 16)) {
+			info ("BIOS handoff failed (%d, %04x)", where, cap);
+			return 1;
+		} 
+		dbg ("BIOS handoff succeeded");
+	} else
+		dbg ("BIOS handoff not needed");
+	return 0;
+}
+
 /* called by khubd or root hub init threads */
 
 static int ehci_start (struct usb_hcd *hcd)
@@ -176,10 +201,6 @@
 	u32			hcc_params;
 	u8                      tempbyte;
 
-	// FIXME:  given EHCI 0.96 or later, and a controller with
-	// the USBLEGSUP/USBLEGCTLSTS extended capability, make sure
-	// the BIOS doesn't still own this controller.
-
 	spin_lock_init (&ehci->lock);
 
 	ehci->caps = (struct ehci_caps *) hcd->regs;
@@ -187,6 +208,30 @@
 	dbg_hcs_params (ehci, "ehci_start");
 	dbg_hcc_params (ehci, "ehci_start");
 
+	hcc_params = readl (&ehci->caps->hcc_params);
+
+	/* EHCI 0.96 and later may have "extended capabilities" */
+	temp = HCC_EXT_CAPS (hcc_params);
+	while (temp) {
+		u32		cap;
+
+		pci_read_config_dword (ehci->hcd.pdev, temp, &cap);
+		dbg ("capability %04x at %02x", cap, temp);
+		switch (cap & 0xff) {
+		case 1:			/* BIOS/SMM/... handoff */
+			if (bios_handoff (ehci, temp, cap) != 0)
+				return -EOPNOTSUPP;
+			break;
+		case 0:			/* illegal reserved capability */
+			warn ("illegal capability!");
+			cap = 0;
+			/* FALLTHROUGH */
+		default:		/* unknown */
+			break;
+		}
+		temp = (cap >> 8) & 0xff;
+	}
+
 	/* cache this readonly data; minimize PCI reads */
 	ehci->hcs_params = readl (&ehci->caps->hcs_params);
 
@@ -197,7 +242,6 @@
 	ehci->periodic_size = DEFAULT_I_TDPS;
 	if ((retval = ehci_mem_init (ehci, SLAB_KERNEL)) < 0)
 		return retval;
-	hcc_params = readl (&ehci->caps->hcc_params);
 
 	/* controllers may cache some of the periodic schedule ... */
 	if (HCC_ISOC_CACHE (hcc_params)) 	// full frame cache
@@ -221,23 +265,24 @@
 	 * hcc_params controls whether ehci->regs->segment must (!!!)
 	 * be used; it constrains QH/ITD/SITD and QTD locations.
 	 * pci_pool consistent memory always uses segment zero.
+	 * streaming mappings for I/O buffers, like pci_map_single(),
+	 * can return segments above 4GB, if the device allows.
+	 *
+	 * NOTE:  layered drivers can't yet tell when we enable that,
+	 * so they can't pass this info along (like NETIF_F_HIGHDMA)
 	 */
 	if (HCC_64BIT_ADDR (hcc_params)) {
 		writel (0, &ehci->regs->segment);
-		/*
-		 * FIXME Enlarge pci_set_dma_mask() when possible.  The DMA
-		 * mapping API spec now says that'll affect only single shot
-		 * mappings, and the pci_pool data will stay safe in seg 0.
-		 * That's what we want:  no extra copies for USB transfers.
-		 */
-		info ("restricting 64bit DMA mappings to segment 0 ...");
+		if (!pci_set_dma_mask (ehci->hcd.pdev, 0xffffffffffffffffULL))
+			info ("enabled 64bit PCI DMA (DAC)");
 	}
 
 	/* clear interrupt enables, set irq latency */
 	temp = readl (&ehci->regs->command) & 0xff;
 	if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
-	    log2_irq_thresh = 0;
+		log2_irq_thresh = 0;
 	temp |= 1 << (16 + log2_irq_thresh);
+	// if hc can park (ehci >= 0.96), default is 3 packets per async QH 
 	// keeping default periodic framelist size
 	temp &= ~(CMD_IAAD | CMD_ASE | CMD_PSE),
 	// Philips, Intel, and maybe others need CMD_RUN before the
@@ -433,10 +478,6 @@
 	scan_async (ehci);
 	if (ehci->next_uframe != -1)
 		scan_periodic (ehci);
-
-	// FIXME:  when nothing is connected to the root hub,
-	// turn off the RUN bit so the host can enter C3 "sleep" power
-	// saving mode; make root hub code scan memory less often.
 }
 
 /*-------------------------------------------------------------------------*/
diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
--- a/drivers/usb/host/ehci-q.c	Sat May 18 00:46:08 2002
+++ b/drivers/usb/host/ehci-q.c	Sat May 18 00:46:08 2002
@@ -144,13 +144,12 @@
 					usb_pipeendpoint (pipe),
 					usb_pipeout (pipe));
 			if (urb->dev->tt && !usb_pipeint (pipe)) {
-err ("must CLEAR_TT_BUFFER, hub port %d%s addr %d ep %d",
-    urb->dev->ttport, /* devpath */
-    urb->dev->tt->multi ? "" : " (all-ports TT)",
-    urb->dev->devnum, usb_pipeendpoint (urb->pipe));
-				// FIXME something (khubd?) should make the hub
-				// CLEAR_TT_BUFFER ASAP, it's blocking other
-				// fs/ls requests... hub_tt_clear_buffer() ?
+				struct usb_device *tt = urb->dev->tt->hub;
+				dbg ("clear tt %s-%s p%d buffer, a%d ep%d",
+					tt->bus->bus_name, tt->devpath,
+    					urb->dev->ttport, urb->dev->devnum,
+    					usb_pipeendpoint (pipe));
+				usb_hub_tt_clear_buffer (urb->dev, pipe);
 			}
 		}
 	}
@@ -817,9 +816,9 @@
 		} else {
 			// dbg_qh ("empty qh", ehci, qh);
 
-// FIXME:  how handle usb_clear_halt() for an EP with queued URBs?
-// usbcore may not let us handle that cleanly...
-// likely must cancel them all first!
+			/* NOTE: we already canceled any queued URBs
+			 * when the endpoint halted.
+			 */
 
 			/* usb_clear_halt() means qh data toggle gets reset */
 			if (usb_pipebulk (urb->pipe)
diff -Nru a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/host/uhci-debug.c	Sat May 18 00:46:15 2002
@@ -0,0 +1,580 @@
+/*
+ * UHCI-specific debugging code. Invaluable when something
+ * goes wrong, but don't get in my face.
+ *
+ * Kernel visible pointers are surrounded in []'s and bus
+ * visible pointers are surrounded in ()'s
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2001 Johannes Erdfelt
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/smp_lock.h>
+#include <asm/io.h>
+
+#include "uhci-hcd.h"
+
+/* Handle REALLY large printk's so we don't overflow buffers */
+static void inline lprintk(char *buf)
+{
+	char *p;
+
+	/* Just write one line at a time */
+	while (buf) {
+		p = strchr(buf, '\n');
+		if (p)
+			*p = 0;
+		printk("%s\n", buf);
+		buf = p;
+		if (buf)
+			buf++;
+	}
+}
+
+static int inline uhci_is_skeleton_td(struct uhci_hcd *uhci, struct uhci_td *td)
+{
+	int i;
+
+	for (i = 0; i < UHCI_NUM_SKELTD; i++)
+		if (td == uhci->skeltd[i])
+			return 1;
+
+	return 0;
+}
+
+static int inline uhci_is_skeleton_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+	int i;
+
+	for (i = 0; i < UHCI_NUM_SKELQH; i++)
+		if (qh == uhci->skelqh[i])
+			return 1;
+
+	return 0;
+}
+
+static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space)
+{
+	char *out = buf;
+	char *spid;
+	u32 status, token;
+
+	/* Try to make sure there's enough memory */
+	if (len < 160)
+		return 0;
+
+	status = td_status(td);
+	out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td, le32_to_cpu(td->link));
+	out += sprintf(out, "e%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
+		((status >> 27) & 3),
+		(status & TD_CTRL_SPD) ?      "SPD " : "",
+		(status & TD_CTRL_LS) ?       "LS " : "",
+		(status & TD_CTRL_IOC) ?      "IOC " : "",
+		(status & TD_CTRL_ACTIVE) ?   "Active " : "",
+		(status & TD_CTRL_STALLED) ?  "Stalled " : "",
+		(status & TD_CTRL_DBUFERR) ?  "DataBufErr " : "",
+		(status & TD_CTRL_BABBLE) ?   "Babble " : "",
+		(status & TD_CTRL_NAK) ?      "NAK " : "",
+		(status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
+		(status & TD_CTRL_BITSTUFF) ? "BitStuff " : "",
+		status & 0x7ff);
+
+	token = td_token(td);
+	switch (uhci_packetid(token)) {
+		case USB_PID_SETUP:
+			spid = "SETUP";
+			break;
+		case USB_PID_OUT:
+			spid = "OUT";
+			break;
+		case USB_PID_IN:
+			spid = "IN";
+			break;
+		default:
+			spid = "?";
+			break;
+	}
+
+	out += sprintf(out, "MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ",
+		token >> 21,
+		((token >> 19) & 1),
+		(token >> 15) & 15,
+		(token >> 8) & 127,
+		(token & 0xff),
+		spid);
+	out += sprintf(out, "(buf=%08x)\n", le32_to_cpu(td->buffer));
+
+	return out - buf;
+}
+
+static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
+{
+	char *out = buf;
+
+	/* Try to make sure there's enough memory */
+	if (len < 80)
+		return 0;
+
+	out += sprintf(out, "  stat%d     =     %04x   %s%s%s%s%s%s%s%s\n",
+		port,
+		status,
+		(status & USBPORTSC_SUSP) ? "PortSuspend " : "",
+		(status & USBPORTSC_PR) ?   "PortReset " : "",
+		(status & USBPORTSC_LSDA) ? "LowSpeed " : "",
+		(status & USBPORTSC_RD) ?   "ResumeDetect " : "",
+		(status & USBPORTSC_PEC) ?  "EnableChange " : "",
+		(status & USBPORTSC_PE) ?   "PortEnabled " : "",
+		(status & USBPORTSC_CSC) ?  "ConnectChange " : "",
+		(status & USBPORTSC_CCS) ?  "PortConnected " : "");
+
+	return out - buf;
+}
+
+static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len)
+{
+	char *out = buf;
+	unsigned int io_addr = uhci->io_addr;
+	unsigned short usbcmd, usbstat, usbint, usbfrnum;
+	unsigned int flbaseadd;
+	unsigned char sof;
+	unsigned short portsc1, portsc2;
+
+	/* Try to make sure there's enough memory */
+	if (len < 80 * 6)
+		return 0;
+
+	usbcmd    = inw(io_addr + 0);
+	usbstat   = inw(io_addr + 2);
+	usbint    = inw(io_addr + 4);
+	usbfrnum  = inw(io_addr + 6);
+	flbaseadd = inl(io_addr + 8);
+	sof       = inb(io_addr + 12);
+	portsc1   = inw(io_addr + 16);
+	portsc2   = inw(io_addr + 18);
+
+	out += sprintf(out, "  usbcmd    =     %04x   %s%s%s%s%s%s%s%s\n",
+		usbcmd,
+		(usbcmd & USBCMD_MAXP) ?    "Maxp64 " : "Maxp32 ",
+		(usbcmd & USBCMD_CF) ?      "CF " : "",
+		(usbcmd & USBCMD_SWDBG) ?   "SWDBG " : "",
+		(usbcmd & USBCMD_FGR) ?     "FGR " : "",
+		(usbcmd & USBCMD_EGSM) ?    "EGSM " : "",
+		(usbcmd & USBCMD_GRESET) ?  "GRESET " : "",
+		(usbcmd & USBCMD_HCRESET) ? "HCRESET " : "",
+		(usbcmd & USBCMD_RS) ?      "RS " : "");
+
+	out += sprintf(out, "  usbstat   =     %04x   %s%s%s%s%s%s\n",
+		usbstat,
+		(usbstat & USBSTS_HCH) ?    "HCHalted " : "",
+		(usbstat & USBSTS_HCPE) ?   "HostControllerProcessError " : "",
+		(usbstat & USBSTS_HSE) ?    "HostSystemError " : "",
+		(usbstat & USBSTS_RD) ?     "ResumeDetect " : "",
+		(usbstat & USBSTS_ERROR) ?  "USBError " : "",
+		(usbstat & USBSTS_USBINT) ? "USBINT " : "");
+
+	out += sprintf(out, "  usbint    =     %04x\n", usbint);
+	out += sprintf(out, "  usbfrnum  =   (%d)%03x\n", (usbfrnum >> 10) & 1,
+		0xfff & (4*(unsigned int)usbfrnum));
+	out += sprintf(out, "  flbaseadd = %08x\n", flbaseadd);
+	out += sprintf(out, "  sof       =       %02x\n", sof);
+	out += uhci_show_sc(1, portsc1, out, len - (out - buf));
+	out += uhci_show_sc(2, portsc2, out, len - (out - buf));
+
+	return out - buf;
+}
+
+static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
+{
+	char *out = buf;
+	struct urb_priv *urbp;
+	struct list_head *head, *tmp;
+	struct uhci_td *td;
+	int i = 0, checked = 0, prevactive = 0;
+
+	/* Try to make sure there's enough memory */
+	if (len < 80 * 6)
+		return 0;
+
+	out += sprintf(out, "%*s[%p] link (%08x) element (%08x)\n", space, "",
+			qh, le32_to_cpu(qh->link), le32_to_cpu(qh->element));
+
+	if (qh->element & UHCI_PTR_QH)
+		out += sprintf(out, "%*s  Element points to QH (bug?)\n", space, "");
+
+	if (qh->element & UHCI_PTR_DEPTH)
+		out += sprintf(out, "%*s  Depth traverse\n", space, "");
+
+	if (qh->element & cpu_to_le32(8))
+		out += sprintf(out, "%*s  Bit 3 set (bug?)\n", space, "");
+
+	if (!(qh->element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH)))
+		out += sprintf(out, "%*s  Element is NULL (bug?)\n", space, "");
+
+	if (!qh->urbp) {
+		out += sprintf(out, "%*s  urbp == NULL\n", space, "");
+		goto out;
+	}
+
+	urbp = qh->urbp;
+
+	head = &urbp->td_list;
+	tmp = head->next;
+
+	td = list_entry(tmp, struct uhci_td, list);
+
+	if (cpu_to_le32(td->dma_handle) != (qh->element & ~UHCI_PTR_BITS))
+		out += sprintf(out, "%*s Element != First TD\n", space, "");
+
+	while (tmp != head) {
+		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		out += sprintf(out, "%*s%d: ", space + 2, "", i++);
+		out += uhci_show_td(td, out, len - (out - buf), 0);
+
+		if (i > 10 && !checked && prevactive && tmp != head &&
+		    debug <= 2) {
+			struct list_head *ntmp = tmp;
+			struct uhci_td *ntd = td;
+			int active = 1, ni = i;
+
+			checked = 1;
+
+			while (ntmp != head && ntmp->next != head && active) {
+				ntd = list_entry(ntmp, struct uhci_td, list);
+
+				ntmp = ntmp->next;
+
+				active = td_status(ntd) & TD_CTRL_ACTIVE;
+
+				ni++;
+			}
+
+			if (active && ni > i) {
+				out += sprintf(out, "%*s[skipped %d active TD's]\n", space, "", ni - i);
+				tmp = ntmp;
+				td = ntd;
+				i = ni;
+			}
+		}
+
+		prevactive = td_status(td) & TD_CTRL_ACTIVE;
+	}
+
+	if (list_empty(&urbp->queue_list) || urbp->queued)
+		goto out;
+
+	out += sprintf(out, "%*sQueued QH's:\n", -space, "--");
+
+	head = &urbp->queue_list;
+	tmp = head->next;
+
+	while (tmp != head) {
+		struct urb_priv *nurbp = list_entry(tmp, struct urb_priv,
+						queue_list);
+		tmp = tmp->next;
+
+		out += uhci_show_qh(nurbp->qh, out, len - (out - buf), space);
+	}
+
+out:
+	return out - buf;
+}
+
+static const char *td_names[] = {"skel_int1_td", "skel_int2_td",
+				 "skel_int4_td", "skel_int8_td",
+				 "skel_int16_td", "skel_int32_td",
+				 "skel_int64_td", "skel_int128_td",
+				 "skel_int256_td", "skel_term_td" };
+static const char *qh_names[] = { "skel_ls_control_qh", "skel_hs_control_qh",
+				  "skel_bulk_qh", "skel_term_qh" };
+
+#define show_frame_num()	\
+	if (!shown) {		\
+	  shown = 1;		\
+	  out += sprintf(out, "- Frame %d\n", i); \
+	}
+
+#define show_td_name()		\
+	if (!shown) {		\
+	  shown = 1;		\
+	  out += sprintf(out, "- %s\n", td_names[i]); \
+	}
+
+#define show_qh_name()		\
+	if (!shown) {		\
+	  shown = 1;		\
+	  out += sprintf(out, "- %s\n", qh_names[i]); \
+	}
+
+static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
+{
+	char *out = buf;
+	int i;
+	struct uhci_qh *qh;
+	struct uhci_td *td;
+	struct list_head *tmp, *head;
+
+	out += sprintf(out, "HC status\n");
+	out += uhci_show_status(uhci, out, len - (out - buf));
+
+	out += sprintf(out, "Frame List\n");
+	for (i = 0; i < UHCI_NUMFRAMES; ++i) {
+		int shown = 0;
+		td = uhci->fl->frame_cpu[i];
+		if (!td)
+			continue;
+
+		if (td->dma_handle != (dma_addr_t)uhci->fl->frame[i]) {
+			show_frame_num();
+			out += sprintf(out, "    frame list does not match td->dma_handle!\n");
+		}
+		if (uhci_is_skeleton_td(uhci, td))
+			continue;
+		show_frame_num();
+
+		head = &td->fl_list;
+		tmp = head;
+		do {
+			td = list_entry(tmp, struct uhci_td, fl_list);
+			tmp = tmp->next;
+			out += uhci_show_td(td, out, len - (out - buf), 4);
+		} while (tmp != head);
+	}
+
+	out += sprintf(out, "Skeleton TD's\n");
+	for (i = UHCI_NUM_SKELTD - 1; i >= 0; i--) {
+		int shown = 0;
+
+		td = uhci->skeltd[i];
+
+		if (debug > 1) {
+			show_td_name();
+			out += uhci_show_td(td, out, len - (out - buf), 4);
+		}
+
+		if (list_empty(&td->fl_list)) {
+			/* TD 0 is the int1 TD and links to control_ls_qh */
+			if (!i) {
+				if (td->link !=
+				    (cpu_to_le32(uhci->skel_ls_control_qh->dma_handle) | UHCI_PTR_QH)) {
+					show_td_name();
+					out += sprintf(out, "    skeleton TD not linked to ls_control QH!\n");
+				}
+			} else if (i < 9) {
+				if (td->link != cpu_to_le32(uhci->skeltd[i - 1]->dma_handle)) {
+					show_td_name();
+					out += sprintf(out, "    skeleton TD not linked to next skeleton TD!\n");
+				}
+			} else {
+				show_td_name();
+
+				if (td->link != cpu_to_le32(td->dma_handle))
+					out += sprintf(out, "    skel_term_td does not link to self\n");
+
+				/* Don't show it twice */
+				if (debug <= 1)
+					out += uhci_show_td(td, out, len - (out - buf), 4);
+			}
+
+			continue;
+		}
+
+		show_td_name();
+
+		head = &td->fl_list;
+		tmp = head->next;
+
+		while (tmp != head) {
+			td = list_entry(tmp, struct uhci_td, fl_list);
+
+			tmp = tmp->next;
+
+			out += uhci_show_td(td, out, len - (out - buf), 4);
+		}
+
+		if (!i) {
+			if (td->link !=
+			    (cpu_to_le32(uhci->skel_ls_control_qh->dma_handle) | UHCI_PTR_QH))
+				out += sprintf(out, "    last TD not linked to ls_control QH!\n");
+		} else if (i < 9) {
+			if (td->link != cpu_to_le32(uhci->skeltd[i - 1]->dma_handle))
+				out += sprintf(out, "    last TD not linked to next skeleton!\n");
+		}
+	}
+
+	out += sprintf(out, "Skeleton QH's\n");
+
+	for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
+		int shown = 0;
+
+		qh = uhci->skelqh[i];
+
+		if (debug > 1) {
+			show_qh_name();
+			out += uhci_show_qh(qh, out, len - (out - buf), 4);
+		}
+
+		/* QH 3 is the Terminating QH, it's different */
+		if (i == 3) {
+			if (qh->link != UHCI_PTR_TERM) {
+				show_qh_name();
+				out += sprintf(out, "    bandwidth reclamation on!\n");
+			}
+
+			if (qh->element != cpu_to_le32(uhci->skel_term_td->dma_handle)) {
+				show_qh_name();
+				out += sprintf(out, "    skel_term_qh element is not set to skel_term_td\n");
+			}
+		}
+
+		if (list_empty(&qh->list)) {
+			if (i < 3) {
+				if (qh->link !=
+				    (cpu_to_le32(uhci->skelqh[i + 1]->dma_handle) | UHCI_PTR_QH)) {
+					show_qh_name();
+					out += sprintf(out, "    skeleton QH not linked to next skeleton QH!\n");
+				}
+			}
+
+			continue;
+		}
+
+		show_qh_name();
+
+		head = &qh->list;
+		tmp = head->next;
+
+		while (tmp != head) {
+			qh = list_entry(tmp, struct uhci_qh, list);
+
+			tmp = tmp->next;
+
+			out += uhci_show_qh(qh, out, len - (out - buf), 4);
+		}
+
+		if (i < 3) {
+			if (qh->link !=
+			    (cpu_to_le32(uhci->skelqh[i + 1]->dma_handle) | UHCI_PTR_QH))
+				out += sprintf(out, "    last QH not linked to next skeleton!\n");
+		}
+	}
+
+	return out - buf;
+}
+
+#ifdef CONFIG_PROC_FS
+#define MAX_OUTPUT	(PAGE_SIZE * 8)
+
+static struct proc_dir_entry *uhci_proc_root = NULL;
+
+struct uhci_proc {
+	int size;
+	char *data;
+	struct uhci_hcd *uhci;
+};
+
+static int uhci_proc_open(struct inode *inode, struct file *file)
+{
+	const struct proc_dir_entry *dp = PDE(inode);
+	struct uhci_hcd *uhci = dp->data;
+	struct uhci_proc *up;
+	unsigned long flags;
+	int ret = -ENOMEM;
+
+	lock_kernel();
+	up = kmalloc(sizeof(*up), GFP_KERNEL);
+	if (!up)
+		goto out;
+
+	up->data = kmalloc(MAX_OUTPUT, GFP_KERNEL);
+	if (!up->data) {
+		kfree(up);
+		goto out;
+	}
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+	up->size = uhci_sprint_schedule(uhci, up->data, MAX_OUTPUT);
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+
+	file->private_data = up;
+
+	ret = 0;
+out:
+	unlock_kernel();
+	return ret;
+}
+
+static loff_t uhci_proc_lseek(struct file *file, loff_t off, int whence)
+{
+	struct uhci_proc *up;
+	loff_t new = -1;
+
+	lock_kernel();
+	up = file->private_data;
+
+	switch (whence) {
+	case 0:
+		new = off;
+		break;
+	case 1:
+		new = file->f_pos + off;
+		break;
+	}
+	if (new < 0 || new > up->size) {
+		unlock_kernel();
+		return -EINVAL;
+	}
+	unlock_kernel();
+	return (file->f_pos = new);
+}
+
+static ssize_t uhci_proc_read(struct file *file, char *buf, size_t nbytes,
+			loff_t *ppos)
+{
+	struct uhci_proc *up = file->private_data;
+	unsigned int pos;
+	unsigned int size;
+
+	pos = *ppos;
+	size = up->size;
+	if (pos >= size)
+		return 0;
+	if (nbytes >= size)
+		nbytes = size;
+	if (pos + nbytes > size)
+		nbytes = size - pos;
+
+	if (!access_ok(VERIFY_WRITE, buf, nbytes))
+		return -EINVAL;
+
+	copy_to_user(buf, up->data + pos, nbytes);
+
+	*ppos += nbytes;
+
+	return nbytes;
+}
+
+static int uhci_proc_release(struct inode *inode, struct file *file)
+{
+	struct uhci_proc *up = file->private_data;
+
+	kfree(up->data);
+	kfree(up);
+
+	return 0;
+}
+
+static struct file_operations uhci_proc_operations = {
+	open:		uhci_proc_open,
+	llseek:		uhci_proc_lseek,
+	read:		uhci_proc_read,
+//	write:		uhci_proc_write,
+	release:	uhci_proc_release,
+};
+#endif
+
diff -Nru a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/host/uhci-hcd.c	Sat May 18 00:46:15 2002
@@ -0,0 +1,2633 @@
+/*
+ * Universal Host Controller Interface driver for USB.
+ *
+ * Maintainer: Johannes Erdfelt <johannes@erdfelt.com>
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
+ * (C) Copyright 1999 Randy Dunlap
+ * (C) Copyright 1999 Georg Acher, acher@in.tum.de
+ * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
+ * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
+ * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at
+ * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
+ *               support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
+ * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
+ *
+ * Intel documents this fairly well, and as far as I know there
+ * are no royalties or anything like that, but even so there are
+ * people who decided that they want to do the same thing in a
+ * completely different way.
+ *
+ * WARNING! The USB documentation is downright evil. Most of it
+ * is just crap, written by a committee. You're better off ignoring
+ * most of it, the important stuff is:
+ *  - the low-level protocol (fairly simple but lots of small details)
+ *  - working around the horridness of the rest
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#ifdef CONFIG_USB_DEBUG
+#define DEBUG
+#else
+#undef DEBUG
+#endif
+#include <linux/usb.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "../core/hcd.h"
+#include "uhci-hcd.h"
+
+#include <linux/pm.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v2.0"
+#define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber"
+#define DRIVER_DESC "USB Universal Host Controller Interface driver"
+
+/*
+ * debug = 0, no debugging messages
+ * debug = 1, dump failed URB's except for stalls
+ * debug = 2, dump all failed URB's (including stalls)
+ *            show all queues in /proc/uhci/hc*
+ * debug = 3, show all TD's in URB's when dumping
+ */
+#ifdef DEBUG
+static int debug = 1;
+#else
+static int debug = 0;
+#endif
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug level");
+static char *errbuf;
+#define ERRBUF_LEN    (PAGE_SIZE * 8)
+
+#include "uhci-hub.c"
+#include "uhci-debug.c"
+
+static kmem_cache_t *uhci_up_cachep;	/* urb_priv */
+
+static int uhci_get_current_frame_number(struct uhci_hcd *uhci);
+static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb);
+static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb);
+static void uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb);
+
+static int  ports_active(struct uhci_hcd *uhci);
+static void suspend_hc(struct uhci_hcd *uhci);
+static void wakeup_hc(struct uhci_hcd *uhci);
+
+/* If a transfer is still active after this much time, turn off FSBR */
+#define IDLE_TIMEOUT	(HZ / 20)	/* 50 ms */
+#define FSBR_DELAY	(HZ / 20)	/* 50 ms */
+
+/* When we timeout an idle transfer for FSBR, we'll switch it over to */
+/* depth first traversal. We'll do it in groups of this number of TD's */
+/* to make sure it doesn't hog all of the bandwidth */
+#define DEPTH_INTERVAL 5
+
+#define MAX_URB_LOOP	2048		/* Maximum number of linked URB's */
+
+/*
+ * Technically, updating td->status here is a race, but it's not really a
+ * problem. The worst that can happen is that we set the IOC bit again
+ * generating a spurios interrupt. We could fix this by creating another
+ * QH and leaving the IOC bit always set, but then we would have to play
+ * games with the FSBR code to make sure we get the correct order in all
+ * the cases. I don't think it's worth the effort
+ */
+static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+	uhci->skel_term_td->status |= cpu_to_le32(TD_CTRL_IOC); 
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+	uhci->skel_term_td->status &= ~cpu_to_le32(TD_CTRL_IOC);
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+static inline void uhci_add_complete(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&uhci->complete_list_lock, flags);
+	list_add(&urbp->complete_list, &uhci->complete_list);
+	spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
+}
+
+static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci, struct usb_device *dev)
+{
+	dma_addr_t dma_handle;
+	struct uhci_td *td;
+
+	td = pci_pool_alloc(uhci->td_pool, GFP_DMA | GFP_ATOMIC, &dma_handle);
+	if (!td)
+		return NULL;
+
+	td->dma_handle = dma_handle;
+
+	td->link = UHCI_PTR_TERM;
+	td->buffer = 0;
+
+	td->frame = -1;
+	td->dev = dev;
+
+	INIT_LIST_HEAD(&td->list);
+	INIT_LIST_HEAD(&td->fl_list);
+
+	usb_get_dev(dev);
+
+	return td;
+}
+
+static void inline uhci_fill_td(struct uhci_td *td, __u32 status,
+		__u32 token, __u32 buffer)
+{
+	td->status = cpu_to_le32(status);
+	td->token = cpu_to_le32(token);
+	td->buffer = cpu_to_le32(buffer);
+}
+
+static void uhci_insert_td(struct uhci_hcd *uhci, struct uhci_td *skeltd, struct uhci_td *td)
+{
+	unsigned long flags;
+	struct uhci_td *ltd;
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+
+	ltd = list_entry(skeltd->fl_list.prev, struct uhci_td, fl_list);
+
+	td->link = ltd->link;
+	mb();
+	ltd->link = cpu_to_le32(td->dma_handle);
+
+	list_add_tail(&td->fl_list, &skeltd->fl_list);
+
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+/*
+ * We insert Isochronous transfers directly into the frame list at the
+ * beginning
+ * The layout looks as follows:
+ * frame list pointer -> iso td's (if any) ->
+ * periodic interrupt td (if frame 0) -> irq td's -> control qh -> bulk qh
+ */
+static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td, unsigned framenum)
+{
+	unsigned long flags;
+
+	framenum %= UHCI_NUMFRAMES;
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+
+	td->frame = framenum;
+
+	/* Is there a TD already mapped there? */
+	if (uhci->fl->frame_cpu[framenum]) {
+		struct uhci_td *ftd, *ltd;
+
+		ftd = uhci->fl->frame_cpu[framenum];
+		ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list);
+
+		list_add_tail(&td->fl_list, &ftd->fl_list);
+
+		td->link = ltd->link;
+		mb();
+		ltd->link = cpu_to_le32(td->dma_handle);
+	} else {
+		td->link = uhci->fl->frame[framenum];
+		mb();
+		uhci->fl->frame[framenum] = cpu_to_le32(td->dma_handle);
+		uhci->fl->frame_cpu[framenum] = td;
+	}
+
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td)
+{
+	unsigned long flags;
+
+	/* If it's not inserted, don't remove it */
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+	if (td->frame == -1 && list_empty(&td->fl_list))
+		goto out;
+
+	if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) {
+		if (list_empty(&td->fl_list)) {
+			uhci->fl->frame[td->frame] = td->link;
+			uhci->fl->frame_cpu[td->frame] = NULL;
+		} else {
+			struct uhci_td *ntd;
+
+			ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list);
+			uhci->fl->frame[td->frame] = cpu_to_le32(ntd->dma_handle);
+			uhci->fl->frame_cpu[td->frame] = ntd;
+		}
+	} else {
+		struct uhci_td *ptd;
+
+		ptd = list_entry(td->fl_list.prev, struct uhci_td, fl_list);
+		ptd->link = td->link;
+	}
+
+	mb();
+	td->link = UHCI_PTR_TERM;
+
+	list_del_init(&td->fl_list);
+	td->frame = -1;
+
+out:
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+/*
+ * Inserts a td into qh list at the top.
+ */
+static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int breadth)
+{
+	struct list_head *tmp, *head;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct uhci_td *td, *ptd;
+
+	if (list_empty(&urbp->td_list))
+		return;
+
+	head = &urbp->td_list;
+	tmp = head->next;
+
+	/* Ordering isn't important here yet since the QH hasn't been */
+	/*  inserted into the schedule yet */
+	td = list_entry(tmp, struct uhci_td, list);
+
+	/* Add the first TD to the QH element pointer */
+	qh->element = cpu_to_le32(td->dma_handle) | (breadth ? 0 : UHCI_PTR_DEPTH);
+
+	ptd = td;
+
+	/* Then link the rest of the TD's */
+	tmp = tmp->next;
+	while (tmp != head) {
+		td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		ptd->link = cpu_to_le32(td->dma_handle) | (breadth ? 0 : UHCI_PTR_DEPTH);
+
+		ptd = td;
+	}
+
+	ptd->link = UHCI_PTR_TERM;
+}
+
+static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)
+{
+/*
+	if (!list_empty(&td->list) || !list_empty(&td->fl_list))
+		dbg("td %p is still in URB list!", td);
+*/
+	if (!list_empty(&td->list))
+		dbg("td %p is still in list!", td);
+	if (!list_empty(&td->fl_list))
+		dbg("td %p is still in fl_list!", td);
+
+	if (td->dev)
+		usb_put_dev(td->dev);
+
+	pci_pool_free(uhci->td_pool, td, td->dma_handle);
+}
+
+static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, struct usb_device *dev)
+{
+	dma_addr_t dma_handle;
+	struct uhci_qh *qh;
+
+	qh = pci_pool_alloc(uhci->qh_pool, GFP_DMA | GFP_ATOMIC, &dma_handle);
+	if (!qh)
+		return NULL;
+
+	qh->dma_handle = dma_handle;
+
+	qh->element = UHCI_PTR_TERM;
+	qh->link = UHCI_PTR_TERM;
+
+	qh->dev = dev;
+	qh->urbp = NULL;
+
+	INIT_LIST_HEAD(&qh->list);
+	INIT_LIST_HEAD(&qh->remove_list);
+
+	usb_get_dev(dev);
+
+	return qh;
+}
+
+static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+	if (!list_empty(&qh->list))
+		dbg("qh %p list not empty!", qh);
+	if (!list_empty(&qh->remove_list))
+		dbg("qh %p still in remove_list!", qh);
+
+	if (qh->dev)
+		usb_put_dev(qh->dev);
+
+	pci_pool_free(uhci->qh_pool, qh, qh->dma_handle);
+}
+
+/*
+ * MUST be called with uhci->frame_list_lock acquired
+ */
+static void _uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct list_head *head, *tmp;
+	struct uhci_qh *lqh;
+
+	/* Grab the last QH */
+	lqh = list_entry(skelqh->list.prev, struct uhci_qh, list);
+
+	if (lqh->urbp) {
+		head = &lqh->urbp->queue_list;
+		tmp = head->next;
+		while (head != tmp) {
+			struct urb_priv *turbp =
+				list_entry(tmp, struct urb_priv, queue_list);
+
+			tmp = tmp->next;
+
+			turbp->qh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
+		}
+	}
+
+	head = &urbp->queue_list;
+	tmp = head->next;
+	while (head != tmp) {
+		struct urb_priv *turbp =
+			list_entry(tmp, struct urb_priv, queue_list);
+
+		tmp = tmp->next;
+
+		turbp->qh->link = lqh->link;
+	}
+
+	urbp->qh->link = lqh->link;
+	mb();				/* Ordering is important */
+	lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
+
+	list_add_tail(&urbp->qh->list, &skelqh->list);
+}
+
+static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+	_uhci_insert_qh(uhci, skelqh, urb);
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+	unsigned long flags;
+	struct uhci_qh *pqh;
+
+	if (!qh)
+		return;
+
+	qh->urbp = NULL;
+
+	/* Only go through the hoops if it's actually linked in */
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+	if (!list_empty(&qh->list)) {
+		pqh = list_entry(qh->list.prev, struct uhci_qh, list);
+
+		if (pqh->urbp) {
+			struct list_head *head, *tmp;
+
+			head = &pqh->urbp->queue_list;
+			tmp = head->next;
+			while (head != tmp) {
+				struct urb_priv *turbp =
+					list_entry(tmp, struct urb_priv, queue_list);
+
+				tmp = tmp->next;
+
+				turbp->qh->link = qh->link;
+			}
+		}
+
+		pqh->link = qh->link;
+		mb();
+		qh->element = qh->link = UHCI_PTR_TERM;
+
+		list_del_init(&qh->list);
+	}
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+
+	spin_lock_irqsave(&uhci->qh_remove_list_lock, flags);
+
+	/* Check to see if the remove list is empty. Set the IOC bit */
+	/* to force an interrupt so we can remove the QH */
+	if (list_empty(&uhci->qh_remove_list))
+		uhci_set_next_interrupt(uhci);
+
+	list_add(&qh->remove_list, &uhci->qh_remove_list);
+
+	spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags);
+}
+
+static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct list_head *head, *tmp;
+
+	head = &urbp->td_list;
+	tmp = head->next;
+	while (head != tmp) {
+		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		if (toggle)
+			td->token |= cpu_to_le32(TD_TOKEN_TOGGLE);
+		else
+			td->token &= ~cpu_to_le32(TD_TOKEN_TOGGLE);
+
+
+		toggle ^= 1;
+	}
+
+	return toggle;
+}
+
+/* This function will append one URB's QH to another URB's QH. This is for */
+/*  USB_QUEUE_BULK support for bulk transfers and soon implicitily for */
+/*  control transfers */
+static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, struct urb *urb)
+{
+	struct urb_priv *eurbp, *urbp, *furbp, *lurbp;
+	struct list_head *tmp;
+	struct uhci_td *lltd;
+	unsigned long flags;
+
+	eurbp = eurb->hcpriv;
+	urbp = urb->hcpriv;
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+
+	/* Find the first URB in the queue */
+	if (eurbp->queued) {
+		struct list_head *head = &eurbp->queue_list;
+
+		tmp = head->next;
+		while (tmp != head) {
+			struct urb_priv *turbp =
+				list_entry(tmp, struct urb_priv, queue_list);
+
+			if (!turbp->queued)
+				break;
+
+			tmp = tmp->next;
+		}
+	} else
+		tmp = &eurbp->queue_list;
+
+	furbp = list_entry(tmp, struct urb_priv, queue_list);
+	lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list);
+
+	lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list);
+
+	usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe),
+		uhci_fixup_toggle(urb, uhci_toggle(td_token(lltd)) ^ 1));
+
+	/* All qh's in the queue need to link to the next queue */
+	urbp->qh->link = eurbp->qh->link;
+
+	mb();			/* Make sure we flush everything */
+	/* Only support bulk right now, so no depth */
+	lltd->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
+
+	list_add_tail(&urbp->queue_list, &furbp->queue_list);
+
+	urbp->queued = 1;
+
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct urb_priv *urbp, *nurbp;
+	struct list_head *head, *tmp;
+	struct urb_priv *purbp;
+	struct uhci_td *pltd;
+	unsigned int toggle;
+	unsigned long flags;
+
+	urbp = urb->hcpriv;
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+
+	if (list_empty(&urbp->queue_list))
+		goto out;
+
+	nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list);
+
+	/* Fix up the toggle for the next URB's */
+	if (!urbp->queued)
+		/* We set the toggle when we unlink */
+		toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+	else {
+		/* If we're in the middle of the queue, grab the toggle */
+		/*  from the TD previous to us */
+		purbp = list_entry(urbp->queue_list.prev, struct urb_priv,
+				queue_list);
+
+		pltd = list_entry(purbp->td_list.prev, struct uhci_td, list);
+
+		toggle = uhci_toggle(td_token(pltd)) ^ 1;
+	}
+
+	head = &urbp->queue_list;
+	tmp = head->next;
+	while (head != tmp) {
+		struct urb_priv *turbp;
+
+		turbp = list_entry(tmp, struct urb_priv, queue_list);
+
+		tmp = tmp->next;
+
+		if (!turbp->queued)
+			break;
+
+		toggle = uhci_fixup_toggle(turbp->urb, toggle);
+	}
+
+	usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+		usb_pipeout(urb->pipe), toggle);
+
+	if (!urbp->queued) {
+		nurbp->queued = 0;
+
+		_uhci_insert_qh(uhci, uhci->skel_bulk_qh, nurbp->urb);
+	} else {
+		/* We're somewhere in the middle (or end). A bit trickier */
+		/*  than the head scenario */
+		purbp = list_entry(urbp->queue_list.prev, struct urb_priv,
+				queue_list);
+
+		pltd = list_entry(purbp->td_list.prev, struct uhci_td, list);
+		if (nurbp->queued)
+			pltd->link = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH;
+		else
+			/* The next URB happens to be the beginning, so */
+			/*  we're the last, end the chain */
+			pltd->link = UHCI_PTR_TERM;
+	}
+
+	list_del_init(&urbp->queue_list);
+
+out:
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+static struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct urb_priv *urbp;
+
+	urbp = kmem_cache_alloc(uhci_up_cachep, SLAB_ATOMIC);
+	if (!urbp) {
+		err("uhci_alloc_urb_priv: couldn't allocate memory for urb_priv\n");
+		return NULL;
+	}
+
+	memset((void *)urbp, 0, sizeof(*urbp));
+
+	urbp->inserttime = jiffies;
+	urbp->fsbrtime = jiffies;
+	urbp->urb = urb;
+	urbp->dev = urb->dev;
+	
+	INIT_LIST_HEAD(&urbp->td_list);
+	INIT_LIST_HEAD(&urbp->queue_list);
+	INIT_LIST_HEAD(&urbp->complete_list);
+	INIT_LIST_HEAD(&urbp->urb_list);
+
+	list_add_tail(&urbp->urb_list, &uhci->urb_list);
+
+	urb->hcpriv = urbp;
+
+	if (urb->transfer_buffer_length) {
+		urbp->transfer_buffer_dma_handle = pci_map_single(uhci->dev,
+			urb->transfer_buffer, urb->transfer_buffer_length,
+			usb_pipein(urb->pipe) ? PCI_DMA_FROMDEVICE :
+			PCI_DMA_TODEVICE);
+		if (!urbp->transfer_buffer_dma_handle)
+			return NULL;
+	}
+
+	if (usb_pipetype(urb->pipe) == PIPE_CONTROL && urb->setup_packet) {
+		urbp->setup_packet_dma_handle = pci_map_single(uhci->dev,
+			urb->setup_packet, sizeof(struct usb_ctrlrequest),
+			PCI_DMA_TODEVICE);
+		if (!urbp->setup_packet_dma_handle)
+			return NULL;
+	}
+
+	return urbp;
+}
+
+/*
+ * MUST be called with urb->lock acquired
+ */
+static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+	td->urb = urb;
+
+	list_add_tail(&td->list, &urbp->td_list);
+}
+
+/*
+ * MUST be called with urb->lock acquired
+ */
+static void uhci_remove_td_from_urb(struct uhci_td *td)
+{
+	if (list_empty(&td->list))
+		return;
+
+	list_del_init(&td->list);
+
+	td->urb = NULL;
+}
+
+/*
+ * MUST be called with urb->lock acquired
+ */
+static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct list_head *head, *tmp;
+	struct urb_priv *urbp;
+
+	urbp = (struct urb_priv *)urb->hcpriv;
+	if (!urbp)
+		return;
+
+	if (!list_empty(&urbp->urb_list))
+		warn("uhci_destroy_urb_priv: urb %p still on uhci->urb_list or uhci->remove_list", urb);
+
+	if (!list_empty(&urbp->complete_list))
+		warn("uhci_destroy_urb_priv: urb %p still on uhci->complete_list", urb);
+
+	head = &urbp->td_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		uhci_remove_td_from_urb(td);
+		uhci_remove_td(uhci, td);
+		uhci_free_td(uhci, td);
+	}
+
+	if (urbp->setup_packet_dma_handle) {
+		pci_unmap_single(uhci->dev, urbp->setup_packet_dma_handle,
+			sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
+		urbp->setup_packet_dma_handle = 0;
+	}
+
+	if (urbp->transfer_buffer_dma_handle) {
+		pci_unmap_single(uhci->dev, urbp->transfer_buffer_dma_handle,
+			urb->transfer_buffer_length, usb_pipein(urb->pipe) ?
+			PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
+		urbp->transfer_buffer_dma_handle = 0;
+	}
+
+	urb->hcpriv = NULL;
+	kmem_cache_free(uhci_up_cachep, urbp);
+}
+
+static void uhci_inc_fsbr(struct uhci_hcd *uhci, struct urb *urb)
+{
+	unsigned long flags;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+
+	if ((!(urb->transfer_flags & USB_NO_FSBR)) && !urbp->fsbr) {
+		urbp->fsbr = 1;
+		if (!uhci->fsbr++ && !uhci->fsbrtimeout)
+			uhci->skel_term_qh->link = cpu_to_le32(uhci->skel_hs_control_qh->dma_handle) | UHCI_PTR_QH;
+	}
+
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+static void uhci_dec_fsbr(struct uhci_hcd *uhci, struct urb *urb)
+{
+	unsigned long flags;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+
+	if ((!(urb->transfer_flags & USB_NO_FSBR)) && urbp->fsbr) {
+		urbp->fsbr = 0;
+		if (!--uhci->fsbr)
+			uhci->fsbrtimeout = jiffies + FSBR_DELAY;
+	}
+
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+/*
+ * Map status to standard result codes
+ *
+ * <status> is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)]
+ * <dir_out> is True for output TDs and False for input TDs.
+ */
+static int uhci_map_status(int status, int dir_out)
+{
+	if (!status)
+		return 0;
+	if (status & TD_CTRL_BITSTUFF)			/* Bitstuff error */
+		return -EPROTO;
+	if (status & TD_CTRL_CRCTIMEO) {		/* CRC/Timeout */
+		if (dir_out)
+			return -ETIMEDOUT;
+		else
+			return -EILSEQ;
+	}
+	if (status & TD_CTRL_NAK)			/* NAK */
+		return -ETIMEDOUT;
+	if (status & TD_CTRL_BABBLE)			/* Babble */
+		return -EOVERFLOW;
+	if (status & TD_CTRL_DBUFERR)			/* Buffer error */
+		return -ENOSR;
+	if (status & TD_CTRL_STALLED)			/* Stalled */
+		return -EPIPE;
+	if (status & TD_CTRL_ACTIVE)			/* Active */
+		return 0;
+
+	return -EINVAL;
+}
+
+/*
+ * Control transfers
+ */
+static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct uhci_td *td;
+	struct uhci_qh *qh;
+	unsigned long destination, status;
+	int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	int len = urb->transfer_buffer_length;
+	dma_addr_t data = urbp->transfer_buffer_dma_handle;
+
+	/* The "pipe" thing contains the destination in bits 8--18 */
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
+
+	/* 3 errors */
+	status = TD_CTRL_ACTIVE | uhci_maxerr(3);
+	if (urb->dev->speed == USB_SPEED_LOW)
+		status |= TD_CTRL_LS;
+
+	/*
+	 * Build the TD for the control request
+	 */
+	td = uhci_alloc_td(uhci, urb->dev);
+	if (!td)
+		return -ENOMEM;
+
+	uhci_add_td_to_urb(urb, td);
+	uhci_fill_td(td, status, destination | uhci_explen(7),
+		urbp->setup_packet_dma_handle);
+
+	/*
+	 * If direction is "send", change the frame from SETUP (0x2D)
+	 * to OUT (0xE1). Else change it from SETUP to IN (0x69).
+	 */
+	destination ^= (USB_PID_SETUP ^ usb_packetid(urb->pipe));
+
+	if (!(urb->transfer_flags & USB_DISABLE_SPD))
+		status |= TD_CTRL_SPD;
+
+	/*
+	 * Build the DATA TD's
+	 */
+	while (len > 0) {
+		int pktsze = len;
+
+		if (pktsze > maxsze)
+			pktsze = maxsze;
+
+		td = uhci_alloc_td(uhci, urb->dev);
+		if (!td)
+			return -ENOMEM;
+
+		/* Alternate Data0/1 (start with Data1) */
+		destination ^= TD_TOKEN_TOGGLE;
+	
+		uhci_add_td_to_urb(urb, td);
+		uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1),
+			data);
+
+		data += pktsze;
+		len -= pktsze;
+	}
+
+	/*
+	 * Build the final TD for control status 
+	 */
+	td = uhci_alloc_td(uhci, urb->dev);
+	if (!td)
+		return -ENOMEM;
+
+	/*
+	 * It's IN if the pipe is an output pipe or we're not expecting
+	 * data back.
+	 */
+	destination &= ~TD_TOKEN_PID_MASK;
+	if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length)
+		destination |= USB_PID_IN;
+	else
+		destination |= USB_PID_OUT;
+
+	destination |= TD_TOKEN_TOGGLE;		/* End in Data1 */
+
+	status &= ~TD_CTRL_SPD;
+
+	uhci_add_td_to_urb(urb, td);
+	uhci_fill_td(td, status | TD_CTRL_IOC,
+		destination | uhci_explen(UHCI_NULL_DATA_SIZE), 0);
+
+	qh = uhci_alloc_qh(uhci, urb->dev);
+	if (!qh)
+		return -ENOMEM;
+
+	urbp->qh = qh;
+	qh->urbp = urbp;
+
+	/* Low speed or small transfers gets a different queue and treatment */
+	if (urb->dev->speed == USB_SPEED_LOW) {
+		uhci_insert_tds_in_qh(qh, urb, 0);
+		uhci_insert_qh(uhci, uhci->skel_ls_control_qh, urb);
+	} else {
+		uhci_insert_tds_in_qh(qh, urb, 1);
+		uhci_insert_qh(uhci, uhci->skel_hs_control_qh, urb);
+		uhci_inc_fsbr(uhci, urb);
+	}
+
+	return -EINPROGRESS;
+}
+
+static int usb_control_retrigger_status(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct list_head *tmp, *head;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+	urbp->short_control_packet = 1;
+
+	/* Create a new QH to avoid pointer overwriting problems */
+	uhci_remove_qh(uhci, urbp->qh);
+
+	/* Delete all of the TD's except for the status TD at the end */
+	head = &urbp->td_list;
+	tmp = head->next;
+	while (tmp != head && tmp->next != head) {
+		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		uhci_remove_td_from_urb(td);
+		uhci_remove_td(uhci, td);
+		uhci_free_td(uhci, td);
+	}
+
+	urbp->qh = uhci_alloc_qh(uhci, urb->dev);
+	if (!urbp->qh) {
+		err("unable to allocate new QH for control retrigger");
+		return -ENOMEM;
+	}
+
+	urbp->qh->urbp = urbp;
+
+	/* One TD, who cares about Breadth first? */
+	uhci_insert_tds_in_qh(urbp->qh, urb, 0);
+
+	/* Low speed or small transfers gets a different queue and treatment */
+	if (urb->dev->speed == USB_SPEED_LOW)
+		uhci_insert_qh(uhci, uhci->skel_ls_control_qh, urb);
+	else
+		uhci_insert_qh(uhci, uhci->skel_hs_control_qh, urb);
+
+	return -EINPROGRESS;
+}
+
+
+static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct list_head *tmp, *head;
+	struct urb_priv *urbp = urb->hcpriv;
+	struct uhci_td *td;
+	unsigned int status;
+	int ret = 0;
+
+	if (list_empty(&urbp->td_list))
+		return -EINVAL;
+
+	head = &urbp->td_list;
+
+	if (urbp->short_control_packet) {
+		tmp = head->prev;
+		goto status_phase;
+	}
+
+	tmp = head->next;
+	td = list_entry(tmp, struct uhci_td, list);
+
+	/* The first TD is the SETUP phase, check the status, but skip */
+	/*  the count */
+	status = uhci_status_bits(td_status(td));
+	if (status & TD_CTRL_ACTIVE)
+		return -EINPROGRESS;
+
+	if (status)
+		goto td_error;
+
+	urb->actual_length = 0;
+
+	/* The rest of the TD's (but the last) are data */
+	tmp = tmp->next;
+	while (tmp != head && tmp->next != head) {
+		td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		status = uhci_status_bits(td_status(td));
+		if (status & TD_CTRL_ACTIVE)
+			return -EINPROGRESS;
+
+		urb->actual_length += uhci_actual_length(td_status(td));
+
+		if (status)
+			goto td_error;
+
+		/* Check to see if we received a short packet */
+		if (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td))) {
+			if (urb->transfer_flags & USB_DISABLE_SPD) {
+				ret = -EREMOTEIO;
+				goto err;
+			}
+
+			if (uhci_packetid(td_token(td)) == USB_PID_IN)
+				return usb_control_retrigger_status(uhci, urb);
+			else
+				return 0;
+		}
+	}
+
+status_phase:
+	td = list_entry(tmp, struct uhci_td, list);
+
+	/* Control status phase */
+	status = td_status(td);
+
+#ifdef I_HAVE_BUGGY_APC_BACKUPS
+	/* APC BackUPS Pro kludge */
+	/* It tries to send all of the descriptor instead of the amount */
+	/*  we requested */
+	if (status & TD_CTRL_IOC &&	/* IOC is masked out by uhci_status_bits */
+	    status & TD_CTRL_ACTIVE &&
+	    status & TD_CTRL_NAK)
+		return 0;
+#endif
+
+	if (status & TD_CTRL_ACTIVE)
+		return -EINPROGRESS;
+
+	if (uhci_status_bits(status))
+		goto td_error;
+
+	return 0;
+
+td_error:
+	ret = uhci_map_status(status, uhci_packetout(td_token(td)));
+	if (ret == -EPIPE)
+		/* endpoint has stalled - mark it halted */
+		usb_endpoint_halt(urb->dev, uhci_endpoint(td_token(td)),
+	    			uhci_packetout(td_token(td)));
+
+err:
+	if ((debug == 1 && ret != -EPIPE) || debug > 1) {
+		/* Some debugging code */
+		dbg("uhci_result_control() failed with status %x", status);
+
+		if (errbuf) {
+			/* Print the chain for debugging purposes */
+			uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
+
+			lprintk(errbuf);
+		}
+	}
+
+	return ret;
+}
+
+/*
+ * Interrupt transfers
+ */
+static int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct uhci_td *td;
+	unsigned long destination, status;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+	if (urb->transfer_buffer_length > usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))
+		return -EINVAL;
+
+	/* The "pipe" thing contains the destination in bits 8--18 */
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+
+	status = TD_CTRL_ACTIVE | TD_CTRL_IOC;
+	if (urb->dev->speed == USB_SPEED_LOW)
+		status |= TD_CTRL_LS;
+
+	td = uhci_alloc_td(uhci, urb->dev);
+	if (!td)
+		return -ENOMEM;
+
+	destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT);
+	destination |= uhci_explen(urb->transfer_buffer_length - 1);
+
+	usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+
+	uhci_add_td_to_urb(urb, td);
+	uhci_fill_td(td, status, destination, urbp->transfer_buffer_dma_handle);
+
+	uhci_insert_td(uhci, uhci->skeltd[__interval_to_skel(urb->interval)], td);
+
+	return -EINPROGRESS;
+}
+
+static int uhci_result_interrupt(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct list_head *tmp, *head;
+	struct urb_priv *urbp = urb->hcpriv;
+	struct uhci_td *td;
+	unsigned int status;
+	int ret = 0;
+
+	urb->actual_length = 0;
+
+	head = &urbp->td_list;
+	tmp = head->next;
+	while (tmp != head) {
+		td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		status = uhci_status_bits(td_status(td));
+		if (status & TD_CTRL_ACTIVE)
+			return -EINPROGRESS;
+
+		urb->actual_length += uhci_actual_length(td_status(td));
+
+		if (status)
+			goto td_error;
+
+		if (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td))) {
+			if (urb->transfer_flags & USB_DISABLE_SPD) {
+				ret = -EREMOTEIO;
+				goto err;
+			} else
+				return 0;
+		}
+	}
+
+	return 0;
+
+td_error:
+	ret = uhci_map_status(status, uhci_packetout(td_token(td)));
+	if (ret == -EPIPE)
+		/* endpoint has stalled - mark it halted */
+		usb_endpoint_halt(urb->dev, uhci_endpoint(td_token(td)),
+	    			uhci_packetout(td_token(td)));
+
+err:
+	if ((debug == 1 && ret != -EPIPE) || debug > 1) {
+		/* Some debugging code */
+		dbg("uhci_result_interrupt/bulk() failed with status %x",
+			status);
+
+		if (errbuf) {
+			/* Print the chain for debugging purposes */
+			if (urbp->qh)
+				uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
+			else
+				uhci_show_td(td, errbuf, ERRBUF_LEN, 0);
+
+			lprintk(errbuf);
+		}
+	}
+
+	return ret;
+}
+
+static void uhci_reset_interrupt(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct uhci_td *td;
+	unsigned long flags;
+
+	spin_lock_irqsave(&urb->lock, flags);
+
+	td = list_entry(urbp->td_list.next, struct uhci_td, list);
+
+	td->status = (td->status & cpu_to_le32(0x2F000000)) | cpu_to_le32(TD_CTRL_ACTIVE | TD_CTRL_IOC);
+	td->token &= ~cpu_to_le32(TD_TOKEN_TOGGLE);
+	td->token |= cpu_to_le32(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT);
+	usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+
+	urb->status = -EINPROGRESS;
+
+	spin_unlock_irqrestore(&urb->lock, flags);
+}
+
+/*
+ * Bulk transfers
+ */
+static int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
+{
+	struct uhci_td *td;
+	struct uhci_qh *qh;
+	unsigned long destination, status;
+	int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	int len = urb->transfer_buffer_length;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	dma_addr_t data = urbp->transfer_buffer_dma_handle;
+
+	if (len < 0)
+		return -EINVAL;
+
+	/* Can't have low speed bulk transfers */
+	if (urb->dev->speed == USB_SPEED_LOW)
+		return -EINVAL;
+
+	/* The "pipe" thing contains the destination in bits 8--18 */
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+
+	/* 3 errors */
+	status = TD_CTRL_ACTIVE | uhci_maxerr(3);
+	if (!(urb->transfer_flags & USB_DISABLE_SPD))
+		status |= TD_CTRL_SPD;
+
+	/*
+	 * Build the DATA TD's
+	 */
+	do {	/* Allow zero length packets */
+		int pktsze = len;
+
+		if (pktsze > maxsze)
+			pktsze = maxsze;
+
+		td = uhci_alloc_td(uhci, urb->dev);
+		if (!td)
+			return -ENOMEM;
+
+		uhci_add_td_to_urb(urb, td);
+		uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1) |
+			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
+			data);
+
+		data += pktsze;
+		len -= maxsze;
+
+		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			usb_pipeout(urb->pipe));
+	} while (len > 0);
+
+	/*
+	 * USB_ZERO_PACKET means adding a 0-length packet, if
+	 * direction is OUT and the transfer_length was an
+	 * exact multiple of maxsze, hence
+	 * (len = transfer_length - N * maxsze) == 0
+	 * however, if transfer_length == 0, the zero packet
+	 * was already prepared above.
+	 */
+	if (usb_pipeout(urb->pipe) && (urb->transfer_flags & USB_ZERO_PACKET) &&
+	   !len && urb->transfer_buffer_length) {
+		td = uhci_alloc_td(uhci, urb->dev);
+		if (!td)
+			return -ENOMEM;
+
+		uhci_add_td_to_urb(urb, td);
+		uhci_fill_td(td, status, destination | uhci_explen(UHCI_NULL_DATA_SIZE) |
+			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
+			data);
+
+		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			usb_pipeout(urb->pipe));
+	}
+
+	/* Set the flag on the last packet */
+	td->status |= cpu_to_le32(TD_CTRL_IOC);
+
+	qh = uhci_alloc_qh(uhci, urb->dev);
+	if (!qh)
+		return -ENOMEM;
+
+	urbp->qh = qh;
+	qh->urbp = urbp;
+
+	/* Always assume breadth first */
+	uhci_insert_tds_in_qh(qh, urb, 1);
+
+	if (urb->transfer_flags & USB_QUEUE_BULK && eurb)
+		uhci_append_queued_urb(uhci, eurb, urb);
+	else
+		uhci_insert_qh(uhci, uhci->skel_bulk_qh, urb);
+
+	uhci_inc_fsbr(uhci, urb);
+
+	return -EINPROGRESS;
+}
+
+/* We can use the result interrupt since they're identical */
+#define uhci_result_bulk uhci_result_interrupt
+
+/*
+ * Isochronous transfers
+ */
+static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end)
+{
+	struct urb *last_urb = NULL;
+	struct list_head *tmp, *head;
+	int ret = 0;
+
+	head = &uhci->urb_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
+		struct urb *u = up->urb;
+
+		tmp = tmp->next;
+
+		/* look for pending URB's with identical pipe handle */
+		if ((urb->pipe == u->pipe) && (urb->dev == u->dev) &&
+		    (u->status == -EINPROGRESS) && (u != urb)) {
+			if (!last_urb)
+				*start = u->start_frame;
+			last_urb = u;
+		}
+	}
+
+	if (last_urb) {
+		*end = (last_urb->start_frame + last_urb->number_of_packets) & 1023;
+		ret = 0;
+	} else
+		ret = -1;	/* no previous urb found */
+
+	return ret;
+}
+
+static int isochronous_find_start(struct uhci_hcd *uhci, struct urb *urb)
+{
+	int limits;
+	unsigned int start = 0, end = 0;
+
+	if (urb->number_of_packets > 900)	/* 900? Why? */
+		return -EFBIG;
+
+	limits = isochronous_find_limits(uhci, urb, &start, &end);
+
+	if (urb->transfer_flags & USB_ISO_ASAP) {
+		if (limits) {
+			int curframe;
+
+			curframe = uhci_get_current_frame_number(uhci) % UHCI_NUMFRAMES;
+			urb->start_frame = (curframe + 10) % UHCI_NUMFRAMES;
+		} else
+			urb->start_frame = end;
+	} else {
+		urb->start_frame %= UHCI_NUMFRAMES;
+		/* FIXME: Sanity check */
+	}
+
+	return 0;
+}
+
+/*
+ * Isochronous transfers
+ */
+static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct uhci_td *td;
+	int i, ret, frame;
+	int status, destination;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+	status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+
+	ret = isochronous_find_start(uhci, urb);
+	if (ret)
+		return ret;
+
+	frame = urb->start_frame;
+	for (i = 0; i < urb->number_of_packets; i++, frame += urb->interval) {
+		if (!urb->iso_frame_desc[i].length)
+			continue;
+
+		td = uhci_alloc_td(uhci, urb->dev);
+		if (!td)
+			return -ENOMEM;
+
+		uhci_add_td_to_urb(urb, td);
+		uhci_fill_td(td, status, destination | uhci_explen(urb->iso_frame_desc[i].length - 1),
+			urbp->transfer_buffer_dma_handle + urb->iso_frame_desc[i].offset);
+
+		if (i + 1 >= urb->number_of_packets)
+			td->status |= cpu_to_le32(TD_CTRL_IOC);
+
+		uhci_insert_td_frame_list(uhci, td, frame);
+	}
+
+	return -EINPROGRESS;
+}
+
+static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct list_head *tmp, *head;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	int status;
+	int i, ret = 0;
+
+	urb->actual_length = 0;
+
+	i = 0;
+	head = &urbp->td_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+		int actlength;
+
+		tmp = tmp->next;
+
+		if (td_status(td) & TD_CTRL_ACTIVE)
+			return -EINPROGRESS;
+
+		actlength = uhci_actual_length(td_status(td));
+		urb->iso_frame_desc[i].actual_length = actlength;
+		urb->actual_length += actlength;
+
+		status = uhci_map_status(uhci_status_bits(td_status(td)), usb_pipeout(urb->pipe));
+		urb->iso_frame_desc[i].status = status;
+		if (status) {
+			urb->error_count++;
+			ret = status;
+		}
+
+		i++;
+	}
+
+	return ret;
+}
+
+/*
+ * MUST be called with uhci->urb_list_lock acquired
+ */
+static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct list_head *tmp, *head;
+
+	/* We don't match Isoc transfers since they are special */
+	if (usb_pipeisoc(urb->pipe))
+		return NULL;
+
+	head = &uhci->urb_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
+		struct urb *u = up->urb;
+
+		tmp = tmp->next;
+
+		if (u->dev == urb->dev && u->status == -EINPROGRESS) {
+			/* For control, ignore the direction */
+			if (usb_pipecontrol(urb->pipe) &&
+			    (u->pipe & ~USB_DIR_IN) == (urb->pipe & ~USB_DIR_IN))
+				return u;
+			else if (u->pipe == urb->pipe)
+				return u;
+		}
+	}
+
+	return NULL;
+}
+
+static int uhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, int mem_flags)
+{
+	int ret = -EINVAL;
+	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+	unsigned long flags;
+	struct urb *eurb;
+	int bustime;
+
+	spin_lock_irqsave(&uhci->urb_list_lock, flags);
+
+	eurb = uhci_find_urb_ep(uhci, urb);
+	if (eurb && !(urb->transfer_flags & USB_QUEUE_BULK)) {
+		spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
+		return -ENXIO;
+	}
+
+	if (!uhci_alloc_urb_priv(uhci, urb)) {
+		spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
+		return -ENOMEM;
+	}
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+		ret = uhci_submit_control(uhci, urb);
+		break;
+	case PIPE_INTERRUPT:
+		if (urb->bandwidth == 0) {	/* not yet checked/allocated */
+			bustime = usb_check_bandwidth(urb->dev, urb);
+			if (bustime < 0)
+				ret = bustime;
+			else {
+				ret = uhci_submit_interrupt(uhci, urb);
+				if (ret == -EINPROGRESS)
+					usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+			}
+		} else		/* bandwidth is already set */
+			ret = uhci_submit_interrupt(uhci, urb);
+		break;
+	case PIPE_BULK:
+		ret = uhci_submit_bulk(uhci, urb, eurb);
+		break;
+	case PIPE_ISOCHRONOUS:
+		if (urb->bandwidth == 0) {	/* not yet checked/allocated */
+			if (urb->number_of_packets <= 0) {
+				ret = -EINVAL;
+				break;
+			}
+			bustime = usb_check_bandwidth(urb->dev, urb);
+			if (bustime < 0) {
+				ret = bustime;
+				break;
+			}
+
+			ret = uhci_submit_isochronous(uhci, urb);
+			if (ret == -EINPROGRESS)
+				usb_claim_bandwidth(urb->dev, urb, bustime, 1);
+		} else		/* bandwidth is already set */
+			ret = uhci_submit_isochronous(uhci, urb);
+		break;
+	}
+
+	spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
+
+	return 0;
+}
+
+/*
+ * Return the result of a transfer
+ *
+ * MUST be called with urb_list_lock acquired
+ */
+static void uhci_transfer_result(struct uhci_hcd *uhci, struct urb *urb)
+{
+	int ret = -EINVAL;
+	unsigned long flags;
+	struct urb_priv *urbp;
+
+	spin_lock_irqsave(&urb->lock, flags);
+
+	urbp = (struct urb_priv *)urb->hcpriv;
+
+	if (urb->status != -EINPROGRESS) {
+		info("uhci_transfer_result: called for URB %p not in flight?", urb);
+		goto out;
+	}
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+		ret = uhci_result_control(uhci, urb);
+		break;
+	case PIPE_INTERRUPT:
+		ret = uhci_result_interrupt(uhci, urb);
+		break;
+	case PIPE_BULK:
+		ret = uhci_result_bulk(uhci, urb);
+		break;
+	case PIPE_ISOCHRONOUS:
+		ret = uhci_result_isochronous(uhci, urb);
+		break;
+	}
+
+	urbp->status = ret;
+
+	if (ret == -EINPROGRESS)
+		goto out;
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+	case PIPE_BULK:
+	case PIPE_ISOCHRONOUS:
+		/* Release bandwidth for Interrupt or Isoc. transfers */
+		/* Spinlock needed ? */
+		if (urb->bandwidth)
+			usb_release_bandwidth(urb->dev, urb, 1);
+		uhci_unlink_generic(uhci, urb);
+		break;
+	case PIPE_INTERRUPT:
+		/* Interrupts are an exception */
+		if (urb->interval)
+			goto out_complete;
+
+		/* Release bandwidth for Interrupt or Isoc. transfers */
+		/* Spinlock needed ? */
+		if (urb->bandwidth)
+			usb_release_bandwidth(urb->dev, urb, 0);
+		uhci_unlink_generic(uhci, urb);
+		break;
+	default:
+		info("uhci_transfer_result: unknown pipe type %d for urb %p\n",
+			usb_pipetype(urb->pipe), urb);
+	}
+
+	/* Remove it from uhci->urb_list */
+	list_del_init(&urbp->urb_list);
+
+out_complete:
+	uhci_add_complete(uhci, urb);
+
+out:
+	spin_unlock_irqrestore(&urb->lock, flags);
+}
+
+/*
+ * MUST be called with urb->lock acquired
+ */
+static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct list_head *head, *tmp;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	int prevactive = 1;
+
+	/* We can get called when urbp allocation fails, so check */
+	if (!urbp)
+		return;
+
+	uhci_dec_fsbr(uhci, urb);	/* Safe since it checks */
+
+	/*
+	 * Now we need to find out what the last successful toggle was
+	 * so we can update the local data toggle for the next transfer
+	 *
+	 * There's 3 way's the last successful completed TD is found:
+	 *
+	 * 1) The TD is NOT active and the actual length < expected length
+	 * 2) The TD is NOT active and it's the last TD in the chain
+	 * 3) The TD is active and the previous TD is NOT active
+	 *
+	 * Control and Isochronous ignore the toggle, so this is safe
+	 * for all types
+	 */
+	head = &urbp->td_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		if (!(td_status(td) & TD_CTRL_ACTIVE) &&
+		    (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td)) ||
+		    tmp == head))
+			usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
+				uhci_packetout(td_token(td)),
+				uhci_toggle(td_token(td)) ^ 1);
+		else if ((td_status(td) & TD_CTRL_ACTIVE) && !prevactive)
+			usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
+				uhci_packetout(td_token(td)),
+				uhci_toggle(td_token(td)));
+
+		prevactive = td_status(td) & TD_CTRL_ACTIVE;
+	}
+
+	uhci_delete_queued_urb(uhci, urb);
+
+	/* The interrupt loop will reclaim the QH's */
+	uhci_remove_qh(uhci, urbp->qh);
+	urbp->qh = NULL;
+}
+
+static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+{
+	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+	unsigned long flags;
+	struct urb_priv *urbp = urb->hcpriv;
+
+	spin_lock_irqsave(&uhci->urb_list_lock, flags);
+
+	list_del_init(&urbp->urb_list);
+
+	uhci_unlink_generic(uhci, urb);
+
+	if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+		urbp->status = urb->status = -ECONNABORTED;
+
+		spin_lock(&uhci->urb_remove_list_lock);
+
+		/* If we're the first, set the next interrupt bit */
+		if (list_empty(&uhci->urb_remove_list))
+			uhci_set_next_interrupt(uhci);
+			
+		list_add(&urbp->urb_list, &uhci->urb_remove_list);
+
+		spin_unlock(&uhci->urb_remove_list_lock);
+
+		spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
+	} else {
+		urb->status = -ENOENT;
+
+		spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
+
+		if (in_interrupt()) {	/* wait at least 1 frame */
+			static int errorcount = 10;
+
+			if (errorcount--)
+				dbg("uhci_urb_dequeue called from interrupt for urb %p", urb);
+			udelay(1000);
+		} else
+			schedule_timeout(1+1*HZ/1000); 
+
+		uhci_finish_urb(hcd, urb);
+	}
+
+	return 0;
+}
+
+static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct list_head *head, *tmp;
+	int count = 0;
+
+	uhci_dec_fsbr(uhci, urb);
+
+	urbp->fsbr_timeout = 1;
+
+	/*
+	 * Ideally we would want to fix qh->element as well, but it's
+	 * read/write by the HC, so that can introduce a race. It's not
+	 * really worth the hassle
+	 */
+
+	head = &urbp->td_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		/*
+		 * Make sure we don't do the last one (since it'll have the
+		 * TERM bit set) as well as we skip every so many TD's to
+		 * make sure it doesn't hog the bandwidth
+		 */
+		if (tmp != head && (count % DEPTH_INTERVAL) == (DEPTH_INTERVAL - 1))
+			td->link |= UHCI_PTR_DEPTH;
+
+		count++;
+	}
+
+	return 0;
+}
+
+/*
+ * uhci_get_current_frame_number()
+ *
+ * returns the current frame number for a USB bus/controller.
+ */
+static int uhci_get_current_frame_number(struct uhci_hcd *uhci)
+{
+	return inw(uhci->io_addr + USBFRNUM);
+}
+
+static int init_stall_timer(struct usb_hcd *hcd);
+
+static void stall_callback(unsigned long ptr)
+{
+	struct usb_hcd *hcd = (struct usb_hcd *)ptr;
+	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+	struct list_head list, *tmp, *head;
+	unsigned long flags;
+
+	INIT_LIST_HEAD(&list);
+
+	spin_lock_irqsave(&uhci->urb_list_lock, flags);
+	head = &uhci->urb_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
+		struct urb *u = up->urb;
+
+		tmp = tmp->next;
+
+		spin_lock(&u->lock);
+
+		/* Check if the FSBR timed out */
+		if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT))
+			uhci_fsbr_timeout(uhci, u);
+
+		/* Check if the URB timed out */
+		if (u->timeout && time_after_eq(jiffies, up->inserttime + u->timeout)) {
+			list_del(&up->urb_list);
+			list_add_tail(&up->urb_list, &list);
+		}
+
+		spin_unlock(&u->lock);
+	}
+	spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
+
+	head = &list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
+		struct urb *u = up->urb;
+
+		tmp = tmp->next;
+
+		u->transfer_flags |= USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED;
+		uhci_urb_dequeue(hcd, u);
+	}
+
+	/* Really disable FSBR */
+	if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) {
+		uhci->fsbrtimeout = 0;
+		uhci->skel_term_qh->link = UHCI_PTR_TERM;
+	}
+
+	/* enter global suspend if nothing connected */
+	if (!uhci->is_suspended && !ports_active(uhci))
+		suspend_hc(uhci);
+
+	init_stall_timer(hcd);
+}
+
+static int init_stall_timer(struct usb_hcd *hcd)
+{
+	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+	init_timer(&uhci->stall_timer);
+	uhci->stall_timer.function = stall_callback;
+	uhci->stall_timer.data = (unsigned long)hcd;
+	uhci->stall_timer.expires = jiffies + (HZ / 10);
+	add_timer(&uhci->stall_timer);
+
+	return 0;
+}
+
+static void uhci_free_pending_qhs(struct uhci_hcd *uhci)
+{
+	struct list_head *tmp, *head;
+	unsigned long flags;
+
+	spin_lock_irqsave(&uhci->qh_remove_list_lock, flags);
+	head = &uhci->qh_remove_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, remove_list);
+
+		tmp = tmp->next;
+
+		list_del_init(&qh->remove_list);
+
+		uhci_free_qh(uhci, qh);
+	}
+	spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags);
+}
+
+static void uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct usb_device *dev = urb->dev;
+	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+	int killed, resubmit_interrupt, status;
+	unsigned long flags;
+
+	spin_lock_irqsave(&urb->lock, flags);
+
+	killed = (urb->status == -ENOENT || urb->status == -ECONNABORTED ||
+			urb->status == -ECONNRESET);
+	resubmit_interrupt = (usb_pipetype(urb->pipe) == PIPE_INTERRUPT &&
+			urb->interval);
+
+	if (urbp->transfer_buffer_dma_handle)
+		pci_dma_sync_single(uhci->dev, urbp->transfer_buffer_dma_handle,
+			urb->transfer_buffer_length, usb_pipein(urb->pipe) ?
+			PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
+
+	if (urbp->setup_packet_dma_handle)
+		pci_dma_sync_single(uhci->dev, urbp->setup_packet_dma_handle,
+			sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
+
+	status = urbp->status;
+	if (!resubmit_interrupt || killed)
+		/* We don't need urb_priv anymore */
+		uhci_destroy_urb_priv(uhci, urb);
+
+	if (!killed)
+		urb->status = status;
+	spin_unlock_irqrestore(&urb->lock, flags);
+
+	if (resubmit_interrupt)
+		urb->complete(urb);
+	else
+		usb_hcd_giveback_urb(hcd, urb);
+
+	if (resubmit_interrupt)
+		/* Recheck the status. The completion handler may have */
+		/*  unlinked the resubmitting interrupt URB */
+		killed = (urb->status == -ENOENT ||
+			  urb->status == -ECONNABORTED ||
+			  urb->status == -ECONNRESET);
+
+	if (resubmit_interrupt && !killed) {
+		urb->dev = dev;
+		uhci_reset_interrupt(uhci, urb);
+	}
+}
+
+static void uhci_finish_completion(struct usb_hcd *hcd)
+{
+	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+	struct list_head *tmp, *head;
+	unsigned long flags;
+
+	spin_lock_irqsave(&uhci->complete_list_lock, flags);
+	head = &uhci->complete_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb_priv *urbp = list_entry(tmp, struct urb_priv, complete_list);
+		struct urb *urb = urbp->urb;
+
+		list_del_init(&urbp->complete_list);
+		spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
+
+		uhci_finish_urb(hcd, urb);
+
+		spin_lock_irqsave(&uhci->complete_list_lock, flags);
+		head = &uhci->complete_list;
+		tmp = head->next;
+	}
+	spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
+}
+
+static void uhci_remove_pending_qhs(struct uhci_hcd *uhci)
+{
+	struct list_head *tmp, *head;
+	unsigned long flags;
+
+	spin_lock_irqsave(&uhci->urb_remove_list_lock, flags);
+	head = &uhci->urb_remove_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
+		struct urb *urb = urbp->urb;
+
+		tmp = tmp->next;
+
+		list_del_init(&urbp->urb_list);
+
+		urbp->status = urb->status = -ECONNRESET;
+
+		uhci_add_complete(uhci, urb);
+	}
+	spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags);
+}
+
+static void uhci_irq(struct usb_hcd *hcd)
+{
+	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+	unsigned int io_addr = uhci->io_addr;
+	unsigned short status;
+	struct list_head *tmp, *head;
+
+	/*
+	 * Read the interrupt status, and write it back to clear the
+	 * interrupt cause
+	 */
+	status = inw(io_addr + USBSTS);
+	if (!status)	/* shared interrupt, not mine */
+		return;
+	outw(status, io_addr + USBSTS);		/* Clear it */
+
+	if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {
+		if (status & USBSTS_HSE)
+			err("%x: host system error, PCI problems?", io_addr);
+		if (status & USBSTS_HCPE)
+			err("%x: host controller process error. something bad happened", io_addr);
+		if ((status & USBSTS_HCH) && !uhci->is_suspended) {
+			err("%x: host controller halted. very bad", io_addr);
+			/* FIXME: Reset the controller, fix the offending TD */
+		}
+	}
+
+	if (status & USBSTS_RD)
+		wakeup_hc(uhci);
+
+	uhci_free_pending_qhs(uhci);
+
+	uhci_remove_pending_qhs(uhci);
+
+	uhci_clear_next_interrupt(uhci);
+
+	/* Walk the list of pending URB's to see which ones completed */
+	spin_lock(&uhci->urb_list_lock);
+	head = &uhci->urb_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
+		struct urb *urb = urbp->urb;
+
+		tmp = tmp->next;
+
+		/* Checks the status and does all of the magic necessary */
+		uhci_transfer_result(uhci, urb);
+	}
+	spin_unlock(&uhci->urb_list_lock);
+
+	uhci_finish_completion(hcd);
+}
+
+static void reset_hc(struct uhci_hcd *uhci)
+{
+	unsigned int io_addr = uhci->io_addr;
+
+	/* Global reset for 50ms */
+	outw(USBCMD_GRESET, io_addr + USBCMD);
+	wait_ms(50);
+	outw(0, io_addr + USBCMD);
+	wait_ms(10);
+}
+
+static void suspend_hc(struct uhci_hcd *uhci)
+{
+	unsigned int io_addr = uhci->io_addr;
+
+	dbg("%x: suspend_hc", io_addr);
+
+	outw(USBCMD_EGSM, io_addr + USBCMD);
+
+	uhci->is_suspended = 1;
+}
+
+static void wakeup_hc(struct uhci_hcd *uhci)
+{
+	unsigned int io_addr = uhci->io_addr;
+	unsigned int status;
+
+	dbg("%x: wakeup_hc", io_addr);
+
+	outw(0, io_addr + USBCMD);
+	
+	/* wait for EOP to be sent */
+	status = inw(io_addr + USBCMD);
+	while (status & USBCMD_FGR)
+		status = inw(io_addr + USBCMD);
+
+	uhci->is_suspended = 0;
+
+	/* Run and mark it configured with a 64-byte max packet */
+	outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
+}
+
+static int ports_active(struct uhci_hcd *uhci)
+{
+	unsigned int io_addr = uhci->io_addr;
+	int connection = 0;
+	int i;
+
+	for (i = 0; i < uhci->rh_numports; i++)
+		connection |= (inw(io_addr + USBPORTSC1 + i * 2) & 0x1);
+
+	return connection;
+}
+
+static void start_hc(struct uhci_hcd *uhci)
+{
+	unsigned int io_addr = uhci->io_addr;
+	int timeout = 1000;
+
+	/*
+	 * Reset the HC - this will force us to get a
+	 * new notification of any already connected
+	 * ports due to the virtual disconnect that it
+	 * implies.
+	 */
+	outw(USBCMD_HCRESET, io_addr + USBCMD);
+	while (inw(io_addr + USBCMD) & USBCMD_HCRESET) {
+		if (!--timeout) {
+			printk(KERN_ERR "uhci: USBCMD_HCRESET timed out!\n");
+			break;
+		}
+	}
+
+	/* Turn on all interrupts */
+	outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
+		io_addr + USBINTR);
+
+	/* Start at frame 0 */
+	outw(0, io_addr + USBFRNUM);
+	outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD);
+
+	/* Run and mark it configured with a 64-byte max packet */
+	outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
+}
+
+#ifdef CONFIG_PROC_FS
+static int uhci_num = 0;
+#endif
+
+/*
+ * De-allocate all resources..
+ */
+static void release_uhci(struct uhci_hcd *uhci)
+{
+	int i;
+#ifdef CONFIG_PROC_FS
+	char buf[8];
+#endif
+
+	for (i = 0; i < UHCI_NUM_SKELQH; i++)
+		if (uhci->skelqh[i]) {
+			uhci_free_qh(uhci, uhci->skelqh[i]);
+			uhci->skelqh[i] = NULL;
+		}
+
+	for (i = 0; i < UHCI_NUM_SKELTD; i++)
+		if (uhci->skeltd[i]) {
+			uhci_free_td(uhci, uhci->skeltd[i]);
+			uhci->skeltd[i] = NULL;
+		}
+
+	if (uhci->qh_pool) {
+		pci_pool_destroy(uhci->qh_pool);
+		uhci->qh_pool = NULL;
+	}
+
+	if (uhci->td_pool) {
+		pci_pool_destroy(uhci->td_pool);
+		uhci->td_pool = NULL;
+	}
+
+	if (uhci->fl) {
+		pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
+		uhci->fl = NULL;
+	}
+
+#ifdef CONFIG_PROC_FS
+	if (uhci->proc_entry) {
+		sprintf(buf, "hc%d", uhci->num);
+
+		remove_proc_entry(buf, uhci_proc_root);
+		uhci->proc_entry = NULL;
+	}
+#endif
+}
+
+/*
+ * Allocate a frame list, and then setup the skeleton
+ *
+ * The hardware doesn't really know any difference
+ * in the queues, but the order does matter for the
+ * protocols higher up. The order is:
+ *
+ *  - any isochronous events handled before any
+ *    of the queues. We don't do that here, because
+ *    we'll create the actual TD entries on demand.
+ *  - The first queue is the interrupt queue.
+ *  - The second queue is the control queue, split into low and high speed
+ *  - The third queue is bulk queue.
+ *  - The fourth queue is the bandwidth reclamation queue, which loops back
+ *    to the high speed control queue.
+ */
+static int __devinit uhci_start(struct usb_hcd *hcd)
+{
+	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+	struct pci_dev *dev = hcd->pdev;
+	int retval = -EBUSY;
+	int i, port;
+	dma_addr_t dma_handle;
+#ifdef CONFIG_PROC_FS
+	char buf[8];
+	struct proc_dir_entry *ent;
+#endif
+
+	uhci->dev = dev;
+
+	/* Should probably move to core/hcd.c */
+	if (pci_set_dma_mask(dev, 0xFFFFFFFF)) {
+		err("couldn't set PCI dma mask");
+		retval = -ENODEV;
+		goto err_pci_set_dma_mask;
+	}
+
+	uhci->io_addr = pci_resource_start(dev, hcd->region);
+	uhci->io_size = pci_resource_len(dev, hcd->region);
+
+#ifdef CONFIG_PROC_FS
+	uhci->num = uhci_num++;
+
+	sprintf(buf, "hc%d", uhci->num);
+
+	ent = create_proc_entry(buf, S_IFREG|S_IRUGO|S_IWUSR, uhci_proc_root);
+	if (!ent) {
+		err("couldn't create uhci proc entry");
+		retval = -ENOMEM;
+		goto err_create_proc_entry;
+	}
+
+	ent->data = uhci;
+	ent->proc_fops = &uhci_proc_operations;
+	ent->size = 0;
+	uhci->proc_entry = ent;
+#endif
+
+	/* Reset here so we don't get any interrupts from an old setup */
+	/*  or broken setup */
+	reset_hc(uhci);
+
+	uhci->fsbr = 0;
+	uhci->fsbrtimeout = 0;
+
+	uhci->is_suspended = 0;
+
+	spin_lock_init(&uhci->qh_remove_list_lock);
+	INIT_LIST_HEAD(&uhci->qh_remove_list);
+
+	spin_lock_init(&uhci->urb_remove_list_lock);
+	INIT_LIST_HEAD(&uhci->urb_remove_list);
+
+	spin_lock_init(&uhci->urb_list_lock);
+	INIT_LIST_HEAD(&uhci->urb_list);
+
+	spin_lock_init(&uhci->complete_list_lock);
+	INIT_LIST_HEAD(&uhci->complete_list);
+
+	spin_lock_init(&uhci->frame_list_lock);
+
+	uhci->fl = pci_alloc_consistent(dev, sizeof(*uhci->fl), &dma_handle);
+	if (!uhci->fl) {
+		err("unable to allocate consistent memory for frame list");
+		goto err_alloc_fl;
+	}
+
+	memset((void *)uhci->fl, 0, sizeof(*uhci->fl));
+
+	uhci->fl->dma_handle = dma_handle;
+
+	uhci->td_pool = pci_pool_create("uhci_td", dev,
+		sizeof(struct uhci_td), 16, 0, GFP_DMA | GFP_ATOMIC);
+	if (!uhci->td_pool) {
+		err("unable to create td pci_pool");
+		goto err_create_td_pool;
+	}
+
+	uhci->qh_pool = pci_pool_create("uhci_qh", dev,
+		sizeof(struct uhci_qh), 16, 0, GFP_DMA | GFP_ATOMIC);
+	if (!uhci->qh_pool) {
+		err("unable to create qh pci_pool");
+		goto err_create_qh_pool;
+	}
+
+	/* Initialize the root hub */
+
+	/* UHCI specs says devices must have 2 ports, but goes on to say */
+	/*  they may have more but give no way to determine how many they */
+	/*  have. However, according to the UHCI spec, Bit 7 is always set */
+	/*  to 1. So we try to use this to our advantage */
+	for (port = 0; port < (uhci->io_size - 0x10) / 2; port++) {
+		unsigned int portstatus;
+
+		portstatus = inw(uhci->io_addr + 0x10 + (port * 2));
+		if (!(portstatus & 0x0080))
+			break;
+	}
+	if (debug)
+		info("detected %d ports", port);
+
+	/* This is experimental so anything less than 2 or greater than 8 is */
+	/*  something weird and we'll ignore it */
+	if (port < 2 || port > 8) {
+		info("port count misdetected? forcing to 2 ports");
+		port = 2;
+	}
+
+	uhci->rh_numports = port;
+
+	hcd->self.root_hub = uhci->rh_dev = usb_alloc_dev(NULL, &hcd->self);
+	if (!uhci->rh_dev) {
+		err("unable to allocate root hub");
+		goto err_alloc_root_hub;
+	}
+
+	uhci->skeltd[0] = uhci_alloc_td(uhci, uhci->rh_dev);
+	if (!uhci->skeltd[0]) {
+		err("unable to allocate TD 0");
+		goto err_alloc_skeltd;
+	}
+
+	/*
+	 * 9 Interrupt queues; link int2 to int1, int4 to int2, etc
+	 * then link int1 to control and control to bulk
+	 */
+	for (i = 1; i < 9; i++) {
+		struct uhci_td *td;
+
+		td = uhci->skeltd[i] = uhci_alloc_td(uhci, uhci->rh_dev);
+		if (!td) {
+			err("unable to allocate TD %d", i);
+			goto err_alloc_skeltd;
+		}
+
+		uhci_fill_td(td, 0, uhci_explen(UHCI_NULL_DATA_SIZE) |
+			(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
+		td->link = cpu_to_le32(uhci->skeltd[i - 1]->dma_handle);
+	}
+
+	uhci->skel_term_td = uhci_alloc_td(uhci, uhci->rh_dev);
+	if (!uhci->skel_term_td) {
+		err("unable to allocate skel TD term");
+		goto err_alloc_skeltd;
+	}
+
+	for (i = 0; i < UHCI_NUM_SKELQH; i++) {
+		uhci->skelqh[i] = uhci_alloc_qh(uhci, uhci->rh_dev);
+		if (!uhci->skelqh[i]) {
+			err("unable to allocate QH %d", i);
+			goto err_alloc_skelqh;
+		}
+	}
+
+	uhci_fill_td(uhci->skel_int1_td, 0, (UHCI_NULL_DATA_SIZE << 21) |
+		(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
+	uhci->skel_int1_td->link = cpu_to_le32(uhci->skel_ls_control_qh->dma_handle) | UHCI_PTR_QH;
+
+	uhci->skel_ls_control_qh->link = cpu_to_le32(uhci->skel_hs_control_qh->dma_handle) | UHCI_PTR_QH;
+	uhci->skel_ls_control_qh->element = UHCI_PTR_TERM;
+
+	uhci->skel_hs_control_qh->link = cpu_to_le32(uhci->skel_bulk_qh->dma_handle) | UHCI_PTR_QH;
+	uhci->skel_hs_control_qh->element = UHCI_PTR_TERM;
+
+	uhci->skel_bulk_qh->link = cpu_to_le32(uhci->skel_term_qh->dma_handle) | UHCI_PTR_QH;
+	uhci->skel_bulk_qh->element = UHCI_PTR_TERM;
+
+	/* This dummy TD is to work around a bug in Intel PIIX controllers */
+	uhci_fill_td(uhci->skel_term_td, 0, (UHCI_NULL_DATA_SIZE << 21) |
+		(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
+	uhci->skel_term_td->link = cpu_to_le32(uhci->skel_term_td->dma_handle);
+
+	uhci->skel_term_qh->link = UHCI_PTR_TERM;
+	uhci->skel_term_qh->element = cpu_to_le32(uhci->skel_term_td->dma_handle);
+
+	/*
+	 * Fill the frame list: make all entries point to
+	 * the proper interrupt queue.
+	 *
+	 * This is probably silly, but it's a simple way to
+	 * scatter the interrupt queues in a way that gives
+	 * us a reasonable dynamic range for irq latencies.
+	 */
+	for (i = 0; i < UHCI_NUMFRAMES; i++) {
+		int irq = 0;
+
+		if (i & 1) {
+			irq++;
+			if (i & 2) {
+				irq++;
+				if (i & 4) { 
+					irq++;
+					if (i & 8) { 
+						irq++;
+						if (i & 16) {
+							irq++;
+							if (i & 32) {
+								irq++;
+								if (i & 64)
+									irq++;
+							}
+						}
+					}
+				}
+			}
+		}
+
+		/* Only place we don't use the frame list routines */
+		uhci->fl->frame[i] = cpu_to_le32(uhci->skeltd[irq]->dma_handle);
+	}
+
+	start_hc(uhci);
+
+	init_stall_timer(hcd);
+
+	/* disable legacy emulation */
+	pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT);
+
+        hcd->state = USB_STATE_READY;
+
+	usb_connect(uhci->rh_dev);
+        uhci->rh_dev->speed = USB_SPEED_FULL;
+
+	if (usb_register_root_hub(uhci->rh_dev, &dev->dev) != 0) {
+		err("unable to start root hub");
+		retval = -ENOMEM;
+		goto err_start_root_hub;
+	}
+
+	return 0;
+
+/*
+ * error exits:
+ */
+err_start_root_hub:
+	reset_hc(uhci);
+
+	del_timer(&uhci->stall_timer);
+
+	for (i = 0; i < UHCI_NUM_SKELQH; i++)
+		if (uhci->skelqh[i]) {
+			uhci_free_qh(uhci, uhci->skelqh[i]);
+			uhci->skelqh[i] = NULL;
+		}
+
+err_alloc_skelqh:
+	for (i = 0; i < UHCI_NUM_SKELTD; i++)
+		if (uhci->skeltd[i]) {
+			uhci_free_td(uhci, uhci->skeltd[i]);
+			uhci->skeltd[i] = NULL;
+		}
+
+err_alloc_skeltd:
+	usb_free_dev(uhci->rh_dev);
+	uhci->rh_dev = NULL;
+
+err_alloc_root_hub:
+	pci_pool_destroy(uhci->qh_pool);
+	uhci->qh_pool = NULL;
+
+err_create_qh_pool:
+	pci_pool_destroy(uhci->td_pool);
+	uhci->td_pool = NULL;
+
+err_create_td_pool:
+	pci_free_consistent(dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
+	uhci->fl = NULL;
+
+err_alloc_fl:
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry(buf, uhci_proc_root);
+	uhci->proc_entry = NULL;
+
+err_create_proc_entry:
+#endif
+
+err_pci_set_dma_mask:
+
+	return retval;
+}
+
+static void __devexit uhci_stop(struct usb_hcd *hcd)
+{
+	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+	if (uhci->rh_dev)
+		usb_disconnect(&uhci->rh_dev);
+
+	del_timer(&uhci->stall_timer);
+
+	/*
+	 * At this point, we're guaranteed that no new connects can be made
+	 * to this bus since there are no more parents
+	 */
+	uhci_free_pending_qhs(uhci);
+	uhci_remove_pending_qhs(uhci);
+
+	reset_hc(uhci);
+
+	uhci_free_pending_qhs(uhci);
+
+	release_uhci(uhci);
+}
+
+#ifdef CONFIG_PM
+static int uhci_suspend(struct usb_hcd *hcd, u32 state)
+{
+	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+	suspend_hc(uhci);
+	return 0;
+}
+
+static int uhci_resume(struct usb_hcd *hcd)
+{
+	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+	pci_set_master(uhci->dev);
+
+	reset_hc(uhci);
+	start_hc(uhci);
+	return 0;
+}
+#endif
+
+static struct usb_hcd *uhci_hcd_alloc(void)
+{
+	struct uhci_hcd *uhci;
+
+	uhci = (struct uhci_hcd *)kmalloc(sizeof(*uhci), GFP_KERNEL);
+	if (!uhci)
+		return NULL;
+
+	memset(uhci, 0, sizeof(*uhci));
+	return &uhci->hcd;
+}
+
+static void uhci_hcd_free(struct usb_hcd *hcd)
+{
+	kfree(hcd_to_uhci(hcd));
+}
+
+static int uhci_hcd_get_frame_number(struct usb_hcd *hcd)
+{
+	return uhci_get_current_frame_number(hcd_to_uhci(hcd));
+}
+
+static const char hcd_name[] = "uhci-hcd";
+
+static const struct hc_driver uhci_driver = {
+	description:		hcd_name,
+
+	/* Generic hardware linkage */
+	irq:			uhci_irq,
+	flags:			HCD_USB11,
+
+	/* Basic lifecycle operations */
+	start:			uhci_start,
+#ifdef CONFIG_PM
+	suspend:		uhci_suspend,
+	resume:			uhci_resume,
+#endif
+	stop:			uhci_stop,
+
+	hcd_alloc:		uhci_hcd_alloc,
+	hcd_free:		uhci_hcd_free,
+
+	urb_enqueue:		uhci_urb_enqueue,
+	urb_dequeue:		uhci_urb_dequeue,
+	free_config:		NULL,
+
+	get_frame_number:	uhci_hcd_get_frame_number,
+
+	hub_status_data:	uhci_hub_status_data,
+	hub_control:		uhci_hub_control,
+};
+
+static const struct pci_device_id __devinitdata uhci_pci_ids[] = { {
+
+	/* handle any USB UHCI controller */
+	class: 		((PCI_CLASS_SERIAL_USB << 8) | 0x00),
+	class_mask: 	~0,
+	driver_data:	(unsigned long) &uhci_driver,
+
+	/* no matter who makes it */
+	vendor:		PCI_ANY_ID,
+	device:		PCI_ANY_ID,
+	subvendor:	PCI_ANY_ID,
+	subdevice:	PCI_ANY_ID,
+
+	}, { /* end: all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE(pci, uhci_pci_ids);
+
+static struct pci_driver uhci_pci_driver = {
+	name:		(char *)hcd_name,
+	id_table:	uhci_pci_ids,
+
+	probe:		usb_hcd_pci_probe,
+	remove:		usb_hcd_pci_remove,
+
+#ifdef	CONFIG_PM
+	suspend:	usb_hcd_pci_suspend,
+	resume:		usb_hcd_pci_resume,
+#endif	/* PM */
+};
+ 
+static int __init uhci_hcd_init(void)
+{
+	int retval = -ENOMEM;
+
+	info(DRIVER_DESC " " DRIVER_VERSION);
+
+	if (debug) {
+		errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
+		if (!errbuf)
+			goto errbuf_failed;
+	}
+
+#ifdef CONFIG_PROC_FS
+	uhci_proc_root = create_proc_entry("driver/uhci", S_IFDIR, 0);
+	if (!uhci_proc_root)
+		goto proc_failed;
+#endif
+
+	uhci_up_cachep = kmem_cache_create("uhci_urb_priv",
+		sizeof(struct urb_priv), 0, 0, NULL, NULL);
+	if (!uhci_up_cachep)
+		goto up_failed;
+
+	retval = pci_module_init(&uhci_pci_driver);
+	if (retval)
+		goto init_failed;
+
+	return 0;
+
+init_failed:
+	if (kmem_cache_destroy(uhci_up_cachep))
+		printk(KERN_INFO "uhci: not all urb_priv's were freed\n");
+
+up_failed:
+
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry("uhci", 0);
+
+proc_failed:
+#endif
+	if (errbuf)
+		kfree(errbuf);
+
+errbuf_failed:
+
+	return retval;
+}
+
+static void __exit uhci_hcd_cleanup(void) 
+{
+	pci_unregister_driver(&uhci_pci_driver);
+	
+	if (kmem_cache_destroy(uhci_up_cachep))
+		printk(KERN_INFO "uhci: not all urb_priv's were freed\n");
+
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry("uhci", 0);
+#endif
+
+	if (errbuf)
+		kfree(errbuf);
+}
+
+module_init(uhci_hcd_init);
+module_exit(uhci_hcd_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
diff -Nru a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/host/uhci-hcd.h	Sat May 18 00:46:15 2002
@@ -0,0 +1,389 @@
+#ifndef __LINUX_UHCI_HCD_H
+#define __LINUX_UHCI_HCD_H
+
+#include <linux/list.h>
+#include <linux/usb.h>
+
+/*
+ * Universal Host Controller Interface data structures and defines
+ */
+
+/* Command register */
+#define USBCMD		0
+#define   USBCMD_RS		0x0001	/* Run/Stop */
+#define   USBCMD_HCRESET	0x0002	/* Host reset */
+#define   USBCMD_GRESET		0x0004	/* Global reset */
+#define   USBCMD_EGSM		0x0008	/* Global Suspend Mode */
+#define   USBCMD_FGR		0x0010	/* Force Global Resume */
+#define   USBCMD_SWDBG		0x0020	/* SW Debug mode */
+#define   USBCMD_CF		0x0040	/* Config Flag (sw only) */
+#define   USBCMD_MAXP		0x0080	/* Max Packet (0 = 32, 1 = 64) */
+
+/* Status register */
+#define USBSTS		2
+#define   USBSTS_USBINT		0x0001	/* Interrupt due to IOC */
+#define   USBSTS_ERROR		0x0002	/* Interrupt due to error */
+#define   USBSTS_RD		0x0004	/* Resume Detect */
+#define   USBSTS_HSE		0x0008	/* Host System Error - basically PCI problems */
+#define   USBSTS_HCPE		0x0010	/* Host Controller Process Error - the scripts were buggy */
+#define   USBSTS_HCH		0x0020	/* HC Halted */
+
+/* Interrupt enable register */
+#define USBINTR		4
+#define   USBINTR_TIMEOUT	0x0001	/* Timeout/CRC error enable */
+#define   USBINTR_RESUME	0x0002	/* Resume interrupt enable */
+#define   USBINTR_IOC		0x0004	/* Interrupt On Complete enable */
+#define   USBINTR_SP		0x0008	/* Short packet interrupt enable */
+
+#define USBFRNUM	6
+#define USBFLBASEADD	8
+#define USBSOF		12
+
+/* USB port status and control registers */
+#define USBPORTSC1	16
+#define USBPORTSC2	18
+#define   USBPORTSC_CCS		0x0001	/* Current Connect Status ("device present") */
+#define   USBPORTSC_CSC		0x0002	/* Connect Status Change */
+#define   USBPORTSC_PE		0x0004	/* Port Enable */
+#define   USBPORTSC_PEC		0x0008	/* Port Enable Change */
+#define   USBPORTSC_LS		0x0030	/* Line Status */
+#define   USBPORTSC_RD		0x0040	/* Resume Detect */
+#define   USBPORTSC_LSDA	0x0100	/* Low Speed Device Attached */
+#define   USBPORTSC_PR		0x0200	/* Port Reset */
+#define   USBPORTSC_SUSP	0x1000	/* Suspend */
+
+/* Legacy support register */
+#define USBLEGSUP		0xc0
+#define   USBLEGSUP_DEFAULT	0x2000	/* only PIRQ enable set */
+
+#define UHCI_NULL_DATA_SIZE	0x7FF	/* for UHCI controller TD */
+
+#define UHCI_PTR_BITS		cpu_to_le32(0x000F)
+#define UHCI_PTR_TERM		cpu_to_le32(0x0001)
+#define UHCI_PTR_QH		cpu_to_le32(0x0002)
+#define UHCI_PTR_DEPTH		cpu_to_le32(0x0004)
+
+#define UHCI_NUMFRAMES		1024	/* in the frame list [array] */
+#define UHCI_MAX_SOF_NUMBER	2047	/* in an SOF packet */
+#define CAN_SCHEDULE_FRAMES	1000	/* how far future frames can be scheduled */
+
+struct uhci_frame_list {
+	__u32 frame[UHCI_NUMFRAMES];
+
+	void *frame_cpu[UHCI_NUMFRAMES];
+
+	dma_addr_t dma_handle;
+};
+
+struct urb_priv;
+
+struct uhci_qh {
+	/* Hardware fields */
+	__u32 link;			/* Next queue */
+	__u32 element;			/* Queue element pointer */
+
+	/* Software fields */
+	dma_addr_t dma_handle;
+
+	struct usb_device *dev;
+	struct urb_priv *urbp;
+
+	struct list_head list;		/* P: uhci->frame_list_lock */
+	struct list_head remove_list;	/* P: uhci->remove_list_lock */
+} __attribute__((aligned(16)));
+
+/*
+ * for TD <status>:
+ */
+#define td_status(td)		le32_to_cpu((td)->status)
+#define TD_CTRL_SPD		(1 << 29)	/* Short Packet Detect */
+#define TD_CTRL_C_ERR_MASK	(3 << 27)	/* Error Counter bits */
+#define TD_CTRL_C_ERR_SHIFT	27
+#define TD_CTRL_LS		(1 << 26)	/* Low Speed Device */
+#define TD_CTRL_IOS		(1 << 25)	/* Isochronous Select */
+#define TD_CTRL_IOC		(1 << 24)	/* Interrupt on Complete */
+#define TD_CTRL_ACTIVE		(1 << 23)	/* TD Active */
+#define TD_CTRL_STALLED		(1 << 22)	/* TD Stalled */
+#define TD_CTRL_DBUFERR		(1 << 21)	/* Data Buffer Error */
+#define TD_CTRL_BABBLE		(1 << 20)	/* Babble Detected */
+#define TD_CTRL_NAK		(1 << 19)	/* NAK Received */
+#define TD_CTRL_CRCTIMEO	(1 << 18)	/* CRC/Time Out Error */
+#define TD_CTRL_BITSTUFF	(1 << 17)	/* Bit Stuff Error */
+#define TD_CTRL_ACTLEN_MASK	0x7FF	/* actual length, encoded as n - 1 */
+
+#define TD_CTRL_ANY_ERROR	(TD_CTRL_STALLED | TD_CTRL_DBUFERR | \
+				 TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF)
+
+#define uhci_maxerr(err)		((err) << TD_CTRL_C_ERR_SHIFT)
+#define uhci_status_bits(ctrl_sts)	((ctrl_sts) & 0xFE0000)
+#define uhci_actual_length(ctrl_sts)	(((ctrl_sts) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
+
+/*
+ * for TD <info>: (a.k.a. Token)
+ */
+#define td_token(td)		le32_to_cpu((td)->token)
+#define TD_TOKEN_DEVADDR_SHIFT	8
+#define TD_TOKEN_TOGGLE_SHIFT	19
+#define TD_TOKEN_TOGGLE		(1 << 19)
+#define TD_TOKEN_EXPLEN_SHIFT	21
+#define TD_TOKEN_EXPLEN_MASK	0x7FF		/* expected length, encoded as n - 1 */
+#define TD_TOKEN_PID_MASK	0xFF
+
+#define uhci_explen(len)	((len) << TD_TOKEN_EXPLEN_SHIFT)
+
+#define uhci_expected_length(token) ((((token) >> 21) + 1) & TD_TOKEN_EXPLEN_MASK)
+#define uhci_toggle(token)	(((token) >> TD_TOKEN_TOGGLE_SHIFT) & 1)
+#define uhci_endpoint(token)	(((token) >> 15) & 0xf)
+#define uhci_devaddr(token)	(((token) >> TD_TOKEN_DEVADDR_SHIFT) & 0x7f)
+#define uhci_devep(token)	(((token) >> TD_TOKEN_DEVADDR_SHIFT) & 0x7ff)
+#define uhci_packetid(token)	((token) & TD_TOKEN_PID_MASK)
+#define uhci_packetout(token)	(uhci_packetid(token) != USB_PID_IN)
+#define uhci_packetin(token)	(uhci_packetid(token) == USB_PID_IN)
+
+/*
+ * The documentation says "4 words for hardware, 4 words for software".
+ *
+ * That's silly, the hardware doesn't care. The hardware only cares that
+ * the hardware words are 16-byte aligned, and we can have any amount of
+ * sw space after the TD entry as far as I can tell.
+ *
+ * But let's just go with the documentation, at least for 32-bit machines.
+ * On 64-bit machines we probably want to take advantage of the fact that
+ * hw doesn't really care about the size of the sw-only area.
+ *
+ * Alas, not anymore, we have more than 4 words for software, woops.
+ * Everything still works tho, surprise! -jerdfelt
+ */
+struct uhci_td {
+	/* Hardware fields */
+	__u32 link;
+	__u32 status;
+	__u32 token;
+	__u32 buffer;
+
+	/* Software fields */
+	dma_addr_t dma_handle;
+
+	struct usb_device *dev;
+	struct urb *urb;
+
+	struct list_head list;		/* P: urb->lock */
+
+	int frame;
+	struct list_head fl_list;	/* P: uhci->frame_list_lock */
+} __attribute__((aligned(16)));
+
+/*
+ * There are various standard queues. We set up several different
+ * queues for each of the three basic queue types: interrupt,
+ * control, and bulk.
+ *
+ *  - There are various different interrupt latencies: ranging from
+ *    every other USB frame (2 ms apart) to every 256 USB frames (ie
+ *    256 ms apart). Make your choice according to how obnoxious you
+ *    want to be on the wire, vs how critical latency is for you.
+ *  - The control list is done every frame.
+ *  - There are 4 bulk lists, so that up to four devices can have a
+ *    bulk list of their own and when run concurrently all four lists
+ *    will be be serviced.
+ *
+ * This is a bit misleading, there are various interrupt latencies, but they
+ * vary a bit, interrupt2 isn't exactly 2ms, it can vary up to 4ms since the
+ * other queues can "override" it. interrupt4 can vary up to 8ms, etc. Minor
+ * problem
+ *
+ * In the case of the root hub, these QH's are just head's of qh's. Don't
+ * be scared, it kinda makes sense. Look at this wonderful picture care of
+ * Linus:
+ *
+ *  generic-  ->  dev1-  ->  generic-  ->  dev1-  ->  control-  ->  bulk- -> ...
+ *   iso-QH      iso-QH       irq-QH      irq-QH        QH           QH
+ *      |           |            |           |           |            |
+ *     End     dev1-iso-TD1     End     dev1-irq-TD1    ...          ... 
+ *                  |
+ *             dev1-iso-TD2
+ *                  |
+ *                ....
+ *
+ * This may vary a bit (the UHCI docs don't explicitly say you can put iso
+ * transfers in QH's and all of their pictures don't have that either) but
+ * other than that, that is what we're doing now
+ *
+ * And now we don't put Iso transfers in QH's, so we don't waste one on it
+ * --jerdfelt
+ *
+ * To keep with Linus' nomenclature, this is called the QH skeleton. These
+ * labels (below) are only signficant to the root hub's QH's
+ */
+
+#define UHCI_NUM_SKELTD		10
+#define skel_int1_td		skeltd[0]
+#define skel_int2_td		skeltd[1]
+#define skel_int4_td		skeltd[2]
+#define skel_int8_td		skeltd[3]
+#define skel_int16_td		skeltd[4]
+#define skel_int32_td		skeltd[5]
+#define skel_int64_td		skeltd[6]
+#define skel_int128_td		skeltd[7]
+#define skel_int256_td		skeltd[8]
+#define skel_term_td		skeltd[9]	/* To work around PIIX UHCI bug */
+
+#define UHCI_NUM_SKELQH		4
+#define skel_ls_control_qh	skelqh[0]
+#define skel_hs_control_qh	skelqh[1]
+#define skel_bulk_qh		skelqh[2]
+#define skel_term_qh		skelqh[3]
+
+/*
+ * Search tree for determining where <interval> fits in the
+ * skelqh[] skeleton.
+ *
+ * An interrupt request should be placed into the slowest skelqh[]
+ * which meets the interval/period/frequency requirement.
+ * An interrupt request is allowed to be faster than <interval> but not slower.
+ *
+ * For a given <interval>, this function returns the appropriate/matching
+ * skelqh[] index value.
+ *
+ * NOTE: For UHCI, we don't really need int256_qh since the maximum interval
+ * is 255 ms.  However, we do need an int1_qh since 1 is a valid interval
+ * and we should meet that frequency when requested to do so.
+ * This will require some change(s) to the UHCI skeleton.
+ */
+static inline int __interval_to_skel(int interval)
+{
+	if (interval < 16) {
+		if (interval < 4) {
+			if (interval < 2)
+				return 0;	/* int1 for 0-1 ms */
+			return 1;		/* int2 for 2-3 ms */
+		}
+		if (interval < 8)
+			return 2;		/* int4 for 4-7 ms */
+		return 3;			/* int8 for 8-15 ms */
+	}
+	if (interval < 64) {
+		if (interval < 32)
+			return 4;		/* int16 for 16-31 ms */
+		return 5;			/* int32 for 32-63 ms */
+	}
+	if (interval < 128)
+		return 6;			/* int64 for 64-127 ms */
+	return 7;				/* int128 for 128-255 ms (Max.) */
+}
+
+#define hcd_to_uhci(hcd_ptr) list_entry(hcd_ptr, struct uhci_hcd, hcd)
+
+/*
+ * This describes the full uhci information.
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs.
+ */
+struct uhci_hcd {
+	struct usb_hcd hcd;
+
+	struct pci_dev *dev;
+
+#ifdef CONFIG_PROC_FS
+	/* procfs */
+	int num;
+	struct proc_dir_entry *proc_entry;
+#endif
+
+	/* Grabbed from PCI */
+	int irq;
+	unsigned int io_addr;
+	unsigned int io_size;
+
+	struct pci_pool *qh_pool;
+	struct pci_pool *td_pool;
+
+	struct usb_bus *bus;
+
+	struct uhci_td *skeltd[UHCI_NUM_SKELTD];	/* Skeleton TD's */
+	struct uhci_qh *skelqh[UHCI_NUM_SKELQH];	/* Skeleton QH's */
+
+	spinlock_t frame_list_lock;
+	struct uhci_frame_list *fl;		/* P: uhci->frame_list_lock */
+	int fsbr;				/* Full speed bandwidth reclamation */
+	unsigned long fsbrtimeout;		/* FSBR delay */
+	int is_suspended;
+
+	/* Main list of URB's currently controlled by this HC */
+	spinlock_t urb_list_lock;
+	struct list_head urb_list;		/* P: uhci->urb_list_lock */
+
+	/* List of QH's that are done, but waiting to be unlinked (race) */
+	spinlock_t qh_remove_list_lock;
+	struct list_head qh_remove_list;	/* P: uhci->qh_remove_list_lock */
+
+	/* List of asynchronously unlinked URB's */
+	spinlock_t urb_remove_list_lock;
+	struct list_head urb_remove_list;	/* P: uhci->urb_remove_list_lock */
+
+	/* List of URB's awaiting completion callback */
+	spinlock_t complete_list_lock;
+	struct list_head complete_list;		/* P: uhci->complete_list_lock */
+
+	struct usb_device *rh_dev;		/* Root hub */
+	int rh_numports;
+
+	struct timer_list stall_timer;
+};
+
+struct urb_priv {
+	struct list_head urb_list;
+
+	struct urb *urb;
+	struct usb_device *dev;
+
+	dma_addr_t setup_packet_dma_handle;
+	dma_addr_t transfer_buffer_dma_handle;
+
+	struct uhci_qh *qh;		/* QH for this URB */
+	struct list_head td_list;	/* P: urb->lock */
+
+	int fsbr : 1;			/* URB turned on FSBR */
+	int fsbr_timeout : 1;		/* URB timed out on FSBR */
+	int queued : 1;			/* QH was queued (not linked in) */
+	int short_control_packet : 1;	/* If we get a short packet during */
+					/*  a control transfer, retrigger */
+					/*  the status phase */
+
+	int status;			/* Final status */
+
+	unsigned long inserttime;	/* In jiffies */
+	unsigned long fsbrtime;		/* In jiffies */
+
+	struct list_head queue_list;	/* P: uhci->frame_list_lock */
+	struct list_head complete_list;	/* P: uhci->complete_list_lock */
+};
+
+/*
+ * Locking in uhci.c
+ *
+ * spinlocks are used extensively to protect the many lists and data
+ * structures we have. It's not that pretty, but it's necessary. We
+ * need to be done with all of the locks (except complete_list_lock) when
+ * we call urb->complete. I've tried to make it simple enough so I don't
+ * have to spend hours racking my brain trying to figure out if the
+ * locking is safe.
+ *
+ * Here's the safe locking order to prevent deadlocks:
+ *
+ * #1 uhci->urb_list_lock
+ * #2 urb->lock
+ * #3 uhci->urb_remove_list_lock, uhci->frame_list_lock, 
+ *   uhci->qh_remove_list_lock
+ * #4 uhci->complete_list_lock
+ *
+ * If you're going to grab 2 or more locks at once, ALWAYS grab the lock
+ * at the lowest level FIRST and NEVER grab locks at the same level at the
+ * same time.
+ * 
+ * So, if you need uhci->urb_list_lock, grab it before you grab urb->lock
+ */
+
+#endif
+
diff -Nru a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/host/uhci-hub.c	Sat May 18 00:46:15 2002
@@ -0,0 +1,184 @@
+/*
+ * Universal Host Controller Interface driver for USB.
+ *
+ * Maintainer: Johannes Erdfelt <johannes@erdfelt.com>
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
+ * (C) Copyright 1999 Randy Dunlap
+ * (C) Copyright 1999 Georg Acher, acher@in.tum.de
+ * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
+ * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
+ */
+
+static __u8 root_hub_hub_des[] =
+{
+	0x09,			/*  __u8  bLength; */
+	0x29,			/*  __u8  bDescriptorType; Hub-descriptor */
+	0x02,			/*  __u8  bNbrPorts; */
+	0x00,			/* __u16  wHubCharacteristics; */
+	0x00,
+	0x01,			/*  __u8  bPwrOn2pwrGood; 2ms */
+	0x00,			/*  __u8  bHubContrCurrent; 0 mA */
+	0x00,			/*  __u8  DeviceRemovable; *** 7 Ports max *** */
+	0xff			/*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
+};
+
+static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+	unsigned int io_addr = uhci->io_addr;
+	int i, len = 1;
+
+	*buf = 0;
+	for (i = 0; i < uhci->rh_numports; i++) {
+		*buf |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0);
+		len = (i + 1) / 8 + 1;
+	}
+
+	return !!*buf;
+}
+
+#define OK(x)			len = (x); break
+
+#define CLR_RH_PORTSTAT(x) \
+	status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \
+	status = (status & 0xfff5) & ~(x); \
+	outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1))
+
+#define SET_RH_PORTSTAT(x) \
+	status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \
+	status = (status & 0xfff5) | (x); \
+	outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1))
+
+
+// FIXME: Shouldn't this return the length of the data too?
+static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+			u16 wIndex, char *buf, u16 wLength)
+{
+	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+	int i, status, retval = 0, len = 0;
+	unsigned int io_addr = uhci->io_addr;
+	__u16 cstatus;
+	char c_p_r[8];
+
+	for (i = 0; i < 8; i++)
+		c_p_r[i] = 0;
+
+	switch (typeReq) {
+		/* Request Destination:
+		   without flags: Device,
+		   RH_INTERFACE: interface,
+		   RH_ENDPOINT: endpoint,
+		   RH_CLASS means HUB here,
+		   RH_OTHER | RH_CLASS  almost ever means HUB_PORT here
+		*/
+
+	case GetHubStatus:
+		*(__u32 *)buf = cpu_to_le32(0);
+		OK(4);		/* hub power */
+	case GetPortStatus:
+		status = inw(io_addr + USBPORTSC1 + 2 * (wIndex - 1));
+		cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) |
+			((status & USBPORTSC_PEC) >> (3 - 1)) |
+			(c_p_r[wIndex - 1] << (0 + 4));
+			status = (status & USBPORTSC_CCS) |
+			((status & USBPORTSC_PE) >> (2 - 1)) |
+			((status & USBPORTSC_SUSP) >> (12 - 2)) |
+			((status & USBPORTSC_PR) >> (9 - 4)) |
+			(1 << 8) |      /* power on */
+			((status & USBPORTSC_LSDA) << (-8 + 9));
+
+		*(__u16 *)buf = cpu_to_le16(status);
+		*(__u16 *)(buf + 2) = cpu_to_le16(cstatus);
+		OK(4);
+	case SetHubFeature:
+		switch (wValue) {
+		case C_HUB_OVER_CURRENT:
+		case C_HUB_LOCAL_POWER:
+			break;
+		default:
+			goto err;
+		}
+		break;
+	case ClearHubFeature:
+		switch (wValue) {
+		case C_HUB_OVER_CURRENT:
+			OK(0);	/* hub power over current */
+		default:
+			goto err;
+		}
+		break;
+	case SetPortFeature:
+		if (!wIndex || wIndex > uhci->rh_numports)
+			goto err;
+
+		switch (wValue) {
+		case USB_PORT_FEAT_SUSPEND:
+			SET_RH_PORTSTAT(USBPORTSC_SUSP);
+			OK(0);
+		case USB_PORT_FEAT_RESET:
+			SET_RH_PORTSTAT(USBPORTSC_PR);
+			mdelay(50);	/* USB v1.1 7.1.7.3 */
+			c_p_r[wIndex - 1] = 1;
+			CLR_RH_PORTSTAT(USBPORTSC_PR);
+			udelay(10);
+			SET_RH_PORTSTAT(USBPORTSC_PE);
+			mdelay(10);
+			SET_RH_PORTSTAT(0xa);
+			OK(0);
+		case USB_PORT_FEAT_POWER:
+			OK(0); /* port power ** */
+		case USB_PORT_FEAT_ENABLE:
+			SET_RH_PORTSTAT(USBPORTSC_PE);
+			OK(0);
+		default:
+			goto err;
+		}
+		break;
+	case ClearPortFeature:
+		if (!wIndex || wIndex > uhci->rh_numports)
+			goto err;
+
+		switch (wValue) {
+		case USB_PORT_FEAT_ENABLE:
+			CLR_RH_PORTSTAT(USBPORTSC_PE);
+			OK(0);
+		case USB_PORT_FEAT_C_ENABLE:
+			SET_RH_PORTSTAT(USBPORTSC_PEC);
+			OK(0);
+		case USB_PORT_FEAT_SUSPEND:
+			CLR_RH_PORTSTAT(USBPORTSC_SUSP);
+			OK(0);
+		case USB_PORT_FEAT_C_SUSPEND:
+			/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
+			OK(0);
+		case USB_PORT_FEAT_POWER:
+			OK(0);	/* port power */
+		case USB_PORT_FEAT_C_CONNECTION:
+			SET_RH_PORTSTAT(USBPORTSC_CSC);
+			OK(0);
+		case USB_PORT_FEAT_C_OVER_CURRENT:
+			OK(0);	/* port power over current */
+		case USB_PORT_FEAT_C_RESET:
+			c_p_r[wIndex - 1] = 0;
+			OK(0);
+		default:
+			goto err;
+		}
+		break;
+	case GetHubDescriptor:
+		len = min_t(unsigned int, wLength,
+			  min_t(unsigned int, sizeof(root_hub_hub_des), wLength));
+		memcpy(buf, root_hub_hub_des, len);
+		if (len > 2)
+			buf[2] = uhci->rh_numports;
+		OK(len);
+	default:
+err:
+		retval = -EPIPE;
+	}
+
+	return retval;
+}
+
diff -Nru a/drivers/usb/host/uhci.c b/drivers/usb/host/uhci.c
--- a/drivers/usb/host/uhci.c	Sat May 18 00:46:14 2002
+++ b/drivers/usb/host/uhci.c	Sat May 18 00:46:14 2002
@@ -101,27 +101,27 @@
 #define IDLE_TIMEOUT	(HZ / 20)	/* 50 ms */
 #define FSBR_DELAY	(HZ / 20)	/* 50 ms */
 
+/* When we timeout an idle transfer for FSBR, we'll switch it over to */
+/* depth first traversal. We'll do it in groups of this number of TD's */
+/* to make sure it doesn't hog all of the bandwidth */
+#define DEPTH_INTERVAL	5
+
 #define MAX_URB_LOOP	2048		/* Maximum number of linked URB's */
 
 /*
- * Only the USB core should call uhci_alloc_dev and uhci_free_dev
+ * Technically, updating td->status here is a race, but it's not really a
+ * problem. The worst that can happen is that we set the IOC bit again
+ * generating a spurios interrupt. We could fix this by creating another
+ * QH and leaving the IOC bit always set, but then we would have to play
+ * games with the FSBR code to make sure we get the correct order in all
+ * the cases. I don't think it's worth the effort
  */
-static int uhci_alloc_dev(struct usb_device *dev)
-{
-	return 0;
-}
-
-static int uhci_free_dev(struct usb_device *dev)
-{
-	return 0;
-}
-
 static inline void uhci_set_next_interrupt(struct uhci *uhci)
 {
 	unsigned long flags;
 
 	spin_lock_irqsave(&uhci->frame_list_lock, flags);
-	set_bit(TD_CTRL_IOC_BIT, &uhci->skel_term_td->status);
+	uhci->skel_term_td->status |= TD_CTRL_IOC;
 	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 }
 
@@ -130,7 +130,7 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(&uhci->frame_list_lock, flags);
-	clear_bit(TD_CTRL_IOC_BIT, &uhci->skel_term_td->status);
+	uhci->skel_term_td->status &= ~TD_CTRL_IOC;
 	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 }
 
@@ -165,7 +165,7 @@
 	INIT_LIST_HEAD(&td->list);
 	INIT_LIST_HEAD(&td->fl_list);
 
-	usb_inc_dev_use(dev);
+	usb_get_dev(dev);
 
 	return td;
 }
@@ -317,7 +317,7 @@
 		dbg("td is still in URB list!");
 
 	if (td->dev)
-		usb_dec_dev_use(td->dev);
+		usb_put_dev(td->dev);
 
 	pci_pool_free(uhci->td_pool, td, td->dma_handle);
 }
@@ -342,7 +342,7 @@
 	INIT_LIST_HEAD(&qh->list);
 	INIT_LIST_HEAD(&qh->remove_list);
 
-	usb_inc_dev_use(dev);
+	usb_get_dev(dev);
 
 	return qh;
 }
@@ -355,7 +355,7 @@
 		dbg("qh still in remove_list!");
 
 	if (qh->dev)
-		usb_dec_dev_use(qh->dev);
+		usb_put_dev(qh->dev);
 
 	pci_pool_free(uhci->qh_pool, qh, qh->dma_handle);
 }
@@ -475,9 +475,9 @@
 		tmp = tmp->next;
 
 		if (toggle)
-			set_bit(TD_TOKEN_TOGGLE, &td->info);
+			td->info |= TD_TOKEN_TOGGLE;
 		else
-			clear_bit(TD_TOKEN_TOGGLE, &td->info);
+			td->info &= ~TD_TOKEN_TOGGLE;
 
 		toggle ^= 1;
 	}
@@ -860,7 +860,7 @@
 			return -ENOMEM;
 
 		/* Alternate Data0/1 (start with Data1) */
-		destination ^= 1 << TD_TOKEN_TOGGLE;
+		destination ^= TD_TOKEN_TOGGLE;
 	
 		uhci_add_td_to_urb(urb, td);
 		uhci_fill_td(td, status, destination | ((pktsze - 1) << 21),
@@ -887,7 +887,7 @@
 	else
 		destination |= USB_PID_OUT;
 
-	destination |= 1 << TD_TOKEN_TOGGLE;		/* End in Data1 */
+	destination |= TD_TOKEN_TOGGLE;		/* End in Data1 */
 
 	status &= ~TD_CTRL_SPD;
 
@@ -956,14 +956,6 @@
 
 		tmp = tmp->next;
 
-		if (urbp->fsbr_timeout && (td->status & TD_CTRL_IOC) &&
-		    !(td->status & TD_CTRL_ACTIVE)) {
-			uhci_inc_fsbr(urb->dev->bus->hcpriv, urb);
-			urbp->fsbr_timeout = 0;
-			urbp->fsbrtime = jiffies;
-			clear_bit(TD_CTRL_IOC_BIT, &td->status);
-		}
-
 		status = uhci_status_bits(td->status);
 		if (status & TD_CTRL_ACTIVE)
 			return -EINPROGRESS;
@@ -1102,7 +1094,7 @@
 	if (!td)
 		return -ENOMEM;
 
-	destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE);
+	destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT);
 	destination |= ((urb->transfer_buffer_length - 1) << 21);
 
 	usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
@@ -1132,14 +1124,6 @@
 
 		tmp = tmp->next;
 
-		if (urbp->fsbr_timeout && (td->status & TD_CTRL_IOC) &&
-		    !(td->status & TD_CTRL_ACTIVE)) {
-			uhci_inc_fsbr(urb->dev->bus->hcpriv, urb);
-			urbp->fsbr_timeout = 0;
-			urbp->fsbrtime = jiffies;
-			clear_bit(TD_CTRL_IOC_BIT, &td->status);
-		}
-
 		status = uhci_status_bits(td->status);
 		if (status & TD_CTRL_ACTIVE)
 			return -EINPROGRESS;
@@ -1203,8 +1187,8 @@
 	td = list_entry(urbp->td_list.next, struct uhci_td, list);
 
 	td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC;
-	td->info &= ~(1 << TD_TOKEN_TOGGLE);
-	td->info |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE);
+	td->info &= ~TD_TOKEN_TOGGLE;
+	td->info |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT);
 	usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
 
 out:
@@ -1260,7 +1244,7 @@
 		uhci_fill_td(td, status, destination |
 			(((pktsze - 1) & UHCI_NULL_DATA_SIZE) << 21) |
 			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
-			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE),
+			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
 			data);
 
 		data += pktsze;
@@ -1288,7 +1272,7 @@
 		uhci_fill_td(td, status, destination |
 			(UHCI_NULL_DATA_SIZE << 21) |
 			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
-			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE),
+			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
 			data);
 
 		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
@@ -1508,7 +1492,7 @@
 	uhci = (struct uhci *)urb->dev->bus->hcpriv;
 
 	INIT_LIST_HEAD(&urb->urb_list);
-	usb_inc_dev_use(urb->dev);
+	usb_get_dev(urb->dev);
 
 	spin_lock_irqsave(&uhci->urb_list_lock, flags);
 	spin_lock(&urb->lock);
@@ -1519,7 +1503,7 @@
 		/* Since we can have problems on the out path */
 		spin_unlock(&urb->lock);
 		spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
-		usb_dec_dev_use(urb->dev);
+		usb_put_dev(urb->dev);
 		usb_put_urb(urb);
 
 		return ret;
@@ -1839,11 +1823,18 @@
 {
 	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
 	struct list_head *head, *tmp;
+	int count = 0;
 
 	uhci_dec_fsbr(uhci, urb);
 
 	urbp->fsbr_timeout = 1;
 
+	/*
+	 * Ideally we would want to fix qh->element as well, but it's
+	 * read/write by the HC, so that can introduce a race. It's not
+	 * really worth the hassle
+	 */
+
 	head = &urbp->td_list;
 	tmp = head->next;
 	while (tmp != head) {
@@ -1851,10 +1842,15 @@
 
 		tmp = tmp->next;
 
-		if (td->status & TD_CTRL_ACTIVE) {
-			set_bit(TD_CTRL_IOC_BIT, &td->status);
-			break;
-		}
+		/*
+		 * Make sure we don't do the last one (since it'll have the
+		 * TERM bit set) as well as we skip every so many TD's to
+		 * make sure it doesn't hog the bandwidth
+		 */
+		if (tmp != head && (count % DEPTH_INTERVAL) == (DEPTH_INTERVAL - 1))
+			td->link |= UHCI_PTR_DEPTH;
+
+		count++;
 	}
 
 	return 0;
@@ -1873,8 +1869,6 @@
 }
 
 struct usb_operations uhci_device_operations = {
-	allocate:		uhci_alloc_dev,
-	deallocate:		uhci_free_dev,
 	get_frame_number:	uhci_get_current_frame_number,
 	submit_urb:		uhci_submit_urb,
 	unlink_urb:		uhci_unlink_urb,
@@ -2382,7 +2376,7 @@
 		} else {
 			/* We decrement the usage count after we're done */
 			/*  with everything */
-			usb_dec_dev_use(dev);
+			usb_put_dev(dev);
 			usb_put_urb(urb);
 		}
 	}
diff -Nru a/drivers/usb/host/uhci.h b/drivers/usb/host/uhci.h
--- a/drivers/usb/host/uhci.h	Sat May 18 00:46:08 2002
+++ b/drivers/usb/host/uhci.h	Sat May 18 00:46:08 2002
@@ -100,7 +100,6 @@
 #define TD_CTRL_C_ERR_SHIFT	27
 #define TD_CTRL_LS		(1 << 26)	/* Low Speed Device */
 #define TD_CTRL_IOS		(1 << 25)	/* Isochronous Select */
-#define TD_CTRL_IOC_BIT		24
 #define TD_CTRL_IOC		(1 << 24)	/* Interrupt on Complete */
 #define TD_CTRL_ACTIVE		(1 << 23)	/* TD Active */
 #define TD_CTRL_STALLED		(1 << 22)	/* TD Stalled */
@@ -120,13 +119,14 @@
 /*
  * for TD <info>: (a.k.a. Token)
  */
-#define TD_TOKEN_TOGGLE		19
+#define TD_TOKEN_TOGGLE_SHIFT	19
+#define TD_TOKEN_TOGGLE		(1 << 19)
 #define TD_TOKEN_PID_MASK	0xFF
 #define TD_TOKEN_EXPLEN_MASK	0x7FF		/* expected length, encoded as n - 1 */
 
 #define uhci_maxlen(token)	((token) >> 21)
 #define uhci_expected_length(info) (((info >> 21) + 1) & TD_TOKEN_EXPLEN_MASK) /* 1-based */
-#define uhci_toggle(token)	(((token) >> TD_TOKEN_TOGGLE) & 1)
+#define uhci_toggle(token)	(((token) >> TD_TOKEN_TOGGLE_SHIFT) & 1)
 #define uhci_endpoint(token)	(((token) >> 15) & 0xf)
 #define uhci_devaddr(token)	(((token) >> 8) & 0x7f)
 #define uhci_devep(token)	(((token) >> 8) & 0x7ff)
diff -Nru a/drivers/usb/host/usb-ohci.c b/drivers/usb/host/usb-ohci.c
--- a/drivers/usb/host/usb-ohci.c	Sat May 18 00:46:08 2002
+++ b/drivers/usb/host/usb-ohci.c	Sat May 18 00:46:08 2002
@@ -210,7 +210,7 @@
 		}
 
 		urb_free_priv ((struct ohci *)urb->dev->bus->hcpriv, urb_priv);
-		usb_dec_dev_use (urb->dev);
+		usb_put_dev (urb->dev);
 		urb->dev = NULL;
 		usb_put_urb (urb);
 	}
@@ -563,7 +563,7 @@
 	/* increment the reference count of the urb, as we now also control it */
 	urb = usb_get_urb (urb);
 
-	usb_inc_dev_use (urb->dev);
+	usb_get_dev (urb->dev);
 	ohci = (ohci_t *) urb->dev->bus->hcpriv;
 	
 #ifdef DEBUG
@@ -577,14 +577,14 @@
 	/* when controller's hung, permit only roothub cleanup attempts
 	 * such as powering down ports */
 	if (ohci->disabled) {
-		usb_dec_dev_use (urb->dev);	
+		usb_put_dev (urb->dev);	
 		usb_put_urb (urb);
 		return -ESHUTDOWN;
 	}
 
 	/* every endpoint has a ed, locate and fill it */
 	if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) {
-		usb_dec_dev_use (urb->dev);	
+		usb_put_dev (urb->dev);	
 		usb_put_urb (urb);
 		return -ENOMEM;
 	}
@@ -606,7 +606,7 @@
 		case PIPE_ISOCHRONOUS: /* number of packets from URB */
 			size = urb->number_of_packets;
 			if (size <= 0) {
-				usb_dec_dev_use (urb->dev);	
+				usb_put_dev (urb->dev);	
 				usb_put_urb (urb);
 				return -EINVAL;
 			}
@@ -627,7 +627,7 @@
 	/* allocate the private part of the URB */
 	urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *), mem_flags);
 	if (!urb_priv) {
-		usb_dec_dev_use (urb->dev);	
+		usb_put_dev (urb->dev);	
 		usb_put_urb (urb);
 		return -ENOMEM;
 	}
@@ -645,7 +645,7 @@
 			urb_priv->length = i;
 			urb_free_priv (ohci, urb_priv);
 			spin_unlock_irqrestore (&usb_ed_lock, flags);
-			usb_dec_dev_use (urb->dev);	
+			usb_put_dev (urb->dev);	
 			usb_put_urb (urb);
 			return -ENOMEM;
 		}
@@ -654,7 +654,7 @@
 	if (ed->state == ED_NEW || (ed->state & ED_DEL)) {
 		urb_free_priv (ohci, urb_priv);
 		spin_unlock_irqrestore (&usb_ed_lock, flags);
-		usb_dec_dev_use (urb->dev);	
+		usb_put_dev (urb->dev);	
 		usb_put_urb (urb);
 		return -EINVAL;
 	}
@@ -677,7 +677,7 @@
 			if (bustime < 0) {
 				urb_free_priv (ohci, urb_priv);
 				spin_unlock_irqrestore (&usb_ed_lock, flags);
-				usb_dec_dev_use (urb->dev);	
+				usb_put_dev (urb->dev);	
 				usb_put_urb (urb);
 				return bustime;
 			}
@@ -802,7 +802,7 @@
 					return -ETIMEDOUT;
 				}
 			} else {
-				/* usb_dec_dev_use done in dl_del_list() */
+				/* usb_put_dev done in dl_del_list() */
 				urb->status = -EINPROGRESS;
 				spin_unlock_irqrestore (&usb_ed_lock, flags);
 				return -EINPROGRESS;
@@ -2109,7 +2109,7 @@
 #endif
 
 	urb->hcpriv = NULL;
-	usb_dec_dev_use (usb_dev);
+	usb_put_dev (usb_dev);
 	urb->dev = NULL;
 	if (urb->complete)
 	    	urb->complete (urb);
@@ -2129,7 +2129,7 @@
 		ohci->rh.urb = NULL;
 
 		urb->hcpriv = NULL;
-		usb_dec_dev_use(urb->dev);
+		usb_put_dev (urb->dev);
 		urb->dev = NULL;
 		if (urb->transfer_flags & USB_ASYNC_UNLINK) {
 			urb->status = -ECONNRESET;
diff -Nru a/drivers/usb/host/usb-uhci-dbg.c b/drivers/usb/host/usb-uhci-dbg.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/host/usb-uhci-dbg.c	Sat May 18 00:46:15 2002
@@ -0,0 +1,151 @@
+/*  
+    UHCI HCD (Host Controller Driver) for USB, debugging calls
+   
+    (c) 1999-2002 
+    Georg Acher      +    Deti Fliegl    +    Thomas Sailer
+    georg@acher.org      deti@fliegl.de   sailer@ife.ee.ethz.ch
+  
+    $Id: usb-uhci-dbg.c,v 1.1 2002/05/14 20:36:57 acher Exp $
+*/
+
+#ifdef DEBUG
+static void __attribute__((__unused__)) uhci_show_qh (puhci_desc_t qh)
+{
+	if (qh->type != QH_TYPE) {
+		dbg("qh has not QH_TYPE");
+		return;
+	}
+	dbg("QH @ %p/%08llX:", qh, (unsigned long long)qh->dma_addr);
+
+	if (qh->hw.qh.head & UHCI_PTR_TERM)
+		dbg("    Head Terminate");
+	else 
+		dbg("    Head: %s @ %08X",
+		    (qh->hw.qh.head & UHCI_PTR_QH?"QH":"TD"),
+		    qh->hw.qh.head & ~UHCI_PTR_BITS);
+
+	if (qh->hw.qh.element & UHCI_PTR_TERM)
+		dbg("    Element Terminate");
+	else 
+		dbg("    Element: %s @ %08X",
+		    (qh->hw.qh.element & UHCI_PTR_QH?"QH":"TD"),
+		    qh->hw.qh.element & ~UHCI_PTR_BITS);
+}
+#endif
+
+#if 0
+static void uhci_show_td (puhci_desc_t td)
+{
+	char *spid;
+	
+	switch (td->hw.td.info & 0xff) {
+	case USB_PID_SETUP:
+		spid = "SETUP";
+		break;
+	case USB_PID_OUT:
+		spid = " OUT ";
+		break;
+	case USB_PID_IN:
+		spid = " IN  ";
+		break;
+	default:
+		spid = "  ?  ";
+		break;
+	}
+
+	warn("  TD @ %p/%08X, MaxLen=%02x DT%d EP=%x Dev=%x PID=(%s) buf=%08x",
+	     td, td->dma_addr,
+	     td->hw.td.info >> 21,
+	     ((td->hw.td.info >> 19) & 1),
+	     (td->hw.td.info >> 15) & 15,
+	     (td->hw.td.info >> 8) & 127,
+	     spid,
+	     td->hw.td.buffer);
+
+	warn("    Len=%02x e%d %s%s%s%s%s%s%s%s%s%s",
+	     td->hw.td.status & 0x7ff,
+	     ((td->hw.td.status >> 27) & 3),
+	     (td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "",
+	     (td->hw.td.status & TD_CTRL_LS) ? "LS " : "",
+	     (td->hw.td.status & TD_CTRL_IOC) ? "IOC " : "",
+	     (td->hw.td.status & TD_CTRL_ACTIVE) ? "Active " : "",
+	     (td->hw.td.status & TD_CTRL_STALLED) ? "Stalled " : "",
+	     (td->hw.td.status & TD_CTRL_DBUFERR) ? "DataBufErr " : "",
+	     (td->hw.td.status & TD_CTRL_BABBLE) ? "Babble " : "",
+	     (td->hw.td.status & TD_CTRL_NAK) ? "NAK " : "",
+	     (td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
+	     (td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : ""
+		);
+
+	if (td->hw.td.link & UHCI_PTR_TERM)
+		warn("   TD Link Terminate");
+	else 
+		warn("    Link points to %s @ %08x, %s",
+		     (td->hw.td.link & UHCI_PTR_QH?"QH":"TD"),
+		     td->hw.td.link & ~UHCI_PTR_BITS,
+		     (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : "Breadth first"));
+}
+#endif
+
+#ifdef DEBUG
+static void __attribute__((__unused__)) uhci_show_sc (int port, unsigned short status)
+{
+	dbg("  stat%d     =     %04x   %s%s%s%s%s%s%s%s",
+	     port,
+	     status,
+	     (status & USBPORTSC_SUSP) ? "PortSuspend " : "",
+	     (status & USBPORTSC_PR) ? "PortReset " : "",
+	     (status & USBPORTSC_LSDA) ? "LowSpeed " : "",
+	     (status & USBPORTSC_RD) ? "ResumeDetect " : "",
+	     (status & USBPORTSC_PEC) ? "EnableChange " : "",
+	     (status & USBPORTSC_PE) ? "PortEnabled " : "",
+	     (status & USBPORTSC_CSC) ? "ConnectChange " : "",
+	     (status & USBPORTSC_CCS) ? "PortConnected " : "");
+}
+
+void uhci_show_status (struct uhci_hcd *uhci)
+{
+	unsigned int io_addr = (int)uhci->hcd.regs;
+	unsigned short usbcmd, usbstat, usbint, usbfrnum;
+	unsigned int flbaseadd;
+	unsigned char sof;
+	unsigned short portsc1, portsc2;
+
+	usbcmd = inw (io_addr + 0);
+	usbstat = inw (io_addr + 2);
+	usbint = inw (io_addr + 4);
+	usbfrnum = inw (io_addr + 6);
+	flbaseadd = inl (io_addr + 8);
+	sof = inb (io_addr + 12);
+	portsc1 = inw (io_addr + 16);
+	portsc2 = inw (io_addr + 18);
+
+	dbg("  usbcmd    =     %04x   %s%s%s%s%s%s%s%s",
+	     usbcmd,
+	     (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ",
+	     (usbcmd & USBCMD_CF) ? "CF " : "",
+	     (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "",
+	     (usbcmd & USBCMD_FGR) ? "FGR " : "",
+	     (usbcmd & USBCMD_EGSM) ? "EGSM " : "",
+	     (usbcmd & USBCMD_GRESET) ? "GRESET " : "",
+	     (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "",
+	     (usbcmd & USBCMD_RS) ? "RS " : "");
+
+	dbg("  usbstat   =     %04x   %s%s%s%s%s%s",
+	     usbstat,
+	     (usbstat & USBSTS_HCH) ? "HCHalted " : "",
+	     (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "",
+	     (usbstat & USBSTS_HSE) ? "HostSystemError " : "",
+	     (usbstat & USBSTS_RD) ? "ResumeDetect " : "",
+	     (usbstat & USBSTS_ERROR) ? "USBError " : "",
+	     (usbstat & USBSTS_USBINT) ? "USBINT " : "");
+
+	dbg("  usbint    =     %04x", usbint);
+	dbg("  usbfrnum  =   (%d)%03x", (usbfrnum >> 10) & 1,
+	     0xfff & (4 * (unsigned int) usbfrnum));
+	dbg("  flbaseadd = %08x", flbaseadd);
+	dbg("  sof       =       %02x", sof);
+	uhci_show_sc (1, portsc1);
+	uhci_show_sc (2, portsc2);
+}
+#endif
diff -Nru a/drivers/usb/host/usb-uhci-hcd.c b/drivers/usb/host/usb-uhci-hcd.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/host/usb-uhci-hcd.c	Sat May 18 00:46:15 2002
@@ -0,0 +1,645 @@
+/*  
+    UHCI HCD (Host Controller Driver) for USB, main part for HCD frame
+   
+    (c) 1999-2002 
+    Georg Acher      +    Deti Fliegl    +    Thomas Sailer
+    georg@acher.org      deti@fliegl.de   sailer@ife.ee.ethz.ch
+   
+    with the help of
+    David Brownell, david-b@pacbell.net 
+    Adam Richter, adam@yggdrasil.com
+    Roman Weissgaerber, weissg@vienna.at    
+    
+    HW-initalization based on material of
+    Randy Dunlap + Johannes Erdfelt + Gregory P. Smith + Linus Torvalds 
+
+    $Id: usb-uhci-hcd.c,v 1.1 2002/05/14 20:36:57 acher Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>  /* for in_interrupt () */
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+#include <linux/usb.h>
+
+#define CONFIG_USB_DEBUG
+#ifdef CONFIG_USB_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+
+#include "../core/hcd.h"
+#include "usb-uhci-hcd.h"
+
+#define DRIVER_VERSION "$Revision: 1.1 $"
+#define DRIVER_AUTHOR "Georg Acher, Deti Fliegl, Thomas Sailer"
+#define DRIVER_DESC "USB 1.1 Universal Host Controller Interface driver (HCD)"
+/*--------------------------------------------------------------------------*/
+// Values you may tweak
+
+/* CONFIG_USB_UHCI_HIGH_BANDWITH turns on Full Speed Bandwidth
+ * Reclamation: feature that puts loop on descriptor loop when
+ * there's some transfer going on. With FSBR, USB performance
+ * is optimal, but PCI can be slowed down up-to 5 times, slowing down
+ * system performance (eg. framebuffer devices).
+ */
+#define CONFIG_USB_UHCI_HIGH_BANDWIDTH 
+
+/* *_DEPTH_FIRST puts descriptor in depth-first mode. This has
+ * somehow similar effect to FSBR (higher speed), but does not
+ * slow PCI down. OTOH USB performace is slightly slower than
+ * in FSBR case and single device could hog whole USB, starving
+ * other devices.
+ */
+#define USE_CTRL_DEPTH_FIRST 0  // 0: Breadth first, 1: Depth first
+#define USE_BULK_DEPTH_FIRST 0  // 0: Breadth first, 1: Depth first
+
+/* Turning off both CONFIG_USB_UHCI_HIGH_BANDWITH and *_DEPTH_FIRST
+ * will lead to <64KB/sec performance over USB for bulk transfers targeting
+ * one device's endpoint. You probably do not want to do that.
+ */
+
+// stop bandwidth reclamation after (roughly) 50ms
+#define IDLE_TIMEOUT  (HZ/20)
+
+// Suppress HC interrupt error messages for 5s
+#define ERROR_SUPPRESSION_TIME (HZ*5)
+
+// HC watchdog
+#define WATCHDOG_TIMEOUT (4*HZ)
+#define MAX_REANIMATIONS 5
+
+#define DEBUG_SYMBOLS
+#ifdef DEBUG_SYMBOLS
+        #ifndef EXPORT_SYMTAB
+                #define EXPORT_SYMTAB
+        #endif
+#endif
+
+#define queue_dbg dbg 
+#define async_dbg dbg
+#define init_dbg dbg
+
+/*--------------------------------------------------------------------------*/
+//                   NO serviceable parts below!
+/*--------------------------------------------------------------------------*/
+
+// How much URBs with ->next are walked
+#define MAX_NEXT_COUNT 2048
+
+static struct uhci *devs = NULL;
+
+/* used by userspace UHCI data structure dumper */
+struct uhci **uhci_devices = &devs;
+
+/* A few prototypes */
+static int uhci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb);
+static int hc_reset (struct uhci_hcd *uhci);
+static void uhci_stop (struct usb_hcd *hcd);
+static int process_transfer (struct uhci_hcd *uhci, struct urb *urb, int mode);
+static int process_iso (struct uhci_hcd *uhci, struct urb *urb, int mode);
+static int process_interrupt (struct uhci_hcd *uhci, struct urb *urb, int mode);
+static int process_urb (struct uhci_hcd *uhci, struct list_head *p);
+static int uhci_urb_enqueue (struct usb_hcd *hcd, struct urb *urb, int mem_flags);
+static int hc_defibrillate(struct uhci_hcd * uhci);
+static int hc_irq_run(struct uhci_hcd *uhci);
+
+#include "usb-uhci-dbg.c"
+#include "usb-uhci-mem.c"
+#include "usb-uhci-hub.c"
+#include "usb-uhci-q.c"
+
+#define PIPESTRING(x) (x==PIPE_BULK?"Bulk":(x==PIPE_INTERRUPT?"Interrupt":(x==PIPE_CONTROL?"Control":"Iso")))
+
+/*--------------------------------------------------------------------------*/
+static int uhci_urb_enqueue (struct usb_hcd *hcd, struct urb *urb, int mem_flags)
+{
+	struct uhci_hcd	*uhci = hcd_to_uhci (hcd);
+	urb_priv_t *urb_priv;
+	int ret = 0, type;
+	unsigned long flags;
+	struct urb *queued_urb=NULL;
+	int bustime;
+	type = usb_pipetype (urb->pipe);
+
+//	err("submit_urb: scheduling %p (%s), tb %p, len %i", urb, 
+//	PIPESTRING(type),urb->transfer_buffer,urb->transfer_buffer_length);
+
+	if (uhci->need_init) {
+		if (in_interrupt())
+			return -ESHUTDOWN;
+
+		spin_lock_irqsave (&uhci->urb_list_lock, flags);
+		ret = hc_defibrillate(uhci);
+		spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
+
+		if (ret)
+			return ret;		
+	}
+
+	if (!uhci->running)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave (&uhci->urb_list_lock, flags);
+
+
+	queued_urb = search_dev_ep (uhci, urb); // returns already queued urb for that pipe
+
+	if (queued_urb) {
+		queue_dbg("found bulk urb %p\n", queued_urb);
+
+		if (( type != PIPE_BULK) ||
+		    ((type == PIPE_BULK) &&
+		     (!(urb->transfer_flags & USB_QUEUE_BULK) || !(queued_urb->transfer_flags & USB_QUEUE_BULK)))) {
+			spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
+			err("ENXIO (%s)  %08x, flags %x, urb %p, burb %p, propably device driver bug...", 
+			    PIPESTRING(type),
+			    urb->pipe,urb->transfer_flags,urb,queued_urb);
+			return -ENXIO;	// urb already queued
+		}
+	}
+
+	urb_priv = uhci_alloc_priv(mem_flags);
+
+	if (!urb_priv) {
+		spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
+		return -ENOMEM;
+	}
+
+	urb->hcpriv = urb_priv;
+	urb_priv->urb=urb;
+	INIT_LIST_HEAD (&urb_priv->desc_list);
+	
+	if (type == PIPE_CONTROL)
+		urb_priv->setup_packet_dma = pci_map_single(uhci->uhci_pci, urb->setup_packet,
+							    sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
+
+	if (urb->transfer_buffer_length)
+		urb_priv->transfer_buffer_dma = pci_map_single(uhci->uhci_pci,
+							       urb->transfer_buffer,
+							       urb->transfer_buffer_length,
+							       usb_pipein(urb->pipe) ?
+							       PCI_DMA_FROMDEVICE :
+							       PCI_DMA_TODEVICE);
+
+	// for bulk queuing it is essential that interrupts are disabled until submission
+	// all other type enable interrupts again
+	switch (type) {
+	case PIPE_BULK:
+		if (queued_urb) {
+			while (((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb)  // find last queued bulk
+				queued_urb=((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb;
+				
+			((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb=urb;
+		}
+		atomic_inc (&uhci->avoid_bulk);
+		ret = uhci_submit_bulk_urb (uhci, urb, queued_urb);
+		atomic_dec (&uhci->avoid_bulk);
+		spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
+		break;
+
+	case PIPE_ISOCHRONOUS:	
+		spin_unlock_irqrestore (&uhci->urb_list_lock, flags);		
+
+		if (urb->bandwidth == 0) {      /* not yet checked/allocated */
+			bustime = usb_check_bandwidth (urb->dev, urb);
+			if (bustime < 0) 
+				ret = bustime;
+			else {
+				ret = uhci_submit_iso_urb(uhci, urb, mem_flags);
+				if (ret == 0)
+					usb_claim_bandwidth (urb->dev, urb, bustime, 1);
+			}
+		} else {        /* bandwidth is already set */
+			ret = uhci_submit_iso_urb(uhci, urb, mem_flags);
+		}
+		break;
+
+	case PIPE_INTERRUPT:
+		spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
+		if (urb->bandwidth == 0) {      /* not yet checked/allocated */
+			bustime = usb_check_bandwidth (urb->dev, urb);
+			if (bustime < 0)
+				ret = bustime;
+			else {
+				ret = uhci_submit_int_urb(uhci, urb);
+				if (ret == 0)
+					usb_claim_bandwidth (urb->dev, urb, bustime, 0);
+			}
+		} else {        /* bandwidth is already set */
+			ret = uhci_submit_int_urb(uhci, urb);
+		}
+		break;
+
+	case PIPE_CONTROL:
+		spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
+		ret = uhci_submit_control_urb (uhci, urb);
+		break;
+
+	default:	
+		spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
+		ret = -EINVAL;
+	}
+
+//	err("submit_urb: scheduled with ret: %d", ret);
+	
+	if (ret != 0)
+ 		uhci_free_priv(uhci, urb, urb_priv);
+
+	return ret;
+}
+/*--------------------------------------------------------------------------*/
+static int uhci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
+{
+	unsigned long flags=0;
+	struct uhci_hcd *uhci;
+
+	dbg("uhci_urb_dequeue called for %p",urb);
+	
+	uhci = hcd_to_uhci (hcd);
+
+	if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+		int ret;
+       		spin_lock_irqsave (&uhci->urb_list_lock, flags);
+		ret = uhci_unlink_urb_async(uhci, urb, UNLINK_ASYNC_STORE_URB);
+		spin_unlock_irqrestore (&uhci->urb_list_lock, flags);	
+		return ret;
+	}
+	else
+		return uhci_unlink_urb_sync(uhci, urb);
+}
+/*--------------------------------------------------------------------------*/
+static int uhci_get_frame (struct usb_hcd *hcd)
+{
+	struct uhci_hcd	*uhci = hcd_to_uhci (hcd);
+	return inw ((int)uhci->hcd.regs + USBFRNUM);
+}
+
+/*--------------------------------------------------------------------------*/
+//             Init and shutdown functions for HW 
+/*--------------------------------------------------------------------------*/
+static int hc_reset (struct uhci_hcd *uhci)
+{
+	unsigned int io_addr = (int)uhci->hcd.regs;
+
+	uhci->apm_state = 0;
+	uhci->running = 0;
+	outw (USBCMD_GRESET, io_addr + USBCMD);
+	uhci_wait_ms (50);   /* Global reset for 50ms */
+	outw (0, io_addr + USBCMD);
+	uhci_wait_ms (10);	
+	return 0;
+}
+/*--------------------------------------------------------------------------*/
+static int hc_irq_run(struct uhci_hcd *uhci)
+{
+	unsigned int io_addr = (int)uhci->hcd.regs;
+	/* Turn on all interrupts */
+	outw (USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR);
+
+	/* Start at frame 0 */
+	outw (0, io_addr + USBFRNUM);
+	outl (uhci->framelist_dma, io_addr + USBFLBASEADD);
+
+	/* Run and mark it configured with a 64-byte max packet */
+	outw (USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
+	uhci->apm_state = 1;
+	uhci->running = 1;
+	uhci->last_hcd_irq = jiffies+4*HZ;
+	return 0;
+}
+/*--------------------------------------------------------------------------*/
+static int hc_start (struct uhci_hcd *uhci)
+{
+	unsigned int io_addr = (int)uhci->hcd.regs;
+	int timeout = 10;
+	struct usb_device	*udev;
+	init_dbg("hc_start uhci %p",uhci);
+	/*
+	 * Reset the HC - this will force us to get a
+	 * new notification of any already connected
+	 * ports due to the virtual disconnect that it
+	 * implies.
+	 */
+	outw (USBCMD_HCRESET, io_addr + USBCMD);
+
+	while (inw (io_addr + USBCMD) & USBCMD_HCRESET) {
+		if (!--timeout) {
+			err("USBCMD_HCRESET timed out!");
+			break;
+		}
+		udelay(1);
+	}
+	
+	hc_irq_run(uhci);
+
+	/* connect the virtual root hub */
+	uhci->hcd.self.root_hub = udev = usb_alloc_dev (NULL, &uhci->hcd.self);
+	uhci->hcd.state = USB_STATE_READY;
+	if (!udev) {
+	    uhci->running = 0;
+// FIXME cleanup
+	    return -ENOMEM;
+	}
+
+	usb_connect (udev);
+	udev->speed = USB_SPEED_FULL;
+	if (usb_register_root_hub (udev, &uhci->hcd.pdev->dev) != 0) {
+		usb_free_dev (udev); 
+		uhci->running = 0;
+// FIXME cleanup
+		return -ENODEV;
+	}
+	
+	return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+// Start up UHCI, find ports, init DMA lists
+
+static int __devinit uhci_start (struct usb_hcd *hcd)
+{
+	struct uhci_hcd	*uhci = hcd_to_uhci (hcd);
+	int ret;
+	int io_addr=(int)hcd->regs, io_size=0x20; // FIXME
+	
+	init_dbg("uhci_start hcd %p uhci %p, pdev %p",hcd,uhci,hcd->pdev);
+	/* disable legacy emulation, Linux takes over... */
+	pci_write_config_word (hcd->pdev, USBLEGSUP, 0);
+
+	/* UHCI specs says devices must have 2 ports, but goes on to say */
+	/* they may have more but give no way to determine how many they */
+	/* have, so default to 2 */
+	/* According to the UHCI spec, Bit 7 is always set to 1. So we try */
+	/* to use this to our advantage */
+
+	for (uhci->maxports = 0; uhci->maxports < (io_size - 0x10) / 2; uhci->maxports++) {
+		unsigned int portstatus;
+
+		portstatus = inw (io_addr + 0x10 + (uhci->maxports * 2));
+		dbg("port %i, adr %x status %x", uhci->maxports,
+			io_addr + 0x10 + (uhci->maxports * 2), portstatus);
+		if (!(portstatus & 0x0080))
+			break;
+	}
+	warn("Detected %d ports", uhci->maxports);
+	
+	if (uhci->maxports < 2 || uhci->maxports > 8) {
+		dbg("Port count misdetected, forcing to 2 ports");
+		uhci->maxports = 2;
+	}	       
+
+	ret=init_skel(uhci);
+	if (ret)
+		return ret;
+
+	hc_reset (uhci);
+
+	if (hc_start (uhci) < 0) {
+		err ("can't start %s", uhci->hcd.self.bus_name);
+		uhci_stop (hcd);
+		return -EBUSY;
+	}
+
+	// Enable PIRQ
+	pci_write_config_word (hcd->pdev, USBLEGSUP, USBLEGSUP_DEFAULT);
+
+	set_td_ioc(uhci->td128ms); // start watchdog interrupt
+	uhci->last_hcd_irq=jiffies+5*HZ;
+
+	return 0;	
+}
+/*--------------------------------------------------------------------------*/
+static void uhci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
+{
+	dbg("uhci_free_config for dev %p", udev);
+	uhci_unlink_urbs (hcd_to_uhci (hcd), udev, 0);  // Forced unlink of remaining URBs
+}
+/*--------------------------------------------------------------------------*/
+static void uhci_stop (struct usb_hcd *hcd)
+{
+	struct uhci_hcd	*uhci = hcd_to_uhci (hcd);
+	
+	init_dbg("%s: stop controller", hcd->bus_name);
+	
+	uhci->running=0;
+	hc_reset (uhci);
+	wait_ms (1);
+	uhci_unlink_urbs (uhci, 0, CLEAN_FORCED);  // Forced unlink of remaining URBs
+	uhci_cleanup_unlink (uhci, CLEAN_FORCED);  // force cleanup of async killed URBs
+	cleanup_skel (uhci);
+}
+/*--------------------------------------------------------------------------*/
+//                  UHCI INTERRUPT PROCESSING
+/*--------------------------------------------------------------------------*/
+static void uhci_irq (struct usb_hcd *hcd)
+{	
+	struct uhci_hcd	*uhci = hcd_to_uhci (hcd);
+	unsigned int io_addr = (int)hcd->regs;
+	unsigned short status;
+	struct list_head *p, *p2;
+	int restarts, work_done;
+
+	/*
+	 * Read the interrupt status, and write it back to clear the
+	 * interrupt cause
+	 */
+
+	status = inw (io_addr + USBSTS);
+
+	if (!status)		/* shared interrupt, not mine */
+		return;
+
+	dbg("interrupt");
+
+	uhci->last_hcd_irq=jiffies;  // for watchdog
+
+	if (status != 1) {
+		// Avoid too much error messages at a time
+		if (time_after(jiffies, uhci->last_error_time + ERROR_SUPPRESSION_TIME)) {
+			warn("interrupt, status %x, frame# %i", status, 
+			     UHCI_GET_CURRENT_FRAME(uhci));
+			uhci->last_error_time = jiffies;
+		}
+		
+		// remove host controller halted state
+		if ((status&0x20) && (uhci->running)) {
+			err("Host controller halted, waiting for timeout.");
+//			outw (USBCMD_RS | inw(io_addr + USBCMD), io_addr + USBCMD);
+		}
+		//uhci_show_status (s);
+	}
+	/*
+	 * traverse the list in *reverse* direction, because new entries
+	 * may be added at the end.
+	 * also, because process_urb may unlink the current urb,
+	 * we need to advance the list before
+	 * New: check for max. workload and restart count
+	 */
+
+	spin_lock (&uhci->urb_list_lock);
+
+	restarts=0;
+	work_done=0;
+
+restart:
+	uhci->unlink_urb_done=0;
+	p = uhci->urb_list.prev;	
+
+	while (p != &uhci->urb_list && (work_done < 1024)) {
+		p2 = p;
+		p = p->prev;
+
+		process_urb (uhci, p2);
+		
+		work_done++;
+
+		if (uhci->unlink_urb_done) {
+			uhci->unlink_urb_done=0;
+			restarts++;
+			
+			if (restarts<16)	// avoid endless restarts
+				goto restart;
+			else 
+				break;
+		}
+	}
+	if (time_after(jiffies, uhci->timeout_check + (HZ/30)))
+		uhci_check_timeouts(uhci);
+
+	clean_descs(uhci, CLEAN_NOT_FORCED);
+	uhci_cleanup_unlink(uhci, CLEAN_NOT_FORCED);
+	uhci_switch_timer_int(uhci);
+							
+	spin_unlock (&uhci->urb_list_lock);
+	
+	outw (status, io_addr + USBSTS);
+
+	//dbg("uhci_interrupt: done");
+}
+
+/*--------------------------------------------------------------------------*/
+//             POWER MANAGEMENT
+
+#ifdef	CONFIG_PM
+static int uhci_suspend (struct usb_hcd *hcd, u32 state)
+{
+	struct uhci_hcd		*uhci = hcd_to_uhci (hcd);
+	hc_reset(uhci);
+	return 0;
+}
+/*--------------------------------------------------------------------------*/
+static int uhci_resume (struct usb_hcd *hcd)
+{
+	struct uhci_hcd		*uhci = hcd_to_uhci (hcd);
+	hc_start(uhci);
+	return 0;
+}
+#endif
+/*--------------------------------------------------------------------------*/
+static const char	hcd_name [] = "usb-uhci-hcd";
+
+static const struct hc_driver uhci_driver = {
+	description:		hcd_name,
+
+	// generic hardware linkage
+	irq:			uhci_irq,
+	flags:			HCD_USB11,
+
+	// basic lifecycle operations
+	start:			uhci_start,
+#ifdef	CONFIG_PM
+	suspend:		uhci_suspend,
+	resume:			uhci_resume,
+#endif
+	stop:			uhci_stop,
+
+	// memory lifecycle (except per-request)
+	hcd_alloc:		uhci_hcd_alloc,
+	hcd_free:		uhci_hcd_free,
+
+	// managing i/o requests and associated device resources
+	urb_enqueue:		uhci_urb_enqueue,
+	urb_dequeue:		uhci_urb_dequeue,
+	free_config:		uhci_free_config,
+
+	// scheduling support
+	get_frame_number:	uhci_get_frame,
+
+	// root hub support
+	hub_status_data:	uhci_hub_status_data,
+	hub_control:		uhci_hub_control,
+};
+
+#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR (DRIVER_AUTHOR);
+MODULE_DESCRIPTION (DRIVER_INFO);
+MODULE_LICENSE ("GPL");
+
+static const struct pci_device_id __devinitdata pci_ids [] = { {
+
+	/* handle any USB UHCI controller */
+	class:		(PCI_CLASS_SERIAL_USB << 8) | 0x00,
+	class_mask:	~0,
+	driver_data:	(unsigned long) &uhci_driver,
+
+	/* no matter who makes it */
+	vendor:		PCI_ANY_ID,
+	device:		PCI_ANY_ID,
+	subvendor:	PCI_ANY_ID,
+	subdevice:	PCI_ANY_ID,
+	}, { /* end: all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE (pci, pci_ids);
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct pci_driver uhci_pci_driver = {
+	name:		(char *) hcd_name,
+	id_table:	pci_ids,
+
+	probe:		usb_hcd_pci_probe,
+	remove:		usb_hcd_pci_remove,
+
+#ifdef	CONFIG_PM
+	suspend:	usb_hcd_pci_suspend,
+	resume:		usb_hcd_pci_resume,
+#endif
+};
+/*-------------------------------------------------------------------------*/
+
+static int __init uhci_hcd_init (void) 
+{
+	init_dbg (DRIVER_INFO);
+	init_dbg ("block sizes: hq %d td %d",
+		sizeof (struct qh), sizeof (struct td));
+	return pci_module_init (&uhci_pci_driver);
+}
+
+static void __exit uhci_hcd_cleanup (void) 
+{	
+	pci_unregister_driver (&uhci_pci_driver);
+}
+
+module_init (uhci_hcd_init);
+module_exit (uhci_hcd_cleanup);
+
+
diff -Nru a/drivers/usb/host/usb-uhci-hcd.h b/drivers/usb/host/usb-uhci-hcd.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/host/usb-uhci-hcd.h	Sat May 18 00:46:15 2002
@@ -0,0 +1,247 @@
+#ifndef __LINUX_USB_UHCI_H
+#define __LINUX_USB_UHCI_H
+
+/*  $Id: usb-uhci-hcd.h,v 1.1 2002/05/14 20:36:57 acher Exp $ */
+
+#ifndef CONFIG_PCI
+#error "UHCI needs the CONFIG_PCI option!"
+#endif
+
+#define MODNAME "usb-uhci-hcd"
+#define UHCI_LATENCY_TIMER 0
+
+static __inline__ void uhci_wait_ms(unsigned int ms)
+{
+	if(!in_interrupt())
+	{
+		current->state = TASK_UNINTERRUPTIBLE;
+		schedule_timeout(1 + ms * HZ / 1000);
+	}
+	else
+		mdelay(ms);
+}
+
+/* Command register */
+#define   USBCMD		0
+#define   USBCMD_RS		0x0001	/* Run/Stop */
+#define   USBCMD_HCRESET	0x0002	/* Host reset */
+#define   USBCMD_GRESET		0x0004	/* Global reset */
+#define   USBCMD_EGSM		0x0008	/* Global Suspend Mode */
+#define   USBCMD_FGR		0x0010	/* Force Global Resume */
+#define   USBCMD_SWDBG		0x0020	/* SW Debug mode */
+#define   USBCMD_CF		0x0040	/* Config Flag (sw only) */
+#define   USBCMD_MAXP		0x0080	/* Max Packet (0 = 32, 1 = 64) */
+
+/* Status register */
+#define   USBSTS		2
+#define   USBSTS_USBINT		0x0001	/* Interrupt due to IOC */
+#define   USBSTS_ERROR		0x0002	/* Interrupt due to error */
+#define   USBSTS_RD		0x0004	/* Resume Detect */
+#define   USBSTS_HSE		0x0008	/* Host System Error - basically PCI problems */
+#define   USBSTS_HCPE		0x0010	/* Host Controller Process Error - the scripts were buggy */
+#define   USBSTS_HCH		0x0020	/* HC Halted */
+
+/* Interrupt enable register */
+#define   USBINTR		4
+#define   USBINTR_TIMEOUT	0x0001	/* Timeout/CRC error enable */
+#define   USBINTR_RESUME	0x0002	/* Resume interrupt enable */
+#define   USBINTR_IOC		0x0004	/* Interrupt On Complete enable */
+#define   USBINTR_SP		0x0008	/* Short packet interrupt enable */
+
+#define USBFRNUM	6
+#define USBFLBASEADD	8
+#define USBSOF		12
+
+/* USB port status and control registers */
+#define   USBPORTSC1	16
+#define   USBPORTSC2	18
+#define   USBPORTSC_CCS		0x0001	/* Current Connect Status ("device present") */
+#define   USBPORTSC_CSC		0x0002	/* Connect Status Change */
+#define   USBPORTSC_PE		0x0004	/* Port Enable */
+#define   USBPORTSC_PEC		0x0008	/* Port Enable Change */
+#define   USBPORTSC_LS		0x0030	/* Line Status */
+#define   USBPORTSC_RD		0x0040	/* Resume Detect */
+#define   USBPORTSC_LSDA	0x0100	/* Low Speed Device Attached */
+#define   USBPORTSC_PR		0x0200	/* Port Reset */
+#define   USBPORTSC_SUSP	0x1000	/* Suspend */
+
+/* Legacy support register */
+#define USBLEGSUP 0xc0
+#define USBLEGSUP_DEFAULT 0x2000	/* only PIRQ enable set */
+
+#define UHCI_NULL_DATA_SIZE	0x7ff	/* for UHCI controller TD */
+#define UHCI_PID 		0xff	/* PID MASK */
+
+#define UHCI_PTR_BITS		0x000F
+#define UHCI_PTR_TERM		0x0001
+#define UHCI_PTR_QH		0x0002
+#define UHCI_PTR_DEPTH		0x0004
+
+#define UHCI_NUMFRAMES		1024	/* in the frame list [array] */
+#define UHCI_MAX_SOF_NUMBER	2047	/* in an SOF packet */
+#define CAN_SCHEDULE_FRAMES	1000	/* how far future frames can be scheduled */
+
+/* for TD <status> */
+#define TD_CTRL_SPD		(1 << 29)	/* Short Packet Detect */
+#define TD_CTRL_C_ERR_MASK	(3 << 27)	/* Error Counter bits */
+#define TD_CTRL_LS		(1 << 26)	/* Low Speed Device */
+#define TD_CTRL_IOS		(1 << 25)	/* Isochronous Select */
+#define TD_CTRL_IOC		(1 << 24)	/* Interrupt on Complete */
+#define TD_CTRL_ACTIVE		(1 << 23)	/* TD Active */
+#define TD_CTRL_STALLED		(1 << 22)	/* TD Stalled */
+#define TD_CTRL_DBUFERR		(1 << 21)	/* Data Buffer Error */
+#define TD_CTRL_BABBLE		(1 << 20)	/* Babble Detected */
+#define TD_CTRL_NAK		(1 << 19)	/* NAK Received */
+#define TD_CTRL_CRCTIMEO	(1 << 18)	/* CRC/Time Out Error */
+#define TD_CTRL_BITSTUFF	(1 << 17)	/* Bit Stuff Error */
+#define TD_CTRL_ACTLEN_MASK	0x7ff	/* actual length, encoded as n - 1 */
+
+#define TD_CTRL_ANY_ERROR	(TD_CTRL_STALLED | TD_CTRL_DBUFERR | \
+				 TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF)
+
+#define uhci_status_bits(ctrl_sts)	(ctrl_sts & 0xFE0000)
+#define uhci_actual_length(desc)	((le32_to_cpu(desc->hw.td.status) + 1) & TD_CTRL_ACTLEN_MASK)	/* 1-based */
+
+/* for TD <flags>: */
+#define UHCI_TD_REMOVE		0x0001	/* Remove when done */
+
+/* for TD <info>: (a.k.a. Token) */
+#define TD_TOKEN_TOGGLE		19
+
+#define uhci_maxlen(token)	((token) >> 21)
+#define uhci_toggle(token)	(((token) >> TD_TOKEN_TOGGLE) & 1)
+#define uhci_endpoint(token)	(((token) >> 15) & 0xf)
+#define uhci_devaddr(token)	(((token) >> 8) & 0x7f)
+#define uhci_devep(token)	(((token) >> 8) & 0x7ff)
+#define uhci_packetid(token)	((token) & 0xff)
+#define uhci_packetout(token)	(uhci_packetid(token) != USB_PID_IN)
+#define uhci_packetin(token)	(uhci_packetid(token) == USB_PID_IN)
+
+#define uhci_do_toggle(urb)     usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe))
+#define uhci_get_toggle(urb)    usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe))
+
+/* ------------------------------------------------------------------------------------
+   TD/QH-structures
+   ------------------------------------------------------------------------------------ */
+
+typedef enum {
+	TD_TYPE, QH_TYPE
+} uhci_desc_type_t;
+
+typedef struct {
+	__u32 link;
+	__u32 status;
+	__u32 info;
+	__u32 buffer;
+} uhci_td_t, *puhci_td_t;
+
+typedef struct {
+	__u32 head;
+	__u32 element;		/* Queue element pointer */
+} uhci_qh_t, *puhci_qh_t;
+
+typedef struct {
+	union {
+		uhci_td_t 	td;
+		uhci_qh_t 	qh;
+	} hw;
+	uhci_desc_type_t 	type;
+	dma_addr_t 		dma_addr;
+	struct list_head 	horizontal;
+	struct list_head 	vertical;
+	struct list_head 	desc_list;
+	int 			last_used;
+} uhci_desc_t, *puhci_desc_t;
+
+typedef struct {
+	struct list_head 	desc_list;	// list pointer to all corresponding TDs/QHs associated with this request
+	struct list_head 	urb_list;
+	struct urb              *urb;  // urb to which this data belongs
+	dma_addr_t 		setup_packet_dma;
+	dma_addr_t 		transfer_buffer_dma;
+	unsigned long 		started;
+	struct urb		*next_queued_urb;	// next queued urb for this EP
+	struct urb 		*prev_queued_urb;
+	uhci_desc_t		*bottom_qh;
+	uhci_desc_t		*next_qh;       	// next helper QH
+	char 			use_loop;
+	char 			flags;
+} urb_priv_t, *purb_priv_t;
+
+struct uhci_hcd {
+	unsigned int 		maxports;
+	int 			running;
+	int 			apm_state;
+
+	struct uhci_hcd 	*next;	// chain of uhci device contexts
+
+	spinlock_t 		urb_list_lock;	// lock to keep consistency 
+	spinlock_t 		qh_lock;
+	spinlock_t 		td_lock;
+	atomic_t 		avoid_bulk;
+	
+	__u32 			*framelist;
+	dma_addr_t 		framelist_dma;
+	uhci_desc_t 		**iso_td;
+	uhci_desc_t 		*int_chain[8];
+	uhci_desc_t 		*ls_control_chain;
+	uhci_desc_t 		*control_chain;
+	uhci_desc_t 		*bulk_chain;
+	uhci_desc_t 		*chain_end;
+	uhci_desc_t 		*td1ms;
+	uhci_desc_t 		*td32ms;
+	uhci_desc_t 		*td128ms;
+
+	struct list_head 	urb_list;	// list of all pending urbs
+	struct list_head 	free_desc_qh;   // Cool down pool for QH
+	struct list_head 	free_desc_td;   // Cool down pool for ISO/INT-TDs
+	struct list_head 	urb_unlinked;	// list of all unlinked  urbs
+	struct pci_dev 		*uhci_pci;
+	struct pci_pool 	*desc_pool;
+
+	int 			unlink_urb_done;
+	int 			loop_usage;            // URBs using bandwidth reclamation
+	long 			timeout_check;
+	int 			timeout_urbs;
+	long 			last_error_time;          // last error output in uhci_interrupt()
+	
+	long                    last_hcd_irq;
+	int                     reanimations;
+	int                     need_init;
+
+	// Framework state
+	struct usb_hcd          hcd;
+};
+
+#define hcd_to_uhci(hcd_ptr) list_entry(hcd_ptr, struct uhci_hcd, hcd)
+
+#define MAKE_TD_ADDR(a) ((a)->dma_addr&~UHCI_PTR_QH)
+#define MAKE_QH_ADDR(a) ((a)->dma_addr|UHCI_PTR_QH)
+#define UHCI_GET_CURRENT_FRAME(uhci) (inw ((int)(uhci->hcd.regs) + USBFRNUM))
+
+#define CLEAN_TRANSFER_NO_DELETION 0
+#define CLEAN_TRANSFER_REGULAR 1
+#define CLEAN_TRANSFER_DELETION_MARK 2
+
+#define CLEAN_NOT_FORCED 0
+#define CLEAN_FORCED 1
+
+#define PROCESS_ISO_REGULAR 0
+#define PROCESS_ISO_FORCE 1
+
+#define PROCESS_INT_REGULAR 0
+#define PROCESS_INT_REMOVE 1
+
+#define UNLINK_ASYNC_STORE_URB 0
+#define UNLINK_ASYNC_DONT_STORE 1
+
+#define is_td_active(desc) (desc->hw.td.status & cpu_to_le32(TD_CTRL_ACTIVE))
+
+#define set_qh_head(desc,val) (desc)->hw.qh.head=cpu_to_le32(val)
+#define set_qh_element(desc,val) (desc)->hw.qh.element=cpu_to_le32(val)
+#define set_td_link(desc,val) (desc)->hw.td.link=cpu_to_le32(val)
+#define set_td_ioc(desc) (desc)->hw.td.status |= cpu_to_le32(TD_CTRL_IOC)
+#define clr_td_ioc(desc) (desc)->hw.td.status &= cpu_to_le32(~TD_CTRL_IOC)
+
+
+#endif
diff -Nru a/drivers/usb/host/usb-uhci-hub.c b/drivers/usb/host/usb-uhci-hub.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/host/usb-uhci-hub.c	Sat May 18 00:46:15 2002
@@ -0,0 +1,213 @@
+/*  
+    UHCI HCD (Host Controller Driver) for USB, UHCI Root Hub
+    
+    (c) 1999-2002 
+    Georg Acher      +    Deti Fliegl    +    Thomas Sailer
+    georg@acher.org      deti@fliegl.de   sailer@ife.ee.ethz.ch
+   
+    with the help of
+    David Brownell, david-b@pacbell.net
+    Adam Richter, adam@yggdrasil.com
+    Roman Weissgaerber, weissg@vienna.at
+    
+    HW-initalization based on material of
+    Randy Dunlap + Johannes Erdfelt + Gregory P. Smith + Linus Torvalds 
+
+    $Id: usb-uhci-hub.c,v 1.1 2002/05/14 20:36:57 acher Exp $
+*/
+
+#define CLR_RH_PORTSTAT(x) \
+		status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \
+		status = (status & 0xfff5) & ~(x); \
+		outw(status, io_addr+USBPORTSC1+2*(wIndex-1))
+
+#define SET_RH_PORTSTAT(x) \
+		status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \
+		status = (status & 0xfff5) | (x); \
+		outw(status, io_addr+USBPORTSC1+2*(wIndex-1))
+
+static int oldval=-1;
+
+/* build "status change" packet (one or two bytes) from HC registers 
+   Since uhci_hub_status_data is called by a SW timer, it is also used
+   for monitoring HC health */
+static int uhci_hub_status_data (struct usb_hcd *hcd, char *buf)
+{
+	struct uhci_hcd *uhci = hcd_to_uhci (hcd);
+	unsigned int io_addr = (int)uhci->hcd.regs;
+	int i, len=0, data = 0, portstate;
+	int changed=0;
+
+	for (i = 0; i < uhci->maxports; i++) {
+		portstate=inw (io_addr + USBPORTSC1 + i * 2);
+#if 0
+		if (i==0 && (portstate&0xf) != (oldval&0xf))
+			err("Port %i: %x", i+1, portstate);
+#endif
+		if (i==0)
+			oldval=portstate;
+		if ((portstate & 0xa) > 0) {
+			changed=1;
+		}
+		data |= ((portstate & 0xa) > 0 ? (1 << (i + 1)) : 0);
+		len = (i + 1) / 8 + 1;
+	}
+
+
+	*(__u16 *)buf = cpu_to_le16 (data);
+
+	// Watchdog
+
+	if (uhci->running && time_after(jiffies, uhci->last_hcd_irq + WATCHDOG_TIMEOUT)) {
+		if (uhci->reanimations > MAX_REANIMATIONS) {
+			err("He's dead, Jim. Giving up reanimating the UHCI host controller.\n"
+			    "Maybe a real module reload helps...");
+			uhci->running = 0;
+		}
+		else {	
+			uhci->running = 0;
+			uhci->need_init=1; // init done in the next submit_urb
+		}
+	}
+	return changed?len:0;
+}
+/*-------------------------------------------------------------------------*/
+static void uhci_hub_descriptor (struct uhci_hcd *uhci, struct usb_hub_descriptor *desc) 
+{
+	int		ports = uhci->maxports;
+	u16		temp;
+	desc->bDescriptorType = 0x29;
+	desc->bPwrOn2PwrGood = 1;
+	desc->bHubContrCurrent = 0;
+	desc->bNbrPorts = ports;
+	temp = 1 + (ports / 8);
+	desc->bDescLength = 7 + 2 * temp;
+	desc->wHubCharacteristics = 0;	
+	desc-> bitmap[0] = 0;
+	desc-> bitmap[1] = 0xff;
+}
+/*-------------------------------------------------------------------------*/
+static int uhci_hub_control (
+        struct usb_hcd  *hcd,
+        u16             typeReq,
+        u16             wValue,
+        u16             wIndex,
+        char            *buf,
+        u16             wLength) 
+{
+	struct uhci_hcd *uhci = hcd_to_uhci (hcd);
+	int status = 0;
+	int stat = 0;
+	int cstatus;
+	unsigned int io_addr = (int)uhci->hcd.regs;
+	int		ports = uhci->maxports;
+
+	switch (typeReq) {	
+
+	case ClearHubFeature:
+		break;
+
+	case ClearPortFeature:
+		if (!wIndex || wIndex > ports)
+                        goto error;
+
+		switch (wValue) {
+		case USB_PORT_FEAT_ENABLE:
+			CLR_RH_PORTSTAT (USBPORTSC_PE);
+			break;
+		case USB_PORT_FEAT_C_ENABLE:
+			SET_RH_PORTSTAT (USBPORTSC_PEC);
+			break;
+
+		case USB_PORT_FEAT_SUSPEND:
+			CLR_RH_PORTSTAT (USBPORTSC_SUSP);
+			break;
+		case USB_PORT_FEAT_C_SUSPEND:
+			/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
+			break;
+
+		case USB_PORT_FEAT_POWER:
+		       	break;/* port power ** */
+
+		case USB_PORT_FEAT_C_CONNECTION:
+			SET_RH_PORTSTAT (USBPORTSC_CSC);
+			break;
+		
+		case USB_PORT_FEAT_C_OVER_CURRENT:
+			break;	/* port power over current ** */
+
+		case USB_PORT_FEAT_C_RESET:
+			break;
+		default:
+			goto error;
+		}
+		break;
+
+	case GetHubDescriptor:
+                uhci_hub_descriptor (uhci, (struct usb_hub_descriptor *) buf);
+                break;
+
+	case GetHubStatus:
+		*(u32 *) buf = cpu_to_le32 (0);
+		break;
+
+	case GetPortStatus:
+		if (!wIndex || wIndex > ports)
+			goto error;
+
+		status = inw (io_addr + USBPORTSC1 + 2 * (wIndex - 1));
+		cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) |
+			((status & USBPORTSC_PEC) >> (3 - 1));
+
+		status = (status & USBPORTSC_CCS) |
+			((status & USBPORTSC_PE) >> (2 - 1)) |
+			((status & USBPORTSC_SUSP) >> (12 - 2)) |
+			((status & USBPORTSC_PR) >> (9 - 4)) |
+			(1 << 8) |	/* power on ** */
+			((status & USBPORTSC_LSDA) << (-8 + 9));
+
+		*(__u16 *) buf = cpu_to_le16 (status);
+		*(__u16 *) (buf + 2) = cpu_to_le16 (cstatus);
+		break;
+
+	case SetHubFeature:
+		// FIXME
+		break;
+
+	case SetPortFeature: 
+		if (!wIndex || wIndex > ports)
+			goto error;
+			
+		switch (wValue) {
+		case USB_PORT_FEAT_SUSPEND:
+			SET_RH_PORTSTAT (USBPORTSC_SUSP);
+			break;
+		case USB_PORT_FEAT_RESET:
+			SET_RH_PORTSTAT (USBPORTSC_PR);
+			uhci_wait_ms (10);
+			CLR_RH_PORTSTAT (USBPORTSC_PR);
+			udelay (10);
+			SET_RH_PORTSTAT (USBPORTSC_PE);
+			uhci_wait_ms (10);
+			SET_RH_PORTSTAT (0xa);
+			break;
+		case USB_PORT_FEAT_POWER:
+			break;	/* port power ** */
+		case  USB_PORT_FEAT_ENABLE:
+			SET_RH_PORTSTAT (USBPORTSC_PE);
+			break;
+		default:
+			goto error;
+		}
+		break;
+				
+	default:
+error:		
+		stat = -EPIPE;
+	}
+
+	dbg("Root-Hub stat port1: %x port2: %x",
+	     inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2));
+
+	return stat;
+}
diff -Nru a/drivers/usb/host/usb-uhci-mem.c b/drivers/usb/host/usb-uhci-mem.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/host/usb-uhci-mem.c	Sat May 18 00:46:15 2002
@@ -0,0 +1,722 @@
+/*  
+    UHCI HCD (Host Controller Driver) for USB
+    UHCI memory allocation and basic descriptor handling
+   
+    (c) 1999-2002 
+    Georg Acher      +    Deti Fliegl    +    Thomas Sailer
+    georg@acher.org      deti@fliegl.de   sailer@ife.ee.ethz.ch
+   
+    with the help of
+    David Brownell, david-b@pacbell.net
+    Adam Richter, adam@yggdrasil.com
+    Roman Weissgaerber, weissg@vienna.at    
+    
+    HW-initalization based on material of
+    Randy Dunlap + Johannes Erdfelt + Gregory P. Smith + Linus Torvalds 
+
+    $Id: usb-uhci-mem.c,v 1.1 2002/05/14 20:36:57 acher Exp $
+ */
+
+/*###########################################################################*/
+//                        UHCI STRUCTURE
+/*###########################################################################*/
+static struct usb_hcd *uhci_hcd_alloc (void)
+{
+	struct uhci_hcd *uhci;
+	int len;
+
+	len=sizeof (struct uhci_hcd);
+	uhci = (struct uhci_hcd *) kmalloc (len, GFP_KERNEL);
+	if (uhci == 0) 
+		return NULL;
+	
+	memset (uhci, 0, len);
+	init_dbg("uhci @ %p, hcd @ %p",uhci, &(uhci->hcd));
+	INIT_LIST_HEAD (&uhci->free_desc_qh);
+	INIT_LIST_HEAD (&uhci->free_desc_td);
+	INIT_LIST_HEAD (&uhci->urb_list);
+	INIT_LIST_HEAD (&uhci->urb_unlinked);
+	spin_lock_init (&uhci->urb_list_lock);
+	spin_lock_init (&uhci->qh_lock);
+	spin_lock_init (&uhci->td_lock);
+	atomic_set(&uhci->avoid_bulk, 0);		
+	return &(uhci->hcd);
+}
+/*-------------------------------------------------------------------*/
+static void uhci_hcd_free (struct usb_hcd *hcd)
+{
+	kfree (hcd_to_uhci (hcd));
+}
+/*###########################################################################*/
+//                   DMA/PCI CONSISTENCY
+/*###########################################################################*/
+
+static void uhci_urb_dma_sync(struct uhci_hcd *uhci, struct urb *urb, urb_priv_t *urb_priv)
+{
+	if (urb_priv->setup_packet_dma)
+		pci_dma_sync_single(uhci->uhci_pci, urb_priv->setup_packet_dma,
+				    sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
+
+	if (urb_priv->transfer_buffer_dma)
+		pci_dma_sync_single(uhci->uhci_pci, urb_priv->transfer_buffer_dma,
+				    urb->transfer_buffer_length,
+				    usb_pipein(urb->pipe) ?
+				    PCI_DMA_FROMDEVICE :
+				    PCI_DMA_TODEVICE);
+}
+/*-------------------------------------------------------------------*/
+static void uhci_urb_dma_unmap(struct uhci_hcd *uhci, struct urb *urb, urb_priv_t *urb_priv)
+{
+	if (urb_priv->setup_packet_dma) {
+		pci_unmap_single(uhci->uhci_pci, urb_priv->setup_packet_dma,
+				 sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
+		urb_priv->setup_packet_dma = 0;
+	}
+	if (urb_priv->transfer_buffer_dma) {
+		pci_unmap_single(uhci->uhci_pci, urb_priv->transfer_buffer_dma,
+				 urb->transfer_buffer_length,
+				 usb_pipein(urb->pipe) ?
+				 PCI_DMA_FROMDEVICE :
+				 PCI_DMA_TODEVICE);
+		urb_priv->transfer_buffer_dma = 0;
+	}
+}
+/*###########################################################################*/
+//                       TRANSFER DESCRIPTORS (TD)
+/*###########################################################################*/
+
+static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer)
+{
+	td->hw.td.status = cpu_to_le32(status);
+	td->hw.td.info = cpu_to_le32(info);
+	td->hw.td.buffer = cpu_to_le32(buffer);
+}
+/*-------------------------------------------------------------------*/
+static int alloc_td (struct uhci_hcd *uhci, uhci_desc_t ** new, int flags)
+{
+	dma_addr_t dma_handle;
+
+	*new = pci_pool_alloc(uhci->desc_pool, GFP_DMA | GFP_ATOMIC, &dma_handle);
+	if (!*new)
+		return -ENOMEM;
+	memset (*new, 0, sizeof (uhci_desc_t));
+	(*new)->dma_addr = dma_handle;
+	set_td_link((*new), UHCI_PTR_TERM | (flags & UHCI_PTR_BITS));	// last by default
+	(*new)->type = TD_TYPE;
+	mb();
+	INIT_LIST_HEAD (&(*new)->vertical);
+	INIT_LIST_HEAD (&(*new)->horizontal);
+	
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+/* insert td at last position in td-list of qh (vertical) */
+static int insert_td (struct uhci_hcd *uhci, uhci_desc_t *qh, uhci_desc_t* new, int flags)
+{
+	uhci_desc_t *prev;
+	unsigned long cpuflags;
+	
+	spin_lock_irqsave (&uhci->td_lock, cpuflags);
+
+	list_add_tail (&new->vertical, &qh->vertical);
+
+	prev = list_entry (new->vertical.prev, uhci_desc_t, vertical);
+
+	if (qh == prev ) {
+		// virgin qh without any tds
+		set_qh_element(qh, new->dma_addr | UHCI_PTR_TERM);
+	}
+	else {
+		// already tds inserted, implicitely remove TERM bit of prev
+		set_td_link(prev, new->dma_addr | (flags & UHCI_PTR_DEPTH));
+	}
+	mb();
+	spin_unlock_irqrestore (&uhci->td_lock, cpuflags);
+	
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+/* insert new_td after td (horizontal) */
+static int insert_td_horizontal (struct uhci_hcd *uhci, uhci_desc_t *td, uhci_desc_t* new)
+{
+	uhci_desc_t *next;
+	unsigned long flags;
+	
+	spin_lock_irqsave (&uhci->td_lock, flags);
+
+	next = list_entry (td->horizontal.next, uhci_desc_t, horizontal);
+	list_add (&new->horizontal, &td->horizontal);
+	new->hw.td.link = td->hw.td.link;
+	mb();
+	set_td_link(td, new->dma_addr);
+	mb();
+	spin_unlock_irqrestore (&uhci->td_lock, flags);	
+	
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+static int unlink_td (struct uhci_hcd *uhci, uhci_desc_t *element, int phys_unlink)
+{
+	uhci_desc_t *next, *prev;
+	int dir = 0;
+	unsigned long flags;
+	
+	spin_lock_irqsave (&uhci->td_lock, flags);
+	
+	next = list_entry (element->vertical.next, uhci_desc_t, vertical);
+	
+	if (next == element) {
+		dir = 1;
+		prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal);
+	}
+	else 
+		prev = list_entry (element->vertical.prev, uhci_desc_t, vertical);
+	
+	if (phys_unlink) {
+		// really remove HW linking
+		if (prev->type == TD_TYPE) {
+			prev->hw.td.link = element->hw.td.link;
+		}
+		else
+			prev->hw.qh.element = element->hw.td.link;
+	}
+ 	mb ();
+
+	if (dir == 0)
+		list_del (&element->vertical);
+	else
+		list_del (&element->horizontal);
+	
+	spin_unlock_irqrestore (&uhci->td_lock, flags);	
+	
+	return 0;
+}
+/*###########################################################################*/
+//               QUEUE HEADS (QH)
+/*###########################################################################*/
+// Allocates qh element
+static int alloc_qh (struct uhci_hcd *uhci, uhci_desc_t ** new)
+{
+	dma_addr_t dma_handle;
+
+	*new = pci_pool_alloc(uhci->desc_pool, GFP_DMA | GFP_ATOMIC, &dma_handle);
+	if (!*new)
+		return -ENOMEM;
+	memset (*new, 0, sizeof (uhci_desc_t));
+	(*new)->dma_addr = dma_handle;
+	set_qh_head(*new, UHCI_PTR_TERM);
+	set_qh_element(*new, UHCI_PTR_TERM);
+	(*new)->type = QH_TYPE;
+	
+	mb();
+	INIT_LIST_HEAD (&(*new)->horizontal);
+	INIT_LIST_HEAD (&(*new)->vertical);
+	
+	dbg("Allocated qh @ %p", *new);
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+// inserts new qh before/after the qh at pos
+// flags: 0: insert before pos, 1: insert after pos (for low speed transfers)
+static int insert_qh (struct uhci_hcd *uhci, uhci_desc_t *pos, uhci_desc_t *new, int order)
+{
+	uhci_desc_t *old;
+	unsigned long flags;
+
+	spin_lock_irqsave (&uhci->qh_lock, flags);
+
+	if (!order) {
+		// (OLD) (POS) -> (OLD) (NEW) (POS)
+		old = list_entry (pos->horizontal.prev, uhci_desc_t, horizontal);
+		list_add_tail (&new->horizontal, &pos->horizontal);
+		set_qh_head(new, MAKE_QH_ADDR (pos)) ;
+		mb();
+		if (!(old->hw.qh.head & cpu_to_le32(UHCI_PTR_TERM)))
+			set_qh_head(old, MAKE_QH_ADDR (new)) ;
+	}
+	else {
+		// (POS) (OLD) -> (POS) (NEW) (OLD)
+		old = list_entry (pos->horizontal.next, uhci_desc_t, horizontal);
+		list_add (&new->horizontal, &pos->horizontal);
+		set_qh_head(new, MAKE_QH_ADDR (old));
+		mb();
+		set_qh_head(pos, MAKE_QH_ADDR (new)) ;
+	}
+ 	mb ();
+	
+	spin_unlock_irqrestore (&uhci->qh_lock, flags);
+
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+// append a qh to td.link physically, the SW linkage is not affected
+static void append_qh(struct uhci_hcd *uhci, uhci_desc_t *td, uhci_desc_t* qh, int  flags)
+{
+	unsigned long cpuflags;
+	
+	spin_lock_irqsave (&uhci->td_lock, cpuflags);
+
+	set_td_link(td, qh->dma_addr | (flags & UHCI_PTR_DEPTH) | UHCI_PTR_QH);
+       
+	mb();
+	spin_unlock_irqrestore (&uhci->td_lock, cpuflags);
+}
+/*-------------------------------------------------------------------*/
+static int unlink_qh (struct uhci_hcd *uhci, uhci_desc_t *element)
+{
+	uhci_desc_t  *prev;
+	unsigned long flags;
+	__u32 old_head;
+	spin_lock_irqsave (&uhci->qh_lock, flags);
+	
+	prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal);
+
+	old_head = element->hw.qh.head;
+	element->hw.qh.head = UHCI_PTR_TERM;
+	mb();
+	prev->hw.qh.head = old_head;
+
+	dbg("unlink qh %p, pqh %p, nxqh %p, to %08x", element, prev, 
+	    list_entry (element->horizontal.next, uhci_desc_t, horizontal),
+	    le32_to_cpu(element->hw.qh.head) &~15);
+	
+	list_del(&element->horizontal);
+
+	mb ();
+	spin_unlock_irqrestore (&uhci->qh_lock, flags);
+	
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+static int delete_desc (struct uhci_hcd *uhci, uhci_desc_t *element)
+{
+	pci_pool_free(uhci->desc_pool, element, element->dma_addr);
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+static int delete_qh (struct uhci_hcd *uhci, uhci_desc_t *qh)
+{
+	uhci_desc_t *td;
+	struct list_head *p;
+	int n=0;
+
+	list_del (&qh->horizontal);
+
+	while ((p = qh->vertical.next) != &qh->vertical && n<10000) {
+		td = list_entry (p, uhci_desc_t, vertical);
+		dbg("unlink td @ %p",td);
+		unlink_td (uhci, td, 0); // no physical unlink
+		delete_desc (uhci, td);
+		n++;
+	}
+	// never trust any software, not even your own...
+	if (n>=10000)
+		err("delete_qh: Garbage in QH list, giving up");
+
+	delete_desc (uhci, qh);
+	
+	return 0;
+}
+/*###########################################################################*/
+//             DESCRIPTOR CHAINING HELPERS
+/*###########################################################################*/
+
+static void clean_td_chain (struct uhci_hcd *uhci, uhci_desc_t *td)
+{
+	struct list_head *p;
+	uhci_desc_t *td1;
+
+	if (!td)
+		return;
+	
+	while ((p = td->horizontal.next) != &td->horizontal) {
+		td1 = list_entry (p, uhci_desc_t, horizontal);
+		delete_desc (uhci, td1);
+	}
+	
+	delete_desc (uhci, td);
+}
+/*-------------------------------------------------------------------*/
+// Cleans up collected QHs/TDs, but not more than 100 in one go
+void clean_descs(struct uhci_hcd *uhci, int force)
+{
+	struct list_head *q;
+	uhci_desc_t *qh,*td;
+	int now=UHCI_GET_CURRENT_FRAME(uhci), n=0;
+
+	q=uhci->free_desc_qh.prev;
+	
+	while (q != &uhci->free_desc_qh && (force || n<100)) {
+		qh = list_entry (q, uhci_desc_t, horizontal);		
+		q=qh->horizontal.prev;
+
+		if ((qh->last_used!=now) || force) {
+			delete_qh(uhci,qh);
+		}
+		n++;
+	}
+
+	q=uhci->free_desc_td.prev;
+	n=0;
+
+	while (q != &uhci->free_desc_td && (force || n<100)) {
+
+		td = list_entry (q, uhci_desc_t, horizontal);		
+		q=td->horizontal.prev;
+
+		if (((td->last_used!=now)&&(td->last_used+1!=now)) || force) {
+			list_del (&td->horizontal);
+			delete_desc(uhci,td);
+		}
+		n++;
+	}
+}
+/*-------------------------------------------------------------------*/
+static void uhci_switch_timer_int(struct uhci_hcd *uhci)
+{
+
+	if (!list_empty(&uhci->urb_unlinked))
+		set_td_ioc(uhci->td1ms);
+	else
+		clr_td_ioc(uhci->td1ms);
+
+	if (uhci->timeout_urbs)
+		set_td_ioc(uhci->td32ms);
+	else
+		clr_td_ioc(uhci->td32ms);
+	wmb();
+}
+/*-------------------------------------------------------------------*/
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+static void enable_desc_loop(struct uhci_hcd *uhci, struct urb *urb)
+{
+	int flags;
+
+	if (urb->transfer_flags & USB_NO_FSBR)
+		return;
+
+	spin_lock_irqsave (&uhci->qh_lock, flags);
+	uhci->chain_end->hw.qh.head&=cpu_to_le32(~UHCI_PTR_TERM);
+	mb();
+	uhci->loop_usage++;
+	((urb_priv_t*)urb->hcpriv)->use_loop=1;
+	spin_unlock_irqrestore (&uhci->qh_lock, flags);
+}
+/*-------------------------------------------------------------------*/
+static void disable_desc_loop(struct uhci_hcd *uhci, struct urb *urb)
+{
+	int flags;
+
+	if (urb->transfer_flags & USB_NO_FSBR)
+		return;
+
+	spin_lock_irqsave (&uhci->qh_lock, flags);
+	if (((urb_priv_t*)urb->hcpriv)->use_loop) {
+		uhci->loop_usage--;
+
+		if (!uhci->loop_usage) {
+			uhci->chain_end->hw.qh.head|=cpu_to_le32(UHCI_PTR_TERM);
+			mb();
+		}
+		((urb_priv_t*)urb->hcpriv)->use_loop=0;
+	}
+	spin_unlock_irqrestore (&uhci->qh_lock, flags);
+}
+#endif
+/*-------------------------------------------------------------------*/
+static void queue_urb_unlocked (struct uhci_hcd *uhci, struct urb *urb)
+{
+	urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+	int type;
+	type=usb_pipetype (urb->pipe);
+	
+	if ((type == PIPE_BULK) || (type == PIPE_CONTROL))
+			enable_desc_loop(uhci, urb);
+#endif
+	urb->status = -EINPROGRESS;
+	priv->started=jiffies;
+
+	list_add (&priv->urb_list, &uhci->urb_list);
+	if (urb->timeout)
+		uhci->timeout_urbs++;
+	uhci_switch_timer_int(uhci);
+}
+/*-------------------------------------------------------------------*/
+static void queue_urb (struct uhci_hcd *uhci, struct urb *urb)
+{
+	unsigned long flags=0;
+
+	spin_lock_irqsave (&uhci->urb_list_lock, flags);
+	queue_urb_unlocked(uhci,urb);
+	spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
+}
+/*-------------------------------------------------------------------*/
+static void dequeue_urb (struct uhci_hcd *uhci, struct urb *urb)
+{
+	urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+	int type;
+	dbg("dequeue URB %p",urb);
+	type=usb_pipetype (urb->pipe);
+
+	if ((type == PIPE_BULK) || (type == PIPE_CONTROL))
+		disable_desc_loop(uhci, urb);
+#endif
+
+	list_del (&priv->urb_list);
+	if (urb->timeout && uhci->timeout_urbs)
+		uhci->timeout_urbs--;
+
+}
+/*###########################################################################*/
+//                  INIT/FREE FRAME LAYOUT IN MEMORY
+/*###########################################################################*/
+
+// Removes ALL qhs in chain (paranoia!)
+static void cleanup_skel (struct uhci_hcd *uhci)
+{
+	unsigned int n;
+	uhci_desc_t *td;
+
+	dbg("cleanup_skel");
+
+	clean_descs(uhci,1);
+	dbg("clean_descs done");
+
+	if (uhci->td32ms) {	
+		unlink_td(uhci,uhci->td32ms,1);
+		delete_desc(uhci, uhci->td32ms);
+	}
+
+	if (uhci->td128ms) {	
+		unlink_td(uhci,uhci->td128ms,1);
+		delete_desc(uhci, uhci->td128ms);
+	}
+
+	for (n = 0; n < 8; n++) {
+		td = uhci->int_chain[n];
+		clean_td_chain (uhci, td);
+	}
+
+	if (uhci->iso_td) {
+		for (n = 0; n < 1024; n++) {
+			td = uhci->iso_td[n];
+			clean_td_chain (uhci, td);
+		}
+		kfree (uhci->iso_td);
+	}
+
+	if (uhci->framelist)
+		pci_free_consistent(uhci->uhci_pci, PAGE_SIZE,
+				    uhci->framelist, uhci->framelist_dma);
+
+	if (uhci->control_chain) {
+		// completed init_skel?
+		struct list_head *p;
+		uhci_desc_t *qh, *qh1;
+
+		qh = uhci->control_chain;
+		while ((p = qh->horizontal.next) != &qh->horizontal) {
+			qh1 = list_entry (p, uhci_desc_t, horizontal);
+			delete_qh (uhci, qh1);
+		}
+
+		delete_qh (uhci, qh);
+	}
+	else {
+		if (uhci->ls_control_chain)
+			delete_desc (uhci, uhci->ls_control_chain);
+		if (uhci->control_chain)
+			delete_desc (uhci, uhci->control_chain);
+		if (uhci->bulk_chain)
+			delete_desc (uhci, uhci->bulk_chain);
+		if (uhci->chain_end)
+			delete_desc (uhci, uhci->chain_end);
+	}
+
+	if (uhci->desc_pool) {
+		pci_pool_destroy(uhci->desc_pool);
+		uhci->desc_pool = NULL;
+	}
+
+	uhci->ls_control_chain = NULL;
+	uhci->control_chain = NULL;
+	uhci->bulk_chain = NULL;
+	uhci->chain_end = NULL;
+	for (n = 0; n < 8; n++)
+		uhci->int_chain[n] = NULL;
+	dbg("cleanup_skel finished");	
+}
+/*-------------------------------------------------------------------*/
+// allocates framelist and qh-skeletons
+// only HW-links provide continous linking, SW-links stay in their domain (ISO/INT)
+static int init_skel (struct uhci_hcd *uhci)
+{
+	int n, ret;
+	uhci_desc_t *qh, *td;
+	
+	init_dbg("init_skel");
+	
+	uhci->framelist = pci_alloc_consistent(uhci->uhci_pci, PAGE_SIZE,
+					    &uhci->framelist_dma);
+
+	if (!uhci->framelist)
+		return -ENOMEM;
+
+	memset (uhci->framelist, 0, 4096);
+
+	init_dbg("creating descriptor pci_pool");
+	uhci->desc_pool = pci_pool_create("uhci_desc", uhci->uhci_pci,
+				       sizeof(uhci_desc_t), 16, 0,
+				       GFP_DMA | GFP_ATOMIC);	
+	if (!uhci->desc_pool)
+		goto init_skel_cleanup;
+
+	init_dbg("allocating iso desc pointer list");
+	uhci->iso_td = (uhci_desc_t **) kmalloc (1024 * sizeof (uhci_desc_t*), GFP_KERNEL);
+	
+	if (!uhci->iso_td)
+		goto init_skel_cleanup;
+
+	uhci->ls_control_chain = NULL;
+	uhci->control_chain = NULL;
+	uhci->bulk_chain = NULL;
+	uhci->chain_end = NULL;
+	for (n = 0; n < 8; n++)
+		uhci->int_chain[n] = NULL;
+
+	init_dbg("allocating iso descs");
+	for (n = 0; n < 1024; n++) {
+	 	// allocate skeleton iso/irq-tds
+		if (alloc_td (uhci, &td, 0))
+			goto init_skel_cleanup;
+
+		uhci->iso_td[n] = td;
+		uhci->framelist[n] = cpu_to_le32((__u32) td->dma_addr);
+	}
+
+	init_dbg("allocating qh: chain_end");
+	if (alloc_qh (uhci, &qh))	
+		goto init_skel_cleanup;
+				
+	uhci->chain_end = qh;
+
+	if (alloc_td (uhci, &td, 0))
+		goto init_skel_cleanup;
+	
+	fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 1ms interrupt (enabled on demand)
+	insert_td (uhci, qh, td, 0);
+	qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM); // remove TERM bit
+	uhci->td1ms=td;
+
+	dbg("allocating qh: bulk_chain");
+	if (alloc_qh (uhci, &qh))
+		goto init_skel_cleanup;
+	
+	insert_qh (uhci, uhci->chain_end, qh, 0);
+	uhci->bulk_chain = qh;
+
+	dbg("allocating qh: control_chain");
+	if ((ret = alloc_qh (uhci, &qh)))
+		goto init_skel_cleanup;
+	
+	insert_qh (uhci, uhci->bulk_chain, qh, 0);
+	uhci->control_chain = qh;
+
+#ifdef	CONFIG_USB_UHCI_HIGH_BANDWIDTH
+	// disabled reclamation loop
+	set_qh_head(uhci->chain_end, uhci->control_chain->dma_addr | UHCI_PTR_QH | UHCI_PTR_TERM);
+#endif
+
+	init_dbg("allocating qh: ls_control_chain");
+	if (alloc_qh (uhci, &qh))
+		goto init_skel_cleanup;
+	
+	insert_qh (uhci, uhci->control_chain, qh, 0);
+	uhci->ls_control_chain = qh;
+
+	init_dbg("allocating skeleton INT-TDs");
+	
+	for (n = 0; n < 8; n++) {
+		uhci_desc_t *td;
+
+		if (alloc_td (uhci, &td, 0))
+			goto init_skel_cleanup;
+
+		uhci->int_chain[n] = td;
+		if (n == 0) {
+			set_td_link(uhci->int_chain[0], uhci->ls_control_chain->dma_addr | UHCI_PTR_QH);
+		}
+		else {
+			set_td_link(uhci->int_chain[n], uhci->int_chain[0]->dma_addr);
+		}
+	}
+
+	init_dbg("Linking skeleton INT-TDs");
+	
+	for (n = 0; n < 1024; n++) {
+		// link all iso-tds to the interrupt chains
+		int m, o;
+		dbg("framelist[%i]=%x",n,le32_to_cpu(uhci->framelist[n]));
+		if ((n&127)==127) 
+			((uhci_desc_t*) uhci->iso_td[n])->hw.td.link = cpu_to_le32(uhci->int_chain[0]->dma_addr);
+		else 
+			for (o = 1, m = 2; m <= 128; o++, m += m)
+				if ((n & (m - 1)) == ((m - 1) / 2))
+					set_td_link(((uhci_desc_t*) uhci->iso_td[n]), uhci->int_chain[o]->dma_addr);
+	}
+
+	if (alloc_td (uhci, &td, 0))
+		goto init_skel_cleanup;
+	
+	fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 32ms interrupt (activated later)
+	uhci->td32ms=td;
+	insert_td_horizontal (uhci, uhci->int_chain[5], td);
+
+	if (alloc_td (uhci, &td, 0))
+		goto init_skel_cleanup;
+	
+	fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 128ms interrupt (activated later)
+	uhci->td128ms=td;
+	insert_td_horizontal (uhci, uhci->int_chain[7], td);
+
+	mb();
+	init_dbg("init_skel exit");
+	return 0;
+
+  init_skel_cleanup:
+	cleanup_skel (uhci);
+	return -ENOMEM;
+}
+/*###########################################################################*/
+//                 UHCI PRIVATE DATA
+/*###########################################################################*/
+
+urb_priv_t *uhci_alloc_priv(int mem_flags)
+{
+	urb_priv_t *p;
+#ifdef DEBUG_SLAB
+	p = kmem_cache_alloc(urb_priv_kmem, SLAB_FLAG);
+#else
+	p = kmalloc (sizeof (urb_priv_t), mem_flags);
+#endif
+	if (p) {
+		memset(p, 0, sizeof(urb_priv_t));
+		INIT_LIST_HEAD (&p->urb_list);
+	}
+	return p;
+}
+/*-------------------------------------------------------------------*/
+void uhci_free_priv(struct uhci_hcd *uhci, struct urb *urb, urb_priv_t* p)
+{
+		uhci_urb_dma_unmap(uhci, urb, p);
+#ifdef DEBUG_SLAB
+		err("free_priv %p",p);
+		kmem_cache_free(urb_priv_kmem, p);
+#else
+		kfree (p);
+#endif
+		urb->hcpriv = NULL;
+}
+
diff -Nru a/drivers/usb/host/usb-uhci-q.c b/drivers/usb/host/usb-uhci-q.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/host/usb-uhci-q.c	Sat May 18 00:46:15 2002
@@ -0,0 +1,1320 @@
+/*  
+    UHCI HCD (Host Controller Driver) for USB, UHCI transfer processing
+    
+    (c) 1999-2002 
+    Georg Acher      +    Deti Fliegl    +    Thomas Sailer
+    georg@acher.org      deti@fliegl.de   sailer@ife.ee.ethz.ch
+   
+    with the help of
+    David Brownell, david-b@pacbell.net
+    Adam Richter, adam@yggdrasil.com
+    Roman Weissgaerber, weissg@vienna.at
+    
+    HW-initalization based on material of
+    Randy Dunlap + Johannes Erdfelt + Gregory P. Smith + Linus Torvalds 
+
+    $Id: usb-uhci-q.c,v 1.1 2002/05/14 20:36:57 acher Exp $
+*/
+
+/*-------------------------------------------------------------------*/
+static inline void finish_urb (struct uhci_hcd *uhci, struct urb *urb)
+{
+	if (urb->hcpriv) 
+		uhci_free_priv (uhci, urb, urb->hcpriv);
+	
+	usb_hcd_giveback_urb (&uhci->hcd, urb);
+}
+/*###########################################################################*/
+//                        URB SUBMISSION STUFF
+//     assembles QHs und TDs for control, bulk, interrupt  and isochronous
+/*###########################################################################*/
+
+// returns: 0 (no transfer queued), urb* (this urb already queued) 
+static struct urb* search_dev_ep (struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct list_head *p;
+	struct urb *tmp;
+	urb_priv_t *priv;
+	unsigned int mask = usb_pipecontrol(urb->pipe) ? (~USB_DIR_IN) : (~0);
+
+	p=uhci->urb_list.next;
+
+	for (; p != &uhci->urb_list; p = p->next) {
+		priv = list_entry (p,  urb_priv_t, urb_list);
+		tmp = priv->urb;
+		dbg("search_dev_ep urb: %p", tmp);
+		// we can accept this urb if it is not queued at this time 
+		// or if non-iso transfer requests should be scheduled for the same device and pipe
+		if ((!usb_pipeisoc(urb->pipe) && (tmp->dev == urb->dev) && !((tmp->pipe ^ urb->pipe) & mask)) ||
+		    (urb == tmp))
+			return tmp;	// found another urb already queued for processing
+	}
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+
+static int uhci_submit_control_urb (struct uhci_hcd *uhci, struct urb *urb)
+{
+	uhci_desc_t *qh, *td;
+	urb_priv_t *urb_priv = urb->hcpriv;
+	unsigned long destination, status;
+	int maxsze = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe));
+	int depth_first=USE_CTRL_DEPTH_FIRST;  // UHCI descriptor chasing method
+	unsigned long len;
+	char *data;
+
+//	err("uhci_submit_control start, buf %p", urb->transfer_buffer);
+	if (alloc_qh (uhci, &qh))		// alloc qh for this request
+		return -ENOMEM;
+
+	if (alloc_td (uhci, &td, UHCI_PTR_DEPTH * depth_first))		// get td for setup stage
+		goto fail_unmap_enomem;
+
+	/* The "pipe" thing contains the destination in bits 8--18 */
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
+	
+	status = TD_CTRL_ACTIVE
+		| (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD)
+		| (3 << 27);                      /* 3 errors */
+
+	if (urb->dev->speed == USB_SPEED_LOW)
+		status |= TD_CTRL_LS;
+
+	/*  Build the TD for the control request, try forever, 8 bytes of data */
+	fill_td (td, status, destination | (7 << 21), urb_priv->setup_packet_dma);
+
+	insert_td (uhci, qh, td, 0);	// queue 'setup stage'-td in qh
+#if 0
+	{
+		char *sp=urb->setup_packet;
+		dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe,
+		    sp[0],sp[1],sp[2],sp[3],sp[4],sp[5],sp[6],sp[7]);
+	}
+	//uhci_show_td(td);
+#endif
+
+	len = urb->transfer_buffer_length;
+	data = urb->transfer_buffer;
+
+	/* If direction is "send", change the frame from SETUP (0x2D)
+	   to OUT (0xE1). Else change it from SETUP to IN (0x69). */
+
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | (usb_pipeout (urb->pipe)?USB_PID_OUT:USB_PID_IN);
+
+	while (len > 0) {
+		int pktsze = len;
+
+		if (alloc_td (uhci, &td, UHCI_PTR_DEPTH * depth_first))
+			goto fail_unmap_enomem;
+
+		if (pktsze > maxsze)
+			pktsze = maxsze;
+
+		destination ^= 1 << TD_TOKEN_TOGGLE;	// toggle DATA0/1
+
+		// Status, pktsze bytes of data
+		fill_td (td, status, destination | ((pktsze - 1) << 21),
+			 urb_priv->transfer_buffer_dma + (data - (char *)urb->transfer_buffer));
+
+		insert_td (uhci, qh, td, UHCI_PTR_DEPTH * depth_first);	// queue 'data stage'-td in qh
+
+		data += pktsze;
+		len -= pktsze;
+	}
+
+	/* Build the final TD for control status 
+	   It's only IN if the pipe is out AND we aren't expecting data */
+
+	destination &= ~UHCI_PID;
+
+	if (usb_pipeout (urb->pipe) || (urb->transfer_buffer_length == 0))
+		destination |= USB_PID_IN;
+	else
+		destination |= USB_PID_OUT;
+
+	destination |= 1 << TD_TOKEN_TOGGLE;	/* End in Data1 */
+
+	if (alloc_td (uhci, &td, UHCI_PTR_DEPTH))
+		goto fail_unmap_enomem;
+
+	status &=~TD_CTRL_SPD;
+
+	/* no limit on errors on final packet, 0 bytes of data */
+	fill_td (td, status | TD_CTRL_IOC, destination | (UHCI_NULL_DATA_SIZE << 21), 0);
+
+	insert_td (uhci, qh, td, UHCI_PTR_DEPTH * depth_first);	// queue status td
+
+	list_add (&qh->desc_list, &urb_priv->desc_list);
+
+	queue_urb (uhci, urb);	// queue _before_ inserting in desc chain
+
+	qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM);
+
+	/* Start it up... put low speed first */
+	if (urb->dev->speed == USB_SPEED_LOW)
+		insert_qh (uhci, uhci->control_chain, qh, 0);
+	else
+		insert_qh (uhci, uhci->bulk_chain, qh, 0);
+
+	return 0;
+
+fail_unmap_enomem:
+	delete_qh(uhci, qh);
+	return -ENOMEM;
+}
+/*-------------------------------------------------------------------*/
+// For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh)
+// Due to the linking with other bulk urbs, it has to be locked with urb_list_lock!
+
+static int uhci_submit_bulk_urb (struct uhci_hcd *uhci, struct urb *urb, struct urb *bulk_urb)
+{
+	urb_priv_t *urb_priv = urb->hcpriv, *upriv, *bpriv=NULL;
+	uhci_desc_t *qh, *td, *nqh=NULL, *bqh=NULL, *first_td=NULL;
+	unsigned long destination, status;
+	char *data;
+	unsigned int pipe = urb->pipe;
+	int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
+	int info, len, last;
+	int depth_first=USE_BULK_DEPTH_FIRST;  // UHCI descriptor chasing method
+
+	if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)))
+		return -EPIPE;
+
+	queue_dbg("uhci_submit_bulk_urb: urb %p, old %p, pipe %08x, len %i",
+		  urb,bulk_urb,urb->pipe,urb->transfer_buffer_length);
+
+	upriv = (urb_priv_t*)urb->hcpriv;
+
+	if (!bulk_urb) {
+		if (alloc_qh (uhci, &qh))		// get qh for this request
+			return -ENOMEM;
+
+		if (urb->transfer_flags & USB_QUEUE_BULK) {
+			if (alloc_qh(uhci, &nqh)) // placeholder for clean unlink
+				goto fail_unmap_enomem;
+			upriv->next_qh = nqh;
+			queue_dbg("new next qh %p",nqh);
+		}
+	}
+	else { 
+		bpriv = (urb_priv_t*)bulk_urb->hcpriv;
+		qh = bpriv->bottom_qh;  // re-use bottom qh and next qh
+		nqh = bpriv->next_qh;
+		upriv->next_qh=nqh;	
+		upriv->prev_queued_urb=bulk_urb;
+	}
+
+	if (urb->transfer_flags & USB_QUEUE_BULK) {
+		if (alloc_qh (uhci, &bqh))  // "bottom" QH
+			goto fail_unmap_enomem;
+
+		set_qh_element(bqh, UHCI_PTR_TERM);
+		set_qh_head(bqh, nqh->dma_addr | UHCI_PTR_QH); // element
+		upriv->bottom_qh = bqh;
+	}
+	queue_dbg("uhci_submit_bulk: qh %p bqh %p nqh %p",qh, bqh, nqh);
+
+	/* The "pipe" thing contains the destination in bits 8--18. */
+	destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe);
+	
+	status = TD_CTRL_ACTIVE
+		| ((urb->transfer_flags & USB_DISABLE_SPD) ? 0 : TD_CTRL_SPD)
+		| (3 << 27);                   /* 3 errors */
+
+	if (urb->dev->speed == USB_SPEED_LOW)
+		status |= TD_CTRL_LS;
+
+	/* Build the TDs for the bulk request */
+	len = urb->transfer_buffer_length;
+	data = urb->transfer_buffer;
+	
+	do {					// TBD: Really allow zero-length packets?
+		int pktsze = len;
+
+		if (alloc_td (uhci, &td, UHCI_PTR_DEPTH * depth_first))
+			goto fail_unmap_enomem;
+
+		if (pktsze > maxsze)
+			pktsze = maxsze;
+
+		// pktsze bytes of data 
+		info = destination | (((pktsze - 1)&UHCI_NULL_DATA_SIZE) << 21) |
+			(uhci_get_toggle (urb) << TD_TOKEN_TOGGLE);
+
+		fill_td (td, status, info,
+			 urb_priv->transfer_buffer_dma + (data - (char *)urb->transfer_buffer));
+
+		data += pktsze;
+		len -= pktsze;
+		// Use USB_ZERO_PACKET to finish bulk OUTs always with a zero length packet
+		last = (len == 0 && (usb_pipein(pipe) || pktsze < maxsze || !(urb->transfer_flags & USB_ZERO_PACKET)));
+
+		if (last)
+			set_td_ioc(td);	// last one generates INT
+
+		insert_td (uhci, qh, td, UHCI_PTR_DEPTH * depth_first);
+		if (!first_td)
+			first_td=td;
+		uhci_do_toggle (urb);
+
+	} while (!last);
+
+	if (bulk_urb && bpriv)   // everything went OK, link with old bulk URB
+		bpriv->next_queued_urb=urb;
+
+	list_add (&qh->desc_list, &urb_priv->desc_list);
+
+	if (urb->transfer_flags & USB_QUEUE_BULK)
+		append_qh(uhci, td, bqh, UHCI_PTR_DEPTH * depth_first);
+
+	queue_urb_unlocked (uhci, urb);
+	
+	if (urb->transfer_flags & USB_QUEUE_BULK)
+		set_qh_element(qh, first_td->dma_addr);
+	else
+		qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM);    // arm QH
+
+	if (!bulk_urb) { 					// new bulk queue	
+		if (urb->transfer_flags & USB_QUEUE_BULK) {
+			spin_lock (&uhci->td_lock);		// both QHs in one go
+			insert_qh (uhci, uhci->chain_end, qh, 0);	// Main QH
+			insert_qh (uhci, uhci->chain_end, nqh, 0);	// Helper QH
+			spin_unlock (&uhci->td_lock);
+		}
+		else
+			insert_qh (uhci, uhci->chain_end, qh, 0);
+	}
+	
+	//dbg("uhci_submit_bulk_urb: exit\n");
+	return 0;
+
+fail_unmap_enomem:
+	delete_qh(uhci, qh);
+	if (bqh) 
+		delete_qh(uhci, bqh);
+	if (!bulk_urb && nqh)
+		delete_qh(uhci, nqh);
+
+	return -ENOMEM;
+}
+/*---------------------------------------------------------------------------*/
+// submits USB interrupt (ie. polling ;-) 
+// ASAP-flag set implicitely
+// if period==0, the transfer is only done once
+
+static int uhci_submit_int_urb (struct uhci_hcd *uhci, struct urb *urb)
+{
+	urb_priv_t *urb_priv = urb->hcpriv;
+	int nint, n;
+	uhci_desc_t *td;
+	int status, destination;
+	int info;
+	unsigned int pipe = urb->pipe;
+	
+	if (urb->interval == 0)
+		nint = 0;
+	else {  // round interval down to 2^n
+		for (nint = 0, n = 1; nint <= 8; nint++, n += n)	
+			if (urb->interval < n) {
+				urb->interval = n / 2;
+				break;
+			}
+		nint--;
+	}
+
+	dbg("Rounded interval to %i, chain  %i", urb->interval, nint);
+	// remember start frame, just in case...
+	urb->start_frame = UHCI_GET_CURRENT_FRAME (uhci) & 1023;	
+
+	urb->number_of_packets = 1;  // INT allows only one packet
+	
+	if (alloc_td (uhci, &td, UHCI_PTR_DEPTH))
+		return -ENOMEM;
+
+	status = TD_CTRL_ACTIVE | TD_CTRL_IOC
+		| (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD)
+		| (3 << 27);
+
+	if (urb->dev->speed == USB_SPEED_LOW)
+		status |= TD_CTRL_LS;
+
+	destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe) |
+		(((urb->transfer_buffer_length - 1) & 0x7ff) << 21);
+
+	info = destination | (uhci_get_toggle (urb) << TD_TOKEN_TOGGLE);
+	fill_td (td, status, info, urb_priv->transfer_buffer_dma);
+	list_add_tail (&td->desc_list, &urb_priv->desc_list);
+	queue_urb (uhci, urb);
+	insert_td_horizontal (uhci, uhci->int_chain[nint], td);	// store in INT-TDs
+	uhci_do_toggle (urb);
+
+	return 0;
+}
+/*###########################################################################*/
+//                  ISOCHRONOUS TRANSFERS
+/*###########################################################################*/
+
+// In case of ASAP iso transfer, search the URB-list for already queued URBs
+// for this EP and calculate the earliest start frame for the new
+// URB (easy seamless URB continuation!)
+static int find_iso_limits (struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end)
+{
+	struct urb *u, *last_urb = NULL;
+	urb_priv_t *priv;
+	struct list_head *p;
+	int ret=-1;
+	unsigned long flags;
+	
+	spin_lock_irqsave (&uhci->urb_list_lock, flags);
+	p=uhci->urb_list.prev;
+
+	for (; p != &uhci->urb_list; p = p->prev) {
+		priv = list_entry (p, urb_priv_t, urb_list);
+		u = priv->urb;
+		// look for pending URBs with identical pipe handle
+		// works only because iso doesn't toggle the data bit!
+		if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && (u->status == -EINPROGRESS)) {
+			if (!last_urb)
+				*start = u->start_frame;
+			last_urb = u;
+		}
+	}
+	
+	if (last_urb) {
+		*end = (last_urb->start_frame + last_urb->number_of_packets) & 1023;
+		ret=0;
+	}
+	
+	spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
+	
+	return ret;
+}
+/*-------------------------------------------------------------------*/
+// adjust start_frame according to scheduling constraints (ASAP etc)
+
+static int iso_find_start (struct uhci_hcd *uhci, struct urb *urb)
+{
+	unsigned int now;
+	unsigned int start_limit = 0, stop_limit = 0, queued_size;
+	int limits;
+
+	now = UHCI_GET_CURRENT_FRAME (uhci) & 1023;
+
+	if ((unsigned) urb->number_of_packets > 900)
+		return -EFBIG;
+	
+	limits = find_iso_limits (uhci, urb, &start_limit, &stop_limit);
+	queued_size = (stop_limit - start_limit) & 1023;
+
+	if (urb->transfer_flags & USB_ISO_ASAP) {
+		// first iso
+		if (limits) {
+			// 10ms setup should be enough //FIXME!
+			urb->start_frame = (now + 10) & 1023;
+		}
+		else {
+			urb->start_frame = stop_limit;		// seamless linkage
+
+			if (((now - urb->start_frame) & 1023) <= (unsigned) urb->number_of_packets) {
+				info("iso_find_start: gap in seamless isochronous scheduling");
+				dbg("iso_find_start: now %u start_frame %u number_of_packets %u pipe 0x%08x",
+					now, urb->start_frame, urb->number_of_packets, urb->pipe);
+				urb->start_frame = (now + 5) & 1023;	// 5ms setup should be enough
+			}
+		}
+	}
+	else {
+		urb->start_frame &= 1023;
+		if (((now - urb->start_frame) & 1023) < (unsigned) urb->number_of_packets) {
+			dbg("iso_find_start: now between start_frame and end");
+			return -EAGAIN;
+		}
+	}
+
+	/* check if either start_frame or start_frame+number_of_packets-1 lies between start_limit and stop_limit */
+	if (limits)
+		return 0;
+
+	if (((urb->start_frame - start_limit) & 1023) < queued_size ||
+	    ((urb->start_frame + urb->number_of_packets - 1 - start_limit) & 1023) < queued_size) {
+		dbg("iso_find_start: start_frame %u number_of_packets %u start_limit %u stop_limit %u",
+			urb->start_frame, urb->number_of_packets, start_limit, stop_limit);
+		return -EAGAIN;
+	}
+
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+static int uhci_submit_iso_urb (struct uhci_hcd *uhci, struct urb *urb, int mem_flags)
+{
+	urb_priv_t *urb_priv = urb->hcpriv;
+	int n=0, i, ret, last=0;
+	uhci_desc_t *td, **tdm;
+	int status, destination;
+	unsigned long flags;
+
+	tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), mem_flags);
+
+	if (!tdm) 
+		return -ENOMEM;
+
+	memset(tdm, 0, urb->number_of_packets * sizeof (uhci_desc_t*));
+
+	// First try to get all TDs. Cause: Removing already inserted TDs can only be done 
+	// racefree in three steps: unlink TDs, wait one frame, delete TDs. 
+	// So, this solutions seems simpler...
+
+	for (n = 0; n < urb->number_of_packets; n++) {
+		dbg("n:%d urb->iso_frame_desc[n].length:%d", n, urb->iso_frame_desc[n].length);
+		if (!urb->iso_frame_desc[n].length)
+			continue;  // allows ISO striping by setting length to zero in iso_descriptor
+		
+		if (alloc_td (uhci, &td, UHCI_PTR_DEPTH)) {
+			ret = -ENOMEM;
+			goto fail_unmap_tds;
+		}
+		
+		last=n;
+		tdm[n] = td;
+	}
+
+	__save_flags(flags);
+	__cli();		      // Disable IRQs to schedule all ISO-TDs in time
+	ret = iso_find_start (uhci, urb);	// adjusts urb->start_frame for later use
+	
+	if (ret) {
+		__restore_flags(flags);
+		n = urb->number_of_packets;
+		goto fail_unmap_tds;
+	}
+
+	status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
+
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid (urb->pipe);
+
+	// Queue all allocated TDs
+	for (n = 0; n < urb->number_of_packets; n++) {
+		td = tdm[n];
+		if (!td)
+			continue;
+			
+		if (n  == last) {
+			status |= TD_CTRL_IOC;
+			queue_urb (uhci, urb);
+		}
+
+		fill_td (td, status, destination | (((urb->iso_frame_desc[n].length - 1) & 0x7ff) << 21),
+			 urb_priv->transfer_buffer_dma + urb->iso_frame_desc[n].offset);
+		list_add_tail (&td->desc_list, &urb_priv->desc_list);
+	
+		insert_td_horizontal (uhci, uhci->iso_td[(urb->start_frame + n) & 1023], td);	// store in iso-tds
+	}
+
+	kfree (tdm);
+	dbg("ISO-INT# %i, start %i, now %i", urb->number_of_packets, urb->start_frame, UHCI_GET_CURRENT_FRAME (uhci) & 1023);
+	ret = 0;
+
+	__restore_flags(flags);
+	return ret;	
+
+	// Cleanup allocated TDs
+fail_unmap_tds:
+	dbg("ISO failed, free %i, ret %i",n,ret);
+	for (i = 0; i < n; i++)
+		if (tdm[i])
+			delete_desc(uhci, tdm[i]);
+	kfree (tdm);
+	return ret;
+}
+/*###########################################################################*/
+//                        URB UNLINK PROCESSING
+/*###########################################################################*/
+
+static void uhci_clean_iso_step1(struct uhci_hcd *uhci, urb_priv_t *urb_priv)
+{
+	struct list_head *p;
+	uhci_desc_t *td;
+	dbg("uhci_clean_iso_step1");
+	for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) {
+				td = list_entry (p, uhci_desc_t, desc_list);
+				unlink_td (uhci, td, 1);
+	}
+}
+/*-------------------------------------------------------------------*/
+static void uhci_clean_iso_step2(struct uhci_hcd *uhci, urb_priv_t *urb_priv)
+{
+	struct list_head *p;
+	uhci_desc_t *td;
+	int now=UHCI_GET_CURRENT_FRAME(uhci);
+
+	dbg("uhci_clean_iso_step2");
+	while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) {
+				td = list_entry (p, uhci_desc_t, desc_list);
+				list_del (p);
+				INIT_LIST_HEAD(&td->horizontal);
+				list_add_tail (&td->horizontal, &uhci->free_desc_td);
+				td->last_used=now;
+	}
+}
+/*-------------------------------------------------------------------*/
+/* mode: CLEAN_TRANSFER_NO_DELETION: unlink but no deletion mark (step 1 of async_unlink)
+         CLEAN_TRANSFER_REGULAR: regular (unlink/delete-mark)
+         CLEAN_TRANSFER_DELETION_MARK: deletion mark for QH (step 2 of async_unlink)
+ looks a bit complicated because of all the bulk queueing goodies
+*/
+
+static void uhci_clean_transfer (struct uhci_hcd *uhci, struct urb *urb, uhci_desc_t *qh, int mode)
+{
+	uhci_desc_t *bqh, *nqh, *prevqh, *prevtd;
+	urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;
+	int now=UHCI_GET_CURRENT_FRAME(uhci);
+
+	bqh=priv->bottom_qh;	
+	
+	if (!priv->next_queued_urb)  { // no more appended bulk queues
+
+		queue_dbg("uhci_clean_transfer: No more bulks for urb %p, qh %p, bqh %p, nqh %p", 
+			  urb, qh, bqh, priv->next_qh);	
+	
+		if (priv->prev_queued_urb && mode != CLEAN_TRANSFER_DELETION_MARK) {  // qh not top of the queue
+				unsigned long flags; 
+				urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
+
+				spin_lock_irqsave (&uhci->qh_lock, flags);
+				prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list);
+				prevtd = list_entry (prevqh->vertical.prev, uhci_desc_t, vertical);
+				set_td_link(prevtd, priv->bottom_qh->dma_addr | UHCI_PTR_QH); // skip current qh
+				mb();
+				queue_dbg("uhci_clean_transfer: relink pqh %p, ptd %p",prevqh, prevtd);
+				spin_unlock_irqrestore (&uhci->qh_lock, flags);
+
+				ppriv->bottom_qh = priv->bottom_qh;
+				ppriv->next_queued_urb = NULL;
+			}
+		else {   // queue is dead, qh is top of the queue
+			
+			if (mode != CLEAN_TRANSFER_DELETION_MARK) 				
+				unlink_qh(uhci, qh); // remove qh from horizontal chain
+
+			if (bqh) {  // remove remainings of bulk queue
+				nqh=priv->next_qh;
+
+				if (mode != CLEAN_TRANSFER_DELETION_MARK) 
+					unlink_qh(uhci, nqh);  // remove nqh from horizontal chain
+				
+				if (mode != CLEAN_TRANSFER_NO_DELETION) {  // add helper QHs to free desc list
+					nqh->last_used = bqh->last_used = now;
+					list_add_tail (&nqh->horizontal, &uhci->free_desc_qh);
+					list_add_tail (&bqh->horizontal, &uhci->free_desc_qh);
+				}			
+			}
+		}
+	}
+	else { // there are queued urbs following
+	
+	  queue_dbg("uhci_clean_transfer: urb %p, prevurb %p, nexturb %p, qh %p, bqh %p, nqh %p",
+		       urb, priv->prev_queued_urb,  priv->next_queued_urb, qh, bqh, priv->next_qh);	
+       	
+		if (mode != CLEAN_TRANSFER_DELETION_MARK) {	// no work for cleanup at unlink-completion
+			struct urb *nurb;
+			unsigned long flags;
+
+			nurb = priv->next_queued_urb;
+			spin_lock_irqsave (&uhci->qh_lock, flags);		
+
+			if (!priv->prev_queued_urb) { // top QH
+				
+				prevqh = list_entry (qh->horizontal.prev, uhci_desc_t, horizontal);
+				set_qh_head(prevqh, bqh->dma_addr | UHCI_PTR_QH);
+				list_del (&qh->horizontal);  // remove this qh from horizontal chain
+				list_add (&bqh->horizontal, &prevqh->horizontal); // insert next bqh in horizontal chain
+			}
+			else {		// intermediate QH
+				urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
+				urb_priv_t* npriv=(urb_priv_t*)nurb->hcpriv;
+				uhci_desc_t * bnqh;
+				
+				bnqh = list_entry (npriv->desc_list.next, uhci_desc_t, desc_list);
+				ppriv->bottom_qh = bnqh;
+				ppriv->next_queued_urb = nurb;				
+				prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list);
+				set_qh_head(prevqh, bqh->dma_addr | UHCI_PTR_QH);
+			}
+
+			mb();
+			((urb_priv_t*)nurb->hcpriv)->prev_queued_urb=priv->prev_queued_urb;
+			spin_unlock_irqrestore (&uhci->qh_lock, flags);
+		}		
+	}
+
+	if (mode != CLEAN_TRANSFER_NO_DELETION) {
+		qh->last_used = now;	
+		list_add_tail (&qh->horizontal, &uhci->free_desc_qh); // mark qh for later deletion/kfree
+	}
+}
+
+/*-------------------------------------------------------------------*/
+// async unlink_urb completion/cleanup work
+// has to be protected by urb_list_lock!
+// features: if set in transfer_flags, the resulting status of the killed
+// transaction is not overwritten
+
+static void uhci_cleanup_unlink(struct uhci_hcd *uhci, int force)
+{
+	struct list_head *q;
+	struct urb *urb;
+	urb_priv_t *urb_priv;
+	int type, now = UHCI_GET_CURRENT_FRAME(uhci);
+
+	q = uhci->urb_unlinked.next;
+
+	while (q != &uhci->urb_unlinked) {
+		urb_priv = list_entry (q, urb_priv_t, urb_list);
+		urb = urb_priv->urb;
+
+		q = urb_priv->urb_list.next;
+					
+		if (force || ((urb_priv->started != ~0) && (urb_priv->started != now))) {
+			async_dbg("async cleanup %p",urb);
+			type=usb_pipetype (urb->pipe);
+
+			switch (type) { // process descriptors
+			case PIPE_CONTROL:
+//				usb_show_device(urb->dev);
+				process_transfer (uhci, urb, CLEAN_TRANSFER_DELETION_MARK);  // don't unlink (already done)
+//				usb_show_device(urb->dev);
+				break;
+			case PIPE_BULK:
+				if (!uhci->avoid_bulk.counter)
+					process_transfer (uhci, urb, CLEAN_TRANSFER_DELETION_MARK); // don't unlink (already done)
+				else
+					continue;
+				break;
+			case PIPE_ISOCHRONOUS:
+				process_iso (uhci, urb, PROCESS_ISO_FORCE); // force, don't unlink
+				break;
+			case PIPE_INTERRUPT:
+				process_interrupt (uhci, urb, PROCESS_INT_REMOVE);
+				break;
+			}
+			
+			list_del (&urb_priv->urb_list);			
+			uhci_urb_dma_sync(uhci, urb, urb_priv);
+			// clean up descriptors for INT/ISO
+//			if (type==PIPE_ISOCHRONOUS || type==PIPE_INTERRUPT) 
+//				uhci_clean_iso_step2(uhci, urb_priv);
+	
+			uhci_free_priv(uhci, urb, urb_priv);		
+
+			if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))
+				urb->status = -ENOENT;  // now the urb is really dead
+
+			spin_unlock(&uhci->urb_list_lock);
+			usb_hcd_giveback_urb(&uhci->hcd, urb);
+			spin_lock(&uhci->urb_list_lock);
+		}
+	}
+}
+/*-------------------------------------------------------------------*/
+/* needs urb_list_lock!
+   mode: UNLINK_ASYNC_STORE_URB: unlink and move URB into unlinked list
+         UNLINK_ASYNC_DONT_STORE: unlink, don't move URB into unlinked list
+*/
+static int uhci_unlink_urb_async (struct uhci_hcd *uhci, struct urb *urb, int mode)
+{
+	uhci_desc_t *qh;
+	urb_priv_t *urb_priv;
+	
+	async_dbg("unlink_urb_async called %p",urb);
+	urb_priv = (urb_priv_t*)urb->hcpriv;
+	if (urb_priv==0) {
+		err("hc_priv for URB %p is zero!",urb);
+		return -EINVAL;
+	}
+	urb_priv->started = ~0;  // mark
+	dequeue_urb (uhci, urb);
+
+	if (mode==UNLINK_ASYNC_STORE_URB)
+		list_add_tail (&urb_priv->urb_list, &uhci->urb_unlinked); // store urb
+
+	uhci_switch_timer_int(uhci);
+	uhci->unlink_urb_done = 1;
+
+	switch (usb_pipetype (urb->pipe)) {
+	case PIPE_INTERRUPT:
+		uhci_do_toggle (urb);
+
+	case PIPE_ISOCHRONOUS:
+		uhci_clean_iso_step1 (uhci, urb_priv);
+		break;
+
+	case PIPE_BULK:
+	case PIPE_CONTROL:
+		qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
+		uhci_clean_transfer (uhci, urb, qh, CLEAN_TRANSFER_NO_DELETION);
+		break;
+	}
+	urb_priv->started = UHCI_GET_CURRENT_FRAME(uhci);
+	return 0;  // completion will follow
+}
+/*-------------------------------------------------------------------*/
+// kills an urb by unlinking descriptors and waiting for at least one frame
+static int uhci_unlink_urb_sync (struct uhci_hcd *uhci, struct urb *urb)
+{
+	uhci_desc_t *qh;
+	urb_priv_t *urb_priv;
+	unsigned long flags=0;
+
+	spin_lock_irqsave (&uhci->urb_list_lock, flags);
+//	err("uhci_unlink_urb_sync %p, %i",urb,urb->status);
+
+	// move descriptors out the the running chains, dequeue urb
+	uhci_unlink_urb_async(uhci, urb, UNLINK_ASYNC_DONT_STORE);
+
+	urb_priv = urb->hcpriv;
+
+	spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
+		
+	// cleanup the rest
+	switch (usb_pipetype (urb->pipe)) {
+	case PIPE_INTERRUPT:
+	case PIPE_ISOCHRONOUS:
+		uhci_wait_ms(1);
+		uhci_clean_iso_step2(uhci, urb_priv);
+		break;
+
+	case PIPE_BULK:
+	case PIPE_CONTROL:
+		qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
+		uhci_clean_transfer(uhci, urb, qh, CLEAN_TRANSFER_DELETION_MARK);
+		uhci_wait_ms(1);
+	}
+	urb->status = -ENOENT;	// mark urb as killed		
+		
+	finish_urb(uhci,urb);
+	
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+// unlink urbs for specific device or all devices
+static void uhci_unlink_urbs(struct uhci_hcd *uhci, struct usb_device *usb_dev, int remove_all)
+{
+	struct list_head *p;
+	struct list_head *p2;
+	struct urb *urb;
+	urb_priv_t *priv;
+	unsigned long flags;
+
+	spin_lock_irqsave (&uhci->urb_list_lock, flags);
+	p = uhci->urb_list.prev;	
+	while (p != &uhci->urb_list) {
+		p2 = p;
+		p = p->prev;
+		priv = list_entry (p2, urb_priv_t, urb_list);
+		urb = priv->urb;
+
+//		err("unlink urb: %p, dev %p, ud %p", urb, usb_dev,urb->dev);
+		
+		//urb->transfer_flags |=USB_ASYNC_UNLINK;
+			
+		if (remove_all || (usb_dev == urb->dev)) {
+			spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
+			err("forced removing of queued URB %p due to disconnect",urb);
+			uhci_urb_dequeue(&uhci->hcd, urb);
+			urb->dev = NULL; // avoid further processing of this URB
+			spin_lock_irqsave (&uhci->urb_list_lock, flags);
+			p = uhci->urb_list.prev;	
+		}
+	}
+	spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
+} 
+/*-------------------------------------------------------------------*/
+
+// Checks for URB timeout and removes bandwidth reclamation if URB idles too long
+static void uhci_check_timeouts(struct uhci_hcd *uhci)
+{
+	struct list_head *p,*p2;
+	struct urb *urb;
+	int type;	
+	
+	p = uhci->urb_list.prev;	
+
+	while (p != &uhci->urb_list) {
+		urb_priv_t *hcpriv;
+
+		p2 = p;
+		p = p->prev;
+		hcpriv = list_entry (p2,  urb_priv_t, urb_list);
+		urb = hcpriv->urb;
+		type = usb_pipetype (urb->pipe);
+
+		if ( urb->timeout && time_after(jiffies, hcpriv->started + urb->timeout)) {
+			urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK;
+			async_dbg("uhci_check_timeout: timeout for %p",urb);
+			uhci_unlink_urb_async(uhci, urb, UNLINK_ASYNC_STORE_URB);
+		}
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+		else if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&  
+			 (hcpriv->use_loop) && time_after(jiffies, hcpriv->started + IDLE_TIMEOUT))
+			disable_desc_loop(uhci, urb);
+#endif
+
+	}
+	uhci->timeout_check=jiffies;
+}
+/*###########################################################################*/
+//                        INTERRUPT PROCESSING ROUTINES
+/*###########################################################################*/
+/*
+ * Map status to standard result codes
+ *
+ * <status> is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)
+ * <dir_out> is True for output TDs and False for input TDs.
+ */
+static int uhci_map_status (int status, int dir_out)
+{
+	if (!status)
+		return 0;
+	if (status & TD_CTRL_BITSTUFF)	/* Bitstuff error */
+		return -EPROTO;
+	if (status & TD_CTRL_CRCTIMEO) {	/* CRC/Timeout */
+		if (dir_out)
+			return -ETIMEDOUT;
+		else
+			return -EILSEQ;
+	}
+	if (status & TD_CTRL_NAK)	/* NAK */
+		return -ETIMEDOUT;
+	if (status & TD_CTRL_BABBLE)	/* Babble */
+		return -EOVERFLOW;
+	if (status & TD_CTRL_DBUFERR)	/* Buffer error */
+		return -ENOSR;
+	if (status & TD_CTRL_STALLED)	/* Stalled */
+		return -EPIPE;
+	if (status & TD_CTRL_ACTIVE)	/* Active */
+		return 0;
+
+	return -EPROTO;
+}
+/*-------------------------------------------------------------------*/
+static void correct_data_toggles(struct urb *urb)
+{
+	usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), 
+		       !uhci_get_toggle (urb));
+
+	while(urb) {
+		urb_priv_t *priv=urb->hcpriv;		
+		uhci_desc_t *qh = list_entry (priv->desc_list.next, uhci_desc_t, desc_list);
+		struct list_head *p = qh->vertical.next;
+		uhci_desc_t *td;
+		dbg("URB to correct %p\n", urb);
+	
+		for (; p != &qh->vertical; p = p->next) {
+			td = list_entry (p, uhci_desc_t, vertical);
+			td->hw.td.info^=cpu_to_le32(1<<TD_TOKEN_TOGGLE);
+		}
+		urb=priv->next_queued_urb;
+	}
+}
+/*-------------------------------------------------------------------*/
+/* 
+ * For IN-control transfers, process_transfer gets a bit more complicated,
+ * since there are devices that return less data (eg. strings) than they
+ * have announced. This leads to a queue abort due to the short packet,
+ * the status stage is not executed. If this happens, the status stage
+ * is manually re-executed.
+ * mode: PROCESS_TRANSFER_REGULAR: regular (unlink QH)
+ *       PROCESS_TRANSFER_DONT_UNLINK: QHs already unlinked (for async unlink_urb)
+ */
+
+static int process_transfer (struct uhci_hcd *uhci, struct urb *urb, int mode)
+{
+	urb_priv_t *urb_priv = urb->hcpriv;
+	struct list_head *qhl = urb_priv->desc_list.next;
+	uhci_desc_t *qh = list_entry (qhl, uhci_desc_t, desc_list);
+	struct list_head *p = qh->vertical.next;
+	uhci_desc_t *desc= list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
+	uhci_desc_t *last_desc = list_entry (desc->vertical.prev, uhci_desc_t, vertical);
+	int data_toggle = uhci_get_toggle (urb);	// save initial data_toggle
+	int maxlength; 	// extracted and remapped info from TD
+	int actual_length;
+	int status = 0, ret = 0;
+
+	//dbg("process_transfer: urb %p, urb_priv %p, qh %p last_desc %p\n",urb,urb_priv, qh, last_desc);
+
+	/* if the status phase has been retriggered and the
+	   queue is empty or the last status-TD is inactive, the retriggered
+	   status stage is completed
+	 */
+
+	if (urb_priv->flags && 
+	    ((qh->hw.qh.element == cpu_to_le32(UHCI_PTR_TERM)) || !is_td_active(desc)))
+		goto transfer_finished;
+
+	urb->actual_length=0;
+
+	for (; p != &qh->vertical; p = p->next) {
+		desc = list_entry (p, uhci_desc_t, vertical);
+
+		if (is_td_active(desc)) {	// do not process active TDs
+			if (mode == CLEAN_TRANSFER_DELETION_MARK) // if called from async_unlink
+				uhci_clean_transfer(uhci, urb, qh, CLEAN_TRANSFER_DELETION_MARK);
+			return ret;
+		}
+	
+		actual_length = uhci_actual_length(desc);		// extract transfer parameters from TD
+		maxlength = (((le32_to_cpu(desc->hw.td.info) >> 21) & 0x7ff) + 1) & 0x7ff;
+		status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe));
+
+		if (status == -EPIPE) { 		// see if EP is stalled
+			// set up stalled condition
+			usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
+		}
+
+		if (status && (status != -EPIPE) && (status != -EOVERFLOW)) {	
+			// if any error occurred stop processing of further TDs
+			// only set ret if status returned an error
+			ret = status;
+			urb->error_count++;
+			break;
+		}
+		else if ((le32_to_cpu(desc->hw.td.info) & 0xff) != USB_PID_SETUP)
+			urb->actual_length += actual_length;
+
+		// got less data than requested
+		if ( (actual_length < maxlength)) {
+			if (urb->transfer_flags & USB_DISABLE_SPD) {
+				status = -EREMOTEIO;	// treat as real error
+				dbg("process_transfer: SPD!!");
+				break;	// exit after this TD because SP was detected
+			}
+
+			// short read during control-IN: re-start status stage
+			if ((usb_pipetype (urb->pipe) == PIPE_CONTROL)) {
+				if (uhci_packetid(le32_to_cpu(last_desc->hw.td.info)) == USB_PID_OUT) {
+			
+					set_qh_element(qh, last_desc->dma_addr);  // re-trigger status stage
+					dbg("short packet during control transfer, retrigger status stage @ %p",last_desc);
+					urb_priv->flags = 1; // mark as short control packet
+					return 0;
+				}
+			}
+			// all other cases: short read is OK
+			data_toggle = uhci_toggle (le32_to_cpu(desc->hw.td.info));
+			break;
+		}
+		else if (status) {
+			ret = status;
+			urb->error_count++;
+			break;
+		}
+
+		data_toggle = uhci_toggle (le32_to_cpu(desc->hw.td.info));
+		queue_dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", 
+			  actual_length, le32_to_cpu(desc->hw.td.status),status, data_toggle);      
+
+	}
+
+	/* toggle correction for short bulk transfers (nonqueued/queued) */
+	if (usb_pipetype (urb->pipe) == PIPE_BULK ) {  
+
+		urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;
+		struct urb *next_queued_urb=priv->next_queued_urb;
+
+		if (next_queued_urb) {
+			urb_priv_t *next_priv=(urb_priv_t*)next_queued_urb->hcpriv;
+			uhci_desc_t *qh = list_entry (next_priv->desc_list.next, uhci_desc_t, desc_list);
+			uhci_desc_t *first_td=list_entry (qh->vertical.next, uhci_desc_t, vertical);
+
+			if (data_toggle == uhci_toggle (le32_to_cpu(first_td->hw.td.info))) {
+				err("process_transfer: fixed toggle");
+				correct_data_toggles(next_queued_urb);
+			}						
+		}
+		else
+			usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !data_toggle);		
+	}
+
+ transfer_finished:
+    
+	uhci_clean_transfer(uhci, urb, qh, mode);
+	urb->status = status;
+
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH	
+	disable_desc_loop(uhci,urb);
+#endif	
+
+	dbg("process_transfer: (end) urb %p, wanted len %d, len %d status %x err %d",
+		urb,urb->transfer_buffer_length,urb->actual_length, urb->status, urb->error_count);
+	return ret;
+}
+/*-------------------------------------------------------------------*/
+static int process_interrupt (struct uhci_hcd *uhci, struct urb *urb, int mode)
+{
+	urb_priv_t *urb_priv = urb->hcpriv;
+	struct list_head *p = urb_priv->desc_list.next;
+	uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
+	int actual_length, status = 0, i, ret = -EINPROGRESS;
+
+	//dbg("urb contains interrupt request");
+
+	for (i = 0; p != &urb_priv->desc_list; p = p->next, i++)	// Maybe we allow more than one TD later ;-)
+	{
+		desc = list_entry (p, uhci_desc_t, desc_list);
+
+		if (is_td_active(desc) || !(desc->hw.td.status & cpu_to_le32(TD_CTRL_IOC))) {
+			// do not process active TDs or one-shot TDs (->no recycling)
+			//dbg("TD ACT Status @%p %08x",desc,le32_to_cpu(desc->hw.td.status));
+			break;
+		}
+	
+		// extract transfer parameters from TD
+		actual_length = uhci_actual_length(desc);
+		status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe));
+
+		// see if EP is stalled
+		if (status == -EPIPE) {
+			// set up stalled condition
+			usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
+		}
+
+		// if any error occurred: ignore this td, and continue
+		if (status != 0) {
+			//uhci_show_td (desc);
+			urb->error_count++;
+			goto recycle;
+		}
+		else
+			urb->actual_length = actual_length;
+
+	recycle:
+		uhci_urb_dma_sync(uhci, urb, urb->hcpriv);
+		if (urb->complete) {
+			//dbg("process_interrupt: calling completion, status %i",status);
+			urb->status = status;
+			((urb_priv_t*)urb->hcpriv)->flags=1; // if unlink_urb is called during completion
+
+			spin_unlock(&uhci->urb_list_lock);
+			urb->complete ((struct urb *) urb);
+			spin_lock(&uhci->urb_list_lock);
+
+			((urb_priv_t*)urb->hcpriv)->flags=0; // FIXME: unlink in completion not handled...
+		}
+		
+		if ((urb->status != -ECONNABORTED) && (urb->status != ECONNRESET) &&
+			    (urb->status != -ENOENT)) {
+
+			urb->status = -EINPROGRESS;
+
+			// Recycle INT-TD if interval!=0, else mark TD as one-shot
+			if (urb->interval) {
+				
+				desc->hw.td.info &= cpu_to_le32(~(1 << TD_TOKEN_TOGGLE));
+				if (status==0) {
+					((urb_priv_t*)urb->hcpriv)->started=jiffies;
+					desc->hw.td.info |= cpu_to_le32((uhci_get_toggle (urb) << TD_TOKEN_TOGGLE));
+					uhci_do_toggle (urb);
+				} else {
+					desc->hw.td.info |= cpu_to_le32((!uhci_get_toggle (urb) << TD_TOKEN_TOGGLE));
+				}
+				desc->hw.td.status= cpu_to_le32(TD_CTRL_ACTIVE | TD_CTRL_IOC |
+					(urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27));
+				if (urb->dev->speed == USB_SPEED_LOW)
+					desc->hw.td.status |=
+					    __constant_cpu_to_le32 (TD_CTRL_LS);
+				mb();
+			}
+			else {
+				uhci_unlink_urb_async(uhci, urb, UNLINK_ASYNC_STORE_URB);				
+				uhci_do_toggle (urb); // correct toggle after unlink
+				clr_td_ioc(desc);    // inactivate TD
+			}
+		}
+		if (mode == PROCESS_INT_REMOVE) {
+			INIT_LIST_HEAD(&desc->horizontal);
+			list_add_tail (&desc->horizontal, &uhci->free_desc_td);
+			desc->last_used=UHCI_GET_CURRENT_FRAME(uhci);
+		}
+	}
+
+	return ret;
+}
+/*-------------------------------------------------------------------*/
+// mode: PROCESS_ISO_REGULAR: processing only for done TDs, unlink TDs
+// mode: PROCESS_ISO_FORCE: force processing, don't unlink TDs (already unlinked)
+
+static int process_iso (struct uhci_hcd *uhci, struct urb *urb, int mode)
+{
+       	urb_priv_t *urb_priv = urb->hcpriv;
+	struct list_head *p = urb_priv->desc_list.next, *p_tmp;
+	uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
+	int i, ret = 0;
+	int now=UHCI_GET_CURRENT_FRAME(uhci);
+
+	dbg("urb contains iso request");
+	if (is_td_active(desc) && mode==PROCESS_ISO_REGULAR)
+		return -EXDEV;	// last TD not finished
+
+	urb->error_count = 0;
+	urb->actual_length = 0;
+	urb->status = 0;
+	dbg("process iso urb %p, %li, %i, %i, %i %08x",urb,jiffies,UHCI_GET_CURRENT_FRAME(s),
+	    urb->number_of_packets,mode,le32_to_cpu(desc->hw.td.status));
+
+	for (i = 0; p != &urb_priv->desc_list;  i++) {
+		desc = list_entry (p, uhci_desc_t, desc_list);
+		
+		//uhci_show_td(desc);
+		if (is_td_active(desc)) {
+			// means we have completed the last TD, but not the TDs before
+			desc->hw.td.status &= cpu_to_le32(~TD_CTRL_ACTIVE);
+			dbg("TD still active (%x)- grrr. paranoia!", le32_to_cpu(desc->hw.td.status));
+			ret = -EXDEV;
+			urb->iso_frame_desc[i].status = ret;
+			unlink_td (uhci, desc, 1); 
+			goto err;
+		}
+
+		if (mode == PROCESS_ISO_REGULAR)
+			unlink_td (uhci, desc, 1);
+
+		if (urb->number_of_packets <= i) {
+			dbg("urb->number_of_packets (%d)<=(%d)", urb->number_of_packets, i);
+			ret = -EINVAL;
+			goto err;
+		}
+
+		urb->iso_frame_desc[i].actual_length = uhci_actual_length(desc);
+		urb->iso_frame_desc[i].status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe));
+		urb->actual_length += urb->iso_frame_desc[i].actual_length;
+
+	err:
+		if (urb->iso_frame_desc[i].status != 0) {
+			urb->error_count++;
+			urb->status = urb->iso_frame_desc[i].status;
+		}
+		dbg("process_iso: %i: len:%d %08x status:%x",
+		     i, urb->iso_frame_desc[i].actual_length, le32_to_cpu(desc->hw.td.status),urb->iso_frame_desc[i].status);
+
+		p_tmp = p;
+		p = p->next;
+		list_del (p_tmp);
+//		delete_desc (uhci, desc);
+
+		// add to cool down pool
+		INIT_LIST_HEAD(&desc->horizontal);
+		list_add_tail (&desc->horizontal, &uhci->free_desc_td);
+		desc->last_used=now;
+	}
+	
+	dbg("process_iso: exit %i (%d), actual_len %i", i, ret,urb->actual_length);
+	return ret;
+}
+
+/*-------------------------------------------------------------------*/
+// called with urb_list_lock set
+static int process_urb (struct uhci_hcd *uhci, struct list_head *p)
+{
+	struct urb *urb, *urbt;	
+	struct usb_device *usb_dev;
+	urb_priv_t *priv;
+	int type, n, ret = 0;
+
+	priv=list_entry (p,  urb_priv_t, urb_list);
+	urb=priv->urb;
+//	dbg("process_urb p %p, udev %p",urb, urb->dev);
+	type=usb_pipetype (urb->pipe);
+
+	switch (type) {
+	case PIPE_CONTROL:
+		ret = process_transfer (uhci, urb, CLEAN_TRANSFER_REGULAR);
+		break;
+	case PIPE_BULK:
+		// if a submit is fiddling with bulk queues, ignore it for now
+		if (!uhci->avoid_bulk.counter)
+			ret = process_transfer (uhci, urb, CLEAN_TRANSFER_REGULAR);
+		else
+			return 0;
+		break;
+	case PIPE_ISOCHRONOUS:
+		ret = process_iso (uhci, urb, PROCESS_ISO_REGULAR);
+		break;
+	case PIPE_INTERRUPT:
+		ret = process_interrupt (uhci, urb, PROCESS_INT_REGULAR);
+		break;
+	}
+
+	if (urb->status != -EINPROGRESS) {
+
+		dequeue_urb (uhci, urb);
+
+		uhci_free_priv(uhci, urb, urb->hcpriv);
+
+		if (type != PIPE_INTERRUPT) {  // process_interrupt does completion on its own		
+			// FIXME: How to detect killed URBs in a ring?			
+
+			if (type == PIPE_ISOCHRONOUS) {
+				for (n=0, urbt = urb->next; urbt && (urbt != urb) && (n<MAX_NEXT_COUNT); urbt = urbt->next, n++)
+					continue;
+
+				if (urbt && (n<MAX_NEXT_COUNT)) {
+					usb_dev=urb->dev;
+					urb->dev = NULL;
+					spin_unlock(&uhci->urb_list_lock);
+					if (urb->complete)
+						urb->complete (urb);
+
+					urb->dev=usb_dev;
+					uhci_urb_enqueue(&uhci->hcd, urb, GFP_ATOMIC); // FIXME memflags!
+					spin_lock(&uhci->urb_list_lock);					
+				}
+				else {
+					spin_unlock(&uhci->urb_list_lock);
+					dbg("giveback iso urb %p, status %i, length %i\n", 
+					    urb, urb->status, urb->transfer_buffer_length);
+
+					usb_hcd_giveback_urb(&uhci->hcd, urb);
+					spin_lock(&uhci->urb_list_lock);
+				}
+			}
+			else {
+				spin_unlock(&uhci->urb_list_lock);
+				dbg("giveback urb %p, status %i, length %i\n", 
+				    urb, urb->status, urb->transfer_buffer_length);
+
+				usb_hcd_giveback_urb(&uhci->hcd, urb);
+				spin_lock(&uhci->urb_list_lock);
+			}
+		}
+	}
+	return ret;
+}
+/*###########################################################################*/
+//                        EMERGENCY ROOM
+/*###########################################################################*/
+/* used to reanimate a halted hostcontroller which signals no interrupts anymore.
+   This is a shortcut for unloading and reloading the module, and should be only
+   used as the last resort, but some VIA chips need it.
+*/
+static int hc_defibrillate(struct uhci_hcd *uhci)
+{
+	int ret;
+
+	err("Watchdog timeout, host controller obviously clinically dead, defibrillating...\n"
+	    "Expect disconnections for all devices on this controller!");
+
+	uhci->running=0;
+	outw (USBCMD_HCRESET, (int)uhci->hcd.regs + USBCMD);
+
+	uhci_stop(&uhci->hcd);
+
+	ret=init_skel(uhci);
+	if (ret)
+		return -ENOMEM;
+
+	set_td_ioc(uhci->td128ms); // enable watchdog interrupt
+	hc_irq_run(uhci);
+	uhci->reanimations++;
+	err("Host controller restart done...");
+	return 0;
+}
diff -Nru a/drivers/usb/host/usb-uhci.c b/drivers/usb/host/usb-uhci.c
--- a/drivers/usb/host/usb-uhci.c	Sat May 18 00:46:09 2002
+++ b/drivers/usb/host/usb-uhci.c	Sat May 18 00:46:09 2002
@@ -1217,7 +1217,7 @@
 			urb->dev = NULL;
 			urb->complete ((struct urb *) urb);
 		}
-		usb_dec_dev_use (usb_dev);
+		usb_put_dev (usb_dev);
 		usb_put_urb (urb);
 	}
 	else
@@ -1301,7 +1301,7 @@
 	
 			uhci_urb_dma_unmap(s, urb, urb_priv);
 
-			usb_dec_dev_use (dev);
+			usb_put_dev (dev);
 #ifdef DEBUG_SLAB
 			kmem_cache_free (urb_priv_kmem, urb_priv);
 #else
@@ -1655,7 +1655,7 @@
 	/* increment the reference count of the urb, as we now also control it */
 	urb = usb_get_urb (urb);
 
-	usb_inc_dev_use (urb->dev);
+	usb_get_dev (urb->dev);
 
 	spin_lock_irqsave (&s->urb_list_lock, flags);
 
@@ -1669,7 +1669,7 @@
 		    ((type == PIPE_BULK) &&
 		     (!(urb->transfer_flags & USB_QUEUE_BULK) || !(queued_urb->transfer_flags & USB_QUEUE_BULK)))) {
 			spin_unlock_irqrestore (&s->urb_list_lock, flags);
-			usb_dec_dev_use (urb->dev);
+			usb_put_dev (urb->dev);
 			usb_put_urb (urb);
 			err("ENXIO %08x, flags %x, urb %p, burb %p",urb->pipe,urb->transfer_flags,urb,queued_urb);
 			return -ENXIO;	// urb already queued
@@ -1682,7 +1682,7 @@
 	urb_priv = kmalloc (sizeof (urb_priv_t), mem_flags);
 #endif
 	if (!urb_priv) {
-		usb_dec_dev_use (urb->dev);
+		usb_put_dev (urb->dev);
 		spin_unlock_irqrestore (&s->urb_list_lock, flags);
 		usb_put_urb (urb);
 		return -ENOMEM;
@@ -1767,7 +1767,7 @@
 	
 	if (ret != 0) {
 		uhci_urb_dma_unmap(s, urb, urb_priv);
-		usb_dec_dev_use (urb->dev);
+		usb_put_dev (urb->dev);
 #ifdef DEBUG_SLAB
 		kmem_cache_free(urb_priv_kmem, urb_priv);
 #else
@@ -2737,7 +2737,7 @@
 				spin_lock(&s->urb_list_lock);
 			}
 			
-			usb_dec_dev_use (usb_dev);
+			usb_put_dev (usb_dev);
 			usb_put_urb (urb);
 		}
 	}
diff -Nru a/drivers/usb/media/usbvideo.c b/drivers/usb/media/usbvideo.c
--- a/drivers/usb/media/usbvideo.c	Sat May 18 00:46:09 2002
+++ b/drivers/usb/media/usbvideo.c	Sat May 18 00:46:09 2002
@@ -970,7 +970,7 @@
 	for (i=0; i < USBVIDEO_NUMSBUF; i++)
 		usb_free_urb(uvd->sbuf[i].urb);
 
-	usb_dec_dev_use(uvd->dev);
+	usb_put_dev(uvd->dev);
 	uvd->dev = NULL;    	    /* USB device is no more */
 
 	video_unregister_device(&uvd->vdev);
@@ -1176,7 +1176,7 @@
 	}
 #endif
 
-	usb_inc_dev_use(uvd->dev);
+	usb_get_dev(uvd->dev);
 	return 0;
 }
 
diff -Nru a/drivers/usb/net/cdc-ether.c b/drivers/usb/net/cdc-ether.c
--- a/drivers/usb/net/cdc-ether.c	Sat May 18 00:46:08 2002
+++ b/drivers/usb/net/cdc-ether.c	Sat May 18 00:46:08 2002
@@ -1256,7 +1256,7 @@
 	                            ether_dev );
 
 	// Does this REALLY do anything???
-	usb_inc_dev_use( usb );
+	usb_get_dev( usb );
 
 	// TODO - last minute HACK
 	ether_dev->comm_ep_in = 5;
@@ -1298,7 +1298,7 @@
 	ether_dev->net = NULL;
 
 	// I ask again, does this do anything???
-	usb_dec_dev_use( usb );
+	usb_put_dev( usb );
 
 	// We are done with this interface
 	usb_driver_release_interface( &CDCEther_driver, 
diff -Nru a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c
--- a/drivers/usb/net/pegasus.c	Sat May 18 00:46:15 2002
+++ b/drivers/usb/net/pegasus.c	Sat May 18 00:46:15 2002
@@ -981,7 +981,7 @@
 		return NULL;
 	}
 
-	usb_inc_dev_use(dev);
+	usb_get_dev(dev);
 	memset(pegasus, 0, sizeof(struct pegasus));
 	pegasus->dev_index = dev_index;
 	init_waitqueue_head(&pegasus->ctrl_wait);
@@ -1086,7 +1086,7 @@
 
 	pegasus->flags |= PEGASUS_UNPLUG;
 	unregister_netdev(pegasus->net);
-	usb_dec_dev_use(dev);
+	usb_put_dev(dev);
 	usb_unlink_urb(pegasus->intr_urb);
 	usb_unlink_urb(pegasus->tx_urb);
 	usb_unlink_urb(pegasus->rx_urb);
diff -Nru a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c
--- a/drivers/usb/net/usbnet.c	Sat May 18 00:46:08 2002
+++ b/drivers/usb/net/usbnet.c	Sat May 18 00:46:08 2002
@@ -1951,7 +1951,7 @@
 	flush_scheduled_tasks ();
 
 	kfree (dev);
-	usb_dec_dev_use (udev);
+	usb_put_dev (udev);
 }
 
 
@@ -1997,7 +1997,7 @@
 	memset (dev, 0, sizeof *dev);
 
 	init_MUTEX_LOCKED (&dev->mutex);
-	usb_inc_dev_use (udev);
+	usb_get_dev (udev);
 	dev->udev = udev;
 	dev->driver_info = info;
 	dev->msg_level = msg_level;
diff -Nru a/drivers/usb/storage/Config.help b/drivers/usb/storage/Config.help
--- a/drivers/usb/storage/Config.help	Sat May 18 00:46:08 2002
+++ b/drivers/usb/storage/Config.help	Sat May 18 00:46:08 2002
@@ -40,3 +40,7 @@
   Say Y here to include additional code to support the Sandisk SDDR-09
   SmartMedia reader in the USB Mass Storage driver.
   Also works for the Microtech Zio! SmartMedia reader.
+
+CONFIG_USB_STORAGE_SDDR55
+  Say Y here to include additional code to support the Sandisk SDDR-55
+  SmartMedia reader in the USB Mass Storage driver.
diff -Nru a/drivers/usb/storage/Config.in b/drivers/usb/storage/Config.in
--- a/drivers/usb/storage/Config.in	Sat May 18 00:46:08 2002
+++ b/drivers/usb/storage/Config.in	Sat May 18 00:46:08 2002
@@ -12,4 +12,5 @@
    dep_mbool '    Microtech CompactFlash/SmartMedia support' CONFIG_USB_STORAGE_DPCM $CONFIG_USB_STORAGE
    dep_mbool '    HP CD-Writer 82xx support' CONFIG_USB_STORAGE_HP8200e $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL
    dep_mbool '    SanDisk SDDR-09 (and other SmartMedia) support' CONFIG_USB_STORAGE_SDDR09 $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL
+   dep_mbool '    SanDisk SDDR-55 SmartMedia support' CONFIG_USB_STORAGE_SDDR55 $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL
    dep_mbool '    Lexar Jumpshot Compact Flash Reader' CONFIG_USB_STORAGE_JUMPSHOT $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL
diff -Nru a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile
--- a/drivers/usb/storage/Makefile	Sat May 18 00:46:14 2002
+++ b/drivers/usb/storage/Makefile	Sat May 18 00:46:14 2002
@@ -13,6 +13,7 @@
 usb-storage-obj-$(CONFIG_USB_STORAGE_DEBUG)	+= debug.o
 usb-storage-obj-$(CONFIG_USB_STORAGE_HP8200e)	+= shuttle_usbat.o
 usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR09)	+= sddr09.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR55)	+= sddr55.o
 usb-storage-obj-$(CONFIG_USB_STORAGE_FREECOM)	+= freecom.o
 usb-storage-obj-$(CONFIG_USB_STORAGE_DPCM)	+= dpcm.o
 usb-storage-obj-$(CONFIG_USB_STORAGE_ISD200)	+= isd200.o
diff -Nru a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c
--- a/drivers/usb/storage/datafab.c	Sat May 18 00:46:15 2002
+++ b/drivers/usb/storage/datafab.c	Sat May 18 00:46:15 2002
@@ -1,16 +1,25 @@
 /* Driver for Datafab USB Compact Flash reader
  *
+ * $Id: datafab.c,v 1.7 2002/02/25 00:40:13 mdharm Exp $
+ *
  * datafab driver v0.1:
  *
  * First release
  *
  * Current development and maintenance by:
  *   (c) 2000 Jimmie Mayfield (mayfield+datafab@sackheads.org)
- *   many thanks to Robert Baruch for the SanDisk SmartMedia reader driver
+ *
+ *   Many thanks to Robert Baruch for the SanDisk SmartMedia reader driver
  *   which I used as a template for this driver.
+ *
  *   Some bugfixes and scatter-gather code by Gregory P. Smith 
  *   (greg-usb@electricrain.com)
  *
+ *   Fix for media change by Joerg Schneider (js@joergschneider.com)
+ *
+ * Other contributors:
+ *   (c) 2002 Alan Stern <stern@rowland.org>
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2, or (at your option) any
@@ -102,7 +111,7 @@
 	if (result == -EPIPE) {
 		US_DEBUGP("datafab_raw_bulk: EPIPE. clearing endpoint halt for"
 			  " pipe 0x%x, stalled at %d bytes\n", pipe, act_len);
-		usb_clear_halt(us->pusb_dev, pipe);
+		usb_stor_clear_halt(us, pipe);
 	}
 
 	if (result) {
@@ -256,7 +265,7 @@
 			while (sg_idx < use_sg && transferred < len) {
 				if (len - transferred >= sg[sg_idx].length - current_sg_offset) {
 					US_DEBUGP("datafab_read_data:  adding %d bytes to %d byte sg buffer\n", sg[sg_idx].length - current_sg_offset, sg[sg_idx].length);
-					memcpy(sg[sg_idx].address + current_sg_offset,
+					memcpy(page_address(sg[sg_idx].page) + sg[sg_idx].offset + current_sg_offset,
 					       buffer + transferred,
 					       sg[sg_idx].length - current_sg_offset);
 					transferred += sg[sg_idx].length - current_sg_offset;
@@ -265,7 +274,7 @@
 					++sg_idx;
 				} else {
 					US_DEBUGP("datafab_read_data:  adding %d bytes to %d byte sg buffer\n", len - transferred, sg[sg_idx].length);
-					memcpy(sg[sg_idx].address + current_sg_offset,
+					memcpy(page_address(sg[sg_idx].page) + sg[sg_idx].offset + current_sg_offset,
 					       buffer + transferred,
 					       len - transferred);
 					current_sg_offset += len - transferred;
@@ -347,7 +356,7 @@
 				if (len - transferred >= sg[sg_idx].length - current_sg_offset) {
 					US_DEBUGP("datafab_write_data:  getting %d bytes from %d byte sg buffer\n", sg[sg_idx].length - current_sg_offset, sg[sg_idx].length);
 					memcpy(ptr + transferred,
-					       sg[sg_idx].address + current_sg_offset,
+					       page_address(sg[sg_idx].page) + sg[sg_idx].offset + current_sg_offset,
 					       sg[sg_idx].length - current_sg_offset);
 					transferred += sg[sg_idx].length - current_sg_offset;
 					current_sg_offset = 0;
@@ -356,7 +365,7 @@
 				} else {
 					US_DEBUGP("datafab_write_data:  getting %d bytes from %d byte sg buffer\n", len - transferred, sg[sg_idx].length);
 					memcpy(ptr + transferred,
-					       sg[sg_idx].address + current_sg_offset,
+					       page_address(sg[sg_idx].page) + sg[sg_idx].offset + current_sg_offset,
 					       len - transferred);
 					current_sg_offset += len - transferred;
 					// we only copied part of this sg buffer
@@ -800,6 +809,23 @@
 		//
 		return USB_STOR_TRANSPORT_GOOD;
 	}
+
+	if (srb->cmnd[0] == START_STOP) {
+		/* this is used by sd.c'check_scsidisk_media_change to detect
+		   media change */
+		US_DEBUGP("datafab_transport:  START_STOP.\n");
+		/* the first datafab_id_device after a media change returns
+		   an error (determined experimentally) */
+		rc = datafab_id_device(us, info);
+		if (rc == USB_STOR_TRANSPORT_GOOD) {
+			info->sense_key = NO_SENSE;
+			srb->result = SUCCESS;
+		} else {
+			info->sense_key = UNIT_ATTENTION;
+			srb->result = CHECK_CONDITION << 1;
+		}
+		return rc;
+        }
 
 	US_DEBUGP("datafab_transport:  Gah! Unknown command: %d (0x%x)\n", srb->cmnd[0], srb->cmnd[0]);
 	return USB_STOR_TRANSPORT_ERROR;
diff -Nru a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c
--- a/drivers/usb/storage/debug.c	Sat May 18 00:46:08 2002
+++ b/drivers/usb/storage/debug.c	Sat May 18 00:46:08 2002
@@ -1,10 +1,13 @@
 /* Driver for USB Mass Storage compliant devices
  * Debugging Functions Source Code File
  *
- * $Id: debug.c,v 1.5 2001/06/27 23:20:45 mdharm Exp $
+ * $Id: debug.c,v 1.9 2002/04/22 03:39:43 mdharm Exp $
  *
  * Current development and maintenance by:
- *   (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *   (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+ * Developed with the assistance of:
+ *   (c) 2002 Alan Stern <stern@rowland.org>
  *
  * Initial work by:
  *   (c) 1999 Michael Gee (michael@linuxspecific.com)
@@ -302,9 +305,11 @@
 	case 0x1902: what="defect list error in primary list"; break;
 	case 0x1903: what="defect list error in grown list"; break;
 	case 0x1C00: what="defect list not found"; break;
+	case 0x2000: what="invalid command operation code"; break;
 	case 0x2400: what="invalid field in CDB"; break;
 	case 0x2703: what="associated write protect"; break;
 	case 0x2800: what="not ready to ready transition"; break;
+	case 0x2900: what="device reset occurred"; break;
 	case 0x2903: what="bus device reset function occurred"; break;
 	case 0x2904: what="device internal reset"; break;
 	case 0x2B00: what="copy can't execute / host can't disconnect"; break;
@@ -327,7 +332,7 @@
 	case 0x3502: what="enclosure services unavailable"; break;
 	case 0x3503: what="enclosure services transfer failure"; break;
 	case 0x3504: what="enclosure services transfer refused"; break;
-	case 0x3A00: what="medium not present"; break;
+	case 0x3A00: what="media not present"; break;
 	case 0x3B0F: what="end of medium reached"; break;
 	case 0x3F02: what="changed operating definition"; break;
 	case 0x4100: what="data path failure (should use 40 NN)"; break;
diff -Nru a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c
--- a/drivers/usb/storage/freecom.c	Sat May 18 00:46:09 2002
+++ b/drivers/usb/storage/freecom.c	Sat May 18 00:46:09 2002
@@ -1,6 +1,6 @@
 /* Driver for Freecom USB/IDE adaptor
  *
- * $Id: freecom.c,v 1.21 2001/12/29 03:47:33 mdharm Exp $
+ * $Id: freecom.c,v 1.22 2002/04/22 03:39:43 mdharm Exp $
  *
  * Freecom v0.1:
  *
diff -Nru a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c
--- a/drivers/usb/storage/isd200.c	Sat May 18 00:46:09 2002
+++ b/drivers/usb/storage/isd200.c	Sat May 18 00:46:10 2002
@@ -1,9 +1,15 @@
 /* Transport & Protocol Driver for In-System Design, Inc. ISD200 ASIC
  *
- * First release
+ * $Id: isd200.c,v 1.16 2002/04/22 03:39:43 mdharm Exp $
  *
- * Current development and maintenance by:
- *   (c) 2000 In-System Design, Inc. (support@in-system.com)
+ * Current development and maintenance:
+ *   (C) 2001-2002 Björn Stenberg (bjorn@haxx.se)
+ *
+ * Developed with the assistance of:
+ *   (C) 2002 Alan Stern <stern@rowland.org>
+ *
+ * Initial work:
+ *   (C) 2000 In-System Design, Inc. (support@in-system.com)
  *
  * The ISD200 ASIC does not natively support ATA devices.  The chip
  * does implement an interface, the ATA Command Block (ATACB) which provides
@@ -27,6 +33,10 @@
  *
  *  2001-02-24: Removed lots of duplicate code and simplified the structure.
  *              (bjorn@haxx.se)
+ *  2002-01-16: Fixed endianness bug so it works on the ppc arch.
+ *              (Luc Saillard <luc@saillard.org>)
+ *  2002-01-17: All bitfields removed.
+ *              (bjorn@haxx.se)
  */
 
 
@@ -45,15 +55,6 @@
 #include <linux/hdreg.h>
 #include <linux/ide.h>
 
-/*
- * Inquiry defines. Used to interpret data returned from target as result
- * of inquiry command.
- *
- * DeviceType field
- */
-
-#define DIRECT_ACCESS_DEVICE            0x00    /* disks */
-
 /* Timeout defines (in Seconds) */
 
 #define ISD200_ENUM_BSY_TIMEOUT         35
@@ -88,6 +89,19 @@
 #define ACTION_SELECT_6             0x40
 #define ACTION_SELECT_7             0x80
 
+/* Register Select bits */
+#define REG_ALTERNATE_STATUS 0x01
+#define REG_DEVICE_CONTROL   0x01
+#define REG_ERROR            0x02
+#define REG_FEATURES         0x02
+#define REG_SECTOR_COUNT     0x04
+#define REG_SECTOR_NUMBER    0x08
+#define REG_CYLINDER_LOW     0x10
+#define REG_CYLINDER_HIGH    0x20
+#define REG_DEVICE_HEAD      0x40
+#define REG_STATUS           0x80
+#define REG_COMMAND          0x80
+
 /* ATA error definitions not in <linux/hdreg.h> */
 #define ATA_ERROR_MEDIA_CHANGE       0x20
 
@@ -152,20 +166,8 @@
 	struct {
 		unsigned char SignatureByte0;
 		unsigned char SignatureByte1;
-		unsigned char ReadRegisterAccessBit : 1;
-		unsigned char NoDeviceSelectionBit : 1;
-		unsigned char NoBSYPollBit : 1;
-		unsigned char IgnorePhaseErrorBit : 1;
-		unsigned char IgnoreDeviceErrorBit : 1;
-		unsigned char Reserved0Bit : 3;
-		unsigned char SelectAlternateStatus : 1;
-		unsigned char SelectError : 1;
-		unsigned char SelectSectorCount : 1;
-		unsigned char SelectSectorNumber : 1;
-		unsigned char SelectCylinderLow : 1;
-		unsigned char SelectCylinderHigh : 1;
-		unsigned char SelectDeviceHead : 1;
-		unsigned char SelectStatus : 1;
+		unsigned char ActionSelect;
+		unsigned char RegisterSelect;
 		unsigned char TransferBlockSize;
 		unsigned char AlternateStatusByte;
 		unsigned char ErrorByte;
@@ -181,20 +183,8 @@
         struct {
 		unsigned char SignatureByte0;
 		unsigned char SignatureByte1;
-		unsigned char ReadRegisterAccessBit : 1;
-		unsigned char NoDeviceSelectionBit : 1;
-		unsigned char NoBSYPollBit : 1;
-		unsigned char IgnorePhaseErrorBit : 1;
-		unsigned char IgnoreDeviceErrorBit : 1;
-		unsigned char Reserved0Bit : 3;
-		unsigned char SelectDeviceControl : 1;
-		unsigned char SelectFeatures : 1;
-		unsigned char SelectSectorCount : 1;
-		unsigned char SelectSectorNumber : 1;
-		unsigned char SelectCylinderLow : 1;
-		unsigned char SelectCylinderHigh : 1;
-		unsigned char SelectDeviceHead : 1;
-		unsigned char SelectCommand : 1;
+		unsigned char ActionSelect;
+		unsigned char RegisterSelect;
 		unsigned char TransferBlockSize;
 		unsigned char DeviceControlByte;
 		unsigned char FeaturesByte;
@@ -218,27 +208,20 @@
  * includes fields through ProductRevisionLevel.
  */
 
+/*
+ * DeviceType field
+ */
+#define DIRECT_ACCESS_DEVICE            0x00    /* disks */
+#define DEVICE_REMOVABLE                0x80
+
 struct inquiry_data {
-	unsigned char DeviceType : 5;
-	unsigned char DeviceTypeQualifier : 3;
-	unsigned char DeviceTypeModifier : 7;
-	unsigned char RemovableMedia : 1;
+   	unsigned char DeviceType;
+	unsigned char DeviceTypeModifier;
 	unsigned char Versions;
-	unsigned char ResponseDataFormat : 4;
-	unsigned char HiSupport : 1;
-	unsigned char NormACA : 1;
-	unsigned char ReservedBit : 1;
-	unsigned char AERC : 1;
+	unsigned char Format; 
 	unsigned char AdditionalLength;
 	unsigned char Reserved[2];
-	unsigned char SoftReset : 1;
-	unsigned char CommandQueue : 1;
-	unsigned char Reserved2 : 1;
-	unsigned char LinkedCommands : 1;
-	unsigned char Synchronous : 1;
-	unsigned char Wide16Bit : 1;
-	unsigned char Wide32Bit : 1;
-	unsigned char RelativeAddressing : 1;
+	unsigned char Capability;
 	unsigned char VendorId[8];
 	unsigned char ProductId[16];
 	unsigned char ProductRevisionLevel[4];
@@ -257,25 +240,30 @@
  * ISD200 CONFIG data struct
  */
 
+#define ATACFG_TIMING          0x0f
+#define ATACFG_ATAPI_RESET     0x10
+#define ATACFG_MASTER          0x20
+#define ATACFG_BLOCKSIZE       0xa0
+
+#define ATACFGE_LAST_LUN       0x07
+#define ATACFGE_DESC_OVERRIDE  0x08
+#define ATACFGE_STATE_SUSPEND  0x10
+#define ATACFGE_SKIP_BOOT      0x20
+#define ATACFGE_CONF_DESC2     0x40
+#define ATACFGE_INIT_STATUS    0x80
+
+#define CFG_CAPABILITY_SRST    0x01
+
 struct isd200_config {
         unsigned char EventNotification;
         unsigned char ExternalClock;
         unsigned char ATAInitTimeout;
-        unsigned char ATATiming : 4;
-        unsigned char ATAPIReset : 1;
-        unsigned char MasterSlaveSelection : 1;
-        unsigned char ATAPICommandBlockSize : 2;
+	unsigned char ATAConfig;
         unsigned char ATAMajorCommand;
         unsigned char ATAMinorCommand;
-        unsigned char LastLUNIdentifier : 3;
-        unsigned char DescriptOverride : 1;
-        unsigned char ATA3StateSuspend : 1;
-        unsigned char SkipDeviceBoot : 1;
-        unsigned char ConfigDescriptor2 : 1;
-        unsigned char InitStatus : 1;
-        unsigned char SRSTEnable : 1;
-        unsigned char Reserved0 : 7;
-};
+	unsigned char ATAExtraConfig;
+	unsigned char Capability;
+}__attribute__ ((packed));
 
 
 /*
@@ -321,15 +309,16 @@
  * Sense Data Format
  */
 
+#define SENSE_ERRCODE           0x7f
+#define SENSE_ERRCODE_VALID     0x80
+#define SENSE_FLAG_SENSE_KEY    0x0f
+#define SENSE_FLAG_BAD_LENGTH   0x20
+#define SENSE_FLAG_END_OF_MEDIA 0x40
+#define SENSE_FLAG_FILE_MARK    0x80
 struct sense_data {
-        unsigned char ErrorCode:7;
-        unsigned char Valid:1;
-        unsigned char SegmentNumber;
-        unsigned char SenseKey:4;
-        unsigned char Reserved:1;
-        unsigned char IncorrectLength:1;
-        unsigned char EndOfMedia:1;
-        unsigned char FileMark:1;
+	unsigned char ErrorCode;
+	unsigned char SegmentNumber;
+	unsigned char Flags;
         unsigned char Information[4];
         unsigned char AdditionalSenseLength;
         unsigned char CommandSpecificInformation[4];
@@ -349,7 +338,6 @@
  * Helper routines
  ***********************************************************************/
 
-
 /**************************************************************************
  * isd200_build_sense
  *                                                                         
@@ -366,38 +354,33 @@
         unsigned char error = info->ATARegs[IDE_ERROR_OFFSET];
 
 	if(error & ATA_ERROR_MEDIA_CHANGE) {
-		buf->ErrorCode = 0x70;
-		buf->Valid     = 1;
+		buf->ErrorCode = 0x70 | SENSE_ERRCODE_VALID;
 		buf->AdditionalSenseLength = 0xb;
-		buf->SenseKey =  UNIT_ATTENTION;
+		buf->Flags = UNIT_ATTENTION;
 		buf->AdditionalSenseCode = 0;
 		buf->AdditionalSenseCodeQualifier = 0;
         } else if(error & MCR_ERR) {
-		buf->ErrorCode = 0x70;
-		buf->Valid     = 1;
+		buf->ErrorCode = 0x70 | SENSE_ERRCODE_VALID;
 		buf->AdditionalSenseLength = 0xb;
-		buf->SenseKey =  UNIT_ATTENTION;
+		buf->Flags =  UNIT_ATTENTION;
 		buf->AdditionalSenseCode = 0;
 		buf->AdditionalSenseCodeQualifier = 0;
         } else if(error & TRK0_ERR) {
-		buf->ErrorCode = 0x70;
-		buf->Valid     = 1;
+		buf->ErrorCode = 0x70 | SENSE_ERRCODE_VALID;
 		buf->AdditionalSenseLength = 0xb;
-		buf->SenseKey =  NOT_READY;
+		buf->Flags =  NOT_READY;
 		buf->AdditionalSenseCode = 0;
 		buf->AdditionalSenseCodeQualifier = 0;
         } else if(error & ECC_ERR) {
-		buf->ErrorCode = 0x70;
-		buf->Valid     = 1;
+		buf->ErrorCode = 0x70 | SENSE_ERRCODE_VALID;
 		buf->AdditionalSenseLength = 0xb;
-		buf->SenseKey =  DATA_PROTECT;
+		buf->Flags =  DATA_PROTECT;
 		buf->AdditionalSenseCode = 0;
 		buf->AdditionalSenseCodeQualifier = 0;
         } else {
 		buf->ErrorCode = 0;
-		buf->Valid     = 0;
 		buf->AdditionalSenseLength = 0;
-		buf->SenseKey =  0;
+		buf->Flags =  0;
 		buf->AdditionalSenseCode = 0;
 		buf->AdditionalSenseCodeQualifier = 0;
         }
@@ -442,7 +425,7 @@
         /* if we stall, we need to clear it before we go on */
         if (result == -EPIPE) {
                 US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
-                usb_stor_clear_halt(us->pusb_dev, pipe);
+                usb_stor_clear_halt(us, pipe);
         }
     
         /* did we send all the data? */
@@ -524,7 +507,7 @@
                         } else
                                 result = isd200_transfer_partial(us, 
                                                                  srb->sc_data_direction,                            
-                                                                 page_address(sg[i].page) + sg[i].offset,
+                                                                 page_address(sg[i].page) + sg[i].offset, 
                                                                  transfer_amount - total_transferred);
 
                         /* if we get an error, end the loop here */
@@ -593,7 +576,7 @@
         US_DEBUGP("Bulk command S 0x%x T 0x%x Trg %d LUN %d L %d F %d CL %d\n",
                   le32_to_cpu(bcb.Signature), bcb.Tag,
                   (bcb.Lun >> 4), (bcb.Lun & 0xFF), 
-                  bcb.DataTransferLength, bcb.Flags, bcb.Length);
+                  le32_to_cpu(bcb.DataTransferLength), bcb.Flags, bcb.Length);
         result = usb_stor_bulk_msg(us, &bcb, pipe, US_BULK_CB_WRAP_LEN, 
 				   &partial);
         US_DEBUGP("Bulk command transfer result=%d\n", result);
@@ -603,7 +586,7 @@
 	else if (result == -EPIPE) {
 		/* if we stall, we need to clear it before we go on */
                 US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
-                usb_stor_clear_halt(us->pusb_dev, pipe);
+                usb_stor_clear_halt(us, pipe);
 	} else if (result)  
                 return ISD200_TRANSPORT_ERROR;
     
@@ -633,7 +616,7 @@
         /* did the attempt to read the CSW fail? */
         if (result == -EPIPE) {
                 US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
-                usb_stor_clear_halt(us->pusb_dev, pipe);
+                usb_stor_clear_halt(us, pipe);
            
                 /* get the status again */
                 US_DEBUGP("Attempting to get CSW (2nd try)...\n");
@@ -647,7 +630,7 @@
                 /* if it fails again, we need a reset and return an error*/
                 if (result == -EPIPE) {
                         US_DEBUGP("clearing halt for pipe 0x%x\n", pipe);
-                        usb_stor_clear_halt(us->pusb_dev, pipe);
+                        usb_stor_clear_halt(us, pipe);
                         return ISD200_TRANSPORT_ERROR;
                 }
         }
@@ -716,10 +699,9 @@
 	case ACTION_READ_STATUS:
 		US_DEBUGP("   isd200_action(READ_STATUS)\n");
 		ata.generic.ActionSelect = ACTION_SELECT_0|ACTION_SELECT_2;
-		ata.read.SelectStatus = 1;
-		ata.read.SelectError = 1;
-		ata.read.SelectCylinderHigh = 1;
-		ata.read.SelectCylinderLow = 1;
+		ata.generic.RegisterSelect =
+		  REG_CYLINDER_LOW | REG_CYLINDER_HIGH |
+		  REG_STATUS | REG_ERROR;
 		srb.sc_data_direction = SCSI_DATA_READ;
 		srb.request_buffer = pointer;
 		srb.request_bufflen = value;
@@ -730,7 +712,7 @@
 		ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_2|
 			                   ACTION_SELECT_3|ACTION_SELECT_4|
 		                           ACTION_SELECT_5;
-		ata.write.SelectDeviceHead = 1;
+		ata.generic.RegisterSelect = REG_DEVICE_HEAD;
 		ata.write.DeviceHeadByte = value;
 		srb.sc_data_direction = SCSI_DATA_NONE;
 		break;
@@ -739,7 +721,7 @@
 		US_DEBUGP("   isd200_action(RESET)\n");
 		ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_2|
 			                   ACTION_SELECT_3|ACTION_SELECT_4;
-		ata.write.SelectDeviceControl = 1;
+		ata.generic.RegisterSelect = REG_DEVICE_CONTROL;
 		ata.write.DeviceControlByte = ATA_DC_RESET_CONTROLLER;
 		srb.sc_data_direction = SCSI_DATA_NONE;
 		break;
@@ -748,7 +730,7 @@
 		US_DEBUGP("   isd200_action(REENABLE)\n");
 		ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_2|
 			                   ACTION_SELECT_3|ACTION_SELECT_4;
-		ata.write.SelectDeviceControl = 1;
+		ata.generic.RegisterSelect = REG_DEVICE_CONTROL;
 		ata.write.DeviceControlByte = ATA_DC_REENABLE_CONTROLLER;
 		srb.sc_data_direction = SCSI_DATA_NONE;
 		break;
@@ -756,16 +738,15 @@
 	case ACTION_SOFT_RESET:
 		US_DEBUGP("   isd200_action(SOFT_RESET)\n");
 		ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_5;
-		ata.write.SelectDeviceHead = 1;
+		ata.generic.RegisterSelect = REG_DEVICE_HEAD | REG_COMMAND;
 		ata.write.DeviceHeadByte = info->DeviceHead;
-		ata.write.SelectCommand = 1;
 		ata.write.CommandByte = WIN_SRST;
 		srb.sc_data_direction = SCSI_DATA_NONE;
 		break;
 
 	case ACTION_IDENTIFY:
 		US_DEBUGP("   isd200_action(IDENTIFY)\n");
-		ata.write.SelectCommand = 1;
+		ata.generic.RegisterSelect = REG_COMMAND;
 		ata.write.CommandByte = WIN_IDENTIFY;
 		srb.sc_data_direction = SCSI_DATA_READ;
 		srb.request_buffer = (void *)&info->drive;
@@ -883,9 +864,46 @@
 	 * condition, show that in the result code
 	 */
 	if (transferStatus == ISD200_TRANSPORT_FAILED)
-		srb->result = CHECK_CONDITION;
+		srb->result = CHECK_CONDITION << 1;
 }
 
+#ifdef CONFIG_USB_STORAGE_DEBUG
+static void isd200_log_config( struct isd200_info* info )
+{
+	US_DEBUGP("      Event Notification: 0x%x\n", 
+		  info->ConfigData.EventNotification);
+	US_DEBUGP("      External Clock: 0x%x\n", 
+		  info->ConfigData.ExternalClock);
+	US_DEBUGP("      ATA Init Timeout: 0x%x\n", 
+		  info->ConfigData.ATAInitTimeout);
+	US_DEBUGP("      ATAPI Command Block Size: 0x%x\n", 
+		  (info->ConfigData.ATAConfig & ATACFG_BLOCKSIZE) >> 6);
+	US_DEBUGP("      Master/Slave Selection: 0x%x\n", 
+		  info->ConfigData.ATAConfig & ATACFG_MASTER);
+	US_DEBUGP("      ATAPI Reset: 0x%x\n",
+		  info->ConfigData.ATAConfig & ATACFG_ATAPI_RESET);
+	US_DEBUGP("      ATA Timing: 0x%x\n",
+		  info->ConfigData.ATAConfig & ATACFG_TIMING);
+	US_DEBUGP("      ATA Major Command: 0x%x\n", 
+		  info->ConfigData.ATAMajorCommand);
+	US_DEBUGP("      ATA Minor Command: 0x%x\n", 
+		  info->ConfigData.ATAMinorCommand);
+	US_DEBUGP("      Init Status: 0x%x\n", 
+		  info->ConfigData.ATAExtraConfig & ATACFGE_INIT_STATUS);
+	US_DEBUGP("      Config Descriptor 2: 0x%x\n", 
+		  info->ConfigData.ATAExtraConfig & ATACFGE_CONF_DESC2);
+	US_DEBUGP("      Skip Device Boot: 0x%x\n",
+		  info->ConfigData.ATAExtraConfig & ATACFGE_SKIP_BOOT);
+	US_DEBUGP("      ATA 3 State Supsend: 0x%x\n",
+		  info->ConfigData.ATAExtraConfig & ATACFGE_STATE_SUSPEND);
+	US_DEBUGP("      Descriptor Override: 0x%x\n", 
+		  info->ConfigData.ATAExtraConfig & ATACFGE_DESC_OVERRIDE);
+	US_DEBUGP("      Last LUN Identifier: 0x%x\n",
+		  info->ConfigData.ATAExtraConfig & ATACFGE_LAST_LUN);
+	US_DEBUGP("      SRST Enable: 0x%x\n", 
+		  info->ConfigData.ATAExtraConfig & CFG_CAPABILITY_SRST);
+}
+#endif
 
 /**************************************************************************
  * isd200_write_config
@@ -901,26 +919,11 @@
 	int retStatus = ISD200_GOOD;
 	int result;
 
-
+#ifdef CONFIG_USB_STORAGE_DEBUG
 	US_DEBUGP("Entering isd200_write_config\n");
-
 	US_DEBUGP("   Writing the following ISD200 Config Data:\n");
-	US_DEBUGP("      Event Notification: 0x%x\n", info->ConfigData.EventNotification);
-	US_DEBUGP("      External Clock: 0x%x\n", info->ConfigData.ExternalClock);
-	US_DEBUGP("      ATA Init Timeout: 0x%x\n", info->ConfigData.ATAInitTimeout);
-	US_DEBUGP("      ATAPI Command Block Size: 0x%x\n", info->ConfigData.ATAPICommandBlockSize);
-	US_DEBUGP("      Master/Slave Selection: 0x%x\n", info->ConfigData.MasterSlaveSelection);
-	US_DEBUGP("      ATAPI Reset: 0x%x\n", info->ConfigData.ATAPIReset);
-	US_DEBUGP("      ATA Timing: 0x%x\n", info->ConfigData.ATATiming);
-	US_DEBUGP("      ATA Major Command: 0x%x\n", info->ConfigData.ATAMajorCommand);
-	US_DEBUGP("      ATA Minor Command: 0x%x\n", info->ConfigData.ATAMinorCommand);
-	US_DEBUGP("      Init Status: 0x%x\n", info->ConfigData.InitStatus);
-	US_DEBUGP("      Config Descriptor 2: 0x%x\n", info->ConfigData.ConfigDescriptor2);
-	US_DEBUGP("      Skip Device Boot: 0x%x\n", info->ConfigData.SkipDeviceBoot);
-	US_DEBUGP("      ATA 3 State Supsend: 0x%x\n", info->ConfigData.ATA3StateSuspend);
-	US_DEBUGP("      Descriptor Override: 0x%x\n", info->ConfigData.DescriptOverride);
-	US_DEBUGP("      Last LUN Identifier: 0x%x\n", info->ConfigData.LastLUNIdentifier);
-	US_DEBUGP("      SRST Enable: 0x%x\n", info->ConfigData.SRSTEnable);
+	isd200_log_config(info);
+#endif
 
 	/* let's send the command via the control pipe */
 	result = usb_stor_control_msg(
@@ -941,8 +944,8 @@
 		/* STALL must be cleared when they are detected */
 		if (result == -EPIPE) {
 			US_DEBUGP("-- Stall on control pipe. Clearing\n");
-			result = usb_stor_clear_halt(us->pusb_dev,
-						     usb_sndctrlpipe(us->pusb_dev, 0));
+			result = usb_stor_clear_halt(us,
+					usb_sndctrlpipe(us->pusb_dev, 0));
 			US_DEBUGP("-- usb_stor_clear_halt() returns %d\n", result);
 
 		}
@@ -986,30 +989,17 @@
 
 	if (result >= 0) {
 		US_DEBUGP("   Retrieved the following ISD200 Config Data:\n");
-		US_DEBUGP("      Event Notification: 0x%x\n", info->ConfigData.EventNotification);
-		US_DEBUGP("      External Clock: 0x%x\n", info->ConfigData.ExternalClock);
-		US_DEBUGP("      ATA Init Timeout: 0x%x\n", info->ConfigData.ATAInitTimeout);
-		US_DEBUGP("      ATAPI Command Block Size: 0x%x\n", info->ConfigData.ATAPICommandBlockSize);
-		US_DEBUGP("      Master/Slave Selection: 0x%x\n", info->ConfigData.MasterSlaveSelection);
-		US_DEBUGP("      ATAPI Reset: 0x%x\n", info->ConfigData.ATAPIReset);
-		US_DEBUGP("      ATA Timing: 0x%x\n", info->ConfigData.ATATiming);
-		US_DEBUGP("      ATA Major Command: 0x%x\n", info->ConfigData.ATAMajorCommand);
-		US_DEBUGP("      ATA Minor Command: 0x%x\n", info->ConfigData.ATAMinorCommand);
-		US_DEBUGP("      Init Status: 0x%x\n", info->ConfigData.InitStatus);
-		US_DEBUGP("      Config Descriptor 2: 0x%x\n", info->ConfigData.ConfigDescriptor2);
-		US_DEBUGP("      Skip Device Boot: 0x%x\n", info->ConfigData.SkipDeviceBoot);
-		US_DEBUGP("      ATA 3 State Supsend: 0x%x\n", info->ConfigData.ATA3StateSuspend);
-		US_DEBUGP("      Descriptor Override: 0x%x\n", info->ConfigData.DescriptOverride);
-		US_DEBUGP("      Last LUN Identifier: 0x%x\n", info->ConfigData.LastLUNIdentifier);
-		US_DEBUGP("      SRST Enable: 0x%x\n", info->ConfigData.SRSTEnable);
+#ifdef CONFIG_USB_STORAGE_DEBUG
+		isd200_log_config(info);
+#endif
         } else {
 		US_DEBUGP("   Request to get ISD200 Config Data failed!\n");
 
 		/* STALL must be cleared when they are detected */
 		if (result == -EPIPE) {
 			US_DEBUGP("-- Stall on control pipe. Clearing\n");
-			result = usb_stor_clear_halt(us->pusb_dev,   
-						     usb_sndctrlpipe(us->pusb_dev, 0));
+			result = usb_stor_clear_halt(us,   
+					usb_sndctrlpipe(us->pusb_dev, 0));
 			US_DEBUGP("-- usb_stor_clear_halt() returns %d\n", result);
 
 		}
@@ -1175,11 +1165,12 @@
 				break;
 			}
 		} else {
-			US_DEBUGP("   Not ATA, not ATAPI. Weird.\n");
+ 			US_DEBUGP("   Not ATA, not ATAPI. Weird.\n");
+			break;
 		}
 
 		/* check for timeout on this request */
-		if (jiffies >= endTime) {
+		if (time_after_eq(jiffies, endTime)) {
 			if (!detect)
 				US_DEBUGP("   BSY check timeout, just continue with next operation...\n");
 			else
@@ -1223,9 +1214,10 @@
 		}
 
 		isslave = (info->DeviceHead & ATA_ADDRESS_DEVHEAD_SLAVE) ? 1 : 0;
-		if (info->ConfigData.MasterSlaveSelection != isslave) {
+		if (!(info->ConfigData.ATAConfig & ATACFG_MASTER)) {
 			US_DEBUGP("   Setting Master/Slave selection to %d\n", isslave);
-			info->ConfigData.MasterSlaveSelection = isslave;
+			info->ConfigData.ATAConfig &= 0x3f;
+			info->ConfigData.ATAConfig |= (isslave<<6);
 			retStatus = isd200_write_config(us);
 		}
 	}
@@ -1272,6 +1264,8 @@
 			} else {
 				/* ATA Command Identify successful */
 				int i;
+				__u16 *src, *dest;
+				ide_fix_driveid(&info->drive);
 
 				US_DEBUGP("   Identify Data Structure:\n");
 				US_DEBUGP("      config = 0x%x\n", info->drive.config);
@@ -1317,31 +1311,25 @@
 
 				if (info->drive.command_set_1 & COMMANDSET_MEDIA_STATUS) {
 					/* set the removable bit */
-					info->InquiryData.RemovableMedia = 1;
+					info->InquiryData.DeviceTypeModifier = DEVICE_REMOVABLE;
 					info->DeviceFlags |= DF_REMOVABLE_MEDIA;
 				}
 
 				/* Fill in vendor identification fields */
-				for (i = 0; i < 20; i += 2) {
-					info->InquiryData.VendorId[i] = 
-						info->drive.model[i + 1];
-					info->InquiryData.VendorId[i+1] = 
-						info->drive.model[i];
-				}
-
-				/* Initialize unused portion of product id */
-				for (i = 0; i < 4; i++) {
-					info->InquiryData.ProductId[12+i] = ' ';
-				}
-
-				/* Move firmware revision from IDENTIFY data to */
-				/* product revision in INQUIRY data             */
-				for (i = 0; i < 4; i += 2) {
-					info->InquiryData.ProductRevisionLevel[i] =
-						info->drive.fw_rev[i+1];
-					info->InquiryData.ProductRevisionLevel[i+1] =
-						info->drive.fw_rev[i];
-				}
+				src = (__u16*)info->drive.model;
+				dest = (__u16*)info->InquiryData.VendorId;
+				for (i=0;i<4;i++)
+					dest[i] = be16_to_cpu(src[i]);
+
+				src = (__u16*)(info->drive.model+8);
+				dest = (__u16*)info->InquiryData.ProductId;
+				for (i=0;i<8;i++)
+					dest[i] = be16_to_cpu(src[i]);
+
+				src = (__u16*)info->drive.fw_rev;
+				dest = (__u16*)info->InquiryData.ProductRevisionLevel;
+				for (i=0;i<2;i++)
+					dest[i] = be16_to_cpu(src[i]);
 
 				/* determine if it supports Media Status Notification */
 				if (info->drive.command_set_2 & COMMANDSET_MEDIA_STATUS) {
@@ -1483,7 +1471,7 @@
 			ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
 			ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
 			ataCdb->generic.TransferBlockSize = 1;
-			ataCdb->write.SelectCommand = 1;
+			ataCdb->generic.RegisterSelect = REG_COMMAND;
 			ataCdb->write.CommandByte = ATA_COMMAND_GET_MEDIA_STATUS;
 			srb->request_bufflen = 0;
 		} else {
@@ -1504,7 +1492,7 @@
 			ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
 			ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
 			ataCdb->generic.TransferBlockSize = 1;
-			ataCdb->write.SelectCommand = 1;
+			ataCdb->generic.RegisterSelect = REG_COMMAND;
 			ataCdb->write.CommandByte = ATA_COMMAND_GET_MEDIA_STATUS;
 			srb->request_bufflen = 0;
 		} else {
@@ -1561,17 +1549,15 @@
 		ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
 		ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
 		ataCdb->generic.TransferBlockSize = 1;
-		ataCdb->write.SelectSectorCount = 1;
+		ataCdb->generic.RegisterSelect =
+		  REG_SECTOR_COUNT | REG_SECTOR_NUMBER |
+		  REG_CYLINDER_LOW | REG_CYLINDER_HIGH |
+		  REG_DEVICE_HEAD  | REG_COMMAND;
 		ataCdb->write.SectorCountByte = (unsigned char)blockCount;
-		ataCdb->write.SelectSectorNumber = 1;
 		ataCdb->write.SectorNumberByte = sectnum;
-		ataCdb->write.SelectCylinderHigh = 1;
 		ataCdb->write.CylinderHighByte = (unsigned char)(cylinder>>8);
-		ataCdb->write.SelectCylinderLow = 1;
 		ataCdb->write.CylinderLowByte = (unsigned char)cylinder;
-		ataCdb->write.SelectDeviceHead = 1;
 		ataCdb->write.DeviceHeadByte = (head | ATA_ADDRESS_DEVHEAD_STD);
-		ataCdb->write.SelectCommand = 1;
 		ataCdb->write.CommandByte = WIN_READ;
 		break;
 
@@ -1594,17 +1580,15 @@
 		ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
 		ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
 		ataCdb->generic.TransferBlockSize = 1;
-		ataCdb->write.SelectSectorCount = 1;
+		ataCdb->generic.RegisterSelect =
+		  REG_SECTOR_COUNT | REG_SECTOR_NUMBER |
+		  REG_CYLINDER_LOW | REG_CYLINDER_HIGH |
+		  REG_DEVICE_HEAD  | REG_COMMAND;
 		ataCdb->write.SectorCountByte = (unsigned char)blockCount;
-		ataCdb->write.SelectSectorNumber = 1;
 		ataCdb->write.SectorNumberByte = sectnum;
-		ataCdb->write.SelectCylinderHigh = 1;
 		ataCdb->write.CylinderHighByte = (unsigned char)(cylinder>>8);
-		ataCdb->write.SelectCylinderLow = 1;
 		ataCdb->write.CylinderLowByte = (unsigned char)cylinder;
-		ataCdb->write.SelectDeviceHead = 1;
 		ataCdb->write.DeviceHeadByte = (head | ATA_ADDRESS_DEVHEAD_STD);
-		ataCdb->write.SelectCommand = 1;
 		ataCdb->write.CommandByte = WIN_WRITE;
 		break;
 
@@ -1617,7 +1601,7 @@
 			ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
 			ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
 			ataCdb->generic.TransferBlockSize = 1;
-			ataCdb->write.SelectCommand = 1;
+			ataCdb->generic.RegisterSelect = REG_COMMAND;
 			ataCdb->write.CommandByte = (srb->cmnd[4] & 0x1) ?
 				WIN_DOORLOCK : WIN_DOORUNLOCK;
 			srb->request_bufflen = 0;
@@ -1640,14 +1624,14 @@
 			ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
 			ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
 			ataCdb->generic.TransferBlockSize = 0;
-			ataCdb->write.SelectCommand = 1;
+			ataCdb->generic.RegisterSelect = REG_COMMAND;
 			ataCdb->write.CommandByte = ATA_COMMAND_MEDIA_EJECT;
 		} else if ((srb->cmnd[4] & 0x3) == 0x1) {
 			US_DEBUGP("   Get Media Status\n");
 			ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
 			ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
 			ataCdb->generic.TransferBlockSize = 1;
-			ataCdb->write.SelectCommand = 1;
+			ataCdb->generic.RegisterSelect = REG_COMMAND;
 			ataCdb->write.CommandByte = ATA_COMMAND_GET_MEDIA_STATUS;
 			srb->request_bufflen = 0;
 		} else {
diff -Nru a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c
--- a/drivers/usb/storage/jumpshot.c	Sat May 18 00:46:08 2002
+++ b/drivers/usb/storage/jumpshot.c	Sat May 18 00:46:08 2002
@@ -1,16 +1,26 @@
 /* Driver for Lexar "Jumpshot" Compact Flash reader
  *
+ * $Id: jumpshot.c,v 1.7 2002/02/25 00:40:13 mdharm Exp $
+ *
  * jumpshot driver v0.1:
  *
  * First release
  *
  * Current development and maintenance by:
  *   (c) 2000 Jimmie Mayfield (mayfield+usb@sackheads.org)
- *   many thanks to Robert Baruch for the SanDisk SmartMedia reader driver
+ *
+ *   Many thanks to Robert Baruch for the SanDisk SmartMedia reader driver
  *   which I used as a template for this driver.
+ *
  *   Some bugfixes and scatter-gather code by Gregory P. Smith 
  *   (greg-usb@electricrain.com)
  *
+ *   Fix for media change by Joerg Schneider (js@joergschneider.com)
+ *
+ * Developed with the assistance of:
+ *
+ *   (C) 2002 Alan Stern <stern@rowland.org>
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2, or (at your option) any
@@ -128,8 +138,8 @@
 		/* a stall is a fatal condition from the device */
 		if (result == -EPIPE) {
 			US_DEBUGP("jumpshot_send_control:  -- Stall on control pipe. Clearing\n");
-			result = usb_clear_halt(us->pusb_dev, pipe);
-			US_DEBUGP("jumpshot_send_control:  -- usb_clear_halt() returns %d\n", result);
+			result = usb_stor_clear_halt(us, pipe);
+			US_DEBUGP("jumpshot_send_control:  -- usb_stor_clear_halt() returns %d\n", result);
 			return USB_STOR_TRANSPORT_FAILED;
 		}
 
@@ -161,7 +171,7 @@
 	if (result == -EPIPE) {
 		US_DEBUGP("jumpshot_raw_bulk:  EPIPE. clearing endpoint halt for"
 			  " pipe 0x%x, stalled at %d bytes\n", pipe, act_len);
-		usb_clear_halt(us->pusb_dev, pipe);
+		usb_stor_clear_halt(us, pipe);
 	}
 
 	if (result) {
@@ -331,7 +341,7 @@
                         while (sg_idx < use_sg && transferred < len) {
                                 if (len - transferred >= sg[sg_idx].length - current_sg_offset) {
                                         US_DEBUGP("jumpshot_read_data:  adding %d bytes to %d byte sg buffer\n", sg[sg_idx].length - current_sg_offset, sg[sg_idx].length);
-                                        memcpy(sg[sg_idx].address + current_sg_offset,
+                                        memcpy(page_address(sg[sg_idx].page) + sg[sg_idx].offset + current_sg_offset,
                                                buffer + transferred,
                                                sg[sg_idx].length - current_sg_offset);
                                         transferred += sg[sg_idx].length - current_sg_offset;
@@ -340,7 +350,7 @@
                                         ++sg_idx;
                                 } else {
                                         US_DEBUGP("jumpshot_read_data:  adding %d bytes to %d byte sg buffer\n", len - transferred, sg[sg_idx].length);
-                                        memcpy(sg[sg_idx].address + current_sg_offset,
+                                        memcpy(page_address(sg[sg_idx].page) + sg[sg_idx].offset + current_sg_offset,
                                                buffer + transferred,
                                                len - transferred);
                                         current_sg_offset += len - transferred;
@@ -413,7 +423,7 @@
                                 if (len - transferred >= sg[sg_idx].length - current_sg_offset) {
                                         US_DEBUGP("jumpshot_write_data:  getting %d bytes from %d byte sg buffer\n", sg[sg_idx].length - current_sg_offset, sg[sg_idx].length);
                                         memcpy(ptr + transferred,
-                                               sg[sg_idx].address + current_sg_offset,
+                                               page_address(sg[sg_idx].page) + sg[sg_idx].offset + current_sg_offset,
                                                sg[sg_idx].length - current_sg_offset);
                                         transferred += sg[sg_idx].length - current_sg_offset;
                                         current_sg_offset = 0;
@@ -422,7 +432,7 @@
                                 } else {
                                         US_DEBUGP("jumpshot_write_data:  getting %d bytes from %d byte sg buffer\n", len - transferred, sg[sg_idx].length);
                                         memcpy(ptr + transferred,
-                                               sg[sg_idx].address + current_sg_offset,
+                                               page_address(sg[sg_idx].page) + sg[sg_idx].offset + current_sg_offset,
                                                len - transferred);
                                         current_sg_offset += len - transferred;
                                         // we only copied part of this sg buffer
@@ -798,6 +808,23 @@
 		//
 		return USB_STOR_TRANSPORT_GOOD;
 	}
+	
+	if (srb->cmnd[0] == START_STOP) {
+		/* this is used by sd.c'check_scsidisk_media_change to detect
+		   media change */
+		US_DEBUGP("jumpshot_transport:  START_STOP.\n");
+		/* the first jumpshot_id_device after a media change returns
+		   an error (determined experimentally) */
+		rc = jumpshot_id_device(us, info);
+		if (rc == USB_STOR_TRANSPORT_GOOD) {
+			info->sense_key = NO_SENSE;
+			srb->result = SUCCESS;
+		} else {
+			info->sense_key = UNIT_ATTENTION;
+			srb->result = CHECK_CONDITION << 1;
+		}
+		return rc;
+        }
 
 	US_DEBUGP("jumpshot_transport:  Gah! Unknown command: %d (0x%x)\n", srb->cmnd[0], srb->cmnd[0]);
 	return USB_STOR_TRANSPORT_ERROR;
diff -Nru a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c
--- a/drivers/usb/storage/protocol.c	Sat May 18 00:46:08 2002
+++ b/drivers/usb/storage/protocol.c	Sat May 18 00:46:08 2002
@@ -1,12 +1,13 @@
 /* Driver for USB Mass Storage compliant devices
  *
- * $Id: protocol.c,v 1.11 2002/01/13 06:40:25 mdharm Exp $
+ * $Id: protocol.c,v 1.14 2002/04/22 03:39:43 mdharm Exp $
  *
  * Current development and maintenance by:
- *   (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *   (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
  *
  * Developed with the assistance of:
  *   (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org)
+ *   (c) 2002 Alan Stern (stern@rowland.org)
  *
  * Initial work by:
  *   (c) 1999 Michael Gee (michael@linuxspecific.com)
@@ -67,10 +68,16 @@
 	US_DEBUGP("Fixing INQUIRY data to show SCSI rev 2\n");
 
 	/* find the location of the data */
-	if (srb->use_sg)
-		BUG();
-
-	data_ptr = (unsigned char *) srb->request_buffer;
+	if (srb->use_sg) {
+		/* this piece of code only works if the first page is big enough to
+		 * hold more than 3 bytes -- which is _very_ likely
+		 */
+		struct scatterlist *sg;
+
+		sg = (struct scatterlist *) srb->request_buffer;
+		data_ptr = (unsigned char *) page_address(sg[0].page) + sg[0].offset;
+	} else
+		data_ptr = (unsigned char *)srb->request_buffer;
 
 	/* Change the SCSI revision number */
 	data_ptr[2] = (data_ptr[2] & ~7) | 2;
@@ -94,9 +101,11 @@
 
 	/* send the command to the transport layer */
 	usb_stor_invoke_transport(srb, us);
+	if (srb->result == GOOD << 1) {
 
-	/* fix the INQUIRY data if necessary */
-	fix_inquiry_data(srb);
+		/* fix the INQUIRY data if necessary */
+		fix_inquiry_data(srb);
+	}
 }
 
 void usb_stor_ATAPI_command(Scsi_Cmnd *srb, struct us_data *us)
@@ -165,13 +174,15 @@
 
 	/* send the command to the transport layer */
 	usb_stor_invoke_transport(srb, us);
+	if (srb->result == GOOD << 1) {
 
-	/* Fix the MODE_SENSE data if we translated the command */
-	if ((old_cmnd == MODE_SENSE) && (status_byte(srb->result) == GOOD))
-		usb_stor_scsiSense10to6(srb);
-
-	/* fix the INQUIRY data if necessary */
-	fix_inquiry_data(srb);
+		/* Fix the MODE_SENSE data if we translated the command */
+		if (old_cmnd == MODE_SENSE)
+			usb_stor_scsiSense10to6(srb);
+
+		/* fix the INQUIRY data if necessary */
+		fix_inquiry_data(srb);
+	}
 }
 
 
@@ -260,13 +271,15 @@
 
 	/* send the command to the transport layer */
 	usb_stor_invoke_transport(srb, us);
+	if (srb->result == GOOD << 1) {
 
-	/* Fix the MODE_SENSE data if we translated the command */
-	if ((old_cmnd == MODE_SENSE) && (status_byte(srb->result) == GOOD))
-		usb_stor_scsiSense10to6(srb);
-
-	/* Fix the data for an INQUIRY, if necessary */
-	fix_inquiry_data(srb);
+		/* Fix the MODE_SENSE data if we translated the command */
+		if (old_cmnd == MODE_SENSE)
+			usb_stor_scsiSense10to6(srb);
+
+		/* Fix the data for an INQUIRY, if necessary */
+		fix_inquiry_data(srb);
+	}
 }
 
 void usb_stor_transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
@@ -327,13 +340,14 @@
 
 	/* send the command to the transport layer */
 	usb_stor_invoke_transport(srb, us);
+	if (srb->result == GOOD << 1) {
 
-	/* Fix the MODE_SENSE data if we translated the command */
-	if ((us->flags & US_FL_MODE_XLATE) && (old_cmnd == MODE_SENSE)
-			&& (status_byte(srb->result) == GOOD))
-		usb_stor_scsiSense10to6(srb);
-
-	/* fix the INQUIRY data if necessary */
-	fix_inquiry_data(srb);
+		/* Fix the MODE_SENSE data if we translated the command */
+		if ((us->flags & US_FL_MODE_XLATE) && (old_cmnd == MODE_SENSE))
+			usb_stor_scsiSense10to6(srb);
+
+		/* fix the INQUIRY data if necessary */
+		fix_inquiry_data(srb);
+	}
 }
 
diff -Nru a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
--- a/drivers/usb/storage/scsiglue.c	Sat May 18 00:46:10 2002
+++ b/drivers/usb/storage/scsiglue.c	Sat May 18 00:46:10 2002
@@ -1,7 +1,7 @@
 /* Driver for USB Mass Storage compliant devices
  * SCSI layer glue code
  *
- * $Id: scsiglue.c,v 1.24 2001/11/11 03:33:58 mdharm Exp $
+ * $Id: scsiglue.c,v 1.26 2002/04/22 03:39:43 mdharm Exp $
  *
  * Current development and maintenance by:
  *   (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -70,7 +70,11 @@
 	return "SCSI emulation for USB Mass Storage devices";
 }
 
-/* detect a virtual adapter (always works) */
+/* detect a virtual adapter (always works)
+ * Synchronization: 2.4: with the io_request_lock
+ * 			2.5: no locks.
+ * fortunately we don't care.
+ * */
 static int detect(struct SHT *sht)
 {
 	struct us_data *us;
@@ -82,7 +86,7 @@
 
 	/* set up the name of our subdirectory under /proc/scsi/ */
 	sprintf(local_name, "usb-storage-%d", us->host_number);
-	sht->proc_name = kmalloc (strlen(local_name) + 1, GFP_KERNEL);
+	sht->proc_name = kmalloc (strlen(local_name) + 1, GFP_ATOMIC);
 	if (!sht->proc_name)
 		return 0;
 	strcpy(sht->proc_name, local_name);
@@ -108,6 +112,7 @@
  *
  * NOTE: There is no contention here, because we're already deregistered
  * the driver and we're doing each virtual host in turn, not in parallel
+ * Synchronization: BLK, no spinlock.
  */
 static int release(struct Scsi_Host *psh)
 {
@@ -145,12 +150,13 @@
 static int queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
 {
 	struct us_data *us = (struct us_data *)srb->host->hostdata[0];
+	unsigned long flags;
 
 	US_DEBUGP("queuecommand() called\n");
 	srb->host_scribble = (unsigned char *)us;
 
 	/* get exclusive access to the structures we want */
-	down(&(us->queue_exclusion));
+	spin_lock_irqsave(&us->queue_exclusion, flags);
 
 	/* enqueue the command */
 	us->queue_srb = srb;
@@ -158,7 +164,7 @@
 	us->action = US_ACT_COMMAND;
 
 	/* release the lock on the structure */
-	up(&(us->queue_exclusion));
+	spin_unlock_irqrestore(&us->queue_exclusion, flags);
 
 	/* wake up the process task */
 	up(&(us->sema));
@@ -177,25 +183,13 @@
 
 	US_DEBUGP("command_abort() called\n");
 
-	/* if we're stuck waiting for an IRQ, simulate it */
-	if (atomic_read(us->ip_wanted)) {
-		US_DEBUGP("-- simulating missing IRQ\n");
-		up(&(us->ip_waitq));
-	}
-
-	/* if the device has been removed, this worked */
-	if (!us->pusb_dev) {
-		US_DEBUGP("-- device removed already\n");
-		return SUCCESS;
-	}
-
-	/* if we have an urb pending, let's wake the control thread up */
-	if (us->current_urb->status == -EINPROGRESS) {
-		/* cancel the URB -- this will automatically wake the thread */
-		usb_unlink_urb(us->current_urb);
+	if (atomic_read(&us->sm_state) == US_STATE_RUNNING) {
+ 		scsi_unlock(srb->host);
+		usb_stor_abort_transport(us);
 
 		/* wait for us to be done */
 		wait_for_completion(&(us->notify));
+ 		scsi_lock(srb->host);
 		return SUCCESS;
 	}
 
@@ -208,47 +202,62 @@
 static int device_reset( Scsi_Cmnd *srb )
 {
 	struct us_data *us = (struct us_data *)srb->host->hostdata[0];
+	int result;
 
 	US_DEBUGP("device_reset() called\n" );
-	return us->transport_reset(us);
+
+	/* if the device was removed, then we're already reset */
+	if (atomic_read(&us->sm_state) == US_STATE_DETACHED)
+		return SUCCESS;
+
+	scsi_unlock(srb->host);
+	/* lock the device pointers */
+	down(&(us->dev_semaphore));
+	us->srb = srb;
+	atomic_set(&us->sm_state, US_STATE_RESETTING);
+	result = us->transport_reset(us);
+	atomic_set(&us->sm_state, US_STATE_IDLE);
+
+	/* unlock the device pointers */
+	up(&(us->dev_semaphore));
+	scsi_lock(srb->host);
+	return result;
 }
 
 /* This resets the device port, and simulates the device
- * disconnect/reconnect for all drivers which have claimed other
- * interfaces. */
+ * disconnect/reconnect for all drivers which have claimed
+ * interfaces, including ourself. */
 static int bus_reset( Scsi_Cmnd *srb )
 {
 	struct us_data *us = (struct us_data *)srb->host->hostdata[0];
 	int i;
 	int result;
+	struct usb_device *pusb_dev_save = us->pusb_dev;
 
 	/* we use the usb_reset_device() function to handle this for us */
 	US_DEBUGP("bus_reset() called\n");
 
 	/* if the device has been removed, this worked */
-	if (!us->pusb_dev) {
+	if (atomic_read(&us->sm_state) == US_STATE_DETACHED) {
 		US_DEBUGP("-- device removed already\n");
 		return SUCCESS;
 	}
 
-	/* release the IRQ, if we have one */
-	down(&(us->irq_urb_sem));
-	if (us->irq_urb) {
-		US_DEBUGP("-- releasing irq URB\n");
-		result = usb_unlink_urb(us->irq_urb);
-		US_DEBUGP("-- usb_unlink_urb() returned %d\n", result);
-	}
-	up(&(us->irq_urb_sem));
-
 	/* attempt to reset the port */
-	if (usb_reset_device(us->pusb_dev) < 0)
+	scsi_unlock(srb->host);
+	result = usb_reset_device(pusb_dev_save);
+	US_DEBUGP("usb_reset_device returns %d\n", result);
+	if (result < 0) {
+		scsi_lock(srb->host);
 		return FAILED;
+	}
 
 	/* FIXME: This needs to lock out driver probing while it's working
 	 * or we can have race conditions */
-        for (i = 0; i < us->pusb_dev->actconfig->bNumInterfaces; i++) {
+	/* Is that still true?  I don't see how...  AS */
+        for (i = 0; i < pusb_dev_save->actconfig->bNumInterfaces; i++) {
  		struct usb_interface *intf =
-			&us->pusb_dev->actconfig->interface[i];
+			&pusb_dev_save->actconfig->interface[i];
 		const struct usb_device_id *id;
 
 		/* if this is an unclaimed interface, skip it */
@@ -256,34 +265,18 @@
 			continue;
 		}
 
-		US_DEBUGP("Examinging driver %s...", intf->driver->name);
-		/* skip interfaces which we've claimed */
-		if (intf->driver == &usb_storage_driver) {
-			US_DEBUGPX("skipping ourselves.\n");
-			continue;
-		}
+		US_DEBUGP("Examining driver %s...", intf->driver->name);
 
 		/* simulate a disconnect and reconnect for all interfaces */
 		US_DEBUGPX("simulating disconnect/reconnect.\n");
 		down(&intf->driver->serialize);
-		intf->driver->disconnect(us->pusb_dev, intf->private_data);
-		id = usb_match_id(us->pusb_dev, intf, intf->driver->id_table);
-		intf->driver->probe(us->pusb_dev, i, id);
+		intf->driver->disconnect(pusb_dev_save, intf->private_data);
+		id = usb_match_id(pusb_dev_save, intf, intf->driver->id_table);
+		intf->driver->probe(pusb_dev_save, i, id);
 		up(&intf->driver->serialize);
 	}
-
-	/* re-allocate the IRQ URB and submit it to restore connectivity
-	 * for CBI devices
-	 */
-	if (us->protocol == US_PR_CBI) {
-		down(&(us->irq_urb_sem));
-		us->irq_urb->dev = us->pusb_dev;
-		result = usb_submit_urb(us->irq_urb, GFP_NOIO);
-		US_DEBUGP("usb_submit_urb() returns %d\n", result);
-		up(&(us->irq_urb_sem));
-	}
-
 	US_DEBUGP("bus_reset() complete\n");
+	scsi_lock(srb->host);
 	return SUCCESS;
 }
 
@@ -291,6 +284,7 @@
 static int host_reset( Scsi_Cmnd *srb )
 {
 	printk(KERN_CRIT "usb-storage: host_reset() requested but not implemented\n" );
+	bus_reset(srb);
 	return FAILED;
 }
 
@@ -346,7 +340,8 @@
 
 	/* show the GUID of the device */
 	SPRINTF("         GUID: " GUID_FORMAT "\n", GUID_ARGS(us->guid));
-	SPRINTF("     Attached: %s\n", us->pusb_dev ? "Yes" : "No");
+	SPRINTF("     Attached: %s\n", (atomic_read(&us->sm_state) ==
+			US_STATE_DETACHED) ? "Yes" : "No");
 
 	/*
 	 * Calculate start of next buffer, and return value.
@@ -565,11 +560,11 @@
 
 	  /* copy one byte */
 	  {
-		  char *src = page_address(sg[sb].page) + sg[sb].offset + si;
-		  char *dst = page_address(sg[db].page) + sg[db].offset + di;
+	        char *src = page_address(sg[sb].page) + sg[sb].offset + si;
+	        char *dst = page_address(sg[db].page) + sg[db].offset + di;
 
-		  *dst = *src;
-	  }
+                 *dst = *src;
+          }
 
 	  /* get next destination */
 	  if ( sg[db].length-1 == di )
@@ -607,7 +602,7 @@
 	      break;
 	    }
 
-	  *(char *)(page_address(sg[db].page) + sg[db].offset) = 0;
+	  *(char*)(page_address(sg[db].page) + sg[db].offset) = 0;
 
 	  /* get next destination */
 	  if ( sg[db].length-1 == di )
@@ -758,11 +753,11 @@
 
 	  /* copy one byte */
 	  {
-		  char *src = page_address(sg[sb].page) + sg[sb].offset + si;
-		  char *dst = page_address(sg[db].page) + sg[db].offset + di;
+	        char *src = page_address(sg[sb].page) + sg[sb].offset + si;
+	        char *dst = page_address(sg[db].page) + sg[db].offset + di;
 
-		  *dst = *src;
-	  }
+                 *dst = *src;
+          }
 
 	  /* get next destination */
 	  if ( di == 0 )
@@ -799,11 +794,12 @@
 	      break;
 	    }
 
-	  {
-		  char *dst = page_address(sg[db].page) + sg[db].offset + di;
+         {
+                 char *dst = page_address(sg[db].page) + sg[db].offset + di;
+
+                 *dst = tempBuffer[element-USB_STOR_SCSI_SENSE_HDRSZ];
+         }
 
-		  *dst = tempBuffer[element-USB_STOR_SCSI_SENSE_HDRSZ];
-	  }
 
 	  /* get next destination */
 	  if ( di == 0 )
@@ -853,19 +849,18 @@
 		  if ( element < USB_STOR_SCSI_SENSE_HDRSZ )
 		    {
 		      /* fill in the pointers for both header types */
-		      the6->array[element] =
-			      page_address(sg[i].page) +
-			      sg[i].offset + j;
-		      the10->array[element] =
-			      page_address(sg[i].page) +
-			      sg[i].offset + j;
+		      the6->array[element] = page_address(sg[i].page) +
+			      			sg[i].offset + j;
+		      the10->array[element] = page_address(sg[i].page) +
+			                        sg[i].offset + j;
+
 		    }
 		  else if ( element < USB_STOR_SCSI_SENSE_10_HDRSZ )
 		    {
 		      /* only the longer headers still cares now */
-		      the10->array[element] =
-			      page_address(sg[i].page) +
-			      sg[i].offset + j;
+		      the10->array[element] = page_address(sg[i].page) +
+			                        sg[i].offset + j;
+		       
 		    }
 		  /* increase element counter */
 		  element++;
diff -Nru a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c
--- a/drivers/usb/storage/sddr09.c	Sat May 18 00:46:14 2002
+++ b/drivers/usb/storage/sddr09.c	Sat May 18 00:46:14 2002
@@ -1,7 +1,10 @@
 /* Driver for SanDisk SDDR-09 SmartMedia reader
  *
+ * $Id: sddr09.c,v 1.24 2002/04/22 03:39:43 mdharm Exp $
  *   (c) 2000, 2001 Robert Baruch (autophile@starband.net)
  *   (c) 2002 Andries Brouwer (aeb@cwi.nl)
+ * Developed with the assistance of:
+ *   (c) 2002 Alan Stern <stern@rowland.org>
  *
  * The SanDisk SDDR-09 SmartMedia reader uses the Shuttle EUSB-01 chip.
  * This chip is a programmable USB controller. In the SDDR-09, it has
@@ -262,8 +265,8 @@
 		/* a stall is a fatal condition from the device */
 		if (result == -EPIPE) {
 			US_DEBUGP("-- Stall on control pipe. Clearing\n");
-			result = usb_clear_halt(us->pusb_dev, pipe);
-			US_DEBUGP("-- usb_clear_halt() returns %d\n", result);
+			result = usb_stor_clear_halt(us, pipe);
+			US_DEBUGP("-- usb_stor_clear_halt() returns %d\n", result);
 			return USB_STOR_TRANSPORT_FAILED;
 		}
 
@@ -317,13 +320,13 @@
 
 	result = usb_stor_bulk_msg(us, data, pipe, len, &act_len);
 
-	/* if we stall, we need to clear it before we go on */
-	if (result == -EPIPE) {
-		US_DEBUGP("EPIPE: clearing endpoint halt for"
-			  " pipe 0x%x, stalled at %d bytes\n",
-			  pipe, act_len);
-		usb_clear_halt(us->pusb_dev, pipe);
-	}
+        /* if we stall, we need to clear it before we go on */
+        if (result == -EPIPE) {
+       	        US_DEBUGP("EPIPE: clearing endpoint halt for"
+			" pipe 0x%x, stalled at %d bytes\n",
+			pipe, act_len);
+               	usb_stor_clear_halt(us, pipe);
+        }
 
 	if (result) {
 		/* -ENOENT -- we canceled this transfer */
@@ -1386,15 +1389,6 @@
 	// Each block is 64 bytes of control data, so block i is located in
 	// scatterlist block i*64/128k = i*(2^6)*(2^-17) = i*(2^-11)
 
-#if 0
-	/* No translation */
-	for (i=0; i<numblocks; i++) {
-		lba = i;
-		info->pba_to_lba[i] = lba;
-		info->lba_to_pba[lba] = i;
-	}
-	printk("sddr09: no translation today\n");
-#else
 	for (i=0; i<numblocks; i++) {
 		ptr = page_address(sg[i>>11].page) +
 			sg[i>>11].offset + ((i&0x7ff)<<6);
@@ -1482,7 +1476,6 @@
 		info->pba_to_lba[i] = lba;
 		info->lba_to_pba[lba] = i;
 	}
-#endif
 
 	/*
 	 * Approximate capacity. This is not entirely correct yet,
@@ -1508,7 +1501,7 @@
 	US_DEBUGP("Found %d LBA's\n", lbact);
 
 	for (i=0; i<alloc_blocks; i++)
-		kfree(page_address(sg[i].page)+sg[i].offset);
+		kfree(page_address(sg[i].page) + sg[i].offset);
 	kfree(sg);
 	return 0;
 }
diff -Nru a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/storage/sddr55.c	Sat May 18 00:46:15 2002
@@ -0,0 +1,1135 @@
+/* Driver for SanDisk SDDR-55 SmartMedia reader
+ *
+ * $Id:$
+ *
+ * SDDR55 driver v0.1:
+ *
+ * First release
+ *
+ * Current development and maintenance by:
+ *   (c) 2002 Simon Munton
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "transport.h"
+#include "protocol.h"
+#include "usb.h"
+#include "debug.h"
+#include "sddr55.h"
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) )
+#define LSB_of(s) ((s)&0xFF)
+#define MSB_of(s) ((s)>>8)
+#define PAGESIZE  512
+
+#define set_sense_info(sk, asc, ascq)	\
+    do {				\
+	info->sense_data[2] = sk;	\
+	info->sense_data[12] = asc;	\
+	info->sense_data[13] = ascq;	\
+	} while (0)
+
+
+struct sddr55_card_info {
+	unsigned long	capacity;	/* Size of card in bytes */
+	int		max_log_blks;	/* maximum number of logical blocks */
+	int		pageshift;	/* log2 of pagesize */
+	int		smallpageshift;	/* 1 if pagesize == 256 */
+	int		blocksize;	/* Size of block in pages */
+	int		blockshift;	/* log2 of blocksize */
+	int		blockmask;	/* 2^blockshift - 1 */
+	int		read_only;	/* non zero if card is write protected */
+	int		force_read_only;	/* non zero if we find a map error*/
+	int		*lba_to_pba;	/* logical to physical map */
+	int		*pba_to_lba;	/* physical to logical map */
+	int		fatal_error;	/* set if we detect something nasty */
+	unsigned long 	last_access;	/* number of jiffies since we last talked to device */
+	unsigned char   sense_data[18];
+};
+
+
+#define NOT_ALLOCATED		0xffffffff
+#define BAD_BLOCK		0xffff
+#define CIS_BLOCK		0x400
+#define UNUSED_BLOCK		0x3ff
+
+
+
+static int sddr55_raw_bulk(struct us_data *us, 
+		int direction,
+		unsigned char *data,
+		unsigned int len) {
+
+	int result;
+	int act_len;
+	int pipe;
+
+	if (direction == SCSI_DATA_READ)
+		pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
+	else
+		pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+
+	result = usb_stor_bulk_msg(us, data, pipe, len, &act_len);
+
+	/* if we stall, we need to clear it before we go on */
+	if (result == -EPIPE) {
+		US_DEBUGP("EPIPE: clearing endpoint halt for"
+			" pipe 0x%x, stalled at %d bytes\n",
+			pipe, act_len);
+		usb_clear_halt(us->pusb_dev, pipe);
+	}
+
+	if (result) {
+
+		/* NAK - that means we've retried a few times already */
+		if (result == -ETIMEDOUT) {
+			US_DEBUGP("usbat_raw_bulk():"
+				" device NAKed\n");
+
+			return US_BULK_TRANSFER_FAILED;
+		}
+
+		/* -ENOENT -- we canceled this transfer */
+		if (result == -ENOENT) {
+			US_DEBUGP("usbat_raw_bulk():"
+				" transfer aborted\n");
+			return US_BULK_TRANSFER_ABORTED;
+		}
+
+		if (result == -EPIPE) {
+			US_DEBUGP("usbat_raw_bulk():"
+				" output pipe stalled\n");
+			return US_BULK_TRANSFER_FAILED;
+		}
+
+		/* the catch-all case */
+		US_DEBUGP("us_transfer_partial(): unknown error\n");
+		return US_BULK_TRANSFER_FAILED;
+	}
+
+	if (act_len != len) {
+		US_DEBUGP("Warning: Transferred only %d bytes\n",
+			act_len);
+		return US_BULK_TRANSFER_SHORT;
+	}
+
+	US_DEBUGP("Transferred %d of %d bytes\n", act_len, len);
+
+	return US_BULK_TRANSFER_GOOD;
+}
+
+/*
+ * Note: direction must be set if command_len == 0.
+ */
+
+static int sddr55_bulk_transport(struct us_data *us,
+			  int direction,
+			  unsigned char *data,
+			  unsigned int len) {
+
+	int result = USB_STOR_TRANSPORT_GOOD;
+	struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
+
+	if (len==0)
+		return USB_STOR_TRANSPORT_GOOD;
+
+	info->last_access = jiffies;
+
+#ifdef CONFIG_USB_STORAGE_DEBUG
+	if (direction == SCSI_DATA_WRITE) {
+		int i;
+		char string[64];
+
+		/* Debug-print the first 48 bytes of the write transfer */
+
+		strcpy(string, "wr: ");
+		for (i=0; i<len && i<48; i++) {
+			sprintf(string+strlen(string), "%02X ",
+			  data[i]);
+			if ((i%16)==15) {
+				US_DEBUGP("%s\n", string);
+				strcpy(string, "wr: ");
+			}
+		}
+		if ((i%16)!=0)
+			US_DEBUGP("%s\n", string);
+	}
+#endif
+
+	/* transfer the data */
+
+	US_DEBUGP("SCM data %s transfer %d\n",
+		  ( direction==SCSI_DATA_READ ? "in" : "out"),
+		  len);
+
+	result = sddr55_raw_bulk(us, direction, data, len);
+
+#ifdef CONFIG_USB_STORAGE_DEBUG
+	if (direction == SCSI_DATA_READ) {
+		int i;
+		char string[64];
+
+		/* Debug-print the first 48 bytes of the read transfer */
+
+		strcpy(string, "rd: ");
+		for (i=0; i<len && i<48; i++) {
+			sprintf(string+strlen(string), "%02X ",
+			  data[i]);
+			if ((i%16)==15) {
+				US_DEBUGP("%s\n", string);
+				strcpy(string, "rd: ");
+			}
+		}
+		if ((i%16)!=0)
+			US_DEBUGP("%s\n", string);
+	}
+#endif
+
+	return result;
+}
+
+
+/* check if card inserted, if there is, update read_only status
+ * return non zero if no card
+ */
+
+static int sddr55_status(struct us_data *us)
+{
+	int result;
+	unsigned char command[8] = {
+		0, 0, 0, 0, 0, 0xb0, 0, 0x80
+	};
+	unsigned char status[8];
+	struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
+
+	/* send command */
+	result = sddr55_bulk_transport(us,
+		SCSI_DATA_WRITE, command, 8);
+
+	US_DEBUGP("Result for send_command in status %d\n",
+		result);
+
+	if (result != US_BULK_TRANSFER_GOOD) {
+		set_sense_info (4, 0, 0);	/* hardware error */
+		return result;
+	}
+
+	result = sddr55_bulk_transport(us,
+		SCSI_DATA_READ, status,	4);
+
+	/* expect to get short transfer if no card fitted */
+	if (result == US_BULK_TRANSFER_SHORT) {
+		/* had a short transfer, no card inserted, free map memory */
+		if (info->lba_to_pba)
+			kfree(info->lba_to_pba);
+		if (info->pba_to_lba)
+			kfree(info->pba_to_lba);
+		info->lba_to_pba = NULL;
+		info->pba_to_lba = NULL;
+
+		info->fatal_error = 0;
+		info->force_read_only = 0;
+
+		set_sense_info (2, 0x3a, 0);	/* not ready, medium not present */
+		return result;
+	}
+
+	if (result != US_BULK_TRANSFER_GOOD) {
+		set_sense_info (4, 0, 0);	/* hardware error */
+		return result;
+	}
+	
+	/* check write protect status */
+	info->read_only = (status[0] & 0x20);
+
+	/* now read status */
+	result = sddr55_bulk_transport(us,
+		SCSI_DATA_READ, status,	2);
+
+	if (result != US_BULK_TRANSFER_GOOD) {
+		set_sense_info (4, 0, 0);	/* hardware error */
+	}
+
+	return result;
+}
+
+
+static int sddr55_read_data(struct us_data *us,
+		unsigned int lba,
+		unsigned int page,
+		unsigned short sectors,
+		unsigned char *content,
+		int use_sg) {
+
+	int result;
+	unsigned char command[8] = {
+		0, 0, 0, 0, 0, 0xb0, 0, 0x85
+	};
+	unsigned char status[8];
+	struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
+
+	unsigned int pba;
+	unsigned long address;
+
+	unsigned short pages;
+	unsigned char *buffer = NULL;
+	unsigned char *ptr;
+	struct scatterlist *sg = NULL;
+	int i;
+	int len;
+	int transferred;
+
+	// If we're using scatter-gather, we have to create a new
+	// buffer to read all of the data in first, since a
+	// scatter-gather buffer could in theory start in the middle
+	// of a page, which would be bad. A developer who wants a
+	// challenge might want to write a limited-buffer
+	// version of this code.
+
+	len = sectors * PAGESIZE;
+
+	if (use_sg) {
+		sg = (struct scatterlist *)content;
+		buffer = kmalloc(len, GFP_NOIO);
+		if (buffer == NULL)
+			return USB_STOR_TRANSPORT_ERROR;
+		ptr = buffer;
+	} else
+		ptr = content;
+
+	// This could be made much more efficient by checking for
+	// contiguous LBA's. Another exercise left to the student.
+
+	while (sectors>0) {
+
+		/* have we got to end? */
+		if (lba >= info->max_log_blks)
+			break;
+
+		pba = info->lba_to_pba[lba];
+
+		// Read as many sectors as possible in this block
+
+		pages = info->blocksize - page;
+		if (pages > (sectors << info->smallpageshift))
+			pages = (sectors << info->smallpageshift);
+
+		US_DEBUGP("Read %02X pages, from PBA %04X"
+			" (LBA %04X) page %02X\n",
+			pages, pba, lba, page);
+
+		if (pba == NOT_ALLOCATED) {
+			/* no pba for this lba, fill with zeroes */
+			memset (ptr, 0, pages << info->pageshift);
+		} else {
+
+			address = (pba << info->blockshift) + page;
+
+			command[1] = LSB_of(address>>16);
+			command[2] = LSB_of(address>>8);
+			command[3] = LSB_of(address);
+
+			command[6] = LSB_of(pages << (1 - info->smallpageshift));
+
+			/* send command */
+			result = sddr55_bulk_transport(us,
+				SCSI_DATA_WRITE, command, 8);
+
+			US_DEBUGP("Result for send_command in read_data %d\n",
+				result);
+
+			if (result != US_BULK_TRANSFER_GOOD) {
+				if (use_sg)
+					kfree(buffer);
+				return result;
+			}
+
+			/* read data */
+			result = sddr55_bulk_transport(us,
+				SCSI_DATA_READ, ptr,
+				pages<<info->pageshift);
+
+			if (result != US_BULK_TRANSFER_GOOD) {
+				if (use_sg)
+					kfree(buffer);
+				return result;
+			}
+
+			/* now read status */
+			result = sddr55_bulk_transport(us,
+				SCSI_DATA_READ, status, 2);
+
+			if (result != US_BULK_TRANSFER_GOOD) {
+				if (use_sg)
+					kfree(buffer);
+				return result;
+			}
+
+			/* check status for error */
+			if (status[0] == 0xff && status[1] == 0x4) {
+				set_sense_info (3, 0x11, 0);
+				if (use_sg)
+					kfree(buffer);
+
+				return USB_STOR_TRANSPORT_FAILED;
+			}
+
+		}
+
+		page = 0;
+		lba++;
+		sectors -= pages >> info->smallpageshift;
+		ptr += (pages << info->pageshift);
+	}
+
+	if (use_sg) {
+		transferred = 0;
+		for (i=0; i<use_sg && transferred<len; i++) {
+			memcpy(page_address(sg[i].page) + sg[i].offset, buffer+transferred,
+				len-transferred > sg[i].length ?
+					sg[i].length : len-transferred);
+			transferred += sg[i].length;
+		}
+		kfree(buffer);
+	}
+
+	return USB_STOR_TRANSPORT_GOOD;
+}
+
+static int sddr55_write_data(struct us_data *us,
+		unsigned int lba,
+		unsigned int page,
+		unsigned short sectors,
+		unsigned char *content,
+		int use_sg) {
+
+	int result;
+	unsigned char command[8] = {
+		0, 0, 0, 0, 0, 0xb0, 0, 0x86
+	};
+	unsigned char status[8];
+	struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
+
+	unsigned int pba;
+	unsigned int new_pba;
+	unsigned long address;
+
+	unsigned short pages;
+	unsigned char *buffer = NULL;
+	unsigned char *ptr;
+	struct scatterlist *sg = NULL;
+	int i;
+	int len;
+	int transferred;
+
+	/* check if we are allowed to write */
+	if (info->read_only || info->force_read_only) {
+		set_sense_info (7, 0x27, 0);	/* read only */
+		return USB_STOR_TRANSPORT_FAILED;
+	}
+
+	// If we're using scatter-gather, we have to create a new
+	// buffer to write all of the data in first, since a
+	// scatter-gather buffer could in theory start in the middle
+	// of a page, which would be bad. A developer who wants a
+	// challenge might want to write a limited-buffer
+	// version of this code.
+
+	len = sectors * PAGESIZE;
+
+	if (use_sg) {
+		sg = (struct scatterlist *)content;
+		buffer = kmalloc(len, GFP_NOIO);
+		if (buffer == NULL)
+			return USB_STOR_TRANSPORT_ERROR;
+
+		transferred = 0;
+		for (i=0; i<use_sg && transferred<len; i++) {
+			memcpy(buffer+transferred, 
+				page_address(sg[i].page) + sg[i].offset,
+				len-transferred > sg[i].length ?
+					sg[i].length : len-transferred);
+			transferred += sg[i].length;
+		}
+
+		ptr = buffer;
+	} else
+		ptr = content;
+
+	while (sectors > 0) {
+
+		/* have we got to end? */
+		if (lba >= info->max_log_blks)
+			break;
+
+		pba = info->lba_to_pba[lba];
+
+		// Write as many sectors as possible in this block
+
+		pages = info->blocksize - page;
+		if (pages > (sectors << info->smallpageshift))
+			pages = (sectors << info->smallpageshift);
+
+		US_DEBUGP("Write %02X pages, to PBA %04X"
+			" (LBA %04X) page %02X\n",
+			pages, pba, lba, page);
+			
+		command[4] = 0;
+
+		if (pba == NOT_ALLOCATED) {
+			/* no pba allocated for this lba, find a free pba to use */
+
+			int max_pba = (info->max_log_blks / 250 ) * 256;
+			int found_count = 0;
+			int found_pba = -1;
+
+			/* set pba to first block in zone lba is in */
+			pba = (lba / 1000) * 1024;
+
+			US_DEBUGP("No PBA for LBA %04X\n",lba);
+
+			if (max_pba > 1024)
+				max_pba = 1024;
+
+			/* scan through the map lookiong for an unused block
+			 * leave 16 unused blocks at start (or as many as possible)
+			 * since the sddr55 seems to reuse a used block when it shouldn't
+			 * if we don't leave space */
+			for (i = 0; i < max_pba; i++, pba++) {
+				if (info->pba_to_lba[pba] == UNUSED_BLOCK) {
+					found_pba = pba;
+					if (found_count++ > 16)
+						break;
+				}
+			}
+
+			pba = found_pba;
+
+			if (pba == -1) {
+				/* oh dear, couldn't find an unallocated block */
+				US_DEBUGP("Couldn't find unallocated block\n");
+
+				set_sense_info (3, 0x31, 0);	/* medium error */
+
+				if (use_sg)
+					kfree(buffer);
+
+				return USB_STOR_TRANSPORT_FAILED;
+			}
+
+			US_DEBUGP("Allocating PBA %04X for LBA %04X\n", pba, lba);
+
+			/* set writing to unallocated block flag */
+			command[4] = 0x40;
+		}
+
+		address = (pba << info->blockshift) + page;
+
+		command[1] = LSB_of(address>>16);
+		command[2] = LSB_of(address>>8); 
+		command[3] = LSB_of(address);
+
+		/* set the lba into the command, modulo 1000 */
+		command[0] = LSB_of(lba % 1000);
+		command[6] = MSB_of(lba % 1000);
+
+		command[4] |= LSB_of(pages >> info->smallpageshift);
+
+		/* send command */
+		result = sddr55_bulk_transport(us,
+			SCSI_DATA_WRITE, command, 8);
+
+		if (result != US_BULK_TRANSFER_GOOD) {
+			US_DEBUGP("Result for send_command in write_data %d\n",
+			result);
+
+			set_sense_info (3, 0x3, 0);	/* peripheral write error */
+			
+			if (use_sg)
+				kfree(buffer);
+			return result;
+		}
+
+		/* send the data */
+		result = sddr55_bulk_transport(us,
+			SCSI_DATA_WRITE, ptr,
+			pages<<info->pageshift);
+
+		if (result != US_BULK_TRANSFER_GOOD) {
+			US_DEBUGP("Result for send_data in write_data %d\n",
+			result);
+
+			set_sense_info (3, 0x3, 0);	/* peripheral write error */
+
+			if (use_sg)
+				kfree(buffer);
+			return result;
+		}
+
+		/* now read status */
+		result = sddr55_bulk_transport(us,
+			SCSI_DATA_READ, status,	6);
+
+		if (result != US_BULK_TRANSFER_GOOD) {
+			US_DEBUGP("Result for get_status in write_data %d\n",
+			result);
+
+			set_sense_info (3, 0x3, 0);	/* peripheral write error */
+
+			if (use_sg)
+				kfree(buffer);
+			return result;
+		}
+
+		new_pba = (status[3] + (status[4] << 8) + (status[5] << 16)) >> info->blockshift;
+
+		/* check status for error */
+		if (status[0] == 0xff && status[1] == 0x4) {
+			set_sense_info (3, 0x0c, 0);
+			if (use_sg)
+				kfree(buffer);
+
+			info->pba_to_lba[new_pba] = BAD_BLOCK;
+
+			return USB_STOR_TRANSPORT_FAILED;
+		}
+
+		US_DEBUGP("Updating maps for LBA %04X: old PBA %04X, new PBA %04X\n",
+			lba, pba, new_pba);
+
+		/* update the lba<->pba maps, note new_pba might be the same as pba */
+		info->lba_to_pba[lba] = new_pba;
+		info->pba_to_lba[pba] = UNUSED_BLOCK;
+
+		/* check that new_pba wasn't already being used */
+		if (info->pba_to_lba[new_pba] != UNUSED_BLOCK) {
+			printk(KERN_ERR "sddr55 error: new PBA %04X already in use for LBA %04X\n",
+				new_pba, info->pba_to_lba[new_pba]);
+			info->fatal_error = 1;
+			set_sense_info (3, 0x31, 0);
+			if (use_sg)
+				kfree(buffer);
+
+			return USB_STOR_TRANSPORT_FAILED;
+		}
+
+		/* update the pba<->lba maps for new_pba */
+		info->pba_to_lba[new_pba] = lba % 1000;
+
+		page = 0;
+		lba++;
+		sectors -= pages >> info->smallpageshift;
+		ptr += (pages << info->pageshift);
+	}
+
+	if (use_sg) {
+		kfree(buffer);
+	}
+
+	return USB_STOR_TRANSPORT_GOOD;
+}
+
+static int sddr55_read_deviceID(struct us_data *us,
+		unsigned char *manufacturerID,
+		unsigned char *deviceID) {
+
+	int result;
+	unsigned char command[8] = {
+		0, 0, 0, 0, 0, 0xb0, 0, 0x84
+	};
+	unsigned char content[64];
+
+	result = sddr55_bulk_transport(us, SCSI_DATA_WRITE, command, 8);
+
+	US_DEBUGP("Result of send_control for device ID is %d\n",
+		result);
+
+	if (result != US_BULK_TRANSFER_GOOD)
+		return result;
+
+	result = sddr55_bulk_transport(us,
+		SCSI_DATA_READ, content, 4);
+
+	if (result != US_BULK_TRANSFER_GOOD)
+		return result;
+
+	*manufacturerID = content[0];
+	*deviceID = content[1];
+
+	if (content[0] != 0xff)	{
+    		result = sddr55_bulk_transport(us,
+			SCSI_DATA_READ, content, 2);
+	}
+
+	return result;
+}
+
+
+int sddr55_reset(struct us_data *us) {
+	return 0;
+}
+
+
+static unsigned long sddr55_get_capacity(struct us_data *us) {
+
+	unsigned char manufacturerID;
+	unsigned char deviceID;
+	int result;
+	struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
+
+	US_DEBUGP("Reading capacity...\n");
+
+	result = sddr55_read_deviceID(us,
+		&manufacturerID,
+		&deviceID);
+
+	US_DEBUGP("Result of read_deviceID is %d\n",
+		result);
+
+	if (result != US_BULK_TRANSFER_GOOD)
+		return 0;
+
+	US_DEBUGP("Device ID = %02X\n", deviceID);
+	US_DEBUGP("Manuf  ID = %02X\n", manufacturerID);
+
+	info->pageshift = 9;
+	info->smallpageshift = 0;
+	info->blocksize = 16;
+	info->blockshift = 4;
+	info->blockmask = 15;
+
+	switch (deviceID) {
+
+	case 0x6e: // 1MB
+	case 0xe8:
+	case 0xec:
+		info->pageshift = 8;
+		info->smallpageshift = 1;
+		return 0x00100000;
+
+	case 0xea: // 2MB
+	case 0x64:
+		info->pageshift = 8;
+		info->smallpageshift = 1;
+	case 0x5d: // 5d is a ROM card with pagesize 512.
+		return 0x00200000;
+
+	case 0xe3: // 4MB
+	case 0xe5:
+	case 0x6b:
+	case 0xd5:
+		return 0x00400000;
+
+	case 0xe6: // 8MB
+	case 0xd6:
+		return 0x00800000;
+
+	case 0x73: // 16MB
+		info->blocksize = 32;
+		info->blockshift = 5;
+		info->blockmask = 31;
+		return 0x01000000;
+
+	case 0x75: // 32MB
+		info->blocksize = 32;
+		info->blockshift = 5;
+		info->blockmask = 31;
+		return 0x02000000;
+
+	case 0x76: // 64MB
+		info->blocksize = 32;
+		info->blockshift = 5;
+		info->blockmask = 31;
+		return 0x04000000;
+
+	case 0x79: // 128MB
+		info->blocksize = 32;
+		info->blockshift = 5;
+		info->blockmask = 31;
+		return 0x08000000;
+
+	default: // unknown
+		return 0;
+
+	}
+}
+
+static int sddr55_read_map(struct us_data *us) {
+
+	struct sddr55_card_info *info = (struct sddr55_card_info *)(us->extra);
+	int numblocks;
+	unsigned char *buffer;
+	unsigned char command[8] = { 0, 0, 0, 0, 0, 0xb0, 0, 0x8a};	
+	int i;
+	unsigned short lba;
+	unsigned short max_lba;
+	int result;
+
+	if (!info->capacity)
+		return -1;
+
+	numblocks = info->capacity >> (info->blockshift + info->pageshift);
+	
+	buffer = kmalloc( numblocks * 2, GFP_NOIO );
+	
+	if (!buffer)
+		return -1;
+
+	command[6] = numblocks * 2 / 256;
+
+	result = sddr55_bulk_transport(us, SCSI_DATA_WRITE, command, 8);
+
+	if ( result != US_BULK_TRANSFER_GOOD) {
+		kfree (buffer);
+		return -1;
+	}
+
+	result = sddr55_bulk_transport(us, SCSI_DATA_READ, buffer, numblocks * 2);
+
+	if ( result != US_BULK_TRANSFER_GOOD) {
+		kfree (buffer);
+		return -1;
+	}
+
+	result = sddr55_bulk_transport(us, SCSI_DATA_READ, command, 2);
+
+	if ( result != US_BULK_TRANSFER_GOOD) {
+		kfree (buffer);
+		return -1;
+	}
+
+	if (info->lba_to_pba)
+		kfree(info->lba_to_pba);
+	if (info->pba_to_lba)
+		kfree(info->pba_to_lba);
+	info->lba_to_pba = kmalloc(numblocks*sizeof(int), GFP_NOIO);
+	info->pba_to_lba = kmalloc(numblocks*sizeof(int), GFP_NOIO);
+
+	if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) {
+		if (info->lba_to_pba != NULL)
+			kfree(info->lba_to_pba);
+		if (info->pba_to_lba != NULL)
+			kfree(info->pba_to_lba);
+		info->lba_to_pba = NULL;
+		info->pba_to_lba = NULL;
+		kfree(buffer);
+		return -1;
+	}
+
+	memset(info->lba_to_pba, 0xff, numblocks*sizeof(int));
+	memset(info->pba_to_lba, 0xff, numblocks*sizeof(int));
+
+	/* set maximum lba */
+	max_lba = info->max_log_blks;
+	if (max_lba > 1000)
+		max_lba = 1000;
+
+	// Each block is 64 bytes of control data, so block i is located in
+	// scatterlist block i*64/128k = i*(2^6)*(2^-17) = i*(2^-11)
+
+	for (i=0; i<numblocks; i++) {
+		int zone = i / 1024;
+
+		lba = short_pack(buffer[i * 2], buffer[i * 2 + 1]);
+
+			/* Every 1024 physical blocks ("zone"), the LBA numbers
+			 * go back to zero, but are within a higher
+			 * block of LBA's. Also, there is a maximum of
+			 * 1000 LBA's per zone. In other words, in PBA
+			 * 1024-2047 you will find LBA 0-999 which are
+			 * really LBA 1000-1999. Yes, this wastes 24
+			 * physical blocks per zone. Go figure. 
+			 * These devices can have blocks go bad, so there
+			 * are 24 spare blocks to use when blocks do go bad.
+			 */
+
+			/* SDDR55 returns 0xffff for a bad block, and 0x400 for the 
+			 * CIS block. (Is this true for cards 8MB or less??)
+			 * Record these in the physical to logical map
+			 */ 
+
+		info->pba_to_lba[i] = lba;
+
+		if (lba >= max_lba) {
+			continue;
+		}
+		
+		if (info->lba_to_pba[lba + zone * 1000] != NOT_ALLOCATED &&
+		    !info->force_read_only) {
+			printk("sddr55: map inconsistency at LBA %04X\n", lba + zone * 1000);
+			info->force_read_only = 1;
+		}
+
+		if (lba<0x10 || (lba>=0x3E0 && lba<0x3EF))
+			US_DEBUGP("LBA %04X <-> PBA %04X\n", lba, i);
+
+		info->lba_to_pba[lba + zone * 1000] = i;
+	}
+
+	kfree(buffer);
+	return 0;
+}
+
+
+static void sddr55_card_info_destructor(void *extra) {
+	struct sddr55_card_info *info = (struct sddr55_card_info *)extra;
+
+	if (!extra)
+		return;
+
+	if (info->lba_to_pba)
+		kfree(info->lba_to_pba);
+	if (info->pba_to_lba)
+		kfree(info->pba_to_lba);
+}
+
+
+/*
+ * Transport for the Sandisk SDDR-55
+ */
+int sddr55_transport(Scsi_Cmnd *srb, struct us_data *us)
+{
+	int result;
+	int i;
+	unsigned char inquiry_response[36] = {
+		0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00
+	};
+	unsigned char mode_page_01[16] = { // write-protected for now
+		0x03, 0x00, 0x80, 0x00,
+		0x01, 0x0A,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+	};
+	unsigned char *ptr;
+	unsigned long capacity;
+	unsigned int lba;
+	unsigned int pba;
+	unsigned int page;
+	unsigned short pages;
+	struct sddr55_card_info *info;
+
+	if (!us->extra) {
+		us->extra = kmalloc(
+			sizeof(struct sddr55_card_info), GFP_NOIO);
+		if (!us->extra)
+			return USB_STOR_TRANSPORT_ERROR;
+		memset(us->extra, 0, sizeof(struct sddr55_card_info));
+		us->extra_destructor = sddr55_card_info_destructor;
+	}
+
+	info = (struct sddr55_card_info *)(us->extra);
+
+	ptr = (unsigned char *)srb->request_buffer;
+
+	if (srb->cmnd[0] == REQUEST_SENSE) {
+		i = srb->cmnd[4];
+
+		if (i > sizeof info->sense_data)
+			i = sizeof info->sense_data;
+
+
+		US_DEBUGP("SDDR55: request sense %02x/%02x/%02x\n", info->sense_data[2], info->sense_data[12], info->sense_data[13]);
+
+		info->sense_data[0] = 0x70;
+		info->sense_data[7] = 10;
+
+		memcpy (ptr, info->sense_data, i);
+		memset (info->sense_data, 0, sizeof info->sense_data);
+
+		return USB_STOR_TRANSPORT_GOOD;
+	}
+
+	memset (info->sense_data, 0, sizeof info->sense_data);
+
+	/* Dummy up a response for INQUIRY since SDDR55 doesn't
+	   respond to INQUIRY commands */
+
+	if (srb->cmnd[0] == INQUIRY) {
+		memset(inquiry_response+8, 0, 28);
+		fill_inquiry_response(us, inquiry_response, 36);
+		return USB_STOR_TRANSPORT_GOOD;
+	}
+
+	/* only check card status if the map isn't allocated, ie no card seen yet
+	 * or if it's been over half a second since we last accessed it
+	 */
+	if (info->lba_to_pba == NULL || jiffies > (info->last_access + HZ/2)) {
+
+		/* check to see if a card is fitted */
+		result = sddr55_status (us);
+		if (result) {
+			result = sddr55_status (us);
+			if (!result) {
+			set_sense_info (6, 0x28, 0);	/* new media, set unit attention, not ready to ready */
+			}
+			return USB_STOR_TRANSPORT_FAILED;
+		}
+	}
+
+	/* if we detected a problem with the map when writing, don't allow any more access */
+	if (info->fatal_error) {
+
+		set_sense_info (3, 0x31, 0);
+		return USB_STOR_TRANSPORT_FAILED;
+	}
+
+	if (srb->cmnd[0] == READ_CAPACITY) {
+
+		capacity = sddr55_get_capacity(us);
+
+		if (!capacity) {
+			set_sense_info (3, 0x30, 0);	/* incompatible medium */
+			return USB_STOR_TRANSPORT_FAILED;
+		}
+
+		info->capacity = capacity;
+
+                /* figure out the maximum logical block number, allowing for the fact
+                 * that only 250 out of every 256 are used */
+		info->max_log_blks = ((info->capacity >> (info->pageshift + info->blockshift)) / 256) * 250;
+
+		/* Last page in the card, adjust as we only use 250 out of every 256 pages */
+		capacity = (capacity / 256) * 250;
+
+		capacity /= PAGESIZE;
+		capacity--;
+
+		ptr[0] = MSB_of(capacity>>16);
+		ptr[1] = LSB_of(capacity>>16);
+		ptr[2] = MSB_of(capacity&0xFFFF);
+		ptr[3] = LSB_of(capacity&0xFFFF);
+
+		// The page size
+
+		ptr[4] = MSB_of(PAGESIZE>>16);
+		ptr[5] = LSB_of(PAGESIZE>>16);
+		ptr[6] = MSB_of(PAGESIZE&0xFFFF);
+		ptr[7] = LSB_of(PAGESIZE&0xFFFF);
+
+		sddr55_read_map(us);
+
+		return USB_STOR_TRANSPORT_GOOD;
+	}
+
+	if (srb->cmnd[0] == MODE_SENSE) {
+
+		mode_page_01[2] = (info->read_only || info->force_read_only) ? 0x80 : 0;
+
+		if ( (srb->cmnd[2] & 0x3F) == 0x01 ) {
+
+			US_DEBUGP(
+			  "SDDR55: Dummy up request for mode page 1\n");
+
+			if (ptr==NULL || 
+			  srb->request_bufflen<sizeof(mode_page_01)) {
+				set_sense_info (5, 0x24, 0);	/* invalid field in command */
+				return USB_STOR_TRANSPORT_FAILED;
+			}
+
+			memcpy(ptr, mode_page_01, sizeof(mode_page_01));
+			return USB_STOR_TRANSPORT_GOOD;
+
+		} else if ( (srb->cmnd[2] & 0x3F) == 0x3F ) {
+
+			US_DEBUGP(
+			  "SDDR55: Dummy up request for all mode pages\n");
+
+			if (ptr==NULL || 
+			  srb->request_bufflen<sizeof(mode_page_01)) {
+				set_sense_info (5, 0x24, 0);	/* invalid field in command */
+				return USB_STOR_TRANSPORT_FAILED;
+			}
+
+			memcpy(ptr, mode_page_01, sizeof(mode_page_01));
+			return USB_STOR_TRANSPORT_GOOD;
+		}
+
+		set_sense_info (5, 0x24, 0);	/* invalid field in command */
+
+		return USB_STOR_TRANSPORT_FAILED;
+	}
+
+	if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) {
+
+		US_DEBUGP(
+		  "SDDR55: %s medium removal. Not that I can do"
+		  " anything about it...\n",
+		  (srb->cmnd[4]&0x03) ? "Prevent" : "Allow");
+
+		return USB_STOR_TRANSPORT_GOOD;
+
+	}
+
+	if (srb->cmnd[0] == READ_10 || srb->cmnd[0] == WRITE_10) {
+
+		page = short_pack(srb->cmnd[3], srb->cmnd[2]);
+		page <<= 16;
+		page |= short_pack(srb->cmnd[5], srb->cmnd[4]);
+		pages = short_pack(srb->cmnd[8], srb->cmnd[7]);
+
+		page <<= info->smallpageshift;
+
+		// convert page to block and page-within-block
+
+		lba = page >> info->blockshift;
+		page = page & info->blockmask;
+
+		// locate physical block corresponding to logical block
+
+		if (lba >= info->max_log_blks) {
+
+			US_DEBUGP("Error: Requested LBA %04X exceeds maximum "
+			  "block %04X\n", lba, info->max_log_blks-1);
+
+			set_sense_info (5, 0x24, 0);	/* invalid field in command */
+
+			return USB_STOR_TRANSPORT_FAILED;
+		}
+
+		pba = info->lba_to_pba[lba];
+
+		if (srb->cmnd[0] == WRITE_10) {
+			US_DEBUGP("WRITE_10: write block %04X (LBA %04X) page %01X"
+			        " pages %d\n",
+			        pba, lba, page, pages);
+
+			return sddr55_write_data(us, lba, page, pages, ptr, srb->use_sg);
+		} else {
+			US_DEBUGP("READ_10: read block %04X (LBA %04X) page %01X"
+			        " pages %d\n",
+			        pba, lba, page, pages);
+
+			return sddr55_read_data(us, lba, page, pages, ptr, srb->use_sg);
+		}
+	}
+
+
+	if (srb->cmnd[0] == TEST_UNIT_READY) {
+		return USB_STOR_TRANSPORT_GOOD;
+	}
+
+	if (srb->cmnd[0] == START_STOP) {
+		return USB_STOR_TRANSPORT_GOOD;
+	}
+
+	set_sense_info (5, 0x20, 0);	/* illegal command */
+
+	return USB_STOR_TRANSPORT_FAILED; // FIXME: sense buffer?
+}
+
diff -Nru a/drivers/usb/storage/sddr55.h b/drivers/usb/storage/sddr55.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/storage/sddr55.h	Sat May 18 00:46:15 2002
@@ -0,0 +1,34 @@
+/* Driver for SanDisk SDDR-55 SmartMedia reader
+ * Header File
+ *
+ * $Id:$
+ *
+ * Current development and maintenance by:
+ *   (c) 2002 Simon Munton
+ *
+ * See sddr55.c for more explanation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _USB_SHUTTLE_EUSB_SDDR55_H
+#define _USB_SHUTTLE_EUSB_SDDR55_H
+
+/* Sandisk SDDR-55 stuff */
+
+extern int sddr55_transport(Scsi_Cmnd *srb, struct us_data *us);
+extern int sddr55_reset(struct us_data *us);
+
+#endif
diff -Nru a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c
--- a/drivers/usb/storage/shuttle_usbat.c	Sat May 18 00:46:08 2002
+++ b/drivers/usb/storage/shuttle_usbat.c	Sat May 18 00:46:08 2002
@@ -1,10 +1,13 @@
 /* Driver for SCM Microsystems USB-ATAPI cable
  *
- * $Id: shuttle_usbat.c,v 1.15 2001/12/08 23:32:48 mdharm Exp $
+ * $Id: shuttle_usbat.c,v 1.17 2002/04/22 03:39:43 mdharm Exp $
  *
  * Current development and maintenance by:
  *   (c) 2000, 2001 Robert Baruch (autophile@starband.net)
  *
+ * Developed with the assistance of:
+ *   (c) 2002 Alan Stern <stern@rowland.org>
+ *
  * Many originally ATAPI devices were slightly modified to meet the USB
  * market by using some kind of translation from ATAPI to USB on the host,
  * and the peripheral would translate from USB back to ATAPI.
@@ -107,8 +110,8 @@
 		/* a stall is a fatal condition from the device */
 		if (result == -EPIPE) {
 			US_DEBUGP("-- Stall on control pipe. Clearing\n");
-			result = usb_clear_halt(us->pusb_dev, pipe);
-			US_DEBUGP("-- usb_clear_halt() returns %d\n", result);
+			result = usb_stor_clear_halt(us, pipe);
+			US_DEBUGP("-- usb_stor_clear_halt() returns %d\n", result);
 			return USB_STOR_TRANSPORT_FAILED;
 		}
 
@@ -140,7 +143,7 @@
        	        US_DEBUGP("EPIPE: clearing endpoint halt for"
 			" pipe 0x%x, stalled at %d bytes\n",
 			pipe, act_len);
-               	usb_clear_halt(us->pusb_dev, pipe);
+               	usb_stor_clear_halt(us, pipe);
         }
 
 	if (result) {
@@ -214,7 +217,7 @@
 		sg = (struct scatterlist *)data;
 		for (i=0; i<use_sg && transferred<len; i++) {
 			result = usbat_raw_bulk(us, direction,
-				page_address(sg[i].page) + sg[i].offset,
+				page_address(sg[i].page) + sg[i].offset, 
 				len-transferred > sg[i].length ?
 					sg[i].length : len-transferred);
 			if (result!=US_BULK_TRANSFER_GOOD)
@@ -515,7 +518,7 @@
 			 */
 
 			if (direction==SCSI_DATA_READ && i==0)
-				usb_clear_halt(us->pusb_dev,
+				usb_stor_clear_halt(us,
 					usb_sndbulkpipe(us->pusb_dev,
 					  us->ep_out));
 			/*
@@ -675,9 +678,15 @@
 		len = short_pack(data[7+9], data[7+8]);
 		len <<= 16;
 		len |= data[7+7];
+		US_DEBUGP("handle_read10: GPCMD_READ_CD: len %d\n", len);
 		srb->transfersize = srb->request_bufflen/len;
 	}
 
+	if (!srb->transfersize)  {
+		srb->transfersize = 2048; /* A guess */
+		US_DEBUGP("handle_read10: transfersize 0, forcing %d\n",
+			srb->transfersize);
+	}
 
 	len = (65535/srb->transfersize) * srb->transfersize;
 	US_DEBUGP("Max read is %d bytes\n", len);
diff -Nru a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
--- a/drivers/usb/storage/transport.c	Sat May 18 00:46:09 2002
+++ b/drivers/usb/storage/transport.c	Sat May 18 00:46:09 2002
@@ -1,13 +1,14 @@
 /* Driver for USB Mass Storage compliant devices
  *
- * $Id: transport.c,v 1.42 2001/12/08 23:32:48 mdharm Exp $
+ * $Id: transport.c,v 1.47 2002/04/22 03:39:43 mdharm Exp $
  *
  * Current development and maintenance by:
- *   (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *   (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
  *
  * Developed with the assistance of:
  *   (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org)
  *   (c) 2000 Stephen J. Gowdy (SGowdy@lbl.gov)
+ *   (c) 2002 Alan Stern <stern@rowland.org>
  *
  * Initial work by:
  *   (c) 1999 Michael Gee (michael@linuxspecific.com)
@@ -329,37 +330,21 @@
 			   for (i = 0; i < srb->use_sg; i++)
 				   total += sg[i].length;
 			   len = total;
+
+			   /* Double-check to see if the advertised buffer
+			    * length less than the actual buffer length --
+			    * in other words, we should tend towards the
+			    * conservative side for data transfers.
+			    */
+			   if (len > srb->request_bufflen)
+				   len = srb->request_bufflen;
 		   }
 		   else
 			   /* Just return the length of the buffer */
 			   len = srb->request_bufflen;
 	   }
 
-return len;
-}
-
-/* This is a version of usb_clear_halt() that doesn't read the status from
- * the device -- this is because some devices crash their internal firmware
- * when the status is requested after a halt
- */
-int usb_stor_clear_halt(struct usb_device *dev, int pipe)
-{
-	int result;
-	int endp = usb_pipeendpoint(pipe) | (usb_pipein(pipe) << 7);
-
-	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
-				 USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0,
-				 endp, NULL, 0, HZ * 3);
-
-	/* this is a failure case */
-	if (result < 0)
-		return result;
-
-	/* reset the toggles and endpoint flags */
-	usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
-	usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0);
-
-	return 0;
+	return len;
 }
 
 /***********************************************************************
@@ -376,109 +361,142 @@
 	complete(urb_done_ptr);
 }
 
-/* This is our function to emulate usb_control_msg() but give us enough
- * access to make aborts/resets work
+/* This is the common part of the URB message submission code
+ * This function expects the current_urb_sem to be held upon entry.
+ */
+static int usb_stor_msg_common(struct us_data *us)
+{
+	struct completion urb_done;
+	int status;
+
+	/* set up data structures for the wakeup system */
+	init_completion(&urb_done);
+
+	/* fill the common fields in the URB */
+	us->current_urb->context = &urb_done;
+	us->current_urb->actual_length = 0;
+	us->current_urb->error_count = 0;
+	us->current_urb->transfer_flags = USB_ASYNC_UNLINK;
+
+	/* submit the URB */
+	status = usb_submit_urb(us->current_urb, GFP_NOIO);
+	if (status) {
+		/* something went wrong */
+		return status;
+	}
+
+	/* has the current command been aborted? */
+	if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+
+		/* avoid a race with usb_stor_abort_transport():
+		 *  if the abort took place before we submitted
+		 *  the URB, we must cancel it ourselves */
+		if (us->current_urb->status == -EINPROGRESS)
+			usb_unlink_urb(us->current_urb);
+		}
+
+	/* wait for the completion of the URB */
+	up(&(us->current_urb_sem));
+	wait_for_completion(&urb_done);
+	down(&(us->current_urb_sem));
+
+	/* return the URB status */
+	return us->current_urb->status;
+}
+
+/* This is our function to emulate usb_control_msg() with enough control
+ * to make aborts/resets/timeouts work
  */
 int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
 			 u8 request, u8 requesttype, u16 value, u16 index, 
 			 void *data, u16 size)
 {
-	struct completion urb_done;
 	int status;
 	struct usb_ctrlrequest *dr;
 
-	/* allocate the device request structure */
 	dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
 	if (!dr)
 		return -ENOMEM;
 
-	/* fill in the structure */
+	/* fill in the devrequest structure */
 	dr->bRequestType = requesttype;
 	dr->bRequest = request;
 	dr->wValue = cpu_to_le16(value);
 	dr->wIndex = cpu_to_le16(index);
 	dr->wLength = cpu_to_le16(size);
 
-	/* set up data structures for the wakeup system */
-	init_completion(&urb_done);
-
 	/* lock the URB */
 	down(&(us->current_urb_sem));
 
 	/* fill the URB */
 	FILL_CONTROL_URB(us->current_urb, us->pusb_dev, pipe, 
-			 (unsigned char*) dr, data, size, 
-			 usb_stor_blocking_completion, &urb_done);
-	us->current_urb->actual_length = 0;
-	us->current_urb->error_count = 0;
-	us->current_urb->transfer_flags = USB_ASYNC_UNLINK;
+			 (unsigned char*) &dr, data, size, 
+			 usb_stor_blocking_completion, NULL);
 
 	/* submit the URB */
-	status = usb_submit_urb(us->current_urb, GFP_NOIO);
-	if (status) {
-		/* something went wrong */
-		up(&(us->current_urb_sem));
-		kfree(dr);
-		return status;
-	}
-
-	/* wait for the completion of the URB */
-	up(&(us->current_urb_sem));
-	wait_for_completion(&urb_done);
-	down(&(us->current_urb_sem));
+	status = usb_stor_msg_common(us);
 
 	/* return the actual length of the data transferred if no error*/
-	status = us->current_urb->status;
 	if (status >= 0)
 		status = us->current_urb->actual_length;
 
 	/* release the lock and return status */
 	up(&(us->current_urb_sem));
-	kfree(dr);
-  	return status;
+	return status;
 }
 
-/* This is our function to emulate usb_bulk_msg() but give us enough
- * access to make aborts/resets work
+/* This is our function to emulate usb_bulk_msg() with enough control
+ * to make aborts/resets/timeouts work
  */
 int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe,
 		      unsigned int len, unsigned int *act_len)
 {
-	struct completion urb_done;
 	int status;
 
-	/* set up data structures for the wakeup system */
-	init_completion(&urb_done);
-
 	/* lock the URB */
 	down(&(us->current_urb_sem));
 
 	/* fill the URB */
 	FILL_BULK_URB(us->current_urb, us->pusb_dev, pipe, data, len,
-		      usb_stor_blocking_completion, &urb_done);
-	us->current_urb->actual_length = 0;
-	us->current_urb->error_count = 0;
-	us->current_urb->transfer_flags = USB_ASYNC_UNLINK;
+		      usb_stor_blocking_completion, NULL);
 
 	/* submit the URB */
-	status = usb_submit_urb(us->current_urb, GFP_NOIO);
-	if (status) {
-		/* something went wrong */
-		up(&(us->current_urb_sem));
-		return status;
-	}
-
-	/* wait for the completion of the URB */
-	up(&(us->current_urb_sem));
-	wait_for_completion(&urb_done);
-	down(&(us->current_urb_sem));
+	status = usb_stor_msg_common(us);
 
 	/* return the actual length of the data transferred */
 	*act_len = us->current_urb->actual_length;
 
 	/* release the lock and return status */
 	up(&(us->current_urb_sem));
-	return us->current_urb->status;
+	return status;
+}
+
+/* This is a version of usb_clear_halt() that doesn't read the status from
+ * the device -- this is because some devices crash their internal firmware
+ * when the status is requested after a halt
+ */
+int usb_stor_clear_halt(struct us_data *us, int pipe)
+{
+	int result;
+	int endp = usb_pipeendpoint(pipe) | (usb_pipein(pipe) << 7);
+
+	result = usb_stor_control_msg(us,
+		usb_sndctrlpipe(us->pusb_dev, 0),
+		USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0,
+		endp, NULL, 0);		/* note: no 3*HZ timeout */
+	US_DEBUGP("usb_stor_clear_halt: result=%d\n", result);
+
+	/* this is a failure case */
+	if (result < 0)
+		return result;
+
+	/* reset the toggles and endpoint flags */
+	usb_endpoint_running(us->pusb_dev, usb_pipeendpoint(pipe),
+		usb_pipeout(pipe));
+	usb_settoggle(us->pusb_dev, usb_pipeendpoint(pipe),
+		usb_pipeout(pipe), 0);
+
+	return 0;
 }
 
 /*
@@ -513,7 +531,13 @@
 	/* if we stall, we need to clear it before we go on */
 	if (result == -EPIPE) {
 		US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
-		usb_stor_clear_halt(us->pusb_dev, pipe);
+		usb_stor_clear_halt(us, pipe);
+	}
+
+	/* did we abort this command? */
+	if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+		US_DEBUGP("usb_stor_transfer_partial(): transfer aborted\n");
+		return US_BULK_TRANSFER_ABORTED;
 	}
 
 	/* did we send all the data? */
@@ -522,21 +546,14 @@
 		return US_BULK_TRANSFER_GOOD;
 	}
 
-	/* uh oh... we have an error code, so something went wrong. */
-	if (result) {
-		/* NAK - that means we've retried a few times already */
-		if (result == -ETIMEDOUT) {
-			US_DEBUGP("usb_stor_transfer_partial(): device NAKed\n");
-			return US_BULK_TRANSFER_FAILED;
-		}
-
-		/* -ENOENT -- we canceled this transfer */
-		if (result == -ENOENT) {
-			US_DEBUGP("usb_stor_transfer_partial(): transfer aborted\n");
-			return US_BULK_TRANSFER_ABORTED;
-		}
+	/* NAK - that means we've retried a few times already */
+	if (result == -ETIMEDOUT) {
+		US_DEBUGP("usb_stor_transfer_partial(): device NAKed\n");
+		return US_BULK_TRANSFER_FAILED;
+	}
 
-		/* the catch-all case */
+	/* the catch-all error case */
+	if (result) {
 		US_DEBUGP("usb_stor_transfer_partial(): unknown error\n");
 		return US_BULK_TRANSFER_FAILED;
 	}
@@ -550,7 +567,7 @@
  * Transfer an entire SCSI command's worth of data payload over the bulk
  * pipe.
  *
- * Note that this uses usb_stor_transfer_partial to achieve it's goals -- this
+ * Note that this uses usb_stor_transfer_partial to achieve its goals -- this
  * function simply determines if we're going to use scatter-gather or not,
  * and acts appropriately.  For now, it also re-interprets the error codes.
  */
@@ -631,6 +648,14 @@
 		return;
 	}
 
+	/* if there is a transport error, reset and don't auto-sense */
+	if (result == USB_STOR_TRANSPORT_ERROR) {
+		US_DEBUGP("-- transport indicates error, resetting\n");
+		us->transport_reset(us);
+		srb->result = DID_ERROR << 16;
+		return;
+	}
+
 	/* Determine if we need to auto-sense
 	 *
 	 * I normally don't use a flag like this, but it's almost impossible
@@ -660,7 +685,7 @@
 	}
 
 	/*
-	 * If we have an error, we're going to do a REQUEST_SENSE 
+	 * If we have a failure, we're going to do a REQUEST_SENSE 
 	 * automatically.  Note that we differentiate between a command
 	 * "failure" and an "error" in the transport mechanism.
 	 */
@@ -668,13 +693,6 @@
 		US_DEBUGP("-- transport indicates command failure\n");
 		need_auto_sense = 1;
 	}
-	if (result == USB_STOR_TRANSPORT_ERROR) {
-		us->transport_reset(us);
-		US_DEBUGP("-- transport indicates transport failure\n");
-		need_auto_sense = 0;
-		srb->result = DID_ERROR << 16;
-		return;
-	}
 
 	/*
 	 * Also, if we have a short transfer on a command that can't have
@@ -730,6 +748,19 @@
 
 		/* issue the auto-sense command */
 		temp_result = us->transport(us->srb, us);
+
+		/* let's clean up right away */
+		srb->request_buffer = old_request_buffer;
+		srb->request_bufflen = old_request_bufflen;
+		srb->use_sg = old_sg;
+		srb->sc_data_direction = old_sc_data_direction;
+		memcpy(srb->cmnd, old_cmnd, MAX_COMMAND_SIZE);
+
+		if (temp_result == USB_STOR_TRANSPORT_ABORTED) {
+			US_DEBUGP("-- auto-sense aborted\n");
+			srb->result = DID_ABORT << 16;
+			return;
+		}
 		if (temp_result != USB_STOR_TRANSPORT_GOOD) {
 			US_DEBUGP("-- auto-sense failure\n");
 
@@ -760,13 +791,6 @@
 		/* set the result so the higher layers expect this data */
 		srb->result = CHECK_CONDITION << 1;
 
-		/* we're done here, let's clean up */
-		srb->request_buffer = old_request_buffer;
-		srb->request_bufflen = old_request_bufflen;
-		srb->use_sg = old_sg;
-		srb->sc_data_direction = old_sc_data_direction;
-		memcpy(srb->cmnd, old_cmnd, MAX_COMMAND_SIZE);
-
 		/* If things are really okay, then let's show that */
 		if ((srb->sense_buffer[2] & 0xf) == 0x0)
 			srb->result = GOOD << 1;
@@ -789,6 +813,39 @@
 		srb->sense_buffer[0] = 0x0;
 }
 
+/* Abort the currently running scsi command or device reset.
+ */
+void usb_stor_abort_transport(struct us_data *us)
+{
+	int state = atomic_read(&us->sm_state);
+
+	US_DEBUGP("usb_stor_abort_transport called\n");
+
+	/* If the current state is wrong or if there's
+	 *  no srb, then there's nothing to do */
+	if ( !(state == US_STATE_RUNNING || state == US_STATE_RESETTING)
+		    || !us->srb) {
+		US_DEBUGP("-- invalid current state\n");
+		return;
+	}
+	atomic_set(&us->sm_state, US_STATE_ABORTING);
+
+	/* If the state machine is blocked waiting for an URB or an IRQ,
+	 *  let's wake it up */
+
+	/* if we have an URB pending, cancel it */
+	if (us->current_urb->status == -EINPROGRESS) {
+		US_DEBUGP("-- cancelling URB\n");
+		usb_unlink_urb(us->current_urb);
+	}
+
+	/* if we are waiting for an IRQ, simulate it */
+	else if (test_bit(IP_WANTED, &us->bitflags)) {
+		US_DEBUGP("-- simulating missing IRQ\n");
+		usb_stor_CBI_irq(us->irq_urb);
+	}
+}
+
 /*
  * Control/Bulk/Interrupt transport
  */
@@ -804,15 +861,40 @@
 	US_DEBUGP("-- Interrupt Status (0x%x, 0x%x)\n",
 			us->irqbuf[0], us->irqbuf[1]);
 
-	/* reject improper IRQs */
-	if (urb->actual_length != 2) {
-		US_DEBUGP("-- IRQ too short\n");
+	/* has the current command been aborted? */
+	if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+
+		/* was this a wanted interrupt? */
+		if (!test_and_clear_bit(IP_WANTED, &us->bitflags)) {
+			US_DEBUGP("ERROR: Unwanted interrupt received!\n");
+			return;
+		}
+		US_DEBUGP("-- command aborted\n");
+
+		/* wake up the command thread */
+		up(&us->ip_waitq);
 		return;
 	}
 
 	/* is the device removed? */
 	if (urb->status == -ENOENT) {
 		US_DEBUGP("-- device has been removed\n");
+
+		/* was this a wanted interrupt? */
+		if (!test_and_clear_bit(IP_WANTED, &us->bitflags))
+			return;
+
+		/* indicate a transport error -- this is the best we can do */
+		us->irqdata[0] = us->irqdata[1] = 0xFF;
+
+		/* wake up the command thread */
+		up(&us->ip_waitq);
+		return;
+	}
+
+	/* reject improper IRQs */
+	if (urb->actual_length != 2) {
+		US_DEBUGP("-- IRQ too short\n");
 		return;
 	}
 
@@ -823,21 +905,16 @@
 	}
 
 	/* was this a wanted interrupt? */
-	if (!atomic_read(us->ip_wanted)) {
+	if (!test_and_clear_bit(IP_WANTED, &us->bitflags)) {
 		US_DEBUGP("ERROR: Unwanted interrupt received!\n");
 		return;
 	}
-
-	/* adjust the flag */
-	atomic_set(us->ip_wanted, 0);
 		
 	/* copy the valid data */
 	us->irqdata[0] = us->irqbuf[0];
 	us->irqdata[1] = us->irqbuf[1];
 
 	/* wake up the command thread */
-	US_DEBUGP("-- Current value of ip_waitq is: %d\n",
-			atomic_read(&us->ip_waitq.count));
 	up(&(us->ip_waitq));
 }
 
@@ -845,13 +922,13 @@
 {
 	int result;
 
-	/* Set up for status notification */
-	atomic_set(us->ip_wanted, 1);
-
 	/* re-initialize the mutex so that we avoid any races with
 	 * early/late IRQs from previous commands */
 	init_MUTEX_LOCKED(&(us->ip_waitq));
 
+	/* Set up for status notification */
+	set_bit(IP_WANTED, &us->bitflags);
+
 	/* COMMAND STAGE */
 	/* let's send the command via the control pipe */
 	result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0),
@@ -863,22 +940,26 @@
 	US_DEBUGP("Call to usb_stor_control_msg() returned %d\n", result);
 	if (result < 0) {
 		/* Reset flag for status notification */
-		atomic_set(us->ip_wanted, 0);
+		clear_bit(IP_WANTED, &us->bitflags);
+	}
+
+	/* if the command was aborted, indicate that */
+	if (result == -ENOENT)
+		return USB_STOR_TRANSPORT_ABORTED;
+
+	/* STALL must be cleared when it is detected */
+	if (result == -EPIPE) {
+		US_DEBUGP("-- Stall on control pipe. Clearing\n");
+		result = usb_stor_clear_halt(us,	
+			usb_sndctrlpipe(us->pusb_dev, 0));
 
 		/* if the command was aborted, indicate that */
 		if (result == -ENOENT)
 			return USB_STOR_TRANSPORT_ABORTED;
+		return USB_STOR_TRANSPORT_FAILED;
+	}
 
-		/* STALL must be cleared when they are detected */
-		if (result == -EPIPE) {
-			US_DEBUGP("-- Stall on control pipe. Clearing\n");
-			result = usb_stor_clear_halt(us->pusb_dev,	
-					    usb_sndctrlpipe(us->pusb_dev,
-							    0));
-			US_DEBUGP("-- usb_stor_clear_halt() returns %d\n", result);
-			return USB_STOR_TRANSPORT_FAILED;
-		}
-
+	if (result < 0) {
 		/* Uh oh... serious problem here */
 		return USB_STOR_TRANSPORT_ERROR;
 	}
@@ -887,24 +968,28 @@
 	/* transfer the data payload for this command, if one exists*/
 	if (usb_stor_transfer_length(srb)) {
 		usb_stor_transfer(srb, us);
-		US_DEBUGP("CBI data stage result is 0x%x\n", srb->result);
+		result = srb->result;
+		US_DEBUGP("CBI data stage result is 0x%x\n", result);
 
-		/* if it was aborted, we need to indicate that */
-		if (srb->result == USB_STOR_TRANSPORT_ABORTED) {
+		/* report any errors */
+		if (result == US_BULK_TRANSFER_ABORTED) {
+			clear_bit(IP_WANTED, &us->bitflags);
 			return USB_STOR_TRANSPORT_ABORTED;
 		}
+		if (result == US_BULK_TRANSFER_FAILED) {
+			clear_bit(IP_WANTED, &us->bitflags);
+			return USB_STOR_TRANSPORT_FAILED;
+		}
 	}
 
 	/* STATUS STAGE */
 
 	/* go to sleep until we get this interrupt */
-	US_DEBUGP("Current value of ip_waitq is: %d\n", atomic_read(&us->ip_waitq.count));
 	down(&(us->ip_waitq));
 
-	/* if we were woken up by an abort instead of the actual interrupt */
-	if (atomic_read(us->ip_wanted)) {
-		US_DEBUGP("Did not get interrupt on CBI\n");
-		atomic_set(us->ip_wanted, 0);
+	/* has the current command been aborted? */
+	if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
+		US_DEBUGP("CBI interrupt aborted\n");
 		return USB_STOR_TRANSPORT_ABORTED;
 	}
 
@@ -922,11 +1007,12 @@
 		if (srb->cmnd[0] == REQUEST_SENSE ||
 		    srb->cmnd[0] == INQUIRY)
 			return USB_STOR_TRANSPORT_GOOD;
-		else
-			if (((unsigned char*)us->irq_urb->transfer_buffer)[0])
+		else {
+			if (us->irqdata[0])
 				return USB_STOR_TRANSPORT_FAILED;
 			else
 				return USB_STOR_TRANSPORT_GOOD;
+		}
 	}
 
 	/* If not UFI, we interpret the data as a result code 
@@ -976,10 +1062,12 @@
 		/* a stall is a fatal condition from the device */
 		if (result == -EPIPE) {
 			US_DEBUGP("-- Stall on control pipe. Clearing\n");
-			result = usb_stor_clear_halt(us->pusb_dev, 
-					    usb_sndctrlpipe(us->pusb_dev,
-							    0));
-			US_DEBUGP("-- usb_stor_clear_halt() returns %d\n", result);
+			result = usb_stor_clear_halt(us,
+				usb_sndctrlpipe(us->pusb_dev, 0));
+
+			/* if the command was aborted, indicate that */
+			if (result == -ENOENT)
+				return USB_STOR_TRANSPORT_ABORTED;
 			return USB_STOR_TRANSPORT_FAILED;
 		}
 
@@ -991,11 +1079,16 @@
 	/* transfer the data payload for this command, if one exists*/
 	if (usb_stor_transfer_length(srb)) {
 		usb_stor_transfer(srb, us);
-		US_DEBUGP("CB data stage result is 0x%x\n", srb->result);
+		result = srb->result;
+		US_DEBUGP("CB data stage result is 0x%x\n", result);
 
-		/* if it was aborted, we need to indicate that */
-		if (srb->result == USB_STOR_TRANSPORT_ABORTED)
+		/* report any errors */
+		if (result == US_BULK_TRANSFER_ABORTED) {
 			return USB_STOR_TRANSPORT_ABORTED;
+		}
+		if (result == US_BULK_TRANSFER_FAILED) {
+			return USB_STOR_TRANSPORT_FAILED;
+		}
 	}
 
 	/* STATUS STAGE */
@@ -1016,7 +1109,8 @@
 	int result;
 	int pipe;
 
-	/* issue the command */
+	/* issue the command -- use usb_control_msg() because
+	 *  the state machine is not yet alive */
 	pipe = usb_rcvctrlpipe(us->pusb_dev, 0);
 	result = usb_control_msg(us->pusb_dev, pipe,
 				 US_BULK_GET_MAX_LUN, 
@@ -1034,15 +1128,16 @@
 	/* if we get a STALL, clear the stall */
 	if (result == -EPIPE) {
 		US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
-		usb_stor_clear_halt(us->pusb_dev, pipe);
+
+		/* Use usb_clear_halt() because the state machine
+		 *  is not yet alive */
+		usb_clear_halt(us->pusb_dev, pipe);
 	}
 
 	/* return the default -- no LUNs */
 	return 0;
 }
 
-int usb_stor_Bulk_reset(struct us_data *us);
-
 int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
 {
 	struct bulk_cb_wrap bcb;
@@ -1051,10 +1146,6 @@
 	int pipe;
 	int partial;
 
-	/* if the device was removed, then we're already reset */
-	if (!us->pusb_dev)
-		return SUCCESS;
-
 	/* set up the command wrapper */
 	bcb.Signature = cpu_to_le32(US_BULK_CB_SIGN);
 	bcb.DataTransferLength = cpu_to_le32(usb_stor_transfer_length(srb));
@@ -1088,7 +1179,12 @@
 	/* if we stall, we need to clear it before we go on */
 	if (result == -EPIPE) {
 		US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
-		usb_stor_clear_halt(us->pusb_dev, pipe);
+		result = usb_stor_clear_halt(us, pipe);
+
+		/* if the command was aborted, indicate that */
+		if (result == -ENOENT)
+			return USB_STOR_TRANSPORT_ABORTED;
+		result = -EPIPE;
 	} else if (result) {
 		/* unknown error -- we've got a problem */
 		return USB_STOR_TRANSPORT_ERROR;
@@ -1099,11 +1195,11 @@
 		/* send/receive data payload, if there is any */
 		if (bcb.DataTransferLength) {
 			usb_stor_transfer(srb, us);
-			US_DEBUGP("Bulk data transfer result 0x%x\n", 
-				  srb->result);
+			result = srb->result;
+			US_DEBUGP("Bulk data transfer result 0x%x\n", result);
 
 			/* if it was aborted, we need to indicate that */
-			if (srb->result == USB_STOR_TRANSPORT_ABORTED)
+			if (result == US_BULK_TRANSFER_ABORTED)
 				return USB_STOR_TRANSPORT_ABORTED;
 		}
 	}
@@ -1127,8 +1223,12 @@
 	/* did the attempt to read the CSW fail? */
 	if (result == -EPIPE) {
 		US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
-		usb_stor_clear_halt(us->pusb_dev, pipe);
-	       
+		result = usb_stor_clear_halt(us, pipe);
+
+		/* if the command was aborted, indicate that */
+		if (result == -ENOENT)
+			return USB_STOR_TRANSPORT_ABORTED;
+
 		/* get the status again */
 		US_DEBUGP("Attempting to get CSW (2nd try)...\n");
 		result = usb_stor_bulk_msg(us, &bcs, pipe,
@@ -1141,7 +1241,11 @@
 		/* if it fails again, we need a reset and return an error*/
 		if (result == -EPIPE) {
 			US_DEBUGP("clearing halt for pipe 0x%x\n", pipe);
-			usb_stor_clear_halt(us->pusb_dev, pipe);
+			result = usb_stor_clear_halt(us, pipe);
+
+			/* if the command was aborted, indicate that */
+			if (result == -ENOENT)
+				return USB_STOR_TRANSPORT_ABORTED;
 			return USB_STOR_TRANSPORT_ERROR;
 		}
 	}
@@ -1188,46 +1292,117 @@
  * Reset routines
  ***********************************************************************/
 
-/* This issues a CB[I] Reset to the device in question
+struct us_timeout {
+	struct us_data *us;
+	spinlock_t timer_lock;
+};
+
+/* The timeout event handler
  */
-int usb_stor_CB_reset(struct us_data *us)
+static void usb_stor_timeout_handler(unsigned long to__)
 {
-	unsigned char cmd[12];
-	int result;
+	struct us_timeout *to = (struct us_timeout *) to__;
+	struct us_data *us = to->us;
 
-	US_DEBUGP("CB_reset() called\n");
+	US_DEBUGP("Timeout occurred\n");
 
-	/* if the device was removed, then we're already reset */
-	if (!us->pusb_dev)
-		return SUCCESS;
+	/* abort the current request */
+	usb_stor_abort_transport(us);
 
-	memset(cmd, 0xFF, sizeof(cmd));
-	cmd[0] = SEND_DIAGNOSTIC;
-	cmd[1] = 4;
-	result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
-				 US_CBI_ADSC, 
-				 USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-				 0, us->ifnum, cmd, sizeof(cmd), HZ*5);
+	/* let the reset routine know we have finished */
+	spin_unlock(&to->timer_lock);
+}
 
-	if (result < 0) {
-		US_DEBUGP("CB[I] soft reset failed %d\n", result);
-		return FAILED;
-	}
+/* This is the common part of the device reset code.
+ *
+ * It's handy that every transport mechanism uses the control endpoint for
+ * resets.
+ *
+ * Basically, we send a reset with a 20-second timeout, so we don't get
+ * jammed attempting to do the reset.
+ */
+void usb_stor_reset_common(struct us_data *us, u8 request, u8 requesttype,
+		u16 value, u16 index, void *data, u16 size)
+{
+	int result;
+	struct us_timeout timeout_data = {us, SPIN_LOCK_UNLOCKED};
+	struct timer_list timeout_list;
+
+	/* prepare the timeout handler */
+	spin_lock(&timeout_data.timer_lock);
+	init_timer(&timeout_list);
+
+	/* A 20-second timeout may seem rather long, but a LaCie
+	 *  StudioDrive USB2 device takes 16+ seconds to get going
+	 *  following a powerup or USB attach event. */
+
+	timeout_list.expires = jiffies + 20 * HZ;
+	timeout_list.data = (unsigned long) &timeout_data;
+	timeout_list.function = usb_stor_timeout_handler;
+	add_timer(&timeout_list);
+
+	result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0),
+			 request, requesttype, value, index, data, size);
+	if (result < 0)
+		goto Done;
 
 	/* long wait for reset */
 	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout(HZ*6);
 	set_current_state(TASK_RUNNING);
 
-	US_DEBUGP("CB_reset: clearing endpoint halt\n");
-	usb_stor_clear_halt(us->pusb_dev, 
-			    usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
-	usb_stor_clear_halt(us->pusb_dev, 
-			    usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
+	US_DEBUGP("Soft reset: clearing bulk-in endpoint halt\n");
+	result = usb_stor_clear_halt(us,
+		usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+	if (result < 0)
+		goto Done;
+
+	US_DEBUGP("Soft reset: clearing bulk-out endpoint halt\n");
+	result = usb_stor_clear_halt(us,
+		usb_sndbulkpipe(us->pusb_dev, us->ep_out));
+
+	Done:
+
+	/* prevent the timer from coming back to haunt us */
+	if (!del_timer(&timeout_list)) {
+		/* the handler has already started; wait for it to finish */
+		spin_lock(&timeout_data.timer_lock);
+		/* change the abort into a timeout */
+		if (result == -ENOENT)
+			result = -ETIMEDOUT;
+	}
 
-	US_DEBUGP("CB_reset done\n");
 	/* return a result code based on the result of the control message */
-	return SUCCESS;
+	if (result >= 0)
+		US_DEBUGP("Soft reset done\n");
+	else
+		US_DEBUGP("Soft reset failed: %d\n", result);
+
+	if (result == -ETIMEDOUT)
+		us->srb->result = DID_TIME_OUT << 16;
+	else if (result == -ENOENT)
+		us->srb->result = DID_ABORT << 16;
+	else if (result < 0)
+		us->srb->result = DID_ERROR << 16;
+	else
+		us->srb->result = GOOD << 1;
+}
+
+/* This issues a CB[I] Reset to the device in question
+ */
+int usb_stor_CB_reset(struct us_data *us)
+{
+	unsigned char cmd[12];
+
+	US_DEBUGP("CB_reset() called\n");
+
+	memset(cmd, 0xFF, sizeof(cmd));
+	cmd[0] = SEND_DIAGNOSTIC;
+	cmd[1] = 4;
+	usb_stor_reset_common(us, US_CBI_ADSC, 
+				 USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+				 0, us->ifnum, cmd, sizeof(cmd));
+	return (us->srb->result == GOOD << 1 ? SUCCESS : FAILED);
 }
 
 /* This issues a Bulk-only Reset to the device in question, including
@@ -1235,34 +1410,10 @@
  */
 int usb_stor_Bulk_reset(struct us_data *us)
 {
-	int result;
-
 	US_DEBUGP("Bulk reset requested\n");
 
-	/* if the device was removed, then we're already reset */
-	if (!us->pusb_dev)
-		return SUCCESS;
-
-	result = usb_control_msg(us->pusb_dev, 
-				 usb_sndctrlpipe(us->pusb_dev,0), 
-				 US_BULK_RESET_REQUEST, 
+	usb_stor_reset_common(us, US_BULK_RESET_REQUEST, 
 				 USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-				 0, us->ifnum, NULL, 0, HZ*5);
-
-	if (result < 0) {
-		US_DEBUGP("Bulk soft reset failed %d\n", result);
-		return FAILED;
-	}
-
-	/* long wait for reset */
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	schedule_timeout(HZ*6);
-	set_current_state(TASK_RUNNING);
-
-	usb_stor_clear_halt(us->pusb_dev, 
-			    usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
-	usb_stor_clear_halt(us->pusb_dev, 
-			    usb_sndbulkpipe(us->pusb_dev, us->ep_out));
-	US_DEBUGP("Bulk soft reset completed\n");
-	return SUCCESS;
+				 0, us->ifnum, NULL, 0);
+	return (us->srb->result == GOOD << 1 ? SUCCESS : FAILED);
 }
diff -Nru a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h
--- a/drivers/usb/storage/transport.h	Sat May 18 00:46:09 2002
+++ b/drivers/usb/storage/transport.h	Sat May 18 00:46:09 2002
@@ -1,7 +1,7 @@
 /* Driver for USB Mass Storage compliant devices
  * Transport Functions Header File
  *
- * $Id: transport.h,v 1.15 2001/03/17 20:06:23 jrmayfield Exp $
+ * $Id: transport.h,v 1.18 2002/04/21 02:57:59 mdharm Exp $
  *
  * Current development and maintenance by:
  *   (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -58,6 +58,9 @@
 #define US_PR_EUSB_SDDR09	0x81	/* SCM-SCSI bridge for
 						SDDR-09 */
 #endif
+#ifdef CONFIG_USB_STORAGE_SDDR55
+#define US_PR_SDDR55	0x82		/* SDDR-55 (made up) */
+#endif
 #define US_PR_DPCM_USB  0xf0		/* Combination CB/SDDR09 */
 
 #ifdef CONFIG_USB_STORAGE_FREECOM
@@ -146,11 +149,12 @@
 
 extern unsigned int usb_stor_transfer_length(Scsi_Cmnd*);
 extern void usb_stor_invoke_transport(Scsi_Cmnd*, struct us_data*);
+extern void usb_stor_abort_transport(struct us_data*);
 extern int usb_stor_transfer_partial(struct us_data*, char*, int);
 extern int usb_stor_bulk_msg(struct us_data*, void*, int, unsigned int,
 		unsigned int*);
 extern int usb_stor_control_msg(struct us_data*, unsigned int, u8, u8,
 		u16, u16, void*, u16);
+extern int usb_stor_clear_halt(struct us_data*, int );
 extern void usb_stor_transfer(Scsi_Cmnd*, struct us_data*);
-extern int usb_stor_clear_halt(struct usb_device*, int );
 #endif
diff -Nru a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
--- a/drivers/usb/storage/unusual_devs.h	Sat May 18 00:46:14 2002
+++ b/drivers/usb/storage/unusual_devs.h	Sat May 18 00:46:14 2002
@@ -1,10 +1,10 @@
 /* Driver for USB Mass Storage compliant devices
  * Ununsual Devices File
  *
- * $Id: unusual_devs.h,v 1.25 2002/01/13 06:39:17 mdharm Exp $
+ * $Id: unusual_devs.h,v 1.32 2002/02/25 02:41:24 mdharm Exp $
  *
  * Current development and maintenance by:
- *   (c) 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *   (c) 2000-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
  *
  * Initial work by:
  *   (c) 2000 Adam J. Richter (adam@yggdrasil.com), Yggdrasil Computing, Inc.
@@ -110,6 +110,13 @@
                "LS-120 Camera",
                US_SC_UFI, US_PR_CBI, NULL, 0),
 
+/* Reported by Peter Wächtler <pwaechtler@loewe-komp.de> */
+UNUSUAL_DEV(  0x04ce, 0x0002, 0x0074, 0x0074,
+		"ScanLogic",
+		"SL11R-IDE 0049SQFP-1.2 A002",
+		US_SC_SCSI, US_PR_BULK, NULL,
+		US_FL_FIX_INQUIRY ),
+
 /* Most of the following entries were developed with the help of
  * Shuttle/SCM directly.
  */
@@ -198,7 +205,7 @@
 /* This entry is needed because the device reports Sub=ff */
 UNUSUAL_DEV(  0x054c, 0x0010, 0x0106, 0x0422, 
 		"Sony",
-		"DSC-S30/S70/S75/505V/F505", 
+		"DSC-S30/S70/S75/505V/F505/F707", 
 		US_SC_SCSI, US_PR_CB, NULL,
 		US_FL_SINGLE_LUN | US_FL_START_STOP | US_FL_MODE_XLATE ),
 
@@ -289,9 +296,36 @@
 		"Lexar",
 		"Jumpshot USB CF Reader",
 		US_SC_SCSI, US_PR_JUMPSHOT, NULL,
-		US_FL_MODE_XLATE | US_FL_START_STOP ),
+		US_FL_MODE_XLATE ),
 #endif
 
+/* Reported by Carlos Villegas <cav@uniscope.co.jp>
+ * This device needs an INQUIRY of exactly 36-bytes to function.
+ * That is the only reason this entry is needed.
+ */
+UNUSUAL_DEV(  0x05e3, 0x0700, 0x0000, 0xffff,
+		"SIIG",
+		"CompactFlash Card Reader",
+		US_SC_SCSI, US_PR_BULK, NULL,
+		US_FL_FIX_INQUIRY ),
+
+/* Reported by Peter Marks <peter.marks@turner.com>
+ * Like the SIIG unit above, this unit needs an INQUIRY to ask for exactly
+ * 36 bytes of data.  No more, no less. That is the only reason this entry
+ * is needed.
+ */
+UNUSUAL_DEV(  0x05e3, 0x0702, 0x0000, 0xffff,
+		"EagleTec",
+		"External Hard Disk",
+		US_SC_SCSI, US_PR_BULK, NULL,
+		US_FL_FIX_INQUIRY ),
+
+UNUSUAL_DEV(  0x05e3, 0x0700, 0x0000, 0x9999,
+		"Unknown",
+		"GL641USB based CF Card reader",
+		US_SC_SCSI, US_PR_BULK, NULL,
+		US_FL_FIX_INQUIRY | US_FL_MODE_XLATE),
+
 UNUSUAL_DEV(  0x0644, 0x0000, 0x0100, 0x0100, 
 		"TEAC",
 		"Floppy Drive",
@@ -305,6 +339,14 @@
 		US_FL_SINGLE_LUN | US_FL_START_STOP ),
 #endif
 
+/* Submitted by kedar@centillium
+ * Needed for START_STOP flag, but that is unconfirmed */
+UNUSUAL_DEV( 0x0686, 0x4006, 0x0001, 0x0001,
+                "Minolta",
+                "Dimage S304",
+                US_SC_SCSI, US_PR_BULK, NULL,
+                US_FL_START_STOP ),
+
 /* Submitted by f.brugmans@hccnet.nl
  * Needed for START_STOP flag */
 UNUSUAL_DEV( 0x0686, 0x4007, 0x0001, 0x0001,
@@ -381,7 +423,7 @@
 		"Datafab",
 		"MDCFE-B USB CF Reader",
 		US_SC_SCSI, US_PR_DATAFAB, NULL,
-		US_FL_MODE_XLATE | US_FL_START_STOP ),
+		US_FL_MODE_XLATE ),
 
 	/*
 	 * The following Datafab-based devices may or may not work
@@ -398,38 +440,47 @@
 		"SIIG/Datafab",
 		"SIIG/Datafab Memory Stick+CF Reader/Writer",
 		US_SC_SCSI, US_PR_DATAFAB, NULL,
-		US_FL_MODE_XLATE | US_FL_START_STOP ),
+		US_FL_MODE_XLATE ),
 
 UNUSUAL_DEV( 0x07c4, 0xa003, 0x0000, 0xffff,
 		"Datafab/Unknown",
 		"Datafab-based Reader",
 		US_SC_SCSI, US_PR_DATAFAB, NULL,
-		US_FL_MODE_XLATE | US_FL_START_STOP ),
+		US_FL_MODE_XLATE ),
 
 UNUSUAL_DEV( 0x07c4, 0xa004, 0x0000, 0xffff,
 		"Datafab/Unknown",
 		"Datafab-based Reader",
 		US_SC_SCSI, US_PR_DATAFAB, NULL,
-		US_FL_MODE_XLATE | US_FL_START_STOP ),
+		US_FL_MODE_XLATE ),
 
 UNUSUAL_DEV( 0x07c4, 0xa005, 0x0000, 0xffff,
 		"PNY/Datafab",
 		"PNY/Datafab CF+SM Reader",
 		US_SC_SCSI, US_PR_DATAFAB, NULL,
-		US_FL_MODE_XLATE | US_FL_START_STOP ),
+		US_FL_MODE_XLATE ),
 
 UNUSUAL_DEV( 0x07c4, 0xa006, 0x0000, 0xffff,
 		"Simple Tech/Datafab",
 		"Simple Tech/Datafab CF+SM Reader",
 		US_SC_SCSI, US_PR_DATAFAB, NULL,
-		US_FL_MODE_XLATE | US_FL_START_STOP ),
+		US_FL_MODE_XLATE ),
 
 /* Submitted by Olaf Hering <olh@suse.de> */
 UNUSUAL_DEV(  0x07c4, 0xa109, 0x0000, 0xffff,
 		"Datafab Systems, Inc.",
 		"USB to CF + SM Combo (LC1)",
 		US_SC_SCSI, US_PR_DATAFAB, NULL,
-		US_FL_MODE_XLATE | US_FL_START_STOP ),
+		US_FL_MODE_XLATE ),
+#endif
+
+#ifdef CONFIG_USB_STORAGE_SDDR55
+/* Contributed by Peter Waechtler */
+UNUSUAL_DEV( 0x07c4, 0xa103, 0x0000, 0x9999,
+		"Datafab",
+		"MDSM-B reader",
+		US_SC_SCSI, US_PR_SDDR55, NULL,
+		US_FL_FIX_INQUIRY ),
 #endif
 
 /* Casio QV 2x00/3x00/4000/8000 digital still cameras are not conformant
@@ -451,6 +502,12 @@
  		US_SC_SCSI, US_PR_CB, NULL,
 		US_FL_MODE_XLATE ),
 
+UNUSUAL_DEV(  0x0a16, 0x8888, 0x0100, 0x0100,
+		"IBM",
+		"IBM USB Memory Key",
+		US_SC_SCSI, US_PR_BULK, NULL,
+		US_FL_FIX_INQUIRY ),
+
 #ifdef CONFIG_USB_STORAGE_ISD200
 UNUSUAL_DEV(  0x0bf6, 0xa001, 0x0100, 0x0110,
                 "ATI",
@@ -459,6 +516,13 @@
                 0 ),
 #endif
 
+/* EasyDisk support. Submitted by Stanislav Karchebny <berk@madfire.net> */
+UNUSUAL_DEV(  0x1065, 0x2136, 0x0000, 0x0001,
+		"Global Channel Solutions",
+		"EasyDisk EDxxxx",
+		US_SC_SCSI, US_PR_BULK, NULL,
+		US_FL_MODE_XLATE | US_FL_START_STOP | US_FL_FIX_INQUIRY ),
+
 /* Submitted by Brian Hall <brihall@bigfoot.com>
  * Needed for START_STOP flag */
 UNUSUAL_DEV(  0x0c76, 0x0003, 0x0100, 0x0100,
@@ -469,9 +533,19 @@
 
 /* Reported by Dan Pilone <pilone@slac.com>
  * The device needs the flags only.
+ * Also reported by Brian Hall <brihall@bigfoot.com>, again for flags.
+ * I also suspect this device may have a broken serial number.
  */
 UNUSUAL_DEV(  0x1065, 0x2136, 0x0000, 0x9999,
 		"CCYU TECHNOLOGY",
 		"EasyDisk Portable Device",
 		US_SC_SCSI, US_PR_BULK, NULL,
 		US_FL_MODE_XLATE | US_FL_START_STOP),
+
+#ifdef CONFIG_USB_STORAGE_SDDR55
+UNUSUAL_DEV(  0x55aa, 0xa103, 0x0000, 0x9999, 
+		"Sandisk",
+		"ImageMate SDDR55",
+		US_SC_SCSI, US_PR_SDDR55, NULL,
+		US_FL_SINGLE_LUN),
+#endif
diff -Nru a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
--- a/drivers/usb/storage/usb.c	Sat May 18 00:46:08 2002
+++ b/drivers/usb/storage/usb.c	Sat May 18 00:46:08 2002
@@ -1,9 +1,9 @@
 /* Driver for USB Mass Storage compliant devices
  *
- * $Id: usb.c,v 1.70 2002/01/06 07:14:12 mdharm Exp $
+ * $Id: usb.c,v 1.75 2002/04/22 03:39:43 mdharm Exp $
  *
  * Current development and maintenance by:
- *   (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *   (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
  *
  * Developed with the assistance of:
  *   (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org)
@@ -60,6 +60,9 @@
 #ifdef CONFIG_USB_STORAGE_SDDR09
 #include "sddr09.h"
 #endif
+#ifdef CONFIG_USB_STORAGE_SDDR55
+#include "sddr55.h"
+#endif
 #ifdef CONFIG_USB_STORAGE_DPCM
 #include "dpcm.h"
 #endif
@@ -315,6 +318,7 @@
 	 * so get rid of all our resources..
 	 */
 	daemonize();
+	reparent_to_init();
 
 	/* avoid getting signals */
 	spin_lock_irq(&current->sigmask_lock);
@@ -333,9 +337,9 @@
 
 	/* signal that we've started the thread */
 	complete(&(us->notify));
-	set_current_state(TASK_INTERRUPTIBLE);
 
 	for(;;) {
+		struct Scsi_Host *host;
 		US_DEBUGP("*** thread sleeping.\n");
 		if(down_interruptible(&us->sema))
 			break;
@@ -343,15 +347,16 @@
 		US_DEBUGP("*** thread awakened.\n");
 
 		/* lock access to the queue element */
-		down(&(us->queue_exclusion));
+		spin_lock_irq(&us->queue_exclusion);
 
 		/* take the command off the queue */
 		action = us->action;
 		us->action = 0;
 		us->srb = us->queue_srb;
+		host = us->srb->host;
 
 		/* release the queue lock as fast as possible */
-		up(&(us->queue_exclusion));
+		spin_unlock_irq(&us->queue_exclusion);
 
 		switch (action) {
 		case US_ACT_COMMAND:
@@ -361,9 +366,10 @@
 			if (us->srb->sc_data_direction == SCSI_DATA_UNKNOWN) {
 				US_DEBUGP("UNKNOWN data direction\n");
 				us->srb->result = DID_ERROR << 16;
-				set_current_state(TASK_INTERRUPTIBLE);
+				scsi_lock(host);
 				us->srb->scsi_done(us->srb);
 				us->srb = NULL;
+				scsi_unlock(host);
 				break;
 			}
 
@@ -376,9 +382,10 @@
 					  us->srb->target, us->srb->lun);
 				us->srb->result = DID_BAD_TARGET << 16;
 
-				set_current_state(TASK_INTERRUPTIBLE);
+				scsi_lock(host);
 				us->srb->scsi_done(us->srb);
 				us->srb = NULL;
+				scsi_unlock(host);
 				break;
 			}
 
@@ -387,9 +394,10 @@
 					  us->srb->target, us->srb->lun);
 				us->srb->result = DID_BAD_TARGET << 16;
 
-				set_current_state(TASK_INTERRUPTIBLE);
+				scsi_lock(host);
 				us->srb->scsi_done(us->srb);
 				us->srb = NULL;
+				scsi_unlock(host);
 				break;
 			}
 
@@ -399,9 +407,10 @@
 				US_DEBUGP("Skipping START_STOP command\n");
 				us->srb->result = GOOD << 1;
 
-				set_current_state(TASK_INTERRUPTIBLE);
+				scsi_lock(host);
 				us->srb->scsi_done(us->srb);
 				us->srb = NULL;
+				scsi_unlock(host);
 				break;
 			}
 
@@ -409,7 +418,7 @@
 			down(&(us->dev_semaphore));
 
 			/* our device has gone - pretend not ready */
-			if (!us->pusb_dev) {
+			if (atomic_read(&us->sm_state) == US_STATE_DETACHED) {
 				US_DEBUGP("Request is for removed device\n");
 				/* For REQUEST_SENSE, it's the data.  But
 				 * for anything else, it should look like
@@ -433,7 +442,7 @@
 					       sizeof(usb_stor_sense_notready));
 					us->srb->result = CHECK_CONDITION << 1;
 				}
-			} else { /* !us->pusb_dev */
+			} else { /* atomic_read(&us->sm_state) == STATE_DETACHED */
 
 				/* Handle those devices which need us to fake 
 				 * their inquiry data */
@@ -449,7 +458,9 @@
 				} else {
 					/* we've got a command, let's do it! */
 					US_DEBUG(usb_stor_show_command(us->srb));
+					atomic_set(&us->sm_state, US_STATE_RUNNING);
 					us->proto_handler(us->srb, us);
+					atomic_set(&us->sm_state, US_STATE_IDLE);
 				}
 			}
 
@@ -460,14 +471,15 @@
 			if (us->srb->result != DID_ABORT << 16) {
 				US_DEBUGP("scsi cmd done, result=0x%x\n", 
 					   us->srb->result);
-				set_current_state(TASK_INTERRUPTIBLE);
+				scsi_lock(host);
 				us->srb->scsi_done(us->srb);
+				us->srb = NULL;
+				scsi_unlock(host);
 			} else {
 				US_DEBUGP("scsi command aborted\n");
-				set_current_state(TASK_INTERRUPTIBLE);
+				us->srb = NULL;
 				complete(&(us->notify));
 			}
-			us->srb = NULL;
 			break;
 
 		case US_ACT_DEVICE_RESET:
@@ -488,9 +500,6 @@
 		}
 	} /* for (;;) */
 
-	/* clean up after ourselves */
-	set_current_state(TASK_INTERRUPTIBLE);
-
 	/* notify the exit routine that we're actually exiting now */
 	complete(&(us->notify));
 
@@ -667,7 +676,7 @@
 	}
 
 	/* At this point, we're committed to using the device */
-	usb_inc_dev_use(dev);
+	usb_get_dev(dev);
 
 	/* clear the GUID and fetch the strings */
 	GUID_CLEAR(guid);
@@ -713,6 +722,7 @@
 		/* establish the connection to the new device upon reconnect */
 		ss->ifnum = ifnum;
 		ss->pusb_dev = dev;
+		atomic_set(&ss->sm_state, US_STATE_IDLE);
 
 		/* copy over the endpoint data */
 		if (ep_in)
@@ -725,14 +735,14 @@
 
 		/* allocate an IRQ callback if one is needed */
 		if ((ss->protocol == US_PR_CBI) && usb_stor_allocate_irq(ss)) {
-			usb_dec_dev_use(dev);
+			usb_put_dev(dev);
 			return NULL;
 		}
 
 		/* allocate the URB we're going to use */
 		ss->current_urb = usb_alloc_urb(0, GFP_KERNEL);
 		if (!ss->current_urb) {
-			usb_dec_dev_use(dev);
+			usb_put_dev(dev);
 			return NULL;
 		}
 
@@ -750,7 +760,7 @@
 		if ((ss = (struct us_data *)kmalloc(sizeof(struct us_data), 
 						    GFP_KERNEL)) == NULL) {
 			printk(KERN_WARNING USB_STORAGE "Out of memory\n");
-			usb_dec_dev_use(dev);
+			usb_put_dev(dev);
 			return NULL;
 		}
 		memset(ss, 0, sizeof(struct us_data));
@@ -759,14 +769,14 @@
 		ss->current_urb = usb_alloc_urb(0, GFP_KERNEL);
 		if (!ss->current_urb) {
 			kfree(ss);
-			usb_dec_dev_use(dev);
+			usb_put_dev(dev);
 			return NULL;
 		}
 
 		/* Initialize the mutexes only when the struct is new */
 		init_completion(&(ss->notify));
 		init_MUTEX_LOCKED(&(ss->ip_waitq));
-		init_MUTEX(&(ss->queue_exclusion));
+		spin_lock_init(&ss->queue_exclusion);
 		init_MUTEX(&(ss->irq_urb_sem));
 		init_MUTEX(&(ss->current_urb_sem));
 		init_MUTEX(&(ss->dev_semaphore));
@@ -860,6 +870,15 @@
 			break;
 #endif
 
+#ifdef CONFIG_USB_STORAGE_SDDR55
+		case US_PR_SDDR55:
+			ss->transport_name = "SDDR55";
+			ss->transport = sddr55_transport;
+			ss->transport_reset = sddr55_reset;
+			ss->max_lun = 0;
+			break;
+#endif
+
 #ifdef CONFIG_USB_STORAGE_DPCM
 		case US_PR_DPCM_USB:
 			ss->transport_name = "Control/Bulk-EUSB/SDDR09";
@@ -900,7 +919,7 @@
 			ss->transport_name = "Unknown";
 			kfree(ss->current_urb);
 			kfree(ss);
-			usb_dec_dev_use(dev);
+			usb_put_dev(dev);
 			return NULL;
 			break;
 		}
@@ -955,6 +974,7 @@
 			ss->protocol_name = "Unknown";
 			kfree(ss->current_urb);
 			kfree(ss);
+			usb_put_dev(dev);
 			return NULL;
 			break;
 		}
@@ -962,7 +982,9 @@
 
 		/* allocate an IRQ callback if one is needed */
 		if ((ss->protocol == US_PR_CBI) && usb_stor_allocate_irq(ss)) {
-			usb_dec_dev_use(dev);
+			kfree(ss->current_urb);
+			kfree(ss);
+			usb_put_dev(dev);
 			return NULL;
 		}
 
@@ -990,6 +1012,7 @@
 			unusual_dev->initFunction(ss);
 
 		/* start up our control thread */
+		atomic_set(&ss->sm_state, US_STATE_IDLE);
 		ss->pid = kernel_thread(usb_stor_control_thread, ss,
 					CLONE_VM);
 		if (ss->pid < 0) {
@@ -997,7 +1020,7 @@
 			       "Unable to start control thread\n");
 			kfree(ss->current_urb);
 			kfree(ss);
-			usb_dec_dev_use(dev);
+			usb_put_dev(dev);
 			return NULL;
 		}
 
@@ -1006,7 +1029,7 @@
 
 		/* now register	 - our detect function will be called */
 		ss->htmplt.module = THIS_MODULE;
-		scsi_register_host(&ss->htmplt);
+		scsi_register_host(&(ss->htmplt));
 
 		/* lock access to the data structures */
 		down(&us_list_semaphore);
@@ -1064,8 +1087,9 @@
 	ss->current_urb = NULL;
 
 	/* mark the device as gone */
-	usb_dec_dev_use(ss->pusb_dev);
+	usb_put_dev(ss->pusb_dev);
 	ss->pusb_dev = NULL;
+	atomic_set(&ss->sm_state, US_STATE_DETACHED);
 
 	/* unlock access to the device data structure */
 	up(&(ss->dev_semaphore));
@@ -1112,7 +1136,7 @@
 	 */
 	for (next = us_list; next; next = next->next) {
 		US_DEBUGP("-- calling scsi_unregister_host()\n");
-		scsi_unregister_host(&next->htmplt);
+		scsi_unregister_host(&(next->htmplt));
 	}
 
 	/* While there are still structures, free them.  Note that we are
diff -Nru a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
--- a/drivers/usb/storage/usb.h	Sat May 18 00:46:14 2002
+++ b/drivers/usb/storage/usb.h	Sat May 18 00:46:14 2002
@@ -1,7 +1,7 @@
 /* Driver for USB Mass Storage compliant devices
  * Main Header File
  *
- * $Id: usb.h,v 1.18 2001/07/30 00:27:59 mdharm Exp $
+ * $Id: usb.h,v 1.21 2002/04/21 02:57:59 mdharm Exp $
  *
  * Current development and maintenance by:
  *   (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -102,6 +102,12 @@
 #define US_FL_SCM_MULT_TARG   0x00000020 /* supports multiple targets */
 #define US_FL_FIX_INQUIRY     0x00000040 /* INQUIRY response needs fixing */
 
+#define US_STATE_DETACHED	1	/* State machine states */
+#define US_STATE_IDLE		2
+#define US_STATE_RUNNING	3
+#define US_STATE_RESETTING	4
+#define US_STATE_ABORTING	5
+
 #define USB_STOR_STRING_LEN 32
 
 typedef int (*trans_cmnd)(Scsi_Cmnd*, struct us_data*);
@@ -152,10 +158,12 @@
 	Scsi_Cmnd		*queue_srb;	 /* the single queue slot */
 	int			action;		 /* what to do		  */
 	int			pid;		 /* control thread	  */
+	atomic_t		sm_state;
 
 	/* interrupt info for CBI devices -- only good if attached */
 	struct semaphore	ip_waitq;	 /* for CBI interrupts	 */
-	atomic_t		ip_wanted[1];	 /* is an IRQ expected?	 */
+	unsigned long		bitflags;	 /* single-bit flags:	 */
+#define IP_WANTED	1			 /* is an IRQ expected?	 */
 
 	/* interrupt communications data */
 	struct semaphore	irq_urb_sem;	 /* to protect irq_urb	 */
@@ -172,7 +180,7 @@
 
 	/* mutual exclusion structures */
 	struct completion	notify;		 /* thread begin/end	    */
-	struct semaphore	queue_exclusion; /* to protect data structs */
+	spinlock_t		queue_exclusion; /* to protect data structs */
 	struct us_unusual_dev   *unusual_dev;	 /* If unusual device       */
 	void			*extra;		 /* Any extra data          */
 	extra_data_destructor	extra_destructor;/* extra data destructor   */
@@ -188,4 +196,17 @@
 /* Function to fill an inquiry response. See usb.c for details */
 extern void fill_inquiry_response(struct us_data *us,
 	unsigned char *data, unsigned int data_len);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,3)
+#define scsi_unlock(host)	spin_unlock_irq(host->host_lock)
+#define scsi_lock(host)		spin_lock_irq(host->host_lock)
+
+#define sg_address(psg)		(page_address((psg)->page) + (psg)->offset)
+#else
+#define scsi_unlock(host)	spin_unlock_irq(&io_request_lock)
+#define scsi_lock(host)		spin_lock_irq(&io_request_lock)
+
+#define sg_address(psg)		((psg)->address)
+#endif
+
 #endif
diff -Nru a/drivers/video/aty/mach64_gx.c b/drivers/video/aty/mach64_gx.c
--- a/drivers/video/aty/mach64_gx.c	Sat May 18 00:46:08 2002
+++ b/drivers/video/aty/mach64_gx.c	Sat May 18 00:46:08 2002
@@ -5,6 +5,7 @@
 
 #include <linux/delay.h>
 #include <linux/fb.h>
+#include <linux/sched.h>
 
 #include <asm/io.h>
 
diff -Nru a/fs/adfs/adfs.h b/fs/adfs/adfs.h
--- a/fs/adfs/adfs.h	Sat May 18 00:46:15 2002
+++ b/fs/adfs/adfs.h	Sat May 18 00:46:15 2002
@@ -77,7 +77,7 @@
 int adfs_notify_change(struct dentry *dentry, struct iattr *attr);
 
 /* map.c */
-extern int adfs_map_lookup(struct super_block *sb, int frag_id, int offset);
+extern int adfs_map_lookup(struct super_block *sb, unsigned int frag_id, unsigned int offset);
 extern unsigned int adfs_map_free(struct super_block *sb);
 
 /* Misc */
diff -Nru a/fs/adfs/dir.c b/fs/adfs/dir.c
--- a/fs/adfs/dir.c	Sat May 18 00:46:08 2002
+++ b/fs/adfs/dir.c	Sat May 18 00:46:08 2002
@@ -24,7 +24,7 @@
 /*
  * For future.  This should probably be per-directory.
  */
-static rwlock_t adfs_dir_lock;
+static rwlock_t adfs_dir_lock = RW_LOCK_UNLOCKED;
 
 static int
 adfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
diff -Nru a/fs/adfs/map.c b/fs/adfs/map.c
--- a/fs/adfs/map.c	Sat May 18 00:46:09 2002
+++ b/fs/adfs/map.c	Sat May 18 00:46:09 2002
@@ -1,7 +1,7 @@
 /*
  *  linux/fs/adfs/map.c
  *
- *  Copyright (C) 1997-1999 Russell King
+ *  Copyright (C) 1997-2002 Russell King
  *
  * 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
@@ -13,30 +13,64 @@
 #include <linux/adfs_fs.h>
 #include <linux/spinlock.h>
 
+#include <asm/unaligned.h>
+
 #include "adfs.h"
 
 /*
+ * The ADFS map is basically a set of sectors.  Each sector is called a
+ * zone which contains a bitstream made up of variable sized fragments.
+ * Each bit refers to a set of bytes in the filesystem, defined by
+ * log2bpmb.  This may be larger or smaller than the sector size, but
+ * the overall size it describes will always be a round number of
+ * sectors.  A fragment id is always idlen bits long.
+ *
+ *  < idlen > <       n        > <1>
+ * +---------+-------//---------+---+
+ * | frag id |  0000....000000  | 1 |
+ * +---------+-------//---------+---+
+ *
+ * The physical disk space used by a fragment is taken from the start of
+ * the fragment id up to and including the '1' bit - ie, idlen + n + 1
+ * bits.
+ *
+ * A fragment id can be repeated multiple times in the whole map for
+ * large or fragmented files.  The first map zone a fragment starts in
+ * is given by fragment id / ids_per_zone - this allows objects to start
+ * from any zone on the disk.
+ *
+ * Free space is described by a linked list of fragments.  Each free
+ * fragment describes free space in the same way as the other fragments,
+ * however, the frag id specifies an offset (in map bits) from the end
+ * of this fragment to the start of the next free fragment.
+ *
+ * Objects stored on the disk are allocated object ids (we use these as
+ * our inode numbers.)  Object ids contain a fragment id and an optional
+ * offset.  This allows a directory fragment to contain small files
+ * associated with that directory.
+ */
+
+/*
  * For the future...
  */
-static rwlock_t adfs_map_lock;
+static rwlock_t adfs_map_lock = RW_LOCK_UNLOCKED;
 
+/*
+ * This is fun.  We need to load up to 19 bits from the map at an
+ * arbitary bit alignment.  (We're limited to 19 bits by F+ version 2).
+ */
 #define GET_FRAG_ID(_map,_start,_idmask)				\
 	({								\
-		unsigned long _v2, _frag;				\
-		unsigned int _tmp;					\
-		_tmp = _start >> 5;					\
-		_frag = le32_to_cpu(_map[_tmp]);			\
-		_v2   = le32_to_cpu(_map[_tmp + 1]);			\
-		_tmp = start & 31;					\
-		_frag = (_frag >> _tmp) | (_v2 << (32 - _tmp));		\
+		unsigned char *_m = _map + (_start >> 3);		\
+		u32 _frag = get_unaligned((u32 *)_m);			\
+		_frag >>= (_start & 7);					\
 		_frag & _idmask;					\
 	})
 
 /*
- * return the map bit offset of the fragment frag_id in
- * the zone dm.
- * Note that the loop is optimised for best asm code -
- * look at the output of:
+ * return the map bit offset of the fragment frag_id in the zone dm.
+ * Note that the loop is optimised for best asm code - look at the
+ * output of:
  *  gcc -D__KERNEL__ -O2 -I../../include -o - -S map.c
  */
 static int
@@ -44,14 +78,13 @@
 	    const unsigned int frag_id, unsigned int *offset)
 {
 	const unsigned int mapsize = dm->dm_endbit;
-	const unsigned int idmask = (1 << idlen) - 1;
-	unsigned long *map = ((unsigned long *)dm->dm_bh->b_data) + 1;
+	const u32 idmask = (1 << idlen) - 1;
+	unsigned char *map = dm->dm_bh->b_data + 4;
 	unsigned int start = dm->dm_startbit;
 	unsigned int mapptr;
+	u32 frag;
 
 	do {
-		unsigned long frag;
-
 		frag = GET_FRAG_ID(map, start, idmask);
 		mapptr = start + idlen;
 
@@ -59,15 +92,17 @@
 		 * find end of fragment
 		 */
 		{
-			unsigned long v2;
+			u32 v, *_map = (u32 *)map;
 
-			while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) {
+			v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
+			while (v == 0) {
 				mapptr = (mapptr & ~31) + 32;
 				if (mapptr >= mapsize)
 					goto error;
+				v = le32_to_cpu(_map[mapptr >> 5]);
 			}
 
-			mapptr += 1 + ffz(~v2);
+			mapptr += 1 + ffz(~v);
 		}
 
 		if (frag == frag_id)
@@ -75,8 +110,11 @@
 again:
 		start = mapptr;
 	} while (mapptr < mapsize);
+	return -1;
 
 error:
+	printk(KERN_ERR "adfs: oversized fragment 0x%x at 0x%x-0x%x\n",
+		frag, start, mapptr);
 	return -1;
 
 found:
@@ -102,10 +140,10 @@
 	const unsigned int mapsize = dm->dm_endbit + 32;
 	const unsigned int idlen  = asb->s_idlen;
 	const unsigned int frag_idlen = idlen <= 15 ? idlen : 15;
-	const unsigned int idmask = (1 << frag_idlen) - 1;
-	unsigned long *map = (unsigned long *)dm->dm_bh->b_data;
+	const u32 idmask = (1 << frag_idlen) - 1;
+	unsigned char *map = dm->dm_bh->b_data;
 	unsigned int start = 8, mapptr;
-	unsigned long frag;
+	u32 frag;
 	unsigned long total = 0;
 
 	/*
@@ -133,15 +171,17 @@
 		 * find end of fragment
 		 */
 		{
-			unsigned long v2;
+			u32 v, *_map = (u32 *)map;
 
-			while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) {
+			v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
+			while (v == 0) {
 				mapptr = (mapptr & ~31) + 32;
 				if (mapptr >= mapsize)
 					goto error;
+				v = le32_to_cpu(_map[mapptr >> 5]);
 			}
 
-			mapptr += 1 + ffz(~v2);
+			mapptr += 1 + ffz(~v);
 		}
 
 		total += mapptr - start;
@@ -212,7 +252,9 @@
 	return signed_asl(total, asb->s_map2blk);
 }
 
-int adfs_map_lookup (struct super_block *sb, int frag_id, int offset)
+int
+adfs_map_lookup(struct super_block *sb, unsigned int frag_id,
+		unsigned int offset)
 {
 	struct adfs_sb_info *asb = &sb->u.adfs_sb;
 	unsigned int zone, mapoff;
@@ -245,12 +287,12 @@
 		return secoff + signed_asl(result, asb->s_map2blk);
 	}
 
-	adfs_error(sb, "fragment %04X at offset %d not found in map",
+	adfs_error(sb, "fragment 0x%04x at offset %d not found in map",
 		   frag_id, offset);
 	return 0;
 
 bad_fragment:
-	adfs_error(sb, "fragment %X is invalid (zone = %d, max = %d)",
+	adfs_error(sb, "invalid fragment 0x%04x (zone = %d, max = %d)",
 		   frag_id, zone, asb->s_map_size);
 	return 0;
 }
diff -Nru a/fs/adfs/super.c b/fs/adfs/super.c
--- a/fs/adfs/super.c	Sat May 18 00:46:08 2002
+++ b/fs/adfs/super.c	Sat May 18 00:46:08 2002
@@ -64,43 +64,9 @@
 	if (dr->disc_size_high >> dr->log2secsize)
 		return 1;
 
-	/*
-	 * The following checks are not required for F+
-	 * stage 1.
-	 */
-#if 0
-	/* idlen must be smaller be no greater than 15 */
-	if (dr->idlen > 15)
-		return 1;
-
-	/* nzones must be less than 128 for the root
-	 * directory to be addressable
-	 */
-	if (dr->nzones >= 128 && dr->nzones_high == 0)
-		return 1;
-
-	/* root must be of the form 0x2.. */
-	if ((le32_to_cpu(dr->root) & 0xffffff00) != 0x00000200)
-		return 1;
-#else
-	/*
-	 * Stage 2 F+ does not require the following check
-	 */
-#if 0
-	/* idlen must be no greater than 16 v2 [1.0] */
-	if (dr->idlen > 16)
-		return 1;
-
-	/* we can't handle F+ discs yet */
-	if (dr->format_version || dr->root_size)
-		return 1;
-
-#else
 	/* idlen must be no greater than 19 v2 [1.0] */
 	if (dr->idlen > 19)
 		return 1;
-#endif
-#endif
 
 	/* reserved bytes should be zero */
 	for (i = 0; i < sizeof(dr->unused52); i++)
diff -Nru a/fs/devfs/base.c b/fs/devfs/base.c
--- a/fs/devfs/base.c	Sat May 18 00:46:08 2002
+++ b/fs/devfs/base.c	Sat May 18 00:46:08 2002
@@ -620,6 +620,19 @@
 	       unlock_kernel() due to recent VFS locking changes. BKL isn't
 	       required in devfs.
   v1.13
+    20020428   Richard Gooch <rgooch@atnf.csiro.au>
+	       Removed 2.4.x compatibility code.
+  v1.14
+    20020510   Richard Gooch <rgooch@atnf.csiro.au>
+	       Added BKL to <devfs_open> because drivers still need it.
+  v1.15
+    20020512   Richard Gooch <rgooch@atnf.csiro.au>
+	       Protected <scan_dir_for_removable> and <get_removable_partition>
+	       from changing directory contents.
+  v1.16
+    20020514   Richard Gooch <rgooch@atnf.csiro.au>
+	       Minor cleanup of <scan_dir_for_removable>.
+  v1.17
 */
 #include <linux/types.h>
 #include <linux/errno.h>
@@ -652,7 +665,7 @@
 #include <asm/bitops.h>
 #include <asm/atomic.h>
 
-#define DEVFS_VERSION            "1.13 (20020406)"
+#define DEVFS_VERSION            "1.17 (20020514)"
 
 #define DEVFS_NAME "devfs"
 
@@ -1400,16 +1413,8 @@
 
 static int is_devfsd_or_child (struct fs_info *fs_info)
 {
-    struct task_struct *p;
-
     if (current == fs_info->devfsd_task) return (TRUE);
     if (current->pgrp == fs_info->devfsd_pgrp) return (TRUE);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,1)
-    for (p = current->p_opptr; p != &init_task; p = p->p_opptr)
-    {
-	if (p == fs_info->devfsd_task) return (TRUE);
-    }
-#endif
     return (FALSE);
 }   /*  End Function is_devfsd_or_child  */
 
@@ -1829,16 +1834,6 @@
     de->info = info;
     if ( ( err = _devfs_append_entry (dir, de, FALSE, &old) ) != 0 )
     {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,1)
-	if ( old && S_ISDIR (old->mode) )
-	{
-	    PRINTK ("(%s): using old entry in dir: %p \"%s\"\n",
-		    name, dir, dir->name);
-	    old->vfs_deletable = FALSE;
-	    devfs_put (dir);
-	    return old;
-	}
-#endif
 	PRINTK ("(%s): could not append to dir: %p \"%s\", err: %d\n",
 		name, dir, dir->name, err);
 	devfs_put (old);
@@ -2427,6 +2422,9 @@
  *	@de: The device.
  *
  *	Returns 1 if the media was changed, else 0.
+ *
+ *	This function may block, and may indirectly cause the parent directory
+ *	contents to be changed due to partition re-reading.
  */
 
 static int check_disc_changed (struct devfs_entry *de)
@@ -2461,19 +2459,29 @@
 /**
  *	scan_dir_for_removable - Scan a directory for removable media devices and check media.
  *	@dir: The directory.
+ *
+ *	This function may block, and may indirectly cause the directory
+ *	contents to be changed due to partition re-reading. The directory will
+ *	be locked for reading.
  */
 
 static void scan_dir_for_removable (struct devfs_entry *dir)
 {
     struct devfs_entry *de;
 
-    if (dir->u.dir.num_removable < 1) return;
-    for (de = dir->u.dir.first; de != NULL; de = de->next)
+    read_lock (&dir->u.dir.lock);
+    if (dir->u.dir.num_removable < 1) de = NULL;
+    else
     {
-	if ( !S_ISBLK (de->mode) ) continue;
-	if (!de->u.fcb.removable) continue;
-	check_disc_changed (de);
+	for (de = dir->u.dir.first; de != NULL; de = de->next)
+	{
+	    if (S_ISBLK (de->mode) && de->u.fcb.removable) break;
+	}
+	devfs_get (de);
     }
+    read_unlock (&dir->u.dir.lock);
+    if (de) check_disc_changed (de);
+    devfs_put (de);
 }   /*  End Function scan_dir_for_removable  */
 
 /**
@@ -2483,25 +2491,37 @@
  *	@namelen: The number of characters in <<name>>.
  *
  *	Returns 1 if the media was changed, else 0.
+ *
+ *	This function may block, and may indirectly cause the directory
+ *	contents to be changed due to partition re-reading. The directory must
+ *	be locked for reading upon entry, and will be unlocked upon exit.
  */
 
 static int get_removable_partition (struct devfs_entry *dir, const char *name,
 				    unsigned int namelen)
 {
+    int retval;
     struct devfs_entry *de;
 
+    if (dir->u.dir.num_removable < 1)
+    {
+	read_unlock (&dir->u.dir.lock);
+	return 0;
+    }
     for (de = dir->u.dir.first; de != NULL; de = de->next)
     {
-	if ( !S_ISBLK (de->mode) ) continue;
-	if (!de->u.fcb.removable) continue;
-	if (strcmp (de->name, "disc") == 0) return check_disc_changed (de);
+	if (!S_ISBLK (de->mode) || !de->u.fcb.removable) continue;
+	if (strcmp (de->name, "disc") == 0) break;
 	/*  Support for names where the partition is appended to the disc name
 	 */
 	if (de->namelen >= namelen) continue;
-	if (strncmp (de->name, name, de->namelen) != 0) continue;
-	return check_disc_changed (de);
+	if (strncmp (de->name, name, de->namelen) == 0) break;
     }
-    return 0;
+    devfs_get (de);
+    read_unlock (&dir->u.dir.lock);
+    retval = de ? check_disc_changed (de) : 0;
+    devfs_put (de);
+    return retval;
 }   /*  End Function get_removable_partition  */
 
 
@@ -2739,15 +2759,22 @@
     {
 	file->f_op = &def_blk_fops;
 	if (df->ops) inode->i_bdev->bd_op = df->ops;
+	err = def_blk_fops.open (inode, file);
     }
-    else file->f_op = fops_get ( (struct file_operations *) df->ops );
-    if (file->f_op)
-	err = file->f_op->open ? (*file->f_op->open) (inode, file) : 0;
     else
     {
-	/*  Fallback to legacy scheme  */
-	if ( S_ISCHR (inode->i_mode) ) err = chrdev_open (inode, file);
-	else err = -ENODEV;
+	file->f_op = fops_get ( (struct file_operations *) df->ops );
+	if (file->f_op)
+	{
+	    lock_kernel ();
+	    err = file->f_op->open ? (*file->f_op->open) (inode, file) : 0;
+	    unlock_kernel ();
+	}
+	else
+	{   /*  Fallback to legacy scheme  */
+	    if ( S_ISCHR (inode->i_mode) ) err = chrdev_open (inode, file);
+	    else err = -ENODEV;
+	}
     }
     if (err < 0) return err;
     /*  Open was successful  */
@@ -2947,15 +2974,17 @@
     if (parent == NULL) return ERR_PTR (-ENOENT);
     read_lock (&parent->u.dir.lock);
     de = _devfs_search_dir (parent, dentry->d_name.name, dentry->d_name.len);
-    read_unlock (&parent->u.dir.lock);
-    if ( (de == NULL) && (parent->u.dir.num_removable > 0) &&
-	 get_removable_partition (parent, dentry->d_name.name,
-				  dentry->d_name.len) )
-    {
-	read_lock (&parent->u.dir.lock);
-	de = _devfs_search_dir (parent, dentry->d_name.name,
-				dentry->d_name.len);
-	read_unlock (&parent->u.dir.lock);
+    if (de) read_unlock (&parent->u.dir.lock);
+    else
+    {   /*  Try re-reading the partition (media may have changed)  */
+	if ( get_removable_partition (parent, dentry->d_name.name,
+				      dentry->d_name.len) )  /*  Unlocks  */
+	{   /*  Media did change  */
+	    read_lock (&parent->u.dir.lock);
+	    de = _devfs_search_dir (parent, dentry->d_name.name,
+				    dentry->d_name.len);
+	    read_unlock (&parent->u.dir.lock);
+	}
     }
     lookup_info.de = de;
     init_waitqueue_head (&lookup_info.wait_queue);
diff -Nru a/fs/devfs/util.c b/fs/devfs/util.c
--- a/fs/devfs/util.c	Sat May 18 00:46:13 2002
+++ b/fs/devfs/util.c	Sat May 18 00:46:13 2002
@@ -56,6 +56,8 @@
                Made major bitfield type and initialiser 64 bit safe.
     20020413   Richard Gooch <rgooch@atnf.csiro.au>
                Fixed shift warning on 64 bit machines.
+    20020428   Richard Gooch <rgooch@atnf.csiro.au>
+               Copied and used macro for error messages from fs/devfs/base.c 
 */
 #include <linux/module.h>
 #include <linux/init.h>
@@ -65,6 +67,9 @@
 
 #include <asm/bitops.h>
 
+#define PRINTK(format, args...) \
+   {printk (KERN_ERR "%s" format, __FUNCTION__ , ## args);}
+
 
 /*  Private functions follow  */
 
@@ -209,9 +214,7 @@
     spin_lock (&list->lock);
     was_set = __test_and_clear_bit (major, list->bits);
     spin_unlock (&list->lock);
-    if (!was_set)
-	printk (KERN_ERR __FUNCTION__ "(): major %d was already free\n",
-		major);
+    if (!was_set) PRINTK ("(): major %d was already free\n", major);
 }   /*  End Function devfs_dealloc_major  */
 EXPORT_SYMBOL(devfs_dealloc_major);
 
@@ -339,12 +342,11 @@
 	if (was_set) list->none_free = 0;
 	up (semaphore);
 	if (!was_set)
-	    printk ( KERN_ERR __FUNCTION__ "(): device %s was already free\n",
-		     kdevname (devnum) );
+	    PRINTK ( "(): device %s was already free\n", kdevname (devnum) );
 	return;
     }
     up (semaphore);
-    printk ( KERN_ERR __FUNCTION__ "(): major for %s not previously allocated\n",
+    PRINTK ( "(): major for %s not previously allocated\n",
 	     kdevname (devnum) );
 }   /*  End Function devfs_dealloc_devnum  */
 EXPORT_SYMBOL(devfs_dealloc_devnum);
@@ -419,8 +421,6 @@
     was_set = __test_and_clear_bit (number, space->bits);
     if (was_set) ++space->num_free;
     up (&space->semaphore);
-    if (!was_set)
-	printk (KERN_ERR __FUNCTION__ "(): number %d was already free\n",
-		number);
+    if (!was_set) PRINTK ("(): number %d was already free\n", number);
 }   /*  End Function devfs_dealloc_unique_number  */
 EXPORT_SYMBOL(devfs_dealloc_unique_number);
diff -Nru a/fs/fat/dir.c b/fs/fat/dir.c
--- a/fs/fat/dir.c	Sat May 18 00:46:08 2002
+++ b/fs/fat/dir.c	Sat May 18 00:46:08 2002
@@ -271,13 +271,10 @@
 				long_slots = 0;
 		}
 
-		for (i = 0; i < 8; i++) {
-			/* see namei.c, msdos_format_name */
-			if (de->name[i] == 0x05)
-				work[i] = 0xE5;
-			else
-				work[i] = de->name[i];
-		}
+		memcpy(work, de->name, sizeof(de->name));
+		/* see namei.c, msdos_format_name */
+		if (work[0] == 0x05)
+			work[0] = 0xE5;
 		for (i = 0, j = 0, last_u = 0; i < 8;) {
 			if (!work[i]) break;
 			chl = fat_shortname2uni(nls_disk, &work[i], 8 - i,
@@ -478,13 +475,10 @@
 		dotoffset = 1;
 	}
 
-	for (i = 0; i < 8; i++) {
-		/* see namei.c, msdos_format_name */
-		if (de->name[i] == 0x05)
-			work[i] = 0xE5;
-		else
-			work[i] = de->name[i];
-	}
+	memcpy(work, de->name, sizeof(de->name));
+	/* see namei.c, msdos_format_name */
+	if (work[0] == 0x05)
+		work[0] = 0xE5;
 	for (i = 0, j = 0, last = 0, last_u = 0; i < 8;) {
 		if (!(c = work[i])) break;
 		chl = fat_shortname2uni(nls_disk, &work[i], 8 - i,
diff -Nru a/fs/isofs/inode.c b/fs/isofs/inode.c
--- a/fs/isofs/inode.c	Sat May 18 00:46:15 2002
+++ b/fs/isofs/inode.c	Sat May 18 00:46:15 2002
@@ -131,6 +131,21 @@
 	statfs:		isofs_statfs,
 };
 
+/* the export_operations structure for describing
+ * how to export (e.g. via kNFSd) is deliberately
+ * empty.
+ * This means that the filesystem want to use iget
+ * to map an inode number into an inode.
+ * The lack of a get_parent operation means that 
+ * if something isn't in the cache, then you cannot
+ * access it.
+ * It should be possible to write a get_parent,
+ * but it would be a bit hairy...
+ */
+static struct export_operations isofs_export_ops = {
+};
+
+
 static struct dentry_operations isofs_dentry_ops[] = {
 	{
 		d_hash:		isofs_hash,
@@ -786,6 +801,7 @@
 	}
 #endif
 	s->s_op = &isofs_sops;
+	s->s_export_op = &isofs_export_ops;
 	sbi->s_mapping = opt.map;
 	sbi->s_rock = (opt.rock == 'y' ? 2 : 0);
 	sbi->s_rock_offset = -1; /* initial offset, will guess until SP is found*/
diff -Nru a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c
--- a/fs/jfs/jfs_txnmgr.c	Sat May 18 00:46:08 2002
+++ b/fs/jfs/jfs_txnmgr.c	Sat May 18 00:46:08 2002
@@ -1155,19 +1155,30 @@
 		jfs_ip = JFS_IP(ip);
 
 		/*
-		 * BUGBUG - Should we call filemap_fdatawrite here instead
-		 * of fsync_inode_data?
-		 * If we do, we have a deadlock condition since we may end
-		 * up recursively calling jfs_get_block with the IWRITELOCK
-		 * held.  We may be able to do away with IWRITELOCK while
-		 * committing transactions and use i_sem instead.
+		 * BUGBUG - This code has temporarily been removed.  The
+		 * intent is to ensure that any file data is written before
+		 * the metadata is committed to the journal.  This prevents
+		 * uninitialized data from appearing in a file after the
+		 * journal has been replayed.  (The uninitialized data
+		 * could be sensitive data removed by another user.)
+		 *
+		 * The problem now is that we are holding the IWRITELOCK
+		 * on the inode, and calling filemap_fdatawrite on an
+		 * unmapped page will cause a deadlock in jfs_get_block.
+		 *
+		 * The long term solution is to pare down the use of
+		 * IWRITELOCK.  We are currently holding it too long.
+		 * We could also be smarter about which data pages need
+		 * to be written before the transaction is committed and
+		 * when we don't need to worry about it at all.
+		 *
+		 * if ((!S_ISDIR(ip->i_mode))
+		 *    && (tblk->flag & COMMIT_DELETE) == 0) {
+		 *	filemap_fdatawait(ip->i_mapping);
+		 *	filemap_fdatawrite(ip->i_mapping);
+		 *	filemap_fdatawait(ip->i_mapping);
+		 * }
 		 */
-		if ((!S_ISDIR(ip->i_mode))
-		    && (tblk->flag & COMMIT_DELETE) == 0) {
-			filemap_fdatawait(ip->i_mapping);
-			filemap_fdatawrite(ip->i_mapping);
-			filemap_fdatawait(ip->i_mapping);
-		}
 
 		/*
 		 * Mark inode as not dirty.  It will still be on the dirty
diff -Nru a/fs/nfsd/export.c b/fs/nfsd/export.c
--- a/fs/nfsd/export.c	Sat May 18 00:46:14 2002
+++ b/fs/nfsd/export.c	Sat May 18 00:46:14 2002
@@ -313,15 +313,12 @@
 	 *       either a device number (so FS_REQUIRES_DEV needed)
 	 *       or an FSID number (so NFSEXP_FSID needed).
 	 * 2:  We must be able to find an inode from a filehandle.
-	 *       either using fh_to_dentry (prefered)
-	 *       or using read_inode (the hack).
+	 *       This means that s_export_op must be set.
 	 */
 	if (((inode->i_sb->s_type->fs_flags & FS_REQUIRES_DEV)
-	      || (nxp->ex_flags & NFSEXP_FSID))
+	     || (nxp->ex_flags & NFSEXP_FSID))
 	    &&
-	    (inode->i_sb->s_op->read_inode
-	     || inode->i_sb->s_export_op
-	     || inode->i_sb->s_op->fh_to_dentry)) 
+	    inode->i_sb->s_export_op)
 		/* Ok, we can export it */;
 	else {
 		dprintk("exp_export: export of invalid fs type.\n");
diff -Nru a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
--- a/fs/nfsd/nfsfh.c	Sat May 18 00:46:09 2002
+++ b/fs/nfsd/nfsfh.c	Sat May 18 00:46:09 2002
@@ -34,474 +34,6 @@
 
 #define	CALL(ops,fun) ((ops->fun)?(ops->fun):export_op_default.fun)
 
-
-
-struct nfsd_getdents_callback {
-	char *name;		/* name that was found. It already points to a buffer NAME_MAX+1 is size */
-	unsigned long ino;	/* the inum we are looking for */
-	int found;		/* inode matched? */
-	int sequence;		/* sequence counter */
-};
-
-/*
- * A rather strange filldir function to capture
- * the name matching the specified inode number.
- */
-static int filldir_one(void * __buf, const char * name, int len,
-			loff_t pos, ino_t ino, unsigned int d_type)
-{
-	struct nfsd_getdents_callback *buf = __buf;
-	int result = 0;
-
-	buf->sequence++;
-#ifdef NFSD_DEBUG_VERBOSE
-dprintk("filldir_one: seq=%d, ino=%ld, name=%s\n", buf->sequence, ino, name);
-#endif
-	if (buf->ino == ino) {
-		memcpy(buf->name, name, len);
-		buf->name[len] = '\0';
-		buf->found = 1;
-		result = -1;
-	}
-	return result;
-}
-
-static int nfsd_get_name(struct dentry *dentry, char *name,
-			struct dentry *child)
-{
-	struct inode *dir = dentry->d_inode;
-	int error;
-	struct file file;
-	struct nfsd_getdents_callback buffer;
-
-	error = -ENOTDIR;
-	if (!dir || !S_ISDIR(dir->i_mode))
-		goto out;
-	error = -EINVAL;
-	if (!dir->i_fop)
-		goto out;
-	/*
-	 * Open the directory ...
-	 */
-	error = init_private_file(&file, dentry, FMODE_READ);
-	if (error)
-		goto out;
-	error = -EINVAL;
-	if (!file.f_op->readdir)
-		goto out_close;
-
-	buffer.name = name;
-	buffer.ino = child->d_inode->i_ino;
-	buffer.found = 0;
-	buffer.sequence = 0;
-	while (1) {
-		int old_seq = buffer.sequence;
-
-		error = vfs_readdir(&file, filldir_one, &buffer);
-
-		if (error < 0)
-			break;
-
-		error = 0;
-		if (buffer.found)
-			break;
-		error = -ENOENT;
-		if (old_seq == buffer.sequence)
-			break;
-	}
-
-out_close:
-	if (file.f_op->release)
-		file.f_op->release(dir, &file);
-out:
-	return error;
-}
-
-static struct dentry *nfsd_iget(struct super_block *sb, unsigned long ino, __u32 generation)
-{
-
-	/* iget isn't really right if the inode is currently unallocated!!
-	 * This should really all be done inside each filesystem
-	 *
-	 * ext2fs' read_inode has been strengthed to return a bad_inode if the inode
-	 *   had been deleted.
-	 *
-	 * Currently we don't know the generation for parent directory, so a generation
-	 * of 0 means "accept any"
-	 */
-	struct inode *inode;
-	struct dentry *result;
-	if (ino == 0)
-		return ERR_PTR(-ESTALE);
-	inode = iget(sb, ino);
-	if (inode == NULL)
-		return ERR_PTR(-ENOMEM);
-	if (is_bad_inode(inode)
-	    || (generation && inode->i_generation != generation)
-		) {
-		/* we didn't find the right inode.. */
-		dprintk("fh_verify: Inode %lu, Bad count: %d %d or version  %u %u\n",
-			inode->i_ino,
-			inode->i_nlink, atomic_read(&inode->i_count),
-			inode->i_generation,
-			generation);
-
-		iput(inode);
-		return ERR_PTR(-ESTALE);
-	}
-	/* now to find a dentry.
-	 * If possible, get a well-connected one
-	 */
-	result = d_alloc_anon(inode);
-	if (!result) {
-		iput(inode);
-		return ERR_PTR(-ENOMEM);
-	}
-	result->d_vfs_flags |= DCACHE_REFERENCED;
-	return result;
-}
-
-static struct dentry *nfsd_get_dentry(struct super_block *sb, __u32 *fh,
-					     int len, int fhtype, int parent)
-{
-	if (sb->s_op->fh_to_dentry)
-		return sb->s_op->fh_to_dentry(sb, fh, len, fhtype, parent);
-	switch (fhtype) {
-	case 1:
-		if (len < 2)
-			break;
-		if (parent)
-			break;
-		return nfsd_iget(sb, fh[0], fh[1]);
-
-	case 2:
-		if (len < 3)
-			break;
-		if (parent)
-			return nfsd_iget(sb,fh[2],0);
-		return nfsd_iget(sb,fh[0],fh[1]);
-	default: break;
-	}
-	return ERR_PTR(-EINVAL);
-}
-
-
-/* this routine links an IS_ROOT dentry into the dcache tree.  It gains "parent"
- * as a parent and "name" as a name
- * It should possibly go in dcache.c
- */
-int d_splice(struct dentry *target, struct dentry *parent, struct qstr *name)
-{
-	struct dentry *tdentry;
-#ifdef NFSD_PARANOIA
-	if (!IS_ROOT(target))
-		printk("nfsd: d_splice with no-root target: %s/%s\n", parent->d_name.name, name->name);
-	if (!(target->d_flags & DCACHE_DISCONNECTED))
-		printk("nfsd: d_splice with non-DISCONNECTED target: %s/%s\n", parent->d_name.name, name->name);
-#endif
-	tdentry = d_alloc(parent, name);
-	if (tdentry == NULL)
-		return -ENOMEM;
-	d_move(target, tdentry);
-
-	d_rehash(target);
-	dput(tdentry);
-
-	/* if parent is properly connected, then we can assert that
-	 * the children are connected, but it must be a singluar (non-forking)
-	 * branch
-	 */
-	if (!(parent->d_flags & DCACHE_DISCONNECTED)) {
-		while (target) {
-			target->d_flags &= ~DCACHE_DISCONNECTED;
-			parent = target;
-			spin_lock(&dcache_lock);
-			if (list_empty(&parent->d_subdirs))
-				target = NULL;
-			else {
-				target = list_entry(parent->d_subdirs.next, struct dentry, d_child);
-#ifdef NFSD_PARANOIA
-				/* must be only child */
-				if (target->d_child.next != &parent->d_subdirs
-				    || target->d_child.prev != &parent->d_subdirs) {
-					printk("nfsd: d_splice found non-singular disconnected branch: %s/%s\n",
-					       parent->d_name.name, target->d_name.name);
-					spin_unlock(&dcache_lock);
-					return 0;
-				}
-#endif
-			}
-			spin_unlock(&dcache_lock);
-		}
-	}
-	return 0;
-}
-
-/* this routine finds the dentry of the parent of a given directory
- * it assumes lookup("..") works.
- */
-struct dentry *nfsd_findparent(struct dentry *child)
-{
-	struct dentry *tdentry, *pdentry;
-	tdentry = d_alloc(child, &(const struct qstr) {"..", 2, 0});
-	if (!tdentry)
-		return ERR_PTR(-ENOMEM);
-
-	/* I'm going to assume that if the returned dentry is different, then
-	 * it is well connected.  But nobody returns different dentrys do they?
-	 */
-	down(&child->d_inode->i_sem);
-	pdentry = child->d_inode->i_op->lookup(child->d_inode, tdentry);
-	up(&child->d_inode->i_sem);
-	d_drop(tdentry); /* we never want ".." hashed */
-	if (!pdentry && tdentry->d_inode == NULL) {
-		/* File system cannot find ".." ... sad but possible */
-		pdentry = ERR_PTR(-EINVAL);
-	}
-	if (!pdentry) {
-		/* I don't want to return a ".." dentry.
-		 * I would prefer to return an unconnected "IS_ROOT" dentry,
-		 * though a properly connected dentry is even better
-		 */
-		/* if first or last of alias list is not tdentry, use that
-		 * else make a root dentry
-		 */
-		struct list_head *aliases = &tdentry->d_inode->i_dentry;
-		spin_lock(&dcache_lock);
-		if (aliases->next != aliases) {
-			pdentry = list_entry(aliases->next, struct dentry, d_alias);
-			if (pdentry == tdentry)
-				pdentry = list_entry(aliases->prev, struct dentry, d_alias);
-			if (pdentry == tdentry)
-				pdentry = NULL;
-			if (pdentry) dget_locked(pdentry);
-		}
-		spin_unlock(&dcache_lock);
-		if (pdentry == NULL) {
-			pdentry = d_alloc_root(tdentry->d_inode);
-			if (pdentry) {
-				igrab(tdentry->d_inode);
-				pdentry->d_flags |= DCACHE_DISCONNECTED;
-			}
-		}
-		if (pdentry == NULL)
-			pdentry = ERR_PTR(-ENOMEM);
-	}
-	dput(tdentry); /* it is not hashed, it will be discarded */
-	return pdentry;
-}
-
-static struct dentry *splice(struct dentry *child, struct dentry *parent)
-{
-	int err = 0, nerr;
-	struct qstr qs;
-	char namebuf[256];
-	struct list_head *lp;
-	/* child is an IS_ROOT (anonymous) dentry, but it is hypothesised that
-	 * it should be a child of parent.
-	 * We see if we can find a name and, if we can - splice it in.
-	 * We lookup the name before locking (i_sem) the directory as namelookup
-	 * also claims i_sem.  If the name gets changed then we will loop around
-	 * and try again in find_fh_dentry.
-	 */
-
-	nerr = nfsd_get_name(parent, namebuf, child);
-
-	/*
-	 * We now claim the parent i_sem so that no-one else tries to create
-	 * a dentry in the parent while we are.
-	 */
-	
-	down(&parent->d_inode->i_sem);
-
-	/* Now, things might have changed while we waited.
-	 * Possibly a friendly filesystem found child and spliced it in in response
-	 * to a lookup (though nobody does this yet).  In this case, just succeed.
-	 */
-	if (child->d_parent == parent) goto out;
-	
-	/* Possibly a new dentry has been made for this child->d_inode in
-	 * parent by a lookup.  In this case return that dentry. Caller must
-	 * notice and act accordingly
-	 */
-	spin_lock(&dcache_lock);
-	list_for_each(lp, &child->d_inode->i_dentry) {
-		struct dentry *tmp = list_entry(lp,struct dentry, d_alias);
-		if (!list_empty(&tmp->d_hash) &&
-		    tmp->d_parent == parent) {
-			child = dget_locked(tmp);
-			spin_unlock(&dcache_lock);
-			goto out;
-		}
-	}
-	spin_unlock(&dcache_lock);
-
-	/* now we need that name.  If there was an error getting it, now is th
-	 * time to bail out.
-	 */
-	if ((err = nerr))
-		goto out;
-	qs.name = namebuf;
-	qs.len = strlen(namebuf);
-	if (find_inode_number(parent, &qs) != 0) {
-		/* Now that IS odd.  I wonder what it means... */
-		err = -EEXIST;
-		printk("nfsd-fh: found a name that I didn't expect: %s/%s\n", parent->d_name.name, qs.name);
-		goto out;
-	}
-	err = d_splice(child, parent, &qs);
-	dprintk("nfsd_fh: found name %s for ino %ld\n", child->d_name.name, child->d_inode->i_ino);
- out:
-	up(&parent->d_inode->i_sem);
-	if (err)
-		return ERR_PTR(err);
-	else
-		return child;
-}
-
-/*
- * This is the basic lookup mechanism for turning an NFS file handle
- * into a dentry.
- * We use nfsd_iget and if that doesn't return a suitably connected dentry,
- * we try to find the parent, and the parent of that and so-on until a
- * connection if made.
- */
-static struct dentry *
-find_fh_dentry(struct super_block *sb, __u32 *datap, int len, int fhtype, int needpath)
-{
-	struct dentry *dentry, *result = NULL;
-	struct dentry *tmp;
-	int err = -ESTALE;
-	/* the sb->s_nfsd_free_path_sem semaphore is needed to make sure that only one unconnected (free)
-	 * dcache path ever exists, as otherwise two partial paths might get
-	 * joined together, which would be very confusing.
-	 * If there is ever an unconnected non-root directory, then this lock
-	 * must be held.
-	 */
-
-
-	nfsdstats.fh_lookup++;
-	/*
-	 * Attempt to find the inode.
-	 */
- retry:
-	down(&sb->s_nfsd_free_path_sem);
-	result = nfsd_get_dentry(sb, datap, len, fhtype, 0);
-	if (IS_ERR(result)
-	    || !(result->d_flags & DCACHE_DISCONNECTED)
-	    || (!S_ISDIR(result->d_inode->i_mode) && ! needpath)) {
-		up(&sb->s_nfsd_free_path_sem);
-	    
-		err = PTR_ERR(result);
-		if (IS_ERR(result))
-			goto err_out;
-		if ((result->d_flags & DCACHE_DISCONNECTED))
-			nfsdstats.fh_anon++;
-		return result;
-	}
-
-	/* It's a directory, or we are required to confirm the file's
-	 * location in the tree.
-	 */
-	dprintk("nfs_fh: need to look harder for %s/%d\n", sb->s_id, datap[0]);
-
-	lock_kernel();
-	if (!S_ISDIR(result->d_inode->i_mode)) {
-		nfsdstats.fh_nocache_nondir++;
-			/* need to iget dirino and make sure this inode is in that directory */
-			dentry = nfsd_get_dentry(sb, datap, len, fhtype, 1);
-			err = PTR_ERR(dentry);
-			if (IS_ERR(dentry))
-				goto err_result;
-			err = -ESTALE;
-			if (!dentry->d_inode
-			    || !S_ISDIR(dentry->d_inode->i_mode)) {
-				goto err_dentry;
-			}
-			tmp = splice(result, dentry);
-			err = PTR_ERR(tmp);
-			if (IS_ERR(tmp))
-				goto err_dentry;
-			if (tmp != result) {
-				/* it is safe to just use tmp instead, but we must discard result first */
-				d_drop(result);
-				dput(result);
-				result = tmp;
-			}
-	} else {
-		nfsdstats.fh_nocache_dir++;
-		dentry = dget(result);
-	}
-
-	while(dentry->d_flags & DCACHE_DISCONNECTED) {
-		/* LOOP INVARIANT */
-		/* haven't found a place in the tree yet, but we do have a free path
-		 * from dentry down to result, and dentry is a directory.
-		 * Have a hold on dentry and result */
-		struct dentry *pdentry;
-		struct inode *parent;
-
-		pdentry = nfsd_findparent(dentry);
-		err = PTR_ERR(pdentry);
-		if (IS_ERR(pdentry))
-			goto err_dentry;
-		parent = pdentry->d_inode;
-		err = -EACCES;
-		if (!parent) {
-			dput(pdentry);
-			goto err_dentry;
-		}
-
-		tmp = splice(dentry, pdentry);
-		if (tmp != dentry) {
-			/* Something wrong.  We need to drop the whole dentry->result path
-			 * whatever it was
-			 */
-			/*
-			 *  FIXME: the loop below will do Bad Things(tm) if
-			 *  dentry (or one of its ancestors) become attached
-			 *  to the tree (e.g. due to VFAT-style alias handling)
-			 */
-			struct dentry *d;
-			for (d=result ; d ; d=(d->d_parent == d)?NULL:d->d_parent)
-				d_drop(d);
-		}
-		if (IS_ERR(tmp)) {
-			err = PTR_ERR(tmp);
-			dput(pdentry);
-			goto err_dentry;
-		}
-		if (tmp != dentry) {
-			/* we lost a race,  try again
-			 */
-			dput(pdentry);
-			dput(tmp);
-			dput(dentry);
-			dput(result);	/* this will discard the whole free path, so we can up the semaphore */
-			up(&sb->s_nfsd_free_path_sem);
-			unlock_kernel();
-			goto retry;
-		}
-		dput(dentry);
-		dentry = pdentry;
-	}
-	dput(dentry);
-	up(&sb->s_nfsd_free_path_sem);
-	unlock_kernel();
-	return result;
-
-err_dentry:
-	dput(dentry);
-err_result:
-	dput(result);
-	up(&sb->s_nfsd_free_path_sem);
-	unlock_kernel();
-err_out:
-	if (err == -ESTALE)
-		nfsdstats.fh_stale++;
-	return ERR_PTR(err);
-}
-
 /*
  * our acceptability function.
  * if NOSUBTREECHECK, accept anything
@@ -660,15 +192,10 @@
 			dentry = dget(exp->ex_dentry);
 		else {
 			struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op;
-			if (nop)
 				dentry = CALL(nop,decode_fh)(exp->ex_mnt->mnt_sb,
 							     datap, data_left,
 							     fileid_type,
 							     nfsd_acceptable, exp);
-			else
-				dentry = find_fh_dentry(exp->ex_dentry->d_sb,
-							datap, data_left, fileid_type,
-							!(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
 		}
 		if (dentry == NULL)
 			goto out;
@@ -716,56 +243,9 @@
 		goto out;
 	}
 
-	/*
-	 * Security: Check that the export is valid for dentry <gam3@acm.org>
-	 *  This is only needed with subtree_check, and if export_operations is
-	 *  not being used -  export_operations does the check via  the "acceptable"
-	 *  callback
-	 */
-	error = 0;
-
-	if (exp->ex_mnt->mnt_sb->s_export_op == NULL &&
-	    !(exp->ex_flags & NFSEXP_NOSUBTREECHECK)) {
-		if (exp->ex_dentry != dentry) {
-			struct dentry *tdentry = dentry;
-
-			spin_lock(&dcache_lock);
-			do {
-				tdentry = tdentry->d_parent;
-				if (exp->ex_dentry == tdentry)
-					break;
-				/* executable only by root and we can't be root */
-				/*
-				 * FIXME: permissions check is not that simple
-				 */
-				if (current->fsuid
-				    && (exp->ex_flags & NFSEXP_ROOTSQUASH)
-				    && !(tdentry->d_inode->i_uid
-					 && (tdentry->d_inode->i_mode & S_IXUSR))
-				    && !(tdentry->d_inode->i_gid
-					 && (tdentry->d_inode->i_mode & S_IXGRP))
-				    && !(tdentry->d_inode->i_mode & S_IXOTH)
-					) {
-					error = nfserr_stale;
-					dprintk("fh_verify: no root_squashed access.\n");
-				}
-			} while ((tdentry != tdentry->d_parent));
-			if (exp->ex_dentry != tdentry) {
-				error = nfserr_stale;
-				printk("nfsd Security: %s/%s bad export.\n",
-				       dentry->d_parent->d_name.name,
-				       dentry->d_name.name);
-				spin_unlock(&dcache_lock);
-				goto out;
-			}
-			spin_unlock(&dcache_lock);
-		}
-	}
-
 	/* Finally, check access permissions. */
-	if (!error) {
-		error = nfsd_permission(exp, dentry, access);
-	}
+	error = nfsd_permission(exp, dentry, access);
+
 #ifdef NFSD_PARANOIA_EXTREME
 	if (error) {
 		printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n",
@@ -790,38 +270,14 @@
 		      __u32 *datap, int *maxsize)
 {
 	struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op;
-	struct super_block *sb = dentry->d_sb;
 	
 	if (dentry == exp->ex_dentry) {
 		*maxsize = 0;
 		return 0;
 	}
 
-	if (nop)
-		return CALL(nop,encode_fh)(dentry, datap, maxsize,
+	return CALL(nop,encode_fh)(dentry, datap, maxsize,
 			  !(exp->ex_flags&NFSEXP_NOSUBTREECHECK));
-
-	if (sb->s_op->dentry_to_fh) {
-		int need_parent = !S_ISDIR(dentry->d_inode->i_mode) &&
-			!(exp->ex_flags & NFSEXP_NOSUBTREECHECK);
-		
-		int type = sb->s_op->dentry_to_fh(dentry, datap, maxsize, need_parent);
-		return type;
-	}
-
-	if (*maxsize < 2)
-		return 255;
-	*datap++ = ino_t_to_u32(dentry->d_inode->i_ino);
-	*datap++ = dentry->d_inode->i_generation;
-	if (*maxsize ==2 ||
-	    S_ISDIR(dentry->d_inode->i_mode) ||
-	    (exp->ex_flags & NFSEXP_NOSUBTREECHECK)) {
-		*maxsize = 2;
-		return 1;
-	}
-	*datap++ = ino_t_to_u32(parent_ino(dentry));
-	*maxsize = 3;
-	return 2;
 }
 
 /*
@@ -845,7 +301,6 @@
 	 * of the same version, where possible.
 	 * Currently, that means that if ref_fh->fh_handle.fh_version == 0xca
 	 * Then create a 32byte filehandle using nfs_fhbase_old
-	 * But only do this if dentry_to_fh is not available
 	 *
 	 */
 	
@@ -870,8 +325,7 @@
 	fhp->fh_export = exp;
 
 	if (ref_fh &&
-	    ref_fh->fh_handle.fh_version == 0xca &&
-	    dentry->d_sb->s_op->dentry_to_fh == NULL) {
+	    ref_fh->fh_handle.fh_version == 0xca) {
 		/* old style filehandle please */
 		memset(&fhp->fh_handle.fh_base, 0, NFS_FHSIZE);
 		fhp->fh_handle.fh_size = NFS_FHSIZE;
diff -Nru a/fs/nls/Makefile b/fs/nls/Makefile
--- a/fs/nls/Makefile	Sat May 18 00:46:08 2002
+++ b/fs/nls/Makefile	Sat May 18 00:46:08 2002
@@ -2,6 +2,9 @@
 # Makefile for native language support
 #
 
+O_TARGET = nls.o
+export-objs := nls_base.o
+
 obj-y	:=	nls_base.o
 obj-m	:=
 obj-n	:=
@@ -52,9 +55,5 @@
 obj-$(CONFIG_NLS_KOI8_U)	+= nls_koi8-u.o nls_koi8-ru.o
 obj-$(CONFIG_NLS_ABC)		+= nls_abc.o
 obj-$(CONFIG_NLS_UTF8)		+= nls_utf8.o
-
-export-objs = $(obj-y)
-
-O_TARGET = nls.o
 
 include $(TOPDIR)/Rules.make
diff -Nru a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog	Sat May 18 00:46:08 2002
+++ b/fs/ntfs/ChangeLog	Sat May 18 00:46:08 2002
@@ -24,6 +24,15 @@
 	- Want to use dummy inodes for address space i/o. We need some VFS
 	  changes first, which are currently under discussion.
 
+2.0.7 - Minor cleanups and updates for changes in core kernel code.
+
+	- Remove much of the NULL struct element initializers.
+	- Various updates to make compatible with recent kernels.
+	- Remove defines of MAX_BUF_PER_PAGE and include linux/buffer_head.h
+	  in fs/ntfs/ntfs.h instead.
+	- Remove no longer needed KERNEL_VERSION checks. We are now in the
+	  kernel proper so they are no longer needed.
+
 2.0.6 - Major bugfix to make compatible with other kernel changes.
 
 	- Initialize the mftbmp address space properly now that there are more
diff -Nru a/fs/ntfs/Makefile b/fs/ntfs/Makefile
--- a/fs/ntfs/Makefile	Sat May 18 00:46:08 2002
+++ b/fs/ntfs/Makefile	Sat May 18 00:46:08 2002
@@ -7,7 +7,7 @@
 
 obj-m   := $(O_TARGET)
 
-EXTRA_CFLAGS = -DNTFS_VERSION=\"2.0.6\"
+EXTRA_CFLAGS = -DNTFS_VERSION=\"2.0.7\"
 
 ifeq ($(CONFIG_NTFS_DEBUG),y)
 EXTRA_CFLAGS += -DDEBUG
diff -Nru a/fs/ntfs/aops.c b/fs/ntfs/aops.c
--- a/fs/ntfs/aops.c	Sat May 18 00:46:14 2002
+++ b/fs/ntfs/aops.c	Sat May 18 00:46:14 2002
@@ -29,12 +29,6 @@
 
 #include "ntfs.h"
 
-#define MAX_BUF_PER_PAGE (PAGE_CACHE_SIZE / 512)
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,8)
-#define page_buffers(page)	(page)->buffers
-#endif
-
 /**
  * end_buffer_read_file_async -
  *
diff -Nru a/fs/ntfs/attraops.c b/fs/ntfs/attraops.c
--- a/fs/ntfs/attraops.c	Sat May 18 00:46:14 2002
+++ b/fs/ntfs/attraops.c	Sat May 18 00:46:14 2002
@@ -43,6 +43,5 @@
 						   disk request queue. */
 	prepare_write:	NULL,			/* . */
 	commit_write:	NULL,			/* . */
-	//truncatepage:	NULL,			/* . */
 };
 
diff -Nru a/fs/ntfs/file.c b/fs/ntfs/file.c
--- a/fs/ntfs/file.c	Sat May 18 00:46:08 2002
+++ b/fs/ntfs/file.c	Sat May 18 00:46:08 2002
@@ -24,127 +24,13 @@
 struct file_operations ntfs_file_ops = {
 	llseek:			generic_file_llseek,	/* Seek inside file. */
 	read:			generic_file_read,	/* Read from file. */
-	write:			NULL,			/* . */
-	readdir:		NULL,			/* . */
-	poll:			NULL,			/* . */
-	ioctl:			NULL,			/* . */
 	mmap:			generic_file_mmap,	/* Mmap file. */
 	open:			generic_file_open,	/* Open file. */
-	flush:			NULL,			/* . */
-	release:		NULL,			/* . */
-	fsync:			NULL,			/* . */
-	fasync:			NULL,			/* . */
-	lock:			NULL,			/* . */
-	readv:			NULL,			/* . */
-	writev:			NULL,			/* . */
-	sendpage:		NULL,			/* . */
-	get_unmapped_area:	NULL,			/* . */
 };
 
-struct inode_operations ntfs_file_inode_ops = {
-	create:		NULL,		/* . */
-	lookup:		NULL,		/* . */
-	link:		NULL,		/* . */
-	unlink:		NULL,		/* . */
-	symlink:	NULL,		/* . */
-	mkdir:		NULL,		/* . */
-	rmdir:		NULL,		/* . */
-	mknod:		NULL,		/* . */
-	rename:		NULL,		/* . */
-	readlink:	NULL,		/* . */
-	follow_link:	NULL,		/* . */
-	truncate:	NULL,		/* . */
-	permission:	NULL,		/* . */
-	revalidate:	NULL,		/* . */
-	setattr:	NULL,		/* . */
-	getattr:	NULL,		/* . */
-};
-
-#if 0
-/* NOTE: read, write, poll, fsync, readv, writev can be called without the big
- * kernel lock held in all filesystems. */
-struct file_operations {
-	struct module *owner;
-	loff_t (*llseek) (struct file *, loff_t, int);
-	ssize_t (*read) (struct file *, char *, size_t, loff_t *);
-	ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
-	int (*readdir) (struct file *, void *, filldir_t);
-	unsigned int (*poll) (struct file *, struct poll_table_struct *);
-	int (*ioctl) (struct inode *, struct file *, unsigned int,
-			unsigned long);
-	int (*mmap) (struct file *, struct vm_area_struct *);
-	int (*flush) (struct file *);
-	int (*release) (struct inode *, struct file *);
-	int (*fsync) (struct file *, struct dentry *, int datasync);
-	int (*fasync) (int, struct file *, int);
-	int (*lock) (struct file *, int, struct file_lock *);
-	ssize_t (*readv) (struct file *, const struct iovec *, unsigned long,
-			loff_t *);
-	ssize_t (*writev) (struct file *, const struct iovec *, unsigned long,
-			loff_t *);
-	ssize_t (*sendpage) (struct file *, struct page *, int, size_t,
-			loff_t *, int);
-	unsigned long (*get_unmapped_area)(struct file *, unsigned long,
-			unsigned long, unsigned long, unsigned long);
-};
+struct inode_operations ntfs_file_inode_ops = {};
 
-struct inode_operations {
-	int (*create) (struct inode *,struct dentry *,int);
-	struct dentry * (*lookup) (struct inode *,struct dentry *);
-	int (*link) (struct dentry *,struct inode *,struct dentry *);
-	int (*unlink) (struct inode *,struct dentry *);
-	int (*symlink) (struct inode *,struct dentry *,const char *);
-	int (*mkdir) (struct inode *,struct dentry *,int);
-	int (*rmdir) (struct inode *,struct dentry *);
-	int (*mknod) (struct inode *,struct dentry *,int,int);
-	int (*rename) (struct inode *, struct dentry *,
-			struct inode *, struct dentry *);
-	int (*readlink) (struct dentry *, char *,int);
-	int (*follow_link) (struct dentry *, struct nameidata *);
-	void (*truncate) (struct inode *);
-	int (*permission) (struct inode *, int);
-	int (*revalidate) (struct dentry *);
-	int (*setattr) (struct dentry *, struct iattr *);
-	int (*getattr) (struct dentry *, struct iattr *);
-};
-#endif
+struct file_operations ntfs_empty_file_ops = {};
 
-struct file_operations ntfs_empty_file_ops = {
-	llseek:			NULL,			/* . */
-	read:			NULL,			/* . */
-	write:			NULL,			/* . */
-	readdir:		NULL,			/* . */
-	poll:			NULL,			/* . */
-	ioctl:			NULL,			/* . */
-	mmap:			NULL,			/* . */
-	open:			NULL,			/* . */
-	flush:			NULL,			/* . */
-	release:		NULL,			/* . */
-	fsync:			NULL,			/* . */
-	fasync:			NULL,			/* . */
-	lock:			NULL,			/* . */
-	readv:			NULL,			/* . */
-	writev:			NULL,			/* . */
-	sendpage:		NULL,			/* . */
-	get_unmapped_area:	NULL,			/* . */
-};
-
-struct inode_operations ntfs_empty_inode_ops = {
-	create:		NULL,		/* . */
-	lookup:		NULL,		/* . */
-	link:		NULL,		/* . */
-	unlink:		NULL,		/* . */
-	symlink:	NULL,		/* . */
-	mkdir:		NULL,		/* . */
-	rmdir:		NULL,		/* . */
-	mknod:		NULL,		/* . */
-	rename:		NULL,		/* . */
-	readlink:	NULL,		/* . */
-	follow_link:	NULL,		/* . */
-	truncate:	NULL,		/* . */
-	permission:	NULL,		/* . */
-	revalidate:	NULL,		/* . */
-	setattr:	NULL,		/* . */
-	getattr:	NULL,		/* . */
-};
+struct inode_operations ntfs_empty_inode_ops = {};
 
diff -Nru a/fs/ntfs/mft.c b/fs/ntfs/mft.c
--- a/fs/ntfs/mft.c	Sat May 18 00:46:08 2002
+++ b/fs/ntfs/mft.c	Sat May 18 00:46:08 2002
@@ -25,8 +25,6 @@
 
 #include "ntfs.h"
 
-#define MAX_BUF_PER_PAGE (PAGE_CACHE_SIZE / 512)
-
 /**
  * __format_mft_record - initialize an empty mft record
  * @m:		mapped, pinned and locked for writing mft record
@@ -114,13 +112,6 @@
 						   disk request queue. */
 	prepare_write:	NULL,			/* . */
 	commit_write:	NULL,			/* . */
-	bmap:		NULL,			/* Needed for FIBMAP.
-						   Don't use it. */
-	flushpage:	NULL,			/* . */
-	releasepage:	NULL,			/* . */
-#ifdef KERNEL_HAS_O_DIRECT
-	direct_IO:	NULL,			/* . */
-#endif
 };
 
 /**
diff -Nru a/fs/ntfs/namei.c b/fs/ntfs/namei.c
--- a/fs/ntfs/namei.c	Sat May 18 00:46:14 2002
+++ b/fs/ntfs/namei.c	Sat May 18 00:46:14 2002
@@ -103,43 +103,6 @@
 }
 
 struct inode_operations ntfs_dir_inode_ops = {
-	create:		NULL,		/* . */
 	lookup:		ntfs_lookup,	/* lookup directory. */
-	link:		NULL,		/* . */
-	unlink:		NULL,		/* . */
-	symlink:	NULL,		/* . */
-	mkdir:		NULL,		/* . */
-	rmdir:		NULL,		/* . */
-	mknod:		NULL,		/* . */
-	rename:		NULL,		/* . */
-	readlink:	NULL,		/* . */
-	follow_link:	NULL,		/* . */
-	truncate:	NULL,		/* . */
-	permission:	NULL,		/* . */
-	revalidate:	NULL,		/* . */
-	setattr:	NULL,		/* . */
-	getattr:	NULL,		/* . */
 };
-
-#if 0
-struct inode_operations {
-	int (*create) (struct inode *,struct dentry *,int);
-	struct dentry * (*lookup) (struct inode *,struct dentry *);
-	int (*link) (struct dentry *,struct inode *,struct dentry *);
-	int (*unlink) (struct inode *,struct dentry *);
-	int (*symlink) (struct inode *,struct dentry *,const char *);
-	int (*mkdir) (struct inode *,struct dentry *,int);
-	int (*rmdir) (struct inode *,struct dentry *);
-	int (*mknod) (struct inode *,struct dentry *,int,int);
-	int (*rename) (struct inode *, struct dentry *,
-			struct inode *, struct dentry *);
-	int (*readlink) (struct dentry *, char *,int);
-	int (*follow_link) (struct dentry *, struct nameidata *);
-	void (*truncate) (struct inode *);
-	int (*permission) (struct inode *, int);
-	int (*revalidate) (struct dentry *);
-	int (*setattr) (struct dentry *, struct iattr *);
-	int (*getattr) (struct dentry *, struct iattr *);
-};
-#endif
 
diff -Nru a/fs/ntfs/ntfs.h b/fs/ntfs/ntfs.h
--- a/fs/ntfs/ntfs.h	Sat May 18 00:46:09 2002
+++ b/fs/ntfs/ntfs.h	Sat May 18 00:46:09 2002
@@ -26,14 +26,11 @@
 
 #include <linux/version.h>
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5)
-#	error The NTFS driver requires at least kernel 2.5.5.
-#endif
-
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/compiler.h>
 #include <linux/fs.h>
+#include <linux/buffer_head.h>
 #include <linux/nls.h>
 #include <linux/pagemap.h>
 #include <linux/smp.h>
@@ -48,10 +45,6 @@
 #include "layout.h"
 #include "attrib.h"
 #include "mft.h"
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-typedef long sector_t;
-#endif
 
 typedef enum {
 	NTFS_BLOCK_SIZE		= 512,
diff -Nru a/fs/ntfs/super.c b/fs/ntfs/super.c
--- a/fs/ntfs/super.c	Sat May 18 00:46:08 2002
+++ b/fs/ntfs/super.c	Sat May 18 00:46:08 2002
@@ -1424,31 +1424,23 @@
 						   called from iget(). */
 	dirty_inode:	ntfs_dirty_inode,	/* VFS: Called from
 						   __mark_inode_dirty(). */
-	write_inode:	NULL,		/* VFS: Write dirty inode to disk. */
-	put_inode:	NULL,		/* VFS: Called whenever the reference
-					   count (i_count) of the inode is
-					   going to be decreased but before the
-					   actual decrease. */
-	delete_inode:	NULL,		/* VFS: Delete inode from disk. Called
-					   when i_count becomes 0 and i_nlink is
-					   also 0. */
+	//write_inode:	NULL,		/* VFS: Write dirty inode to disk. */
+	//put_inode:	NULL,		/* VFS: Called whenever the reference
+	//				   count (i_count) of the inode is
+	//				   going to be decreased but before the
+	//				   actual decrease. */
+	//delete_inode:	NULL,		/* VFS: Delete inode from disk. Called
+	//				   when i_count becomes 0 and i_nlink is
+	//				   also 0. */
 	put_super:	ntfs_put_super,	/* Syscall: umount. */
-	write_super:	NULL,		/* Flush dirty super block to disk. */
-	write_super_lockfs:	NULL,	/* ? */
-	unlockfs:	NULL,		/* ? */
+	//write_super:	NULL,		/* Flush dirty super block to disk. */
+	//write_super_lockfs:	NULL,	/* ? */
+	//unlockfs:	NULL,		/* ? */
 	statfs:		ntfs_statfs,	/* Syscall: statfs */
 	remount_fs:	ntfs_remount,	/* Syscall: mount -o remount. */
 	clear_inode:	ntfs_clear_big_inode,	/* VFS: Called when an inode is
 						   removed from memory. */
-	umount_begin:	NULL,		/* Forced umount. */
-	/*
-	 * These are NFSd support functions but NTFS is a standard fs so
-	 * shouldn't need to implement these manually. At least we can try
-	 * without and if it doesn't work in some way we can always implement
-	 * something here.
-	 */
-	fh_to_dentry:	NULL,		/* Get dentry for given file handle. */
-	dentry_to_fh:	NULL,		/* Get file handle for given dentry. */
+	//umount_begin:	NULL,		/* Forced umount. */
 	show_options:	ntfs_show_options, /* Show mount options in proc. */
 };
 
diff -Nru a/fs/super.c b/fs/super.c
--- a/fs/super.c	Sat May 18 00:46:09 2002
+++ b/fs/super.c	Sat May 18 00:46:09 2002
@@ -58,7 +58,6 @@
 		s->s_count = S_BIAS;
 		atomic_set(&s->s_active, 1);
 		sema_init(&s->s_vfs_rename_sem,1);
-		sema_init(&s->s_nfsd_free_path_sem,1);
 		sema_init(&s->s_dquot.dqio_sem, 1);
 		sema_init(&s->s_dquot.dqoff_sem, 1);
 		s->s_maxbytes = MAX_NON_LFS;
diff -Nru a/include/asm-alpha/ide.h b/include/asm-alpha/ide.h
--- a/include/asm-alpha/ide.h	Sat May 18 00:46:08 2002
+++ b/include/asm-alpha/ide.h	Sat May 18 00:46:08 2002
@@ -82,10 +82,6 @@
 #endif
 }
 
-#define ide_ack_intr(hwif)		(1)
-#define ide_release_lock(lock)		do {} while (0)
-#define ide_get_lock(lock, hdlr, data)	do {} while (0)
-
 #endif /* __KERNEL__ */
 
 #endif /* __ASMalpha_IDE_H */
diff -Nru a/include/asm-arm/arch-clps711x/memory.h b/include/asm-arm/arch-clps711x/memory.h
--- a/include/asm-arm/arch-clps711x/memory.h	Sat May 18 00:46:08 2002
+++ b/include/asm-arm/arch-clps711x/memory.h	Sat May 18 00:46:08 2002
@@ -120,9 +120,10 @@
 		(((unsigned long)(addr) - PAGE_OFFSET) >> NODE_MAX_MEM_SHIFT)
 
 /*
- * Given a physical address, convert it to a node id.
+ * Given a page frame number, convert it to a node id.
  */
-#define PHYS_TO_NID(addr) KVADDR_TO_NID(__phys_to_virt(addr))
+#define PFN_TO_NID(pfn) \
+	(((pfn) - PHYS_PFN_OFFSET) >> (NODE_MAX_MEM_SHIFT - PAGE_SHIFT))
 
 /*
  * Given a kaddr, ADDR_TO_MAPBASE finds the owning node of the memory
@@ -131,30 +132,15 @@
 #define ADDR_TO_MAPBASE(kaddr) \
 			NODE_MEM_MAP(KVADDR_TO_NID((unsigned long)(kaddr)))
 
+#define PFN_TO_MAPBASE(pfn)	NODE_MEM_MAP(PFN_TO_NID(pfn))
+
 /*
  * Given a kaddr, LOCAL_MAR_NR finds the owning node of the memory
  * and returns the index corresponding to the appropriate page in the
  * node's mem_map.
  */
-#define LOCAL_MAP_NR(kaddr) \
-	(((unsigned long)(kaddr)-LOCAL_BASE_ADDR((kaddr))) >> PAGE_SHIFT)
-
-/*
- * Given a kaddr, virt_to_page returns a pointer to the corresponding 
- * mem_map entry.
- */
-#define virt_to_page(kaddr) \
-	(ADDR_TO_MAPBASE(kaddr) + LOCAL_MAP_NR(kaddr))
-
-/*
- * VALID_PAGE returns a non-zero value if given page pointer is valid.
- * This assumes all node's mem_maps are stored within the node they refer to.
- */
-#define VALID_PAGE(page) \
-({ unsigned int node = KVADDR_TO_NID(page); \
-   ( (node < NR_NODES) && \
-     ((unsigned)((page) - NODE_MEM_MAP(node)) < NODE_DATA(node)->node_size) ); \
-})
+#define LOCAL_MAP_NR(addr) \
+	(((unsigned long)(addr) & (NODE_MAX_MEM_SIZE - 1)) >> PAGE_SHIFT)
 
 /*
  * The PS7211 allows up to 256MB max per DRAM bank, but the EDB7211
@@ -167,40 +153,13 @@
 #define NODE_MAX_MEM_SHIFT	24
 #define NODE_MAX_MEM_SIZE	(1<<NODE_MAX_MEM_SHIFT)
 
-/*
- * Given a mem_map_t, LOCAL_MAP_BASE finds the owning node for the
- * physical page and returns the kaddr for the mem_map of that node.
- */
-#define LOCAL_MAP_BASE(page) \
-			NODE_MEM_MAP(KVADDR_TO_NID((unsigned long)(page)))
-
-/*
- * Given a kaddr, LOCAL_BASE_ADDR finds the owning node of the memory
- * and returns the kaddr corresponding to first physical page in the
- * node's mem_map.
- */
-#define LOCAL_BASE_ADDR(kaddr)	((unsigned long)(kaddr) & ~(NODE_MAX_MEM_SIZE-1))
-
-/* 
- * With discontigmem, the conceptual mem_map array starts from PAGE_OFFSET.
- * Given a kaddr, MAP_NR returns the appropriate global mem_map index so 
- * it matches the corresponding node's local mem_map.
- */
-#define MAP_NR(kaddr)	(LOCAL_MAP_NR((kaddr)) + \
-		(((unsigned long)ADDR_TO_MAPBASE((kaddr)) - PAGE_OFFSET) / \
-		sizeof(mem_map_t)))
-
 #else
 
-#define PHYS_TO_NID(addr)	(0)
+#define PFN_TO_NID(pfn)		(0)
 
 #endif /* CONFIG_DISCONTIGMEM */
 
 #endif	/* CONFIG_ARCH_EDB7211 */
-
-#ifndef PHYS_TO_NID
-#define PHYS_TO_NID(addr)	(0)
-#endif
 
 #endif
 
diff -Nru a/include/asm-arm/arch-integrator/time.h b/include/asm-arm/arch-integrator/time.h
--- a/include/asm-arm/arch-integrator/time.h	Sat May 18 00:46:08 2002
+++ b/include/asm-arm/arch-integrator/time.h	Sat May 18 00:46:08 2002
@@ -129,7 +129,6 @@
 	timer2->TimerControl = 0;
 
 	timer1->TimerLoad    = TIMER_RELOAD;
-	timer1->TimerValue   = TIMER_RELOAD;
 	timer1->TimerControl = TIMER_CTRL | 0x40;	/* periodic */
 
 	/* 
diff -Nru a/include/asm-arm/arch-sa1100/SA-1100.h b/include/asm-arm/arch-sa1100/SA-1100.h
--- a/include/asm-arm/arch-sa1100/SA-1100.h	Sat May 18 00:46:09 2002
+++ b/include/asm-arm/arch-sa1100/SA-1100.h	Sat May 18 00:46:09 2002
@@ -22,6 +22,16 @@
 
 #include "bitfield.h"
 
+/*
+ * SA1100 CS line to physical address
+ */
+
+#define SA1100_CS0_PHYS	0x00000000
+#define SA1100_CS1_PHYS	0x08000000
+#define SA1100_CS2_PHYS	0x10000000
+#define SA1100_CS3_PHYS	0x18000000
+#define SA1100_CS4_PHYS	0x40000000
+#define SA1100_CS5_PHYS	0x48000000
 
 /*
  * Personal Computer Memory Card International Association (PCMCIA) sockets
diff -Nru a/include/asm-arm/arch-sa1100/hardware.h b/include/asm-arm/arch-sa1100/hardware.h
--- a/include/asm-arm/arch-sa1100/hardware.h	Sat May 18 00:46:09 2002
+++ b/include/asm-arm/arch-sa1100/hardware.h	Sat May 18 00:46:09 2002
@@ -24,15 +24,6 @@
 
 
 /*
- * Those are statically mapped PCMCIA IO space for designs using it as a
- * generic IO bus, typically with ISA parts, hardwired IDE interfaces, etc.
- * The actual PCMCIA code is mapping required IO region at run time.
- */
-#define PCMCIA_IO_0_BASE	0xf6000000
-#define PCMCIA_IO_1_BASE	0xf7000000
-
-
-/*
  * We requires absolute addresses i.e. (PCMCIA_IO_0_BASE + 0x3f8) for 
  * in*()/out*() macros to be usable for all cases.
  */
diff -Nru a/include/asm-arm/arch-sa1100/irqs.h b/include/asm-arm/arch-sa1100/irqs.h
--- a/include/asm-arm/arch-sa1100/irqs.h	Sat May 18 00:46:08 2002
+++ b/include/asm-arm/arch-sa1100/irqs.h	Sat May 18 00:46:08 2002
@@ -61,11 +61,6 @@
 #define	IRQ_GPIO27		48
 
 /*
- * To get the GPIO number from an IRQ number
- */
-#define GPIO_11_27_IRQ(i)	((i) - 21)
-
-/*
  * The next 16 interrupts are for board specific purposes.  Since
  * the kernel can only run on one machine at a time, we can re-use
  * these.  If you need more, increase IRQ_BOARD_END, but keep it
diff -Nru a/include/asm-arm/arch-sa1100/memory.h b/include/asm-arm/arch-sa1100/memory.h
--- a/include/asm-arm/arch-sa1100/memory.h	Sat May 18 00:46:08 2002
+++ b/include/asm-arm/arch-sa1100/memory.h	Sat May 18 00:46:08 2002
@@ -79,49 +79,36 @@
 /*
  * Given a kernel address, find the home node of the underlying memory.
  */
-#define KVADDR_TO_NID(addr) \
-		(((unsigned long)(addr) - 0xc0000000) >> 27)
+#define KVADDR_TO_NID(addr) (((unsigned long)(addr) - PAGE_OFFSET) >> 27)
 
 /*
- * Given a physical address, convert it to a node id.
+ * Given a page frame number, convert it to a node id.
  */
-#define PHYS_TO_NID(addr) KVADDR_TO_NID(__phys_to_virt(addr))
+#define PFN_TO_NID(pfn)		(((pfn) - PHYS_PFN_OFFSET) >> (27 - PAGE_SHIFT))
 
 /*
  * Given a kaddr, ADDR_TO_MAPBASE finds the owning node of the memory
- * and returns the mem_map of that node.
+ * and return the mem_map of that node.
  */
-#define ADDR_TO_MAPBASE(kaddr) \
-			NODE_MEM_MAP(KVADDR_TO_NID((unsigned long)(kaddr)))
+#define ADDR_TO_MAPBASE(kaddr)	NODE_MEM_MAP(KVADDR_TO_NID(kaddr))
 
 /*
- * Given a kaddr, LOCAL_MEM_MAP finds the owning node of the memory
- * and returns the index corresponding to the appropriate page in the
- * node's mem_map.
+ * Given a page frame number, find the owning node of the memory
+ * and return the mem_map of that node.
  */
-#define LOCAL_MAP_NR(kvaddr) \
-	(((unsigned long)(kvaddr) & 0x07ffffff) >> PAGE_SHIFT)
+#define PFN_TO_MAPBASE(pfn)	NODE_MEM_MAP(PFN_TO_NID(pfn))
 
 /*
- * Given a kaddr, virt_to_page returns a pointer to the corresponding 
- * mem_map entry.
- */
-#define virt_to_page(kaddr) \
-	(ADDR_TO_MAPBASE(kaddr) + LOCAL_MAP_NR(kaddr))
-
-/*
- * VALID_PAGE returns a non-zero value if given page pointer is valid.
- * This assumes all node's mem_maps are stored within the node they refer to.
+ * Given a kaddr, LOCAL_MEM_MAP finds the owning node of the memory
+ * and returns the index corresponding to the appropriate page in the
+ * node's mem_map.
  */
-#define VALID_PAGE(page) \
-({ unsigned int node = KVADDR_TO_NID(page); \
-   ( (node < NR_NODES) && \
-     ((unsigned)((page) - NODE_MEM_MAP(node)) < NODE_DATA(node)->node_size) ); \
-})
+#define LOCAL_MAP_NR(addr) \
+	(((unsigned long)(addr) & 0x07ffffff) >> PAGE_SHIFT)
 
 #else
 
-#define PHYS_TO_NID(addr)	(0)
+#define PFN_TO_NID(addr)	(0)
 
 #endif
 
diff -Nru a/include/asm-arm/glue.h b/include/asm-arm/glue.h
--- a/include/asm-arm/glue.h	Sat May 18 00:46:08 2002
+++ b/include/asm-arm/glue.h	Sat May 18 00:46:08 2002
@@ -158,7 +158,8 @@
  *
  *	We have the following to choose from:
  *	  v3		- ARMv3
- *	  v4		- ARMv4 without minicache
+ *	  v4wt		- ARMv4 with writethrough cache, without minicache
+ *	  v4wb		- ARMv4 with writeback cache, without minicache
  *	  v4_mc		- ARMv4 with minicache
  *	  v5te_mc	- ARMv5TE with minicache
  */
@@ -173,13 +174,21 @@
 # endif
 #endif
 
-#if defined(CONFIG_CPU_ARM720T) || defined(CONFIG_CPU_ARM920T) || \
-    defined(CONFIG_CPU_ARM922T) || defined(CONFIG_CPU_ARM926T) || \
-    defined(CONFIG_CPU_SA110)   || defined(CONFIG_CPU_ARM1020)
+#if defined(CONFIG_CPU_ARM720T)
 # ifdef _USER
 #  define MULTI_USER 1
 # else
-#  define _USER v4
+#  define _USER v4wt
+# endif
+#endif
+
+#if defined(CONFIG_CPU_ARM920T) || defined(CONFIG_CPU_ARM922T) || \
+    defined(CONFIG_CPU_ARM926T) || defined(CONFIG_CPU_SA110)   || \
+    defined(CONFIG_CPU_ARM1020)
+# ifdef _USER
+#  define MULTI_USER 1
+# else
+#  define _USER v4wb
 # endif
 #endif
 
diff -Nru a/include/asm-arm/ide.h b/include/asm-arm/ide.h
--- a/include/asm-arm/ide.h	Sat May 18 00:46:09 2002
+++ b/include/asm-arm/ide.h	Sat May 18 00:46:09 2002
@@ -21,10 +21,6 @@
 
 #include <asm/arch/ide.h>
 
-#define ide_ack_intr(hwif)		(1)
-#define ide_release_lock(lock)		do {} while (0)
-#define ide_get_lock(lock, hdlr, data)	do {} while (0)
-
 /*
  * We always use the new IDE port registering,
  * so these are fixed here.
diff -Nru a/include/asm-arm/io.h b/include/asm-arm/io.h
--- a/include/asm-arm/io.h	Sat May 18 00:46:09 2002
+++ b/include/asm-arm/io.h	Sat May 18 00:46:09 2002
@@ -270,24 +270,6 @@
 extern void consistent_sync(void *vaddr, size_t size, int rw);
 
 /*
- * Change "struct page" to physical address.
- */
-#ifdef CONFIG_DISCONTIGMEM
-#define page_to_phys(page)					  \
-	((((page) - page_zone(page)->zone_mem_map) << PAGE_SHIFT) \
-		  + page_zone(page)->zone_start_paddr)
-#else
-#define page_to_phys(page)					  \
-	(PHYS_OFFSET + (((page) - mem_map) << PAGE_SHIFT))
-#endif
-
-/*
- * We should really eliminate virt_to_bus() here - it's depreciated.
- */
-#define page_to_bus(page)					  \
-	(virt_to_bus(page_address(page)))
-
-/*
  * can the hardware map this into one segment or not, given no other
  * constraints.
  */
diff -Nru a/include/asm-arm/memory.h b/include/asm-arm/memory.h
--- a/include/asm-arm/memory.h	Sat May 18 00:46:15 2002
+++ b/include/asm-arm/memory.h	Sat May 18 00:46:15 2002
@@ -1,22 +1,34 @@
 /*
  *  linux/include/asm-arm/memory.h
  *
- *  Copyright (C) 2000 Russell King
+ *  Copyright (C) 2000-2002 Russell King
  *
  * 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.
  *
  *  Note: this file should not be included by non-asm/.h files
- *
- *  Modifications:
  */
 #ifndef __ASM_ARM_MEMORY_H
 #define __ASM_ARM_MEMORY_H
 
+#include <linux/config.h>
 #include <asm/arch/memory.h>
 
-static inline unsigned long virt_to_phys(volatile void *x)
+/*
+ * PFNs are used to describe any physical page; this means
+ * PFN 0 == physical address 0.
+ *
+ * This is the PFN of the first RAM page in the kernel
+ * direct-mapped view.  We assume this is the first page
+ * of RAM in the mem_map as well.
+ */
+#define PHYS_PFN_OFFSET	(PHYS_OFFSET >> PAGE_SHIFT)
+
+/*
+ * These are *only* valid on the kernel direct mapped RAM memory.
+ */
+static inline unsigned long virt_to_phys(void *x)
 {
 	return __virt_to_phys((unsigned long)(x));
 }
@@ -26,10 +38,77 @@
 	return (void *)(__phys_to_virt((unsigned long)(x)));
 }
 
+#define __pa(x)			__virt_to_phys((unsigned long)(x))
+#define __va(x)			((void *)__phys_to_virt((unsigned long)(x)))
+
 /*
  * Virtual <-> DMA view memory address translations
+ * Again, these are *only* valid on the kernel direct mapped RAM
+ * memory.  Use of these is *depreciated*.
  */
 #define virt_to_bus(x)		(__virt_to_bus((unsigned long)(x)))
 #define bus_to_virt(x)		((void *)(__bus_to_virt((unsigned long)(x))))
+
+/*
+ * Conversion between a struct page and a physical address.
+ *
+ * Note: when converting an unknown physical address to a
+ * struct page, the resulting pointer must be validated
+ * using VALID_PAGE().  It must return an invalid struct page
+ * for any physical address not corresponding to a system
+ * RAM address.
+ *
+ *  page_to_pfn(page)	convert a struct page * to a PFN number
+ *  pfn_to_page(pfn)	convert a _valid_ PFN number to struct page *
+ *  pfn_valid(pfn)	indicates whether a PFN number is valid
+ *
+ *  virt_to_page(k)	convert a _valid_ virtual address to struct page *
+ *  virt_addr_valid(k)	indicates whether a virtual address is valid
+ */
+#ifndef CONFIG_DISCONTIGMEM
+
+#define page_to_pfn(page)	(((page) - mem_map) + PHYS_PFN_OFFSET)
+#define pfn_to_page(pfn)	((mem_map + (pfn)) - PHYS_PFN_OFFSET)
+#define pfn_valid(pfn)		((pfn) >= PHYS_PFN_OFFSET && (pfn) < max_mapnr)
+
+#define virt_to_page(kaddr)	(pfn_to_page(__pa(kaddr) >> PAGE_SHIFT))
+#define virt_addr_valid(kaddr)	((kaddr) >= PAGE_OFFSET && (kaddr) < (unsigned long)high_memory)
+
+#else
+/*
+ * This is more complex.  We have a set of mem_map arrays spread
+ * around in memory.
+ */
+#define page_to_pfn(page)					\
+	(((page) - page_zone(page)->zone_mem_map)		\
+	  + (page_zone(page)->zone_start_paddr >> PAGE_SHIFT))
+
+#define pfn_to_page(pfn)					\
+	(PFN_TO_MAPBASE(pfn) + LOCAL_MAP_NR((pfn) << PAGE_SHIFT))
+
+#define pfn_valid(pfn)		(PFN_TO_NID(pfn) < NR_NODES)
+
+#define virt_to_page(kaddr)					\
+	(ADDR_TO_MAPBASE(kaddr) + LOCAL_MAP_NR(kaddr))
+
+#define virt_addr_valid(kaddr)	(KVADDR_TO_NID(kaddr) < NR_NODES)
+
+/*
+ * Common discontigmem stuff.
+ *  PHYS_TO_NID is used by the ARM kernel/setup.c
+ */
+#define PHYS_TO_NID(addr)	PFN_TO_NID((addr) >> PAGE_SHIFT)
+
+#endif
+
+/*
+ * For BIO.  "will die".  Kill me when bio_to_phys() and bvec_to_phys() die.
+ */
+#define page_to_phys(page)	(page_to_pfn(page) << PAGE_SHIFT)
+
+/*
+ * We should really eliminate virt_to_bus() here - it's depreciated.
+ */
+#define page_to_bus(page)	(virt_to_bus(page_address(page)))
 
 #endif
diff -Nru a/include/asm-arm/page.h b/include/asm-arm/page.h
--- a/include/asm-arm/page.h	Sat May 18 00:46:08 2002
+++ b/include/asm-arm/page.h	Sat May 18 00:46:08 2002
@@ -125,18 +125,9 @@
 	return order;
 }
 
-#endif /* !__ASSEMBLY__ */
-
-#include <asm/arch/memory.h>
+#include <asm/memory.h>
 
-#define __pa(x)			__virt_to_phys((unsigned long)(x))
-#define __va(x)			((void *)__phys_to_virt((unsigned long)(x)))
-
-#ifndef CONFIG_DISCONTIGMEM
-#define virt_to_page(kaddr)	(mem_map + (__pa(kaddr) >> PAGE_SHIFT) - \
-				 (PHYS_OFFSET >> PAGE_SHIFT))
-#define VALID_PAGE(page)	((page - mem_map) < max_mapnr)
-#endif
+#endif /* !__ASSEMBLY__ */
 
 #define VM_DATA_DEFAULT_FLAGS	(VM_READ | VM_WRITE | VM_EXEC | \
 				 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
diff -Nru a/include/asm-arm/pgtable.h b/include/asm-arm/pgtable.h
--- a/include/asm-arm/pgtable.h	Sat May 18 00:46:08 2002
+++ b/include/asm-arm/pgtable.h	Sat May 18 00:46:08 2002
@@ -11,7 +11,7 @@
 #define _ASMARM_PGTABLE_H
 
 #include <linux/config.h>
-#include <asm/arch/memory.h>
+#include <asm/memory.h>
 #include <asm/arch/vmalloc.h>
 
 /*
@@ -79,21 +79,12 @@
 extern struct page *empty_zero_page;
 #define ZERO_PAGE(vaddr)	(empty_zero_page)
 
+#define pte_pfn(pte)		(pte_val(pte) >> PAGE_SHIFT)
+#define pfn_pte(pfn,prot)	(__pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot)))
+
 #define pte_none(pte)		(!pte_val(pte))
 #define pte_clear(ptep)		set_pte((ptep), __pte(0))
-
-#ifndef CONFIG_DISCONTIGMEM
-#define pte_page(x)		(mem_map + (pte_val((x)) >> PAGE_SHIFT) - \
-				 (PHYS_OFFSET >> PAGE_SHIFT))
-#else
-/*
- * I'm not happy with this - we needlessly convert a physical address
- * to a virtual one, and then immediately back to a physical address,
- * which, if __va and __pa are expensive causes twice the expense for
- * zero gain. --rmk
- */
-#define pte_page(x)		(virt_to_page(__va(pte_val((x)))))
-#endif
+#define pte_page(pte)		(pfn_to_page(pte_pfn(pte)))
 
 #define pmd_none(pmd)		(!pmd_val(pmd))
 #define pmd_present(pmd)	(pmd_val(pmd))
@@ -107,12 +98,7 @@
  * Conversion functions: convert a page and protection to a page entry,
  * and a page entry and page directory to the page they refer to.
  */
-static inline pte_t mk_pte_phys(unsigned long physpage, pgprot_t pgprot)
-{
-	return __pte(physpage | pgprot_val(pgprot));
-}
-
-#define mk_pte(page,pgprot)	mk_pte_phys(__pa(page_address(page)), pgprot)
+#define mk_pte(page,prot)	pfn_pte(page_to_pfn(page),prot)
 
 /*
  * The "pgd_xxx()" functions here are trivial for a folded two-level
diff -Nru a/include/asm-arm/setup.h b/include/asm-arm/setup.h
--- a/include/asm-arm/setup.h	Sat May 18 00:46:10 2002
+++ b/include/asm-arm/setup.h	Sat May 18 00:46:10 2002
@@ -10,77 +10,12 @@
  *  Structure passed to kernel to tell it about the
  *  hardware it's running on.  See linux/Documentation/arm/Setup
  *  for more info.
- *
- * NOTE:
- *  This file contains two ways to pass information from the boot
- *  loader to the kernel. The old struct param_struct is deprecated,
- *  but it will be kept in the kernel for 5 years from now
- *  (2001). This will allow boot loaders to convert to the new struct
- *  tag way.
  */
 #ifndef __ASMARM_SETUP_H
 #define __ASMARM_SETUP_H
 
-/*
- * Usage:
- *  - do not go blindly adding fields, add them at the end
- *  - when adding fields, don't rely on the address until
- *    a patch from me has been released
- *  - unused fields should be zero (for future expansion)
- *  - this structure is relatively short-lived - only
- *    guaranteed to contain useful data in setup_arch()
- */
 #define COMMAND_LINE_SIZE 1024
 
-/* This is the old deprecated way to pass parameters to the kernel */
-struct param_struct {
-    union {
-	struct {
-	    unsigned long page_size;		/*  0 */
-	    unsigned long nr_pages;		/*  4 */
-	    unsigned long ramdisk_size;		/*  8 */
-	    unsigned long flags;		/* 12 */
-#define FLAG_READONLY	1
-#define FLAG_RDLOAD	4
-#define FLAG_RDPROMPT	8
-	    unsigned long rootdev;		/* 16 */
-	    unsigned long video_num_cols;	/* 20 */
-	    unsigned long video_num_rows;	/* 24 */
-	    unsigned long video_x;		/* 28 */
-	    unsigned long video_y;		/* 32 */
-	    unsigned long memc_control_reg;	/* 36 */
-	    unsigned char sounddefault;		/* 40 */
-	    unsigned char adfsdrives;		/* 41 */
-	    unsigned char bytes_per_char_h;	/* 42 */
-	    unsigned char bytes_per_char_v;	/* 43 */
-	    unsigned long pages_in_bank[4];	/* 44 */
-	    unsigned long pages_in_vram;	/* 60 */
-	    unsigned long initrd_start;		/* 64 */
-	    unsigned long initrd_size;		/* 68 */
-	    unsigned long rd_start;		/* 72 */
-	    unsigned long system_rev;		/* 76 */
-	    unsigned long system_serial_low;	/* 80 */
-	    unsigned long system_serial_high;	/* 84 */
-	    unsigned long mem_fclk_21285;       /* 88 */
-	} s;
-	char unused[256];
-    } u1;
-    union {
-	char paths[8][128];
-	struct {
-	    unsigned long magic;
-	    char n[1024 - sizeof(unsigned long)];
-	} s;
-    } u2;
-    char commandline[COMMAND_LINE_SIZE];
-};
-
-
-
-/*
- * The new way of passing information: a list of tagged entries
- */
-
 /* The list ends with an ATAG_NONE node. */
 #define ATAG_NONE	0x00000000
 
@@ -131,7 +66,14 @@
 };
 
 /* describes where the compressed ramdisk image lives */
+/*
+ * this one accidentally used virtual addresses - as such,
+ * its depreciated.
+ */
 #define ATAG_INITRD	0x54410005
+
+/* describes where the compressed ramdisk image lives */
+#define ATAG_INITRD2	0x54420005
 
 struct tag_initrd {
 	u32 start;	/* physical start address */
diff -Nru a/include/asm-cris/ide.h b/include/asm-cris/ide.h
--- a/include/asm-cris/ide.h	Sat May 18 00:46:08 2002
+++ b/include/asm-cris/ide.h	Sat May 18 00:46:08 2002
@@ -90,15 +90,8 @@
 
 /* some configuration options we don't need */
 
-#undef SUPPORT_VLB_SYNC
-#define SUPPORT_VLB_SYNC 0
-
 #undef SUPPORT_SLOW_DATA_PORTS
 #define SUPPORT_SLOW_DATA_PORTS	0
-
-#define ide_ack_intr(hwif)		(1)
-#define ide_release_lock(lock)		do {} while (0)
-#define ide_get_lock(lock, hdlr, data)	do {} while (0)
 
 /* the drive addressing is done through a controller register on the Etrax CPU */
 void OUT_BYTE(unsigned char data, ide_ioreg_t reg);
diff -Nru a/include/asm-generic/tlb.h b/include/asm-generic/tlb.h
--- a/include/asm-generic/tlb.h	Sat May 18 00:46:08 2002
+++ b/include/asm-generic/tlb.h	Sat May 18 00:46:08 2002
@@ -16,7 +16,6 @@
 #include <linux/config.h>
 #include <asm/tlbflush.h>
 
-#ifdef CONFIG_SMP
 /* aim for something that fits in the L1 cache */
 #define FREE_PTE_NR	508
 
@@ -26,90 +25,81 @@
  * shootdown.
  */
 typedef struct free_pte_ctx {
-	struct vm_area_struct	*vma;
+	struct mm_struct	*mm;
 	unsigned long		nr;	/* set to ~0UL means fast mode */
-	unsigned long	start_addr, end_addr;
-	pte_t	ptes[FREE_PTE_NR];
+	unsigned long		freed;
+	struct page *		pages[FREE_PTE_NR];
 } mmu_gather_t;
 
 /* Users of the generic TLB shootdown code must declare this storage space. */
 extern mmu_gather_t	mmu_gathers[NR_CPUS];
 
+/* Do me later */
+#define tlb_start_vma(tlb, vma) do { } while (0)
+#define tlb_end_vma(tlb, vma) do { } while (0)
+
 /* tlb_gather_mmu
  *	Return a pointer to an initialized mmu_gather_t.
  */
-static inline mmu_gather_t *tlb_gather_mmu(struct vm_area_struct *vma)
+static inline mmu_gather_t *tlb_gather_mmu(struct mm_struct *mm)
 {
 	mmu_gather_t *tlb = &mmu_gathers[smp_processor_id()];
-	struct mm_struct *mm = vma->vm_mm;
 
-	tlb->vma = vma;
-	/* Use fast mode if there is only one user of this mm (this process) */
-	tlb->nr = (atomic_read(&(mm)->mm_users) == 1) ? ~0UL : 0UL;
+	tlb->mm = mm;
+	tlb->freed = 0;
+
+	/* Use fast mode if only one CPU is online */
+	tlb->nr = smp_num_cpus > 1 ? 0UL : ~0UL;
 	return tlb;
 }
 
-/* void tlb_remove_page(mmu_gather_t *tlb, pte_t *ptep, unsigned long addr)
- *	Must perform the equivalent to __free_pte(pte_get_and_clear(ptep)), while
- *	handling the additional races in SMP caused by other CPUs caching valid
- *	mappings in their TLBs.
- */
-#define tlb_remove_page(ctxp, pte, addr) do {\
-		/* Handle the common case fast, first. */\
-		if ((ctxp)->nr == ~0UL) {\
-			__free_pte(*(pte));\
-			pte_clear((pte));\
-			break;\
-		}\
-		if (!(ctxp)->nr) \
-			(ctxp)->start_addr = (addr);\
-		(ctxp)->ptes[(ctxp)->nr++] = ptep_get_and_clear(pte);\
-		(ctxp)->end_addr = (addr) + PAGE_SIZE;\
-		if ((ctxp)->nr >= FREE_PTE_NR)\
-			tlb_finish_mmu((ctxp), 0, 0);\
-	} while (0)
+static inline void tlb_flush_mmu(mmu_gather_t *tlb, unsigned long start, unsigned long end)
+{
+	unsigned long nr;
+
+	flush_tlb_mm(tlb->mm);
+	nr = tlb->nr;
+	if (nr != ~0UL) {
+		unsigned long i;
+		tlb->nr = 0;
+		for (i=0; i < nr; i++)
+			free_page_and_swap_cache(tlb->pages[i]);
+	}
+}
 
 /* tlb_finish_mmu
  *	Called at the end of the shootdown operation to free up any resources
  *	that were required.  The page table lock is still held at this point.
  */
-static inline void tlb_finish_mmu(struct free_pte_ctx *ctx, unsigned long start, unsigned long end)
+static inline void tlb_finish_mmu(mmu_gather_t *tlb, unsigned long start, unsigned long end)
 {
-	unsigned long i, nr;
-
-	/* Handle the fast case first. */
-	if (ctx->nr == ~0UL) {
-		flush_tlb_range(ctx->vma, start, end);
-		return;
-	}
-	nr = ctx->nr;
-	ctx->nr = 0;
-	if (nr)
-		flush_tlb_range(ctx->vma, ctx->start_addr, ctx->end_addr);
-	for (i=0; i < nr; i++) {
-		pte_t pte = ctx->ptes[i];
-		__free_pte(pte);
-	}
+	int freed = tlb->freed;
+	struct mm_struct *mm = tlb->mm;
+	int rss = mm->rss;
+
+	if (rss < freed)
+		freed = rss;
+	mm->rss = rss - freed;
+	tlb_flush_mmu(tlb, start, end);
 }
 
-#else
 
-/* The uniprocessor functions are quite simple and are inline macros in an
- * attempt to get gcc to generate optimal code since this code is run on each
- * page in a process at exit.
+/* void tlb_remove_page(mmu_gather_t *tlb, pte_t *ptep, unsigned long addr)
+ *	Must perform the equivalent to __free_pte(pte_get_and_clear(ptep)), while
+ *	handling the additional races in SMP caused by other CPUs caching valid
+ *	mappings in their TLBs.
  */
-typedef struct vm_area_struct mmu_gather_t;
-
-#define tlb_gather_mmu(vma)	(vma)
-#define tlb_finish_mmu(tlb, start, end)	flush_tlb_range(tlb, start, end)
-#define tlb_remove_page(tlb, ptep, addr)	do {\
-		pte_t __pte = *(ptep);\
-		pte_clear(ptep);\
-		__free_pte(__pte);\
-	} while (0)
-
-#endif
-
+static inline void tlb_remove_page(mmu_gather_t *tlb, struct page *page)
+{
+	/* Handle the common case fast, first. */\
+	if (tlb->nr == ~0UL) {
+		free_page_and_swap_cache(page);
+		return;
+	}
+	tlb->pages[tlb->nr++] = page;
+	if (tlb->nr >= FREE_PTE_NR)
+		tlb_flush_mmu(tlb, 0, 0);
+}
 
 #endif /* _ASM_GENERIC__TLB_H */
 
diff -Nru a/include/asm-i386/ide.h b/include/asm-i386/ide.h
--- a/include/asm-i386/ide.h	Sat May 18 00:46:08 2002
+++ b/include/asm-i386/ide.h	Sat May 18 00:46:08 2002
@@ -86,10 +86,6 @@
 #endif
 }
 
-#define ide_ack_intr(hwif)		(1)
-#define ide_release_lock(lock)		do {} while (0)
-#define ide_get_lock(lock, hdlr, data)	do {} while (0)
-
 #endif /* __KERNEL__ */
 
 #endif /* __ASMi386_IDE_H */
diff -Nru a/include/asm-i386/pgalloc.h b/include/asm-i386/pgalloc.h
--- a/include/asm-i386/pgalloc.h	Sat May 18 00:46:08 2002
+++ b/include/asm-i386/pgalloc.h	Sat May 18 00:46:08 2002
@@ -35,6 +35,9 @@
 	__free_page(pte);
 }
 
+
+#define pte_free_tlb(tlb,pte) tlb_remove_page((tlb),(pte))
+
 /*
  * allocating and freeing a pmd is trivial: the 1-entry pmd is
  * inside the pgd, so has no extra memory associated with it.
@@ -43,6 +46,7 @@
 
 #define pmd_alloc_one(mm, addr)		({ BUG(); ((pmd_t *)2); })
 #define pmd_free(x)			do { } while (0)
+#define pmd_free_tlb(tlb,x)		do { } while (0)
 #define pgd_populate(mm, pmd, pte)	BUG()
 
 #define check_pgt_cache()	do { } while (0)
diff -Nru a/include/asm-ia64/ide.h b/include/asm-ia64/ide.h
--- a/include/asm-ia64/ide.h	Sat May 18 00:46:13 2002
+++ b/include/asm-ia64/ide.h	Sat May 18 00:46:13 2002
@@ -92,10 +92,6 @@
 #endif
 }
 
-#define ide_ack_intr(hwif)		(1)
-#define ide_release_lock(lock)		do {} while (0)
-#define ide_get_lock(lock, hdlr, data)	do {} while (0)
-
 #endif /* __KERNEL__ */
 
 #endif /* __ASM_IA64_IDE_H */
diff -Nru a/include/asm-m68k/ide.h b/include/asm-m68k/ide.h
--- a/include/asm-m68k/ide.h	Sat May 18 00:46:08 2002
+++ b/include/asm-m68k/ide.h	Sat May 18 00:46:08 2002
@@ -83,9 +83,6 @@
 #undef SUPPORT_SLOW_DATA_PORTS
 #define SUPPORT_SLOW_DATA_PORTS 0
 
-#undef SUPPORT_VLB_SYNC
-#define SUPPORT_VLB_SYNC 0
-
 /* this definition is used only on startup .. */
 #undef HD_DATA
 #define HD_DATA NULL
@@ -145,10 +142,13 @@
 
 #endif /* CONFIG_ATARI || CONFIG_Q40 */
 
+#define ATA_ARCH_ACK_INTR
+
+#ifdef CONFIG_ATARI
+#define ATA_ARCH_LOCK
 
 static __inline__ void ide_release_lock (int *ide_lock)
 {
-#ifdef CONFIG_ATARI
 	if (MACH_IS_ATARI) {
 		if (*ide_lock == 0) {
 			printk("ide_release_lock: bug\n");
@@ -157,12 +157,10 @@
 		*ide_lock = 0;
 		stdma_release();
 	}
-#endif /* CONFIG_ATARI */
 }
 
 static __inline__ void ide_get_lock (int *ide_lock, void (*handler)(int, void *, struct pt_regs *), void *data)
 {
-#ifdef CONFIG_ATARI
 	if (MACH_IS_ATARI) {
 		if (*ide_lock == 0) {
 			if (in_interrupt() > 0)
@@ -171,10 +169,8 @@
 			*ide_lock = 1;
 		}
 	}
-#endif /* CONFIG_ATARI */
 }
-
-#define ide_ack_intr(hwif)	((hwif)->hw.ack_intr ? (hwif)->hw.ack_intr(hwif) : 1)
+#endif /* CONFIG_ATARI */
 
 /*
  * On the Atari, we sometimes can't enable interrupts:
diff -Nru a/include/asm-mips/ide.h b/include/asm-mips/ide.h
--- a/include/asm-mips/ide.h	Sat May 18 00:46:08 2002
+++ b/include/asm-mips/ide.h	Sat May 18 00:46:08 2002
@@ -65,13 +65,6 @@
 #endif
 }
 
-#undef  SUPPORT_VLB_SYNC
-#define SUPPORT_VLB_SYNC 0
-
-#define ide_ack_intr(hwif)		(1)
-#define ide_release_lock(lock)		do {} while (0)
-#define ide_get_lock(lock, hdlr, data)	do {} while (0)
-
 #endif /* __KERNEL__ */
 
 #endif /* __ASM_IDE_H */
diff -Nru a/include/asm-mips64/ide.h b/include/asm-mips64/ide.h
--- a/include/asm-mips64/ide.h	Sat May 18 00:46:08 2002
+++ b/include/asm-mips64/ide.h	Sat May 18 00:46:08 2002
@@ -68,10 +68,6 @@
 #endif
 }
 
-#define ide_ack_intr(hwif)		(1)
-#define ide_release_lock(lock)		do {} while (0)
-#define ide_get_lock(lock, hdlr, data)	do {} while (0)
-
 #endif /* __KERNEL__ */
 
 #endif /* __ASM_IDE_H */
diff -Nru a/include/asm-parisc/ide.h b/include/asm-parisc/ide.h
--- a/include/asm-parisc/ide.h	Sat May 18 00:46:09 2002
+++ b/include/asm-parisc/ide.h	Sat May 18 00:46:09 2002
@@ -81,10 +81,6 @@
 #endif
 }
 
-#define ide_ack_intr(hwif)		(1)
-#define ide_release_lock(lock)		do {} while (0)
-#define ide_get_lock(lock, hdlr, data)	do {} while (0)
-
 #endif /* __KERNEL__ */
 
 #endif /* __ASMi386_IDE_H */
diff -Nru a/include/asm-ppc/ide.h b/include/asm-ppc/ide.h
--- a/include/asm-ppc/ide.h	Sat May 18 00:46:09 2002
+++ b/include/asm-ppc/ide.h	Sat May 18 00:46:09 2002
@@ -43,8 +43,6 @@
 
 #undef	SUPPORT_SLOW_DATA_PORTS
 #define	SUPPORT_SLOW_DATA_PORTS	0
-#undef	SUPPORT_VLB_SYNC
-#define SUPPORT_VLB_SYNC	0
 
 #define ide__sti()	__sti()
 
@@ -108,12 +106,8 @@
 }
 
 #if (defined CONFIG_APUS || defined CONFIG_BLK_DEV_MPC8xx_IDE )
-#define ide_ack_intr(hwif) (hwif->hw.ack_intr ? hwif->hw.ack_intr(hwif) : 1)
-#else
-#define ide_ack_intr(hwif)		(1)
+#define ATA_ARCH_ACK_INTR
 #endif
-#define ide_release_lock(lock)		do {} while (0)
-#define ide_get_lock(lock, hdlr, data)	do {} while (0)
 
 #endif /* __KERNEL__ */
 
diff -Nru a/include/asm-ppc64/ide.h b/include/asm-ppc64/ide.h
--- a/include/asm-ppc64/ide.h	Sat May 18 00:46:09 2002
+++ b/include/asm-ppc64/ide.h	Sat May 18 00:46:09 2002
@@ -50,10 +50,6 @@
 {
 }
 
-#define ide_ack_intr(hwif)		(1)
-#define ide_release_lock(lock)		do {} while (0)
-#define ide_get_lock(lock, hdlr, data)	do {} while (0)
-
 #endif /* __KERNEL__ */
 
 #endif /* __ASMPPC64_IDE_H */
diff -Nru a/include/asm-s390/ide.h b/include/asm-s390/ide.h
--- a/include/asm-s390/ide.h	Sat May 18 00:46:13 2002
+++ b/include/asm-s390/ide.h	Sat May 18 00:46:13 2002
@@ -18,14 +18,6 @@
 #define ide__sti()	do {} while (0)
 
 /*
- * The following are not needed for the non-m68k ports
- */
-#define ide_ack_intr(hwif)		(1)
-#define ide_fix_driveid(id)		do {} while (0)
-#define ide_release_lock(lock)		do {} while (0)
-#define ide_get_lock(lock, hdlr, data)	do {} while (0)
-
-/*
  * We always use the new IDE port registering,
  * so these are fixed here.
  */
diff -Nru a/include/asm-s390x/ide.h b/include/asm-s390x/ide.h
--- a/include/asm-s390x/ide.h	Sat May 18 00:46:08 2002
+++ b/include/asm-s390x/ide.h	Sat May 18 00:46:08 2002
@@ -17,10 +17,6 @@
 
 #define ide__sti()	do {} while (0)
 
-#define ide_ack_intr(hwif)		(1)
-#define ide_release_lock(lock)		do {} while (0)
-#define ide_get_lock(lock, hdlr, data)	do {} while (0)
-
 /*
  * We always use the new IDE port registering,
  * so these are fixed here.
diff -Nru a/include/asm-sh/ide.h b/include/asm-sh/ide.h
--- a/include/asm-sh/ide.h	Sat May 18 00:46:08 2002
+++ b/include/asm-sh/ide.h	Sat May 18 00:46:08 2002
@@ -107,10 +107,6 @@
 #endif
 }
 
-#define ide_ack_intr(hwif)		(1)
-#define ide_release_lock(lock)		do {} while (0)
-#define ide_get_lock(lock, hdlr, data)	do {} while (0)
-
 #endif /* __KERNEL__ */
 
 #endif /* __ASM_SH_IDE_H */
diff -Nru a/include/asm-sparc/ide.h b/include/asm-sparc/ide.h
--- a/include/asm-sparc/ide.h	Sat May 18 00:46:10 2002
+++ b/include/asm-sparc/ide.h	Sat May 18 00:46:10 2002
@@ -76,9 +76,6 @@
 #undef  SUPPORT_SLOW_DATA_PORTS
 #define SUPPORT_SLOW_DATA_PORTS 0
 
-#undef  SUPPORT_VLB_SYNC
-#define SUPPORT_VLB_SYNC 0
-
 #undef  HD_DATA
 #define HD_DATA ((ide_ioreg_t)0)
 
@@ -164,11 +161,6 @@
 
 	/* __flush_dcache_range((unsigned long)src, end); */ /* P3 see hme */
 }
-
-#define ide_ack_intr(hwif)		(1)
-/* #define ide_ack_intr(hwif)	((hwif)->hw.ack_intr ? (hwif)->hw.ack_intr(hwif) : 1) */
-#define ide_release_lock(lock)		do {} while (0)
-#define ide_get_lock(lock, hdlr, data)	do {} while (0)
 
 #endif /* __KERNEL__ */
 
diff -Nru a/include/asm-sparc64/bitops.h b/include/asm-sparc64/bitops.h
--- a/include/asm-sparc64/bitops.h	Sat May 18 00:46:08 2002
+++ b/include/asm-sparc64/bitops.h	Sat May 18 00:46:08 2002
@@ -10,9 +10,9 @@
 #include <linux/compiler.h>
 #include <asm/byteorder.h>
 
-extern long ___test_and_set_bit(unsigned long nr, volatile void *addr);
-extern long ___test_and_clear_bit(unsigned long nr, volatile void *addr);
-extern long ___test_and_change_bit(unsigned long nr, volatile void *addr);
+extern long ___test_and_set_bit(unsigned long nr, volatile unsigned long *addr);
+extern long ___test_and_clear_bit(unsigned long nr, volatile unsigned long *addr);
+extern long ___test_and_change_bit(unsigned long nr, volatile unsigned long *addr);
 
 #define test_and_set_bit(nr,addr)	({___test_and_set_bit(nr,addr)!=0;})
 #define test_and_clear_bit(nr,addr)	({___test_and_clear_bit(nr,addr)!=0;})
@@ -22,52 +22,64 @@
 #define change_bit(nr,addr)		((void)___test_and_change_bit(nr,addr))
 
 /* "non-atomic" versions... */
-#define __set_bit(X,Y)					\
-do {	unsigned long __nr = (X);			\
-	long *__m = ((long *) (Y)) + (__nr >> 6);	\
-	*__m |= (1UL << (__nr & 63));			\
-} while (0)
-#define __clear_bit(X,Y)				\
-do {	unsigned long __nr = (X);			\
-	long *__m = ((long *) (Y)) + (__nr >> 6);	\
-	*__m &= ~(1UL << (__nr & 63));			\
-} while (0)
-#define __change_bit(X,Y)				\
-do {	unsigned long __nr = (X);			\
-	long *__m = ((long *) (Y)) + (__nr >> 6);	\
-	*__m ^= (1UL << (__nr & 63));			\
-} while (0)
-#define __test_and_set_bit(X,Y)				\
-({	unsigned long __nr = (X);			\
-	long *__m = ((long *) (Y)) + (__nr >> 6);	\
-	long __old = *__m;				\
-	long __mask = (1UL << (__nr & 63));		\
-	*__m = (__old | __mask);			\
-	((__old & __mask) != 0);			\
-})
-#define __test_and_clear_bit(X,Y)			\
-({	unsigned long __nr = (X);			\
-	long *__m = ((long *) (Y)) + (__nr >> 6);	\
-	long __old = *__m;				\
-	long __mask = (1UL << (__nr & 63));		\
-	*__m = (__old & ~__mask);			\
-	((__old & __mask) != 0);			\
-})
-#define __test_and_change_bit(X,Y)			\
-({	unsigned long __nr = (X);			\
-	long *__m = ((long *) (Y)) + (__nr >> 6);	\
-	long __old = *__m;				\
-	long __mask = (1UL << (__nr & 63));		\
-	*__m = (__old ^ __mask);			\
-	((__old & __mask) != 0);			\
-})
+
+static __inline__ void __set_bit(int nr, volatile unsigned long *addr)
+{
+	volatile unsigned long *m = addr + (nr >> 6);
+
+	*m |= (1UL << (nr & 63));
+}
+
+static __inline__ void __clear_bit(int nr, volatile unsigned long *addr)
+{
+	volatile unsigned long *m = addr + (nr >> 6);
+
+	*m &= ~(1UL << (nr & 63));
+}
+
+static __inline__ void __change_bit(int nr, volatile unsigned long *addr)
+{
+	volatile unsigned long *m = addr + (nr >> 6);
+
+	*m ^= (1UL << (nr & 63));
+}
+
+static __inline__ int __test_and_set_bit(int nr, volatile unsigned long *addr)
+{
+	volatile unsigned long *m = addr + (nr >> 6);
+	long old = *m;
+	long mask = (1UL << (nr & 63));
+
+	*m = (old | mask);
+	return ((old & mask) != 0);
+}
+
+static __inline__ int __test_and_clear_bit(int nr, volatile unsigned long *addr)
+{
+	volatile unsigned long *m = addr + (nr >> 6);
+	long old = *m;
+	long mask = (1UL << (nr & 63));
+
+	*m = (old & ~mask);
+	return ((old & mask) != 0);
+}
+
+static __inline__ int __test_and_change_bit(int nr, volatile unsigned long *addr)
+{
+	volatile unsigned long *m = addr + (nr >> 6);
+	long old = *m;
+	long mask = (1UL << (nr & 63));
+
+	*m = (old ^ mask);
+	return ((old & mask) != 0);
+}
 
 #define smp_mb__before_clear_bit()	do { } while(0)
 #define smp_mb__after_clear_bit()	do { } while(0)
 
-static __inline__ int test_bit(int nr, __const__ void *addr)
+static __inline__ int test_bit(int nr, __const__ volatile unsigned long *addr)
 {
-	return (1UL & (((__const__ long *) addr)[nr >> 6] >> (nr & 63))) != 0UL;
+	return (1UL & ((addr)[nr >> 6] >> (nr & 63))) != 0UL;
 }
 
 /* The easy/cheese version for now. */
@@ -177,9 +189,9 @@
  * @offset: The bitnumber to start searching at
  * @size: The maximum size to search
  */
-static __inline__ unsigned long find_next_bit(void *addr, unsigned long size, unsigned long offset)
+static __inline__ unsigned long find_next_bit(unsigned long *addr, unsigned long size, unsigned long offset)
 {
-	unsigned long *p = ((unsigned long *) addr) + (offset >> 6);
+	unsigned long *p = addr + (offset >> 6);
 	unsigned long result = offset & ~63UL;
 	unsigned long tmp;
 
@@ -231,9 +243,9 @@
  * on Linus's ALPHA routines, which are pretty portable BTW.
  */
 
-static __inline__ unsigned long find_next_zero_bit(void *addr, unsigned long size, unsigned long offset)
+static __inline__ unsigned long find_next_zero_bit(unsigned long *addr, unsigned long size, unsigned long offset)
 {
-	unsigned long *p = ((unsigned long *) addr) + (offset >> 6);
+	unsigned long *p = addr + (offset >> 6);
 	unsigned long result = offset & ~63UL;
 	unsigned long tmp;
 
@@ -272,15 +284,15 @@
 #define find_first_zero_bit(addr, size) \
         find_next_zero_bit((addr), (size), 0)
 
-extern long ___test_and_set_le_bit(int nr, volatile void *addr);
-extern long ___test_and_clear_le_bit(int nr, volatile void *addr);
+extern long ___test_and_set_le_bit(int nr, volatile unsigned long *addr);
+extern long ___test_and_clear_le_bit(int nr, volatile unsigned long *addr);
 
 #define test_and_set_le_bit(nr,addr)	({___test_and_set_le_bit(nr,addr)!=0;})
 #define test_and_clear_le_bit(nr,addr)	({___test_and_clear_le_bit(nr,addr)!=0;})
 #define set_le_bit(nr,addr)		((void)___test_and_set_le_bit(nr,addr))
 #define clear_le_bit(nr,addr)		((void)___test_and_clear_le_bit(nr,addr))
 
-static __inline__ int test_le_bit(int nr, __const__ void * addr)
+static __inline__ int test_le_bit(int nr, __const__ unsigned long * addr)
 {
 	int			mask;
 	__const__ unsigned char	*ADDR = (__const__ unsigned char *) addr;
@@ -293,9 +305,9 @@
 #define find_first_zero_le_bit(addr, size) \
         find_next_zero_le_bit((addr), (size), 0)
 
-static __inline__ unsigned long find_next_zero_le_bit(void *addr, unsigned long size, unsigned long offset)
+static __inline__ unsigned long find_next_zero_le_bit(unsigned long *addr, unsigned long size, unsigned long offset)
 {
-	unsigned long *p = ((unsigned long *) addr) + (offset >> 6);
+	unsigned long *p = addr + (offset >> 6);
 	unsigned long result = offset & ~63UL;
 	unsigned long tmp;
 
@@ -332,18 +344,22 @@
 
 #ifdef __KERNEL__
 
-#define ext2_set_bit			test_and_set_le_bit
-#define ext2_clear_bit			test_and_clear_le_bit
-#define ext2_test_bit  			test_le_bit
-#define ext2_find_first_zero_bit	find_first_zero_le_bit
-#define ext2_find_next_zero_bit		find_next_zero_le_bit
+#define ext2_set_bit(nr,addr)		test_and_set_le_bit((nr),(unsigned long *)(addr))
+#define ext2_clear_bit(nr,addr)		test_and_clear_le_bit((nr),(unsigned long *)(addr))
+#define ext2_test_bit(nr,addr)		test_le_bit((nr),(unsigned long *)(addr))
+#define ext2_find_first_zero_bit(addr, size) \
+	find_first_zero_le_bit((unsigned long *)(addr), (size))
+#define ext2_find_next_zero_bit(addr, size, off) \
+	find_next_zero_le_bit((unsigned long *)(addr), (size), (off))
 
 /* Bitmap functions for the minix filesystem.  */
-#define minix_test_and_set_bit(nr,addr) test_and_set_bit(nr,addr)
-#define minix_set_bit(nr,addr) set_bit(nr,addr)
-#define minix_test_and_clear_bit(nr,addr) test_and_clear_bit(nr,addr)
-#define minix_test_bit(nr,addr) test_bit(nr,addr)
-#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size)
+#define minix_test_and_set_bit(nr,addr)	test_and_set_bit((nr),(unsigned long *)(addr))
+#define minix_set_bit(nr,addr)		set_bit((nr),(unsigned long *)(addr))
+#define minix_test_and_clear_bit(nr,addr) \
+	test_and_clear_bit((nr),(unsigned long *)(addr))
+#define minix_test_bit(nr,addr)		test_bit((nr),(unsigned long *)(addr))
+#define minix_find_first_zero_bit(addr,size) \
+	find_first_zero_bit((unsigned long *)(addr),(size))
 
 #endif /* __KERNEL__ */
 
diff -Nru a/include/asm-sparc64/ide.h b/include/asm-sparc64/ide.h
--- a/include/asm-sparc64/ide.h	Sat May 18 00:46:09 2002
+++ b/include/asm-sparc64/ide.h	Sat May 18 00:46:09 2002
@@ -72,9 +72,6 @@
 #undef  SUPPORT_SLOW_DATA_PORTS
 #define SUPPORT_SLOW_DATA_PORTS 0
 
-#undef  SUPPORT_VLB_SYNC
-#define SUPPORT_VLB_SYNC 0
-
 #undef  HD_DATA
 #define HD_DATA ((ide_ioreg_t)0)
 
@@ -180,10 +177,6 @@
 	__flush_dcache_range((unsigned long)src, end);
 #endif
 }
-
-#define ide_ack_intr(hwif)		(1)
-#define ide_release_lock(lock)		do {} while (0)
-#define ide_get_lock(lock, hdlr, data)	do {} while (0)
 
 #endif /* __KERNEL__ */
 
diff -Nru a/include/asm-sparc64/visasm.h b/include/asm-sparc64/visasm.h
--- a/include/asm-sparc64/visasm.h	Sat May 18 00:46:08 2002
+++ b/include/asm-sparc64/visasm.h	Sat May 18 00:46:08 2002
@@ -10,8 +10,6 @@
 #include <asm/pstate.h>
 #include <asm/ptrace.h>
 
-#define AOFF_task_fpregs	(((ASIZ_task) + (64 - 1)) & ~(64 - 1))
- 
 /* Clobbers %o5, %g1, %g2, %g3, %g7, %icc, %xcc */
 
 #define VISEntry					\
diff -Nru a/include/asm-x86_64/ide.h b/include/asm-x86_64/ide.h
--- a/include/asm-x86_64/ide.h	Sat May 18 00:46:09 2002
+++ b/include/asm-x86_64/ide.h	Sat May 18 00:46:09 2002
@@ -86,10 +86,6 @@
 #endif
 }
 
-#define ide_ack_intr(hwif)		(1)
-#define ide_release_lock(lock)		do {} while (0)
-#define ide_get_lock(lock, hdlr, data)	do {} while (0)
-
 #endif /* __KERNEL__ */
 
 #endif /* __ASMi386_IDE_H */
diff -Nru a/include/linux/b1lli.h b/include/linux/b1lli.h
--- a/include/linux/b1lli.h	Sat May 18 00:46:08 2002
+++ b/include/linux/b1lli.h	Sat May 18 00:46:08 2002
@@ -61,12 +61,12 @@
 } avmb1_extcarddef;
 
 #define	AVMB1_LOAD		0	/* load image to card */
-#define AVMB1_ADDCARD		1	/* add a new card */
+#define AVMB1_ADDCARD		1	/* add a new card - OBSOLETE */
 #define AVMB1_RESETCARD		2	/* reset a card */
 #define	AVMB1_LOAD_AND_CONFIG	3	/* load image and config to card */
 #define	AVMB1_ADDCARD_WITH_TYPE	4	/* add a new card, with cardtype */
 #define AVMB1_GET_CARDINFO	5	/* get cardtype */
-#define AVMB1_REMOVECARD	6	/* remove a card (useful for T1) */
+#define AVMB1_REMOVECARD	6	/* remove a card - OBSOLETE */
 
 #define	AVMB1_REGISTERCARD_IS_OBSOLETE
 
diff -Nru a/include/linux/cuda.h b/include/linux/cuda.h
--- a/include/linux/cuda.h	Sat May 18 00:46:08 2002
+++ b/include/linux/cuda.h	Sat May 18 00:46:08 2002
@@ -29,7 +29,6 @@
 #ifdef __KERNEL__
 
 extern int find_via_cuda(void);
-extern int via_cuda_start(void);
 extern int cuda_request(struct adb_request *req,
 			void (*done)(struct adb_request *), int nbytes, ...);
 extern void cuda_poll(void);
diff -Nru a/include/linux/fs.h b/include/linux/fs.h
--- a/include/linux/fs.h	Sat May 18 00:46:08 2002
+++ b/include/linux/fs.h	Sat May 18 00:46:08 2002
@@ -660,15 +660,6 @@
 	 * even looking at it. You had been warned.
 	 */
 	struct semaphore s_vfs_rename_sem;	/* Kludge */
-
-	/* The next field is used by knfsd when converting a (inode number based)
-	 * file handle into a dentry. As it builds a path in the dcache tree from
-	 * the bottom up, there may for a time be a subpath of dentrys which is not
-	 * connected to the main tree.  This semaphore ensure that there is only ever
-	 * one such free path per filesystem.  Note that unconnected files (or other
-	 * non-directories) are allowed, but not unconnected diretories.
-	 */
-	struct semaphore s_nfsd_free_path_sem;
 };
 
 /*
@@ -798,30 +789,6 @@
 	void (*clear_inode) (struct inode *);
 	void (*umount_begin) (struct super_block *);
 
-	/* Following are for knfsd to interact with "interesting" filesystems
-	 * Currently just reiserfs, but possibly FAT and others later
-	 *
-	 * fh_to_dentry is given a filehandle fragement with length, and a type flag
-	 *   and must return a dentry for the referenced object or, if "parent" is
-	 *   set, a dentry for the parent of the object.
-	 *   If a dentry cannot be found, a "root" dentry should be created and
-	 *   flaged as DCACHE_DISCONNECTED. nfsd_iget is an example implementation.
-	 *
-	 * dentry_to_fh is given a dentry and must generate the filesys specific
-	 *   part of the file handle.  Available length is passed in *lenp and used
-	 *   length should be returned therein.
-	 *   If need_parent is set, then dentry_to_fh should encode sufficient information
-	 *   to find the (current) parent.
-	 *   dentry_to_fh should return a 1byte "type" which will be passed back in
-	 *   the fhtype arguement to fh_to_dentry.  Type of 0 is reserved.
-	 *   If filesystem was exportable before the introduction of fh_to_dentry,
-	 *   types 1 and 2 should be used is that same way as the generic code.
-	 *   Type 255 means error.
-	 *
-	 * Lengths are in units of 4bytes, not bytes.
-	 */
-	struct dentry * (*fh_to_dentry)(struct super_block *sb, __u32 *fh, int len, int fhtype, int parent);
-	int (*dentry_to_fh)(struct dentry *, __u32 *fh, int *lenp, int need_parent);
 	int (*show_options)(struct seq_file *, struct vfsmount *);
 };
 
diff -Nru a/include/linux/ide.h b/include/linux/ide.h
--- a/include/linux/ide.h	Sat May 18 00:46:08 2002
+++ b/include/linux/ide.h	Sat May 18 00:46:08 2002
@@ -40,9 +40,6 @@
 
 /* Right now this is only needed by a promise controlled.
  */
-#ifndef SUPPORT_VLB_SYNC		/* 1 to support weird 32-bit chips */
-# define SUPPORT_VLB_SYNC	1	/* 0 to reduce kernel size */
-#endif
 #ifndef DISK_RECOVERY_TIME		/* off=0; on=access_delay_time */
 # define DISK_RECOVERY_TIME	0	/*  for hardware that needs it */
 #endif
@@ -74,8 +71,6 @@
  */
 #define DMA_PIO_RETRY	1	/* retrying in PIO */
 
-#define HWGROUP(drive)		(drive->channel->hwgroup)
-
 /*
  * Definitions for accessing IDE controller registers
  */
@@ -239,6 +234,19 @@
 
 #include <asm/ide.h>
 
+/* Currently only m68k, apus and m8xx need it */
+#ifdef ATA_ARCH_ACK_INTR
+# define ide_ack_intr(hwif) (hwif->hw.ack_intr ? hwif->hw.ack_intr(hwif) : 1)
+#else
+# define ide_ack_intr(hwif) (1)
+#endif
+
+/* Currently only Atari needs it */
+#ifndef ATA_ARCH_LOCK
+# define ide_release_lock(lock)		do {} while (0)
+# define ide_get_lock(lock, hdlr, data)	do {} while (0)
+#endif
+
 /*
  * If the arch-dependant ide.h did not declare/define any OUT_BYTE or IN_BYTE
  * functions, we make some defaults here. The only architecture currently
@@ -264,8 +272,6 @@
 #define ATA_SCSI	0x21
 #define ATA_NO_LUN      0x7f
 
-struct ide_settings_s;
-
 typedef union {
 	unsigned all			: 8;	/* all of the bits together */
 	struct {
@@ -326,16 +332,16 @@
 	 * magically just go away.
 	 */
 	request_queue_t	queue;	/* per device request queue */
+	struct request *rq;		/* current request */
 
 	unsigned long sleep;	/* sleep until this time */
 
-	u8 XXX_tune_req;			/* requested drive tuning setting */
-
-	byte     using_dma;		/* disk is using dma for read/write */
-	byte	 using_tcq;		/* disk is using queueing */
 	byte	 retry_pio;		/* retrying dma capable host in pio */
 	byte	 state;			/* retry state */
-	byte     dsc_overlap;		/* flag: DSC overlap */
+
+	unsigned using_dma	: 1;	/* disk is using dma for read/write */
+	unsigned using_tcq	: 1;	/* disk is using queueing */
+	unsigned dsc_overlap	: 1;	/* flag: DSC overlap */
 
 	unsigned waiting_for_dma: 1;	/* dma currently in progress */
 	unsigned busy		: 1;	/* currently doing revalidate_disk() */
@@ -379,7 +385,6 @@
 
 	void		*driver_data;	/* extra driver data */
 	devfs_handle_t	de;		/* directory for device */
-	struct ide_settings_s *settings;    /* ioctl entires */
 	char		driver_req[10];	/* requests specific driver */
 
 	int		last_lun;	/* last logical unit */
@@ -408,24 +413,56 @@
 	int		max_depth;
 } ide_drive_t;
 
+/*
+ * Status returned by various functions.
+ */
+typedef enum {
+	ide_stopped,	/* no drive operation was started */
+	ide_started,	/* a drive operation was started, and a handler was set */
+	ide_released	/* started and released bus */
+} ide_startstop_t;
+
+/*
+ *  Interrupt and timeout handler type.
+ */
+typedef ide_startstop_t (ata_handler_t)(struct ata_device *, struct request *);
+typedef int (ata_expiry_t)(struct ata_device *, struct request *);
+
 enum {
 	ATA_PRIMARY	= 0,
 	ATA_SECONDARY	= 1
 };
 
+enum {
+	IDE_BUSY,	/* awaiting an interrupt */
+	IDE_SLEEP,
+	IDE_DMA		/* DMA in progress */
+};
+
 struct ata_channel {
 	struct device	dev;		/* device handle */
 	int		unit;		/* channel number */
 
-	struct hwgroup_s *hwgroup;	/* actually (ide_hwgroup_t *) */
+	/* This lock is used to serialize requests on the same device queue or
+	 * between differen queues sharing the same irq line.
+	 */
+	spinlock_t *lock;
 
-	ide_ioreg_t	io_ports[IDE_NR_PORTS];	/* task file registers */
-	hw_regs_t	hw;		/* Hardware info */
+	ide_startstop_t (*handler)(struct ata_device *, struct request *);	/* irq handler, if active */
+	struct timer_list timer;				/* failsafe timer */
+	int (*expiry)(struct ata_device *, struct request *);	/* irq handler, if active */
+	unsigned long poll_timeout;				/* timeout value during polled operations */
+	struct ata_device *drive;				/* last serviced drive */
+
+	unsigned long active;		/* active processing request */
+
+	ide_ioreg_t io_ports[IDE_NR_PORTS];	/* task file registers */
+	hw_regs_t hw;				/* hardware info */
 #ifdef CONFIG_PCI
-	struct pci_dev	*pci_dev;	/* for pci chipsets */
+	struct pci_dev *pci_dev;		/* for pci chipsets */
 #endif
 	struct ata_device drives[MAX_DRIVES];	/* drive info */
-	struct gendisk	*gd;		/* gendisk structure */
+	struct gendisk *gd;			/* gendisk structure */
 
 	/*
 	 * Routines to tune PIO and DMA mode for drives.
@@ -433,7 +470,11 @@
 	 * A value of 255 indicates that the function should choose the optimal
 	 * mode itself.
 	 */
+
+	/* setup disk on a channel for a particular transfer mode */
 	void (*tuneproc) (struct ata_device *, byte pio);
+
+	/* setup the chipset timing for a particular transfer mode */
 	int (*speedproc) (struct ata_device *, byte pio);
 
 	/* tweaks hardware to select drive */
@@ -451,6 +492,9 @@
 	/* check host's drive quirk list */
 	int (*quirkproc) (struct ata_device *);
 
+	/* driver soft-power interface */
+	int (*busproc)(struct ata_device *, int);
+
 	/* CPU-polled transfer routines */
 	void (*ata_read)(struct ata_device *, void *, unsigned int);
 	void (*ata_write)(struct ata_device *, void *, unsigned int);
@@ -499,18 +543,14 @@
 	unsigned no_io_32bit	: 1;	/* disallow enabling 32bit I/O */
 	unsigned no_unmask	: 1;	/* disallow setting unmask bit */
 	unsigned auto_poll	: 1;	/* supports nop auto-poll */
-	byte		io_32bit;	/* 0=16-bit, 1=32-bit, 2/3=32bit+sync */
-	byte		unmask;		/* flag: okay to unmask other irqs */
-	byte		slow;		/* flag: slow data port */
+	unsigned unmask		: 1;	/* flag: okay to unmask other irqs */
+	unsigned slow		: 1;	/* flag: slow data port */
+	unsigned io_32bit	: 1;	/* 0=16-bit, 1=32-bit */
+	unsigned char bus_state;	/* power state of the IDE bus */
 
 #if (DISK_RECOVERY_TIME > 0)
-	unsigned long	last_time;	/* time when previous rq was done */
+	unsigned long last_time;	/* time when previous rq was done */
 #endif
-	/* driver soft-power interface */
-	int (*busproc)(struct ata_device *, int);
-	byte		bus_state;	/* power state of the IDE bus */
-
-	unsigned long poll_timeout; /* timeout value during polled operations */
 };
 
 /*
@@ -519,27 +559,8 @@
 extern int ide_register_hw(hw_regs_t *hw, struct ata_channel **hwifp);
 extern void ide_unregister(struct ata_channel *hwif);
 
-/*
- * Status returned by various functions.
- */
-typedef enum {
-	ide_stopped,	/* no drive operation was started */
-	ide_started,	/* a drive operation was started, and a handler was set */
-	ide_released	/* started and released bus */
-} ide_startstop_t;
-
-/*
- *  Interrupt and timeout handler type.
- */
-typedef ide_startstop_t (ata_handler_t)(struct ata_device *, struct request *);
-typedef int (ata_expiry_t)(struct ata_device *, struct request *);
-
 struct ata_taskfile;
 
-#define IDE_BUSY	0	/* awaiting an interrupt */
-#define IDE_SLEEP	1
-#define IDE_DMA		2	/* DMA in progress */
-
 #define IDE_MAX_TAG	32
 
 #ifdef CONFIG_BLK_DEV_IDE_TCQ
@@ -563,56 +584,7 @@
 # define ata_can_queue(drive)		(1)
 #endif
 
-typedef struct hwgroup_s {
-	/* FIXME: We should look for busy request queues instead of looking at
-	 * the !NULL state of this field.
-	 */
-	ide_startstop_t (*handler)(struct ata_device *, struct request *);	/* irq handler, if active */
-	unsigned long flags;		/* BUSY, SLEEPING */
-	struct ata_device *XXX_drive;	/* current drive */
-	struct request *rq;		/* current request */
-	struct timer_list timer;	/* failsafe timer */
-	int (*expiry)(struct ata_device *, struct request *);	/* irq handler, if active */
-} ide_hwgroup_t;
-
-/* structure attached to the request for IDE_TASK_CMDS */
-
-/*
- * configurable drive settings
- */
-
-#define TYPE_INT	0
-#define TYPE_INTA	1
-#define TYPE_BYTE	2
-#define TYPE_SHORT	3
-
-#define SETTING_READ	(1 << 0)
-#define SETTING_WRITE	(1 << 1)
-#define SETTING_RW	(SETTING_READ | SETTING_WRITE)
-
-typedef int (ide_procset_t)(struct ata_device *, int);
-typedef struct ide_settings_s {
-	char			*name;
-	int			rw;
-	int			read_ioctl;
-	int			write_ioctl;
-	int			data_type;
-	int			min;
-	int			max;
-	int			mul_factor;
-	int			div_factor;
-	void			*data;
-	ide_procset_t		*set;
-	int			auto_remove;
-	struct ide_settings_s	*next;
-} ide_settings_t;
-
-extern void ide_add_setting(struct ata_device *, const char *, int, int, int, int, int, int, int, int, void *, ide_procset_t *);
-extern void ide_remove_setting(struct ata_device *, char *);
-extern int ide_read_setting(struct ata_device *, ide_settings_t *);
-extern int ide_write_setting(struct ata_device *, ide_settings_t *, int);
-extern void ide_add_generic_settings(struct ata_device *);
-
+/* FIXME: kill this as soon as possible */
 #define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) return 0;
 
 /*
@@ -683,19 +655,10 @@
 /*
  * Error reporting, in human readable form (luxurious, but a memory hog).
  */
-extern byte ide_dump_status(struct ata_device *, const char *, byte);
+extern u8 ide_dump_status(struct ata_device *, struct request *rq, const char *, u8);
 
-/*
- * ide_error() takes action based on the error returned by the controller.
- * The caller should return immediately after invoking this.
- */
-extern ide_startstop_t ide_error(struct ata_device *, const char *, byte);
-
-/*
- * Issue a simple drive command
- * The drive must be selected beforehand.
- */
-void ide_cmd(struct ata_device *, byte, byte, ata_handler_t);
+extern ide_startstop_t ide_error(struct ata_device *, struct request *rq,
+		const char *, byte);
 
 /*
  * ide_fixstring() cleans up and (optionally) byte-swaps a text string,
@@ -713,7 +676,9 @@
  * caller should return the updated value of "startstop" in this case.
  * "startstop" is unchanged when the function returns 0;
  */
-extern int ide_wait_stat(ide_startstop_t *, struct ata_device *, byte, byte, unsigned long);
+extern int ide_wait_stat(ide_startstop_t *,
+		struct ata_device *, struct request *rq,
+		byte, byte, unsigned long);
 
 extern int ide_wait_noerr(struct ata_device *, byte, byte, unsigned long);
 
@@ -744,7 +709,6 @@
  */
 typedef enum {
 	ide_wait,	/* insert rq at end of list, and wait for it */
-	ide_next,	/* insert rq immediately after current request */
 	ide_preempt,	/* insert rq in front of current request */
 	ide_end		/* insert rq at end of list, but don't wait for it */
 } ide_action_t;
@@ -759,7 +723,7 @@
 /*
  * Clean up after success/failure of an explicit drive cmd.
  */
-extern void ide_end_drive_cmd(struct ata_device *, byte, byte);
+extern void ide_end_drive_cmd(struct ata_device *, struct request *, u8, u8);
 
 struct ata_taskfile {
 	struct hd_drive_task_hdr taskfile;
@@ -794,21 +758,14 @@
 
 void ide_delay_50ms(void);
 
-extern byte ide_auto_reduce_xfer(struct ata_device *);
 extern void ide_fix_driveid(struct hd_driveid *id);
 extern int ide_driveid_update(struct ata_device *);
-extern int ide_ata66_check(struct ata_device *, struct ata_taskfile *);
 extern int ide_config_drive_speed(struct ata_device *, byte);
 extern byte eighty_ninty_three(struct ata_device *);
-extern int set_transfer(struct ata_device *, struct ata_taskfile *);
 
 extern int system_bus_speed;
 
-/*
- * ide_stall_queue() can be used by a drive to give excess bandwidth back
- * to the hwgroup by sleeping for timeout jiffies.
- */
-void ide_stall_queue(struct ata_device *, unsigned long);
+extern void ide_stall_queue(struct ata_device *, unsigned long);
 
 /*
  * CompactFlash cards and their brethern pretend to be removable hard disks,
@@ -863,22 +820,55 @@
 
 void __init ide_scan_pcibus(int scan_direction);
 #endif
+
+static inline void udma_enable(struct ata_device *drive, int on, int verbose)
+{
+	drive->channel->udma_enable(drive, on, verbose);
+}
+
+static inline int udma_start(struct ata_device *drive, struct request *rq)
+{
+	return drive->channel->udma_start(drive, rq);
+}
+
+static inline int udma_stop(struct ata_device *drive)
+{
+	return drive->channel->udma_stop(drive);
+}
+
+static inline int udma_read(struct ata_device *drive, struct request *rq)
+{
+	return drive->channel->udma_read(drive, rq);
+}
+
+static inline int udma_write(struct ata_device *drive, struct request *rq)
+{
+	return drive->channel->udma_write(drive, rq);
+}
+
+static inline int udma_irq_status(struct ata_device *drive)
+{
+	return drive->channel->udma_irq_status(drive);
+}
+
+static inline void udma_timeout(struct ata_device *drive)
+{
+	return drive->channel->udma_timeout(drive);
+}
+
+static inline void udma_irq_lost(struct ata_device *drive)
+{
+	return drive->channel->udma_irq_lost(drive);
+}
+
 #ifdef CONFIG_BLK_DEV_IDEDMA
 
 extern int udma_new_table(struct ata_channel *, struct request *);
 extern void udma_destroy_table(struct ata_channel *);
 extern void udma_print(struct ata_device *);
 
-extern void udma_enable(struct ata_device *, int, int);
 extern int udma_black_list(struct ata_device *);
 extern int udma_white_list(struct ata_device *);
-extern void udma_timeout(struct ata_device *);
-extern void udma_irq_lost(struct ata_device *);
-extern int udma_start(struct ata_device *, struct request *rq);
-extern int udma_stop(struct ata_device *);
-extern int udma_read(struct ata_device *, struct request *rq);
-extern int udma_write(struct ata_device *, struct request *rq);
-extern int udma_irq_status(struct ata_device *);
 
 extern int ata_do_udma(unsigned int reading, struct ata_device *drive, struct request *rq);
 
diff -Nru a/include/linux/init_task.h b/include/linux/init_task.h
--- a/include/linux/init_task.h	Sat May 18 00:46:08 2002
+++ b/include/linux/init_task.h	Sat May 18 00:46:08 2002
@@ -45,8 +45,8 @@
     thread_info:	&init_thread_info,				\
     flags:		0,						\
     lock_depth:		-1,						\
-    prio:		120,						\
-    static_prio:	120,						\
+    prio:		MAX_PRIO-20,					\
+    static_prio:	MAX_PRIO-20,					\
     policy:		SCHED_OTHER,					\
     cpus_allowed:	-1,						\
     mm:			NULL,						\
diff -Nru a/include/linux/input.h b/include/linux/input.h
--- a/include/linux/input.h	Sat May 18 00:46:09 2002
+++ b/include/linux/input.h	Sat May 18 00:46:09 2002
@@ -325,6 +325,9 @@
 
 #define KEY_UNKNOWN		240
 
+#define KEY_BRIGHTNESSDOWN	224
+#define KEY_BRIGHTNESSUP	225
+
 #define BTN_MISC		0x100
 #define BTN_0			0x100
 #define BTN_1			0x101
diff -Nru a/include/linux/isdn/capilli.h b/include/linux/isdn/capilli.h
--- a/include/linux/isdn/capilli.h	Sat May 18 00:46:09 2002
+++ b/include/linux/isdn/capilli.h	Sat May 18 00:46:09 2002
@@ -12,7 +12,10 @@
 #ifndef __CAPILLI_H__
 #define __CAPILLI_H__
 
+#include <linux/kernel.h>
 #include <linux/list.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
 
 typedef struct capiloaddatapart {
 	int user;		/* data in userspace ? */
@@ -60,10 +63,6 @@
         void (*handle_capimsg)(struct capi_ctr * card,
 			   	__u16 appl, struct sk_buff *skb);
 
-        void (*new_ncci)(struct capi_ctr * card,
-			   	__u16 appl, __u32 ncci, __u32 winsize);
-        void (*free_ncci)(struct capi_ctr * card, __u16 appl, __u32 ncci);
-
 	/* management information for kcapi */
 
 	unsigned long nrecvctlpkt;
@@ -81,11 +80,10 @@
 	char revision[32];
 	int (*load_firmware)(struct capi_ctr *, capiloaddata *);
 	void (*reset_ctr)(struct capi_ctr *);
-	void (*remove_ctr)(struct capi_ctr *);
 	void (*register_appl)(struct capi_ctr *, __u16 appl,
 			      capi_register_params *);
 	void (*release_appl)(struct capi_ctr *, __u16 appl);
-	void (*send_message)(struct capi_ctr *, struct sk_buff *skb);
+	u16  (*send_message)(struct capi_ctr *, struct sk_buff *skb);
 	
 	char *(*procinfo)(struct capi_ctr *);
 	int (*ctr_read_proc)(char *page, char **start, off_t off,
@@ -93,8 +91,6 @@
 	int (*driver_read_proc)(char *page, char **start, off_t off,
 				int count, int *eof, struct capi_driver *driver);
 	
-	int (*add_card)(struct capi_driver *driver, capicardparams *data);
-	
 	/* intitialized by kcapi */
 	struct list_head contr_head;		/* list of controllers */
 	struct list_head driver_list;
@@ -108,5 +104,17 @@
 
 struct capi_ctr *attach_capi_ctr(struct capi_driver *driver, char *name, void *data);
 int detach_capi_ctr(struct capi_ctr *);
+
+
+
+// ---------------------------------------------------------------------------
+// library functions for use by hardware controller drivers
+
+void capilib_new_ncci(struct list_head *head, u16 applid, u32 ncci, u32 winsize);
+void capilib_free_ncci(struct list_head *head, u16 applid, u32 ncci);
+void capilib_release_appl(struct list_head *head, u16 applid);
+void capilib_release(struct list_head *head);
+void capilib_data_b3_conf(struct list_head *head, u16 applid, u32 ncci, u16 msgid);
+u16  capilib_data_b3_req(struct list_head *head, u16 applid, u32 ncci, u16 msgid);
 
 #endif				/* __CAPILLI_H__ */
diff -Nru a/include/linux/kernelcapi.h b/include/linux/kernelcapi.h
--- a/include/linux/kernelcapi.h	Sat May 18 00:46:08 2002
+++ b/include/linux/kernelcapi.h	Sat May 18 00:46:08 2002
@@ -32,7 +32,7 @@
 
 /* new ioctls >= 10 */
 #define KCAPI_CMD_TRACE		10
-#define KCAPI_CMD_ADDCARD	11	/* add card to named driver */
+#define KCAPI_CMD_ADDCARD	11	/* OBSOLETE */
 
 /* 
  * flag > 2 => trace also data
@@ -46,6 +46,8 @@
 
 
 #ifdef __KERNEL__
+
+#include <linux/skbuff.h>
 
 struct capi_interface {
 	__u16 (*capi_isinstalled) (void);
diff -Nru a/include/linux/mm.h b/include/linux/mm.h
--- a/include/linux/mm.h	Sat May 18 00:46:08 2002
+++ b/include/linux/mm.h	Sat May 18 00:46:08 2002
@@ -311,8 +311,6 @@
 extern void show_free_areas(void);
 extern void show_free_areas_node(pg_data_t *pgdat);
 
-extern void clear_page_tables(struct mm_struct *, unsigned long, int);
-
 extern int fail_writepage(struct page *);
 struct page * shmem_nopage(struct vm_area_struct * vma, unsigned long address, int unused);
 struct file *shmem_file_setup(char * name, loff_t size);
diff -Nru a/include/linux/pmu.h b/include/linux/pmu.h
--- a/include/linux/pmu.h	Sat May 18 00:46:08 2002
+++ b/include/linux/pmu.h	Sat May 18 00:46:08 2002
@@ -113,11 +113,12 @@
 #define PMU_IOC_HAS_ADB		_IOR('B', 4, sizeof(__u32*)) 
 /* out param: u32*	can_sleep: 0 or 1 */
 #define PMU_IOC_CAN_SLEEP	_IOR('B', 5, sizeof(__u32*)) 
+/* no param */
+#define PMU_IOC_GRAB_BACKLIGHT	_IOR('B', 6, 0) 
 
 #ifdef __KERNEL__
 
 extern int find_via_pmu(void);
-extern int via_pmu_start(void);
 
 extern int pmu_request(struct adb_request *req,
 		void (*done)(struct adb_request *), int nbytes, ...);
@@ -168,19 +169,41 @@
 
 /* priority levels in notifiers */
 #define SLEEP_LEVEL_VIDEO	100	/* Video driver (first wake) */
-#define SLEEP_LEVEL_SOUND	90	/* Sound driver */
-#define SLEEP_LEVEL_MEDIABAY	80	/* Media bay driver */
-#define SLEEP_LEVEL_BLOCK	70	/* IDE, SCSI */
-#define SLEEP_LEVEL_NET		60	/* bmac */
-#define SLEEP_LEVEL_ADB		50	/* ADB */
-#define SLEEP_LEVEL_MISC	30	/* Anything */
-#define SLEEP_LEVEL_LAST	0	/* Reserved for apm_emu */
+#define SLEEP_LEVEL_MEDIABAY	90	/* Media bay driver */
+#define SLEEP_LEVEL_BLOCK	80	/* IDE, SCSI */
+#define SLEEP_LEVEL_NET		70	/* bmac, gmac */
+#define SLEEP_LEVEL_MISC	60	/* Anything else */
+#define SLEEP_LEVEL_USERLAND	55	/* Reserved for apm_emu */
+#define SLEEP_LEVEL_ADB		50	/* ADB (async) */
+#define SLEEP_LEVEL_SOUND	40	/* Sound driver (blocking) */
 
 /* special register notifier functions */
 int pmu_register_sleep_notifier(struct pmu_sleep_notifier* notifier);
 int pmu_unregister_sleep_notifier(struct pmu_sleep_notifier* notifier);
 
-#endif /* CONFIG_PMAC_PBOOK */
+#define PMU_MAX_BATTERIES	2
+
+/* values for pmu_power_flags */
+#define PMU_PWR_AC_PRESENT	0x00000001
+
+/* values for pmu_battery_info.flags */
+#define PMU_BATT_PRESENT	0x00000001
+#define PMU_BATT_CHARGING	0x00000002
+
+struct pmu_battery_info
+{
+	unsigned int	flags;
+	unsigned int	charge;		/* current charge */
+	unsigned int	max_charge;	/* maximum charge */
+	signed int	current;	/* current, positive if charging */
+	unsigned int	voltage;	/* voltage */
+	unsigned int	time_remaining;	/* remaining time */
+};
 
+extern int pmu_battery_count;
+extern struct pmu_battery_info pmu_batteries[PMU_MAX_BATTERIES];
+extern unsigned int pmu_power_flags;
+
+#endif /* CONFIG_PMAC_PBOOK */
 
 #endif	/* __KERNEL__ */
diff -Nru a/include/linux/sched.h b/include/linux/sched.h
--- a/include/linux/sched.h	Sat May 18 00:46:08 2002
+++ b/include/linux/sched.h	Sat May 18 00:46:08 2002
@@ -39,7 +39,7 @@
 #define CLONE_FS	0x00000200	/* set if fs info shared between processes */
 #define CLONE_FILES	0x00000400	/* set if open files shared between processes */
 #define CLONE_SIGHAND	0x00000800	/* set if signal handlers and blocked signals shared */
-#define CLONE_PID	0x00001000	/* set if pid shared */
+#define CLONE_IDLETASK	0x00001000	/* set if new pid should be 0 (kernel only)*/
 #define CLONE_PTRACE	0x00002000	/* set if we want to let tracing continue on the child too */
 #define CLONE_VFORK	0x00004000	/* set if the parent wants the child to wake it up on mm_release */
 #define CLONE_PARENT	0x00008000	/* set if we want to have the same parent as the cloner */
@@ -206,7 +206,24 @@
 	spinlock_t		siglock;
 };
 
+/*
+ * Priority of a process goes from 0..MAX_PRIO-1, valid RT
+ * priority is 0..MAX_RT_PRIO-1, and SCHED_OTHER tasks are
+ * in the range MAX_RT_PRIO..MAX_PRIO-1. Priority values
+ * are inverted: lower p->prio value means higher priority.
+ *
+ * The MAX_RT_USER_PRIO value allows the actual maximum
+ * RT priority to be separate from the value exported to
+ * user-space.  This allows kernel threads to set their
+ * priority to a value higher than any user task. Note:
+ * MAX_RT_PRIO must not be smaller than MAX_USER_RT_PRIO.
+ */
 
+#define MAX_USER_RT_PRIO	100
+#define MAX_RT_PRIO		MAX_USER_RT_PRIO
+
+#define MAX_PRIO		(MAX_RT_PRIO + 40)
+ 
 /*
  * Some day this will be a full-fledged user tracking system..
  */
@@ -459,6 +476,11 @@
 
 #include <asm/current.h>
 
+/*
+ * The 64-bit value is not volatile - you MUST NOT read it
+ * without holding read_lock_irq(&xtime_lock)
+ */
+extern u64 jiffies_64;
 extern unsigned long volatile jiffies;
 extern unsigned long itimer_ticks;
 extern unsigned long itimer_next;
@@ -658,7 +680,7 @@
 extern task_t *child_reaper;
 
 extern int do_execve(char *, char **, char **, struct pt_regs *);
-extern int do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned long);
+extern struct task_struct *do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned long);
 
 extern void FASTCALL(add_wait_queue(wait_queue_head_t *q, wait_queue_t * wait));
 extern void FASTCALL(add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t * wait));
diff -Nru a/include/linux/umem.h b/include/linux/umem.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/linux/umem.h	Sat May 18 00:46:15 2002
@@ -0,0 +1,130 @@
+
+/*
+ * This file contains defines for the
+ *   Micro Memory MM5415
+ * family PCI Memory Module with Battery Backup.
+ *
+ * Copyright Micro Memory INC 2001.  All rights reserved.
+ * Release under the terms of the GNU GENERAL PUBLIC LICENSE version 2.
+ * See the file COPYING.
+ */
+
+#ifndef _DRIVERS_BLOCK_MM_H
+#define _DRIVERS_BLOCK_MM_H
+
+
+#define IRQ_TIMEOUT (1 * HZ)
+
+/* CSR register definition */
+#define MEMCTRLSTATUS_MAGIC	0x00
+#define  MM_MAGIC_VALUE		(unsigned char)0x59
+
+#define MEMCTRLSTATUS_BATTERY	0x04
+#define  BATTERY_1_DISABLED	0x01
+#define  BATTERY_1_FAILURE	0x02
+#define  BATTERY_2_DISABLED	0x04
+#define  BATTERY_2_FAILURE	0x08
+
+#define MEMCTRLSTATUS_MEMORY	0x07
+#define  MEM_128_MB		0xfe
+#define  MEM_256_MB		0xfc
+#define  MEM_512_MB		0xf8
+#define  MEM_1_GB		0xf0
+#define  MEM_2_GB		0xe0
+
+#define MEMCTRLCMD_LEDCTRL	0x08
+#define  LED_REMOVE		2
+#define  LED_FAULT		4
+#define  LED_POWER		6
+#define	 LED_FLIP		255
+#define  LED_OFF		0x00
+#define  LED_ON			0x01
+#define  LED_FLASH_3_5		0x02
+#define  LED_FLASH_7_0		0x03
+#define  LED_POWER_ON		0x00
+#define  LED_POWER_OFF		0x01
+#define  USER_BIT1		0x01
+#define  USER_BIT2		0x02
+
+#define MEMORY_INITIALIZED	USER_BIT1
+
+#define MEMCTRLCMD_ERRCTRL	0x0C
+#define  EDC_NONE_DEFAULT	0x00
+#define  EDC_NONE		0x01
+#define  EDC_STORE_READ		0x02
+#define  EDC_STORE_CORRECT	0x03
+
+#define MEMCTRLCMD_ERRCNT	0x0D
+#define MEMCTRLCMD_ERRSTATUS	0x0E
+
+#define ERROR_DATA_LOG		0x20
+#define ERROR_ADDR_LOG		0x28
+#define ERROR_COUNT		0x3D
+#define ERROR_SYNDROME		0x3E
+#define ERROR_CHECK		0x3F
+
+#define DMA_PCI_ADDR		0x40
+#define DMA_LOCAL_ADDR		0x48
+#define DMA_TRANSFER_SIZE	0x50
+#define DMA_DESCRIPTOR_ADDR	0x58
+#define DMA_SEMAPHORE_ADDR	0x60
+#define DMA_STATUS_CTRL		0x68
+#define  DMASCR_GO		0x00001
+#define  DMASCR_TRANSFER_READ	0x00002
+#define  DMASCR_CHAIN_EN	0x00004
+#define  DMASCR_SEM_EN		0x00010
+#define  DMASCR_DMA_COMP_EN	0x00020
+#define  DMASCR_CHAIN_COMP_EN	0x00040
+#define  DMASCR_ERR_INT_EN	0x00080
+#define  DMASCR_PARITY_INT_EN	0x00100
+#define  DMASCR_ANY_ERR		0x00800
+#define  DMASCR_MBE_ERR		0x01000
+#define  DMASCR_PARITY_ERR_REP	0x02000
+#define  DMASCR_PARITY_ERR_DET	0x04000
+#define  DMASCR_SYSTEM_ERR_SIG	0x08000
+#define  DMASCR_TARGET_ABT	0x10000
+#define  DMASCR_MASTER_ABT	0x20000
+#define  DMASCR_DMA_COMPLETE	0x40000
+#define  DMASCR_CHAIN_COMPLETE	0x80000
+
+/* 
+3.SOME PCs HAVE HOST BRIDGES WHICH APPARENTLY DO NOT CORRECTLY HANDLE 
+READ-LINE (0xE) OR READ-MULTIPLE (0xC) PCI COMMAND CODES DURING DMA 
+TRANSFERS. IN OTHER SYSTEMS THESE COMMAND CODES WILL CAUSE THE HOST BRIDGE 
+TO ALLOW LONGER BURSTS DURING DMA READ OPERATIONS. THE UPPER FOUR BITS 
+(31..28) OF THE DMA CSR HAVE BEEN MADE PROGRAMMABLE, SO THAT EITHER A 0x6, 
+AN 0xE OR A 0xC CAN BE WRITTEN TO THEM TO SET THE COMMAND CODE USED DURING 
+DMA READ OPERATIONS.
+*/
+#define        DMASCR_READ   0x60000000
+#define        DMASCR_READLINE   0xE0000000
+#define        DMASCR_READMULTI   0xC0000000
+
+
+#define DMASCR_ERROR_MASK	(DMASCR_MASTER_ABT | DMASCR_TARGET_ABT | DMASCR_SYSTEM_ERR_SIG | DMASCR_PARITY_ERR_DET | DMASCR_MBE_ERR | DMASCR_ANY_ERR)
+#define DMASCR_HARD_ERROR	(DMASCR_MASTER_ABT | DMASCR_TARGET_ABT | DMASCR_SYSTEM_ERR_SIG | DMASCR_PARITY_ERR_DET | DMASCR_MBE_ERR)
+
+#define WINDOWMAP_WINNUM	0x7B
+
+#define DMA_READ_FROM_HOST 0
+#define DMA_WRITE_TO_HOST 1
+
+struct mm_dma_desc {
+	u64	pci_addr;
+	u64	local_addr;
+	u32	transfer_size;
+	u32	zero1;
+	u64	next_desc_addr;
+	u64	sem_addr;
+	u32	control_bits;
+	u32	zero2;
+
+	dma_addr_t data_dma_handle;
+
+	/* Copy of the bits */
+	u64	sem_control_bits;
+} __attribute__((aligned(8)));
+
+#define PCI_VENDOR_ID_MICRO_MEMORY		0x1332
+#define PCI_DEVICE_ID_MICRO_MEMORY_5415CN	0x5415
+#endif
diff -Nru a/include/linux/usb.h b/include/linux/usb.h
--- a/include/linux/usb.h	Sat May 18 00:46:09 2002
+++ b/include/linux/usb.h	Sat May 18 00:46:09 2002
@@ -363,22 +363,6 @@
 extern int usb_root_hub_string(int id, int serial,
 		char *type, __u8 *data, int len);
 
-/*
- * As of USB 2.0, full/low speed devices are segregated into trees.
- * One type grows from USB 1.1 host controllers (OHCI, UHCI etc).
- * The other type grows from high speed hubs when they connect to
- * full/low speed devices using "Transaction Translators" (TTs).
- *
- * TTs should only be known to the hub driver, and high speed bus
- * drivers (only EHCI for now).  They affect periodic scheduling and
- * sometimes control/bulk error recovery.
- */
-struct usb_tt {
-	struct usb_device	*hub;	/* upstream highspeed hub */
-	int			multi;	/* true means one TT per port */
-};
-
-
 /* -------------------------------------------------------------------------- */
 
 /* This is arbitrary.
@@ -387,6 +371,8 @@
  */
 #define USB_MAXCHILDREN		(16)
 
+struct usb_tt;
+
 struct usb_device {
 	int		devnum;		/* Address on USB bus */
 	char		devpath [16];	/* Use in messages: /port/port/... */
@@ -440,6 +426,11 @@
 	struct usb_device *children[USB_MAXCHILDREN];
 };
 
+extern struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *);
+extern struct usb_device *usb_get_dev(struct usb_device *dev);
+extern void usb_free_dev(struct usb_device *);
+#define usb_put_dev usb_free_dev
+
 /* for when layers above USB add new non-USB drivers */
 extern void usb_scan_devices(void);
 
@@ -449,50 +440,6 @@
 /* for drivers using iso endpoints */
 extern int usb_get_current_frame_number (struct usb_device *usb_dev);
 
-/**
- * usb_inc_dev_use - record another reference to a device
- * @dev: the device being referenced
- *
- * Each live reference to a device should be refcounted.
- *
- * Drivers for USB interfaces should normally record such references in
- * their probe() methods, when they bind to an interface, and release
- * them usb_dec_dev_use(), in their disconnect() methods.
- */
-static inline void usb_inc_dev_use (struct usb_device *dev)
-{
-	atomic_inc (&dev->refcnt);
-}
-
-/**
- * usb_dec_dev_use - drop a reference to a device
- * @dev: the device no longer being referenced
- *
- * Each live reference to a device should be refcounted.
- *
- * Drivers for USB interfaces should normally release such references in
- * their disconnect() methods, and record them in probe().
- *
- * Note that driver disconnect() methods must guarantee that when they
- * return, all of their outstanding references to the device (and its
- * interfaces) are cleaned up.  That means that all pending URBs from
- * this driver must have completed, and that no more copies of the device
- * handle are saved in driver records (including other kernel threads).
- */
-static inline void usb_dec_dev_use (struct usb_device *dev)
-{
-	if (atomic_dec_and_test (&dev->refcnt)) {
-		/* May only go to zero when usbcore finishes
-		 * usb_disconnect() processing:  khubd or HCDs.
-		 *
-		 * If you hit this BUG() it's likely a problem
-		 * with some driver's disconnect() routine.
-		 */
-		BUG ();
-	}
-}
-
-
 /* used these for multi-interface device registration */
 extern int usb_find_interface_driver_for_ifnum(struct usb_device *dev, unsigned int ifnum);
 extern void usb_driver_claim_interface(struct usb_driver *driver,
@@ -1176,6 +1123,7 @@
  * appropriately.
  */
 
+/* NOTE:  these are not the standard USB_ENDPOINT_XFER_* values!! */
 #define PIPE_ISOCHRONOUS		0
 #define PIPE_INTERRUPT			1
 #define PIPE_CONTROL			2
diff -Nru a/include/math-emu/op-common.h b/include/math-emu/op-common.h
--- a/include/math-emu/op-common.h	Sat May 18 00:46:09 2002
+++ b/include/math-emu/op-common.h	Sat May 18 00:46:09 2002
@@ -765,23 +765,25 @@
   do {									\
     if (r)								\
       {									\
+        unsigned rtype ur_;						\
 	X##_c = FP_CLS_NORMAL;						\
 									\
 	if ((X##_s = (r < 0)))						\
 	  r = -r;							\
 									\
+	ur_ = (unsigned rtype) r;					\
 	if (rsize <= _FP_W_TYPE_SIZE)					\
-	  __FP_CLZ(X##_e, r);						\
+	  __FP_CLZ(X##_e, ur_);						\
 	else								\
-	  __FP_CLZ_2(X##_e, (_FP_W_TYPE)(r >> _FP_W_TYPE_SIZE), 	\
-		     (_FP_W_TYPE)r);					\
+	  __FP_CLZ_2(X##_e, (_FP_W_TYPE)(ur_ >> _FP_W_TYPE_SIZE), 	\
+		     (_FP_W_TYPE)ur_);					\
 	if (rsize < _FP_W_TYPE_SIZE)					\
 		X##_e -= (_FP_W_TYPE_SIZE - rsize);			\
 	X##_e = rsize - X##_e - 1;					\
 									\
 	if (_FP_FRACBITS_##fs < rsize && _FP_WFRACBITS_##fs < X##_e)	\
-	  __FP_FRAC_SRS_1(r, (X##_e - _FP_WFRACBITS_##fs + 1), rsize);	\
-	_FP_FRAC_DISASSEMBLE_##wc(X, ((unsigned rtype)r), rsize);	\
+	  __FP_FRAC_SRS_1(ur_, (X##_e - _FP_WFRACBITS_##fs + 1), rsize);\
+	_FP_FRAC_DISASSEMBLE_##wc(X, ur_, rsize);			\
 	if ((_FP_WFRACBITS_##fs - X##_e - 1) > 0)			\
 	  _FP_FRAC_SLL_##wc(X, (_FP_WFRACBITS_##fs - X##_e - 1));	\
       }									\
diff -Nru a/include/net/irda/irlmp.h b/include/net/irda/irlmp.h
--- a/include/net/irda/irlmp.h	Sat May 18 00:46:08 2002
+++ b/include/net/irda/irlmp.h	Sat May 18 00:46:08 2002
@@ -100,7 +100,7 @@
 	irda_queue_t queue;      /* Must be first */
 	magic_t magic;
 
-	int  connected;
+	unsigned long connected;	/* set_bit used on this */
 	int  persistent;
 
 	__u8 slsap_sel;   /* Source (this) LSAP address */
@@ -120,6 +120,21 @@
 };
 
 /*
+ *  Used for caching the last slsap->dlsap->handle mapping
+ *
+ * We don't need to keep/match the remote address in the cache because
+ * we are associated with a specific LAP (which implies it).
+ * Jean II
+ */
+typedef struct {
+	int valid;
+
+	__u8 slsap_sel;
+	__u8 dlsap_sel;
+	struct lsap_cb *lsap;
+} CACHE_ENTRY;
+
+/*
  *  Information about each registred IrLAP layer
  */
 struct lap_cb {
@@ -140,20 +155,16 @@
 	
 	struct qos_info *qos;  /* LAP QoS for this session */
 	struct timer_list idle_timer;
+	
+#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
+	/* The lsap cache was moved from struct irlmp_cb to here because
+	 * it must be associated with the specific LAP. Also, this
+	 * improves performance. - Jean II */
+	CACHE_ENTRY cache;  /* Caching last slsap->dlsap->handle mapping */
+#endif
 };
 
 /*
- *  Used for caching the last slsap->dlsap->handle mapping
- */
-typedef struct {
-	int valid;
-
-	__u8 slsap_sel;
-	__u8 dlsap_sel;
-	struct lsap_cb *lsap;
-} CACHE_ENTRY;
-
-/*
  *  Main structure for IrLMP
  */
 struct irlmp_cb {
@@ -166,9 +177,6 @@
 
 	int free_lsap_sel;
 
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
-	CACHE_ENTRY cache;  /* Caching last slsap->dlsap->handle mapping */
-#endif
 	struct timer_list discovery_timer;
 
  	hashbin_t *links;         /* IrLAP connection table */
diff -Nru a/include/net/irda/irttp.h b/include/net/irda/irttp.h
--- a/include/net/irda/irttp.h	Sat May 18 00:46:08 2002
+++ b/include/net/irda/irttp.h	Sat May 18 00:46:08 2002
@@ -139,7 +139,7 @@
 	__u32 tx_max_sdu_size; /* Max transmit user data size */
 
 	int close_pend;        /* Close, but disconnect_pend */
-	int disconnect_pend;   /* Disconnect, but still data to send */
+	unsigned long disconnect_pend; /* Disconnect, but still data to send */
 	struct sk_buff *disconnect_skb;
 };
 
diff -Nru a/include/net/iw_handler.h b/include/net/iw_handler.h
--- a/include/net/iw_handler.h	Sat May 18 00:46:08 2002
+++ b/include/net/iw_handler.h	Sat May 18 00:46:08 2002
@@ -245,7 +245,8 @@
 /* Wrapper level flags */
 #define IW_DESCR_FLAG_DUMP	0x0001	/* Not part of the dump command */
 #define IW_DESCR_FLAG_EVENT	0x0002	/* Generate an event on SET */
-#define IW_DESCR_FLAG_RESTRICT	0x0004	/* GET request is ROOT only */
+#define IW_DESCR_FLAG_RESTRICT	0x0004	/* GET : request is ROOT only */
+				/* SET : Omit payload from generated iwevent */
 /* Driver level flags */
 #define IW_DESCR_FLAG_WAIT	0x0100	/* Wait for driver event */
 
diff -Nru a/kernel/fork.c b/kernel/fork.c
--- a/kernel/fork.c	Sat May 18 00:46:08 2002
+++ b/kernel/fork.c	Sat May 18 00:46:08 2002
@@ -136,8 +136,8 @@
 	struct task_struct *p;
 	int pid;
 
-	if (flags & CLONE_PID)
-		return current->pid;
+	if (flags & CLONE_IDLETASK)
+		return 0;
 
 	spin_lock(&lastpid_lock);
 	if((++last_pid) & 0xffff8000) {
@@ -608,27 +608,18 @@
  * For an example that's using stack_top, see
  * arch/ia64/kernel/process.c.
  */
-int do_fork(unsigned long clone_flags, unsigned long stack_start,
-	    struct pt_regs *regs, unsigned long stack_size)
+struct task_struct *do_fork(unsigned long clone_flags,
+			    unsigned long stack_start,
+			    struct pt_regs *regs,
+			    unsigned long stack_size)
 {
 	int retval;
 	unsigned long flags;
-	struct task_struct *p;
+	struct task_struct *p = NULL;
 	struct completion vfork;
 
 	if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
-		return -EINVAL;
-
-	retval = -EPERM;
-
-	/* 
-	 * CLONE_PID is only allowed for the initial SMP swapper
-	 * calls
-	 */
-	if (clone_flags & CLONE_PID) {
-		if (current->pid)
-			goto fork_out;
-	}
+		return ERR_PTR(-EINVAL);
 
 	retval = -ENOMEM;
 	p = dup_task_struct(current);
@@ -768,8 +759,7 @@
 	 *
 	 * Let it rip!
 	 */
-	retval = p->pid;
-	p->tgid = retval;
+	p->tgid = p->pid;
 	INIT_LIST_HEAD(&p->thread_group);
 
 	/* Need tasklist lock for parent etc handling! */
@@ -807,9 +797,12 @@
 		 * COW overhead when the child exec()s afterwards.
 		 */
 		set_need_resched();
+	retval = 0;
 
 fork_out:
-	return retval;
+	if (retval)
+		return ERR_PTR(retval);
+	return p;
 
 bad_fork_cleanup_namespace:
 	exit_namespace(p);
diff -Nru a/kernel/ksyms.c b/kernel/ksyms.c
--- a/kernel/ksyms.c	Sat May 18 00:46:08 2002
+++ b/kernel/ksyms.c	Sat May 18 00:46:08 2002
@@ -471,6 +471,7 @@
 EXPORT_SYMBOL_GPL(set_cpus_allowed);
 #endif
 EXPORT_SYMBOL(jiffies);
+EXPORT_SYMBOL(jiffies_64);
 EXPORT_SYMBOL(xtime);
 EXPORT_SYMBOL(do_gettimeofday);
 EXPORT_SYMBOL(do_settimeofday);
diff -Nru a/kernel/sched.c b/kernel/sched.c
--- a/kernel/sched.c	Sat May 18 00:46:08 2002
+++ b/kernel/sched.c	Sat May 18 00:46:08 2002
@@ -24,22 +24,8 @@
 #include <linux/kernel_stat.h>
 
 /*
- * Priority of a process goes from 0 to 139. The 0-99
- * priority range is allocated to RT tasks, the 100-139
- * range is for SCHED_OTHER tasks. Priority values are
- * inverted: lower p->prio value means higher priority.
- * 
- * MAX_USER_RT_PRIO allows the actual maximum RT priority
- * to be separate from the value exported to user-space.
- * NOTE: MAX_RT_PRIO must not be smaller than MAX_USER_RT_PRIO.
- */
-#define MAX_RT_PRIO		100
-#define MAX_USER_RT_PRIO	100
-#define MAX_PRIO		(MAX_RT_PRIO + 40)
-
-/*
  * Convert user-nice values [ -20 ... 0 ... 19 ]
- * to static priority [ 100 ... 139 (MAX_PRIO-1) ],
+ * to static priority [ MAX_RT_PRIO..MAX_PRIO-1 ],
  * and back.
  */
 #define NICE_TO_PRIO(nice)	(MAX_RT_PRIO + (nice) + 20)
@@ -1138,8 +1124,8 @@
 	}
 	
 	/*
-	 * Valid priorities for SCHED_FIFO and SCHED_RR are 1..99, valid
-	 * priority for SCHED_OTHER is 0.
+	 * Valid priorities for SCHED_FIFO and SCHED_RR are
+	 * 1..MAX_USER_RT_PRIO, valid priority for SCHED_OTHER is 0.
 	 */
 	retval = -EINVAL;
 	if (lp.sched_priority < 0 || lp.sched_priority > MAX_USER_RT_PRIO-1)
diff -Nru a/kernel/sys.c b/kernel/sys.c
--- a/kernel/sys.c	Sat May 18 00:46:08 2002
+++ b/kernel/sys.c	Sat May 18 00:46:08 2002
@@ -679,8 +679,8 @@
 			wmb();
 		}
 		current->euid = euid;
-		current->fsuid = euid;
 	}
+	current->fsuid = current->euid;
 	if (suid != (uid_t) -1)
 		current->suid = suid;
 
@@ -725,8 +725,8 @@
 			wmb();
 		}
 		current->egid = egid;
-		current->fsgid = egid;
 	}
+	current->fsgid = current->egid;
 	if (rgid != (gid_t) -1)
 		current->gid = rgid;
 	if (sgid != (gid_t) -1)
diff -Nru a/kernel/timer.c b/kernel/timer.c
--- a/kernel/timer.c	Sat May 18 00:46:08 2002
+++ b/kernel/timer.c	Sat May 18 00:46:08 2002
@@ -67,7 +67,12 @@
 
 extern int do_setitimer(int, struct itimerval *, struct itimerval *);
 
-unsigned long volatile jiffies;
+/*
+ * The 64-bit value is not volatile - you MUST NOT read it
+ * without holding read_lock_irq(&xtime_lock).
+ * jiffies is defined in the linker script...
+ */
+u64 jiffies_64;
 
 unsigned int * prof_buffer;
 unsigned long prof_len;
@@ -664,7 +669,7 @@
 
 void do_timer(struct pt_regs *regs)
 {
-	(*(unsigned long *)&jiffies)++;
+	jiffies_64++;
 #ifndef CONFIG_SMP
 	/* SMP process accounting uses the local APIC timer */
 
diff -Nru a/lib/zlib_inflate/inflate.c b/lib/zlib_inflate/inflate.c
--- a/lib/zlib_inflate/inflate.c	Sat May 18 00:46:13 2002
+++ b/lib/zlib_inflate/inflate.c	Sat May 18 00:46:13 2002
@@ -110,7 +110,7 @@
 
 #undef NEEDBYTE
 #undef NEXTBYTE
-#define NEEDBYTE {if(z->avail_in==0)goto empty;r=f;}
+#define NEEDBYTE {if(z->avail_in==0)goto empty;r=trv;}
 #define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++)
 
 int ZEXPORT zlib_inflate(z, f)
diff -Nru a/mm/memory.c b/mm/memory.c
--- a/mm/memory.c	Sat May 18 00:46:09 2002
+++ b/mm/memory.c	Sat May 18 00:46:09 2002
@@ -72,28 +72,10 @@
 mem_map_t * mem_map;
 
 /*
- * Called by TLB shootdown 
- */
-void __free_pte(pte_t pte)
-{
-	struct page *page;
-	unsigned long pfn = pte_pfn(pte);
-	if (!pfn_valid(pfn))
-		return;
-	page = pfn_to_page(pfn);
-	if (PageReserved(page))
-		return;
-	if (pte_dirty(pte))
-		set_page_dirty(page);		
-	free_page_and_swap_cache(page);
-}
-
-
-/*
  * Note: this doesn't free the actual pages themselves. That
  * has been handled earlier when unmapping all the memory regions.
  */
-static inline void free_one_pmd(pmd_t * dir)
+static inline void free_one_pmd(mmu_gather_t *tlb, pmd_t * dir)
 {
 	struct page *pte;
 
@@ -106,10 +88,10 @@
 	}
 	pte = pmd_page(*dir);
 	pmd_clear(dir);
-	pte_free(pte);
+	pte_free_tlb(tlb, pte);
 }
 
-static inline void free_one_pgd(pgd_t * dir)
+static inline void free_one_pgd(mmu_gather_t *tlb, pgd_t * dir)
 {
 	int j;
 	pmd_t * pmd;
@@ -125,26 +107,26 @@
 	pgd_clear(dir);
 	for (j = 0; j < PTRS_PER_PMD ; j++) {
 		prefetchw(pmd+j+(PREFETCH_STRIDE/16));
-		free_one_pmd(pmd+j);
+		free_one_pmd(tlb, pmd+j);
 	}
-	pmd_free(pmd);
+	pmd_free_tlb(tlb, pmd);
 }
 
 /*
  * This function clears all user-level page tables of a process - this
  * is needed by execve(), so that old pages aren't in the way.
+ *
+ * Must be called with pagetable lock held.
  */
-void clear_page_tables(struct mm_struct *mm, unsigned long first, int nr)
+void clear_page_tables(mmu_gather_t *tlb, unsigned long first, int nr)
 {
-	pgd_t * page_dir = mm->pgd;
+	pgd_t * page_dir = tlb->mm->pgd;
 
-	spin_lock(&mm->page_table_lock);
 	page_dir += first;
 	do {
-		free_one_pgd(page_dir);
+		free_one_pgd(tlb, page_dir);
 		page_dir++;
 	} while (--nr);
-	spin_unlock(&mm->page_table_lock);
 
 	/* keep the page table cache within bounds */
 	check_pgt_cache();
@@ -340,18 +322,17 @@
 	}
 }
 
-static inline int zap_pte_range(mmu_gather_t *tlb, pmd_t * pmd, unsigned long address, unsigned long size)
+static void zap_pte_range(mmu_gather_t *tlb, pmd_t * pmd, unsigned long address, unsigned long size)
 {
 	unsigned long offset;
 	pte_t *ptep;
-	int freed = 0;
 
 	if (pmd_none(*pmd))
-		return 0;
+		return;
 	if (pmd_bad(*pmd)) {
 		pmd_ERROR(*pmd);
 		pmd_clear(pmd);
-		return 0;
+		return;
 	}
 	ptep = pte_offset_map(pmd, address);
 	offset = address & ~PMD_MASK;
@@ -363,49 +344,63 @@
 		if (pte_none(pte))
 			continue;
 		if (pte_present(pte)) {
-			struct page *page;
 			unsigned long pfn = pte_pfn(pte);
+
+			pte_clear(ptep);
+			pfn = pte_pfn(pte);
 			if (pfn_valid(pfn)) {
-				page = pfn_to_page(pfn);
-				if (!PageReserved(page))
-					freed++;
+				struct page *page = pfn_to_page(pfn);
+				if (!PageReserved(page)) {
+					if (pte_dirty(pte))
+						set_page_dirty(page);
+					tlb_remove_page(tlb, page);
+				}
 			}
-			/* This will eventually call __free_pte on the pte. */
-			tlb_remove_page(tlb, ptep, address + offset);
 		} else {
 			free_swap_and_cache(pte_to_swp_entry(pte));
 			pte_clear(ptep);
 		}
 	}
 	pte_unmap(ptep-1);
-
-	return freed;
 }
 
-static inline int zap_pmd_range(mmu_gather_t *tlb, pgd_t * dir, unsigned long address, unsigned long size)
+static void zap_pmd_range(mmu_gather_t *tlb, pgd_t * dir, unsigned long address, unsigned long size)
 {
 	pmd_t * pmd;
 	unsigned long end;
-	int freed;
 
 	if (pgd_none(*dir))
-		return 0;
+		return;
 	if (pgd_bad(*dir)) {
 		pgd_ERROR(*dir);
 		pgd_clear(dir);
-		return 0;
+		return;
 	}
 	pmd = pmd_offset(dir, address);
 	end = address + size;
 	if (end > ((address + PGDIR_SIZE) & PGDIR_MASK))
 		end = ((address + PGDIR_SIZE) & PGDIR_MASK);
-	freed = 0;
 	do {
-		freed += zap_pte_range(tlb, pmd, address, end - address);
+		zap_pte_range(tlb, pmd, address, end - address);
 		address = (address + PMD_SIZE) & PMD_MASK; 
 		pmd++;
 	} while (address < end);
-	return freed;
+}
+
+void unmap_page_range(mmu_gather_t *tlb, struct vm_area_struct *vma, unsigned long address, unsigned long end)
+{
+	pgd_t * dir;
+
+	if (address >= end)
+		BUG();
+	dir = pgd_offset(vma->vm_mm, address);
+	tlb_start_vma(tlb, vma);
+	do {
+		zap_pmd_range(tlb, dir, address, end - address);
+		address = (address + PGDIR_SIZE) & PGDIR_MASK;
+		dir++;
+	} while (address && (address < end));
+	tlb_end_vma(tlb, vma);
 }
 
 /*
@@ -417,7 +412,6 @@
 	mmu_gather_t *tlb;
 	pgd_t * dir;
 	unsigned long start = address, end = address + size;
-	int freed = 0;
 
 	dir = pgd_offset(mm, address);
 
@@ -432,25 +426,10 @@
 		BUG();
 	spin_lock(&mm->page_table_lock);
 	flush_cache_range(vma, address, end);
-	tlb = tlb_gather_mmu(vma);
 
-	do {
-		freed += zap_pmd_range(tlb, dir, address, end - address);
-		address = (address + PGDIR_SIZE) & PGDIR_MASK;
-		dir++;
-	} while (address && (address < end));
-
-	/* this will flush any remaining tlb entries */
+	tlb = tlb_gather_mmu(mm);
+	unmap_page_range(tlb, vma, address, end);
 	tlb_finish_mmu(tlb, start, end);
-
-	/*
-	 * Update rss for the mm_struct (not necessarily current->mm)
-	 * Notice that rss is an unsigned long.
-	 */
-	if (mm->rss > freed)
-		mm->rss -= freed;
-	else
-		mm->rss = 0;
 	spin_unlock(&mm->page_table_lock);
 }
 
diff -Nru a/mm/mmap.c b/mm/mmap.c
--- a/mm/mmap.c	Sat May 18 00:46:08 2002
+++ b/mm/mmap.c	Sat May 18 00:46:08 2002
@@ -17,7 +17,10 @@
 
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
-#include <asm/tlbflush.h>
+#include <asm/tlb.h>
+
+extern void unmap_page_range(mmu_gather_t *,struct vm_area_struct *vma, unsigned long address, unsigned long size);
+extern void clear_page_tables(mmu_gather_t *tlb, unsigned long first, int nr);
 
 /*
  * WARNING: the debugging will use recursive algorithms so never enable this
@@ -329,11 +332,11 @@
 static inline void vma_link(struct mm_struct * mm, struct vm_area_struct * vma, struct vm_area_struct * prev,
 			    rb_node_t ** rb_link, rb_node_t * rb_parent)
 {
-	lock_vma_mappings(vma);
 	spin_lock(&mm->page_table_lock);
+	lock_vma_mappings(vma);
 	__vma_link(mm, vma, prev, rb_link, rb_parent);
-	spin_unlock(&mm->page_table_lock);
 	unlock_vma_mappings(vma);
+	spin_unlock(&mm->page_table_lock);
 
 	mm->map_count++;
 	validate_mm(mm);
@@ -729,101 +732,6 @@
 	return vma;
 }
 
-/* Normal function to fix up a mapping
- * This function is the default for when an area has no specific
- * function.  This may be used as part of a more specific routine.
- * This function works out what part of an area is affected and
- * adjusts the mapping information.  Since the actual page
- * manipulation is done in do_mmap(), none need be done here,
- * though it would probably be more appropriate.
- *
- * By the time this function is called, the area struct has been
- * removed from the process mapping list, so it needs to be
- * reinserted if necessary.
- *
- * The 4 main cases are:
- *    Unmapping the whole area
- *    Unmapping from the start of the segment to a point in it
- *    Unmapping from an intermediate point to the end
- *    Unmapping between to intermediate points, making a hole.
- *
- * Case 4 involves the creation of 2 new areas, for each side of
- * the hole.  If possible, we reuse the existing area rather than
- * allocate a new one, and the return indicates whether the old
- * area was reused.
- */
-static struct vm_area_struct * unmap_fixup(struct mm_struct *mm, 
-	struct vm_area_struct *area, unsigned long addr, size_t len, 
-	struct vm_area_struct *extra)
-{
-	struct vm_area_struct *mpnt;
-	unsigned long end = addr + len;
-
-	area->vm_mm->total_vm -= len >> PAGE_SHIFT;
-	if (area->vm_flags & VM_LOCKED)
-		area->vm_mm->locked_vm -= len >> PAGE_SHIFT;
-
-	/* Unmapping the whole area. */
-	if (addr == area->vm_start && end == area->vm_end) {
-		if (area->vm_ops && area->vm_ops->close)
-			area->vm_ops->close(area);
-		if (area->vm_file)
-			fput(area->vm_file);
-		kmem_cache_free(vm_area_cachep, area);
-		return extra;
-	}
-
-	/* Work out to one of the ends. */
-	if (end == area->vm_end) {
-		/*
-		 * here area isn't visible to the semaphore-less readers
-		 * so we don't need to update it under the spinlock.
-		 */
-		area->vm_end = addr;
-		lock_vma_mappings(area);
-		spin_lock(&mm->page_table_lock);
-	} else if (addr == area->vm_start) {
-		area->vm_pgoff += (end - area->vm_start) >> PAGE_SHIFT;
-		/* same locking considerations of the above case */
-		area->vm_start = end;
-		lock_vma_mappings(area);
-		spin_lock(&mm->page_table_lock);
-	} else {
-	/* Unmapping a hole: area->vm_start < addr <= end < area->vm_end */
-		/* Add end mapping -- leave beginning for below */
-		mpnt = extra;
-		extra = NULL;
-
-		mpnt->vm_mm = area->vm_mm;
-		mpnt->vm_start = end;
-		mpnt->vm_end = area->vm_end;
-		mpnt->vm_page_prot = area->vm_page_prot;
-		mpnt->vm_flags = area->vm_flags;
-		mpnt->vm_raend = 0;
-		mpnt->vm_ops = area->vm_ops;
-		mpnt->vm_pgoff = area->vm_pgoff + ((end - area->vm_start) >> PAGE_SHIFT);
-		mpnt->vm_file = area->vm_file;
-		mpnt->vm_private_data = area->vm_private_data;
-		if (mpnt->vm_file)
-			get_file(mpnt->vm_file);
-		if (mpnt->vm_ops && mpnt->vm_ops->open)
-			mpnt->vm_ops->open(mpnt);
-		area->vm_end = addr;	/* Truncate area */
-
-		/* Because mpnt->vm_file == area->vm_file this locks
-		 * things correctly.
-		 */
-		lock_vma_mappings(area);
-		spin_lock(&mm->page_table_lock);
-		__insert_vm_struct(mm, mpnt);
-	}
-
-	__insert_vm_struct(mm, area);
-	spin_unlock(&mm->page_table_lock);
-	unlock_vma_mappings(area);
-	return extra;
-}
-
 /*
  * Try to free as many page directory entries as we can,
  * without having to work very hard at actually scanning
@@ -837,12 +745,13 @@
  * "prev", if it exists, points to a vma before the one
  * we just free'd - but there's no telling how much before.
  */
-static void free_pgtables(struct mm_struct * mm, struct vm_area_struct *prev,
+static void free_pgtables(mmu_gather_t *tlb, struct vm_area_struct *prev,
 	unsigned long start, unsigned long end)
 {
 	unsigned long first = start & PGDIR_MASK;
 	unsigned long last = end + PGDIR_SIZE - 1;
 	unsigned long start_index, end_index;
+	struct mm_struct *mm = tlb->mm;
 
 	if (!prev) {
 		prev = mm->mmap;
@@ -877,105 +786,205 @@
 	start_index = pgd_index(first);
 	end_index = pgd_index(last);
 	if (end_index > start_index) {
-		clear_page_tables(mm, start_index, end_index - start_index);
+		clear_page_tables(tlb, start_index, end_index - start_index);
 		flush_tlb_pgtables(mm, first & PGDIR_MASK, last & PGDIR_MASK);
 	}
 }
 
+/* Normal function to fix up a mapping
+ * This function is the default for when an area has no specific
+ * function.  This may be used as part of a more specific routine.
+ *
+ * By the time this function is called, the area struct has been
+ * removed from the process mapping list.
+ */
+static void unmap_vma(struct mm_struct *mm, struct vm_area_struct *area)
+{
+	size_t len = area->vm_end - area->vm_start;
+
+	area->vm_mm->total_vm -= len >> PAGE_SHIFT;
+	if (area->vm_flags & VM_LOCKED)
+		area->vm_mm->locked_vm -= len >> PAGE_SHIFT;
+
+	remove_shared_vm_struct(area);
+
+	if (area->vm_ops && area->vm_ops->close)
+		area->vm_ops->close(area);
+	if (area->vm_file)
+		fput(area->vm_file);
+	kmem_cache_free(vm_area_cachep, area);
+}
+
+
+/*
+ * Update the VMA and inode share lists.
+ *
+ * Ok - we have the memory areas we should free on the 'free' list,
+ * so release them, and do the vma updates.
+ */
+static void unmap_vma_list(struct mm_struct *mm,
+	struct vm_area_struct *mpnt)
+{
+	do {
+		struct vm_area_struct *next = mpnt->vm_next;
+		unmap_vma(mm, mpnt);
+		mpnt = next;
+	} while (mpnt != NULL);
+	validate_mm(mm);
+}
+
+/*
+ * Get rid of page table information in the indicated region.
+ *
+ * Called with the page table lock held.
+ */
+static void unmap_region(struct mm_struct *mm,
+	struct vm_area_struct *mpnt,
+	struct vm_area_struct *prev,
+	unsigned long start,
+	unsigned long end)
+{
+	mmu_gather_t *tlb;
+
+	tlb = tlb_gather_mmu(mm);
+
+	do {
+		unsigned long from, to;
+
+		from = start < mpnt->vm_start ? mpnt->vm_start : start;
+		to = end > mpnt->vm_end ? mpnt->vm_end : end;
+
+		unmap_page_range(tlb, mpnt, from, to);
+	} while ((mpnt = mpnt->vm_next) != NULL);
+
+	free_pgtables(tlb, prev, start, end);
+	tlb_finish_mmu(tlb, start, end);
+}
+
+/*
+ * Create a list of vma's touched by the unmap,
+ * removing them from the VM lists as we go..
+ *
+ * Called with the page_table_lock held.
+ */
+static struct vm_area_struct *touched_by_munmap(struct mm_struct *mm,
+	struct vm_area_struct *mpnt,
+	struct vm_area_struct *prev,
+	unsigned long end)
+{
+	struct vm_area_struct **npp, *touched;
+
+	npp = (prev ? &prev->vm_next : &mm->mmap);
+
+	touched = NULL;
+	do {
+		struct vm_area_struct *next = mpnt->vm_next;
+		mpnt->vm_next = touched;
+		touched = mpnt;
+		mm->map_count--;
+		rb_erase(&mpnt->vm_rb, &mm->mm_rb);
+		mpnt = next;
+	} while (mpnt && mpnt->vm_start < end);
+	*npp = mpnt;
+	mm->mmap_cache = NULL;	/* Kill the cache. */
+	return touched;
+}
+
+/*
+ * Split a vma into two pieces at address 'addr', the original vma
+ * will contain the first part, a new vma is allocated for the tail.
+ */
+static int splitvma(struct mm_struct *mm, struct vm_area_struct *mpnt, unsigned long addr)
+{
+	struct vm_area_struct *new;
+
+	if (mm->map_count >= MAX_MAP_COUNT)
+		return -ENOMEM;
+
+	new = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+	if (!new)
+		return -ENOMEM;
+
+	/* most fields are the same, copy all, and then fixup */
+	*new = *mpnt;
+
+	new->vm_start = addr;
+	new->vm_pgoff = mpnt->vm_pgoff + ((addr - mpnt->vm_start) >> PAGE_SHIFT);
+	new->vm_raend = 0;
+	if (mpnt->vm_file) {
+		struct file *file = mpnt->vm_file;
+		get_file(file);
+		if (mpnt->vm_flags & VM_DENYWRITE)
+			atomic_dec(&file->f_dentry->d_inode->i_writecount);
+	}
+
+	if (mpnt->vm_ops && mpnt->vm_ops->open)
+		mpnt->vm_ops->open(mpnt);
+	mpnt->vm_end = addr;	/* Truncate area */
+
+	spin_lock(&mm->page_table_lock);
+	lock_vma_mappings(mpnt);
+	__insert_vm_struct(mm, new);
+	unlock_vma_mappings(mpnt);
+	spin_unlock(&mm->page_table_lock);
+
+	return 0;
+}
+
 /* Munmap is split into 2 main parts -- this part which finds
  * what needs doing, and the areas themselves, which do the
  * work.  This now handles partial unmappings.
  * Jeremy Fitzhardine <jeremy@sw.oz.au>
  */
-int do_munmap(struct mm_struct *mm, unsigned long addr, size_t len)
+int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
 {
-	struct vm_area_struct *mpnt, *prev, **npp, *free, *extra;
+	unsigned long end;
+	struct vm_area_struct *mpnt, *prev, *last;
 
-	if ((addr & ~PAGE_MASK) || addr > TASK_SIZE || len > TASK_SIZE-addr)
+	if ((start & ~PAGE_MASK) || start > TASK_SIZE || len > TASK_SIZE-start)
 		return -EINVAL;
 
 	if ((len = PAGE_ALIGN(len)) == 0)
 		return -EINVAL;
 
-	/* Check if this memory area is ok - put it on the temporary
-	 * list if so..  The checks here are pretty simple --
-	 * every area affected in some way (by any overlap) is put
-	 * on the list.  If nothing is put on, nothing is affected.
-	 */
-	mpnt = find_vma_prev(mm, addr, &prev);
+	/* Find the first overlapping VMA */
+	mpnt = find_vma_prev(mm, start, &prev);
 	if (!mpnt)
 		return 0;
-	/* we have  addr < mpnt->vm_end  */
+	/* we have  start < mpnt->vm_end  */
 
-	if (mpnt->vm_start >= addr+len)
+	/* if it doesn't overlap, we have nothing.. */
+	end = start + len;
+	if (mpnt->vm_start >= end)
 		return 0;
 
-	/* If we'll make "hole", check the vm areas limit */
-	if ((mpnt->vm_start < addr && mpnt->vm_end > addr+len)
-	    && mm->map_count >= MAX_MAP_COUNT)
-		return -ENOMEM;
-
 	/*
-	 * We may need one additional vma to fix up the mappings ... 
-	 * and this is the last chance for an easy error exit.
+	 * If we need to split any vma, do it now to save pain later.
 	 */
-	extra = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
-	if (!extra)
-		return -ENOMEM;
-
-	npp = (prev ? &prev->vm_next : &mm->mmap);
-	free = NULL;
-	spin_lock(&mm->page_table_lock);
-	for ( ; mpnt && mpnt->vm_start < addr+len; mpnt = *npp) {
-		*npp = mpnt->vm_next;
-		mpnt->vm_next = free;
-		free = mpnt;
-		rb_erase(&mpnt->vm_rb, &mm->mm_rb);
+	if (start > mpnt->vm_start) {
+		if (splitvma(mm, mpnt, start))
+			return -ENOMEM;
+		prev = mpnt;
+		mpnt = mpnt->vm_next;
 	}
-	mm->mmap_cache = NULL;	/* Kill the cache. */
-	spin_unlock(&mm->page_table_lock);
 
-	/* Ok - we have the memory areas we should free on the 'free' list,
-	 * so release them, and unmap the page range..
-	 * If the one of the segments is only being partially unmapped,
-	 * it will put new vm_area_struct(s) into the address space.
-	 * In that case we have to be careful with VM_DENYWRITE.
-	 */
-	while ((mpnt = free) != NULL) {
-		unsigned long st, end, size;
-		struct file *file = NULL;
-
-		free = free->vm_next;
-
-		st = addr < mpnt->vm_start ? mpnt->vm_start : addr;
-		end = addr+len;
-		end = end > mpnt->vm_end ? mpnt->vm_end : end;
-		size = end - st;
-
-		if (mpnt->vm_flags & VM_DENYWRITE &&
-		    (st != mpnt->vm_start || end != mpnt->vm_end) &&
-		    (file = mpnt->vm_file) != NULL) {
-			atomic_dec(&file->f_dentry->d_inode->i_writecount);
-		}
-		remove_shared_vm_struct(mpnt);
-		mm->map_count--;
-
-		zap_page_range(mpnt, st, size);
-
-		/*
-		 * Fix the mapping, and free the old area if it wasn't reused.
-		 */
-		extra = unmap_fixup(mm, mpnt, st, size, extra);
-		if (file)
-			atomic_inc(&file->f_dentry->d_inode->i_writecount);
+	/* Does it split the last one? */
+	last = find_vma(mm, end);
+	if (last && end > last->vm_start) {
+		if (splitvma(mm, last, end))
+			return -ENOMEM;
 	}
-	validate_mm(mm);
 
-	/* Release the extra vma struct if it wasn't used */
-	if (extra)
-		kmem_cache_free(vm_area_cachep, extra);
+	/*
+	 * Remove the vma's, and unmap the actual pages
+	 */
+	spin_lock(&mm->page_table_lock);
+	mpnt = touched_by_munmap(mm, mpnt, prev, end);
+	unmap_region(mm, mpnt, prev, start, end);
+	spin_unlock(&mm->page_table_lock);
 
-	free_pgtables(mm, prev, addr, addr+len);
+	/* Fix up all other VM information */
+	unmap_vma_list(mm, mpnt);
 
 	return 0;
 }
@@ -1092,44 +1101,58 @@
 /* Release all mmaps. */
 void exit_mmap(struct mm_struct * mm)
 {
+	mmu_gather_t *tlb;
 	struct vm_area_struct * mpnt;
 
 	release_segments(mm);
 	spin_lock(&mm->page_table_lock);
+
+	tlb = tlb_gather_mmu(mm);
+
+	flush_cache_mm(mm);
+	mpnt = mm->mmap;
+	while (mpnt) {
+		unsigned long start = mpnt->vm_start;
+		unsigned long end = mpnt->vm_end;
+
+		mm->map_count--;
+		remove_shared_vm_struct(mpnt);
+		unmap_page_range(tlb, mpnt, start, end);
+		mpnt = mpnt->vm_next;
+	}
+
+	/* This is just debugging */
+	if (mm->map_count)
+		BUG();
+
+	clear_page_tables(tlb, FIRST_USER_PGD_NR, USER_PTRS_PER_PGD);
+	tlb_finish_mmu(tlb, FIRST_USER_PGD_NR*PGDIR_SIZE, USER_PTRS_PER_PGD*PGDIR_SIZE);
+
 	mpnt = mm->mmap;
 	mm->mmap = mm->mmap_cache = NULL;
 	mm->mm_rb = RB_ROOT;
 	mm->rss = 0;
-	spin_unlock(&mm->page_table_lock);
 	mm->total_vm = 0;
 	mm->locked_vm = 0;
 
-	flush_cache_mm(mm);
+	spin_unlock(&mm->page_table_lock);
+
+	/*
+	 * Walk the list again, actually closing and freeing it
+	 * without holding any MM locks.
+	 */
 	while (mpnt) {
 		struct vm_area_struct * next = mpnt->vm_next;
-		unsigned long start = mpnt->vm_start;
-		unsigned long end = mpnt->vm_end;
-		unsigned long size = end - start;
-
 		if (mpnt->vm_ops) {
 			if (mpnt->vm_ops->close)
 				mpnt->vm_ops->close(mpnt);
 		}
-		mm->map_count--;
-		remove_shared_vm_struct(mpnt);
-		zap_page_range(mpnt, start, size);
 		if (mpnt->vm_file)
 			fput(mpnt->vm_file);
 		kmem_cache_free(vm_area_cachep, mpnt);
 		mpnt = next;
 	}
-	flush_tlb_mm(mm);
-
-	/* This is just debugging */
-	if (mm->map_count)
-		BUG();
-
-	clear_page_tables(mm, FIRST_USER_PGD_NR, USER_PTRS_PER_PGD);
+		
 }
 
 /* Insert vm structure into process list sorted by address
diff -Nru a/net/core/wireless.c b/net/core/wireless.c
--- a/net/core/wireless.c	Sat May 18 00:46:14 2002
+++ b/net/core/wireless.c	Sat May 18 00:46:14 2002
@@ -28,11 +28,13 @@
  *
  * v3 - 19.12.01 - Jean II
  *	o Make sure we don't go out of standard_ioctl[] in ioctl_standard_call
- *	o Fix /proc/net/wireless to handle __u8 to __s8 change in iwqual
  *	o Add event dispatcher function
  *	o Add event description
  *	o Propagate events as rtnetlink IFLA_WIRELESS option
  *	o Generate event on selected SET requests
+ *
+ * v4 - 18.04.01 - Jean II
+ *	o Fix stupid off by one in iw_ioctl_description : IW_ESSID_MAX_SIZE + 1
  */
 
 /***************************** INCLUDES *****************************/
@@ -122,13 +124,13 @@
 	/* SIOCGIWSCAN */
 	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_SCAN_MAX_DATA, 0},
 	/* SIOCSIWESSID */
-	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, IW_DESCR_FLAG_EVENT},
+	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE + 1, IW_DESCR_FLAG_EVENT},
 	/* SIOCGIWESSID */
-	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, IW_DESCR_FLAG_DUMP},
+	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE + 1, IW_DESCR_FLAG_DUMP},
 	/* SIOCSIWNICKN */
-	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, 0},
+	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE + 1, 0},
 	/* SIOCGIWNICKN */
-	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, 0},
+	{ IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE + 1, 0},
 	/* -- hole -- */
 	{ IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
 	/* -- hole -- */
diff -Nru a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
--- a/net/ipv4/syncookies.c	Sat May 18 00:46:08 2002
+++ b/net/ipv4/syncookies.c	Sat May 18 00:46:08 2002
@@ -14,8 +14,6 @@
  *  Missing: IPv6 support. 
  */
 
-#include <linux/config.h>
-#if defined(CONFIG_SYN_COOKIES) 
 #include <linux/tcp.h>
 #include <linux/slab.h>
 #include <linux/random.h>
@@ -194,4 +192,3 @@
 	ret = get_cookie_sock(sk, skb, req, &rt->u.dst);
 out:	return ret;
 }
-#endif
diff -Nru a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
--- a/net/ipv4/tcp_ipv4.c	Sat May 18 00:46:09 2002
+++ b/net/ipv4/tcp_ipv4.c	Sat May 18 00:46:09 2002
@@ -1518,7 +1518,6 @@
 			NETDEBUG(if (net_ratelimit()) \
 				printk(KERN_DEBUG "TCP: drop open request from %u.%u.%u.%u/%u\n", \
 					NIPQUAD(saddr), ntohs(skb->h.th->source)));
-			TCP_INC_STATS_BH(TcpAttemptFails);
 			dst_release(dst);
 			goto drop_and_free;
 		}
diff -Nru a/net/irda/irlmp.c b/net/irda/irlmp.c
--- a/net/irda/irlmp.c	Sat May 18 00:46:14 2002
+++ b/net/irda/irlmp.c	Sat May 18 00:46:14 2002
@@ -93,9 +93,6 @@
 	irlmp->cachelog = hashbin_new(HB_GLOBAL);
 	
 	irlmp->free_lsap_sel = 0x10; /* Reserved 0x00-0x0f */
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
-	irlmp->cache.valid = FALSE;
-#endif
 	strcpy(sysctl_devname, "Linux");
 	
 	/* Do discovery every 3 seconds */
@@ -208,10 +205,6 @@
 	if (self->conn_skb)
 		dev_kfree_skb(self->conn_skb);
 
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
-	ASSERT(irlmp != NULL, return;);
-	irlmp->cache.valid = FALSE;
-#endif
 	kfree(self);
 }
 
@@ -247,6 +240,9 @@
 		}
 		/* Now, remove from the link */
 		lsap = hashbin_remove(lap->lsaps, (int) self, NULL);
+#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
+		lap->cache.valid = FALSE;
+#endif
 	}
 	self->lap = NULL;
 	/* Check if we found the LSAP! If not then try the unconnected lsaps */
@@ -292,6 +288,9 @@
 	lap->saddr = saddr;
 	lap->daddr = DEV_ADDR_ANY;
 	lap->lsaps = hashbin_new(HB_GLOBAL);
+#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
+	lap->cache.valid = FALSE;
+#endif
 
 	lap->lap_state = LAP_STANDBY;
 	
@@ -602,7 +601,7 @@
 
 	/* Make sure that we invalidate the cache */
 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
-	irlmp->cache.valid = FALSE;
+	new->lap->cache.valid = FALSE;
 #endif /* CONFIG_IRDA_CACHE_LAST_LSAP */
 
 	return new;
@@ -692,17 +691,16 @@
 		return;
 	}
 
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
-	irlmp->cache.valid = FALSE;
-#endif
-
 	/* 
 	 *  Remove association between this LSAP and the link it used 
 	 */
 	ASSERT(self->lap != NULL, return;);
 	ASSERT(self->lap->lsaps != NULL, return;);
-
+	
 	lsap = hashbin_remove(self->lap->lsaps, (int) self, NULL);
+#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
+	self->lap->cache.valid = FALSE;
+#endif
 
 	ASSERT(lsap != NULL, return;);
 	ASSERT(lsap == self, return;);
diff -Nru a/net/irda/irlmp_frame.c b/net/irda/irlmp_frame.c
--- a/net/irda/irlmp_frame.c	Sat May 18 00:46:14 2002
+++ b/net/irda/irlmp_frame.c	Sat May 18 00:46:14 2002
@@ -408,13 +408,14 @@
 }
 
 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
-inline void irlmp_update_cache(struct lsap_cb *self)
+static inline void irlmp_update_cache(struct lap_cb *lap,
+				      struct lsap_cb *lsap)
 {
 	/* Update cache entry */
-	irlmp->cache.dlsap_sel = self->dlsap_sel;
-	irlmp->cache.slsap_sel = self->slsap_sel;
-	irlmp->cache.lsap = self;
-	irlmp->cache.valid = TRUE;
+	lap->cache.dlsap_sel = lsap->dlsap_sel;
+	lap->cache.slsap_sel = lsap->slsap_sel;
+	lap->cache.lsap = lsap;
+	lap->cache.valid = TRUE;
 }
 #endif
 
@@ -423,6 +424,17 @@
  *
  *    Find handle assosiated with destination and source LSAP
  *
+ * Any IrDA connection (LSAP/TSAP) is uniquely identified by
+ * 3 parameters, the local lsap, the remote lsap and the remote address. 
+ * We may initiate multiple connections to the same remote service
+ * (they will have different local lsap), a remote device may initiate
+ * multiple connections to the same local service (they will have
+ * different remote lsap), or multiple devices may connect to the same
+ * service and may use the same remote lsap (and they will have
+ * different remote address).
+ * So, where is the remote address ? Each LAP connection is made with
+ * a single remote device, so imply a specific remote address.
+ * Jean II
  */
 static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap_sel,
 				       __u8 slsap_sel, int status,
@@ -436,11 +448,11 @@
 	 *  cache first to avoid the linear search
 	 */
 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
-	if ((irlmp->cache.valid) && 
-	    (irlmp->cache.slsap_sel == slsap_sel) && 
-	    (irlmp->cache.dlsap_sel == dlsap_sel)) 
+	if ((self->cache.valid) && 
+	    (self->cache.slsap_sel == slsap_sel) && 
+	    (self->cache.dlsap_sel == dlsap_sel)) 
 	{
-		return (irlmp->cache.lsap);
+		return (self->cache.lsap);
 	}
 #endif
 	lsap = (struct lsap_cb *) hashbin_get_first(queue);
@@ -458,7 +470,7 @@
 			lsap->dlsap_sel = dlsap_sel;
 			
 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
-			irlmp_update_cache(lsap);
+			irlmp_update_cache(self, lsap);
 #endif
 			return lsap;
 		}
@@ -469,7 +481,7 @@
 		    (lsap->dlsap_sel == dlsap_sel)) 
 		{
 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
-			irlmp_update_cache(lsap);
+			irlmp_update_cache(self, lsap);
 #endif
 			return lsap;
 		}
diff -Nru a/net/irda/irnet/irnet.h b/net/irda/irnet/irnet.h
--- a/net/irda/irnet/irnet.h	Sat May 18 00:46:14 2002
+++ b/net/irda/irnet/irnet.h	Sat May 18 00:46:14 2002
@@ -409,8 +409,8 @@
 
   /* ------------------------ IrTTP part ------------------------ */
   /* We create a pseudo "socket" over the IrDA tranport */
-  int			ttp_open;	/* Set when IrTTP is ready */
-  int			ttp_connect;	/* Set when IrTTP is connecting */
+  unsigned long		ttp_open;	/* Set when IrTTP is ready */
+  unsigned long		ttp_connect;	/* Set when IrTTP is connecting */
   struct tsap_cb *	tsap;		/* IrTTP instance (the connection) */
 
   char			rname[NICKNAME_MAX_LEN + 1];
diff -Nru a/net/irda/irnet/irnet_ppp.c b/net/irda/irnet/irnet_ppp.c
--- a/net/irda/irnet/irnet_ppp.c	Sat May 18 00:46:14 2002
+++ b/net/irda/irnet/irnet_ppp.c	Sat May 18 00:46:14 2002
@@ -860,7 +860,7 @@
       irda_irnet_connect(self);
 #endif /* CONNECT_IN_SEND */
 
-      DEBUG(PPP_INFO, "IrTTP not ready ! (%d-%d)\n",
+      DEBUG(PPP_INFO, "IrTTP not ready ! (%ld-%ld)\n",
 	    self->ttp_open, self->ttp_connect);
 
       /* Note : we can either drop the packet or block the packet.
diff -Nru a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
--- a/net/sched/sch_ingress.c	Sat May 18 00:46:14 2002
+++ b/net/sched/sch_ingress.c	Sat May 18 00:46:14 2002
@@ -146,8 +146,8 @@
 	 * Unlike normal "enqueue" functions, ingress_enqueue returns a
 	 * firewall FW_* code.
 	 */
-	switch (result) {
 #ifdef CONFIG_NET_CLS_POLICE
+	switch (result) {
 		case TC_POLICE_SHOT:
 			result = NF_DROP;
 			sch->stats.drops++;
@@ -160,7 +160,7 @@
 			sch->stats.bytes += skb->len;
 			result = NF_ACCEPT;
 			break;
-	}
+	};
 #else
 	sch->stats.packets++;
 	sch->stats.bytes += skb->len;
diff -Nru a/sound/oss/ac97_codec.c b/sound/oss/ac97_codec.c
--- a/sound/oss/ac97_codec.c	Sat May 18 00:46:08 2002
+++ b/sound/oss/ac97_codec.c	Sat May 18 00:46:08 2002
@@ -31,6 +31,8 @@
  **************************************************************************
  *
  * History
+ * Mar 28, 2002 Randolph Bentson <bentson@holmsjoen.com>
+ *	corrections to support WM9707 in ViewPad 1000
  * v0.4 Mar 15 2000 Ollie Lho
  *	dual codecs support verified with 4 channels output
  * v0.3 Feb 22 2000 Ollie Lho
@@ -59,7 +61,9 @@
 
 static int ac97_init_mixer(struct ac97_codec *codec);
 
-static int wolfson_init(struct ac97_codec * codec);
+static int wolfson_init00(struct ac97_codec * codec);
+static int wolfson_init03(struct ac97_codec * codec);
+static int wolfson_init04(struct ac97_codec * codec);
 static int tritech_init(struct ac97_codec * codec);
 static int tritech_maestro_init(struct ac97_codec * codec);
 static int sigmatel_9708_init(struct ac97_codec *codec);
@@ -88,7 +92,9 @@
  
 static struct ac97_ops null_ops = { NULL, NULL, NULL };
 static struct ac97_ops default_ops = { NULL, eapd_control, NULL };
-static struct ac97_ops wolfson_ops = { wolfson_init, NULL, NULL };
+static struct ac97_ops wolfson_ops00 = { wolfson_init00, NULL, NULL };
+static struct ac97_ops wolfson_ops03 = { wolfson_init03, NULL, NULL };
+static struct ac97_ops wolfson_ops04 = { wolfson_init04, NULL, NULL };
 static struct ac97_ops tritech_ops = { tritech_init, NULL, NULL };
 static struct ac97_ops tritech_m_ops = { tritech_maestro_init, NULL, NULL };
 static struct ac97_ops sigmatel_9708_ops = { sigmatel_9708_init, NULL, NULL };
@@ -114,6 +120,7 @@
 	{0x414B4D01, "Asahi Kasei AK4542",	&null_ops},
 	{0x414B4D02, "Asahi Kasei AK4543",	&null_ops},
 	{0x414C4710, "ALC200/200P",		&null_ops},
+	{0x414C4720, "ALC650",			&null_ops},
 	{0x43525900, "Cirrus Logic CS4297",	&default_ops},
 	{0x43525903, "Cirrus Logic CS4297",	&default_ops},
 	{0x43525913, "Cirrus Logic CS4297A rev A", &default_ops},
@@ -135,9 +142,9 @@
 	{0x54524106, "TriTech TR28026",		&null_ops},
 	{0x54524108, "TriTech TR28028",		&tritech_ops},
 	{0x54524123, "TriTech TR A5",		&null_ops},
-	{0x574D4C00, "Wolfson WM9704",		&wolfson_ops},
-	{0x574D4C03, "Wolfson WM9703/9704",	&wolfson_ops},
-	{0x574D4C04, "Wolfson WM9704 (quad)",	&wolfson_ops},
+	{0x574D4C00, "Wolfson WM9700A",		&wolfson_ops00},
+	{0x574D4C03, "Wolfson WM9703/WM9707",	&wolfson_ops03},
+	{0x574D4C04, "Wolfson WM9704M/WM9704Q",	&wolfson_ops04},
 	{0x83847600, "SigmaTel STAC????",	&null_ops},
 	{0x83847604, "SigmaTel STAC9701/3/4/5", &null_ops},
 	{0x83847605, "SigmaTel STAC9704",	&null_ops},
@@ -145,6 +152,7 @@
 	{0x83847609, "SigmaTel STAC9721/23",	&sigmatel_9721_ops},
 	{0x83847644, "SigmaTel STAC9744/45",	&sigmatel_9744_ops},
 	{0x83847656, "SigmaTel STAC9756/57",	&sigmatel_9744_ops},
+	{0x83847666, "SigmaTel STAC9750T",	&sigmatel_9744_ops},
 	{0x83847684, "SigmaTel STAC9783/84?",	&null_ops},
 	{0x57454301, "Winbond 83971D",		&null_ops},
 };
@@ -837,7 +845,38 @@
 }
 
 
-static int wolfson_init(struct ac97_codec * codec)
+static int wolfson_init00(struct ac97_codec * codec)
+{
+	/* This initialization is suspect, but not known to be wrong.
+	   It was copied from the initialization for the WM9704Q, but
+	   that same sequence is known to fail for the WM9707.  Thus
+	   this warning may help someone with hardware to test
+	   this code. */
+	codec->codec_write(codec, 0x72, 0x0808);
+	codec->codec_write(codec, 0x74, 0x0808);
+
+	// patch for DVD noise
+	codec->codec_write(codec, 0x5a, 0x0200);
+
+	// init vol as PCM vol
+	codec->codec_write(codec, 0x70,
+		codec->codec_read(codec, AC97_PCMOUT_VOL));
+
+	codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000);
+	return 0;
+}
+
+
+static int wolfson_init03(struct ac97_codec * codec)
+{
+	/* this is known to work for the ViewSonic ViewPad 1000 */
+	codec->codec_write(codec, 0x72, 0x0808);
+	codec->codec_write(codec, 0x20, 0x8000);
+	return 0;
+}
+
+
+static int wolfson_init04(struct ac97_codec * codec)
 {
 	codec->codec_write(codec, 0x72, 0x0808);
 	codec->codec_write(codec, 0x74, 0x0808);
