## Automatically generated incremental diff
## From:   linux-2.5.1-pre4
## To:     linux-2.5.1-pre5
## Robot:  $Id: make-incremental-diff,v 1.5 2001/12/01 08:32:36 hpa Exp $

diff -urN linux-2.5.1-pre4/Documentation/Changes linux/Documentation/Changes
--- linux-2.5.1-pre4/Documentation/Changes	Mon Sep 17 23:10:44 2001
+++ linux/Documentation/Changes	Sat Dec  1 00:42:26 2001
@@ -2,13 +2,13 @@
 =====
 
 This document is designed to provide a list of the minimum levels of
-software necessary to run the 2.4 kernels, as well as provide brief
+software necessary to run the 2.5 kernels, as well as provide brief
 instructions regarding any other "Gotchas" users may encounter when
-trying life on the Bleeding Edge.  If upgrading from a pre-2.2.x
-kernel, please consult the Changes file included with 2.2.x kernels for
+trying life on the Bleeding Edge.  If upgrading from a pre-2.4.x
+kernel, please consult the Changes file included with 2.4.x kernels for
 additional information; most of that information will not be repeated
 here.  Basically, this document assumes that your system is already
-functional and running at least 2.2.x kernels.
+functional and running at least 2.4.x kernels.
 
 This document is originally based on my "Changes" file for 2.0.x kernels
 and therefore owes credit to the same people as that file (Jared Mauch,
@@ -31,7 +31,7 @@
 Eine deutsche Version dieser Datei finden Sie unter
 <http://www.stefan-winter.de/Changes-2.4.0.txt>.
 
-Last updated: May 9, 2001
+Last updated: November 29, 2001
 
 Chris Ricker (kaboom@gatech.edu or chris.ricker@genetics.utah.edu).
 
@@ -43,14 +43,14 @@
 running, the suggested command should tell you.
 
 Again, keep in mind that this list assumes you are already
-functionally running a Linux 2.2 kernel.  Also, not all tools are
+functionally running a Linux 2.4 kernel.  Also, not all tools are
 necessary on all systems; obviously, if you don't have any PCMCIA (PC
 Card) hardware, for example, you probably needn't concern yourself
 with pcmcia-cs.
 
 o  Gnu C                  2.95.3                  # gcc --version
 o  Gnu make               3.77                    # make --version
-o  binutils               2.9.1.0.25              # ld -v
+o  binutils               2.9.5.0.24              # ld -v
 o  util-linux             2.10o                   # fdformat --version
 o  modutils               2.4.2                   # insmod -V
 o  e2fsprogs              1.19                    # tune2fs
@@ -70,9 +70,9 @@
 necessarily to users of other CPUs. Users of other CPUs should obtain
 information about their gcc version requirements from another source.
 
-The recommended compiler for the kernel is gcc 2.95.3 or .4, and it
+The recommended compiler for the kernel is gcc 2.95.x (x >= 3), and it
 should be used when you need absolute stability. You may use gcc 3.0.x
-instead if you wish, although it may cause problems. Later  versions of gcc 
+instead if you wish, although it may cause problems. Later versions of gcc 
 have not received much testing for Linux kernel compilation, and there are 
 almost certainly bugs (mainly, but not exclusively, in the kernel) that
 will need to be fixed in order to use these compilers. In any case, using
@@ -106,12 +106,6 @@
 your kernel.  This change does, however, mean that you need a recent
 release of binutils.
 
-If you can, upgrade to the latest 2.9.5 or 2.10 binutils release.  Older
-releases such as 2.8, 2.8.xx, and the FSF's 2.9.1 should be avoided if
-at all possible.  The later releases of 2.9.1.0.x (anything where x >= 22)
-can and do compile the kernel properly, but there are many benefits in
-upgrading to 2.9.5 or 2.10 if you're up to it.
-
 System utils
 ============
 
@@ -276,7 +270,7 @@
 
 gcc 2.95.3
 ----------
-o  <ftp://ftp.gnu.org/pub/gnu/gcc/gcc-2.95.3.tar.gz>
+o  <ftp://ftp.gnu.org/gnu/gcc/gcc-2.95.3.tar.gz>
 
 Gnu Make
 ********
@@ -287,21 +281,14 @@
 
 Binutils
 ********
-
-2.9.1 series
-------------
-o  <ftp://ftp.valinux.com/pub/support/hjl/binutils/2.9.1/binutils-2.9.1.0.25.tar.gz>
-
-2.9.5 and 2.10 series
----------------------
-o  <ftp://ftp.valinux.com/pub/support/hjl/binutils/>
+o  <ftp://ftp.kernel.org/pub/linux/devel/binutils/>
 
 System utilities
 ****************
 
 Util-linux
 ----------
-o  <ftp://ftp.kernel.org/pub/linux/utils/util-linux/util-linux-2.10o.tar.gz>
+o  <ftp://ftp.kernel.org/pub/linux/utils/util-linux/>
 
 Ksymoops
 --------
diff -urN linux-2.5.1-pre4/Documentation/Configure.help linux/Documentation/Configure.help
--- linux-2.5.1-pre4/Documentation/Configure.help	Sat Dec  1 00:41:31 2001
+++ linux/Documentation/Configure.help	Sat Dec  1 00:42:26 2001
@@ -17004,8 +17004,8 @@
   can be downloaded from:
   <http://developer.intel.com/technology/iapc/acpi/downloads.htm>.
 
-  The ACPI mailing list may also be of interest:
-  <http://phobos.fs.tum.de/acpi/index.html>.
+  The ACPI Sourceforge project may also be of interest:
+  <http://sf.net/projects/acpi/>
 
 Enable ACPI 2.0 with errata 1.3
 CONFIG_ACPI20
@@ -22281,11 +22281,6 @@
   no management frames, simple fixed header).
   Ultra is available as a special socket : socket(AF_IRDA, SOCK_DGRAM, 1);
 
-IrDA protocol options
-CONFIG_IRDA_OPTIONS
-  Say Y here if you want to configure any of the following IrDA
-  options.
-
 IrDA cache last LSAP
 CONFIG_IRDA_CACHE_LAST_LSAP
   Say Y here if you want IrLMP to cache the last LSAP used.  This
@@ -22297,46 +22292,34 @@
 IrDA Fast RRs
 CONFIG_IRDA_FAST_RR
   Say Y here is you want IrLAP to send fast RR (Receive Ready) frames
-  when acting as a primary station. This will make IrLAP send out a RR
-  frame immediately when receiving a frame if its own transmit queue
-  is currently empty. This will give a lot of speed improvement when
-  receiving much data since the secondary station will not have to
-  wait the max. turn around time before it is allowed to transmit the
-  next time. If the transmit queue of the secondary is also empty the
-  primary will back off waiting longer for sending out the RR frame
-  until the timeout reaches the normal value. Enabling this option
-  will make the IR-diode burn more power and thus reduce your battery
-  life.
+  when acting as a primary station.
+  Disabling this option will make latency over IrDA very bad. Enabling
+  this option will make the IrDA stack send more packet than strictly
+  necessary, thus reduce your battery life (but not that much).
+
+  Fast RR will make IrLAP send out a RR frame immediately when
+  receiving a frame if its own transmit queue is currently empty. This
+  will give a lot of speed improvement when receiving much data since
+  the secondary station will not have to wait the max. turn around
+  time (usually 500ms) before it is allowed to transmit the next time.
+  If the transmit queue of the secondary is also empty, the primary will
+  start backing-off before sending another RR frame, waiting longer
+  each time until the back-off reaches the max. turn around time.
+  This back-off increase in controlled via
+  /proc/sys/net/irda/fast_poll_increase
 
-  If unsure, say N.
+  If unsure, say Y.
 
 IrDA debugging information
 CONFIG_IRDA_DEBUG
   Say Y here if you want the IrDA subsystem to write debug information
   to your syslog. You can change the debug level in
   /proc/sys/net/irda/debug .
+  When this option is enabled, the IrDA also perform many extra internal
+  verifications which will usually prevent the kernel to crash in case of
+  bugs.
 
   If unsure, say Y (since it makes it easier to find the bugs).
-
-IrLAP compression support
-CONFIG_IRDA_COMPRESSION
-  Compression is _not_ part of the IrDA(tm) protocol specification,
-  but it's working great! Linux is the first to try out compression
-  support at the IrLAP layer. This means that you will only benefit
-  from compression if you are running a Linux <-> Linux configuration.
-
-  If you say Y here, you also need to say Y or M to a compression
-  protocol below.
-
-IrLAP Deflate compression
-CONFIG_IRDA_DEFLATE
-  Say Y here if you want to build support for the Deflate compression
-  protocol. The deflate compression (GZIP) is exactly
-  the same as the one used by the PPP protocol.
-
-  If you want to compile this compression support as a module, say M
-  here and read <file:Documentation/modules.txt>.  The module will be
-  called irda_deflate.o.
 
 IrLAN protocol
 CONFIG_IRLAN
diff -urN linux-2.5.1-pre4/Documentation/pm.txt linux/Documentation/pm.txt
--- linux-2.5.1-pre4/Documentation/pm.txt	Fri Apr  6 10:42:55 2001
+++ linux/Documentation/pm.txt	Sat Dec  1 00:42:26 2001
@@ -34,7 +34,7 @@
 system the associated daemon will exit gracefully.
 
   apmd:   http://worldvisions.ca/~apenwarr/apmd/
-  acpid:  http://phobos.fs.tum.de/acpi/
+  acpid:  http://acpid.sf.net/
 
 Driver Interface
 ----------------
@@ -260,7 +260,7 @@
 Q: Who do I contact for additional information about
    enabling power management for my specific driver/device?
 
-ACPI4Linux mailing list: acpi@phobos.fs.tum.de
+ACPI Development mailing list: acpi-devel@lists.sourceforge.net
 
 System Interface
 ----------------
diff -urN linux-2.5.1-pre4/MAINTAINERS linux/MAINTAINERS
--- linux-2.5.1-pre4/MAINTAINERS	Sat Dec  1 00:41:34 2001
+++ linux/MAINTAINERS	Sat Dec  1 00:42:26 2001
@@ -148,8 +148,8 @@
 ACPI
 P:	Andy Grover
 M:	andrew.grover@intel.com
-L:	acpi@phobos.fs.tum.de
-W:	http://phobos.fs.tum.de/acpi/index.html
+L:	acpi-devel@lists.sourceforge.net
+W:	http://sf.net/projects/acpi/
 S:	Maintained
 
 AD1816 SOUND DRIVER
diff -urN linux-2.5.1-pre4/Makefile linux/Makefile
--- linux-2.5.1-pre4/Makefile	Sat Dec  1 00:41:34 2001
+++ linux/Makefile	Sat Dec  1 00:42:26 2001
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 5
 SUBLEVEL = 1
-EXTRAVERSION =-pre4
+EXTRAVERSION =-pre5
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
diff -urN linux-2.5.1-pre4/README linux/README
--- linux-2.5.1-pre4/README	Sat Dec  1 00:41:34 2001
+++ linux/README	Sat Dec  1 00:42:26 2001
@@ -153,13 +153,12 @@
 
 COMPILING the kernel:
 
- - Make sure you have gcc-2.91.66 (egcs-1.1.2) available.  gcc 2.95.2 may
+ - Make sure you have gcc 2.95.3 available.  gcc 2.91.66 (egcs-1.1.2) may
    also work but is not as safe, and *gcc 2.7.2.3 is no longer supported*.
    Also remember to upgrade your binutils package (for as/ld/nm and company)
    if necessary. For more information, refer to ./Documentation/Changes.
 
-   Please note that you can still run a.out user programs with this
-   kernel.
+   Please note that you can still run a.out user programs with this kernel.
 
  - Do a "make bzImage" to create a compressed kernel image.  If you want
    to make a boot disk (without root filesystem or LILO), insert a floppy
diff -urN linux-2.5.1-pre4/arch/i386/defconfig linux/arch/i386/defconfig
--- linux-2.5.1-pre4/arch/i386/defconfig	Sat Dec  1 00:41:34 2001
+++ linux/arch/i386/defconfig	Sat Dec  1 00:42:26 2001
@@ -496,6 +496,9 @@
 # IrDA (infrared) support
 #
 # CONFIG_IRDA is not set
+CONFIG_IRDA_CACHE_LAST_LSAP=y
+CONFIG_IRDA_FAST_RR=y
+CONFIG_IRDA_DEBUG=y
 
 #
 # ISDN subsystem
diff -urN linux-2.5.1-pre4/arch/i386/kernel/apm.c linux/arch/i386/kernel/apm.c
--- linux-2.5.1-pre4/arch/i386/kernel/apm.c	Sat Dec  1 00:41:34 2001
+++ linux/arch/i386/kernel/apm.c	Sat Dec  1 00:42:26 2001
@@ -387,7 +387,7 @@
 static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
 static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
 static struct apm_user *	user_list;
-static spinlock_t		user_list_lock;
+static spinlock_t		user_list_lock = SPIN_LOCK_UNLOCKED;
 
 static char			driver_version[] = "1.15";	/* no spaces */
 
@@ -1054,9 +1054,9 @@
 {
 	struct apm_user *	as;
 
-	spin_lock( &user_list_lock );
+	spin_lock(&user_list_lock);
 	if (user_list == NULL)
-		return;
+		goto out;
 	for (as = user_list; as != NULL; as = as->next) {
 		if ((as == sender) || (!as->reader))
 			continue;
@@ -1085,8 +1085,9 @@
 			break;
 		}
 	}
-	spin_unlock( &user_list_lock );
 	wake_up_interruptible(&apm_waitqueue);
+out:
+	spin_unlock(&user_list_lock);
 }
 
 static void set_time(void)
@@ -1182,12 +1183,12 @@
 	send_event(APM_NORMAL_RESUME);
 	sti();
 	queue_event(APM_NORMAL_RESUME, NULL);
-	spin_lock( &user_list_lock );
+	spin_lock(&user_list_lock);
 	for (as = user_list; as != NULL; as = as->next) {
 		as->suspend_wait = 0;
 		as->suspend_result = ((err == APM_SUCCESS) ? 0 : -EIO);
 	}
-	spin_unlock( &user_list_lock );
+	spin_unlock(&user_list_lock);
 	ignore_normal_resume = 1;
 	wake_up_interruptible(&apm_suspend_waitqueue);
 	return err;
@@ -1534,7 +1535,7 @@
 		if (suspends_pending <= 0)
 			(void) suspend();
 	}
-  	spin_lock( &user_list_lock );
+  	spin_lock(&user_list_lock);
 	if (user_list == as)
 		user_list = as->next;
 	else {
@@ -1549,7 +1550,7 @@
 		else
 			as1->next = as->next;
 	}
-	spin_unlock( &user_list_lock );
+	spin_unlock(&user_list_lock);
 	kfree(as);
 	return 0;
 }
@@ -1578,10 +1579,10 @@
 	as->suser = capable(CAP_SYS_ADMIN);
 	as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE;
 	as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ;
-	spin_lock( &user_list_lock );
+	spin_lock(&user_list_lock);
 	as->next = user_list;
 	user_list = as;
-	spin_unlock( &user_list_lock );
+	spin_unlock(&user_list_lock);
 	filp->private_data = as;
 	return 0;
 }
diff -urN linux-2.5.1-pre4/arch/i386/kernel/dmi_scan.c linux/arch/i386/kernel/dmi_scan.c
--- linux-2.5.1-pre4/arch/i386/kernel/dmi_scan.c	Sun Nov 11 10:38:46 2001
+++ linux/arch/i386/kernel/dmi_scan.c	Sat Dec  1 00:42:26 2001
@@ -191,6 +191,7 @@
  *	corruption problems
  */ 
  
+#if 0
 static __init int disable_ide_dma(struct dmi_blacklist *d)
 {
 #ifdef CONFIG_BLK_DEV_IDE
@@ -203,6 +204,7 @@
 #endif	
 	return 0;
 }
+#endif
 
 /* 
  * Reboot options and system auto-detection code provided by
diff -urN linux-2.5.1-pre4/arch/i386/kernel/semaphore.c linux/arch/i386/kernel/semaphore.c
--- linux-2.5.1-pre4/arch/i386/kernel/semaphore.c	Fri Nov  9 13:58:02 2001
+++ linux/arch/i386/kernel/semaphore.c	Sat Dec  1 00:42:26 2001
@@ -238,31 +238,30 @@
  */
 #if defined(CONFIG_SMP)
 asm(
-"
-.align	4
-.globl	__write_lock_failed
-__write_lock_failed:
-	" LOCK "addl	$" RW_LOCK_BIAS_STR ",(%eax)
-1:	rep; nop
-	cmpl	$" RW_LOCK_BIAS_STR ",(%eax)
-	jne	1b
+".text\n"
+".align	4\n"
+".globl	__write_lock_failed\n"
+"__write_lock_failed:\n\t"
+	LOCK "addl	$" RW_LOCK_BIAS_STR ",(%eax)\n"
+"1:	rep; nop\n\t"
+	"cmpl	$" RW_LOCK_BIAS_STR ",(%eax)\n\t"
+	"jne	1b\n\t"
+	LOCK "subl	$" RW_LOCK_BIAS_STR ",(%eax)\n\t"
+	"jnz	__write_lock_failed\n\t"
+	"ret"
+);
 
-	" LOCK "subl	$" RW_LOCK_BIAS_STR ",(%eax)
-	jnz	__write_lock_failed
-	ret
-
-
-.align	4
-.globl	__read_lock_failed
-__read_lock_failed:
-	lock ; incl	(%eax)
-1:	rep; nop
-	cmpl	$1,(%eax)
-	js	1b
-
-	lock ; decl	(%eax)
-	js	__read_lock_failed
-	ret
-"
+asm(
+".text\n"
+".align	4\n"
+".globl	__read_lock_failed\n"
+"__read_lock_failed:\n\t"
+	LOCK "incl	(%eax)\n"
+"1:	rep; nop\n\t"
+	"cmpl	$1,(%eax)\n\t"
+	"js	1b\n\t"
+	LOCK "decl	(%eax)\n\t"
+	"js	__read_lock_failed\n\t"
+	"ret"
 );
 #endif
diff -urN linux-2.5.1-pre4/drivers/block/floppy.c linux/drivers/block/floppy.c
--- linux-2.5.1-pre4/drivers/block/floppy.c	Sat Dec  1 00:41:35 2001
+++ linux/drivers/block/floppy.c	Sat Dec  1 00:42:26 2001
@@ -2542,7 +2542,7 @@
 			break;
 		}
 #endif
-		size = bio->bi_size;;
+		size = bio->bi_size;
 		buffer = bio_data(bio);
 	}
 #ifdef FLOPPY_SANITY_CHECK
diff -urN linux-2.5.1-pre4/drivers/block/loop.c linux/drivers/block/loop.c
--- linux-2.5.1-pre4/drivers/block/loop.c	Sat Dec  1 00:41:35 2001
+++ linux/drivers/block/loop.c	Sat Dec  1 00:42:26 2001
@@ -315,10 +315,13 @@
 
 	pos = ((loff_t) bio->bi_sector << 9) + lo->lo_offset;
 
-	if (bio_rw(bio) == WRITE)
-		ret = lo_send(lo, bio, loop_get_bs(lo), pos);
-	else
-		ret = lo_receive(lo, bio, loop_get_bs(lo), pos);
+	do {
+		if (bio_rw(bio) == WRITE)
+			ret = lo_send(lo, bio, loop_get_bs(lo), pos);
+		else
+			ret = lo_receive(lo, bio, loop_get_bs(lo), pos);
+
+	} while (++bio->bi_idx < bio->bi_vcnt);
 
 	return ret;
 }
@@ -489,6 +492,24 @@
 	goto out;
 }
 
+static int do_bio_blockbacked(struct loop_device *lo, struct bio *bio,
+			      struct bio *rbh)
+{
+	unsigned long IV = loop_get_iv(lo, rbh->bi_sector);
+	struct bio_vec *to;
+	char *vto, *vfrom;
+	int ret = 0, i;
+
+	bio_for_each_segment(to, bio, i) {
+		vfrom = page_address(rbh->bi_io_vec[i].bv_page) + rbh->bi_io_vec[i].bv_offset;
+		vto = page_address(to->bv_page) + to->bv_offset;
+		ret |= lo_do_transfer(lo, bio_data_dir(bio), vto, vfrom,
+					to->bv_len, IV);
+	}
+
+	return ret;
+}
+
 static inline void loop_handle_bio(struct loop_device *lo, struct bio *bio)
 {
 	int ret;
@@ -501,10 +522,8 @@
 		bio_endio(bio, !ret, bio_sectors(bio));
 	} else {
 		struct bio *rbh = bio->bi_private;
-		unsigned long IV = loop_get_iv(lo, rbh->bi_sector);
 
-		ret = lo_do_transfer(lo, READ, bio_data(bio), bio_data(rbh),
-				     bio->bi_size, IV);
+		ret = do_bio_blockbacked(lo, bio, rbh);
 
 		bio_endio(rbh, !ret, bio_sectors(bio));
 		loop_put_buffer(bio);
diff -urN linux-2.5.1-pre4/drivers/block/paride/pcd.c linux/drivers/block/paride/pcd.c
--- linux-2.5.1-pre4/drivers/block/paride/pcd.c	Sat Oct 27 02:03:47 2001
+++ linux/drivers/block/paride/pcd.c	Sat Dec  1 00:42:26 2001
@@ -821,11 +821,11 @@
 
 	if (pcd_command(unit,rd_cmd,2048,"read block")) {
 		pcd_bufblk = -1; 
-		spin_lock_irqsave(&io_request_lock,saved_flags);
+		spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
 		pcd_busy = 0;
 		end_request(0);
 		do_pcd_request(NULL);
-		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+		spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
 		return;
 	}
 
@@ -845,11 +845,11 @@
 	pcd_retries = 0;
 	pcd_transfer();
 	if (!pcd_count) {
-		spin_lock_irqsave(&io_request_lock,saved_flags);
+		spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
 		end_request(1);
 		pcd_busy = 0;
 		do_pcd_request(NULL);
-		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+		spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
 		return;
 	}
 
@@ -868,19 +868,19 @@
 			pi_do_claimed(PI,pcd_start);
                         return;
                         }
-		spin_lock_irqsave(&io_request_lock,saved_flags);
+		spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
 		pcd_busy = 0;
 		pcd_bufblk = -1;
 		end_request(0);
 		do_pcd_request(NULL);
-		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+		spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
 		return;
 	}
 
 	do_pcd_read();
-	spin_lock_irqsave(&io_request_lock,saved_flags);
+	spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
 	do_pcd_request(NULL);
-	spin_unlock_irqrestore(&io_request_lock,saved_flags); 
+	spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags); 
 }
 
 /* the audio_ioctl stuff is adapted from sr_ioctl.c */
diff -urN linux-2.5.1-pre4/drivers/block/paride/pd.c linux/drivers/block/paride/pd.c
--- linux-2.5.1-pre4/drivers/block/paride/pd.c	Sat Dec  1 00:41:35 2001
+++ linux/drivers/block/paride/pd.c	Sat Dec  1 00:42:26 2001
@@ -838,8 +838,7 @@
 }
 
 static void do_pd_request (request_queue_t * q)
-
-{       struct buffer_head * bh;
+{
 	int	unit;
 
         if (pd_busy) return;
@@ -853,8 +852,6 @@
         pd_run = CURRENT->nr_sectors;
         pd_count = CURRENT->current_nr_sectors;
 
-	bh = CURRENT->bh;
-
         if ((pd_dev >= PD_DEVS) || 
 	    ((pd_block+pd_count) > pd_hd[pd_dev].nr_sects)) {
                 end_request(0);
@@ -878,9 +875,9 @@
 
 {	long	saved_flags;
 
-	spin_lock_irqsave(&io_request_lock,saved_flags);
+	spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
 	end_request(1);
-	if (!pd_run) {  spin_unlock_irqrestore(&io_request_lock,saved_flags);
+	if (!pd_run) {  spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
 			return; 
 	}
 	
@@ -896,7 +893,7 @@
 
 	pd_count = CURRENT->current_nr_sectors;
 	pd_buf = CURRENT->buffer;
-	spin_unlock_irqrestore(&io_request_lock,saved_flags);
+	spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
 }
 
 static void do_pd_read( void )
@@ -919,11 +916,11 @@
                         pi_do_claimed(PI,do_pd_read_start);
 			return;
                 }
-		spin_lock_irqsave(&io_request_lock,saved_flags);
+		spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pd_busy = 0;
 		do_pd_request(NULL);
-		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+		spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                 return;
         }
         pd_ide_command(unit,IDE_READ,pd_block,pd_run);
@@ -943,11 +940,11 @@
                         pi_do_claimed(PI,do_pd_read_start);
                         return;
                 }
-		spin_lock_irqsave(&io_request_lock,saved_flags);
+		spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pd_busy = 0;
 		do_pd_request(NULL);
-		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+		spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                 return;
             }
             pi_read_block(PI,pd_buf,512);
@@ -958,11 +955,11 @@
 	    if (!pd_count) pd_next_buf(unit);
         }
         pi_disconnect(PI);
-	spin_lock_irqsave(&io_request_lock,saved_flags);
+	spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
         end_request(1);
         pd_busy = 0;
 	do_pd_request(NULL);
-	spin_unlock_irqrestore(&io_request_lock,saved_flags);
+	spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
 }
 
 static void do_pd_write( void )
@@ -985,11 +982,11 @@
 			pi_do_claimed(PI,do_pd_write_start);
                         return;
                 }
-		spin_lock_irqsave(&io_request_lock,saved_flags);
+		spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pd_busy = 0;
 		do_pd_request(NULL);
-		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+		spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                 return;
         }
         pd_ide_command(unit,IDE_WRITE,pd_block,pd_run);
@@ -1001,11 +998,11 @@
                         pi_do_claimed(PI,do_pd_write_start);
                         return;
                 }
-		spin_lock_irqsave(&io_request_lock,saved_flags);
+		spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pd_busy = 0;
 		do_pd_request(NULL);
-                spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
 		return;
             }
             pi_write_block(PI,pd_buf,512);
@@ -1030,19 +1027,19 @@
                         pi_do_claimed(PI,do_pd_write_start);
                         return;
                 }
-		spin_lock_irqsave(&io_request_lock,saved_flags);
+		spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pd_busy = 0;
 		do_pd_request(NULL);
-		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+		spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                 return;
         }
         pi_disconnect(PI);
-	spin_lock_irqsave(&io_request_lock,saved_flags);
+	spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
         end_request(1);
         pd_busy = 0;
 	do_pd_request(NULL);
-	spin_unlock_irqrestore(&io_request_lock,saved_flags);
+	spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
 }
 
 /* end of pd.c */
diff -urN linux-2.5.1-pre4/drivers/block/paride/pf.c linux/drivers/block/paride/pf.c
--- linux-2.5.1-pre4/drivers/block/paride/pf.c	Sat Dec  1 00:41:35 2001
+++ linux/drivers/block/paride/pf.c	Sat Dec  1 00:42:26 2001
@@ -340,56 +340,6 @@
         }
 } 
 
-static inline int pf_new_segment(request_queue_t *q, struct request *req, int max_segments)
-{
-	if (max_segments > cluster)
-		max_segments = cluster;
-
-	if (req->nr_segments < max_segments) {
-		req->nr_segments++;
-		return 1;
-	}
-	return 0;
-}
-
-static int pf_back_merge_fn(request_queue_t *q, struct request *req, 
-			    struct buffer_head *bh, int max_segments)
-{
-	if (req->bhtail->b_data + req->bhtail->b_size == bh->b_data)
-		return 1;
-	return pf_new_segment(q, req, max_segments);
-}
-
-static int pf_front_merge_fn(request_queue_t *q, struct request *req, 
-			     struct buffer_head *bh, int max_segments)
-{
-	if (bh->b_data + bh->b_size == req->bh->b_data)
-		return 1;
-	return pf_new_segment(q, req, max_segments);
-}
-
-static int pf_merge_requests_fn(request_queue_t *q, struct request *req,
-				struct request *next, int max_segments)
-{
-	int total_segments = req->nr_segments + next->nr_segments;
-	int same_segment;
-
-	if (max_segments > cluster)
-		max_segments = cluster;
-
-	same_segment = 0;
-	if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data) {
-		total_segments--;
-		same_segment = 1;
-	}
-    
-	if (total_segments > max_segments)
-		return 0;
-
-	req->nr_segments = total_segments;
-	return 1;
-}
-
 int pf_init (void)      /* preliminary initialisation */
 
 {       int i;
@@ -409,9 +359,7 @@
         }
 	q = BLK_DEFAULT_QUEUE(MAJOR_NR);
 	blk_init_queue(q, DEVICE_REQUEST);
-	q->back_merge_fn = pf_back_merge_fn;
-	q->front_merge_fn = pf_front_merge_fn;
-	q->merge_requests_fn = pf_merge_requests_fn;
+	blk_queue_max_segments(q, cluster);
         read_ahead[MAJOR_NR] = 8;       /* 8 sector (4kB) read ahead */
         
 	for (i=0;i<PF_UNITS;i++) pf_blocksizes[i] = 1024;
@@ -893,8 +841,7 @@
 }
 
 static void do_pf_request (request_queue_t * q)
-
-{       struct buffer_head * bh;
+{
 	int unit;
 
         if (pf_busy) return;
@@ -907,8 +854,6 @@
         pf_run = CURRENT->nr_sectors;
         pf_count = CURRENT->current_nr_sectors;
 
-	bh = CURRENT->bh;
-
         if ((pf_unit >= PF_UNITS) || (pf_block+pf_count > PF.capacity)) {
                 end_request(0);
                 goto repeat;
@@ -931,9 +876,9 @@
 
 {	long	saved_flags;
 
-	spin_lock_irqsave(&io_request_lock,saved_flags);
+	spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
 	end_request(1);
-	if (!pf_run) { spin_unlock_irqrestore(&io_request_lock,saved_flags);
+	if (!pf_run) { spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
 		       return; 
 	}
 	
@@ -949,7 +894,7 @@
 
 	pf_count = CURRENT->current_nr_sectors;
 	pf_buf = CURRENT->buffer;
-	spin_unlock_irqrestore(&io_request_lock,saved_flags);
+	spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
 }
 
 static void do_pf_read( void )
@@ -973,11 +918,11 @@
                         pi_do_claimed(PI,do_pf_read_start);
 			return;
                 }
-		spin_lock_irqsave(&io_request_lock,saved_flags);
+		spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pf_busy = 0;
 		do_pf_request(NULL);
-		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+		spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                 return;
         }
 	pf_mask = STAT_DRQ;
@@ -999,11 +944,11 @@
                         pi_do_claimed(PI,do_pf_read_start);
                         return;
                 }
-		spin_lock_irqsave(&io_request_lock,saved_flags);
+		spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pf_busy = 0;
 		do_pf_request(NULL);
-		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+		spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                 return;
             }
             pi_read_block(PI,pf_buf,512);
@@ -1014,11 +959,11 @@
 	    if (!pf_count) pf_next_buf(unit);
         }
         pi_disconnect(PI);
-	spin_lock_irqsave(&io_request_lock,saved_flags); 
+	spin_lock_irqsave(&QUEUE->queue_lock,saved_flags); 
         end_request(1);
         pf_busy = 0;
 	do_pf_request(NULL);
-	spin_unlock_irqrestore(&io_request_lock,saved_flags);
+	spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
 }
 
 static void do_pf_write( void )
@@ -1040,11 +985,11 @@
                         pi_do_claimed(PI,do_pf_write_start);
 			return;
                 }
-		spin_lock_irqsave(&io_request_lock,saved_flags);
+		spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pf_busy = 0;
 		do_pf_request(NULL);
-		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+		spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                 return;
         }
 
@@ -1057,11 +1002,11 @@
                         pi_do_claimed(PI,do_pf_write_start);
                         return;
                 }
-		spin_lock_irqsave(&io_request_lock,saved_flags);
+		spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pf_busy = 0;
 		do_pf_request(NULL);
-		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+		spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                 return;
             }
             pi_write_block(PI,pf_buf,512);
@@ -1087,19 +1032,19 @@
 			pi_do_claimed(PI,do_pf_write_start);
                         return;
                 }
-		spin_lock_irqsave(&io_request_lock,saved_flags);
+		spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
                 end_request(0);
                 pf_busy = 0;
 		do_pf_request(NULL);
-		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+		spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
                 return;
         }
         pi_disconnect(PI);
-	spin_lock_irqsave(&io_request_lock,saved_flags);
+	spin_lock_irqsave(&QUEUE->queue_lock,saved_flags);
         end_request(1);
         pf_busy = 0;
 	do_pf_request(NULL);
-	spin_unlock_irqrestore(&io_request_lock,saved_flags);
+	spin_unlock_irqrestore(&QUEUE->queue_lock,saved_flags);
 }
 
 /* end of pf.c */
diff -urN linux-2.5.1-pre4/drivers/block/rd.c linux/drivers/block/rd.c
--- linux-2.5.1-pre4/drivers/block/rd.c	Sat Dec  1 00:41:35 2001
+++ linux/drivers/block/rd.c	Sat Dec  1 00:42:26 2001
@@ -228,18 +228,19 @@
 	commit_write: ramdisk_commit_write,
 };
 
-static int rd_blkdev_pagecache_IO(int rw, struct bio *sbh, int minor)
+static int rd_blkdev_pagecache_IO(int rw, struct bio_vec *vec,
+				  sector_t sector, int minor)
 {
 	struct address_space * mapping;
 	unsigned long index;
 	int offset, size, err;
 
-	err = -EIO;
+	err = 0;
 	mapping = rd_bdev[minor]->bd_inode->i_mapping;
 
-	index = sbh->bi_sector >> (PAGE_CACHE_SHIFT - 9);
-	offset = (sbh->bi_sector << 9) & ~PAGE_CACHE_MASK;
-	size = bio_size(sbh);
+	index = sector >> (PAGE_CACHE_SHIFT - 9);
+	offset = (sector << 9) & ~PAGE_CACHE_MASK;
+	size = vec->bv_len;
 
 	do {
 		int count;
@@ -276,18 +277,18 @@
 		if (rw == READ) {
 			src = kmap(page);
 			src += offset;
-			dst = bio_kmap(sbh);
+			dst = kmap(vec->bv_page) + vec->bv_offset;
 		} else {
 			dst = kmap(page);
 			dst += offset;
-			src = bio_kmap(sbh);
+			src = kmap(vec->bv_page) + vec->bv_offset;
 		}
 		offset = 0;
 
 		memcpy(dst, src, count);
 
 		kunmap(page);
-		bio_kunmap(sbh);
+		kunmap(vec->bv_page);
 
 		if (rw == READ) {
 			flush_dcache_page(page);
@@ -303,6 +304,22 @@
 	return err;
 }
 
+static int rd_blkdev_bio_IO(struct bio *bio, unsigned int minor)
+{
+	struct bio_vec *bvec;
+	sector_t sector;
+	int ret = 0, i, rw;
+
+	sector = bio->bi_sector;
+	rw = bio_data_dir(bio);
+	bio_for_each_segment(bvec, bio, i) {
+		ret |= rd_blkdev_pagecache_IO(rw, bvec, sector, minor);
+		sector += bvec->bv_len >> 9;
+	}
+
+	return ret;
+}
+
 /*
  *  Basically, my strategy here is to set up a buffer-head which can't be
  *  deleted, and make that my Ramdisk.  If the request is outside of the
@@ -323,7 +340,7 @@
 		goto fail;
 
 	offset = sbh->bi_sector << 9;
-	len = bio_size(sbh);
+	len = sbh->bi_size;
 
 	if ((offset + len) > rd_length[minor])
 		goto fail;
@@ -335,7 +352,7 @@
 		goto fail;
 	}
 
-	if (rd_blkdev_pagecache_IO(rw, sbh, minor))
+	if (rd_blkdev_bio_IO(sbh, minor))
 		goto fail;
 
 	set_bit(BIO_UPTODATE, &sbh->bi_flags);
@@ -437,10 +454,11 @@
 
 #ifdef CONFIG_BLK_DEV_INITRD
 	if (unit == INITRD_MINOR) {
-		if (!initrd_start) return -ENODEV;
-		spin_lock( &initrd_users_lock );
+		spin_lock(&initrd_users_lock);
 		initrd_users++;
-		spin_unlock( &initrd_users_lock );
+		spin_unlock(&initrd_users_lock);
+		if (!initrd_start) 
+			return -ENODEV;
 		filp->f_op = &initrd_fops;
 		return 0;
 	}
@@ -539,10 +557,8 @@
 	return 0;
 }
 
-#ifdef MODULE
 module_init(rd_init);
 module_exit(rd_cleanup);
-#endif
 
 /* loadable module support */
 MODULE_PARM     (rd_size, "1i");
diff -urN linux-2.5.1-pre4/drivers/char/agp/agpgart_be.c linux/drivers/char/agp/agpgart_be.c
--- linux-2.5.1-pre4/drivers/char/agp/agpgart_be.c	Sat Dec  1 00:41:35 2001
+++ linux/drivers/char/agp/agpgart_be.c	Sat Dec  1 00:42:26 2001
@@ -1399,10 +1399,6 @@
 }
 
 #endif /* CONFIG_AGP_I810 */
- 
- #ifdef CONFIG_AGP_INTEL
-
-#endif /* CONFIG_AGP_I810 */
 
 #ifdef CONFIG_AGP_INTEL
 
@@ -3563,12 +3559,6 @@
 		AMD_IRONGATE,
 		"AMD",
 		"Irongate",
-		amd_irongate_setup },
-	{ PCI_DEVICE_ID_AMD_762_0,
-		PCI_VENDOR_ID_AMD,
-		AMD_IRONGATE,
-		"AMD",
-		"AMD 760MP",
 		amd_irongate_setup },
 	{ PCI_DEVICE_ID_AMD_761_0,
 		PCI_VENDOR_ID_AMD,
diff -urN linux-2.5.1-pre4/drivers/char/ftape/zftape/zftape-init.c linux/drivers/char/ftape/zftape/zftape-init.c
--- linux-2.5.1-pre4/drivers/char/ftape/zftape/zftape-init.c	Sat Dec  1 00:41:35 2001
+++ linux/drivers/char/ftape/zftape/zftape-init.c	Sat Dec  1 00:42:26 2001
@@ -114,7 +114,7 @@
 	TRACE_FUN(ft_t_flow);
 
 	TRACE(ft_t_flow, "called for minor %d", MINOR(ino->i_rdev));
-	if ( test_and_set_bit(0,&busy_flag) )) {
+	if ( test_and_set_bit(0,&busy_flag) ) {
 		TRACE_ABORT(-EBUSY, ft_t_warn, "failed: already busy");
 	}
 	if ((MINOR(ino->i_rdev) & ~(ZFT_MINOR_OP_MASK | FTAPE_NO_REWIND))
diff -urN linux-2.5.1-pre4/drivers/char/pc_keyb.c linux/drivers/char/pc_keyb.c
--- linux-2.5.1-pre4/drivers/char/pc_keyb.c	Sat Dec  1 00:41:35 2001
+++ linux/drivers/char/pc_keyb.c	Sat Dec  1 00:42:26 2001
@@ -1074,13 +1074,18 @@
 static int open_aux(struct inode * inode, struct file * file)
 {
 	unsigned long flags;
+	int ret;
+
 	spin_lock_irqsave(&aux_count_lock, flags);
 	if ( aux_count++ ) {
 		spin_unlock_irqrestore(&aux_count_lock, flags);
 		return 0;
 	}
 	queue->head = queue->tail = 0;		/* Flush input queue */
-	if (aux_request_irq(keyboard_interrupt, AUX_DEV)) {
+	spin_unlock_irqrestore(&aux_count_lock, flags);
+	ret = aux_request_irq(keyboard_interrupt, AUX_DEV);
+	spin_lock_irqsave(&aux_count_lock, flags);
+	if (ret) {
 		aux_count--;
 		spin_unlock_irqrestore(&aux_count_lock, flags);
 		return -EBUSY;
diff -urN linux-2.5.1-pre4/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- linux-2.5.1-pre4/drivers/char/tty_io.c	Sat Dec  1 00:41:35 2001
+++ linux/drivers/char/tty_io.c	Sat Dec  1 00:42:26 2001
@@ -102,10 +102,6 @@
 
 #include <linux/kmod.h>
 
-#ifdef CONFIG_VT
-extern void con_init_devfs (void);
-#endif
-
 #define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
 #define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
 #define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1)
@@ -2246,6 +2242,8 @@
 static struct tty_driver dev_ptmx_driver;
 #endif
 #ifdef CONFIG_VT
+extern void con_init_devfs (void);
+extern void console_map_init(void);
 static struct tty_driver dev_console_driver;
 #endif
 
diff -urN linux-2.5.1-pre4/drivers/ide/ide-floppy.c linux/drivers/ide/ide-floppy.c
--- linux-2.5.1-pre4/drivers/ide/ide-floppy.c	Sat Dec  1 00:41:35 2001
+++ linux/drivers/ide/ide-floppy.c	Sat Dec  1 00:42:26 2001
@@ -711,7 +711,7 @@
 	int count;
 
 	while (bcount) {
-		if (pc->b_count == bio_size(bio)) {
+		if (pc->b_count == bio->bi_size) {
 			rq->sector += rq->current_nr_sectors;
 			rq->nr_sectors -= rq->current_nr_sectors;
 			idefloppy_end_request (1, HWGROUP(drive));
@@ -723,7 +723,7 @@
 			idefloppy_discard_data (drive, bcount);
 			return;
 		}
-		count = IDEFLOPPY_MIN (bio_size(bio) - pc->b_count, bcount);
+		count = IDEFLOPPY_MIN (bio->bi_size - pc->b_count, bcount);
 		atapi_input_bytes (drive, bio_data(bio) + pc->b_count, count);
 		bcount -= count; pc->b_count += count;
 	}
@@ -742,7 +742,7 @@
 			idefloppy_end_request (1, HWGROUP(drive));
 			if ((bio = rq->bio) != NULL) {
 				pc->b_data = bio_data(bio);
-				pc->b_count = bio_size(bio);
+				pc->b_count = bio->bi_size;
 			}
 		}
 		if (bio == NULL) {
@@ -1210,7 +1210,7 @@
 	pc->callback = &idefloppy_rw_callback;
 	pc->rq = rq;
 	pc->b_data = rq->buffer;
-	pc->b_count = rq->cmd == READ ? 0 : bio_size(rq->bio);
+	pc->b_count = rq->cmd == READ ? 0 : rq->bio->bi_size;
 	if (rq->cmd == WRITE)
 		set_bit (PC_WRITING, &pc->flags);
 	pc->buffer = NULL;
diff -urN linux-2.5.1-pre4/drivers/ide/ide-pci.c linux/drivers/ide/ide-pci.c
--- linux-2.5.1-pre4/drivers/ide/ide-pci.c	Sat Dec  1 00:41:35 2001
+++ linux/drivers/ide/ide-pci.c	Sat Dec  1 00:42:26 2001
@@ -30,14 +30,14 @@
 #define DEVID_MPIIX	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371MX})
 #define DEVID_PIIX3	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371SB_1})
 #define DEVID_PIIX4	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371AB})
-#define DEVID_PIIX4E	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801AB_1})
+#define DEVID_ICH0	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801AB_1})
 #define DEVID_PIIX4E2	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82443MX_1})
-#define DEVID_PIIX4U	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801AA_1})
+#define DEVID_ICH	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801AA_1})
 #define DEVID_PIIX4U2	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82372FB_1})
 #define DEVID_PIIX4NX	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82451NX})
-#define DEVID_PIIX4U3	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801BA_9})
-#define DEVID_PIIX4U4	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801BA_8})
-#define DEVID_PIIX4U5	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801CA_10})
+#define DEVID_ICH2	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801BA_9})
+#define DEVID_ICH2M	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801BA_8})
+#define DEVID_ICH3	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801CA_10})
 #define DEVID_VIA_IDE	((ide_pci_devid_t){PCI_VENDOR_ID_VIA,     PCI_DEVICE_ID_VIA_82C561})
 #define DEVID_MR_IDE	((ide_pci_devid_t){PCI_VENDOR_ID_VIA,     PCI_DEVICE_ID_VIA_82C576_1})
 #define DEVID_VP_IDE	((ide_pci_devid_t){PCI_VENDOR_ID_VIA,     PCI_DEVICE_ID_VIA_82C586_1})
@@ -380,14 +380,14 @@
 	{DEVID_MPIIX,	"MPIIX",	NULL,		NULL,		INIT_PIIX,	NULL,		{{0x6D,0x80,0x80}, {0x6F,0x80,0x80}},	ON_BOARD,	0 },
 	{DEVID_PIIX3,	"PIIX3",	PCI_PIIX,	NULL,		INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}}, 	ON_BOARD,	0 },
 	{DEVID_PIIX4,	"PIIX4",	PCI_PIIX,	NULL,		INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}}, 	ON_BOARD,	0 },
-	{DEVID_PIIX4E,	"PIIX4",	PCI_PIIX,	NULL,		INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}},	ON_BOARD,	0 },
+	{DEVID_ICH0,	"ICH0",		PCI_PIIX,	NULL,		INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}},	ON_BOARD,	0 },
 	{DEVID_PIIX4E2,	"PIIX4",	PCI_PIIX,	NULL,		INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}},	ON_BOARD,	0 },
-	{DEVID_PIIX4U,	"PIIX4",	PCI_PIIX,	ATA66_PIIX,	INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}},	ON_BOARD,	0 },
+	{DEVID_ICH,	"ICH",		PCI_PIIX,	ATA66_PIIX,	INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}},	ON_BOARD,	0 },
 	{DEVID_PIIX4U2,	"PIIX4",	PCI_PIIX,	ATA66_PIIX,	INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}},	ON_BOARD,	0 },
 	{DEVID_PIIX4NX,	"PIIX4",	PCI_PIIX,	NULL,		INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}},	ON_BOARD,	0 },
-	{DEVID_PIIX4U3,	"PIIX4",	PCI_PIIX,	ATA66_PIIX,	INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}},	ON_BOARD,	0 },
-	{DEVID_PIIX4U4, "PIIX4",	PCI_PIIX,	ATA66_PIIX,	INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}},	ON_BOARD,	0 },
-	{DEVID_PIIX4U5, "PIIX4",	PCI_PIIX,	ATA66_PIIX,	INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}},	ON_BOARD,	0 },
+	{DEVID_ICH2,	"ICH2",		PCI_PIIX,	ATA66_PIIX,	INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}},	ON_BOARD,	0 },
+	{DEVID_ICH2M,	"ICH2-M",	PCI_PIIX,	ATA66_PIIX,	INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}},	ON_BOARD,	0 },
+	{DEVID_ICH3,	"ICH3",		PCI_PIIX,	ATA66_PIIX,	INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}},	ON_BOARD,	0 },
 	{DEVID_VIA_IDE,	"VIA_IDE",	NULL,		NULL,		NULL,		NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
 	{DEVID_MR_IDE,	"VP_IDE",	PCI_VIA82CXXX,	ATA66_VIA82CXXX,INIT_VIA82CXXX,	DMA_VIA82CXXX,	{{0x40,0x02,0x02}, {0x40,0x01,0x01}}, 	ON_BOARD,	0 },
 	{DEVID_VP_IDE,	"VP_IDE",	PCI_VIA82CXXX,	ATA66_VIA82CXXX,INIT_VIA82CXXX,	DMA_VIA82CXXX,	{{0x40,0x02,0x02}, {0x40,0x01,0x01}}, 	ON_BOARD,	0 },
@@ -842,7 +842,7 @@
 
 	switch(class_rev) {
 		case 4:
-		case 3:	printk("%s: IDE controller on PCI bus %02x dev %02x\n", d->name, dev->bus->number, dev->devfn);
+		case 3:	printk("%s: IDE controller on PCI slot %s\n", d->name, dev->slot_name);
 			ide_setup_pci_device(dev, d);
 			return;
 		default:	break;
@@ -871,12 +871,12 @@
 			break;
 		}
 	}
-	printk("%s: IDE controller on PCI bus %02x dev %02x\n", d->name, dev->bus->number, dev->devfn);
+	printk("%s: IDE controller on PCI slot %s\n", d->name, dev->slot_name);
 	ide_setup_pci_device(dev, d);
 	if (!dev2)
 		return;
 	d2 = d;
-	printk("%s: IDE controller on PCI bus %02x dev %02x\n", d2->name, dev2->bus->number, dev2->devfn);
+	printk("%s: IDE controller on PCI slot %s\n", d2->name, dev2->slot_name);
 	ide_setup_pci_device(dev2, d2);
 }
 
@@ -906,10 +906,10 @@
 		hpt366_device_order_fixup(dev, d);
 	else if (!IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL) || (dev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
 		if (IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL))
-			printk("%s: unknown IDE controller on PCI bus %02x device %02x, VID=%04x, DID=%04x\n",
-			       d->name, dev->bus->number, dev->devfn, devid.vid, devid.did);
+			printk("%s: unknown IDE controller on PCI slot %s, VID=%04x, DID=%04x\n",
+			       d->name, dev->slot_name, devid.vid, devid.did);
 		else
-			printk("%s: IDE controller on PCI bus %02x dev %02x\n", d->name, dev->bus->number, dev->devfn);
+			printk("%s: IDE controller on PCI slot %s\n", d->name, dev->slot_name);
 		ide_setup_pci_device(dev, d);
 	}
 }
diff -urN linux-2.5.1-pre4/drivers/ide/piix.c linux/drivers/ide/piix.c
--- linux-2.5.1-pre4/drivers/ide/piix.c	Sat Dec  1 00:41:35 2001
+++ linux/drivers/ide/piix.c	Sat Dec  1 00:42:26 2001
@@ -88,33 +88,9 @@
 	u8  c0 = 0, c1 = 0;
 	u8  reg44 = 0, reg48 = 0, reg4a = 0, reg4b = 0, reg54 = 0, reg55 = 0;
 
-	switch(bmide_dev->device) {
-		case PCI_DEVICE_ID_INTEL_82801BA_8:
-		case PCI_DEVICE_ID_INTEL_82801BA_9:
-	        case PCI_DEVICE_ID_INTEL_82801CA_10:
-			p += sprintf(p, "\n                                Intel PIIX4 Ultra 100 Chipset.\n");
-			break;
-		case PCI_DEVICE_ID_INTEL_82372FB_1:
-		case PCI_DEVICE_ID_INTEL_82801AA_1:
-			p += sprintf(p, "\n                                Intel PIIX4 Ultra 66 Chipset.\n");
-			break;
-		case PCI_DEVICE_ID_INTEL_82451NX:
-		case PCI_DEVICE_ID_INTEL_82801AB_1:
-		case PCI_DEVICE_ID_INTEL_82443MX_1:
-		case PCI_DEVICE_ID_INTEL_82371AB:
-			p += sprintf(p, "\n                                Intel PIIX4 Ultra 33 Chipset.\n");
-			break;
-		case PCI_DEVICE_ID_INTEL_82371SB_1:
-			p += sprintf(p, "\n                                Intel PIIX3 Chipset.\n");
-			break;
-		case PCI_DEVICE_ID_INTEL_82371MX:
-			p += sprintf(p, "\n                                Intel MPIIX Chipset.\n");
-			return p-buffer;	/* => must be less than 4k! */
-		case PCI_DEVICE_ID_INTEL_82371FB_1:
-		case PCI_DEVICE_ID_INTEL_82371FB_0:
-		default:
-			p += sprintf(p, "\n                                Intel PIIX Chipset.\n");
-			break;
+	if (bmide_dev->device == PCI_DEVICE_ID_INTEL_82371MX) {
+		p += sprintf(p, "\n                                Intel MPIIX Chipset.\n");
+		return p-buffer;	/* => must be less than 4k! */
 	}
 
 	pci_read_config_word(bmide_dev, 0x40, &reg40);
@@ -136,6 +112,7 @@
 	c0 = inb_p((unsigned short)bibma + 0x02);
 	c1 = inb_p((unsigned short)bibma + 0x0a);
 
+	p += sprintf(p, "\n                    %s Chipset.\n", bmide_dev->name);
 	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
 	p += sprintf(p, "                %sabled                         %sabled\n",
 			(c0&0x80) ? "dis" : " en",
diff -urN linux-2.5.1-pre4/drivers/isdn/Config.in linux/drivers/isdn/Config.in
--- linux-2.5.1-pre4/drivers/isdn/Config.in	Sun Sep  9 10:45:43 2001
+++ linux/drivers/isdn/Config.in	Sat Dec  1 00:42:26 2001
@@ -81,6 +81,7 @@
    dep_tristate 'Sedlbauer PCMCIA cards' CONFIG_HISAX_SEDLBAUER_CS $CONFIG_PCMCIA
    dep_tristate 'ELSA PCMCIA MicroLink cards' CONFIG_HISAX_ELSA_CS $CONFIG_PCMCIA
    dep_tristate 'ST5481 USB ISDN modem (EXPERIMENTAL)' CONFIG_HISAX_ST5481 $CONFIG_HISAX $CONFIG_USB $CONFIG_EXPERIMENTAL
+   dep_tristate 'Fritz!PCIv2 support (EXPERIMENTAL)' CONFIG_HISAX_FRITZ_PCIPNP $CONFIG_HISAX $CONFIG_EXPERIMENTAL
 fi
 endmenu
 
diff -urN linux-2.5.1-pre4/drivers/isdn/divert/divert_procfs.c linux/drivers/isdn/divert/divert_procfs.c
--- linux-2.5.1-pre4/drivers/isdn/divert/divert_procfs.c	Sat Dec  1 00:41:35 2001
+++ linux/drivers/isdn/divert/divert_procfs.c	Sat Dec  1 00:42:26 2001
@@ -58,7 +58,6 @@
 	else
 		divert_info_tail->next = ib;	/* follows existing messages */
 	divert_info_tail = ib;	/* new tail */
-	restore_flags(flags);
 
 	/* delete old entrys */
 	while (divert_info_head->next) {
@@ -70,7 +69,7 @@
 		} else
 			break;
 	}			/* divert_info_head->next */
-	spin_lock_irqrestore( &divert_info_lock, flags );
+	spin_unlock_irqrestore( &divert_info_lock, flags );
 	wake_up_interruptible(&(rd_queue));
 }				/* put_info_buffer */
 
@@ -163,7 +162,6 @@
 		inf->usage_cnt--;
 		inf = inf->next;
 	}
-	restore_flags(flags);
 	if (if_used <= 0)
 		while (divert_info_head) {
 			inf = divert_info_head;
diff -urN linux-2.5.1-pre4/drivers/isdn/hisax/Makefile linux/drivers/isdn/hisax/Makefile
--- linux-2.5.1-pre4/drivers/isdn/hisax/Makefile	Sun Sep  9 10:45:43 2001
+++ linux/drivers/isdn/hisax/Makefile	Sat Dec  1 00:42:26 2001
@@ -6,7 +6,7 @@
 
 # Objects that export symbols.
 
-export-objs	  := config.o fsm.o
+export-objs	  := config.o fsm.o hisax_isac.o
 
 # Multipart objects.
 
@@ -58,6 +58,7 @@
 obj-$(CONFIG_HISAX_SEDLBAUER_CS)	+= sedlbauer_cs.o
 obj-$(CONFIG_HISAX_ELSA_CS)		+= elsa_cs.o
 obj-$(CONFIG_HISAX_ST5481)		+= hisax_st5481.o
+obj-$(CONFIG_HISAX_FRITZ_PCIPNP)        += hisax_isac.o hisax_fcpcipnp.o
 
 CERT := $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?)
 CFLAGS_cert.o := -DCERTIFICATION=$(CERT)
diff -urN linux-2.5.1-pre4/drivers/isdn/hisax/config.c linux/drivers/isdn/hisax/config.c
--- linux-2.5.1-pre4/drivers/isdn/hisax/config.c	Sun Sep 30 12:26:05 2001
+++ linux/drivers/isdn/hisax/config.c	Sat Dec  1 00:42:26 2001
@@ -1505,15 +1505,9 @@
 	       nrcards, (nrcards > 1) ? "s" : "");
 
 	/* Install only, if at least one card found */
-	if (!HiSax_inithardware(NULL)) {
-		retval = -EIO;
-		goto out_isdnl1;
-	}
-
+	HiSax_inithardware(NULL);
 	return 0;
 
- out_isdnl1:
-	Isdnl1Free();
  out_tei:
 	TeiFree();
  out_isdnl2:
diff -urN linux-2.5.1-pre4/drivers/isdn/hisax/hisax_fcpcipnp.c linux/drivers/isdn/hisax/hisax_fcpcipnp.c
--- linux-2.5.1-pre4/drivers/isdn/hisax/hisax_fcpcipnp.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hisax_fcpcipnp.c	Sat Dec  1 00:42:26 2001
@@ -0,0 +1,1001 @@
+/*
+ * Driver for AVM Fritz!PCI, Fritz!PCI v2, Fritz!PnP ISDN cards
+ *
+ * Author       Kai Germaschewski
+ * Copyright    2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ *              2001 by Karsten Keil       <keil@isdn4linux.de>
+ * 
+ * based upon Karsten Keil's original avm_pci.c driver
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Wizard Computersysteme GmbH, Bremervoerde and
+ *           SoHaNet Technology GmbH, Berlin
+ * for supporting the development of this driver
+ */
+
+
+/* TODO:
+ *
+ * o POWER PC
+ * o clean up debugging
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include "hisax_fcpcipnp.h"
+
+// debugging cruft
+#define __debug_variable debug
+#include "hisax_debug.h"
+
+#ifdef CONFIG_HISAX_DEBUG
+static int debug = 0;
+MODULE_PARM(debug, "i");
+#endif
+
+MODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>");
+MODULE_DESCRIPTION("AVM Fritz!PCI/PnP ISDN driver");
+
+static struct pci_device_id fcpci_ids[] __devinitdata = {
+	{ PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1   , PCI_ANY_ID, PCI_ANY_ID,
+	  0, 0, (unsigned long) "Fritz!Card PCI" },
+	{ PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1_V2, PCI_ANY_ID, PCI_ANY_ID,
+	  0, 0, (unsigned long) "Fritz!Card PCI v2" },
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, fcpci_ids);
+
+static struct isapnp_device_id fcpnp_ids[] __devinitdata = {
+	{ ISAPNP_VENDOR('A', 'V', 'M'), ISAPNP_FUNCTION(0x0900),
+	  ISAPNP_VENDOR('A', 'V', 'M'), ISAPNP_FUNCTION(0x0900), 
+	  (unsigned long) "Fritz!Card PnP" },
+	{ }
+};
+MODULE_DEVICE_TABLE(isapnp, fcpnp_ids);
+
+static int protocol = 2;       /* EURO-ISDN Default */
+MODULE_PARM(protocol, "i");
+
+// ----------------------------------------------------------------------
+
+#define  AVM_INDEX              0x04
+#define  AVM_DATA               0x10
+
+#define	 AVM_IDX_HDLC_1		0x00
+#define	 AVM_IDX_HDLC_2		0x01
+#define	 AVM_IDX_ISAC_FIFO	0x02
+#define	 AVM_IDX_ISAC_REG_LOW	0x04
+#define	 AVM_IDX_ISAC_REG_HIGH	0x06
+
+#define  AVM_STATUS0            0x02
+
+#define  AVM_STATUS0_IRQ_ISAC	0x01
+#define  AVM_STATUS0_IRQ_HDLC	0x02
+#define  AVM_STATUS0_IRQ_TIMER	0x04
+#define  AVM_STATUS0_IRQ_MASK	0x07
+
+#define  AVM_STATUS0_RESET	0x01
+#define  AVM_STATUS0_DIS_TIMER	0x02
+#define  AVM_STATUS0_RES_TIMER	0x04
+#define  AVM_STATUS0_ENA_IRQ	0x08
+#define  AVM_STATUS0_TESTBIT	0x10
+
+#define  AVM_STATUS1            0x03
+#define  AVM_STATUS1_ENA_IOM	0x80
+
+#define  HDLC_FIFO		0x0
+#define  HDLC_STATUS		0x4
+#define  HDLC_CTRL		0x4
+
+#define  HDLC_MODE_ITF_FLG	0x01
+#define  HDLC_MODE_TRANS	0x02
+#define  HDLC_MODE_CCR_7	0x04
+#define  HDLC_MODE_CCR_16	0x08
+#define  HDLC_MODE_TESTLOOP	0x80
+
+#define  HDLC_INT_XPR		0x80
+#define  HDLC_INT_XDU		0x40
+#define  HDLC_INT_RPR		0x20
+#define  HDLC_INT_MASK		0xE0
+
+#define  HDLC_STAT_RME		0x01
+#define  HDLC_STAT_RDO		0x10
+#define  HDLC_STAT_CRCVFRRAB	0x0E
+#define  HDLC_STAT_CRCVFR	0x06
+#define  HDLC_STAT_RML_MASK	0x3f00
+
+#define  HDLC_CMD_XRS		0x80
+#define  HDLC_CMD_XME		0x01
+#define  HDLC_CMD_RRS		0x20
+#define  HDLC_CMD_XML_MASK	0x3f00
+
+#define  AVM_HDLC_FIFO_1        0x10
+#define  AVM_HDLC_FIFO_2        0x18
+
+#define  AVM_HDLC_STATUS_1      0x14
+#define  AVM_HDLC_STATUS_2      0x1c
+
+#define  AVM_ISACSX_INDEX       0x04
+#define  AVM_ISACSX_DATA        0x08
+
+// ----------------------------------------------------------------------
+// Fritz!PCI
+
+static unsigned char fcpci_read_isac(struct isac *isac, unsigned char offset)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	unsigned char idx = (offset > 0x2f) ? 
+		AVM_IDX_ISAC_REG_HIGH : AVM_IDX_ISAC_REG_LOW;
+	unsigned char val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outb(idx, adapter->io + AVM_INDEX);
+	val = inb(adapter->io + AVM_DATA + (offset & 0xf));
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+	DBG(0x1000, " port %#x, value %#x",
+	    offset, val);
+	return val;
+}
+
+static void fcpci_write_isac(struct isac *isac, unsigned char offset,
+			     unsigned char value)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	unsigned char idx = (offset > 0x2f) ? 
+		AVM_IDX_ISAC_REG_HIGH : AVM_IDX_ISAC_REG_LOW;
+	unsigned long flags;
+
+	DBG(0x1000, " port %#x, value %#x",
+	    offset, value);
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outb(idx, adapter->io + AVM_INDEX);
+	outb(value, adapter->io + AVM_DATA + (offset & 0xf));
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci_read_isac_fifo(struct isac *isac, unsigned char * data, 
+				 int size)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outb(AVM_IDX_ISAC_FIFO, adapter->io + AVM_INDEX);
+	insb(adapter->io + AVM_DATA, data, size);
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci_write_isac_fifo(struct isac *isac, unsigned char * data, 
+				  int size)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outb(AVM_IDX_ISAC_FIFO, adapter->io + AVM_INDEX);
+	outsb(adapter->io + AVM_DATA, data, size);
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static u32 fcpci_read_hdlc_status(struct fritz_adapter *adapter, int nr)
+{
+	u32 val;
+	int idx = nr ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outl(idx, adapter->io + AVM_INDEX);
+	val = inl(adapter->io + AVM_DATA + HDLC_STATUS);
+	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+	return val;
+}
+
+static void __fcpci_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	int idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+
+	DBG(0x40, "hdlc %c wr%x ctrl %x",
+	    'A' + bcs->channel, which, bcs->ctrl.ctrl);
+
+	outl(idx, adapter->io + AVM_INDEX);
+	outl(bcs->ctrl.ctrl, adapter->io + AVM_DATA + HDLC_CTRL);
+}
+
+static void fcpci_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	__fcpci_write_ctrl(bcs, which);
+	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+// ----------------------------------------------------------------------
+// Fritz!PCI v2
+
+static unsigned char fcpci2_read_isac(struct isac *isac, unsigned char offset)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	unsigned char val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outl(offset, adapter->io + AVM_ISACSX_INDEX);
+	val = inl(adapter->io + AVM_ISACSX_DATA);
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+	DBG(0x1000, " port %#x, value %#x",
+	    offset, val);
+
+	return val;
+}
+
+static void fcpci2_write_isac(struct isac *isac, unsigned char offset, 
+			      unsigned char value)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	unsigned long flags;
+
+	DBG(0x1000, " port %#x, value %#x",
+	    offset, value);
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outl(offset, adapter->io + AVM_ISACSX_INDEX);
+	outl(value, adapter->io + AVM_ISACSX_DATA);
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci2_read_isac_fifo(struct isac *isac, unsigned char * data, 
+				  int size)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outl(0, adapter->io + AVM_ISACSX_INDEX);
+	for (i = 0; i < size; i++)
+		data[i] = inl(adapter->io + AVM_ISACSX_DATA);
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci2_write_isac_fifo(struct isac *isac, unsigned char * data, 
+				   int size)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outl(0, adapter->io + AVM_ISACSX_INDEX);
+	for (i = 0; i < size; i++)
+		outl(data[i], adapter->io + AVM_ISACSX_DATA);
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static u32 fcpci2_read_hdlc_status(struct fritz_adapter *adapter, int nr)
+{
+	int offset = nr ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1;
+
+	return inl(adapter->io + offset);
+}
+
+static void fcpci2_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	int offset = bcs->channel ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1;
+
+	DBG(0x40, "hdlc %c wr%x ctrl %x",
+	    'A' + bcs->channel, which, bcs->ctrl.ctrl);
+
+	outl(bcs->ctrl.ctrl, adapter->io + offset);
+}
+
+// ----------------------------------------------------------------------
+// Fritz!PnP (ISAC access as for Fritz!PCI)
+
+static u32 fcpnp_read_hdlc_status(struct fritz_adapter *adapter, int nr)
+{
+	unsigned char idx = nr ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+	u32 val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outb(idx, adapter->io + AVM_INDEX);
+	val = inb(adapter->io + AVM_DATA + HDLC_STATUS);
+	if (val & HDLC_INT_RPR)
+		val |= inb(adapter->io + AVM_DATA + HDLC_STATUS + 1) << 8;
+	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+	return val;
+}
+
+static void __fcpnp_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	unsigned char idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+
+	DBG(0x40, "hdlc %c wr%x ctrl %x",
+	    'A' + bcs->channel, which, bcs->ctrl.ctrl);
+
+	outb(idx, adapter->io + AVM_INDEX);
+	if (which & 4)
+		outb(bcs->ctrl.sr.mode, 
+		     adapter->io + AVM_DATA + HDLC_STATUS + 2);
+	if (which & 2)
+		outb(bcs->ctrl.sr.xml, 
+		     adapter->io + AVM_DATA + HDLC_STATUS + 1);
+	if (which & 1)
+		outb(bcs->ctrl.sr.cmd,
+		     adapter->io + AVM_DATA + HDLC_STATUS + 0);
+}
+
+static void fcpnp_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	__fcpnp_write_ctrl(bcs, which);
+	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+// ----------------------------------------------------------------------
+
+static inline void B_L1L2(struct fritz_bcs *bcs, int pr, void *arg)
+{
+	struct hisax_if *ifc = (struct hisax_if *) &bcs->b_if;
+
+	DBG(2, "pr %#x", pr);
+	ifc->l1l2(ifc, pr, arg);
+}
+
+static void hdlc_fill_fifo(struct fritz_bcs *bcs)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	struct sk_buff *skb = bcs->tx_skb;
+	int count;
+	int fifo_size = 32;
+	unsigned long flags;
+	unsigned char *p;
+
+	DBG(0x40, "hdlc_fill_fifo");
+
+	if (!skb)
+		BUG();
+
+	if (skb->len == 0)
+		BUG();
+
+	bcs->ctrl.sr.cmd &= ~HDLC_CMD_XME;
+	if (bcs->tx_skb->len > fifo_size) {
+		count = fifo_size;
+	} else {
+		count = bcs->tx_skb->len;
+		if (bcs->mode != L1_MODE_TRANS)
+			bcs->ctrl.sr.cmd |= HDLC_CMD_XME;
+	}
+	DBG(0x40, "hdlc_fill_fifo %d/%d", count, bcs->tx_skb->len);
+	p = bcs->tx_skb->data;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt += count;
+	bcs->ctrl.sr.xml = ((count == fifo_size) ? 0 : count);
+
+	switch (adapter->type) {
+	case AVM_FRITZ_PCI:
+		spin_lock_irqsave(&adapter->hw_lock, flags);
+		// sets the correct AVM_INDEX, too
+		__fcpci_write_ctrl(bcs, 3);
+		outsl(adapter->io + AVM_DATA + HDLC_FIFO,
+		      p, (count + 3) / 4);
+		spin_unlock_irqrestore(&adapter->hw_lock, flags);
+		break;
+	case AVM_FRITZ_PCIV2:
+		fcpci2_write_ctrl(bcs, 3);
+		outsl(adapter->io + 
+		      (bcs->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1),
+		      p, (count + 3) / 4);
+		break;
+	case AVM_FRITZ_PNP:
+		spin_lock_irqsave(&adapter->hw_lock, flags);
+		// sets the correct AVM_INDEX, too
+		__fcpnp_write_ctrl(bcs, 3);
+		outsb(adapter->io + AVM_DATA, p, count);
+		spin_unlock_irqrestore(&adapter->hw_lock, flags);
+		break;
+	}
+}
+
+static inline void hdlc_empty_fifo(struct fritz_bcs *bcs, int count)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	unsigned char *p;
+	unsigned char idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+
+	DBG(0x10, "hdlc_empty_fifo %d", count);
+	if (bcs->rcvidx + count > HSCX_BUFMAX) {
+		DBG(0x10, "hdlc_empty_fifo: incoming packet too large");
+		return;
+	}
+	p = bcs->rcvbuf + bcs->rcvidx;
+	bcs->rcvidx += count;
+	switch (adapter->type) {
+	case AVM_FRITZ_PCI:
+		spin_lock(&adapter->hw_lock);
+		outl(idx, adapter->io + AVM_INDEX);
+		insl(adapter->io + AVM_DATA + HDLC_FIFO, 
+		     p, (count + 3) / 4);
+		spin_unlock(&adapter->hw_lock);
+		break;
+	case AVM_FRITZ_PCIV2:
+		insl(adapter->io + 
+		     (bcs->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1),
+		     p, (count + 3) / 4);
+		break;
+	case AVM_FRITZ_PNP:
+		spin_lock(&adapter->hw_lock);
+		outb(idx, adapter->io + AVM_INDEX);
+		insb(adapter->io + AVM_DATA, p, count);
+		spin_unlock(&adapter->hw_lock);
+		break;
+	}
+}
+
+static inline void hdlc_rpr_irq(struct fritz_bcs *bcs, u32 stat)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	struct sk_buff *skb;
+	int len;
+
+	if (stat & HDLC_STAT_RDO) {
+		DBG(0x10, "RDO");
+		bcs->ctrl.sr.xml = 0;
+		bcs->ctrl.sr.cmd |= HDLC_CMD_RRS;
+		adapter->write_ctrl(bcs, 1);
+		bcs->ctrl.sr.cmd &= ~HDLC_CMD_RRS;
+		adapter->write_ctrl(bcs, 1);
+		bcs->rcvidx = 0;
+		return;
+	}
+
+	len = (stat & HDLC_STAT_RML_MASK) >> 8;
+	if (len == 0)
+		len = 32;
+
+	hdlc_empty_fifo(bcs, len);
+
+	if ((stat & HDLC_STAT_RME) || (bcs->mode == L1_MODE_TRANS)) {
+		if (((stat & HDLC_STAT_CRCVFRRAB)== HDLC_STAT_CRCVFR) ||
+		    (bcs->mode == L1_MODE_TRANS)) {
+			skb = dev_alloc_skb(bcs->rcvidx);
+			if (!skb) {
+				printk(KERN_WARNING "HDLC: receive out of memory\n");
+			} else {
+				memcpy(skb_put(skb, bcs->rcvidx), bcs->rcvbuf,
+				       bcs->rcvidx);
+				DBG_SKB(1, skb);
+				B_L1L2(bcs, PH_DATA | INDICATION, skb);
+			}
+			bcs->rcvidx = 0;
+		} else {
+			DBG(0x10, "ch%d invalid frame %#x",
+			    bcs->channel, stat);
+			bcs->rcvidx = 0;
+		}
+	}
+}
+
+static inline void hdlc_xdu_irq(struct fritz_bcs *bcs)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+
+	/* Here we lost an TX interrupt, so
+	 * restart transmitting the whole frame.
+	 */
+	bcs->ctrl.sr.xml = 0;
+	bcs->ctrl.sr.cmd |= HDLC_CMD_XRS;
+	adapter->write_ctrl(bcs, 1);
+	bcs->ctrl.sr.cmd &= ~HDLC_CMD_XRS;
+	adapter->write_ctrl(bcs, 1);
+
+	if (!bcs->tx_skb) {
+		DBG(0x10, "XDU without skb");
+		return;
+	}
+	skb_push(bcs->tx_skb, bcs->tx_cnt);
+	bcs->tx_cnt = 0;
+}
+
+static inline void hdlc_xpr_irq(struct fritz_bcs *bcs)
+{
+	struct sk_buff *skb;
+
+	skb = bcs->tx_skb;
+	if (!skb)
+		return;
+
+	if (skb->len) {
+		hdlc_fill_fifo(bcs);
+		return;
+	}
+	bcs->tx_cnt = 0;
+	bcs->tx_skb = NULL;
+	B_L1L2(bcs, PH_DATA | CONFIRM, (void *) skb->truesize);
+	dev_kfree_skb_irq(skb);
+}
+
+static void hdlc_irq(struct fritz_bcs *bcs, u32 stat)
+{
+	DBG(0x10, "ch%d stat %#x", bcs->channel, stat);
+	if (stat & HDLC_INT_RPR) {
+		DBG(0x10, "RPR");
+		hdlc_rpr_irq(bcs, stat);
+	}
+	if (stat & HDLC_INT_XDU) {
+		DBG(0x10, "XDU");
+		hdlc_xdu_irq(bcs);
+	}
+	if (stat & HDLC_INT_XPR) {
+		DBG(0x10, "XPR");
+		hdlc_xpr_irq(bcs);
+	}
+}
+
+static inline void hdlc_interrupt(struct fritz_adapter *adapter)
+{
+	int nr;
+	u32 stat;
+
+	for (nr = 0; nr < 2; nr++) {
+		stat = adapter->read_hdlc_status(adapter, nr);
+		DBG(0x10, "HDLC %c stat %#x", 'A' + nr, stat);
+		if (stat & HDLC_INT_MASK)
+			hdlc_irq(&adapter->bcs[nr], stat);
+	}
+}
+
+static void modehdlc(struct fritz_bcs *bcs, int mode)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	
+	DBG(0x40, "hdlc %c mode %d --> %d",
+	    'A' + bcs->channel, bcs->mode, mode);
+
+	if (bcs->mode == mode)
+		return;
+
+	bcs->ctrl.ctrl = 0;
+	bcs->ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+	switch (mode) {
+	case L1_MODE_NULL:
+		bcs->ctrl.sr.mode = HDLC_MODE_TRANS;
+		adapter->write_ctrl(bcs, 5);
+		break;
+	case L1_MODE_TRANS:
+		bcs->ctrl.sr.mode = HDLC_MODE_TRANS;
+		adapter->write_ctrl(bcs, 5);
+		bcs->ctrl.sr.cmd = HDLC_CMD_XRS;
+		adapter->write_ctrl(bcs, 1);
+		bcs->ctrl.sr.cmd = 0;
+		break;
+	case L1_MODE_HDLC:
+		bcs->ctrl.sr.mode = HDLC_MODE_ITF_FLG;
+		adapter->write_ctrl(bcs, 5);
+		bcs->ctrl.sr.cmd = HDLC_CMD_XRS;
+		adapter->write_ctrl(bcs, 1);
+		bcs->ctrl.sr.cmd = 0;
+		break;
+	}
+	bcs->mode = mode;
+}
+
+static void fritz_b_l2l1(struct hisax_if *ifc, int pr, void *arg)
+{
+	struct fritz_bcs *bcs = ifc->priv;
+	struct sk_buff *skb = arg;
+	int mode;
+
+	DBG(0x10, "pr %#x", pr);
+
+	switch (pr) {
+	case PH_DATA | REQUEST:
+		if (bcs->tx_skb)
+			BUG();
+		
+		bcs->tx_skb = skb;
+		DBG_SKB(1, skb);
+		hdlc_fill_fifo(bcs);
+		break;
+	case PH_ACTIVATE | REQUEST:
+		mode = (int) arg;
+		DBG(4,"B%d,PH_ACTIVATE_REQUEST %d", bcs->channel + 1, mode);
+		modehdlc(bcs, mode);
+		B_L1L2(bcs, PH_ACTIVATE | INDICATION, NULL);
+		break;
+	case PH_DEACTIVATE | REQUEST:
+		DBG(4,"B%d,PH_DEACTIVATE_REQUEST", bcs->channel + 1);
+		modehdlc(bcs, L1_MODE_NULL);
+		B_L1L2(bcs, PH_DEACTIVATE | INDICATION, NULL);
+		break;
+	}
+}
+
+// ----------------------------------------------------------------------
+
+static void fcpci2_irq(int intno, void *dev, struct pt_regs *regs)
+{
+	struct fritz_adapter *adapter = dev;
+	unsigned char val;
+
+	val = inb(adapter->io + AVM_STATUS0);
+	if (!(val & AVM_STATUS0_IRQ_MASK))
+		/* hopefully a shared  IRQ reqest */
+		return;
+	DBG(2, "STATUS0 %#x", val);
+	if (val & AVM_STATUS0_IRQ_ISAC)
+		isacsx_interrupt(&adapter->isac);
+
+	if (val & AVM_STATUS0_IRQ_HDLC)
+		hdlc_interrupt(adapter);
+}
+
+static void fcpci_irq(int intno, void *dev, struct pt_regs *regs)
+{
+	struct fritz_adapter *adapter = dev;
+	unsigned char sval;
+
+	sval = inb(adapter->io + 2);
+	if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK)
+		/* possibly a shared  IRQ reqest */
+		return;
+	DBG(2, "sval %#x", sval);
+	if (!(sval & AVM_STATUS0_IRQ_ISAC))
+		isac_interrupt(&adapter->isac);
+
+	if (!(sval & AVM_STATUS0_IRQ_HDLC))
+		hdlc_interrupt(adapter);
+}
+
+// ----------------------------------------------------------------------
+
+static inline void fcpci2_init(struct fritz_adapter *adapter)
+{
+	outb(AVM_STATUS0_RES_TIMER, adapter->io + AVM_STATUS0);
+	outb(AVM_STATUS0_ENA_IRQ, adapter->io + AVM_STATUS0);
+
+}
+
+static inline void fcpci_init(struct fritz_adapter *adapter)
+{
+	outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | 
+	     AVM_STATUS0_ENA_IRQ, adapter->io + AVM_STATUS0);
+
+	outb(AVM_STATUS1_ENA_IOM | adapter->irq, 
+	     adapter->io + AVM_STATUS1);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(50*HZ / 1000); /* Timeout 50ms */
+}
+
+// ----------------------------------------------------------------------
+
+static int __devinit fcpcipnp_setup(struct fritz_adapter *adapter)
+{
+	u32 val = 0;
+	int retval;
+
+	DBG(1,"");
+
+	isac_init(&adapter->isac); // FIXME is this okay now
+
+	retval = -EBUSY;
+	if (!request_region(adapter->io, 32, "fcpcipnp"))
+		goto err;
+
+	switch (adapter->type) {
+	case AVM_FRITZ_PCIV2:
+		retval = request_irq(adapter->irq, fcpci2_irq, SA_SHIRQ, 
+				     "fcpcipnp", adapter);
+		break;
+	case AVM_FRITZ_PCI:
+		retval = request_irq(adapter->irq, fcpci_irq, SA_SHIRQ,
+				     "fcpcipnp", adapter);
+		break;
+	case AVM_FRITZ_PNP:
+		retval = request_irq(adapter->irq, fcpci_irq, 0,
+				     "fcpcipnp", adapter);
+		break;
+	}
+	if (retval)
+		goto err_region;
+
+	switch (adapter->type) {
+	case AVM_FRITZ_PCIV2:
+	case AVM_FRITZ_PCI:
+		val = inl(adapter->io);
+		break;
+	case AVM_FRITZ_PNP:
+		val = inb(adapter->io);
+		val |= inb(adapter->io + 1) << 8;
+		break;
+	}
+
+	DBG(1, "stat %#x Class %X Rev %d",
+	    val, val & 0xff, (val>>8) & 0xff);
+
+	spin_lock_init(&adapter->hw_lock);
+	adapter->isac.priv = adapter;
+	switch (adapter->type) {
+	case AVM_FRITZ_PCIV2:
+		adapter->isac.read_isac       = &fcpci2_read_isac;;
+		adapter->isac.write_isac      = &fcpci2_write_isac;
+		adapter->isac.read_isac_fifo  = &fcpci2_read_isac_fifo;
+		adapter->isac.write_isac_fifo = &fcpci2_write_isac_fifo;
+
+		adapter->read_hdlc_status     = &fcpci2_read_hdlc_status;
+		adapter->write_ctrl           = &fcpci2_write_ctrl;
+		break;
+	case AVM_FRITZ_PCI:
+		adapter->isac.read_isac       = &fcpci_read_isac;;
+		adapter->isac.write_isac      = &fcpci_write_isac;
+		adapter->isac.read_isac_fifo  = &fcpci_read_isac_fifo;
+		adapter->isac.write_isac_fifo = &fcpci_write_isac_fifo;
+
+		adapter->read_hdlc_status     = &fcpci_read_hdlc_status;
+		adapter->write_ctrl           = &fcpci_write_ctrl;
+		break;
+	case AVM_FRITZ_PNP:
+		adapter->isac.read_isac       = &fcpci_read_isac;;
+		adapter->isac.write_isac      = &fcpci_write_isac;
+		adapter->isac.read_isac_fifo  = &fcpci_read_isac_fifo;
+		adapter->isac.write_isac_fifo = &fcpci_write_isac_fifo;
+
+		adapter->read_hdlc_status     = &fcpnp_read_hdlc_status;
+		adapter->write_ctrl           = &fcpnp_write_ctrl;
+		break;
+	}
+
+	// Reset
+	outb(0, adapter->io + AVM_STATUS0);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(50 * HZ / 1000); // 50 msec
+	outb(AVM_STATUS0_RESET, adapter->io + AVM_STATUS0);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(50 * HZ / 1000); // 50 msec
+	outb(0, adapter->io + AVM_STATUS0);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(10 * HZ / 1000); // 10 msec
+
+	switch (adapter->type) {
+	case AVM_FRITZ_PCIV2:
+		fcpci2_init(adapter);
+		isacsx_setup(&adapter->isac);
+		break;
+	case AVM_FRITZ_PCI:
+	case AVM_FRITZ_PNP:
+		fcpci_init(adapter);
+		isac_setup(&adapter->isac);
+		break;
+	}
+	val = adapter->read_hdlc_status(adapter, 0);
+	DBG(0x20, "HDLC A STA %x", val);
+	val = adapter->read_hdlc_status(adapter, 1);
+	DBG(0x20, "HDLC B STA %x", val);
+
+	adapter->bcs[0].mode = -1;
+	adapter->bcs[1].mode = -1;
+	modehdlc(&adapter->bcs[0], L1_MODE_NULL);
+	modehdlc(&adapter->bcs[1], L1_MODE_NULL);
+
+	return 0;
+
+ err_region:
+	release_region(adapter->io, 32);
+ err:
+	return retval;
+}
+
+static void __devexit fcpcipnp_release(struct fritz_adapter *adapter)
+{
+	DBG(1,"");
+
+	outb(0, adapter->io + AVM_STATUS0);
+	free_irq(adapter->irq, adapter);
+	release_region(adapter->io, 32);
+}
+
+// ----------------------------------------------------------------------
+
+static struct fritz_adapter * __devinit 
+new_adapter(struct pci_dev *pdev)
+{
+	struct fritz_adapter *adapter;
+	struct hisax_b_if *b_if[2];
+	int i;
+
+	adapter = kmalloc(sizeof(struct fritz_adapter), GFP_KERNEL);
+	if (!adapter)
+		return NULL;
+
+	memset(adapter, 0, sizeof(struct fritz_adapter));
+
+	SET_MODULE_OWNER(&adapter->isac.hisax_d_if);
+	adapter->isac.hisax_d_if.ifc.priv = &adapter->isac;
+	adapter->isac.hisax_d_if.ifc.l2l1 = isac_d_l2l1;
+	
+	for (i = 0; i < 2; i++) {
+		adapter->bcs[i].adapter = adapter;
+		adapter->bcs[i].channel = i;
+		adapter->bcs[i].b_if.ifc.priv = &adapter->bcs[i];
+		adapter->bcs[i].b_if.ifc.l2l1 = fritz_b_l2l1;
+	}
+
+	pci_set_drvdata(pdev, adapter);
+
+	for (i = 0; i < 2; i++)
+		b_if[i] = &adapter->bcs[i].b_if;
+
+	hisax_register(&adapter->isac.hisax_d_if, b_if, "fcpcipnp", protocol);
+
+	return adapter;
+}
+
+static void delete_adapter(struct fritz_adapter *adapter)
+{
+	hisax_unregister(&adapter->isac.hisax_d_if);
+	kfree(adapter);
+}
+
+static int __devinit fcpci_probe(struct pci_dev *pdev,
+				 const struct pci_device_id *ent)
+{
+	struct fritz_adapter *adapter;
+	int retval;
+
+	retval = -ENOMEM;
+	adapter = new_adapter(pdev);
+	if (!adapter)
+		goto err;
+
+	if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2) 
+		adapter->type = AVM_FRITZ_PCIV2;
+	else
+		adapter->type = AVM_FRITZ_PCI;
+
+	retval = pci_enable_device(pdev);
+	if (retval)
+		goto err_free;
+
+	adapter->io = pci_resource_start(pdev, 1);
+	adapter->irq = pdev->irq;
+
+	printk(KERN_INFO "hisax_fcpcipnp: found adapter %s at %s\n",
+	       (char *) ent->driver_data, pdev->slot_name);
+
+	retval = fcpcipnp_setup(adapter);
+	if (retval)
+		goto err_free;
+
+	return 0;
+	
+ err_free:
+	delete_adapter(adapter);
+ err:
+	return retval;
+}
+
+static int __devinit fcpnp_probe(struct pci_dev *pdev,
+				 const struct isapnp_device_id *ent)
+{
+	struct fritz_adapter *adapter;
+	int retval;
+
+	retval = -ENOMEM;
+	adapter = new_adapter(pdev);
+	if (!adapter)
+		goto err;
+
+	adapter->type = AVM_FRITZ_PNP;
+
+	pdev->prepare(pdev);
+	pdev->deactivate(pdev); // why?
+	pdev->activate(pdev);
+	adapter->io = pdev->resource[0].start;
+	adapter->irq = pdev->irq_resource[0].start;
+
+	printk(KERN_INFO "hisax_fcpcipnp: found adapter %s at IO %#x irq %d\n",
+	       (char *) ent->driver_data, adapter->io, adapter->irq);
+
+	retval = fcpcipnp_setup(adapter);
+	if (retval)
+		goto err_free;
+
+	return 0;
+	
+ err_free:
+	delete_adapter(adapter);
+ err:
+	return retval;
+}
+
+static void __devexit fcpci_remove(struct pci_dev *pdev)
+{
+	struct fritz_adapter *adapter = pci_get_drvdata(pdev);
+
+	fcpcipnp_release(adapter);
+	pci_disable_device(pdev);
+	delete_adapter(adapter);
+}
+
+static void __devexit fcpnp_remove(struct pci_dev *pdev)
+{
+	struct fritz_adapter *adapter = pci_get_drvdata(pdev);
+
+	fcpcipnp_release(adapter);
+	pdev->deactivate(pdev);
+	delete_adapter(adapter);
+}
+
+static struct pci_driver fcpci_driver = {
+	name:     "fcpci",
+	probe:    fcpci_probe,
+	remove:   fcpci_remove,
+	id_table: fcpci_ids,
+};
+
+static struct isapnp_driver fcpnp_driver = {
+	name:     "fcpnp",
+	probe:    fcpnp_probe,
+	remove:   fcpnp_remove,
+	id_table: fcpnp_ids,
+};
+
+static int __init hisax_fcpcipnp_init(void)
+{
+	int retval, pci_nr_found;
+
+	printk(KERN_INFO "hisax_fcpcipnp: Fritz!Card PCI/PCIv2/PnP ISDN driver v0.0.1\n");
+
+	retval = pci_register_driver(&fcpci_driver);
+	if (retval < 0)
+		goto out;
+	pci_nr_found = retval;
+
+	retval = isapnp_register_driver(&fcpnp_driver);
+	if (retval < 0)
+		goto out_unregister_pci;
+
+#if !defined(CONFIG_HOTPLUG) || defined(MODULE)
+	if (pci_nr_found + retval == 0) {
+		retval = -ENODEV;
+		goto out_unregister_isapnp;
+	}
+#endif
+	return 0;
+
+#if !defined(CONFIG_HOTPLUG) || defined(MODULE)
+ out_unregister_isapnp:
+	isapnp_unregister_driver(&fcpnp_driver);
+#endif
+ out_unregister_pci:
+	pci_unregister_driver(&fcpci_driver);
+ out:
+	return retval;
+}
+
+static void __exit hisax_fcpcipnp_exit(void)
+{
+	isapnp_unregister_driver(&fcpnp_driver);
+	pci_unregister_driver(&fcpci_driver);
+}
+
+module_init(hisax_fcpcipnp_init);
+module_exit(hisax_fcpcipnp_exit);
diff -urN linux-2.5.1-pre4/drivers/isdn/hisax/hisax_fcpcipnp.h linux/drivers/isdn/hisax/hisax_fcpcipnp.h
--- linux-2.5.1-pre4/drivers/isdn/hisax/hisax_fcpcipnp.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hisax_fcpcipnp.h	Sat Dec  1 00:42:26 2001
@@ -0,0 +1,57 @@
+#include "hisax_if.h"
+#include "hisax_isac.h"
+#include <linux/pci.h>
+
+#define HSCX_BUFMAX	4096
+
+enum {
+	AVM_FRITZ_PCI,
+	AVM_FRITZ_PNP,
+	AVM_FRITZ_PCIV2,
+};
+
+struct hdlc_stat_reg {
+#ifdef __BIG_ENDIAN
+	u_char fill __attribute__((packed));
+	u_char mode __attribute__((packed));
+	u_char xml  __attribute__((packed));
+	u_char cmd  __attribute__((packed));
+#else
+	u_char cmd  __attribute__((packed));
+	u_char xml  __attribute__((packed));
+	u_char mode __attribute__((packed));
+	u_char fill __attribute__((packed));
+#endif
+};
+
+struct fritz_bcs {
+	struct hisax_b_if b_if;
+	struct fritz_adapter *adapter;
+	int mode;
+	int channel;
+
+	union {
+		u_int ctrl;
+		struct hdlc_stat_reg sr;
+	} ctrl;
+	u_int stat;
+	int rcvidx;
+	u_char rcvbuf[HSCX_BUFMAX]; /* B-Channel receive Buffer */
+
+	int tx_cnt;		    /* B-Channel transmit counter */
+	struct sk_buff *tx_skb;     /* B-Channel transmit Buffer */
+};
+
+struct fritz_adapter {
+	int type;
+	spinlock_t hw_lock;
+	unsigned int io;
+	unsigned int irq;
+	struct isac isac;
+
+	struct fritz_bcs bcs[2];
+
+	u32  (*read_hdlc_status) (struct fritz_adapter *adapter, int nr);
+	void (*write_ctrl) (struct fritz_bcs *bcs, int which);
+};
+
diff -urN linux-2.5.1-pre4/drivers/isdn/hisax/hisax_isac.c linux/drivers/isdn/hisax/hisax_isac.c
--- linux-2.5.1-pre4/drivers/isdn/hisax/hisax_isac.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hisax_isac.c	Sat Dec  1 00:42:26 2001
@@ -0,0 +1,896 @@
+/*
+ * Driver for ISAC-S and ISAC-SX 
+ * ISDN Subscriber Access Controller for Terminals
+ *
+ * Author       Kai Germaschewski
+ * Copyright    2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ *              2001 by Karsten Keil       <keil@isdn4linux.de>
+ * 
+ * based upon Karsten Keil's original isac.c driver
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Wizard Computersysteme GmbH, Bremervoerde and
+ *           SoHaNet Technology GmbH, Berlin
+ * for supporting the development of this driver
+ */
+
+/* TODO:
+ * specifically handle level vs edge triggered?
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include "hisax_isac.h"
+
+// debugging cruft
+
+#define __debug_variable debug
+#include "hisax_debug.h"
+
+#ifdef CONFIG_HISAX_DEBUG
+static int debug = 1;
+MODULE_PARM(debug, "i");
+
+static char *ISACVer[] = {
+  "2086/2186 V1.1", 
+  "2085 B1", 
+  "2085 B2",
+  "2085 V2.3"
+};
+#endif
+
+MODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>");
+MODULE_DESCRIPTION("ISAC/ISAC-SX driver");
+
+#define DBG_WARN      0x0001
+#define DBG_IRQ       0x0002
+#define DBG_L1M       0x0004
+#define DBG_PR        0x0008
+#define DBG_RFIFO     0x0100
+#define DBG_RPACKET   0x0200
+#define DBG_XFIFO     0x1000
+#define DBG_XPACKET   0x2000
+
+// we need to distinguish ISAC-S and ISAC-SX
+#define TYPE_ISAC        0x00
+#define TYPE_ISACSX      0x01
+
+// registers etc.
+#define ISAC_MASK        0x20
+#define ISAC_ISTA        0x20
+#define ISAC_ISTA_EXI    0x01
+#define ISAC_ISTA_SIN    0x02
+#define ISAC_ISTA_CISQ   0x04
+#define ISAC_ISTA_XPR    0x10
+#define ISAC_ISTA_RSC    0x20
+#define ISAC_ISTA_RPF    0x40
+#define ISAC_ISTA_RME    0x80
+
+#define ISAC_STAR        0x21
+#define ISAC_CMDR        0x21
+#define ISAC_CMDR_XRES   0x01
+#define ISAC_CMDR_XME    0x02
+#define ISAC_CMDR_XTF    0x08
+#define ISAC_CMDR_RRES   0x40
+#define ISAC_CMDR_RMC    0x80
+
+#define ISAC_EXIR        0x24
+#define ISAC_EXIR_MOS    0x04
+#define ISAC_EXIR_XDU    0x40
+#define ISAC_EXIR_XMR    0x80
+
+#define ISAC_ADF2        0x39
+#define ISAC_SPCR        0x30
+#define ISAC_ADF1        0x38
+
+#define ISAC_CIR0        0x31
+#define ISAC_CIX0        0x31
+#define ISAC_CIR0_CIC0   0x02
+#define ISAC_CIR0_CIC1   0x01
+
+#define ISAC_CIR1        0x33
+#define ISAC_CIX1        0x33
+#define ISAC_STCR        0x37
+#define ISAC_MODE        0x22
+
+#define ISAC_RSTA        0x27
+#define ISAC_RSTA_RDO    0x40
+#define ISAC_RSTA_CRC    0x20
+#define ISAC_RSTA_RAB    0x10
+
+#define ISAC_RBCL 0x25
+#define ISAC_RBCH 0x2A
+#define ISAC_TIMR 0x23
+#define ISAC_SQXR 0x3b
+#define ISAC_MOSR 0x3a
+#define ISAC_MOCR 0x3a
+#define ISAC_MOR0 0x32
+#define ISAC_MOX0 0x32
+#define ISAC_MOR1 0x34
+#define ISAC_MOX1 0x34
+
+#define ISAC_RBCH_XAC 0x80
+
+#define ISAC_CMD_TIM    0x0
+#define ISAC_CMD_RES    0x1
+#define ISAC_CMD_SSP    0x2
+#define ISAC_CMD_SCP    0x3
+#define ISAC_CMD_AR8    0x8
+#define ISAC_CMD_AR10   0x9
+#define ISAC_CMD_ARL    0xa
+#define ISAC_CMD_DI     0xf
+
+#define ISACSX_MASK       0x60
+#define ISACSX_ISTA       0x60
+#define ISACSX_ISTA_ICD   0x01
+#define ISACSX_ISTA_CIC   0x10
+
+#define ISACSX_MASKD      0x20
+#define ISACSX_ISTAD      0x20
+#define ISACSX_ISTAD_XDU  0x04
+#define ISACSX_ISTAD_XMR  0x08
+#define ISACSX_ISTAD_XPR  0x10
+#define ISACSX_ISTAD_RFO  0x20
+#define ISACSX_ISTAD_RPF  0x40
+#define ISACSX_ISTAD_RME  0x80
+
+#define ISACSX_CMDRD      0x21
+#define ISACSX_CMDRD_XRES 0x01
+#define ISACSX_CMDRD_XME  0x02
+#define ISACSX_CMDRD_XTF  0x08
+#define ISACSX_CMDRD_RRES 0x40
+#define ISACSX_CMDRD_RMC  0x80
+
+#define ISACSX_MODED      0x22
+
+#define ISACSX_RBCLD      0x26
+
+#define ISACSX_RSTAD      0x28
+#define ISACSX_RSTAD_RAB  0x10
+#define ISACSX_RSTAD_CRC  0x20
+#define ISACSX_RSTAD_RDO  0x40
+#define ISACSX_RSTAD_VFR  0x80
+
+#define ISACSX_CIR0       0x2e
+#define ISACSX_CIR0_CIC0  0x08
+#define ISACSX_CIX0       0x2e
+
+#define ISACSX_TR_CONF0   0x30
+
+#define ISACSX_TR_CONF2   0x32
+
+static struct Fsm l1fsm;
+
+enum {
+	ST_L1_RESET,
+	ST_L1_F3_PDOWN,
+	ST_L1_F3_PUP,
+	ST_L1_F3_PEND_DEACT,
+	ST_L1_F4,
+	ST_L1_F5,
+	ST_L1_F6,
+	ST_L1_F7,
+	ST_L1_F8,
+};
+
+#define L1_STATE_COUNT (ST_L1_F8+1)
+
+static char *strL1State[] =
+{
+	"ST_L1_RESET",
+	"ST_L1_F3_PDOWN",
+	"ST_L1_F3_PUP",
+	"ST_L1_F3_PEND_DEACT",
+	"ST_L1_F4",
+	"ST_L1_F5",
+	"ST_L1_F6",
+	"ST_L1_F7",
+	"ST_L1_F8",
+};
+
+enum {
+	EV_PH_DR,           // 0000
+	EV_PH_RES,          // 0001
+	EV_PH_TMA,          // 0010
+	EV_PH_SLD,          // 0011
+	EV_PH_RSY,          // 0100
+	EV_PH_DR6,          // 0101
+	EV_PH_EI,           // 0110
+	EV_PH_PU,           // 0111
+	EV_PH_AR,           // 1000
+	EV_PH_9,            // 1001
+	EV_PH_ARL,          // 1010
+	EV_PH_CVR,          // 1011
+	EV_PH_AI8,          // 1100
+	EV_PH_AI10,         // 1101
+	EV_PH_AIL,          // 1110
+	EV_PH_DC,           // 1111
+	EV_PH_ACTIVATE_REQ,
+	EV_PH_DEACTIVATE_REQ,
+	EV_TIMER3,
+};
+
+#define L1_EVENT_COUNT (EV_TIMER3 + 1)
+
+static char *strL1Event[] =
+{
+	"EV_PH_DR",           // 0000
+	"EV_PH_RES",          // 0001
+	"EV_PH_TMA",          // 0010
+	"EV_PH_SLD",          // 0011
+	"EV_PH_RSY",          // 0100
+	"EV_PH_DR6",          // 0101
+	"EV_PH_EI",           // 0110
+	"EV_PH_PU",           // 0111
+	"EV_PH_AR",           // 1000
+	"EV_PH_9",            // 1001
+	"EV_PH_ARL",          // 1010
+	"EV_PH_CVR",          // 1011
+	"EV_PH_AI8",          // 1100
+	"EV_PH_AI10",         // 1101
+	"EV_PH_AIL",          // 1110
+	"EV_PH_DC",           // 1111
+	"EV_PH_ACTIVATE_REQ",
+	"EV_PH_DEACTIVATE_REQ",
+	"EV_TIMER3",
+};
+
+static inline void D_L1L2(struct isac *isac, int pr, void *arg)
+{
+	struct hisax_if *ifc = (struct hisax_if *) &isac->hisax_d_if;
+
+	DBG(DBG_PR, "pr %#x", pr);
+	ifc->l1l2(ifc, pr, arg);
+}
+
+static void ph_command(struct isac *isac, unsigned int command)
+{
+	DBG(DBG_L1M, "ph_command %#x", command);
+	switch (isac->type) {
+	case TYPE_ISAC:
+		isac->write_isac(isac, ISAC_CIX0, (command << 2) | 3);
+		break;
+	case TYPE_ISACSX:
+		isac->write_isac(isac, ISACSX_CIX0, (command << 4) | (7 << 1));
+		break;
+	}
+}
+
+// ----------------------------------------------------------------------
+
+static void l1_di(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_RESET);
+	ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_di_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_RESET);
+	D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+	ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_go_f3pdown(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F3_PDOWN);
+}
+
+static void l1_go_f3pend_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_F3_PEND_DEACT);
+	D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+	ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_go_f3pend(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_F3_PEND_DEACT);
+	ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_go_f4(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F4);
+}
+
+static void l1_go_f5(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F5);
+}
+
+static void l1_go_f6(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F6);
+}
+
+static void l1_go_f6_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_F6);
+	D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+static void l1_go_f7_act_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmDelTimer(&isac->timer, 0);
+	FsmChangeState(fi, ST_L1_F7);
+	ph_command(isac, ISAC_CMD_AR8);
+	D_L1L2(isac, PH_ACTIVATE | INDICATION, NULL);
+}
+
+static void l1_go_f8(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F8);
+}
+
+static void l1_go_f8_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_F8);
+	D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+static void l1_ar8(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmRestartTimer(&isac->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+	ph_command(isac, ISAC_CMD_AR8);
+}
+
+static void l1_timer3(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	ph_command(isac, ISAC_CMD_DI);
+	D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+// state machines according to data sheet PSB 2186 / 3186
+
+static struct FsmNode L1FnList[] __initdata =
+{
+	{ST_L1_RESET,         EV_PH_RES,            l1_di},
+	{ST_L1_RESET,         EV_PH_EI,             l1_di},
+	{ST_L1_RESET,         EV_PH_DC,             l1_go_f3pdown},
+	{ST_L1_RESET,         EV_PH_AR,             l1_go_f6},
+	{ST_L1_RESET,         EV_PH_AI8,            l1_go_f7_act_ind},
+
+	{ST_L1_F3_PDOWN,      EV_PH_RES,            l1_di},
+	{ST_L1_F3_PDOWN,      EV_PH_EI,             l1_di},
+	{ST_L1_F3_PDOWN,      EV_PH_AR,             l1_go_f6},
+	{ST_L1_F3_PDOWN,      EV_PH_RSY,            l1_go_f5},
+	{ST_L1_F3_PDOWN,      EV_PH_PU,             l1_go_f4},
+	{ST_L1_F3_PDOWN,      EV_PH_AI8,            l1_go_f7_act_ind},
+	{ST_L1_F3_PDOWN,      EV_PH_ACTIVATE_REQ,   l1_ar8},
+	{ST_L1_F3_PDOWN,      EV_TIMER3,            l1_timer3},
+	
+	{ST_L1_F3_PEND_DEACT, EV_PH_RES,            l1_di},
+	{ST_L1_F3_PEND_DEACT, EV_PH_EI,             l1_di},
+	{ST_L1_F3_PEND_DEACT, EV_PH_DC,             l1_go_f3pdown},
+	{ST_L1_F3_PEND_DEACT, EV_PH_RSY,            l1_go_f5},
+	{ST_L1_F3_PEND_DEACT, EV_PH_AR,             l1_go_f6},
+	{ST_L1_F3_PEND_DEACT, EV_PH_AI8,            l1_go_f7_act_ind},
+
+	{ST_L1_F4,            EV_PH_RES,            l1_di},
+	{ST_L1_F4,            EV_PH_EI,             l1_di},
+	{ST_L1_F4,            EV_PH_RSY,            l1_go_f5},
+	{ST_L1_F4,            EV_PH_AI8,            l1_go_f7_act_ind},
+	{ST_L1_F4,            EV_TIMER3,            l1_timer3},
+	{ST_L1_F4,            EV_PH_DC,             l1_go_f3pdown},
+
+	{ST_L1_F5,            EV_PH_RES,            l1_di},
+	{ST_L1_F5,            EV_PH_EI,             l1_di},
+	{ST_L1_F5,            EV_PH_AR,             l1_go_f6},
+	{ST_L1_F5,            EV_PH_AI8,            l1_go_f7_act_ind},
+	{ST_L1_F5,            EV_TIMER3,            l1_timer3},
+	{ST_L1_F5,            EV_PH_DR,             l1_go_f3pend},
+	{ST_L1_F5,            EV_PH_DC,             l1_go_f3pdown},
+
+	{ST_L1_F6,            EV_PH_RES,            l1_di},
+	{ST_L1_F6,            EV_PH_EI,             l1_di},
+	{ST_L1_F6,            EV_PH_RSY,            l1_go_f8},
+	{ST_L1_F6,            EV_PH_AI8,            l1_go_f7_act_ind},
+	{ST_L1_F6,            EV_PH_DR6,            l1_go_f3pend},
+	{ST_L1_F6,            EV_TIMER3,            l1_timer3},
+	{ST_L1_F6,            EV_PH_DC,             l1_go_f3pdown},
+
+	{ST_L1_F7,            EV_PH_RES,            l1_di_deact_ind},
+	{ST_L1_F7,            EV_PH_EI,             l1_di_deact_ind},
+	{ST_L1_F7,            EV_PH_AR,             l1_go_f6_deact_ind},
+	{ST_L1_F7,            EV_PH_RSY,            l1_go_f8_deact_ind},
+	{ST_L1_F7,            EV_PH_DR,             l1_go_f3pend_deact_ind},
+
+	{ST_L1_F8,            EV_PH_RES,            l1_di},
+	{ST_L1_F8,            EV_PH_EI,             l1_di},
+	{ST_L1_F8,            EV_PH_AR,             l1_go_f6},
+	{ST_L1_F8,            EV_PH_DR,             l1_go_f3pend},
+	{ST_L1_F8,            EV_PH_AI8,            l1_go_f7_act_ind},
+	{ST_L1_F8,            EV_TIMER3,            l1_timer3},
+	{ST_L1_F8,            EV_PH_DC,             l1_go_f3pdown},
+};
+
+static void l1m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	va_list args;
+	char buf[256];
+	
+	va_start(args, fmt);
+	vsprintf(buf, fmt, args);
+	DBG(DBG_L1M, "%s", buf);
+	va_end(args);
+}
+
+static void isac_version(struct isac *cs)
+{
+	int val;
+
+	val = cs->read_isac(cs, ISAC_RBCH);
+	DBG(1, "ISAC version (%x): %s", val, ISACVer[(val >> 5) & 3]);
+}
+
+static void isac_empty_fifo(struct isac *isac, int count)
+{
+	// this also works for isacsx, since
+	// CMDR(D) register works the same
+	u_char *ptr;
+
+	DBG(DBG_IRQ, "count %d", count);
+
+	if ((isac->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+		DBG(DBG_WARN, "overrun %d", isac->rcvidx + count);
+		isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC);
+		isac->rcvidx = 0;
+		return;
+	}
+	ptr = isac->rcvbuf + isac->rcvidx;
+	isac->rcvidx += count;
+	isac->read_isac_fifo(isac, ptr, count);
+	isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC);
+	DBG_PACKET(DBG_RFIFO, ptr, count);
+}
+
+static void isac_fill_fifo(struct isac *isac)
+{
+	// this also works for isacsx, since
+	// CMDR(D) register works the same
+
+	int count;
+	unsigned char cmd;
+	u_char *ptr;
+
+	if (!isac->tx_skb)
+		BUG();
+
+	count = isac->tx_skb->len;
+	if (count <= 0)
+		BUG();
+
+	DBG(DBG_IRQ, "count %d", count);
+
+	if (count > 0x20) {
+		count = 0x20;
+		cmd = ISAC_CMDR_XTF;
+	} else {
+		cmd = ISAC_CMDR_XTF | ISAC_CMDR_XME;
+	}
+
+	ptr = isac->tx_skb->data;
+	skb_pull(isac->tx_skb, count);
+	isac->tx_cnt += count;
+	DBG_PACKET(DBG_XFIFO, ptr, count);
+	isac->write_isac_fifo(isac, ptr, count);
+	isac->write_isac(isac, ISAC_CMDR, cmd);
+}
+
+static void isac_retransmit(struct isac *isac)
+{
+	if (!isac->tx_skb) {
+		DBG(DBG_WARN, "no skb");
+		return;
+	}
+	skb_push(isac->tx_skb, isac->tx_cnt);
+	isac->tx_cnt = 0;
+}
+
+
+static inline void isac_cisq_interrupt(struct isac *isac)
+{
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISAC_CIR0);
+	DBG(DBG_IRQ, "CIR0 %#x", val);
+	if (val & ISAC_CIR0_CIC0) {
+		DBG(DBG_IRQ, "CODR0 %#x", (val >> 2) & 0xf);
+		FsmEvent(&isac->l1m, (val >> 2) & 0xf, NULL);
+	}
+	if (val & ISAC_CIR0_CIC1) {
+		val = isac->read_isac(isac, ISAC_CIR1);
+		DBG(DBG_WARN, "ISAC CIR1 %#x", val );
+	}
+}
+
+static inline void isac_rme_interrupt(struct isac *isac)
+{
+	unsigned char val;
+	int count;
+	struct sk_buff *skb;
+	
+	val = isac->read_isac(isac, ISAC_RSTA);
+	if ((val & (ISAC_RSTA_RDO | ISAC_RSTA_CRC | ISAC_RSTA_RAB) )
+	     != ISAC_RSTA_CRC) {
+		DBG(DBG_WARN, "RSTA %#x, dropped", val);
+		isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC);
+		goto out;
+	}
+
+	count = isac->read_isac(isac, ISAC_RBCL) & 0x1f;
+	DBG(DBG_IRQ, "RBCL %#x", count);
+	if (count == 0)
+		count = 0x20;
+
+	isac_empty_fifo(isac, count);
+	count = isac->rcvidx;
+	if (count < 1) {
+		DBG(DBG_WARN, "count %d < 1", count);
+		goto out;
+	}
+
+	skb = alloc_skb(count, GFP_ATOMIC);
+	if (!skb) {
+		DBG(DBG_WARN, "no memory, dropping\n");
+		goto out;
+	}
+	memcpy(skb_put(skb, count), isac->rcvbuf, count);
+	DBG_SKB(DBG_RPACKET, skb);
+	D_L1L2(isac, PH_DATA | INDICATION, skb);
+ out:
+	isac->rcvidx = 0;
+}
+
+static inline void isac_xpr_interrupt(struct isac *isac)
+{
+	if (!isac->tx_skb)
+		return;
+
+	if (isac->tx_skb->len > 0) {
+		isac_fill_fifo(isac);
+		return;
+	}
+	dev_kfree_skb_irq(isac->tx_skb);
+	isac->tx_cnt = 0;
+	isac->tx_skb = NULL;
+	D_L1L2(isac, PH_DATA | CONFIRM, NULL);
+}
+
+static inline void isac_exi_interrupt(struct isac *isac)
+{
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISAC_EXIR);
+	DBG(2, "EXIR %#x", val);
+
+	if (val & ISAC_EXIR_XMR) {
+		DBG(DBG_WARN, "ISAC XMR");
+		isac_retransmit(isac);
+	}
+	if (val & ISAC_EXIR_XDU) {
+		DBG(DBG_WARN, "ISAC XDU");
+		isac_retransmit(isac);
+	}
+	if (val & ISAC_EXIR_MOS) {  /* MOS */
+		DBG(DBG_WARN, "MOS");
+		val = isac->read_isac(isac, ISAC_MOSR);
+		DBG(2, "ISAC MOSR %#x", val);
+	}
+}
+
+void isac_interrupt(struct isac *isac)
+{
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISAC_ISTA);
+	DBG(DBG_IRQ, "ISTA %#x", val);
+
+	if (val & ISAC_ISTA_EXI) {
+		DBG(DBG_IRQ, "EXI");
+		isac_exi_interrupt(isac);
+	}
+	if (val & ISAC_ISTA_XPR) {
+		DBG(DBG_IRQ, "XPR");
+		isac_xpr_interrupt(isac);
+	}
+	if (val & ISAC_ISTA_RME) {
+		DBG(DBG_IRQ, "RME");
+		isac_rme_interrupt(isac);
+	}
+	if (val & ISAC_ISTA_RPF) {
+		DBG(DBG_IRQ, "RPF");
+		isac_empty_fifo(isac, 0x20);
+	}
+	if (val & ISAC_ISTA_CISQ) {
+		DBG(DBG_IRQ, "CISQ");
+		isac_cisq_interrupt(isac);
+	}
+	if (val & ISAC_ISTA_RSC) {
+		DBG(DBG_WARN, "RSC");
+	}
+	if (val & ISAC_ISTA_SIN) {
+		DBG(DBG_WARN, "SIN");
+	}
+	isac->write_isac(isac, ISAC_MASK, 0xff);
+	isac->write_isac(isac, ISAC_MASK, 0x00);
+}
+
+// ======================================================================
+
+static inline void isacsx_cic_interrupt(struct isac *isac)
+{
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISACSX_CIR0);
+	DBG(DBG_IRQ, "CIR0 %#x", val);
+	if (val & ISACSX_CIR0_CIC0) {
+		DBG(DBG_IRQ, "CODR0 %#x", val >> 4);
+		FsmEvent(&isac->l1m, val >> 4, NULL);
+	}
+}
+
+static inline void isacsx_rme_interrupt(struct isac *isac)
+{
+	int count;
+	struct sk_buff *skb;
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISACSX_RSTAD);
+	if ((val & (ISACSX_RSTAD_VFR | 
+		    ISACSX_RSTAD_RDO | 
+		    ISACSX_RSTAD_CRC | 
+		    ISACSX_RSTAD_RAB)) 
+	    != (ISACSX_RSTAD_VFR | ISACSX_RSTAD_CRC)) {
+		DBG(DBG_WARN, "RSTAD %#x, dropped", val);
+		isac->write_isac(isac, ISACSX_CMDRD, ISACSX_CMDRD_RMC);
+		goto out;
+	}
+
+	count = isac->read_isac(isac, ISACSX_RBCLD) & 0x1f;
+	DBG(DBG_IRQ, "RBCLD %#x", count);
+	if (count == 0)
+		count = 0x20;
+
+	isac_empty_fifo(isac, count);
+	// strip trailing status byte
+	count = isac->rcvidx - 1;
+	if (count < 1) {
+		DBG(DBG_WARN, "count %d < 1", count);
+		goto out;
+	}
+
+	skb = dev_alloc_skb(count);
+	if (!skb) {
+		DBG(DBG_WARN, "no memory, dropping");
+		goto out;
+	}
+	memcpy(skb_put(skb, count), isac->rcvbuf, count);
+	DBG_SKB(DBG_RPACKET, skb);
+	D_L1L2(isac, PH_DATA | INDICATION, skb);
+ out:
+	isac->rcvidx = 0;
+}
+
+static inline void isacsx_xpr_interrupt(struct isac *isac)
+{
+	if (!isac->tx_skb)
+		return;
+
+	if (isac->tx_skb->len > 0) {
+		isac_fill_fifo(isac);
+		return;
+	}
+	dev_kfree_skb_irq(isac->tx_skb);
+	isac->tx_skb = NULL;
+	isac->tx_cnt = 0;
+	D_L1L2(isac, PH_DATA | CONFIRM, NULL);
+}
+
+static inline void isacsx_icd_interrupt(struct isac *isac)
+{
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISACSX_ISTAD);
+	DBG(DBG_IRQ, "ISTAD %#x", val);
+	if (val & ISACSX_ISTAD_XDU) {
+		DBG(DBG_WARN, "ISTAD XDU");
+		isac_retransmit(isac);
+	}
+	if (val & ISACSX_ISTAD_XMR) {
+		DBG(DBG_WARN, "ISTAD XMR");
+		isac_retransmit(isac);
+	}
+	if (val & ISACSX_ISTAD_XPR) {
+		DBG(DBG_IRQ, "ISTAD XPR");
+		isacsx_xpr_interrupt(isac);
+	}
+	if (val & ISACSX_ISTAD_RFO) {
+		DBG(DBG_WARN, "ISTAD RFO");
+		isac->write_isac(isac, ISACSX_CMDRD, ISACSX_CMDRD_RMC);
+	}
+	if (val & ISACSX_ISTAD_RME) {
+		DBG(DBG_IRQ, "ISTAD RME");
+		isacsx_rme_interrupt(isac);
+	}
+	if (val & ISACSX_ISTAD_RPF) {
+		DBG(DBG_IRQ, "ISTAD RPF");
+		isac_empty_fifo(isac, 0x20);
+	}
+}
+
+void isacsx_interrupt(struct isac *isac)
+{
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISACSX_ISTA);
+	DBG(DBG_IRQ, "ISTA %#x", val);
+
+	if (val & ISACSX_ISTA_ICD)
+		isacsx_icd_interrupt(isac);
+	if (val & ISACSX_ISTA_CIC)
+		isacsx_cic_interrupt(isac);
+}
+
+void isac_init(struct isac *isac)
+{
+	isac->tx_skb = NULL;
+	isac->l1m.fsm = &l1fsm;
+	isac->l1m.state = ST_L1_RESET;
+#ifdef CONFIG_HISAX_DEBUG
+	isac->l1m.debug = 1;
+#else
+	isac->l1m.debug = 0;
+#endif
+	isac->l1m.userdata = isac;
+	isac->l1m.printdebug = l1m_debug;
+	FsmInitTimer(&isac->l1m, &isac->timer);
+}
+
+void isac_setup(struct isac *isac)
+{
+	int val, eval;
+
+	isac->type = TYPE_ISAC;
+	isac_version(isac);
+
+	ph_command(isac, ISAC_CMD_RES);
+
+  	isac->write_isac(isac, ISAC_MASK, 0xff);
+  	isac->mocr = 0xaa;
+	if (test_bit(ISAC_IOM1, &isac->flags)) {
+		/* IOM 1 Mode */
+		isac->write_isac(isac, ISAC_ADF2, 0x0);
+		isac->write_isac(isac, ISAC_SPCR, 0xa);
+		isac->write_isac(isac, ISAC_ADF1, 0x2);
+		isac->write_isac(isac, ISAC_STCR, 0x70);
+		isac->write_isac(isac, ISAC_MODE, 0xc9);
+	} else {
+		/* IOM 2 Mode */
+		if (!isac->adf2)
+			isac->adf2 = 0x80;
+		isac->write_isac(isac, ISAC_ADF2, isac->adf2);
+		isac->write_isac(isac, ISAC_SQXR, 0x2f);
+		isac->write_isac(isac, ISAC_SPCR, 0x00);
+		isac->write_isac(isac, ISAC_STCR, 0x70);
+		isac->write_isac(isac, ISAC_MODE, 0xc9);
+		isac->write_isac(isac, ISAC_TIMR, 0x00);
+		isac->write_isac(isac, ISAC_ADF1, 0x00);
+	}
+	val = isac->read_isac(isac, ISAC_STAR);
+	DBG(2, "ISAC STAR %x", val);
+	val = isac->read_isac(isac, ISAC_MODE);
+	DBG(2, "ISAC MODE %x", val);
+	val = isac->read_isac(isac, ISAC_ADF2);
+	DBG(2, "ISAC ADF2 %x", val);
+	val = isac->read_isac(isac, ISAC_ISTA);
+	DBG(2, "ISAC ISTA %x", val);
+	if (val & 0x01) {
+		eval = isac->read_isac(isac, ISAC_EXIR);
+		DBG(2, "ISAC EXIR %x", eval);
+	}
+	val = isac->read_isac(isac, ISAC_CIR0);
+	DBG(2, "ISAC CIR0 %x", val);
+	FsmEvent(&isac->l1m, (val >> 2) & 0xf, NULL);
+
+	isac->write_isac(isac, ISAC_MASK, 0x0);
+	// RESET Receiver and Transmitter
+	isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_XRES | ISAC_CMDR_RRES);
+}
+
+void isacsx_setup(struct isac *isac)
+{
+	isac->type = TYPE_ISACSX;
+	// clear LDD
+	isac->write_isac(isac, ISACSX_TR_CONF0, 0x00);
+	// enable transmitter
+	isac->write_isac(isac, ISACSX_TR_CONF2, 0x00);
+	// transparent mode 0, RAC, stop/go
+	isac->write_isac(isac, ISACSX_MODED,    0xc9);
+	// all HDLC IRQ unmasked
+	isac->write_isac(isac, ISACSX_MASKD,    0x03);
+	// unmask ICD, CID IRQs
+	isac->write_isac(isac, ISACSX_MASK,            
+			 ~(ISACSX_ISTA_ICD | ISACSX_ISTA_CIC));
+}
+
+void isac_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg)
+{
+	struct isac *isac = hisax_d_if->priv;
+	struct sk_buff *skb = arg;
+
+	DBG(DBG_PR, "pr %#x", pr);
+
+	switch (pr) {
+	case PH_ACTIVATE | REQUEST:
+		FsmEvent(&isac->l1m, EV_PH_ACTIVATE_REQ, NULL);
+		break;
+	case PH_DEACTIVATE | REQUEST:
+		FsmEvent(&isac->l1m, EV_PH_DEACTIVATE_REQ, NULL);
+		break;
+	case PH_DATA | REQUEST:
+		DBG(DBG_PR, "PH_DATA REQUEST len %d", skb->len);
+		DBG_SKB(DBG_XPACKET, skb);
+		if (isac->l1m.state != ST_L1_F7) {
+			DBG(1, "L1 wrong state %d\n", isac->l1m.state);
+			dev_kfree_skb(skb);
+			break;
+		}
+		if (isac->tx_skb)
+			BUG();
+
+		isac->tx_skb = skb;
+		isac_fill_fifo(isac);
+		break;
+	}
+}
+
+static int __init hisax_isac_init(void)
+{
+	printk(KERN_INFO "hisax_isac: ISAC-S/ISAC-SX ISDN driver v0.1.0\n");
+
+	l1fsm.state_count = L1_STATE_COUNT;
+	l1fsm.event_count = L1_EVENT_COUNT;
+	l1fsm.strState = strL1State;
+	l1fsm.strEvent = strL1Event;
+	return FsmNew(&l1fsm, L1FnList, ARRAY_SIZE(L1FnList));
+}
+
+static void __exit hisax_isac_exit(void)
+{
+	FsmFree(&l1fsm);
+}
+
+EXPORT_SYMBOL(isac_init);
+EXPORT_SYMBOL(isac_d_l2l1);
+
+EXPORT_SYMBOL(isacsx_setup);
+EXPORT_SYMBOL(isacsx_interrupt);
+
+EXPORT_SYMBOL(isac_setup);
+EXPORT_SYMBOL(isac_interrupt);
+
+module_init(hisax_isac_init);
+module_exit(hisax_isac_exit);
diff -urN linux-2.5.1-pre4/drivers/isdn/hisax/hisax_isac.h linux/drivers/isdn/hisax/hisax_isac.h
--- linux-2.5.1-pre4/drivers/isdn/hisax/hisax_isac.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hisax_isac.h	Sat Dec  1 00:42:26 2001
@@ -0,0 +1,45 @@
+#ifndef __HISAX_ISAC_H__
+#define __HISAX_ISAC_H__
+
+#include <linux/kernel.h>
+#include "fsm.h"
+#include "hisax_if.h"
+
+#define TIMER3_VALUE 7000
+#define MAX_DFRAME_LEN_L1 300
+
+#define ISAC_IOM1	0
+
+struct isac {
+	void *priv;
+
+	u_long flags;
+	struct hisax_d_if hisax_d_if;
+	struct FsmInst l1m;
+	struct FsmTimer timer;
+	u_char mocr;
+	u_char adf2;
+	int    type;
+
+	u_char rcvbuf[MAX_DFRAME_LEN_L1];
+	int rcvidx;
+
+	struct sk_buff *tx_skb;
+	int tx_cnt;
+
+	u_char (*read_isac)      (struct isac *, u_char);
+	void   (*write_isac)     (struct isac *, u_char, u_char);
+	void   (*read_isac_fifo) (struct isac *, u_char *, int);
+	void   (*write_isac_fifo)(struct isac *, u_char *, int);
+};
+
+void isac_init(struct isac *isac);
+void isac_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg);
+
+void isac_setup(struct isac *isac);
+void isac_interrupt(struct isac *isac);
+
+void isacsx_setup(struct isac *isac);
+void isacsx_interrupt(struct isac *isac);
+
+#endif
diff -urN linux-2.5.1-pre4/drivers/md/linear.c linux/drivers/md/linear.c
--- linux-2.5.1-pre4/drivers/md/linear.c	Sun Sep 30 12:26:06 2001
+++ linux/drivers/md/linear.c	Sat Dec  1 00:42:26 2001
@@ -119,22 +119,21 @@
 	return 0;
 }
 
-static int linear_make_request (mddev_t *mddev,
-			int rw, struct buffer_head * bh)
+static int linear_make_request (mddev_t *mddev, int rw, struct bio *bio)
 {
         linear_conf_t *conf = mddev_to_conf(mddev);
         struct linear_hash *hash;
         dev_info_t *tmp_dev;
         long block;
 
-	block = bh->b_rsector >> 1;
+	block = bio->bi_sector >> 1;
 	hash = conf->hash_table + (block / conf->smallest->size);
   
 	if (block >= (hash->dev0->size + hash->dev0->offset)) {
 		if (!hash->dev1) {
 			printk ("linear_make_request : hash->dev1==NULL for block %ld\n",
 						block);
-			buffer_IO_error(bh);
+			bio_io_error(bio);
 			return 0;
 		}
 		tmp_dev = hash->dev1;
@@ -144,11 +143,11 @@
 	if (block >= (tmp_dev->size + tmp_dev->offset)
 				|| block < tmp_dev->offset) {
 		printk ("linear_make_request: Block %ld out of bounds on dev %s size %ld offset %ld\n", block, kdevname(tmp_dev->dev), tmp_dev->size, tmp_dev->offset);
-		buffer_IO_error(bh);
+		bio_io_error(bio);
 		return 0;
 	}
-	bh->b_rdev = tmp_dev->dev;
-	bh->b_rsector = bh->b_rsector - (tmp_dev->offset << 1);
+	bio->bi_dev = tmp_dev->dev;
+	bio->bi_sector = bio->bi_sector - (tmp_dev->offset << 1);
 
 	return 1;
 }
diff -urN linux-2.5.1-pre4/drivers/md/raid0.c linux/drivers/md/raid0.c
--- linux-2.5.1-pre4/drivers/md/raid0.c	Sun Sep 30 12:26:06 2001
+++ linux/drivers/md/raid0.c	Sat Dec  1 00:42:26 2001
@@ -223,8 +223,7 @@
  * Of course, those facts may not be valid anymore (and surely won't...)
  * Hey guys, there's some work out there ;-)
  */
-static int raid0_make_request (mddev_t *mddev,
-			       int rw, struct buffer_head * bh)
+static int raid0_make_request (mddev_t *mddev, int rw, struct bio *bio)
 {
 	unsigned int sect_in_chunk, chunksize_bits,  chunk_size;
 	raid0_conf_t *conf = mddev_to_conf(mddev);
@@ -235,11 +234,11 @@
 
 	chunk_size = mddev->param.chunk_size >> 10;
 	chunksize_bits = ffz(~chunk_size);
-	block = bh->b_rsector >> 1;
+	block = bio->bi_sector >> 1;
 	hash = conf->hash_table + block / conf->smallest->size;
 
 	/* Sanity check */
-	if (chunk_size < (block % chunk_size) + (bh->b_size >> 10))
+	if (chunk_size < (block % chunk_size) + (bio->bi_size >> 10))
 		goto bad_map;
  
 	if (!hash)
@@ -255,7 +254,7 @@
 	} else
 		zone = hash->zone0;
     
-	sect_in_chunk = bh->b_rsector & ((chunk_size<<1) -1);
+	sect_in_chunk = bio->bi_sector & ((chunk_size<<1) -1);
 	chunk = (block - zone->zone_offset) / (zone->nb_dev << chunksize_bits);
 	tmp_dev = zone->dev[(block >> chunksize_bits) % zone->nb_dev];
 	rsect = (((chunk << chunksize_bits) + zone->dev_offset)<<1)
@@ -265,8 +264,8 @@
 	 * The new BH_Lock semantics in ll_rw_blk.c guarantee that this
 	 * is the only IO operation happening on this bh.
 	 */
-	bh->b_rdev = tmp_dev->dev;
-	bh->b_rsector = rsect;
+	bio->bi_dev = tmp_dev->dev;
+	bio->bi_sector = rsect;
 
 	/*
 	 * Let the main block layer submit the IO and resolve recursion:
@@ -274,7 +273,7 @@
 	return 1;
 
 bad_map:
-	printk ("raid0_make_request bug: can't convert block across chunks or bigger than %dk %ld %d\n", chunk_size, bh->b_rsector, bh->b_size >> 10);
+	printk ("raid0_make_request bug: can't convert block across chunks or bigger than %dk %ld %d\n", chunk_size, bio->bi_sector, bio->bi_size >> 10);
 	goto outerr;
 bad_hash:
 	printk("raid0_make_request bug: hash==NULL for block %ld\n", block);
@@ -285,7 +284,7 @@
 bad_zone1:
 	printk ("raid0_make_request bug: hash->zone1==NULL for block %ld\n", block);
  outerr:
-	buffer_IO_error(bh);
+	bio_io_error(bio);
 	return 0;
 }
 			   
diff -urN linux-2.5.1-pre4/drivers/net/irda/vlsi_ir.c linux/drivers/net/irda/vlsi_ir.c
--- linux-2.5.1-pre4/drivers/net/irda/vlsi_ir.c	Fri Nov  9 13:41:42 2001
+++ linux/drivers/net/irda/vlsi_ir.c	Sat Dec  1 00:42:26 2001
@@ -2,7 +2,7 @@
  *
  *	vlsi_ir.c:	VLSI82C147 PCI IrDA controller driver for Linux
  *
- *	Version:	0.3, Sep 30, 2001
+ *	Version:	0.3a, Nov 10, 2001
  *
  *	Copyright (c) 2001 Martin Diehl
  *
@@ -490,7 +490,7 @@
 	if (mode == IFF_FIR)
 		config ^= IRENABLE_FIR_ON;
 	else if (mode == IFF_MIR)
-		config ^= (IRENABLE_FIR_ON|IRENABLE_CRC16_ON);
+		config ^= (IRENABLE_MIR_ON|IRENABLE_CRC16_ON);
 	else
 		config ^= IRENABLE_SIR_ON;
 
@@ -877,6 +877,7 @@
 	idev->irlap = irlap_open(ndev,&idev->qos,hwname);
 
 	netif_start_queue(ndev);
+	outw(0, ndev->base_addr+VLSI_PIO_PROMPT);	/* kick hw state machine */
 
 	printk(KERN_INFO "%s: device %s operational using (%d,%d) tx,rx-ring\n",
 		__FUNCTION__, ndev->name, ringsize[0], ringsize[1]);
@@ -1200,7 +1201,6 @@
 	int			alloc_size;
 
 
-	vlsi_reg_debug(0x3000, "vlsi initial state");
 	if (pci_enable_device(pdev))
 		goto out;
 
diff -urN linux-2.5.1-pre4/drivers/scsi/ide-scsi.c linux/drivers/scsi/ide-scsi.c
--- linux-2.5.1-pre4/drivers/scsi/ide-scsi.c	Sat Dec  1 00:41:36 2001
+++ linux/drivers/scsi/ide-scsi.c	Sat Dec  1 00:42:26 2001
@@ -708,16 +708,25 @@
 		printk ("ide-scsi: %s: building DMA table, %d segments, %dkB total\n", drive->name, segments, pc->request_transfer >> 10);
 #endif /* IDESCSI_DEBUG_LOG */
 		while (segments--) {
-			bh->bi_io_vec[0].bv_page = sg->page;
+			struct page *page = sg->page;
+			int offset = sg->offset;
+
+			if (!page) {
+				BUG_ON(!sg->address);
+				page = virt_to_page(sg->address);
+				offset = (unsigned long) sg->address & ~PAGE_MASK;
+			}
+				
+			bh->bi_io_vec[0].bv_page = page;
 			bh->bi_io_vec[0].bv_len = sg->length;
-			bh->bi_io_vec[0].bv_offset = sg->offset;
+			bh->bi_io_vec[0].bv_offset = offset;
 			bh->bi_size = sg->length;
 			bh = bh->bi_next;
 			/*
 			 * just until scsi_merge is fixed up...
 			 */
-			BUG_ON(PageHighMem(sg->page));
-			sg->address = page_address(sg->page) + sg->offset;
+			BUG_ON(PageHighMem(page));
+			sg->address = page_address(page) + offset;
 			sg++;
 		}
 	} else {
diff -urN linux-2.5.1-pre4/drivers/scsi/qlogicfc.c linux/drivers/scsi/qlogicfc.c
--- linux-2.5.1-pre4/drivers/scsi/qlogicfc.c	Sat Dec  1 00:41:36 2001
+++ linux/drivers/scsi/qlogicfc.c	Sat Dec  1 00:42:26 2001
@@ -2042,10 +2042,6 @@
 	}
 
 	pci_set_master(pdev);
-	if (!(command & PCI_COMMAND_MASTER)) {
-		printk("qlogicfc%d : bus mastering is disabled\n", hostdata->host_id);
-		return 1;
-	}
 	if (revision != ISP2100_REV_ID1 && revision != ISP2100_REV_ID3 && revision != ISP2200_REV_ID5)
 		printk("qlogicfc%d : new isp2x00 revision ID (%d)\n", hostdata->host_id,  revision);
 
diff -urN linux-2.5.1-pre4/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- linux-2.5.1-pre4/drivers/scsi/scsi.c	Sat Dec  1 00:41:36 2001
+++ linux/drivers/scsi/scsi.c	Sat Dec  1 00:42:27 2001
@@ -741,11 +741,13 @@
  		  int timeout, int retries)
 {
 	DECLARE_COMPLETION(wait);
+	request_queue_t *q = &SRpnt->sr_device->request_queue;
 	
 	SRpnt->sr_request.waiting = &wait;
 	SRpnt->sr_request.rq_status = RQ_SCSI_BUSY;
 	scsi_do_req (SRpnt, (void *) cmnd,
 		buffer, bufflen, scsi_wait_done, timeout, retries);
+	generic_unplug_device(q);
 	wait_for_completion(&wait);
 	SRpnt->sr_request.waiting = NULL;
 	if( SRpnt->sr_command != NULL )
diff -urN linux-2.5.1-pre4/drivers/scsi/scsi_lib.c linux/drivers/scsi/scsi_lib.c
--- linux-2.5.1-pre4/drivers/scsi/scsi_lib.c	Sat Dec  1 00:41:36 2001
+++ linux/drivers/scsi/scsi_lib.c	Sat Dec  1 00:42:27 2001
@@ -582,7 +582,7 @@
 	 */
 	if (good_sectors > 0) {
 		SCSI_LOG_HLCOMPLETE(1, printk("%ld sectors total, %d sectors done.\n",
-					      req->nr_sectors good_sectors));
+					      req->nr_sectors, good_sectors));
 		SCSI_LOG_HLCOMPLETE(1, printk("use_sg is %d\n ", SCpnt->use_sg));
 
 		req->errors = 0;
diff -urN linux-2.5.1-pre4/drivers/sgi/char/shmiq.c linux/drivers/sgi/char/shmiq.c
--- linux-2.5.1-pre4/drivers/sgi/char/shmiq.c	Sat Dec  1 00:41:36 2001
+++ linux/drivers/sgi/char/shmiq.c	Sat Dec  1 00:42:27 2001
@@ -330,12 +330,11 @@
 
 	size  = vma->vm_end - vma->vm_start;
 	start = vma->vm_start; 
-	spin_lock( &shmiqs [minor].shmiq_lock );
-	mem = (unsigned long) shmiqs [minor].shmiq_vaddr =  vmalloc_uncached (size);
-	if (!mem) {
-		spin_unlock( &shmiqs [minor].shmiq_lock );
+	mem = vmalloc_uncached (size);
+	if (!mem)
 		return -EINVAL;
-	}
+	spin_lock( &shmiqs [minor].shmiq_lock );
+	hmiqs [minor].shmiq_vaddr = mem;
 
 	/* Prevent the swapper from considering these pages for swap and touching them */
 	vma->vm_flags    |= (VM_SHM  | VM_LOCKED | VM_IO);
@@ -344,12 +343,12 @@
 	/* Uncache the pages */
 	vma->vm_page_prot = PAGE_USERIO;
 
-	error = vmap_page_range (vma->vm_start, size, mem);
 
 	shmiqs [minor].tail = 0;
 	/* Init the shared memory input queue */
-	memset (shmiqs [minor].shmiq_vaddr, 0, size);
 	spin_unlock( &shmiqs [minor].shmiq_lock );
+	memset (shmiqs [minor].shmiq_vaddr, 0, size);
+	error = vmap_page_range (vma->vm_start, size, mem);
 	return error;
 }
 
@@ -435,8 +434,10 @@
 		return -EINVAL;
 
 	spin_lock( &shmiqs [minor].shmiq_lock );
-	if (shmiqs [minor].opened == 0)
+	if (shmiqs [minor].opened == 0) {
+		spin_unlock( &shmiqs [minor].shmiq_lock );
 		return -EINVAL;
+	}
 
 	shmiq_qcntl_fasync (-1, filp, 0);
 	shmiqs [minor].opened 	   = 0;
diff -urN linux-2.5.1-pre4/fs/Makefile linux/fs/Makefile
--- linux-2.5.1-pre4/fs/Makefile	Sat Dec  1 00:41:42 2001
+++ linux/fs/Makefile	Sat Dec  1 00:42:27 2001
@@ -24,6 +24,7 @@
 
 subdir-$(CONFIG_PROC_FS)	+= proc
 subdir-y			+= partitions
+subdir-y			+= driverfs
 
 # Do not add any filesystems before this line
 subdir-$(CONFIG_EXT3_FS)	+= ext3    # Before ext2 so root fs can be ext3
diff -urN linux-2.5.1-pre4/fs/bio.c linux/fs/bio.c
--- linux-2.5.1-pre4/fs/bio.c	Sat Dec  1 00:41:42 2001
+++ linux/fs/bio.c	Sat Dec  1 00:42:27 2001
@@ -588,7 +588,6 @@
 	bio->bi_sector = sector;
 	bio->bi_dev = dev;
 	bio->bi_idx = 0;
-	bio->bi_flags |= 1 << BIO_PREBUILT;
 	bio->bi_end_io = bio_end_io_kio;
 	bio->bi_private = kio;
 
@@ -619,9 +618,13 @@
 		sector += nbytes >> 9;
 		size -= nbytes;
 		total_nr_pages--;
+		kio->offset += nbytes;
 	}
 
 queue_io:
+	if (bio->bi_vcnt > 1)
+		bio->bi_flags |= 1 << BIO_PREBUILT;
+
 	submit_bio(rw, bio);
 
 	if (total_nr_pages)
diff -urN linux-2.5.1-pre4/fs/block_dev.c linux/fs/block_dev.c
--- linux-2.5.1-pre4/fs/block_dev.c	Sat Dec  1 00:41:42 2001
+++ linux/fs/block_dev.c	Sat Dec  1 00:42:27 2001
@@ -234,7 +234,7 @@
 #define HASH_SIZE	(1UL << HASH_BITS)
 #define HASH_MASK	(HASH_SIZE-1)
 static struct list_head bdev_hashtable[HASH_SIZE];
-static spinlock_t bdev_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t bdev_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 static kmem_cache_t * bdev_cachep;
 
 #define alloc_bdev() \
diff -urN linux-2.5.1-pre4/fs/buffer.c linux/fs/buffer.c
--- linux-2.5.1-pre4/fs/buffer.c	Sat Dec  1 00:41:42 2001
+++ linux/fs/buffer.c	Sat Dec  1 00:42:27 2001
@@ -74,7 +74,7 @@
 static rwlock_t hash_table_lock = RW_LOCK_UNLOCKED;
 
 static struct buffer_head *lru_list[NR_LIST];
-static spinlock_t lru_list_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t lru_list_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 static int nr_buffers_type[NR_LIST];
 static unsigned long size_buffers_type[NR_LIST];
 
@@ -794,9 +794,10 @@
 	return;
 }
 
-inline void set_buffer_async_io(struct buffer_head *bh) {
-    bh->b_end_io = end_buffer_io_async ;
-    mark_buffer_async(bh, 1);
+inline void set_buffer_async_io(struct buffer_head *bh)
+{
+	bh->b_end_io = end_buffer_io_async;
+	mark_buffer_async(bh, 1);
 }
 
 /*
@@ -1036,7 +1037,7 @@
 {
 	unsigned long dirty, tot, hard_dirty_limit, soft_dirty_limit;
 
-	dirty = size_buffers_type[BUF_DIRTY] >> PAGE_SHIFT;
+	dirty = (size_buffers_type[BUF_DIRTY] + size_buffers_type[BUF_LOCKED]) >> PAGE_SHIFT;
 	tot = nr_free_buffer_pages();
 
 	dirty *= 100;
diff -urN linux-2.5.1-pre4/fs/cramfs/inode.c linux/fs/cramfs/inode.c
--- linux-2.5.1-pre4/fs/cramfs/inode.c	Thu Oct 25 13:53:53 2001
+++ linux/fs/cramfs/inode.c	Sat Dec  1 00:42:27 2001
@@ -19,6 +19,7 @@
 #include <linux/locks.h>
 #include <linux/blkdev.h>
 #include <linux/cramfs_fs.h>
+#include <asm/semaphore.h>
 
 #include <asm/uaccess.h>
 
@@ -33,6 +34,9 @@
 static struct file_operations cramfs_directory_operations;
 static struct address_space_operations cramfs_aops;
 
+static DECLARE_MUTEX(read_mutex);
+
+
 /* These two macros may change in future, to provide better st_ino
    semantics. */
 #define CRAMINO(x)	((x)->offset?(x)->offset<<2:1)
@@ -199,8 +203,10 @@
 	for (i = 0; i < READ_BUFFERS; i++)
 		buffer_blocknr[i] = -1;
 
+	down(&read_mutex);
 	/* Read the first block and get the superblock from it */
 	memcpy(&super, cramfs_read(sb, 0, sizeof(super)), sizeof(super));
+	up(&read_mutex);
 
 	/* Do sanity checks on the superblock */
 	if (super.magic != CRAMFS_MAGIC) {
@@ -291,7 +297,9 @@
 		char *name;
 		int namelen, error;
 
+		down(&read_mutex);
 		de = cramfs_read(sb, OFFSET(inode) + offset, sizeof(*de)+256);
+		up(&read_mutex);
 		name = (char *)(de+1);
 
 		/*
@@ -332,7 +340,9 @@
 		char *name;
 		int namelen, retval;
 
+		down(&read_mutex);
 		de = cramfs_read(dir->i_sb, OFFSET(dir) + offset, sizeof(*de)+256);
+		up(&read_mutex);
 		name = (char *)(de+1);
 
 		/* Try to take advantage of sorted directories */
@@ -384,18 +394,22 @@
 		u32 start_offset, compr_len;
 
 		start_offset = OFFSET(inode) + maxblock*4;
+		down(&read_mutex);
 		if (page->index)
 			start_offset = *(u32 *) cramfs_read(sb, blkptr_offset-4, 4);
-		compr_len = (*(u32 *) cramfs_read(sb, blkptr_offset, 4)
-			     - start_offset);
+		compr_len = (*(u32 *) cramfs_read(sb, blkptr_offset, 4) - start_offset);
+		up(&read_mutex);
 		pgdata = kmap(page);
 		if (compr_len == 0)
 			; /* hole */
-		else
+		else {
+			down(&read_mutex);
 			bytes_filled = cramfs_uncompress_block(pgdata,
 				 PAGE_CACHE_SIZE,
 				 cramfs_read(sb, start_offset, compr_len),
 				 compr_len);
+			up(&read_mutex);
+		}
 	} else
 		pgdata = kmap(page);
 	memset(pgdata + bytes_filled, 0, PAGE_CACHE_SIZE - bytes_filled);
diff -urN linux-2.5.1-pre4/fs/dcache.c linux/fs/dcache.c
--- linux-2.5.1-pre4/fs/dcache.c	Wed Oct  3 22:57:36 2001
+++ linux/fs/dcache.c	Sat Dec  1 00:42:27 2001
@@ -29,7 +29,7 @@
 #define DCACHE_PARANOIA 1
 /* #define DCACHE_DEBUG 1 */
 
-spinlock_t dcache_lock = SPIN_LOCK_UNLOCKED;
+spinlock_t dcache_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 
 /* Right now the dcache depends on the kernel lock */
 #define check_lock()	if (!kernel_locked()) BUG()
diff -urN linux-2.5.1-pre4/fs/devfs/base.c linux/fs/devfs/base.c
--- linux-2.5.1-pre4/fs/devfs/base.c	Sat Dec  1 00:41:42 2001
+++ linux/fs/devfs/base.c	Sat Dec  1 00:42:27 2001
@@ -3358,6 +3358,9 @@
     printk ("%s: devfs_debug: 0x%0x\n", DEVFS_NAME, devfs_debug);
 #endif
     printk ("%s: boot_options: 0x%0x\n", DEVFS_NAME, boot_options);
+    devfsd_buf_cache = kmem_cache_create ("devfsd_event",
+					  sizeof (struct devfsd_buf_entry),
+					  0, 0, NULL, NULL);
     err = register_filesystem (&devfs_fs_type);
     if (!err)
     {
@@ -3372,9 +3375,6 @@
 {
     int err;
 
-    devfsd_buf_cache = kmem_cache_create ("devfsd_event",
-					  sizeof (struct devfsd_buf_entry),
-					  0, 0, NULL, NULL);
     if ( !(boot_options & OPTION_MOUNT) ) return;
     err = do_mount ("none", "/dev", "devfs", 0, "");
     if (err == 0) printk ("Mounted devfs on /dev\n");
diff -urN linux-2.5.1-pre4/fs/driverfs/Makefile linux/fs/driverfs/Makefile
--- linux-2.5.1-pre4/fs/driverfs/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/fs/driverfs/Makefile	Sat Dec  1 00:42:27 2001
@@ -0,0 +1,8 @@
+O_TARGET := driverfs.o
+
+export-objs := inode.o
+
+obj-y := inode.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff -urN linux-2.5.1-pre4/fs/driverfs/inode.c linux/fs/driverfs/inode.c
--- linux-2.5.1-pre4/fs/driverfs/inode.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/driverfs/inode.c	Sat Dec  1 00:42:27 2001
@@ -0,0 +1,822 @@
+/*
+ * driverfs.c - The device driver file system
+ *
+ * Copyright (c) 2001 Patrick Mochel <mochel@osdl.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 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This is a simple, ram-based filesystem, which allows kernel
+ * callbacks for read/write of files.
+ *
+ * Please see Documentation/filesystems/driverfs.txt for more information.
+ */
+
+#include <linux/list.h>
+#include <linux/init.h>
+#include <linux/pagemap.h>
+#include <linux/stat.h>
+#include <linux/fs.h>
+#include <linux/dcache.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/driverfs_fs.h>
+
+#include <asm/uaccess.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+# define DBG(x...) printk(x)
+#else
+# define DBG(x...)
+#endif
+
+/* Random magic number */
+#define DRIVERFS_MAGIC 0x42454552
+
+static struct super_operations driverfs_ops;
+static struct address_space_operations driverfs_aops;
+static struct file_operations driverfs_dir_operations;
+static struct file_operations driverfs_file_operations;
+static struct inode_operations driverfs_dir_inode_operations;
+static struct dentry_operations driverfs_dentry_dir_ops;
+static struct dentry_operations driverfs_dentry_file_ops;
+
+
+static struct vfsmount *driverfs_mount;
+static spinlock_t mount_lock = SPIN_LOCK_UNLOCKED;
+static int mount_count = 0;
+
+static int driverfs_statfs(struct super_block *sb, struct statfs *buf)
+{
+	buf->f_type = DRIVERFS_MAGIC;
+	buf->f_bsize = PAGE_CACHE_SIZE;
+	buf->f_namelen = 255;
+	return 0;
+}
+
+static struct dentry *driverfs_lookup(struct inode *dir, struct dentry *dentry)
+{
+	d_add(dentry, NULL);
+	return NULL;
+}
+
+struct inode *driverfs_get_inode(struct super_block *sb, int mode, int dev)
+{
+	struct inode *inode = new_inode(sb);
+
+	if (inode) {
+		inode->i_mode = mode;
+		inode->i_uid = current->fsuid;
+		inode->i_gid = current->fsgid;
+		inode->i_blksize = PAGE_CACHE_SIZE;
+		inode->i_blocks = 0;
+		inode->i_rdev = NODEV;
+		inode->i_mapping->a_ops = &driverfs_aops;
+		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+		switch (mode & S_IFMT) {
+		default:
+			init_special_inode(inode, mode, dev);
+			break;
+		case S_IFREG:
+			inode->i_fop = &driverfs_file_operations;
+			break;
+		case S_IFDIR:
+			inode->i_op = &driverfs_dir_inode_operations;
+			inode->i_fop = &driverfs_dir_operations;
+			break;
+		}
+	}
+	return inode;
+}
+
+static int driverfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev)
+{
+	struct inode *inode = driverfs_get_inode(dir->i_sb, mode, dev);
+	int error = -ENOSPC;
+
+	if (inode) {
+		d_instantiate(dentry, inode);
+		dget(dentry);
+		error = 0;
+	}
+	return error;
+}
+
+static int driverfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	dentry->d_op = &driverfs_dentry_dir_ops;
+	return driverfs_mknod(dir, dentry, mode | S_IFDIR, 0);
+}
+
+static int driverfs_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+	dentry->d_op = &driverfs_dentry_file_ops;
+ 	return driverfs_mknod(dir, dentry, mode | S_IFREG, 0);
+}
+
+static int
+driverfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
+{
+	struct inode *inode = old_dentry->d_inode;
+
+	if(S_ISDIR(inode->i_mode))
+		return -EPERM;
+
+	inode->i_nlink++;
+	atomic_inc(&inode->i_count);
+ 	dget(dentry);
+	d_instantiate(dentry, inode);
+	return 0;
+}
+
+static inline int driverfs_positive(struct dentry *dentry)
+{
+	return (dentry->d_inode && !d_unhashed(dentry));
+}
+
+static int driverfs_empty(struct dentry *dentry)
+{
+	struct list_head *list;
+
+	spin_lock(&dcache_lock);
+
+	list_for_each(list, &dentry->d_subdirs) {
+		struct dentry *de = list_entry(list, struct dentry, d_child);
+		if (driverfs_positive(de)) {
+			spin_unlock(&dcache_lock);
+			return 0;
+		}
+	}
+
+	spin_unlock(&dcache_lock);
+	return 1;
+}
+
+static int driverfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+	int error = -ENOTEMPTY;
+
+	if (driverfs_empty(dentry)) {
+		struct inode *inode = dentry->d_inode;
+
+		inode->i_nlink--;
+		dput(dentry);
+		error = 0;
+	}
+	return error;
+}
+
+static int
+driverfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+		 struct inode *new_dir, struct dentry *new_dentry)
+{
+	int error = -ENOTEMPTY;
+
+	if (driverfs_empty(new_dentry)) {
+		struct inode *inode = new_dentry->d_inode;
+		if (inode) {
+			inode->i_nlink--;
+			dput(new_dentry);
+		}
+		error = 0;
+	}
+	return error;
+}
+
+static int driverfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+{
+	int error;
+
+	error = driverfs_mknod(dir, dentry, S_IFLNK | S_IRWXUGO, 0);
+	if (!error) {
+		int l = strlen(symname) + 1;
+		struct inode *inode = dentry->d_inode;
+		error = block_symlink(inode,symname,l);
+	}
+	return error;
+}
+
+#define driverfs_rmdir driverfs_unlink
+
+/**
+ * driverfs_read_file - "read" data from a file.
+ * @file:	file pointer
+ * @buf:	buffer to fill
+ * @count:	number of bytes to read
+ * @ppos:	starting offset in file
+ *
+ * Userspace wants data from a file. It is up to the creator of the file to
+ * provide that data.
+ * There is a struct driver_file_entry embedded in file->private_data. We
+ * obtain that and check if the read callback is implemented. If so, we call
+ * it, passing the data field of the file entry.
+ * Said callback is responsible for filling the buffer and returning the number
+ * of bytes it put in it. We update @ppos correctly.
+ */
+static ssize_t
+driverfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+	struct driver_file_entry * entry;
+	unsigned char *page;
+	ssize_t retval = 0;
+
+	entry = (struct driver_file_entry *)file->private_data;
+	if (!entry) {
+		DBG("%s: file entry is NULL\n",__FUNCTION__);
+		return -ENOENT;
+	}
+
+	if (!entry->ops || !entry->ops->read) {
+		DBG("%s: no read callback\n",__FUNCTION__);
+		return -ENOENT;
+	}
+
+	page = (unsigned char*)__get_free_page(GFP_KERNEL);
+	if (!page) {
+		retval = -ENOMEM;
+		goto done;
+	}
+
+	while (count > 0) {
+		ssize_t len;
+
+		len = entry->ops->read(page,count,*ppos,entry->data);
+
+		if (len <= 0) {
+			if (len < 0)
+				retval = len;
+			break;
+		}
+
+		if (copy_to_user(buf,page,len)) {
+			retval = -EFAULT;
+			break;
+		}
+
+		*ppos += len;
+		count -= len;
+		buf += len;
+		retval += len;
+	}
+	free_page((unsigned long)page);
+
+ done:
+	return retval;
+}
+
+/**
+ * driverfs_write_file - "write" to a file
+ * @file:	file pointer
+ * @buf:	data to write
+ * @count:	number of bytes
+ * @ppos:	starting offset
+ *
+ * Similarly to driverfs_read_file, we act essentially as a bit pipe.
+ * We check for a "write" callback in file->private_data, and pass
+ * @buffer, @count, @ppos, and the file entry's data to the callback.
+ * The number of bytes written is returned, and we handle updating
+ * @ppos properly.
+ */
+static ssize_t
+driverfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+	struct driver_file_entry * entry;
+	ssize_t retval = 0;
+
+	entry = (struct driver_file_entry *)file->private_data;
+	if (!entry) {
+		DBG("%s: file entry is NULL\n",__FUNCTION__);
+		return -ENOENT;
+	}
+
+	if (!entry->ops || !entry->ops->write) {
+		DBG("%s: no write callback\n",__FUNCTION__);
+		retval = -ENOENT;
+		goto done;
+	}
+
+	while (count > 0) {
+		ssize_t len;
+
+		len = entry->ops->write(buf,count,*ppos,entry->data);
+
+		if (len <= 0) {
+			if (len < 0)
+				retval = len;
+			break;
+		}
+		retval += len;
+		count -= len;
+		*ppos += len;
+		buf += len;
+	}
+
+ done:
+	return retval;
+}
+
+static loff_t
+driverfs_file_lseek(struct file *file, loff_t offset, int orig)
+{
+	loff_t retval = -EINVAL;
+
+	switch(orig) {
+	case 0:
+		if (offset > 0) {
+			file->f_pos = offset;
+			retval = file->f_pos;
+		}
+		break;
+	case 1:
+		if ((offset + file->f_pos) > 0) {
+			file->f_pos += offset;
+			retval = file->f_pos;
+		}
+		break;
+	default:
+		break;
+	}
+	return retval;
+}
+
+static int driverfs_open_file(struct inode * inode, struct file * filp)
+{
+	if (filp && inode)
+		filp->private_data = inode->u.generic_ip;
+
+	return 0;
+}
+
+static int driverfs_sync_file (struct file *file, struct dentry *dentry, int datasync)
+{
+	return 0;
+}
+
+/* When the dentry goes away (the refcount hits 0), free the
+ * driver_file_entry for it.
+ */
+static int driverfs_d_delete_file (struct dentry * dentry)
+{
+	struct driver_file_entry * entry;
+
+	entry = (struct driver_file_entry *)dentry->d_fsdata;
+	if (entry)
+		kfree(dentry);
+	return 0;
+}
+
+/* Similar to above - if this dentry goes away, free the
+ * driver_dir_entry associated with it..
+ */
+static int driverfs_d_delete_dir (struct dentry * dentry)
+{
+	struct driver_dir_entry * entry;
+	entry = (struct driver_dir_entry *)dentry->d_fsdata;
+	if (entry)
+		kfree(entry);
+	return 0;
+}
+
+static struct address_space_operations driverfs_aops = {
+
+};
+
+static struct file_operations driverfs_file_operations = {
+	read:		driverfs_read_file,
+	write:		driverfs_write_file,
+	llseek:		driverfs_file_lseek,
+	mmap:		generic_file_mmap,
+	open:		driverfs_open_file,
+	fsync:		driverfs_sync_file,
+};
+
+static struct file_operations driverfs_dir_operations = {
+	read:		generic_read_dir,
+	readdir:	dcache_readdir,
+	fsync:		driverfs_sync_file,
+};
+
+static struct inode_operations driverfs_dir_inode_operations = {
+	create:		driverfs_create,
+	lookup:		driverfs_lookup,
+	link:		driverfs_link,
+	unlink:		driverfs_unlink,
+	symlink:	driverfs_symlink,
+	mkdir:		driverfs_mkdir,
+	rmdir:		driverfs_rmdir,
+	mknod:		driverfs_mknod,
+	rename:		driverfs_rename,
+};
+
+static struct dentry_operations driverfs_dentry_file_ops = {
+	d_delete:	driverfs_d_delete_file,
+};
+
+static struct dentry_operations driverfs_dentry_dir_ops = {
+	d_delete:	driverfs_d_delete_dir,
+};
+
+static struct super_operations driverfs_ops = {
+	statfs:		driverfs_statfs,
+	put_inode:	force_delete,
+};
+
+static struct super_block*
+driverfs_read_super(struct super_block *sb, void *data, int silent)
+{
+	struct inode *inode;
+	struct dentry *root;
+
+	sb->s_blocksize = PAGE_CACHE_SIZE;
+	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+	sb->s_magic = DRIVERFS_MAGIC;
+	sb->s_op = &driverfs_ops;
+	inode = driverfs_get_inode(sb, S_IFDIR | 0755, 0);
+
+	if (!inode) {
+		DBG("%s: could not get inode!\n",__FUNCTION__);
+		return NULL;
+	}
+
+	root = d_alloc_root(inode);
+	if (!root) {
+		DBG("%s: could not get root dentry!\n",__FUNCTION__);
+		iput(inode);
+		return NULL;
+	}
+	sb->s_root = root;
+	return sb;
+}
+
+static DECLARE_FSTYPE(driverfs_fs_type, "driverfs", driverfs_read_super, FS_SINGLE | FS_LITTER);
+
+static int get_mount(void)
+{
+	struct vfsmount * mnt;
+
+	spin_lock(&mount_lock);
+	if (driverfs_mount) {
+		mntget(driverfs_mount);
+		++mount_count;
+		spin_unlock(&mount_lock);
+		goto go_ahead;
+	}
+
+	spin_unlock(&mount_lock);
+	mnt = kern_mount(&driverfs_fs_type);
+
+	if (IS_ERR(mnt)) {
+		printk(KERN_ERR "driverfs: could not mount!\n");
+		return -ENODEV;
+	}
+
+	spin_lock(&mount_lock);
+	if (!driverfs_mount) {
+		driverfs_mount = mnt;
+		++mount_count;
+		spin_unlock(&mount_lock);
+		goto go_ahead;
+	}
+
+	mntget(driverfs_mount);
+	++mount_count;
+	spin_unlock(&mount_lock);
+
+ go_ahead:
+	DBG("driverfs: mount_count = %d\n",mount_count);
+	return 0;
+}
+
+static void put_mount(void)
+{
+	struct vfsmount * mnt;
+
+	spin_lock(&mount_lock);
+	mnt = driverfs_mount;
+	--mount_count;
+	if (!mount_count)
+		driverfs_mount = NULL;
+	spin_unlock(&mount_lock);
+	mntput(mnt);
+	DBG("driverfs: mount_count = %d\n",mount_count);
+}
+
+int __init init_driverfs_fs(void)
+{
+	int error;
+
+	DBG("%s: registering filesystem.\n",__FUNCTION__);
+	error = register_filesystem(&driverfs_fs_type);
+
+	if (error)
+		DBG(KERN_ERR "%s: register_filesystem failed with %d\n",
+		       __FUNCTION__,error);
+
+	return error;
+}
+
+static void __exit exit_driverfs_fs(void)
+{
+	unregister_filesystem(&driverfs_fs_type);
+}
+#if 0
+module_init(init_driverfs_fs);
+module_exit(exit_driverfs_fs);
+#endif
+
+EXPORT_SYMBOL(driverfs_create_file);
+EXPORT_SYMBOL(driverfs_create_dir);
+EXPORT_SYMBOL(driverfs_remove_file);
+EXPORT_SYMBOL(driverfs_remove_dir);
+
+MODULE_DESCRIPTION("The device driver filesystem");
+MODULE_LICENSE("GPL");
+
+/**
+ * driverfs_create_dir_entry - allocate and initialise directory entry
+ * @name:	name of the directory
+ * @mode:	permissions of the dir
+ */
+struct driver_dir_entry *
+driverfs_create_dir_entry(const char * name, mode_t mode)
+{
+	struct driver_dir_entry * entry;
+	int size = sizeof(struct driver_dir_entry) + strlen(name) + 1;
+
+
+	entry = kmalloc(size, GFP_KERNEL);
+	if (!entry)
+		return NULL;
+
+	memset(entry, 0, size);
+	strcpy((char *)entry + sizeof(struct driver_dir_entry), name);
+
+	entry->name = (char *)entry + sizeof(struct driver_dir_entry);
+
+	INIT_LIST_HEAD(&entry->files);
+	entry->mode = mode;
+
+	return entry;
+}
+
+/**
+ * driverfs_create_dir - create a directory in the filesystem
+ * @entry:	directory entry
+ * @parent:	parent directory entry
+ */
+int
+driverfs_create_dir(struct driver_dir_entry * entry,
+		    struct driver_dir_entry * parent)
+{
+	struct dentry * dentry = NULL;
+	struct dentry * parent_dentry;
+	struct qstr qstr;
+	int error = 0;
+
+	if (!entry)
+		return -EINVAL;
+
+	get_mount();
+
+	parent_dentry = parent ? parent->dentry : NULL;
+
+	if (!parent_dentry)
+		if (driverfs_mount && driverfs_mount->mnt_sb)
+			parent_dentry = driverfs_mount->mnt_sb->s_root;
+
+	if (!parent_dentry) {
+		put_mount();
+		return -EFAULT;
+	}
+
+	dget(parent_dentry);
+	down(&parent_dentry->d_inode->i_sem);
+
+	qstr.name = entry->name;
+	qstr.len = strlen(entry->name);
+	qstr.hash = full_name_hash(entry->name,qstr.len);
+
+	dentry = lookup_hash(&qstr,parent_dentry);
+	if (IS_ERR(dentry)) {
+		error = PTR_ERR(dentry);
+		up(&parent_dentry->d_inode->i_sem);
+		dput(parent_dentry);
+	} else {
+		dentry->d_fsdata = (void *) entry;
+		entry->dentry = dentry;
+		error = vfs_mkdir(parent_dentry->d_inode,dentry,entry->mode);
+		up(&parent_dentry->d_inode->i_sem);
+	}
+
+	if (error)
+		put_mount();
+	return error;
+}
+
+/**
+ * driverfs_create_file - create a file
+ * @entry:	structure describing the file
+ * @parent:	directory to create it in
+ */
+int
+driverfs_create_file(struct driver_file_entry * entry,
+		     struct driver_dir_entry * parent)
+{
+	struct dentry * dentry;
+	struct dentry * parent_dentry;
+	struct qstr qstr;
+	int error = 0;
+
+	if (!entry || !parent)
+		return -EINVAL;
+
+	/* make sure we're mounted */
+	get_mount();
+
+	if (!parent->dentry) {
+		put_mount();
+		return -EINVAL;
+	}
+
+	/* make sure daddy doesn't run out on us again... */
+	parent_dentry = dget(parent->dentry);
+	down(&parent_dentry->d_inode->i_sem);
+
+	qstr.name = entry->name;
+	qstr.len = strlen(entry->name);
+	qstr.hash = full_name_hash(entry->name,qstr.len);
+
+	dentry = lookup_hash(&qstr,parent_dentry);
+	if (IS_ERR(dentry))
+		error = PTR_ERR(dentry);
+	else
+		error = vfs_create(parent_dentry->d_inode,dentry,entry->mode);
+
+
+	/* Still good? Ok, then fill in the blanks: */
+	if (!error) {
+		dentry->d_fsdata = (void *)entry;
+		dentry->d_inode->u.generic_ip = (void *)entry;
+
+		entry->dentry = dentry;
+
+		list_add_tail(&entry->node,&parent->files);
+	}
+	up(&parent_dentry->d_inode->i_sem);
+
+	if (error) {
+		dput(parent_dentry);
+		put_mount();
+	}
+	return error;
+}
+
+/**
+ * __remove_file - remove a regular file in the filesystem
+ * @dentry:	dentry of file to remove
+ *
+ * Call unlink to remove the file, and dput on the dentry to drop
+ * the refcount.
+ */
+static void __remove_file(struct dentry * dentry)
+{
+	dget(dentry);
+	down(&dentry->d_inode->i_sem);
+
+	vfs_unlink(dentry->d_parent->d_inode,dentry);
+
+	unlock_dir(dentry);
+
+	/* remove reference count from when file was created */
+	dput(dentry);
+
+	put_mount();
+}
+
+/**
+ * driverfs_remove_file - exported file removal
+ * @dir:	directory the file supposedly resides in
+ * @name:	name of the file
+ *
+ * Try and find the file in the dir's list.
+ * If it's there, call __remove_file() (above) for the dentry.
+ */
+void driverfs_remove_file(struct driver_dir_entry * dir, const char * name)
+{
+	struct dentry * dentry;
+	struct list_head * node;
+
+	if (!dir->dentry)
+		return;
+
+	dentry = dget(dir->dentry);
+	down(&dentry->d_inode->i_sem);
+
+	node = dir->files.next;
+	while (node != &dir->files) {
+		struct driver_file_entry * entry = NULL;
+
+		entry = list_entry(node,struct driver_file_entry,node);
+		if (!strcmp(entry->name,name)) {
+			list_del_init(node);
+
+			__remove_file(entry->dentry);
+			break;
+		}
+		node = node->next;
+	}
+	unlock_dir(dentry);
+}
+
+/**
+ * driverfs_remove_dir - exportable directory removal
+ * @dir:	directory to remove
+ *
+ * To make sure we don't orphan anyone, first remove
+ * all the children in the list, then do vfs_rmdir() to remove it
+ * and decrement the refcount..
+ */
+void driverfs_remove_dir(struct driver_dir_entry * dir)
+{
+	struct list_head * node;
+	struct dentry * dentry;
+
+	if (!dir->dentry)
+		goto done;
+
+	/* lock the directory while we remove the files */
+	dentry = dget(dir->dentry);
+	down(&dentry->d_inode->i_sem);
+
+	node = dir->files.next;
+	while (node != &dir->files) {
+		struct driver_file_entry * entry;
+		entry = list_entry(node,struct driver_file_entry,node);
+
+		list_del_init(node);
+
+		__remove_file(entry->dentry);
+
+		node = dir->files.next;
+	}
+
+	/* now lock the parent, so we can remove this directory */
+	lock_parent(dentry);
+
+	vfs_rmdir(dentry->d_parent->d_inode,dentry);
+	double_unlock(dentry,dentry->d_parent);
+
+	/* remove reference count from when directory was created */
+	dput(dentry);
+ done:
+	put_mount();
+}
+
+/**
+ * driverfs_create_entry - allocate and initialise a struct driver_file_entry
+ * @name:	name of the file
+ * @mode:	permissions of the file
+ * @ops:	Operations for the file
+ * @data:	data that will be passed back to the callback
+ *
+ */
+struct driver_file_entry *
+driverfs_create_entry (const char * name, mode_t mode,
+		       struct driverfs_operations * ops, void * data)
+{
+	struct driver_file_entry * entry;
+	int size;
+
+	size = sizeof(struct driver_file_entry) + strlen(name) + 1;
+
+	entry = kmalloc(size,GFP_KERNEL);
+	if (!entry)
+		return NULL;
+
+	memset(entry, 0, size);
+	strcpy((char *)entry + sizeof(struct driver_file_entry), name);
+
+	entry->name = (char *)entry + sizeof(struct driver_file_entry);
+
+	INIT_LIST_HEAD(&entry->node);
+
+	entry->mode = mode;
+	entry->ops = ops;
+	entry->data = data;
+
+	return entry;
+}
+
diff -urN linux-2.5.1-pre4/fs/fat/file.c linux/fs/fat/file.c
--- linux-2.5.1-pre4/fs/fat/file.c	Sun Aug 12 10:56:56 2001
+++ linux/fs/fat/file.c	Sat Dec  1 00:42:27 2001
@@ -48,7 +48,7 @@
 }
 
 
-int fat_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create)
+int fat_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create)
 {
 	struct super_block *sb = inode->i_sb;
 	unsigned long phys;
diff -urN linux-2.5.1-pre4/fs/inode.c linux/fs/inode.c
--- linux-2.5.1-pre4/fs/inode.c	Sat Dec  1 00:41:42 2001
+++ linux/fs/inode.c	Sat Dec  1 00:42:27 2001
@@ -1176,6 +1176,16 @@
 	unused_inodes_flush_task.routine = try_to_sync_unused_inodes;
 }
 
+static inline void do_atime_update(struct inode *inode)
+{
+	unsigned long time = CURRENT_TIME;
+	if (inode->i_atime != time) {
+		inode->i_atime = time;
+		mark_inode_dirty_sync(inode);
+	}
+}
+
+
 /**
  *	update_atime	-	update the access time
  *	@inode: inode accessed
@@ -1190,8 +1200,7 @@
 	if ( IS_NOATIME (inode) ) return;
 	if ( IS_NODIRATIME (inode) && S_ISDIR (inode->i_mode) ) return;
 	if ( IS_RDONLY (inode) ) return;
-	inode->i_atime = CURRENT_TIME;
-	mark_inode_dirty_sync (inode);
+	do_atime_update(inode);
 }   /*  End Function update_atime  */
 
 
diff -urN linux-2.5.1-pre4/fs/minix/itree_common.c linux/fs/minix/itree_common.c
--- linux-2.5.1-pre4/fs/minix/itree_common.c	Fri Sep  7 09:45:51 2001
+++ linux/fs/minix/itree_common.c	Sat Dec  1 00:42:27 2001
@@ -140,7 +140,7 @@
 	return -EAGAIN;
 }
 
-static inline int get_block(struct inode * inode, long block,
+static inline int get_block(struct inode * inode, sector_t block,
 			struct buffer_head *bh_result, int create)
 {
 	int err = -EIO;
diff -urN linux-2.5.1-pre4/fs/namei.c linux/fs/namei.c
--- linux-2.5.1-pre4/fs/namei.c	Wed Oct 17 14:46:29 2001
+++ linux/fs/namei.c	Sat Dec  1 00:42:27 2001
@@ -434,6 +434,8 @@
 		mntput(nd->mnt);
 		nd->mnt = parent;
 	}
+	while (d_mountpoint(nd->dentry) && __follow_down(&nd->mnt, &nd->dentry))
+		;
 }
 
 /*
diff -urN linux-2.5.1-pre4/fs/namespace.c linux/fs/namespace.c
--- linux-2.5.1-pre4/fs/namespace.c	Sat Dec  1 00:41:42 2001
+++ linux/fs/namespace.c	Sat Dec  1 00:42:27 2001
@@ -561,6 +561,67 @@
 	return err;
 }
 
+static int do_move_mount(struct nameidata *nd, char *old_name)
+{
+	struct nameidata old_nd, parent_nd;
+	struct vfsmount *p;
+	int err = 0;
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (!old_name || !*old_name)
+		return -EINVAL;
+	if (path_init(old_name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &old_nd))
+		err = path_walk(old_name, &old_nd);
+	if (err)
+		return err;
+
+	down(&mount_sem);
+	while(d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry))
+		;
+	err = -EINVAL;
+	if (!check_mnt(nd->mnt) || !check_mnt(old_nd.mnt))
+		goto out;
+
+	err = -ENOENT;
+	down(&nd->dentry->d_inode->i_zombie);
+	if (IS_DEADDIR(nd->dentry->d_inode))
+		goto out1;
+
+	spin_lock(&dcache_lock);
+	if (!IS_ROOT(nd->dentry) && d_unhashed(nd->dentry))
+		goto out2;
+
+	err = -EINVAL;
+	if (old_nd.dentry != old_nd.mnt->mnt_root)
+		goto out2;
+
+	if (old_nd.mnt == old_nd.mnt->mnt_parent)
+		goto out2;
+
+	if (S_ISDIR(nd->dentry->d_inode->i_mode) !=
+	      S_ISDIR(old_nd.dentry->d_inode->i_mode))
+		goto out2;
+
+	err = -ELOOP;
+	for (p = nd->mnt; p->mnt_parent!=p; p = p->mnt_parent)
+		if (p == old_nd.mnt)
+			goto out2;
+	err = 0;
+
+	detach_mnt(old_nd.mnt, &parent_nd);
+	attach_mnt(old_nd.mnt, nd);
+out2:
+	spin_unlock(&dcache_lock);
+out1:
+	up(&nd->dentry->d_inode->i_zombie);
+out:
+	up(&mount_sem);
+	if (!err)
+		path_release(&parent_nd);
+	path_release(&old_nd);
+	return err;
+}
+
 static int do_add_mount(struct nameidata *nd, char *type, int flags,
 			int mnt_flags, char *name, void *data)
 {
@@ -677,6 +738,8 @@
 				    data_page);
 	else if (flags & MS_BIND)
 		retval = do_loopback(&nd, dev_name, flags & MS_REC);
+	else if (flags & MS_MOVE)
+		retval = do_move_mount(&nd, dev_name);
 	else
 		retval = do_add_mount(&nd, type_page, flags, mnt_flags,
 				      dev_name, data_page);
@@ -906,6 +969,8 @@
 	root_vfsmnt = do_kern_mount("rootfs", 0, "rootfs", NULL);
 	if (IS_ERR(root_vfsmnt))
 		panic("can't allocate root vfsmount");
+	set_fs_pwd(current->fs, root_vfsmnt, root_vfsmnt->mnt_root);
+	set_fs_root(current->fs, root_vfsmnt, root_vfsmnt->mnt_root);
 }
 
 void __init mnt_init(unsigned long mempages)
@@ -992,6 +1057,8 @@
 		}
 		path_release(&devfs_nd);
 	}
+	set_fs_pwd(current->fs, root_vfsmnt, root_vfsmnt->mnt_root);
+	set_fs_root(current->fs, root_vfsmnt, root_vfsmnt->mnt_root);
 	spin_lock(&dcache_lock);
 	detach_mnt(old_rootmnt, &parent_nd);
 	spin_unlock(&dcache_lock);
diff -urN linux-2.5.1-pre4/fs/reiserfs/journal.c linux/fs/reiserfs/journal.c
--- linux-2.5.1-pre4/fs/reiserfs/journal.c	Fri Nov  9 14:18:25 2001
+++ linux/fs/reiserfs/journal.c	Sat Dec  1 00:42:27 2001
@@ -147,7 +147,7 @@
   }
   bn = allocate_bitmap_node(p_s_sb) ;
   if (!bn) {
-    current->policy = SCHED_YIELD ;
+    current->policy |= SCHED_YIELD ;
     schedule() ;
     goto repeat ;
   }
diff -urN linux-2.5.1-pre4/fs/super.c linux/fs/super.c
--- linux-2.5.1-pre4/fs/super.c	Sat Dec  1 00:41:42 2001
+++ linux/fs/super.c	Sat Dec  1 00:42:27 2001
@@ -374,6 +374,56 @@
 	put_super(s);
 	return 0;
 }
+ 
+/**
+ *	insert_super	-	put superblock on the lists
+ *	@s:	superblock in question
+ *	@type:	filesystem type it will belong to
+ *
+ *	Associates superblock with fs type and puts it on per-type and global
+ *	superblocks' lists.  Should be called with sb_lock held; drops it.
+ */
+static void insert_super(struct super_block *s, struct file_system_type *type)
+{
+	s->s_type = type;
+	list_add(&s->s_list, super_blocks.prev);
+	list_add(&s->s_instances, &type->fs_supers);
+	spin_unlock(&sb_lock);
+	get_filesystem(type);
+}
+
+void put_unnamed_dev(kdev_t dev);	/* should become static */
+
+/**
+ *	remove_super	-	makes superblock unreachable
+ *	@s:	superblock in question
+ *
+ *	Removes superblock from the lists, unlocks it, drop the reference
+ *	and releases the hosting device.  @s should have no active
+ *	references by that time and after remove_super() it's essentially
+ *	in rundown mode - all remaining references are temporary, no new
+ *	reference of any sort are going to appear and all holders of
+ *	temporary ones will eventually drop them.  At that point superblock
+ *	itself will be destroyed; all its contents is already gone.
+ */
+static void remove_super(struct super_block *s)
+{
+	kdev_t dev = s->s_dev;
+	struct block_device *bdev = s->s_bdev;
+	struct file_system_type *fs = s->s_type;
+
+	spin_lock(&sb_lock);
+	list_del(&s->s_list);
+	list_del(&s->s_instances);
+	spin_unlock(&sb_lock);
+	up_write(&s->s_umount);
+	put_super(s);
+	put_filesystem(fs);
+	if (bdev)
+		blkdev_put(bdev, BDEV_FS);
+	else
+		put_unnamed_dev(dev);
+}
 
 struct vfsmount *alloc_vfsmnt(void);
 void free_vfsmnt(struct vfsmount *mnt);
@@ -510,11 +560,8 @@
 	s->s_dev = dev;
 	s->s_bdev = bdev;
 	s->s_flags = flags;
-	s->s_type = type;
 	spin_lock(&sb_lock);
-	list_add (&s->s_list, super_blocks.prev);
-	list_add (&s->s_instances, &type->fs_supers);
-	spin_unlock(&sb_lock);
+	insert_super(s, type);
 	lock_super(s);
 	if (!type->read_super(s, data, flags & MS_VERBOSE ? 1 : 0))
 		goto out_fail;
@@ -527,17 +574,9 @@
 	return s;
 
 out_fail:
-	s->s_dev = 0;
-	s->s_bdev = 0;
-	s->s_type = NULL;
 	unlock_super(s);
 	deactivate_super(s);
-	spin_lock(&sb_lock);
-	list_del(&s->s_list);
-	list_del(&s->s_instances);
-	spin_unlock(&sb_lock);
-	up_write(&s->s_umount);
-	put_super(s);
+	remove_super(s);
 	return NULL;
 }
 
@@ -609,13 +648,17 @@
 		goto out;
 	check_disk_change(dev);
 	error = -EACCES;
-	if (!(flags & MS_RDONLY) && is_read_only(dev))
-		goto out1;
+	if (!(flags & MS_RDONLY) && is_read_only(dev)) {
+		blkdev_put(bdev, BDEV_FS);
+		goto out;
+	}
 
 	error = -ENOMEM;
 	s = alloc_super();
-	if (!s)
-		goto out1;
+	if (!s) {
+		blkdev_put(bdev, BDEV_FS);
+		goto out;
+	}
 
 	error = -EBUSY;
 restart:
@@ -629,7 +672,8 @@
 		    ((flags ^ old->s_flags) & MS_RDONLY)) {
 			spin_unlock(&sb_lock);
 			destroy_super(s);
-			goto out1;
+			blkdev_put(bdev, BDEV_FS);
+			goto out;
 		}
 		if (!grab_super(old))
 			goto restart;
@@ -641,10 +685,7 @@
 	s->s_dev = dev;
 	s->s_bdev = bdev;
 	s->s_flags = flags;
-	s->s_type = fs_type;
-	list_add (&s->s_list, super_blocks.prev);
-	list_add (&s->s_instances, &fs_type->fs_supers);
-	spin_unlock(&sb_lock);
+	insert_super(s, fs_type);
 
 	error = -EINVAL;
 	lock_super(s);
@@ -652,24 +693,13 @@
 		goto out_fail;
 	s->s_flags |= MS_ACTIVE;
 	unlock_super(s);
-	get_filesystem(fs_type);
 	path_release(&nd);
 	return s;
 
 out_fail:
-	s->s_dev = 0;
-	s->s_bdev = 0;
-	s->s_type = NULL;
 	unlock_super(s);
 	deactivate_super(s);
-	spin_lock(&sb_lock);
-	list_del(&s->s_list);
-	list_del(&s->s_instances);
-	spin_unlock(&sb_lock);
-	up_write(&s->s_umount);
-	put_super(s);
-out1:
-	blkdev_put(bdev, BDEV_FS);
+	remove_super(s);
 out:
 	path_release(&nd);
 	return ERR_PTR(error);
@@ -685,11 +715,8 @@
 		struct super_block * sb;
 		error = -EINVAL;
 		sb = read_super(dev, NULL, fs_type, flags, data);
-		if (sb) {
-			get_filesystem(fs_type);
+		if (sb)
 			return sb;
-		}
-		put_unnamed_dev(dev);
 	}
 	return ERR_PTR(error);
 }
@@ -724,39 +751,24 @@
 		}
 		s->s_dev = dev;
 		s->s_flags = flags;
-		s->s_type = fs_type;
-		list_add (&s->s_list, super_blocks.prev);
-		list_add (&s->s_instances, &fs_type->fs_supers);
-		spin_unlock(&sb_lock);
+		insert_super(s, fs_type);
 		lock_super(s);
 		if (!fs_type->read_super(s, data, flags & MS_VERBOSE ? 1 : 0))
 			goto out_fail;
 		s->s_flags |= MS_ACTIVE;
 		unlock_super(s);
-		get_filesystem(fs_type);
 		return s;
 
 	out_fail:
-		s->s_dev = 0;
-		s->s_bdev = 0;
-		s->s_type = NULL;
 		unlock_super(s);
 		deactivate_super(s);
-		spin_lock(&sb_lock);
-		list_del(&s->s_list);
-		list_del(&s->s_instances);
-		spin_unlock(&sb_lock);
-		up_write(&s->s_umount);
-		put_super(s);
-		put_unnamed_dev(dev);
+		remove_super(s);
 		return ERR_PTR(-EINVAL);
 	}
 }
 
 void kill_super(struct super_block *sb)
 {
-	struct block_device *bdev;
-	kdev_t dev;
 	struct dentry *root = sb->s_root;
 	struct file_system_type *fs = sb->s_type;
 	struct super_operations *sop = sb->s_op;
@@ -789,24 +801,9 @@
 			"Self-destruct in 5 seconds.  Have a nice day...\n");
 	}
 
-	dev = sb->s_dev;
-	sb->s_dev = 0;		/* Free the superblock */
-	bdev = sb->s_bdev;
-	sb->s_bdev = NULL;
-	put_filesystem(fs);
-	sb->s_type = NULL;
-	unlock_super(sb);
 	unlock_kernel();
-	if (bdev)
-		blkdev_put(bdev, BDEV_FS);
-	else
-		put_unnamed_dev(dev);
-	spin_lock(&sb_lock);
-	list_del(&sb->s_list);
-	list_del(&sb->s_instances);
-	spin_unlock(&sb_lock);
-	up_write(&sb->s_umount);
-	put_super(sb);
+	unlock_super(sb);
+	remove_super(sb);
 }
 
 /*
@@ -957,7 +954,6 @@
 	int retval;
 	void *handle;
 	char path[64];
-	int path_start = -1;
 	char *name = "/dev/root";
 	char *fs_names, *p;
 #ifdef CONFIG_ROOT_NFS
@@ -1012,27 +1008,28 @@
 	handle = devfs_find_handle (NULL, ROOT_DEVICE_NAME,
 	                            MAJOR (ROOT_DEV), MINOR (ROOT_DEV),
 				    DEVFS_SPECIAL_BLK, 1);
-	if (handle)  /*  Sigh: bd*() functions only paper over the cracks  */
-	{
-	    unsigned major, minor;
-
-	    devfs_get_maj_min (handle, &major, &minor);
-	    ROOT_DEV = MKDEV (major, minor);
+	if (handle) {
+		int n;
+		unsigned major, minor;
+
+		devfs_get_maj_min (handle, &major, &minor);
+		ROOT_DEV = MKDEV (major, minor);
+		if (!ROOT_DEV)
+			panic("I have no root and I want to scream");
+		n = devfs_generate_path (handle, path + 5, sizeof (path) - 5);
+		if (n >= 0) {
+			name = path + n;
+			devfs_mk_symlink (NULL, "root", DEVFS_FL_DEFAULT,
+					  name + 5, NULL, NULL);
+			memcpy (name, "/dev/", 5);
+		}
 	}
 
-	/*
-	 * Probably pure paranoia, but I'm less than happy about delving into
-	 * devfs crap and checking it right now. Later.
-	 */
-	if (!ROOT_DEV)
-		panic("I have no root and I want to scream");
-
 retry:
 	bdev = bdget(kdev_t_to_nr(ROOT_DEV));
 	if (!bdev)
 		panic(__FUNCTION__ ": unable to allocate root device");
 	bdev->bd_op = devfs_get_ops (handle);
-	path_start = devfs_generate_path (handle, path + 5, sizeof (path) - 5);
 	mode = FMODE_READ;
 	if (!(root_mountflags & MS_RDONLY))
 		mode |= FMODE_WRITE;
@@ -1046,6 +1043,7 @@
 		 * Allow the user to distinguish between failed open
 		 * and bad superblock on root device.
 		 */
+Eio:
 		printk ("VFS: Cannot open root device \"%s\" or %s\n",
 			root_device_name, kdevname (ROOT_DEV));
 		printk ("Please append a correct \"root=\" boot option\n");
@@ -1068,11 +1066,17 @@
 		struct file_system_type * fs_type = get_fs_type(p);
 		if (!fs_type)
   			continue;
+		atomic_inc(&bdev->bd_count);
+		retval = blkdev_get(bdev, mode, 0, BDEV_FS);
+		if (retval)
+			goto Eio;
   		sb = read_super(ROOT_DEV, bdev, fs_type,
 				root_mountflags, root_mount_data);
-		if (sb) 
-			goto mount_it;
 		put_filesystem(fs_type);
+		if (sb) {
+			blkdev_put(bdev, BDEV_FS);
+			goto mount_it;
+		}
 	}
 	panic("VFS: Unable to mount root fs on %s", kdevname(ROOT_DEV));
 
@@ -1082,12 +1086,6 @@
 	printk ("VFS: Mounted root (%s filesystem)%s.\n", p,
 		(sb->s_flags & MS_RDONLY) ? " readonly" : "");
 	putname(fs_names);
-	if (path_start >= 0) {
-		name = path + path_start;
-		devfs_mk_symlink (NULL, "root", DEVFS_FL_DEFAULT,
-				  name + 5, NULL, NULL);
-		memcpy (name, "/dev/", 5);
-	}
 	vfsmnt = alloc_vfsmnt();
 	if (!vfsmnt)
 		panic("VFS: alloc_vfsmnt failed for root fs");
diff -urN linux-2.5.1-pre4/include/asm-alpha/page.h linux/include/asm-alpha/page.h
--- linux-2.5.1-pre4/include/asm-alpha/page.h	Sat Dec  1 00:41:42 2001
+++ linux/include/asm-alpha/page.h	Sat Dec  1 00:42:27 2001
@@ -73,6 +73,12 @@
 			BUG();			\
 	} while (0)
 
+#define BUG_ON(condition)			\
+	do {					\
+		if (unlikely((int)(condition)))	\
+			BUG();			\
+	} while (0)
+
 /* Pure 2^n version of get_order */
 extern __inline__ int get_order(unsigned long size)
 {
diff -urN linux-2.5.1-pre4/include/linux/bio.h linux/include/linux/bio.h
--- linux-2.5.1-pre4/include/linux/bio.h	Sat Dec  1 00:41:42 2001
+++ linux/include/linux/bio.h	Sat Dec  1 00:42:27 2001
@@ -1,12 +1,11 @@
 /*
- * New 2.5 block I/O model
+ * 2.5 block I/O model
  *
  * Copyright (C) 2001 Jens Axboe <axboe@suse.de>
  *
- * This program is free software; you can redistribute it and/or mo
- * it under the terms of the GNU General Public License as publishe
- * the Free Software Foundation; either version 2 of the License, o
- * (at your option) any later version.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -121,7 +120,7 @@
  */
 #define __BVEC_END(bio) bio_iovec_idx((bio), (bio)->bi_idx - 1)
 #define BIO_CONTIG(bio, nxt) \
-	(bvec_to_phys(__BVEC_END((bio)) + (bio)->bi_size) ==bio_to_phys((nxt)))
+	(bvec_to_phys(__BVEC_END((bio)) + (bio)->bi_size) == bio_to_phys((nxt)))
 #define __BIO_SEG_BOUNDARY(addr1, addr2, mask) \
 	(((addr1) | (mask)) == (((addr2) - 1) | (mask)))
 #define BIO_SEG_BOUNDARY(q, b1, b2) \
diff -urN linux-2.5.1-pre4/include/linux/blkdev.h linux/include/linux/blkdev.h
--- linux-2.5.1-pre4/include/linux/blkdev.h	Sat Dec  1 00:41:42 2001
+++ linux/include/linux/blkdev.h	Sat Dec  1 00:42:27 2001
@@ -100,11 +100,6 @@
 	unsigned long		bounce_pfn;
 
 	/*
-	 * for memory zoning (<= 4GB and > 4GB)
-	 */
-	int			bounce_gfp;
-
-	/*
 	 * This is used to remove the plug when tq_disk runs.
 	 */
 	struct tq_struct	plug_tq;
@@ -177,11 +172,11 @@
 
 #ifdef CONFIG_HIGHMEM
 
-extern void create_bounce(unsigned long pfn, struct bio **bio_orig, int gfp_mask);
+extern void create_bounce(unsigned long pfn, struct bio **bio_orig);
 
 extern inline void blk_queue_bounce(request_queue_t *q, struct bio **bio)
 {
-	create_bounce(q->bounce_pfn, bio, q->bounce_gfp);
+	create_bounce(q->bounce_pfn, bio);
 }
 
 #else /* CONFIG_HIGHMEM */
diff -urN linux-2.5.1-pre4/include/linux/cache.h linux/include/linux/cache.h
--- linux-2.5.1-pre4/include/linux/cache.h	Thu Nov 22 11:46:18 2001
+++ linux/include/linux/cache.h	Sat Dec  1 00:42:27 2001
@@ -34,4 +34,12 @@
 #endif
 #endif /* __cacheline_aligned */
 
+#ifndef __cacheline_aligned_in_smp
+#ifdef CONFIG_SMP
+#define __cacheline_aligned_in_smp __cacheline_aligned
+#else
+#define __cacheline_aligned_in_smp
+#endif /* CONFIG_SMP */
+#endif
+
 #endif /* __LINUX_CACHE_H */
diff -urN linux-2.5.1-pre4/include/linux/device.h linux/include/linux/device.h
--- linux-2.5.1-pre4/include/linux/device.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/device.h	Sat Dec  1 00:42:27 2001
@@ -0,0 +1,257 @@
+/*
+ * device.h - generic, centralized driver model
+ *
+ * Copyright (c) 2001 Patrick Mochel <mochel@osdl.org>
+ *
+ * This is a relatively simple centralized driver model.
+ * The data structures were mainly lifted directly from the PCI
+ * driver model. These are thought to be the common fields that
+ * are relevant to all device buses.
+ *
+ * All the devices are arranged in a tree. All devices should
+ * have some sort of parent bus of whom they are children of.
+ * Devices should not be direct children of the system root.
+ *
+ * Device drivers should not directly call the device_* routines
+ * or access the contents of struct device directly. Instead,
+ * abstract that from the drivers and write bus-specific wrappers
+ * that do it for you.
+ *
+ * See Documentation/driver-model.txt for more information.
+ */
+
+#ifndef _DEVICE_H_
+#define _DEVICE_H_
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/ioport.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/driverfs_fs.h>
+
+#define DEVICE_NAME_SIZE	80
+#define DEVICE_ID_SIZE		32
+#define BUS_ID_SIZE		16
+
+
+enum {
+	SUSPEND_NOTIFY,
+	SUSPEND_SAVE_STATE,
+	SUSPEND_DISABLE,
+	SUSPEND_POWER_DOWN,
+};
+
+enum {
+	RESUME_POWER_ON,
+	RESUME_RESTORE_STATE,
+	RESUME_ENABLE,
+};
+
+enum {
+	REMOVE_NOTIFY,
+	REMOVE_FREE_RESOURCES,
+};
+
+struct device;
+struct iobus;
+
+struct device_driver {
+	int	(*probe)	(struct device * dev);
+	int 	(*remove)	(struct device * dev, u32 flags);
+
+	int	(*suspend)	(struct device * dev, u32 state, u32 stage);
+	int	(*resume)	(struct device * dev, u32 stage);
+};
+
+struct device {
+	struct list_head node;		/* node in sibling list */
+	struct iobus	*parent;	/* parent bus */
+
+	struct iobus	*subordinate;	/* only valid if this device is a
+					   bridge device */
+
+	char	name[DEVICE_NAME_SIZE];	/* descriptive ascii string */
+	char	bus_id[BUS_ID_SIZE];	/* position on parent bus */
+
+	spinlock_t	lock;		/* lock for the device to ensure two
+					   different layers don't access it at
+					   the same time. */
+	atomic_t	refcount;	/* refcount to make sure the device
+					 * persists for the right amount of time */
+
+	struct driver_dir_entry	* dir;
+
+	struct device_driver *driver;	/* which driver has allocated this
+					   device */
+	void		*driver_data;	/* data private to the driver */
+	void		*platform_data;	/* Platform specific data (e.g. ACPI,
+					   BIOS data relevant to device */
+
+	u32		current_state;  /* Current operating state. In
+					   ACPI-speak, this is D0-D3, D0
+					   being fully functional, and D3
+					   being off. */
+
+	unsigned char *saved_state;	/* saved device state */
+};
+
+/*
+ * struct bus_type - descriptor for a type of bus
+ * There are some instances when you need to know what type of bus a
+ * device is on. Instead of having some sort of enumerated integer type,
+ * each struct iobus will have a pointer to a struct bus_type that gives
+ * actually meaningful data.
+ * There should be one struct bus_type for each type of bus (one for PCI,
+ * one for USB, etc).
+ */
+struct iobus_driver {
+	char	name[16];	/* ascii descriptor of type of bus */
+	struct	list_head node; /* node in global list of bus drivers */
+
+	int	(*scan)		(struct iobus*);
+	int	(*add_device)	(struct iobus*, char*);
+};
+
+extern int iobus_register_driver(struct iobus_driver * driver);
+extern void iobus_unregister_driver(struct iobus_driver * driver);
+extern struct iobus_driver * iobus_find_driver(char *name);
+
+struct iobus {
+	spinlock_t	lock;		/* lock for bus */
+	atomic_t	refcount;
+
+	struct list_head node;		/* node in sibling list */
+	struct iobus *parent;		/* parent bus */
+	struct list_head children;	/* children buses */
+	struct list_head devices;	/* children devices */
+
+	struct device *self;		/* pointer to controlling device */
+	struct driver_dir_entry	* dir;	/* driverfs directory */
+
+	char	name[DEVICE_NAME_SIZE];
+	char	bus_id[BUS_ID_SIZE];
+
+	struct	iobus_driver *driver;	/* bus operations */
+};
+
+static inline struct device *
+list_to_dev(struct list_head *node)
+{
+	return list_entry(node, struct device, node);
+}
+
+static inline struct iobus *
+list_to_iobus(const struct list_head *node)
+{
+	return list_entry(node, struct iobus, node);
+}
+
+/*
+ * High level routines for use by the bus drivers
+ */
+extern int device_register(struct device * dev);
+extern struct device * device_alloc(void);
+extern void device_init_dev(struct device * dev);
+
+extern int iobus_register(struct iobus * iobus);
+extern struct iobus * iobus_alloc(void);
+extern void iobus_init(struct iobus * iobus);
+
+extern int device_create_file(struct device *device, const char * name, mode_t mode,
+			      struct driverfs_operations * ops, void * data);
+extern void device_remove_file(struct device * dev, const char * name);
+
+extern int iobus_create_file(struct iobus *bus, const char * name, mode_t mode,
+			     struct driverfs_operations * ops, void * data);
+extern void iobus_remove_file(struct iobus * iobus, const char * name);
+
+
+/*
+ * Platform "fixup" functions - allow the platform to have their say
+ * about devices and actions that the general device layer doesn't
+ * know about.
+ */
+/* Notify platform of device discovery */
+extern int (*platform_notify)(struct device * dev);
+
+extern int (*platform_notify_remove)(struct device * dev);
+
+extern int device_driver_init(void);
+
+
+/* device and bus locking helpers.
+ *
+ * FIXME: Is there anything else we need to do?
+ */
+static inline void lock_device(struct device * dev)
+{
+	spin_lock(&dev->lock);
+}
+
+static inline void unlock_device(struct device * dev)
+{
+	spin_unlock(&dev->lock);
+}
+
+/**
+ * get_device - atomically increment the reference count for the device.
+ *
+ */
+static inline void get_device(struct device * dev)
+{
+	atomic_inc(&dev->refcount);
+}
+
+extern void put_device(struct device * dev);
+
+
+/**
+ * valid_device - check if device is valid
+ * @dev:	device in question
+ *
+ * Check whether or not a device can be operated on.
+ * If so, increment the reference count and carry on.
+ */
+static inline int valid_device(struct device * dev)
+{
+	int val;
+
+	lock_device(dev);
+	val = atomic_read(&dev->refcount);
+	if (val)
+		get_device(dev);
+	unlock_device(dev);
+	return (val > 0);
+}
+
+
+static inline void lock_iobus(struct iobus * iobus)
+{
+	spin_lock(&iobus->lock);
+}
+
+static inline void unlock_iobus(struct iobus * iobus)
+{
+	spin_unlock(&iobus->lock);
+}
+
+static inline void get_iobus(struct iobus * iobus)
+{
+	atomic_inc(&iobus->refcount);
+}
+
+static inline int valid_iobus(struct iobus * iobus)
+{
+	int val;
+	lock_iobus(iobus);
+	val = atomic_read(&iobus->refcount);
+	if (val)
+		get_iobus(iobus);
+	unlock_iobus(iobus);
+	return (val > 0);
+}
+
+extern void put_iobus(struct iobus * iobus);
+
+#endif /* _DEVICE_H_ */
diff -urN linux-2.5.1-pre4/include/linux/driverfs_fs.h linux/include/linux/driverfs_fs.h
--- linux-2.5.1-pre4/include/linux/driverfs_fs.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/driverfs_fs.h	Sat Dec  1 00:42:27 2001
@@ -0,0 +1,73 @@
+/*
+ * driverfs_fs.h - definitions for the device driver filesystem
+ *
+ * Copyright (c) 2001 Patrick Mochel <mochel@osdl.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 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This is a simple, ram-based filesystem, which allows kernel
+ * callbacks for read/write of files.
+ *
+ * Please see Documentation/filesystems/driverfs.txt for more information.
+ */
+
+#ifndef _DRIVER_FS_H_
+#define _DRIVER_FS_H_
+
+struct driverfs_operations {
+	ssize_t	(*read) (char *, size_t, loff_t, void *);
+	ssize_t (*write)(const char *, size_t, loff_t, void*);
+};
+
+struct driver_dir_entry {
+	char			* name;
+	struct dentry		* dentry;
+	mode_t			mode;
+	struct list_head	files;
+};
+
+struct driver_file_entry {
+	struct driver_dir_entry * parent;
+	struct list_head	node;
+	char			* name;
+	mode_t			mode;
+	struct dentry		* dentry;
+	void			* data;
+	struct driverfs_operations	* ops;
+};
+
+extern struct driver_dir_entry *
+driverfs_create_dir_entry(const char * name, mode_t mode);
+
+extern int
+driverfs_create_dir(struct driver_dir_entry *, struct driver_dir_entry *);
+
+extern void
+driverfs_remove_dir(struct driver_dir_entry * entry);
+
+extern struct driver_file_entry *
+driverfs_create_entry (const char * name, mode_t mode,
+		       struct driverfs_operations * ops, void * data);
+
+extern int
+driverfs_create_file(struct driver_file_entry * entry,
+		     struct driver_dir_entry * parent);
+
+extern void
+driverfs_remove_file(struct driver_dir_entry *, const char * name);
+
+extern int init_driverfs_fs(void);
+
+#endif /* _DDFS_H_ */
diff -urN linux-2.5.1-pre4/include/linux/fs.h linux/include/linux/fs.h
--- linux-2.5.1-pre4/include/linux/fs.h	Sat Dec  1 00:41:42 2001
+++ linux/include/linux/fs.h	Sat Dec  1 00:42:27 2001
@@ -111,6 +111,7 @@
 #define MS_NOATIME	1024	/* Do not update access times. */
 #define MS_NODIRATIME	2048	/* Do not update directory access times */
 #define MS_BIND		4096
+#define MS_MOVE		8192
 #define MS_REC		16384
 #define MS_VERBOSE	32768
 #define MS_ACTIVE	(1<<30)
diff -urN linux-2.5.1-pre4/include/linux/highmem.h linux/include/linux/highmem.h
--- linux-2.5.1-pre4/include/linux/highmem.h	Sat Dec  1 00:41:42 2001
+++ linux/include/linux/highmem.h	Sat Dec  1 00:42:27 2001
@@ -13,7 +13,7 @@
 /* declarations for linux/mm/highmem.c */
 unsigned int nr_free_highpages(void);
 
-extern void create_bounce(unsigned long pfn, struct bio **bio_orig, int gfp_mask);
+extern void create_bounce(unsigned long pfn, struct bio **bio_orig);
 
 static inline char *bh_kmap(struct buffer_head *bh)
 {
diff -urN linux-2.5.1-pre4/include/linux/msdos_fs.h linux/include/linux/msdos_fs.h
--- linux-2.5.1-pre4/include/linux/msdos_fs.h	Fri Oct 12 13:48:42 2001
+++ linux/include/linux/msdos_fs.h	Sat Dec  1 00:42:27 2001
@@ -273,7 +273,7 @@
 extern struct inode_operations fat_file_inode_operations;
 extern ssize_t fat_file_read(struct file *filp, char *buf, size_t count,
 			     loff_t *ppos);
-extern int fat_get_block(struct inode *inode, long iblock,
+extern int fat_get_block(struct inode *inode, sector_t iblock,
 			 struct buffer_head *bh_result, int create);
 extern ssize_t fat_file_write(struct file *filp, const char *buf, size_t count,
 			      loff_t *ppos);
diff -urN linux-2.5.1-pre4/include/linux/pci_ids.h linux/include/linux/pci_ids.h
--- linux-2.5.1-pre4/include/linux/pci_ids.h	Fri Nov  9 14:11:15 2001
+++ linux/include/linux/pci_ids.h	Sat Dec  1 00:42:27 2001
@@ -1229,6 +1229,7 @@
 #define PCI_DEVICE_ID_AVM_B1		0x0700
 #define PCI_DEVICE_ID_AVM_C4		0x0800
 #define PCI_DEVICE_ID_AVM_A1		0x0a00
+#define PCI_DEVICE_ID_AVM_A1_V2		0x0e00
 #define PCI_DEVICE_ID_AVM_C2		0x1100
 #define PCI_DEVICE_ID_AVM_T1		0x1200
 
diff -urN linux-2.5.1-pre4/include/net/irda/irlmp.h linux/include/net/irda/irlmp.h
--- linux-2.5.1-pre4/include/net/irda/irlmp.h	Fri Nov  9 14:22:17 2001
+++ linux/include/net/irda/irlmp.h	Sat Dec  1 00:42:27 2001
@@ -264,4 +264,16 @@
 	return IRLAP_GET_TX_QUEUE_LEN(self->lap->irlap);
 }
 
+/* After doing a irlmp_dup(), this get one of the two socket back into
+ * a state where it's waiting incomming connections.
+ * Note : this can be used *only* if the socket is not yet connected
+ * (i.e. NO irlmp_connect_response() done on this socket).
+ * - Jean II */
+static inline void irlmp_listen(struct lsap_cb *self)
+{
+	self->dlsap_sel = LSAP_ANY;
+	self->lap = NULL;
+	self->lsap_state = LSAP_DISCONNECTED;
+}
+
 #endif
diff -urN linux-2.5.1-pre4/include/net/irda/irttp.h linux/include/net/irda/irttp.h
--- linux-2.5.1-pre4/include/net/irda/irttp.h	Mon Dec 11 13:33:09 2000
+++ linux/include/net/irda/irttp.h	Sat Dec  1 00:42:27 2001
@@ -148,6 +148,17 @@
 	return self->max_seg_size;
 }
 
+/* After doing a irttp_dup(), this get one of the two socket back into
+ * a state where it's waiting incomming connections.
+ * Note : this can be used *only* if the socket is not yet connected
+ * (i.e. NO irttp_connect_response() done on this socket).
+ * - Jean II */
+static inline void irttp_listen(struct tsap_cb *self)
+{
+	irlmp_listen(self->lsap);
+	self->dtsap_sel = LSAP_ANY;
+}
+
 extern struct irttp_cb *irttp;
 
 #endif /* IRTTP_H */
diff -urN linux-2.5.1-pre4/init/main.c linux/init/main.c
--- linux-2.5.1-pre4/init/main.c	Sat Dec  1 00:41:42 2001
+++ linux/init/main.c	Sat Dec  1 00:42:27 2001
@@ -31,6 +31,8 @@
 #include <asm/io.h>
 #include <asm/bugs.h>
 
+#include <linux/device.h>
+
 #if defined(CONFIG_ARCH_S390)
 #include <asm/s390mach.h>
 #include <asm/ccwcache.h>
@@ -693,6 +695,9 @@
 #if defined(CONFIG_ARCH_S390)
 	s390_init_machine_check();
 #endif
+
+	/* bring up the device tree */
+	device_driver_init();
 
 #ifdef CONFIG_PCI
 	pci_init();
diff -urN linux-2.5.1-pre4/kernel/Makefile linux/kernel/Makefile
--- linux-2.5.1-pre4/kernel/Makefile	Sun Sep 16 21:22:40 2001
+++ linux/kernel/Makefile	Sat Dec  1 00:42:27 2001
@@ -9,12 +9,13 @@
 
 O_TARGET := kernel.o
 
-export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o printk.o
+export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o \
+		printk.o device.o
 
 obj-y     = sched.o dma.o fork.o exec_domain.o panic.o printk.o \
 	    module.o exit.o itimer.o info.o time.o softirq.o resource.o \
 	    sysctl.o acct.o capability.o ptrace.o timer.o user.o \
-	    signal.o sys.o kmod.o context.o
+	    signal.o sys.o kmod.o context.o device.o
 
 obj-$(CONFIG_UID16) += uid16.o
 obj-$(CONFIG_MODULES) += ksyms.o
diff -urN linux-2.5.1-pre4/kernel/device.c linux/kernel/device.c
--- linux-2.5.1-pre4/kernel/device.c	Wed Dec 31 16:00:00 1969
+++ linux/kernel/device.c	Sat Dec  1 00:42:27 2001
@@ -0,0 +1,1028 @@
+/*
+ * device.c
+ *
+ * Copyright (c) Patrick Mochel <mochel@osdl.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 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Please see Documentation/driver-model.txt for more explanation.
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/pagemap.h>
+#include <linux/module.h>
+#include <linux/stat.h>
+#include <linux/driverfs_fs.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/spinlock.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+# define DBG(x...) printk(x)
+#else
+# define DBG(x...)
+#endif
+
+static struct iobus device_root = {
+	bus_id: "root",
+	name:	"Logical System Root",
+};
+
+int (*platform_notify)(struct device * dev) = NULL;
+int (*platform_notify_remove)(struct device * dev) = NULL;
+
+static spinlock_t device_lock;
+static LIST_HEAD(device_gc_list);
+
+static int kdeviced_pid = 0;
+static DECLARE_WAIT_QUEUE_HEAD(kdeviced_wait);
+static DECLARE_COMPLETION(kdeviced_exited);
+
+static ssize_t device_read_status(char *, size_t, loff_t, void *);
+static ssize_t device_write_status(const char *, size_t, loff_t, void *);
+
+static struct driverfs_operations device_status_ops = {
+	read:	device_read_status,
+	write:	device_write_status,
+};
+
+static ssize_t device_read_power(char *, size_t, loff_t, void *);
+static ssize_t device_write_power(const char *, size_t, loff_t, void *);
+
+static struct driverfs_operations device_power_ops = {
+	read:	device_read_power,
+	write:	device_write_power,
+};
+
+static ssize_t iobus_read_status(char *, size_t, loff_t, void *);
+static ssize_t iobus_write_status(const char *, size_t, loff_t, void *);
+
+static struct driverfs_operations iobus_status_ops = {
+	read:	iobus_read_status,
+	write:	iobus_write_status,
+};
+
+
+/**
+ * device_create_file - create a driverfs file for a device
+ * @dev:	device requesting file
+ * @name:	name of file
+ * @mode:	permissions of file
+ * @ops:	operations for the file
+ * @data:	private data for the file
+ *
+ * Create a driverfs entry, then create the actual file the entry describes.
+ */
+int device_create_file(struct device * dev, const char * name, mode_t mode,
+		       struct driverfs_operations * ops, void * data)
+{
+	int error = -EFAULT;
+	struct driver_file_entry * entry;
+
+	if (!dev)
+		return -EINVAL;
+
+	if (!valid_device(dev))
+		return -EFAULT;
+
+	entry = driverfs_create_entry(name,mode,ops,data);
+	if (entry)
+		error = driverfs_create_file(entry,dev->dir);
+
+	put_device(dev);
+	return error;
+}
+
+/**
+ * device_remove_file - remove a device's file by name
+ * @dev:	device requesting removal
+ * @name:	name of the file
+ *
+ */
+void device_remove_file(struct device * dev, const char * name)
+{
+	if (!dev)
+		return;
+
+	if (!valid_device(dev))
+		return;
+
+	driverfs_remove_file(dev->dir,name);
+
+	put_device(dev);
+}
+
+/**
+ * device_remove_dir - remove a device's directory
+ * @dev:	device in question
+ */
+void device_remove_dir(struct device * dev)
+{
+	struct driver_dir_entry * dir;
+
+	if (!dev)
+		return;
+
+	lock_device(dev);
+	dir = dev->dir;
+	dev->dir = NULL;
+	unlock_device(dev);
+
+	if (dir)
+		driverfs_remove_dir(dir);
+}
+
+/**
+ * device_make_dir - create a driverfs directory
+ * @name:	name of directory
+ * @parent:	dentry for the parent directory
+ *
+ * Do the initial creation of the device's driverfs directory
+ * and populate it with the one default file.
+ *
+ * This is just a helper for device_register(), as we
+ * don't export this function. (Yes, that means we don't allow
+ * devices to create subdirectories).
+ */
+static int device_make_dir(struct device * dev)
+{
+	struct driver_dir_entry * entry;
+	int error;
+
+	entry = driverfs_create_dir_entry(dev->bus_id,(S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO));
+	if (!entry)
+		return -EFAULT;
+
+	error = driverfs_create_dir(entry,dev->parent->dir);
+
+	if (error) {
+		kfree(entry);
+		return error;
+	}
+
+	lock_device(dev);
+	dev->dir = entry;
+	unlock_device(dev);
+
+	/* first the status file */
+	error = device_create_file(dev, "status", S_IRUGO | S_IWUSR,
+				   &device_status_ops, (void *) dev);
+	if (error) {
+		device_remove_dir(dev);
+		goto done;
+	}
+
+	/* now the power file */
+	error = device_create_file(dev,"power",S_IRUGO | S_IWUSR,
+				   &device_power_ops, (void *) dev);
+	if (error)
+		device_remove_dir(dev);
+
+ done:
+	return error;
+}
+
+/* iobus interface.
+ * For practical purposes, it's exactly the same as the device interface above.
+ * Even below, the two are almost identical, only taking different pointer
+ * types.
+ * I have fantasized about removing struct iobus completely. It would reduce
+ * this file by about 30%, and make life much easier. However, it will take some
+ * time to really work everything out..
+ */
+
+int iobus_create_file(struct iobus * iobus, const char * name, mode_t mode,
+		      struct driverfs_operations * ops, void * data)
+{
+	int error = -EFAULT;
+	struct driver_file_entry * entry;
+
+	if (!iobus)
+		return -EINVAL;
+
+	if (!valid_iobus(iobus))
+		return -EFAULT;
+
+	entry = driverfs_create_entry(name,mode,ops,data);
+	if (entry)
+		error = driverfs_create_file(entry,iobus->dir);
+
+	put_iobus(iobus);
+	return error;
+}
+
+void iobus_remove_file(struct iobus * iobus, const char * name)
+{
+	if (!iobus)
+		return;
+
+	if (!valid_iobus(iobus))
+		return;
+
+	driverfs_remove_file(iobus->dir,name);
+
+	put_iobus(iobus);
+}
+
+void iobus_remove_dir(struct iobus * iobus)
+{
+	struct driver_dir_entry * dir;
+
+	if (!iobus)
+		return;
+
+	lock_iobus(iobus);
+	dir = iobus->dir;
+	iobus->dir = NULL;
+	unlock_iobus(iobus);
+
+	if (dir)
+		driverfs_remove_dir(dir);
+}
+
+static int iobus_make_dir(struct iobus * iobus)
+{
+	struct driver_dir_entry * entry;
+	struct driver_dir_entry * parent = NULL;
+	int error;
+
+	entry = driverfs_create_dir_entry(iobus->bus_id,(S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO));
+	if (!entry)
+		return -EFAULT;
+
+	if (iobus->parent)
+		parent = iobus->parent->dir;
+
+	error = driverfs_create_dir(entry,parent);
+	if (error) {
+		kfree(entry);
+		return error;
+	}
+
+	lock_iobus(iobus);
+	iobus->dir = entry;
+	unlock_iobus(iobus);
+
+	error = iobus_create_file(iobus, "status", S_IRUGO | S_IWUSR,
+				  &iobus_status_ops, (void *)iobus);
+	if (error)
+		iobus_remove_dir(iobus);
+
+	return error;
+}
+
+/**
+ * device_register - register a device
+ * @dev:	pointer to the device structure
+ *
+ * First, make sure that the device has a parent, create
+ * a directory for it, then add it to the parent's list of
+ * children.
+ */
+int device_register(struct device *dev)
+{
+	struct iobus * parent;
+	int error = -EFAULT;
+
+	if (!dev)
+		return -EINVAL;
+
+	if (!dev->parent)
+		dev->parent = &device_root;
+	parent = dev->parent;
+
+	if (valid_iobus(parent)) {
+		if (!valid_device(dev)) {
+			put_iobus(parent);
+			goto register_done;
+		}
+	} else
+		return -EFAULT;
+
+	if (!strlen(dev->name)) {
+		error = -EINVAL;
+		goto register_done;
+	}
+
+	error = device_make_dir(dev);
+	if (error)
+		goto register_done;
+
+
+	/* finally add it to its parent's list */
+	lock_iobus(parent);
+	list_add_tail(&dev->node, &parent->devices);
+	unlock_iobus(parent);
+
+	DBG("DEV: registering device: ID = '%s', name = %s, parent = %s\n",
+	    dev->bus_id, dev->name, parent->bus_id);
+
+	/* notify platform of device entry */
+	if (platform_notify)
+		platform_notify(dev);
+
+	return 0;
+
+ register_done:
+	put_device(dev);
+	put_iobus(parent);
+
+	return error;
+}
+
+/**
+ * put_device - clean up device
+ * @dev:	device in question
+ *
+ * Decrement reference count for device.
+ * If it hits 0, we need to clean it up.
+ * However, we may be here in interrupt context, and it may
+ * take some time to do proper clean up (removing files, calling
+ * back down to device to clean up everything it has).
+ * So, we remove it from its parent's list and add it to the list of
+ * devices to be cleaned up.
+ */
+void put_device(struct device * dev)
+{
+	struct iobus * parent;
+
+	if (!atomic_dec_and_lock(&dev->refcount,&dev->lock))
+		return;
+
+	parent = dev->parent;
+	dev->parent = NULL;
+	unlock_device(dev);
+
+	DBG("DEV: Unregistering device. ID = '%s', name = '%s'\n",
+	    dev->bus_id,dev->name);
+
+	/* disavow parent's knowledge */
+	lock_iobus(parent);
+	list_del_init(&dev->node);
+	unlock_iobus(parent);
+
+	/* queue the device to be removed by the reaper. */
+	spin_lock(&device_lock);
+	list_add_tail(&dev->node,&device_gc_list);
+	spin_unlock(&device_lock);
+
+	wake_up(&kdeviced_wait);
+
+	put_iobus(parent);
+}
+
+int iobus_register(struct iobus *bus)
+{
+	struct iobus * parent;
+	int error = -EINVAL;
+
+	if (!bus)
+		return -EINVAL;
+
+	if (!bus->parent)
+		bus->parent = &device_root;
+	parent = bus->parent;
+
+	DBG("DEV: registering bus. ID = '%s' name = '%s' parent = %p\n",
+	    bus->bus_id,bus->name,bus->parent);
+
+	if (valid_iobus(parent)) {
+		if (!valid_iobus(bus)) {
+			put_iobus(parent);
+			goto register_done;
+		}
+	} else
+		goto register_done;
+
+	if (!strlen(bus->bus_id))
+		goto register_done_put;
+
+	error = iobus_make_dir(bus);
+	if (error)
+		goto register_done_put;
+
+	lock_iobus(parent);
+	list_add_tail(&bus->node,&parent->children);
+	unlock_iobus(parent);
+
+	return 0;
+
+ register_done_put:
+	put_iobus(bus);
+	put_iobus(parent);
+ register_done:
+	return error;
+}
+
+/**
+ * iobus_unregister - remove bus and children from device tree
+ * @bus:	pointer to bus structure
+ *
+ * Remove device from parent's list of children and decrement
+ * reference count on controlling device. That should take care of
+ * the rest of the cleanup.
+ */
+void put_iobus(struct iobus * iobus)
+{
+	struct iobus * parent;
+
+	if (!atomic_dec_and_lock(&iobus->refcount,&iobus->lock))
+		return;
+
+	parent = iobus->parent;
+	iobus->parent = NULL;
+	unlock_iobus(iobus);
+
+	if (!list_empty(&iobus->devices) ||
+	    !list_empty(&iobus->children))
+		BUG();
+
+	/* disavow parent's knowledge */
+	if (parent) {
+		lock_iobus(parent);
+		list_del(&iobus->node);
+		unlock_iobus(parent);
+
+		put_iobus(parent);
+	}
+
+	/* unregister itself */
+	put_device(iobus->self);
+
+	return;
+}
+
+/**
+ * device_init_dev - initialise a struct device
+ * @dev:	pointer to device struct
+ */
+void device_init_dev(struct device * dev)
+{
+	INIT_LIST_HEAD(&dev->node);
+	spin_lock_init(&dev->lock);
+	atomic_set(&dev->refcount,1);
+}
+
+/**
+ * device_alloc_dev - allocate and initialise a device structure
+ *
+ */
+struct device * device_alloc(void)
+{
+	struct device * dev;
+
+	dev = kmalloc(sizeof(struct device), GFP_KERNEL);
+
+	if (!dev)
+		return NULL;
+
+	memset(dev,0,sizeof(struct device));
+	device_init_dev(dev);
+
+	return dev;
+}
+
+void iobus_init(struct iobus *bus)
+{
+	spin_lock_init(&bus->lock);
+	atomic_set(&bus->refcount,1);
+
+	INIT_LIST_HEAD(&bus->node);
+	INIT_LIST_HEAD(&bus->children);
+	INIT_LIST_HEAD(&bus->devices);
+}
+
+struct iobus *iobus_alloc(void)
+{
+	struct iobus *bus;
+
+	bus = kmalloc(sizeof(struct iobus), GFP_KERNEL);
+
+	if (!bus)
+		return NULL;
+
+	memset(bus,0,sizeof(struct iobus));
+
+	iobus_init(bus);
+
+	return bus;
+}
+
+static int do_device_suspend(struct device * dev, u32 state)
+{
+	int error = 0;
+
+	if (!dev->driver->suspend)
+		return error;
+
+	error = dev->driver->suspend(dev,state,SUSPEND_NOTIFY);
+
+	if (error)
+		return error;
+
+	error = dev->driver->suspend(dev,state,SUSPEND_SAVE_STATE);
+	if (error) {
+		if (dev->driver->resume)
+			dev->driver->resume(dev,RESUME_RESTORE_STATE);
+		return error;
+	}
+	error = dev->driver->suspend(dev,state,SUSPEND_POWER_DOWN);
+	if (error) {
+		if (dev->driver->resume)
+			dev->driver->resume(dev,RESUME_RESTORE_STATE);
+	}
+	return error;
+}
+
+static int do_device_resume(struct device * dev)
+{
+	int error = 0;
+
+	if (!dev->driver->resume)
+		return 0;
+	error = dev->driver->resume(dev,RESUME_POWER_ON);
+	if (error)
+		return error;
+	error = dev->driver->resume(dev,RESUME_RESTORE_STATE);
+	return error;
+}
+
+/**
+ * device_read_status - report some device information
+ * @page:	page-sized buffer to write into
+ * @count:	number of bytes requested
+ * @off:	offset into buffer
+ * @data:	device-specific data
+ *
+ * Report some human-readable information about the device.
+ * This includes the name, the bus id, and the current power state.
+ */
+static ssize_t device_read_status(char * page, size_t count,
+				  loff_t off, void * data)
+{
+	char *str = page;
+	struct device *dev = (struct device*)data;
+	ssize_t len = 0;
+
+	if (!dev)
+		return -EINVAL;
+
+	if (!valid_device(dev))
+		return -EFAULT;
+
+	if (off)
+		goto done;
+
+	str += sprintf(str,"Name:       %s\n",dev->name);
+	str += sprintf(str,"Bus ID:     %s\n",dev->bus_id);
+
+	len = str - page;
+
+	if (len > count)
+		len = count;
+
+	if (len < 0)
+		len = 0;
+
+ done:
+	put_device(dev);
+
+	return len;
+}
+
+/**
+ * device_write_status - forward a command to a driver
+ * @buf:	encoded command
+ * @count:	number of bytes in buffer
+ * @off:	offset into buffer to start with
+ * @data:	device-specific data
+ *
+ * Send a comamnd to a device driver.
+ * The following actions are supported:
+ * probe - scan slot for device
+ * remove - detach driver from slot
+ * suspend <state> <stage> - perform <stage> for entering <state>
+ * resume <stage> - perform <stage> for waking device up.
+ * (See Documentation/driver-model.txt for the theory of an n-stage
+ * suspend sequence).
+ */
+static ssize_t device_write_status(const char* buf, size_t count, loff_t off, void *data)
+{
+	char command[20];
+	struct device *dev = (struct device *)data;
+	int num;
+	int arg = 0;
+	int error = 0;
+
+	if (!dev)
+		return 0;
+
+	if (!valid_device(dev))
+		return -EFAULT;
+
+	if (off)
+		goto done_put;
+
+	/* everything involves dealing with the driver. */
+	if (!dev->driver)
+		goto done_put;
+
+	num = sscanf(buf,"%10s %d",command,&arg);
+
+	if (!num)
+		goto done_put;
+
+	if (!strcmp(command,"probe")) {
+		if (dev->driver->probe)
+			error = dev->driver->probe(dev);
+
+	} else if (!strcmp(command,"remove")) {
+		if (dev->driver->remove)
+			error = dev->driver->remove(dev,REMOVE_NOTIFY);
+	} else
+		error = -EFAULT;
+
+ done_put:
+	put_device(dev);
+	return error < 0 ? error : count;
+}
+
+static ssize_t
+device_read_power(char * page, size_t count, loff_t off, void * data)
+{
+	char	* str = page;
+	struct	device * dev = (struct device *)data;
+	ssize_t len = 0;
+
+	if (!dev)
+		return 0;
+
+	if (!valid_device(dev))
+		return 0;
+
+	str += sprintf(str,"State:      %d\n",dev->current_state);
+
+	len = str - page;
+
+	if (off) {
+		if (len < off) {
+			len = 0;
+			goto done;
+		}
+		str += off;
+		len -= off;
+	}
+
+	if (len > count)
+		len = count;
+
+ done:
+	put_device(dev);
+	return len;
+}
+
+static ssize_t
+device_write_power(const char * buf, size_t count, loff_t off, void * data)
+{
+	struct	device * dev = (struct device *)data;
+	char	str_command[20];
+	char	str_stage[20];
+	int	num_args;
+	u32	state;
+	u32	int_stage;
+	int	error = 0;
+
+	if (!dev)
+		return 0;
+
+	if (!valid_device(dev))
+		return -EFAULT;
+
+	if (off)
+		goto done;
+	if (!dev->driver)
+		goto done;
+
+	num_args = sscanf(buf,"%s %s %u",str_command,str_stage,&state);
+
+	error = -EINVAL;
+
+	if (!num_args) {
+		printk("have no arguments\n");
+		goto done;
+	}
+
+	if (!strnicmp(str_command,"suspend",7)) {
+
+		printk("%s: we know it's a suspend action\n",__FUNCTION__);
+
+		if (num_args != 3)
+			goto done;
+		if (!strnicmp(str_stage,"notify",6))
+			int_stage = SUSPEND_NOTIFY;
+		else if (!strnicmp(str_stage,"save",4))
+			int_stage = SUSPEND_SAVE_STATE;
+		else if (!strnicmp(str_stage,"disable",7))
+			int_stage = SUSPEND_DISABLE;
+		else if (!strnicmp(str_stage,"powerdown",8))
+			int_stage = SUSPEND_POWER_DOWN;
+		else
+			goto done;
+
+		if (dev->driver->suspend)
+			error = dev->driver->suspend(dev,state,int_stage);
+		else
+			error = 0;
+	} else if (!strnicmp(str_command,"resume",6)) {
+		if (num_args != 2)
+			goto done;
+
+		if (!strnicmp(str_stage,"poweron",7))
+			int_stage = RESUME_POWER_ON;
+		else if (!strnicmp(str_stage,"restore",7))
+			int_stage = RESUME_RESTORE_STATE;
+		else if (!strnicmp(str_stage,"enable",6))
+			int_stage = RESUME_ENABLE;
+		else
+			goto done;
+
+		if (dev->driver->resume)
+			error = dev->driver->resume(dev,int_stage);
+		else
+			error = 0;
+	} else
+		printk("%s: couldn't find any thing to do\n",__FUNCTION__);
+ done:
+	put_device(dev);
+
+	DBG("%s: returning %d\n",__FUNCTION__,error);
+
+	return error < 0 ? error : count;
+}
+
+/**
+ * bus_read_status - report human readable information
+ * @page:	page-sized buffer to write into
+ * @count:	number of bytes requested
+ * @off:	offset into buffer to start at
+ * @data:	bus-specific data
+ */
+static ssize_t iobus_read_status(char *page, size_t count,
+				 loff_t off, void *data)
+{
+	char *str = page;
+	struct iobus *bus = (struct iobus*)data;
+	ssize_t len = 0;
+
+	if (!bus)
+		return -EINVAL;
+
+	if (!valid_iobus(bus))
+		return -EFAULT;
+
+	if (off)
+		goto done;
+
+	str += sprintf(str,"Name:       %s\n",bus->name);
+	str += sprintf(str,"Bus ID:     %s\n",bus->bus_id);
+
+	if (bus->driver)
+		str += sprintf(str,"Type:       %s\n",bus->driver->name);
+
+	len = str - page;
+	if (len < off)
+		len = 0;
+	if (len > count)
+		len = count;
+	if (len < 0)
+		len = 0;
+
+ done:
+	put_iobus(bus);
+	return len;
+}
+
+/**
+ * bus_write_status - forward a command to a bus
+ * @buf:	string encoded command
+ * @count:	number of bytes requested
+ * @off:	offset into buffer to start at
+ * @data:	bus-specific data
+ *
+ * Like device_write_status, this sends a command to a bus driver.
+ * Supported actions are:
+ * scan - scan a bus for devices
+ * add_device <id> - add a child device
+ */
+static ssize_t iobus_write_status(const char *buf, size_t count, loff_t off, void *data)
+{
+	char command[10];
+	char which[15];
+	char id[10];
+	struct iobus *bus = (struct iobus*)data;
+	int num;
+	int error = -EINVAL;
+
+	if (!bus)
+		return -EINVAL;
+
+	if (!valid_iobus(bus))
+		return -EFAULT;
+
+	if (!bus->driver)
+		goto done;
+
+	num = sscanf(buf,"%10s %15s %10s",command,which,id);
+
+	if (!num)
+		goto done;
+
+	if (!strnicmp(command,"scan",4)) {
+		if (bus->driver->scan)
+			error = bus->driver->scan(bus);
+	} else if (!strnicmp(command,"add",3) && num == 2) {
+		error = bus->driver->add_device(bus,id);
+	} else if (!strnicmp(command, "suspend",7)) {
+		u32 state = simple_strtoul(which,NULL,0);
+		if (state > 0)
+			error = do_device_suspend(bus->self,state);
+
+	} else if (!strnicmp(command,"resume",6)) {
+		error = do_device_resume(bus->self);
+
+	}
+
+ done:
+	put_iobus(bus);
+	return error < 0 ? error : count;
+}
+
+/* Device Garbage Collection
+ * When a device's reference count reaches 0, it is removed from it's
+ * parent's list and added to a list of devices waiting to be removed.
+ *
+ * We don't directly remove it ourselves, because someone could have an
+ * open file.
+ *
+ * We don't allocate an event for keventd, becuase we may be here from
+ * an interrupt; and how do those things get freed, anyway?
+ *
+ * Instead, when a device's reference count reaches 0, it is removed
+ * from its parent's list of children and added to the list of devices
+ * to be reaped.
+ *
+ * When we spawn a thread that gets woken up every time a device is added
+ * to the unused list.
+ */
+static inline void __reap_device(struct device * dev)
+{
+	/* FIXME: What do we do for a bridge? */
+
+	/* remove the driverfs directory */
+	device_remove_dir(dev);
+
+	if (dev->subordinate)
+		iobus_remove_dir(dev->subordinate);
+
+	/* Notify the platform of the removal, in case they
+	 * need to do anything...
+	 */
+	if (platform_notify_remove)
+		platform_notify_remove(dev);
+
+	/* Tell the driver to clean up after itself.
+	 * Note that we likely didn't allocate the device,
+	 * so this is the driver's chance to free that up...
+	 */
+	if (dev->driver && dev->driver->remove)
+		dev->driver->remove(dev,REMOVE_FREE_RESOURCES);
+}
+
+static int device_cleanup_thread(void * data)
+{
+	daemonize();
+
+	strcpy(current->comm,"kdeviced");
+
+	do {
+		struct list_head * node;
+
+		spin_lock(&device_lock);
+		node = device_gc_list.next;
+		while(node != &device_gc_list) {
+			list_del_init(node);
+			spin_unlock(&device_lock);
+			__reap_device(list_to_dev(node));
+
+			spin_lock(&device_lock);
+			node = device_gc_list.next;
+		}
+		spin_unlock(&device_lock);
+
+		interruptible_sleep_on(&kdeviced_wait);
+	} while(!signal_pending(current));
+
+	DBG("kdeviced exiting\n");
+	complete_and_exit(&kdeviced_exited,0);
+	return 0;
+}
+
+static int __init device_init_root(void)
+{
+	/* initialize parent bus lists */
+	iobus_init(&device_root);
+
+	/* don't call iobus_register, as the only thing it really
+	 * needs to do is create the root directory. Easier
+	 * to just do it here than special case it elsewhere..
+	 */
+	iobus_make_dir(&device_root);
+
+	return (device_root.dir ? 0 : -EFAULT);
+}
+
+int __init device_driver_init(void)
+{
+	int error = 0;
+	int pid;
+
+	DBG("DEV: Initialising Device Tree\n");
+
+	spin_lock_init(&device_lock);
+
+	error = init_driverfs_fs();
+
+	if (error) {
+		panic("DEV: could not initialise driverfs\n");
+		return error;
+	}
+
+	error = device_init_root();
+	if (error) {
+		printk(KERN_ERR "%s: device root init failed!\n", __FUNCTION__);
+		return error;
+	}
+
+	/* initialise the garbage collection */
+	pid = kernel_thread(device_cleanup_thread,NULL,
+			    (CLONE_FS | CLONE_FILES | CLONE_SIGHAND));
+	if (pid > 0)
+		kdeviced_pid = pid;
+	else {
+		DBG("DEV: Could not start cleanup thread\n");
+		return pid;
+	}
+
+	DBG("DEV: Done Initialising\n");
+	return error;
+}
+
+void __exit device_driver_exit(void)
+{
+	if (kdeviced_pid) {
+		kill_proc(kdeviced_pid,SIGTERM,1);
+		kdeviced_pid = 0;
+		wait_for_completion(&kdeviced_exited);
+	}
+}
+
+static int __init device_setup(char *str)
+{
+	return 0;
+}
+
+__setup("device=",device_setup);
+
+EXPORT_SYMBOL(device_register);
+EXPORT_SYMBOL(device_alloc);
+EXPORT_SYMBOL(device_init_dev);
+
+EXPORT_SYMBOL(device_create_file);
+EXPORT_SYMBOL(device_remove_file);
+
+EXPORT_SYMBOL(iobus_register);
+EXPORT_SYMBOL(iobus_alloc);
+EXPORT_SYMBOL(iobus_init);
+
+EXPORT_SYMBOL(iobus_create_file);
+EXPORT_SYMBOL(iobus_remove_file);
+
+EXPORT_SYMBOL(device_driver_init);
diff -urN linux-2.5.1-pre4/kernel/exit.c linux/kernel/exit.c
--- linux-2.5.1-pre4/kernel/exit.c	Wed Nov 21 14:42:27 2001
+++ linux/kernel/exit.c	Sat Dec  1 00:42:27 2001
@@ -150,21 +150,34 @@
 }
 
 /*
- * When we die, we re-parent all our children to
+ * When we die, we re-parent all our children.
+ * Try to give them to another thread in our process
+ * group, and if no such member exists, give it to
  * the global child reaper process (ie "init")
  */
 static inline void forget_original_parent(struct task_struct * father)
 {
-	struct task_struct * p;
+	struct task_struct * p, *reaper;
 
 	read_lock(&tasklist_lock);
 
+	/* Next in our thread group */
+	reaper = next_thread(father);
+	if (reaper == father)
+		reaper = child_reaper;
+
 	for_each_task(p) {
 		if (p->p_opptr == father) {
 			/* We dont want people slaying init */
 			p->exit_signal = SIGCHLD;
 			p->self_exec_id++;
-			p->p_opptr = child_reaper;
+
+			/* Make sure we're not reparenting to ourselves */
+			if (p == reaper)
+				p->p_opptr = child_reaper;
+			else
+				p->p_opptr = reaper;
+
 			if (p->pdeath_signal) send_sig(p->pdeath_signal, p, 0);
 		}
 	}
diff -urN linux-2.5.1-pre4/kernel/fork.c linux/kernel/fork.c
--- linux-2.5.1-pre4/kernel/fork.c	Wed Nov 21 10:18:42 2001
+++ linux/kernel/fork.c	Sat Dec  1 00:42:27 2001
@@ -206,7 +206,7 @@
 	return retval;
 }
 
-spinlock_t mmlist_lock __cacheline_aligned = SPIN_LOCK_UNLOCKED;
+spinlock_t mmlist_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 int mmlist_nr;
 
 #define allocate_mm()	(kmem_cache_alloc(mm_cachep, SLAB_KERNEL))
diff -urN linux-2.5.1-pre4/kernel/softirq.c linux/kernel/softirq.c
--- linux-2.5.1-pre4/kernel/softirq.c	Wed Oct 31 10:26:02 2001
+++ linux/kernel/softirq.c	Sat Dec  1 00:42:27 2001
@@ -42,7 +42,7 @@
 
 irq_cpustat_t irq_stat[NR_CPUS];
 
-static struct softirq_action softirq_vec[32] __cacheline_aligned;
+static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp;
 
 /*
  * we cannot loop indefinitely here to avoid userspace starvation,
@@ -146,8 +146,8 @@
 
 /* Tasklets */
 
-struct tasklet_head tasklet_vec[NR_CPUS] __cacheline_aligned;
-struct tasklet_head tasklet_hi_vec[NR_CPUS] __cacheline_aligned;
+struct tasklet_head tasklet_vec[NR_CPUS] __cacheline_aligned_in_smp;
+struct tasklet_head tasklet_hi_vec[NR_CPUS] __cacheline_aligned_in_smp;
 
 void __tasklet_schedule(struct tasklet_struct *t)
 {
diff -urN linux-2.5.1-pre4/mm/filemap.c linux/mm/filemap.c
--- linux-2.5.1-pre4/mm/filemap.c	Sat Dec  1 00:41:42 2001
+++ linux/mm/filemap.c	Sat Dec  1 00:42:27 2001
@@ -47,7 +47,7 @@
 unsigned int page_hash_bits;
 struct page **page_hash_table;
 
-spinlock_t pagecache_lock ____cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
+spinlock_t pagecache_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 /*
  * NOTE: to avoid deadlocking you must never acquire the pagemap_lru_lock 
  *	with the pagecache_lock held.
@@ -57,7 +57,7 @@
  *		pagemap_lru_lock ->
  *			pagecache_lock
  */
-spinlock_t pagemap_lru_lock ____cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
+spinlock_t pagemap_lru_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 
 #define CLUSTER_PAGES		(1 << page_cluster)
 #define CLUSTER_OFFSET(x)	(((x) >> page_cluster) << page_cluster)
diff -urN linux-2.5.1-pre4/mm/highmem.c linux/mm/highmem.c
--- linux-2.5.1-pre4/mm/highmem.c	Sat Dec  1 00:41:42 2001
+++ linux/mm/highmem.c	Sat Dec  1 00:42:27 2001
@@ -343,7 +343,7 @@
 	goto repeat_alloc;
 }
 
-void create_bounce(unsigned long pfn, struct bio **bio_orig, int gfp_mask)
+void create_bounce(unsigned long pfn, struct bio **bio_orig)
 {
 	struct page *page;
 	struct bio *bio = NULL;
@@ -369,7 +369,7 @@
 
 		to = &bio->bi_io_vec[i];
 
-		to->bv_page = alloc_bounce_page(gfp_mask);
+		to->bv_page = alloc_bounce_page(GFP_NOHIGHIO);
 		to->bv_len = from->bv_len;
 		to->bv_offset = from->bv_offset;
 
diff -urN linux-2.5.1-pre4/mm/swapfile.c linux/mm/swapfile.c
--- linux-2.5.1-pre4/mm/swapfile.c	Sat Nov  3 17:05:25 2001
+++ linux/mm/swapfile.c	Sat Dec  1 00:42:27 2001
@@ -804,25 +804,17 @@
 {
 	char * page = (char *) __get_free_page(GFP_KERNEL);
 	struct swap_info_struct *ptr = swap_info;
-	int i, j, len = 0, usedswap;
+	int i, len;
 
 	if (!page)
 		return -ENOMEM;
 
-	len += sprintf(buf, "Filename\t\t\tType\t\tSize\tUsed\tPriority\n");
+	len = sprintf(buf, "Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n");
 	for (i = 0 ; i < nr_swapfiles ; i++, ptr++) {
 		if ((ptr->flags & SWP_USED) && ptr->swap_map) {
 			char * path = d_path(ptr->swap_file, ptr->swap_vfsmnt,
 						page, PAGE_SIZE);
-
-			len += sprintf(buf + len, "%-31s ", path);
-
-			if (!ptr->swap_device)
-				len += sprintf(buf + len, "file\t\t");
-			else
-				len += sprintf(buf + len, "partition\t");
-
-			usedswap = 0;
+			int j, usedswap = 0;
 			for (j = 0; j < ptr->max; ++j)
 				switch (ptr->swap_map[j]) {
 					case SWAP_MAP_BAD:
@@ -831,8 +823,12 @@
 					default:
 						usedswap++;
 				}
-			len += sprintf(buf + len, "%d\t%d\t%d\n", ptr->pages << (PAGE_SHIFT - 10), 
-				usedswap << (PAGE_SHIFT - 10), ptr->prio);
+			len += sprintf(buf + len, "%-39s %s\t%d\t%d\t%d\n",
+				       path,
+				       ptr->swap_device ? "partition" : "file\t",
+				       ptr->pages << (PAGE_SHIFT - 10),
+				       usedswap << (PAGE_SHIFT - 10),
+				       ptr->prio);
 		}
 	}
 	free_page((unsigned long) page);
diff -urN linux-2.5.1-pre4/net/irda/Config.in linux/net/irda/Config.in
--- linux-2.5.1-pre4/net/irda/Config.in	Sat Nov 11 18:11:23 2000
+++ linux/net/irda/Config.in	Sat Dec  1 00:42:27 2001
@@ -14,13 +14,10 @@
       source net/irda/irnet/Config.in
       source net/irda/ircomm/Config.in
       bool '  Ultra (connectionless) protocol' CONFIG_IRDA_ULTRA
-      bool '  IrDA protocol options' CONFIG_IRDA_OPTIONS
-      if [ "$CONFIG_IRDA_OPTIONS" != "n" ]; then
-	 comment '  IrDA options'
-	 bool '    Cache last LSAP' CONFIG_IRDA_CACHE_LAST_LSAP
-	 bool '    Fast RRs' CONFIG_IRDA_FAST_RR
-	 bool '    Debug information' CONFIG_IRDA_DEBUG
-      fi
+      comment 'IrDA options'
+      bool '  Cache last LSAP' CONFIG_IRDA_CACHE_LAST_LSAP
+      bool '  Fast RRs (low latency)' CONFIG_IRDA_FAST_RR
+      bool '  Debug information' CONFIG_IRDA_DEBUG
    fi
 
    if [ "$CONFIG_IRDA" != "n" ]; then
diff -urN linux-2.5.1-pre4/net/irda/af_irda.c linux/net/irda/af_irda.c
--- linux-2.5.1-pre4/net/irda/af_irda.c	Fri Nov  9 14:22:17 2001
+++ linux/net/irda/af_irda.c	Sat Dec  1 00:42:27 2001
@@ -914,8 +914,7 @@
 	memcpy(&new->qos_tx, &self->qos_tx, sizeof(struct qos_info));
 
 	/* Clean up the original one to keep it in listen state */
-	self->tsap->dtsap_sel = self->tsap->lsap->dlsap_sel = LSAP_ANY;
-	self->tsap->lsap->lsap_state = LSAP_DISCONNECTED;
+	irttp_listen(self->tsap);
 
 	skb->sk = NULL;
 	skb->destructor = NULL;
@@ -1845,15 +1844,36 @@
 		  	return -EFAULT;
 		}
 
-		/* Find the object we target */
-		ias_obj = irias_find_object(ias_opt->irda_class_name);
+		/* Find the object we target.
+		 * If the user gives us an empty string, we use the object
+		 * associated with this socket. This will workaround
+		 * duplicated class name - Jean II */
+		if(ias_opt->irda_class_name[0] == '\0') {
+			if(self->ias_obj == NULL) {
+				kfree(ias_opt);
+				return -EINVAL;
+			}
+			ias_obj = self->ias_obj;
+		} else
+			ias_obj = irias_find_object(ias_opt->irda_class_name);
+
+		/* Only ROOT can mess with the global IAS database.
+		 * Users can only add attributes to the object associated
+		 * with the socket they own - Jean II */
+		if((!capable(CAP_NET_ADMIN)) &&
+		   ((ias_obj == NULL) || (ias_obj != self->ias_obj))) {
+			kfree(ias_opt);
+			return -EPERM;
+		}
+
+		/* If the object doesn't exist, create it */
 		if(ias_obj == (struct ias_object *) NULL) {
 			/* Create a new object */
 			ias_obj = irias_new_object(ias_opt->irda_class_name,
 						   jiffies);
 		}
 
-		/* Do we have it already ? */
+		/* Do we have the attribute already ? */
 		if(irias_find_attrib(ias_obj, ias_opt->irda_attrib_name)) {
 			kfree(ias_opt);
 			return -EINVAL;
@@ -1927,13 +1947,28 @@
 		  	return -EFAULT;
 		}
 
-		/* Find the object we target */
-		ias_obj = irias_find_object(ias_opt->irda_class_name);
+		/* Find the object we target.
+		 * If the user gives us an empty string, we use the object
+		 * associated with this socket. This will workaround
+		 * duplicated class name - Jean II */
+		if(ias_opt->irda_class_name[0] == '\0')
+			ias_obj = self->ias_obj;
+		else
+			ias_obj = irias_find_object(ias_opt->irda_class_name);
 		if(ias_obj == (struct ias_object *) NULL) {
 			kfree(ias_opt);
 			return -EINVAL;
 		}
 
+		/* Only ROOT can mess with the global IAS database.
+		 * Users can only del attributes from the object associated
+		 * with the socket they own - Jean II */
+		if((!capable(CAP_NET_ADMIN)) &&
+		   ((ias_obj == NULL) || (ias_obj != self->ias_obj))) {
+			kfree(ias_opt);
+			return -EPERM;
+		}
+
 		/* Find the attribute (in the object) we target */
 		ias_attr = irias_find_attrib(ias_obj,
 					     ias_opt->irda_attrib_name); 
@@ -2166,8 +2201,14 @@
 		  	return -EFAULT;
 		}
 
-		/* Find the object we target */
-		ias_obj = irias_find_object(ias_opt->irda_class_name);
+		/* Find the object we target.
+		 * If the user gives us an empty string, we use the object
+		 * associated with this socket. This will workaround
+		 * duplicated class name - Jean II */
+		if(ias_opt->irda_class_name[0] == '\0')
+			ias_obj = self->ias_obj;
+		else
+			ias_obj = irias_find_object(ias_opt->irda_class_name);
 		if(ias_obj == (struct ias_object *) NULL) {
 			kfree(ias_opt);
 			return -EINVAL;
diff -urN linux-2.5.1-pre4/net/irda/iriap.c linux/net/irda/iriap.c
--- linux-2.5.1-pre4/net/irda/iriap.c	Fri Nov  9 14:22:17 2001
+++ linux/net/irda/iriap.c	Sat Dec  1 00:42:27 2001
@@ -800,9 +800,7 @@
 	new->max_header_size = max_header_size;
 
 	/* Clean up the original one to keep it in listen state */
-	self->lsap->dlsap_sel = LSAP_ANY;
-	self->lsap->lsap_state = LSAP_DISCONNECTED;
-	/* FIXME: refcount in irlmp might get wrong */
+	irlmp_listen(self->lsap);
 	
 	iriap_do_server_event(new, IAP_LM_CONNECT_INDICATION, userdata);
 }
diff -urN linux-2.5.1-pre4/net/irda/irlap.c linux/net/irda/irlap.c
--- linux-2.5.1-pre4/net/irda/irlap.c	Fri Nov  9 14:22:17 2001
+++ linux/net/irda/irlap.c	Sat Dec  1 00:42:27 2001
@@ -51,6 +51,11 @@
 hashbin_t *irlap = NULL;
 int sysctl_slot_timeout = SLOT_TIMEOUT * 1000 / HZ;
 
+/* This is the delay of missed pf period before generating an event
+ * to the application. The spec mandate 3 seconds, but in some cases
+ * it's way too long. - Jean II */
+int sysctl_warn_noreply_time = 3;
+
 extern void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb);
 static void __irlap_close(struct irlap_cb *self);
 
@@ -527,22 +532,7 @@
 	self->discovery_cmd = discovery;
 	info.discovery = discovery;
 	
-	/* Check if the slot timeout is within limits */
-	if (sysctl_slot_timeout < 20) {
-		ERROR(__FUNCTION__ 
-		      "(), to low value for slot timeout!\n");
-		sysctl_slot_timeout = 20;
-	}
-	/* 
-	 * Highest value is actually 8, but we allow higher since
-	 * some devices seems to require it.
-	 */
-	if (sysctl_slot_timeout > 160) {
-		ERROR(__FUNCTION__ 
-		      "(), to high value for slot timeout!\n");
-		sysctl_slot_timeout = 160;
-	}
-	
+	/* sysctl_slot_timeout bounds are checked in irsysctl.c - Jean II */
 	self->slot_timeout = sysctl_slot_timeout * HZ / 1000;
 	
 	irlap_do_event(self, DISCOVERY_REQUEST, NULL, &info);
@@ -931,9 +921,6 @@
 	/* Set data size */
 	/*self->qos_rx.data_size.bits &= 0x03;*/
 
-	/* Set disconnect time -> done properly in qos.c */
-	/*self->qos_rx.link_disc_time.bits &= 0x07;*/
-
 	irda_qos_bits_to_value(&self->qos_rx);
 }
 
@@ -1070,8 +1057,11 @@
 	/*
 	 *  Set N1 to 0 if Link Disconnect/Threshold Time = 3 and set it to 
 	 *  3 seconds otherwise. See page 71 in IrLAP for more details.
+	 *  Actually, it's not always 3 seconds, as we allow to set
+	 *  it via sysctl... Max maxtt is 500ms, and N1 need to be multiple
+	 *  of 2, so 1 second is minimum we can allow. - Jean II
 	 */
-	if (self->qos_tx.link_disc_time.value == 3)
+	if (self->qos_tx.link_disc_time.value == sysctl_warn_noreply_time)
 		/* 
 		 * If we set N1 to 0, it will trigger immediately, which is
 		 * not what we want. What we really want is to disable it,
@@ -1079,7 +1069,8 @@
 		 */
 		self->N1 = -2; /* Disable - Need to be multiple of 2*/
 	else
-		self->N1 = 3000 / self->qos_rx.max_turn_time.value;
+		self->N1 = sysctl_warn_noreply_time * 1000 /
+		  self->qos_rx.max_turn_time.value;
 	
 	IRDA_DEBUG(4, "Setting N1 = %d\n", self->N1);
 	
diff -urN linux-2.5.1-pre4/net/irda/irlap_event.c linux/net/irda/irlap_event.c
--- linux-2.5.1-pre4/net/irda/irlap_event.c	Fri Nov  9 14:22:17 2001
+++ linux/net/irda/irlap_event.c	Sat Dec  1 00:42:27 2001
@@ -1405,30 +1405,29 @@
 		}
 		self->add_wait = FALSE;
 
-		if ((self->retry_count < self->N2) && 
-		    (self->retry_count != self->N1)) {
-			
+		/* N2 is the disconnect timer. Until we reach it, we retry */
+		if (self->retry_count < self->N2) {
+			/* Retry sending the pf bit to the secondary */
 			irlap_wait_min_turn_around(self, &self->qos_tx);
 			irlap_send_rr_frame(self, CMD_FRAME);
 			
 			irlap_start_final_timer(self, self->final_timeout);
 		 	self->retry_count++;
-
 			IRDA_DEBUG(4, "irlap_state_nrm_p: FINAL_TIMER_EXPIRED:"
 				   " retry_count=%d\n", self->retry_count);
-			/* Keep state */
-		} else if (self->retry_count == self->N1) {
-			irlap_status_indication(self, STATUS_NO_ACTIVITY);
-			irlap_wait_min_turn_around(self, &self->qos_tx);
-			irlap_send_rr_frame(self, CMD_FRAME);
-			
-			irlap_start_final_timer(self, self->final_timeout);
-			self->retry_count++;
 
-			IRDA_DEBUG(4, "retry count = N1; retry_count=%d\n", 
-				   self->retry_count);
+			/* Early warning event. I'm using a pretty liberal
+			 * interpretation of the spec and generate an event
+			 * every time the timer is multiple of N1 (and not
+			 * only the first time). This allow application
+			 * to know precisely if connectivity restart...
+			 * Jean II */
+			if((self->retry_count % self->N1) == 0)
+				irlap_status_indication(self,
+							STATUS_NO_ACTIVITY);
+
 			/* Keep state */
-		} else if (self->retry_count >= self->N2) {
+		} else {
 			irlap_apply_default_connection_parameters(self);
 
 			/* Always switch state before calling upper layers */
@@ -1991,6 +1990,7 @@
 		 *  Wait until retry_count * n matches negotiated threshold/
 		 *  disconnect time (note 2 in IrLAP p. 82)
 		 *
+		 * Similar to irlap_state_nrm_p() -> FINAL_TIMER_EXPIRED
 		 * Note : self->wd_timeout = (self->final_timeout * 2),
 		 *   which explain why we use (self->N2 / 2) here !!!
 		 * Jean II
@@ -1998,16 +1998,15 @@
 		IRDA_DEBUG(1, __FUNCTION__ "(), retry_count = %d\n", 
 			   self->retry_count);
 
-		if ((self->retry_count <  (self->N2 / 2))  && 
-		    (self->retry_count != (self->N1 / 2))) {
-			
+		if (self->retry_count < (self->N2 / 2)) {
+			/* No retry, just wait for primary */
 			irlap_start_wd_timer(self, self->wd_timeout);
 			self->retry_count++;
-		} else if (self->retry_count == (self->N1 / 2)) {
-			irlap_status_indication(self, STATUS_NO_ACTIVITY);
-			irlap_start_wd_timer(self, self->wd_timeout);
-			self->retry_count++;
-		} else if (self->retry_count >= (self->N2 / 2)) {
+
+			if((self->retry_count % (self->N1 / 2)) == 0)
+				irlap_status_indication(self,
+							STATUS_NO_ACTIVITY);
+		} else {
 			irlap_apply_default_connection_parameters(self);
 			
 			/* Always switch state before calling upper layers */
diff -urN linux-2.5.1-pre4/net/irda/irlmp.c linux/net/irda/irlmp.c
--- linux-2.5.1-pre4/net/irda/irlmp.c	Fri Nov  9 14:22:17 2001
+++ linux/net/irda/irlmp.c	Sat Dec  1 00:42:27 2001
@@ -469,7 +469,12 @@
 
 	IRDA_DEBUG(2, __FUNCTION__ "(), slsap_sel=%02x, dlsap_sel=%02x\n", 
 		   self->slsap_sel, self->dlsap_sel);
-	
+
+	/* Note : self->lap is set in irlmp_link_data_indication(),
+	 * (case CONNECT_CMD:) because we have no way to set it here.
+	 * Similarly, self->dlsap_sel is usually set in irlmp_find_lsap().
+	 * Jean II */
+
 	self->qos = *self->lap->qos;
 
 	max_seg_size = self->lap->qos->data_size.value-LMP_HEADER;
@@ -577,7 +582,9 @@
 	/* Dup */
 	memcpy(new, orig, sizeof(struct lsap_cb));
 	new->notify.instance = instance;
-	
+	/* new->lap = orig->lap; => done in the memcpy() */
+	/* new->slsap_sel = orig->slsap_sel; => done in the memcpy() */
+
 	init_timer(&new->watchdog_timer);
 	
 	hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) new, (int) new, 
diff -urN linux-2.5.1-pre4/net/irda/irlmp_event.c linux/net/irda/irlmp_event.c
--- linux-2.5.1-pre4/net/irda/irlmp_event.c	Fri Nov  9 14:22:17 2001
+++ linux/net/irda/irlmp_event.c	Sat Dec  1 00:42:27 2001
@@ -407,12 +407,7 @@
 		 *  removed later and moved to the list of unconnected LSAPs
 		 */
 		if (HASHBIN_GET_SIZE(self->lsaps) > 0) {
-			/* Make sure the timer has sensible value (the user
-			 * may have set it) - Jean II */
-			if(sysctl_lap_keepalive_time < 100)	/* 100ms */
-				sysctl_lap_keepalive_time = 100;
-			if(sysctl_lap_keepalive_time > 10000)	/* 10s */
-				sysctl_lap_keepalive_time = 10000;
+			/* Timer value is checked in irsysctl - Jean II */
 			irlmp_start_idle_timer(self, sysctl_lap_keepalive_time * HZ / 1000);
 		} else {
 			/* No more connections, so close IrLAP */
diff -urN linux-2.5.1-pre4/net/irda/irnet/irnet.h linux/net/irda/irnet/irnet.h
--- linux-2.5.1-pre4/net/irda/irnet/irnet.h	Fri Nov  9 14:22:17 2001
+++ linux/net/irda/irnet/irnet.h	Sat Dec  1 00:42:27 2001
@@ -199,6 +199,13 @@
  *	o Avoid leaking discovery log and skb
  *	o Replace "self" with "server" in irnet_connect_indication() to
  *	  better detect cut'n'paste error ;-)
+ *
+ * v9 - 29.11.01 - Jean II
+ *	o Fix event generation in disconnect indication that I broke in v8
+ *	  It was always generation "No-Answer" because I was testing ttp_open
+ *	  just after clearing it. *blush*.
+ *	o Use newly created irttp_listen() to fix potential crash when LAP
+ *	  destroyed before irnet module removed.
  */
 
 /***************************** INCLUDES *****************************/
diff -urN linux-2.5.1-pre4/net/irda/irnet/irnet_irda.c linux/net/irda/irnet/irnet_irda.c
--- linux-2.5.1-pre4/net/irda/irnet/irnet_irda.c	Fri Nov  9 14:22:17 2001
+++ linux/net/irda/irnet/irnet_irda.c	Sat Dec  1 00:42:27 2001
@@ -830,8 +830,7 @@
 #endif /* STREAM_COMPAT */
 
   /* Clean up the original one to keep it in listen state */
-  server->tsap->dtsap_sel = server->tsap->lsap->dlsap_sel = LSAP_ANY;
-  server->tsap->lsap->lsap_state = LSAP_DISCONNECTED;
+  irttp_listen(server->tsap);
 
   /* Send a connection response on the new socket */
   irttp_connect_response(new->tsap, new->max_sdu_size_rx, NULL);
@@ -897,8 +896,7 @@
 		   self->saddr, self->daddr, self->rname);
 
   /* Clean up the server to keep it in listen state */
-  self->tsap->dtsap_sel = self->tsap->lsap->dlsap_sel = LSAP_ANY;
-  self->tsap->lsap->lsap_state = LSAP_DISCONNECTED;
+  irttp_listen(self->tsap);
 
   DEXIT(IRDA_SERV_TRACE, "\n");
   return;
@@ -1081,7 +1079,8 @@
 			    struct sk_buff *skb)
 {
   irnet_socket *	self = (irnet_socket *) instance;
-  int			test = 0;
+  int			test_open;
+  int			test_connect;
 
   DENTER(IRDA_TCB_TRACE, "(self=0x%X)\n", (unsigned int) self);
   DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n");
@@ -1091,23 +1090,23 @@
     dev_kfree_skb(skb);
 
   /* Prevent higher layer from accessing IrTTP */
-  test = test_and_clear_bit(0, &self->ttp_open);
+  test_open = test_and_clear_bit(0, &self->ttp_open);
   /* Not connecting anymore...
    * (note : TSAP is open, so IAP callbacks are no longer pending...) */
-  test |= test_and_clear_bit(0, &self->ttp_connect);
+  test_connect = test_and_clear_bit(0, &self->ttp_connect);
 
   /* If both self->ttp_open and self->ttp_connect are NULL, it mean that we
    * have a race condition with irda_irnet_destroy() or
    * irnet_connect_indication(), so don't mess up tsap...
    */
-  if(!test)
+  if(!(test_open || test_connect))
     {
       DERROR(IRDA_CB_ERROR, "Race condition detected...\n");
       return;
     }
 
   /* If we were active, notify the control channel */
-  if(test_bit(0, &self->ttp_open))
+  if(test_open)
     irnet_post_event(self, IRNET_DISCONNECT_FROM,
 		     self->saddr, self->daddr, self->rname);
   else
diff -urN linux-2.5.1-pre4/net/irda/irsysctl.c linux/net/irda/irsysctl.c
--- linux-2.5.1-pre4/net/irda/irsysctl.c	Thu Sep 13 16:26:52 2001
+++ linux/net/irda/irsysctl.c	Sat Dec  1 00:42:27 2001
@@ -10,6 +10,7 @@
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1997, 1999 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.com>
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -32,24 +33,47 @@
 #include <net/irda/irias_object.h>
 
 #define NET_IRDA 412 /* Random number */
-enum { DISCOVERY=1, DEVNAME, DEBUG, SLOTS, DISCOVERY_TIMEOUT, 
-       SLOT_TIMEOUT, MAX_BAUD_RATE, MAX_INACTIVE_TIME, LAP_KEEPALIVE_TIME, };
+enum { DISCOVERY=1, DEVNAME, DEBUG, FAST_POLL, DISCOVERY_SLOTS,
+       DISCOVERY_TIMEOUT, SLOT_TIMEOUT, MAX_BAUD_RATE, MIN_TX_TURN_TIME,
+       MAX_NOREPLY_TIME, WARN_NOREPLY_TIME, LAP_KEEPALIVE_TIME };
 
 extern int  sysctl_discovery;
 extern int  sysctl_discovery_slots;
 extern int  sysctl_discovery_timeout;
-extern int  sysctl_slot_timeout;
+extern int  sysctl_slot_timeout;	/* Candidate */
 extern int  sysctl_fast_poll_increase;
 int         sysctl_compression = 0;
 extern char sysctl_devname[];
 extern int  sysctl_max_baud_rate;
-extern int  sysctl_max_inactive_time;
+extern int  sysctl_min_tx_turn_time;
+extern int  sysctl_max_noreply_time;
+extern int  sysctl_warn_noreply_time;
 extern int  sysctl_lap_keepalive_time;
 
 #ifdef CONFIG_IRDA_DEBUG
 extern unsigned int irda_debug;
 #endif
 
+/* this is needed for the proc_dointvec_minmax - Jean II */
+static int max_discovery_slots = 16;		/* ??? */
+static int min_discovery_slots = 1;
+/* IrLAP 6.13.2 says 25ms to 10+70ms - allow higher since some devices
+ * seems to require it. (from Dag's comment) */
+static int max_slot_timeout = 160;
+static int min_slot_timeout = 20;
+static int max_max_baud_rate = 16000000;	/* See qos.c - IrLAP spec */
+static int min_max_baud_rate = 2400;
+static int max_min_tx_turn_time = 10000;	/* See qos.c - IrLAP spec */
+static int min_min_tx_turn_time = 0;
+static int max_max_noreply_time = 40;		/* See qos.c - IrLAP spec */
+static int min_max_noreply_time = 3;
+static int max_warn_noreply_time = 3;		/* 3s == standard */
+static int min_warn_noreply_time = 1;		/* 1s == min WD_TIMER */
+static int max_lap_keepalive_time = 10000;	/* 10s */
+static int min_lap_keepalive_time = 100;	/* 100us */
+/* For other sysctl, I've no idea of the range. Maybe Dag could help
+ * us on that - Jean II */
+
 static int do_devname(ctl_table *table, int write, struct file *filp,
 		      void *buffer, size_t *lenp)
 {
@@ -77,21 +101,32 @@
 	  sizeof(int), 0644, NULL, &proc_dointvec },
 #endif
 #ifdef CONFIG_IRDA_FAST_RR
-        { SLOTS, "fast_poll_increase", &sysctl_fast_poll_increase,
+        { FAST_POLL, "fast_poll_increase", &sysctl_fast_poll_increase,
 	  sizeof(int), 0644, NULL, &proc_dointvec },
 #endif
-	{ SLOTS, "discovery_slots", &sysctl_discovery_slots,
-	  sizeof(int), 0644, NULL, &proc_dointvec },
+	{ DISCOVERY_SLOTS, "discovery_slots", &sysctl_discovery_slots,
+	  sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
+	  NULL, &min_discovery_slots, &max_discovery_slots },
 	{ DISCOVERY_TIMEOUT, "discovery_timeout", &sysctl_discovery_timeout,
 	  sizeof(int), 0644, NULL, &proc_dointvec },
 	{ SLOT_TIMEOUT, "slot_timeout", &sysctl_slot_timeout,
-	  sizeof(int), 0644, NULL, &proc_dointvec },
+	  sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
+	  NULL, &min_slot_timeout, &max_slot_timeout },
 	{ MAX_BAUD_RATE, "max_baud_rate", &sysctl_max_baud_rate,
-	  sizeof(int), 0644, NULL, &proc_dointvec },
-	{ MAX_INACTIVE_TIME, "max_inactive_time", &sysctl_max_inactive_time,
-	  sizeof(int), 0644, NULL, &proc_dointvec },
+	  sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
+	  NULL, &min_max_baud_rate, &max_max_baud_rate },
+	{ MIN_TX_TURN_TIME, "min_tx_turn_time", &sysctl_min_tx_turn_time,
+	  sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
+	  NULL, &min_min_tx_turn_time, &max_min_tx_turn_time },
+	{ MAX_NOREPLY_TIME, "max_noreply_time", &sysctl_max_noreply_time,
+	  sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
+	  NULL, &min_max_noreply_time, &max_max_noreply_time },
+	{ WARN_NOREPLY_TIME, "warn_noreply_time", &sysctl_warn_noreply_time,
+	  sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
+	  NULL, &min_warn_noreply_time, &max_warn_noreply_time },
 	{ LAP_KEEPALIVE_TIME, "lap_keepalive_time", &sysctl_lap_keepalive_time,
-	  sizeof(int), 0644, NULL, &proc_dointvec },
+	  sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
+	  NULL, &min_lap_keepalive_time, &max_lap_keepalive_time },
 	{ 0 }
 };
 
diff -urN linux-2.5.1-pre4/net/irda/qos.c linux/net/irda/qos.c
--- linux-2.5.1-pre4/net/irda/qos.c	Fri Nov  9 14:22:17 2001
+++ linux/net/irda/qos.c	Sat Dec  1 00:42:27 2001
@@ -51,7 +51,16 @@
  * may want to keep the LAP alive longuer or shorter in case of link failure.
  * Remember that the threshold time (early warning) is fixed to 3s...
  */
-int sysctl_max_inactive_time = 12;
+int sysctl_max_noreply_time = 12;
+/*
+ * Minimum turn time to be applied before transmitting to the peer.
+ * Nonzero values (usec) are used as lower limit to the per-connection
+ * mtt value which was announced by the other end during negotiation.
+ * Might be helpful if the peer device provides too short mtt.
+ * Default is 10 which means using the unmodified value given by the peer
+ * except if it's 0 (0 is likely a bug in the other stack).
+ */
+unsigned sysctl_min_tx_turn_time = 10;
 
 static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get);
 static int irlap_param_link_disconnect(void *instance, irda_param_t *parm, 
@@ -184,7 +193,6 @@
  * Function value_lower_bits (value, array)
  *
  *    Returns a bit field marking all possibility lower than value.
- *    We may need a "value_higher_bits" in the future...
  */
 static inline int value_lower_bits(__u32 value, __u32 *array, int size, __u16 *field)
 {
@@ -207,6 +215,33 @@
 	return i;
 }
 
+/*
+ * Function value_highest_bit (value, array)
+ *
+ *    Returns a bit field marking the highest possibility lower than value.
+ */
+static inline int value_highest_bit(__u32 value, __u32 *array, int size, __u16 *field)
+{
+	int	i;
+	__u16	mask = 0x1;
+	__u16	result = 0x0;
+
+	for (i=0; i < size; i++) {
+		/* Finished ? */
+		if (array[i] <= value)
+			break;
+		/* Shift mask */
+		mask <<= 1;
+	}
+	/* Set the current value to the bit field */
+	result |= mask;
+	/* Send back a valid index */
+	if(i >= size)
+	  i = size - 1;	/* Last item */
+	*field = result;
+	return i;
+}
+
 /* -------------------------- MAIN CALLS -------------------------- */
 
 /*
@@ -254,9 +289,9 @@
 	sysctl_max_baud_rate = index_value(i, baud_rates);
 
 	/* Set configured max disc time */
-	i = value_lower_bits(sysctl_max_inactive_time, link_disc_times, 8,
+	i = value_lower_bits(sysctl_max_noreply_time, link_disc_times, 8,
 			     &qos->link_disc_time.bits);
-	sysctl_max_inactive_time = index_value(i, link_disc_times);
+	sysctl_max_noreply_time = index_value(i, link_disc_times);
 
 	/* LSB is first byte, MSB is second byte */
 	qos->baud_rate.bits    &= 0x03ff;
@@ -281,6 +316,19 @@
 	int index;
 
 	IRDA_DEBUG(2, __FUNCTION__ "()\n");
+
+	/*
+	 * Make sure the mintt is sensible.
+	 */
+	if (sysctl_min_tx_turn_time > qos->min_turn_time.value) {
+		int i;
+
+		/* We don't really need bits, but easier this way */
+		i = value_highest_bit(sysctl_min_tx_turn_time, min_turn_times,
+				      8, &qos->min_turn_time.bits);
+		sysctl_min_tx_turn_time = index_value(i, min_turn_times);
+		qos->min_turn_time.value = sysctl_min_tx_turn_time;
+	}
 
 	/* 
 	 * Not allowed to use a max turn time less than 500 ms if the baudrate
